mysensors / NodeManager

Plugin for a rapid development of battery-powered sensors
130 stars 82 forks source link
arduino iot mysensors nrf24l01 rmf69 sensor

MySensors NodeManager Build Status

NodeManager is intended to take care on your behalf of all those common tasks that a MySensors node has to accomplish, speeding up the development cycle of your projects. Consider it as a sort of frontend for your MySensors projects. When you need to add a sensor (which requires just uncommeting a single line), NodeManager will take care of importing the required library, presenting the sensor to the gateway/controller, executing periodically the main function of the sensor (e.g. measure a temperature, detect a motion, etc.), allowing you to interact with the sensor and even configuring it remotely.

Features

Installation

Upgrade

Please be aware when upgrading to v1.8 from an older version this procedure is not supported and the code should be migrated manually.

Configuration

MySensors configuration

Since NodeManager has to communicate with the MySensors network on your behalf, it has to know how to do it. On top of the template sketch you will find the typical MySensors directives you are used to which can be customized to configure the board to act as a MySensors node or a MySensors gateway.

NodeManager configuration

NodeManager built-in capabilities can be enabled/disabled also when you need to save some storage for your code. To enable/disable a built-in feature:

A list of the supported capabilities and the required dependencies is presented below:

Capability Default Description Dependencies
NODEMANAGER_DEBUG ON NodeManager's debug output on serial console -
NODEMANAGER_DEBUG_VERBOSE OFF increase NodeManager's debug output on the serial console -
NODEMANAGER_POWER_MANAGER OFF allow powering on your sensors only while the node is awake -
NODEMANAGER_INTERRUPTS ON allow managing interrupt-based sensors like a PIR or a door sensor -
NODEMANAGER_CONDITIONAL_REPORT OFF allow reporting a measure only when different from the previous or above/below a given threshold -
NODEMANAGER_EEPROM OFF allow keeping track of some information in the EEPROM -
NODEMANAGER_SLEEP ON allow managing automatically the complexity behind battery-powered sleeping sensors -
NODEMANAGER_RECEIVE ON allow the node to receive messages; can be used by the remote API or for triggering the sensors -
NODEMANAGER_TIME OFF allow keeping the current system time in sync with the controller https://github.com/PaulStoffregen/Time
NODEMANAGER_RTC OFF allow keeping the current system time in sync with an attached RTC device https://github.com/JChristensen/DS3232RTC
NODEMANAGER_SD OFF allow reading from and writing to SD cards -
NODEMANAGER_HOOKING OFF allow custom code to be hooked in the out of the box sensors -
NODEMANAGER_OTA_CONFIGURATION OFF allow over-the-air configuration of the sensors -
NODEMANAGER_SERIAL_INPUT OFF read from the serial port at the end of each loop cycle expecting a serial protocol command -

Once the NodeManager library header file is included, a global instance of the NodeManager class called nodeManager is made available and can be used all along the sketch.

Add your sensors

NodeManager provides built-in implementation of a number of sensors through ad-hoc classes located within the "sensors" directory of the library. To use a built-in sensor:

Once created, the sensor will automatically present one or more child to the gateway and controller. A list of built-in sensors, required dependencies and the number of child automatically created is presented below:

