From 01bc9d8a1fc890f27483f6b9ad425321318c6cd9 Mon Sep 17 00:00:00 2001 From: user2684 <user2684@users.noreply.github.com> Date: Sun, 9 Jul 2017 19:26:39 +0200 Subject: [PATCH] New interrupt handling framework which covers both sleeping and NOT sleeping nodes (#142, #149, #166, #154) --- NodeManager.cpp | 317 ++++++++++++++++++++++++++++++++---------------- NodeManager.h | 54 +++++++-- NodeManager.ino | 2 + README.md | 22 +++- 4 files changed, 279 insertions(+), 116 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 1e32a47..21d6451 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -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; diff --git a/NodeManager.h b/NodeManager.h index ab3f841..cc96a56 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -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 = 10000; int _nominal_temperature = 25; @@ -602,6 +608,7 @@ class SensorML8511: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: float _mapfloat(float x, float in_min, float in_max, float out_min, float out_max); }; @@ -623,6 +630,7 @@ class SensorACS712: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: int _ACS_offset = 2500; int _mv_per_amp = 185; @@ -635,24 +643,21 @@ class SensorACS712: public Sensor { class SensorRainGauge: public Sensor { public: SensorRainGauge(NodeManager* node_manager, int child_id, int pin); - // [101] set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60) - void setReportInterval(int value); // [102] set how many mm of rain to count for each tip (default: 0.11) void setSingleTip(float value); + // set initial value - internal pull up (default: HIGH) + void setInitialValue(int value); // define what to do at each stage of the sketch void onBefore(); void onSetup(); void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); - public: - static void _onTipped(); - static long _last_tip; - static long _count; + void onInterrupt(); protected: - int _report_interval = 60; + long _count = 0; float _single_tip = 0.11; - Timer* _timer; + int _initial_value = HIGH; }; /* @@ -686,6 +691,7 @@ class SensorDigitalInput: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); }; #endif @@ -716,6 +722,7 @@ class SensorDigitalOutput: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: int _on_value = HIGH; int _status = OFF; @@ -773,6 +780,7 @@ class SensorDHT: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -797,6 +805,7 @@ class SensorSHT21: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -836,6 +845,7 @@ class SensorSwitch: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: int _debounce = 0; int _trigger_time = 0; @@ -880,6 +890,7 @@ class SensorDs18b20: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: float _offset = 0; int _index; @@ -902,6 +913,7 @@ class SensorBH1750: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: BH1750* _lightSensor; }; @@ -920,6 +932,7 @@ class SensorMLX90614: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); // constants const static int TEMPERATURE_AMBIENT = 0; const static int TEMPERATURE_OBJECT = 1; @@ -946,6 +959,7 @@ class SensorBosch: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -1012,6 +1026,7 @@ class SensorHCSR04: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: int _trigger_pin; int _echo_pin; @@ -1039,6 +1054,7 @@ class SensorSonoff: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: Bounce _debouncer = Bounce(); int _button_pin = 0; @@ -1068,6 +1084,7 @@ class SensorMCP9808: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: Adafruit_MCP9808* _mcp; }; @@ -1108,6 +1125,7 @@ class SensorMQ: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: float _rl_value = 1.0; float _ro_clean_air_factor = 9.83; @@ -1202,7 +1220,9 @@ class NodeManager { // [19] if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true) void setSleepInterruptPin(int value); // configure the interrupt pin and mode. Mode can be CHANGE, RISING, FALLING (default: MODE_NOT_DEFINED) - void setInterrupt(int pin, int mode, int pull = -1); + void setInterrupt(int pin, int mode, int initial = -1); + // [28] ignore two consecutive interrupts if happening within this timeframe in milliseconds (default: 100) + void setInterruptMinDelta(long value); // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); int getSleepBetweenSend(); @@ -1259,6 +1279,10 @@ class NodeManager { void saveToMemory(int index, int value); // return vcc in V float getVcc(); + // setup the configured interrupt pins + void setupInterrupts(); + // return the pin from which the last interrupt came + int getLastInterruptPin(); // hook into the main sketch functions void before(); void presentation(); @@ -1266,6 +1290,9 @@ class NodeManager { void loop(); void receive(const MyMessage & msg); void receiveTime(unsigned long ts); + // handle interrupts + static void _onInterrupt_1(); + static void _onInterrupt_2(); private: #if BATTERY_MANAGER == 1 float _battery_min = 2.6; @@ -1291,9 +1318,12 @@ class NodeManager { int _retries = 1; int _interrupt_1_mode = MODE_NOT_DEFINED; int _interrupt_2_mode = MODE_NOT_DEFINED; - int _interrupt_1_pull = -1; - int _interrupt_2_pull = -1; - int _last_interrupt_pin = -1; + int _interrupt_1_initial = -1; + int _interrupt_2_initial = -1; + static int _last_interrupt_pin; + static long _interrupt_min_delta; + static long _last_interrupt_1; + static long _last_interrupt_2; long _timestamp = -1; Sensor* _sensors[MAX_SENSORS+1] = {0}; bool _ack = false; diff --git a/NodeManager.ino b/NodeManager.ino index c864792..32e2fa7 100644 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -35,6 +35,8 @@ void before() { */ + + /* * Register above your sensors */ diff --git a/README.md b/README.md index 241491c..fe8c0eb 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,10 @@ Node Manager comes with a reasonable default configuration. If you want/need to void saveToMemory(int index, int value); // return vcc in V float getVcc(); + // setup the configured interrupt pins + void setupInterrupts(); + // return the pin from which the last interrupt came + int getLastInterruptPin(); ~~~ For example @@ -361,6 +365,8 @@ If you want to create a custom sensor and register it with NodeManager so it can void onReceive(const MyMessage & message); // define what to do when receiving a remote configuration message void onProcess(Request & request); + // define what to do when receiving an interrupt + void onInterrupt(); ~~~ You can then instantiate your newly created class and register with NodeManager: @@ -434,6 +440,8 @@ The following methods are available for all the sensors: 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); ~~~ #### Sensor's specific configuration @@ -504,10 +512,10 @@ Each sensor class can expose additional methods. * SensorRainGauge ~~~c - // [101] set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60) - void setReportInterval(int value); // [102] set how many mm of rain to count for each tip (default: 0.11) void setSingleTip(float value); + // set initial value - internal pull up (default: HIGH) + void setInitialValue(int value); ~~~ * SensorDigitalOutput / SensorRelay @@ -701,6 +709,16 @@ NodeManager::receive(): Sensor::receive(): * Invoke `Sensor::loop()` which will execute the sensor main taks and eventually call `Sensor::onReceive()` +NodeManager::process(): +* Process an incoming remote configuration request + +Sensor::process(): +* Process a sensor-generic incoming remote configuration request +* Calls onProcess() for sensor-specific incoming remote configuration request + +Sensor::interrupt(): +* Calls the sensor's implementation of onInterrupt() to handle the interrupt + ## Examples All the examples below takes place within the before() function in the main sketch, just below the "Register below your sensors" comment. -- GitLab