In this article, we explore the concepts of ESP32 timer and interrupt. We focus on an example that shows motion detection through a PIR motion sensor. This PIR motion sensor will be interfaced with the ESP32 board and a 5mm LED. The LED will be controlled by interrupts and timers on the ESP32. When motion is detected, an interrupt is triggered, a timer is started, and the LED turns on for a predetermined amount of time. The LED will turn off when the timer expires.
PARTS REQUIRED
To follow this tutorial you need the following parts
- ESP32 DOIT DEVKIT V1 Board –
- Mini PIR motion sensor (AM312) or PIR motion sensor (HC-SR501
- 5mm LED
- 330 Ohm resistor
- Jumper wires
- Breadboard
ESP32 DOIT DEVKIT V1 Board
The Esp32 DevKit v1 is one of the development boards created to evaluate the ESP-WROOM-32 module. It is based on the ESP32 microcontroller that boasts Wifi, Bluetooth, Ethernet, and Low Power support all in a single chip.ESP32 is already integrated antenna and RF balun, power amplifier, low-noise amplifiers, filters, and power management module. The entire solution takes up the least amount of printed circuit board area.
Mini PIR motion sensor (AM312) or PIR motion sensor (HC-SR501)
It is referred to as PIR, “Passive Infrared”, “Pyroelectric”, or “IR motion” sensors.PIR sensors allow to sense motion, almost always used to detect whether a human has moved in or out of the sensors range.
INTRODUCTION TO INTERRUPTS
Interrupts are used to handle events that do not happen during the sequential execution of a program. An external interrupt or a ‘hardware interrupt’ is caused by the external hardware module. With interrupt, we do not need to continuously check the state of the digital input pin. When an interrupt occurs (a change is detected), the processor stops the execution of the main program and a function is called upon known as ISR or the Interrupt Service Routine. The processor then temporarily works on a different task (ISR) and then gets back to the main program after the handling routine has ended.
To set an interrupt in the Arduino IDE, you use the attachInterrupt() function, which accepts as arguments: the GPIO pin, the name of the function to be executed, and mode:
attachInterrupt(digitalPinToInterrupt(GPIO), function, mode);
The attachInterrupt() function takes in three arguments:
1. GPIO Interrupt
The first argument is a GPIO number. Normally, we should use digitalPinToInterrupt(GPIO) to set the actual GPIO as an interrupt pin. For example
digitalPinToInterrupt(27)
2. Function to be triggered
The second argument of the attachInterrupt() function is the name of the function that will be called every time the interrupt is triggered.
3. Mode
The third argument is the mode. There are 5 different modes:
- LOW: to trigger the interrupt whenever the pin is LOW;
- HIGH: to trigger the interrupt whenever the pin is HIGH;
- CHANGE: to trigger the interrupt whenever the pin changes value – for example from HIGH to LOW or LOW to HIGH;
- FALLING: for when the pin goes from HIGH to LOW;
- RISING: to trigger when the pin goes from LOW to HIGH.
For this example will be using the RISING mode, because when the PIR motion sensor detects motion, the GPIO it is connected to goes from LOW to HIGH.
INTRODUCTION TO TIMERS
When motion is detected by our PIR motion sensor, we want the LED to stay on for a specific amount of seconds and then turn off when the timer expires. We’ll do this by using the millis() method, which acts as a timer. The delay(ms) method can also be used, but it requires the time in milliseconds as an input, which indicates the time for which the programme code stops running and is paused.
SCHEMATIC
We’ll be using an LED with a resistor. The LED is connected to GPIO 26. We’ll be using the Mini AM312 PIR Motion Sensor that operates at 3.3V. It will be connected to GPIO 27.
SOURCE CODE
Copy the code provided to the Arduino IDE after wiring the circuit as shown in the schematic diagram. You can either upload the code as is or change the number of seconds the LED stays bright after motion is detected. Simply replace the time seconds variable with the desired amount of seconds.
/ constants won't change. Used here to set a pin number : const int ledPin = 26; // the number of the LED pin // Variables will change : int ledState = LOW; // ledState used to set the LED // Generally, you should use "unsigned long" for variables that hold time // The value will quickly become too large for an int to store unsigned long previousMillis = 0; // will store last time LED was updated // constants won't change : const long interval = 1000; // interval at which to blink (milliseconds) void setup() { // set the digital pin as output: pinMode(ledPin, OUTPUT); } void loop() { // here is where you'd put code that needs to be running all the time. // check to see if it's time to blink the LED; that is, if the // difference between the current time and last time you blinked // the LED is bigger than the interval at which you want to // blink the LED. unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: if (ledState == LOW) { ledState = HIGH; } else { ledState = LOW; } // set the LED with the ledState of the variable: digitalWrite(ledPin, ledState); }
HOW CODE WORKS
Start by assigning two GPIO pins to the led and motionSensor variables.
// Set GPIOs for LED and PIR Motion Sensor
const int led = 26;
const int motionSensor = 27;
Then, create variables that will allow you to set a timer to turn the LED off after motion is detected.
// Timer: Auxiliar variables
long now = millis();
long lastTrigger = 0;
boolean startTimer = false;
The current time is stored in the now variable. The time when the PIR sensor detects motion is stored in the lastTrigger variable. When motion is detected, the startTimer boolean variable starts the timer.
setup()
In the setup(), start by initializing the Serial port at 115200 baud rate.
Serial.begin(115200);
Set the PIR Motion sensor as an INPUT PULLUP.
pinMode(motionSensor, INPUT_PULLUP);
To set the PIR sensor pin as an interrupt, use the attachInterrupt() function as described earlier.
The pin that will detect motion is GPIO 27 and it will call the function detectsMovement() on RISING mode.
The LED is an OUTPUT whose state starts at LOW.
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
loop()
The loop() method is used to repeat a process over and over again. The now variable is updated with the current time in each loop.
now = millis();
Nothing else is done in the loop().
However, because we’ve previously placed an interrupt on the setup, the detectsMovement() function is invoked when motion is detected.The detectsMovement() function publishes a message in the Serial Monitor, illuminates the LED, sets the startTimer boolean variable to true, and sets the lastTrigger variable to the current time.
void IRAM_ATTR detectsMovement() {
Serial.println(“MOTION DETECTED!!!”);
digitalWrite(led, HIGH);
startTimer = true;
lastTrigger = millis();
}
Note: IRAM_ATTR is used to run the interrupt code in RAM, otherwise code is stored in flash and it’s slower.
The code returns to the loop() after this step.The startTimer variable is set to true this time. As a result, the following if statement will be true when the period defined in seconds has passed (since motion was detected).
if(startTimer && (now – lastTrigger > (timeSeconds*1000))) {
Serial.println(“Motion stopped…”);
digitalWrite(led, LOW);
startTimer = false;
}
The “Motion stopped…” message will be printed in the Serial Monitor, the LED is turned off, and the startTimer variable is set to false.
DEMONSTRATION
Upload the code to your ESP32 board.Open the Serial Monitor at a baud rate of 115200.
Place your hand in front of the PIR sensor to activate it. The LED should light up, and the Serial Monitor should display the message “MOTION DETECTED!!!” The LED should turn off after 10 seconds.