An Arduino traffic light, with a crossing button that works
Menengah
Atau lihat semua pelajaran.
- 503komponen di meja kerja
- 44board sedang berjalan
- 1.00xwaktu nyata, di semua board
Lampu lalu lintas adalah proyek yang dipilih hampir setiap kursus Arduino di minggu keduanya, karena isinya cuma tiga LED dan beberapa delay tapi hasilnya tetap terlihat keren. Versi di atas selangkah lebih jauh dari kebanyakan: ini adalah penyeberangan pelikan, dengan lampu pejalan kaki, tombol permintaan, dan aturan bahwa lampu hijau tidak pernah berakhir lebih cepat dari seharusnya. Proyek ini sedang berjalan di Arduino Uno simulasi sekarang, dengan sketch yang sama dengan yang bisa kamu baca di bagian bawah halaman ini.
Tekan tombol di breadboard dan lihat apa yang terjadi. Lampu biru WAIT menyala begitu kontaknya tertutup, hijau bertahan sampai waktu minimumnya terpenuhi, lalu kuning, lalu merah bersama lampu putih tanda jalan, lalu fase berkedip, dan kembali ke hijau. Buka di editor untuk mengubah pengaturan waktu mana pun, lalu jalankan lagi.
What you need
- Arduino Uno
- Lima LED: merah, kuning, hijau, putih, dan biru
- Lima resistor 220 ohm, satu per LED
- Satu tombol tekan
- Satu inverter hex 74HC04, untuk lampu WAIT
- Breadboard dan kabel jumper
Wiring
| Komponen dan pin | Disambung ke | Catatan |
|---|---|---|
| LED merah, lewat 220 ohm | Pin 8 | Lampu merah kendaraan |
| LED kuning, lewat 220 ohm | Pin 9 | Lampu kuning kendaraan |
| LED hijau, lewat 220 ohm | Pin 10 | Lampu hijau kendaraan |
| LED putih, lewat 220 ohm | Pin 11 | Lampu tanda jalan |
| Tombol tekan, satu sisi | Pin 2 | Sisi lainnya ke GND; sketch menyalakan pull-up internal |
| Input 74HC04 | Jalur tombol | Output-nya menyalakan lampu biru WAIT, tanpa kode sama sekali |
| 74HC04 pin 14 dan pin 7 | 5 V dan GND | Chip ini tidak melakukan apa-apa sampai keduanya tersambung |
Cara urutannya disusun
Sebuah penyeberangan punya satu keadaan diam, hijau untuk kendaraan, dan hanya meninggalkan keadaan itu kalau ada yang meminta. Sketch menuliskannya sebagai loop dengan empat fase: hijau sampai ada permintaan (tapi tidak pernah kurang dari tiga detik dan tidak pernah lebih dari sembilan), kuning selama satu setengah detik, merah bersama lampu tanda jalan selama empat detik, lalu dua belas kedipan setengah detik dari lampu kuning dan lampu tanda jalan bersamaan, seperti yang dilakukan penyeberangan pelikan sungguhan di akhir fase jalannya.
Setiap fase adalah panggilan ke helper kecil lights() yang mengatur merah, kuning, dan hijau sekaligus. Menulis ketiga lampu bersamaan, setiap kali, adalah cara mencegah bug lampu lalu lintas yang paling umum, yaitu saat sebuah fase lupa mematikan lampu dari fase sebelumnya sehingga dua warna menyala bersamaan.
void loop() {
lights(0, 0, 1); // green
waitForRequest();
lights(0, 1, 0); // amber
delay(AMBER_MS);
lights(1, 0, 0); // red, and walk
digitalWrite(WALK, HIGH);
delay(WALK_MS);
for (int i = 0; i < 12; i++) { // the flashing phase
int on = (i & 1) ? LOW : HIGH;
digitalWrite(WALK, on);
lights(0, on == HIGH, 0);
delay(500);
}
digitalWrite(WALK, LOW);
}Kenapa tombolnya memakai interrupt
Sketch penyeberangan pertama yang ditulis kebanyakan orang mengecek tombol dengan digitalRead() di suatu tempat di loop(). Cara itu berhasil saat diuji dengan menahan tombol, dan gagal begitu seseorang hanya mengetuknya, karena hampir sepanjang siklus board sedang berada di dalam delay() dan sama sekali tidak melihat pin-nya. Ketukan di fase merah tidak pernah terlihat.
Sketch ini memasang tombol di pin 2, yaitu pin INT0 milik Uno, dan memasang interrupt di edge turunnya. Handler-nya hanya melakukan satu hal: memasang penanda bernama requested. Apa pun yang sedang dikerjakan loop utama, termasuk sedang diam di dalam delay, tekanannya tertangkap. Loop lalu membaca penanda itu sambil menahan interrupt sebentar, jadi tidak pernah membaca nilai yang baru setengah ditulis, dan menghapusnya di akhir siklus.
Penanda itu dideklarasikan volatile, yang memberi tahu compiler bahwa ada sesuatu di luar alur normal program yang bisa mengubahnya. Kalau kata itu tidak ditulis, compiler yang melakukan optimasi boleh membaca variabelnya sekali dan tidak pernah melihatnya lagi.
volatile bool requested = false;
static void onPress() { requested = true; }
void setup() {
pinMode(BUTTON, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON), onPress, FALLING);
}Lampu yang tidak butuh kode
Lampu biru WAIT sama sekali tidak dikendalikan oleh Uno. Lampu ini tergantung di satu gerbang inverter 74HC04 yang disambung langsung ke jalur tombol: jalurnya berada di high lewat pull-up, tombol menariknya ke low, inverter mengubah low itu menjadi high, dan lampunya menyala. Reaksinya dalam hitungan nanodetik, sebelum sketch sempat menyadari apa pun.
Lampu ini ada di rangkaian sebagai pengingat bahwa tidak semua tugas harus dikerjakan mikrokontroler. Lampu ini juga menunjukkan sesuatu tentang simulatornya: 74HC04 di sini adalah chip 14 pin sungguhan dengan catu daya di pin 14 dan 7, dan kalau kamu menghapus salah satu kabel catu dayanya di editor, lampunya berhenti bekerja, persis seperti di meja sungguhan.
Coba di editor
Buka rangkaiannya di editor dan ubah MIN_GREEN dari 3000 menjadi 500. Tekan tombolnya begitu lampu hijau menyala, maka sekarang penyeberangan menghentikan kendaraan hampir seketika, dan itulah alasan penyeberangan sungguhan punya waktu minimum.
Lalu sengaja rusak. Ganti interrupt dengan digitalRead() biasa pada pin 2 di dalam waitForRequest() dan ketuk tombolnya di fase merah. Tekanannya hilang. Pasang kembali interrupt-nya, maka tekanannya tertangkap setiap kali.
Terakhir, seret logic analyzer dari kotak komponen dan jepitkan keempat channel-nya ke pin 8, 9, 10, dan 11. Seluruh urutannya muncul sebagai diagram waktu, dengan fase berkedip sebagai deretan pulsa yang rapi, cara yang jauh lebih cepat untuk mengecek pengaturan waktunya daripada menonton lampu.
Common mistakes
LED tanpa resistor. Setiap lampu di sini punya resistor 220 ohm sendiri, yang menahan arus di sekitar 14 miliampere dari pin 5 volt. LED yang disambung langsung ke pin akan mencoba menarik arus jauh lebih besar daripada yang seharusnya diberikan pin.
Membaca tombol hanya di antara delay, seperti dijelaskan di atas. Kalau tekanan kadang diabaikan, hampir selalu inilah penyebabnya.
Mengira tombol terbaca HIGH saat ditekan. Dengan INPUT_PULLUP, pin diam di high dan tekanan menariknya ke ground, jadi saat ditekan terbaca LOW. Interrupt dipasang di edge turun karena alasan yang sama.
Dua warna menyala bersamaan, karena satu fase menyalakan lampu dan fase berikutnya tidak pernah mematikannya. Mengatur ketiganya bersamaan dalam satu fungsi menghilangkan seluruh jenis bug ini.
Pertanyaan
Bisakah aku membuat lampu lalu lintas sederhana tanpa tombol?
Bisa. Hapus tombol dan panggilan waitForRequest(), lalu beri lampu hijau delay() tetapnya sendiri. Hasilnya adalah urutan tiga LED yang klasik. Versi dengan tombol layak ditambah beberapa baris karena begitulah cara kerja penyeberangan sungguhan.
Kenapa tombolnya di pin 2?
Di Uno, hanya pin 2 dan 3 yang punya interrupt eksternal sendiri, INT0 dan INT1. Pin mana pun bisa dibaca dengan digitalRead(), tapi hanya kedua pin itu yang bisa memanggil fungsi begitu nilainya berubah.
Apakah ini berjalan di Arduino Uno sungguhan tanpa diubah?
Sketch-nya hanya memakai panggilan Arduino standar: pinMode, digitalWrite, delay, millis, dan attachInterrupt. Ini kode yang sama dengan yang akan kamu upload dari Arduino IDE.
Bagaimana cara membuatnya jadi persimpangan empat arah?
Tambahkan satu set tiga lampu lagi di tiga pin lain dan beri setiap fase dua panggilan, satu untuk setiap jalan. Persimpangan juga punya fase merah-dan-kuning sebelum hijau, yang tidak ada di penyeberangan pelikan.
Lanjutkan
Rakit versi nyatanya
Buka editor, ubah sebuah nilai, dan lihat angkanya ikut berubah. Tidak ada yang perlu dipasang, dan tidak perlu akun.