Commit 01bc9d8a authored by user2684's avatar user2684 Committed by GitHub
Browse files

New interrupt handling framework which covers both sleeping and NOT sleeping...

New interrupt handling framework which covers both sleeping and NOT sleeping nodes  (#142, #149, #166, #154)
parent b0312ff3
......@@ -323,6 +323,12 @@ void Sensor::setReportIntervalMinutes(int value) {
_report_timer->start(value,MINUTES);
}
// listen for interrupts on the given pin so interrupt() will be called when occurring
void Sensor::setInterrupt(int pin, int mode, int initial) {
_interrupt_pin = pin;
_node_manager->setInterrupt(pin,mode,initial);
}
// present the sensor to the gateway and controller
void Sensor::presentation() {
#if DEBUG == 1
......@@ -365,7 +371,6 @@ void Sensor::loop(const MyMessage & message) {
#endif
// for numeric sensor requiring multiple samples, keep track of the total
float total = 0;
// keep track of the number of cycles since the last update
// collect multiple samples if needed
for (int i = 0; i < _samples; i++) {
// call the sensor-specific implementation of the main task which will store the result in the _value variable
......@@ -391,6 +396,7 @@ void Sensor::loop(const MyMessage & message) {
if (_isReceive(message) || _isWorthSending(avg != _last_value_int)) {
_last_value_int = avg;
_send(_msg.set(avg));
_value_int = -1;
}
}
// process a float value
......@@ -401,6 +407,7 @@ void Sensor::loop(const MyMessage & message) {
if (_isReceive(message) || _isWorthSending(avg != _last_value_float)) {
_last_value_float = avg;
_send(_msg.set(avg, _float_precision));
_value_float = -1;
}
}
// process a string value
......@@ -409,6 +416,7 @@ void Sensor::loop(const MyMessage & message) {
if (_isReceive(message) || _isWorthSending(strcmp(_value_string, _last_value_string) != 0)) {
_last_value_string = _value_string;
_send(_msg.set(_value_string));
_value_string = "";
}
}
// turn the sensor off
......@@ -419,6 +427,12 @@ void Sensor::loop(const MyMessage & message) {
if (! _isReceive(message) && _report_timer->isRunning() && _report_timer->isOver()) _report_timer->restart();
}
// receive and handle an interrupt
void Sensor::interrupt() {
// call the implementation of onInterrupt()
onInterrupt();
}
// receive a message from the radio network
void Sensor::receive(const MyMessage &message) {
// return if not for this sensor
......@@ -587,6 +601,10 @@ void SensorAnalogInput::onProcess(Request & request) {
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorAnalogInput::onInterrupt() {
}
// read the analog input
int SensorAnalogInput::_getAnalogRead() {
#ifndef MY_GATEWAY_ESP8266
......@@ -712,6 +730,10 @@ void SensorThermistor::onProcess(Request & request) {
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorThermistor::onInterrupt() {
}
/*
SensorML8511
*/
......@@ -764,6 +786,10 @@ void SensorML8511::onReceive(const MyMessage & message) {
void SensorML8511::onProcess(Request & request) {
}
// what to do when receiving an interrupt
void SensorML8511::onInterrupt() {
}
// The Arduino Map function but for floats
float SensorML8511::_mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
......@@ -830,6 +856,10 @@ void SensorACS712::onProcess(Request & request) {
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorACS712::onInterrupt() {
}
/*
SensorRainGauge
*/
......@@ -840,75 +870,47 @@ SensorRainGauge::SensorRainGauge(NodeManager* node_manager, int child_id, int pi
setPresentation(S_RAIN);
setType(V_RAIN);
setValueType(TYPE_FLOAT);
// create the timer
_timer = new Timer(node_manager);
}
// initialize static variables
long SensorRainGauge::_last_tip = 0;
long SensorRainGauge::_count = 0;
// setter/getter
void SensorRainGauge::setReportInterval(int value) {
_report_interval = value;
}
void SensorRainGauge::setSingleTip(float value) {
_single_tip = value;
}
void SensorRainGauge::setInitialValue(int value) {
_initial_value = value;
}
// what to do during before
void SensorRainGauge::onBefore() {
// set the pin as input and enabled pull up
pinMode(_pin, INPUT_PULLUP);
// attach to the pin's interrupt and execute the routine on falling
attachInterrupt(digitalPinToInterrupt(_pin), _onTipped, FALLING);
// start the timer
_timer->start(_report_interval,MINUTES);
// configure the interrupt pin so onInterrupt() will be called on tip
setInterrupt(_pin,FALLING,_initial_value);
}
// what to do during setup
void SensorRainGauge::onSetup() {
}
// what to do when when receiving an interrupt
void SensorRainGauge::_onTipped() {
long now = millis();
// on tipping, two consecutive interrupts are received, ignore the second one
if ( (now - _last_tip > 100) || (now < _last_tip) ){
// increase the counter
_count++;
#if DEBUG == 1
Serial.println(F("RAIN+"));
#endif
}
_last_tip = now;
}
// what to do during loop
void SensorRainGauge::onLoop() {
// avoid reporting the same value multiple times
_value_float = -1;
_timer->update();
// time to report
if (_timer->isOver()) {
// report the total amount of rain for the last period
_value_float = _count * _single_tip;
#if DEBUG == 1
Serial.print(F("RAIN I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(_value_float);
#endif
// reset the timer
_timer->restart();
}
// do not execute loop if called by an interrupt
if (_node_manager->getLastInterruptPin() == _interrupt_pin) return;
// time to report the rain so far
_value_float = _count * _single_tip;
#if DEBUG == 1
Serial.print(F("RAIN I="));
Serial.print(_child_id);
Serial.print(F(" T="));
Serial.println(_value_float);
#endif
// reset the counter
_count = 0;
}
// what to do as the main task when receiving a message
void SensorRainGauge::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) {
// report the total amount of rain for the last period
_value_float = _count * _single_tip;
_value_float = _count * _single_tip;
}
}
......@@ -916,13 +918,21 @@ void SensorRainGauge::onReceive(const MyMessage & message) {
void SensorRainGauge::onProcess(Request & request) {
int function = request.getFunction();
switch(function) {
case 101: setReportInterval(request.getValueInt()); break;
case 102: setSingleTip(request.getValueFloat()); break;
default: return;
}
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorRainGauge::onInterrupt() {
// increase the counter
_count++;
#if DEBUG == 1
Serial.println(F("RAIN+"));
#endif
}
/*
SensorRain
*/
......@@ -998,6 +1008,10 @@ void SensorDigitalInput::onReceive(const MyMessage & message) {
// what to do when receiving a remote message
void SensorDigitalInput::onProcess(Request & request) {
}
// what to do when receiving an interrupt
void SensorDigitalInput::onInterrupt() {
}
#endif
......@@ -1080,6 +1094,10 @@ void SensorDigitalOutput::onProcess(Request & request) {
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorDigitalOutput::onInterrupt() {
}
// write the value to the output
void SensorDigitalOutput::setStatus(int value) {
// pre-process the input value
......@@ -1301,6 +1319,10 @@ void SensorDHT::onReceive(const MyMessage & message) {
// what to do when receiving a remote message
void SensorDHT::onProcess(Request & request) {
}
// what to do when receiving an interrupt
void SensorDHT::onInterrupt() {
}
#endif
/*
......@@ -1376,6 +1398,10 @@ void SensorSHT21::onReceive(const MyMessage & message) {
// what to do when receiving a remote message
void SensorSHT21::onProcess(Request & request) {
}
// what to do when receiving an interrupt
void SensorSHT21::onInterrupt() {
}
#endif
/*
......@@ -1415,8 +1441,7 @@ void SensorSwitch::onBefore() {
if (_mode == RISING) _value_int = LOW;
else if (_mode == FALLING) _value_int = HIGH;
// set the interrupt pin so it will be called only when waking up from that interrupt
_interrupt_pin = _pin;
_node_manager->setInterrupt(_pin,_mode,_initial);
setInterrupt(_pin,_mode,_initial);
}
// what to do during setup
......@@ -1425,6 +1450,30 @@ void SensorSwitch::onSetup() {
// what to do during loop
void SensorSwitch::onLoop() {
}
// what to do as the main task when receiving a message
void SensorSwitch::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) {
_value_int = digitalRead(_pin);
}
}
// what to do when receiving a remote message
void SensorSwitch::onProcess(Request & request) {
int function = request.getFunction();
switch(function) {
case 101: setMode(request.getValueInt()); break;
case 102: setDebounce(request.getValueInt()); break;
case 103: setTriggerTime(request.getValueInt()); break;
case 104: setInitial(request.getValueInt()); break;
default: return;
}
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorSwitch::onInterrupt() {
// wait to ensure the the input is not floating
if (_debounce > 0) wait(_debounce);
// read the value of the pin
......@@ -1447,23 +1496,6 @@ void SensorSwitch::onLoop() {
_value_int = -1;
}
}
// what to do as the main task when receiving a message
void SensorSwitch::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) onLoop();
}
// what to do when receiving a remote message
void SensorSwitch::onProcess(Request & request) {
int function = request.getFunction();
switch(function) {
case 101: setMode(request.getValueInt()); break;
case 102: setDebounce(request.getValueInt()); break;
case 103: setTriggerTime(request.getValueInt()); break;
case 104: setInitial(request.getValueInt()); break;
default: return;
}
_send(_msg_service.set(function));
}
/*
* SensorDoor
......@@ -1548,6 +1580,10 @@ void SensorDs18b20::onProcess(Request & request) {
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorDs18b20::onInterrupt() {
}
// function to print a device address
DeviceAddress* SensorDs18b20::getDeviceAddress() {
return &_device_address;
......@@ -1610,6 +1646,11 @@ void SensorBH1750::onReceive(const MyMessage & message) {
// what to do when receiving a remote message
void SensorBH1750::onProcess(Request & request) {
}
// what to do when receiving an interrupt
void SensorBH1750::onInterrupt() {
}
#endif
/*
......@@ -1658,6 +1699,10 @@ void SensorMLX90614::onReceive(const MyMessage & message) {
// what to do when receiving a remote message
void SensorMLX90614::onProcess(Request & request) {
}
// what to do when receiving an interrupt
void SensorMLX90614::onInterrupt() {
}
#endif
......@@ -1728,6 +1773,10 @@ void SensorBosch::onProcess(Request & request) {
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorBosch::onInterrupt() {
}
// calculate and send the forecast back
void SensorBosch::_forecast(float pressure) {
if (isnan(pressure)) return;
......@@ -2010,6 +2059,10 @@ void SensorHCSR04::onProcess(Request & request) {
}
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorHCSR04::onInterrupt() {
}
#endif
/*
......@@ -2055,8 +2108,6 @@ void SensorSonoff::onSetup() {
// what to do during loop
void SensorSonoff::onLoop() {
// set the value to -1 so to avoid reporting to the gateway during loop
_value_int = -1;
_debouncer.update();
// Get the update value from the button
int value = _debouncer.read();
......@@ -2094,6 +2145,10 @@ void SensorSonoff::onProcess(Request & request) {
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorSonoff::onInterrupt() {
}
// toggle the state
void SensorSonoff::_toggle() {
// toggle the state
......@@ -2167,6 +2222,10 @@ void SensorMCP9808::onReceive(const MyMessage & message) {
// what to do when receiving a remote message
void SensorMCP9808::onProcess(Request & request) {
}
// what to do when receiving an interrupt
void SensorMCP9808::onInterrupt() {
}
#endif
......@@ -2285,6 +2344,10 @@ void SensorMQ::onProcess(Request & request) {
_send(_msg_service.set(function));
}
// what to do when receiving an interrupt
void SensorMQ::onInterrupt() {
}
// returns the calculated sensor resistance
float SensorMQ::_MQResistanceCalculation(int raw_adc) {
return ( ((float)_rl_value*(1023-raw_adc)/raw_adc));
......@@ -2449,6 +2512,11 @@ NodeManager::NodeManager() {
_msg = MyMessage(CONFIGURATION_CHILD_ID, V_CUSTOM);
}
int NodeManager::_last_interrupt_pin = -1;
long NodeManager::_last_interrupt_1 = millis();
long NodeManager::_last_interrupt_2 = millis();
long NodeManager::_interrupt_min_delta = 100;
// setter/getter
void NodeManager::setRetries(int value) {
_retries = value;
......@@ -2511,16 +2579,19 @@ void NodeManager::setSleep(int value1, int value2, int value3) {
void NodeManager::setSleepInterruptPin(int value) {
_sleep_interrupt_pin = value;
}
void NodeManager::setInterrupt(int pin, int mode, int pull) {
void NodeManager::setInterrupt(int pin, int mode, int initial) {
if (pin == INTERRUPT_PIN_1) {
_interrupt_1_mode = mode;
_interrupt_1_pull = pull;
_interrupt_1_initial = initial;
}
if (pin == INTERRUPT_PIN_2) {
if (pin == INTERRUPT_PIN_2) {
_interrupt_2_mode = mode;
_interrupt_2_pull = pull;
_interrupt_2_initial = initial;
}
}
void NodeManager::setInterruptMinDelta(long value) {
_interrupt_min_delta = value;
}
#if POWER_MANAGER == 1
void NodeManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) {
_powerManager.setPowerPins(ground_pin, vcc_pin, wait_time);
......@@ -2823,26 +2894,6 @@ void NodeManager::before() {
Serial.print(F(" B="));
Serial.println(MY_CAP_RXBUF);
#endif
// setup the sleep interrupt pin
if (_sleep_interrupt_pin > -1) {
// set the interrupt when the pin is connected to ground
setInterrupt(_sleep_interrupt_pin,FALLING,HIGH);
}
// setup the interrupt pins
if (_interrupt_1_mode != MODE_NOT_DEFINED) {
pinMode(INTERRUPT_PIN_1,INPUT);
if (_interrupt_1_pull > -1) digitalWrite(INTERRUPT_PIN_1,_interrupt_1_pull);
}
if (_interrupt_2_mode != MODE_NOT_DEFINED) {
pinMode(INTERRUPT_PIN_2, INPUT);
if (_interrupt_2_pull > -1) digitalWrite(INTERRUPT_PIN_2,_interrupt_2_pull);
}
#if DEBUG == 1
Serial.print(F("INT1 M="));
Serial.println(_interrupt_1_mode);
Serial.print(F("INT2 M="));
Serial.println(_interrupt_2_mode);
#endif
#if PERSIST == 1
// restore the configuration saved in the eeprom
_loadConfig();
......@@ -2860,6 +2911,8 @@ void NodeManager::before() {
// call each sensor's setup()
_sensors[i]->before();
}
// setup the interrupt pins
setupInterrupts();
}
// present NodeManager and its sensors
......@@ -2919,13 +2972,11 @@ void NodeManager::loop() {
// if in idle mode, do nothing
if (_sleep_mode == IDLE) return;
// if sleep time is not set, do nothing
if ((_sleep_mode == SLEEP || _sleep_mode == WAIT) && _sleep_time == 0) return;
if (isSleepingNode() && _sleep_time == 0) return;
#if BATTERY_MANAGER == 1
// update the timer for battery report
if (_battery_report_timer.getUnit() == MINUTES) _battery_report_timer.update();
if (_battery_report_timer.getUnit() == CYCLES && (_last_interrupt_pin == -1 || _battery_report_with_interrupt)) _battery_report_timer.update();
// keep track of the number of sleeping cycles (ignoring if )
if (_last_interrupt_pin == -1 || _battery_report_with_interrupt)
// if it is time to report the battery level
if (_battery_report_timer.isOver()) {
// time to report the battery level again
......@@ -2942,17 +2993,19 @@ void NodeManager::loop() {
for (int i = 1; i <= MAX_SENSORS; i++) {
// skip not configured sensors
if (_sensors[i] == 0) continue;
// if waking up from an interrupt skip all the sensor without that interrupt configured
if (_last_interrupt_pin != -1 && _sensors[i]->getInterruptPin() != _last_interrupt_pin) continue;
// call each sensor's loop()
// if there was an interrupt for this sensor, call the sensor's interrupt()
if (_last_interrupt_pin != -1 && _sensors[i]->getInterruptPin() == _last_interrupt_pin) _sensors[i]->interrupt();
// call the sensor's loop()
_sensors[i]->loop(empty);
}
// reset the last interrupt pin
_last_interrupt_pin = -1;
#if POWER_MANAGER == 1
// turn off the pin powering all the sensors
if (_auto_power_pins) powerOff();
#endif
// continue/start sleeping as requested
if (_sleep_mode == SLEEP || _sleep_mode == WAIT) _sleep();
if (isSleepingNode()) _sleep();
}
// dispacth inbound messages
......@@ -3071,6 +3124,7 @@ void NodeManager::process(Request & request) {
#endif
case 26: unRegisterSensor(request.getValueInt()); break;
case 27: saveToMemory(0,request.getValueInt()); break;
case 28: setInterruptMinDelta(request.getValueInt()); break;
default: return;
}
_send(_msg.set(function));
......@@ -3178,6 +3232,67 @@ float NodeManager::getVcc() {
#endif
}
// setup the interrupt pins
void NodeManager::setupInterrupts() {
// configure wakeup pin if needed
if (_sleep_interrupt_pin > -1) {
// set the interrupt when the pin is connected to ground
setInterrupt(_sleep_interrupt_pin,FALLING,HIGH);
}
// setup the interrupt pins
if (_interrupt_1_mode != MODE_NOT_DEFINED) {
pinMode(INTERRUPT_PIN_1,INPUT);
if (_interrupt_1_initial > -1) digitalWrite(INTERRUPT_PIN_1,_interrupt_1_initial);
// for non sleeping nodes, we need to handle the interrupt by ourselves
if (_sleep_mode != SLEEP) attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_1), _onInterrupt_1, _interrupt_1_mode);
}
if (_interrupt_2_mode != MODE_NOT_DEFINED) {
pinMode(INTERRUPT_PIN_2, INPUT);
if (_interrupt_2_initial > -1) digitalWrite(INTERRUPT_PIN_2,_interrupt_2_initial);
// for non sleeping nodes, we need to handle the interrupt by ourselves
if (_sleep_mode != SLEEP) attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_2), _onInterrupt_2, _interrupt_2_mode);
}
#if DEBUG == 1
Serial.print(F("INT P="));
Serial.print(INTERRUPT_PIN_1);
Serial.print(F(" M="));
Serial.println(_interrupt_1_mode);
Serial.print(F("INT P="));
Serial.print(INTERRUPT_PIN_2);
Serial.print(F(" M="));
Serial.println(_interrupt_2_mode);
#endif
}
// return the pin from which the last interrupt came
int NodeManager::getLastInterruptPin() {
return _last_interrupt_pin;
}
// handle an interrupt
void NodeManager::_onInterrupt_1() {
long now = millis();
if ( (now - _last_interrupt_1 > _interrupt_min_delta) || (now < _last_interrupt_1) ) {
_last_interrupt_pin = INTERRUPT_PIN_1;
#if DEBUG == 1
Serial.print(F("INT P="));
Serial.println(INTERRUPT_PIN_1);
#endif
_last_interrupt_1 = now;
}
}
void NodeManager::_onInterrupt_2() {
long now = millis();
if ( (now - _last_interrupt_2 > _interrupt_min_delta) || (now < _last_interrupt_2) ) {
_last_interrupt_pin = INTERRUPT_PIN_2;
#if DEBUG == 1
Serial.print(F("INT P="));
Serial.println(INTERRUPT_PIN_2);
#endif
_last_interrupt_2 = now;
}
}
// send a message to the network
void NodeManager::_send(MyMessage & message) {
// send the message, multiple times if requested
......@@ -3206,8 +3321,6 @@ void NodeManager::_send(MyMessage & message) {
// wrapper of smart sleep
void NodeManager::_sleep() {
// reset the last interrupt pin
_last_interrupt_pin = -1;
// calculate the seconds to sleep
long sleep_sec = _sleep_time;
if (_sleep_unit == MINUTES) sleep_sec = sleep_sec * 60;
......
......@@ -474,11 +474,14 @@ class Sensor {
void process(Request & request);
// return the pin the interrupt is attached to
int getInterruptPin();
// listen for interrupts on the given pin so interrupt() will be called when occurring
void setInterrupt(int pin, int mode, int initial);
// define what to do at each stage of the sketch
virtual void before();
virtual void presentation();
virtual void setup();
virtual void loop(const MyMessage & message);
virtual void interrupt();
virtual void receive(const MyMessage & message);
// abstract functions, subclasses need to implement
virtual void onBefore() = 0;
......@@ -486,6 +489,7 @@ class Sensor {
virtual void onLoop() = 0;
virtual void onReceive(const MyMessage & message) = 0;
virtual void onProcess(Request & request) = 0;
virtual void onInterrupt() = 0;
protected:
MyMessage _msg;
MyMessage _msg_service;
......@@ -541,6 +545,7 @@ class SensorAnalogInput: public Sensor {
void onLoop();
void onReceive(const MyMessage & message);
void onProcess(Request & request);
void onInterrupt();
protected:
int _reference = -1;
bool _reverse = false;
......@@ -581,6 +586,7 @@ class SensorThermistor: public Sensor {
void onLoop();
void onReceive(const MyMessage & message);
void onProcess(Request & request);
void onInterrupt();
protected:
long _nominal_resistor