Sensor/Class Name #Child Description Dependencies
SensorBattery 1 Built-in sensor for automatic battery reporting -
SensorSignal 1 Built-in sensor for automatic signal level reporting -
SensorAnalogInput 1 Generic analog sensor, return a pin's analog value or its percentage -
SensorLDR 1 LDR sensor, return the light level of an attached light resistor in percentage -
SensorRain 1 Rain sensor, return the percentage of rain from an attached analog sensor -
SensorSoilMoisture 1 Soil moisture sensor, return the percentage of moisture from an attached analog sensor -
SensorThermistor 1 Thermistor sensor, return the temperature based on the attached thermistor -
SensorML8511 1 ML8511 sensor, return UV intensity -
SensorACS712 1 ACS712 sensor, measure the current going through the attached module -
SensorDigitalInput 1 Generic digital sensor, return a pin's digital value -
SensorDigitalOutput 1 Generic digital output sensor, allows setting the digital output of a pin to the requested value -
SensorRelay 1 Relay sensor, allows activating the relay -
SensorLatchingRelay1Pin 1 Latching Relay sensor, allows toggling the relay with a pulse on the configured pin -
SensorLatchingRelay2Pins 1 Latching Relay sensor, allows turing the relay on and off with a pulse on the configured pins -
SensorDHT11 2 DHT11 sensor, return temperature/humidity based on the attached DHT sensor https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/DHT
SensorDHT22 2 DHT22 sensor, return temperature/humidity based on the attached DHT sensor https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/DHT
SensorSHT21 2 SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor https://github.com/SodaqMoja/Sodaq_SHT2x
SensorHTU21D 2 HTU21D sensor, return temperature/humidity based on the attached HTU21D sensor https://github.com/SodaqMoja/Sodaq_SHT2x
SensorInterrupt 1 Generic interrupt-based sensor, wake up the board when a pin changes status -
SensorDoor 1 Door sensor, wake up the board and report when an attached magnetic sensor has been opened/closed -
SensorMotion 1 Motion sensor, wake up the board and report when an attached PIR has triggered -
SensorDs18b20 1+ DS18B20 sensor, return the temperature based on the attached sensor https://github.com/milesburton/Arduino-Temperature-Control-Library
SensorBH1750 1 BH1750 sensor, return light level in lux https://github.com/claws/BH1750
SensorMLX90614 2 MLX90614 contactless temperature sensor, return ambient and object temperature https://github.com/adafruit/Adafruit-MLX90614-Library
SensorBME280 4 BME280 sensor, return temperature/humidity/pressure based on the attached BME280 sensor https://github.com/adafruit/Adafruit_BME280_Library
SensorBMP085 3 BMP085 sensor, return temperature and pressure https://github.com/adafruit/Adafruit-BMP085-Library
SensorBMP180 3 BMP180 sensor, return temperature and pressure https://github.com/adafruit/Adafruit-BMP085-Library
SensorBMP280 3 BMP280 sensor, return temperature/pressure based on the attached BMP280 sensor https://github.com/adafruit/Adafruit_BMP280_Library
SensorSonoff 1 Sonoff wireless smart switch https://github.com/thomasfredericks/Bounce2
SensorHCSR04 1 HC-SR04 sensor, return the distance between the sensor and an object https://github.com/mysensors/MySensorsArduinoExamples/tree/master/libraries/NewPing
SensorMCP9808 1 MCP9808 sensor, measure the temperature through the attached module https://github.com/adafruit/Adafruit_MCP9808_Library
SensorMQ 1 MQ sensor, return ppm of the target gas. Tuned by default for MQ135 and CO2 -
SensorMHZ19 1 MH-Z19 CO2 sensor via UART (SoftwareSerial, default on pins 6(Rx) and 7(Tx) -
SensorAM2320 2 AM2320 sensors, return temperature/humidity based on the attached AM2320 sensor https://github.com/thakshak/AM2320
SensorTSL2561 1 TSL2561 sensor, return light in lux https://github.com/adafruit/TSL2561-Arduino-Library
SensorPT100 1 DFRobot Driver high temperature sensor, return the temperature from the attached PT100 sensor https://github.com/nxcosa/HighTemperatureSensor
SensorDimmer 1 Generic dimmer sensor used to drive a pwm output -
SensorRainGauge 1 Rain gauge sensor -
SensorPowerMeter 1 Power meter pulse sensor -
SensorWaterMeter 1 Water meter pulse sensor -
SensorPlantowerPMS 3 Plantower PMS particulate matter sensors (reporting PM<=1.0, PM<=2.5 and PM<=10.0 in µg/m³) https://github.com/fu-hsi/pms
SensorVL53L0X 1 VL53L0X laser time-of-flight distance sensor via I²C, sleep pin supported (optional) https://github.com/pololu/vl53l0x-arduino
DisplaySSD1306 1 SSD1306 128x64 OLED display (I²C); By default displays values of all sensors and children https://github.com/greiman/SSD1306Ascii.git
SensorSHT31 2 SHT31 sensor, return temperature/humidity based on the attached SHT31 sensor https://github.com/adafruit/Adafruit_SHT31
SensorSI7021 2 SI7021 sensor, return temperature/humidity based on the attached SI7021 sensor https://github.com/sparkfun/SparkFun_Si701_Breakout_Arduino_Library
SensorChirp 3 Chirp soil moisture sensor (includes temperature and light sensors) https://github.com/Apollon77/I2CSoilMoistureSensor
DisplayHD44780 1 Supports most Hitachi HD44780 based LCDs, by default displays values of all sensors and children https://github.com/cyberang3l/NewLiquidCrystal
SensorTTP 1 TTP226/TTP229 Touch control sensor -
SensorServo 1 Control a generic Servo motor sensor -
SensorAPDS9960 1 SparkFun RGB and Gesture Sensor https://github.com/sparkfun/APDS-9960_RGB_and_Gesture_Sensor
SensorNeopixel 1 Control a Neopixel LED https://github.com/adafruit/Adafruit_NeoPixel
SensorSDS011 2 SDS011 air quality sensor, return concentrations of 2.5 and 10 micrometer particles. https://github.com/ricki-z/SDS011
SensorFPM10A 1 FPM10A fingerprint sensor https://github.com/adafruit/Adafruit-Fingerprint-Sensor-Library
SensorPH 1 PH ( SKU SEN161 ) sensor, measure the analog value from the amplifier module -
SensorPca9685W 2 Generic dimmer sensor (S_DIMMER) used to drive a single channel pwm output of PCA9685 https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library
SensorPca9685Rgb 2 Generic RGB-dimmer sensor (S_RGB_LIGHT) used to drive RGB resp. 3-channel pwm output of PCA9685 https://github.com/adafruit/Adafruit-PWM-Servo- Driver-Library
SensorPca9685Rgbw 2 Generic RGBW-dimmer sensor (S_RGBW_LIGHT) used to drive RGBW resp. 4-channel pwm output of PCA9685 https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library
SensorDSM501A 1 Dust sensor module DSM501A for PM1.0 and PM2.5 particles -
SensorPN532 1 PN532 NFC RFID Module https://github.com/elechouse/PN532
SensorCCS811 1 CCS811 gas/Air Quality sensor. Measure VOC and eCO2 https://github.com/adafruit/Adafruit_CCS811
SensorMPR121 1 MPR121-based capacitive touch control sensor https://github.com/adafruit/Adafruit_MPR121
SensorGSM 1 Send SMS through an attached serial modem (e.g. SIM900) -

Those sensors requiring a pin to operate would take it as an argument in the constructor. NodeManager automatically creates all the child_ids, assigning an incremental counter. If you need to set your own child_id, pass it as the last argument to the constructor

Examples:

// Add a thermistor sensor attached to pin A0
#include <sensors/SensorThermistor.h>
SensorThermistor thermistor(A0);

// Add a LDR sensor attached to pin A0 and assing child_id 5
#include <sensors/SensorLDR.h>
SensorLDR ldr(A1,5);

// Add a temperature/humidity sensor SHT21 sensor. No pin required since using i2c
#include <sensors/SensorSHT21.h>
SensorSHT21 sht21;

The sensor will be then registered automatically with NodeManager which will take care of it all along its lifecycle. NodeManager will 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/SET message with the expected type to their assigned child id.

Installing the dependencies

Some of the sensors and buit-in capabilities 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 you are planning to enable to use the sensor.

Configure your sensors

NodeManager and all the sensors can be configured from within before() in the main sketch. Find Configure your sensors below to customize the behavior of any sensor by invoking one of the functions available.

Examples:

// report measures of every attached sensors every 10 minutes
nodeManager.setReportIntervalMinutes(10);
// set the node to sleep in 5 minutes cycles
nodeManager.setSleepMinutes(5);
// report battery level every 10 minutes
battery.setReportIntervalMinutes(10);
// set an offset to -1 to a thermistor sensor
thermistor.setOffset(-1);
// Change the id of a the first child of a sht21 sensor
sht21.children.get(1)->child_id = 5;
// power all the nodes through dedicated pins
nodeManager.setPowerManager(power);

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 (if the corresponding sensors have been added).

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.

Running your code

Once finished configuring your node, upload your sketch to your Arduino board as you are used to.

Check your gateway's logs to ensure the node is working as expected. You should see the node presenting itself, presenting all the registered sensors and reporting new measures at the configured reporting interval. When NODEMANAGER_DEBUG is enabled, detailed information will be available through the serial port. The better understand the logs generaged by NodeManager, paste them into MySensors Log Parser (https://mysensors.org/build/parser). Remember to disable debug once the tests have been completed to save additional storage.

Communicate with the sensors

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 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 sensors this is handled automatically.

API

You can interact with each class provided by NodeManager through a set of API functions.

NodeManager API

    // instantiate a NodeManager object. An optional fixed number of sensors can be passed as an argument
    NodeManager(uint8_t sensorcount = 0);
    // [10] send the same message multiple times (default: 1)
    void setRetries(uint8_t value);
    // [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 the controller's configuration on startup (default: true)
    void setGetControllerConfig(bool value);
    // [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();
    // [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. On sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
    void setReportIntervalSeconds(unsigned long value);
    unsigned long getReportIntervalSeconds();
    // [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. On sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
    void setReportIntervalMinutes(unsigned long 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. On sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
    void setReportIntervalHours(unsigned 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. On sleeping sensors, the elapsed time can be evaluated only upon wake up (default: 10 minutes)
    void setReportIntervalDays(uint8_t 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(unsigned 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(int8_t value);
    // [32] turn the ADC off so to save 0.2 mA
    void setADCOff();
    // send a message by providing the source child, type of the message and value
    void sendMessage(uint8_t child_id, uint8_t type, int value);
    void sendMessage(uint8_t child_id, uint8_t type, float value, uint8_t precision);
    void sendMessage(uint8_t child_id, uint8_t type, double value, uint8_t precision);
    void sendMessage(uint8_t child_id, uint8_t type, const char* value);
    // register a sensor
    void registerSensor(Sensor* sensor);
#if NODEMANAGER_SLEEP == ON
    // register a timer
    void registerTimer(Timer* timer);
#endif
    // return the next-available child id
    uint8_t getAvailableChildId(uint8_t child_id = 0);
    // list containing all the registered sensors
    List<Sensor*> sensors;
    // return the Child object of the given child_id
    Child* getChild(uint8_t child_id);
    // return the sensor object of the given child_id
    Sensor* getSensorWithChild(uint8_t child_id);
    // sleep between send()
    void sleepBetweenSend();
    // set the analog reference to the given value and optionally perform some fake reading on the given pin
    void setAnalogReference(uint8_t value, uint8_t pin = -1);
#if NODEMANAGER_SLEEP == ON
    // [3] set the duration (in seconds) of a sleep cycle
    void setSleepSeconds(unsigned long value);
    unsigned long getSleepSeconds();
    // [4] set the duration (in minutes) of a sleep cycle
    void setSleepMinutes(unsigned long value);
    // [5] set the duration (in hours) of a sleep cycle
    void setSleepHours(unsigned int value);
    // [29] set the duration (in days) of a sleep cycle
    void setSleepDays(uint8_t value);
    // [20] optionally sleep interval in milliseconds before sending each message to the radio network (default: 0)
    void setSleepBetweenSend(unsigned int value);
    // [9] wake up the board
    void wakeup();
    // use smart sleep for sleeping boards (default: true)
    void setSmartSleep(bool value);
#endif
#if NODEMANAGER_INTERRUPTS == ON
    // [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(int8_t value);
    // configure the interrupt pin and mode. Mode can be CHANGE, RISING, FALLING (default: MODE_NOT_DEFINED)
    void setInterrupt(int8_t pin, uint8_t mode, int8_t initial = -1);
    // [28] ignore two consecutive interrupts if happening within this timeframe in milliseconds (default: 100)
    void setInterruptDebounce(unsigned long value);
    // return the pin from which the last interrupt came
    int8_t getLastInterruptPin();
    // return the value of the pin from which the last interrupt came
    int8_t getLastInterruptValue();
#endif
#if NODEMANAGER_POWER_MANAGER == ON
    // configure a PowerManager common to all the sensors
    void setPowerManager(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(int8_t ground_pin, int8_t vcc_pin, unsigned long wait_time = 50);
    // [24] manually turn the power on
    void powerOn();
    // [25] manually turn the power off
    void powerOff();
#endif
#if NODEMANAGER_EEPROM == ON
    // [7] clear the EEPROM
    void clearEeprom();
    // 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);
    // [40] if set save the sleep settings in memory, also when changed remotely (default: false)
    void setSaveSleepSettings(bool value);
#endif
#if NODEMANAGER_TIME == ON
    // [41] synchronize the local time with the controller
    void syncTime();
    // [42] returns the current system time
    unsigned long getTime();
    // [43] set the hour offset for when syncronizing the time (default: 0)
    void setTimezone(int8_t value);
    // request the current time to the controller during setup(). Time with a RTC if configured is always synchronized (default: true)
    void setSyncTimeOnSetup(bool value);
    // request the current time to the controller just after a sleep cycle. Time with a RTC if configured is always synchronized (default: true)
    void setSyncTimeAfterSleep(bool value);
    // request the current time to the controller after the configured number of minutes (default: 0)
    void setSyncTimeAfterInterval(unsigned long value);
    // receiveTime() callback
    void receiveTime(unsigned long ts);
#endif
#if NODEMANAGER_SD == ON
    // SD card variables
    Sd2Card sd_card;
    SdVolume sd_volume;
    SdFile sd_root;
    SdFile sd_file;
#endif
    // hook into the main sketch functions
    void before();
    void presentation();
    void setup();
    void loop();
#if NODEMANAGER_RECEIVE == ON
    void receive(const MyMessage & msg);

Sensor API

The following methods are available for all the sensors:

    Sensor(int8_t pin = -1);
    // return the name of the sensor
    const char* getName();
    // [1] where the sensor is attached to (default: not set)
    void setPin(int8_t value);
    // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1)
    void setSamples(unsigned int value);
    // [6] If more then one sample has to be taken, set the interval in milliseconds between measurements (default: 0)
    void setSamplesInterval(unsigned long value);
    // [17] After how many seconds the sensor will report back its measure (default: 10 minutes)
    void setReportIntervalSeconds(unsigned long value);
    // [16] After how many minutes the sensor will report back its measure (default: 10 minutes)
    void setReportIntervalMinutes(unsigned long value);
    // [19] After how many hours the sensor will report back its measure (default: 10 minutes)
    void setReportIntervalHours(unsigned int value);
    // [20] After how many days the sensor will report back its measure (default: 10 minutes)
    void setReportIntervalDays(uint8_t value);
    // [24] Set the way the timer used for reporting to the gateway should operate. It can be either TIME_INTERVAL (e.g. report every X seconds with the amount of time set with setReportTimerValue()), IMMEDIATELY (e.g. report at every cycle, useful for sensors like actuators which should report as soon as the value has changed), DO_NOT_REPORT (e.g. never report, useful for when there is no need to report, like a Display) and when NODEMANAGER_TIME is ON, EVERY_MINUTE/EVERY_HOUR/EVERY_DAY (e.g. to report the value set in the previous timeframe, useful for sensors reporting an accumulated value linked to a timeframe at regular intervals), AT_MINUTE/AT_HOUR/AT_DAY (e.g. report at a given minute/hour/day, useful if the measure is expected at a specified time, set with setReportTimerValue())
    void setReportTimerMode(timer_mode value);
    // [25] Set the value for the reporting timer's mode which has been set with setReportTimerMode()
    void setReportTimerValue(unsigned long value);
    // [26] Set the way the timer used for taking measures should operate. Takes the same parameters as setReportTimerMode(). If not set explicitly, will be set as the reporting timer
    void setMeasureTimerMode(timer_mode value);
    // [27] Set the value for the reporting timer's mode which has been set with setReportTimerMode() If not set explicitely, will be set with the same value as the reporting timer
    void setMeasureTimerValue(unsigned long value);
    // list of configured child
    List<Child*> children;
    // return the child object based on the provided child_id
    Child* getChild(uint8_t child_id);
    // register a child
    void registerChild(Child* child);
#if NODEMANAGER_INTERRUPTS == ON
    // return the pin the interrupt is attached to
    int8_t getInterruptPin();
    // set initial value of the configured pin. Can be used for internal pull up
    void setPinInitialValue(int8_t value);
    // for interrupt-based sensor, set the interrupt mode. Can be CHANGE, RISING, FALLING (default: CHANGE)
    void setInterruptMode(uint8_t value);
    // [22] for interrupt-based sensor, milliseconds to wait/sleep after the interrupt before reporting (default: 0)
    void setWaitAfterInterrupt(unsigned long value);
    // [23] for interrupt-based sensor, the value of the pin is checked and the interrupt ignored if RISING and not HIGH or FALLING and not LOW (default: true)
    void setInterruptStrict(bool value);
#endif
#if NODEMANAGER_POWER_MANAGER == ON
    // set a previously configured PowerManager to the sensor so to powering it up with custom pins
    void setPowerManager(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(int8_t ground_pin, int8_t vcc_pin, unsigned long wait_time = 50);
    // [13] manually turn the power on
    void powerOn();
    // [14] manually turn the power off
    void powerOff();
#endif
#if NODEMANAGER_HOOKING == ON
    // set a custom hook function to be called when the sensor executes its setup() function
    void setSetupHook(void (*function)(Sensor* sensor));
    // set a custom hook function to be called just before the sensor executes its loop() function
    void setPreLoopHook(void (*function)(Sensor* sensor));
    // set a custom hook function to be called just after the sensor executes its loop() function
    void setPostLoopHook(void (*function)(Sensor* sensor));
    // set a custom hook function to be called when the sensor executes its interrupt() function
    void setInterruptHook(void (*function)(Sensor* sensor));
    // set a custom hook function to be called when the sensor executes its receive() function
    void setReceiveHook(void (*function)(Sensor* sensor, MyMessage* message));
#endif
    // define what to do at each stage of the sketch
    void presentation();
    void setup();
    void loop(MyMessage* message);
#if NODEMANAGER_INTERRUPTS == ON
    bool interrupt();
#endif
#if NODEMANAGER_RECEIVE == ON
    void receive(MyMessage* message);
#endif
    // abstract functions, subclasses may implement
    virtual void onSetup();
    virtual void onLoop(Child* child);
    virtual void onReceive(MyMessage* message);
    virtual void onInterrupt();
#if NODEMANAGER_OTA_CONFIGURATION == ON
    virtual void onOTAConfiguration(ConfigurationRequest* request);
#endif

Child API

The following methods are available for all the child:

    Child(Sensor* sensor, value_format format, uint8_t child_id, uint8_t presentation, uint8_t type, const char* description = "");
    // set child id used to communicate with the gateway/controller
    void setChildId(uint8_t value);
    uint8_t getChildId();
    // set sensor format
    void setFormat(value_format value);
    value_format getFormat();
    // set sensor presentation (default: S_CUSTOM)
    void setPresentation(uint8_t value);
    uint8_t getPresentation();
    // set sensor type (default: V_CUSTOM)
    void setType(uint8_t value);
    uint8_t getType();
    // set how many decimal digits to use (default: 2 for ChildFloat, 4 for ChildDouble)
    void setFloatPrecision(uint8_t value);
    // set sensor description
    void setDescription(const char* value);
    const char* getDescription();
    // configure the behavior of the child when setValue() is called multiple times. It can be NONE (ignore the previous values but the last one),  AVG (averages the values), SUM (sum up the values) (default: AVG)
    void setValueProcessing(child_processing value);
    // send the value to the gateway even if there have been no samples collected (default: false)
    void setSendWithoutValue(bool value);
    // set the value of the child
    void setValue(int value);
    void setValue(float value);
    void setValue(double value);
    void setValue(const char* value);
    // get the value of the child
    int getValueInt();
    float getValueFloat();
    double getValueDouble();
    const char* getValueString();
    // check if the value must be sended back to the controller
    bool valueReadyToSend();
    // send the current value to the gateway
    void sendValue(bool force = 0);
    // print the current value on a LCD display
    void print(Print& device);
    // reset all the counters
    void reset();
#if NODEMANAGER_CONDITIONAL_REPORT == ON
    // force to send an update after the configured number of minutes
    void setForceUpdateTimerValue(unsigned long value);
    // never report values below this threshold (default: -FLT_MAX)
    void setMinThreshold(float value);
    // never report values above this threshold (default: FLT_MAX)
    void setMaxThreshold(float value);
    // do not report values if too close to the previous one (default: 0)
    void setValueDelta(float value);
    // set when the last value is updated. Possible values are UPDATE_ALWAYS (at every cycle), UPDATE_ON_SEND (only after sending) (default: UPDATE_ON_SEND)
    void setUpdateLastValue(last_value_mode value);
    // get the last value of the child
    int getLastValueInt();
    float getLastValueFloat();
    double getLastValueDouble();
    const char* getLastValueString();
#endif
#if NODEMANAGER_EEPROM == ON
    // persist the child's value in EEPROM. The value will be saved at each update and loaded at boot time (default: false)
    void setPersistValue(bool value);
    bool getPersistValue();
    // load old value from EEPROM
    void loadValue();
    // load current value to EEPROM
    void saveValue();
#endif

Built-in sensors API

Each sensor class may expose additional methods.

OTA Configuration

When NODEMANAGER_OTA_CONFIGURATION is set to ON the API presented above can be also called remotely through SensorConfiguration, which is automatically added to NodeManager. SensorConfiguration exposes by default child id 200 that can be used to interact with the service 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 <child_id>,<function_id>[,<value_to_set>] where child_id is the recipient child id you want to communicate with (the node has child id 0), function_id is the number between square brackets you can find in the API documentation and, if the function takes and argument, this can be passed along in value_to_set. For example, to change the sleep time to e.g. 10 minutes:

    // [4] set the duration (in minutes) of a sleep cycle
    void setSleepMinutes(unsigned long value);

<node_id>;<configuration_child_id>;<req>;0;<V_CUSTOM>;<child_id>,<function_id>,<value> 100;200;2;0;48;0,4,10

To wake up a node previously configured as sleeping, send the following as the node wakes up next:

    // [9] wake up the board
    void wakeup();

100;200;2;0;48;0,9

if you want to collect and average 10 samples for the sensor on child_id 1:

    // [5] For some sensors, the measurement can be queried multiple times and an average is returned (default: 1)
    void setSamples(unsigned int value);

100;200;2;0;48;1,5,10

If you want to decrease the temperature offset of a thermistor sensor to -2:

    // [105] set a temperature offset
    void setOffset(float value);

100;200;2;0;48;1,105,-2

Please note that anything set remotely will NOT persist a reboot apart from the sleep interval which is saved to the EEPROM if setSaveSleepSettings() is set.

How it works

The following is detailed what happens when the different callback functions are called:

NodeManager::before():

NodeManager::presentation():

NodeManager::setup():

Sensor::setup():

NodeManager::loop():

Sensor::loop():

NodeManager::receive():

Sensor::receive():

Sensor::interrupt():

Each sensor is in dedicated header file under the sensors directory of the library which has be directly included if needed into the main sketch. The implementation of the class is inline. Required libraries and OTA configuration request handling is everything happening inside the class.

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 (https://www.mysensors.org/download/node-manager), on the MySensors Forum (https://forum.mysensors.org/category/43/nodemanager) or open an issue directly on Github (https://github.com/mysensors/NodeManager/issues).

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:

Before applying any change, always ensure you have the latest development version available:

Create a branch for the fix/feature you want to work on and apply changes to the code:

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:

Contributing with a new sensor

When contributing with a new sensor follows the same guidelines presented above and proceed with the following steps:

Compatibility

This version of NodeManager has been tested and is compatible with the following MySensors library:

You don't necessarily need a NodeManager gateway to interact with a NodeManager node. A NodeManager node is fully compatible with any existing gateway you are currently operating with.

There are generally speaking no compatibility issues in having in your network nodes running different versions of NodeManager.

Starting from v1.8 NodeManager is released as an Arduino library hence your code has to be migrated manually from previous versions.

Release Notes

v1.0:

v1.1:

v1.2:

v1.3:

v1.4:

v1.5:

v1.6:

v1.7:

v1.8: