Motion

The motion module is intended to work with passive infrared (PIR) sensors. These have onboard logic that determine when a trigger point has been reached, at which point they change an interrupt pin into a high signal. We can use this to determine whether a person is in a room, or is walking through a hallway.

Its code looks as follows:

#include "base_module.h"

#define GPIO_PIN 0

class MotionModule {
static int pin;
static Timer timer;
static Timer warmup;
static bool motion;
static bool firstLow;

public:
static bool initialize();
static bool start();
static bool shutdown();
static void config(String cmd);
static void warmupSensor();
static void readSensor();
static void IRAM_ATTR interruptHandler();
};

Of note here is that we explicitly move the interrupt handler method into the MCU's SRAM with the IRAM_ATTR keyword, to prevent any delay when the interrupt gets called.

Its implementation is as follows:

#include "motion_module.h"
int MotionModule::pin = GPIO_PIN;
Timer MotionModule::timer;
Timer MotionModule::warmup;
bool MotionModule::motion = false;
bool MotionModule::firstLow = true;
bool MotionModule::initialize() {
BaseModule::registerModule(MOD_IDX_MOTION, MotionModule::start,
MotionModule::shutdown);
}
bool MotionModule::start() {
if (!OtaCore::claimPin(ESP8266_gpio00)) { return false; }
pinMode(pin, INPUT);
warmup.initializeMs(60000, MotionModule::warmupSensor).start();
return true;
}

A PIR sensor requires warm-up time to stabilize its readings. We give it a minute using the warm-up timer. We also set the mode for the GPIO pin we're using.


bool MotionModule::shutdown() {
if (!OtaCore::releasePin(ESP8266_gpio00)) { return false; } // RX 0

timer.stop();
detachInterrupt(pin);

return true;
}


void MotionModule::config(String cmd) {
Vector<String> output;
int numToken = splitString(cmd, '=', output);
if (output[0] == "set_pin" && numToken > 1) {
//
}
}


void MotionModule::warmupSensor() {
warmup.stop();
attachInterrupt(pin, &MotionModule::interruptHandler, CHANGE);

timer.initializeMs(5000, MotionModule::readSensor).start();
}

After the sensor has finished warming up, we stop its timer and attach the interrupt to handle any signals from the sensor. We'll check up on the shared variable with the interrupt routine, to see whether the value has changed, publishing the current value every 5 seconds:

 void MotionModule::readSensor() {
if (!motion) {
if (firstLow) { firstLow = false; }
else {
OtaCore::publish("nsa/motion", OtaCore::getLocation() + ";0");
firstLow = true;
}
}
else if (motion) {
OtaCore::publish("nsa/motion", OtaCore::getLocation() + ";1");
firstLow = true;
}
}

When checking the current sensor value, we make it a point to ignore the first time that the sensor reports LOW. This in order to ensure that we ignore moments when people do not move a lot in the room. The resulting value is then published on the MQTT topic:

void IRAM_ATTR MotionModule::interruptHandler() {
int val = digitalRead(pin);
if (val == HIGH) { motion = true; }
else { motion = false; }
}

The interrupt handler merely updates the local Boolean value. Because of the relatively long transition times for most processing circuits for PIR sensor there is quite a bit of time (seconds) before the sensor will detect motion again, creating dead zones. Here we keep track of the last registered value.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.116.20