From 83814b81c29df08b6e789943578532383c0b5afe Mon Sep 17 00:00:00 2001 From: user2684 <user2684@users.noreply.github.com> Date: Sun, 30 Jul 2017 18:19:45 +0200 Subject: [PATCH] Added support for Power/Water pulse sensors (#96) --- NodeManager.cpp | 250 ++++++++++++++++++++++++++++-------------------- NodeManager.h | 99 +++++++++++++------ README.md | 17 +++- config.h | 4 +- 4 files changed, 233 insertions(+), 137 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index e996aab..54e451f 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -276,6 +276,9 @@ int Sensor::getValueType() { void Sensor::setFloatPrecision(int value) { _float_precision = value; } +void Sensor::setDoublePrecision(int value) { + _double_precision = value; +} #if POWER_MANAGER == 1 void Sensor::setPowerPins(int ground_pin, int vcc_pin, int wait_time) { _powerManager.setPowerPins(ground_pin, vcc_pin, wait_time); @@ -378,7 +381,7 @@ void Sensor::loop(const MyMessage & message) { if (_auto_power_pins) powerOn(); #endif // for numeric sensor requiring multiple samples, keep track of the total - float total = 0; + double total = 0; // 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 @@ -390,9 +393,10 @@ void Sensor::loop(const MyMessage & message) { // we'be been called from loop() onLoop(); } - // for integers and floats, keep track of the total + // for integers, floats and doubles, keep track of the total if (_value_type == TYPE_INTEGER) total += (float)_value_int; else if (_value_type == TYPE_FLOAT) total += _value_float; + else if (_value_type == TYPE_DOUBLE) total += _value_double; // wait between samples if (_samples_interval > 0) _node_manager->sleepOrWait(_samples_interval); } @@ -418,6 +422,17 @@ void Sensor::loop(const MyMessage & message) { _value_float = -1; } } + // process a double value + else if (_value_type == TYPE_DOUBLE && total > -1) { + // calculate the average value of the samples + double avg = total / _samples; + // report the value back + if (_isReceive(message) || _isWorthSending(avg != _last_value_double)) { + _last_value_double = avg; + _send(_msg.set(avg, _double_precision)); + _value_double = -1; + } + } // process a string value 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 @@ -485,6 +500,7 @@ void Sensor::process(Request & request) { case 19: setReportIntervalHours(request.getValueInt()); break; case 20: setReportIntervalDays(request.getValueInt()); break; case 18: setForceUpdateHours(request.getValueInt()); break; + case 21: setDoublePrecision(request.getValueInt()); break; default: return; } _send(_msg_service.set(function)); @@ -870,79 +886,6 @@ void SensorACS712::onProcess(Request & request) { void SensorACS712::onInterrupt() { } -/* - SensorRainGauge -*/ - -// contructor -SensorRainGauge::SensorRainGauge(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) { - // set presentation, type and value type - setPresentation(S_RAIN); - setType(V_RAIN); - setValueType(TYPE_FLOAT); -} - -// setter/getter -void SensorRainGauge::setSingleTip(float value) { - _single_tip = value; -} -void SensorRainGauge::setInitialValue(int value) { - _initial_value = value; -} - -// what to do during before -void SensorRainGauge::onBefore() { - // 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 during loop -void SensorRainGauge::onLoop() { - // 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; - } -} - -// what to do when receiving a remote message -void SensorRainGauge::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - 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 */ @@ -2938,7 +2881,119 @@ float SensorDimmer::_getEasing(float t, float b, float c, float d) { else if (_easing == EASE_INOUTSINE) return -c/2 * (cos(M_PI*t/d) - 1) + b; else return c*t/d + b; } +#endif + +/* + SensorPulseMeter +*/ +#if MODULE_PULSE_METER == 1 +// contructor +SensorPulseMeter::SensorPulseMeter(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) { + // set presentation, type and value type + setValueType(TYPE_FLOAT); +} + +// setter/getter +void SensorPulseMeter::setPulseFactor(float value) { + _pulse_factor = value; +} +void SensorPulseMeter::setInitialValue(int value) { + _initial_value = value; +} +void SensorPulseMeter::setInterruptMode(int value) { + _interrupt_mode = value; +} + +// what to do during before +void SensorPulseMeter::onBefore() { + // configure the interrupt pin so onInterrupt() will be called on tip + setInterrupt(_pin,_interrupt_mode,_initial_value); +} + +// what to do during setup +void SensorPulseMeter::onSetup() { +} + +// what to do during loop +void SensorPulseMeter::onLoop() { + // do not report anything if called by an interrupt + if (_node_manager->getLastInterruptPin() == _interrupt_pin) return; + // time to report the rain so far + _reportTotal(); + #if DEBUG == 1 + Serial.print(F("PLS 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 SensorPulseMeter::onReceive(const MyMessage & message) { + if (message.getCommand() == C_REQ) { + // report the total the last period + _reportTotal(); + } +} +// what to do when receiving a remote message +void SensorPulseMeter::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 102: setPulseFactor(request.getValueFloat()); break; + default: return; + } + _send(_msg_service.set(function)); +} + +// what to do when receiving an interrupt +void SensorPulseMeter::onInterrupt() { + // increase the counter + _count++; + #if DEBUG == 1 + Serial.println(F("PLS+")); + #endif +} + +// return the total based on the pulses counted +void SensorPulseMeter::_reportTotal() { + if (_value_type == TYPE_DOUBLE) _value_double = _count / _pulse_factor; + else _value_float = _count / _pulse_factor; +} + +/* + SensorRainGauge +*/ +// contructor +SensorRainGauge::SensorRainGauge(NodeManager* node_manager, int child_id, int pin): SensorPulseMeter(node_manager,child_id, pin) { + setPresentation(S_RAIN); + setType(V_RAIN); + setPulseFactor(9.09); +} + +/* + SensorPowerMeter +*/ +// contructor +SensorPowerMeter::SensorPowerMeter(NodeManager* node_manager, int child_id, int pin): SensorPulseMeter(node_manager,child_id, pin) { + setPresentation(S_POWER); + setType(V_KWH); + setValueType(TYPE_DOUBLE); + setPulseFactor(1000); +} + +/* + SensorWaterMeter +*/ +// contructor +SensorWaterMeter::SensorWaterMeter(NodeManager* node_manager, int child_id, int pin): SensorPulseMeter(node_manager,child_id, pin) { + setPresentation(S_WATER); + setType(V_VOLUME); + setValueType(TYPE_DOUBLE); + setPulseFactor(1000); +} #endif /******************************************* @@ -3092,7 +3147,6 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(this,child_id, pin)); else if (sensor_type == SENSOR_ML8511) return registerSensor(new SensorML8511(this,child_id, pin)); else if (sensor_type == SENSOR_ACS712) return registerSensor(new SensorACS712(this,child_id, pin)); - else if (sensor_type == SENSOR_RAIN_GAUGE) return registerSensor(new SensorRainGauge(this,child_id, pin)); else if (sensor_type == SENSOR_RAIN) return registerSensor(new SensorRain(this,child_id, pin)); else if (sensor_type == SENSOR_SOIL_MOISTURE) return registerSensor(new SensorSoilMoisture(this,child_id, pin)); #endif @@ -3160,9 +3214,7 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { } #endif #if MODULE_BH1750 == 1 - else if (sensor_type == SENSOR_BH1750) { - return registerSensor(new SensorBH1750(this,child_id)); - } + else if (sensor_type == SENSOR_BH1750) return registerSensor(new SensorBH1750(this,child_id)); #endif #if MODULE_MLX90614 == 1 else if (sensor_type == SENSOR_MLX90614) { @@ -3217,9 +3269,7 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { } #endif #if MODULE_SONOFF == 1 - else if (sensor_type == SENSOR_SONOFF) { - return registerSensor(new SensorSonoff(this,child_id)); - } + else if (sensor_type == SENSOR_SONOFF) return registerSensor(new SensorSonoff(this,child_id)); #endif #if MODULE_BMP085 == 1 else if (sensor_type == SENSOR_BMP085) { @@ -3241,9 +3291,7 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { } #endif #if MODULE_HCSR04 == 1 - else if (sensor_type == SENSOR_HCSR04) { - return registerSensor(new SensorHCSR04(this,child_id, pin)); - } + else if (sensor_type == SENSOR_HCSR04) return registerSensor(new SensorHCSR04(this,child_id, pin)); #endif #if MODULE_MCP9808 == 1 else if (sensor_type == SENSOR_MCP9808) { @@ -3259,14 +3307,10 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { } #endif #if MODULE_MQ == 1 - else if (sensor_type == SENSOR_MQ) { - return registerSensor(new SensorMQ(this,child_id, pin)); - } + else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(this,child_id, pin)); #endif #if MODULE_MHZ19 == 1 - else if (sensor_type == SENSOR_MHZ19) { - return registerSensor(new SensorMHZ19(this, child_id, pin)); - } + else if (sensor_type == SENSOR_MHZ19) return registerSensor(new SensorMHZ19(this, child_id, pin)); #endif #if MODULE_AM2320 == 1 else if (sensor_type == SENSOR_AM2320) { @@ -3279,22 +3323,18 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { } #endif #if MODULE_TSL2561 == 1 - else if (sensor_type == SENSOR_TSL2561) { - // register light sensor - return registerSensor(new SensorTSL2561(this,child_id)); - } + else if (sensor_type == SENSOR_TSL2561) return registerSensor(new SensorTSL2561(this,child_id)); #endif - #if MODULE_PT100 == 1 - else if (sensor_type == SENSOR_PT100) { - // register temperature sensor - return registerSensor(new SensorPT100(this,child_id,pin)); - } + #if MODULE_PT100 == 1 + else if (sensor_type == SENSOR_PT100) return registerSensor(new SensorPT100(this,child_id,pin)); #endif - #if MODULE_DIMMER == 1 - else if (sensor_type == SENSOR_DIMMER) { - // register the dimmer sensor - return registerSensor(new SensorDimmer(this,child_id,pin)); - } + #if MODULE_DIMMER == 1 + else if (sensor_type == SENSOR_DIMMER) return registerSensor(new SensorDimmer(this,child_id,pin)); + #endif + #if MODULE_PULSE_METER == 1 + else if (sensor_type == SENSOR_RAIN_GAUGE) return registerSensor(new SensorRainGauge(this,child_id,pin)); + else if (sensor_type == SENSOR_POWER_METER) return registerSensor(new SensorPowerMeter(this,child_id,pin)); + else if (sensor_type == SENSOR_WATER_METER) return registerSensor(new SensorWaterMeter(this,child_id,pin)); #endif else { #if DEBUG == 1 diff --git a/NodeManager.h b/NodeManager.h index 87d3b68..ac40f34 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -31,6 +31,7 @@ #define TYPE_INTEGER 0 #define TYPE_FLOAT 1 #define TYPE_STRING 2 +#define TYPE_DOUBLE 2 // define interrupt pins #define INTERRUPT_PIN_1 3 @@ -118,7 +119,7 @@ Default module settings */ -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE, SENSOR_RAIN, SENSOR_SOIL_MOISTURE +// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN, SENSOR_SOIL_MOISTURE #ifndef MODULE_ANALOG_INPUT #define MODULE_ANALOG_INPUT 0 #endif @@ -202,6 +203,10 @@ #ifndef MODULE_DIMMER #define MODULE_DIMMER 0 #endif +// Enable this module to use one of the following sensors: SENSOR_RAIN_GAUGE, SENSOR_POWER_METER, SENSOR_WATER_METER +#ifndef MODULE_PULSE_METER + #define MODULE_PULSE_METER 0 +#endif /*********************************** Supported Sensors @@ -218,8 +223,6 @@ enum supported_sensors { SENSOR_ML8511, // Current sensor SENSOR_ACS712, - // rain gauge sensor - SENSOR_RAIN_GAUGE, // Rain sensor, return the percentage of rain from an attached analog sensor SENSOR_RAIN, // Soil moisture sensor, return the percentage of moisture from an attached analog sensor @@ -315,6 +318,14 @@ enum supported_sensors { // Generic dimmer sensor used to drive a pwm output SENSOR_DIMMER, #endif + #if MODULE_PULSE_METER == 1 + // rain gauge sensor + SENSOR_RAIN_GAUGE, + // power meter pulse sensor + SENSOR_POWER_METER, + // water meter pulse sensor + SENSOR_WATER_METER, + #endif }; /*********************************** @@ -519,6 +530,8 @@ class Sensor { int getValueType(); // [11] for float values, set the float precision (default: 2) void setFloatPrecision(int value); + // [21] for double values, set the double precision (default: 4) + void setDoublePrecision(int value); #if POWER_MANAGER == 1 // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); @@ -577,8 +590,11 @@ class Sensor { bool _track_last_value = false; int _value_type = TYPE_INTEGER; int _float_precision = 2; + int _double_precision = 4; int _value_int = -1; float _value_float = -1; + double _value_double = -1; + double _last_value_double = -1; char * _value_string = ""; int _last_value_int = -1; float _last_value_float = -1; @@ -709,30 +725,6 @@ class SensorACS712: public Sensor { int _mv_per_amp = 185; }; -/* - SensorRainGauge -*/ - -class SensorRainGauge: public Sensor { - public: - SensorRainGauge(NodeManager* node_manager, int child_id, int pin); - // [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); - void onInterrupt(); - protected: - long _count = 0; - float _single_tip = 0.11; - int _initial_value = HIGH; -}; - /* SensorRain */ @@ -1388,6 +1380,59 @@ class SensorDimmer: public Sensor { }; #endif +/* + SensorPulseMeter +*/ +#if MODULE_PULSE_METER == 1 +class SensorPulseMeter: public Sensor { + public: + SensorPulseMeter(NodeManager* node_manager, int child_id, int pin); + // [102] set how many pulses for each unit (e.g. 1000 pulses for 1 kwh of power, 9 pulses for 1 mm of rain, etc.) + void setPulseFactor(float value); + // set initial value - internal pull up (default: HIGH) + void setInitialValue(int value); + // set the interrupt mode to attach to (default: FALLING) + void setInterruptMode(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); + void onInterrupt(); + protected: + long _count = 20; + float _pulse_factor; + int _initial_value = HIGH; + int _interrupt_mode = FALLING; + void _reportTotal(); +}; + +/* + SensorRainGauge +*/ +class SensorRainGauge: public SensorPulseMeter { + public: + SensorRainGauge(NodeManager* node_manager, int child_id, int pin); +}; + +/* + SensorPowerMeter +*/ +class SensorPowerMeter: public SensorPulseMeter { + public: + SensorPowerMeter(NodeManager* node_manager, int child_id, int pin); +}; + +/* + SensorWaterMeter +*/ +class SensorWaterMeter: public SensorPulseMeter { + public: + SensorWaterMeter(NodeManager* node_manager, int child_id, int pin); +}; +#endif + /*************************************** NodeManager: manages all the aspects of the node */ diff --git a/README.md b/README.md index 37039ed..9bc784f 100755 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ The next step is to enable NodeManager's additional functionalities and the modu // if enabled, send a SLEEPING and AWAKE service messages just before entering and just after leaving a sleep cycle and STARTED when starting/rebooting #define SERVICE_MESSAGES 0 -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE, SENSOR_RAIN, SENSOR_SOIL_MOISTURE +// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN, SENSOR_SOIL_MOISTURE #define MODULE_ANALOG_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT #define MODULE_DIGITAL_INPUT 1 @@ -214,6 +214,8 @@ The next step is to enable NodeManager's additional functionalities and the modu #define MODULE_BMP280 0 // Enable this module to use one of the following sensors: SENSOR_DIMMER #define MODULE_DIMMER 0 +// Enable this module to use one of the following sensors: SENSOR_RAIN_GAUGE, SENSOR_POWER_METER, SENSOR_WATER_METER +#define MODULE_PULSE_METER 0 ~~~ ### Installing the dependencies @@ -441,6 +443,8 @@ SENSOR_AM2320 | AM2320 sensors, return temperature/humidity based on the attache SENSOR_PT100 | High temperature sensor associated with DFRobot Driver, return the temperature in C° from the attached PT100 sensor SENSOR_BMP280 | BMP280 sensor, return temperature/pressure based on the attached BMP280 sensor SENSOR_DIMMER | Generic dimmer sensor used to drive a pwm output +SENSOR_POWER_METER | Power meter pulse sensor +SENSOR_WATER_METER | Water meter pulse sensor To register a sensor simply call the NodeManager instance with the sensory type and the pin the sensor is conncted to and optionally a child id. For example: ~~~c @@ -519,6 +523,8 @@ The following methods are available for all the sensors and can be called on the int getValueType(); // [11] for float values, set the float precision (default: 2) void setFloatPrecision(int value); + // [21] for double values, set the double precision (default: 4) + void setDoublePrecision(int value); #if POWER_MANAGER == 1 // to save battery the sensor can be optionally connected to two pins which will act as vcc and ground and activated on demand void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); @@ -617,12 +623,14 @@ Each sensor class can expose additional methods. void setOffset(int value); ~~~ -* SensorRainGauge +* SensorRainGauge / SensorPowerMeter / SensorWaterMeter ~~~c - // [102] set how many mm of rain to count for each tip (default: 0.11) - void setSingleTip(float value); + // [102] set how many pulses for each unit (e.g. 1000 pulses for 1 kwh of power, 9 pulses for 1 mm of rain, etc.) + void setPulseFactor(float value); // set initial value - internal pull up (default: HIGH) void setInitialValue(int value); + // set the interrupt mode to attach to (default: FALLING) + void setInterruptMode(int value); ~~~ * SensorDigitalOutput / SensorRelay @@ -1396,6 +1404,7 @@ v1.6: * Added support for MH-Z19 CO2 sensor * Added buil-in rain and soil moisture analog sensors * Added support for generic dimmer sensor (PWM output) +* Added support for power and water meter pulse sensors * Radio signal level is reported automatically and on demand through child 202 * SensorRainGauge now supports sleep mode * SensorSwitch now supports awake mode diff --git a/config.h b/config.h index 4a64b2c..3570198 100755 --- a/config.h +++ b/config.h @@ -125,7 +125,7 @@ // if enabled, send a SLEEPING and AWAKE service messages just before entering and just after leaving a sleep cycle and STARTED when starting/rebooting #define SERVICE_MESSAGES 0 -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE, SENSOR_RAIN, SENSOR_SOIL_MOISTURE +// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN, SENSOR_SOIL_MOISTURE #define MODULE_ANALOG_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT #define MODULE_DIGITAL_INPUT 1 @@ -167,6 +167,8 @@ #define MODULE_BMP280 0 // Enable this module to use one of the following sensors: SENSOR_DIMMER #define MODULE_DIMMER 0 +// Enable this module to use one of the following sensors: SENSOR_RAIN_GAUGE, SENSOR_POWER_METER, SENSOR_WATER_METER +#define MODULE_PULSE_METER 0 #endif -- GitLab