Pada unit ini, Anda akan belajar cara mendeteksi gerakan menggunakan sensor gerak PIR (Passive Infrared). Dalam contoh ini, ketika gerakan terdeteksi, ESP32 akan memulai timer dan menyalakan LED selama jumlah detik yang telah ditentukan. Ketika timer berakhir, LED akan mati secara otomatis.
Dengan contoh ini, kita akan memperkenalkan dua konsep baru yang sangat penting dalam pemrograman mikrokontroler: interupsi dan timer. Konsep-konsep ini memungkinkan program kita untuk lebih responsif dan efisien.
ESP32 dengan sensor PIR dan LED pada breadboard
1
Pengenalan Interupsi (Interrupts)
Apa itu Interupsi?
Untuk memicu suatu peristiwa dengan sensor gerak PIR, Anda menggunakan interupsi. Interupsi sangat berguna untuk membuat hal-hal terjadi secara otomatis dalam program mikrokontroler dan dapat membantu menyelesaikan masalah timing.
Dengan interupsi, Anda tidak perlu memeriksa nilai pin saat ini secara konstan. Ketika perubahan terdeteksi, suatu peristiwa dipicu (fungsi dipanggil). Fungsi ini disebut sebagai Interrupt Service Routine (ISR).
Ilustrasi cara kerja interupsi: program utama akan terhenti sementara saat interupsi terjadi
Ketika interupsi terjadi, prosesor menghentikan eksekusi program utama untuk mengeksekusi tugas tertentu, dan kemudian kembali ke program utama seperti yang ditunjukkan pada gambar di atas.
Fungsi attachInterrupt()
Untuk mengatur interupsi di Arduino IDE, Anda menggunakan fungsi attachInterrupt()
, yang menerima argumen berupa pin GPIO, nama fungsi yang akan dieksekusi, dan mode.
attachInterrupt(digitalPinToInterrupt(GPIO), fungsi, mode);
Pin GPIO Interupsi
Argumen pertama adalah nomor GPIO. Biasanya, Anda harus menggunakan digitalPinToInterrupt(GPIO)
untuk mengatur GPIO yang sebenarnya sebagai pin interupsi. Misalnya, jika Anda ingin menggunakan GPIO 27 sebagai interupsi, gunakan:
digitalPinToInterrupt(27)
Dengan board ESP32, semua pin yang disorot dengan persegi panjang merah dalam gambar berikut dapat dikonfigurasi sebagai pin interupsi. Dalam proyek ini, kita akan menggunakan GPIO 27 sebagai interupsi yang terhubung ke sensor gerak PIR.
Pin ESP32 yang dapat digunakan sebagai interupsi (disorot merah)
Fungsi yang Dipicu – ISR
Argumen kedua dari fungsi attachInterrupt()
adalah nama fungsi yang akan dipanggil setiap kali interupsi dipicu – interrupt service routine (ISR).
Fungsi ISR harus sesederhana mungkin, sehingga prosesor kembali ke eksekusi program utama dengan cepat.
Pendekatan terbaik adalah memberi sinyal ke kode utama bahwa interupsi telah terjadi dengan menggunakan variabel global dan dalam loop()
memeriksa dan menghapus flag tersebut, dan mengeksekusi kode.
ISR perlu memiliki awalan IRAM_ATTR
sebelum definisi fungsi untuk menjalankan kode interupsi di RAM.
void IRAM_ATTR detectsMovement() {
// Kode untuk menangani interupsi
}
Mode
Argumen ketiga adalah mode. Ada lima mode berbeda:
- LOW: untuk memicu interupsi setiap kali pin berada pada level LOW;
- HIGH: untuk memicu interupsi setiap kali pin berada pada level HIGH;
- CHANGE: untuk memicu interupsi setiap kali pin berubah nilai—misalnya, dari HIGH ke LOW atau LOW ke HIGH;
- FALLING: untuk ketika pin beralih dari HIGH ke LOW;
- RISING: untuk memicu ketika pin beralih dari LOW ke HIGH;
Ketika sensor gerak PIR mendeteksi gerakan, ia mengirimkan sinyal HIGH—GPIO yang terhubung beralih dari LOW ke HIGH. Jadi, kita harus menggunakan mode RISING.
2
Pengenalan Timer
Mengapa Menggunakan Timer?
Untuk proyek ini, kita juga akan memperkenalkan timer. Kita ingin LED tetap menyala selama jumlah detik yang telah ditentukan setelah gerakan terdeteksi. Alih-alih menggunakan fungsi delay()
yang memblokir kode Anda dan tidak memungkinkan Anda melakukan hal lain selama jumlah detik tertentu, kita akan menggunakan timer.
Timer memungkinkan kode tetap berjalan sambil menghitung waktu di latar belakang
Fungsi delay()
Sampai sekarang, kita telah menggunakan fungsi delay()
yang cukup mudah dimengerti. Fungsi ini menerima satu angka integer sebagai argumen. Angka ini mewakili waktu dalam milidetik program harus menunggu sampai pindah ke baris kode berikutnya.
delay(waktu dalam milidetik)
Ketika Anda melakukan delay(1000)
, program Anda berhenti pada baris itu selama 1 detik.
Catatan: delay()
adalah fungsi blocking. Fungsi blocking mencegah program melakukan hal lain sampai tugas tertentu selesai. Jika Anda memerlukan beberapa tugas untuk terjadi secara bersamaan, Anda tidak dapat menggunakan delay()
.
Penting: Untuk sebagian besar proyek, Anda harus menghindari penggunaan delay dan menggunakan timer sebagai gantinya.
Fungsi millis()
Menggunakan fungsi bernama millis()
, Anda dapat mengembalikan jumlah milidetik yang telah berlalu sejak program pertama kali dimulai.
millis()
Mengapa fungsi itu berguna? Karena dengan menggunakan beberapa perhitungan matematika, Anda dapat dengan mudah memverifikasi berapa banyak waktu yang telah berlalu tanpa memblokir kode Anda.
LED Berkedip dengan millis()
Cuplikan kode berikut menunjukkan cara menggunakan fungsi millis()
untuk membuat proyek LED berkedip. Ini menyalakan LED selama 1000 milidetik, dan kemudian mematikannya.
// konstanta tidak akan berubah. Digunakan di sini untuk mengatur nomor pin: const int ledPin = 26; // nomor pin LED // Variabel akan berubah: int ledState = LOW; // ledState digunakan untuk mengatur LED // Umumnya, Anda harus menggunakan "unsigned long" untuk variabel yang menyimpan waktu // Nilainya akan dengan cepat menjadi terlalu besar untuk int untuk menyimpan unsigned long previousMillis = 0; // akan menyimpan waktu terakhir LED diperbarui // konstanta tidak akan berubah: const long interval = 1000; // interval di mana untuk berkedip (milidetik) void setup() { // mengatur pin digital sebagai output: pinMode(ledPin, OUTPUT); } void loop() { // di sinilah Anda akan menempatkan kode yang perlu berjalan sepanjang waktu. // periksa apakah sudah waktunya untuk berkedip LED; yaitu, jika // perbedaan antara waktu saat ini dan waktu terakhir Anda berkedip // LED lebih besar dari interval di mana Anda ingin // berkedip LED. unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { // simpan waktu terakhir Anda berkedip LED previousMillis = currentMillis; // jika LED mati, nyalakan dan sebaliknya: if (ledState == LOW) { ledState = HIGH; } else { ledState = LOW; } // atur LED dengan ledState variabel: digitalWrite(ledPin, ledState); } }
Mari kita lihat lebih dekat pada sketsa berkedip ini yang bekerja tanpa fungsi delay()
(menggunakan fungsi millis()
sebagai gantinya).
Kode ini mengurangi waktu yang direkam sebelumnya (previousMillis
) dari waktu saat ini (currentMillis
).
Jika sisa hasil bagi lebih besar dari interval (dalam hal ini, 1000 milidetik), program memperbarui variabel previousMillis
ke waktu saat ini dan baik menyalakan atau mematikan LED.
Karena cuplikan ini non-blocking, kode apa pun yang terletak di luar pernyataan if
pertama itu akan berfungsi normal.
Anda sekarang harus dapat memahami bahwa Anda dapat menambahkan tugas lain ke fungsi loop()
Anda, dan kode Anda masih akan berkedip LED setiap satu detik.
Tip: Anda bisa mengunggah kode berkedip yang disediakan ke ESP32 Anda untuk melihat bagaimana itu bekerja. Anda dapat memodifikasi jumlah milidetik untuk melihat bagaimana itu bekerja.
Contoh kode berkedip lengkap tersedia di GitHub repository ESP32 Course.
3
ESP32 dengan Sensor Gerak PIR
Skema Rangkaian
Berikut adalah daftar komponen yang Anda perlukan untuk merakit rangkaian:
- ESP32 DOIT DEVKIT V1 Board
- Sensor gerak PIR Mini (AM312) atau Sensor gerak PIR (HC-SR501)
- LED 5mm
- Resistor 330 Ohm
- Kabel jumper
- Breadboard
Rangkaian ini mudah untuk dirakit. LED terhubung ke GPIO 26. Kami menggunakan Mini AM312 PIR Motion Sensor yang beroperasi pada 3.3V—ini terhubung ke GPIO 27.
Cukup ikuti diagram skematik berikut:
Skema rangkaian ESP32 dengan sensor PIR dan LED
Penting: Skema ini menggunakan modul ESP32 DEVKIT V1 versi dengan 36 GPIO – jika Anda menggunakan model lain, silakan periksa pinout untuk board yang Anda gunakan.
Catatan: Sensor Mini AM312 PIR Motion yang digunakan dalam proyek ini beroperasi pada 3.3V. Namun, jika Anda menggunakan sensor gerak PIR lain seperti HC-SR01, itu beroperasi pada 5V. Anda dapat memodifikasinya untuk beroperasi pada 3.3V atau cukup memberikan daya menggunakan pin Vin ESP32.
Gambar berikut menunjukkan pinout sensor gerak PIR AM312.
Pinout sensor gerak PIR AM312
Kode
Setelah merangkai rangkaian seperti yang ditunjukkan dalam diagram skematik, salin kode yang disediakan ke Arduino IDE Anda.
Anda dapat mengunggah kode apa adanya, atau Anda dapat memodifikasi jumlah detik LED menyala setelah mendeteksi gerakan. Cukup ubah variabel timeSeconds
dengan jumlah detik yang Anda inginkan.
#define timeSeconds 10 // Atur GPIO untuk LED dan Sensor Gerak PIR const int led = 26; const int motionSensor = 27; // Timer: Variabel tambahan unsigned long now = millis(); unsigned long lastTrigger = 0; boolean startTimer = false; boolean motion = false; // Memeriksa apakah gerakan terdeteksi, mengatur LED HIGH dan memulai timer void IRAM_ATTR detectsMovement() { digitalWrite(led, HIGH); startTimer = true; lastTrigger = millis(); } void setup() { // Port serial untuk tujuan debugging Serial.begin(115200); // Mode Sensor Gerak PIR INPUT_PULLUP pinMode(motionSensor, INPUT_PULLUP); // Atur pin motionSensor sebagai interupsi, tetapkan fungsi interupsi dan atur mode RISING attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING); // Atur LED ke LOW pinMode(led, OUTPUT); digitalWrite(led, LOW); } void loop() { // Waktu saat ini now = millis(); // Cek apakah gerakan terdeteksi if((digitalRead(led) == HIGH) && (motion == false)) { Serial.println("GERAKAN TERDETEKSI!!!"); motion = true; } // Matikan LED setelah jumlah detik yang ditentukan dalam variabel timeSeconds if(startTimer && (now - lastTrigger > (timeSeconds*1000))) { Serial.println("Gerakan berhenti..."); digitalWrite(led, LOW); startTimer = false; motion = false; } }
Cara Kerja Kode
Mari kita lihat kode ini lebih detail.
Mulai dengan menetapkan dua pin GPIO ke variabel led
dan motionSensor
.
const int led = 26;
const int motionSensor = 27;
Kemudian, buat variabel yang akan memungkinkan Anda mengatur timer untuk mematikan LED setelah gerakan terdeteksi.
unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;
Variabel now
menyimpan waktu saat ini. Variabel lastTrigger
menyimpan waktu ketika sensor PIR mendeteksi gerakan. startTimer
adalah variabel boolean yang memulai timer ketika gerakan terdeteksi.
Fungsi ISR
Fungsi detectsMovement()
berjalan ketika gerakan terpicu. Fungsi ini menyalakan LED, mengatur variabel boolean startTimer
ke True, dan memperbarui variabel lastTrigger
dengan waktu saat ini.
void IRAM_ATTR detectsMovement() {
digitalWrite(led, HIGH);
startTimer = true;
lastTrigger = millis();
}
Catatan: IRAM_ATTR
digunakan untuk menjalankan kode interupsi di RAM. Jika tidak, kode disimpan di flash dan lebih lambat.
Setup
Dalam setup()
, mulai dengan menginisialisasi monitor serial pada baud rate 115200.
Serial.begin(115200);
Atur sensor gerak PIR sebagai INPUT PULLUP.
pinMode(motionSensor, INPUT_PULLUP);
Untuk mengatur pin sensor PIR sebagai interupsi, gunakan fungsi attachInterrupt()
yang dijelaskan sebelumnya.
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
Pin yang akan mendeteksi gerakan adalah GPIO 27, dan itu akan memanggil fungsi detectsMovement()
pada mode RISING.
LED adalah OUTPUT yang statusnya dimulai pada LOW.
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
Loop
Fungsi loop()
terus berjalan berulang-ulang. Dalam setiap loop, variabel now
diperbarui dengan waktu saat ini.
now = millis();
Kondisi berikut memeriksa apakah LED saat ini menyala (HIGH) dan apakah flag gerakan adalah false. Flag gerakan menunjukkan apakah gerakan telah terdeteksi sejak LED dinyalakan.
if((digitalRead(led) == HIGH) && (motion == false)) {
Serial.println("GERAKAN TERDETEKSI!!!");
motion = true;
}
Jika kedua kondisi benar, itu berarti gerakan terdeteksi saat LED menyala. Pesan “GERAKAN TERDETEKSI!!!” dicetak ke Serial Monitor dan flag gerakan diatur ke true.
Setelah tahap ini, kode kembali ke loop()
.
Kali ini, variabel startTimer
benar. Jadi, ketika waktu yang ditentukan dalam detik telah berlalu (sejak gerakan terdeteksi), pernyataan if
berikut akan benar.
if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
Serial.println("Gerakan berhenti...");
digitalWrite(led, LOW);
startTimer = false;
motion = false;
}
Pesan “Gerakan berhenti…” akan dicetak di monitor serial, LED dimatikan, dan variabel startTimer
diatur ke false.
Demonstrasi
Unggah kode ke board ESP32 Anda. Pastikan Anda telah memilih board dan port COM yang benar. Buka Serial Monitor dengan baud rate 115200.
Gerakkan tangan Anda di depan sensor PIR. LED akan menyala, dan pesan dicetak di Serial Monitor yang mengatakan “GERAKAN TERDETEKSI!!!”. Setelah 10 detik, LED akan mati.
Demonstrasi ESP32 dengan sensor PIR – LED menyala saat gerakan terdeteksi
Tip aplikasi: Proyek ini bisa menjadi dasar untuk berbagai aplikasi praktis:
- Sistem pencahayaan otomatis yang hanya menyala ketika ada orang di ruangan
- Sistem keamanan yang mengirimkan pemberitahuan ketika gerakan terdeteksi
- Penghitung kunjungan untuk mengetahui berapa banyak orang yang melewati area tertentu
- Pengontrol perangkat yang mengaktifkan layar atau fungsi hanya ketika pengguna terdeteksi
4
Kesimpulan
Sebagai ringkasan, interupsi digunakan untuk mendeteksi perubahan dalam kondisi GPIO tanpa perlu terus-menerus membaca nilai GPIO saat ini. Dengan interupsi, ketika perubahan terdeteksi, suatu fungsi dipicu.
Anda juga telah belajar cara mengatur timer sederhana yang memungkinkan Anda memeriksa apakah sejumlah detik yang telah ditentukan telah berlalu tanpa memblokir kode Anda.
Interupsi dan timer adalah dua konsep penting yang akan sangat berguna dalam proyek-proyek Anda nantinya. Mereka memungkinkan kode Anda menjadi lebih responsif dan efisien, menangani beberapa tugas secara bersamaan tanpa terhambat oleh operasi yang memakan waktu.
Praktik Terbaik
Berikut adalah beberapa praktik terbaik untuk diingat ketika bekerja dengan interupsi dan timer:
Untuk Interupsi
- Jaga fungsi ISR sesederhana mungkin
- Hindari penggunaan delay() dalam ISR
- Gunakan variabel volatile untuk variabel yang diubah dalam ISR
- Selalu sertakan awalan IRAM_ATTR pada fungsi ISR di ESP32
Untuk Timer
- Selalu gunakan unsigned long untuk variabel waktu
- Perhitungkan kemungkinan overflow dari millis()
- Hindari penggunaan delay() untuk tugas yang memerlukan eksekusi lain
- Pisahkan logika waktu untuk tugas yang berbeda dengan variabel terpisah
Komunitas & Dukungan
Jika Anda memiliki pertanyaan atau mengalami kesulitan dengan contoh sensor PIR, interupsi, atau timer pada ESP32, silakan tinggalkan komentar di bawah atau bertanya pada grup telegram https://t.me/kodingindonesia. Selamat belajar dan bereksperimen dengan ESP32!