Berikut ini adalah implementasi lampu penyeberangan jalan (“traffic light at a pedestrian
crosswalk”) dari Example 3.9 di buku “Introduction to Embedded Systems, a cyber physical systems approch” karangan Lee & Seshia.
Model sistem menggunakan Extended State Machine, sebagai berikut:
Daftar Isi
Perangkat Keras
Implementasi hardware menggunakan Arduino Nano ATmega328
Pin yang dipakai:
- output lampu merah : pin D2 (active high)
- output lampu hijau: D3 (active high)
- output lampu kuning: pin D4 (active high)
- input tombol: pin D11 (active low)
- output lampu kedip: pin LED_BUILTIN / D13
Pada model tidak diperlukan lampu kedip, namun pada aplikasi ini ditambahkan lampu kedip sebagai indikasi bahwa sistem berfungsi.
Perangkat Lunak
Model FSM perangkat lunak mengikuti Example 3.9 sebagai berikut:
Kode aplikasi dapat dilihat di https://github.com/waskita/embedded/blob/master/traffic-light-controller/nano-fsm-extended/nano-fsm-extended.ino
Perangkat lunak dibuat menggunakan library Arduino. Perangkat lunak terbagi menjadi bagian inisialisasi yang hanya dilakukan sekali di fungsi setup() dan bagian yang dilakukan berulang-ulang di fungsi loop().
Implementasi finite state machine (FSM) ada di fungsi fsm_init() dan fsm(). Fungsi fsm_init() berisi inisialisasi variabel FSM. fungsi fsm() berisi perubahan (reaction) yang terjadi pada FSM. Sesuai dengan penjelasan di Example 3.9, fungsi fsm() ini dipanggil secara setiap 1 detik, dengan cara menambahkan delay dengan fungsi delay().
Membuat fungsi periodik dengan delay() sebenarnya tidak tepat sekali pewaktuannya. Jika diperlukan pewaktuan yang lebih tepat, dapat menggunakan interupsi Timer1.
Penjelasan Perangkat Lunak
Pada sistem ini terdapat 4 buah state. Untuk itu, setiap state didefinisikan menggunakan macro. Penggunaan macro ini untuk membantu memahami kode dengan lebih mudah.
#define STATE_RED 100
#define STATE_GREEN 101
#define STATE_PENDING 102
#define STATE_YELLOW 103
Setiap state diasosiasikan dengan suatu angka tertentu. Angka yang dipakai bebas, asal memperhatikan batas tipe data yang dipakai. Pada program ini tipe data yang dipakai adalah int, sehingga nilainya yang dapat dipakai adalah antara -32,768 sampai dengan 32,767
Bagian selanjutnya adalah definisi untuk pin-pin yang dipakai. Ada 4 pin yang dipakai, sehingga diperlukan 4 buah macro sebagai berikut.
#define PEDESTRIAN_BUTTON 11
#define OUTPUT_RED 2
#define OUTPUT_GREEN 3
#define OUTPUT_YELLOW 4
Penggunaan macro ini untuk memudahkan pembacaan source code.
Bagian selanjutnya adalah variabel global
int state ;
int sig_r = 0;
int sig_g = 0;
int sig_y = 0;
int count = 0;
Pada program ini diperlukan beberapa variabel global. Variabel global diperlukan karena fungsi fsm_init() dipanggil di setup(), sedangkan fsm() dipanggil di fungsi loop(), padahal ada variabel FSM diperlukan untuk keduanya.
Software embedded yang baik seharusnya meminimalkan penggunaan variabel global, supaya program lebih rapi.
Fungsi setup()
Selanjutnya adalah fungsi setup() untuk inisialisasi Arduino
pinMode(OUTPUT_RED, OUTPUT);
pinMode(OUTPUT_GREEN, OUTPUT);
pinMode(OUTPUT_YELLOW, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PEDESTRIAN_BUTTON, INPUT_PULLUP);
Serial.begin(115200);
Serial.println(__FILE__);
fungsi pinMode() dipanggil untuk mengatur konfigurasi pin-pin sebagai input dan output. Pada kode ini, yang dipakai adalah macro dari nomor pin, dengan tujuan memudahkan pembacaan software.
Port serial sebenarnya tidak wajib diaktifkan untuk sistem lampu penyeberangan ini. Pada implementasi ini, port serial dipakai untuk melakukan monitoring dan debugging. Variabel state, input dan output dicetak ke port serial secara teratur untuk mengecek apakah implementasi sudah berjalan sebagaimana mestinya.
Bagian selanjutnya dari fungsi setup() adalah melakukan inisialisasi FSM.
fsm_init();
activate_output(sig_r, sig_g, sig_y);
print_state(0);
delay(1000);
Fungsi activate_output()
Fungsi activate_output(int r, int g, int y) fungsinya adalah menyalakan lampu output sesuai output dari FSM. Pada penjelasan Example 3.9, output adalah berupa sinyal sigR, sigR dan sigY. Sinyal-sinyal ini hanya ada / aktif ketika diperlukan perubahan lampu yang menyala. Untuk itu perlu fungsi ini untuk mengubah 3 buah sinyal ini menjadi penyalaan lampu yang sesuai.
Fungsi fsm_init()
Fungsi fsm_init() berfungsi memasukkan nilai awal (state) di FSM.
Berikut ini adalah bagian yang diimplementasikan di fungsi fsm_init()
Menurut model FSM, yang perlu dilakukan di awal adalah variabel count diubah menjadi 0, dan state awal adalah RED. Namun pada program ini ditambahkan juga kondisi awal lampu merah menyala, dengan cara memberikan sinyal sigR.
Fungsi loop()
Fungsi loop() dipanggil secara rutin oleh Arduino.
Hal-hal yang dilakukan di fungsi ini adalah:
- membaca input dari tombol dengan perintah digitalRead()
- membalik nilai input, karena tombol di hardware dibuat active low, sedangkan FSM memerlukan tombol pedestrian yang active_high
- memanggil fungsi fsm() untuk menghitung state berikut dari FSM berdasarkan state terkini dan input pada saat itu. Hasilnya adalah state akan diubah, variabel count diubah, dan ada keluaran di variabel output.
- memanggil activate_output() untuk melakukan perubahan lampu menyala jika diperlukan
- mencetak state,input dan output untuk keperluan debugging / monitoring
- membuat delay 1 detik
Fungsi print_state()
Fungsi ini tugasnya adalah mencetak semua variabel sistem untuk melakukan debugging
Fungsi fsm()
Fungsi ini adalah implementasi state chart.
Fungsi ini dirancang untuk tidak menggunakan variabel global, karena variabel global kurang baik dipakai . Semua output menggunakan pointer sebagai metode passing parameter. Pada fungsi ini tidak ada variabel global ataupun variabel jenis static.
Jenis FSM yang dipakai adalah extended state machine, jadi pada FSM ini ada variabel state dan variabel ‘count’.
Pada bagian awal, variabel sig_r,sig_g dan sig_y dinolkan, untuk menandakan bahwa defaultnya tidak ada output apapun.
Pada fungsi fsm() , yang dihitung adalah:
- state selanjutnya, di variabel *state
- variabel count (*count)
Perhitungan state dan variabel *count dibagi berdasarkan state terkini.
Implementasi STATE_RED
Berikut ini adalah bagian dari state chart yang diimplementasikan untuk case STATE_RED
case STATE_RED: {
if (*count >= 60) {
*state = STATE_GREEN;
*sig_g = 1;
*count = 0;
} else {
*count = *count + 1;
}
break;
}
Dari state RED akan pindah ke state GREEN dengan syarat varioabel count lebih besar atau sama dengan 60. Jika terjadi perpindahan ini, maka variabel count diubah menjadi 0.
Jika tidak terjadi perpindahan state, maka variabel count ditambah dengan 1.
Implementasi STATE_GREEN
Berikut ini adalah bagian dari state chart yang diimplementasikan untuk case STATE_GREEN
case STATE_GREEN: {
if (pedestrian == 1 && *count < 60) {
*state = STATE_PENDING;
*count = *count + 1;
} else if (pedestrian == 1 && *count >= 60) {
*sig_y = 1;
*state = STATE_YELLOW;
*count = 0;
} else if (pedestrian == 0 && *count < 60) {
*count = *count + 1;
} else {
// do nothing
}
break;
}
Dari state GREEN akan pindah ke state PENDING dengan syarat tombol input pedestrian ditekan, dan variabel count<60. Jika terjadi perpindahan ini, maka variabel count ditambah 1.
Dari state GREEN akan pindah ke state YELLOW dengan syarat tombol input pedestrian ditekan, dan variabel count lebih dari atau sama dengan 60. Jika terjadi perpindahan ini, maka variabel count diubah menjadi 0.
Implementasi STATE_PENDING
Berikut ini adalah bagian dari state chart yang diimplementasikan untuk case STATE_PENDING
case STATE_PENDING: {
if (*count >= 60) {
*state = STATE_YELLOW;
*sig_y = 1;
*count = 0;
} else {
*count = *count + 1;
}
break;
}
Dari state PENDING akan pindah ke state YELLOW dengan syarat variabel count lebih atau sama dengan 60. Jika terjadi perpindahan ini, maka terjadi output sigY dan variabel count diset menjadi 0.
Jika tidak terjadi perpindahan tersebut, maka variabel count ditambah 1
Implementasi STATE_YELLOW
Berikut ini adalah bagian dari state chart yang diimplementasikan untuk case STATE_YELLOW
case STATE_YELLOW: {
if (*count >= 5) {
*state = STATE_RED;
*sig_r = 1;
*count = 0;
} else {
*count = *count + 1;
}
break;
}
Dari state YELLOW akan pindah ke state RED dengan syarat count>5. Jika terjadi transisi ini , maka akan ada sinyal sigR dan variabel count dibuat menjadi 0
Jika tidak terjadi pindah ke state RED, maka variabel count ditambah 1.
Komentar
Pada model FSM yang diberikan di Example 3.9, ada kelemahan:
- pada initial state tidak diberikan output, sehingga lampu yang menyala tidak terdefinisi. Pada program, hal ini diselesaikan dengan menambahkan sinyal sigR untuk menyalakan lampu merah
Sistem ini memiliki kelemahan dalam pembacaan tombol input. Penekanan tombol perlu dilakukan cukup lama agar terbaca oleh FSM. Hal ini terjadi karena fungsi fsm() dipanggil setiap detik. Jadi pada kondisi worst case, tombol input pedestrian perlu ditekan dan ditahan selama 1 detik. Pada best case cukup ditahan mendekati 0 detik. Rata-rata 0,5 detik.
Untuk mengatasi masalah lambatnya pembacaan tombol, dapat diatas dengan memperkecil perioda pemanggilan fungsi fsm(). Supaya perilaku sistem tidak berubah, maka batas-batas untuk variabel count di FSM perlu diubah. Misal perioda diubah dari 1 detik menjadi 1 milidetik. Maka batasan count<60 di FSM pada STATE_GREEN perlu diubah menjadi count<60000, demikian juga untuk batasan lainnya.
Video Demonstrasi
Penutup
Contoh-contoh lain pemodelan dengan FSM dapat dilihat di artikel “Contoh Implementasi FSM dengan mikrokontroler“
Referensi:
- Lee & Seshia “Introduction to Embedded Systems, a cyber physical systems approach”