NodeManager: plugin for a rapid development of battery-powered sensors



  • Hi, being a newbie in this wonderful MySensors world, I found myself excited about the flexibility of the overall platform but also somehow implementing the same common functionalities across my sensors over and over again. I decided then to spend some reasonable amount of time to expand the idea behind MyExtention (https://forum.mysensors.org/topic/6062/myextension) into something I have called NodeManager (I know, I'm bad with names!).

    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. It should help who is new to MySensors to get up to speed quicker and for those already into the matter to delegate to NodeManager common and boring tasks so to focus on more interesting scenarios.

    NodeManager main features are:

    • Manage all the aspects of a sleeping cycle by leveraging smart sleep
    • Allow configuring the sleep mode and the sleep duration 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
    • 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

    Specifically on this last point, the idea behind providing built-in the functionalities of most common sensors is to simplify the development especially for somebody new to it.
    E.g. if I connect a thermistor to one of the pin, I just need a single line to have it working, as it was leveraging some sort of embedded firmware:

    nodeManager.registerSensor(SENSOR_THERMISTOR,A2);
    

    The following built-in sensors are available:

    • SENSOR_ANALOG_INPUT Generic analog sensor, return a pin's analog value or its percentage
    • SENSOR_LDR LDR sensor, return the light level of an attached light resistor in percentage
    • SENSOR_THERMISTOR Thermistor sensor, return the temperature based on the attached thermistor
    • SENSOR_DIGITAL_INPUT Generic digital sensor, return a pin's digital value
    • SENSOR_DIGITAL_OUTPUT Generic digital output sensor, allows setting the digital output of a pin to the requested value
    • SENSOR_RELAY Relay sensor, allows activating the relay
    • SENSOR_LATCHING_RELAY Latching Relay sensor, allows activating the relay with a pulse
    • SENSOR_DHT11 DHT11 sensor, return temperature/humidity based on the attached DHT sensor
    • SENSOR_DHT22 DHT22 sensor, return temperature/humidity based on the attached DHT sensor
    • SENSOR_SHT21 SHT21 sensor, return temperature/humidity based on the attached SHT21 sensor
    • SENSOR_SWITCH Generic switch, wake up the board when a pin changes status
    • SENSOR_DOOR Door sensor, wake up the board and report when an attached magnetic sensor has been opened/closed
    • SENSOR_MOTION Motion sensor, wake up the board and report when an attached PIR has triggered
    • SENSOR_DS18B20 DS18B20 sensor, return the temperature based on the attached sensor

    I put the package on Sourceforce so to avoid leaving here piece of code which may become obsolete in a short time.

    The project is here: https://sourceforge.net/projects/mynodemanager
    I've also tried to document everything the best as I could on https://sourceforge.net/p/mynodemanager/wiki

    Thanks



  • I'm amazed this hasn't got more attention. A superb contribution to the community and one which I'm sure has taken many hours... Thank you!



  • Thanks @Mark-Swift!
    By the way, if anybody is interested I've just fixed a few bugs and added some minor enhancements (for details: https://sourceforge.net/p/mynodemanager/tickets/milestone/v1.1/).

    The new version (v1.1) is available as always at https://sourceforge.net/projects/mynodemanager


  • Hero Member

    @Mark-Swift said in NodeManager: plugin for a rapid development of battery-powered sensors:

    I'm amazed this hasn't got more attention. A superb contribution to the community and one which I'm sure has taken many hours... Thank you!

    I agree with you, for a lot of beginners this is like the graal for sketch creation if I believe the many posts asking how to combine some sketches.



  • Hi, I've made an update of NodeManager in case can be of interest. Latest version v1.2 is available on https://sourceforge.net/projects/mynodemanager/.
    Short changelog:



  • ...and I've moved the repository (including tickets and documentation) under the MySensors community on https://github.com/mysensors/NodeManager (thanks again for your help @hek!).

    I'll keep the Sourceforge project open to avoid broken links but I will upload only new releases there in sync with github.



  • Cool stuff! was looking for a way to do some configuration of mysensors remotely!
    Gonna give it a spin this week!



  • I love the look of this setup. I am making my own slim nodes running at 8MHz with a basic 328P chip.

    I am starting with a basic Thermistor which is fine but when I try to compile the code I get the following error.

    exit status 1
    default argument given for parameter 3 of 'void PowerManager::setPowerPins(int, int, long int)' [-fpermissive]

    My code is as follows:

    // load user settings
    #include "config.h"
    // load MySensors library
    #include <MySensors.h>
    // load NodeManager library
    #include "NodeManager.h"
    
    // create a NodeManager instance
    NodeManager nodeManager;
    
    // before
    void before() {
      // setup the serial port baud rate
      Serial.begin(9600);  
      // instruct the board to sleep for 10 minutes for each cycle
    //  nodeManager.setSleep(SLEEP,10,MINUTES);
    
      // register the sensors
      nodeManager.registerSensor(SENSOR_THERMISTOR,A1);
    //  nodeManager.registerSensor(SENSOR_LDR,A2);
      /*
       * Register below your sensors
      */
      
    
      /*
       * Register above your sensors
      */
      nodeManager.before();
    }
    
    // presentation
    void presentation() {
      // Send the sketch version information to the gateway and Controller
    	sendSketchInfo("Battery Temp Sensor","1.0");
      // call NodeManager presentation routine
      nodeManager.presentation();
    
    }
    
    // setup
    void setup() {
      // call NodeManager setup routine
      nodeManager.setup();
    }
    
    // loop
    void loop() {
      // call NodeManager loop routine
      nodeManager.loop();
    
    }
    
    // receive
    void receive(const MyMessage &message) {
      // call NodeManager receive routine
      nodeManager.receive(message);
    }
    

    Any help with sorting this out will be great.
    Cheers,

    Ben



  • Thanks for your comment @Ben-Coton! The error message tells me that I'm doing something wrong in the code (http://stackoverflow.com/questions/2545720/error-default-argument-given-for-parameter-1) but I wonder why it compiles without error or warning on my system.

    Meanwhile I'll fix the problem (tracking it with https://github.com/mysensors/NodeManager/issues/30), in case you want to try a workaround, I share below my platform.txt file since I'm sure there must be some flag in mine which is somehow ignoring the error when I compile it:

    # Arduino AVR Core and platform.
    # ------------------------------
    #
    # For more info:
    # https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification
    
    name=Arduino AVR Boards
    version=1.6.17
    
    # AVR compile variables
    # ---------------------
    
    compiler.warning_flags=-w
    compiler.warning_flags.none=-w
    compiler.warning_flags.default=
    compiler.warning_flags.more=-Wall
    compiler.warning_flags.all=-Wall -Wextra
    
    # Default "compiler.path" is correct, change only if you want to override the initial value
    compiler.path={runtime.tools.avr-gcc.path}/bin/
    compiler.c.cmd=avr-gcc
    compiler.c.flags=-c -g -Os {compiler.warning_flags} -std=gnu11 -ffunction-sections -fdata-sections -MMD -flto -fno-fat-lto-objects
    compiler.c.elf.flags={compiler.warning_flags} -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections
    compiler.c.elf.cmd=avr-gcc
    compiler.S.flags=-c -g -x assembler-with-cpp -flto -MMD
    compiler.cpp.cmd=avr-g++
    compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -flto
    compiler.ar.cmd=avr-gcc-ar
    compiler.ar.flags=rcs
    compiler.objcopy.cmd=avr-objcopy
    compiler.objcopy.eep.flags=-O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0
    compiler.elf2hex.flags=-O ihex -R .eeprom
    compiler.elf2hex.cmd=avr-objcopy
    compiler.ldflags=
    compiler.size.cmd=avr-size
    
    # This can be overridden in boards.txt
    build.extra_flags=
    
    # These can be overridden in platform.local.txt
    compiler.c.extra_flags=
    compiler.c.elf.extra_flags=
    compiler.S.extra_flags=
    compiler.cpp.extra_flags=
    compiler.ar.extra_flags=
    compiler.objcopy.eep.extra_flags=
    compiler.elf2hex.extra_flags=
    
    # AVR compile patterns
    # --------------------
    
    ## Compile c files
    recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.c.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
    
    ## Compile c++ files
    recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
    
    ## Compile S files
    recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.S.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.S.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}"
    
    ## Create archives
    # archive_file_path is needed for backwards compatibility with IDE 1.6.5 or older, IDE 1.6.6 or newer overrides this value
    archive_file_path={build.path}/{archive_file}
    recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}"
    
    ## Combine gc-sections, archives, and objects
    recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mmcu={build.mcu} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" {object_files} "{build.path}/{archive_file}" "-L{build.path}" -lm
    
    ## Create output files (.eep and .hex)
    recipe.objcopy.eep.pattern="{compiler.path}{compiler.objcopy.cmd}" {compiler.objcopy.eep.flags} {compiler.objcopy.eep.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.eep"
    recipe.objcopy.hex.pattern="{compiler.path}{compiler.elf2hex.cmd}" {compiler.elf2hex.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.hex"
    
    ## Save hex
    recipe.output.tmp_file={build.project_name}.hex
    recipe.output.save_file={build.project_name}.{build.variant}.hex
    
    ## Compute size
    recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build.project_name}.elf"
    recipe.size.regex=^(?:\.text|\.data|\.bootloader)\s+([0-9]+).*
    recipe.size.regex.data=^(?:\.data|\.bss|\.noinit)\s+([0-9]+).*
    recipe.size.regex.eeprom=^(?:\.eeprom)\s+([0-9]+).*
    
    ## Preprocessor
    preproc.includes.flags=-w -x c++ -M -MG -MP
    recipe.preproc.includes="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} {preproc.includes.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}"
    
    preproc.macros.flags=-w -x c++ -E -CC
    recipe.preproc.macros="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} {preproc.macros.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{preprocessed_file_path}"
    
    # AVR Uploader/Programmers tools
    # ------------------------------
    
    tools.avrdude.path={runtime.tools.avrdude.path}
    tools.avrdude.cmd.path={path}/bin/avrdude
    tools.avrdude.config.path={path}/etc/avrdude.conf
    
    tools.avrdude.network_cmd={runtime.tools.arduinoOTA.path}/bin/arduinoOTA
    
    tools.avrdude.upload.params.verbose=-v
    tools.avrdude.upload.params.quiet=-q -q
    # tools.avrdude.upload.verify is needed for backwards compatibility with IDE 1.6.8 or older, IDE 1.6.9 or newer overrides this value
    tools.avrdude.upload.verify=
    tools.avrdude.upload.params.noverify=-V
    tools.avrdude.upload.pattern="{cmd.path}" "-C{config.path}" {upload.verbose} {upload.verify} -p{build.mcu} -c{upload.protocol} -P{serial.port} -b{upload.speed} -D "-Uflash:w:{build.path}/{build.project_name}.hex:i"
    
    tools.avrdude.program.params.verbose=-v
    tools.avrdude.program.params.quiet=-q -q
    # tools.avrdude.program.verify is needed for backwards compatibility with IDE 1.6.8 or older, IDE 1.6.9 or newer overrides this value
    tools.avrdude.program.verify=
    tools.avrdude.program.params.noverify=-V
    tools.avrdude.program.pattern="{cmd.path}" "-C{config.path}" {program.verbose} {program.verify} -p{build.mcu} -c{protocol} {program.extra_params} "-Uflash:w:{build.path}/{build.project_name}.hex:i"
    
    tools.avrdude.erase.params.verbose=-v
    tools.avrdude.erase.params.quiet=-q -q
    tools.avrdude.erase.pattern="{cmd.path}" "-C{config.path}" {erase.verbose} -p{build.mcu} -c{protocol} {program.extra_params} -e -Ulock:w:{bootloader.unlock_bits}:m -Uefuse:w:{bootloader.extended_fuses}:m -Uhfuse:w:{bootloader.high_fuses}:m -Ulfuse:w:{bootloader.low_fuses}:m
    
    tools.avrdude.bootloader.params.verbose=-v
    tools.avrdude.bootloader.params.quiet=-q -q
    tools.avrdude.bootloader.pattern="{cmd.path}" "-C{config.path}" {bootloader.verbose} -p{build.mcu} -c{protocol} {program.extra_params} "-Uflash:w:{runtime.platform.path}/bootloaders/{bootloader.file}:i" -Ulock:w:{bootloader.lock_bits}:m
    
    tools.avrdude_remote.upload.pattern=/usr/bin/run-avrdude /tmp/sketch.hex {upload.verbose} -p{build.mcu}
    
    tools.avrdude.upload.network_pattern="{network_cmd}" -address {serial.port} -port {upload.network.port} -sketch "{build.path}/{build.project_name}.hex" -upload {upload.network.endpoint_upload} -sync {upload.network.endpoint_sync} -reset {upload.network.endpoint_reset} -sync_exp {upload.network.sync_return}
    
    # USB Default Flags
    # Default blank usb manufacturer will be filled in at compile time
    # - from numeric vendor ID, set to Unknown otherwise
    build.usb_manufacturer="Unknown"
    build.usb_flags=-DUSB_VID={build.vid} -DUSB_PID={build.pid} '-DUSB_MANUFACTURER={build.usb_manufacturer}' '-DUSB_PRODUCT={build.usb_product}'
    

    Thanks!



  • Thanks for the reply.

    I have no idea about the platform.txt file so I will wait to see if someone much smarter than me can figure it out :)

    If it helps, I am using the Arduino IDE V.1.81, compiling with a custom atmega328p board running at 8MHz with BOD disabled.

    Ben



  • Oh I see, when you have custom hardware sometimes you have different options set for the compiler, this is why with the default settings works here. Nevertheless it is an easy fix on my side, just wait for v1.3 to be out very soon and it should compile without issues.

    Thanks!



  • Hi, I've updated NodeManager to its 1.3 version. This should also solve your issue @Ben-Coton. Short changelog:

    • Added support for BME280 temperature/humudity/pressure sensor
    • Added option to measure battery level via a pin in addition to internal Vcc
    • Added example sketches to the documentation
    • Fixed a few bugs

    Code and updated documentation (now with with four comprehensive examples including the full sketches at the end) on https://github.com/mysensors/NodeManager



  • @user2684 , Thanks for the new version.

    Are you planning to include Virtual Button and IOExpander(MCP23017) support too? Thst would really help especially adding consistent child-ids for the expander pins, just like multiple temp sensors on the SPI bus...



  • @vikasjee I'm very open to whatever can make the life of an average user easier, so far I've just pitched what were common requirements for me as a new user but I'm sure there is a lot more that I'm missing. Give me some more info or pointers regarding Virtual Button and IOExpander and how you are using it right now, I couldn't find much here on the forum and my knowledge is limited and I'll look into it for sure!
    Thanks



  • A long time reader and "lurker" here, but just registered today to thank @user2684 for this contribution to MYS Community! In my opinion NodeManager is really a great addition to MYS!



  • I just started playing with this and must say FANTASTIC! This makes things almost too simple.

    I found a few small things┬┤(bugs?)

    • You refer to the sourforge page for the latest releases (nit pick)
    • Baud in the config.h is default set to 9800, shouldn't this be 9600? However, setting it to whatever doesn't help, only 4800 outputs anything, this could be something on my setup as well ...

    Again, thanks!


  • Plugin Developer

    It sounds absolutely terrific ! :+1:

    Thank you very much !



  • @Efflon thanks for reporting these two issues, I'll track them down with https://github.com/mysensors/NodeManager/issues/34 and https://github.com/mysensors/NodeManager/issues/33 and fix asap!
    Thank you all for your comments!



  • @user2684 Excellent!
    A question, I'm doing a simple door sensor

      nodeManager.setBatteryMin(1.8);
      nodeManager.setBatteryMax(3.2);
      nodeManager.setBatteryReportCycles(1);
      int door = nodeManager.registerSensor(SENSOR_DOOR,3);
      ((SensorDoor*)nodeManager.get(door))->setDebounce(500);
      nodeManager.setSleep(SLEEP,1,HOURS);
    

    Now, the node is constantly sending. It seems as if every wake-up triggers a sending, and a wakeup from the debounce sleep, then battery reporting etc.. Removing the debounce helps and lets the node sleep.

    AWAKE
    SEND D=0 I=200 C=1 T=48 S=AWAKE I=0 F=0.00
    BATT V=3.16 P=97
    SEND D=0 I=201 C=0 T=38 S= I=0 F=3.16
    SWITCH I=1 P=3 V=0
    SEND D=0 I=1 C=1 T=16 S= N=0 F=0.00
    SLEEP 60s
    SEND D=0 I=200 C=1 T=48 S=SLEEPING I=0 F=0.00
    
    [here i "open" the door]
    
    WAKE P=3, M=1
    AWAKE
    SEND D=0 I=200 C=1 T=48 S=AWAKE I=0 F=0.00
    BATT V=3.16 P=97
    SEND D=0 I=201 C=0 T=38 S= I=0 F=3.16
    SWITCH I=1 P=3 V=1
    SEND D=0 I=1 C=1 T=16 S= N=1 F=0.00
    SLEEP 60s
    SEND D=0 I=200 C=1 T=48 S=SLEEPING I=0 F=0.00
    
    [just a second wait]
    
    WAKE P=3, M=1
    AWAKE
    SEND D=0 I=200 C=1 T=48 S=AWAKE I=0 F=0.00
    BATT V=3.16 P=97
    SEND D=0 I=201 C=0 T=38 S= I=0 F=3.16
    SWITCH I=1 P=3 V=1
    SEND D=0 I=1 C=1 T=16 S= N=1 F=0.00
    SLEEP 60s
    
    [repeated until closing "door"]
    

    Edit:

    I changed the sleep to a wait and now the behavior is correct (this line).

    // what do to during loop
    void SensorSwitch::onLoop() {
      // wait to ensure the the input is not floating
      if (_debounce > 0) wait(_debounce);
      // read the value of the pin
      int value = digitalRead(_pin);
    
    MY I=8 M=1
    SEND D=0 I=200 C=0 T=48 S=STARTED I=0 F=0.00
    SWITCH I=1 P=3 V=0
    SEND D=0 I=1 C=0 T=16 S= N=0 F=0.00
    SLEEP 60s
    SEND D=0 I=200 C=1 T=48 S=SLEEPING I=0 F=0.00
    
    [open]
    
    WAKE P=3, M=1
    AWAKE
    SEND D=0 I=200 C=1 T=48 S=AWAKE I=0 F=0.00
    BATT V=3.16 P=97
    SEND D=0 I=201 C=0 T=38 S= I=0 F=3.16
    SWITCH I=1 P=3 V=1
    SEND D=0 I=1 C=1 T=16 S= N=1 F=0.00
    SLEEP 60s
    SEND D=0 I=200 C=1 T=48 S=SLEEPING I=0 F=0.00
    
    [close]
    
    WAKE P=3, M=1
    AWAKE
    SEND D=0 I=200 C=1 T=48 S=AWAKE I=0 F=0.00
    BATT V=3.16 P=97
    SEND D=0 I=201 C=0 T=38 S= I=0 F=3.16
    SWITCH I=1 P=3 V=0
    SEND D=0 I=1 C=1 T=16 S= N=0 F=0.00
    SLEEP 60s
    SEND D=0 I=200 C=1 T=48 S=SLEEPING I=0 F=0.00
    


  • Thanks for the hit @Efflon! I accidentally fixed that sleep() in the v1.4 dev branch this afternoon because of another issue. Is everything else now behaving correctly?

    I have a concern regarding battery level reports when operating with interrupts: right now I do not distinguish between a cycle because a full sleep has completed from a cycle interrupted by an interrupt like in this case. This may cause a lot more battery reports, depending on how often the sensor triggers. To avoid this unexpected behavior, in the dev branch the default behavior is now the opposite: waking up from an interrupt does not count as a cycle. The drawback though is that if the sensor triggers continuously, it will never report the battery to the controller since unable to complete a full sleeping cycle. This is why I've added the following so allow the user to pick up the policy he likes the most:

    // If true, wake up by an interrupt counts as a valid cycle for battery reports otherwise only uninterrupted sleep cycles would contribute (default: false)
    void setBatteryReportWithInterrupt(bool value);
    

    In case you want to give this a try, the dev branch is available at https://github.com/mysensors/NodeManager/tree/v1.4-dev. You just need to replace the existing NodeManager.cpp and NodeManager.h files, you can keep both your sketch and your config.h.
    Thanks


Log in to reply
 

Looks like your connection to MySensors Forum was lost, please wait while we try to reconnect.