From 5788ff2e020a77c2f0b6c41c688b0c0853de7ea4 Mon Sep 17 00:00:00 2001 From: user2684 Date: Fri, 26 May 2017 12:15:57 +0200 Subject: [PATCH 01/33] v1.6-dev --- NodeManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NodeManager.h b/NodeManager.h index 6e3ed5ed..6bf3edd5 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -7,7 +7,7 @@ #include // define NodeManager version -#define VERSION "1.5" +#define VERSION "1.6-dev" /*********************************** Constants From 0328c280cdba86507459d842260f704f85191640 Mon Sep 17 00:00:00 2001 From: user2684 Date: Tue, 30 May 2017 00:30:17 +0200 Subject: [PATCH 02/33] Added MAX_SENSORS to limit the size of _sensors --- NodeManager.cpp | 10 +++++----- NodeManager.h | 35 +++++++++++++++++++---------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 0624c7a0..7a6b3f6d 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -2193,7 +2193,7 @@ void NodeManager::before() { if (! _battery_internal_vcc && _battery_pin > -1) analogReference(INTERNAL); #endif // setup individual sensors - for (int i = 0; i < 255; i++) { + for (int i = 0; i < MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; // call each sensor's setup() _sensors[i]->before(); @@ -2217,7 +2217,7 @@ void NodeManager::presentation() { _process("BATTERY"); #endif // present each sensor - for (int i = 0; i < 255; i++) { + for (int i = 0; i < MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; // call each sensor's presentation() if (_sleep_between_send > 0) sleep(_sleep_between_send); @@ -2242,7 +2242,7 @@ void NodeManager::setup() { _send(_msg.set("STARTED")); #endif // run setup for all the registered sensors - for (int i = 0; i < 255; i++) { + for (int i = 0; i < MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; // call each sensor's setup() _sensors[i]->setup(); @@ -2261,7 +2261,7 @@ void NodeManager::loop() { if (_auto_power_pins) powerOn(); #endif // run loop for all the registered sensors - for (int i = 0; i < 255; i++) { + for (int i = 0; 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 @@ -2609,7 +2609,7 @@ void NodeManager::_present(int child_id, int type) { // return the next available child_id int NodeManager::_getAvailableChildId() { - for (int i = 1; i < 255; i++) { + for (int i = 1; i < MAX_SENSORS; i++) { if (i == CONFIGURATION_CHILD_ID) continue; if (i == BATTERY_CHILD_ID) continue; // empty place, return it diff --git a/NodeManager.h b/NodeManager.h index 6bf3edd5..c27da05b 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -51,6 +51,10 @@ /*********************************** Default configuration settings */ +// if enabled, enable debug messages on serial port +#ifndef DEBUG + #define DEBUG 1 +#endif // if enabled, enable the capability to power on sensors with the arduino's pins to save battery while sleeping #ifndef POWER_MANAGER @@ -69,29 +73,15 @@ #define PERSIST 0 #endif -// if enabled, enable debug messages on serial port -#ifndef DEBUG - #define DEBUG 1 -#endif - // if enabled, send a SLEEPING and AWAKE service messages just before entering and just after leaving a sleep cycle #ifndef SERVICE_MESSAGES - #define SERVICE_MESSAGES 1 + #define SERVICE_MESSAGES 0 #endif // if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage #ifndef BATTERY_SENSOR #define BATTERY_SENSOR 1 #endif -// the child id used to allow remote configuration -#ifndef CONFIGURATION_CHILD_ID - #define CONFIGURATION_CHILD_ID 200 -#endif -// the child id used to report the battery voltage to the controller -#ifndef BATTERY_CHILD_ID - #define BATTERY_CHILD_ID 201 -#endif - // Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_MQ, SENSOR_ACS712 #ifndef MODULE_ANALOG_INPUT #define MODULE_ANALOG_INPUT 0 @@ -149,6 +139,19 @@ #define MODULE_MCP9808 0 #endif +// the child id used to allow remote configuration +#ifndef CONFIGURATION_CHILD_ID + #define CONFIGURATION_CHILD_ID 200 +#endif +// the child id used to report the battery voltage to the controller +#ifndef BATTERY_CHILD_ID + #define BATTERY_CHILD_ID 201 +#endif +// define the maximum number of sensors that can be managed +#ifndef MAX_SENSORS + #define MAX_SENSORS 10 +#endif + /*********************************** Sensors types */ @@ -1063,7 +1066,7 @@ class NodeManager { int _interrupt_2_pull = -1; int _last_interrupt_pin = -1; long _timestamp = -1; - Sensor* _sensors[255] = {0}; + Sensor* _sensors[MAX_SENSORS] = {0}; bool _ack = false; void _process(const char * message); void _sleep(); From 040a160a9ea47720d3c2078f8c548e5a641a2944 Mon Sep 17 00:00:00 2001 From: user2684 Date: Tue, 30 May 2017 15:28:45 +0200 Subject: [PATCH 03/33] Use enum instead of define for defining each sensor #121 (#122) --- NodeManager.h | 191 ++++++++++++++++++++++++++------------------------ 1 file changed, 98 insertions(+), 93 deletions(-) diff --git a/NodeManager.h b/NodeManager.h index c27da05b..4b0f104d 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -82,6 +82,23 @@ #define BATTERY_SENSOR 1 #endif +// the child id used to allow remote configuration +#ifndef CONFIGURATION_CHILD_ID + #define CONFIGURATION_CHILD_ID 200 +#endif +// the child id used to report the battery voltage to the controller +#ifndef BATTERY_CHILD_ID + #define BATTERY_CHILD_ID 201 +#endif +// define the maximum number of sensors that can be managed +#ifndef MAX_SENSORS + #define MAX_SENSORS 10 +#endif + +/*********************************** + Default module settings +*/ + // Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_MQ, SENSOR_ACS712 #ifndef MODULE_ANALOG_INPUT #define MODULE_ANALOG_INPUT 0 @@ -139,101 +156,89 @@ #define MODULE_MCP9808 0 #endif -// the child id used to allow remote configuration -#ifndef CONFIGURATION_CHILD_ID - #define CONFIGURATION_CHILD_ID 200 -#endif -// the child id used to report the battery voltage to the controller -#ifndef BATTERY_CHILD_ID - #define BATTERY_CHILD_ID 201 -#endif -// define the maximum number of sensors that can be managed -#ifndef MAX_SENSORS - #define MAX_SENSORS 10 -#endif - /*********************************** - Sensors types + Supported Sensors */ -#if MODULE_ANALOG_INPUT == 1 - // Generic analog sensor, return a pin's analog value or its percentage - #define SENSOR_ANALOG_INPUT 1 - // LDR sensor, return the light level of an attached light resistor in percentage - #define SENSOR_LDR 2 - // Thermistor sensor, return the temperature based on the attached thermistor - #define SENSOR_THERMISTOR 3 - // MQ2 air quality sensor - #define SENSOR_MQ 19 - // ML8511 UV sensor - #define SENSOR_ML8511 20 - // Current sensor - #define SENSOR_ACS712 24 - // rain gauge sensor - #define SENSOR_RAIN_GAUGE 26 -#endif -#if MODULE_DIGITAL_INPUT == 1 - // Generic digital sensor, return a pin's digital value - #define SENSOR_DIGITAL_INPUT 4 -#endif -#if MODULE_DIGITAL_OUTPUT == 1 - // Generic digital output sensor, allows setting the digital output of a pin to the requested value - #define SENSOR_DIGITAL_OUTPUT 5 - // Relay sensor, allows activating the relay - #define SENSOR_RELAY 6 - // Latching Relay sensor, allows activating the relay with a pulse - #define SENSOR_LATCHING_RELAY 7 -#endif -#if MODULE_DHT == 1 - // DHT11/DHT22 sensors, return temperature/humidity based on the attached DHT sensor - #define SENSOR_DHT11 8 - #define SENSOR_DHT22 9 -#endif -#if MODULE_SHT21 == 1 - // SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor - #define SENSOR_SHT21 10 - #define SENSOR_HTU21D 15 -#endif -#if MODULE_SWITCH == 1 - // Generic switch, wake up the board when a pin changes status - #define SENSOR_SWITCH 11 - // Door sensor, wake up the board and report when an attached magnetic sensor has been opened/closed - #define SENSOR_DOOR 12 - // Motion sensor, wake up the board and report when an attached PIR has triggered - #define SENSOR_MOTION 13 -#endif -#if MODULE_DS18B20 == 1 - // DS18B20 sensor, return the temperature based on the attached sensor - #define SENSOR_DS18B20 14 -#endif -#if MODULE_BH1750 == 1 - // BH1750 sensor, return light in lux - #define SENSOR_BH1750 16 -#endif -#if MODULE_MLX90614 == 1 - // MLX90614 sensor, contactless temperature sensor - #define SENSOR_MLX90614 17 -#endif -#if MODULE_BME280 == 1 - // MLX90614 sensor, contactless temperature sensor - #define SENSOR_BME280 18 -#endif -#if MODULE_SONOFF == 1 - // Sonoff wireless smart switch - #define SENSOR_SONOFF 21 -#endif -#if MODULE_BMP085 == 1 - // BMP085/BMP180 sensor, return temperature and pressure - #define SENSOR_BMP085 22 -#endif -#if MODULE_HCSR04 == 1 - // HC-SR04 sensor, return the distance between the sensor and an object - #define SENSOR_HCSR04 23 -#endif -#if MODULE_MCP9808 == 1 - // MCP9808 sensor, precision temperature sensor - #define SENSOR_MCP9808 25 -#endif -// last Id: 26 +enum supported_sensors { + #if MODULE_ANALOG_INPUT == 1 + // Generic analog sensor, return a pin's analog value or its percentage + SENSOR_ANALOG_INPUT, + // LDR sensor, return the light level of an attached light resistor in percentage + SENSOR_LDR, + // Thermistor sensor, return the temperature based on the attached thermistor + SENSOR_THERMISTOR, + // MQ2 air quality sensor + SENSOR_MQ, + // ML8511 UV sensor + SENSOR_ML8511, + // Current sensor + SENSOR_ACS712, + // rain gauge sensor + SENSOR_RAIN_GAUGE, + #endif + #if MODULE_DIGITAL_INPUT == 1 + // Generic digital sensor, return a pin's digital value + SENSOR_DIGITAL_INPUT, + #endif + #if MODULE_DIGITAL_OUTPUT == 1 + // Generic digital output sensor, allows setting the digital output of a pin to the requested value + SENSOR_DIGITAL_OUTPUT, + // Relay sensor, allows activating the relay + SENSOR_RELAY, + // Latching Relay sensor, allows activating the relay with a pulse + SENSOR_LATCHING_RELAY, + #endif + #if MODULE_DHT == 1 + // DHT11/DHT22 sensors, return temperature/humidity based on the attached DHT sensor + SENSOR_DHT11, + SENSOR_DHT22, + #endif + #if MODULE_SHT21 == 1 + // SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor + SENSOR_SHT21, + SENSOR_HTU21D, + #endif + #if MODULE_SWITCH == 1 + // Generic switch, wake up the board when a pin changes status + SENSOR_SWITCH, + // Door sensor, wake up the board and report when an attached magnetic sensor has been opened/closed + SENSOR_DOOR, + // Motion sensor, wake up the board and report when an attached PIR has triggered + SENSOR_MOTION, + #endif + #if MODULE_DS18B20 == 1 + // DS18B20 sensor, return the temperature based on the attached sensor + SENSOR_DS18B20, + #endif + #if MODULE_BH1750 == 1 + // BH1750 sensor, return light in lux + SENSOR_BH1750, + #endif + #if MODULE_MLX90614 == 1 + // MLX90614 sensor, contactless temperature sensor + SENSOR_MLX90614, + #endif + #if MODULE_BME280 == 1 + // MLX90614 sensor, contactless temperature sensor + SENSOR_BME280, + #endif + #if MODULE_SONOFF == 1 + // Sonoff wireless smart switch + SENSOR_SONOFF, + #endif + #if MODULE_BMP085 == 1 + // BMP085/BMP180 sensor, return temperature and pressure + SENSOR_BMP085, + #endif + #if MODULE_HCSR04 == 1 + // HC-SR04 sensor, return the distance between the sensor and an object + SENSOR_HCSR04, + #endif + #if MODULE_MCP9808 == 1 + // MCP9808 sensor, precision temperature sensor + SENSOR_MCP9808, + #endif +}; /*********************************** Libraries */ From 5b3fb815e299d44aac3d070d02783c2e0f8cd5bb Mon Sep 17 00:00:00 2001 From: user2684 Date: Tue, 30 May 2017 16:27:00 +0200 Subject: [PATCH 04/33] Adde rain/soil moisture sensor. Updated documentation (#123) * Added new sensors, updated docs * Added SensorRain * Added SensorSoilMoisture * Updated docs with how to contribute instructions --- NodeManager.cpp | 32 +++++++++++ NodeManager.h | 20 +++++++ NodeManager.ino | 2 +- README.md | 144 ++++++++++++++++++++++++++++++------------------ 4 files changed, 144 insertions(+), 54 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 7a6b3f6d..a2af65d1 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -673,6 +673,36 @@ void SensorRainGauge::onReceive(const MyMessage & message) { } } +/* + SensorRain +*/ + +// contructor +SensorRain::SensorRain(int child_id, int pin): SensorAnalogInput(child_id, pin) { + // set presentation and type and reverse + setPresentation(S_RAIN); + setType(V_RAINRATE); + setReference(DEFAULT); + setOutputPercentage(true); + setReverse(true); + setRangeMin(100); +} + +/* + SensorSoilMoisture +*/ + +// contructor +SensorSoilMoisture::SensorSoilMoisture(int child_id, int pin): SensorAnalogInput(child_id, pin) { + // set presentation and type and reverse + setPresentation(S_MOISTURE); + setType(V_LEVEL); + setReference(DEFAULT); + setOutputPercentage(true); + setReverse(true); + setRangeMin(100); +} + /* * SensorMQ @@ -1935,6 +1965,8 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { else if (sensor_type == SENSOR_ML8511) return registerSensor(new SensorML8511(child_id, pin)); else if (sensor_type == SENSOR_ACS712) return registerSensor(new SensorACS712(child_id, pin)); else if (sensor_type == SENSOR_RAIN_GAUGE) return registerSensor(new SensorRainGauge(child_id, pin)); + else if (sensor_type == SENSOR_RAIN) return registerSensor(new SensorRain(child_id, pin)); + else if (sensor_type == SENSOR_SOIL_MOISTURE) return registerSensor(new SensorSoilMoisture(child_id, pin)); #endif #if MODULE_DIGITAL_INPUT == 1 else if (sensor_type == SENSOR_DIGITAL_INPUT) return registerSensor(new SensorDigitalInput(child_id, pin)); diff --git a/NodeManager.h b/NodeManager.h index 4b0f104d..33b5bfb2 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -175,6 +175,10 @@ enum supported_sensors { 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 + SENSOR_SOIL_MOISTURE, #endif #if MODULE_DIGITAL_INPUT == 1 // Generic digital sensor, return a pin's digital value @@ -603,6 +607,22 @@ class SensorRainGauge: public Sensor { long _last_report = 0; }; +/* + SensorRain +*/ +class SensorRain: public SensorAnalogInput { + public: + SensorRain(int child_id, int pin); +}; + +/* + SensorSoilMoisture +*/ +class SensorSoilMoisture: public SensorAnalogInput { + public: + SensorSoilMoisture(int child_id, int pin); +}; + /* SensorDigitalInput: read the digital input of the configured pin */ diff --git a/NodeManager.ino b/NodeManager.ino index 148442d4..6422f437 100644 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -34,7 +34,7 @@ void before() { * Register below your sensors */ - + /* * Register above your sensors diff --git a/README.md b/README.md index f7a40c38..1eaba15a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -# Introduction - NodeManager is intended to take care on your behalf of all those common tasks a MySensors node has to accomplish, speeding up the development cycle of your projects. NodeManager includes the following main components: @@ -23,7 +21,7 @@ NodeManager includes the following main components: * Allow rebooting the board remotely * Provide out-of-the-box sensors personalities and automatically execute their main task at each cycle -# Installation +## Installation * Download the package or clone the git repository at https://github.com/mysensors/NodeManager * Open the provided sketch template and save it under a different name * Open `config.h` and customize both MySensors configuration and NodeManager global settings @@ -32,15 +30,15 @@ NodeManager includes the following main components: Please note NodeManager cannot be used as an arduino library since requires access to your MySensors configuration directives, hence its files have to be placed into the same directory of your sketch. -## Upgrade +### Upgrade * Download the package * Replace the NodeManager.cpp and NodeManager.h of your project with those just downloaded * Review the release notes in case there is any manual change required to the existing sketch or config.h file -# Configuration +## Configuration NodeManager configuration includes compile-time configuration directives (which can be set in config.h), runtime global and per-sensor configuration settings (which can be set in your sketch) and settings that can be customized remotely (via a special child id). -## Setup MySensors +### Setup MySensors Since NodeManager has to communicate with the MySensors gateway on your behalf, it has to know how to do it. Place on top of the `config.h` file all the MySensors typical directives you are used to set on top of your sketch so both your sketch AND NodeManager will be able to share the same configuration. For example: ~~~c /********************************** @@ -123,11 +121,14 @@ Since NodeManager has to communicate with the MySensors gateway on your behalf, //#define MY_DEFAULT_TX_LED_PIN 6 ~~~ -## Enable/Disable NodeManager's modules +### Enable/Disable NodeManager's modules Those NodeManager's directives in the `config.h` file control which module/library/functionality will be made available to your sketch. Enable (e.g. set to 1) only what you need to ensure enough space is left to your custom code. ~~~c +// if enabled, enable debug messages on serial port +#define DEBUG 1 + // if enabled, enable the capability to power on sensors with the arduino's pins to save battery while sleeping #define POWER_MANAGER 1 // if enabled, will load the battery manager library to allow the battery level to be reported automatically or on demand @@ -137,15 +138,12 @@ Those NodeManager's directives in the `config.h` file control which module/libra // if enabled, persist the configuration settings on EEPROM #define PERSIST 0 -// if enabled, enable debug messages on serial port -#define DEBUG 1 - // 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 1 +#define SERVICE_MESSAGES 0 // if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage #define BATTERY_SENSOR 1 -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_MQ, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE +// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_MQ, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE, 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 @@ -175,7 +173,7 @@ Those NodeManager's directives in the `config.h` file control which module/libra #define MODULE_MCP9808 0 ~~~ -## Installing the dependencies +### Installing the dependencies Some of the modules above rely on third party libraries. Those libraries are not included within NodeManager and have to be installed from the Arduino IDE Library Manager (Sketch -> Include Library -> Manager Libraries). You need to install the library ONLY if the module is enabled: @@ -192,7 +190,7 @@ MODULE_BMP085 | https://github.com/adafruit/Adafruit-BMP085-Library MODULE_HCSR04 | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/NewPing MODULE_MCP9808 | https://github.com/adafruit/Adafruit_MCP9808_Library -## Configure NodeManager +### Configure NodeManager Node Manager comes with a reasonable default configuration. If you want/need to change its settings, this can be done in your sketch, inside the `before()` function and just before registering your sensors. The following methods are exposed for your convenience: @@ -266,7 +264,7 @@ For example nodeManager.setBatteryMin(1.8); ~~~ -## Register your sensors +### Register your sensors In your sketch, inside the `before()` function and just before calling `nodeManager.before()`, you can register your sensors against NodeManager. The following built-in sensor types are available: Sensor type | Description @@ -297,6 +295,8 @@ SENSOR_HCSR04 | HC-SR04 sensor, return the distance between the sensor and an ob SENSOR_ACS712 | ACS712 sensor, measure the current going through the attached module SENSOR_MCP9808 | MCP9808 sensor, measure the temperature through the attached module SENSOR_RAIN_GAUGE | Rain gauge sensor +SENSOR_RAIN | Rain sensor, return the percentage of rain from an attached analog sensor +SENSOR_SOIL_MOISTURE | Soil moisture sensor, return the percentage of moisture from an attached analog sensor To register a sensor simply call the NodeManager instance with the sensory type and the pin the sensor is conncted to. For example: ~~~c @@ -308,7 +308,7 @@ Once registered, your job is done. NodeManager will assign a child id automatica When called, registerSensor returns the child_id of the sensor so you will be able to retrieve it later if needed. If you want to set a child_id manually, this can be passed as third argument to the function. -### Creating a custom sensor +#### Creating a custom sensor If you want to create a custom sensor and register it with NodeManager so it can take care of all the common tasks, you can create a class inheriting from `Sensor` and implement the following methods: ~~~c @@ -327,7 +327,7 @@ You can then instantiate your newly created class and register with NodeManager: nodeManager.registerSensor(new SensorCustom(child_id, pin)); ~~~ -## Configuring the sensors +### Configuring the sensors Each built-in sensor class comes with reasonable default settings. In case you want/need to customize any of those settings, after having registered the sensor, you can retrieve it back and call set functions common to all the sensors or specific for a given class. To do so, use `nodeManager.getSensor(child_id)` which will return a pointer to the sensor. Remeber to cast it to the right class before calling their functions. For example: @@ -337,7 +337,7 @@ To do so, use `nodeManager.getSensor(child_id)` which will return a pointer to t ~~~ -### Sensor's general configuration +#### Sensor's general configuration The following methods are available for all the sensors: ~~~c @@ -393,11 +393,11 @@ The following methods are available for all the sensors: char* getValueString(); ~~~ -### Sensor's specific configuration +#### Sensor's specific configuration Each sensor class can expose additional methods. -#### SensorAnalogInput / SensorLDR +** SensorAnalogInput / SensorLDR / SensorRain / SensorSoilMoisture ** ~~~c // the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) void setReference(int value); @@ -411,7 +411,7 @@ Each sensor class can expose additional methods. void setRangeMax(int value); ~~~ -#### SensorThermistor +** SensorThermistor ** ~~~c // resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); @@ -425,7 +425,7 @@ Each sensor class can expose additional methods. void setOffset(float value); ~~~ -#### SensorMQ +** SensorMQ ** ~~~c // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); void setTargetGas(int value); @@ -451,7 +451,7 @@ Each sensor class can expose additional methods. void setSmokeCurve(float *value); ~~~ -#### SensorDigitalOutput / SensorRelay / SensorLatchingRelay +** SensorDigitalOutput / SensorRelay / SensorLatchingRelay ** ~~~c // set how to initialize the output (default: LOW) void setInitialValue(int value); @@ -463,7 +463,7 @@ Each sensor class can expose additional methods. void setLegacyMode(bool value); ~~~ -#### SensorSwitch / SensorDoor / SensorMotion +** SensorSwitch / SensorDoor / SensorMotion ** ~~~c // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); @@ -475,7 +475,7 @@ Each sensor class can expose additional methods. void setInitial(int value); ~~~ -#### SensorDs18b20 +** SensorDs18b20** ~~~c // return the sensors' device address DeviceAddress* getDeviceAddress(); @@ -487,13 +487,13 @@ Each sensor class can expose additional methods. void setSleepDuringConversion(bool value); ~~~ -#### SensorBME280 +** SensorBME280 ** ~~~c // define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); ~~~ -#### SensorSonoff +** SensorSonoff ** ~~~c // set the button's pin (default: 0) void setButtonPin(int value); @@ -503,13 +503,13 @@ Each sensor class can expose additional methods. void setLedPin(int value); ~~~ -#### SensorBMP085 +** SensorBMP085 ** ~~~c // define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); ~~~ -#### SensorHCSR04 +** SensorHCSR04 ** ~~~c // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setTriggerPin(int value); @@ -519,7 +519,7 @@ Each sensor class can expose additional methods. void setMaxDistance(int value); ~~~ -#### SensorACS712 +** SensorACS712 ** ~~~c // set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); void setmVPerAmp(int value); @@ -527,7 +527,7 @@ Each sensor class can expose additional methods. void setOffset(int value); ~~~ -#### SensorRainGauge +** SensorRainGauge ** ~~~c // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60); void setReportInterval(int value); @@ -535,16 +535,14 @@ Each sensor class can expose additional methods. void setSingleTip(float value); ~~~ -## Upload your sketch +### Upload your sketch Upload your sketch to your arduino board as you are used to. -## Verify if everything works fine - Check your gateway's logs to ensure the node is working as expected. You should see the node presenting itself, reporting battery level, presenting all the registered sensors and the configuration child id service. When `DEBUG` is enabled, detailed information is available through the serial port. Remember to disable debug once the tests have been completed. -## Communicate with each sensor +### Communicate with NodeManager and its sensors You can interact with each registered sensor asking to execute their main tasks by sending to the child id a `REQ` command. For example to request the temperature to node_id 254 and child_id 1: @@ -557,8 +555,6 @@ To activate a relay connected to the same node, child_id 100: No need to implement anything on your side since for built-in sensor types this is handled automatically. Once the node will be sleeping, it will report automatically each measure at the end of every sleep cycle. -## Communicate with the node - NodeManager exposes a configuration service by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message. The following custom commands are available: @@ -594,45 +590,45 @@ In addition, NodeManager will report with custom messages every time the board i If `PERSIST` is enabled, the settings provided with `INTVLnnnX` and `MODEx` are saved to the EEPROM to be persistent even after rebooting the board. -# How it works +## Understanding NodeManager: how it works A NodeManager object must be created and called from within your sketch during `before()`, `presentation()`, `loop()` and `receive()` to work properly. NodeManager will do the following during each phase: -## NodeManager::before() +** NodeManager::before() ** * Setup the interrupt pins to wake up the board based on the configured interrupts (e.g. stop sleeping when the pin is connected to ground or wake up and notify when a motion sensor has trigger) * If persistance is enabled, restore from the EEPROM the latest sleeping settings * Call `before()` of each registered sensor -### Sensor::before() +** Sensor::before() ** * Call sensor-specific implementation of before by invoking `onBefore()` to initialize the sensor -## NodeManager::setup() +** NodeManager::setup() ** * Send a custom message with a STARTED payload to the controller * Call `setup()` of each registered sensor -### Sensor::setup() +** Sensor::setup() ** * Call sensor-specific implementation of setup by invoking `onSetup()` to initialize the sensor -## NodeManager::loop() +** NodeManager::loop() ** * If all the sensors are powered by an arduino pin, this is set to HIGH * Call `loop()` of each registered sensor * If all the sensors are powered by an arduino pin, this is set to LOW -### Sensor::loop() +** Sensor::loop() ** * If the sensor is powered by an arduino pin, this is set to HIGH * For each registered sensor, the sensor-specific `onLoop()` is called. If multiple samples are requested, this is run multiple times. * In case multiple samples have been collected, the average is calculated * A message is sent to the gateway with the calculated value. Depending on the configuration, this is not sent if it is the same as the previous value or sent anyway after a given number of cycles. These functionalies are not sensor-specific and common to all the sensors inheriting from the `Sensor` class. * If the sensor is powered by an arduino pin, this is set to LOW -## NodeManager::receive() +** NodeManager::receive() ** * Receive a message from the radio network * If the destination child id is the configuration node, it will handle the incoming message, otherwise will dispatch the message to the recipient sensor -### Sensor::receive() +** Sensor::receive() ** * Invoke `Sensor::loop()` which will execute the sensor main taks and eventually call `Sensor::onReceive()` -# Examples +## Examples All the examples below takes place within the before() function in the main sketch, just below the "Register below your sensors" comment. Set battery minimum and maxium voltage. This will be used to calculate the level percentage: @@ -700,9 +696,9 @@ Register a latching relay connecting to pin 6 (set) and pin 7 (unset): nodeManager.registerSensor(SENSOR_LATCHING_RELAY,7); ~~~ -# Example Sketches +## Example Sketches -## Analog Light and Temperature Sensor +** Analog Light and Temperature Sensor ** The following sketch can be used to report the temperature and the light level based on a thermistor and LDR sensors attached to two analog pins of the arduino board (A1 and A2). Both the thermistor and the LDR are connected to ground on one side and to vcc via a resistor on the other so to measure the voltage drop across each of them through the analog pins. @@ -780,7 +776,7 @@ void receive(const MyMessage &message) { } ~~~ -## Motion Sensor +** Motion Sensor ** The following sketch can be used to report back to the controller when a motion sensor attached to the board's pin 3 triggers. In this example, the board will be put to sleep just after startup and will report a heartbeat every hour. NodeManager will take care of configuring an interrupt associated to the provided pin so automatically wake up when a motion is detected and report a V_TRIPPED message back. This sketch requires MODULE_SWITCH to be enabled in the global config.h file. @@ -854,7 +850,7 @@ void receive(const MyMessage &message) { } ~~~ -## Boiler Sensor +** Boiler Sensor ** The following sketch controls a latching relay connected to a boiler. A latching relay (requiring only a pulse to switch) has been chosen to minimize the power consumption required by a traditional relay to stay on. This relay has normally two pins, one for closing and the other for opening the controlled circuit, connected to pin 6 and 7 of the arduino board. This is why we have to register two sensors against NodeManager so to control the two funtions indipendently. Since using a SENSOR_LATCHING_RELAY type of sensor, NodeManager will take care of just sending out a single pulse only when a REQ command of type V_STATUS is sent to one or the other child id. @@ -936,7 +932,7 @@ void receive(const MyMessage &message) { ~~~ -## Rain and Soil Moisture Sensor +** Rain and Soil Moisture Sensor ** The following sketch can be used to report the rain level and the soil moisture based on two sensors connected to the board's analog pins (A1 and A2). In this case we are customizing the out-of-the-box SENSOR_ANALOG_INPUT sensor type since we just need to measure an analog input but we also want to provide the correct type and presentation for each sensor. @@ -1039,7 +1035,49 @@ void receive(const MyMessage &message) { } ~~~ -# Release Notes +## Contributing + +Contributes to NodeManager are of course more than welcome. + +### Reporting an issue or request an enhancement + +For reporting an issue, requesting support for a new sensor or any other kind of enhancement, please drop a message either on the project's main page () or directly on Github (). + + +### Contributing to the code + +If you want to contribute to the code, a pull request on Github is the way to go. First of all setup your development environment: + +* Create a copy of the project in your Github account by clicking on the "Fork" button on https://github.com/mysensors/NodeManager. +* Check the copy actually exists on https://github.com//NodeManager +* Clone your repository on your computer: `git clone https://github.com//NodeManager.git` +* Configure the main project's repository as an upstream: `git remote add upstream https://github.com/mysensors/NodeManager.git` +* Create and switch to a local development branch: `git checkout -b development origin/development` + +Before applying any change, ensure you have the latest development version available: +* Switch to your local development branch: `git checkout development` +* Fetch the latest version from the main project's repository: `git fetch upstream` +* Merge into your development copy all the changes from the main repository: `git merge development upstream/development` + +Create a branch for the fix/feature you want to work on and apply changes to the code: +* Create and switch to a new branch (give it a significant name, e.g. fix/enum-sensors): `git checkout -b ` +* Do any required change to the code +* Include all the files changed for your commit: `git add .` +* Commit the changes: `git commit -m"Use enum instead of define for defining each sensor #121"` +* Push the branch with the changes to your repository: `git push origin ` +* Visit https://github.com//NodeManager/branches and click the "New pull request" button just aside your newly created branch +* Fill in the request with a significant title and description and select the "development" branch from the main repository to be compared against your branch +* Submit the request and start the discussion +* Any additional commits to your branch which will be presented within the same pull request + +If there are changes introduced to the development branch that conflicts with an open pull request, you will have to resolve the conflicts and update the PR: +* Fetch and merge into development any change from upstream/development as detailed above +* Switch to your branch: `git checkout ` +* Rebase the branch you filed the PR from against your updated development branch: `git rebase development` +* Resolve the conflicts and commit again +* Force push your updated branch so the PR gets updated: `git push HEAD: -f` + +## Release Notes v1.0: From b23a113133604b75a8351de47bcfdc210fb4092d Mon Sep 17 00:00:00 2001 From: user2684 Date: Tue, 30 May 2017 17:12:36 +0200 Subject: [PATCH 05/33] Allow a sensor to access NodeManager's or other sensors' functions #93 (#124) --- NodeManager.cpp | 131 ++++++++++++++++++++++++------------------------ NodeManager.h | 60 +++++++++++----------- README.md | 97 ++++++++++++++++++++++------------- 3 files changed, 160 insertions(+), 128 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index a2af65d1..721eb4d6 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -96,7 +96,8 @@ void PowerManager::powerOff() { Sensor class */ // constructor -Sensor::Sensor(int child_id, int pin) { +Sensor::Sensor(NodeManager* node_manager, int child_id, int pin) { + _node_manager = node_manager; _child_id = child_id; _pin = pin; _msg = MyMessage(_child_id, _type); @@ -318,7 +319,7 @@ void Sensor::_send(MyMessage & message) { */ // contructor -SensorAnalogInput::SensorAnalogInput(int child_id, int pin): Sensor(child_id, pin) { +SensorAnalogInput::SensorAnalogInput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { } // setter/getter @@ -405,7 +406,7 @@ int SensorAnalogInput::_getPercentage(int adc) { */ // contructor -SensorLDR::SensorLDR(int child_id, int pin): SensorAnalogInput(child_id, pin) { +SensorLDR::SensorLDR(NodeManager* node_manager, int child_id, int pin): SensorAnalogInput(node_manager, child_id, pin) { // set presentation and type and reverse (0: no light, 100: max light) setPresentation(S_LIGHT_LEVEL); setType(V_LIGHT_LEVEL); @@ -417,7 +418,7 @@ SensorLDR::SensorLDR(int child_id, int pin): SensorAnalogInput(child_id, pin) { */ // contructor -SensorThermistor::SensorThermistor(int child_id, int pin): Sensor(child_id, pin) { +SensorThermistor::SensorThermistor(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { // set presentation, type and value type setPresentation(S_TEMP); setType(V_TEMP); @@ -491,7 +492,7 @@ void SensorThermistor::onReceive(const MyMessage & message) { */ // contructor -SensorML8511::SensorML8511(int child_id, int pin): Sensor(child_id, pin) { +SensorML8511::SensorML8511(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { // set presentation, type and value type setPresentation(S_UV); setType(V_UV); @@ -544,7 +545,7 @@ float SensorML8511::_mapfloat(float x, float in_min, float in_max, float out_min */ // contructor -SensorACS712::SensorACS712(int child_id, int pin): Sensor(child_id, pin) { +SensorACS712::SensorACS712(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { // set presentation, type and value type setPresentation(S_MULTIMETER); setType(V_CURRENT); @@ -594,7 +595,7 @@ void SensorACS712::onReceive(const MyMessage & message) { */ // contructor -SensorRainGauge::SensorRainGauge(int child_id, int pin): Sensor(child_id, pin) { +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); @@ -678,7 +679,7 @@ void SensorRainGauge::onReceive(const MyMessage & message) { */ // contructor -SensorRain::SensorRain(int child_id, int pin): SensorAnalogInput(child_id, pin) { +SensorRain::SensorRain(NodeManager* node_manager, int child_id, int pin): SensorAnalogInput(node_manager,child_id, pin) { // set presentation and type and reverse setPresentation(S_RAIN); setType(V_RAINRATE); @@ -693,7 +694,7 @@ SensorRain::SensorRain(int child_id, int pin): SensorAnalogInput(child_id, pin) */ // contructor -SensorSoilMoisture::SensorSoilMoisture(int child_id, int pin): SensorAnalogInput(child_id, pin) { +SensorSoilMoisture::SensorSoilMoisture(NodeManager* node_manager, int child_id, int pin): SensorAnalogInput(node_manager, child_id, pin) { // set presentation and type and reverse setPresentation(S_MOISTURE); setType(V_LEVEL); @@ -707,7 +708,7 @@ SensorSoilMoisture::SensorSoilMoisture(int child_id, int pin): SensorAnalogInput /* * SensorMQ */ -SensorMQ::SensorMQ(int child_id, int pin): Sensor(child_id,pin) { +SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { setPresentation(S_AIR_QUALITY); setType(V_LEVEL); } @@ -853,7 +854,7 @@ int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) { */ // contructor -SensorDigitalInput::SensorDigitalInput(int child_id, int pin): Sensor(child_id, pin) { +SensorDigitalInput::SensorDigitalInput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) { } // what to do during before @@ -893,7 +894,7 @@ void SensorDigitalInput::onReceive(const MyMessage & message) { */ // contructor -SensorDigitalOutput::SensorDigitalOutput(int child_id, int pin): Sensor(child_id, pin) { +SensorDigitalOutput::SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) { } // what to do during before @@ -974,7 +975,7 @@ void SensorDigitalOutput::onReceive(const MyMessage & message) { */ // contructor -SensorRelay::SensorRelay(int child_id, int pin): SensorDigitalOutput(child_id, pin) { +SensorRelay::SensorRelay(NodeManager* node_manager, int child_id, int pin): SensorDigitalOutput(node_manager, child_id, pin) { // set presentation and type setPresentation(S_BINARY); setType(V_STATUS); @@ -991,7 +992,7 @@ void SensorRelay::onLoop() { */ // contructor -SensorLatchingRelay::SensorLatchingRelay(int child_id, int pin): SensorRelay(child_id, pin) { +SensorLatchingRelay::SensorLatchingRelay(NodeManager* node_manager, int child_id, int pin): SensorRelay(node_manager, child_id, pin) { // like a sensor with a default pulse set setPulseWidth(50); } @@ -1001,7 +1002,7 @@ SensorLatchingRelay::SensorLatchingRelay(int child_id, int pin): SensorRelay(chi */ #if MODULE_DHT == 1 // contructor -SensorDHT::SensorDHT(int child_id, int pin, DHT* dht, int sensor_type, int dht_type): Sensor(child_id, pin) { +SensorDHT::SensorDHT(NodeManager* node_manager, int child_id, int pin, DHT* dht, int sensor_type, int dht_type): Sensor(node_manager, child_id, pin) { // store the dht object _dht = dht; _sensor_type = sensor_type; @@ -1074,7 +1075,7 @@ void SensorDHT::onReceive(const MyMessage & message) { */ #if MODULE_SHT21 == 1 // contructor -SensorSHT21::SensorSHT21(int child_id, int sensor_type): Sensor(child_id,A2) { +SensorSHT21::SensorSHT21(NodeManager* node_manager, int child_id, int sensor_type): Sensor(node_manager,child_id,A2) { // store the sensor type (0: temperature, 1: humidity) _sensor_type = sensor_type; if (_sensor_type == SensorSHT21::TEMPERATURE) { @@ -1145,14 +1146,14 @@ void SensorSHT21::onReceive(const MyMessage & message) { */ #if MODULE_SHT21 == 1 // constructor -SensorHTU21D::SensorHTU21D(int child_id, int pin): SensorSHT21(child_id, pin) { +SensorHTU21D::SensorHTU21D(NodeManager* node_manager, int child_id, int pin): SensorSHT21(node_manager, child_id, pin) { } #endif /* * SensorSwitch */ -SensorSwitch::SensorSwitch(int child_id, int pin): Sensor(child_id,pin) { +SensorSwitch::SensorSwitch(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { setType(V_TRIPPED); } @@ -1219,14 +1220,14 @@ void SensorSwitch::onReceive(const MyMessage & message) { /* * SensorDoor */ -SensorDoor::SensorDoor(int child_id, int pin): SensorSwitch(child_id,pin) { +SensorDoor::SensorDoor(NodeManager* node_manager, int child_id, int pin): SensorSwitch(node_manager,child_id,pin) { setPresentation(S_DOOR); } /* * SensorMotion */ -SensorMotion::SensorMotion(int child_id, int pin): SensorSwitch(child_id,pin) { +SensorMotion::SensorMotion(NodeManager* node_manager, int child_id, int pin): SensorSwitch(node_manager, child_id,pin) { setPresentation(S_MOTION); // capture only when it triggers setMode(RISING); @@ -1239,7 +1240,7 @@ SensorMotion::SensorMotion(int child_id, int pin): SensorSwitch(child_id,pin) { */ #if MODULE_DS18B20 == 1 // contructor -SensorDs18b20::SensorDs18b20(int child_id, int pin, DallasTemperature* sensors, int index): Sensor(child_id, pin) { +SensorDs18b20::SensorDs18b20(NodeManager* node_manager, int child_id, int pin, DallasTemperature* sensors, int index): Sensor(node_manager,child_id, pin) { setPresentation(S_TEMP); setType(V_TEMP); setValueType(TYPE_FLOAT); @@ -1314,7 +1315,7 @@ void SensorDs18b20::setSleepDuringConversion(bool value) { */ #if MODULE_BH1750 == 1 // contructor -SensorBH1750::SensorBH1750(int child_id): Sensor(child_id,A4) { +SensorBH1750::SensorBH1750(NodeManager* node_manager, int child_id): Sensor(node_manager,child_id,A4) { setPresentation(S_LIGHT_LEVEL); setType(V_LEVEL); _lightSensor = new BH1750(); @@ -1352,7 +1353,7 @@ void SensorBH1750::onReceive(const MyMessage & message) { */ #if MODULE_MLX90614 == 1 // contructor -SensorMLX90614::SensorMLX90614(int child_id, Adafruit_MLX90614* mlx, int sensor_type): Sensor(child_id,A4) { +SensorMLX90614::SensorMLX90614(NodeManager* node_manager, int child_id, Adafruit_MLX90614* mlx, int sensor_type): Sensor(node_manager,child_id,A4) { _sensor_type = sensor_type; _mlx = mlx; // set presentation and type @@ -1397,7 +1398,7 @@ void SensorMLX90614::onReceive(const MyMessage & message) { */ #if MODULE_BME280 == 1 || MODULE_BMP085 == 1 // contructor -SensorBosch::SensorBosch(int child_id, int sensor_type): Sensor(child_id,A4) { +SensorBosch::SensorBosch(NodeManager* node_manager, int child_id, int sensor_type): Sensor(node_manager, child_id,A4) { _sensor_type = sensor_type; if (_sensor_type == SensorBosch::TEMPERATURE) { // temperature sensor @@ -1562,7 +1563,7 @@ uint8_t SensorBosch::GetI2CAddress(uint8_t chip_id) { * SensorBME280 */ #if MODULE_BME280 == 1 -SensorBME280::SensorBME280(int child_id, Adafruit_BME280* bme, int sensor_type): SensorBosch(child_id,sensor_type) { +SensorBME280::SensorBME280(NodeManager* node_manager, int child_id, Adafruit_BME280* bme, int sensor_type): SensorBosch(node_manager, child_id,sensor_type) { _bme = bme; } @@ -1625,7 +1626,7 @@ void SensorBME280::onLoop() { */ #if MODULE_BMP085 == 1 // contructor -SensorBMP085::SensorBMP085(int child_id, Adafruit_BMP085* bmp, int sensor_type): SensorBosch(child_id,sensor_type) { +SensorBMP085::SensorBMP085(NodeManager* node_manager, int child_id, Adafruit_BMP085* bmp, int sensor_type): SensorBosch(node_manager, child_id,sensor_type) { _bmp = bmp; } @@ -1675,7 +1676,7 @@ void SensorBMP085::onLoop() { */ #if MODULE_SONOFF == 1 // contructor -SensorSonoff::SensorSonoff(int child_id): Sensor(child_id,1) { +SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,1) { setPresentation(S_BINARY); setType(V_STATUS); } @@ -1775,7 +1776,7 @@ void SensorSonoff::_blink() { */ #if MODULE_HCSR04 == 1 // contructor -SensorHCSR04::SensorHCSR04(int child_id, int pin): Sensor(child_id, pin) { +SensorHCSR04::SensorHCSR04(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { // set presentation and type setPresentation(S_DISTANCE); setType(V_DISTANCE); @@ -1827,7 +1828,7 @@ void SensorHCSR04::onReceive(const MyMessage & message) { */ #if MODULE_MCP9808 == 1 // contructor -SensorMCP9808::SensorMCP9808(int child_id, Adafruit_MCP9808* mcp): Sensor(child_id,A2) { +SensorMCP9808::SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp): Sensor(node_manager, child_id,A2) { _mcp = mcp; setPresentation(S_TEMP); setType(V_TEMP); @@ -1958,49 +1959,49 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { // based on the given sensor type instantiate the appropriate class if (sensor_type == 0) return -1; #if MODULE_ANALOG_INPUT == 1 - else if (sensor_type == SENSOR_ANALOG_INPUT) return registerSensor(new SensorAnalogInput(child_id, pin)); - else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(child_id, pin)); - else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(child_id, pin)); - else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(child_id, pin)); - else if (sensor_type == SENSOR_ML8511) return registerSensor(new SensorML8511(child_id, pin)); - else if (sensor_type == SENSOR_ACS712) return registerSensor(new SensorACS712(child_id, pin)); - else if (sensor_type == SENSOR_RAIN_GAUGE) return registerSensor(new SensorRainGauge(child_id, pin)); - else if (sensor_type == SENSOR_RAIN) return registerSensor(new SensorRain(child_id, pin)); - else if (sensor_type == SENSOR_SOIL_MOISTURE) return registerSensor(new SensorSoilMoisture(child_id, pin)); + else if (sensor_type == SENSOR_ANALOG_INPUT) return registerSensor(new SensorAnalogInput(this,child_id, pin)); + else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(this,child_id, pin)); + else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(this,child_id, pin)); + else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(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 #if MODULE_DIGITAL_INPUT == 1 - else if (sensor_type == SENSOR_DIGITAL_INPUT) return registerSensor(new SensorDigitalInput(child_id, pin)); + else if (sensor_type == SENSOR_DIGITAL_INPUT) return registerSensor(new SensorDigitalInput(this,child_id, pin)); #endif #if MODULE_DIGITAL_OUTPUT == 1 - else if (sensor_type == SENSOR_DIGITAL_OUTPUT) return registerSensor(new SensorDigitalOutput(child_id, pin)); - else if (sensor_type == SENSOR_RELAY) return registerSensor(new SensorRelay(child_id, pin)); - else if (sensor_type == SENSOR_LATCHING_RELAY) return registerSensor(new SensorLatchingRelay(child_id, pin)); + else if (sensor_type == SENSOR_DIGITAL_OUTPUT) return registerSensor(new SensorDigitalOutput(this,child_id, pin)); + else if (sensor_type == SENSOR_RELAY) return registerSensor(new SensorRelay(this,child_id, pin)); + else if (sensor_type == SENSOR_LATCHING_RELAY) return registerSensor(new SensorLatchingRelay(this,child_id, pin)); #endif #if MODULE_DHT == 1 else if (sensor_type == SENSOR_DHT11 || sensor_type == SENSOR_DHT22) { int dht_type = sensor_type == SENSOR_DHT11 ? DHT11 : DHT22; DHT* dht = new DHT(pin,dht_type); // register temperature sensor - registerSensor(new SensorDHT(child_id,pin,dht,SensorDHT::TEMPERATURE,dht_type)); + registerSensor(new SensorDHT(this,child_id,pin,dht,SensorDHT::TEMPERATURE,dht_type)); // register humidity sensor child_id = _getAvailableChildId(); - return registerSensor(new SensorDHT(child_id,pin,dht,SensorDHT::HUMIDITY,dht_type)); + return registerSensor(new SensorDHT(this,child_id,pin,dht,SensorDHT::HUMIDITY,dht_type)); } #endif #if MODULE_SHT21 == 1 else if (sensor_type == SENSOR_SHT21) { // register temperature sensor - registerSensor(new SensorSHT21(child_id,SensorSHT21::TEMPERATURE)); + registerSensor(new SensorSHT21(this,child_id,SensorSHT21::TEMPERATURE)); // register humidity sensor child_id = _getAvailableChildId(); - return registerSensor(new SensorSHT21(child_id,SensorSHT21::HUMIDITY)); + return registerSensor(new SensorSHT21(this,child_id,SensorSHT21::HUMIDITY)); } else if (sensor_type == SENSOR_HTU21D) { // register temperature sensor - registerSensor(new SensorHTU21D(child_id,SensorHTU21D::TEMPERATURE)); + registerSensor(new SensorHTU21D(this,child_id,SensorHTU21D::TEMPERATURE)); // register humidity sensor child_id = _getAvailableChildId(); - return registerSensor(new SensorHTU21D(child_id,SensorHTU21D::HUMIDITY)); + return registerSensor(new SensorHTU21D(this,child_id,SensorHTU21D::HUMIDITY)); } #endif #if MODULE_SWITCH == 1 @@ -2009,9 +2010,9 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { if (pin != INTERRUPT_PIN_1 && pin != INTERRUPT_PIN_2) return -1; // register the sensor int index = 0; - if (sensor_type == SENSOR_SWITCH) index = registerSensor(new SensorSwitch(child_id, pin)); - else if (sensor_type == SENSOR_DOOR) index = registerSensor(new SensorDoor(child_id, pin)); - else if (sensor_type == SENSOR_MOTION) index = registerSensor(new SensorMotion(child_id, pin)); + if (sensor_type == SENSOR_SWITCH) index = registerSensor(new SensorSwitch(this,child_id, pin)); + else if (sensor_type == SENSOR_DOOR) index = registerSensor(new SensorDoor(this,child_id, pin)); + else if (sensor_type == SENSOR_MOTION) index = registerSensor(new SensorMotion(this,child_id, pin)); // set an interrupt on the pin and set the initial value SensorSwitch* sensor = (SensorSwitch*)getSensor(index); sensor->setInterruptPin(pin); @@ -2030,24 +2031,24 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { // register a new child for each sensor on the bus for(int i = 0; i < sensors->getDeviceCount(); i++) { if (i > 0) child_id = _getAvailableChildId(); - index = registerSensor(new SensorDs18b20(child_id,pin,sensors,i)); + index = registerSensor(new SensorDs18b20(this,child_id,pin,sensors,i)); } return index; } #endif #if MODULE_BH1750 == 1 else if (sensor_type == SENSOR_BH1750) { - return registerSensor(new SensorBH1750(child_id)); + return registerSensor(new SensorBH1750(this,child_id)); } #endif #if MODULE_MLX90614 == 1 else if (sensor_type == SENSOR_MLX90614) { Adafruit_MLX90614* mlx = new Adafruit_MLX90614(); // register ambient temperature sensor - registerSensor(new SensorMLX90614(child_id,mlx,SensorMLX90614::TEMPERATURE_AMBIENT)); + registerSensor(new SensorMLX90614(this,child_id,mlx,SensorMLX90614::TEMPERATURE_AMBIENT)); // register object temperature sensor child_id = _getAvailableChildId(); - return registerSensor(new SensorMLX90614(child_id,mlx,SensorMLX90614::TEMPERATURE_OBJECT)); + return registerSensor(new SensorMLX90614(this,child_id,mlx,SensorMLX90614::TEMPERATURE_OBJECT)); } #endif #if MODULE_BME280 == 1 @@ -2060,21 +2061,21 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { return -1; } // register temperature sensor - registerSensor(new SensorBME280(child_id,bme,SensorBME280::TEMPERATURE)); + registerSensor(new SensorBME280(this,child_id,bme,SensorBME280::TEMPERATURE)); child_id = _getAvailableChildId(); // register humidity sensor - registerSensor(new SensorBME280(child_id,bme,SensorBME280::HUMIDITY)); + registerSensor(new SensorBME280(this,child_id,bme,SensorBME280::HUMIDITY)); // register pressure sensor child_id = _getAvailableChildId(); - registerSensor(new SensorBME280(child_id,bme,SensorBME280::PRESSURE)); + registerSensor(new SensorBME280(this,child_id,bme,SensorBME280::PRESSURE)); // register forecast sensor child_id = _getAvailableChildId(); - return registerSensor(new SensorBME280(child_id,bme,SensorBME280::FORECAST)); + return registerSensor(new SensorBME280(this,child_id,bme,SensorBME280::FORECAST)); } #endif #if MODULE_SONOFF == 1 else if (sensor_type == SENSOR_SONOFF) { - return registerSensor(new SensorSonoff(child_id)); + return registerSensor(new SensorSonoff(this,child_id)); } #endif #if MODULE_BMP085 == 1 @@ -2087,18 +2088,18 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { return -1; } // register temperature sensor - registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::TEMPERATURE)); + registerSensor(new SensorBMP085(this,child_id,bmp,SensorBMP085::TEMPERATURE)); // register pressure sensor child_id = _getAvailableChildId(); - registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::PRESSURE)); + registerSensor(new SensorBMP085(this,child_id,bmp,SensorBMP085::PRESSURE)); // register forecast sensor child_id = _getAvailableChildId(); - return registerSensor(new SensorBMP085(child_id,bmp,SensorBMP085::FORECAST)); + return registerSensor(new SensorBMP085(this,child_id,bmp,SensorBMP085::FORECAST)); } #endif #if MODULE_HCSR04 == 1 else if (sensor_type == SENSOR_HCSR04) { - return registerSensor(new SensorHCSR04(child_id, pin)); + return registerSensor(new SensorHCSR04(this,child_id, pin)); } #endif #if MODULE_MCP9808 == 1 @@ -2111,7 +2112,7 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { return -1; } // register temperature sensor - registerSensor(new SensorMCP9808(child_id,mcp)); + registerSensor(new SensorMCP9808(this,child_id,mcp)); } #endif else { diff --git a/NodeManager.h b/NodeManager.h index 33b5bfb2..9da43189 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -302,6 +302,7 @@ enum supported_sensors { /************************************** Classes */ +class NodeManager; /* PowerManager @@ -328,7 +329,7 @@ class PowerManager { */ class Sensor { public: - Sensor(int child_id, int pin); + Sensor(NodeManager* node_manager, int child_id, int pin); // where the sensor is attached to (default: not set) void setPin(int value); int getPin(); @@ -392,6 +393,7 @@ class Sensor { virtual void onReceive(const MyMessage & message) = 0; protected: MyMessage _msg; + NodeManager* _node_manager; int _sleep_between_send = 0; int _pin = -1; int _child_id; @@ -426,7 +428,7 @@ class Sensor { */ class SensorAnalogInput: public Sensor { public: - SensorAnalogInput(int child_id, int pin); + SensorAnalogInput(NodeManager* node_manager, int child_id, int pin); // the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) void setReference(int value); // reverse the value or the percentage (e.g. 70% -> 30%) (default: false) @@ -457,7 +459,7 @@ class SensorAnalogInput: public Sensor { */ class SensorLDR: public SensorAnalogInput { public: - SensorLDR(int child_id, int pin); + SensorLDR(NodeManager* node_manager, int child_id, int pin); }; /* @@ -465,7 +467,7 @@ class SensorLDR: public SensorAnalogInput { */ class SensorThermistor: public Sensor { public: - SensorThermistor(int child_id, int pin); + SensorThermistor(NodeManager* node_manager, int child_id, int pin); // resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); // temperature for nominal resistance (default: 25) @@ -494,7 +496,7 @@ class SensorThermistor: public Sensor { */ class SensorMQ: public Sensor { public: - SensorMQ(int child_id, int pin); + SensorMQ(NodeManager* node_manager, int child_id, int pin); // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); void setTargetGas(int value); // define the load resistance on the board, in kilo ohms (default: 1); @@ -550,7 +552,7 @@ class SensorMQ: public Sensor { class SensorML8511: public Sensor { public: - SensorML8511(int child_id, int pin); + SensorML8511(NodeManager* node_manager, int child_id, int pin); // define what to do at each stage of the sketch void onBefore(); void onSetup(); @@ -566,7 +568,7 @@ class SensorML8511: public Sensor { class SensorACS712: public Sensor { public: - SensorACS712(int child_id, int pin); + SensorACS712(NodeManager* node_manager, int child_id, int pin); // set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); void setmVPerAmp(int value); // set ACS offset (default: 2500); @@ -587,7 +589,7 @@ class SensorACS712: public Sensor { class SensorRainGauge: public Sensor { public: - SensorRainGauge(int child_id, int pin); + SensorRainGauge(NodeManager* node_manager, int child_id, int pin); // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60); void setReportInterval(int value); // set how many mm of rain to count for each tip (default: 0.11); @@ -612,7 +614,7 @@ class SensorRainGauge: public Sensor { */ class SensorRain: public SensorAnalogInput { public: - SensorRain(int child_id, int pin); + SensorRain(NodeManager* node_manager, int child_id, int pin); }; /* @@ -620,7 +622,7 @@ class SensorRain: public SensorAnalogInput { */ class SensorSoilMoisture: public SensorAnalogInput { public: - SensorSoilMoisture(int child_id, int pin); + SensorSoilMoisture(NodeManager* node_manager, int child_id, int pin); }; /* @@ -628,7 +630,7 @@ class SensorSoilMoisture: public SensorAnalogInput { */ class SensorDigitalInput: public Sensor { public: - SensorDigitalInput(int child_id, int pin); + SensorDigitalInput(NodeManager* node_manager, int child_id, int pin); // define what to do at each stage of the sketch void onBefore(); void onSetup(); @@ -641,7 +643,7 @@ class SensorDigitalInput: public Sensor { */ class SensorDigitalOutput: public Sensor { public: - SensorDigitalOutput(int child_id, int pin); + SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin); // set how to initialize the output (default: LOW) void setInitialValue(int value); // if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) @@ -669,7 +671,7 @@ class SensorDigitalOutput: public Sensor { */ class SensorRelay: public SensorDigitalOutput { public: - SensorRelay(int child_id, int pin); + SensorRelay(NodeManager* node_manager, int child_id, int pin); // define what to do at each stage of the sketch void onLoop(); }; @@ -679,7 +681,7 @@ class SensorRelay: public SensorDigitalOutput { */ class SensorLatchingRelay: public SensorRelay { public: - SensorLatchingRelay(int child_id, int pin); + SensorLatchingRelay(NodeManager* node_manager, int child_id, int pin); }; /* @@ -688,7 +690,7 @@ class SensorLatchingRelay: public SensorRelay { #if MODULE_DHT == 1 class SensorDHT: public Sensor { public: - SensorDHT(int child_id, int pin, DHT* dht, int sensor_type, int dht_type); + SensorDHT(NodeManager* node_manager, int child_id, int pin, DHT* dht, int sensor_type, int dht_type); // define what to do at each stage of the sketch void onBefore(); void onSetup(); @@ -711,7 +713,7 @@ class SensorDHT: public Sensor { #if MODULE_SHT21 == 1 class SensorSHT21: public Sensor { public: - SensorSHT21(int child_id, int sensor_type); + SensorSHT21(NodeManager* node_manager, int child_id, int sensor_type); // define what to do at each stage of the sketch void onBefore(); void onSetup(); @@ -731,7 +733,7 @@ class SensorSHT21: public Sensor { class SensorHTU21D: public SensorSHT21 { public: - SensorHTU21D(int child_id, int pin); + SensorHTU21D(NodeManager* node_manager, int child_id, int pin); }; #endif @@ -740,7 +742,7 @@ class SensorHTU21D: public SensorSHT21 { */ class SensorSwitch: public Sensor { public: - SensorSwitch(int child_id, int pin); + SensorSwitch(NodeManager* node_manager, int child_id, int pin); // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); int getMode(); @@ -768,7 +770,7 @@ class SensorSwitch: public Sensor { */ class SensorDoor: public SensorSwitch { public: - SensorDoor(int child_id, int pin); + SensorDoor(NodeManager* node_manager, int child_id, int pin); }; /* @@ -776,7 +778,7 @@ class SensorDoor: public SensorSwitch { */ class SensorMotion: public SensorSwitch { public: - SensorMotion(int child_id, int pin); + SensorMotion(NodeManager* node_manager, int child_id, int pin); }; /* @@ -785,7 +787,7 @@ class SensorMotion: public SensorSwitch { #if MODULE_DS18B20 == 1 class SensorDs18b20: public Sensor { public: - SensorDs18b20(int child_id, int pin, DallasTemperature* sensors, int index); + SensorDs18b20(NodeManager* node_manager, int child_id, int pin, DallasTemperature* sensors, int index); // return the sensors' device address DeviceAddress* getDeviceAddress(); // returns the sensor's resolution in bits @@ -814,7 +816,7 @@ class SensorDs18b20: public Sensor { #if MODULE_BH1750 == 1 class SensorBH1750: public Sensor { public: - SensorBH1750(int child_id); + SensorBH1750(NodeManager* node_manager, int child_id); // define what to do at each stage of the sketch void onBefore(); void onSetup(); @@ -831,7 +833,7 @@ class SensorBH1750: public Sensor { #if MODULE_MLX90614 == 1 class SensorMLX90614: public Sensor { public: - SensorMLX90614(int child_id, Adafruit_MLX90614* mlx, int sensor_type); + SensorMLX90614(NodeManager* node_manager, int child_id, Adafruit_MLX90614* mlx, int sensor_type); // define what to do at each stage of the sketch void onBefore(); void onSetup(); @@ -854,7 +856,7 @@ class SensorMLX90614: public Sensor { #if MODULE_BME280 == 1 || MODULE_BMP085 == 1 class SensorBosch: public Sensor { public: - SensorBosch(int child_id, int sensor_type); + SensorBosch(NodeManager* node_manager, int child_id, int sensor_type); // define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); // define what to do at each stage of the sketch @@ -889,7 +891,7 @@ class SensorBosch: public Sensor { #if MODULE_BME280 == 1 class SensorBME280: public SensorBosch { public: - SensorBME280(int child_id, Adafruit_BME280* bme, int sensor_type); + SensorBME280(NodeManager* node_manager, int child_id, Adafruit_BME280* bme, int sensor_type); void onLoop(); protected: Adafruit_BME280* _bme; @@ -902,7 +904,7 @@ class SensorBME280: public SensorBosch { #if MODULE_BMP085 == 1 class SensorBMP085: public SensorBosch { public: - SensorBMP085(int child_id, Adafruit_BMP085* bmp, int sensor_type); + SensorBMP085(NodeManager* node_manager, int child_id, Adafruit_BMP085* bmp, int sensor_type); void onLoop(); protected: Adafruit_BMP085* _bmp; @@ -915,7 +917,7 @@ class SensorBMP085: public SensorBosch { #if MODULE_HCSR04 == 1 class SensorHCSR04: public Sensor { public: - SensorHCSR04(int child_id, int pin); + SensorHCSR04(NodeManager* node_manager, int child_id, int pin); // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setTriggerPin(int value); // Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) @@ -941,7 +943,7 @@ class SensorHCSR04: public Sensor { #if MODULE_SONOFF == 1 class SensorSonoff: public Sensor { public: - SensorSonoff(int child_id); + SensorSonoff(NodeManager* node_manager, int child_id); // set the button's pin (default: 0) void setButtonPin(int value); // set the relay's pin (default: 12) @@ -975,7 +977,7 @@ class SensorSonoff: public Sensor { #if MODULE_MCP9808 == 1 class SensorMCP9808: public Sensor { public: - SensorMCP9808(int child_id, Adafruit_MCP9808* mcp); + SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp); // define what to do at each stage of the sketch void onBefore(); void onSetup(); diff --git a/README.md b/README.md index 1eaba15a..22eb60c8 100644 --- a/README.md +++ b/README.md @@ -310,7 +310,7 @@ When called, registerSensor returns the child_id of the sensor so you will be ab #### Creating a custom sensor -If you want to create a custom sensor and register it with NodeManager so it can take care of all the common tasks, you can create a class inheriting from `Sensor` and implement the following methods: +If you want to create a custom sensor and register it with NodeManager so it can take care of all the common tasks, you can create an inline class inheriting from `Sensor` or other subclasses and implement the following methods: ~~~c // define what to do during before() to setup the sensor void onBefore(); @@ -324,7 +324,7 @@ If you want to create a custom sensor and register it with NodeManager so it can You can then instantiate your newly created class and register with NodeManager: ~~~c - nodeManager.registerSensor(new SensorCustom(child_id, pin)); + nodeManager.registerSensor(new SensorCustom(&nodeManager,child_id, pin)); ~~~ ### Configuring the sensors @@ -397,7 +397,7 @@ The following methods are available for all the sensors: Each sensor class can expose additional methods. -** SensorAnalogInput / SensorLDR / SensorRain / SensorSoilMoisture ** +* SensorAnalogInput / SensorLDR / SensorRain / SensorSoilMoisture * ~~~c // the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) void setReference(int value); @@ -411,7 +411,7 @@ Each sensor class can expose additional methods. void setRangeMax(int value); ~~~ -** SensorThermistor ** +* SensorThermistor * ~~~c // resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); @@ -425,7 +425,7 @@ Each sensor class can expose additional methods. void setOffset(float value); ~~~ -** SensorMQ ** +* SensorMQ * ~~~c // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); void setTargetGas(int value); @@ -451,7 +451,7 @@ Each sensor class can expose additional methods. void setSmokeCurve(float *value); ~~~ -** SensorDigitalOutput / SensorRelay / SensorLatchingRelay ** +* SensorDigitalOutput / SensorRelay / SensorLatchingRelay * ~~~c // set how to initialize the output (default: LOW) void setInitialValue(int value); @@ -463,7 +463,7 @@ Each sensor class can expose additional methods. void setLegacyMode(bool value); ~~~ -** SensorSwitch / SensorDoor / SensorMotion ** +* SensorSwitch / SensorDoor / SensorMotion * ~~~c // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); @@ -475,7 +475,7 @@ Each sensor class can expose additional methods. void setInitial(int value); ~~~ -** SensorDs18b20** +* SensorDs18b20** ~~~c // return the sensors' device address DeviceAddress* getDeviceAddress(); @@ -487,13 +487,13 @@ Each sensor class can expose additional methods. void setSleepDuringConversion(bool value); ~~~ -** SensorBME280 ** +* SensorBME280 * ~~~c // define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); ~~~ -** SensorSonoff ** +* SensorSonoff * ~~~c // set the button's pin (default: 0) void setButtonPin(int value); @@ -503,13 +503,13 @@ Each sensor class can expose additional methods. void setLedPin(int value); ~~~ -** SensorBMP085 ** +* SensorBMP085 * ~~~c // define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); ~~~ -** SensorHCSR04 ** +* SensorHCSR04 * ~~~c // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setTriggerPin(int value); @@ -519,7 +519,7 @@ Each sensor class can expose additional methods. void setMaxDistance(int value); ~~~ -** SensorACS712 ** +* SensorACS712 * ~~~c // set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); void setmVPerAmp(int value); @@ -527,7 +527,7 @@ Each sensor class can expose additional methods. void setOffset(int value); ~~~ -** SensorRainGauge ** +* SensorRainGauge * ~~~c // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60); void setReportInterval(int value); @@ -594,38 +594,38 @@ If `PERSIST` is enabled, the settings provided with `INTVLnnnX` and `MODEx` are A NodeManager object must be created and called from within your sketch during `before()`, `presentation()`, `loop()` and `receive()` to work properly. NodeManager will do the following during each phase: -** NodeManager::before() ** +* NodeManager::before() * * Setup the interrupt pins to wake up the board based on the configured interrupts (e.g. stop sleeping when the pin is connected to ground or wake up and notify when a motion sensor has trigger) * If persistance is enabled, restore from the EEPROM the latest sleeping settings * Call `before()` of each registered sensor -** Sensor::before() ** +* Sensor::before() * * Call sensor-specific implementation of before by invoking `onBefore()` to initialize the sensor -** NodeManager::setup() ** +* NodeManager::setup() * * Send a custom message with a STARTED payload to the controller * Call `setup()` of each registered sensor -** Sensor::setup() ** +* Sensor::setup() * * Call sensor-specific implementation of setup by invoking `onSetup()` to initialize the sensor -** NodeManager::loop() ** +* NodeManager::loop() * * If all the sensors are powered by an arduino pin, this is set to HIGH * Call `loop()` of each registered sensor * If all the sensors are powered by an arduino pin, this is set to LOW -** Sensor::loop() ** +* Sensor::loop() * * If the sensor is powered by an arduino pin, this is set to HIGH * For each registered sensor, the sensor-specific `onLoop()` is called. If multiple samples are requested, this is run multiple times. * In case multiple samples have been collected, the average is calculated * A message is sent to the gateway with the calculated value. Depending on the configuration, this is not sent if it is the same as the previous value or sent anyway after a given number of cycles. These functionalies are not sensor-specific and common to all the sensors inheriting from the `Sensor` class. * If the sensor is powered by an arduino pin, this is set to LOW -** NodeManager::receive() ** +* NodeManager::receive() * * Receive a message from the radio network * If the destination child id is the configuration node, it will handle the incoming message, otherwise will dispatch the message to the recipient sensor -** Sensor::receive() ** +* Sensor::receive() * * Invoke `Sensor::loop()` which will execute the sensor main taks and eventually call `Sensor::onReceive()` ## Examples @@ -698,7 +698,7 @@ Register a latching relay connecting to pin 6 (set) and pin 7 (unset): ## Example Sketches -** Analog Light and Temperature Sensor ** +* Analog Light and Temperature Sensor * The following sketch can be used to report the temperature and the light level based on a thermistor and LDR sensors attached to two analog pins of the arduino board (A1 and A2). Both the thermistor and the LDR are connected to ground on one side and to vcc via a resistor on the other so to measure the voltage drop across each of them through the analog pins. @@ -749,8 +749,6 @@ void before() { // presentation void presentation() { - // Send the sketch version information to the gateway and Controller - sendSketchInfo(SKETCH_NAME,SKETCH_VERSION); // call NodeManager presentation routine nodeManager.presentation(); @@ -774,9 +772,15 @@ void receive(const MyMessage &message) { // call NodeManager receive routine nodeManager.receive(message); } + +// receiveTime +void receiveTime(unsigned long ts) { + // call NodeManager receiveTime routine + nodeManager.receiveTime(ts); +} ~~~ -** Motion Sensor ** +* Motion Sensor * The following sketch can be used to report back to the controller when a motion sensor attached to the board's pin 3 triggers. In this example, the board will be put to sleep just after startup and will report a heartbeat every hour. NodeManager will take care of configuring an interrupt associated to the provided pin so automatically wake up when a motion is detected and report a V_TRIPPED message back. This sketch requires MODULE_SWITCH to be enabled in the global config.h file. @@ -797,6 +801,10 @@ Documentation available on: https://github.com/mysensors/NodeManager // load user settings #include "config.h" +// include supporting libraries +#ifdef MY_GATEWAY_ESP8266 + #include +#endif // load MySensors library #include // load NodeManager library @@ -823,8 +831,6 @@ void before() { // presentation void presentation() { - // Send the sketch version information to the gateway and Controller - sendSketchInfo(SKETCH_NAME,SKETCH_VERSION); // call NodeManager presentation routine nodeManager.presentation(); @@ -848,9 +854,15 @@ void receive(const MyMessage &message) { // call NodeManager receive routine nodeManager.receive(message); } + +// receiveTime +void receiveTime(unsigned long ts) { + // call NodeManager receiveTime routine + nodeManager.receiveTime(ts); +} ~~~ -** Boiler Sensor ** +* Boiler Sensor * The following sketch controls a latching relay connected to a boiler. A latching relay (requiring only a pulse to switch) has been chosen to minimize the power consumption required by a traditional relay to stay on. This relay has normally two pins, one for closing and the other for opening the controlled circuit, connected to pin 6 and 7 of the arduino board. This is why we have to register two sensors against NodeManager so to control the two funtions indipendently. Since using a SENSOR_LATCHING_RELAY type of sensor, NodeManager will take care of just sending out a single pulse only when a REQ command of type V_STATUS is sent to one or the other child id. @@ -875,6 +887,10 @@ Documentation available on: https://github.com/mysensors/NodeManager // load user settings #include "config.h" +// include supporting libraries +#ifdef MY_GATEWAY_ESP8266 + #include +#endif // load MySensors library #include // load NodeManager library @@ -904,8 +920,6 @@ void before() { // presentation void presentation() { - // Send the sketch version information to the gateway and Controller - sendSketchInfo(SKETCH_NAME,SKETCH_VERSION); // call NodeManager presentation routine nodeManager.presentation(); @@ -929,10 +943,16 @@ void receive(const MyMessage &message) { // call NodeManager receive routine nodeManager.receive(message); } + +// receiveTime +void receiveTime(unsigned long ts) { + // call NodeManager receiveTime routine + nodeManager.receiveTime(ts); +} ~~~ -** Rain and Soil Moisture Sensor ** +* Rain and Soil Moisture Sensor * The following sketch can be used to report the rain level and the soil moisture based on two sensors connected to the board's analog pins (A1 and A2). In this case we are customizing the out-of-the-box SENSOR_ANALOG_INPUT sensor type since we just need to measure an analog input but we also want to provide the correct type and presentation for each sensor. @@ -960,6 +980,10 @@ Documentation available on: https://github.com/mysensors/NodeManager // load user settings #include "config.h" +// include supporting libraries +#ifdef MY_GATEWAY_ESP8266 + #include +#endif // load MySensors library #include // load NodeManager library @@ -1008,8 +1032,6 @@ void before() { // presentation void presentation() { - // Send the sketch version information to the gateway and Controller - sendSketchInfo(SKETCH_NAME,SKETCH_VERSION); // call NodeManager presentation routine nodeManager.presentation(); @@ -1033,6 +1055,12 @@ void receive(const MyMessage &message) { // call NodeManager receive routine nodeManager.receive(message); } + +// receiveTime +void receiveTime(unsigned long ts) { + // call NodeManager receiveTime routine + nodeManager.receiveTime(ts); +} ~~~ ## Contributing @@ -1069,6 +1097,7 @@ Create a branch for the fix/feature you want to work on and apply changes to the * Fill in the request with a significant title and description and select the "development" branch from the main repository to be compared against your branch * Submit the request and start the discussion * Any additional commits to your branch which will be presented within the same pull request +* When the pull request is merged, delete your working branch: `git branch -d ` If there are changes introduced to the development branch that conflicts with an open pull request, you will have to resolve the conflicts and update the PR: * Fetch and merge into development any change from upstream/development as detailed above From cccba366a8ece6c048678f15c697e70ed61a0837 Mon Sep 17 00:00:00 2001 From: user2684 Date: Tue, 30 May 2017 18:04:51 +0200 Subject: [PATCH 06/33] Add debug message with encryption status #116 Add debug message with encryption status --- NodeManager.cpp | 18 +++++++++++++++ NodeManager.h | 1 + README.md | 59 ++++++++++++++++++++++++++----------------------- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 721eb4d6..4d379a61 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -2176,10 +2176,28 @@ bool NodeManager::renameSensor(int old_child_id, int new_child_id) { // setup NodeManager void NodeManager::before() { + // print out the version #if DEBUG == 1 Serial.print(F("NodeManager v")); Serial.println(VERSION); #endif + // print out MySensors' library capabilities + #if DEBUG == 1 + Serial.print(F("LIB R=")); + Serial.print(MY_CAP_RADIO); + #ifdef MY_CAP_ENCR + Serial.print(F(" E=")); + Serial.print(MY_CAP_ENCR); + #endif + Serial.print(F(" T=")); + Serial.print(MY_CAP_TYPE); + Serial.print(F(" A=")); + Serial.print(MY_CAP_ARCH); + Serial.print(F(" S=")); + Serial.print(MY_CAP_SIGN); + 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 diff --git a/NodeManager.h b/NodeManager.h index 9da43189..c6fa7c3a 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -257,6 +257,7 @@ enum supported_sensors { // include MySensors libraries #include +#include // include third party libraries #if MODULE_DHT == 1 diff --git a/README.md b/README.md index 22eb60c8..07ab3b01 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Since NodeManager has to communicate with the MySensors gateway on your behalf, * Sketch configuration */ -#define SKETCH_NAME "NodeManagerTemplate" +#define SKETCH_NAME "NodeManager" #define SKETCH_VERSION "1.0" /********************************** @@ -397,7 +397,7 @@ The following methods are available for all the sensors: Each sensor class can expose additional methods. -* SensorAnalogInput / SensorLDR / SensorRain / SensorSoilMoisture * +* SensorAnalogInput / SensorLDR / SensorRain / SensorSoilMoisture ~~~c // the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) void setReference(int value); @@ -411,7 +411,7 @@ Each sensor class can expose additional methods. void setRangeMax(int value); ~~~ -* SensorThermistor * +* SensorThermistor ~~~c // resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); @@ -425,7 +425,7 @@ Each sensor class can expose additional methods. void setOffset(float value); ~~~ -* SensorMQ * +* SensorMQ ~~~c // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); void setTargetGas(int value); @@ -451,7 +451,7 @@ Each sensor class can expose additional methods. void setSmokeCurve(float *value); ~~~ -* SensorDigitalOutput / SensorRelay / SensorLatchingRelay * +* SensorDigitalOutput / SensorRelay / SensorLatchingRelay ~~~c // set how to initialize the output (default: LOW) void setInitialValue(int value); @@ -463,7 +463,7 @@ Each sensor class can expose additional methods. void setLegacyMode(bool value); ~~~ -* SensorSwitch / SensorDoor / SensorMotion * +* SensorSwitch / SensorDoor / SensorMotion ~~~c // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); @@ -487,13 +487,13 @@ Each sensor class can expose additional methods. void setSleepDuringConversion(bool value); ~~~ -* SensorBME280 * +* SensorBME280 ~~~c // define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); ~~~ -* SensorSonoff * +* SensorSonoff ~~~c // set the button's pin (default: 0) void setButtonPin(int value); @@ -503,13 +503,13 @@ Each sensor class can expose additional methods. void setLedPin(int value); ~~~ -* SensorBMP085 * +* SensorBMP085 ~~~c // define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); ~~~ -* SensorHCSR04 * +* SensorHCSR04 ~~~c // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setTriggerPin(int value); @@ -519,7 +519,7 @@ Each sensor class can expose additional methods. void setMaxDistance(int value); ~~~ -* SensorACS712 * +* SensorACS712 ~~~c // set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); void setmVPerAmp(int value); @@ -527,7 +527,7 @@ Each sensor class can expose additional methods. void setOffset(int value); ~~~ -* SensorRainGauge * +* SensorRainGauge ~~~c // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60); void setReportInterval(int value); @@ -594,38 +594,38 @@ If `PERSIST` is enabled, the settings provided with `INTVLnnnX` and `MODEx` are A NodeManager object must be created and called from within your sketch during `before()`, `presentation()`, `loop()` and `receive()` to work properly. NodeManager will do the following during each phase: -* NodeManager::before() * +NodeManager::before(): * Setup the interrupt pins to wake up the board based on the configured interrupts (e.g. stop sleeping when the pin is connected to ground or wake up and notify when a motion sensor has trigger) * If persistance is enabled, restore from the EEPROM the latest sleeping settings * Call `before()` of each registered sensor -* Sensor::before() * +Sensor::before(): * Call sensor-specific implementation of before by invoking `onBefore()` to initialize the sensor -* NodeManager::setup() * +NodeManager::setup(): * Send a custom message with a STARTED payload to the controller * Call `setup()` of each registered sensor -* Sensor::setup() * +Sensor::setup(): * Call sensor-specific implementation of setup by invoking `onSetup()` to initialize the sensor -* NodeManager::loop() * +NodeManager::loop(): * If all the sensors are powered by an arduino pin, this is set to HIGH * Call `loop()` of each registered sensor * If all the sensors are powered by an arduino pin, this is set to LOW -* Sensor::loop() * +Sensor::loop(): * If the sensor is powered by an arduino pin, this is set to HIGH * For each registered sensor, the sensor-specific `onLoop()` is called. If multiple samples are requested, this is run multiple times. * In case multiple samples have been collected, the average is calculated * A message is sent to the gateway with the calculated value. Depending on the configuration, this is not sent if it is the same as the previous value or sent anyway after a given number of cycles. These functionalies are not sensor-specific and common to all the sensors inheriting from the `Sensor` class. * If the sensor is powered by an arduino pin, this is set to LOW -* NodeManager::receive() * +NodeManager::receive(): * Receive a message from the radio network * If the destination child id is the configuration node, it will handle the incoming message, otherwise will dispatch the message to the recipient sensor -* Sensor::receive() * +Sensor::receive(): * Invoke `Sensor::loop()` which will execute the sensor main taks and eventually call `Sensor::onReceive()` ## Examples @@ -698,7 +698,7 @@ Register a latching relay connecting to pin 6 (set) and pin 7 (unset): ## Example Sketches -* Analog Light and Temperature Sensor * +* Analog Light and Temperature Sensor The following sketch can be used to report the temperature and the light level based on a thermistor and LDR sensors attached to two analog pins of the arduino board (A1 and A2). Both the thermistor and the LDR are connected to ground on one side and to vcc via a resistor on the other so to measure the voltage drop across each of them through the analog pins. @@ -780,7 +780,7 @@ void receiveTime(unsigned long ts) { } ~~~ -* Motion Sensor * +* Motion Sensor The following sketch can be used to report back to the controller when a motion sensor attached to the board's pin 3 triggers. In this example, the board will be put to sleep just after startup and will report a heartbeat every hour. NodeManager will take care of configuring an interrupt associated to the provided pin so automatically wake up when a motion is detected and report a V_TRIPPED message back. This sketch requires MODULE_SWITCH to be enabled in the global config.h file. @@ -862,7 +862,7 @@ void receiveTime(unsigned long ts) { } ~~~ -* Boiler Sensor * +* Boiler Sensor The following sketch controls a latching relay connected to a boiler. A latching relay (requiring only a pulse to switch) has been chosen to minimize the power consumption required by a traditional relay to stay on. This relay has normally two pins, one for closing and the other for opening the controlled circuit, connected to pin 6 and 7 of the arduino board. This is why we have to register two sensors against NodeManager so to control the two funtions indipendently. Since using a SENSOR_LATCHING_RELAY type of sensor, NodeManager will take care of just sending out a single pulse only when a REQ command of type V_STATUS is sent to one or the other child id. @@ -952,7 +952,7 @@ void receiveTime(unsigned long ts) { ~~~ -* Rain and Soil Moisture Sensor * +* Rain and Soil Moisture Sensor The following sketch can be used to report the rain level and the soil moisture based on two sensors connected to the board's analog pins (A1 and A2). In this case we are customizing the out-of-the-box SENSOR_ANALOG_INPUT sensor type since we just need to measure an analog input but we also want to provide the correct type and presentation for each sensor. @@ -1076,8 +1076,8 @@ For reporting an issue, requesting support for a new sensor or any other kind of If you want to contribute to the code, a pull request on Github is the way to go. First of all setup your development environment: -* Create a copy of the project in your Github account by clicking on the "Fork" button on https://github.com/mysensors/NodeManager. -* Check the copy actually exists on https://github.com//NodeManager +* Create a copy of the project in your Github account by clicking on the "Fork" button on `https://github.com/mysensors/NodeManager` +* Check the copy actually exists on `https://github.com//NodeManager` * Clone your repository on your computer: `git clone https://github.com//NodeManager.git` * Configure the main project's repository as an upstream: `git remote add upstream https://github.com/mysensors/NodeManager.git` * Create and switch to a local development branch: `git checkout -b development origin/development` @@ -1086,18 +1086,21 @@ Before applying any change, ensure you have the latest development version avail * Switch to your local development branch: `git checkout development` * Fetch the latest version from the main project's repository: `git fetch upstream` * Merge into your development copy all the changes from the main repository: `git merge development upstream/development` +* Update the development branch of your repository: `git push origin development` Create a branch for the fix/feature you want to work on and apply changes to the code: * Create and switch to a new branch (give it a significant name, e.g. fix/enum-sensors): `git checkout -b ` * Do any required change to the code * Include all the files changed for your commit: `git add .` * Commit the changes: `git commit -m"Use enum instead of define for defining each sensor #121"` +* Ensure both the main sketch and the config.h file do not present any change * Push the branch with the changes to your repository: `git push origin ` -* Visit https://github.com//NodeManager/branches and click the "New pull request" button just aside your newly created branch +* Visit `https://github.com//NodeManager/branches` and click the "New pull request" button just aside your newly created branch * Fill in the request with a significant title and description and select the "development" branch from the main repository to be compared against your branch * Submit the request and start the discussion * Any additional commits to your branch which will be presented within the same pull request -* When the pull request is merged, delete your working branch: `git branch -d ` +* When the pull request is merged, delete your working branch: `git branch -D ` +* Update your local and remote development branch as per the instructions above If there are changes introduced to the development branch that conflicts with an open pull request, you will have to resolve the conflicts and update the PR: * Fetch and merge into development any change from upstream/development as detailed above From 97585d55e1302d95710036c3941ba71baa0c8dfd Mon Sep 17 00:00:00 2001 From: user2684 Date: Wed, 31 May 2017 00:01:58 +0200 Subject: [PATCH 07/33] Move isMetric in a function and allow users to set it manually from the sketch #115 --- NodeManager.cpp | 40 ++++++++++++++++++++++++++++------------ NodeManager.h | 9 +++++++++ README.md | 7 +++++++ 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 4d379a61..6aeb15b4 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -466,7 +466,7 @@ void SensorThermistor::onLoop() { temperature += 1.0 / (_nominal_temperature + 273.15); // + (1/To) temperature = 1.0 / temperature; // Invert temperature -= 273.15; // convert to C - if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 Serial.print(F("THER I=")); Serial.print(_child_id); @@ -474,8 +474,6 @@ void SensorThermistor::onLoop() { Serial.print(adc); Serial.print(F(" T=")); Serial.print(temperature); - Serial.print(F(" M=")); - Serial.println(getControllerConfig().isMetric); #endif // store the value _value_float = temperature; @@ -1038,7 +1036,7 @@ void SensorDHT::onLoop() { // read the temperature float temperature = _dht->readTemperature(); // convert it - if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 Serial.print(F("DHT I=")); Serial.print(_child_id); @@ -1109,7 +1107,7 @@ void SensorSHT21::onLoop() { // read the temperature float temperature = SHT2x.GetTemperature(); // convert it - if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 Serial.print(F("SHT I=")); Serial.print(_child_id); @@ -1272,7 +1270,7 @@ void SensorDs18b20::onLoop() { // read the temperature float temperature = _sensors->getTempCByIndex(_index); // convert it - if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 Serial.print(F("DS18B20 I=")); Serial.print(_child_id); @@ -1376,7 +1374,7 @@ void SensorMLX90614::onSetup() { void SensorMLX90614::onLoop() { float temperature = _sensor_type == SensorMLX90614::TEMPERATURE_OBJECT ? _mlx->readAmbientTempC() : _mlx->readObjectTempC(); // convert it - if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 Serial.print(F("MLX I=")); Serial.print(_child_id); @@ -1573,7 +1571,7 @@ void SensorBME280::onLoop() { // read the temperature float temperature = _bme->readTemperature(); // convert it - if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 Serial.print(F("BME I=")); Serial.print(_child_id); @@ -1637,7 +1635,7 @@ void SensorBMP085::onLoop() { // read the temperature float temperature = _bmp->readTemperature(); // convert it - if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 Serial.print(F("BMP I=")); Serial.print(_child_id); @@ -1807,7 +1805,7 @@ void SensorHCSR04::onSetup() { // what to do during loop void SensorHCSR04::onLoop() { - int distance = getControllerConfig().isMetric ? _sonar->ping_cm() : _sonar->ping_in(); + int distance = _node_manager->getIsMetric() ? _sonar->ping_cm() : _sonar->ping_in(); #if DEBUG == 1 Serial.print(F("HC I=")); Serial.print(_child_id); @@ -1847,7 +1845,7 @@ void SensorMCP9808::onSetup() { void SensorMCP9808::onLoop() { float temperature = _mcp->readTempC(); // convert it - if (! getControllerConfig().isMetric) temperature = temperature * 1.8 + 32; + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 Serial.print(F("MCP I=")); Serial.print(_child_id); @@ -1951,6 +1949,22 @@ void NodeManager::setSleepBetweenSend(int value) { void NodeManager::setAck(bool value) { _ack = value; } +void NodeManager::setGetControllerConfig(bool value) { + _get_controller_config = value; +} +void NodeManager::setIsMetric(bool value) { + _is_metric = value; +} +bool NodeManager::getIsMetric() { + return _is_metric; +} + +// Convert a temperature from celsius to fahrenheit depending on how isMetric is set +float NodeManager::celsiusToFahrenheit(float temperature) { + if (_is_metric) return temperature; + // convert the temperature from C to F + return temperature * 1.8 + 32; +} // register a sensor to this manager int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { @@ -2283,11 +2297,13 @@ void NodeManager::presentation() { // setup NodeManager void NodeManager::setup() { + // retrieve and store isMetric from the controller + if (_get_controller_config) _is_metric = getControllerConfig().isMetric; #if DEBUG == 1 Serial.print(F("MY I=")); Serial.print(getNodeId()); Serial.print(F(" M=")); - Serial.println(getControllerConfig().isMetric); + Serial.println(_is_metric); #endif #if SERVICE_MESSAGES == 1 _send(_msg.set("STARTED")); diff --git a/NodeManager.h b/NodeManager.h index c6fa7c3a..c1ccddf0 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -1056,6 +1056,13 @@ class NodeManager { void setAck(bool value); // request and return the current timestamp from the controller long getTimestamp(); + // Request the controller's configuration on startup (default: true) + void setGetControllerConfig(bool value); + // Manually set isMetric setting + void setIsMetric(bool value); + bool getIsMetric(); + // Convert a temperature from celsius to fahrenheit depending on how isMetric is set + float celsiusToFahrenheit(float temperature); // hook into the main sketch functions void before(); void presentation(); @@ -1101,6 +1108,8 @@ class NodeManager { void _present(int child_id, int type); int _getAvailableChildId(); int _getInterruptInitialValue(int mode); + bool _get_controller_config = true; + int _is_metric = 1; }; #endif diff --git a/README.md b/README.md index 07ab3b01..a67ffbbc 100644 --- a/README.md +++ b/README.md @@ -256,6 +256,13 @@ Node Manager comes with a reasonable default configuration. If you want/need to void setAck(bool value); // request and return the current timestamp from the controller long getTimestamp(); + // Request the controller's configuration on startup (default: true) + void setGetControllerConfig(bool value); + // Manually set isMetric setting + void setIsMetric(bool value); + bool getIsMetric(); + // Convert a temperature from celsius to fahrenheit depending on how isMetric is set + float celsiusToFahrenheit(float temperature); ~~~ For example From 06032fd28f555caf3a1204d9a01ee85407337899 Mon Sep 17 00:00:00 2001 From: user2684 Date: Wed, 31 May 2017 22:57:20 +0200 Subject: [PATCH 08/33] 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 --- NodeManager.cpp | 397 +++++++++++++++++++++++++++++++++++++----------- NodeManager.h | 92 +++++++++-- NodeManager.ino | 2 +- README.md | 23 ++- 4 files changed, 410 insertions(+), 104 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 6aeb15b4..0002d32a 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -87,6 +87,113 @@ void PowerManager::powerOff() { 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 @@ -101,6 +208,8 @@ Sensor::Sensor(NodeManager* node_manager, int child_id, int pin) { _child_id = child_id; _pin = pin; _msg = MyMessage(_child_id, _type); + _report_timer = new Timer(_node_manager); + _force_update_timer = new Timer(_node_manager); } // setter/getter @@ -144,11 +253,17 @@ void Sensor::setSamples(int value) { void Sensor::setSamplesInterval(int value) { _samples_interval = value; } -void Sensor::setTackLastValue(bool value) { +void Sensor::setTrackLastValue(bool value) { _track_last_value = 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) { _value_type = value; @@ -192,6 +307,16 @@ char* Sensor::getValueString() { 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 void Sensor::presentation() { #if DEBUG == 1 @@ -218,6 +343,16 @@ void Sensor::setup() { // call the sensor-specific implementation of loop void Sensor::loop(const MyMessage & message) { 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 // turn the sensor on if (_auto_power_pins) powerOn(); @@ -225,31 +360,29 @@ void Sensor::loop(const MyMessage & message) { // 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 - if (_force_update > 0) _cycles++; // 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 - if (message.sender == 0 && message.sensor == 0 && message.getCommand() == 0 && message.type == 0) { - // empty message, we'be been called from loop() - onLoop(); - } - else { + if (_isReceive(message)) { // we've been called from receive(), pass the message along onReceive(message); } + else { + // we'be been called from loop() + onLoop(); + } // for integers and floats, keep track of the total if (_value_type == TYPE_INTEGER) total += (float)_value_int; else if (_value_type == TYPE_FLOAT) total += _value_float; // wait between samples 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 the value is an integer, calculate the average value of the 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 || (_track_last_value && avg != _last_value_int) || (_track_last_value && _force_update > 0 && _cycles > _force_update)) { - _cycles = 0; + if (_isReceive(message) || _isWorthSending(avg != _last_value_int)) { _last_value_int = avg; _send(_msg.set(avg)); } @@ -258,9 +391,8 @@ void Sensor::loop(const MyMessage & message) { else if (_value_type == TYPE_FLOAT && total > -1) { // calculate the average value of the 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 - if (! _track_last_value || (_track_last_value && avg != _last_value_float) || (_track_last_value && _cycles >= _force_update)) { - _cycles = 0; + // report the value back + if (_isReceive(message) || _isWorthSending(avg != _last_value_float)) { _last_value_float = avg; _send(_msg.set(avg, _float_precision)); } @@ -268,8 +400,7 @@ void Sensor::loop(const MyMessage & message) { // 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 - if (! _track_last_value || (_track_last_value && strcmp(_value_string, _last_value_string) != 0) || (_track_last_value && _cycles >= _force_update)) { - _cycles = 0; + if (_isReceive(message) || _isWorthSending(strcmp(_value_string, _last_value_string) != 0)) { _last_value_string = _value_string; _send(_msg.set(_value_string)); } @@ -278,6 +409,8 @@ void Sensor::loop(const MyMessage & message) { #if POWER_MANAGER == 1 if (_auto_power_pins) powerOff(); #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 @@ -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 */ @@ -598,7 +753,8 @@ 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 @@ -619,6 +775,8 @@ void SensorRainGauge::onBefore() { 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); } // what to do during setup @@ -643,24 +801,19 @@ void SensorRainGauge::_onTipped() { void SensorRainGauge::onLoop() { // avoid reporting the same value multiple times _value_float = -1; - long now = millis(); - // time elapsed since the last report - long elapsed = now - _last_report; - // 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)) { + _timer->update(); + // time to report + if (_timer->isOver()) { // report the total amount of rain for the last period - _value_float = _count*_single_tip; + _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 counters - _count = 0; - _last_report = now; + // reset the timer + _timer->restart(); } } @@ -668,7 +821,7 @@ void SensorRainGauge::onLoop() { 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; } } @@ -893,6 +1046,7 @@ void SensorDigitalInput::onReceive(const MyMessage & message) { // contructor 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 @@ -903,6 +1057,7 @@ void SensorDigitalOutput::onBefore() { digitalWrite(_pin, _state); // the initial value is now the current value _value_int = _initial_value; + // create the safeguard timer } // what to do during setup @@ -922,45 +1077,36 @@ void SensorDigitalOutput::setOnValue(int value) { void SensorDigitalOutput::setLegacyMode(bool 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 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 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 if ( (message.getCommand() == C_SET && ! _legacy_mode) || (message.getCommand() == C_REQ && _legacy_mode)) { - // retrieve from the message the value to set - int value = message.getInt(); - if (value != 0 && value != 1) return; - #if DEBUG == 1 - Serial.print(F("DOUT I=")); - Serial.print(_child_id); - Serial.print(F(" P=")); - Serial.print(_pin); - Serial.print(F(" V=")); - Serial.print(value); - Serial.print(F(" P=")); - Serial.println(_pulse_width); - #endif - // reverse the value if needed - int value_to_write = value; - if (_on_value == LOW) { - if (value == HIGH) value_to_write = LOW; - if (value == LOW) value_to_write = HIGH; - } - // set the value - digitalWrite(_pin, value_to_write); - if (_pulse_width > 0) { - // if this is a pulse output, restore the value to the original value after the pulse - wait(_pulse_width); - digitalWrite(_pin, value_to_write == 0 ? HIGH: LOW); - } - // store the current value so it will be sent to the controller - _state = value; - _value_int = value; + // switch the output + set(message.getInt()); } if (message.getCommand() == C_REQ && ! _legacy_mode) { // return the current status @@ -968,6 +1114,51 @@ void SensorDigitalOutput::onReceive(const MyMessage & message) { } } +// 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 + Serial.print(F("DOUT I=")); + Serial.print(_child_id); + Serial.print(F(" P=")); + Serial.print(_pin); + Serial.print(F(" V=")); + Serial.print(value); + Serial.print(F(" P=")); + Serial.println(_pulse_width); + #endif + // reverse the value if needed + int value_to_write = value; + if (_on_value == LOW) { + if (value == HIGH) value_to_write = LOW; + if (value == LOW) value_to_write = HIGH; + } + // set the value + digitalWrite(_pin, value_to_write); + if (_pulse_width > 0) { + // if this is a pulse output, restore the value to the original value after the pulse + wait(_pulse_width); + digitalWrite(_pin, value_to_write == 0 ? HIGH: LOW); + } + // store the current value so it will be sent to the controller + _state = value; + _value_int = value; +} + + /* SensorRelay */ @@ -978,13 +1169,14 @@ SensorRelay::SensorRelay(NodeManager* node_manager, int child_id, int pin): Sens setPresentation(S_BINARY); setType(V_STATUS); } - +/* // define what to do during loop void SensorRelay::onLoop() { // set the value to -1 so to avoid reporting to the gateway during loop _value_int = -1; + _last_value_int = -1; } - +*/ /* SensorLatchingRelay */ @@ -1884,7 +2076,10 @@ void NodeManager::setRetries(int value) { _battery_max = 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) { _battery_internal_vcc = value; @@ -1905,12 +2100,21 @@ void NodeManager::setSleepMode(int value) { void NodeManager::setMode(int value) { setSleepMode(value); } +int NodeManager::getMode() { + return _sleep_mode; +} void NodeManager::setSleepTime(int value) { _sleep_time = value; } +int NodeManager::getSleepTime() { + return _sleep_time; +} void NodeManager::setSleepUnit(int value) { _sleep_unit = value; } +int NodeManager::getSleepUnit() { + return _sleep_unit; +} void NodeManager::setSleep(int value1, int value2, int value3) { _sleep_mode = value1; _sleep_time = value2; @@ -1966,6 +2170,12 @@ float NodeManager::celsiusToFahrenheit(float temperature) { 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 int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { // get a child_id if not provided by the user @@ -2256,6 +2466,9 @@ void NodeManager::before() { #if BATTERY_MANAGER == 1 && !defined(MY_GATEWAY_ESP8266) // set analogReference to internal if measuring the battery through a pin 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 // setup individual sensors for (int i = 0; i < MAX_SENSORS; i++) { @@ -2323,25 +2536,39 @@ void NodeManager::loop() { 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 POWER_MANAGER == 1 - // turn on the pin powering all the sensors - if (_auto_power_pins) powerOn(); - #endif - // run loop for all the registered sensors - for (int i = 0; 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() - _sensors[i]->loop(empty); + #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 + _process("BATTERY"); + // restart the timer + _battery_report_timer.restart(); } - #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(); + #endif + #if POWER_MANAGER == 1 + // turn on the pin powering all the sensors + if (_auto_power_pins) powerOn(); + #endif + // run loop for all the registered sensors + for (int i = 0; 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() + _sensors[i]->loop(empty); + } + #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(); } // dispacth inbound messages @@ -2650,16 +2877,6 @@ void NodeManager::_sleep() { // notify the controller I am awake _send(_msg.set("AWAKE")); #endif - #if BATTERY_MANAGER == 1 - // keep track of the number of sleeping cycles (ignoring if woke up by an interrupt) - if (interrupt == -1 || _battery_report_with_interrupt) _cycles++; - // battery has to be reported after the configured number of sleep cycles - if (_battery_report_cycles == _cycles) { - // time to report the battery level again - _process("BATTERY"); - _cycles = 0; - } - #endif } // present the service diff --git a/NodeManager.h b/NodeManager.h index c1ccddf0..224e3e5f 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -24,6 +24,7 @@ #define MINUTES 1 #define HOURS 2 #define DAYS 3 +#define CYCLES 4 // define value type #define TYPE_INTEGER 0 @@ -314,9 +315,13 @@ class PowerManager { PowerManager() {}; // 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); + // turns the power pins on void powerOn(); + // turns the power pins on void powerOff(); + // returns the Vcc voltge float getVcc(); + // turns true if power pins are configured bool isConfigured(); private: int _vcc_pin = -1; @@ -324,7 +329,47 @@ class PowerManager { long _wait = 0; }; +/* + Timer +*/ +class Timer { + public: + Timer(NodeManager* node_manager); + // start the timer which will be over when interval passes by. Unit can be either CYCLES or MINUTES + void start(long target, int unit); + void start(); + // stop the timer + void stop(); + // set the timer configuration but do not start it + void set(long target, int unit); + // update the timer. To be called at every cycle + void update(); + // returns true if the time is over + bool isOver(); + // return true if the timer is running + bool isRunning(); + // returns true if the timer has been configured + bool isConfigured(); + // reset the timer and start over + void restart(); + // return the current elapsed time + float getElapsed(); + // return the configured unit + int getUnit(); + // return the configured target + int getTarget(); + private: + NodeManager* _node_manager; + long _target = 0; + int _unit = 0; + float _elapsed = 0; + bool _use_millis = false; + long _last_millis = 0; + float _sleep_time = 0; + bool _is_running = false; + bool _is_configured = false; +}; /*************************************** Sensor: generic sensor class */ @@ -353,10 +398,13 @@ class Sensor { void setSamples(int value); // If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) void setSamplesInterval(int value); - // if true will report the measure only if different then the previous one (default: false) - void setTackLastValue(bool value); + // if true will report the measure only if different than the previous one (default: false) + void setTrackLastValue(bool value); // if track last value is enabled, force to send an update after the configured number of cycles (default: -1) void setForceUpdate(int value); + void setForceUpdateCycles(int value); + // if track last value is enabled, force to send an update after the configured number of minutes (default: -1) + void setForceUpdateMinutes(int value); // the value type of this sensor (default: TYPE_INTEGER) void setValueType(int value); int getValueType(); @@ -381,6 +429,10 @@ class Sensor { int getValueInt(); float getValueFloat(); char* getValueString(); + // After how many cycles the sensor will report back its measure (default: 1 cycle) + void setReportIntervalCycles(int value); + // After how many minutes the sensor will report back its measure (default: 1 cycle) + void setReportIntervalMinutes(int value); // define what to do at each stage of the sketch virtual void before(); virtual void presentation(); @@ -406,8 +458,6 @@ class Sensor { int _samples = 1; int _samples_interval = 0; bool _track_last_value = false; - int _cycles = 0; - int _force_update = -1; int _value_type = TYPE_INTEGER; int _float_precision = 2; int _value_int = -1; @@ -421,7 +471,11 @@ class Sensor { PowerManager _powerManager; bool _auto_power_pins = true; #endif + Timer* _report_timer; + Timer* _force_update_timer; void _send(MyMessage & msg); + bool _isReceive(const MyMessage & message); + bool _isWorthSending(bool comparison); }; /* @@ -591,9 +645,9 @@ class SensorACS712: public Sensor { class SensorRainGauge: public Sensor { public: SensorRainGauge(NodeManager* node_manager, int child_id, int pin); - // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60); + // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60) void setReportInterval(int value); - // set how many mm of rain to count for each tip (default: 0.11); + // set how many mm of rain to count for each tip (default: 0.11) void setSingleTip(float value); // define what to do at each stage of the sketch void onBefore(); @@ -607,7 +661,7 @@ class SensorRainGauge: public Sensor { protected: int _report_interval = 60; float _single_tip = 0.11; - long _last_report = 0; + Timer* _timer; }; /* @@ -653,6 +707,14 @@ class SensorDigitalOutput: public Sensor { void setOnValue(int value); // when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) void setLegacyMode(bool value); + // automatically turn the output off after the given number of minutes + void setSafeguard(int value); + // if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) + void setInputIsElapsed(bool value); + // manually switch the output to the provided value + void set(int value); + // get the current state + int getState(); // define what to do at each stage of the sketch void onBefore(); void onSetup(); @@ -664,6 +726,8 @@ class SensorDigitalOutput: public Sensor { int _state = 0; int _pulse_width = 0; bool _legacy_mode = false; + bool _input_is_elapsed = false; + Timer* _safeguard_timer; }; @@ -674,7 +738,7 @@ class SensorRelay: public SensorDigitalOutput { public: SensorRelay(NodeManager* node_manager, int child_id, int pin); // define what to do at each stage of the sketch - void onLoop(); + //void onLoop(); }; /* @@ -1005,8 +1069,10 @@ class NodeManager { void setBatteryMin(float value); // the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: 10) + // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) void setBatteryReportCycles(int value); + // after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) + void setBatteryReportMinutes(int value); // if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) void setBatteryInternalVcc(bool value); // if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) @@ -1019,10 +1085,13 @@ class NodeManager { // define the way the node should behave. It can be IDLE (stay awake withtout executing each sensors' loop), SLEEP (go to sleep for the configured interval), WAIT (wait for the configured interval), ALWAYS_ON (stay awake and execute each sensors' loop) void setSleepMode(int value); void setMode(int value); + int getMode(); // define for how long the board will sleep (default: 0) void setSleepTime(int value); + int getSleepTime(); // define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) void setSleepUnit(int value); + int getSleepUnit(); // configure the node's behavior, parameters are mode, time and unit void setSleep(int value1, int value2, int value3); // if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true) @@ -1063,6 +1132,8 @@ class NodeManager { bool getIsMetric(); // Convert a temperature from celsius to fahrenheit depending on how isMetric is set float celsiusToFahrenheit(float temperature); + // return true if sleep or wait is configured and hence this is a sleeping node + bool isSleepingNode(); // hook into the main sketch functions void before(); void presentation(); @@ -1074,12 +1145,11 @@ class NodeManager { #if BATTERY_MANAGER == 1 float _battery_min = 2.6; float _battery_max = 3.3; - int _battery_report_cycles = 10; + Timer _battery_report_timer = Timer(this); bool _battery_report_with_interrupt = true; bool _battery_internal_vcc = true; int _battery_pin = -1; float _battery_volts_per_bit = 0.003363075; - int _cycles = 0; float _getVcc(); #endif #if POWER_MANAGER == 1 diff --git a/NodeManager.ino b/NodeManager.ino index 6422f437..4e6def9f 100644 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -36,6 +36,7 @@ void before() { + /* * Register above your sensors */ @@ -58,7 +59,6 @@ void setup() { void loop() { // call NodeManager loop routine nodeManager.loop(); - } // receive diff --git a/README.md b/README.md index a67ffbbc..f9c299d3 100644 --- a/README.md +++ b/README.md @@ -202,8 +202,10 @@ Node Manager comes with a reasonable default configuration. If you want/need to void setBatteryMin(float value); // the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: 10) + // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) void setBatteryReportCycles(int value); + // after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) + void setBatteryReportMinutes(int value); // if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) void setBatteryInternalVcc(bool value); // if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) @@ -263,6 +265,8 @@ Node Manager comes with a reasonable default configuration. If you want/need to bool getIsMetric(); // Convert a temperature from celsius to fahrenheit depending on how isMetric is set float celsiusToFahrenheit(float temperature); + // return true if sleep or wait is configured and hence this is a sleeping node + bool isSleepingNode(); ~~~ For example @@ -371,9 +375,12 @@ The following methods are available for all the sensors: // If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) void setSamplesInterval(int value); // if true will report the measure only if different then the previous one (default: false) - void setTackLastValue(bool value); + void setTrackLastValue(bool value); // if track last value is enabled, force to send an update after the configured number of cycles (default: -1) void setForceUpdate(int value); + void setForceUpdateCycles(int value); + // if track last value is enabled, force to send an update after the configured number of minutes (default: -1) + void setForceUpdateMinutes(int value); // the value type of this sensor (default: TYPE_INTEGER) void setValueType(int value); int getValueType(); @@ -398,6 +405,10 @@ The following methods are available for all the sensors: int getValueInt(); float getValueFloat(); char* getValueString(); + // After how many cycles the sensor will report back its measure (default: 1 cycle) + void setReportIntervalCycles(int value); + // After how many minutes the sensor will report back its measure (default: 1 cycle) + void setReportIntervalMinutes(int value); ~~~ #### Sensor's specific configuration @@ -468,6 +479,14 @@ Each sensor class can expose additional methods. void setOnValue(int value); // when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) void setLegacyMode(bool value); + // automatically turn the output off after the given number of minutes + void setSafeguard(int value); + // if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) + void setInputIsElapsed(bool value); + // manually switch the output to the provided value + void set(int value); + // get the current state + int getState(); ~~~ * SensorSwitch / SensorDoor / SensorMotion From 34e073fc008462d698b3a9af5c39af8cff1ba6bf Mon Sep 17 00:00:00 2001 From: user2684 Date: Thu, 1 Jun 2017 17:01:46 +0200 Subject: [PATCH 09/33] Enhanced remote API #102 #20 #28 #53 #78 --- NodeManager.cpp | 1073 +++++++++++++++++++++++++++++------------------ NodeManager.h | 357 ++++++++++------ NodeManager.ino | 10 +- README.md | 328 ++++++++------- config.h | 12 +- 5 files changed, 1085 insertions(+), 695 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 0002d32a..5e5e6117 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -195,6 +195,53 @@ int Timer::getUnit() { return _unit; } + +/****************************************** + Request +*/ + +Request::Request(const char* string) { + char str[10]; + char* ptr; + strcpy(str,string); + // tokenize the string and split function from value + strtok_r(str,",",&ptr); + _function = atoi(str); + strcpy(_value,ptr); + #if DEBUG == 1 + Serial.print(F("REQ F=")); + Serial.print(getFunction()); + Serial.print(F(" I=")); + Serial.print(getValueInt()); + Serial.print(F(" F=")); + Serial.print(getValueFloat()); + Serial.print(F(" S=")); + Serial.println(getValueString()); + #endif +} + +// return the parsed function +int Request::getFunction() { + return _function; +} + +// return the value as an int +int Request::getValueInt() { + return atoi(_value); + +} + +// return the value as a float +float Request::getValueFloat() { + return atof(_value); +} + +// return the value as a string +char* Request::getValueString() { + return _value; +} + + /****************************************** Sensors */ @@ -208,6 +255,7 @@ Sensor::Sensor(NodeManager* node_manager, int child_id, int pin) { _child_id = child_id; _pin = pin; _msg = MyMessage(_child_id, _type); + _msg_service = MyMessage(_child_id, V_CUSTOM); _report_timer = new Timer(_node_manager); _force_update_timer = new Timer(_node_manager); } @@ -416,11 +464,50 @@ void Sensor::loop(const MyMessage & message) { // receive a message from the radio network void Sensor::receive(const MyMessage &message) { // return if not for this sensor - if (message.sensor != _child_id || message.type != _type) return; + if (message.sensor != _child_id) return; + // check if it is a request for the API + if (message.getCommand() == C_REQ && message.type == V_CUSTOM) { + #if REMOTE_CONFIGURATION == 1 + // parse the request + Request request = Request(message.getString()); + // if it is for a sensor-generic function, call process(), otherwise the sensor-specific onProcess(); + if (request.getFunction() < 100) process(request); + else onProcess(request); + #endif + } + // return if the type is not correct + if (message.type != _type) return; // a request would make the sensor executing its main task passing along the message loop(message); } +// process a remote configuration request message +void Sensor::process(Request & request) { + int function = request.getFunction(); + switch(function) { + case 1: setPin(request.getValueInt()); break; + case 2: setChildId(request.getValueInt()); break; + case 3: setType(request.getValueInt()); break; + case 4: setDescription(request.getValueString()); break; + case 5: setSamples(request.getValueInt()); break; + case 6: setSamplesInterval(request.getValueInt()); break; + case 7: setTrackLastValue(request.getValueInt()); break; + case 8: setForceUpdateCycles(request.getValueInt()); break; + case 9: setForceUpdateMinutes(request.getValueInt()); break; + case 10: setValueType(request.getValueInt()); break; + case 11: setFloatPrecision(request.getValueInt()); break; + #if POWER_MANAGER == 1 + case 12: setAutoPowerPins(request.getValueInt()); break; + case 13: powerOn(); break; + case 14: powerOff(); break; + #endif + case 15: setReportIntervalCycles(request.getValueInt()); break; + case 16: setReportIntervalMinutes(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + // send a message to the network void Sensor::_send(MyMessage & message) { // send the message, multiple times if requested @@ -468,7 +555,7 @@ bool Sensor::_isWorthSending(bool comparison) { return false; } - +#if MODULE_ANALOG_INPUT == 1 /* SensorAnalogInput */ @@ -528,6 +615,20 @@ void SensorAnalogInput::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorAnalogInput::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setReference(request.getValueInt()); break; + case 102: setReverse(request.getValueInt()); break; + case 103: setOutputPercentage(request.getValueInt()); break; + case 104: setRangeMin(request.getValueInt()); break; + case 105: setRangeMax(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + // read the analog input int SensorAnalogInput::_getAnalogRead() { #ifndef MY_GATEWAY_ESP8266 @@ -639,6 +740,19 @@ void SensorThermistor::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorThermistor::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setNominalResistor((long)request.getValueInt()); break; + case 102: setNominalTemperature(request.getValueInt()); break; + case 103: setBCoefficient(request.getValueInt()); break; + case 104: setSeriesResistor((long)request.getValueString()); break; + case 105: setOffset(request.getValueFloat()); break; + default: return; + } + _send(_msg_service.set(function)); +} /* SensorML8511 @@ -688,6 +802,10 @@ void SensorML8511::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorML8511::onProcess(Request & request) { +} + // 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; @@ -743,6 +861,17 @@ void SensorACS712::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorACS712::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 100: setmVPerAmp(request.getValueInt()); break; + case 102: setOffset(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + /* SensorRainGauge */ @@ -825,6 +954,17 @@ void SensorRainGauge::onReceive(const MyMessage & message) { } } +// what to do when receiving a remote 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)); +} + /* SensorRain */ @@ -855,151 +995,9 @@ SensorSoilMoisture::SensorSoilMoisture(NodeManager* node_manager, int child_id, setRangeMin(100); } +#endif -/* - * SensorMQ - */ -SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { - setPresentation(S_AIR_QUALITY); - setType(V_LEVEL); -} - -//setter/getter -void SensorMQ::setRlValue(float value) { - _rl_value = value; -} -void SensorMQ::setRoValue(float value) { - _ro = value; -} -void SensorMQ::setCleanAirFactor(float value) { - _ro_clean_air_factor = value; -} -void SensorMQ::setCalibrationSampleTimes(int value) { - _calibration_sample_times = value; -} -void SensorMQ::setCalibrationSampleInterval(int value){ - _calibration_sample_interval = value; -} -void SensorMQ::setReadSampleTimes(int value) { - _read_sample_times = value; -} -void SensorMQ::setReadSampleInterval(int value) { - _read_sample_interval = value; -} -void SensorMQ::setLPGCurve(float *value) { - _LPGCurve[0] = value[0]; - _LPGCurve[2] = value[1]; - _LPGCurve[2] = value[2]; -} -void SensorMQ::setCOCurve(float *value) { - _COCurve[0] = value[0]; - _COCurve[2] = value[1]; - _COCurve[2] = value[2]; -} -void SensorMQ::setSmokeCurve(float *value) { - _SmokeCurve[0] = value[0]; - _SmokeCurve[2] = value[1]; - _SmokeCurve[2] = value[2]; -} - -// what to do during before -void SensorMQ::onBefore() { - // prepare the pin for input - pinMode(_pin, INPUT); -} - -// what to do during setup -void SensorMQ::onSetup() { - _ro = _MQCalibration(); -} - -// what to do during loop -void SensorMQ::onLoop() { - if (_pin == -1) return; - // calculate rs/ro - float mq = _MQRead()/_ro; - // calculate the ppm - float lpg = _MQGetGasPercentage(mq,_gas_lpg); - float co = _MQGetGasPercentage(mq,_gas_co); - float smoke = _MQGetGasPercentage(mq,_gas_smoke); - // assign to the value the requested gas - uint16_t value; - if (_target_gas == _gas_lpg) value = lpg; - if (_target_gas == _gas_co) value = co; - if (_target_gas == _gas_smoke) value = smoke; - #if DEBUG == 1 - Serial.print(F("MQ I=")); - Serial.print(_child_id); - Serial.print(F(" V=")); - Serial.print(value); - Serial.print(F(" LPG=")); - Serial.print(lpg); - Serial.print(F(" CO=")); - Serial.print(co); - Serial.print(F(" SMOKE=")); - Serial.println(smoke); - #endif - // store the value - _value_int = (int16_t)ceil(value); -} - -// what to do as the main task when receiving a message -void SensorMQ::onReceive(const MyMessage & message) { - if (message.getCommand() == C_REQ) onLoop(); -} - -// returns the calculated sensor resistance -float SensorMQ::_MQResistanceCalculation(int raw_adc) { - return ( ((float)_rl_value*(1023-raw_adc)/raw_adc)); -} - -// This function assumes that the sensor is in clean air -float SensorMQ::_MQCalibration() { - int i; - float val=0; - //take multiple samples - for (i=0; i< _calibration_sample_times; i++) { - val += _MQResistanceCalculation(analogRead(_pin)); - wait(_calibration_sample_interval); - } - //calculate the average value - val = val/_calibration_sample_times; - //divided by RO_CLEAN_AIR_FACTOR yields the Ro - val = val/_ro_clean_air_factor; - //according to the chart in the datasheet - return val; -} - -// This function use MQResistanceCalculation to caculate the sensor resistenc (Rs). -float SensorMQ::_MQRead() { - int i; - float rs=0; - for (i=0; i<_read_sample_times; i++) { - rs += _MQResistanceCalculation(analogRead(_pin)); - wait(_read_sample_interval); - } - rs = rs/_read_sample_times; - return rs; -} - -// This function passes different curves to the MQGetPercentage function which calculates the ppm (parts per million) of the target gas. -int SensorMQ::_MQGetGasPercentage(float rs_ro_ratio, int gas_id) { - if ( gas_id == _gas_lpg ) { - return _MQGetPercentage(rs_ro_ratio,_LPGCurve); - } else if ( gas_id == _gas_co) { - return _MQGetPercentage(rs_ro_ratio,_COCurve); - } else if ( gas_id == _gas_smoke) { - return _MQGetPercentage(rs_ro_ratio,_SmokeCurve); - } - return 0; -} - -// returns ppm of the target gas -int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) { - return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0]))); -} - - +#if MODULE_DIGITAL_INPUT == 1 /* SensorDigitalInput */ @@ -1039,12 +1037,16 @@ void SensorDigitalInput::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorDigitalInput::onProcess(Request & request) { +} +#endif +#if MODULE_DIGITAL_OUTPUT == 1 /* SensorDigitalOutput */ -// contructor SensorDigitalOutput::SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) { _safeguard_timer = new Timer(node_manager); } @@ -1114,6 +1116,21 @@ void SensorDigitalOutput::onReceive(const MyMessage & message) { } } +// what to do when receiving a remote message +void SensorDigitalOutput::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setInitialValue(request.getValueInt()); break; + case 102: setPulseWidth(request.getValueInt()); break; + case 103: setOnValue(request.getValueInt()); break; + case 104: setLegacyMode(request.getValueInt()); break; + case 105: setSafeguard(request.getValueInt()); break; + case 106: setInputIsElapsed(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + // write the value to the output void SensorDigitalOutput::set(int value) { if (_input_is_elapsed) { @@ -1169,14 +1186,7 @@ SensorRelay::SensorRelay(NodeManager* node_manager, int child_id, int pin): Sens setPresentation(S_BINARY); setType(V_STATUS); } -/* -// define what to do during loop -void SensorRelay::onLoop() { - // set the value to -1 so to avoid reporting to the gateway during loop - _value_int = -1; - _last_value_int = -1; -} -*/ + /* SensorLatchingRelay */ @@ -1187,6 +1197,7 @@ SensorLatchingRelay::SensorLatchingRelay(NodeManager* node_manager, int child_id setPulseWidth(50); } +#endif /* SensorDHT */ @@ -1258,6 +1269,10 @@ void SensorDHT::onLoop() { void SensorDHT::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorDHT::onProcess(Request & request) { +} #endif /* @@ -1329,6 +1344,10 @@ void SensorSHT21::onLoop() { void SensorSHT21::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorSHT21::onProcess(Request & request) { +} #endif /* @@ -1340,6 +1359,7 @@ SensorHTU21D::SensorHTU21D(NodeManager* node_manager, int child_id, int pin): Se } #endif +#if MODULE_SWITCH == 1 /* * SensorSwitch */ @@ -1407,6 +1427,19 @@ 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 */ @@ -1424,6 +1457,7 @@ SensorMotion::SensorMotion(NodeManager* node_manager, int child_id, int pin): Se // set initial value to LOW setInitial(LOW); } +#endif /* SensorDs18b20 @@ -1478,6 +1512,17 @@ void SensorDs18b20::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorDs18b20::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setResolution(request.getValueInt()); break; + case 102: setSleepDuringConversion(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + // function to print a device address DeviceAddress* SensorDs18b20::getDeviceAddress() { return &_device_address; @@ -1536,6 +1581,10 @@ void SensorBH1750::onLoop() { void SensorBH1750::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorBH1750::onProcess(Request & request) { +} #endif /* @@ -1580,6 +1629,10 @@ void SensorMLX90614::onLoop() { void SensorMLX90614::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorMLX90614::onProcess(Request & request) { +} #endif @@ -1640,7 +1693,17 @@ void SensorBosch::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorBosch::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setForecastSamplesCount(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} +// calculate and send the forecast back void SensorBosch::_forecast(float pressure) { if (isnan(pressure)) return; // Calculate the average of the last n minutes. @@ -1860,35 +1923,98 @@ void SensorBMP085::onLoop() { } #endif - /* - SensorSonoff + SensorHCSR04 */ -#if MODULE_SONOFF == 1 +#if MODULE_HCSR04 == 1 // contructor -SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,1) { - setPresentation(S_BINARY); - setType(V_STATUS); +SensorHCSR04::SensorHCSR04(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { + // set presentation and type + setPresentation(S_DISTANCE); + setType(V_DISTANCE); + _trigger_pin = pin; + _echo_pin = pin; +} + +// what to do during before +void SensorHCSR04::onBefore() { + // initialize the library + _sonar = new NewPing(_trigger_pin,_echo_pin,_max_distance); } // setter/getter -void SensorSonoff::setButtonPin(int value) { - _button_pin = value; +void SensorHCSR04::setTriggerPin(int value) { + _trigger_pin = value; } -void SensorSonoff::setRelayPin(int value) { - _relay_pin = value; +void SensorHCSR04::setEchoPin(int value) { + _echo_pin = value; } -void SensorSonoff::setLedPin(int value) { - _led_pin = value; +void SensorHCSR04::setMaxDistance(int value) { + _max_distance = value; } -// what to do during before -void SensorSonoff::onBefore() { +// what to do during setup +void SensorHCSR04::onSetup() { } -// what to do during setup -void SensorSonoff::onSetup() { - // Setup the button +// what to do during loop +void SensorHCSR04::onLoop() { + int distance = _node_manager->getIsMetric() ? _sonar->ping_cm() : _sonar->ping_in(); + #if DEBUG == 1 + Serial.print(F("HC I=")); + Serial.print(_child_id); + Serial.print(F(" D=")); + Serial.println(distance); + #endif + _value_int = distance; +} + +// what to do as the main task when receiving a message +void SensorHCSR04::onReceive(const MyMessage & message) { + if (message.getCommand() == C_REQ) onLoop(); +} + +// what to do when receiving a remote message +void SensorHCSR04::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setTriggerPin(request.getValueInt()); break; + case 102: setEchoPin(request.getValueInt()); break; + case 103: setMaxDistance(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} +#endif + +/* + SensorSonoff +*/ +#if MODULE_SONOFF == 1 +// contructor +SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,1) { + setPresentation(S_BINARY); + setType(V_STATUS); +} + +// setter/getter +void SensorSonoff::setButtonPin(int value) { + _button_pin = value; +} +void SensorSonoff::setRelayPin(int value) { + _relay_pin = value; +} +void SensorSonoff::setLedPin(int value) { + _led_pin = value; +} + +// what to do during before +void SensorSonoff::onBefore() { +} + +// what to do during setup +void SensorSonoff::onSetup() { + // Setup the button pinMode(_button_pin, INPUT_PULLUP); // After setting up the button, setup debouncer _debouncer.attach(_button_pin); @@ -1931,6 +2057,18 @@ void SensorSonoff::onReceive(const MyMessage & message) { } } +// what to do when receiving a remote message +void SensorSonoff::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setButtonPin(request.getValueInt()); break; + case 102: setRelayPin(request.getValueInt()); break; + case 103: setLedPin(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + // toggle the state void SensorSonoff::_toggle() { // toggle the state @@ -1962,96 +2100,211 @@ void SensorSonoff::_blink() { /* - SensorHCSR04 + SensorMCP9808 */ -#if MODULE_HCSR04 == 1 +#if MODULE_MCP9808 == 1 // contructor -SensorHCSR04::SensorHCSR04(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { - // set presentation and type - setPresentation(S_DISTANCE); - setType(V_DISTANCE); - _trigger_pin = pin; - _echo_pin = pin; +SensorMCP9808::SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp): Sensor(node_manager, child_id,A2) { + _mcp = mcp; + setPresentation(S_TEMP); + setType(V_TEMP); + setValueType(TYPE_FLOAT); } // what to do during before -void SensorHCSR04::onBefore() { - // initialize the library - _sonar = new NewPing(_trigger_pin,_echo_pin,_max_distance); -} - -// setter/getter -void SensorHCSR04::setTriggerPin(int value) { - _trigger_pin = value; -} -void SensorHCSR04::setEchoPin(int value) { - _echo_pin = value; -} -void SensorHCSR04::setMaxDistance(int value) { - _max_distance = value; +void SensorMCP9808::onBefore() { } // what to do during setup -void SensorHCSR04::onSetup() { +void SensorMCP9808::onSetup() { } // what to do during loop -void SensorHCSR04::onLoop() { - int distance = _node_manager->getIsMetric() ? _sonar->ping_cm() : _sonar->ping_in(); +void SensorMCP9808::onLoop() { + float temperature = _mcp->readTempC(); + // convert it + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 - Serial.print(F("HC I=")); + Serial.print(F("MCP I=")); Serial.print(_child_id); - Serial.print(F(" D=")); - Serial.println(distance); + Serial.print(F(" T=")); + Serial.println(temperature); #endif - _value_int = distance; + // store the value + if (! isnan(temperature)) _value_float = temperature; } // what to do as the main task when receiving a message -void SensorHCSR04::onReceive(const MyMessage & message) { +void SensorMCP9808::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorMCP9808::onProcess(Request & request) { +} #endif + /* - SensorMCP9808 -*/ -#if MODULE_MCP9808 == 1 -// contructor -SensorMCP9808::SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp): Sensor(node_manager, child_id,A2) { - _mcp = mcp; - setPresentation(S_TEMP); - setType(V_TEMP); - setValueType(TYPE_FLOAT); + * SensorMQ + */ +#if MODULE_MQ == 1 +SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { + setPresentation(S_AIR_QUALITY); + setType(V_LEVEL); +} + +//setter/getter +void SensorMQ::setRlValue(float value) { + _rl_value = value; +} +void SensorMQ::setRoValue(float value) { + _ro = value; +} +void SensorMQ::setCleanAirFactor(float value) { + _ro_clean_air_factor = value; +} +void SensorMQ::setCalibrationSampleTimes(int value) { + _calibration_sample_times = value; +} +void SensorMQ::setCalibrationSampleInterval(int value){ + _calibration_sample_interval = value; +} +void SensorMQ::setReadSampleTimes(int value) { + _read_sample_times = value; +} +void SensorMQ::setReadSampleInterval(int value) { + _read_sample_interval = value; +} +void SensorMQ::setLPGCurve(float *value) { + _LPGCurve[0] = value[0]; + _LPGCurve[2] = value[1]; + _LPGCurve[2] = value[2]; +} +void SensorMQ::setCOCurve(float *value) { + _COCurve[0] = value[0]; + _COCurve[2] = value[1]; + _COCurve[2] = value[2]; +} +void SensorMQ::setSmokeCurve(float *value) { + _SmokeCurve[0] = value[0]; + _SmokeCurve[2] = value[1]; + _SmokeCurve[2] = value[2]; } // what to do during before -void SensorMCP9808::onBefore() { +void SensorMQ::onBefore() { + // prepare the pin for input + pinMode(_pin, INPUT); } // what to do during setup -void SensorMCP9808::onSetup() { +void SensorMQ::onSetup() { + _ro = _MQCalibration(); } // what to do during loop -void SensorMCP9808::onLoop() { - float temperature = _mcp->readTempC(); - // convert it - temperature = _node_manager->celsiusToFahrenheit(temperature); +void SensorMQ::onLoop() { + if (_pin == -1) return; + // calculate rs/ro + float mq = _MQRead()/_ro; + // calculate the ppm + float lpg = _MQGetGasPercentage(mq,_gas_lpg); + float co = _MQGetGasPercentage(mq,_gas_co); + float smoke = _MQGetGasPercentage(mq,_gas_smoke); + // assign to the value the requested gas + uint16_t value; + if (_target_gas == _gas_lpg) value = lpg; + if (_target_gas == _gas_co) value = co; + if (_target_gas == _gas_smoke) value = smoke; #if DEBUG == 1 - Serial.print(F("MCP I=")); + Serial.print(F("MQ I=")); Serial.print(_child_id); - Serial.print(F(" T=")); - Serial.println(temperature); + Serial.print(F(" V=")); + Serial.print(value); + Serial.print(F(" LPG=")); + Serial.print(lpg); + Serial.print(F(" CO=")); + Serial.print(co); + Serial.print(F(" SMOKE=")); + Serial.println(smoke); #endif // store the value - if (! isnan(temperature)) _value_float = temperature; + _value_int = (int16_t)ceil(value); } // what to do as the main task when receiving a message -void SensorMCP9808::onReceive(const MyMessage & message) { +void SensorMQ::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorMQ::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 1: setTargetGas(request.getValueInt()); break; + case 2: setRlValue(request.getValueFloat()); break; + case 3: setRoValue(request.getValueFloat()); break; + case 4: setCleanAirFactor(request.getValueFloat()); break; + case 5: setCalibrationSampleTimes(request.getValueInt()); break; + case 6: setCalibrationSampleInterval(request.getValueInt()); break; + case 7: setReadSampleTimes(request.getValueInt()); break; + case 8: setReadSampleInterval(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + +// returns the calculated sensor resistance +float SensorMQ::_MQResistanceCalculation(int raw_adc) { + return ( ((float)_rl_value*(1023-raw_adc)/raw_adc)); +} + +// This function assumes that the sensor is in clean air +float SensorMQ::_MQCalibration() { + int i; + float val=0; + //take multiple samples + for (i=0; i< _calibration_sample_times; i++) { + val += _MQResistanceCalculation(analogRead(_pin)); + wait(_calibration_sample_interval); + } + //calculate the average value + val = val/_calibration_sample_times; + //divided by RO_CLEAN_AIR_FACTOR yields the Ro + val = val/_ro_clean_air_factor; + //according to the chart in the datasheet + return val; +} + +// This function use MQResistanceCalculation to caculate the sensor resistenc (Rs). +float SensorMQ::_MQRead() { + int i; + float rs=0; + for (i=0; i<_read_sample_times; i++) { + rs += _MQResistanceCalculation(analogRead(_pin)); + wait(_read_sample_interval); + } + rs = rs/_read_sample_times; + return rs; +} + +// This function passes different curves to the MQGetPercentage function which calculates the ppm (parts per million) of the target gas. +int SensorMQ::_MQGetGasPercentage(float rs_ro_ratio, int gas_id) { + if ( gas_id == _gas_lpg ) { + return _MQGetPercentage(rs_ro_ratio,_LPGCurve); + } else if ( gas_id == _gas_co) { + return _MQGetPercentage(rs_ro_ratio,_COCurve); + } else if ( gas_id == _gas_smoke) { + return _MQGetPercentage(rs_ro_ratio,_SmokeCurve); + } + return 0; +} + +// returns ppm of the target gas +int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) { + return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0]))); +} #endif /******************************************* @@ -2116,9 +2369,9 @@ int NodeManager::getSleepUnit() { return _sleep_unit; } void NodeManager::setSleep(int value1, int value2, int value3) { - _sleep_mode = value1; - _sleep_time = value2; - _sleep_unit = value3; + setMode(value1); + setSleepTime(value2); + setSleepUnit(value3); } void NodeManager::setSleepInterruptPin(int value) { _sleep_interrupt_pin = value; @@ -2186,7 +2439,6 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { else if (sensor_type == SENSOR_ANALOG_INPUT) return registerSensor(new SensorAnalogInput(this,child_id, pin)); else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(this,child_id, pin)); else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(this,child_id, pin)); - else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(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)); @@ -2339,6 +2591,11 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { registerSensor(new SensorMCP9808(this,child_id,mcp)); } #endif + #if MODULE_MQ == 1 + else if (sensor_type == SENSOR_MQ) { + return registerSensor(new SensorMQ(this,child_id, pin)); + } + #endif else { #if DEBUG == 1 Serial.print(F("INVALID ")); @@ -2442,26 +2699,9 @@ void NodeManager::before() { Serial.print(F("INT2 M=")); Serial.println(_interrupt_2_mode); #endif - #if REMOTE_CONFIGURATION == 1 && PERSIST == 1 - // restore sleep configuration from eeprom - if (loadState(EEPROM_SLEEP_SAVED) == 1) { - // sleep settings found in the eeprom, restore them - _sleep_mode = loadState(EEPROM_SLEEP_MODE); - _sleep_time = loadState(EEPROM_SLEEP_TIME_MINOR); - int major = loadState(EEPROM_SLEEP_TIME_MAJOR); - if (major == 1) _sleep_time = _sleep_time + 250; - else if (major == 2) _sleep_time = _sleep_time + 250 * 2; - else if (major == 3) _sleep_time = _sleep_time + 250 * 3; - _sleep_unit = loadState(EEPROM_SLEEP_UNIT); - #if DEBUG == 1 - Serial.print(F("LOADSLP M=")); - Serial.print(_sleep_mode); - Serial.print(F(" T=")); - Serial.print(_sleep_time); - Serial.print(F(" U=")); - Serial.println(_sleep_unit); - #endif - } + #if PERSIST == 1 + // restore the configuration saved in the eeprom + _loadConfig(); #endif #if BATTERY_MANAGER == 1 && !defined(MY_GATEWAY_ESP8266) // set analogReference to internal if measuring the battery through a pin @@ -2492,7 +2732,7 @@ void NodeManager::presentation() { // present the battery service _present(BATTERY_CHILD_ID, S_MULTIMETER); // report battery level - _process("BATTERY"); + batteryReport(); #endif // present each sensor for (int i = 0; i < MAX_SENSORS; i++) { @@ -2545,7 +2785,7 @@ void NodeManager::loop() { // if it is time to report the battery level if (_battery_report_timer.isOver()) { // time to report the battery level again - _process("BATTERY"); + batteryReport(); // restart the timer _battery_report_timer.restart(); } @@ -2585,9 +2825,14 @@ void NodeManager::receive(const MyMessage &message) { Serial.print(F(" P=")); Serial.println(message.getString()); #endif - // process incoming service messages + // process incoming configuration message if (message.sensor == CONFIGURATION_CHILD_ID && message.getCommand() == C_REQ && message.type == V_CUSTOM) { - _process(message.getString()); + #if REMOTE_CONFIGURATION == 1 + // parse the request + Request request = Request(message.getString()); + // process the request + process(request); + #endif } // dispatch the message to the registered sensor else if (_sensors[message.sensor] != 0) { @@ -2630,6 +2875,140 @@ void NodeManager::receiveTime(unsigned long ts) { #endif } +// process a request message +void NodeManager::process(Request & request) { + int function = request.getFunction(); + switch(function) { + case 1: hello(); break; + #if BATTERY_MANAGER == 1 + case 2: batteryReport(); return; + case 11: setBatteryMin(request.getValueFloat()); break; + case 12: setBatteryMax(request.getValueFloat()); break; + case 13: setBatteryReportCycles(request.getValueInt()); break; + case 14: setBatteryReportMinutes(request.getValueInt()); break; + case 15: setBatteryInternalVcc(request.getValueInt()); break; + case 16: setBatteryPin(request.getValueInt()); break; + case 17: setBatteryVoltsPerBit(request.getValueFloat()); break; + case 18: setBatteryReportWithInterrupt(request.getValueInt()); break; + #endif + case 3: + setSleepMode(request.getValueInt()); + #if PERSIST == 1 + _saveConfig(SAVE_SLEEP_MODE); + #endif + break; + case 4: + setSleepTime(request.getValueInt()); + #if PERSIST == 1 + _saveConfig(SAVE_SLEEP_TIME); + #endif + break; + case 5: + setSleepUnit(request.getValueInt()); + #if PERSIST == 1 + _saveConfig(SAVE_SLEEP_UNIT); + #endif + break; + #ifndef MY_GATEWAY_ESP8266 + case 6: reboot(); return; + #endif + case 7: clearEeprom(); break; + case 8: version(); return; + case 9: wakeup(); break; + case 10: setRetries(request.getValueInt()); break; + case 19: setSleepInterruptPin(request.getValueInt()); break; + case 20: setSleepBetweenSend(request.getValueInt()); break; + case 21: setAck(request.getValueInt()); break; + case 22: setIsMetric(request.getValueInt()); break; + #if POWER_MANAGER == 1 + case 23: setAutoPowerPins(request.getValueInt()); break; + case 24: powerOn(); break; + case 25: powerOff(); break; + #endif + case 26: unRegisterSensor(request.getValueInt()); break; + case 27: saveToMemory(0,request.getValueInt()); break; + default: return; + } + _send(_msg.set(function)); +} + + +// Send a hello message back to the controller +void NodeManager::hello() { + // do nothing, the request will be echoed back +} + +#if BATTERY_MANAGER == 1 +// Send a battery level report to the controller +void NodeManager::batteryReport() { + // measure the board vcc + float volt = 0; + if (_battery_internal_vcc || _battery_pin == -1) volt = getVcc(); + else volt = analogRead(_battery_pin) * _battery_volts_per_bit; + // calculate the percentage + int percentage = ((volt - _battery_min) / (_battery_max - _battery_min)) * 100; + if (percentage > 100) percentage = 100; + if (percentage < 0) percentage = 0; + #if DEBUG == 1 + Serial.print(F("BATT V=")); + Serial.print(volt); + Serial.print(F(" P=")); + Serial.println(percentage); + #endif + #if BATTERY_SENSOR == 1 + // report battery voltage + MyMessage battery_msg(BATTERY_CHILD_ID, V_VOLTAGE); + _send(battery_msg.set(volt, 2)); + #endif + // report battery level percentage + sendBatteryLevel(percentage,_ack); +} +#endif + +// reboot the board +void NodeManager::reboot() { + #if DEBUG == 1 + Serial.println(F("REBOOT")); + #endif + // Software reboot with watchdog timer. Enter Watchdog Configuration mode: + WDTCSR |= (1< 100) percentage = 100; - if (percentage < 0) percentage = 0; - #if DEBUG == 1 - Serial.print(F("BATT V=")); - Serial.print(volt); - Serial.print(F(" P=")); - Serial.println(percentage); - #endif - #if BATTERY_SENSOR == 1 - // report battery voltage - MyMessage battery_msg(BATTERY_CHILD_ID, V_VOLTAGE); - _send(battery_msg.set(volt, 2)); - #endif - // report battery level percentage - sendBatteryLevel(percentage,_ack); - } - #endif - #ifndef MY_GATEWAY_ESP8266 - // REBOOT: reboot the board - else if (strcmp(message, "REBOOT") == 0) { - #if DEBUG == 1 - Serial.println(F("REBOOT")); - #endif - // set the reboot pin connected to RST to low so to reboot the board - _send(_msg.set(message)); - // Software reboot with watchdog timer. Enter Watchdog Configuration mode: - WDTCSR |= (1< 30%) (default: false) + // [102] reverse the value or the percentage (e.g. 70% -> 30%) (default: false) void setReverse(bool value); - // when true returns the value as a percentage (default: true) + // [103] when true returns the value as a percentage (default: true) void setOutputPercentage(bool value); - // minimum value for calculating the percentage (default: 0) + // [104] minimum value for calculating the percentage (default: 0) void setRangeMin(int value); - // maximum value for calculating the percentage (default: 1024) + // [105] maximum value for calculating the percentage (default: 1024) void setRangeMax(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); protected: int _reference = -1; bool _reverse = false; @@ -523,21 +564,22 @@ class SensorLDR: public SensorAnalogInput { class SensorThermistor: public Sensor { public: SensorThermistor(NodeManager* node_manager, int child_id, int pin); - // resistance at 25 degrees C (default: 10000) + // [101] resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); - // temperature for nominal resistance (default: 25) + // [102] temperature for nominal resistance (default: 25) void setNominalTemperature(int value); - // The beta coefficient of the thermistor (default: 3950) + // [103] The beta coefficient of the thermistor (default: 3950) void setBCoefficient(int value); - // the value of the resistor in series with the thermistor (default: 10000) + // [104] the value of the resistor in series with the thermistor (default: 10000) void setSeriesResistor(long value); - // set a temperature offset + // [105] set a temperature offset void setOffset(float 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); protected: long _nominal_resistor = 10000; int _nominal_temperature = 25; @@ -546,61 +588,6 @@ class SensorThermistor: public Sensor { float _offset = 0; }; -/* - SensorMQ - */ -class SensorMQ: public Sensor { - public: - SensorMQ(NodeManager* node_manager, int child_id, int pin); - // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); - void setTargetGas(int value); - // define the load resistance on the board, in kilo ohms (default: 1); - void setRlValue(float value); - // define the Ro resistance on the board (default: 10000); - void setRoValue(float value); - // Sensor resistance in clean air (default: 9.83); - void setCleanAirFactor(float value); - // define how many samples you are going to take in the calibration phase (default: 50); - void setCalibrationSampleTimes(int value); - // define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); - void setCalibrationSampleInterval(int value); - // define how many samples you are going to take in normal operation (default: 50); - void setReadSampleTimes(int value); - // define the time interal(in milisecond) between each samples in the normal operations (default: 5); - void setReadSampleInterval(int value); - // set the LPGCurve array (default: {2.3,0.21,-0.47}) - void setLPGCurve(float *value); - // set the COCurve array (default: {2.3,0.72,-0.34}) - void setCOCurve(float *value); - // set the SmokeCurve array (default: {2.3,0.53,-0.44}) - void setSmokeCurve(float *value); - // define what to do at each stage of the sketch - void onBefore(); - void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - protected: - float _rl_value = 1.0; - float _ro_clean_air_factor = 9.83; - int _calibration_sample_times = 50; - int _calibration_sample_interval = 500; - int _read_sample_interval = 50; - int _read_sample_times = 5; - float _ro = 10000.0; - float _LPGCurve[3] = {2.3,0.21,-0.47}; - float _COCurve[3] = {2.3,0.72,-0.34}; - float _SmokeCurve[3] = {2.3,0.53,-0.44}; - float _MQResistanceCalculation(int raw_adc); - float _MQCalibration(); - float _MQRead(); - int _MQGetGasPercentage(float rs_ro_ratio, int gas_id); - int _MQGetPercentage(float rs_ro_ratio, float *pcurve); - int _gas_lpg = 0; - int _gas_co = 1; - int _gas_smoke = 2; - int _target_gas = _gas_co; -}; - /* SensorML8511 */ @@ -613,6 +600,7 @@ class SensorML8511: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); protected: float _mapfloat(float x, float in_min, float in_max, float out_min, float out_max); }; @@ -624,15 +612,16 @@ class SensorML8511: public Sensor { class SensorACS712: public Sensor { public: SensorACS712(NodeManager* node_manager, int child_id, int pin); - // set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); + // [101] set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); void setmVPerAmp(int value); - // set ACS offset (default: 2500); + // [102] set ACS offset (default: 2500); void setOffset(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); protected: int _ACS_offset = 2500; int _mv_per_amp = 185; @@ -645,15 +634,16 @@ class SensorACS712: public Sensor { class SensorRainGauge: public Sensor { public: SensorRainGauge(NodeManager* node_manager, int child_id, int pin); - // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60) + // [101] set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60) void setReportInterval(int value); - // set how many mm of rain to count for each tip (default: 0.11) + // [102] set how many mm of rain to count for each tip (default: 0.11) void setSingleTip(float 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; @@ -679,7 +669,10 @@ class SensorSoilMoisture: public SensorAnalogInput { public: SensorSoilMoisture(NodeManager* node_manager, int child_id, int pin); }; +#endif + +#if MODULE_DIGITAL_INPUT == 1 /* SensorDigitalInput: read the digital input of the configured pin */ @@ -691,25 +684,28 @@ class SensorDigitalInput: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); }; +#endif +#if MODULE_DIGITAL_OUTPUT == 1 /* SensorDigitalOutput: control a digital output of the configured pin */ class SensorDigitalOutput: public Sensor { public: SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin); - // set how to initialize the output (default: LOW) + // [101] set how to initialize the output (default: LOW) void setInitialValue(int value); - // if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) + // [102] if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) void setPulseWidth(int value); - // define which value to set to the output when set to on (default: HIGH) + // [103] define which value to set to the output when set to on (default: HIGH) void setOnValue(int value); - // when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) + // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) void setLegacyMode(bool value); - // automatically turn the output off after the given number of minutes + // [105] automatically turn the output off after the given number of minutes void setSafeguard(int value); - // if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) + // [106] if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) void setInputIsElapsed(bool value); // manually switch the output to the provided value void set(int value); @@ -720,6 +716,7 @@ class SensorDigitalOutput: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); protected: int _initial_value = LOW; int _on_value = HIGH; @@ -737,8 +734,6 @@ class SensorDigitalOutput: public Sensor { class SensorRelay: public SensorDigitalOutput { public: SensorRelay(NodeManager* node_manager, int child_id, int pin); - // define what to do at each stage of the sketch - //void onLoop(); }; /* @@ -748,6 +743,7 @@ class SensorLatchingRelay: public SensorRelay { public: SensorLatchingRelay(NodeManager* node_manager, int child_id, int pin); }; +#endif /* SensorDHT @@ -761,6 +757,7 @@ class SensorDHT: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -784,6 +781,7 @@ class SensorSHT21: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -805,17 +803,18 @@ class SensorHTU21D: public SensorSHT21 { /* * SensorSwitch */ +#if MODULE_SWITCH == 1 class SensorSwitch: public Sensor { public: SensorSwitch(NodeManager* node_manager, int child_id, int pin); - // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) + // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); int getMode(); - // milliseconds to wait before reading the input (default: 0) + // [102] milliseconds to wait before reading the input (default: 0) void setDebounce(int value); - // time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) + // [103] time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) void setTriggerTime(int value); - // Set initial value on the interrupt pin (default: HIGH) + // [104] Set initial value on the interrupt pin (default: HIGH) void setInitial(int value); int getInitial(); // define what to do at each stage of the sketch @@ -823,6 +822,7 @@ class SensorSwitch: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); protected: int _debounce = 0; int _trigger_time = 0; @@ -845,7 +845,7 @@ class SensorMotion: public SensorSwitch { public: SensorMotion(NodeManager* node_manager, int child_id, int pin); }; - +#endif /* SensorDs18b20 */ @@ -853,19 +853,20 @@ class SensorMotion: public SensorSwitch { class SensorDs18b20: public Sensor { public: SensorDs18b20(NodeManager* node_manager, int child_id, int pin, DallasTemperature* sensors, int index); - // return the sensors' device address - DeviceAddress* getDeviceAddress(); // returns the sensor's resolution in bits int getResolution(); - // set the sensor's resolution in bits + // [101] set the sensor's resolution in bits void setResolution(int value); - // sleep while DS18B20 calculates temperature (default: false) + // [102] sleep while DS18B20 calculates temperature (default: false) void setSleepDuringConversion(bool value); + // return the sensors' device address + DeviceAddress* getDeviceAddress(); // 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); protected: float _offset = 0; int _index; @@ -887,6 +888,7 @@ class SensorBH1750: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); protected: BH1750* _lightSensor; }; @@ -904,6 +906,7 @@ class SensorMLX90614: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); // constants const static int TEMPERATURE_AMBIENT = 0; const static int TEMPERATURE_OBJECT = 1; @@ -922,13 +925,14 @@ class SensorMLX90614: public Sensor { class SensorBosch: public Sensor { public: SensorBosch(NodeManager* node_manager, int child_id, int sensor_type); - // define how many pressure samples to keep track of for calculating the forecast (default: 5) + // [101] define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(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); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -983,17 +987,18 @@ class SensorBMP085: public SensorBosch { class SensorHCSR04: public Sensor { public: SensorHCSR04(NodeManager* node_manager, int child_id, int pin); - // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setTriggerPin(int value); - // Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setEchoPin(int value); - // Maximum distance we want to ping for (in centimeters) (default: 300) + // [103] Maximum distance we want to ping for (in centimeters) (default: 300) void setMaxDistance(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); protected: int _trigger_pin; int _echo_pin; @@ -1009,17 +1014,18 @@ class SensorHCSR04: public Sensor { class SensorSonoff: public Sensor { public: SensorSonoff(NodeManager* node_manager, int child_id); - // set the button's pin (default: 0) + // [101] set the button's pin (default: 0) void setButtonPin(int value); - // set the relay's pin (default: 12) + // [102] set the relay's pin (default: 12) void setRelayPin(int value); - // set the led's pin (default: 13) + // [103] set the led's pin (default: 13) void setLedPin(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); protected: Bounce _debouncer = Bounce(); int _button_pin = 0; @@ -1048,11 +1054,69 @@ class SensorMCP9808: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); protected: Adafruit_MCP9808* _mcp; }; #endif +/* + SensorMQ + */ + #if MODULE_MQ == 1 +class SensorMQ: public Sensor { + public: + SensorMQ(NodeManager* node_manager, int child_id, int pin); + // [101] define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); + void setTargetGas(int value); + // [102] define the load resistance on the board, in kilo ohms (default: 1); + void setRlValue(float value); + // [103] define the Ro resistance on the board (default: 10000); + void setRoValue(float value); + // [104] Sensor resistance in clean air (default: 9.83); + void setCleanAirFactor(float value); + // [105] define how many samples you are going to take in the calibration phase (default: 50); + void setCalibrationSampleTimes(int value); + // [106] define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); + void setCalibrationSampleInterval(int value); + // [107] define how many samples you are going to take in normal operation (default: 50); + void setReadSampleTimes(int value); + // [108] define the time interal(in milisecond) between each samples in the normal operations (default: 5); + void setReadSampleInterval(int value); + // set the LPGCurve array (default: {2.3,0.21,-0.47}) + void setLPGCurve(float *value); + // set the COCurve array (default: {2.3,0.72,-0.34}) + void setCOCurve(float *value); + // set the SmokeCurve array (default: {2.3,0.53,-0.44}) + void setSmokeCurve(float *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); + protected: + float _rl_value = 1.0; + float _ro_clean_air_factor = 9.83; + int _calibration_sample_times = 50; + int _calibration_sample_interval = 500; + int _read_sample_interval = 50; + int _read_sample_times = 5; + float _ro = 10000.0; + float _LPGCurve[3] = {2.3,0.21,-0.47}; + float _COCurve[3] = {2.3,0.72,-0.34}; + float _SmokeCurve[3] = {2.3,0.53,-0.44}; + float _MQResistanceCalculation(int raw_adc); + float _MQCalibration(); + float _MQRead(); + int _MQGetGasPercentage(float rs_ro_ratio, int gas_id); + int _MQGetPercentage(float rs_ro_ratio, float *pcurve); + int _gas_lpg = 0; + int _gas_co = 1; + int _gas_smoke = 2; + int _target_gas = _gas_co; +}; +#endif /*************************************** NodeManager: manages all the aspects of the node @@ -1060,51 +1124,51 @@ class SensorMCP9808: public Sensor { class NodeManager { public: NodeManager(); - // the pin to connect to the RST pin to reboot the board (default: 4) - void setRebootPin(int value); - // send the same service message multiple times (default: 1) + // [10] send the same service message multiple times (default: 1) void setRetries(int value); #if BATTERY_MANAGER == 1 - // the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) + // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) void setBatteryMin(float value); - // the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) + // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) + // [13] after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) void setBatteryReportCycles(int value); - // after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) + // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) void setBatteryReportMinutes(int value); - // if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) + // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) void setBatteryInternalVcc(bool value); - // if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) + // [16] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) void setBatteryPin(int value); - // if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) + // [17] if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) void setBatteryVoltsPerBit(float value); - // If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) + // [18] If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) void setBatteryReportWithInterrupt(bool value); + // [2] Send a battery level report to the controller + void batteryReport(); #endif - // define the way the node should behave. It can be IDLE (stay awake withtout executing each sensors' loop), SLEEP (go to sleep for the configured interval), WAIT (wait for the configured interval), ALWAYS_ON (stay awake and execute each sensors' loop) + // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) void setSleepMode(int value); void setMode(int value); int getMode(); - // define for how long the board will sleep (default: 0) + // [4] define for how long the board will sleep (default: 0) void setSleepTime(int value); int getSleepTime(); - // define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) + // [5] define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) void setSleepUnit(int value); int getSleepUnit(); // configure the node's behavior, parameters are mode, time and unit void setSleep(int value1, int value2, int value3); - // if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true) + // [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); - // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) + // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); // register a built-in sensor int registerSensor(int sensor_type, int pin = -1, int child_id = -1); // register a custom sensor int registerSensor(Sensor* sensor); - // un-register a sensor + // [26] un-register a sensor void unRegisterSensor(int sensor_index); // return a sensor by its index Sensor* get(int sensor_index); @@ -1114,26 +1178,42 @@ class NodeManager { #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); - // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) + // [23] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) void setAutoPowerPins(bool value); - // manually turn the power on + // [24] manually turn the power on void powerOn(); - // manually turn the power off + // [25] manually turn the power off void powerOff(); #endif - // set this to true if you want destination node to send ack back to this node (default: false) + // [21] set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); // request and return the current timestamp from the controller long getTimestamp(); // Request the controller's configuration on startup (default: true) void setGetControllerConfig(bool value); - // Manually set isMetric setting + // [22] Manually set isMetric setting void setIsMetric(bool value); bool getIsMetric(); // Convert a temperature from celsius to fahrenheit depending on how isMetric is set float celsiusToFahrenheit(float temperature); // return true if sleep or wait is configured and hence this is a sleeping node bool isSleepingNode(); + // [1] Send a hello message back to the controller + void hello(); + // [6] reboot the board + void reboot(); + // [8] send NodeManager's the version back to the controller + void version(); + // [7] clear the EEPROM + void clearEeprom(); + // [9] wake up the board + void wakeup(); + // process a remote request + void process(Request & request); + // return the value stored at the requested index from the EEPROM + int loadFromMemory(int index); + // [27] save the given index of the EEPROM the provided value + void saveToMemory(int index, int value); // hook into the main sketch functions void before(); void presentation(); @@ -1173,13 +1253,14 @@ class NodeManager { long _timestamp = -1; Sensor* _sensors[MAX_SENSORS] = {0}; bool _ack = false; - void _process(const char * message); void _sleep(); void _present(int child_id, int type); int _getAvailableChildId(); int _getInterruptInitialValue(int mode); bool _get_controller_config = true; int _is_metric = 1; + void _loadConfig(); + void _saveConfig(int what); }; #endif diff --git a/NodeManager.ino b/NodeManager.ino index 4e6def9f..c1d9f953 100644 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -33,10 +33,14 @@ void before() { /* * Register below your sensors */ + int n = nodeManager.registerSensor(SENSOR_THERMISTOR,A1); + Sensor* sensor = nodeManager.get(n); + Serial.println(sizeof(Sensor)); + Serial.println(sizeof(SensorThermistor)); + Serial.println(sizeof(NodeManager)); + Serial.println(sizeof(Timer)); - - - + /* * Register above your sensors */ diff --git a/README.md b/README.md index f9c299d3..e5cc8a87 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Those NodeManager's directives in the `config.h` file control which module/libra // if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage #define BATTERY_SENSOR 1 -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_MQ, 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_GAUGE, 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 @@ -171,6 +171,8 @@ Those NodeManager's directives in the `config.h` file control which module/libra #define MODULE_HCSR04 0 // Enable this module to use one of the following sensors: SENSOR_MCP9808 #define MODULE_MCP9808 0 +// Enable this module to use one of the following sensors: SENSOR_MQ +#define MODULE_MQ 0 ~~~ ### Installing the dependencies @@ -195,78 +197,96 @@ MODULE_MCP9808 | https://github.com/adafruit/Adafruit_MCP9808_Library Node Manager comes with a reasonable default configuration. If you want/need to change its settings, this can be done in your sketch, inside the `before()` function and just before registering your sensors. The following methods are exposed for your convenience: ~~~c - // send the same service message multiple times (default: 1) + // [10] send the same service message multiple times (default: 1) void setRetries(int value); #if BATTERY_MANAGER == 1 - // the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) + // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) void setBatteryMin(float value); - // the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) + // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) + // [13] after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) void setBatteryReportCycles(int value); - // after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) + // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) void setBatteryReportMinutes(int value); - // if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) + // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) void setBatteryInternalVcc(bool value); - // if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) + // [16] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) void setBatteryPin(int value); - // if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) + // [17] if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) void setBatteryVoltsPerBit(float value); - // If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) + // [18] If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) void setBatteryReportWithInterrupt(bool value); #endif - // define the way the node should behave. It can be IDLE (stay awake withtout executing each sensors' loop), SLEEP (go to sleep for the configured interval), WAIT (wait for the configured interval), ALWAYS_ON (stay awake and execute each sensors' loop) + // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) void setSleepMode(int value); void setMode(int value); - // define for how long the board will sleep (default: 0) + int getMode(); + // [4] define for how long the board will sleep (default: 0) void setSleepTime(int value); - // define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) + int getSleepTime(); + // [5] define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) void setSleepUnit(int value); + int getSleepUnit(); // configure the node's behavior, parameters are mode, time and unit void setSleep(int value1, int value2, int value3); - // if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true) + // [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); - #endif // 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); - // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) + // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); - // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1) - void setInterruptPin(int value); // register a built-in sensor int registerSensor(int sensor_type, int pin = -1, int child_id = -1); - // un-register a sensor - void unRegisterSensor(int sensor_index); // register a custom sensor int registerSensor(Sensor* sensor); + // [26] un-register a sensor + void unRegisterSensor(int sensor_index); // return a sensor by its index Sensor* get(int sensor_index); - Sensor* getSensor(int sensor_index); - // assign a different child id to a sensor + Sensor* getSensor(int sensor_index); + // assign a different child id to a sensor bool renameSensor(int old_child_id, int new_child_id); #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, long wait = 0); - // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) + void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); + // [23] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) void setAutoPowerPins(bool value); - // manually turn the power on + // [24] manually turn the power on void powerOn(); - // manually turn the power off + // [25] manually turn the power off void powerOff(); #endif - // set this to true if you want destination node to send ack back to this node (default: false) + // [21] set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); // request and return the current timestamp from the controller long getTimestamp(); // Request the controller's configuration on startup (default: true) void setGetControllerConfig(bool value); - // Manually set isMetric setting + // [22] Manually set isMetric setting void setIsMetric(bool value); bool getIsMetric(); // Convert a temperature from celsius to fahrenheit depending on how isMetric is set float celsiusToFahrenheit(float temperature); // return true if sleep or wait is configured and hence this is a sleeping node bool isSleepingNode(); + // [1] Send a hello message back to the controller + void hello(); + // [2] Send a battery level report to the controller + void batteryReport(); + // [6] reboot the board + void reboot(); + // [8] send NodeManager's the version back to the controller + void version(); + // [7] clear the EEPROM + void clearEeprom(); + // [9] wake up the board + void wakeup(); + // process a remote request + void process(const char * message); + // return the value stored at the requested index from the EEPROM + int loadFromMemory(int index); + // save the given index of the EEPROM the provided value + void saveToMemory(int index, int value); ~~~ For example @@ -331,6 +351,8 @@ If you want to create a custom sensor and register it with NodeManager so it can void onLoop(); // define what to do during receive() when the sensor receives a message void onReceive(const MyMessage & message); + // define what to do when receiving a remote configuration message + void onProcess(Request & request); ~~~ You can then instantiate your newly created class and register with NodeManager: @@ -352,40 +374,40 @@ To do so, use `nodeManager.getSensor(child_id)` which will return a pointer to t The following methods are available for all the sensors: ~~~c - // where the sensor is attached to (default: not set) + // [1] where the sensor is attached to (default: not set) void setPin(int value); - int getPin(); - // child_id of this sensor (default: not set) + int getPin(); + // [2] child_id of this sensor (default: not set) void setChildId(int value); - int getChildId(); + int getChildId(); // presentation of this sensor (default: S_CUSTOM) void setPresentation(int value); - int getPresentation(); - // type of this sensor (default: V_CUSTOM) + int getPresentation(); + // [3] type of this sensor (default: V_CUSTOM) void setType(int value); - int getType(); - // description of the sensor (default: '') + int getType(); + // [4] description of the sensor (default: '') void setDescription(char *value); // set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); // when queried, send the message multiple times (default: 1) void setRetries(int value); - // For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) + // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) void setSamples(int value); - // If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) + // [6] If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) void setSamplesInterval(int value); - // if true will report the measure only if different then the previous one (default: false) + // [7] if true will report the measure only if different than the previous one (default: false) void setTrackLastValue(bool value); - // if track last value is enabled, force to send an update after the configured number of cycles (default: -1) + // [8] if track last value is enabled, force to send an update after the configured number of cycles (default: -1) void setForceUpdate(int value); void setForceUpdateCycles(int value); - // if track last value is enabled, force to send an update after the configured number of minutes (default: -1) + // [9] if track last value is enabled, force to send an update after the configured number of minutes (default: -1) void setForceUpdateMinutes(int value); - // the value type of this sensor (default: TYPE_INTEGER) + // [10] the value type of this sensor (default: TYPE_INTEGER) void setValueType(int value); - int getValueType(); - // for float values, set the float precision (default: 2) - void setFloatPrecision(int value); + int getValueType(); + // [11] for float values, set the float precision (default: 2) + void setFloatPrecision(int value); // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1) @@ -393,22 +415,24 @@ The following methods are available for all the sensors: int getInterruptPin(); #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, long wait = 0); - // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) + void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); + // [12] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) void setAutoPowerPins(bool value); - // manually turn the power on + // [13] manually turn the power on void powerOn(); - // manually turn the power off + // [14] manually turn the power off void powerOff(); #endif // get the latest recorded value from the sensor int getValueInt(); float getValueFloat(); char* getValueString(); - // After how many cycles the sensor will report back its measure (default: 1 cycle) + // [15] After how many cycles the sensor will report back its measure (default: 1 cycle) void setReportIntervalCycles(int value); - // After how many minutes the sensor will report back its measure (default: 1 cycle) + // [16] After how many minutes the sensor will report back its measure (default: 1 cycle) void setReportIntervalMinutes(int value); + // process a remote request + void process(Request & request); ~~~ #### Sensor's specific configuration @@ -417,49 +441,49 @@ Each sensor class can expose additional methods. * SensorAnalogInput / SensorLDR / SensorRain / SensorSoilMoisture ~~~c - // the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) + // [101] the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) void setReference(int value); - // reverse the value or the percentage (e.g. 70% -> 30%) (default: false) + // [102] reverse the value or the percentage (e.g. 70% -> 30%) (default: false) void setReverse(bool value); - // when true returns the value as a percentage (default: true) + // [103] when true returns the value as a percentage (default: true) void setOutputPercentage(bool value); - // minimum value for calculating the percentage (default: 0) + // [104] minimum value for calculating the percentage (default: 0) void setRangeMin(int value); - // maximum value for calculating the percentage (default: 1024) + // [105] maximum value for calculating the percentage (default: 1024) void setRangeMax(int value); ~~~ * SensorThermistor ~~~c - // resistance at 25 degrees C (default: 10000) + // [101] resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); - // temperature for nominal resistance (default: 25) + // [102] temperature for nominal resistance (default: 25) void setNominalTemperature(int value); - // The beta coefficient of the thermistor (default: 3950) + // [103] The beta coefficient of the thermistor (default: 3950) void setBCoefficient(int value); - // the value of the resistor in series with the thermistor (default: 10000) + // [104] the value of the resistor in series with the thermistor (default: 10000) void setSeriesResistor(long value); - // set a temperature offset + // [105] set a temperature offset void setOffset(float value); ~~~ * SensorMQ ~~~c - // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); + // [101] define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); void setTargetGas(int value); - // define the load resistance on the board, in kilo ohms (default: 1); + // [102] define the load resistance on the board, in kilo ohms (default: 1); void setRlValue(float value); - // define the Ro resistance on the board (default: 10000); + // [103] define the Ro resistance on the board (default: 10000); void setRoValue(float value); - // Sensor resistance in clean air (default: 9.83); + // [104] Sensor resistance in clean air (default: 9.83); void setCleanAirFactor(float value); - // define how many samples you are going to take in the calibration phase (default: 50); + // [105] define how many samples you are going to take in the calibration phase (default: 50); void setCalibrationSampleTimes(int value); - // define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); + // [106] define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); void setCalibrationSampleInterval(int value); - // define how many samples you are going to take in normal operation (default: 50); + // [107] define how many samples you are going to take in normal operation (default: 50); void setReadSampleTimes(int value); - // define the time interal(in milisecond) between each samples in the normal operations (default: 5); + // [108] define the time interal(in milisecond) between each samples in the normal operations (default: 5); void setReadSampleInterval(int value); // set the LPGCurve array (default: {2.3,0.21,-0.47}) void setLPGCurve(float *value); @@ -469,19 +493,35 @@ Each sensor class can expose additional methods. void setSmokeCurve(float *value); ~~~ +* SensorACS712 +~~~c + // [101] set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); + void setmVPerAmp(int value); + // [102] set ACS offset (default: 2500); + void setOffset(int value); +~~~ + +* 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); +~~~ + * SensorDigitalOutput / SensorRelay / SensorLatchingRelay ~~~c - // set how to initialize the output (default: LOW) + // [101] set how to initialize the output (default: LOW) void setInitialValue(int value); - // if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) + // [102] if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) void setPulseWidth(int value); - // define which value to set to the output when set to on (default: HIGH) + // [103] define which value to set to the output when set to on (default: HIGH) void setOnValue(int value); - // when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) + // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) void setLegacyMode(bool value); - // automatically turn the output off after the given number of minutes + // [105] automatically turn the output off after the given number of minutes void setSafeguard(int value); - // if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) + // [106] if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) void setInputIsElapsed(bool value); // manually switch the output to the provided value void set(int value); @@ -491,74 +531,54 @@ Each sensor class can expose additional methods. * SensorSwitch / SensorDoor / SensorMotion ~~~c - // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) + // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); - // milliseconds to wait before reading the input (default: 0) + int getMode(); + // [102] milliseconds to wait before reading the input (default: 0) void setDebounce(int value); - // time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) + // [103] time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) void setTriggerTime(int value); - // Set initial value on the interrupt pin (default: HIGH) + // [104] Set initial value on the interrupt pin (default: HIGH) void setInitial(int value); + int getInitial(); ~~~ * SensorDs18b20** ~~~c - // return the sensors' device address - DeviceAddress* getDeviceAddress(); // returns the sensor's resolution in bits int getResolution(); - // set the sensor's resolution in bits + // [101] set the sensor's resolution in bits void setResolution(int value); - // sleep while DS18B20 calculates temperature (default: false) + // [102] sleep while DS18B20 calculates temperature (default: false) void setSleepDuringConversion(bool value); + // return the sensors' device address + DeviceAddress* getDeviceAddress(); ~~~ -* SensorBME280 -~~~c - // define how many pressure samples to keep track of for calculating the forecast (default: 5) - void setForecastSamplesCount(int value); -~~~ - -* SensorSonoff -~~~c - // set the button's pin (default: 0) - void setButtonPin(int value); - // set the relay's pin (default: 12) - void setRelayPin(int value); - // set the led's pin (default: 13) - void setLedPin(int value); -~~~ - -* SensorBMP085 +* SensorBME280 / SensorBMP085 ~~~c - // define how many pressure samples to keep track of for calculating the forecast (default: 5) + // [101] define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); ~~~ * SensorHCSR04 ~~~c - // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setTriggerPin(int value); - // Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setEchoPin(int value); - // Maximum distance we want to ping for (in centimeters) (default: 300) + // [103] Maximum distance we want to ping for (in centimeters) (default: 300) void setMaxDistance(int value); ~~~ -* SensorACS712 -~~~c - // set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); - void setmVPerAmp(int value); - // set ACS offset (default: 2500); - void setOffset(int value); -~~~ - -* SensorRainGauge +* SensorSonoff ~~~c - // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60); - void setReportInterval(int value); - // set how many mm of rain to count for each tip (default: 0.11); - void setSingleTip(float value); + // [101] set the button's pin (default: 0) + void setButtonPin(int value); + // [102] set the relay's pin (default: 12) + void setRelayPin(int value); + // [103] set the led's pin (default: 13) + void setLedPin(int value); ~~~ ### Upload your sketch @@ -570,51 +590,65 @@ When `DEBUG` is enabled, detailed information is available through the serial po ### Communicate with NodeManager and its sensors -You can interact with each registered sensor asking to execute their main tasks by sending to the child id a `REQ` command. For example to request the temperature to node_id 254 and child_id 1: +You can interact with each registered sensor by asking to execute their main tasks by sending to the child id a `REQ` command (or a `SET` for output sensors like relays). For example to request the temperature to node_id 254 and child_id 1: `254;1;2;0;0;` To activate a relay connected to the same node, child_id 100: -`254;100;2;0;2;1` +`254;100;1;0;2;1` No need to implement anything on your side since for built-in sensor types this is handled automatically. -Once the node will be sleeping, it will report automatically each measure at the end of every sleep cycle. - -NodeManager exposes a configuration service by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message. -The following custom commands are available: - -NodeManager command | Description - ------------- | ------------- -BATTERY | Report the battery level back to the gateway/controller -HELLO | Hello request -REBOOT | Reboot the board -CLEAR | Wipe from the EEPROM NodeManager's settings -VERSION | Respond with NodeManager's version -IDxxx | Change the node id to the provided one. E.g. ID025: change the node id to 25. Requires a reboot to take effect -INTVLnnnX | Set the wait/sleep interval to nnn where X is S=Seconds, M=mins, H=Hours, D=Days. E.g. INTVL010M would be 10 minutes -MODEx | change the way the node behaves. 0: stay awake withtout executing each sensors' loop(), 1: go to sleep for the configured interval, 2: wait for the configured interval, 3: stay awake and execute each sensors' loop() -AWAKE | When received after a sleeping cycle or during wait, abort the cycle and stay awake - -For example, to request the battery level to node id 254: - -`254;200;2;0;48;BATTERY` +Once the node will be sleeping, it will report automatically each measure at the end of every sleep cycle, unless configured otherwise. -To set the sleeping cycle to 1 hour: +NodeManager exposes also a configuration service by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message if successful. +Almost all the functions made available through the API can be called remotely. To do so, the payload must be in the format `[,]` where function_id is the number between square brackets you can find in the description just above each function and, if the function takes and argument, this can be passed along in value_to_set. +For example, to request a battery report, find the function you need to call remotely within the documentation: +~~~c + // [2] Send a battery level report to the controller + void batteryReport(); +~~~ +In this case the function_id will be 2. To request a battery report to the node_id 100, send the following message: +`;;;0;;` +`100;200;2;0;48;2` -`254;200;2;0;48;INTVL001H` +The change the sleep time from e.g. 10 minutes as set in the sketch to 5 minutes: +~~~c + // [4] define for how long the board will sleep (default: 0) + void setSleepTime(int value); +~~~ +`;;;0;;,` +`100;200;2;0;48;4,5` To ask the node to start sleeping (and waking up based on the previously configured interval): +~~~c + // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) + void setSleepMode(int value); +~~~ +`100;200;2;0;48;3,1` -`254;200;2;0;48;MODE1` - -To wake up a node previously configured with `MODE1`, send the following just after reporting `AWAKE`: +To wake up a node previously configured as sleeping, send the following just it wakes up next: +~~~c + // [9] wake up the board + void wakeup(); +~~~ +`100;200;2;0;48;9` -`254;200;2;0;48;WAKEUP` +The same protocol can be used to execute remotely also sensor-specific functions. In this case the message has to be sent to the sensor's child_id, with a V_CUSTOM type of message. For example if you want to collect and average 10 samples for child_id 1: +~~~c + // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) + void setSamples(int value); +~~~ +`100;1;2;0;48;5,10` -In addition, NodeManager will report with custom messages every time the board is going to sleep (`SLEEPING`) or it is awake (`AWAKE`). +If you want to decrease the temperature offset of a thermistor sensor to -2: +~~~c + // [105] set a temperature offset + void setOffset(float value); +~~~ +`100;1;2;0;48;105,-2` -If `PERSIST` is enabled, the settings provided with `INTVLnnnX` and `MODEx` are saved to the EEPROM to be persistent even after rebooting the board. +Please note that anything set remotely will NOT persist a reboot apart from those provided to setSleepMode(), setSleepTime() and setSleepUnit() which are saved to the EEPROM (provided `PERSIST` is enabled). ## Understanding NodeManager: how it works diff --git a/config.h b/config.h index eded817e..497e4eee 100644 --- a/config.h +++ b/config.h @@ -15,11 +15,11 @@ // General settings #define MY_BAUD_RATE 9600 //#define MY_DEBUG -//#define MY_NODE_ID 100 +#define MY_NODE_ID 100 // NRF24 radio settings #define MY_RADIO_NRF24 -//#define MY_RF24_ENABLE_ENCRYPTION +#define MY_RF24_ENABLE_ENCRYPTION //#define MY_RF24_CHANNEL 76 //#define MY_RF24_PA_LEVEL RF24_PA_HIGH //#define MY_DEBUG_VERBOSE_RF24 @@ -102,12 +102,12 @@ // 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_MQ, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE +// 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 #define MODULE_ANALOG_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT -#define MODULE_DIGITAL_INPUT 1 +#define MODULE_DIGITAL_INPUT 0 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY -#define MODULE_DIGITAL_OUTPUT 1 +#define MODULE_DIGITAL_OUTPUT 0 // Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 #define MODULE_DHT 0 // Enable this module to use one of the following sensors: SENSOR_SHT21 @@ -130,5 +130,7 @@ #define MODULE_HCSR04 0 // Enable this module to use one of the following sensors: SENSOR_MCP9808 #define MODULE_MCP9808 0 +// Enable this module to use one of the following sensors: SENSOR_MQ +#define MODULE_MQ 0 #endif From effa256806ef5a11dc6994ae027b631f025f5c6e Mon Sep 17 00:00:00 2001 From: user2684 Date: Thu, 1 Jun 2017 17:11:56 +0200 Subject: [PATCH 10/33] Revert "Enhanced remote API #102 #20 #28 #53 #78" This reverts commit 34e073fc008462d698b3a9af5c39af8cff1ba6bf. --- NodeManager.cpp | 1067 ++++++++++++++++++----------------------------- NodeManager.h | 357 ++++++---------- NodeManager.ino | 10 +- README.md | 328 +++++++-------- config.h | 12 +- 5 files changed, 692 insertions(+), 1082 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 5e5e6117..0002d32a 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -195,53 +195,6 @@ int Timer::getUnit() { return _unit; } - -/****************************************** - Request -*/ - -Request::Request(const char* string) { - char str[10]; - char* ptr; - strcpy(str,string); - // tokenize the string and split function from value - strtok_r(str,",",&ptr); - _function = atoi(str); - strcpy(_value,ptr); - #if DEBUG == 1 - Serial.print(F("REQ F=")); - Serial.print(getFunction()); - Serial.print(F(" I=")); - Serial.print(getValueInt()); - Serial.print(F(" F=")); - Serial.print(getValueFloat()); - Serial.print(F(" S=")); - Serial.println(getValueString()); - #endif -} - -// return the parsed function -int Request::getFunction() { - return _function; -} - -// return the value as an int -int Request::getValueInt() { - return atoi(_value); - -} - -// return the value as a float -float Request::getValueFloat() { - return atof(_value); -} - -// return the value as a string -char* Request::getValueString() { - return _value; -} - - /****************************************** Sensors */ @@ -255,7 +208,6 @@ Sensor::Sensor(NodeManager* node_manager, int child_id, int pin) { _child_id = child_id; _pin = pin; _msg = MyMessage(_child_id, _type); - _msg_service = MyMessage(_child_id, V_CUSTOM); _report_timer = new Timer(_node_manager); _force_update_timer = new Timer(_node_manager); } @@ -464,50 +416,11 @@ void Sensor::loop(const MyMessage & message) { // receive a message from the radio network void Sensor::receive(const MyMessage &message) { // return if not for this sensor - if (message.sensor != _child_id) return; - // check if it is a request for the API - if (message.getCommand() == C_REQ && message.type == V_CUSTOM) { - #if REMOTE_CONFIGURATION == 1 - // parse the request - Request request = Request(message.getString()); - // if it is for a sensor-generic function, call process(), otherwise the sensor-specific onProcess(); - if (request.getFunction() < 100) process(request); - else onProcess(request); - #endif - } - // return if the type is not correct - if (message.type != _type) return; + if (message.sensor != _child_id || message.type != _type) return; // a request would make the sensor executing its main task passing along the message loop(message); } -// process a remote configuration request message -void Sensor::process(Request & request) { - int function = request.getFunction(); - switch(function) { - case 1: setPin(request.getValueInt()); break; - case 2: setChildId(request.getValueInt()); break; - case 3: setType(request.getValueInt()); break; - case 4: setDescription(request.getValueString()); break; - case 5: setSamples(request.getValueInt()); break; - case 6: setSamplesInterval(request.getValueInt()); break; - case 7: setTrackLastValue(request.getValueInt()); break; - case 8: setForceUpdateCycles(request.getValueInt()); break; - case 9: setForceUpdateMinutes(request.getValueInt()); break; - case 10: setValueType(request.getValueInt()); break; - case 11: setFloatPrecision(request.getValueInt()); break; - #if POWER_MANAGER == 1 - case 12: setAutoPowerPins(request.getValueInt()); break; - case 13: powerOn(); break; - case 14: powerOff(); break; - #endif - case 15: setReportIntervalCycles(request.getValueInt()); break; - case 16: setReportIntervalMinutes(request.getValueInt()); break; - default: return; - } - _send(_msg_service.set(function)); -} - // send a message to the network void Sensor::_send(MyMessage & message) { // send the message, multiple times if requested @@ -555,7 +468,7 @@ bool Sensor::_isWorthSending(bool comparison) { return false; } -#if MODULE_ANALOG_INPUT == 1 + /* SensorAnalogInput */ @@ -615,20 +528,6 @@ void SensorAnalogInput::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } -// what to do when receiving a remote message -void SensorAnalogInput::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setReference(request.getValueInt()); break; - case 102: setReverse(request.getValueInt()); break; - case 103: setOutputPercentage(request.getValueInt()); break; - case 104: setRangeMin(request.getValueInt()); break; - case 105: setRangeMax(request.getValueInt()); break; - default: return; - } - _send(_msg_service.set(function)); -} - // read the analog input int SensorAnalogInput::_getAnalogRead() { #ifndef MY_GATEWAY_ESP8266 @@ -740,19 +639,6 @@ void SensorThermistor::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } -// what to do when receiving a remote message -void SensorThermistor::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setNominalResistor((long)request.getValueInt()); break; - case 102: setNominalTemperature(request.getValueInt()); break; - case 103: setBCoefficient(request.getValueInt()); break; - case 104: setSeriesResistor((long)request.getValueString()); break; - case 105: setOffset(request.getValueFloat()); break; - default: return; - } - _send(_msg_service.set(function)); -} /* SensorML8511 @@ -802,10 +688,6 @@ void SensorML8511::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } -// what to do when receiving a remote message -void SensorML8511::onProcess(Request & request) { -} - // 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; @@ -861,17 +743,6 @@ void SensorACS712::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } -// what to do when receiving a remote message -void SensorACS712::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 100: setmVPerAmp(request.getValueInt()); break; - case 102: setOffset(request.getValueInt()); break; - default: return; - } - _send(_msg_service.set(function)); -} - /* SensorRainGauge */ @@ -954,17 +825,6 @@ void SensorRainGauge::onReceive(const MyMessage & message) { } } -// what to do when receiving a remote 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)); -} - /* SensorRain */ @@ -995,9 +855,151 @@ SensorSoilMoisture::SensorSoilMoisture(NodeManager* node_manager, int child_id, setRangeMin(100); } -#endif -#if MODULE_DIGITAL_INPUT == 1 +/* + * SensorMQ + */ +SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { + setPresentation(S_AIR_QUALITY); + setType(V_LEVEL); +} + +//setter/getter +void SensorMQ::setRlValue(float value) { + _rl_value = value; +} +void SensorMQ::setRoValue(float value) { + _ro = value; +} +void SensorMQ::setCleanAirFactor(float value) { + _ro_clean_air_factor = value; +} +void SensorMQ::setCalibrationSampleTimes(int value) { + _calibration_sample_times = value; +} +void SensorMQ::setCalibrationSampleInterval(int value){ + _calibration_sample_interval = value; +} +void SensorMQ::setReadSampleTimes(int value) { + _read_sample_times = value; +} +void SensorMQ::setReadSampleInterval(int value) { + _read_sample_interval = value; +} +void SensorMQ::setLPGCurve(float *value) { + _LPGCurve[0] = value[0]; + _LPGCurve[2] = value[1]; + _LPGCurve[2] = value[2]; +} +void SensorMQ::setCOCurve(float *value) { + _COCurve[0] = value[0]; + _COCurve[2] = value[1]; + _COCurve[2] = value[2]; +} +void SensorMQ::setSmokeCurve(float *value) { + _SmokeCurve[0] = value[0]; + _SmokeCurve[2] = value[1]; + _SmokeCurve[2] = value[2]; +} + +// what to do during before +void SensorMQ::onBefore() { + // prepare the pin for input + pinMode(_pin, INPUT); +} + +// what to do during setup +void SensorMQ::onSetup() { + _ro = _MQCalibration(); +} + +// what to do during loop +void SensorMQ::onLoop() { + if (_pin == -1) return; + // calculate rs/ro + float mq = _MQRead()/_ro; + // calculate the ppm + float lpg = _MQGetGasPercentage(mq,_gas_lpg); + float co = _MQGetGasPercentage(mq,_gas_co); + float smoke = _MQGetGasPercentage(mq,_gas_smoke); + // assign to the value the requested gas + uint16_t value; + if (_target_gas == _gas_lpg) value = lpg; + if (_target_gas == _gas_co) value = co; + if (_target_gas == _gas_smoke) value = smoke; + #if DEBUG == 1 + Serial.print(F("MQ I=")); + Serial.print(_child_id); + Serial.print(F(" V=")); + Serial.print(value); + Serial.print(F(" LPG=")); + Serial.print(lpg); + Serial.print(F(" CO=")); + Serial.print(co); + Serial.print(F(" SMOKE=")); + Serial.println(smoke); + #endif + // store the value + _value_int = (int16_t)ceil(value); +} + +// what to do as the main task when receiving a message +void SensorMQ::onReceive(const MyMessage & message) { + if (message.getCommand() == C_REQ) onLoop(); +} + +// returns the calculated sensor resistance +float SensorMQ::_MQResistanceCalculation(int raw_adc) { + return ( ((float)_rl_value*(1023-raw_adc)/raw_adc)); +} + +// This function assumes that the sensor is in clean air +float SensorMQ::_MQCalibration() { + int i; + float val=0; + //take multiple samples + for (i=0; i< _calibration_sample_times; i++) { + val += _MQResistanceCalculation(analogRead(_pin)); + wait(_calibration_sample_interval); + } + //calculate the average value + val = val/_calibration_sample_times; + //divided by RO_CLEAN_AIR_FACTOR yields the Ro + val = val/_ro_clean_air_factor; + //according to the chart in the datasheet + return val; +} + +// This function use MQResistanceCalculation to caculate the sensor resistenc (Rs). +float SensorMQ::_MQRead() { + int i; + float rs=0; + for (i=0; i<_read_sample_times; i++) { + rs += _MQResistanceCalculation(analogRead(_pin)); + wait(_read_sample_interval); + } + rs = rs/_read_sample_times; + return rs; +} + +// This function passes different curves to the MQGetPercentage function which calculates the ppm (parts per million) of the target gas. +int SensorMQ::_MQGetGasPercentage(float rs_ro_ratio, int gas_id) { + if ( gas_id == _gas_lpg ) { + return _MQGetPercentage(rs_ro_ratio,_LPGCurve); + } else if ( gas_id == _gas_co) { + return _MQGetPercentage(rs_ro_ratio,_COCurve); + } else if ( gas_id == _gas_smoke) { + return _MQGetPercentage(rs_ro_ratio,_SmokeCurve); + } + return 0; +} + +// returns ppm of the target gas +int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) { + return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0]))); +} + + /* SensorDigitalInput */ @@ -1037,16 +1039,12 @@ void SensorDigitalInput::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } -// what to do when receiving a remote message -void SensorDigitalInput::onProcess(Request & request) { -} -#endif -#if MODULE_DIGITAL_OUTPUT == 1 /* SensorDigitalOutput */ +// contructor SensorDigitalOutput::SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) { _safeguard_timer = new Timer(node_manager); } @@ -1116,21 +1114,6 @@ void SensorDigitalOutput::onReceive(const MyMessage & message) { } } -// what to do when receiving a remote message -void SensorDigitalOutput::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setInitialValue(request.getValueInt()); break; - case 102: setPulseWidth(request.getValueInt()); break; - case 103: setOnValue(request.getValueInt()); break; - case 104: setLegacyMode(request.getValueInt()); break; - case 105: setSafeguard(request.getValueInt()); break; - case 106: setInputIsElapsed(request.getValueInt()); break; - default: return; - } - _send(_msg_service.set(function)); -} - // write the value to the output void SensorDigitalOutput::set(int value) { if (_input_is_elapsed) { @@ -1186,7 +1169,14 @@ SensorRelay::SensorRelay(NodeManager* node_manager, int child_id, int pin): Sens setPresentation(S_BINARY); setType(V_STATUS); } - +/* +// define what to do during loop +void SensorRelay::onLoop() { + // set the value to -1 so to avoid reporting to the gateway during loop + _value_int = -1; + _last_value_int = -1; +} +*/ /* SensorLatchingRelay */ @@ -1197,7 +1187,6 @@ SensorLatchingRelay::SensorLatchingRelay(NodeManager* node_manager, int child_id setPulseWidth(50); } -#endif /* SensorDHT */ @@ -1269,10 +1258,6 @@ void SensorDHT::onLoop() { void SensorDHT::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } - -// what to do when receiving a remote message -void SensorDHT::onProcess(Request & request) { -} #endif /* @@ -1344,10 +1329,6 @@ void SensorSHT21::onLoop() { void SensorSHT21::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } - -// what to do when receiving a remote message -void SensorSHT21::onProcess(Request & request) { -} #endif /* @@ -1359,7 +1340,6 @@ SensorHTU21D::SensorHTU21D(NodeManager* node_manager, int child_id, int pin): Se } #endif -#if MODULE_SWITCH == 1 /* * SensorSwitch */ @@ -1427,19 +1407,6 @@ 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 */ @@ -1457,7 +1424,6 @@ SensorMotion::SensorMotion(NodeManager* node_manager, int child_id, int pin): Se // set initial value to LOW setInitial(LOW); } -#endif /* SensorDs18b20 @@ -1512,17 +1478,6 @@ void SensorDs18b20::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } -// what to do when receiving a remote message -void SensorDs18b20::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setResolution(request.getValueInt()); break; - case 102: setSleepDuringConversion(request.getValueInt()); break; - default: return; - } - _send(_msg_service.set(function)); -} - // function to print a device address DeviceAddress* SensorDs18b20::getDeviceAddress() { return &_device_address; @@ -1581,10 +1536,6 @@ void SensorBH1750::onLoop() { void SensorBH1750::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } - -// what to do when receiving a remote message -void SensorBH1750::onProcess(Request & request) { -} #endif /* @@ -1629,10 +1580,6 @@ void SensorMLX90614::onLoop() { void SensorMLX90614::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } - -// what to do when receiving a remote message -void SensorMLX90614::onProcess(Request & request) { -} #endif @@ -1693,17 +1640,7 @@ void SensorBosch::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } -// what to do when receiving a remote message -void SensorBosch::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setForecastSamplesCount(request.getValueInt()); break; - default: return; - } - _send(_msg_service.set(function)); -} -// calculate and send the forecast back void SensorBosch::_forecast(float pressure) { if (isnan(pressure)) return; // Calculate the average of the last n minutes. @@ -1923,93 +1860,30 @@ void SensorBMP085::onLoop() { } #endif + /* - SensorHCSR04 + SensorSonoff */ -#if MODULE_HCSR04 == 1 +#if MODULE_SONOFF == 1 // contructor -SensorHCSR04::SensorHCSR04(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { - // set presentation and type - setPresentation(S_DISTANCE); - setType(V_DISTANCE); - _trigger_pin = pin; - _echo_pin = pin; -} - -// what to do during before -void SensorHCSR04::onBefore() { - // initialize the library - _sonar = new NewPing(_trigger_pin,_echo_pin,_max_distance); +SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,1) { + setPresentation(S_BINARY); + setType(V_STATUS); } // setter/getter -void SensorHCSR04::setTriggerPin(int value) { - _trigger_pin = value; +void SensorSonoff::setButtonPin(int value) { + _button_pin = value; } -void SensorHCSR04::setEchoPin(int value) { - _echo_pin = value; +void SensorSonoff::setRelayPin(int value) { + _relay_pin = value; } -void SensorHCSR04::setMaxDistance(int value) { - _max_distance = value; +void SensorSonoff::setLedPin(int value) { + _led_pin = value; } -// what to do during setup -void SensorHCSR04::onSetup() { -} - -// what to do during loop -void SensorHCSR04::onLoop() { - int distance = _node_manager->getIsMetric() ? _sonar->ping_cm() : _sonar->ping_in(); - #if DEBUG == 1 - Serial.print(F("HC I=")); - Serial.print(_child_id); - Serial.print(F(" D=")); - Serial.println(distance); - #endif - _value_int = distance; -} - -// what to do as the main task when receiving a message -void SensorHCSR04::onReceive(const MyMessage & message) { - if (message.getCommand() == C_REQ) onLoop(); -} - -// what to do when receiving a remote message -void SensorHCSR04::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setTriggerPin(request.getValueInt()); break; - case 102: setEchoPin(request.getValueInt()); break; - case 103: setMaxDistance(request.getValueInt()); break; - default: return; - } - _send(_msg_service.set(function)); -} -#endif - -/* - SensorSonoff -*/ -#if MODULE_SONOFF == 1 -// contructor -SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,1) { - setPresentation(S_BINARY); - setType(V_STATUS); -} - -// setter/getter -void SensorSonoff::setButtonPin(int value) { - _button_pin = value; -} -void SensorSonoff::setRelayPin(int value) { - _relay_pin = value; -} -void SensorSonoff::setLedPin(int value) { - _led_pin = value; -} - -// what to do during before -void SensorSonoff::onBefore() { +// what to do during before +void SensorSonoff::onBefore() { } // what to do during setup @@ -2057,18 +1931,6 @@ void SensorSonoff::onReceive(const MyMessage & message) { } } -// what to do when receiving a remote message -void SensorSonoff::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 101: setButtonPin(request.getValueInt()); break; - case 102: setRelayPin(request.getValueInt()); break; - case 103: setLedPin(request.getValueInt()); break; - default: return; - } - _send(_msg_service.set(function)); -} - // toggle the state void SensorSonoff::_toggle() { // toggle the state @@ -2100,211 +1962,96 @@ void SensorSonoff::_blink() { /* - SensorMCP9808 + SensorHCSR04 */ -#if MODULE_MCP9808 == 1 +#if MODULE_HCSR04 == 1 // contructor -SensorMCP9808::SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp): Sensor(node_manager, child_id,A2) { - _mcp = mcp; - setPresentation(S_TEMP); - setType(V_TEMP); - setValueType(TYPE_FLOAT); +SensorHCSR04::SensorHCSR04(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { + // set presentation and type + setPresentation(S_DISTANCE); + setType(V_DISTANCE); + _trigger_pin = pin; + _echo_pin = pin; } // what to do during before -void SensorMCP9808::onBefore() { +void SensorHCSR04::onBefore() { + // initialize the library + _sonar = new NewPing(_trigger_pin,_echo_pin,_max_distance); +} + +// setter/getter +void SensorHCSR04::setTriggerPin(int value) { + _trigger_pin = value; +} +void SensorHCSR04::setEchoPin(int value) { + _echo_pin = value; +} +void SensorHCSR04::setMaxDistance(int value) { + _max_distance = value; } // what to do during setup -void SensorMCP9808::onSetup() { +void SensorHCSR04::onSetup() { } // what to do during loop -void SensorMCP9808::onLoop() { - float temperature = _mcp->readTempC(); - // convert it - temperature = _node_manager->celsiusToFahrenheit(temperature); +void SensorHCSR04::onLoop() { + int distance = _node_manager->getIsMetric() ? _sonar->ping_cm() : _sonar->ping_in(); #if DEBUG == 1 - Serial.print(F("MCP I=")); + Serial.print(F("HC I=")); Serial.print(_child_id); - Serial.print(F(" T=")); - Serial.println(temperature); + Serial.print(F(" D=")); + Serial.println(distance); #endif - // store the value - if (! isnan(temperature)) _value_float = temperature; + _value_int = distance; } // what to do as the main task when receiving a message -void SensorMCP9808::onReceive(const MyMessage & message) { +void SensorHCSR04::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } - -// what to do when receiving a remote message -void SensorMCP9808::onProcess(Request & request) { -} #endif - /* - * SensorMQ - */ -#if MODULE_MQ == 1 -SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { - setPresentation(S_AIR_QUALITY); - setType(V_LEVEL); -} - -//setter/getter -void SensorMQ::setRlValue(float value) { - _rl_value = value; -} -void SensorMQ::setRoValue(float value) { - _ro = value; -} -void SensorMQ::setCleanAirFactor(float value) { - _ro_clean_air_factor = value; -} -void SensorMQ::setCalibrationSampleTimes(int value) { - _calibration_sample_times = value; -} -void SensorMQ::setCalibrationSampleInterval(int value){ - _calibration_sample_interval = value; -} -void SensorMQ::setReadSampleTimes(int value) { - _read_sample_times = value; -} -void SensorMQ::setReadSampleInterval(int value) { - _read_sample_interval = value; -} -void SensorMQ::setLPGCurve(float *value) { - _LPGCurve[0] = value[0]; - _LPGCurve[2] = value[1]; - _LPGCurve[2] = value[2]; -} -void SensorMQ::setCOCurve(float *value) { - _COCurve[0] = value[0]; - _COCurve[2] = value[1]; - _COCurve[2] = value[2]; -} -void SensorMQ::setSmokeCurve(float *value) { - _SmokeCurve[0] = value[0]; - _SmokeCurve[2] = value[1]; - _SmokeCurve[2] = value[2]; + SensorMCP9808 +*/ +#if MODULE_MCP9808 == 1 +// contructor +SensorMCP9808::SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp): Sensor(node_manager, child_id,A2) { + _mcp = mcp; + setPresentation(S_TEMP); + setType(V_TEMP); + setValueType(TYPE_FLOAT); } // what to do during before -void SensorMQ::onBefore() { - // prepare the pin for input - pinMode(_pin, INPUT); +void SensorMCP9808::onBefore() { } // what to do during setup -void SensorMQ::onSetup() { - _ro = _MQCalibration(); +void SensorMCP9808::onSetup() { } // what to do during loop -void SensorMQ::onLoop() { - if (_pin == -1) return; - // calculate rs/ro - float mq = _MQRead()/_ro; - // calculate the ppm - float lpg = _MQGetGasPercentage(mq,_gas_lpg); - float co = _MQGetGasPercentage(mq,_gas_co); - float smoke = _MQGetGasPercentage(mq,_gas_smoke); - // assign to the value the requested gas - uint16_t value; - if (_target_gas == _gas_lpg) value = lpg; - if (_target_gas == _gas_co) value = co; - if (_target_gas == _gas_smoke) value = smoke; +void SensorMCP9808::onLoop() { + float temperature = _mcp->readTempC(); + // convert it + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 - Serial.print(F("MQ I=")); + Serial.print(F("MCP I=")); Serial.print(_child_id); - Serial.print(F(" V=")); - Serial.print(value); - Serial.print(F(" LPG=")); - Serial.print(lpg); - Serial.print(F(" CO=")); - Serial.print(co); - Serial.print(F(" SMOKE=")); - Serial.println(smoke); + Serial.print(F(" T=")); + Serial.println(temperature); #endif // store the value - _value_int = (int16_t)ceil(value); + if (! isnan(temperature)) _value_float = temperature; } // what to do as the main task when receiving a message -void SensorMQ::onReceive(const MyMessage & message) { +void SensorMCP9808::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } - -// what to do when receiving a remote message -void SensorMQ::onProcess(Request & request) { - int function = request.getFunction(); - switch(function) { - case 1: setTargetGas(request.getValueInt()); break; - case 2: setRlValue(request.getValueFloat()); break; - case 3: setRoValue(request.getValueFloat()); break; - case 4: setCleanAirFactor(request.getValueFloat()); break; - case 5: setCalibrationSampleTimes(request.getValueInt()); break; - case 6: setCalibrationSampleInterval(request.getValueInt()); break; - case 7: setReadSampleTimes(request.getValueInt()); break; - case 8: setReadSampleInterval(request.getValueInt()); break; - default: return; - } - _send(_msg_service.set(function)); -} - -// returns the calculated sensor resistance -float SensorMQ::_MQResistanceCalculation(int raw_adc) { - return ( ((float)_rl_value*(1023-raw_adc)/raw_adc)); -} - -// This function assumes that the sensor is in clean air -float SensorMQ::_MQCalibration() { - int i; - float val=0; - //take multiple samples - for (i=0; i< _calibration_sample_times; i++) { - val += _MQResistanceCalculation(analogRead(_pin)); - wait(_calibration_sample_interval); - } - //calculate the average value - val = val/_calibration_sample_times; - //divided by RO_CLEAN_AIR_FACTOR yields the Ro - val = val/_ro_clean_air_factor; - //according to the chart in the datasheet - return val; -} - -// This function use MQResistanceCalculation to caculate the sensor resistenc (Rs). -float SensorMQ::_MQRead() { - int i; - float rs=0; - for (i=0; i<_read_sample_times; i++) { - rs += _MQResistanceCalculation(analogRead(_pin)); - wait(_read_sample_interval); - } - rs = rs/_read_sample_times; - return rs; -} - -// This function passes different curves to the MQGetPercentage function which calculates the ppm (parts per million) of the target gas. -int SensorMQ::_MQGetGasPercentage(float rs_ro_ratio, int gas_id) { - if ( gas_id == _gas_lpg ) { - return _MQGetPercentage(rs_ro_ratio,_LPGCurve); - } else if ( gas_id == _gas_co) { - return _MQGetPercentage(rs_ro_ratio,_COCurve); - } else if ( gas_id == _gas_smoke) { - return _MQGetPercentage(rs_ro_ratio,_SmokeCurve); - } - return 0; -} - -// returns ppm of the target gas -int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) { - return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0]))); -} #endif /******************************************* @@ -2369,9 +2116,9 @@ int NodeManager::getSleepUnit() { return _sleep_unit; } void NodeManager::setSleep(int value1, int value2, int value3) { - setMode(value1); - setSleepTime(value2); - setSleepUnit(value3); + _sleep_mode = value1; + _sleep_time = value2; + _sleep_unit = value3; } void NodeManager::setSleepInterruptPin(int value) { _sleep_interrupt_pin = value; @@ -2439,6 +2186,7 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { else if (sensor_type == SENSOR_ANALOG_INPUT) return registerSensor(new SensorAnalogInput(this,child_id, pin)); else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(this,child_id, pin)); else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(this,child_id, pin)); + else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(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)); @@ -2591,11 +2339,6 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { registerSensor(new SensorMCP9808(this,child_id,mcp)); } #endif - #if MODULE_MQ == 1 - else if (sensor_type == SENSOR_MQ) { - return registerSensor(new SensorMQ(this,child_id, pin)); - } - #endif else { #if DEBUG == 1 Serial.print(F("INVALID ")); @@ -2699,9 +2442,26 @@ void NodeManager::before() { Serial.print(F("INT2 M=")); Serial.println(_interrupt_2_mode); #endif - #if PERSIST == 1 - // restore the configuration saved in the eeprom - _loadConfig(); + #if REMOTE_CONFIGURATION == 1 && PERSIST == 1 + // restore sleep configuration from eeprom + if (loadState(EEPROM_SLEEP_SAVED) == 1) { + // sleep settings found in the eeprom, restore them + _sleep_mode = loadState(EEPROM_SLEEP_MODE); + _sleep_time = loadState(EEPROM_SLEEP_TIME_MINOR); + int major = loadState(EEPROM_SLEEP_TIME_MAJOR); + if (major == 1) _sleep_time = _sleep_time + 250; + else if (major == 2) _sleep_time = _sleep_time + 250 * 2; + else if (major == 3) _sleep_time = _sleep_time + 250 * 3; + _sleep_unit = loadState(EEPROM_SLEEP_UNIT); + #if DEBUG == 1 + Serial.print(F("LOADSLP M=")); + Serial.print(_sleep_mode); + Serial.print(F(" T=")); + Serial.print(_sleep_time); + Serial.print(F(" U=")); + Serial.println(_sleep_unit); + #endif + } #endif #if BATTERY_MANAGER == 1 && !defined(MY_GATEWAY_ESP8266) // set analogReference to internal if measuring the battery through a pin @@ -2732,7 +2492,7 @@ void NodeManager::presentation() { // present the battery service _present(BATTERY_CHILD_ID, S_MULTIMETER); // report battery level - batteryReport(); + _process("BATTERY"); #endif // present each sensor for (int i = 0; i < MAX_SENSORS; i++) { @@ -2785,7 +2545,7 @@ void NodeManager::loop() { // if it is time to report the battery level if (_battery_report_timer.isOver()) { // time to report the battery level again - batteryReport(); + _process("BATTERY"); // restart the timer _battery_report_timer.restart(); } @@ -2825,14 +2585,9 @@ void NodeManager::receive(const MyMessage &message) { Serial.print(F(" P=")); Serial.println(message.getString()); #endif - // process incoming configuration message + // process incoming service messages if (message.sensor == CONFIGURATION_CHILD_ID && message.getCommand() == C_REQ && message.type == V_CUSTOM) { - #if REMOTE_CONFIGURATION == 1 - // parse the request - Request request = Request(message.getString()); - // process the request - process(request); - #endif + _process(message.getString()); } // dispatch the message to the registered sensor else if (_sensors[message.sensor] != 0) { @@ -2875,140 +2630,6 @@ void NodeManager::receiveTime(unsigned long ts) { #endif } -// process a request message -void NodeManager::process(Request & request) { - int function = request.getFunction(); - switch(function) { - case 1: hello(); break; - #if BATTERY_MANAGER == 1 - case 2: batteryReport(); return; - case 11: setBatteryMin(request.getValueFloat()); break; - case 12: setBatteryMax(request.getValueFloat()); break; - case 13: setBatteryReportCycles(request.getValueInt()); break; - case 14: setBatteryReportMinutes(request.getValueInt()); break; - case 15: setBatteryInternalVcc(request.getValueInt()); break; - case 16: setBatteryPin(request.getValueInt()); break; - case 17: setBatteryVoltsPerBit(request.getValueFloat()); break; - case 18: setBatteryReportWithInterrupt(request.getValueInt()); break; - #endif - case 3: - setSleepMode(request.getValueInt()); - #if PERSIST == 1 - _saveConfig(SAVE_SLEEP_MODE); - #endif - break; - case 4: - setSleepTime(request.getValueInt()); - #if PERSIST == 1 - _saveConfig(SAVE_SLEEP_TIME); - #endif - break; - case 5: - setSleepUnit(request.getValueInt()); - #if PERSIST == 1 - _saveConfig(SAVE_SLEEP_UNIT); - #endif - break; - #ifndef MY_GATEWAY_ESP8266 - case 6: reboot(); return; - #endif - case 7: clearEeprom(); break; - case 8: version(); return; - case 9: wakeup(); break; - case 10: setRetries(request.getValueInt()); break; - case 19: setSleepInterruptPin(request.getValueInt()); break; - case 20: setSleepBetweenSend(request.getValueInt()); break; - case 21: setAck(request.getValueInt()); break; - case 22: setIsMetric(request.getValueInt()); break; - #if POWER_MANAGER == 1 - case 23: setAutoPowerPins(request.getValueInt()); break; - case 24: powerOn(); break; - case 25: powerOff(); break; - #endif - case 26: unRegisterSensor(request.getValueInt()); break; - case 27: saveToMemory(0,request.getValueInt()); break; - default: return; - } - _send(_msg.set(function)); -} - - -// Send a hello message back to the controller -void NodeManager::hello() { - // do nothing, the request will be echoed back -} - -#if BATTERY_MANAGER == 1 -// Send a battery level report to the controller -void NodeManager::batteryReport() { - // measure the board vcc - float volt = 0; - if (_battery_internal_vcc || _battery_pin == -1) volt = getVcc(); - else volt = analogRead(_battery_pin) * _battery_volts_per_bit; - // calculate the percentage - int percentage = ((volt - _battery_min) / (_battery_max - _battery_min)) * 100; - if (percentage > 100) percentage = 100; - if (percentage < 0) percentage = 0; - #if DEBUG == 1 - Serial.print(F("BATT V=")); - Serial.print(volt); - Serial.print(F(" P=")); - Serial.println(percentage); - #endif - #if BATTERY_SENSOR == 1 - // report battery voltage - MyMessage battery_msg(BATTERY_CHILD_ID, V_VOLTAGE); - _send(battery_msg.set(volt, 2)); - #endif - // report battery level percentage - sendBatteryLevel(percentage,_ack); -} -#endif - -// reboot the board -void NodeManager::reboot() { - #if DEBUG == 1 - Serial.println(F("REBOOT")); - #endif - // Software reboot with watchdog timer. Enter Watchdog Configuration mode: - WDTCSR |= (1< 100) percentage = 100; + if (percentage < 0) percentage = 0; + #if DEBUG == 1 + Serial.print(F("BATT V=")); + Serial.print(volt); + Serial.print(F(" P=")); + Serial.println(percentage); + #endif + #if BATTERY_SENSOR == 1 + // report battery voltage + MyMessage battery_msg(BATTERY_CHILD_ID, V_VOLTAGE); + _send(battery_msg.set(volt, 2)); + #endif + // report battery level percentage + sendBatteryLevel(percentage,_ack); + } + #endif + #ifndef MY_GATEWAY_ESP8266 + // REBOOT: reboot the board + else if (strcmp(message, "REBOOT") == 0) { + #if DEBUG == 1 + Serial.println(F("REBOOT")); + #endif + // set the reboot pin connected to RST to low so to reboot the board + _send(_msg.set(message)); + // Software reboot with watchdog timer. Enter Watchdog Configuration mode: + WDTCSR |= (1< 30%) (default: false) + // reverse the value or the percentage (e.g. 70% -> 30%) (default: false) void setReverse(bool value); - // [103] when true returns the value as a percentage (default: true) + // when true returns the value as a percentage (default: true) void setOutputPercentage(bool value); - // [104] minimum value for calculating the percentage (default: 0) + // minimum value for calculating the percentage (default: 0) void setRangeMin(int value); - // [105] maximum value for calculating the percentage (default: 1024) + // maximum value for calculating the percentage (default: 1024) void setRangeMax(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); protected: int _reference = -1; bool _reverse = false; @@ -564,22 +523,21 @@ class SensorLDR: public SensorAnalogInput { class SensorThermistor: public Sensor { public: SensorThermistor(NodeManager* node_manager, int child_id, int pin); - // [101] resistance at 25 degrees C (default: 10000) + // resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); - // [102] temperature for nominal resistance (default: 25) + // temperature for nominal resistance (default: 25) void setNominalTemperature(int value); - // [103] The beta coefficient of the thermistor (default: 3950) + // The beta coefficient of the thermistor (default: 3950) void setBCoefficient(int value); - // [104] the value of the resistor in series with the thermistor (default: 10000) + // the value of the resistor in series with the thermistor (default: 10000) void setSeriesResistor(long value); - // [105] set a temperature offset + // set a temperature offset void setOffset(float 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); protected: long _nominal_resistor = 10000; int _nominal_temperature = 25; @@ -588,6 +546,61 @@ class SensorThermistor: public Sensor { float _offset = 0; }; +/* + SensorMQ + */ +class SensorMQ: public Sensor { + public: + SensorMQ(NodeManager* node_manager, int child_id, int pin); + // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); + void setTargetGas(int value); + // define the load resistance on the board, in kilo ohms (default: 1); + void setRlValue(float value); + // define the Ro resistance on the board (default: 10000); + void setRoValue(float value); + // Sensor resistance in clean air (default: 9.83); + void setCleanAirFactor(float value); + // define how many samples you are going to take in the calibration phase (default: 50); + void setCalibrationSampleTimes(int value); + // define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); + void setCalibrationSampleInterval(int value); + // define how many samples you are going to take in normal operation (default: 50); + void setReadSampleTimes(int value); + // define the time interal(in milisecond) between each samples in the normal operations (default: 5); + void setReadSampleInterval(int value); + // set the LPGCurve array (default: {2.3,0.21,-0.47}) + void setLPGCurve(float *value); + // set the COCurve array (default: {2.3,0.72,-0.34}) + void setCOCurve(float *value); + // set the SmokeCurve array (default: {2.3,0.53,-0.44}) + void setSmokeCurve(float *value); + // define what to do at each stage of the sketch + void onBefore(); + void onSetup(); + void onLoop(); + void onReceive(const MyMessage & message); + protected: + float _rl_value = 1.0; + float _ro_clean_air_factor = 9.83; + int _calibration_sample_times = 50; + int _calibration_sample_interval = 500; + int _read_sample_interval = 50; + int _read_sample_times = 5; + float _ro = 10000.0; + float _LPGCurve[3] = {2.3,0.21,-0.47}; + float _COCurve[3] = {2.3,0.72,-0.34}; + float _SmokeCurve[3] = {2.3,0.53,-0.44}; + float _MQResistanceCalculation(int raw_adc); + float _MQCalibration(); + float _MQRead(); + int _MQGetGasPercentage(float rs_ro_ratio, int gas_id); + int _MQGetPercentage(float rs_ro_ratio, float *pcurve); + int _gas_lpg = 0; + int _gas_co = 1; + int _gas_smoke = 2; + int _target_gas = _gas_co; +}; + /* SensorML8511 */ @@ -600,7 +613,6 @@ class SensorML8511: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); - void onProcess(Request & request); protected: float _mapfloat(float x, float in_min, float in_max, float out_min, float out_max); }; @@ -612,16 +624,15 @@ class SensorML8511: public Sensor { class SensorACS712: public Sensor { public: SensorACS712(NodeManager* node_manager, int child_id, int pin); - // [101] set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); + // set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); void setmVPerAmp(int value); - // [102] set ACS offset (default: 2500); + // set ACS offset (default: 2500); void setOffset(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); protected: int _ACS_offset = 2500; int _mv_per_amp = 185; @@ -634,16 +645,15 @@ 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) + // 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) + // set how many mm of rain to count for each tip (default: 0.11) void setSingleTip(float 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; @@ -669,10 +679,7 @@ class SensorSoilMoisture: public SensorAnalogInput { public: SensorSoilMoisture(NodeManager* node_manager, int child_id, int pin); }; -#endif - -#if MODULE_DIGITAL_INPUT == 1 /* SensorDigitalInput: read the digital input of the configured pin */ @@ -684,28 +691,25 @@ class SensorDigitalInput: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); - void onProcess(Request & request); }; -#endif -#if MODULE_DIGITAL_OUTPUT == 1 /* SensorDigitalOutput: control a digital output of the configured pin */ class SensorDigitalOutput: public Sensor { public: SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin); - // [101] set how to initialize the output (default: LOW) + // set how to initialize the output (default: LOW) void setInitialValue(int value); - // [102] if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) + // if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) void setPulseWidth(int value); - // [103] define which value to set to the output when set to on (default: HIGH) + // define which value to set to the output when set to on (default: HIGH) void setOnValue(int value); - // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) + // when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) void setLegacyMode(bool value); - // [105] automatically turn the output off after the given number of minutes + // automatically turn the output off after the given number of minutes void setSafeguard(int value); - // [106] if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) + // if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) void setInputIsElapsed(bool value); // manually switch the output to the provided value void set(int value); @@ -716,7 +720,6 @@ class SensorDigitalOutput: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); - void onProcess(Request & request); protected: int _initial_value = LOW; int _on_value = HIGH; @@ -734,6 +737,8 @@ class SensorDigitalOutput: public Sensor { class SensorRelay: public SensorDigitalOutput { public: SensorRelay(NodeManager* node_manager, int child_id, int pin); + // define what to do at each stage of the sketch + //void onLoop(); }; /* @@ -743,7 +748,6 @@ class SensorLatchingRelay: public SensorRelay { public: SensorLatchingRelay(NodeManager* node_manager, int child_id, int pin); }; -#endif /* SensorDHT @@ -757,7 +761,6 @@ class SensorDHT: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); - void onProcess(Request & request); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -781,7 +784,6 @@ class SensorSHT21: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); - void onProcess(Request & request); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -803,18 +805,17 @@ class SensorHTU21D: public SensorSHT21 { /* * SensorSwitch */ -#if MODULE_SWITCH == 1 class SensorSwitch: public Sensor { public: SensorSwitch(NodeManager* node_manager, int child_id, int pin); - // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) + // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); int getMode(); - // [102] milliseconds to wait before reading the input (default: 0) + // milliseconds to wait before reading the input (default: 0) void setDebounce(int value); - // [103] time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) + // time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) void setTriggerTime(int value); - // [104] Set initial value on the interrupt pin (default: HIGH) + // Set initial value on the interrupt pin (default: HIGH) void setInitial(int value); int getInitial(); // define what to do at each stage of the sketch @@ -822,7 +823,6 @@ class SensorSwitch: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); - void onProcess(Request & request); protected: int _debounce = 0; int _trigger_time = 0; @@ -845,7 +845,7 @@ class SensorMotion: public SensorSwitch { public: SensorMotion(NodeManager* node_manager, int child_id, int pin); }; -#endif + /* SensorDs18b20 */ @@ -853,20 +853,19 @@ class SensorMotion: public SensorSwitch { class SensorDs18b20: public Sensor { public: SensorDs18b20(NodeManager* node_manager, int child_id, int pin, DallasTemperature* sensors, int index); + // return the sensors' device address + DeviceAddress* getDeviceAddress(); // returns the sensor's resolution in bits int getResolution(); - // [101] set the sensor's resolution in bits + // set the sensor's resolution in bits void setResolution(int value); - // [102] sleep while DS18B20 calculates temperature (default: false) + // sleep while DS18B20 calculates temperature (default: false) void setSleepDuringConversion(bool value); - // return the sensors' device address - DeviceAddress* getDeviceAddress(); // 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); protected: float _offset = 0; int _index; @@ -888,7 +887,6 @@ class SensorBH1750: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); - void onProcess(Request & request); protected: BH1750* _lightSensor; }; @@ -906,7 +904,6 @@ class SensorMLX90614: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); - void onProcess(Request & request); // constants const static int TEMPERATURE_AMBIENT = 0; const static int TEMPERATURE_OBJECT = 1; @@ -925,14 +922,13 @@ class SensorMLX90614: public Sensor { class SensorBosch: public Sensor { public: SensorBosch(NodeManager* node_manager, int child_id, int sensor_type); - // [101] define how many pressure samples to keep track of for calculating the forecast (default: 5) + // define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(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); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -987,18 +983,17 @@ class SensorBMP085: public SensorBosch { class SensorHCSR04: public Sensor { public: SensorHCSR04(NodeManager* node_manager, int child_id, int pin); - // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setTriggerPin(int value); - // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setEchoPin(int value); - // [103] Maximum distance we want to ping for (in centimeters) (default: 300) + // Maximum distance we want to ping for (in centimeters) (default: 300) void setMaxDistance(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); protected: int _trigger_pin; int _echo_pin; @@ -1014,18 +1009,17 @@ class SensorHCSR04: public Sensor { class SensorSonoff: public Sensor { public: SensorSonoff(NodeManager* node_manager, int child_id); - // [101] set the button's pin (default: 0) + // set the button's pin (default: 0) void setButtonPin(int value); - // [102] set the relay's pin (default: 12) + // set the relay's pin (default: 12) void setRelayPin(int value); - // [103] set the led's pin (default: 13) + // set the led's pin (default: 13) void setLedPin(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); protected: Bounce _debouncer = Bounce(); int _button_pin = 0; @@ -1054,69 +1048,11 @@ class SensorMCP9808: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); - void onProcess(Request & request); protected: Adafruit_MCP9808* _mcp; }; #endif -/* - SensorMQ - */ - #if MODULE_MQ == 1 -class SensorMQ: public Sensor { - public: - SensorMQ(NodeManager* node_manager, int child_id, int pin); - // [101] define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); - void setTargetGas(int value); - // [102] define the load resistance on the board, in kilo ohms (default: 1); - void setRlValue(float value); - // [103] define the Ro resistance on the board (default: 10000); - void setRoValue(float value); - // [104] Sensor resistance in clean air (default: 9.83); - void setCleanAirFactor(float value); - // [105] define how many samples you are going to take in the calibration phase (default: 50); - void setCalibrationSampleTimes(int value); - // [106] define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); - void setCalibrationSampleInterval(int value); - // [107] define how many samples you are going to take in normal operation (default: 50); - void setReadSampleTimes(int value); - // [108] define the time interal(in milisecond) between each samples in the normal operations (default: 5); - void setReadSampleInterval(int value); - // set the LPGCurve array (default: {2.3,0.21,-0.47}) - void setLPGCurve(float *value); - // set the COCurve array (default: {2.3,0.72,-0.34}) - void setCOCurve(float *value); - // set the SmokeCurve array (default: {2.3,0.53,-0.44}) - void setSmokeCurve(float *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); - protected: - float _rl_value = 1.0; - float _ro_clean_air_factor = 9.83; - int _calibration_sample_times = 50; - int _calibration_sample_interval = 500; - int _read_sample_interval = 50; - int _read_sample_times = 5; - float _ro = 10000.0; - float _LPGCurve[3] = {2.3,0.21,-0.47}; - float _COCurve[3] = {2.3,0.72,-0.34}; - float _SmokeCurve[3] = {2.3,0.53,-0.44}; - float _MQResistanceCalculation(int raw_adc); - float _MQCalibration(); - float _MQRead(); - int _MQGetGasPercentage(float rs_ro_ratio, int gas_id); - int _MQGetPercentage(float rs_ro_ratio, float *pcurve); - int _gas_lpg = 0; - int _gas_co = 1; - int _gas_smoke = 2; - int _target_gas = _gas_co; -}; -#endif /*************************************** NodeManager: manages all the aspects of the node @@ -1124,51 +1060,51 @@ class SensorMQ: public Sensor { class NodeManager { public: NodeManager(); - // [10] send the same service message multiple times (default: 1) + // the pin to connect to the RST pin to reboot the board (default: 4) + void setRebootPin(int value); + // send the same service message multiple times (default: 1) void setRetries(int value); #if BATTERY_MANAGER == 1 - // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) + // the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) void setBatteryMin(float value); - // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) + // the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // [13] after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) + // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) void setBatteryReportCycles(int value); - // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) + // after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) void setBatteryReportMinutes(int value); - // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) + // if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) void setBatteryInternalVcc(bool value); - // [16] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) + // if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) void setBatteryPin(int value); - // [17] if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) + // if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) void setBatteryVoltsPerBit(float value); - // [18] If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) + // If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) void setBatteryReportWithInterrupt(bool value); - // [2] Send a battery level report to the controller - void batteryReport(); #endif - // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) + // define the way the node should behave. It can be IDLE (stay awake withtout executing each sensors' loop), SLEEP (go to sleep for the configured interval), WAIT (wait for the configured interval), ALWAYS_ON (stay awake and execute each sensors' loop) void setSleepMode(int value); void setMode(int value); int getMode(); - // [4] define for how long the board will sleep (default: 0) + // define for how long the board will sleep (default: 0) void setSleepTime(int value); int getSleepTime(); - // [5] define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) + // define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) void setSleepUnit(int value); int getSleepUnit(); // configure the node's behavior, parameters are mode, time and unit void setSleep(int value1, int value2, int value3); - // [19] if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true) + // 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); - // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) + // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); // register a built-in sensor int registerSensor(int sensor_type, int pin = -1, int child_id = -1); // register a custom sensor int registerSensor(Sensor* sensor); - // [26] un-register a sensor + // un-register a sensor void unRegisterSensor(int sensor_index); // return a sensor by its index Sensor* get(int sensor_index); @@ -1178,42 +1114,26 @@ class NodeManager { #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); - // [23] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) + // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) void setAutoPowerPins(bool value); - // [24] manually turn the power on + // manually turn the power on void powerOn(); - // [25] manually turn the power off + // manually turn the power off void powerOff(); #endif - // [21] set this to true if you want destination node to send ack back to this node (default: false) + // set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); // request and return the current timestamp from the controller long getTimestamp(); // Request the controller's configuration on startup (default: true) void setGetControllerConfig(bool value); - // [22] Manually set isMetric setting + // Manually set isMetric setting void setIsMetric(bool value); bool getIsMetric(); // Convert a temperature from celsius to fahrenheit depending on how isMetric is set float celsiusToFahrenheit(float temperature); // return true if sleep or wait is configured and hence this is a sleeping node bool isSleepingNode(); - // [1] Send a hello message back to the controller - void hello(); - // [6] reboot the board - void reboot(); - // [8] send NodeManager's the version back to the controller - void version(); - // [7] clear the EEPROM - void clearEeprom(); - // [9] wake up the board - void wakeup(); - // process a remote request - void process(Request & request); - // return the value stored at the requested index from the EEPROM - int loadFromMemory(int index); - // [27] save the given index of the EEPROM the provided value - void saveToMemory(int index, int value); // hook into the main sketch functions void before(); void presentation(); @@ -1253,14 +1173,13 @@ class NodeManager { long _timestamp = -1; Sensor* _sensors[MAX_SENSORS] = {0}; bool _ack = false; + void _process(const char * message); void _sleep(); void _present(int child_id, int type); int _getAvailableChildId(); int _getInterruptInitialValue(int mode); bool _get_controller_config = true; int _is_metric = 1; - void _loadConfig(); - void _saveConfig(int what); }; #endif diff --git a/NodeManager.ino b/NodeManager.ino index c1d9f953..4e6def9f 100644 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -33,14 +33,10 @@ void before() { /* * Register below your sensors */ - int n = nodeManager.registerSensor(SENSOR_THERMISTOR,A1); - Sensor* sensor = nodeManager.get(n); - Serial.println(sizeof(Sensor)); - Serial.println(sizeof(SensorThermistor)); - Serial.println(sizeof(NodeManager)); - Serial.println(sizeof(Timer)); - + + + /* * Register above your sensors */ diff --git a/README.md b/README.md index e5cc8a87..f9c299d3 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Those NodeManager's directives in the `config.h` file control which module/libra // if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage #define BATTERY_SENSOR 1 -// 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_MQ, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE, 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 @@ -171,8 +171,6 @@ Those NodeManager's directives in the `config.h` file control which module/libra #define MODULE_HCSR04 0 // Enable this module to use one of the following sensors: SENSOR_MCP9808 #define MODULE_MCP9808 0 -// Enable this module to use one of the following sensors: SENSOR_MQ -#define MODULE_MQ 0 ~~~ ### Installing the dependencies @@ -197,96 +195,78 @@ MODULE_MCP9808 | https://github.com/adafruit/Adafruit_MCP9808_Library Node Manager comes with a reasonable default configuration. If you want/need to change its settings, this can be done in your sketch, inside the `before()` function and just before registering your sensors. The following methods are exposed for your convenience: ~~~c - // [10] send the same service message multiple times (default: 1) + // send the same service message multiple times (default: 1) void setRetries(int value); #if BATTERY_MANAGER == 1 - // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) + // the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) void setBatteryMin(float value); - // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) + // the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // [13] after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) + // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) void setBatteryReportCycles(int value); - // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) + // after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) void setBatteryReportMinutes(int value); - // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) + // if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) void setBatteryInternalVcc(bool value); - // [16] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) + // if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) void setBatteryPin(int value); - // [17] if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) + // if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) void setBatteryVoltsPerBit(float value); - // [18] If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) + // If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) void setBatteryReportWithInterrupt(bool value); #endif - // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) + // define the way the node should behave. It can be IDLE (stay awake withtout executing each sensors' loop), SLEEP (go to sleep for the configured interval), WAIT (wait for the configured interval), ALWAYS_ON (stay awake and execute each sensors' loop) void setSleepMode(int value); void setMode(int value); - int getMode(); - // [4] define for how long the board will sleep (default: 0) + // define for how long the board will sleep (default: 0) void setSleepTime(int value); - int getSleepTime(); - // [5] define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) + // define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) void setSleepUnit(int value); - int getSleepUnit(); // configure the node's behavior, parameters are mode, time and unit void setSleep(int value1, int value2, int value3); - // [19] if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true) + // 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); + #endif // 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); - // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) + // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); + // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1) + void setInterruptPin(int value); // register a built-in sensor int registerSensor(int sensor_type, int pin = -1, int child_id = -1); + // un-register a sensor + void unRegisterSensor(int sensor_index); // register a custom sensor int registerSensor(Sensor* sensor); - // [26] un-register a sensor - void unRegisterSensor(int sensor_index); // return a sensor by its index Sensor* get(int sensor_index); - Sensor* getSensor(int sensor_index); - // assign a different child id to a sensor + Sensor* getSensor(int sensor_index); + // assign a different child id to a sensor bool renameSensor(int old_child_id, int new_child_id); #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); - // [23] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) + void setPowerPins(int ground_pin, int vcc_pin, long wait = 0); + // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) void setAutoPowerPins(bool value); - // [24] manually turn the power on + // manually turn the power on void powerOn(); - // [25] manually turn the power off + // manually turn the power off void powerOff(); #endif - // [21] set this to true if you want destination node to send ack back to this node (default: false) + // set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); // request and return the current timestamp from the controller long getTimestamp(); // Request the controller's configuration on startup (default: true) void setGetControllerConfig(bool value); - // [22] Manually set isMetric setting + // Manually set isMetric setting void setIsMetric(bool value); bool getIsMetric(); // Convert a temperature from celsius to fahrenheit depending on how isMetric is set float celsiusToFahrenheit(float temperature); // return true if sleep or wait is configured and hence this is a sleeping node bool isSleepingNode(); - // [1] Send a hello message back to the controller - void hello(); - // [2] Send a battery level report to the controller - void batteryReport(); - // [6] reboot the board - void reboot(); - // [8] send NodeManager's the version back to the controller - void version(); - // [7] clear the EEPROM - void clearEeprom(); - // [9] wake up the board - void wakeup(); - // process a remote request - void process(const char * message); - // return the value stored at the requested index from the EEPROM - int loadFromMemory(int index); - // save the given index of the EEPROM the provided value - void saveToMemory(int index, int value); ~~~ For example @@ -351,8 +331,6 @@ If you want to create a custom sensor and register it with NodeManager so it can void onLoop(); // define what to do during receive() when the sensor receives a message void onReceive(const MyMessage & message); - // define what to do when receiving a remote configuration message - void onProcess(Request & request); ~~~ You can then instantiate your newly created class and register with NodeManager: @@ -374,40 +352,40 @@ To do so, use `nodeManager.getSensor(child_id)` which will return a pointer to t The following methods are available for all the sensors: ~~~c - // [1] where the sensor is attached to (default: not set) + // where the sensor is attached to (default: not set) void setPin(int value); - int getPin(); - // [2] child_id of this sensor (default: not set) + int getPin(); + // child_id of this sensor (default: not set) void setChildId(int value); - int getChildId(); + int getChildId(); // presentation of this sensor (default: S_CUSTOM) void setPresentation(int value); - int getPresentation(); - // [3] type of this sensor (default: V_CUSTOM) + int getPresentation(); + // type of this sensor (default: V_CUSTOM) void setType(int value); - int getType(); - // [4] description of the sensor (default: '') + int getType(); + // description of the sensor (default: '') void setDescription(char *value); // set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); // when queried, send the message multiple times (default: 1) void setRetries(int value); - // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) + // For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) void setSamples(int value); - // [6] If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) + // If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) void setSamplesInterval(int value); - // [7] if true will report the measure only if different than the previous one (default: false) + // if true will report the measure only if different then the previous one (default: false) void setTrackLastValue(bool value); - // [8] if track last value is enabled, force to send an update after the configured number of cycles (default: -1) + // if track last value is enabled, force to send an update after the configured number of cycles (default: -1) void setForceUpdate(int value); void setForceUpdateCycles(int value); - // [9] if track last value is enabled, force to send an update after the configured number of minutes (default: -1) + // if track last value is enabled, force to send an update after the configured number of minutes (default: -1) void setForceUpdateMinutes(int value); - // [10] the value type of this sensor (default: TYPE_INTEGER) + // the value type of this sensor (default: TYPE_INTEGER) void setValueType(int value); - int getValueType(); - // [11] for float values, set the float precision (default: 2) - void setFloatPrecision(int value); + int getValueType(); + // for float values, set the float precision (default: 2) + void setFloatPrecision(int value); // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1) @@ -415,24 +393,22 @@ The following methods are available for all the sensors: int getInterruptPin(); #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); - // [12] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) + void setPowerPins(int ground_pin, int vcc_pin, long wait = 0); + // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) void setAutoPowerPins(bool value); - // [13] manually turn the power on + // manually turn the power on void powerOn(); - // [14] manually turn the power off + // manually turn the power off void powerOff(); #endif // get the latest recorded value from the sensor int getValueInt(); float getValueFloat(); char* getValueString(); - // [15] After how many cycles the sensor will report back its measure (default: 1 cycle) + // After how many cycles the sensor will report back its measure (default: 1 cycle) void setReportIntervalCycles(int value); - // [16] After how many minutes the sensor will report back its measure (default: 1 cycle) + // After how many minutes the sensor will report back its measure (default: 1 cycle) void setReportIntervalMinutes(int value); - // process a remote request - void process(Request & request); ~~~ #### Sensor's specific configuration @@ -441,49 +417,49 @@ Each sensor class can expose additional methods. * SensorAnalogInput / SensorLDR / SensorRain / SensorSoilMoisture ~~~c - // [101] the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) + // the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) void setReference(int value); - // [102] reverse the value or the percentage (e.g. 70% -> 30%) (default: false) + // reverse the value or the percentage (e.g. 70% -> 30%) (default: false) void setReverse(bool value); - // [103] when true returns the value as a percentage (default: true) + // when true returns the value as a percentage (default: true) void setOutputPercentage(bool value); - // [104] minimum value for calculating the percentage (default: 0) + // minimum value for calculating the percentage (default: 0) void setRangeMin(int value); - // [105] maximum value for calculating the percentage (default: 1024) + // maximum value for calculating the percentage (default: 1024) void setRangeMax(int value); ~~~ * SensorThermistor ~~~c - // [101] resistance at 25 degrees C (default: 10000) + // resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); - // [102] temperature for nominal resistance (default: 25) + // temperature for nominal resistance (default: 25) void setNominalTemperature(int value); - // [103] The beta coefficient of the thermistor (default: 3950) + // The beta coefficient of the thermistor (default: 3950) void setBCoefficient(int value); - // [104] the value of the resistor in series with the thermistor (default: 10000) + // the value of the resistor in series with the thermistor (default: 10000) void setSeriesResistor(long value); - // [105] set a temperature offset + // set a temperature offset void setOffset(float value); ~~~ * SensorMQ ~~~c - // [101] define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); + // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); void setTargetGas(int value); - // [102] define the load resistance on the board, in kilo ohms (default: 1); + // define the load resistance on the board, in kilo ohms (default: 1); void setRlValue(float value); - // [103] define the Ro resistance on the board (default: 10000); + // define the Ro resistance on the board (default: 10000); void setRoValue(float value); - // [104] Sensor resistance in clean air (default: 9.83); + // Sensor resistance in clean air (default: 9.83); void setCleanAirFactor(float value); - // [105] define how many samples you are going to take in the calibration phase (default: 50); + // define how many samples you are going to take in the calibration phase (default: 50); void setCalibrationSampleTimes(int value); - // [106] define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); + // define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); void setCalibrationSampleInterval(int value); - // [107] define how many samples you are going to take in normal operation (default: 50); + // define how many samples you are going to take in normal operation (default: 50); void setReadSampleTimes(int value); - // [108] define the time interal(in milisecond) between each samples in the normal operations (default: 5); + // define the time interal(in milisecond) between each samples in the normal operations (default: 5); void setReadSampleInterval(int value); // set the LPGCurve array (default: {2.3,0.21,-0.47}) void setLPGCurve(float *value); @@ -493,35 +469,19 @@ Each sensor class can expose additional methods. void setSmokeCurve(float *value); ~~~ -* SensorACS712 -~~~c - // [101] set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); - void setmVPerAmp(int value); - // [102] set ACS offset (default: 2500); - void setOffset(int value); -~~~ - -* 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); -~~~ - * SensorDigitalOutput / SensorRelay / SensorLatchingRelay ~~~c - // [101] set how to initialize the output (default: LOW) + // set how to initialize the output (default: LOW) void setInitialValue(int value); - // [102] if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) + // if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) void setPulseWidth(int value); - // [103] define which value to set to the output when set to on (default: HIGH) + // define which value to set to the output when set to on (default: HIGH) void setOnValue(int value); - // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) + // when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) void setLegacyMode(bool value); - // [105] automatically turn the output off after the given number of minutes + // automatically turn the output off after the given number of minutes void setSafeguard(int value); - // [106] if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) + // if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) void setInputIsElapsed(bool value); // manually switch the output to the provided value void set(int value); @@ -531,54 +491,74 @@ Each sensor class can expose additional methods. * SensorSwitch / SensorDoor / SensorMotion ~~~c - // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) + // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); - int getMode(); - // [102] milliseconds to wait before reading the input (default: 0) + // milliseconds to wait before reading the input (default: 0) void setDebounce(int value); - // [103] time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) + // time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) void setTriggerTime(int value); - // [104] Set initial value on the interrupt pin (default: HIGH) + // Set initial value on the interrupt pin (default: HIGH) void setInitial(int value); - int getInitial(); ~~~ * SensorDs18b20** ~~~c + // return the sensors' device address + DeviceAddress* getDeviceAddress(); // returns the sensor's resolution in bits int getResolution(); - // [101] set the sensor's resolution in bits + // set the sensor's resolution in bits void setResolution(int value); - // [102] sleep while DS18B20 calculates temperature (default: false) + // sleep while DS18B20 calculates temperature (default: false) void setSleepDuringConversion(bool value); - // return the sensors' device address - DeviceAddress* getDeviceAddress(); ~~~ -* SensorBME280 / SensorBMP085 +* SensorBME280 ~~~c - // [101] define how many pressure samples to keep track of for calculating the forecast (default: 5) + // define how many pressure samples to keep track of for calculating the forecast (default: 5) + void setForecastSamplesCount(int value); +~~~ + +* SensorSonoff +~~~c + // set the button's pin (default: 0) + void setButtonPin(int value); + // set the relay's pin (default: 12) + void setRelayPin(int value); + // set the led's pin (default: 13) + void setLedPin(int value); +~~~ + +* SensorBMP085 +~~~c + // define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); ~~~ * SensorHCSR04 ~~~c - // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setTriggerPin(int value); - // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setEchoPin(int value); - // [103] Maximum distance we want to ping for (in centimeters) (default: 300) + // Maximum distance we want to ping for (in centimeters) (default: 300) void setMaxDistance(int value); ~~~ -* SensorSonoff +* SensorACS712 ~~~c - // [101] set the button's pin (default: 0) - void setButtonPin(int value); - // [102] set the relay's pin (default: 12) - void setRelayPin(int value); - // [103] set the led's pin (default: 13) - void setLedPin(int value); + // set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); + void setmVPerAmp(int value); + // set ACS offset (default: 2500); + void setOffset(int value); +~~~ + +* SensorRainGauge +~~~c + // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60); + void setReportInterval(int value); + // set how many mm of rain to count for each tip (default: 0.11); + void setSingleTip(float value); ~~~ ### Upload your sketch @@ -590,65 +570,51 @@ When `DEBUG` is enabled, detailed information is available through the serial po ### Communicate with NodeManager and its sensors -You can interact with each registered sensor by asking to execute their main tasks by sending to the child id a `REQ` command (or a `SET` for output sensors like relays). For example to request the temperature to node_id 254 and child_id 1: +You can interact with each registered sensor asking to execute their main tasks by sending to the child id a `REQ` command. For example to request the temperature to node_id 254 and child_id 1: `254;1;2;0;0;` To activate a relay connected to the same node, child_id 100: -`254;100;1;0;2;1` +`254;100;2;0;2;1` No need to implement anything on your side since for built-in sensor types this is handled automatically. -Once the node will be sleeping, it will report automatically each measure at the end of every sleep cycle, unless configured otherwise. +Once the node will be sleeping, it will report automatically each measure at the end of every sleep cycle. -NodeManager exposes also a configuration service by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message if successful. -Almost all the functions made available through the API can be called remotely. To do so, the payload must be in the format `[,]` where function_id is the number between square brackets you can find in the description just above each function and, if the function takes and argument, this can be passed along in value_to_set. -For example, to request a battery report, find the function you need to call remotely within the documentation: -~~~c - // [2] Send a battery level report to the controller - void batteryReport(); -~~~ -In this case the function_id will be 2. To request a battery report to the node_id 100, send the following message: -`;;;0;;` -`100;200;2;0;48;2` +NodeManager exposes a configuration service by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message. +The following custom commands are available: -The change the sleep time from e.g. 10 minutes as set in the sketch to 5 minutes: -~~~c - // [4] define for how long the board will sleep (default: 0) - void setSleepTime(int value); -~~~ -`;;;0;;,` -`100;200;2;0;48;4,5` +NodeManager command | Description + ------------- | ------------- +BATTERY | Report the battery level back to the gateway/controller +HELLO | Hello request +REBOOT | Reboot the board +CLEAR | Wipe from the EEPROM NodeManager's settings +VERSION | Respond with NodeManager's version +IDxxx | Change the node id to the provided one. E.g. ID025: change the node id to 25. Requires a reboot to take effect +INTVLnnnX | Set the wait/sleep interval to nnn where X is S=Seconds, M=mins, H=Hours, D=Days. E.g. INTVL010M would be 10 minutes +MODEx | change the way the node behaves. 0: stay awake withtout executing each sensors' loop(), 1: go to sleep for the configured interval, 2: wait for the configured interval, 3: stay awake and execute each sensors' loop() +AWAKE | When received after a sleeping cycle or during wait, abort the cycle and stay awake + +For example, to request the battery level to node id 254: + +`254;200;2;0;48;BATTERY` + +To set the sleeping cycle to 1 hour: + +`254;200;2;0;48;INTVL001H` To ask the node to start sleeping (and waking up based on the previously configured interval): -~~~c - // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) - void setSleepMode(int value); -~~~ -`100;200;2;0;48;3,1` -To wake up a node previously configured as sleeping, send the following just it wakes up next: -~~~c - // [9] wake up the board - void wakeup(); -~~~ -`100;200;2;0;48;9` +`254;200;2;0;48;MODE1` -The same protocol can be used to execute remotely also sensor-specific functions. In this case the message has to be sent to the sensor's child_id, with a V_CUSTOM type of message. For example if you want to collect and average 10 samples for child_id 1: -~~~c - // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) - void setSamples(int value); -~~~ -`100;1;2;0;48;5,10` +To wake up a node previously configured with `MODE1`, send the following just after reporting `AWAKE`: -If you want to decrease the temperature offset of a thermistor sensor to -2: -~~~c - // [105] set a temperature offset - void setOffset(float value); -~~~ -`100;1;2;0;48;105,-2` +`254;200;2;0;48;WAKEUP` + +In addition, NodeManager will report with custom messages every time the board is going to sleep (`SLEEPING`) or it is awake (`AWAKE`). -Please note that anything set remotely will NOT persist a reboot apart from those provided to setSleepMode(), setSleepTime() and setSleepUnit() which are saved to the EEPROM (provided `PERSIST` is enabled). +If `PERSIST` is enabled, the settings provided with `INTVLnnnX` and `MODEx` are saved to the EEPROM to be persistent even after rebooting the board. ## Understanding NodeManager: how it works diff --git a/config.h b/config.h index 497e4eee..eded817e 100644 --- a/config.h +++ b/config.h @@ -15,11 +15,11 @@ // General settings #define MY_BAUD_RATE 9600 //#define MY_DEBUG -#define MY_NODE_ID 100 +//#define MY_NODE_ID 100 // NRF24 radio settings #define MY_RADIO_NRF24 -#define MY_RF24_ENABLE_ENCRYPTION +//#define MY_RF24_ENABLE_ENCRYPTION //#define MY_RF24_CHANNEL 76 //#define MY_RF24_PA_LEVEL RF24_PA_HIGH //#define MY_DEBUG_VERBOSE_RF24 @@ -102,12 +102,12 @@ // 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_MQ, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE #define MODULE_ANALOG_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT -#define MODULE_DIGITAL_INPUT 0 +#define MODULE_DIGITAL_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY -#define MODULE_DIGITAL_OUTPUT 0 +#define MODULE_DIGITAL_OUTPUT 1 // Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 #define MODULE_DHT 0 // Enable this module to use one of the following sensors: SENSOR_SHT21 @@ -130,7 +130,5 @@ #define MODULE_HCSR04 0 // Enable this module to use one of the following sensors: SENSOR_MCP9808 #define MODULE_MCP9808 0 -// Enable this module to use one of the following sensors: SENSOR_MQ -#define MODULE_MQ 0 #endif From f4edca3036a878fc70ffbdbb2ac1bb3df0a9e266 Mon Sep 17 00:00:00 2001 From: user2684 Date: Thu, 1 Jun 2017 17:14:14 +0200 Subject: [PATCH 11/33] Enhanced remote API - fixed (#102 #20 #28 #53 #78) --- NodeManager.cpp | 1073 +++++++++++++++++++++++++++++------------------ NodeManager.h | 357 ++++++++++------ NodeManager.ino | 6 +- README.md | 328 ++++++++------- config.h | 4 +- 5 files changed, 1076 insertions(+), 692 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 0002d32a..5e5e6117 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -195,6 +195,53 @@ int Timer::getUnit() { return _unit; } + +/****************************************** + Request +*/ + +Request::Request(const char* string) { + char str[10]; + char* ptr; + strcpy(str,string); + // tokenize the string and split function from value + strtok_r(str,",",&ptr); + _function = atoi(str); + strcpy(_value,ptr); + #if DEBUG == 1 + Serial.print(F("REQ F=")); + Serial.print(getFunction()); + Serial.print(F(" I=")); + Serial.print(getValueInt()); + Serial.print(F(" F=")); + Serial.print(getValueFloat()); + Serial.print(F(" S=")); + Serial.println(getValueString()); + #endif +} + +// return the parsed function +int Request::getFunction() { + return _function; +} + +// return the value as an int +int Request::getValueInt() { + return atoi(_value); + +} + +// return the value as a float +float Request::getValueFloat() { + return atof(_value); +} + +// return the value as a string +char* Request::getValueString() { + return _value; +} + + /****************************************** Sensors */ @@ -208,6 +255,7 @@ Sensor::Sensor(NodeManager* node_manager, int child_id, int pin) { _child_id = child_id; _pin = pin; _msg = MyMessage(_child_id, _type); + _msg_service = MyMessage(_child_id, V_CUSTOM); _report_timer = new Timer(_node_manager); _force_update_timer = new Timer(_node_manager); } @@ -416,11 +464,50 @@ void Sensor::loop(const MyMessage & message) { // receive a message from the radio network void Sensor::receive(const MyMessage &message) { // return if not for this sensor - if (message.sensor != _child_id || message.type != _type) return; + if (message.sensor != _child_id) return; + // check if it is a request for the API + if (message.getCommand() == C_REQ && message.type == V_CUSTOM) { + #if REMOTE_CONFIGURATION == 1 + // parse the request + Request request = Request(message.getString()); + // if it is for a sensor-generic function, call process(), otherwise the sensor-specific onProcess(); + if (request.getFunction() < 100) process(request); + else onProcess(request); + #endif + } + // return if the type is not correct + if (message.type != _type) return; // a request would make the sensor executing its main task passing along the message loop(message); } +// process a remote configuration request message +void Sensor::process(Request & request) { + int function = request.getFunction(); + switch(function) { + case 1: setPin(request.getValueInt()); break; + case 2: setChildId(request.getValueInt()); break; + case 3: setType(request.getValueInt()); break; + case 4: setDescription(request.getValueString()); break; + case 5: setSamples(request.getValueInt()); break; + case 6: setSamplesInterval(request.getValueInt()); break; + case 7: setTrackLastValue(request.getValueInt()); break; + case 8: setForceUpdateCycles(request.getValueInt()); break; + case 9: setForceUpdateMinutes(request.getValueInt()); break; + case 10: setValueType(request.getValueInt()); break; + case 11: setFloatPrecision(request.getValueInt()); break; + #if POWER_MANAGER == 1 + case 12: setAutoPowerPins(request.getValueInt()); break; + case 13: powerOn(); break; + case 14: powerOff(); break; + #endif + case 15: setReportIntervalCycles(request.getValueInt()); break; + case 16: setReportIntervalMinutes(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + // send a message to the network void Sensor::_send(MyMessage & message) { // send the message, multiple times if requested @@ -468,7 +555,7 @@ bool Sensor::_isWorthSending(bool comparison) { return false; } - +#if MODULE_ANALOG_INPUT == 1 /* SensorAnalogInput */ @@ -528,6 +615,20 @@ void SensorAnalogInput::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorAnalogInput::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setReference(request.getValueInt()); break; + case 102: setReverse(request.getValueInt()); break; + case 103: setOutputPercentage(request.getValueInt()); break; + case 104: setRangeMin(request.getValueInt()); break; + case 105: setRangeMax(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + // read the analog input int SensorAnalogInput::_getAnalogRead() { #ifndef MY_GATEWAY_ESP8266 @@ -639,6 +740,19 @@ void SensorThermistor::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorThermistor::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setNominalResistor((long)request.getValueInt()); break; + case 102: setNominalTemperature(request.getValueInt()); break; + case 103: setBCoefficient(request.getValueInt()); break; + case 104: setSeriesResistor((long)request.getValueString()); break; + case 105: setOffset(request.getValueFloat()); break; + default: return; + } + _send(_msg_service.set(function)); +} /* SensorML8511 @@ -688,6 +802,10 @@ void SensorML8511::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorML8511::onProcess(Request & request) { +} + // 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; @@ -743,6 +861,17 @@ void SensorACS712::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorACS712::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 100: setmVPerAmp(request.getValueInt()); break; + case 102: setOffset(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + /* SensorRainGauge */ @@ -825,6 +954,17 @@ void SensorRainGauge::onReceive(const MyMessage & message) { } } +// what to do when receiving a remote 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)); +} + /* SensorRain */ @@ -855,151 +995,9 @@ SensorSoilMoisture::SensorSoilMoisture(NodeManager* node_manager, int child_id, setRangeMin(100); } +#endif -/* - * SensorMQ - */ -SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { - setPresentation(S_AIR_QUALITY); - setType(V_LEVEL); -} - -//setter/getter -void SensorMQ::setRlValue(float value) { - _rl_value = value; -} -void SensorMQ::setRoValue(float value) { - _ro = value; -} -void SensorMQ::setCleanAirFactor(float value) { - _ro_clean_air_factor = value; -} -void SensorMQ::setCalibrationSampleTimes(int value) { - _calibration_sample_times = value; -} -void SensorMQ::setCalibrationSampleInterval(int value){ - _calibration_sample_interval = value; -} -void SensorMQ::setReadSampleTimes(int value) { - _read_sample_times = value; -} -void SensorMQ::setReadSampleInterval(int value) { - _read_sample_interval = value; -} -void SensorMQ::setLPGCurve(float *value) { - _LPGCurve[0] = value[0]; - _LPGCurve[2] = value[1]; - _LPGCurve[2] = value[2]; -} -void SensorMQ::setCOCurve(float *value) { - _COCurve[0] = value[0]; - _COCurve[2] = value[1]; - _COCurve[2] = value[2]; -} -void SensorMQ::setSmokeCurve(float *value) { - _SmokeCurve[0] = value[0]; - _SmokeCurve[2] = value[1]; - _SmokeCurve[2] = value[2]; -} - -// what to do during before -void SensorMQ::onBefore() { - // prepare the pin for input - pinMode(_pin, INPUT); -} - -// what to do during setup -void SensorMQ::onSetup() { - _ro = _MQCalibration(); -} - -// what to do during loop -void SensorMQ::onLoop() { - if (_pin == -1) return; - // calculate rs/ro - float mq = _MQRead()/_ro; - // calculate the ppm - float lpg = _MQGetGasPercentage(mq,_gas_lpg); - float co = _MQGetGasPercentage(mq,_gas_co); - float smoke = _MQGetGasPercentage(mq,_gas_smoke); - // assign to the value the requested gas - uint16_t value; - if (_target_gas == _gas_lpg) value = lpg; - if (_target_gas == _gas_co) value = co; - if (_target_gas == _gas_smoke) value = smoke; - #if DEBUG == 1 - Serial.print(F("MQ I=")); - Serial.print(_child_id); - Serial.print(F(" V=")); - Serial.print(value); - Serial.print(F(" LPG=")); - Serial.print(lpg); - Serial.print(F(" CO=")); - Serial.print(co); - Serial.print(F(" SMOKE=")); - Serial.println(smoke); - #endif - // store the value - _value_int = (int16_t)ceil(value); -} - -// what to do as the main task when receiving a message -void SensorMQ::onReceive(const MyMessage & message) { - if (message.getCommand() == C_REQ) onLoop(); -} - -// returns the calculated sensor resistance -float SensorMQ::_MQResistanceCalculation(int raw_adc) { - return ( ((float)_rl_value*(1023-raw_adc)/raw_adc)); -} - -// This function assumes that the sensor is in clean air -float SensorMQ::_MQCalibration() { - int i; - float val=0; - //take multiple samples - for (i=0; i< _calibration_sample_times; i++) { - val += _MQResistanceCalculation(analogRead(_pin)); - wait(_calibration_sample_interval); - } - //calculate the average value - val = val/_calibration_sample_times; - //divided by RO_CLEAN_AIR_FACTOR yields the Ro - val = val/_ro_clean_air_factor; - //according to the chart in the datasheet - return val; -} - -// This function use MQResistanceCalculation to caculate the sensor resistenc (Rs). -float SensorMQ::_MQRead() { - int i; - float rs=0; - for (i=0; i<_read_sample_times; i++) { - rs += _MQResistanceCalculation(analogRead(_pin)); - wait(_read_sample_interval); - } - rs = rs/_read_sample_times; - return rs; -} - -// This function passes different curves to the MQGetPercentage function which calculates the ppm (parts per million) of the target gas. -int SensorMQ::_MQGetGasPercentage(float rs_ro_ratio, int gas_id) { - if ( gas_id == _gas_lpg ) { - return _MQGetPercentage(rs_ro_ratio,_LPGCurve); - } else if ( gas_id == _gas_co) { - return _MQGetPercentage(rs_ro_ratio,_COCurve); - } else if ( gas_id == _gas_smoke) { - return _MQGetPercentage(rs_ro_ratio,_SmokeCurve); - } - return 0; -} - -// returns ppm of the target gas -int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) { - return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0]))); -} - - +#if MODULE_DIGITAL_INPUT == 1 /* SensorDigitalInput */ @@ -1039,12 +1037,16 @@ void SensorDigitalInput::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorDigitalInput::onProcess(Request & request) { +} +#endif +#if MODULE_DIGITAL_OUTPUT == 1 /* SensorDigitalOutput */ -// contructor SensorDigitalOutput::SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id, pin) { _safeguard_timer = new Timer(node_manager); } @@ -1114,6 +1116,21 @@ void SensorDigitalOutput::onReceive(const MyMessage & message) { } } +// what to do when receiving a remote message +void SensorDigitalOutput::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setInitialValue(request.getValueInt()); break; + case 102: setPulseWidth(request.getValueInt()); break; + case 103: setOnValue(request.getValueInt()); break; + case 104: setLegacyMode(request.getValueInt()); break; + case 105: setSafeguard(request.getValueInt()); break; + case 106: setInputIsElapsed(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + // write the value to the output void SensorDigitalOutput::set(int value) { if (_input_is_elapsed) { @@ -1169,14 +1186,7 @@ SensorRelay::SensorRelay(NodeManager* node_manager, int child_id, int pin): Sens setPresentation(S_BINARY); setType(V_STATUS); } -/* -// define what to do during loop -void SensorRelay::onLoop() { - // set the value to -1 so to avoid reporting to the gateway during loop - _value_int = -1; - _last_value_int = -1; -} -*/ + /* SensorLatchingRelay */ @@ -1187,6 +1197,7 @@ SensorLatchingRelay::SensorLatchingRelay(NodeManager* node_manager, int child_id setPulseWidth(50); } +#endif /* SensorDHT */ @@ -1258,6 +1269,10 @@ void SensorDHT::onLoop() { void SensorDHT::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorDHT::onProcess(Request & request) { +} #endif /* @@ -1329,6 +1344,10 @@ void SensorSHT21::onLoop() { void SensorSHT21::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorSHT21::onProcess(Request & request) { +} #endif /* @@ -1340,6 +1359,7 @@ SensorHTU21D::SensorHTU21D(NodeManager* node_manager, int child_id, int pin): Se } #endif +#if MODULE_SWITCH == 1 /* * SensorSwitch */ @@ -1407,6 +1427,19 @@ 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 */ @@ -1424,6 +1457,7 @@ SensorMotion::SensorMotion(NodeManager* node_manager, int child_id, int pin): Se // set initial value to LOW setInitial(LOW); } +#endif /* SensorDs18b20 @@ -1478,6 +1512,17 @@ void SensorDs18b20::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorDs18b20::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setResolution(request.getValueInt()); break; + case 102: setSleepDuringConversion(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + // function to print a device address DeviceAddress* SensorDs18b20::getDeviceAddress() { return &_device_address; @@ -1536,6 +1581,10 @@ void SensorBH1750::onLoop() { void SensorBH1750::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorBH1750::onProcess(Request & request) { +} #endif /* @@ -1580,6 +1629,10 @@ void SensorMLX90614::onLoop() { void SensorMLX90614::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorMLX90614::onProcess(Request & request) { +} #endif @@ -1640,7 +1693,17 @@ void SensorBosch::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } +// what to do when receiving a remote message +void SensorBosch::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setForecastSamplesCount(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} +// calculate and send the forecast back void SensorBosch::_forecast(float pressure) { if (isnan(pressure)) return; // Calculate the average of the last n minutes. @@ -1860,35 +1923,98 @@ void SensorBMP085::onLoop() { } #endif - /* - SensorSonoff + SensorHCSR04 */ -#if MODULE_SONOFF == 1 +#if MODULE_HCSR04 == 1 // contructor -SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,1) { - setPresentation(S_BINARY); - setType(V_STATUS); +SensorHCSR04::SensorHCSR04(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { + // set presentation and type + setPresentation(S_DISTANCE); + setType(V_DISTANCE); + _trigger_pin = pin; + _echo_pin = pin; +} + +// what to do during before +void SensorHCSR04::onBefore() { + // initialize the library + _sonar = new NewPing(_trigger_pin,_echo_pin,_max_distance); } // setter/getter -void SensorSonoff::setButtonPin(int value) { - _button_pin = value; +void SensorHCSR04::setTriggerPin(int value) { + _trigger_pin = value; } -void SensorSonoff::setRelayPin(int value) { - _relay_pin = value; +void SensorHCSR04::setEchoPin(int value) { + _echo_pin = value; } -void SensorSonoff::setLedPin(int value) { - _led_pin = value; +void SensorHCSR04::setMaxDistance(int value) { + _max_distance = value; } -// what to do during before -void SensorSonoff::onBefore() { +// what to do during setup +void SensorHCSR04::onSetup() { } -// what to do during setup -void SensorSonoff::onSetup() { - // Setup the button +// what to do during loop +void SensorHCSR04::onLoop() { + int distance = _node_manager->getIsMetric() ? _sonar->ping_cm() : _sonar->ping_in(); + #if DEBUG == 1 + Serial.print(F("HC I=")); + Serial.print(_child_id); + Serial.print(F(" D=")); + Serial.println(distance); + #endif + _value_int = distance; +} + +// what to do as the main task when receiving a message +void SensorHCSR04::onReceive(const MyMessage & message) { + if (message.getCommand() == C_REQ) onLoop(); +} + +// what to do when receiving a remote message +void SensorHCSR04::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setTriggerPin(request.getValueInt()); break; + case 102: setEchoPin(request.getValueInt()); break; + case 103: setMaxDistance(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} +#endif + +/* + SensorSonoff +*/ +#if MODULE_SONOFF == 1 +// contructor +SensorSonoff::SensorSonoff(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,1) { + setPresentation(S_BINARY); + setType(V_STATUS); +} + +// setter/getter +void SensorSonoff::setButtonPin(int value) { + _button_pin = value; +} +void SensorSonoff::setRelayPin(int value) { + _relay_pin = value; +} +void SensorSonoff::setLedPin(int value) { + _led_pin = value; +} + +// what to do during before +void SensorSonoff::onBefore() { +} + +// what to do during setup +void SensorSonoff::onSetup() { + // Setup the button pinMode(_button_pin, INPUT_PULLUP); // After setting up the button, setup debouncer _debouncer.attach(_button_pin); @@ -1931,6 +2057,18 @@ void SensorSonoff::onReceive(const MyMessage & message) { } } +// what to do when receiving a remote message +void SensorSonoff::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setButtonPin(request.getValueInt()); break; + case 102: setRelayPin(request.getValueInt()); break; + case 103: setLedPin(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + // toggle the state void SensorSonoff::_toggle() { // toggle the state @@ -1962,96 +2100,211 @@ void SensorSonoff::_blink() { /* - SensorHCSR04 + SensorMCP9808 */ -#if MODULE_HCSR04 == 1 +#if MODULE_MCP9808 == 1 // contructor -SensorHCSR04::SensorHCSR04(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { - // set presentation and type - setPresentation(S_DISTANCE); - setType(V_DISTANCE); - _trigger_pin = pin; - _echo_pin = pin; +SensorMCP9808::SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp): Sensor(node_manager, child_id,A2) { + _mcp = mcp; + setPresentation(S_TEMP); + setType(V_TEMP); + setValueType(TYPE_FLOAT); } // what to do during before -void SensorHCSR04::onBefore() { - // initialize the library - _sonar = new NewPing(_trigger_pin,_echo_pin,_max_distance); -} - -// setter/getter -void SensorHCSR04::setTriggerPin(int value) { - _trigger_pin = value; -} -void SensorHCSR04::setEchoPin(int value) { - _echo_pin = value; -} -void SensorHCSR04::setMaxDistance(int value) { - _max_distance = value; +void SensorMCP9808::onBefore() { } // what to do during setup -void SensorHCSR04::onSetup() { +void SensorMCP9808::onSetup() { } // what to do during loop -void SensorHCSR04::onLoop() { - int distance = _node_manager->getIsMetric() ? _sonar->ping_cm() : _sonar->ping_in(); +void SensorMCP9808::onLoop() { + float temperature = _mcp->readTempC(); + // convert it + temperature = _node_manager->celsiusToFahrenheit(temperature); #if DEBUG == 1 - Serial.print(F("HC I=")); + Serial.print(F("MCP I=")); Serial.print(_child_id); - Serial.print(F(" D=")); - Serial.println(distance); + Serial.print(F(" T=")); + Serial.println(temperature); #endif - _value_int = distance; + // store the value + if (! isnan(temperature)) _value_float = temperature; } // what to do as the main task when receiving a message -void SensorHCSR04::onReceive(const MyMessage & message) { +void SensorMCP9808::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorMCP9808::onProcess(Request & request) { +} #endif + /* - SensorMCP9808 -*/ -#if MODULE_MCP9808 == 1 -// contructor -SensorMCP9808::SensorMCP9808(NodeManager* node_manager, int child_id, Adafruit_MCP9808* mcp): Sensor(node_manager, child_id,A2) { - _mcp = mcp; - setPresentation(S_TEMP); - setType(V_TEMP); - setValueType(TYPE_FLOAT); + * SensorMQ + */ +#if MODULE_MQ == 1 +SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { + setPresentation(S_AIR_QUALITY); + setType(V_LEVEL); +} + +//setter/getter +void SensorMQ::setRlValue(float value) { + _rl_value = value; +} +void SensorMQ::setRoValue(float value) { + _ro = value; +} +void SensorMQ::setCleanAirFactor(float value) { + _ro_clean_air_factor = value; +} +void SensorMQ::setCalibrationSampleTimes(int value) { + _calibration_sample_times = value; +} +void SensorMQ::setCalibrationSampleInterval(int value){ + _calibration_sample_interval = value; +} +void SensorMQ::setReadSampleTimes(int value) { + _read_sample_times = value; +} +void SensorMQ::setReadSampleInterval(int value) { + _read_sample_interval = value; +} +void SensorMQ::setLPGCurve(float *value) { + _LPGCurve[0] = value[0]; + _LPGCurve[2] = value[1]; + _LPGCurve[2] = value[2]; +} +void SensorMQ::setCOCurve(float *value) { + _COCurve[0] = value[0]; + _COCurve[2] = value[1]; + _COCurve[2] = value[2]; +} +void SensorMQ::setSmokeCurve(float *value) { + _SmokeCurve[0] = value[0]; + _SmokeCurve[2] = value[1]; + _SmokeCurve[2] = value[2]; } // what to do during before -void SensorMCP9808::onBefore() { +void SensorMQ::onBefore() { + // prepare the pin for input + pinMode(_pin, INPUT); } // what to do during setup -void SensorMCP9808::onSetup() { +void SensorMQ::onSetup() { + _ro = _MQCalibration(); } // what to do during loop -void SensorMCP9808::onLoop() { - float temperature = _mcp->readTempC(); - // convert it - temperature = _node_manager->celsiusToFahrenheit(temperature); +void SensorMQ::onLoop() { + if (_pin == -1) return; + // calculate rs/ro + float mq = _MQRead()/_ro; + // calculate the ppm + float lpg = _MQGetGasPercentage(mq,_gas_lpg); + float co = _MQGetGasPercentage(mq,_gas_co); + float smoke = _MQGetGasPercentage(mq,_gas_smoke); + // assign to the value the requested gas + uint16_t value; + if (_target_gas == _gas_lpg) value = lpg; + if (_target_gas == _gas_co) value = co; + if (_target_gas == _gas_smoke) value = smoke; #if DEBUG == 1 - Serial.print(F("MCP I=")); + Serial.print(F("MQ I=")); Serial.print(_child_id); - Serial.print(F(" T=")); - Serial.println(temperature); + Serial.print(F(" V=")); + Serial.print(value); + Serial.print(F(" LPG=")); + Serial.print(lpg); + Serial.print(F(" CO=")); + Serial.print(co); + Serial.print(F(" SMOKE=")); + Serial.println(smoke); #endif // store the value - if (! isnan(temperature)) _value_float = temperature; + _value_int = (int16_t)ceil(value); } // what to do as the main task when receiving a message -void SensorMCP9808::onReceive(const MyMessage & message) { +void SensorMQ::onReceive(const MyMessage & message) { if (message.getCommand() == C_REQ) onLoop(); } + +// what to do when receiving a remote message +void SensorMQ::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 1: setTargetGas(request.getValueInt()); break; + case 2: setRlValue(request.getValueFloat()); break; + case 3: setRoValue(request.getValueFloat()); break; + case 4: setCleanAirFactor(request.getValueFloat()); break; + case 5: setCalibrationSampleTimes(request.getValueInt()); break; + case 6: setCalibrationSampleInterval(request.getValueInt()); break; + case 7: setReadSampleTimes(request.getValueInt()); break; + case 8: setReadSampleInterval(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + +// returns the calculated sensor resistance +float SensorMQ::_MQResistanceCalculation(int raw_adc) { + return ( ((float)_rl_value*(1023-raw_adc)/raw_adc)); +} + +// This function assumes that the sensor is in clean air +float SensorMQ::_MQCalibration() { + int i; + float val=0; + //take multiple samples + for (i=0; i< _calibration_sample_times; i++) { + val += _MQResistanceCalculation(analogRead(_pin)); + wait(_calibration_sample_interval); + } + //calculate the average value + val = val/_calibration_sample_times; + //divided by RO_CLEAN_AIR_FACTOR yields the Ro + val = val/_ro_clean_air_factor; + //according to the chart in the datasheet + return val; +} + +// This function use MQResistanceCalculation to caculate the sensor resistenc (Rs). +float SensorMQ::_MQRead() { + int i; + float rs=0; + for (i=0; i<_read_sample_times; i++) { + rs += _MQResistanceCalculation(analogRead(_pin)); + wait(_read_sample_interval); + } + rs = rs/_read_sample_times; + return rs; +} + +// This function passes different curves to the MQGetPercentage function which calculates the ppm (parts per million) of the target gas. +int SensorMQ::_MQGetGasPercentage(float rs_ro_ratio, int gas_id) { + if ( gas_id == _gas_lpg ) { + return _MQGetPercentage(rs_ro_ratio,_LPGCurve); + } else if ( gas_id == _gas_co) { + return _MQGetPercentage(rs_ro_ratio,_COCurve); + } else if ( gas_id == _gas_smoke) { + return _MQGetPercentage(rs_ro_ratio,_SmokeCurve); + } + return 0; +} + +// returns ppm of the target gas +int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) { + return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0]))); +} #endif /******************************************* @@ -2116,9 +2369,9 @@ int NodeManager::getSleepUnit() { return _sleep_unit; } void NodeManager::setSleep(int value1, int value2, int value3) { - _sleep_mode = value1; - _sleep_time = value2; - _sleep_unit = value3; + setMode(value1); + setSleepTime(value2); + setSleepUnit(value3); } void NodeManager::setSleepInterruptPin(int value) { _sleep_interrupt_pin = value; @@ -2186,7 +2439,6 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { else if (sensor_type == SENSOR_ANALOG_INPUT) return registerSensor(new SensorAnalogInput(this,child_id, pin)); else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(this,child_id, pin)); else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(this,child_id, pin)); - else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(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)); @@ -2339,6 +2591,11 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { registerSensor(new SensorMCP9808(this,child_id,mcp)); } #endif + #if MODULE_MQ == 1 + else if (sensor_type == SENSOR_MQ) { + return registerSensor(new SensorMQ(this,child_id, pin)); + } + #endif else { #if DEBUG == 1 Serial.print(F("INVALID ")); @@ -2442,26 +2699,9 @@ void NodeManager::before() { Serial.print(F("INT2 M=")); Serial.println(_interrupt_2_mode); #endif - #if REMOTE_CONFIGURATION == 1 && PERSIST == 1 - // restore sleep configuration from eeprom - if (loadState(EEPROM_SLEEP_SAVED) == 1) { - // sleep settings found in the eeprom, restore them - _sleep_mode = loadState(EEPROM_SLEEP_MODE); - _sleep_time = loadState(EEPROM_SLEEP_TIME_MINOR); - int major = loadState(EEPROM_SLEEP_TIME_MAJOR); - if (major == 1) _sleep_time = _sleep_time + 250; - else if (major == 2) _sleep_time = _sleep_time + 250 * 2; - else if (major == 3) _sleep_time = _sleep_time + 250 * 3; - _sleep_unit = loadState(EEPROM_SLEEP_UNIT); - #if DEBUG == 1 - Serial.print(F("LOADSLP M=")); - Serial.print(_sleep_mode); - Serial.print(F(" T=")); - Serial.print(_sleep_time); - Serial.print(F(" U=")); - Serial.println(_sleep_unit); - #endif - } + #if PERSIST == 1 + // restore the configuration saved in the eeprom + _loadConfig(); #endif #if BATTERY_MANAGER == 1 && !defined(MY_GATEWAY_ESP8266) // set analogReference to internal if measuring the battery through a pin @@ -2492,7 +2732,7 @@ void NodeManager::presentation() { // present the battery service _present(BATTERY_CHILD_ID, S_MULTIMETER); // report battery level - _process("BATTERY"); + batteryReport(); #endif // present each sensor for (int i = 0; i < MAX_SENSORS; i++) { @@ -2545,7 +2785,7 @@ void NodeManager::loop() { // if it is time to report the battery level if (_battery_report_timer.isOver()) { // time to report the battery level again - _process("BATTERY"); + batteryReport(); // restart the timer _battery_report_timer.restart(); } @@ -2585,9 +2825,14 @@ void NodeManager::receive(const MyMessage &message) { Serial.print(F(" P=")); Serial.println(message.getString()); #endif - // process incoming service messages + // process incoming configuration message if (message.sensor == CONFIGURATION_CHILD_ID && message.getCommand() == C_REQ && message.type == V_CUSTOM) { - _process(message.getString()); + #if REMOTE_CONFIGURATION == 1 + // parse the request + Request request = Request(message.getString()); + // process the request + process(request); + #endif } // dispatch the message to the registered sensor else if (_sensors[message.sensor] != 0) { @@ -2630,6 +2875,140 @@ void NodeManager::receiveTime(unsigned long ts) { #endif } +// process a request message +void NodeManager::process(Request & request) { + int function = request.getFunction(); + switch(function) { + case 1: hello(); break; + #if BATTERY_MANAGER == 1 + case 2: batteryReport(); return; + case 11: setBatteryMin(request.getValueFloat()); break; + case 12: setBatteryMax(request.getValueFloat()); break; + case 13: setBatteryReportCycles(request.getValueInt()); break; + case 14: setBatteryReportMinutes(request.getValueInt()); break; + case 15: setBatteryInternalVcc(request.getValueInt()); break; + case 16: setBatteryPin(request.getValueInt()); break; + case 17: setBatteryVoltsPerBit(request.getValueFloat()); break; + case 18: setBatteryReportWithInterrupt(request.getValueInt()); break; + #endif + case 3: + setSleepMode(request.getValueInt()); + #if PERSIST == 1 + _saveConfig(SAVE_SLEEP_MODE); + #endif + break; + case 4: + setSleepTime(request.getValueInt()); + #if PERSIST == 1 + _saveConfig(SAVE_SLEEP_TIME); + #endif + break; + case 5: + setSleepUnit(request.getValueInt()); + #if PERSIST == 1 + _saveConfig(SAVE_SLEEP_UNIT); + #endif + break; + #ifndef MY_GATEWAY_ESP8266 + case 6: reboot(); return; + #endif + case 7: clearEeprom(); break; + case 8: version(); return; + case 9: wakeup(); break; + case 10: setRetries(request.getValueInt()); break; + case 19: setSleepInterruptPin(request.getValueInt()); break; + case 20: setSleepBetweenSend(request.getValueInt()); break; + case 21: setAck(request.getValueInt()); break; + case 22: setIsMetric(request.getValueInt()); break; + #if POWER_MANAGER == 1 + case 23: setAutoPowerPins(request.getValueInt()); break; + case 24: powerOn(); break; + case 25: powerOff(); break; + #endif + case 26: unRegisterSensor(request.getValueInt()); break; + case 27: saveToMemory(0,request.getValueInt()); break; + default: return; + } + _send(_msg.set(function)); +} + + +// Send a hello message back to the controller +void NodeManager::hello() { + // do nothing, the request will be echoed back +} + +#if BATTERY_MANAGER == 1 +// Send a battery level report to the controller +void NodeManager::batteryReport() { + // measure the board vcc + float volt = 0; + if (_battery_internal_vcc || _battery_pin == -1) volt = getVcc(); + else volt = analogRead(_battery_pin) * _battery_volts_per_bit; + // calculate the percentage + int percentage = ((volt - _battery_min) / (_battery_max - _battery_min)) * 100; + if (percentage > 100) percentage = 100; + if (percentage < 0) percentage = 0; + #if DEBUG == 1 + Serial.print(F("BATT V=")); + Serial.print(volt); + Serial.print(F(" P=")); + Serial.println(percentage); + #endif + #if BATTERY_SENSOR == 1 + // report battery voltage + MyMessage battery_msg(BATTERY_CHILD_ID, V_VOLTAGE); + _send(battery_msg.set(volt, 2)); + #endif + // report battery level percentage + sendBatteryLevel(percentage,_ack); +} +#endif + +// reboot the board +void NodeManager::reboot() { + #if DEBUG == 1 + Serial.println(F("REBOOT")); + #endif + // Software reboot with watchdog timer. Enter Watchdog Configuration mode: + WDTCSR |= (1< 100) percentage = 100; - if (percentage < 0) percentage = 0; - #if DEBUG == 1 - Serial.print(F("BATT V=")); - Serial.print(volt); - Serial.print(F(" P=")); - Serial.println(percentage); - #endif - #if BATTERY_SENSOR == 1 - // report battery voltage - MyMessage battery_msg(BATTERY_CHILD_ID, V_VOLTAGE); - _send(battery_msg.set(volt, 2)); - #endif - // report battery level percentage - sendBatteryLevel(percentage,_ack); - } - #endif - #ifndef MY_GATEWAY_ESP8266 - // REBOOT: reboot the board - else if (strcmp(message, "REBOOT") == 0) { - #if DEBUG == 1 - Serial.println(F("REBOOT")); - #endif - // set the reboot pin connected to RST to low so to reboot the board - _send(_msg.set(message)); - // Software reboot with watchdog timer. Enter Watchdog Configuration mode: - WDTCSR |= (1< 30%) (default: false) + // [102] reverse the value or the percentage (e.g. 70% -> 30%) (default: false) void setReverse(bool value); - // when true returns the value as a percentage (default: true) + // [103] when true returns the value as a percentage (default: true) void setOutputPercentage(bool value); - // minimum value for calculating the percentage (default: 0) + // [104] minimum value for calculating the percentage (default: 0) void setRangeMin(int value); - // maximum value for calculating the percentage (default: 1024) + // [105] maximum value for calculating the percentage (default: 1024) void setRangeMax(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); protected: int _reference = -1; bool _reverse = false; @@ -523,21 +564,22 @@ class SensorLDR: public SensorAnalogInput { class SensorThermistor: public Sensor { public: SensorThermistor(NodeManager* node_manager, int child_id, int pin); - // resistance at 25 degrees C (default: 10000) + // [101] resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); - // temperature for nominal resistance (default: 25) + // [102] temperature for nominal resistance (default: 25) void setNominalTemperature(int value); - // The beta coefficient of the thermistor (default: 3950) + // [103] The beta coefficient of the thermistor (default: 3950) void setBCoefficient(int value); - // the value of the resistor in series with the thermistor (default: 10000) + // [104] the value of the resistor in series with the thermistor (default: 10000) void setSeriesResistor(long value); - // set a temperature offset + // [105] set a temperature offset void setOffset(float 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); protected: long _nominal_resistor = 10000; int _nominal_temperature = 25; @@ -546,61 +588,6 @@ class SensorThermistor: public Sensor { float _offset = 0; }; -/* - SensorMQ - */ -class SensorMQ: public Sensor { - public: - SensorMQ(NodeManager* node_manager, int child_id, int pin); - // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); - void setTargetGas(int value); - // define the load resistance on the board, in kilo ohms (default: 1); - void setRlValue(float value); - // define the Ro resistance on the board (default: 10000); - void setRoValue(float value); - // Sensor resistance in clean air (default: 9.83); - void setCleanAirFactor(float value); - // define how many samples you are going to take in the calibration phase (default: 50); - void setCalibrationSampleTimes(int value); - // define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); - void setCalibrationSampleInterval(int value); - // define how many samples you are going to take in normal operation (default: 50); - void setReadSampleTimes(int value); - // define the time interal(in milisecond) between each samples in the normal operations (default: 5); - void setReadSampleInterval(int value); - // set the LPGCurve array (default: {2.3,0.21,-0.47}) - void setLPGCurve(float *value); - // set the COCurve array (default: {2.3,0.72,-0.34}) - void setCOCurve(float *value); - // set the SmokeCurve array (default: {2.3,0.53,-0.44}) - void setSmokeCurve(float *value); - // define what to do at each stage of the sketch - void onBefore(); - void onSetup(); - void onLoop(); - void onReceive(const MyMessage & message); - protected: - float _rl_value = 1.0; - float _ro_clean_air_factor = 9.83; - int _calibration_sample_times = 50; - int _calibration_sample_interval = 500; - int _read_sample_interval = 50; - int _read_sample_times = 5; - float _ro = 10000.0; - float _LPGCurve[3] = {2.3,0.21,-0.47}; - float _COCurve[3] = {2.3,0.72,-0.34}; - float _SmokeCurve[3] = {2.3,0.53,-0.44}; - float _MQResistanceCalculation(int raw_adc); - float _MQCalibration(); - float _MQRead(); - int _MQGetGasPercentage(float rs_ro_ratio, int gas_id); - int _MQGetPercentage(float rs_ro_ratio, float *pcurve); - int _gas_lpg = 0; - int _gas_co = 1; - int _gas_smoke = 2; - int _target_gas = _gas_co; -}; - /* SensorML8511 */ @@ -613,6 +600,7 @@ class SensorML8511: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); protected: float _mapfloat(float x, float in_min, float in_max, float out_min, float out_max); }; @@ -624,15 +612,16 @@ class SensorML8511: public Sensor { class SensorACS712: public Sensor { public: SensorACS712(NodeManager* node_manager, int child_id, int pin); - // set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); + // [101] set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); void setmVPerAmp(int value); - // set ACS offset (default: 2500); + // [102] set ACS offset (default: 2500); void setOffset(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); protected: int _ACS_offset = 2500; int _mv_per_amp = 185; @@ -645,15 +634,16 @@ class SensorACS712: public Sensor { class SensorRainGauge: public Sensor { public: SensorRainGauge(NodeManager* node_manager, int child_id, int pin); - // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60) + // [101] set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60) void setReportInterval(int value); - // set how many mm of rain to count for each tip (default: 0.11) + // [102] set how many mm of rain to count for each tip (default: 0.11) void setSingleTip(float 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; @@ -679,7 +669,10 @@ class SensorSoilMoisture: public SensorAnalogInput { public: SensorSoilMoisture(NodeManager* node_manager, int child_id, int pin); }; +#endif + +#if MODULE_DIGITAL_INPUT == 1 /* SensorDigitalInput: read the digital input of the configured pin */ @@ -691,25 +684,28 @@ class SensorDigitalInput: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); }; +#endif +#if MODULE_DIGITAL_OUTPUT == 1 /* SensorDigitalOutput: control a digital output of the configured pin */ class SensorDigitalOutput: public Sensor { public: SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin); - // set how to initialize the output (default: LOW) + // [101] set how to initialize the output (default: LOW) void setInitialValue(int value); - // if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) + // [102] if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) void setPulseWidth(int value); - // define which value to set to the output when set to on (default: HIGH) + // [103] define which value to set to the output when set to on (default: HIGH) void setOnValue(int value); - // when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) + // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) void setLegacyMode(bool value); - // automatically turn the output off after the given number of minutes + // [105] automatically turn the output off after the given number of minutes void setSafeguard(int value); - // if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) + // [106] if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) void setInputIsElapsed(bool value); // manually switch the output to the provided value void set(int value); @@ -720,6 +716,7 @@ class SensorDigitalOutput: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); protected: int _initial_value = LOW; int _on_value = HIGH; @@ -737,8 +734,6 @@ class SensorDigitalOutput: public Sensor { class SensorRelay: public SensorDigitalOutput { public: SensorRelay(NodeManager* node_manager, int child_id, int pin); - // define what to do at each stage of the sketch - //void onLoop(); }; /* @@ -748,6 +743,7 @@ class SensorLatchingRelay: public SensorRelay { public: SensorLatchingRelay(NodeManager* node_manager, int child_id, int pin); }; +#endif /* SensorDHT @@ -761,6 +757,7 @@ class SensorDHT: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -784,6 +781,7 @@ class SensorSHT21: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -805,17 +803,18 @@ class SensorHTU21D: public SensorSHT21 { /* * SensorSwitch */ +#if MODULE_SWITCH == 1 class SensorSwitch: public Sensor { public: SensorSwitch(NodeManager* node_manager, int child_id, int pin); - // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) + // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); int getMode(); - // milliseconds to wait before reading the input (default: 0) + // [102] milliseconds to wait before reading the input (default: 0) void setDebounce(int value); - // time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) + // [103] time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) void setTriggerTime(int value); - // Set initial value on the interrupt pin (default: HIGH) + // [104] Set initial value on the interrupt pin (default: HIGH) void setInitial(int value); int getInitial(); // define what to do at each stage of the sketch @@ -823,6 +822,7 @@ class SensorSwitch: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); protected: int _debounce = 0; int _trigger_time = 0; @@ -845,7 +845,7 @@ class SensorMotion: public SensorSwitch { public: SensorMotion(NodeManager* node_manager, int child_id, int pin); }; - +#endif /* SensorDs18b20 */ @@ -853,19 +853,20 @@ class SensorMotion: public SensorSwitch { class SensorDs18b20: public Sensor { public: SensorDs18b20(NodeManager* node_manager, int child_id, int pin, DallasTemperature* sensors, int index); - // return the sensors' device address - DeviceAddress* getDeviceAddress(); // returns the sensor's resolution in bits int getResolution(); - // set the sensor's resolution in bits + // [101] set the sensor's resolution in bits void setResolution(int value); - // sleep while DS18B20 calculates temperature (default: false) + // [102] sleep while DS18B20 calculates temperature (default: false) void setSleepDuringConversion(bool value); + // return the sensors' device address + DeviceAddress* getDeviceAddress(); // 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); protected: float _offset = 0; int _index; @@ -887,6 +888,7 @@ class SensorBH1750: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); protected: BH1750* _lightSensor; }; @@ -904,6 +906,7 @@ class SensorMLX90614: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); // constants const static int TEMPERATURE_AMBIENT = 0; const static int TEMPERATURE_OBJECT = 1; @@ -922,13 +925,14 @@ class SensorMLX90614: public Sensor { class SensorBosch: public Sensor { public: SensorBosch(NodeManager* node_manager, int child_id, int sensor_type); - // define how many pressure samples to keep track of for calculating the forecast (default: 5) + // [101] define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(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); // constants const static int TEMPERATURE = 0; const static int HUMIDITY = 1; @@ -983,17 +987,18 @@ class SensorBMP085: public SensorBosch { class SensorHCSR04: public Sensor { public: SensorHCSR04(NodeManager* node_manager, int child_id, int pin); - // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setTriggerPin(int value); - // Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setEchoPin(int value); - // Maximum distance we want to ping for (in centimeters) (default: 300) + // [103] Maximum distance we want to ping for (in centimeters) (default: 300) void setMaxDistance(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); protected: int _trigger_pin; int _echo_pin; @@ -1009,17 +1014,18 @@ class SensorHCSR04: public Sensor { class SensorSonoff: public Sensor { public: SensorSonoff(NodeManager* node_manager, int child_id); - // set the button's pin (default: 0) + // [101] set the button's pin (default: 0) void setButtonPin(int value); - // set the relay's pin (default: 12) + // [102] set the relay's pin (default: 12) void setRelayPin(int value); - // set the led's pin (default: 13) + // [103] set the led's pin (default: 13) void setLedPin(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); protected: Bounce _debouncer = Bounce(); int _button_pin = 0; @@ -1048,11 +1054,69 @@ class SensorMCP9808: public Sensor { void onSetup(); void onLoop(); void onReceive(const MyMessage & message); + void onProcess(Request & request); protected: Adafruit_MCP9808* _mcp; }; #endif +/* + SensorMQ + */ + #if MODULE_MQ == 1 +class SensorMQ: public Sensor { + public: + SensorMQ(NodeManager* node_manager, int child_id, int pin); + // [101] define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); + void setTargetGas(int value); + // [102] define the load resistance on the board, in kilo ohms (default: 1); + void setRlValue(float value); + // [103] define the Ro resistance on the board (default: 10000); + void setRoValue(float value); + // [104] Sensor resistance in clean air (default: 9.83); + void setCleanAirFactor(float value); + // [105] define how many samples you are going to take in the calibration phase (default: 50); + void setCalibrationSampleTimes(int value); + // [106] define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); + void setCalibrationSampleInterval(int value); + // [107] define how many samples you are going to take in normal operation (default: 50); + void setReadSampleTimes(int value); + // [108] define the time interal(in milisecond) between each samples in the normal operations (default: 5); + void setReadSampleInterval(int value); + // set the LPGCurve array (default: {2.3,0.21,-0.47}) + void setLPGCurve(float *value); + // set the COCurve array (default: {2.3,0.72,-0.34}) + void setCOCurve(float *value); + // set the SmokeCurve array (default: {2.3,0.53,-0.44}) + void setSmokeCurve(float *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); + protected: + float _rl_value = 1.0; + float _ro_clean_air_factor = 9.83; + int _calibration_sample_times = 50; + int _calibration_sample_interval = 500; + int _read_sample_interval = 50; + int _read_sample_times = 5; + float _ro = 10000.0; + float _LPGCurve[3] = {2.3,0.21,-0.47}; + float _COCurve[3] = {2.3,0.72,-0.34}; + float _SmokeCurve[3] = {2.3,0.53,-0.44}; + float _MQResistanceCalculation(int raw_adc); + float _MQCalibration(); + float _MQRead(); + int _MQGetGasPercentage(float rs_ro_ratio, int gas_id); + int _MQGetPercentage(float rs_ro_ratio, float *pcurve); + int _gas_lpg = 0; + int _gas_co = 1; + int _gas_smoke = 2; + int _target_gas = _gas_co; +}; +#endif /*************************************** NodeManager: manages all the aspects of the node @@ -1060,51 +1124,51 @@ class SensorMCP9808: public Sensor { class NodeManager { public: NodeManager(); - // the pin to connect to the RST pin to reboot the board (default: 4) - void setRebootPin(int value); - // send the same service message multiple times (default: 1) + // [10] send the same service message multiple times (default: 1) void setRetries(int value); #if BATTERY_MANAGER == 1 - // the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) + // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) void setBatteryMin(float value); - // the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) + // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) + // [13] after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) void setBatteryReportCycles(int value); - // after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) + // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) void setBatteryReportMinutes(int value); - // if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) + // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) void setBatteryInternalVcc(bool value); - // if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) + // [16] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) void setBatteryPin(int value); - // if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) + // [17] if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) void setBatteryVoltsPerBit(float value); - // If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) + // [18] If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) void setBatteryReportWithInterrupt(bool value); + // [2] Send a battery level report to the controller + void batteryReport(); #endif - // define the way the node should behave. It can be IDLE (stay awake withtout executing each sensors' loop), SLEEP (go to sleep for the configured interval), WAIT (wait for the configured interval), ALWAYS_ON (stay awake and execute each sensors' loop) + // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) void setSleepMode(int value); void setMode(int value); int getMode(); - // define for how long the board will sleep (default: 0) + // [4] define for how long the board will sleep (default: 0) void setSleepTime(int value); int getSleepTime(); - // define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) + // [5] define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) void setSleepUnit(int value); int getSleepUnit(); // configure the node's behavior, parameters are mode, time and unit void setSleep(int value1, int value2, int value3); - // if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true) + // [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); - // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) + // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); // register a built-in sensor int registerSensor(int sensor_type, int pin = -1, int child_id = -1); // register a custom sensor int registerSensor(Sensor* sensor); - // un-register a sensor + // [26] un-register a sensor void unRegisterSensor(int sensor_index); // return a sensor by its index Sensor* get(int sensor_index); @@ -1114,26 +1178,42 @@ class NodeManager { #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); - // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) + // [23] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) void setAutoPowerPins(bool value); - // manually turn the power on + // [24] manually turn the power on void powerOn(); - // manually turn the power off + // [25] manually turn the power off void powerOff(); #endif - // set this to true if you want destination node to send ack back to this node (default: false) + // [21] set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); // request and return the current timestamp from the controller long getTimestamp(); // Request the controller's configuration on startup (default: true) void setGetControllerConfig(bool value); - // Manually set isMetric setting + // [22] Manually set isMetric setting void setIsMetric(bool value); bool getIsMetric(); // Convert a temperature from celsius to fahrenheit depending on how isMetric is set float celsiusToFahrenheit(float temperature); // return true if sleep or wait is configured and hence this is a sleeping node bool isSleepingNode(); + // [1] Send a hello message back to the controller + void hello(); + // [6] reboot the board + void reboot(); + // [8] send NodeManager's the version back to the controller + void version(); + // [7] clear the EEPROM + void clearEeprom(); + // [9] wake up the board + void wakeup(); + // process a remote request + void process(Request & request); + // return the value stored at the requested index from the EEPROM + int loadFromMemory(int index); + // [27] save the given index of the EEPROM the provided value + void saveToMemory(int index, int value); // hook into the main sketch functions void before(); void presentation(); @@ -1173,13 +1253,14 @@ class NodeManager { long _timestamp = -1; Sensor* _sensors[MAX_SENSORS] = {0}; bool _ack = false; - void _process(const char * message); void _sleep(); void _present(int child_id, int type); int _getAvailableChildId(); int _getInterruptInitialValue(int mode); bool _get_controller_config = true; int _is_metric = 1; + void _loadConfig(); + void _saveConfig(int what); }; #endif diff --git a/NodeManager.ino b/NodeManager.ino index 4e6def9f..c8647929 100644 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -33,10 +33,8 @@ void before() { /* * Register below your sensors */ - - - - + + /* * Register above your sensors */ diff --git a/README.md b/README.md index f9c299d3..e5cc8a87 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Those NodeManager's directives in the `config.h` file control which module/libra // if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage #define BATTERY_SENSOR 1 -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_MQ, 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_GAUGE, 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 @@ -171,6 +171,8 @@ Those NodeManager's directives in the `config.h` file control which module/libra #define MODULE_HCSR04 0 // Enable this module to use one of the following sensors: SENSOR_MCP9808 #define MODULE_MCP9808 0 +// Enable this module to use one of the following sensors: SENSOR_MQ +#define MODULE_MQ 0 ~~~ ### Installing the dependencies @@ -195,78 +197,96 @@ MODULE_MCP9808 | https://github.com/adafruit/Adafruit_MCP9808_Library Node Manager comes with a reasonable default configuration. If you want/need to change its settings, this can be done in your sketch, inside the `before()` function and just before registering your sensors. The following methods are exposed for your convenience: ~~~c - // send the same service message multiple times (default: 1) + // [10] send the same service message multiple times (default: 1) void setRetries(int value); #if BATTERY_MANAGER == 1 - // the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) + // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) void setBatteryMin(float value); - // the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) + // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) + // [13] after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) void setBatteryReportCycles(int value); - // after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) + // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) void setBatteryReportMinutes(int value); - // if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) + // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) void setBatteryInternalVcc(bool value); - // if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) + // [16] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) void setBatteryPin(int value); - // if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) + // [17] if setBatteryInternalVcc() is set to false, the volts per bit ratio used to calculate the battery voltage (default: 0.003363075) void setBatteryVoltsPerBit(float value); - // If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) + // [18] If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) void setBatteryReportWithInterrupt(bool value); #endif - // define the way the node should behave. It can be IDLE (stay awake withtout executing each sensors' loop), SLEEP (go to sleep for the configured interval), WAIT (wait for the configured interval), ALWAYS_ON (stay awake and execute each sensors' loop) + // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) void setSleepMode(int value); void setMode(int value); - // define for how long the board will sleep (default: 0) + int getMode(); + // [4] define for how long the board will sleep (default: 0) void setSleepTime(int value); - // define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) + int getSleepTime(); + // [5] define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) void setSleepUnit(int value); + int getSleepUnit(); // configure the node's behavior, parameters are mode, time and unit void setSleep(int value1, int value2, int value3); - // if enabled, when waking up from the interrupt, the board stops sleeping. Disable it when attaching e.g. a motion sensor (default: true) + // [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); - #endif // 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); - // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) + // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); - // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1) - void setInterruptPin(int value); // register a built-in sensor int registerSensor(int sensor_type, int pin = -1, int child_id = -1); - // un-register a sensor - void unRegisterSensor(int sensor_index); // register a custom sensor int registerSensor(Sensor* sensor); + // [26] un-register a sensor + void unRegisterSensor(int sensor_index); // return a sensor by its index Sensor* get(int sensor_index); - Sensor* getSensor(int sensor_index); - // assign a different child id to a sensor + Sensor* getSensor(int sensor_index); + // assign a different child id to a sensor bool renameSensor(int old_child_id, int new_child_id); #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, long wait = 0); - // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) + void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); + // [23] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) void setAutoPowerPins(bool value); - // manually turn the power on + // [24] manually turn the power on void powerOn(); - // manually turn the power off + // [25] manually turn the power off void powerOff(); #endif - // set this to true if you want destination node to send ack back to this node (default: false) + // [21] set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); // request and return the current timestamp from the controller long getTimestamp(); // Request the controller's configuration on startup (default: true) void setGetControllerConfig(bool value); - // Manually set isMetric setting + // [22] Manually set isMetric setting void setIsMetric(bool value); bool getIsMetric(); // Convert a temperature from celsius to fahrenheit depending on how isMetric is set float celsiusToFahrenheit(float temperature); // return true if sleep or wait is configured and hence this is a sleeping node bool isSleepingNode(); + // [1] Send a hello message back to the controller + void hello(); + // [2] Send a battery level report to the controller + void batteryReport(); + // [6] reboot the board + void reboot(); + // [8] send NodeManager's the version back to the controller + void version(); + // [7] clear the EEPROM + void clearEeprom(); + // [9] wake up the board + void wakeup(); + // process a remote request + void process(const char * message); + // return the value stored at the requested index from the EEPROM + int loadFromMemory(int index); + // save the given index of the EEPROM the provided value + void saveToMemory(int index, int value); ~~~ For example @@ -331,6 +351,8 @@ If you want to create a custom sensor and register it with NodeManager so it can void onLoop(); // define what to do during receive() when the sensor receives a message void onReceive(const MyMessage & message); + // define what to do when receiving a remote configuration message + void onProcess(Request & request); ~~~ You can then instantiate your newly created class and register with NodeManager: @@ -352,40 +374,40 @@ To do so, use `nodeManager.getSensor(child_id)` which will return a pointer to t The following methods are available for all the sensors: ~~~c - // where the sensor is attached to (default: not set) + // [1] where the sensor is attached to (default: not set) void setPin(int value); - int getPin(); - // child_id of this sensor (default: not set) + int getPin(); + // [2] child_id of this sensor (default: not set) void setChildId(int value); - int getChildId(); + int getChildId(); // presentation of this sensor (default: S_CUSTOM) void setPresentation(int value); - int getPresentation(); - // type of this sensor (default: V_CUSTOM) + int getPresentation(); + // [3] type of this sensor (default: V_CUSTOM) void setType(int value); - int getType(); - // description of the sensor (default: '') + int getType(); + // [4] description of the sensor (default: '') void setDescription(char *value); // set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); // when queried, send the message multiple times (default: 1) void setRetries(int value); - // For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) + // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) void setSamples(int value); - // If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) + // [6] If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) void setSamplesInterval(int value); - // if true will report the measure only if different then the previous one (default: false) + // [7] if true will report the measure only if different than the previous one (default: false) void setTrackLastValue(bool value); - // if track last value is enabled, force to send an update after the configured number of cycles (default: -1) + // [8] if track last value is enabled, force to send an update after the configured number of cycles (default: -1) void setForceUpdate(int value); void setForceUpdateCycles(int value); - // if track last value is enabled, force to send an update after the configured number of minutes (default: -1) + // [9] if track last value is enabled, force to send an update after the configured number of minutes (default: -1) void setForceUpdateMinutes(int value); - // the value type of this sensor (default: TYPE_INTEGER) + // [10] the value type of this sensor (default: TYPE_INTEGER) void setValueType(int value); - int getValueType(); - // for float values, set the float precision (default: 2) - void setFloatPrecision(int value); + int getValueType(); + // [11] for float values, set the float precision (default: 2) + void setFloatPrecision(int value); // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1) @@ -393,22 +415,24 @@ The following methods are available for all the sensors: int getInterruptPin(); #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, long wait = 0); - // if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) + void setPowerPins(int ground_pin, int vcc_pin, int wait_time = 50); + // [12] if enabled the pins will be automatically powered on while awake and off during sleeping (default: true) void setAutoPowerPins(bool value); - // manually turn the power on + // [13] manually turn the power on void powerOn(); - // manually turn the power off + // [14] manually turn the power off void powerOff(); #endif // get the latest recorded value from the sensor int getValueInt(); float getValueFloat(); char* getValueString(); - // After how many cycles the sensor will report back its measure (default: 1 cycle) + // [15] After how many cycles the sensor will report back its measure (default: 1 cycle) void setReportIntervalCycles(int value); - // After how many minutes the sensor will report back its measure (default: 1 cycle) + // [16] After how many minutes the sensor will report back its measure (default: 1 cycle) void setReportIntervalMinutes(int value); + // process a remote request + void process(Request & request); ~~~ #### Sensor's specific configuration @@ -417,49 +441,49 @@ Each sensor class can expose additional methods. * SensorAnalogInput / SensorLDR / SensorRain / SensorSoilMoisture ~~~c - // the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) + // [101] the analog reference to use (default: not set, can be either INTERNAL or DEFAULT) void setReference(int value); - // reverse the value or the percentage (e.g. 70% -> 30%) (default: false) + // [102] reverse the value or the percentage (e.g. 70% -> 30%) (default: false) void setReverse(bool value); - // when true returns the value as a percentage (default: true) + // [103] when true returns the value as a percentage (default: true) void setOutputPercentage(bool value); - // minimum value for calculating the percentage (default: 0) + // [104] minimum value for calculating the percentage (default: 0) void setRangeMin(int value); - // maximum value for calculating the percentage (default: 1024) + // [105] maximum value for calculating the percentage (default: 1024) void setRangeMax(int value); ~~~ * SensorThermistor ~~~c - // resistance at 25 degrees C (default: 10000) + // [101] resistance at 25 degrees C (default: 10000) void setNominalResistor(long value); - // temperature for nominal resistance (default: 25) + // [102] temperature for nominal resistance (default: 25) void setNominalTemperature(int value); - // The beta coefficient of the thermistor (default: 3950) + // [103] The beta coefficient of the thermistor (default: 3950) void setBCoefficient(int value); - // the value of the resistor in series with the thermistor (default: 10000) + // [104] the value of the resistor in series with the thermistor (default: 10000) void setSeriesResistor(long value); - // set a temperature offset + // [105] set a temperature offset void setOffset(float value); ~~~ * SensorMQ ~~~c - // define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); + // [101] define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1); void setTargetGas(int value); - // define the load resistance on the board, in kilo ohms (default: 1); + // [102] define the load resistance on the board, in kilo ohms (default: 1); void setRlValue(float value); - // define the Ro resistance on the board (default: 10000); + // [103] define the Ro resistance on the board (default: 10000); void setRoValue(float value); - // Sensor resistance in clean air (default: 9.83); + // [104] Sensor resistance in clean air (default: 9.83); void setCleanAirFactor(float value); - // define how many samples you are going to take in the calibration phase (default: 50); + // [105] define how many samples you are going to take in the calibration phase (default: 50); void setCalibrationSampleTimes(int value); - // define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); + // [106] define the time interal(in milisecond) between each samples in the cablibration phase (default: 500); void setCalibrationSampleInterval(int value); - // define how many samples you are going to take in normal operation (default: 50); + // [107] define how many samples you are going to take in normal operation (default: 50); void setReadSampleTimes(int value); - // define the time interal(in milisecond) between each samples in the normal operations (default: 5); + // [108] define the time interal(in milisecond) between each samples in the normal operations (default: 5); void setReadSampleInterval(int value); // set the LPGCurve array (default: {2.3,0.21,-0.47}) void setLPGCurve(float *value); @@ -469,19 +493,35 @@ Each sensor class can expose additional methods. void setSmokeCurve(float *value); ~~~ +* SensorACS712 +~~~c + // [101] set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); + void setmVPerAmp(int value); + // [102] set ACS offset (default: 2500); + void setOffset(int value); +~~~ + +* 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); +~~~ + * SensorDigitalOutput / SensorRelay / SensorLatchingRelay ~~~c - // set how to initialize the output (default: LOW) + // [101] set how to initialize the output (default: LOW) void setInitialValue(int value); - // if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) + // [102] if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) void setPulseWidth(int value); - // define which value to set to the output when set to on (default: HIGH) + // [103] define which value to set to the output when set to on (default: HIGH) void setOnValue(int value); - // when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) + // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) void setLegacyMode(bool value); - // automatically turn the output off after the given number of minutes + // [105] automatically turn the output off after the given number of minutes void setSafeguard(int value); - // if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) + // [106] if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) void setInputIsElapsed(bool value); // manually switch the output to the provided value void set(int value); @@ -491,74 +531,54 @@ Each sensor class can expose additional methods. * SensorSwitch / SensorDoor / SensorMotion ~~~c - // set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) + // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); - // milliseconds to wait before reading the input (default: 0) + int getMode(); + // [102] milliseconds to wait before reading the input (default: 0) void setDebounce(int value); - // time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) + // [103] time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) void setTriggerTime(int value); - // Set initial value on the interrupt pin (default: HIGH) + // [104] Set initial value on the interrupt pin (default: HIGH) void setInitial(int value); + int getInitial(); ~~~ * SensorDs18b20** ~~~c - // return the sensors' device address - DeviceAddress* getDeviceAddress(); // returns the sensor's resolution in bits int getResolution(); - // set the sensor's resolution in bits + // [101] set the sensor's resolution in bits void setResolution(int value); - // sleep while DS18B20 calculates temperature (default: false) + // [102] sleep while DS18B20 calculates temperature (default: false) void setSleepDuringConversion(bool value); + // return the sensors' device address + DeviceAddress* getDeviceAddress(); ~~~ -* SensorBME280 -~~~c - // define how many pressure samples to keep track of for calculating the forecast (default: 5) - void setForecastSamplesCount(int value); -~~~ - -* SensorSonoff -~~~c - // set the button's pin (default: 0) - void setButtonPin(int value); - // set the relay's pin (default: 12) - void setRelayPin(int value); - // set the led's pin (default: 13) - void setLedPin(int value); -~~~ - -* SensorBMP085 +* SensorBME280 / SensorBMP085 ~~~c - // define how many pressure samples to keep track of for calculating the forecast (default: 5) + // [101] define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); ~~~ * SensorHCSR04 ~~~c - // Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // [101] Arduino pin tied to trigger pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setTriggerPin(int value); - // Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) + // [102] Arduino pin tied to echo pin on the ultrasonic sensor (default: the pin set while registering the sensor) void setEchoPin(int value); - // Maximum distance we want to ping for (in centimeters) (default: 300) + // [103] Maximum distance we want to ping for (in centimeters) (default: 300) void setMaxDistance(int value); ~~~ -* SensorACS712 -~~~c - // set how many mV are equivalent to 1 Amp. The value depends on the module (100 for 20A Module, 66 for 30A Module) (default: 185); - void setmVPerAmp(int value); - // set ACS offset (default: 2500); - void setOffset(int value); -~~~ - -* SensorRainGauge +* SensorSonoff ~~~c - // set how frequently to report back to the controller in minutes. After reporting the measure is resetted (default: 60); - void setReportInterval(int value); - // set how many mm of rain to count for each tip (default: 0.11); - void setSingleTip(float value); + // [101] set the button's pin (default: 0) + void setButtonPin(int value); + // [102] set the relay's pin (default: 12) + void setRelayPin(int value); + // [103] set the led's pin (default: 13) + void setLedPin(int value); ~~~ ### Upload your sketch @@ -570,51 +590,65 @@ When `DEBUG` is enabled, detailed information is available through the serial po ### Communicate with NodeManager and its sensors -You can interact with each registered sensor asking to execute their main tasks by sending to the child id a `REQ` command. For example to request the temperature to node_id 254 and child_id 1: +You can interact with each registered sensor by asking to execute their main tasks by sending to the child id a `REQ` command (or a `SET` for output sensors like relays). For example to request the temperature to node_id 254 and child_id 1: `254;1;2;0;0;` To activate a relay connected to the same node, child_id 100: -`254;100;2;0;2;1` +`254;100;1;0;2;1` No need to implement anything on your side since for built-in sensor types this is handled automatically. -Once the node will be sleeping, it will report automatically each measure at the end of every sleep cycle. - -NodeManager exposes a configuration service by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message. -The following custom commands are available: - -NodeManager command | Description - ------------- | ------------- -BATTERY | Report the battery level back to the gateway/controller -HELLO | Hello request -REBOOT | Reboot the board -CLEAR | Wipe from the EEPROM NodeManager's settings -VERSION | Respond with NodeManager's version -IDxxx | Change the node id to the provided one. E.g. ID025: change the node id to 25. Requires a reboot to take effect -INTVLnnnX | Set the wait/sleep interval to nnn where X is S=Seconds, M=mins, H=Hours, D=Days. E.g. INTVL010M would be 10 minutes -MODEx | change the way the node behaves. 0: stay awake withtout executing each sensors' loop(), 1: go to sleep for the configured interval, 2: wait for the configured interval, 3: stay awake and execute each sensors' loop() -AWAKE | When received after a sleeping cycle or during wait, abort the cycle and stay awake - -For example, to request the battery level to node id 254: - -`254;200;2;0;48;BATTERY` +Once the node will be sleeping, it will report automatically each measure at the end of every sleep cycle, unless configured otherwise. -To set the sleeping cycle to 1 hour: +NodeManager exposes also a configuration service by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message if successful. +Almost all the functions made available through the API can be called remotely. To do so, the payload must be in the format `[,]` where function_id is the number between square brackets you can find in the description just above each function and, if the function takes and argument, this can be passed along in value_to_set. +For example, to request a battery report, find the function you need to call remotely within the documentation: +~~~c + // [2] Send a battery level report to the controller + void batteryReport(); +~~~ +In this case the function_id will be 2. To request a battery report to the node_id 100, send the following message: +`;;;0;;` +`100;200;2;0;48;2` -`254;200;2;0;48;INTVL001H` +The change the sleep time from e.g. 10 minutes as set in the sketch to 5 minutes: +~~~c + // [4] define for how long the board will sleep (default: 0) + void setSleepTime(int value); +~~~ +`;;;0;;,` +`100;200;2;0;48;4,5` To ask the node to start sleeping (and waking up based on the previously configured interval): +~~~c + // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) + void setSleepMode(int value); +~~~ +`100;200;2;0;48;3,1` -`254;200;2;0;48;MODE1` - -To wake up a node previously configured with `MODE1`, send the following just after reporting `AWAKE`: +To wake up a node previously configured as sleeping, send the following just it wakes up next: +~~~c + // [9] wake up the board + void wakeup(); +~~~ +`100;200;2;0;48;9` -`254;200;2;0;48;WAKEUP` +The same protocol can be used to execute remotely also sensor-specific functions. In this case the message has to be sent to the sensor's child_id, with a V_CUSTOM type of message. For example if you want to collect and average 10 samples for child_id 1: +~~~c + // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) + void setSamples(int value); +~~~ +`100;1;2;0;48;5,10` -In addition, NodeManager will report with custom messages every time the board is going to sleep (`SLEEPING`) or it is awake (`AWAKE`). +If you want to decrease the temperature offset of a thermistor sensor to -2: +~~~c + // [105] set a temperature offset + void setOffset(float value); +~~~ +`100;1;2;0;48;105,-2` -If `PERSIST` is enabled, the settings provided with `INTVLnnnX` and `MODEx` are saved to the EEPROM to be persistent even after rebooting the board. +Please note that anything set remotely will NOT persist a reboot apart from those provided to setSleepMode(), setSleepTime() and setSleepUnit() which are saved to the EEPROM (provided `PERSIST` is enabled). ## Understanding NodeManager: how it works diff --git a/config.h b/config.h index eded817e..5fe1facf 100644 --- a/config.h +++ b/config.h @@ -102,7 +102,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_MQ, SENSOR_ML8511, SENSOR_ACS712, SENSOR_RAIN_GAUGE +// 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 #define MODULE_ANALOG_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT #define MODULE_DIGITAL_INPUT 1 @@ -130,5 +130,7 @@ #define MODULE_HCSR04 0 // Enable this module to use one of the following sensors: SENSOR_MCP9808 #define MODULE_MCP9808 0 +// Enable this module to use one of the following sensors: SENSOR_MQ +#define MODULE_MQ 0 #endif From 00ae05a8cec0cc27c3571649f8e26064e29ab72d Mon Sep 17 00:00:00 2001 From: user2684 Date: Tue, 6 Jun 2017 18:37:33 +0200 Subject: [PATCH 12/33] Optimized functions between Sensor and NodeManager classes (#140) --- NodeManager.cpp | 50 +++++++++++++++++++------------------------------ NodeManager.h | 19 +++++-------------- README.md | 26 ++++++++++--------------- 3 files changed, 34 insertions(+), 61 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 5e5e6117..e73a9518 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -289,12 +289,6 @@ int Sensor::getType() { void Sensor::setDescription(char* value) { _description = value; } -void Sensor::setAck(bool value) { - _ack = value; -} -void Sensor::setRetries(int value) { - _retries = value; -} void Sensor::setSamples(int value) { _samples = value; } @@ -336,12 +330,6 @@ void Sensor::setFloatPrecision(int value) { _powerManager.powerOff(); } #endif -void Sensor::setSleepBetweenSend(int value) { - _sleep_between_send = value; -} -void Sensor::setInterruptPin(int value) { - _interrupt_pin = value; -} int Sensor::getInterruptPin() { return _interrupt_pin; } @@ -373,7 +361,7 @@ void Sensor::presentation() { Serial.print(F(" T=")); Serial.println(_presentation); #endif - present(_child_id, _presentation,_description,_ack); + present(_child_id, _presentation,_description,_node_manager->getAck()); } // call the sensor-specific implementation of before @@ -511,9 +499,9 @@ void Sensor::process(Request & request) { // send a message to the network void Sensor::_send(MyMessage & message) { // send the message, multiple times if requested - for (int i = 0; i < _retries; i++) { + for (int i = 0; i < _node_manager->getRetries(); i++) { // if configured, sleep beetween each send - if (_sleep_between_send > 0) sleep(_sleep_between_send); + if (_node_manager->getSleepBetweenSend() > 0) sleep(_node_manager->getSleepBetweenSend()); #if DEBUG == 1 Serial.print(F("SEND D=")); Serial.print(message.destination); @@ -530,7 +518,7 @@ void Sensor::_send(MyMessage & message) { Serial.print(F(" F=")); Serial.println(message.getFloat()); #endif - send(message,_ack); + send(message,_node_manager->getAck()); } } @@ -1371,9 +1359,6 @@ SensorSwitch::SensorSwitch(NodeManager* node_manager, int child_id, int pin): Se void SensorSwitch::setMode(int value) { _mode = value; } -int SensorSwitch::getMode() { - return _mode; -} void SensorSwitch::setDebounce(int value) { _debounce = value; } @@ -1383,15 +1368,15 @@ void SensorSwitch::setTriggerTime(int value) { void SensorSwitch::setInitial(int value) { _initial = value; } -int SensorSwitch::getInitial() { - return _initial; -} // what to do during before void SensorSwitch::onBefore() { // initialize the value 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); } // what to do during setup @@ -2321,6 +2306,9 @@ NodeManager::NodeManager() { void NodeManager::setRetries(int value) { _retries = value; } +int NodeManager::getRetries() { + return _retries; +} #if BATTERY_MANAGER == 1 void NodeManager::setBatteryMin(float value) { _battery_min = value; @@ -2403,9 +2391,15 @@ void NodeManager::setInterrupt(int pin, int mode, int pull) { void NodeManager::setSleepBetweenSend(int value) { _sleep_between_send = value; } +int NodeManager::getSleepBetweenSend() { + return _sleep_between_send; +} void NodeManager::setAck(bool value) { _ack = value; } +bool NodeManager::getAck() { + return _ack; +} void NodeManager::setGetControllerConfig(bool value) { _get_controller_config = value; } @@ -2485,15 +2479,9 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { // ensure an interrupt pin is provided if (pin != INTERRUPT_PIN_1 && pin != INTERRUPT_PIN_2) return -1; // register the sensor - int index = 0; - if (sensor_type == SENSOR_SWITCH) index = registerSensor(new SensorSwitch(this,child_id, pin)); - else if (sensor_type == SENSOR_DOOR) index = registerSensor(new SensorDoor(this,child_id, pin)); - else if (sensor_type == SENSOR_MOTION) index = registerSensor(new SensorMotion(this,child_id, pin)); - // set an interrupt on the pin and set the initial value - SensorSwitch* sensor = (SensorSwitch*)getSensor(index); - sensor->setInterruptPin(pin); - setInterrupt(pin,sensor->getMode(),sensor->getInitial()); - return index; + if (sensor_type == SENSOR_SWITCH) return registerSensor(new SensorSwitch(this,child_id, pin)); + else if (sensor_type == SENSOR_DOOR) return registerSensor(new SensorDoor(this,child_id, pin)); + else if (sensor_type == SENSOR_MOTION) return registerSensor(new SensorMotion(this,child_id, pin)); } #endif #if MODULE_DS18B20 == 1 diff --git a/NodeManager.h b/NodeManager.h index 8498dd94..47f8ec32 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -425,10 +425,6 @@ class Sensor { int getType(); // [4] description of the sensor (default: '') void setDescription(char *value); - // set this to true if you want destination node to send ack back to this node (default: false) - void setAck(bool value); - // when queried, send the message multiple times (default: 1) - void setRetries(int value); // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) void setSamples(int value); // [6] If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) @@ -445,11 +441,6 @@ class Sensor { int getValueType(); // [11] for float values, set the float precision (default: 2) void setFloatPrecision(int value); - // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) - void setSleepBetweenSend(int value); - // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1) - void setInterruptPin(int value); - int getInterruptPin(); #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); @@ -470,6 +461,8 @@ class Sensor { void setReportIntervalMinutes(int value); // process a remote request void process(Request & request); + // return the pin the interrupt is attached to + int getInterruptPin(); // define what to do at each stage of the sketch virtual void before(); virtual void presentation(); @@ -486,14 +479,11 @@ class Sensor { MyMessage _msg; MyMessage _msg_service; NodeManager* _node_manager; - int _sleep_between_send = 0; int _pin = -1; int _child_id; int _presentation = S_CUSTOM; int _type = V_CUSTOM; char* _description = ""; - bool _ack = false; - int _retries = 1; int _samples = 1; int _samples_interval = 0; bool _track_last_value = false; @@ -809,14 +799,12 @@ class SensorSwitch: public Sensor { SensorSwitch(NodeManager* node_manager, int child_id, int pin); // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); - int getMode(); // [102] milliseconds to wait before reading the input (default: 0) void setDebounce(int value); // [103] time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) void setTriggerTime(int value); // [104] Set initial value on the interrupt pin (default: HIGH) void setInitial(int value); - int getInitial(); // define what to do at each stage of the sketch void onBefore(); void onSetup(); @@ -1126,6 +1114,7 @@ class NodeManager { NodeManager(); // [10] send the same service message multiple times (default: 1) void setRetries(int value); + int getRetries(); #if BATTERY_MANAGER == 1 // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) void setBatteryMin(float value); @@ -1164,6 +1153,7 @@ class NodeManager { void setInterrupt(int pin, int mode, int pull = -1); // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); + int getSleepBetweenSend(); // register a built-in sensor int registerSensor(int sensor_type, int pin = -1, int child_id = -1); // register a custom sensor @@ -1187,6 +1177,7 @@ class NodeManager { #endif // [21] set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); + bool getAck(); // request and return the current timestamp from the controller long getTimestamp(); // Request the controller's configuration on startup (default: true) diff --git a/README.md b/README.md index e5cc8a87..faef3cad 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,7 @@ Node Manager comes with a reasonable default configuration. If you want/need to ~~~c // [10] send the same service message multiple times (default: 1) void setRetries(int value); + int getRetries(); #if BATTERY_MANAGER == 1 // [11] the expected vcc when the batter is fully discharged, used to calculate the percentage (default: 2.7) void setBatteryMin(float value); @@ -216,6 +217,8 @@ Node Manager comes with a reasonable default configuration. If you want/need to void setBatteryVoltsPerBit(float value); // [18] If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: true) void setBatteryReportWithInterrupt(bool value); + // [2] Send a battery level report to the controller + void batteryReport(); #endif // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) void setSleepMode(int value); @@ -235,6 +238,7 @@ Node Manager comes with a reasonable default configuration. If you want/need to void setInterrupt(int pin, int mode, int pull = -1); // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) void setSleepBetweenSend(int value); + int getSleepBetweenSend(); // register a built-in sensor int registerSensor(int sensor_type, int pin = -1, int child_id = -1); // register a custom sensor @@ -258,6 +262,7 @@ Node Manager comes with a reasonable default configuration. If you want/need to #endif // [21] set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); + bool getAck(); // request and return the current timestamp from the controller long getTimestamp(); // Request the controller's configuration on startup (default: true) @@ -271,8 +276,6 @@ Node Manager comes with a reasonable default configuration. If you want/need to bool isSleepingNode(); // [1] Send a hello message back to the controller void hello(); - // [2] Send a battery level report to the controller - void batteryReport(); // [6] reboot the board void reboot(); // [8] send NodeManager's the version back to the controller @@ -282,10 +285,10 @@ Node Manager comes with a reasonable default configuration. If you want/need to // [9] wake up the board void wakeup(); // process a remote request - void process(const char * message); + void process(Request & request); // return the value stored at the requested index from the EEPROM int loadFromMemory(int index); - // save the given index of the EEPROM the provided value + // [27] save the given index of the EEPROM the provided value void saveToMemory(int index, int value); ~~~ @@ -388,10 +391,6 @@ The following methods are available for all the sensors: int getType(); // [4] description of the sensor (default: '') void setDescription(char *value); - // set this to true if you want destination node to send ack back to this node (default: false) - void setAck(bool value); - // when queried, send the message multiple times (default: 1) - void setRetries(int value); // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) void setSamples(int value); // [6] If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0) @@ -408,11 +407,6 @@ The following methods are available for all the sensors: int getValueType(); // [11] for float values, set the float precision (default: 2) void setFloatPrecision(int value); - // optionally sleep interval in milliseconds before sending each message to the radio network (default: 0) - void setSleepBetweenSend(int value); - // set the interrupt pin the sensor is attached to so its loop() will be executed only upon that interrupt (default: -1) - void setInterruptPin(int value); - int getInterruptPin(); #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); @@ -433,6 +427,8 @@ The following methods are available for all the sensors: void setReportIntervalMinutes(int value); // process a remote request void process(Request & request); + // return the pin the interrupt is attached to + int getInterruptPin(); ~~~ #### Sensor's specific configuration @@ -533,17 +529,15 @@ Each sensor class can expose additional methods. ~~~c // [101] set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE) void setMode(int value); - int getMode(); // [102] milliseconds to wait before reading the input (default: 0) void setDebounce(int value); // [103] time to wait in milliseconds after a change is detected to allow the signal to be restored to its normal value (default: 0) void setTriggerTime(int value); // [104] Set initial value on the interrupt pin (default: HIGH) void setInitial(int value); - int getInitial(); ~~~ -* SensorDs18b20** +* SensorDs18b20 ~~~c // returns the sensor's resolution in bits int getResolution(); From 69f2705cd10cddbc3a6a8769404cadc4d43ac5f8 Mon Sep 17 00:00:00 2001 From: user2684 Date: Tue, 6 Jun 2017 18:58:45 +0200 Subject: [PATCH 13/33] PowerManager allows Vcc only to be set --- NodeManager.cpp | 87 +++++++++++++++++++++++-------------------------- NodeManager.h | 7 ++-- README.md | 2 ++ 3 files changed, 45 insertions(+), 51 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index e73a9518..f7a8ed10 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -4,36 +4,6 @@ #include "NodeManager.h" -/*************************************** - Global functions -*/ - -// return vcc in V -float getVcc() { - #ifndef MY_GATEWAY_ESP8266 - // Measure Vcc against 1.1V Vref - #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) - ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)); - #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) - ADMUX = (_BV(MUX5) | _BV(MUX0)); - #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) - ADMUX = (_BV(MUX3) | _BV(MUX2)); - #else - ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)); - #endif - // Vref settle - wait(70); - // Do conversion - ADCSRA |= _BV(ADSC); - while (bit_is_set(ADCSRA, ADSC)) {}; - // return Vcc in mV - return (float)((1125300UL) / ADC) / 1000; - #else - return (float)0; - #endif -} - - /*************************************** PowerManager */ @@ -46,26 +16,26 @@ void PowerManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) { Serial.print(F(" V=")); Serial.println(vcc_pin); #endif - // configure the vcc pin as output and initialize to high (power on) - _vcc_pin = vcc_pin; - pinMode(_vcc_pin, OUTPUT); - digitalWrite(_vcc_pin, HIGH); - // configure the ground pin as output and initialize to low - _ground_pin = ground_pin; - pinMode(_ground_pin, OUTPUT); - digitalWrite(_ground_pin, LOW); + if (_ground_pin > 0) { + // configure the ground pin as output and initialize to low + _ground_pin = ground_pin; + pinMode(_ground_pin, OUTPUT); + digitalWrite(_ground_pin, LOW); + } + if (_vcc_pin > 0) { + // configure the vcc pin as output and initialize to high (power on) + _vcc_pin = vcc_pin; + pinMode(_vcc_pin, OUTPUT); + digitalWrite(_vcc_pin, HIGH); + } + // save wait time _wait = wait_time; } -// return true if power pins have been configured -bool PowerManager::isConfigured() { - if (_vcc_pin != -1 && _ground_pin != -1) return true; - return false; -} // turn on the sensor by activating its power pins void PowerManager::powerOn() { - if (! isConfigured()) return; + if (_vcc_pin == -1) return; #if DEBUG == 1 Serial.print(F("ON P=")); Serial.println(_vcc_pin); @@ -78,7 +48,7 @@ void PowerManager::powerOn() { // turn off the sensor void PowerManager::powerOff() { - if (! isConfigured()) return; + if (_vcc_pin == -1) return; #if DEBUG == 1 Serial.print(F("OFF P=")); Serial.println(_vcc_pin); @@ -768,7 +738,7 @@ void SensorML8511::onSetup() { void SensorML8511::onLoop() { // read the voltage int uvLevel = analogRead(_pin); - int refLevel = getVcc()*1024/3.3; + int refLevel = _node_manager->getVcc()*1024/3.3; //Use the 3.3V power pin as a reference to get a very accurate output value from sensor float outputVoltage = 3.3 / refLevel * uvLevel; //Convert the voltage to a UV intensity level @@ -2997,6 +2967,31 @@ void NodeManager::saveToMemory(int index, int value) { saveState(index+EEPROM_USER_START, value); } +// return vcc in V +float NodeManager::getVcc() { + #ifndef MY_GATEWAY_ESP8266 + // Measure Vcc against 1.1V Vref + #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)); + #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) + ADMUX = (_BV(MUX5) | _BV(MUX0)); + #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) + ADMUX = (_BV(MUX3) | _BV(MUX2)); + #else + ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)); + #endif + // Vref settle + wait(70); + // Do conversion + ADCSRA |= _BV(ADSC); + while (bit_is_set(ADCSRA, ADSC)) {}; + // return Vcc in mV + return (float)((1125300UL) / ADC) / 1000; + #else + return (float)0; + #endif +} + // send a message to the network void NodeManager::_send(MyMessage & message) { // send the message, multiple times if requested diff --git a/NodeManager.h b/NodeManager.h index 47f8ec32..d71114c5 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -332,10 +332,6 @@ class PowerManager { void powerOn(); // turns the power pins on void powerOff(); - // returns the Vcc voltge - float getVcc(); - // turns true if power pins are configured - bool isConfigured(); private: int _vcc_pin = -1; int _ground_pin = -1; @@ -1205,6 +1201,8 @@ class NodeManager { int loadFromMemory(int index); // [27] save the given index of the EEPROM the provided value void saveToMemory(int index, int value); + // return vcc in V + float getVcc(); // hook into the main sketch functions void before(); void presentation(); @@ -1221,7 +1219,6 @@ class NodeManager { bool _battery_internal_vcc = true; int _battery_pin = -1; float _battery_volts_per_bit = 0.003363075; - float _getVcc(); #endif #if POWER_MANAGER == 1 // to optionally controller power pins diff --git a/README.md b/README.md index faef3cad..65f1d1ea 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,8 @@ Node Manager comes with a reasonable default configuration. If you want/need to int loadFromMemory(int index); // [27] save the given index of the EEPROM the provided value void saveToMemory(int index, int value); + // return vcc in V + float getVcc(); ~~~ For example From 15a199c95fdb79600a17a22ca5045088a8ed706a Mon Sep 17 00:00:00 2001 From: user2684 Date: Sat, 24 Jun 2017 18:55:36 +0200 Subject: [PATCH 14/33] Latching Relay can not be configured with a single line and controlled with a single child id --- NodeManager.cpp | 166 ++++++++++++++++++++++++++++++++++-------------- NodeManager.h | 36 ++++++++--- README.md | 28 ++++---- 3 files changed, 162 insertions(+), 68 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index f7a8ed10..2f5f832f 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -1000,6 +1000,7 @@ void SensorDigitalInput::onProcess(Request & request) { } #endif + #if MODULE_DIGITAL_OUTPUT == 1 /* SensorDigitalOutput @@ -1011,13 +1012,7 @@ SensorDigitalOutput::SensorDigitalOutput(NodeManager* node_manager, int child_id // what to do during before void SensorDigitalOutput::onBefore() { - // set the pin as output and initialize it accordingly - pinMode(_pin, OUTPUT); - _state = _initial_value == LOW ? LOW : HIGH; - digitalWrite(_pin, _state); - // the initial value is now the current value - _value_int = _initial_value; - // create the safeguard timer + _setupPin(_pin); } // what to do during setup @@ -1025,12 +1020,6 @@ void SensorDigitalOutput::onSetup() { } // setter/getter -void SensorDigitalOutput::setInitialValue(int value) { - _initial_value = value; -} -void SensorDigitalOutput::setPulseWidth(int value) { - _pulse_width = value; -} void SensorDigitalOutput::setOnValue(int value) { _on_value = value; } @@ -1040,24 +1029,27 @@ void SensorDigitalOutput::setLegacyMode(bool value) { void SensorDigitalOutput::setSafeguard(int value) { _safeguard_timer->set(value,MINUTES); } -int SensorDigitalOutput::getState() { - return _state; +int SensorDigitalOutput::getStatus() { + return _status; } void SensorDigitalOutput::setInputIsElapsed(bool value) { _input_is_elapsed = value; } +void SensorDigitalOutput::setWaitAfterSet(int value) { + _wait_after_set = value; +} // main task void SensorDigitalOutput::onLoop() { - // set the value to -1 so to avoid reporting to the gateway during loop - _value_int = -1; - _last_value_int = -1; + // 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); + if (_safeguard_timer->isOver()) setStatus(OFF); } } @@ -1066,11 +1058,11 @@ 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 if ( (message.getCommand() == C_SET && ! _legacy_mode) || (message.getCommand() == C_REQ && _legacy_mode)) { // switch the output - set(message.getInt()); + setStatus(message.getInt()); } if (message.getCommand() == C_REQ && ! _legacy_mode) { // return the current status - _value_int = _state; + _value_int = _status; } } @@ -1078,59 +1070,78 @@ void SensorDigitalOutput::onReceive(const MyMessage & message) { void SensorDigitalOutput::onProcess(Request & request) { int function = request.getFunction(); switch(function) { - case 101: setInitialValue(request.getValueInt()); break; - case 102: setPulseWidth(request.getValueInt()); break; case 103: setOnValue(request.getValueInt()); break; case 104: setLegacyMode(request.getValueInt()); break; case 105: setSafeguard(request.getValueInt()); break; case 106: setInputIsElapsed(request.getValueInt()); break; + case 107: setWaitAfterSet(request.getValueInt()); break; default: return; } _send(_msg_service.set(function)); } // write the value to the output -void SensorDigitalOutput::set(int value) { +void SensorDigitalOutput::setStatus(int value) { + // pre-process the input value if (_input_is_elapsed) { - if (value == LOW) { - // stop the timer + // the input provided is an elapsed time + if (value == OFF) { + // turning it off, no need for a safeguard anymore, stop the timer _safeguard_timer->stop(); - } else { + } + else if (value == ON) { // 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; + // if the input is an elapsed time, unless the value is OFF, the output will be always ON + value = ON; } } 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 (value == ON && _safeguard_timer->isConfigured() && ! _safeguard_timer->isRunning()) _safeguard_timer->start(); } + _setStatus(value); + // wait if needed for relay drawing a lot of current + if (_wait_after_set > 0) wait(_wait_after_set); + // store the new status so it will be sent to the controller + _status = value; + _value_int = value; +} + +// setup the provided pin for output +void SensorDigitalOutput::_setupPin(int pin) { + // set the pin as output and initialize it accordingly + pinMode(pin, OUTPUT); + // setup the pin in a off status + _status = ! _on_value; + digitalWrite(pin, _status); + // the initial value is now the current value + _value_int = _status; +} + +// switch to the requested status +void SensorDigitalOutput::_setStatus(int value) { + int value_to_write = _getValueToWrite(value); + // set the value to the pin + digitalWrite(_pin, value_to_write); #if DEBUG == 1 Serial.print(F("DOUT I=")); Serial.print(_child_id); Serial.print(F(" P=")); Serial.print(_pin); Serial.print(F(" V=")); - Serial.print(value); - Serial.print(F(" P=")); - Serial.println(_pulse_width); + Serial.println(value_to_write); #endif - // reverse the value if needed +} + +// reverse the value if needed based on the _on_value +int SensorDigitalOutput::_getValueToWrite(int value) { int value_to_write = value; if (_on_value == LOW) { - if (value == HIGH) value_to_write = LOW; - if (value == LOW) value_to_write = HIGH; + // if the "on" value is LOW, reverse the value + if (value == ON) value_to_write = LOW; + if (value == OFF) value_to_write = HIGH; } - // set the value - digitalWrite(_pin, value_to_write); - if (_pulse_width > 0) { - // if this is a pulse output, restore the value to the original value after the pulse - wait(_pulse_width); - digitalWrite(_pin, value_to_write == 0 ? HIGH: LOW); - } - // store the current value so it will be sent to the controller - _state = value; - _value_int = value; + return value_to_write; } @@ -1151,8 +1162,67 @@ SensorRelay::SensorRelay(NodeManager* node_manager, int child_id, int pin): Sens // contructor SensorLatchingRelay::SensorLatchingRelay(NodeManager* node_manager, int child_id, int pin): SensorRelay(node_manager, child_id, pin) { - // like a sensor with a default pulse set - setPulseWidth(50); + // set the "off" pin to the provided pin + setPinOff(pin); + // set the "on" pin to the provided pin + 1 + setPinOn(pin + 1); +} + +// setter/getter +void SensorLatchingRelay::setPulseWidth(int value) { + _pulse_width = value; +} +void SensorLatchingRelay::setPinOn(int value) { + _pin_on = value; +} +void SensorLatchingRelay::setPinOff(int value) { + _pin_off = value; +} + +// what to do during before +void SensorLatchingRelay::onBefore() { + _setupPin(_pin_on); + _setupPin(_pin_off); +} + +// what to do when receiving a remote message +void SensorLatchingRelay::onProcess(Request & request) { + int function = request.getFunction(); + if (function < 200) { + // if this is for SensorDigitalOutput call its onProcess() + SensorDigitalOutput::onProcess(request); + return; + } + switch(function) { + case 201: setPulseWidth(request.getValueInt()); break; + case 202: setPinOff(request.getValueInt()); break; + case 203: setPinOn(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + +// switch to the requested status +void SensorLatchingRelay::_setStatus(int value) { + // select the right pin to send the pulse to + int pin = value == OFF ? _pin_off : _pin_on; + // set the value + digitalWrite(pin, _on_value); + // wait for the given time before restoring the value to the original value after the pulse + wait(_pulse_width); + digitalWrite(pin, ! _on_value); + #if DEBUG == 1 + Serial.print(F("LAT I=")); + Serial.print(_child_id); + Serial.print(F(" P=")); + Serial.print(pin); + Serial.print(F(" S=")); + Serial.print(value); + Serial.print(F(" V=")); + Serial.print(_on_value); + Serial.print(F(" P=")); + Serial.println(_pulse_width); + #endif } #endif diff --git a/NodeManager.h b/NodeManager.h index d71114c5..46474ba1 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -26,6 +26,10 @@ #define DAYS 3 #define CYCLES 4 +// define on/off +#define OFF 0 +#define ON 1 + // define value type #define TYPE_INTEGER 0 #define TYPE_FLOAT 1 @@ -681,10 +685,6 @@ class SensorDigitalInput: public Sensor { class SensorDigitalOutput: public Sensor { public: SensorDigitalOutput(NodeManager* node_manager, int child_id, int pin); - // [101] set how to initialize the output (default: LOW) - void setInitialValue(int value); - // [102] if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) - void setPulseWidth(int value); // [103] define which value to set to the output when set to on (default: HIGH) void setOnValue(int value); // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) @@ -693,10 +693,12 @@ class SensorDigitalOutput: public Sensor { void setSafeguard(int value); // [106] if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) void setInputIsElapsed(bool value); + // [107] optionally wait for the given number of milliseconds after changing the status (default: 0) + void setWaitAfterSet(int value); // manually switch the output to the provided value - void set(int value); + void setStatus(int value); // get the current state - int getState(); + int getStatus(); // define what to do at each stage of the sketch void onBefore(); void onSetup(); @@ -704,13 +706,15 @@ class SensorDigitalOutput: public Sensor { void onReceive(const MyMessage & message); void onProcess(Request & request); protected: - int _initial_value = LOW; int _on_value = HIGH; - int _state = 0; - int _pulse_width = 0; + int _status = OFF; bool _legacy_mode = false; bool _input_is_elapsed = false; + int _wait_after_set = 0; Timer* _safeguard_timer; + void _setupPin(int pin); + virtual void _setStatus(int value); + int _getValueToWrite(int value); }; @@ -728,6 +732,20 @@ class SensorRelay: public SensorDigitalOutput { class SensorLatchingRelay: public SensorRelay { public: SensorLatchingRelay(NodeManager* node_manager, int child_id, int pin); + // [201] set the duration of the pulse to send in ms to activate the relay (default: 50) + void setPulseWidth(int value); + // [202] set the pin which turns the relay off (default: the pin provided while registering the sensor) + void setPinOff(int value); + // [203] set the pin which turns the relay on (default: the pin provided while registering the sensor + 1) + void setPinOn(int value); + // define what to do at each stage of the sketch + void onBefore(); + void onProcess(Request & request); + protected: + int _pin_on; + int _pin_off; + int _pulse_width = 50; + void _setStatus(int value); }; #endif diff --git a/README.md b/README.md index 65f1d1ea..cca7ebe0 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,7 @@ Node Manager comes with a reasonable default configuration. If you want/need to // [24] manually turn the power on void powerOn(); // [25] manually turn the power off - void powerOff(); + void powerOff(); #endif // [21] set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); @@ -507,12 +507,8 @@ Each sensor class can expose additional methods. void setSingleTip(float value); ~~~ -* SensorDigitalOutput / SensorRelay / SensorLatchingRelay +* SensorDigitalOutput / SensorRelay ~~~c - // [101] set how to initialize the output (default: LOW) - void setInitialValue(int value); - // [102] if greater than 0, send a pulse of the given duration in ms and then restore the output back to the original value (default: 0) - void setPulseWidth(int value); // [103] define which value to set to the output when set to on (default: HIGH) void setOnValue(int value); // [104] when legacy mode is enabled expect a REQ message to trigger, otherwise the default SET (default: false) @@ -521,10 +517,22 @@ Each sensor class can expose additional methods. void setSafeguard(int value); // [106] if true the input value becomes a duration in minutes after which the output will be automatically turned off (default: false) void setInputIsElapsed(bool value); + // [107] optionally wait for the given number of milliseconds after changing the status (default: 0) + void setWaitAfterSet(int value); // manually switch the output to the provided value - void set(int value); + void setStatus(int value); // get the current state - int getState(); + int getStatus(); +~~~ + +* SensorLatchingRelay (in addition to those available for SensorDigitalOutput / SensorRelay) +~~~c + // [201] set the duration of the pulse to send in ms to activate the relay (default: 50) + void setPulseWidth(int value); + // [202] set the pin which turns the relay off (default: the pin provided while registering the sensor) + void setPinOff(int value); + // [203] set the pin which turns the relay on (default: the pin provided while registering the sensor + 1) + void setPinOn(int value); ~~~ * SensorSwitch / SensorDoor / SensorMotion @@ -749,7 +757,6 @@ Register a latching relay connecting to pin 6 (set) and pin 7 (unset): ~~~c nodeManager.registerSensor(SENSOR_LATCHING_RELAY,6); - nodeManager.registerSensor(SENSOR_LATCHING_RELAY,7); ~~~ ## Example Sketches @@ -920,7 +927,7 @@ void receiveTime(unsigned long ts) { * Boiler Sensor -The following sketch controls a latching relay connected to a boiler. A latching relay (requiring only a pulse to switch) has been chosen to minimize the power consumption required by a traditional relay to stay on. This relay has normally two pins, one for closing and the other for opening the controlled circuit, connected to pin 6 and 7 of the arduino board. This is why we have to register two sensors against NodeManager so to control the two funtions indipendently. Since using a SENSOR_LATCHING_RELAY type of sensor, NodeManager will take care of just sending out a single pulse only when a REQ command of type V_STATUS is sent to one or the other child id. +The following sketch controls a latching relay connected to a boiler. A latching relay (requiring only a pulse to switch) has been chosen to minimize the power consumption required by a traditional relay to stay on. This relay has normally two pins, one for closing and the other for opening the controlled circuit, connected to pin 6 and 7 of the arduino board. Since using a SENSOR_LATCHING_RELAY type of sensor, NodeManager will automatically consider the provided pin as the ON pin and the one just after as the OFF pin and will take care of just sending out a single pulse only when a SET command of type V_STATUS is sent to the child id. The appropriate pin will be then used. In this example, the board also runs at 1Mhz so it can go down to 1.8V: by setting setBatteryMin() and setBatteryMax(), the battery percentage will be calculated and reported (by default, automatically every 10 sleeping cycles) based on these custom boundaries. @@ -966,7 +973,6 @@ void before() { nodeManager.setBatteryMax(3.2); nodeManager.setSleep(SLEEP,5,MINUTES); nodeManager.registerSensor(SENSOR_LATCHING_RELAY,6); - nodeManager.registerSensor(SENSOR_LATCHING_RELAY,7); /* * Register above your sensors From 1087ba0c58e5b81c24c0c3b51e356fa049f234be Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer Date: Sun, 2 Jul 2017 17:23:55 +0200 Subject: [PATCH 15/33] Added support for the MH-Z19 CO2 sensor (#161) --- NodeManager.cpp | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ NodeManager.h | 35 ++++++++++++++++ README.md | 11 ++++- config.h | 2 + 4 files changed, 154 insertions(+), 1 deletion(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 2f5f832f..7336b570 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -2332,6 +2332,108 @@ int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) { } #endif + + +/* + SensorMHZ19 +*/ +#if MODULE_MHZ19 == 1 +// contructor +SensorMHZ19::SensorMHZ19(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { + // set presentation, type and value type + setPresentation(S_AIR_QUALITY); + setType(V_LEVEL); + setRxTx(pin, pin+1); +} + +void SensorMHZ19::setRxTx(int rxpin, int txpin) { + _rx_pin = rxpin; + _tx_pin = txpin; +} + + +// what to do during before +void SensorMHZ19::onBefore() { +} + +// what to do during setup +void SensorMHZ19::onSetup() { + _ser = new SoftwareSerial(_rx_pin, _tx_pin); + _ser->begin(9600); + delay(2000); + while (_ser->read()!=-1) {}; // clear CO2 buffer. +} + +// what to do during loop +void SensorMHZ19::onLoop() { + // Read the ppm value + int co2ppm = readCO2(); // This is there the function gets called that talks to the Co2 sensor. + #if DEBUG == 1 + Serial.print(F("CO2 I=")); + Serial.print(_child_id); + Serial.print(F(" ppm=")); + Serial.println(co2ppm); + #endif + // store the value + _value_int = co2ppm; +} + +// Read out the CO2 data +int SensorMHZ19::readCO2() { + while (_ser->read() != -1) {}; //clear serial buffer + + unsigned char response[9]; // for answer + byte cmd[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; + + // Command to ask for data. + _ser->write(cmd, 9); //request PPM CO2 + + // Then for 1 second listen for 9 bytes of data. + _ser->readBytes(response, 9); + + #if DEBUG == 1 + for (int i=0; i<9; i++) { + Serial.print(response[i], HEX); + Serial.print(F("-")); + } + Serial.println(F("END")); + #endif + + if (response[0] != 0xFF) { + Serial.println(F("Wrong starting byte from co2 sensor! (should be FF)")); + return -1; + } + + if (response[1] != 0x86) { + Serial.println(F("Wrong command from co2 sensor! (should be 86)")); + return -1; + } + + int responseHigh = (int) response[2]; + int responseLow = (int) response[3]; + int ppm = (256 * responseHigh) + responseLow; + + return ppm; +} + + +// what to do as the main task when receiving a message +void SensorMHZ19::onReceive(const MyMessage & message) { + if (message.getCommand() == C_REQ) onLoop(); +} + +// what to do when receiving a remote message +void SensorMHZ19::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + default: return; + } + _send(_msg_service.set(function)); +} + +#endif + + /******************************************* NodeManager */ @@ -2624,6 +2726,11 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { 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)); + } + #endif else { #if DEBUG == 1 Serial.print(F("INVALID ")); diff --git a/NodeManager.h b/NodeManager.h index 46474ba1..5123a117 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -171,6 +171,10 @@ #ifndef MODULE_MQ #define MODULE_MQ 0 #endif +// Enable this module to use one of the following sensors: SENSOR_MHZ19 +#ifndef MODULE_MHZ19 + #define MODULE_MHZ19 0 +#endif /*********************************** Supported Sensors @@ -260,6 +264,10 @@ enum supported_sensors { // MQ2 air quality sensor SENSOR_MQ, #endif + #if MODULE_MHZ19 == 1 + // MH-Z19 CO2 sensor + SENSOR_MHZ19, + #endif }; /*********************************** Libraries @@ -317,6 +325,9 @@ enum supported_sensors { #include #include "Adafruit_MCP9808.h" #endif +#if MODULE_MHZ19 == 1 + #include +#endif /******************************************************************* Classes @@ -1120,6 +1131,30 @@ class SensorMQ: public Sensor { }; #endif +/* + SensorMHZ19 +*/ +#if MODULE_MHZ19 == 1 +class SensorMHZ19: public Sensor { + public: + SensorMHZ19(NodeManager* node_manager, int child_id, int pin); + // set the pins for RX and TX of the SoftwareSerial (default: Rx=6, Tx=7) + void setRxTx(int rxpin, int txpin); + // 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); + int readCO2(); + protected: + SoftwareSerial* _ser; + int _tx_pin = 6; + int _rx_pin = 7; +}; +#endif + + /*************************************** NodeManager: manages all the aspects of the node */ diff --git a/README.md b/README.md index cca7ebe0..241491cb 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,8 @@ Those NodeManager's directives in the `config.h` file control which module/libra #define MODULE_MCP9808 0 // Enable this module to use one of the following sensors: SENSOR_MQ #define MODULE_MQ 0 +// Enable this module to use one of the following sensors: SENSOR_MHZ19 +#define MODULE_MHZ19 0 ~~~ ### Installing the dependencies @@ -333,6 +335,7 @@ SENSOR_MCP9808 | MCP9808 sensor, measure the temperature through the attached mo SENSOR_RAIN_GAUGE | Rain gauge sensor SENSOR_RAIN | Rain sensor, return the percentage of rain from an attached analog sensor SENSOR_SOIL_MOISTURE | Soil moisture sensor, return the percentage of moisture from an attached analog sensor +SENSOR_MHZ19 | MH-Z19 CO2 sensor via UART (SoftwareSerial, default on pins 6(Rx) and 7(Tx) To register a sensor simply call the NodeManager instance with the sensory type and the pin the sensor is conncted to. For example: ~~~c @@ -585,6 +588,12 @@ Each sensor class can expose additional methods. void setLedPin(int value); ~~~ +* SensorMHZ19 +~~~c + // set the RX and TX pins for the software serial port to talk to the sensor + void setRxTx(int rxpin, int txpin); +~~~ + ### Upload your sketch Upload your sketch to your arduino board as you are used to. @@ -1229,4 +1238,4 @@ v1.5: * ESP8266WiFi.h has to be included in the main sketch if MY_GATEWAY_ESP8266 is defined * Added receiveTime() wrapper in the main sketch * Fixed the logic for output sensors -* Added common gateway settings in config.h \ No newline at end of file +* Added common gateway settings in config.h diff --git a/config.h b/config.h index 5fe1facf..0e8878eb 100644 --- a/config.h +++ b/config.h @@ -132,5 +132,7 @@ #define MODULE_MCP9808 0 // Enable this module to use one of the following sensors: SENSOR_MQ #define MODULE_MQ 0 +// Enable this module to use one of the following sensors: SENSOR_MHZ19 +#define MODULE_MHZ19 0 #endif From ea9596ecaf1a6e467b9f7a4b7c9bd1ac979bf423 Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer Date: Sun, 2 Jul 2017 17:32:41 +0200 Subject: [PATCH 16/33] Optimize MQ sensors (share default curve coefficients) (#162) * Fix compilation and other issues -) Enums (in particular the sensor types) start with 0, so we have to allow a sensor type of 0 -) Add missing SensorMQ::setTargetGas method -) Make gas type identifiers for MQ sensors const static to preserve 6 bytes of memory per MQ instance * Fix wrong array index (typo) * Optimize the MQ sensors by using one static array for the default curve coefficients. This adds three extra pointers, but saves 48 bytes for each additional MQ sensor after the first --- NodeManager.cpp | 25 +++++++++++++++---------- NodeManager.h | 15 +++++++++------ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 7336b570..06ead41a 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -2174,12 +2174,23 @@ void SensorMCP9808::onProcess(Request & request) { * SensorMQ */ #if MODULE_MQ == 1 + +static float SensorMQ::_default_LPGCurve[3] = {2.3,0.21,-0.47}; +static float SensorMQ::_default_COCurve[3] = {2.3,0.72,-0.34}; +static float SensorMQ::_default_SmokeCurve[3] = {2.3,0.53,-0.44}; + SensorMQ::SensorMQ(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager,child_id,pin) { setPresentation(S_AIR_QUALITY); setType(V_LEVEL); + _LPGCurve = SensorMQ::_default_LPGCurve; + _COCurve = SensorMQ::_default_COCurve; + _SmokeCurve = SensorMQ::_default_SmokeCurve; } //setter/getter +void SensorMQ::setTargetGas(int value) { + _target_gas = value; +} void SensorMQ::setRlValue(float value) { _rl_value = value; } @@ -2202,19 +2213,13 @@ void SensorMQ::setReadSampleInterval(int value) { _read_sample_interval = value; } void SensorMQ::setLPGCurve(float *value) { - _LPGCurve[0] = value[0]; - _LPGCurve[2] = value[1]; - _LPGCurve[2] = value[2]; + _LPGCurve = value; } void SensorMQ::setCOCurve(float *value) { - _COCurve[0] = value[0]; - _COCurve[2] = value[1]; - _COCurve[2] = value[2]; + _COCurve = value; } void SensorMQ::setSmokeCurve(float *value) { - _SmokeCurve[0] = value[0]; - _SmokeCurve[2] = value[1]; - _SmokeCurve[2] = value[2]; + _SmokeCurve = value; } // what to do during before @@ -2570,7 +2575,7 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { // get a child_id if not provided by the user if (child_id < 0) child_id = _getAvailableChildId(); // based on the given sensor type instantiate the appropriate class - if (sensor_type == 0) return -1; + if (sensor_type < 0) return -1; #if MODULE_ANALOG_INPUT == 1 else if (sensor_type == SENSOR_ANALOG_INPUT) return registerSensor(new SensorAnalogInput(this,child_id, pin)); else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(this,child_id, pin)); diff --git a/NodeManager.h b/NodeManager.h index 5123a117..7e752f69 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -1116,17 +1116,20 @@ class SensorMQ: public Sensor { int _read_sample_interval = 50; int _read_sample_times = 5; float _ro = 10000.0; - float _LPGCurve[3] = {2.3,0.21,-0.47}; - float _COCurve[3] = {2.3,0.72,-0.34}; - float _SmokeCurve[3] = {2.3,0.53,-0.44}; + static float _default_LPGCurve[3]; + static float _default_COCurve[3]; + static float _default_SmokeCurve[3]; + float *_LPGCurve; + float *_COCurve; + float *_SmokeCurve; float _MQResistanceCalculation(int raw_adc); float _MQCalibration(); float _MQRead(); int _MQGetGasPercentage(float rs_ro_ratio, int gas_id); int _MQGetPercentage(float rs_ro_ratio, float *pcurve); - int _gas_lpg = 0; - int _gas_co = 1; - int _gas_smoke = 2; + const static int _gas_lpg = 0; + const static int _gas_co = 1; + const static int _gas_smoke = 2; int _target_gas = _gas_co; }; #endif From b0312ff36f9e475eb84b8855fe1bfec058c1916e Mon Sep 17 00:00:00 2001 From: user2684 Date: Sun, 9 Jul 2017 11:53:35 +0200 Subject: [PATCH 17/33] Backport of 1.5.1 hotfix for maximum allocated sensors --- NodeManager.cpp | 17 +++++++++++------ NodeManager.h | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 06ead41a..1e32a478 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -2747,6 +2747,7 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { // attach a built-in or custom sensor to this manager int NodeManager::registerSensor(Sensor* sensor) { + if (sensor->getChildId() > MAX_SENSORS) return; #if DEBUG == 1 Serial.print(F("REG I=")); Serial.print(sensor->getChildId()); @@ -2769,12 +2770,14 @@ int NodeManager::registerSensor(Sensor* sensor) { // un-register a sensor to this manager void NodeManager::unRegisterSensor(int sensor_index) { + if (sensor_index > MAX_SENSORS) return; // unlink the pointer to this sensor _sensors[sensor_index] == 0; } // return a sensor given its index Sensor* NodeManager::get(int child_id) { + if (child_id > MAX_SENSORS) return 0; // return a pointer to the sensor from the given child_id return _sensors[child_id]; } @@ -2784,6 +2787,7 @@ Sensor* NodeManager::getSensor(int child_id) { // assign a different child id to a sensor' bool NodeManager::renameSensor(int old_child_id, int new_child_id) { + if (old_child_id > MAX_SENSORS || new_child_id > MAX_SENSORS) return; // ensure the old id exists and the new is available if (_sensors[old_child_id] == 0 || _sensors[new_child_id] != 0) return false; // assign the sensor to new id @@ -2851,7 +2855,7 @@ void NodeManager::before() { _battery_report_timer.start(); #endif // setup individual sensors - for (int i = 0; i < MAX_SENSORS; i++) { + for (int i = 1; i <= MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; // call each sensor's setup() _sensors[i]->before(); @@ -2875,7 +2879,7 @@ void NodeManager::presentation() { batteryReport(); #endif // present each sensor - for (int i = 0; i < MAX_SENSORS; i++) { + for (int i = 1; i <= MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; // call each sensor's presentation() if (_sleep_between_send > 0) sleep(_sleep_between_send); @@ -2902,7 +2906,7 @@ void NodeManager::setup() { _send(_msg.set("STARTED")); #endif // run setup for all the registered sensors - for (int i = 0; i < MAX_SENSORS; i++) { + for (int i = 1; i <= MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; // call each sensor's setup() _sensors[i]->setup(); @@ -2935,7 +2939,7 @@ void NodeManager::loop() { if (_auto_power_pins) powerOn(); #endif // run loop for all the registered sensors - for (int i = 0; i < MAX_SENSORS; i++) { + 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 @@ -2975,7 +2979,7 @@ void NodeManager::receive(const MyMessage &message) { #endif } // dispatch the message to the registered sensor - else if (_sensors[message.sensor] != 0) { + else if (message.sensor <= MAX_SENSORS && _sensors[message.sensor] != 0) { #if POWER_MANAGER == 1 // turn on the pin powering all the sensors if (_auto_power_pins) powerOn(); @@ -3285,12 +3289,13 @@ void NodeManager::_present(int child_id, int type) { // return the next available child_id int NodeManager::_getAvailableChildId() { - for (int i = 1; i < MAX_SENSORS; i++) { + for (int i = 1; i <= MAX_SENSORS; i++) { if (i == CONFIGURATION_CHILD_ID) continue; if (i == BATTERY_CHILD_ID) continue; // empty place, return it if (_sensors[i] == 0) return i; } + return MAX_SENSORS; } // guess the initial value of a digital output based on the configured interrupt mode diff --git a/NodeManager.h b/NodeManager.h index 7e752f69..ab3f8416 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -1295,7 +1295,7 @@ class NodeManager { int _interrupt_2_pull = -1; int _last_interrupt_pin = -1; long _timestamp = -1; - Sensor* _sensors[MAX_SENSORS] = {0}; + Sensor* _sensors[MAX_SENSORS+1] = {0}; bool _ack = false; void _sleep(); void _present(int child_id, int type); From 01bc9d8a1fc890f27483f6b9ad425321318c6cd9 Mon Sep 17 00:00:00 2001 From: user2684 Date: Sun, 9 Jul 2017 19:26:39 +0200 Subject: [PATCH 18/33] 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 1e32a478..21d64518 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 ab3f8416..cc96a569 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 c8647929..32e2fa71 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 241491cb..fe8c0eba 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. From 548685492c37b54e5765868316e787d8af056544 Mon Sep 17 00:00:00 2001 From: getlarge Date: Sat, 15 Jul 2017 15:58:59 +0200 Subject: [PATCH 19/33] Added support for AM2320, DHT21, PT100 and TSL2561 sensors (#171 #109 #110 #111) --- .gitignore | 0 NodeManager.cpp | 306 ++++++++++++++++++++++++++++++++++++++++++++++-- NodeManager.h | 121 ++++++++++++++++++- NodeManager.ino | 12 +- README.md | 22 +++- config.h | 12 +- 6 files changed, 455 insertions(+), 18 deletions(-) mode change 100644 => 100755 .gitignore mode change 100644 => 100755 NodeManager.cpp mode change 100644 => 100755 NodeManager.h mode change 100644 => 100755 NodeManager.ino mode change 100644 => 100755 README.md mode change 100644 => 100755 config.h diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/NodeManager.cpp b/NodeManager.cpp old mode 100644 new mode 100755 index 21d64518..03a891a9 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -10,21 +10,21 @@ // set the vcc and ground pin the sensor is connected to void PowerManager::setPowerPins(int ground_pin, int vcc_pin, int wait_time) { + _ground_pin = ground_pin; + _vcc_pin = vcc_pin; #if DEBUG == 1 Serial.print(F("PWR G=")); - Serial.print(ground_pin); + Serial.print(_ground_pin); Serial.print(F(" V=")); - Serial.println(vcc_pin); + Serial.println(_vcc_pin); #endif if (_ground_pin > 0) { // configure the ground pin as output and initialize to low - _ground_pin = ground_pin; pinMode(_ground_pin, OUTPUT); digitalWrite(_ground_pin, LOW); } if (_vcc_pin > 0) { // configure the vcc pin as output and initialize to high (power on) - _vcc_pin = vcc_pin; pinMode(_vcc_pin, OUTPUT); digitalWrite(_vcc_pin, HIGH); } @@ -1027,6 +1027,7 @@ SensorDigitalOutput::SensorDigitalOutput(NodeManager* node_manager, int child_id // what to do during before void SensorDigitalOutput::onBefore() { _setupPin(_pin); + } // what to do during setup @@ -2501,6 +2502,272 @@ void SensorMHZ19::onProcess(Request & request) { #endif +/* + SensorAM2320 +*/ +#if MODULE_AM2320 == 1 +// constructor +SensorAM2320::SensorAM2320(NodeManager* node_manager, int child_id, AM2320* th, int sensor_type): Sensor(node_manager, child_id,A2) { + _th = th; + _sensor_type = sensor_type; + if (_sensor_type == SensorAM2320::TEMPERATURE) { + // temperature sensor + setPresentation(S_TEMP); + setType(V_TEMP); + setValueType(TYPE_FLOAT); + } + else if (_sensor_type == SensorAM2320::HUMIDITY) { + // humidity sensor + setPresentation(S_HUM); + setType(V_HUM); + setValueType(TYPE_FLOAT); + } +} + +// what do to during before +void SensorAM2320::onBefore() { + +} + +// what do to during setup +void SensorAM2320::onSetup() { +} + +// what do to during loop +void SensorAM2320::onLoop() { + switch(_th->Read()) { + case 0: + // temperature sensor + if (_sensor_type == SensorAM2320::TEMPERATURE) { + // read the temperature + float temperature = _th->t; + #if DEBUG == 1 + Serial.print(F("AM2320 I=")); + Serial.print(_child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + _value_float = temperature; + } + // humidity sensor + else if (_sensor_type == SensorAM2320::HUMIDITY) { + // read humidity + float humidity = _th->h; + if (isnan(humidity)) return; + #if DEBUG == 1 + Serial.print(F("AM2320 I=")); + Serial.print(_child_id); + Serial.print(F(" H=")); + Serial.println(humidity); + #endif + // store the value + _value_float = humidity; + } + break; + case 1: Serial.println(F("AM2320 offline")); break; + case 2: Serial.println(F("AM2320 CRC failed")); break; + } +} + +// what do to as the main task when receiving a message +void SensorAM2320::onReceive(const MyMessage & message) { + onLoop(); +} + +// what to do when receiving a remote message +void SensorAM2320::onProcess(Request & request) { +} +#endif + +/* + SensorTSL2561 +*/ +#if MODULE_TSL2561 == 1 +// contructor +SensorTSL2561::SensorTSL2561(NodeManager* node_manager, int child_id): Sensor(node_manager, child_id,A2) { + setPresentation(S_LIGHT_LEVEL); + setType(V_LEVEL); +} + +// setter/getter +void SensorTSL2561::setGain(int value) { + _tsl_gain = value; +} +void SensorTSL2561::setTiming(int value) { + _tsl_timing = value; +} +void SensorTSL2561::setSpectrum(int value) { + _tsl_spectrum = value; +} +void SensorTSL2561::setAddress(int value) { + _tsl_address = value; +} + +// what do to during before +void SensorTSL2561::onBefore() { + switch (_tsl_address) { + case SensorTSL2561::ADDR_FLOAT: + _tsl = new TSL2561(TSL2561_ADDR_FLOAT); + break; + case SensorTSL2561::ADDR_LOW: + _tsl = new TSL2561(TSL2561_ADDR_LOW); + break; + case SensorTSL2561::ADDR_HIGH: + _tsl = new TSL2561(TSL2561_ADDR_HIGH); + break; + } +} + +// what do to during setup +void SensorTSL2561::onSetup() { + if (_tsl->begin()) { + switch (_tsl_gain) { + case SensorTSL2561::GAIN_0X: + _tsl->setGain(TSL2561_GAIN_0X); + break; + case SensorTSL2561::GAIN_16X: + _tsl->setGain(TSL2561_GAIN_16X); + break; + } + switch (_tsl_timing) { + case SensorTSL2561::INTEGRATIONTIME_13MS: + _tsl->setTiming(TSL2561_INTEGRATIONTIME_13MS); + break; + case SensorTSL2561::INTEGRATIONTIME_101MS: + _tsl->setTiming(TSL2561_INTEGRATIONTIME_101MS); + break; + case SensorTSL2561::INTEGRATIONTIME_402MS: + _tsl->setTiming(TSL2561_INTEGRATIONTIME_402MS); + break; + } + } + else { + Serial.println(F("TSL2561 offline")); + } +} + +// what do to during loop +void SensorTSL2561::onLoop() { + // request the light level + switch (_tsl_spectrum) { + case SensorTSL2561::VISIBLE: + _value_int = _tsl->getLuminosity(TSL2561_VISIBLE); + break; + case SensorTSL2561::FULLSPECTRUM: + _value_int = _tsl->getLuminosity(TSL2561_FULLSPECTRUM); + break; + case SensorTSL2561::INFRARED: + _value_int = _tsl->getLuminosity(TSL2561_INFRARED); + break; + case SensorTSL2561::FULL: + // request the full light level + uint32_t lum = _tsl->getFullLuminosity(); + uint16_t ir, full; + ir = lum >> 16; + full = lum & 0xFFFF; + _value_int = _tsl->calculateLux(full, ir); + #if DEBUG == 1 + Serial.print(F("TSL I=")); + Serial.print(_child_id); + Serial.print(F(" LUX=")); + Serial.print(_value_int); + Serial.print(F(" IR=")); + Serial.print(ir); + Serial.print(F(" FULL=")); + Serial.print(full); + Serial.print(F(" VIS=")); + Serial.println(full-ir); + #endif + break; + } + #if DEBUG == 1 + if (_tsl_spectrum < 3) { + Serial.print(F("TSL I=")); + Serial.print(_child_id); + Serial.print(F(" L=")); + Serial.println(_value_int); + } + #endif +} + +// what do to as the main task when receiving a message +void SensorTSL2561::onReceive(const MyMessage & message) { + onLoop(); +} + +// what to do when receiving a remote message +void SensorTSL2561::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setGain(request.getValueInt()); break; + case 102: setTiming(request.getValueInt()); break; + case 103: setSpectrum(request.getValueInt()); break; + case 104: setAddress(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} +#endif + +/* + SensorPT100 +*/ + +// contructor +SensorPT100::SensorPT100(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { + // set presentation, type and value type + setPresentation(S_TEMP); + setType(V_TEMP); + setValueType(TYPE_FLOAT); +} + +//// setter/getter +void SensorPT100::setVoltageRef(float value) { + _voltageRef = value; +} + +// what to do during before +void SensorPT100::onBefore() { + _PT100 = new DFRobotHighTemperature(_voltageRef); + // set the pin as input + pinMode(_pin, INPUT); +} + +// what to do during setup +void SensorPT100::onSetup() { +} + +// what to do during loop +void SensorPT100::onLoop() { + // read the PT100 sensor + int temperature = _PT100->readTemperature(_pin); + + #if DEBUG == 1 + Serial.print(F("PT100 I=")); + Serial.print(_child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + // store the value + _value_float = temperature; +} + +// what to do as the main task when receiving a message +void SensorPT100::onReceive(const MyMessage & message) { + if (message.getCommand() == C_REQ) onLoop(); +} + +// what to do when receiving a remote message +void SensorPT100::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setVoltageRef(request.getValueFloat()); break; + default: return; + } + _send(_msg_service.set(function)); +} + /******************************************* NodeManager @@ -2597,7 +2864,7 @@ void NodeManager::setInterruptMinDelta(long value) { _powerManager.setPowerPins(ground_pin, vcc_pin, wait_time); } void NodeManager::setAutoPowerPins(bool value) { - _auto_power_pins = value; + _auto_power_pins = value; } void NodeManager::powerOn() { _powerManager.powerOn(); @@ -2666,8 +2933,11 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { else if (sensor_type == SENSOR_LATCHING_RELAY) return registerSensor(new SensorLatchingRelay(this,child_id, pin)); #endif #if MODULE_DHT == 1 - else if (sensor_type == SENSOR_DHT11 || sensor_type == SENSOR_DHT22) { - int dht_type = sensor_type == SENSOR_DHT11 ? DHT11 : DHT22; + else if (sensor_type == SENSOR_DHT11 || sensor_type == SENSOR_DHT22 || sensor_type == SENSOR_DHT21) { + int dht_type; + if (sensor_type == SENSOR_DHT11) dht_type = DHT11; + else if (sensor_type == SENSOR_DHT21) dht_type = DHT21; + else if (sensor_type == SENSOR_DHT22) dht_type = DHT22; DHT* dht = new DHT(pin,dht_type); // register temperature sensor registerSensor(new SensorDHT(this,child_id,pin,dht,SensorDHT::TEMPERATURE,dht_type)); @@ -2807,6 +3077,28 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { return registerSensor(new SensorMHZ19(this, child_id, pin)); } #endif + #if MODULE_AM2320 == 1 + else if (sensor_type == SENSOR_AM2320) { + AM2320* th = new AM2320(); + // register temperature sensor + registerSensor(new SensorAM2320(this,child_id,th,SensorAM2320::TEMPERATURE)); + // register humidity sensor + child_id = _getAvailableChildId(); + return registerSensor(new SensorAM2320(this,child_id,th,SensorAM2320::HUMIDITY)); + } + #endif + #if MODULE_TSL2561 == 1 + else if (sensor_type == SENSOR_TSL2561) { + // register light sensor + 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)); + } + #endif else { #if DEBUG == 1 Serial.print(F("INVALID ")); diff --git a/NodeManager.h b/NodeManager.h old mode 100644 new mode 100755 index cc96a569..58c4c445 --- a/NodeManager.h +++ b/NodeManager.h @@ -111,7 +111,7 @@ Default module settings */ -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ACS712 +// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ACS712, SENSOR_PT100 #ifndef MODULE_ANALOG_INPUT #define MODULE_ANALOG_INPUT 0 #endif @@ -127,7 +127,11 @@ #ifndef MODULE_SHT21 #define MODULE_SHT21 0 #endif -// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 +// Enable this module to use one of the following sensors: SENSOR_AM2320 +#ifndef MODULE_AM2320 + #define MODULE_AM2320 0 +#endif +// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22, SENSOR_DHT21 #ifndef MODULE_DHT #define MODULE_DHT 0 #endif @@ -143,6 +147,10 @@ #ifndef MODULE_BH1750 #define MODULE_BH1750 0 #endif +// Enable this module to use one of the following sensors: SENSOR_TSL2561 +#ifndef MODULE_TSL2561 + #define MODULE_TSL2561 0 +#endif // Enable this module to use one of the following sensors: SENSOR_MLX90614 #ifndef MODULE_MLX90614 #define MODULE_MLX90614 0 @@ -214,6 +222,7 @@ enum supported_sensors { // DHT11/DHT22 sensors, return temperature/humidity based on the attached DHT sensor SENSOR_DHT11, SENSOR_DHT22, + SENSOR_DHT21, #endif #if MODULE_SHT21 == 1 // SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor @@ -268,7 +277,20 @@ enum supported_sensors { // MH-Z19 CO2 sensor SENSOR_MHZ19, #endif + #if MODULE_TSL2561 == 1 + // TSL2561 sensor, return light in lux + SENSOR_TSL2561, + #endif + #if MODULE_AM2320 == 1 + // AM2320 sensors, return temperature/humidity based on the attached AM2320 sensor + SENSOR_AM2320, + #endif + #if MODULE_PT100 == 1 + // High temperature sensor associated with DFRobot Driver, return the temperature in C° from the attached PT100 sensor + SENSOR_PT100, + #endif }; + /*********************************** Libraries */ @@ -328,6 +350,17 @@ enum supported_sensors { #if MODULE_MHZ19 == 1 #include #endif +#if MODULE_AM2320 == 1 + #include + #include +#endif +#if MODULE_TSL2561 == 1 + #include + #include +#endif +#if MODULE_PT100 == 1 + #include +#endif /******************************************************************* Classes @@ -1175,6 +1208,90 @@ class SensorMHZ19: public Sensor { }; #endif +/* + SensorAM2320 +*/ +#if MODULE_AM2320 == 1 +class SensorAM2320: public Sensor { + public: + SensorAM2320(NodeManager* node_manager, int child_id, AM2320* th, int sensor_type); + // 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); + // constants + const static int TEMPERATURE = 0; + const static int HUMIDITY = 1; + protected: + AM2320* _th; + int _sensor_type = 0; +}; +#endif + +/* + SensorTSL2561 +*/ +#if MODULE_TSL2561 == 1 +class SensorTSL2561: public Sensor { + public: + SensorTSL2561(NodeManager* node_manager, int child_id); + // [101] set the gain, possible values are SensorTSL2561::GAIN_0X (0), SensorTSL2561::GAIN_16X (1) (default 16x) + void setGain(int value); + // [102] set the timing, possible values are SensorTSL2561::INTEGRATIONTIME_13MS (0), SensorTSL2561::INTEGRATIONTIME_101MS (1), SensorTSL2561::INTEGRATIONTIME_402MS (2) (default: 13ms) + void setTiming(int value); + // [103] set the spectrum, possible values are SensorTSL2561::VISIBLE (0), SensorTSL2561::FULLSPECTRUM (1), SensorTSL2561::INFRARED (2), SensorTSL2561::FULL (3) (default: visible) + void setSpectrum(int value); + // [104] set the i2c address values are SensorTSL2561::ADDR_FLOAT, SensorTSL2561::ADDR_LOW, SensorTSL2561::ADDR_HIGH + void setAddress(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); + // constants + const static int ADDR_FLOAT = 0; + const static int ADDR_LOW = 1; + const static int ADDR_HIGH = 2; + const static int GAIN_0X = 0; + const static int GAIN_16X = 1; + const static int INTEGRATIONTIME_13MS = 0; + const static int INTEGRATIONTIME_101MS = 1; + const static int INTEGRATIONTIME_402MS = 2; + const static int VISIBLE = 0; + const static int FULLSPECTRUM = 1; + const static int INFRARED = 2; + const static int FULL = 3; + protected: + TSL2561* _tsl; + int _tsl_address = 0; + int _tsl_gain = 1; + int _tsl_timing = 0; + int _tsl_spectrum = 0; +}; +#endif + +/* + SensorPT100 +*/ + +class SensorPT100: public Sensor { + public: + SensorPT100(NodeManager* node_manager, int child_id, int pin); + // [101] set the voltageRef used to compare with analog measures + void setVoltageRef(float 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); + protected: + DFRobotHighTemperature* _PT100; + float _voltageRef = 3.3; +}; /*************************************** NodeManager: manages all the aspects of the node diff --git a/NodeManager.ino b/NodeManager.ino old mode 100644 new mode 100755 index 32e2fa71..f2296b5f --- a/NodeManager.ino +++ b/NodeManager.ino @@ -1,13 +1,11 @@ /* NodeManager is intended to take care on your behalf of all those common tasks a MySensors node has to accomplish, speeding up the development cycle of your projects. - NodeManager includes the following main components: - Sleep manager: allows managing automatically the complexity behind battery-powered sensors spending most of their time sleeping - Power manager: allows powering on your sensors only while the node is awake - Battery manager: provides common functionalities to read and report the battery level - Remote configuration: allows configuring remotely the node without the need to have physical access to it - Built-in personalities: for the most common sensors, provide embedded code so to allow their configuration with a single line - Documentation available on: https://github.com/mysensors/NodeManager */ @@ -30,11 +28,15 @@ NodeManager nodeManager; void before() { // setup the serial port baud rate Serial.begin(MY_BAUD_RATE); + /* - * Register below your sensors + * Register below your device specific config */ - + + /* + * Register below your sensors + */ /* @@ -72,5 +74,3 @@ void receiveTime(unsigned long ts) { // call NodeManager receiveTime routine nodeManager.receiveTime(ts); } - - diff --git a/README.md b/README.md old mode 100644 new mode 100755 index fe8c0eba..3c851316 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ Those NodeManager's directives in the `config.h` file control which module/libra #define MODULE_DIGITAL_OUTPUT 1 // Enable this module to use one of the following sensors: SENSOR_SHT21 #define MODULE_SHT21 0 -// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 +// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22, SENSOR_DHT21 #define MODULE_DHT 0 // Enable this module to use one of the following sensors: SENSOR_SWITCH, SENSOR_DOOR, SENSOR_MOTION #define MODULE_SWITCH 0 @@ -175,6 +175,10 @@ Those NodeManager's directives in the `config.h` file control which module/libra #define MODULE_MQ 0 // Enable this module to use one of the following sensors: SENSOR_MHZ19 #define MODULE_MHZ19 0 +// Enable this module to use one of the following sensors: SENSOR_AM2320 +#define MODULE_AM2320 0 +// Enable this module to use one of the following sensors: SENSOR_TSL2561 +#define MODULE_TSL2561 0 ~~~ ### Installing the dependencies @@ -193,6 +197,8 @@ MODULE_SONOFF | https://github.com/thomasfredericks/Bounce2 MODULE_BMP085 | https://github.com/adafruit/Adafruit-BMP085-Library MODULE_HCSR04 | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/NewPing MODULE_MCP9808 | https://github.com/adafruit/Adafruit_MCP9808_Library +MODULE_AM2320 | https://github.com/thakshak/AM2320 +MODULE_TSL2561 | https://github.com/adafruit/TSL2561-Arduino-Library ### Configure NodeManager @@ -340,6 +346,8 @@ SENSOR_RAIN_GAUGE | Rain gauge sensor SENSOR_RAIN | Rain sensor, return the percentage of rain from an attached analog sensor SENSOR_SOIL_MOISTURE | Soil moisture sensor, return the percentage of moisture from an attached analog sensor SENSOR_MHZ19 | MH-Z19 CO2 sensor via UART (SoftwareSerial, default on pins 6(Rx) and 7(Tx) +SENSOR_TSL2561 | TSL2561 sensor, return light in lux +SENSOR_AM2320 | AM2320 sensors, return temperature/humidity based on the attached AM2320 sensor To register a sensor simply call the NodeManager instance with the sensory type and the pin the sensor is conncted to. For example: ~~~c @@ -602,6 +610,18 @@ Each sensor class can expose additional methods. void setRxTx(int rxpin, int txpin); ~~~ +* SensorTSL2561 +~~~c + // [101] set the gain, possible values are SensorTSL2561::GAIN_0X (0), SensorTSL2561::GAIN_16X (1) (default 16x) + void setGain(int value); + // [102] set the timing, possible values are SensorTSL2561::INTEGRATIONTIME_13MS (0), SensorTSL2561::INTEGRATIONTIME_101MS (1), SensorTSL2561::INTEGRATIONTIME_402MS (2) (default: 13ms) + void setTiming(int value); + // [103] set the spectrum, possible values are SensorTSL2561::VISIBLE (0), SensorTSL2561::FULLSPECTRUM (1), SensorTSL2561::INFRARED (2), SensorTSL2561::FULL (3) (default: visible) + void setSpectrum(int value); + // [104] set the i2c address values are SensorTSL2561::ADDR_FLOAT, SensorTSL2561::ADDR_LOW, SensorTSL2561::ADDR_HIGH + void setAddress(int value); +~~~ + ### Upload your sketch Upload your sketch to your arduino board as you are used to. diff --git a/config.h b/config.h old mode 100644 new mode 100755 index 0e8878eb..f3136dd1 --- a/config.h +++ b/config.h @@ -18,11 +18,13 @@ //#define MY_NODE_ID 100 // NRF24 radio settings -#define MY_RADIO_NRF24 +//#define MY_RADIO_NRF24 //#define MY_RF24_ENABLE_ENCRYPTION + //#define MY_RF24_CHANNEL 76 //#define MY_RF24_PA_LEVEL RF24_PA_HIGH //#define MY_DEBUG_VERBOSE_RF24 +//#define MY_RF24_DATARATE RF24_250KBPS // RFM69 radio settings //#define MY_RADIO_RFM69 @@ -108,7 +110,7 @@ #define MODULE_DIGITAL_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY #define MODULE_DIGITAL_OUTPUT 1 -// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 +// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22, SENSOR_DHT21 #define MODULE_DHT 0 // Enable this module to use one of the following sensors: SENSOR_SHT21 #define MODULE_SHT21 0 @@ -134,5 +136,11 @@ #define MODULE_MQ 0 // Enable this module to use one of the following sensors: SENSOR_MHZ19 #define MODULE_MHZ19 0 +// Enable this module to use one of the following sensors: SENSOR_AM2320 +#define MODULE_AM2320 0 +// Enable this module to use one of the following sensors: SENSOR_TSL2561 +#define MODULE_TSL2561 0 +// Enable this module to use one of the following sensors: SENSOR_PT100 +#define MODULE_PT100 0 #endif From eacaaab9dc9c626b6c7e806bbc482c65fc41bae7 Mon Sep 17 00:00:00 2001 From: user2684 Date: Sat, 15 Jul 2017 16:21:15 +0200 Subject: [PATCH 20/33] Added support for RS485 (#169) --- NodeManager.cpp | 4 ++-- NodeManager.h | 33 +++++++++++++++++++-------------- NodeManager.ino | 2 +- config.h | 10 ++++++++-- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 03a891a9..5a02a55f 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -2713,7 +2713,7 @@ void SensorTSL2561::onProcess(Request & request) { /* SensorPT100 */ - +#if MODULE_PT100 == 1 // contructor SensorPT100::SensorPT100(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { // set presentation, type and value type @@ -2767,7 +2767,7 @@ void SensorPT100::onProcess(Request & request) { } _send(_msg_service.set(function)); } - +#endif /******************************************* NodeManager diff --git a/NodeManager.h b/NodeManager.h index 58c4c445..2f7b9db3 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -111,7 +111,7 @@ Default module settings */ -// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_ACS712, SENSOR_PT100 +// 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 #ifndef MODULE_ANALOG_INPUT #define MODULE_ANALOG_INPUT 0 #endif @@ -123,18 +123,14 @@ #ifndef MODULE_DIGITAL_OUTPUT #define MODULE_DIGITAL_OUTPUT 0 #endif -// Enable this module to use one of the following sensors: SENSOR_SHT21, SENSOR_HTU21D -#ifndef MODULE_SHT21 - #define MODULE_SHT21 0 -#endif -// Enable this module to use one of the following sensors: SENSOR_AM2320 -#ifndef MODULE_AM2320 - #define MODULE_AM2320 0 -#endif // Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22, SENSOR_DHT21 #ifndef MODULE_DHT #define MODULE_DHT 0 #endif +// Enable this module to use one of the following sensors: SENSOR_SHT21, SENSOR_HTU21D +#ifndef MODULE_SHT21 + #define MODULE_SHT21 0 +#endif // Enable this module to use one of the following sensors: SENSOR_SWITCH, SENSOR_DOOR, SENSOR_MOTION #ifndef MODULE_SWITCH #define MODULE_SWITCH 0 @@ -147,10 +143,6 @@ #ifndef MODULE_BH1750 #define MODULE_BH1750 0 #endif -// Enable this module to use one of the following sensors: SENSOR_TSL2561 -#ifndef MODULE_TSL2561 - #define MODULE_TSL2561 0 -#endif // Enable this module to use one of the following sensors: SENSOR_MLX90614 #ifndef MODULE_MLX90614 #define MODULE_MLX90614 0 @@ -183,6 +175,18 @@ #ifndef MODULE_MHZ19 #define MODULE_MHZ19 0 #endif +// Enable this module to use one of the following sensors: SENSOR_AM2320 +#ifndef MODULE_AM2320 + #define MODULE_AM2320 0 +#endif +// Enable this module to use one of the following sensors: SENSOR_TSL2561 +#ifndef MODULE_TSL2561 + #define MODULE_TSL2561 0 +#endif +// Enable this module to use one of the following sensors: SENSOR_PT100 +#ifndef MODULE_PT100 + #define SENSOR_PT100 0 +#endif /*********************************** Supported Sensors @@ -1276,7 +1280,7 @@ class SensorTSL2561: public Sensor { /* SensorPT100 */ - +#if MODULE_PT100 == 1 class SensorPT100: public Sensor { public: SensorPT100(NodeManager* node_manager, int child_id, int pin); @@ -1292,6 +1296,7 @@ class SensorPT100: public Sensor { DFRobotHighTemperature* _PT100; float _voltageRef = 3.3; }; +#endif /*************************************** NodeManager: manages all the aspects of the node diff --git a/NodeManager.ino b/NodeManager.ino index f2296b5f..657bfafa 100755 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -30,7 +30,7 @@ void before() { Serial.begin(MY_BAUD_RATE); /* - * Register below your device specific config + * Register below your sensors */ diff --git a/config.h b/config.h index f3136dd1..42d0447e 100755 --- a/config.h +++ b/config.h @@ -18,9 +18,8 @@ //#define MY_NODE_ID 100 // NRF24 radio settings -//#define MY_RADIO_NRF24 +#define MY_RADIO_NRF24 //#define MY_RF24_ENABLE_ENCRYPTION - //#define MY_RF24_CHANNEL 76 //#define MY_RF24_PA_LEVEL RF24_PA_HIGH //#define MY_DEBUG_VERBOSE_RF24 @@ -38,6 +37,13 @@ //#define MY_RF69_IRQ_NUM MY_RF69_IRQ_PIN //#define MY_RF69_SPI_CS D2 +// RS485 serial transport settings +//#define MY_RS485 +//#define MY_RS485_BAUD_RATE 9600 +//#define MY_RS485_DE_PIN 2 +//#define MY_RS485_MAX_MESSAGE_LENGTH 40 +//#define MY_RS485_HWSERIAL Serial1 + /********************************** * MySensors gateway configuration */ From 6664b5ebd37a46643d842bf0b65fada2f9d1947e Mon Sep 17 00:00:00 2001 From: user2684 Date: Sat, 15 Jul 2017 17:37:36 +0200 Subject: [PATCH 21/33] Added support for BMP280 sensor (#88) --- NodeManager.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++++++++- NodeManager.h | 32 +++++++++++++++++++--- NodeManager.ino | 4 --- README.md | 10 ++++++- config.h | 3 +++ 5 files changed, 110 insertions(+), 9 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 5a02a55f..b6fc63d5 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -1710,7 +1710,7 @@ void SensorMLX90614::onInterrupt() { /* SensorBosch */ -#if MODULE_BME280 == 1 || MODULE_BMP085 == 1 +#if MODULE_BME280 == 1 || MODULE_BMP085 == 1 || MODULE_BMP280 == 1 // contructor SensorBosch::SensorBosch(NodeManager* node_manager, int child_id, int sensor_type): Sensor(node_manager, child_id,A4) { _sensor_type = sensor_type; @@ -1998,6 +1998,54 @@ void SensorBMP085::onLoop() { } #endif +/* + * SensorBMP280 + */ +#if MODULE_BMP280 == 1 +SensorBMP280::SensorBMP280(NodeManager* node_manager, int child_id, Adafruit_BMP280* bmp, int sensor_type): SensorBosch(node_manager, child_id,sensor_type) { + _bmp = bmp; +} + +void SensorBMP280::onLoop() { + // temperature sensor + if (_sensor_type == SensorBMP280::TEMPERATURE) { + // read the temperature + float temperature = _bmp->readTemperature(); + // convert it + temperature = _node_manager->celsiusToFahrenheit(temperature); + #if DEBUG == 1 + Serial.print(F("BMP I=")); + Serial.print(_child_id); + Serial.print(F(" T=")); + Serial.println(temperature); + #endif + if (isnan(temperature)) return; + // store the value + _value_float = temperature; + } + // Pressure Sensor + else if (_sensor_type == SensorBMP280::PRESSURE) { + // read pressure + float pressure = _bmp->readPressure() / 100.0F; + if (isnan(pressure)) return; + #if DEBUG == 1 + Serial.print(F("BMP I=")); + Serial.print(_child_id); + Serial.print(F(" P=")); + Serial.println(pressure); + #endif + if (isnan(pressure)) return; + // store the value + _value_float = pressure; + } + // Forecast Sensor + else if (_sensor_type == SensorBMP280::FORECAST) { + float pressure = _bmp->readPressure() / 100.0F; + _forecast(pressure); + } +} +#endif + /* SensorHCSR04 */ @@ -3025,6 +3073,26 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { return registerSensor(new SensorBME280(this,child_id,bme,SensorBME280::FORECAST)); } #endif + #if MODULE_BMP280 == 1 + else if (sensor_type == SENSOR_BMP280) { + Adafruit_BMP280* bmp = new Adafruit_BMP280(); + if (! bmp->begin(SensorBosch::GetI2CAddress(0x58))) { + #if DEBUG == 1 + Serial.println(F("NO BMP")); + #endif + return -1; + } + // register temperature sensor + registerSensor(new SensorBMP280(this,child_id,bmp,SensorBMP280::TEMPERATURE)); + child_id = _getAvailableChildId(); + // register pressure sensor + child_id = _getAvailableChildId(); + registerSensor(new SensorBMP280(this,child_id,bmp,SensorBMP280::PRESSURE)); + // register forecast sensor + child_id = _getAvailableChildId(); + return registerSensor(new SensorBMP280(this,child_id,bmp,SensorBMP280::FORECAST)); + } + #endif #if MODULE_SONOFF == 1 else if (sensor_type == SENSOR_SONOFF) { return registerSensor(new SensorSonoff(this,child_id)); diff --git a/NodeManager.h b/NodeManager.h index 2f7b9db3..77dac5e6 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -187,6 +187,10 @@ #ifndef MODULE_PT100 #define SENSOR_PT100 0 #endif +// Enable this module to use one of the following sensors: SENSOR_BMP280 +#ifndef MODULE_BMP280 + #define MODULE_BMP280 0 +#endif /*********************************** Supported Sensors @@ -254,7 +258,7 @@ enum supported_sensors { SENSOR_MLX90614, #endif #if MODULE_BME280 == 1 - // MLX90614 sensor, contactless temperature sensor + // BME280 sensor, return temperature, humidity and pressure SENSOR_BME280, #endif #if MODULE_SONOFF == 1 @@ -293,6 +297,10 @@ enum supported_sensors { // High temperature sensor associated with DFRobot Driver, return the temperature in C° from the attached PT100 sensor SENSOR_PT100, #endif + #if MODULE_BMP280 == 1 + // BMP280 sensor, return temperature and pressure + SENSOR_BMP280, + #endif }; /*********************************** @@ -363,7 +371,12 @@ enum supported_sensors { #include #endif #if MODULE_PT100 == 1 - #include + #include +#endif +#if MODULE_BMP280 == 1 + #include + #include + #include #endif /******************************************************************* @@ -984,7 +997,7 @@ class SensorMLX90614: public Sensor { * SensorBosch */ -#if MODULE_BME280 == 1 || MODULE_BMP085 == 1 +#if MODULE_BME280 == 1 || MODULE_BMP085 == 1 || MODULE_BMP280 == 1 class SensorBosch: public Sensor { public: SensorBosch(NodeManager* node_manager, int child_id, int sensor_type); @@ -1044,6 +1057,19 @@ class SensorBMP085: public SensorBosch { }; #endif +/* + SensorBMP280 +*/ +#if MODULE_BMP280 == 1 +class SensorBMP280: public SensorBosch { + public: + SensorBMP280(NodeManager* node_manager, int child_id, Adafruit_BMP280* bmp, int sensor_type); + void onLoop(); + protected: + Adafruit_BMP280* _bmp; +}; +#endif + /* SensorHCSR04 */ diff --git a/NodeManager.ino b/NodeManager.ino index 657bfafa..ad7a457a 100755 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -33,10 +33,6 @@ void before() { * Register below your sensors */ - - /* - * Register below your sensors - */ /* diff --git a/README.md b/README.md index 3c851316..24e820d2 100755 --- a/README.md +++ b/README.md @@ -179,6 +179,11 @@ Those NodeManager's directives in the `config.h` file control which module/libra #define MODULE_AM2320 0 // Enable this module to use one of the following sensors: SENSOR_TSL2561 #define MODULE_TSL2561 0 +// Enable this module to use one of the following sensors: SENSOR_PT100 +#define MODULE_PT100 0 +// Enable this module to use one of the following sensors: SENSOR_BMP280 +#define MODULE_BMP280 0 + ~~~ ### Installing the dependencies @@ -199,6 +204,7 @@ MODULE_HCSR04 | https://github.com/mysensors/MySensorsArduinoExamples/tree/maste MODULE_MCP9808 | https://github.com/adafruit/Adafruit_MCP9808_Library MODULE_AM2320 | https://github.com/thakshak/AM2320 MODULE_TSL2561 | https://github.com/adafruit/TSL2561-Arduino-Library +MODULE_BMP280 | https://github.com/adafruit/Adafruit_BMP280_Library ### Configure NodeManager @@ -348,6 +354,8 @@ SENSOR_SOIL_MOISTURE | Soil moisture sensor, return the percentage of moisture f SENSOR_MHZ19 | MH-Z19 CO2 sensor via UART (SoftwareSerial, default on pins 6(Rx) and 7(Tx) SENSOR_TSL2561 | TSL2561 sensor, return light in lux SENSOR_AM2320 | AM2320 sensors, return temperature/humidity based on the attached AM2320 sensor +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 To register a sensor simply call the NodeManager instance with the sensory type and the pin the sensor is conncted to. For example: ~~~c @@ -578,7 +586,7 @@ Each sensor class can expose additional methods. DeviceAddress* getDeviceAddress(); ~~~ -* SensorBME280 / SensorBMP085 +* SensorBME280 / SensorBMP085 / SensorBMP280 ~~~c // [101] define how many pressure samples to keep track of for calculating the forecast (default: 5) void setForecastSamplesCount(int value); diff --git a/config.h b/config.h index 42d0447e..5d3945b5 100755 --- a/config.h +++ b/config.h @@ -148,5 +148,8 @@ #define MODULE_TSL2561 0 // Enable this module to use one of the following sensors: SENSOR_PT100 #define MODULE_PT100 0 +// Enable this module to use one of the following sensors: SENSOR_BMP280 +#define MODULE_BMP280 0 + #endif From 89ba0f4c43679cec274497bf0f34dbc1c80142fe Mon Sep 17 00:00:00 2001 From: user2684 Date: Sun, 23 Jul 2017 15:39:04 +0200 Subject: [PATCH 22/33] Introduced new way for managing reporting intervals and sleeping cycles (#153, #179, #184) --- NodeManager.cpp | 365 +++++++++++++++++++++++------------------------- NodeManager.h | 101 ++++++-------- NodeManager.ino | 1 + README.md | 232 +++++++++++++++++------------- 4 files changed, 355 insertions(+), 344 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index b6fc63d5..80129873 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -66,7 +66,7 @@ Timer::Timer(NodeManager* node_manager) { } // start the timer -void Timer::start(long target, int unit) { +void Timer::start(int target, int unit) { set(target,unit); start(); } @@ -79,51 +79,52 @@ void Timer::stop() { _is_running = false; } -// setup the timer -void Timer::set(long target, int unit) { +// reset the timer +void Timer::reset() { // reset the timer _elapsed = 0; - _use_millis = false; _last_millis = 0; - _sleep_time = 0; +} + +// restart the timer +void Timer::restart() { + if (! isRunning()) return; + stop(); + reset(); + // if using millis(), keep track of the current timestamp for calculating the difference + if (! _node_manager->isSleepingNode()) _last_millis = millis(); + start(); +} + +// setup the timer +void Timer::set(int target, int unit) { + reset(); // 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; - } - } + if (unit == MINUTES) _target = _target * 60; + else if (unit == HOURS) _target = _target * 60 *60; + else if (unit == DAYS) _target = _target * 60 * 60 *24; + _is_running = false; + _is_configured = true; +} + +// unset the timer +void Timer::unset() { + stop(); _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; - } + if (_node_manager->isSleepingNode()) { + // millis() is not reliable while sleeping so calculate how long a sleep cycle would last in seconds and update the elapsed time + _elapsed += _node_manager->getSleepSeconds(); + } else { + // use millis() to calculate the elapsed time in seconds + _elapsed = (long)((millis() - _last_millis)/1000); } + _first_run = false; } // return true if the time is over @@ -138,6 +139,7 @@ bool Timer::isOver() { // return true if the timer is running bool Timer::isRunning() { + if (! isConfigured()) return false; return _is_running; } @@ -146,25 +148,16 @@ 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 true if this is the first time the timer runs +bool Timer::isFirstRun() { + return _first_run; } -// return elapsed minutes so far +// return elapsed seconds so far float Timer::getElapsed() { return _elapsed; } -// return the configured unit -int Timer::getUnit() { - return _unit; -} - /****************************************** Request @@ -268,15 +261,12 @@ void Sensor::setSamplesInterval(int value) { void Sensor::setTrackLastValue(bool value) { _track_last_value = value; } -void Sensor::setForceUpdate(int 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::setForceUpdateHours(int value) { + _force_update_timer->start(value,HOURS); +} void Sensor::setValueType(int value) { _value_type = value; } @@ -313,16 +303,21 @@ char* Sensor::getValueString() { 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) +// After how many minutes the sensor will report back its measure void Sensor::setReportIntervalMinutes(int value) { _report_timer->start(value,MINUTES); } +// After how many seconds the sensor will report back its measure +void Sensor::setReportIntervalSeconds(int value) { + _report_timer->start(value,SECONDS); +} + +// return true if the report interval has been already configured +bool Sensor::isReportIntervalConfigured() { + return _report_timer->isConfigured(); +} + // 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; @@ -358,10 +353,12 @@ void Sensor::loop(const MyMessage & message) { // update the timers if within a loop cycle if (! _isReceive(message)) { if (_report_timer->isRunning()) { + // store the elapsed time before updating it + bool first_run = _report_timer->isFirstRun(); // 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 it is not the time yet to report a new measure, just return (unless the first time) + if (! _report_timer->isOver() && ! first_run) return; } if (_force_update_timer->isRunning()) _force_update_timer->update(); } @@ -464,7 +461,6 @@ void Sensor::process(Request & request) { case 5: setSamples(request.getValueInt()); break; case 6: setSamplesInterval(request.getValueInt()); break; case 7: setTrackLastValue(request.getValueInt()); break; - case 8: setForceUpdateCycles(request.getValueInt()); break; case 9: setForceUpdateMinutes(request.getValueInt()); break; case 10: setValueType(request.getValueInt()); break; case 11: setFloatPrecision(request.getValueInt()); break; @@ -473,8 +469,9 @@ void Sensor::process(Request & request) { case 13: powerOn(); break; case 14: powerOff(); break; #endif - case 15: setReportIntervalCycles(request.getValueInt()); break; case 16: setReportIntervalMinutes(request.getValueInt()); break; + case 17: setReportIntervalSeconds(request.getValueInt()); break; + case 18: setForceUpdateHours(request.getValueInt()); break; default: return; } _send(_msg_service.set(function)); @@ -705,7 +702,7 @@ void SensorThermistor::onLoop() { Serial.print(F(" V=")); Serial.print(adc); Serial.print(F(" T=")); - Serial.print(temperature); + Serial.println(temperature); #endif // store the value _value_float = temperature; @@ -1438,15 +1435,14 @@ void SensorSwitch::setInitial(int value) { // what to do during before void SensorSwitch::onBefore() { - // initialize the value - 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 setInterrupt(_pin,_mode,_initial); } // what to do during setup void SensorSwitch::onSetup() { + // report immediately + _report_timer->unset(); } // what to do during loop @@ -1510,8 +1506,6 @@ SensorDoor::SensorDoor(NodeManager* node_manager, int child_id, int pin): Sensor */ SensorMotion::SensorMotion(NodeManager* node_manager, int child_id, int pin): SensorSwitch(node_manager, child_id,pin) { setPresentation(S_MOTION); - // capture only when it triggers - setMode(RISING); // set initial value to LOW setInitial(LOW); } @@ -2846,9 +2840,6 @@ int NodeManager::getRetries() { void NodeManager::setBatteryMax(float value) { _battery_max = value; } - void NodeManager::setBatteryReportCycles(int value) { - _battery_report_timer.set(value,CYCLES); - } void NodeManager::setBatteryReportMinutes(int value) { _battery_report_timer.set(value,MINUTES); } @@ -2865,31 +2856,24 @@ int NodeManager::getRetries() { _battery_report_with_interrupt = value; } #endif -void NodeManager::setSleepMode(int value) { - _sleep_mode = value; -} -void NodeManager::setMode(int value) { - setSleepMode(value); -} -int NodeManager::getMode() { - return _sleep_mode; -} -void NodeManager::setSleepTime(int value) { +void NodeManager::setSleepSeconds(int value) { + // set the status to AWAKE if the time provided is 0, SLEEP otherwise + if (value == 0) _status = AWAKE; + else _status = SLEEP; + // store the time _sleep_time = value; } -int NodeManager::getSleepTime() { - return _sleep_time; +void NodeManager::setSleepMinutes(int value) { + setSleepSeconds(value*60); } -void NodeManager::setSleepUnit(int value) { - _sleep_unit = value; +void NodeManager::setSleepHours(int value) { + setSleepMinutes(value*60); } -int NodeManager::getSleepUnit() { - return _sleep_unit; +void NodeManager::setSleepDays(int value) { + setSleepHours(value*24); } -void NodeManager::setSleep(int value1, int value2, int value3) { - setMode(value1); - setSleepTime(value2); - setSleepUnit(value3); +long NodeManager::getSleepSeconds() { + return _sleep_time; } void NodeManager::setSleepInterruptPin(int value) { _sleep_interrupt_pin = value; @@ -2952,7 +2936,7 @@ float NodeManager::celsiusToFahrenheit(float temperature) { // 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; + if (_status == SLEEP) return true; return false; } @@ -3261,14 +3245,16 @@ void NodeManager::before() { #if BATTERY_MANAGER == 1 && !defined(MY_GATEWAY_ESP8266) // set analogReference to internal if measuring the battery through a pin if (! _battery_internal_vcc && _battery_pin > -1) analogReference(INTERNAL); - // if not configured report battery every 10 cycles + // if not already configured, report battery level every 60 minutes if (! _battery_report_timer.isConfigured()) _battery_report_timer.set(60,MINUTES); _battery_report_timer.start(); #endif // setup individual sensors for (int i = 1; i <= MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; - // call each sensor's setup() + // configure reporting interval + if (! _sensors[i]->isReportIntervalConfigured()) _sensors[i]->setReportIntervalSeconds(_report_interval_seconds); + // call each sensor's before() _sensors[i]->before(); } // setup the interrupt pins @@ -3329,14 +3315,9 @@ void NodeManager::setup() { // run the main function for all the register sensors void NodeManager::loop() { MyMessage empty; - // if in idle mode, do nothing - if (_sleep_mode == IDLE) return; - // if sleep time is not set, do nothing - 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(); + // update the timer for battery report when not waking up from an interrupt + if (_battery_report_timer.isRunning() && _last_interrupt_pin == -1) _battery_report_timer.update(); // if it is time to report the battery level if (_battery_report_timer.isOver()) { // time to report the battery level again @@ -3351,15 +3332,20 @@ void NodeManager::loop() { #endif // run loop for all the registered sensors for (int i = 1; i <= MAX_SENSORS; i++) { - // skip not configured sensors + // skip unconfigured sensors if (_sensors[i] == 0) continue; - // 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); + if (_last_interrupt_pin != -1 && _sensors[i]->getInterruptPin() == _last_interrupt_pin) { + // if there was an interrupt for this sensor, call the sensor's interrupt() and then loop() + _sensors[i]->interrupt(); + _sensors[i]->loop(empty); + // reset the last interrupt pin + _last_interrupt_pin = -1; + } + else if (_last_interrupt_pin == -1) { + // if just at the end of a cycle, 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(); @@ -3441,7 +3427,6 @@ void NodeManager::process(Request & request) { case 2: batteryReport(); return; case 11: setBatteryMin(request.getValueFloat()); break; case 12: setBatteryMax(request.getValueFloat()); break; - case 13: setBatteryReportCycles(request.getValueInt()); break; case 14: setBatteryReportMinutes(request.getValueInt()); break; case 15: setBatteryInternalVcc(request.getValueInt()); break; case 16: setBatteryPin(request.getValueInt()); break; @@ -3449,21 +3434,27 @@ void NodeManager::process(Request & request) { case 18: setBatteryReportWithInterrupt(request.getValueInt()); break; #endif case 3: - setSleepMode(request.getValueInt()); + setSleepSeconds(request.getValueInt()); #if PERSIST == 1 - _saveConfig(SAVE_SLEEP_MODE); + _saveConfig(); #endif break; case 4: - setSleepTime(request.getValueInt()); + setSleepMinutes(request.getValueInt()); #if PERSIST == 1 - _saveConfig(SAVE_SLEEP_TIME); + _saveConfig(); #endif break; case 5: - setSleepUnit(request.getValueInt()); + setSleepHours(request.getValueInt()); + #if PERSIST == 1 + _saveConfig(); + #endif + break; + case 29: + setSleepDays(request.getValueInt()); #if PERSIST == 1 - _saveConfig(SAVE_SLEEP_UNIT); + _saveConfig(); #endif break; #ifndef MY_GATEWAY_ESP8266 @@ -3554,7 +3545,7 @@ void NodeManager::wakeup() { #if DEBUG == 1 Serial.println(F("WAKEUP")); #endif - _sleep_mode = IDLE; + _status = AWAKE; } // return the value stored at the requested index from the EEPROM @@ -3604,13 +3595,13 @@ void NodeManager::setupInterrupts() { 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 (_status != 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 (_status != SLEEP) attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_2), _onInterrupt_2, _interrupt_2_mode); } #if DEBUG == 1 Serial.print(F("INT P=")); @@ -3629,6 +3620,16 @@ int NodeManager::getLastInterruptPin() { return _last_interrupt_pin; } +// set the default interval in minutes all the sensors will report their measures +void NodeManager::setReportIntervalMinutes(int value) { + _report_interval_seconds = value*60; +} + +// set the default interval in seconds all the sensors will report their measures +void NodeManager::setReportIntervalSeconds(int value) { + _report_interval_seconds = value; +} + // handle an interrupt void NodeManager::_onInterrupt_1() { long now = millis(); @@ -3681,15 +3682,9 @@ void NodeManager::_send(MyMessage & message) { // wrapper of smart sleep void NodeManager::_sleep() { - // calculate the seconds to sleep - long sleep_sec = _sleep_time; - if (_sleep_unit == MINUTES) sleep_sec = sleep_sec * 60; - else if (_sleep_unit == HOURS) sleep_sec = sleep_sec * 3600; - else if (_sleep_unit == DAYS) sleep_sec = sleep_sec * 43200; - long sleep_ms = sleep_sec * 1000; #if DEBUG == 1 Serial.print(F("SLEEP ")); - Serial.print(sleep_sec); + Serial.print(_sleep_time); Serial.println(F("s")); #endif #if SERVICE_MESSAGES == 1 @@ -3702,41 +3697,33 @@ void NodeManager::_sleep() { #endif // go to sleep int interrupt = -1; - if (_sleep_mode == WAIT) { - // wait for the given interval - wait(sleep_ms); - // send heartbeat to the controller - sendHeartbeat(_ack); - } - else if (_sleep_mode == SLEEP) { - // setup interrupt pins - int interrupt_1_pin = _interrupt_1_mode == MODE_NOT_DEFINED ? INTERRUPT_NOT_DEFINED : digitalPinToInterrupt(INTERRUPT_PIN_1); - int interrupt_2_pin = _interrupt_2_mode == MODE_NOT_DEFINED ? INTERRUPT_NOT_DEFINED : digitalPinToInterrupt(INTERRUPT_PIN_2); - // enter smart sleep for the requested sleep interval and with the configured interrupts - interrupt = sleep(interrupt_1_pin,_interrupt_1_mode,interrupt_2_pin,_interrupt_2_mode,sleep_ms, true); - if (interrupt > -1) { - // woke up by an interrupt - int pin_number = -1; - int interrupt_mode = -1; - // map the interrupt to the pin - if (digitalPinToInterrupt(INTERRUPT_PIN_1) == interrupt) { - pin_number = INTERRUPT_PIN_1; - interrupt_mode = _interrupt_1_mode; - } - if (digitalPinToInterrupt(INTERRUPT_PIN_2) == interrupt) { - pin_number = INTERRUPT_PIN_2; - interrupt_mode = _interrupt_2_mode; - } - _last_interrupt_pin = pin_number; - #if DEBUG == 1 - Serial.print(F("WAKE P=")); - Serial.print(pin_number); - Serial.print(F(", M=")); - Serial.println(interrupt_mode); - #endif - // when waking up from an interrupt on the wakup pin, stop sleeping - if (_sleep_interrupt_pin == pin_number) _sleep_mode = IDLE; + // setup interrupt pins + int interrupt_1_pin = _interrupt_1_mode == MODE_NOT_DEFINED ? INTERRUPT_NOT_DEFINED : digitalPinToInterrupt(INTERRUPT_PIN_1); + int interrupt_2_pin = _interrupt_2_mode == MODE_NOT_DEFINED ? INTERRUPT_NOT_DEFINED : digitalPinToInterrupt(INTERRUPT_PIN_2); + // enter smart sleep for the requested sleep interval and with the configured interrupts + interrupt = sleep(interrupt_1_pin,_interrupt_1_mode,interrupt_2_pin,_interrupt_2_mode,_sleep_time*1000, true); + if (interrupt > -1) { + // woke up by an interrupt + int pin_number = -1; + int interrupt_mode = -1; + // map the interrupt to the pin + if (digitalPinToInterrupt(INTERRUPT_PIN_1) == interrupt) { + pin_number = INTERRUPT_PIN_1; + interrupt_mode = _interrupt_1_mode; } + if (digitalPinToInterrupt(INTERRUPT_PIN_2) == interrupt) { + pin_number = INTERRUPT_PIN_2; + interrupt_mode = _interrupt_2_mode; + } + _last_interrupt_pin = pin_number; + #if DEBUG == 1 + Serial.print(F("INT P=")); + Serial.print(pin_number); + Serial.print(F(", M=")); + Serial.println(interrupt_mode); + #endif + // when waking up from an interrupt on the wakup pin, stop sleeping + if (_sleep_interrupt_pin == pin_number) _status = AWAKE; } // coming out of sleep #if DEBUG == 1 @@ -3781,43 +3768,35 @@ int NodeManager::_getInterruptInitialValue(int mode) { // load the configuration stored in the eeprom void NodeManager::_loadConfig() { if (loadState(EEPROM_SLEEP_SAVED) == 1) { - // sleep settings found in the eeprom, restore them - _sleep_mode = loadState(EEPROM_SLEEP_MODE); - _sleep_time = loadState(EEPROM_SLEEP_TIME_MINOR); - int major = loadState(EEPROM_SLEEP_TIME_MAJOR); - if (major == 1) _sleep_time = _sleep_time + 250; - else if (major == 2) _sleep_time = _sleep_time + 250 * 2; - else if (major == 3) _sleep_time = _sleep_time + 250 * 3; - _sleep_unit = loadState(EEPROM_SLEEP_UNIT); + // load sleep settings + int bit_1 = loadState(EEPROM_SLEEP_1); + int bit_2 = loadState(EEPROM_SLEEP_2); + int bit_3 = loadState(EEPROM_SLEEP_3); + _sleep_time = bit_3*255*255 + bit_2*255 + bit_1; #if DEBUG == 1 - Serial.print(F("LOADSLP M=")); - Serial.print(_sleep_mode); - Serial.print(F(" T=")); - Serial.print(_sleep_time); - Serial.print(F(" U=")); - Serial.println(_sleep_unit); + Serial.print(F("LOADSLP T=")); + Serial.println(_sleep_time); #endif } } // save the configuration in the eeprom -void NodeManager::_saveConfig(int what) { - if (what == SAVE_SLEEP_MODE) { - saveState(EEPROM_SLEEP_SAVED, 1); - saveState(EEPROM_SLEEP_MODE, _sleep_mode); - } - else if (what == SAVE_SLEEP_TIME) { - // encode sleep time - int major = 0; - if (_sleep_time > 750) major = 3; - else if (_sleep_time > 500) major = 2; - else if (_sleep_time > 250) major = 1; - int minor = _sleep_time - 250 * major; - saveState(EEPROM_SLEEP_SAVED, 1); - saveState(EEPROM_SLEEP_TIME_MINOR, minor); - saveState(EEPROM_SLEEP_TIME_MAJOR, major); - } - else if (what == SAVE_SLEEP_UNIT) { - saveState(EEPROM_SLEEP_UNIT, _sleep_unit); - } +void NodeManager::_saveConfig() { + if (_sleep_time == 0) return; + // encode the sleep time in 3 bits + int bit_1, bit_2, bit_3 = 0; + bit_1 = _sleep_time; + if (bit_1 >= 255) { + bit_2 = (int)bit_1/255; + bit_1 = bit_1 - bit_2*255; + } + if (bit_2 >= 255) { + bit_3 = (int)bit_2/255; + bit_2 = bit_2 - bit_3*255; + } + // save the 3 bits + saveState(EEPROM_SLEEP_SAVED,1); + saveState(EEPROM_SLEEP_1,bit_1); + saveState(EEPROM_SLEEP_2,bit_2); + saveState(EEPROM_SLEEP_3,bit_3); } diff --git a/NodeManager.h b/NodeManager.h index 77dac5e6..8728831f 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -13,18 +13,15 @@ Constants */ -// define sleep mode -#define IDLE 0 +// define board status +#define AWAKE 0 #define SLEEP 1 -#define WAIT 2 -#define ALWAYS_ON 3 // define time unit #define SECONDS 0 #define MINUTES 1 #define HOURS 2 #define DAYS 3 -#define CYCLES 4 // define on/off #define OFF 0 @@ -39,17 +36,11 @@ #define INTERRUPT_PIN_1 3 #define INTERRUPT_PIN_2 2 -// define configuration settings that can be saved and loaded from the EEPROM -#define SAVE_SLEEP_MODE 0 -#define SAVE_SLEEP_TIME 1 -#define SAVE_SLEEP_UNIT 2 - // define eeprom addresses #define EEPROM_SLEEP_SAVED 0 -#define EEPROM_SLEEP_MODE 1 -#define EEPROM_SLEEP_TIME_MAJOR 2 -#define EEPROM_SLEEP_TIME_MINOR 3 -#define EEPROM_SLEEP_UNIT 4 +#define EEPROM_SLEEP_1 5 +#define EEPROM_SLEEP_2 6 +#define EEPROM_SLEEP_3 7 #define EEPROM_USER_START 100 // define requests @@ -410,39 +401,38 @@ class PowerManager { class Timer { public: Timer(NodeManager* node_manager); - // start the timer which will be over when interval passes by. Unit can be either CYCLES or MINUTES - void start(long target, int unit); + // start the timer which will be over when the configured target passes by + void start(int target, int unit); void start(); // stop the timer void stop(); + // reset the timer + void reset(); + // reset the timer and start over + void restart(); // set the timer configuration but do not start it - void set(long target, int unit); + void set(int target, int unit); + void unset(); // update the timer. To be called at every cycle void update(); - // returns true if the time is over + // return true if the time is over bool isOver(); // return true if the timer is running bool isRunning(); - // returns true if the timer has been configured + // return true if the timer has been configured bool isConfigured(); - // reset the timer and start over - void restart(); + // return true if this is the first time the timer runs + bool isFirstRun(); // return the current elapsed time float getElapsed(); - // return the configured unit - int getUnit(); - // return the configured target - int getTarget(); private: NodeManager* _node_manager; - long _target = 0; - int _unit = 0; - float _elapsed = 0; - bool _use_millis = false; + int _target = 0; + long _elapsed = 0; long _last_millis = 0; - float _sleep_time = 0; bool _is_running = false; bool _is_configured = false; + bool _first_run = true; }; /* @@ -492,11 +482,10 @@ class Sensor { void setSamplesInterval(int value); // [7] if true will report the measure only if different than the previous one (default: false) void setTrackLastValue(bool value); - // [8] if track last value is enabled, force to send an update after the configured number of cycles (default: -1) - void setForceUpdate(int value); - void setForceUpdateCycles(int value); - // [9] if track last value is enabled, force to send an update after the configured number of minutes (default: -1) + // [9] if track last value is enabled, force to send an update after the configured number of minutes void setForceUpdateMinutes(int value); + // [19] if track last value is enabled, force to send an update after the configured number of hours + void setForceUpdateHours(int value); // [10] the value type of this sensor (default: TYPE_INTEGER) void setValueType(int value); int getValueType(); @@ -516,10 +505,12 @@ class Sensor { int getValueInt(); float getValueFloat(); char* getValueString(); - // [15] After how many cycles the sensor will report back its measure (default: 1 cycle) - void setReportIntervalCycles(int value); - // [16] After how many minutes the sensor will report back its measure (default: 1 cycle) + // [16] After how many minutes the sensor will report back its measure (default: 10 minutes) void setReportIntervalMinutes(int value); + // [17] After how many minutes the sensor will report back its measure (default: 10 minutes) + void setReportIntervalSeconds(int value); + // return true if the report interval has been already configured + bool isReportIntervalConfigured(); // process a remote request void process(Request & request); // return the pin the interrupt is attached to @@ -1338,8 +1329,6 @@ class NodeManager { void setBatteryMin(float value); // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // [13] after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) - void setBatteryReportCycles(int value); // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) void setBatteryReportMinutes(int value); // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) @@ -1353,18 +1342,15 @@ class NodeManager { // [2] Send a battery level report to the controller void batteryReport(); #endif - // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) - void setSleepMode(int value); - void setMode(int value); - int getMode(); - // [4] define for how long the board will sleep (default: 0) - void setSleepTime(int value); - int getSleepTime(); - // [5] define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) - void setSleepUnit(int value); - int getSleepUnit(); - // configure the node's behavior, parameters are mode, time and unit - void setSleep(int value1, int value2, int value3); + // [3] set the duration (in seconds) of a sleep cycle + void setSleepSeconds(int value); + long getSleepSeconds(); + // [4] set the duration (in minutes) of a sleep cycle + void setSleepMinutes(int value); + // [5] set the duration (in hours) of a sleep cycle + void setSleepHours(int value); + // [29] set the duration (in days) of a sleep cycle + void setSleepDays(int value); // [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) @@ -1431,6 +1417,11 @@ class NodeManager { void setupInterrupts(); // return the pin from which the last interrupt came int getLastInterruptPin(); + // set the default interval in minutes all the sensors will report their measures. + // If the same function is called on a specific sensor, this will not change the previously set value + // For sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) + void setReportIntervalMinutes(int value); + void setReportIntervalSeconds(int value); // hook into the main sketch functions void before(); void presentation(); @@ -1458,9 +1449,8 @@ class NodeManager { #endif MyMessage _msg; void _send(MyMessage & msg); - int _sleep_mode = IDLE; - int _sleep_time = 0; - int _sleep_unit = MINUTES; + int _status = AWAKE; + long _sleep_time = 0; int _sleep_interrupt_pin = -1; int _sleep_between_send = 0; int _retries = 1; @@ -1481,8 +1471,9 @@ class NodeManager { int _getInterruptInitialValue(int mode); bool _get_controller_config = true; int _is_metric = 1; + int _report_interval_seconds = 10*60; void _loadConfig(); - void _saveConfig(int what); + void _saveConfig(); }; #endif diff --git a/NodeManager.ino b/NodeManager.ino index ad7a457a..3df7d474 100755 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -35,6 +35,7 @@ void before() { + /* * Register above your sensors */ diff --git a/README.md b/README.md index 24e820d2..daf0971c 100755 --- a/README.md +++ b/README.md @@ -11,19 +11,19 @@ NodeManager includes the following main components: ## Features * Manage all the aspects of a sleeping cycle by leveraging smart sleep -* Allow configuring the sleep mode and the sleep duration remotely +* Allow configuring the node and any attached sensors remotely * Allow waking up a sleeping node remotely at the end of a sleeping cycle * Allow powering on each connected sensor only while the node is awake to save battery -* Report battery level periodically and automatically +* Report battery level periodically and automatically or on demand * Calculate battery level without requiring an additional pin and the resistors -* Report battery voltage through a built-in sensor -* Can report battery level on demand * Allow rebooting the board remotely * Provide out-of-the-box sensors personalities and automatically execute their main task at each cycle +* Allow collecting and averaging multiple samples, tracking the last value and forcing periodic updates for any sensor +* Provide buil-in capabilities to handle interrupt-based sensors ## Installation -* Download the package or clone the git repository at https://github.com/mysensors/NodeManager -* Open the provided sketch template and save it under a different name +* Download the package or clone the git repository from https://github.com/mysensors/NodeManager +* Open the provided sketch and save it under a different name * Open `config.h` and customize both MySensors configuration and NodeManager global settings * Register your sensors in the sketch file * Upload the sketch to your arduino board @@ -36,7 +36,7 @@ Please note NodeManager cannot be used as an arduino library since requires acce * Review the release notes in case there is any manual change required to the existing sketch or config.h file ## Configuration -NodeManager configuration includes compile-time configuration directives (which can be set in config.h), runtime global and per-sensor configuration settings (which can be set in your sketch) and settings that can be customized remotely (via a special child id). +NodeManager configuration includes compile-time configuration directives (which can be set in config.h), runtime global and per-sensor configuration settings (which can be set in your sketch). ### Setup MySensors Since NodeManager has to communicate with the MySensors gateway on your behalf, it has to know how to do it. Place on top of the `config.h` file all the MySensors typical directives you are used to set on top of your sketch so both your sketch AND NodeManager will be able to share the same configuration. For example: @@ -123,38 +123,41 @@ Since NodeManager has to communicate with the MySensors gateway on your behalf, ### Enable/Disable NodeManager's modules -Those NodeManager's directives in the `config.h` file control which module/library/functionality will be made available to your sketch. Enable (e.g. set to 1) only what you need to ensure enough space is left to your custom code. +The next step is to enable NodeManager's additional functionalities and the modules required for your sensors. The directives in the `config.h` file control which module/library/functionality will be made available to your sketch. Enable (e.g. set to 1) only what you need to ensure enough storage is left to your custom code. ~~~c +/*********************************** + * NodeManager configuration + */ + // if enabled, enable debug messages on serial port #define DEBUG 1 // if enabled, enable the capability to power on sensors with the arduino's pins to save battery while sleeping -#define POWER_MANAGER 1 +#define POWER_MANAGER 0 // if enabled, will load the battery manager library to allow the battery level to be reported automatically or on demand -#define BATTERY_MANAGER 1 +#define BATTERY_MANAGER 0 // if enabled, allow modifying the configuration remotely by interacting with the configuration child id -#define REMOTE_CONFIGURATION 1 -// if enabled, persist the configuration settings on EEPROM +#define REMOTE_CONFIGURATION 0 +// if enabled, persist the remote configuration settings on EEPROM #define PERSIST 0 - +// if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage +#define BATTERY_SENSOR 0 // 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 -// if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage -#define BATTERY_SENSOR 1 // 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 #define MODULE_ANALOG_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT -#define MODULE_DIGITAL_INPUT 1 +#define MODULE_DIGITAL_INPUT 0 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY -#define MODULE_DIGITAL_OUTPUT 1 -// Enable this module to use one of the following sensors: SENSOR_SHT21 -#define MODULE_SHT21 0 +#define MODULE_DIGITAL_OUTPUT 0 // Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22, SENSOR_DHT21 #define MODULE_DHT 0 +// Enable this module to use one of the following sensors: SENSOR_SHT21 +#define MODULE_SHT21 0 // Enable this module to use one of the following sensors: SENSOR_SWITCH, SENSOR_DOOR, SENSOR_MOTION -#define MODULE_SWITCH 0 +#define MODULE_SWITCH 1 // Enable this module to use one of the following sensors: SENSOR_DS18B20 #define MODULE_DS18B20 0 // Enable this module to use one of the following sensors: SENSOR_BH1750 @@ -175,9 +178,9 @@ Those NodeManager's directives in the `config.h` file control which module/libra #define MODULE_MQ 0 // Enable this module to use one of the following sensors: SENSOR_MHZ19 #define MODULE_MHZ19 0 -// Enable this module to use one of the following sensors: SENSOR_AM2320 +// Enable this module to use one of the following sensors: SENSOR_AM2320 #define MODULE_AM2320 0 -// Enable this module to use one of the following sensors: SENSOR_TSL2561 +// Enable this module to use one of the following sensors: SENSOR_TSL2561 #define MODULE_TSL2561 0 // Enable this module to use one of the following sensors: SENSOR_PT100 #define MODULE_PT100 0 @@ -188,7 +191,7 @@ Those NodeManager's directives in the `config.h` file control which module/libra ### Installing the dependencies -Some of the modules above rely on third party libraries. Those libraries are not included within NodeManager and have to be installed from the Arduino IDE Library Manager (Sketch -> Include Library -> Manager Libraries). You need to install the library ONLY if the module is enabled: +Some of the modules above rely on third party libraries. Those libraries are not included within NodeManager and have to be installed from the Arduino IDE Library Manager (Sketch -> Include Library -> Manager Libraries) or manually. You need to install the library ONLY if the module is enabled: Module | Required Library ------------- | ------------- @@ -208,7 +211,7 @@ MODULE_BMP280 | https://github.com/adafruit/Adafruit_BMP280_Library ### Configure NodeManager -Node Manager comes with a reasonable default configuration. If you want/need to change its settings, this can be done in your sketch, inside the `before()` function and just before registering your sensors. The following methods are exposed for your convenience: +The next step is to configure NodeManager with settings which will instruct how the node should behave. To do so, go to the main sketch, inside the `before()` function and add call one or more of the functions below just before registering your sensors. The following methods are exposed for your convenience and can be called on the `nodeManager` object already created for you: ~~~c // [10] send the same service message multiple times (default: 1) @@ -219,8 +222,6 @@ Node Manager comes with a reasonable default configuration. If you want/need to void setBatteryMin(float value); // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // [13] after how many sleeping cycles report the battery level to the controller. When reset the battery is always reported (default: -) - void setBatteryReportCycles(int value); // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) void setBatteryReportMinutes(int value); // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) @@ -234,22 +235,21 @@ Node Manager comes with a reasonable default configuration. If you want/need to // [2] Send a battery level report to the controller void batteryReport(); #endif - // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) - void setSleepMode(int value); - void setMode(int value); - int getMode(); - // [4] define for how long the board will sleep (default: 0) - void setSleepTime(int value); - int getSleepTime(); - // [5] define the unit of SLEEP_TIME. It can be SECONDS, MINUTES, HOURS or DAYS (default: MINUTES) - void setSleepUnit(int value); - int getSleepUnit(); - // configure the node's behavior, parameters are mode, time and unit - void setSleep(int value1, int value2, int value3); + // [3] set the duration (in seconds) of a sleep cycle + void setSleepSeconds(int value); + long getSleepSeconds(); + // [4] set the duration (in minutes) of a sleep cycle + void setSleepMinutes(int value); + // [5] set the duration (in hours) of a sleep cycle + void setSleepHours(int value); + // [29] set the duration (in days) of a sleep cycle + void setSleepDays(int value); // [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(); @@ -272,7 +272,7 @@ Node Manager comes with a reasonable default configuration. If you want/need to // [24] manually turn the power on void powerOn(); // [25] manually turn the power off - void powerOff(); + void powerOff(); #endif // [21] set this to true if you want destination node to send ack back to this node (default: false) void setAck(bool value); @@ -310,16 +310,41 @@ Node Manager comes with a reasonable default configuration. If you want/need to void setupInterrupts(); // return the pin from which the last interrupt came int getLastInterruptPin(); + // set the default interval in minutes all the sensors will report their measures. + // If the same function is called on a specific sensor, this will not change the previously set value + // For sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) + void setReportIntervalMinutes(int value); + void setReportIntervalSeconds(int value); ~~~ -For example +### Set reporting intervals and sleeping cycles + +If not instructed differently, the node will stay in awake, all the sensors will report every 10 minutes and the battery level will be automatically reported every 60 minutes. To change those settings, you can call the following functions on the nodeManager object: +Function | Description +------------ | ------------- +setSleepSeconds()/setSleepMinutes()/setSleepHours()/setSleepDays() | the time interval the node will spend in a (smart) sleep cycle +setReportIntervalMinutes() / setReportIntervalSeconds() | the time interval the node will report the measures of all the attached sensors +setBatteryReportMinutes() | the time interval the node will report the battery level + +For example, to put the node to sleep in cycles of 10 minutes: + +~~~c + nodeManager.setSleepMinutes(10); +~~~ + +If you need every sensor to report at a different time interval, you can call `setReportIntervalMinutes()` or `setReportIntervalSeconds()` on the sensor's object. For example to have a DHT sensor reporting every 60 seconds while all the other sensors every 20 minutes: ~~~c - nodeManager.setBatteryMin(1.8); +int id = nodeManager.registerSensor(SENSOR_DHT22,6); +SensorDHT* dht = (SensorDHT*)nodeManager.get(id); +dht->setReportIntervalSeconds(60); +nodeManager.setReportIntervalMinutes(20); ~~~ +Please note, if you configure a sleep cycle, this may have an impact on the reporting interval since the sensor will be able to report its measures ONLY when awake. For example if you set a report interval of 5 minutes and a sleep cycle of 10 minutes, the sensors will report every 10 minutes. + ### Register your sensors -In your sketch, inside the `before()` function and just before calling `nodeManager.before()`, you can register your sensors against NodeManager. The following built-in sensor types are available: +Once configured the node, it is time to tell NodeManager which sensors are attached to the board and where. In your sketch, inside the `before()` function and just before calling `nodeManager.before()`, you can register your sensors against NodeManager. The following built-in sensor types are available. Remember the corresponding module should be enabled in `config.h` for a successful compilation: Sensor type | Description ------------- | ------------- @@ -357,15 +382,15 @@ 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 -To register a sensor simply call the NodeManager instance with the sensory type and the pin the sensor is conncted to. For example: +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 nodeManager.registerSensor(SENSOR_THERMISTOR,A2); - nodeManager.registerSensor(SENSOR_DOOR,3); + nodeManager.registerSensor(SENSOR_DOOR,3,1); ~~~ -Once registered, your job is done. NodeManager will assign a child id automatically, present each sensor for you to the controller, query each sensor and report the value back to the gateway/controller at at the end of each sleep cycle. An optional child id can be provided as a third argument if you want to assign it manually. For actuators (e.g. relays) those can be triggered by sending a `REQ` message to their assigned child id. +Once registered, your job is done. NodeManager will assign a child id automatically if not instructed differently, present each sensor for you to the controller, query each sensor and report the measure back to the gateway/controller. For actuators (e.g. relays) those can be triggered by sending a `REQ` message with the expected type to their assigned child id. -When called, registerSensor returns the child_id of the sensor so you will be able to retrieve it later if needed. If you want to set a child_id manually, this can be passed as third argument to the function. +When called, registerSensor returns the child_id of the sensor so you will be able to retrieve it later if needed. Please note for sensors creating multiple child IDs (like a DHT sensor which creates a temperature and humidity sensor with different IDs), the last id is returned. #### Creating a custom sensor @@ -396,13 +421,14 @@ Each built-in sensor class comes with reasonable default settings. In case you w To do so, use `nodeManager.getSensor(child_id)` which will return a pointer to the sensor. Remeber to cast it to the right class before calling their functions. For example: ~~~c - ((SensorLatchingRelay*)nodeManager.getSensor(2))->setPulseWidth(50); + SensorLatchingRelay* relay = (SensorLatchingRelay*) nodeManager.getSensor(2); + relay->setPulseWidth(50); ~~~ #### Sensor's general configuration -The following methods are available for all the sensors: +The following methods are available for all the sensors and can be called on the object reference as per the example above: ~~~c // [1] where the sensor is attached to (default: not set) void setPin(int value); @@ -424,11 +450,10 @@ The following methods are available for all the sensors: void setSamplesInterval(int value); // [7] if true will report the measure only if different than the previous one (default: false) void setTrackLastValue(bool value); - // [8] if track last value is enabled, force to send an update after the configured number of cycles (default: -1) - void setForceUpdate(int value); - void setForceUpdateCycles(int value); - // [9] if track last value is enabled, force to send an update after the configured number of minutes (default: -1) + // [9] if track last value is enabled, force to send an update after the configured number of minutes void setForceUpdateMinutes(int value); + // [19] if track last value is enabled, force to send an update after the configured number of hours + void setForceUpdateHours(int value); // [10] the value type of this sensor (default: TYPE_INTEGER) void setValueType(int value); int getValueType(); @@ -448,10 +473,12 @@ The following methods are available for all the sensors: int getValueInt(); float getValueFloat(); char* getValueString(); - // [15] After how many cycles the sensor will report back its measure (default: 1 cycle) - void setReportIntervalCycles(int value); - // [16] After how many minutes the sensor will report back its measure (default: 1 cycle) + // [16] After how many minutes the sensor will report back its measure (default: 10 minutes) void setReportIntervalMinutes(int value); + // [17] After how many minutes the sensor will report back its measure (default: 10 minutes) + void setReportIntervalSeconds(int value); + // return true if the report interval has been already configured + bool isReportIntervalConfigured(); // process a remote request void process(Request & request); // return the pin the interrupt is attached to @@ -639,51 +666,43 @@ When `DEBUG` is enabled, detailed information is available through the serial po ### Communicate with NodeManager and its sensors -You can interact with each registered sensor by asking to execute their main tasks by sending to the child id a `REQ` command (or a `SET` for output sensors like relays). For example to request the temperature to node_id 254 and child_id 1: +You can interact with each registered sensor by sending to the child id a `REQ` command (or a `SET` for output sensors like relays). For example to request the temperature to node_id 254 and child_id 1: `254;1;2;0;0;` -To activate a relay connected to the same node, child_id 100: +To activate a relay connected to the same node, child_id 100 we need to send a `SET` command with payload set to 1: `254;100;1;0;2;1` -No need to implement anything on your side since for built-in sensor types this is handled automatically. -Once the node will be sleeping, it will report automatically each measure at the end of every sleep cycle, unless configured otherwise. +No need to implement anything on your side since for built-in sensors this is handled automatically. -NodeManager exposes also a configuration service by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message if successful. -Almost all the functions made available through the API can be called remotely. To do so, the payload must be in the format `[,]` where function_id is the number between square brackets you can find in the description just above each function and, if the function takes and argument, this can be passed along in value_to_set. +NodeManager exposes also a configuration service which is by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message if successful. +Almost all the functions made available through the API can be called remotely. To do so, the payload must be in the format `[,]` where `function_id` is the number between square brackets you can find in the description above and, if the function takes and argument, this can be passed along in `value_to_set`. For example, to request a battery report, find the function you need to call remotely within the documentation: ~~~c // [2] Send a battery level report to the controller void batteryReport(); ~~~ -In this case the function_id will be 2. To request a battery report to the node_id 100, send the following message: +In this case `function_id` will be 2. To request a battery report to the node_id 100, send the following message: `;;;0;;` `100;200;2;0;48;2` -The change the sleep time from e.g. 10 minutes as set in the sketch to 5 minutes: +The change the sleep time to e.g. 10 minutes: ~~~c - // [4] define for how long the board will sleep (default: 0) - void setSleepTime(int value); + // [4] set the duration (in minutes) of a sleep cycle + void setSleepMinutes(int value); ~~~ `;;;0;;,` -`100;200;2;0;48;4,5` +`100;200;2;0;48;4,10` -To ask the node to start sleeping (and waking up based on the previously configured interval): -~~~c - // [3] define the way the node should behave. It can be (0) IDLE (stay awake withtout executing each sensors' loop), (1) SLEEP (go to sleep for the configured interval), (2) WAIT (wait for the configured interval), (3) ALWAYS_ON (stay awake and execute each sensors' loop) - void setSleepMode(int value); -~~~ -`100;200;2;0;48;3,1` - -To wake up a node previously configured as sleeping, send the following just it wakes up next: +To wake up a node previously configured as sleeping, send the following as the node wakes up next: ~~~c // [9] wake up the board void wakeup(); ~~~ `100;200;2;0;48;9` -The same protocol can be used to execute remotely also sensor-specific functions. In this case the message has to be sent to the sensor's child_id, with a V_CUSTOM type of message. For example if you want to collect and average 10 samples for child_id 1: +The same protocol can be used to execute remotely also sensor-specific functions. In this case the message has to be sent to the sensor's child_id, with a `V_CUSTOM` type of message. For example if you want to collect and average 10 samples for child_id 1: ~~~c // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1) void setSamples(int value); @@ -697,14 +716,14 @@ If you want to decrease the temperature offset of a thermistor sensor to -2: ~~~ `100;1;2;0;48;105,-2` -Please note that anything set remotely will NOT persist a reboot apart from those provided to setSleepMode(), setSleepTime() and setSleepUnit() which are saved to the EEPROM (provided `PERSIST` is enabled). +Please note that anything set remotely will NOT persist a reboot apart from those setting the sleep interval which are saved to the EEPROM (provided `PERSIST` is enabled). ## Understanding NodeManager: how it works -A NodeManager object must be created and called from within your sketch during `before()`, `presentation()`, `loop()` and `receive()` to work properly. NodeManager will do the following during each phase: +A NodeManager object is created for you at the beginning of your sketch and its main functions must called from within `before()`, `presentation()`, `loop()` and `receive()` to work properly. NodeManager will do the following during each phase: NodeManager::before(): -* Setup the interrupt pins to wake up the board based on the configured interrupts (e.g. stop sleeping when the pin is connected to ground or wake up and notify when a motion sensor has trigger) +* Setup the interrupt pins to wake up the board based on the configured interrupts * If persistance is enabled, restore from the EEPROM the latest sleeping settings * Call `before()` of each registered sensor @@ -719,16 +738,16 @@ Sensor::setup(): * Call sensor-specific implementation of setup by invoking `onSetup()` to initialize the sensor NodeManager::loop(): -* If all the sensors are powered by an arduino pin, this is set to HIGH +* If all the sensors are powered by an arduino pin, this is turned on * Call `loop()` of each registered sensor -* If all the sensors are powered by an arduino pin, this is set to LOW +* If all the sensors are powered by an arduino pin, this is turned off Sensor::loop(): -* If the sensor is powered by an arduino pin, this is set to HIGH -* For each registered sensor, the sensor-specific `onLoop()` is called. If multiple samples are requested, this is run multiple times. +* If the sensor is powered by an arduino pin, this is set to on +* For each registered sensor, the sensor-specific `onLoop()` is called. If multiple samples are requested, this is run multiple times. `onLoop()` is not intended to send out any message but just sets a new value to a local variable * In case multiple samples have been collected, the average is calculated * A message is sent to the gateway with the calculated value. Depending on the configuration, this is not sent if it is the same as the previous value or sent anyway after a given number of cycles. These functionalies are not sensor-specific and common to all the sensors inheriting from the `Sensor` class. -* If the sensor is powered by an arduino pin, this is set to LOW +* If the sensor is powered by an arduino pin, this is turned off NodeManager::receive(): * Receive a message from the radio network @@ -742,10 +761,10 @@ NodeManager::process(): Sensor::process(): * Process a sensor-generic incoming remote configuration request -* Calls onProcess() for sensor-specific 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 +* 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. @@ -760,7 +779,7 @@ Set battery minimum and maxium voltage. This will be used to calculate the level Instruct the board to sleep for 10 minutes at each cycle: ~~~c - nodeManager.setSleep(SLEEP,10,MINUTES); + nodeManager.setSleepMinutes(10); ~~~ Configure a wake up pin. When pin 3 is connected to ground, the board will stop sleeping: @@ -781,7 +800,7 @@ Register a thermistor sensor attached to pin A2. NodeManager will then send the nodeManager.registerSensor(SENSOR_THERMISTOR,A2); ~~~ -Register a SHT21 temperature/humidity sensor; since using i2c for communicating with the sensor, the pins used are implicit (A4 and A5). NodeManager will then send the temperature and the humidity to the controller at the end of each sleeping cycle: +Register a SHT21 temperature/humidity sensor; since using I2C for communicating with the sensor, the pins used are implicit (A4 and A5). NodeManager will then send the temperature and the humidity to the controller at the end of each sleeping cycle: ~~~c nodeManager.registerSensor(SENSOR_SHT21); @@ -820,9 +839,9 @@ Register a latching relay connecting to pin 6 (set) and pin 7 (unset): The following sketch can be used to report the temperature and the light level based on a thermistor and LDR sensors attached to two analog pins of the arduino board (A1 and A2). Both the thermistor and the LDR are connected to ground on one side and to vcc via a resistor on the other so to measure the voltage drop across each of them through the analog pins. -The sensor will be put to sleep after startup and will report both the measures every 10 minutes. NodeManager will take care of presenting the sensors, managing the sleep cycle, reporting the battery level every 10 cycles and report the measures in the appropriate format. This sketch requires MODULE_ANALOG_INPUT enabled in the global config.h file. +The sensor will be put to sleep after startup and will report both the measures every 10 minutes. NodeManager will take care of presenting the sensors, managing the sleep cycle, reporting the battery level every hour and report the measures in the appropriate format. This sketch requires MODULE_ANALOG_INPUT enabled in the global config.h file. -Even if the sensor is sleeping most of the time, it can be potentially woke up by sending a V_CUSTOM message with a WAKEUP payload to NodeManager service child id (200 by default) just after having reported its heartbeat. At this point the node will report AWAKE and the user can interact with it by e.g. sending REQ messages to its child IDs, changing the duration of a sleep cycle with a V_CUSTOM message to the NodeManager service child id, etc. +Even if the sensor is sleeping most of the time, it can be potentially woke up by sending a V_CUSTOM message to NodeManager service child id (200 by default) just after having reported its heartbeat. At this point the node will report awake and the user can interact with it by e.g. sending REQ messages to its child IDs, changing the duration of a sleep cycle, etc. ~~~c /* @@ -856,7 +875,8 @@ void before() { /* * Register below your sensors */ - nodeManager.setSleep(SLEEP,10,MINUTES); + nodeManager.setSleepMinutes(10); + nodeManager.setReportIntervalMinutes(10); nodeManager.registerSensor(SENSOR_THERMISTOR,A1); nodeManager.registerSensor(SENSOR_LDR,A2); /* @@ -938,7 +958,7 @@ void before() { /* * Register below your sensors */ - nodeManager.setSleep(SLEEP,60,MINUTES); + nodeManager.setSleepHours(1); nodeManager.registerSensor(SENSOR_MOTION,3); /* @@ -1026,7 +1046,7 @@ void before() { */ nodeManager.setBatteryMin(1.8); nodeManager.setBatteryMax(3.2); - nodeManager.setSleep(SLEEP,5,MINUTES); + nodeManager.setSleepMinutes(5); nodeManager.registerSensor(SENSOR_LATCHING_RELAY,6); /* @@ -1117,7 +1137,8 @@ void before() { * Register below your sensors */ analogReference(DEFAULT); - nodeManager.setSleep(SLEEP,10,MINUTES); + nodeManager.setSleepMinutes(10); + nodeManager.setReportIntervalMinutes(10); int rain = nodeManager.registerSensor(SENSOR_ANALOG_INPUT,A1); int soil = nodeManager.registerSensor(SENSOR_ANALOG_INPUT,A2); @@ -1209,11 +1230,11 @@ Create a branch for the fix/feature you want to work on and apply changes to the * Create and switch to a new branch (give it a significant name, e.g. fix/enum-sensors): `git checkout -b ` * Do any required change to the code * Include all the files changed for your commit: `git add .` -* Commit the changes: `git commit -m"Use enum instead of define for defining each sensor #121"` * Ensure both the main sketch and the config.h file do not present any change +* Commit the changes: `git commit -m"Use enum instead of define for defining each sensor #121"` * Push the branch with the changes to your repository: `git push origin ` * Visit `https://github.com//NodeManager/branches` and click the "New pull request" button just aside your newly created branch -* Fill in the request with a significant title and description and select the "development" branch from the main repository to be compared against your branch +* Fill in the request with a significant title and description and select the "development" branch from the main repository to be compared against your branch. Ensure there is one or more issues the pull request will fix and make it explicit within the description * Submit the request and start the discussion * Any additional commits to your branch which will be presented within the same pull request * When the pull request is merged, delete your working branch: `git branch -D ` @@ -1285,3 +1306,22 @@ v1.5: * Added receiveTime() wrapper in the main sketch * Fixed the logic for output sensors * Added common gateway settings in config.h + +v1.6: +* Introduced new remote API to allow calling all NodeManager's and sensors' functions remotely +* Decoupled reporting intervals from sleeping cycles +* All intervals (measure/battery reports) are now time-based +* Added support for BMP280 temperature and pressure sensor +* Added support for RS485 serial transport +* Added support for TSL2561 light sensor +* Added support for DHT21 temperature/humidity sensor +* Added support for AM2320 temperature/humidity sensor +* Added support for PT100 high temperature sensor +* Added support for MH-Z19 CO2 sensor +* Added buil-in rain and soil moisture analog sensors +* SensorRainGauge now supports sleep mode +* SensorSwitch now supports awake mode +* SensorLatchingRealy now handles automatically both on and off commands +* SensorMQ now depends on its own module +* Added automatic off capability (safeguard) to SensorDigitalOutput +* Any sensor can now access all NodeManager's functions From df306b8dc4a5029addddc524625d2d691c555010 Mon Sep 17 00:00:00 2001 From: user2684 Date: Sat, 29 Jul 2017 14:58:14 +0200 Subject: [PATCH 23/33] DHT library update and fixes (#175, #145, #176, #148) --- NodeManager.cpp | 23 +++++++++++------------ NodeManager.h | 5 ++--- README.md | 5 +++-- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 80129873..d437b12d 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -1268,22 +1268,23 @@ SensorDHT::SensorDHT(NodeManager* node_manager, int child_id, int pin, DHT* dht, // what to do during before void SensorDHT::onBefore() { - // initialize the dht library - _dht->begin(); } // what to do during setup void SensorDHT::onSetup() { + // initialize the dht library + _dht->setup(_pin,_dht_type); } // what to do during loop void SensorDHT::onLoop() { + wait(_dht->getMinimumSamplingPeriod()); + _dht->readSensor(true); // temperature sensor if (_sensor_type == SensorDHT::TEMPERATURE) { // read the temperature - float temperature = _dht->readTemperature(); - // convert it - temperature = _node_manager->celsiusToFahrenheit(temperature); + float temperature = _dht->getTemperature(); + if (! _node_manager->getIsMetric()) temperature = _dht->toFahrenheit(temperature); #if DEBUG == 1 Serial.print(F("DHT I=")); Serial.print(_child_id); @@ -1296,8 +1297,7 @@ void SensorDHT::onLoop() { // humidity sensor else if (_sensor_type == SensorDHT::HUMIDITY) { // read humidity - float humidity = _dht->readHumidity(); - if (isnan(humidity)) return; + float humidity = _dht->getHumidity(); #if DEBUG == 1 Serial.print(F("DHT I=")); Serial.print(_child_id); @@ -2965,12 +2965,11 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { else if (sensor_type == SENSOR_LATCHING_RELAY) return registerSensor(new SensorLatchingRelay(this,child_id, pin)); #endif #if MODULE_DHT == 1 - else if (sensor_type == SENSOR_DHT11 || sensor_type == SENSOR_DHT22 || sensor_type == SENSOR_DHT21) { + else if (sensor_type == SENSOR_DHT11 || sensor_type == SENSOR_DHT22) { int dht_type; - if (sensor_type == SENSOR_DHT11) dht_type = DHT11; - else if (sensor_type == SENSOR_DHT21) dht_type = DHT21; - else if (sensor_type == SENSOR_DHT22) dht_type = DHT22; - DHT* dht = new DHT(pin,dht_type); + if (sensor_type == SENSOR_DHT11) dht_type = DHT::DHT11; + else if (sensor_type == SENSOR_DHT22) dht_type = DHT::DHT22; + DHT* dht = new DHT(); // register temperature sensor registerSensor(new SensorDHT(this,child_id,pin,dht,SensorDHT::TEMPERATURE,dht_type)); // register humidity sensor diff --git a/NodeManager.h b/NodeManager.h index 8728831f..53144830 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -114,7 +114,7 @@ #ifndef MODULE_DIGITAL_OUTPUT #define MODULE_DIGITAL_OUTPUT 0 #endif -// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22, SENSOR_DHT21 +// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 #ifndef MODULE_DHT #define MODULE_DHT 0 #endif @@ -221,7 +221,6 @@ enum supported_sensors { // DHT11/DHT22 sensors, return temperature/humidity based on the attached DHT sensor SENSOR_DHT11, SENSOR_DHT22, - SENSOR_DHT21, #endif #if MODULE_SHT21 == 1 // SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor @@ -827,7 +826,7 @@ class SensorDHT: public Sensor { const static int HUMIDITY = 1; protected: DHT* _dht; - int _dht_type = DHT11; + int _dht_type; float _offset = 0; int _sensor_type = 0; }; diff --git a/README.md b/README.md index daf0971c..15d2b571 100755 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ The next step is to enable NodeManager's additional functionalities and the modu #define MODULE_DIGITAL_INPUT 0 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY #define MODULE_DIGITAL_OUTPUT 0 -// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22, SENSOR_DHT21 +// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 #define MODULE_DHT 0 // Enable this module to use one of the following sensors: SENSOR_SHT21 #define MODULE_SHT21 0 @@ -196,7 +196,7 @@ Some of the modules above rely on third party libraries. Those libraries are not Module | Required Library ------------- | ------------- MODULE_SHT21 | https://github.com/SodaqMoja/Sodaq_SHT2x -MODULE_DHT | https://github.com/adafruit/DHT-sensor-library +MODULE_DHT | https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/DHT MODULE_DS18B20 | https://github.com/milesburton/Arduino-Temperature-Control-Library MODULE_BH1750 | https://github.com/claws/BH1750 MODULE_MLX90614 | https://github.com/adafruit/Adafruit-MLX90614-Library @@ -1325,3 +1325,4 @@ v1.6: * SensorMQ now depends on its own module * Added automatic off capability (safeguard) to SensorDigitalOutput * Any sensor can now access all NodeManager's functions +* DHT sensor now using MySensors' DHT library \ No newline at end of file From 1bd4de7e6a0b1085c2e6ca818aa1176dc407fed7 Mon Sep 17 00:00:00 2001 From: user2684 Date: Sat, 29 Jul 2017 15:29:04 +0200 Subject: [PATCH 24/33] Using sleep() or wait() depending on the node's status (#150) --- NodeManager.cpp | 27 ++++++++++++++++++++------- NodeManager.h | 5 +++++ README.md | 4 ++++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index d437b12d..888f26ff 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -383,7 +383,7 @@ void Sensor::loop(const MyMessage & message) { if (_value_type == TYPE_INTEGER) total += (float)_value_int; else if (_value_type == TYPE_FLOAT) total += _value_float; // wait between samples - if (_samples_interval > 0) wait(_samples_interval); + if (_samples_interval > 0) _node_manager->sleepOrWait(_samples_interval); } // process the result and send a response back if (_value_type == TYPE_INTEGER && total > -1) { @@ -1117,7 +1117,7 @@ void SensorDigitalOutput::setStatus(int value) { } _setStatus(value); // wait if needed for relay drawing a lot of current - if (_wait_after_set > 0) wait(_wait_after_set); + if (_wait_after_set > 0) _node_manager->sleepOrWait(_wait_after_set); // store the new status so it will be sent to the controller _status = value; _value_int = value; @@ -1225,7 +1225,7 @@ void SensorLatchingRelay::_setStatus(int value) { // set the value digitalWrite(pin, _on_value); // wait for the given time before restoring the value to the original value after the pulse - wait(_pulse_width); + _node_manager->sleepOrWait(_pulse_width); digitalWrite(pin, ! _on_value); #if DEBUG == 1 Serial.print(F("LAT I=")); @@ -1278,7 +1278,7 @@ void SensorDHT::onSetup() { // what to do during loop void SensorDHT::onLoop() { - wait(_dht->getMinimumSamplingPeriod()); + _node_manager->sleepOrWait(_dht->getMinimumSamplingPeriod()); _dht->readSensor(true); // temperature sensor if (_sensor_type == SensorDHT::TEMPERATURE) { @@ -1472,7 +1472,7 @@ void SensorSwitch::onProcess(Request & request) { // what to do when receiving an interrupt void SensorSwitch::onInterrupt() { // wait to ensure the the input is not floating - if (_debounce > 0) wait(_debounce); + if (_debounce > 0) _node_manager->sleepOrWait(_debounce); // read the value of the pin int value = digitalRead(_pin); // process the value @@ -1487,7 +1487,7 @@ void SensorSwitch::onInterrupt() { #endif _value_int = value; // allow the signal to be restored to its normal value - if (_trigger_time > 0) wait(_trigger_time); + if (_trigger_time > 0) _node_manager->sleepOrWait(_trigger_time); } else { // invalid _value_int = -1; @@ -3402,7 +3402,7 @@ long NodeManager::getTimestamp() { // request the time to the controller requestTime(); // keep asking every 1 second - wait(1000); + sleepOrWait(1000); retries--; } return _timestamp; @@ -3475,6 +3475,7 @@ void NodeManager::process(Request & request) { case 26: unRegisterSensor(request.getValueInt()); break; case 27: saveToMemory(0,request.getValueInt()); break; case 28: setInterruptMinDelta(request.getValueInt()); break; + case 30: setSleepOrWait(request.getValueInt()); break; default: return; } _send(_msg.set(function)); @@ -3629,6 +3630,18 @@ void NodeManager::setReportIntervalSeconds(int value) { _report_interval_seconds = value; } +// if set and when the board is battery powered, sleep() is always called instead of wait() +void NodeManager::setSleepOrWait(bool value) { + _sleep_or_wait = value; +} + +// sleep if the node is a battery powered or wait if it is not for the given number of milliseconds +void NodeManager::sleepOrWait(long value) { + // if the node is sleeping, sleep-or-wait is enabled and we need to sleep for a decent amount of time, call sleep() otherwise wait() + if (isSleepingNode() && _sleep_or_wait && value > 200) sleep(value); + else wait(value); +} + // handle an interrupt void NodeManager::_onInterrupt_1() { long now = millis(); diff --git a/NodeManager.h b/NodeManager.h index 53144830..ce151be8 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -1421,6 +1421,10 @@ class NodeManager { // For sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) void setReportIntervalMinutes(int value); void setReportIntervalSeconds(int value); + // [30] if set and when the board is battery powered, sleep() is always called instead of wait() (default: true) + void setSleepOrWait(bool value); + // sleep if the node is a battery powered or wait if it is not for the given number of milliseconds + void sleepOrWait(long value); // hook into the main sketch functions void before(); void presentation(); @@ -1471,6 +1475,7 @@ class NodeManager { bool _get_controller_config = true; int _is_metric = 1; int _report_interval_seconds = 10*60; + bool _sleep_or_wait = true; void _loadConfig(); void _saveConfig(); }; diff --git a/README.md b/README.md index 15d2b571..277c26af 100755 --- a/README.md +++ b/README.md @@ -315,6 +315,10 @@ The next step is to configure NodeManager with settings which will instruct how // For sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) void setReportIntervalMinutes(int value); void setReportIntervalSeconds(int value); + // [30] if set and when the board is battery powered, sleep() is always called instead of wait() (default: true) + void setSleepOrWait(bool value); + // sleep if the node is a battery powered or wait if it is not for the given number of milliseconds + void sleepOrWait(long value); ~~~ ### Set reporting intervals and sleeping cycles From ac53650fab0ae02e22711ee12e8262e3058ca09c Mon Sep 17 00:00:00 2001 From: user2684 Date: Sat, 29 Jul 2017 15:59:17 +0200 Subject: [PATCH 25/33] Updated config.h with additional default entries (#143) --- NodeManager.h | 8 ++++++++ config.h | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/NodeManager.h b/NodeManager.h index ce151be8..6cde1c3f 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -97,6 +97,14 @@ #ifndef MAX_SENSORS #define MAX_SENSORS 10 #endif +// define default sketch name and version +#ifndef SKETCH_NAME + #define SKETCH_NAME "NodeManager" +#endif +#ifndef SKETCH_VERSION + #define SKETCH_VERSION "1.0" +#endif + /*********************************** Default module settings diff --git a/config.h b/config.h index 5d3945b5..0259c24d 100755 --- a/config.h +++ b/config.h @@ -16,6 +16,7 @@ #define MY_BAUD_RATE 9600 //#define MY_DEBUG //#define MY_NODE_ID 100 +//#define MY_SMART_SLEEP_WAIT_DURATION_MS 500 // NRF24 radio settings #define MY_RADIO_NRF24 @@ -44,6 +45,18 @@ //#define MY_RS485_MAX_MESSAGE_LENGTH 40 //#define MY_RS485_HWSERIAL Serial1 +// Message signing settings +//#define MY_SIGNING_SOFT +//#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 +//#define MY_SIGNING_REQUEST_SIGNATURES +//#define MY_SIGNING_ATSHA204 + +// OTA Firmware update settings +//#define MY_OTA_FIRMWARE_FEATURE +//#define OTA_WAIT_PERIOD 300 +//#define FIRMWARE_MAX_REQUESTS 2 +//#define MY_OTA_RETRY 2 + /********************************** * MySensors gateway configuration */ From 8bf0952d0fb74b4f6e6e39869da71eaed083ef2a Mon Sep 17 00:00:00 2001 From: user2684 Date: Sat, 29 Jul 2017 16:16:35 +0200 Subject: [PATCH 26/33] Added capability to reboot by setting reboot pin (#133) --- NodeManager.cpp | 32 ++++++++++++++++++++++++++------ NodeManager.h | 3 +++ README.md | 29 +++++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 888f26ff..47ce6d43 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -3220,6 +3220,15 @@ void NodeManager::before() { Serial.print(F("NodeManager v")); Serial.println(VERSION); #endif + // setup the reboot pin if needed + if (_reboot_pin > -1) { + #if DEBUG == 1 + Serial.print("REB P="); + Serial.println(_reboot_pin); + #endif + pinMode(_reboot_pin, OUTPUT); + digitalWrite(_reboot_pin, HIGH); + } // print out MySensors' library capabilities #if DEBUG == 1 Serial.print(F("LIB R=")); @@ -3476,6 +3485,7 @@ void NodeManager::process(Request & request) { case 27: saveToMemory(0,request.getValueInt()); break; case 28: setInterruptMinDelta(request.getValueInt()); break; case 30: setSleepOrWait(request.getValueInt()); break; + case 31: setRebootPin(request.getValueInt()); break; default: return; } _send(_msg.set(function)); @@ -3519,12 +3529,17 @@ void NodeManager::reboot() { #if DEBUG == 1 Serial.println(F("REBOOT")); #endif - // Software reboot with watchdog timer. Enter Watchdog Configuration mode: - WDTCSR |= (1< -1) { + // reboot the board through the reboot pin which is connected to RST by setting it to low + digitalWrite(_reboot_pin, LOW); + } else { + // Software reboot with watchdog timer. Enter Watchdog Configuration mode: + WDTCSR |= (1< Date: Sat, 29 Jul 2017 16:22:29 +0200 Subject: [PATCH 27/33] Added ability to turn the ADC off so save power (#97) --- NodeManager.cpp | 9 +++++++++ NodeManager.h | 2 ++ README.md | 6 ++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 47ce6d43..0e0eca16 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -3486,6 +3486,7 @@ void NodeManager::process(Request & request) { case 28: setInterruptMinDelta(request.getValueInt()); break; case 30: setSleepOrWait(request.getValueInt()); break; case 31: setRebootPin(request.getValueInt()); break; + case 32: setADCOff(); break; default: return; } _send(_msg.set(function)); @@ -3655,6 +3656,14 @@ void NodeManager::setRebootPin(int value) { _reboot_pin = value; } +// turn the ADC off so to save 0.2 mA +void NodeManager::setADCOff() { + // Disable the ADC by setting the ADEN bit (bit 7) to zero + ADCSRA = ADCSRA & B01111111; + // Disable the analog comparator by setting the ACD bit (bit 7) to one + ACSR = B10000000; +} + // sleep if the node is a battery powered or wait if it is not for the given number of milliseconds void NodeManager::sleepOrWait(long value) { // if the node is sleeping, sleep-or-wait is enabled and we need to sleep for a decent amount of time, call sleep() otherwise wait() diff --git a/NodeManager.h b/NodeManager.h index e9c3f3b1..883b3209 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -1435,6 +1435,8 @@ class NodeManager { void sleepOrWait(long value); // [31] set which pin is connected to RST of the board to reboot the board when requested. If not set the software reboot is used instead (default: -1) void setRebootPin(int value); + // [32] turn the ADC off so to save 0.2 mA + void setADCOff(); // hook into the main sketch functions void before(); void presentation(); diff --git a/README.md b/README.md index 2b277200..122990f4 100755 --- a/README.md +++ b/README.md @@ -340,10 +340,12 @@ The next step is to configure NodeManager with settings which will instruct how void setReportIntervalSeconds(int value); // [30] if set and when the board is battery powered, sleep() is always called instead of wait() (default: true) void setSleepOrWait(bool value); - // [31] set which pin is connected to RST of the board to reboot the board when requested. If not set the software reboot is used instead (default: -1) - void setRebootPin(int value); // sleep if the node is a battery powered or wait if it is not for the given number of milliseconds void sleepOrWait(long value); + // [31] set which pin is connected to RST of the board to reboot the board when requested. If not set the software reboot is used instead (default: -1) + void setRebootPin(int value); + // [32] turn the ADC off so to save 0.2 mA + void setADCOff(); ~~~ ### Set reporting intervals and sleeping cycles From 65b848b9c9361e78dff07e4438a36903e8a8df04 Mon Sep 17 00:00:00 2001 From: user2684 Date: Sat, 29 Jul 2017 19:38:45 +0200 Subject: [PATCH 28/33] Added support for PWM output with SensorDimmer (#118) --- NodeManager.cpp | 127 +++++++++++++++++++++++++++++++++++++++++++++++- NodeManager.h | 54 +++++++++++++++++++- NodeManager.ino | 1 + README.md | 17 ++++++- config.h | 2 + 5 files changed, 197 insertions(+), 4 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 0e0eca16..bcee7dba 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -2542,6 +2542,10 @@ void SensorMHZ19::onProcess(Request & request) { _send(_msg_service.set(function)); } +// what to do when receiving an interrupt +void SensorMHZ19::onInterrupt() { +} + #endif /* @@ -2620,6 +2624,10 @@ void SensorAM2320::onReceive(const MyMessage & message) { // what to do when receiving a remote message void SensorAM2320::onProcess(Request & request) { } + +// what to do when receiving an interrupt +void SensorAM2320::onInterrupt() { +} #endif /* @@ -2750,6 +2758,10 @@ void SensorTSL2561::onProcess(Request & request) { } _send(_msg_service.set(function)); } + +// what to do when receiving an interrupt +void SensorTSL2561::onInterrupt() { +} #endif /* @@ -2764,7 +2776,7 @@ SensorPT100::SensorPT100(NodeManager* node_manager, int child_id, int pin): Sens setValueType(TYPE_FLOAT); } -//// setter/getter +// setter/getter void SensorPT100::setVoltageRef(float value) { _voltageRef = value; } @@ -2784,7 +2796,6 @@ void SensorPT100::onSetup() { void SensorPT100::onLoop() { // read the PT100 sensor int temperature = _PT100->readTemperature(_pin); - #if DEBUG == 1 Serial.print(F("PT100 I=")); Serial.print(_child_id); @@ -2809,6 +2820,112 @@ void SensorPT100::onProcess(Request & request) { } _send(_msg_service.set(function)); } + +// what to do when receiving an interrupt +void SensorPT100::onInterrupt() { +} +#endif + +/* + SensorDimmer +*/ + +#if MODULE_DIMMER == 1 +// contructor +SensorDimmer::SensorDimmer(NodeManager* node_manager, int child_id, int pin): Sensor(node_manager, child_id, pin) { + // set presentation, type and value type + setPresentation(S_DIMMER); + setType(V_PERCENTAGE); +} + +// setter/getter +void SensorDimmer::setEasing(int value) { + _easing = value; +} +void SensorDimmer::setDuration(int value) { + _duration = value*1000; +} +void SensorDimmer::setStepDuration(int value) { + _duration = value; +} + +// what to do during before +void SensorDimmer::onBefore() { + pinMode(_pin, OUTPUT); +} + +// what to do during setup +void SensorDimmer::onSetup() { +} + +// what to do during loop +void SensorDimmer::onLoop() { +} + +// what to do as the main task when receiving a message +void SensorDimmer::onReceive(const MyMessage & message) { + if (message.getCommand() == C_SET) { + int percentage = message.getInt(); + // normalize the provided percentage + if (percentage < 0) percentage = 0; + if (percentage > 100) percentage = 100; + fadeTo(percentage); + _value_int = percentage; + } + if (message.getCommand() == C_REQ) { + // return the current status + _value_int = _percentage; + } +} + +// what to do when receiving a remote message +void SensorDimmer::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setEasing(request.getValueInt()); break; + case 102: setDuration(request.getValueInt()); break; + case 103: setStepDuration(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); +} + +// what to do when receiving an interrupt +void SensorDimmer::onInterrupt() { +} + +// fade to the provided value +void SensorDimmer::fadeTo(int target_percentage) { + #if DEBUG == 1 + Serial.print(F("DIM I=")); + Serial.print(_child_id); + Serial.print(F(" V=")); + Serial.println(target_percentage); + #endif + // count how many steps we need to do + int steps = _duration / _step_duration; + // for each step + for (int current_step = 1; current_step <= steps; current_step++) { + // calculate the delta between the target value and the current + int delta = target_percentage - _percentage; + // calculate the smooth transition and adjust it in the 0-255 range + int value_to_write = (int)(_getEasing(current_step,_percentage,delta,steps) / 100. * 255); + // write to the PWM output + analogWrite(_pin,value_to_write); + // wait at the end of this step + wait(_step_duration); + } + _percentage = target_percentage; +} + +// for smooth transitions. t: current time, b: beginning value, c: change in value, d: duration +float SensorDimmer::_getEasing(float t, float b, float c, float d) { + if (_easing == EASE_INSINE) return -c * cos(t/d * (M_PI/2)) + c + b; + else if (_easing == EASE_OUTSINE) return c * sin(t/d * (M_PI/2)) + b; + else if (_easing == EASE_INOUTSINE) return -c/2 * (cos(M_PI*t/d) - 1) + b; + else return c*t/d + b; +} + #endif /******************************************* @@ -3149,6 +3266,12 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { // register temperature sensor 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)); + } #endif else { #if DEBUG == 1 diff --git a/NodeManager.h b/NodeManager.h index 883b3209..a0f94b82 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -190,6 +190,10 @@ #ifndef MODULE_BMP280 #define MODULE_BMP280 0 #endif +// Enable this module to use one of the following sensors: SENSOR_DIMMER +#ifndef MODULE_DIMMER + #define MODULE_DIMMER 0 +#endif /*********************************** Supported Sensors @@ -299,6 +303,10 @@ enum supported_sensors { // BMP280 sensor, return temperature and pressure SENSOR_BMP280, #endif + #if MODULE_DIMMER == 1 + // Generic dimmer sensor used to drive a pwm output + SENSOR_DIMMER, + #endif }; /*********************************** @@ -376,6 +384,9 @@ enum supported_sensors { #include #include #endif +#if MODULE_DIMMER == 1 + #include +#endif /******************************************************************* Classes @@ -1222,13 +1233,14 @@ class SensorMHZ19: public Sensor { SensorMHZ19(NodeManager* node_manager, int child_id, int pin); // set the pins for RX and TX of the SoftwareSerial (default: Rx=6, Tx=7) void setRxTx(int rxpin, int txpin); + int readCO2(); // 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); - int readCO2(); + void onInterrupt(); protected: SoftwareSerial* _ser; int _tx_pin = 6; @@ -1249,6 +1261,7 @@ class SensorAM2320: 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; @@ -1279,6 +1292,7 @@ class SensorTSL2561: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); // constants const static int ADDR_FLOAT = 0; const static int ADDR_LOW = 1; @@ -1316,12 +1330,50 @@ class SensorPT100: public Sensor { void onLoop(); void onReceive(const MyMessage & message); void onProcess(Request & request); + void onInterrupt(); protected: DFRobotHighTemperature* _PT100; float _voltageRef = 3.3; }; #endif +/* + SensorPT100 +*/ +#if MODULE_DIMMER == 1 +class SensorDimmer: public Sensor { + public: + SensorDimmer(NodeManager* node_manager, int child_id, int pin); + // [101] set the effect to use for a smooth transition, can be one of SensorDimmer::EASE_LINEAR, SensorDimmer::EASE_INSINE, SensorDimmer::EASE_OUTSINE, SensorDimmer::EASE_INOUTSINE (default: EASE_LINEAR) + void setEasing(int value); + // [102] the duration of entire the transition in seconds (default: 1) + void setDuration(int value); + // [103] the duration of a single step of the transition in milliseconds (default: 100) + void setStepDuration(int value); + // fade the output from the current value to the target provided in the range 0-100 + void fadeTo(int value); + enum easing { + EASE_LINEAR, + EASE_INSINE, + EASE_OUTSINE, + EASE_INOUTSINE, + }; + // 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: + int _percentage = 0; + int _easing = EASE_LINEAR; + int _duration = 1000; + int _step_duration = 100; + float _getEasing(float t, float b, float c, float d); +}; +#endif + /*************************************** NodeManager: manages all the aspects of the node */ diff --git a/NodeManager.ino b/NodeManager.ino index 3df7d474..e42bf435 100755 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -1,3 +1,4 @@ + /* NodeManager is intended to take care on your behalf of all those common tasks a MySensors node has to accomplish, speeding up the development cycle of your projects. NodeManager includes the following main components: diff --git a/README.md b/README.md index 122990f4..e5cb7f16 100755 --- a/README.md +++ b/README.md @@ -209,7 +209,8 @@ The next step is to enable NodeManager's additional functionalities and the modu #define MODULE_PT100 0 // Enable this module to use one of the following sensors: SENSOR_BMP280 #define MODULE_BMP280 0 - +// Enable this module to use one of the following sensors: SENSOR_DIMMER +#define MODULE_DIMMER 0 ~~~ ### Installing the dependencies @@ -412,6 +413,7 @@ SENSOR_TSL2561 | TSL2561 sensor, return light in lux SENSOR_AM2320 | AM2320 sensors, return temperature/humidity based on the attached AM2320 sensor 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 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 @@ -688,6 +690,18 @@ Each sensor class can expose additional methods. void setAddress(int value); ~~~ +* SensorDimmer +~~~c + // [101] set the effect to use for a smooth transition, can be one of SensorDimmer::EASE_LINEAR, SensorDimmer::EASE_INSINE, SensorDimmer::EASE_OUTSINE, SensorDimmer::EASE_INOUTSINE (default: EASE_LINEAR) + void setEasing(int value); + // [102] the duration of entire the transition in seconds (default: 1) + void setDuration(int value); + // [103] the duration of a single step of the transition in milliseconds (default: 100) + void setStepDuration(int value); + // fade the output from the current value to the target provided in the range 0-100 + void fadeTo(int value); +~~~ + ### Upload your sketch Upload your sketch to your arduino board as you are used to. @@ -1350,6 +1364,7 @@ v1.6: * Added support for PT100 high temperature sensor * Added support for MH-Z19 CO2 sensor * Added buil-in rain and soil moisture analog sensors +* Added support for generic dimmer sensor (PWM output) * SensorRainGauge now supports sleep mode * SensorSwitch now supports awake mode * SensorLatchingRealy now handles automatically both on and off commands diff --git a/config.h b/config.h index 0259c24d..c367a80a 100755 --- a/config.h +++ b/config.h @@ -163,6 +163,8 @@ #define MODULE_PT100 0 // Enable this module to use one of the following sensors: SENSOR_BMP280 #define MODULE_BMP280 0 +// Enable this module to use one of the following sensors: SENSOR_DIMMER +#define MODULE_DIMMER 0 #endif From 303806a23c5acf4f8ca3d3797a9f41290b2975b0 Mon Sep 17 00:00:00 2001 From: user2684 Date: Sun, 30 Jul 2017 16:22:05 +0200 Subject: [PATCH 29/33] Added radio signal sensor (#99) --- NodeManager.cpp | 114 +++++++++++++++++++++++++++++++++++++++++++++--- NodeManager.h | 57 ++++++++++++++++++++---- NodeManager.ino | 1 - README.md | 66 ++++++++++++++++++++-------- config.h | 4 +- 5 files changed, 208 insertions(+), 34 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index bcee7dba..e996aab2 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -303,16 +303,27 @@ char* Sensor::getValueString() { return _last_value_string; } +// After how many seconds the sensor will report back its measure +void Sensor::setReportIntervalSeconds(int value) { + _report_timer->start(value,SECONDS); +} + // After how many minutes the sensor will report back its measure void Sensor::setReportIntervalMinutes(int value) { _report_timer->start(value,MINUTES); } -// After how many seconds the sensor will report back its measure -void Sensor::setReportIntervalSeconds(int value) { - _report_timer->start(value,SECONDS); +// After how many minutes the sensor will report back its measure +void Sensor::setReportIntervalHours(int value) { + _report_timer->start(value,HOURS); +} + +// After how many minutes the sensor will report back its measure +void Sensor::setReportIntervalDays(int value) { + _report_timer->start(value,DAYS); } + // return true if the report interval has been already configured bool Sensor::isReportIntervalConfigured() { return _report_timer->isConfigured(); @@ -471,6 +482,8 @@ void Sensor::process(Request & request) { #endif case 16: setReportIntervalMinutes(request.getValueInt()); break; case 17: setReportIntervalSeconds(request.getValueInt()); break; + case 19: setReportIntervalHours(request.getValueInt()); break; + case 20: setReportIntervalDays(request.getValueInt()); break; case 18: setForceUpdateHours(request.getValueInt()); break; default: return; } @@ -2957,9 +2970,18 @@ int NodeManager::getRetries() { void NodeManager::setBatteryMax(float value) { _battery_max = value; } + void NodeManager::setBatteryReportSeconds(int value) { + _battery_report_timer.set(value,SECONDS); + } void NodeManager::setBatteryReportMinutes(int value) { _battery_report_timer.set(value,MINUTES); } + void NodeManager::setBatteryReportHours(int value) { + _battery_report_timer.set(value,HOURS); + } + void NodeManager::setBatteryReportDays(int value) { + _battery_report_timer.set(value,DAYS); + } void NodeManager::setBatteryInternalVcc(bool value) { _battery_internal_vcc = value; } @@ -2973,6 +2995,7 @@ int NodeManager::getRetries() { _battery_report_with_interrupt = value; } #endif + void NodeManager::setSleepSeconds(int value) { // set the status to AWAKE if the time provided is 0, SLEEP otherwise if (value == 0) _status = AWAKE; @@ -3354,7 +3377,9 @@ void NodeManager::before() { } // print out MySensors' library capabilities #if DEBUG == 1 - Serial.print(F("LIB R=")); + Serial.print(F("LIB V=")); + Serial.print(MYSENSORS_LIBRARY_VERSION); + Serial.print(F(" R=")); Serial.print(MY_CAP_RADIO); #ifdef MY_CAP_ENCR Serial.print(F(" E=")); @@ -3380,6 +3405,11 @@ void NodeManager::before() { if (! _battery_report_timer.isConfigured()) _battery_report_timer.set(60,MINUTES); _battery_report_timer.start(); #endif + #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) + // if not already configured, report signal level every 60 minutes + if (! _signal_report_timer.isConfigured()) _signal_report_timer.set(60,MINUTES); + _signal_report_timer.start(); + #endif // setup individual sensors for (int i = 1; i <= MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; @@ -3408,6 +3438,12 @@ void NodeManager::presentation() { // report battery level batteryReport(); #endif + #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) + // present the signal service + _present(SIGNAL_CHILD_ID, S_SOUND); + // report battery level + signalReport(); + #endif // present each sensor for (int i = 1; i <= MAX_SENSORS; i++) { if (_sensors[i] == 0) continue; @@ -3457,6 +3493,17 @@ void NodeManager::loop() { _battery_report_timer.restart(); } #endif + #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) + // update the timer for signal report when not waking up from an interrupt + if (_signal_report_timer.isRunning() && _last_interrupt_pin == -1) _signal_report_timer.update(); + // if it is time to report the signal level + if (_signal_report_timer.isOver()) { + // time to report the signal level again + signalReport(); + // restart the timer + _signal_report_timer.restart(); + } + #endif #if POWER_MANAGER == 1 // turn on the pin powering all the sensors if (_auto_power_pins) powerOn(); @@ -3610,6 +3657,21 @@ void NodeManager::process(Request & request) { case 30: setSleepOrWait(request.getValueInt()); break; case 31: setRebootPin(request.getValueInt()); break; case 32: setADCOff(); break; + #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) + case 33: setSignalReportMinutes(request.getValueInt()); break; + case 43: setSignalReportSeconds(request.getValueInt()); break; + case 44: setSignalReportHours(request.getValueInt()); break; + case 45: setSignalReportDays(request.getValueInt()); break; + case 34: setSignalCommand(request.getValueInt()); break; + case 35: signalReport(); break; + #endif + case 36: setReportIntervalSeconds(request.getValueInt()); break; + case 37: setReportIntervalMinutes(request.getValueInt()); break; + case 38: setReportIntervalHours(request.getValueInt()); break; + case 39: setReportIntervalDays(request.getValueInt()); break; + case 40: setBatteryReportSeconds(request.getValueInt()); break; + case 41: setBatteryReportHours(request.getValueInt()); break; + case 42: setBatteryReportDays(request.getValueInt()); break; default: return; } _send(_msg.set(function)); @@ -3759,14 +3821,24 @@ int NodeManager::getLastInterruptPin() { return _last_interrupt_pin; } +// set the default interval in seconds all the sensors will report their measures +void NodeManager::setReportIntervalSeconds(int value) { + _report_interval_seconds = value; +} + // set the default interval in minutes all the sensors will report their measures void NodeManager::setReportIntervalMinutes(int value) { _report_interval_seconds = value*60; } -// set the default interval in seconds all the sensors will report their measures -void NodeManager::setReportIntervalSeconds(int value) { - _report_interval_seconds = value; +// set the default interval in hours all the sensors will report their measures +void NodeManager::setReportIntervalHours(int value) { + _report_interval_seconds = value*60*60; +} + +// set the default interval in days all the sensors will report their measures +void NodeManager::setReportIntervalDays(int value) { + _report_interval_seconds = value*60*60*24; } // if set and when the board is battery powered, sleep() is always called instead of wait() @@ -3794,6 +3866,34 @@ void NodeManager::sleepOrWait(long value) { else wait(value); } +#if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) + void NodeManager::setSignalReportSeconds(int value) { + _signal_report_timer.set(value,SECONDS); + } + void NodeManager::setSignalReportMinutes(int value) { + _signal_report_timer.set(value,MINUTES); + } + void NodeManager::setSignalReportHours(int value) { + _signal_report_timer.set(value,HOURS); + } + void NodeManager::setSignalReportDays(int value) { + _signal_report_timer.set(value,DAYS); + } + void NodeManager::setSignalCommand(int value) { + _signal_command = value; + } + void NodeManager::signalReport() { + int16_t value = transportGetSignalReport(_signal_command); + #if DEBUG == 1 + Serial.print(F("SIG V=")); + Serial.println(value); + #endif + // report signal level + MyMessage signal_msg(SIGNAL_CHILD_ID, V_LEVEL); + _send(signal_msg.set(value)); + } +#endif + // handle an interrupt void NodeManager::_onInterrupt_1() { long now = millis(); diff --git a/NodeManager.h b/NodeManager.h index a0f94b82..87d3b68b 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -80,10 +80,14 @@ #ifndef SERVICE_MESSAGES #define SERVICE_MESSAGES 0 #endif -// if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage +// if enabled, a battery sensor will be created at BATTERY_CHILD_ID (201 by default) and will report vcc voltage together with the battery level percentage #ifndef BATTERY_SENSOR #define BATTERY_SENSOR 1 #endif +// if enabled, a RSSI sensor will be created at SIGNAL_CHILD_ID (202 by default) and will report the signal quality of the transport layer +#ifndef SIGNAL_SENSOR + #define SIGNAL_SENSOR 1 +#endif // the child id used to allow remote configuration #ifndef CONFIGURATION_CHILD_ID @@ -93,6 +97,10 @@ #ifndef BATTERY_CHILD_ID #define BATTERY_CHILD_ID 201 #endif +// the child id used to report the rssi level to the controller +#ifndef SIGNAL_CHILD_ID + #define SIGNAL_CHILD_ID 202 +#endif // define the maximum number of sensors that can be managed #ifndef MAX_SENSORS #define MAX_SENSORS 10 @@ -324,6 +332,8 @@ enum supported_sensors { // include MySensors libraries #include #include +#include +#include // include third party libraries #if MODULE_DHT == 1 @@ -523,10 +533,14 @@ class Sensor { int getValueInt(); float getValueFloat(); char* getValueString(); - // [16] After how many minutes the sensor will report back its measure (default: 10 minutes) - void setReportIntervalMinutes(int value); // [17] After how many minutes the sensor will report back its measure (default: 10 minutes) void setReportIntervalSeconds(int value); + // [16] After how many minutes the sensor will report back its measure (default: 10 minutes) + void setReportIntervalMinutes(int value); + // [19] After how many hours the sensor will report back its measure (default: 10 minutes) + void setReportIntervalHours(int value); + // [20] After how many days the sensor will report back its measure (default: 10 minutes) + void setReportIntervalDays(int value); // return true if the report interval has been already configured bool isReportIntervalConfigured(); // process a remote request @@ -1388,8 +1402,14 @@ class NodeManager { void setBatteryMin(float value); // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) + // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) void setBatteryReportMinutes(int value); + // [40] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) + void setBatteryReportSeconds(int value); + // [41] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) + void setBatteryReportHours(int value); + // [42] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) + void setBatteryReportDays(int value); // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) void setBatteryInternalVcc(bool value); // [16] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) @@ -1476,11 +1496,14 @@ class NodeManager { void setupInterrupts(); // return the pin from which the last interrupt came int getLastInterruptPin(); - // set the default interval in minutes all the sensors will report their measures. - // If the same function is called on a specific sensor, this will not change the previously set value - // For sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) - void setReportIntervalMinutes(int value); + // [36] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) void setReportIntervalSeconds(int value); + // [37] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) + void setReportIntervalMinutes(int value); + // [38] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) + void setReportIntervalHours(int value); + // [39] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) + void setReportIntervalDays(int value); // [30] if set and when the board is battery powered, sleep() is always called instead of wait() (default: true) void setSleepOrWait(bool value); // sleep if the node is a battery powered or wait if it is not for the given number of milliseconds @@ -1489,6 +1512,20 @@ class NodeManager { void setRebootPin(int value); // [32] turn the ADC off so to save 0.2 mA void setADCOff(); + #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) + // [33] How frequenly to send a signal report to the controller (default: 60 minutes) + void setSignalReportMinutes(int value); + // [43] How frequenly to send a signal report to the controller (default: 60 minutes) + void setSignalReportSeconds(int value); + // [44] How frequenly to send a signal report to the controller (default: 60 minutes) + void setSignalReportHours(int value); + // [45] How frequenly to send a signal report to the controller (default: 60 minutes) + void setSignalReportDays(int value); + // [34] define which signal report to send. Possible values are SR_UPLINK_QUALITY, SR_TX_POWER_LEVEL, SR_TX_POWER_PERCENT, SR_TX_RSSI, SR_RX_RSSI, SR_TX_SNR, SR_RX_SNR (default: SR_RX_RSSI) + void setSignalCommand(int value); + // [35] report the signal level to the controller + void signalReport(); + #endif // hook into the main sketch functions void before(); void presentation(); @@ -1514,6 +1551,10 @@ class NodeManager { PowerManager _powerManager; bool _auto_power_pins = true; #endif + #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) + Timer _signal_report_timer = Timer(this); + int _signal_command = SR_RX_RSSI; + #endif MyMessage _msg; void _send(MyMessage & msg); int _status = AWAKE; diff --git a/NodeManager.ino b/NodeManager.ino index e42bf435..7d585c64 100755 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -36,7 +36,6 @@ void before() { - /* * Register above your sensors */ diff --git a/README.md b/README.md index e5cb7f16..37039ed1 100755 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ NodeManager includes the following main components: * Allow waking up a sleeping node remotely at the end of a sleeping cycle * Allow powering on each connected sensor only while the node is awake to save battery * Report battery level periodically and automatically or on demand +* Report signal level periodically and automatically or on demand * Calculate battery level without requiring an additional pin and the resistors * Allow rebooting the board remotely * Provide out-of-the-box sensors personalities and automatically execute their main task at each cycle @@ -157,30 +158,32 @@ The next step is to enable NodeManager's additional functionalities and the modu #define DEBUG 1 // if enabled, enable the capability to power on sensors with the arduino's pins to save battery while sleeping -#define POWER_MANAGER 0 +#define POWER_MANAGER 1 // if enabled, will load the battery manager library to allow the battery level to be reported automatically or on demand -#define BATTERY_MANAGER 0 +#define BATTERY_MANAGER 1 // if enabled, allow modifying the configuration remotely by interacting with the configuration child id -#define REMOTE_CONFIGURATION 0 +#define REMOTE_CONFIGURATION 1 // if enabled, persist the remote configuration settings on EEPROM #define PERSIST 0 // if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage -#define BATTERY_SENSOR 0 +#define BATTERY_SENSOR 1 +// if enabled, a signal sensor will be created at RSSI_CHILD_ID (202 by default) and will report the signal quality of the transport layer +#define SIGNAL_SENSOR 1 // 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 #define MODULE_ANALOG_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT -#define MODULE_DIGITAL_INPUT 0 +#define MODULE_DIGITAL_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY -#define MODULE_DIGITAL_OUTPUT 0 +#define MODULE_DIGITAL_OUTPUT 1 // Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 #define MODULE_DHT 0 // Enable this module to use one of the following sensors: SENSOR_SHT21 #define MODULE_SHT21 0 // Enable this module to use one of the following sensors: SENSOR_SWITCH, SENSOR_DOOR, SENSOR_MOTION -#define MODULE_SWITCH 1 +#define MODULE_SWITCH 0 // Enable this module to use one of the following sensors: SENSOR_DS18B20 #define MODULE_DS18B20 0 // Enable this module to use one of the following sensors: SENSOR_BH1750 @@ -246,8 +249,14 @@ The next step is to configure NodeManager with settings which will instruct how void setBatteryMin(float value); // [12] the expected vcc when the batter is fully charged, used to calculate the percentage (default: 3.3) void setBatteryMax(float value); - // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60) + // [14] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) void setBatteryReportMinutes(int value); + // [40] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) + void setBatteryReportSeconds(int value); + // [41] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) + void setBatteryReportHours(int value); + // [42] after how many minutes report the battery level to the controller. When reset the battery is always reported (default: 60 minutes) + void setBatteryReportDays(int value); // [15] if true, the battery level will be evaluated by measuring the internal vcc without the need to connect any pin, if false the voltage divider methon will be used (default: true) void setBatteryInternalVcc(bool value); // [16] if setBatteryInternalVcc() is set to false, the analog pin to which the battery's vcc is attached (https://www.mysensors.org/build/battery) (default: -1) @@ -334,11 +343,14 @@ The next step is to configure NodeManager with settings which will instruct how void setupInterrupts(); // return the pin from which the last interrupt came int getLastInterruptPin(); - // set the default interval in minutes all the sensors will report their measures. - // If the same function is called on a specific sensor, this will not change the previously set value - // For sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) - void setReportIntervalMinutes(int value); + // [36] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) void setReportIntervalSeconds(int value); + // [37] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) + void setReportIntervalMinutes(int value); + // [38] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) + void setReportIntervalHours(int value); + // [39] set the default interval in minutes all the sensors will report their measures. If the same function is called on a specific sensor, this will not change the previously set value. or sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes) + void setReportIntervalDays(int value); // [30] if set and when the board is battery powered, sleep() is always called instead of wait() (default: true) void setSleepOrWait(bool value); // sleep if the node is a battery powered or wait if it is not for the given number of milliseconds @@ -347,17 +359,32 @@ The next step is to configure NodeManager with settings which will instruct how void setRebootPin(int value); // [32] turn the ADC off so to save 0.2 mA void setADCOff(); + #if SIGNAL_SENSOR == 1 && defined(MY_SIGNAL_REPORT_ENABLED) + // [33] How frequenly to send a signal report to the controller (default: 60 minutes) + void setSignalReportMinutes(int value); + // [43] How frequenly to send a signal report to the controller (default: 60 minutes) + void setSignalReportSeconds(int value); + // [44] How frequenly to send a signal report to the controller (default: 60 minutes) + void setSignalReportHours(int value); + // [45] How frequenly to send a signal report to the controller (default: 60 minutes) + void setSignalReportDays(int value); + // [34] define which signal report to send. Possible values are SR_UPLINK_QUALITY, SR_TX_POWER_LEVEL, SR_TX_POWER_PERCENT, SR_TX_RSSI, SR_RX_RSSI, SR_TX_SNR, SR_RX_SNR (default: SR_RX_RSSI) + void setSignalCommand(int value); + // [35] report the signal level to the controller + void signalReport(); + #endif ~~~ ### Set reporting intervals and sleeping cycles -If not instructed differently, the node will stay in awake, all the sensors will report every 10 minutes and the battery level will be automatically reported every 60 minutes. To change those settings, you can call the following functions on the nodeManager object: +If not instructed differently, the node will stay in awake, all the sensors will report every 10 minutes. Battery level and signal level will be automatically reported every 60 minutes. To change those settings, you can call the following functions on the nodeManager object: Function | Description ------------ | ------------- setSleepSeconds()/setSleepMinutes()/setSleepHours()/setSleepDays() | the time interval the node will spend in a (smart) sleep cycle -setReportIntervalMinutes() / setReportIntervalSeconds() | the time interval the node will report the measures of all the attached sensors -setBatteryReportMinutes() | the time interval the node will report the battery level +setReportIntervalSeconds()/setReportIntervalMinutes()/setReportIntervalHours()/setReportIntervalDays() | the time interval the node will report the measures of all the attached sensors +setBatteryReportSeconds()/setBatteryReportMinutes()/setBatteryReportHours()/setBatteryReportDays() | the time interval the node will report the battery level +setSignalReportSeconds()/setSignalReportMinutes()/setSignalReportHours()/setSignalReportDays() | the time interval the node will report the radio signal level For example, to put the node to sleep in cycles of 10 minutes: @@ -506,10 +533,14 @@ The following methods are available for all the sensors and can be called on the int getValueInt(); float getValueFloat(); char* getValueString(); - // [16] After how many minutes the sensor will report back its measure (default: 10 minutes) - void setReportIntervalMinutes(int value); // [17] After how many minutes the sensor will report back its measure (default: 10 minutes) void setReportIntervalSeconds(int value); + // [16] After how many minutes the sensor will report back its measure (default: 10 minutes) + void setReportIntervalMinutes(int value); + // [19] After how many hours the sensor will report back its measure (default: 10 minutes) + void setReportIntervalHours(int value); + // [20] After how many days the sensor will report back its measure (default: 10 minutes) + void setReportIntervalDays(int value); // return true if the report interval has been already configured bool isReportIntervalConfigured(); // process a remote request @@ -1365,6 +1396,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) +* Radio signal level is reported automatically and on demand through child 202 * SensorRainGauge now supports sleep mode * SensorSwitch now supports awake mode * SensorLatchingRealy now handles automatically both on and off commands diff --git a/config.h b/config.h index c367a80a..4a64b2cd 100755 --- a/config.h +++ b/config.h @@ -118,8 +118,10 @@ #define REMOTE_CONFIGURATION 1 // if enabled, persist the remote configuration settings on EEPROM #define PERSIST 0 -// if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage +// if enabled, a battery sensor will be created at BATTERY_CHILD_ID (201 by default) and will report vcc voltage together with the battery level percentage #define BATTERY_SENSOR 1 +// if enabled, a signal sensor will be created at RSSI_CHILD_ID (202 by default) and will report the signal quality of the transport layer +#define SIGNAL_SENSOR 1 // 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 From 83814b81c29df08b6e789943578532383c0b5afe Mon Sep 17 00:00:00 2001 From: user2684 Date: Sun, 30 Jul 2017 18:19:45 +0200 Subject: [PATCH 30/33] 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 e996aab2..54e451f3 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 87d3b68b..ac40f34e 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 37039ed1..9bc784fe 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 4a64b2cd..35701982 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 From 09f53f69fef51726615cabd7ffa238b03dc5e4b1 Mon Sep 17 00:00:00 2001 From: Wolfgang Gaar Date: Sun, 6 Aug 2017 10:12:04 +0200 Subject: [PATCH 31/33] Allow to set power savings mode of BH1750 light level sensor (#197) --- NodeManager.cpp | 10 ++++++++++ NodeManager.h | 2 ++ README.md | 8 +++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index 54e451f3..9f86a133 100755 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -1568,6 +1568,10 @@ SensorBH1750::SensorBH1750(NodeManager* node_manager, int child_id): Sensor(node _lightSensor = new BH1750(); } +void SensorBH1750::setMode(uint8_t mode) { + _lightSensor->configure(mode); +} + // what to do during before void SensorBH1750::onBefore() { _lightSensor->begin(); @@ -1596,6 +1600,12 @@ void SensorBH1750::onReceive(const MyMessage & message) { // what to do when receiving a remote message void SensorBH1750::onProcess(Request & request) { + int function = request.getFunction(); + switch(function) { + case 101: setMode(request.getValueInt()); break; + default: return; + } + _send(_msg_service.set(function)); } diff --git a/NodeManager.h b/NodeManager.h index ac40f34e..4ed5a24f 100755 --- a/NodeManager.h +++ b/NodeManager.h @@ -972,6 +972,8 @@ class SensorDs18b20: public Sensor { class SensorBH1750: public Sensor { public: SensorBH1750(NodeManager* node_manager, int child_id); + // [101] set sensor reading mode, e.g. BH1750_ONE_TIME_HIGH_RES_MODE + void setMode(uint8_t mode); // define what to do at each stage of the sketch void onBefore(); void onSetup(); diff --git a/README.md b/README.md index 9bc784fe..094fe2d3 100755 --- a/README.md +++ b/README.md @@ -685,6 +685,12 @@ Each sensor class can expose additional methods. DeviceAddress* getDeviceAddress(); ~~~ +* SensorBH1750 +~~~c + // [101] set sensor reading mode, e.g. BH1750_ONE_TIME_HIGH_RES_MODE + void setMode(uint8_t mode); +~~~ + * SensorBME280 / SensorBMP085 / SensorBMP280 ~~~c // [101] define how many pressure samples to keep track of for calculating the forecast (default: 5) @@ -1412,4 +1418,4 @@ v1.6: * SensorMQ now depends on its own module * Added automatic off capability (safeguard) to SensorDigitalOutput * Any sensor can now access all NodeManager's functions -* DHT sensor now using MySensors' DHT library \ No newline at end of file +* DHT sensor now using MySensors' DHT library From e70b14d75990aa1075fe644dbada46e25ea925ff Mon Sep 17 00:00:00 2001 From: user2684 Date: Tue, 22 Aug 2017 16:37:35 +0200 Subject: [PATCH 32/33] Compilation error when BATTERY_MANAGER is off #206 --- NodeManager.cpp | 10 ++++++---- NodeManager.h | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/NodeManager.cpp b/NodeManager.cpp index c516c4ee..72ec1595 100644 --- a/NodeManager.cpp +++ b/NodeManager.cpp @@ -3719,9 +3719,11 @@ void NodeManager::process(Request & request) { case 37: setReportIntervalMinutes(request.getValueInt()); break; case 38: setReportIntervalHours(request.getValueInt()); break; case 39: setReportIntervalDays(request.getValueInt()); break; - case 40: setBatteryReportSeconds(request.getValueInt()); break; - case 41: setBatteryReportHours(request.getValueInt()); break; - case 42: setBatteryReportDays(request.getValueInt()); break; + #if BATTERY_MANAGER == 1 + case 40: setBatteryReportSeconds(request.getValueInt()); break; + case 41: setBatteryReportHours(request.getValueInt()); break; + case 42: setBatteryReportDays(request.getValueInt()); break; + #endif default: return; } _send(_msg.set(function)); @@ -4113,4 +4115,4 @@ void NodeManager::_saveConfig() { saveState(EEPROM_SLEEP_1,bit_1); saveState(EEPROM_SLEEP_2,bit_2); saveState(EEPROM_SLEEP_3,bit_3); -} \ No newline at end of file +} diff --git a/NodeManager.h b/NodeManager.h index 2ca463bf..c3bbee9f 100644 --- a/NodeManager.h +++ b/NodeManager.h @@ -7,7 +7,7 @@ #include // define NodeManager version -#define VERSION "1.6-dev" +#define VERSION "1.6" /*********************************** Constants @@ -1633,4 +1633,4 @@ class NodeManager { void _saveConfig(); }; -#endif \ No newline at end of file +#endif From d4d7d8853d5ac6c028ae14e15b00d38df03a81d6 Mon Sep 17 00:00:00 2001 From: user2684 Date: Sun, 3 Sep 2017 12:33:21 +0200 Subject: [PATCH 33/33] Updated documentation --- NodeManager.ino | 7 ++++--- README.md | 54 +++++++++++++++++++++++++++++-------------------- config.h | 10 +++++---- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/NodeManager.ino b/NodeManager.ino index 7d585c64..38bf742c 100755 --- a/NodeManager.ino +++ b/NodeManager.ino @@ -8,7 +8,7 @@ NodeManager includes the following main components: - Remote configuration: allows configuring remotely the node without the need to have physical access to it - Built-in personalities: for the most common sensors, provide embedded code so to allow their configuration with a single line Documentation available on: https://github.com/mysensors/NodeManager - */ +*/ // load user settings @@ -33,9 +33,10 @@ void before() { /* * Register below your sensors */ + + - - + /* * Register above your sensors */ diff --git a/README.md b/README.md index 094fe2d3..0804f7af 100755 --- a/README.md +++ b/README.md @@ -70,14 +70,16 @@ Since NodeManager has to communicate with the MySensors gateway on your behalf, // RFM69 radio settings //#define MY_RADIO_RFM69 //#define MY_RFM69_FREQUENCY RF69_868MHZ +//#define MY_RFM69_FREQUENCY RFM69_868MHZ //#define MY_IS_RFM69HW -//#define MY_DEBUG_VERBOSE_RFM69 //#define MY_RFM69_NEW_DRIVER //#define MY_RFM69_ENABLE_ENCRYPTION //#define MY_RFM69_NETWORKID 100 +//#define MY_DEBUG_VERBOSE_RFM69 //#define MY_RF69_IRQ_PIN D1 //#define MY_RF69_IRQ_NUM MY_RF69_IRQ_PIN //#define MY_RF69_SPI_CS D2 +//#define MY_RFM69_ATC_MODE_DISABLED // RS485 serial transport settings //#define MY_RS485 @@ -168,7 +170,7 @@ The next step is to enable NodeManager's additional functionalities and the modu // if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage #define BATTERY_SENSOR 1 // if enabled, a signal sensor will be created at RSSI_CHILD_ID (202 by default) and will report the signal quality of the transport layer -#define SIGNAL_SENSOR 1 +#define SIGNAL_SENSOR 0 // 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 @@ -180,7 +182,7 @@ The next step is to enable NodeManager's additional functionalities and the modu #define MODULE_DIGITAL_OUTPUT 1 // Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 #define MODULE_DHT 0 -// Enable this module to use one of the following sensors: SENSOR_SHT21 +// Enable this module to use one of the following sensors: SENSOR_SHT21, SENSOR_HTU21D #define MODULE_SHT21 0 // Enable this module to use one of the following sensors: SENSOR_SWITCH, SENSOR_DOOR, SENSOR_MOTION #define MODULE_SWITCH 0 @@ -379,14 +381,14 @@ The next step is to configure NodeManager with settings which will instruct how ### Set reporting intervals and sleeping cycles -If not instructed differently, the node will stay in awake, all the sensors will report every 10 minutes. Battery level and signal level will be automatically reported every 60 minutes. To change those settings, you can call the following functions on the nodeManager object: +If not instructed differently, the node will stay awake and all the sensors will report every 10 minutes, battery level and signal level will be automatically reported every 60 minutes. To change those settings, you can call the following functions on the nodeManager object: Function | Description ------------ | ------------- -setSleepSeconds()/setSleepMinutes()/setSleepHours()/setSleepDays() | the time interval the node will spend in a (smart) sleep cycle -setReportIntervalSeconds()/setReportIntervalMinutes()/setReportIntervalHours()/setReportIntervalDays() | the time interval the node will report the measures of all the attached sensors -setBatteryReportSeconds()/setBatteryReportMinutes()/setBatteryReportHours()/setBatteryReportDays() | the time interval the node will report the battery level -setSignalReportSeconds()/setSignalReportMinutes()/setSignalReportHours()/setSignalReportDays() | the time interval the node will report the radio signal level +setSleepSeconds(), setSleepMinutes(), setSleepHours(), setSleepDays() | the time interval the node will spend in a (smart) sleep cycle +setReportIntervalSeconds(), setReportIntervalMinutes(), setReportIntervalHours(), setReportIntervalDays() | the time interval the node will report the measures of all the attached sensors +setBatteryReportSeconds(), setBatteryReportMinutes(), setBatteryReportHours(), setBatteryReportDays() | the time interval the node will report the battery level +setSignalReportSeconds(), setSignalReportMinutes(), setSignalReportHours(), setSignalReportDays() | the time interval the node will report the radio signal level For example, to put the node to sleep in cycles of 10 minutes: @@ -394,7 +396,7 @@ For example, to put the node to sleep in cycles of 10 minutes: nodeManager.setSleepMinutes(10); ~~~ -If you need every sensor to report at a different time interval, you can call `setReportIntervalMinutes()` or `setReportIntervalSeconds()` on the sensor's object. For example to have a DHT sensor reporting every 60 seconds while all the other sensors every 20 minutes: +If you need every sensor to report at a different time interval, you can call `setBatteryReportSeconds(), setBatteryReportMinutes(), setBatteryReportHours(), setBatteryReportDays()` on the sensor's object. For example to have a DHT sensor reporting every 60 seconds while all the other sensors every 20 minutes: ~~~c int id = nodeManager.registerSensor(SENSOR_DHT22,6); SensorDHT* dht = (SensorDHT*)nodeManager.get(id); @@ -469,14 +471,14 @@ If you want to create a custom sensor and register it with NodeManager so it can // define what to do during receive() when the sensor receives a message 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(); + 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: +You can then instantiate your newly created class and register it with NodeManager: ~~~c - nodeManager.registerSensor(new SensorCustom(&nodeManager,child_id, pin)); + nodeManager.registerSensor(new SensorCustom(&nodeManager,child_id, pin)); ~~~ ### Configuring the sensors @@ -747,6 +749,12 @@ Each sensor class can expose additional methods. void fadeTo(int value); ~~~ +### Creating a gateway + +NodeManager can be also used to create a MySensors gateway. Open your config.h file and look for the gateway-specific defines under "MySensors gateway configuration". The most common settings are reported there, just uncomment those you need to use based on the network you are creating. + +Please note you don't necessarily need a NodeManager gateway to interact with a NodeManager node. The NodeManager node is fully compatible with any existing gateway you are currently operating with. + ### Upload your sketch Upload your sketch to your arduino board as you are used to. @@ -767,6 +775,7 @@ To activate a relay connected to the same node, child_id 100 we need to send a ` No need to implement anything on your side since for built-in sensors this is handled automatically. NodeManager exposes also a configuration service which is by default on child_id 200 so you can interact with it by sending `V_CUSTOM` type of messages and commands within the payload. For each `REQ` message, the node will respond with a `SET` message if successful. + Almost all the functions made available through the API can be called remotely. To do so, the payload must be in the format `[,]` where `function_id` is the number between square brackets you can find in the description above and, if the function takes and argument, this can be passed along in `value_to_set`. For example, to request a battery report, find the function you need to call remotely within the documentation: ~~~c @@ -806,11 +815,11 @@ If you want to decrease the temperature offset of a thermistor sensor to -2: ~~~ `100;1;2;0;48;105,-2` -Please note that anything set remotely will NOT persist a reboot apart from those setting the sleep interval which are saved to the EEPROM (provided `PERSIST` is enabled). +Please note that anything set remotely will NOT persist a reboot apart from the sleep interval which is saved to the EEPROM (provided `PERSIST` is enabled). ## Understanding NodeManager: how it works -A NodeManager object is created for you at the beginning of your sketch and its main functions must called from within `before()`, `presentation()`, `loop()` and `receive()` to work properly. NodeManager will do the following during each phase: +A NodeManager object is created for you at the beginning of your sketch and its main functions must be called from within `before()`, `presentation()`, `loop()` and `receive()` to work properly. NodeManager will do the following during each phase: NodeManager::before(): * Setup the interrupt pins to wake up the board based on the configured interrupts @@ -1297,7 +1306,7 @@ Contributes to NodeManager are of course more than welcome. ### Reporting an issue or request an enhancement -For reporting an issue, requesting support for a new sensor or any other kind of enhancement, please drop a message either on the project's main page () or directly on Github (). +For reporting an issue, requesting support for a new sensor or any other kind of enhancement, please drop a message either on the project's main page (), on the MySensors Forum () or open an issue directly on Github (). ### Contributing to the code @@ -1398,8 +1407,9 @@ v1.5: * Added common gateway settings in config.h v1.6: -* Introduced new remote API to allow calling all NodeManager's and sensors' functions remotely -* Decoupled reporting intervals from sleeping cycles +* Introduced new remote API to allow calling almost ALL NodeManager's and its sensors' functions remotely +* Reporting interval configuration is now indipendent from the sleep cycle +* Reporting interval can be customized per-sensor * All intervals (measure/battery reports) are now time-based * Added support for BMP280 temperature and pressure sensor * Added support for RS485 serial transport @@ -1408,14 +1418,14 @@ v1.6: * Added support for AM2320 temperature/humidity sensor * Added support for PT100 high temperature sensor * Added support for MH-Z19 CO2 sensor -* Added buil-in rain and soil moisture analog sensors +* Added support for analog rain and soil moisture 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 +* Radio signal level (RSSI) is now reported automatically like the battery level * SensorRainGauge now supports sleep mode * SensorSwitch now supports awake mode * SensorLatchingRealy now handles automatically both on and off commands * SensorMQ now depends on its own module -* Added automatic off capability (safeguard) to SensorDigitalOutput +* Added safeguard (automatic off) to SensorDigitalOutput * Any sensor can now access all NodeManager's functions * DHT sensor now using MySensors' DHT library diff --git a/config.h b/config.h index 35701982..d93ab3f9 100755 --- a/config.h +++ b/config.h @@ -29,14 +29,16 @@ // RFM69 radio settings //#define MY_RADIO_RFM69 //#define MY_RFM69_FREQUENCY RF69_868MHZ +//#define MY_RFM69_FREQUENCY RFM69_868MHZ //#define MY_IS_RFM69HW -//#define MY_DEBUG_VERBOSE_RFM69 //#define MY_RFM69_NEW_DRIVER //#define MY_RFM69_ENABLE_ENCRYPTION //#define MY_RFM69_NETWORKID 100 +//#define MY_DEBUG_VERBOSE_RFM69 //#define MY_RF69_IRQ_PIN D1 //#define MY_RF69_IRQ_NUM MY_RF69_IRQ_PIN //#define MY_RF69_SPI_CS D2 +//#define MY_RFM69_ATC_MODE_DISABLED // RS485 serial transport settings //#define MY_RS485 @@ -121,7 +123,7 @@ // if enabled, a battery sensor will be created at BATTERY_CHILD_ID (201 by default) and will report vcc voltage together with the battery level percentage #define BATTERY_SENSOR 1 // if enabled, a signal sensor will be created at RSSI_CHILD_ID (202 by default) and will report the signal quality of the transport layer -#define SIGNAL_SENSOR 1 +#define SIGNAL_SENSOR 0 // 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 @@ -131,9 +133,9 @@ #define MODULE_DIGITAL_INPUT 1 // Enable this module to use one of the following sensors: SENSOR_DIGITAL_OUTPUT, SENSOR_RELAY, SENSOR_LATCHING_RELAY #define MODULE_DIGITAL_OUTPUT 1 -// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22, SENSOR_DHT21 +// Enable this module to use one of the following sensors: SENSOR_DHT11, SENSOR_DHT22 #define MODULE_DHT 0 -// Enable this module to use one of the following sensors: SENSOR_SHT21 +// Enable this module to use one of the following sensors: SENSOR_SHT21, SENSOR_HTU21D #define MODULE_SHT21 0 // Enable this module to use one of the following sensors: SENSOR_SWITCH, SENSOR_DOOR, SENSOR_MOTION #define MODULE_SWITCH 0