Commit 06032fd2 authored by user2684's avatar user2684 Committed by GitHub
Browse files

Added Timer class and fixed #127 #131 #107 #105

* Added functions to get sleep mode from NM

* Implemented Timer class

* Commented the Timer class

* Improved Timer class

* Replaced battery report with Timer

* Using Timer in SensorRainGauge class

* Add custom report time to Sensor

* Used Timer for force update

* Added safeguard to SensorDigitalOutput

* Added capability to turn on with a timer for SensorDigitalOutput

* Tested all the new functions and fixed the issues

* Stopping the timer if setInputIsElapsed is on and a LOW value is set
parent 97585d55
...@@ -87,6 +87,113 @@ void PowerManager::powerOff() { ...@@ -87,6 +87,113 @@ void PowerManager::powerOff() {
digitalWrite(_vcc_pin, LOW); digitalWrite(_vcc_pin, LOW);
} }
/******************************************
Timer
*/
Timer::Timer(NodeManager* node_manager) {
_node_manager = node_manager;
}
// start the timer
void Timer::start(long target, int unit) {
set(target,unit);
start();
}
void Timer::start() {
if (_is_configured) _is_running = true;
}
// stop the timer
void Timer::stop() {
_is_running = false;
}
// setup the timer
void Timer::set(long target, int unit) {
// reset the timer
_elapsed = 0;
_use_millis = false;
_last_millis = 0;
_sleep_time = 0;
// save the settings
_target = target;
_unit = unit;
if (_unit == MINUTES) {
if (_node_manager->isSleepingNode()) {
// this is a sleeping node and millis() is not reliable so calculate how long a sleep/wait cycle would last
int sleep_unit = _node_manager->getSleepUnit();
_sleep_time = (float)_node_manager->getSleepTime();
if (sleep_unit == SECONDS) _sleep_time = _sleep_time/60;
else if (sleep_unit == HOURS) _sleep_time = _sleep_time*60;
else if (sleep_unit == DAYS) _sleep_time = _sleep_time*1440;
}
else {
// this is not a sleeping node, use millis() to keep track of the elapsed time
_use_millis = true;
}
}
_is_configured = true;
}
// update the timer at every cycle
void Timer::update() {
if (! isRunning()) return;
if (_unit == CYCLES) {
// if not a sleeping node, counting the cycles do not make sense
if (! _node_manager->isSleepingNode()) return;
// just increase the cycle counter
_elapsed++;
}
else if (_unit == MINUTES) {
// if using millis(), calculate the elapsed minutes, otherwise add a sleep interval
if (_use_millis) {
_elapsed = (float)(millis() - _last_millis)/1000/60;
}
else {
_elapsed += _sleep_time;
}
}
}
// return true if the time is over
bool Timer::isOver() {
if (! isRunning()) return false;
// time has elapsed
if (_elapsed >= _target) return true;
// millis has started over
if (_elapsed < 0 ) return true;
return false;
}
// return true if the timer is running
bool Timer::isRunning() {
return _is_running;
}
// return true if the time is configured
bool Timer::isConfigured() {
return _is_configured;
}
// restart the timer
void Timer::restart() {
if (! isRunning()) return;
// reset elapsed
_elapsed = 0;
// if using millis, keep track of the now timestamp
if (_use_millis) _last_millis = millis();
}
// return elapsed minutes so far
float Timer::getElapsed() {
return _elapsed;
}
// return the configured unit
int Timer::getUnit() {
return _unit;
}
/****************************************** /******************************************
Sensors Sensors
...@@ -101,6 +208,8 @@ Sensor::Sensor(NodeManager* node_manager, int child_id, int pin) { ...@@ -101,6 +208,8 @@ Sensor::Sensor(NodeManager* node_manager, int child_id, int pin) {
_child_id = child_id; _child_id = child_id;
_pin = pin; _pin = pin;
_msg = MyMessage(_child_id, _type); _msg = MyMessage(_child_id, _type);
_report_timer = new Timer(_node_manager);
_force_update_timer = new Timer(_node_manager);
} }
// setter/getter // setter/getter
...@@ -144,11 +253,17 @@ void Sensor::setSamples(int value) { ...@@ -144,11 +253,17 @@ void Sensor::setSamples(int value) {
void Sensor::setSamplesInterval(int value) { void Sensor::setSamplesInterval(int value) {
_samples_interval = value; _samples_interval = value;
} }
void Sensor::setTackLastValue(bool value) { void Sensor::setTrackLastValue(bool value) {
_track_last_value = value; _track_last_value = value;
} }
void Sensor::setForceUpdate(int value) { void Sensor::setForceUpdate(int value) {
_force_update = value; setForceUpdateCycles(value);
}
void Sensor::setForceUpdateCycles(int value) {
_force_update_timer->start(value,CYCLES);
}
void Sensor::setForceUpdateMinutes(int value) {
_force_update_timer->start(value,MINUTES);
} }
void Sensor::setValueType(int value) { void Sensor::setValueType(int value) {
_value_type = value; _value_type = value;
...@@ -192,6 +307,16 @@ char* Sensor::getValueString() { ...@@ -192,6 +307,16 @@ char* Sensor::getValueString() {
return _last_value_string; return _last_value_string;
} }
// After how many cycles the sensor will report back its measure (default: 1 cycle)
void Sensor::setReportIntervalCycles(int value) {
_report_timer->start(value,CYCLES);
}
// After how many minutes the sensor will report back its measure (default: 1 cycle)
void Sensor::setReportIntervalMinutes(int value) {
_report_timer->start(value,MINUTES);
}
// present the sensor to the gateway and controller // present the sensor to the gateway and controller
void Sensor::presentation() { void Sensor::presentation() {
#if DEBUG == 1 #if DEBUG == 1
...@@ -218,6 +343,16 @@ void Sensor::setup() { ...@@ -218,6 +343,16 @@ void Sensor::setup() {
// call the sensor-specific implementation of loop // call the sensor-specific implementation of loop
void Sensor::loop(const MyMessage & message) { void Sensor::loop(const MyMessage & message) {
if (_pin == -1) return; if (_pin == -1) return;
// update the timers if within a loop cycle
if (! _isReceive(message)) {
if (_report_timer->isRunning()) {
// update the timer
_report_timer->update();
// if it is not the time yet to report a new measure, just return
if (! _report_timer->isOver()) return;
}
if (_force_update_timer->isRunning()) _force_update_timer->update();
}
#if POWER_MANAGER == 1 #if POWER_MANAGER == 1
// turn the sensor on // turn the sensor on
if (_auto_power_pins) powerOn(); if (_auto_power_pins) powerOn();
...@@ -225,31 +360,29 @@ void Sensor::loop(const MyMessage & message) { ...@@ -225,31 +360,29 @@ void Sensor::loop(const MyMessage & message) {
// for numeric sensor requiring multiple samples, keep track of the total // for numeric sensor requiring multiple samples, keep track of the total
float total = 0; float total = 0;
// keep track of the number of cycles since the last update // keep track of the number of cycles since the last update
if (_force_update > 0) _cycles++;
// collect multiple samples if needed // collect multiple samples if needed
for (int i = 0; i < _samples; i++) { 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 // call the sensor-specific implementation of the main task which will store the result in the _value variable
if (message.sender == 0 && message.sensor == 0 && message.getCommand() == 0 && message.type == 0) { if (_isReceive(message)) {
// empty message, we'be been called from loop()
onLoop();
}
else {
// we've been called from receive(), pass the message along // we've been called from receive(), pass the message along
onReceive(message); onReceive(message);
} }
else {
// we'be been called from loop()
onLoop();
}
// for integers and floats, keep track of the total // for integers and floats, keep track of the total
if (_value_type == TYPE_INTEGER) total += (float)_value_int; if (_value_type == TYPE_INTEGER) total += (float)_value_int;
else if (_value_type == TYPE_FLOAT) total += _value_float; else if (_value_type == TYPE_FLOAT) total += _value_float;
// wait between samples // wait between samples
if (_samples_interval > 0) wait(_samples_interval); if (_samples_interval > 0) wait(_samples_interval);
} }
// process the result and send a response back. // process the result and send a response back
if (_value_type == TYPE_INTEGER && total > -1) { if (_value_type == TYPE_INTEGER && total > -1) {
// if the value is an integer, calculate the average value of the samples // if the value is an integer, calculate the average value of the samples
int avg = (int) (total / _samples); int avg = (int) (total / _samples);
// if track last value is disabled or if enabled and the current value is different then the old value, send it back // if track last value is disabled or if enabled and the current value is different then the old value, send it back
if (! _track_last_value || (_track_last_value && avg != _last_value_int) || (_track_last_value && _force_update > 0 && _cycles > _force_update)) { if (_isReceive(message) || _isWorthSending(avg != _last_value_int)) {
_cycles = 0;
_last_value_int = avg; _last_value_int = avg;
_send(_msg.set(avg)); _send(_msg.set(avg));
} }
...@@ -258,9 +391,8 @@ void Sensor::loop(const MyMessage & message) { ...@@ -258,9 +391,8 @@ void Sensor::loop(const MyMessage & message) {
else if (_value_type == TYPE_FLOAT && total > -1) { else if (_value_type == TYPE_FLOAT && total > -1) {
// calculate the average value of the samples // calculate the average value of the samples
float avg = total / _samples; float avg = total / _samples;
// if track last value is disabled or if enabled and the current value is different then the old value, send it back // report the value back
if (! _track_last_value || (_track_last_value && avg != _last_value_float) || (_track_last_value && _cycles >= _force_update)) { if (_isReceive(message) || _isWorthSending(avg != _last_value_float)) {
_cycles = 0;
_last_value_float = avg; _last_value_float = avg;
_send(_msg.set(avg, _float_precision)); _send(_msg.set(avg, _float_precision));
} }
...@@ -268,8 +400,7 @@ void Sensor::loop(const MyMessage & message) { ...@@ -268,8 +400,7 @@ void Sensor::loop(const MyMessage & message) {
// process a string value // process a string value
else if (_value_type == TYPE_STRING) { else if (_value_type == TYPE_STRING) {
// if track last value is disabled or if enabled and the current value is different then the old value, send it back // if track last value is disabled or if enabled and the current value is different then the old value, send it back
if (! _track_last_value || (_track_last_value && strcmp(_value_string, _last_value_string) != 0) || (_track_last_value && _cycles >= _force_update)) { if (_isReceive(message) || _isWorthSending(strcmp(_value_string, _last_value_string) != 0)) {
_cycles = 0;
_last_value_string = _value_string; _last_value_string = _value_string;
_send(_msg.set(_value_string)); _send(_msg.set(_value_string));
} }
...@@ -278,6 +409,8 @@ void Sensor::loop(const MyMessage & message) { ...@@ -278,6 +409,8 @@ void Sensor::loop(const MyMessage & message) {
#if POWER_MANAGER == 1 #if POWER_MANAGER == 1
if (_auto_power_pins) powerOff(); if (_auto_power_pins) powerOff();
#endif #endif
// restart the report timer if over
if (! _isReceive(message) && _report_timer->isRunning() && _report_timer->isOver()) _report_timer->restart();
} }
// receive a message from the radio network // receive a message from the radio network
...@@ -314,6 +447,28 @@ void Sensor::_send(MyMessage & message) { ...@@ -314,6 +447,28 @@ void Sensor::_send(MyMessage & message) {
} }
} }
// return true if the message is coming from the radio network
bool Sensor::_isReceive(const MyMessage & message) {
if (message.sender == 0 && message.sensor == 0 && message.getCommand() == 0 && message.type == 0) return false;
return true;
}
// determine if a value is worth sending back to the controller
bool Sensor::_isWorthSending(bool comparison) {
// track last value is disabled
if (! _track_last_value) return true;
// track value is enabled and the current value is different then the old value
if (_track_last_value && comparison) return true;
// track value is enabled and the timer is over
if (_track_last_value && _force_update_timer->isRunning() && _force_update_timer->isOver()) {
// restart the timer
_force_update_timer->restart();
return true;
}
return false;
}
/* /*
SensorAnalogInput SensorAnalogInput
*/ */
...@@ -598,7 +753,8 @@ SensorRainGauge::SensorRainGauge(NodeManager* node_manager, int child_id, int pi ...@@ -598,7 +753,8 @@ SensorRainGauge::SensorRainGauge(NodeManager* node_manager, int child_id, int pi
setPresentation(S_RAIN); setPresentation(S_RAIN);
setType(V_RAIN); setType(V_RAIN);
setValueType(TYPE_FLOAT); setValueType(TYPE_FLOAT);
// create the timer
_timer = new Timer(node_manager);
} }
// initialize static variables // initialize static variables
...@@ -619,6 +775,8 @@ void SensorRainGauge::onBefore() { ...@@ -619,6 +775,8 @@ void SensorRainGauge::onBefore() {
pinMode(_pin, INPUT_PULLUP); pinMode(_pin, INPUT_PULLUP);
// attach to the pin's interrupt and execute the routine on falling // attach to the pin's interrupt and execute the routine on falling
attachInterrupt(digitalPinToInterrupt(_pin), _onTipped, FALLING); attachInterrupt(digitalPinToInterrupt(_pin), _onTipped, FALLING);
// start the timer
_timer->start(_report_interval,MINUTES);
} }
// what to do during setup // what to do during setup
...@@ -643,24 +801,19 @@ void SensorRainGauge::_onTipped() { ...@@ -643,24 +801,19 @@ void SensorRainGauge::_onTipped() {
void SensorRainGauge::onLoop() { void SensorRainGauge::onLoop() {
// avoid reporting the same value multiple times // avoid reporting the same value multiple times
_value_float = -1; _value_float = -1;
long now = millis(); _timer->update();
// time elapsed since the last report // time to report
long elapsed = now - _last_report; if (_timer->isOver()) {
// minimum time interval between reports
long min_interval = ((long)_report_interval*1000)*60;
// time to report or millis() reset
if ( (elapsed > min_interval) || (now < _last_report)) {
// report the total amount of rain for the last period // report the total amount of rain for the last period
_value_float = _count*_single_tip; _value_float = _count * _single_tip;
#if DEBUG == 1 #if DEBUG == 1
Serial.print(F("RAIN I=")); Serial.print(F("RAIN I="));
Serial.print(_child_id); Serial.print(_child_id);
Serial.print(F(" T=")); Serial.print(F(" T="));
Serial.println(_value_float); Serial.println(_value_float);
#endif #endif
// reset the counters // reset the timer
_count = 0; _timer->restart();
_last_report = now;
} }
} }
...@@ -668,7 +821,7 @@ void SensorRainGauge::onLoop() { ...@@ -668,7 +821,7 @@ void SensorRainGauge::onLoop() {
void SensorRainGauge::onReceive(const MyMessage & message) { void SensorRainGauge::onReceive(const MyMessage & message) {
if (message.getCommand() == C_REQ) { if (message.getCommand() == C_REQ) {
// report the total amount of rain for the last period // report the total amount of rain for the last period
_value_float = _count*_single_tip; _value_float = _count * _single_tip;
} }
} }
...@@ -893,6 +1046,7 @@ void SensorDigitalInput::onReceive(const MyMessage & message) { ...@@ -893,6 +1046,7 @@ void SensorDigitalInput::onReceive(const MyMessage & message) {
// contructor // contructor
SensorDigitalOutput::SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) { SensorDigitalOutput::SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) {
_safeguard_timer = new Timer(node_manager);
} }
// what to do during before // what to do during before
...@@ -903,6 +1057,7 @@ void SensorDigitalOutput::onBefore() { ...@@ -903,6 +1057,7 @@ void SensorDigitalOutput::onBefore() {
digitalWrite(_pin, _state); digitalWrite(_pin, _state);
// the initial value is now the current value // the initial value is now the current value
_value_int = _initial_value; _value_int = _initial_value;
// create the safeguard timer
} }
// what to do during setup // what to do during setup
...@@ -922,19 +1077,59 @@ void SensorDigitalOutput::setOnValue(int value) { ...@@ -922,19 +1077,59 @@ void SensorDigitalOutput::setOnValue(int value) {
void SensorDigitalOutput::setLegacyMode(bool value) { void SensorDigitalOutput::setLegacyMode(bool value) {
_legacy_mode = value; _legacy_mode = value;
} }
void SensorDigitalOutput::setSafeguard(int value) {
_safeguard_timer->set(value,MINUTES);
}
int SensorDigitalOutput::getState() {
return _state;
}
void SensorDigitalOutput::setInputIsElapsed(bool value) {
_input_is_elapsed = value;
}
// main task // main task
void SensorDigitalOutput::onLoop() { void SensorDigitalOutput::onLoop() {
// do nothing on loop // set the value to -1 so to avoid reporting to the gateway during loop
_value_int = -1;
_last_value_int = -1;
// if a safeguard is set, check if it is time for it
if (_safeguard_timer->isRunning()) {
// update the timer
_safeguard_timer->update();
// if the time is over, turn the output off
if (_safeguard_timer->isOver()) set(LOW);
}
} }
// what to do as the main task when receiving a message // what to do as the main task when receiving a message
void SensorDigitalOutput::onReceive(const MyMessage & message) { void SensorDigitalOutput::onReceive(const MyMessage & message) {
// by default handle a SET message but when legacy mode is set when a REQ message is expected instead // by default handle a SET message but when legacy mode is set when a REQ message is expected instead
if ( (message.getCommand() == C_SET && ! _legacy_mode) || (message.getCommand() == C_REQ && _legacy_mode)) { if ( (message.getCommand() == C_SET && ! _legacy_mode) || (message.getCommand() == C_REQ && _legacy_mode)) {
// retrieve from the message the value to set // switch the output
int value = message.getInt(); set(message.getInt());
if (value != 0 && value != 1) return; }
if (message.getCommand() == C_REQ && ! _legacy_mode) {
// return the current status
_value_int = _state;
}
}
// write the value to the output
void SensorDigitalOutput::set(int value) {
if (_input_is_elapsed) {
if (value == LOW) {
// stop the timer
_safeguard_timer->stop();
} else {
// configure and start the timer
_safeguard_timer->start(value,MINUTES);
// if the input is an elapsed time, unless the value is LOW, the output will be always on
value = HIGH;
}
} else {
// if turning the output on and a safeguard timer is configured, start it
if (value == HIGH && _safeguard_timer->isConfigured() && ! _safeguard_timer->isRunning()) _safeguard_timer->start();
}
#if DEBUG == 1 #if DEBUG == 1
Serial.print(F("DOUT I=")); Serial.print(F("DOUT I="));
Serial.print(_child_id); Serial.print(_child_id);
...@@ -961,13 +1156,9 @@ void SensorDigitalOutput::onReceive(const MyMessage & message) { ...@@ -961,13 +1156,9 @@ void SensorDigitalOutput::onReceive(const MyMessage & message) {
// store the current value so it will be sent to the controller // store the current value so it will be sent to the controller
_state = value; _state = value;
_value_int = value; _value_int = value;
}
if (message.getCommand() == C_REQ && ! _legacy_mode) {
// return the current status
_value_int = _state;
}
} }
/* /*
SensorRelay SensorRelay
*/ */
...@@ -978,13 +1169,14 @@ SensorRelay::SensorRelay(NodeManager* node_manager, int child_id, int pin): Sens ...@@ -978,13 +1169,14 @@ SensorRelay::SensorRelay(NodeManager* node_manager, int child_id, int pin): Sens
setPresentation(S_BINARY); setPresentation(S_BINARY);
setType(V_STATUS); setType(V_STATUS);
} }
/*
// define what to do during loop // define what to do during loop
void SensorRelay::onLoop() { void SensorRelay::onLoop() {
// set the value to -1 so to avoid reporting to the gateway during loop // set the value to -1 so to avoid reporting to the gateway during loop
_value_int = -1; _value_int = -1;
_last_value_int = -1;
} }
*/
/* /*
SensorLatchingRelay SensorLatchingRelay
*/ */
...@@ -1884,7 +2076,10 @@ void NodeManager::setRetries(int value) { ...@@ -1884,7 +2076,10 @@ void NodeManager::setRetries(int value) {
_battery_max = value; _battery_max = value;
} }
void NodeManager::setBatteryReportCycles(int value) { void NodeManager::setBatteryReportCycles(int value) {
_battery_report_cycles = value; _battery_report_timer.set(value,CYCLES);
}
void NodeManager::setBatteryReportMinutes(int value) {
_battery_report_timer.set(value,MINUTES);
} }
void NodeManager::setBatteryInternalVcc(bool value) { void NodeManager::setBatteryInternalVcc(bool value) {
_battery_internal_vcc = value; _battery_internal_vcc = value;
...@@ -1905,12 +2100,21 @@ void NodeManager::setSleepMode(int value) { ...@@ -1905,12 +2100,21 @@ void NodeManager::setSleepMode(int value) {
void NodeManager::setMode(int value) { void NodeManager::setMode(int value) {
setSleepMode(value); setSleepMode(value);
} }
int NodeManager::getMode() {
return _sleep_mode;
}
void NodeManager::setSleepTime(int value) { void NodeManager::setSleepTime(int value) {
_sleep_time = value; _sleep_time = value;
} }
int NodeManager::getSleepTime() {
return _sleep_time;
}
void NodeManager::setSleepUnit(int value) { void NodeManager::setSleepUnit(int value) {
_sleep_unit = value; _sleep_unit = value;
} }
int NodeManager::getSleepUnit() {
return _sleep_unit;
}
void NodeManager::setSleep(int value1, int value2, int value3) { void NodeManager::setSleep(int value1, int value2, int value3) {
_sleep_mode = value1; _sleep_mode = value1;
_sleep_time = value2; _sleep_time = value2;
...@@ -1966,6 +2170,12 @@ float NodeManager::celsiusToFahrenheit(float temperature) { ...@@ -1966,6 +2170,12 @@ float NodeManager::celsiusToFahrenheit(float temperature) {
return temperature * 1.8 + 32; return temperature * 1.8 + 32;
} }
// return true if sleep or wait is configured and hence this is a sleeping node
bool NodeManager::isSleepingNode() {
if (_sleep_mode == SLEEP || _sleep_mode == WAIT) return true;
return false;
}
// register a sensor to this manager // register a sensor to this manager
int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
// get a child_id if not provided by the user // get a child_id if not provided by the user
...@@ -2256,6 +2466,9 @@ void NodeManager::before() { ...@@ -2256,6 +2466,9 @@ void NodeManager::before() {
#if BATTERY_MANAGER == 1 && !defined(MY_GATEWAY_ESP8266) #if BATTERY_MANAGER == 1 && !defined(MY_GATEWAY_ESP8266)
// set analogReference to internal if measuring the battery through a pin // set analogReference to internal if measuring the battery through a pin
if (! _battery_internal_vcc && _battery_pin > -1) analogReference(INTERNAL); if (! _battery_internal_vcc && _battery_pin > -1) analogReference(INTERNAL);
// if not configured report battery every 10 cycles
if (! _battery_report_timer.isConfigured()) _battery_report_timer.set(60,MINUTES);
_battery_report_timer.start();
#endif #endif