Combined Sensor RepeaterNode and sleep(SLEEP_TIME) in MySensors 2.0.0


  • MySensors Evangelist

    Just a question. To cover the distance of my sensors to the Gateway I need a repeater!
    Maybe even 2....

    As I always want things to be 'helpfull' I currently have a lightmeter and repeater running on 1 Arduino fine (1.5.4).

    I am currently migrating all sketches to 2.0.0.
    As I've read that things should be backwards compatible ,,,, advised is to move everything to 2.0.0
    So that means rewrtiting all my sketches. A well keeps you off the street for some time.

    I've studied the MySensors 2.0.0 sketches examples of repeater and lightmeter.
    However is it safe to use sleep(SLEEP_TIME) from the lightmeter sketch if the same device has to be a repeater?

    In 1.5.4 sleep was not advised I believe.. To avoid package loss.

    At the moment to avoid the sleep routine I use a counter .....

     if (sleepTimer == SLEEP_TIME) {
        read light from lightmeter;
        sleepTimer = 0;
       }
     else sleepTimer++;
    [/code] 
    

    Seems to do the trick.
    I am not operating the sensors on batteries but I believe there is always room for improvement.

    Please advise 😄


  • Contest Winner

    @sincze Since sleep will put your Arduino to sleep, it will not be able to receive messages. So you shouldn't use sleep on a repeater node. You should use wait instead.


  • Contest Winner

    @sincze If you post your sketch, I can give you more advice. It'll save you the walk across the street next monday 😉


  • MySensors Evangelist

    tnx @TheoL

    Don't posted my repeater sketch on github yet (for my own reference and backup).

    However the sketch that resembles my repeaternode is stored here:
    https://github.com/sincze/mysensors-2..0.x-temperature-humidity/commit/5824defa434c750bf706351562c3ee0dfc8ab4ea

    I want to capture the door being opened however... don't want to have the temperature readings measured every second or so 😉

    I think repeater mode is only one extra line in the code ;-0

    // Enabled repeater feature for this node
    #define MY_REPEATER_FEATURE
    

    Your help is appreciated 😉


  • Contest Winner

    @sincze I've created a library for this purpose. It's not completely tested, so there might be some bugs in it. It's a sort of buy me a beer if you like it library ;-). And yeah I know implementation code should be put in .cpp. Will do that when I'm done testing.

    /**
     * Library ThresholdUtil.h 
     * 
     * Copyright (c) ByTheo
     * 
     * Description:
     *   This library handles all interval based reading of simpels who's values you want to send on a periodical basis. This means that at least every given interval number of readings
     *   the callback handler for that sensor is being called. If howver the value of the sensors goes past the defined threshold value of that sensor, it will call the callbackhandler
     *   the soon as it detects the threshold. This library was based upon the Sensbender Micro example Sketch.
     * 
     * Useage:
     *   1. register each individual sensor to the library with the registerThresholdedSensor() function in your Sketchs setup() function. See comments in code.
     *   2. implement a SensorValueRequestedCallback() function in your Sketch
     *   3. Implement a SensorValueTransmission() function in your sketch 
     *   4. Call checkThresholdedSensors from within your loop as much as you can.
     * 
     * History
     *   August 1st 2016 - Initial version
     * 
     * Design decisions:
     * - don't put sensro specific code into this library (separation of concern). The main sketch is responsible for the retrieval of sensor values.
     * - Don't use C++ is absorps too much resources. PLain old C is more resource efficient
     * - Don't implement memory management. Freeing up is unnecessary because we don't need to support it. Sensors shouldn't be dynamic in a Sketch
     * 
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * version 2 as published by the Free Software Foundation.
     *
     *******************************
    */
    
    #ifndef THRESHHOLD_UTIL_H
    #define THRESHHOLD_UTIL_H
    
    /**
     * Declartion of the type definition used by the library
     */
    typedef enum { TEMPERATURE_SENSOR, HUMIDTY_SENSOR, LIGHTLEVEL_SENSOR, CUSTOM_SENSOR } ThreshHoldedSensorType; // The different kind of sensor type for which you can store values. You can extend this enum to your likings or do a pus request on GITHUB
    typedef void (*SensorValueRequestedCallback)( uint8_t aSensorId, ThreshHoldedSensorType aType, float *value ); // Callback handler which is called whenever the library needs a new value for a specific sensor
    typedef void (*SensorValueTransmission)( uint8_t child_id, uint8_t sensor_id, ThreshHoldedSensorType sensor_type, float value ); // Callback handler called whenever the library notices that the value of a specific sensor has to be send to the gateway
    
    /**
     * Structure used by the library for administrating the variabels for a specific sensor. The library uses a linked list of structures.
     */
    typedef struct ThresholdedSensorStruct {
      uint8_t child_id;
      uint8_t sensor_id; // unique id for the sensors for which we hold the values
      ThreshHoldedSensorType sensor_type;
      float      threshHold;
      uint8_t    forcedTransmissionInterval;
      uint8_t    measureCounter;
      float      lastValue;
      unsigned long lastChecked;
      uint8_t    readingInterval;
      ThresholdedSensorStruct *nextNode;
    } *thssPtr;
    
    /**
     * The root node of the linked list of ThresholdedSensorStructs
     */
    thssPtr thresHoldedSensorsrootNode = NULL;
    
    /**
     * Mehthod for adding a sensor who's value needs to be monitored.
     * child_id: The id of child for which the sensor is monitored. It's only for the convenience of the main sketch and isn't used by the library 
     * sensor_id: An id of a sensor. You can use this to group different sensor values if they come from the same sensor. Some sensors like the HDT11 or the BMP180 report multiple sensor types
     *            The main sketch should use this to request the values of such kind of sensors just once and not multiple times for each request for value made by the library
     * sensor_type: Indicates what kind of values are being monitored. Use this to determine what kind of value is required when dealing with multi purpose sensors
     * theshHold: The value of threshold is indicates how much the new value needs to differ from the last send value before it triggers a resend.
     * readingInterval: The amount of seconds the library waits before it request a new value for this sensor
     * forcedTransmissionInterval: The amount of measurements without retransmissions, before a retransmission is triggered. In other words of the values of a sensor don't extend the threshold, a retransmission
     *            is triggered of this given amount of readings.
     */
    void registerThresholdedSensor(   uint8_t child_id, uint8_t sensor_id, ThreshHoldedSensorType sensor_type, float threshHold, uint8_t readingInterval, uint8_t forcedTransmissionInterval ) { // single sensor
      thssPtr aSensor = (ThresholdedSensorStruct*)malloc(sizeof(struct ThresholdedSensorStruct));
      aSensor->sensor_id = sensor_id;
      aSensor->child_id = child_id;
      aSensor->sensor_type = sensor_type;
      aSensor->threshHold = threshHold;
      aSensor->forcedTransmissionInterval = forcedTransmissionInterval;
      aSensor->measureCounter = forcedTransmissionInterval - 1; // correct initialzation
      aSensor->lastValue = 0;
      aSensor->readingInterval = readingInterval;
      aSensor->lastChecked = millis(); // not checked
      aSensor->nextNode = NULL;
      if ( thresHoldedSensorsrootNode == NULL ) {
        thresHoldedSensorsrootNode = aSensor;
      }
      else {
        thssPtr node = thresHoldedSensorsrootNode;
        while ( node->nextNode != NULL ) {
          node = node->nextNode;
        }
        node->nextNode = aSensor;
      }
    }
    
    /**
     * Supportive method for the checkThresholdedSensors routine. It checks if the value of the given sensors needs to be read according to the given timeStamp.
     * The given SensorValueRequestedCallback is used to query the values of a sensor. The given SensorValueTransmission is called when a (re)transmisson for the value
     * of the given Sensor is required.
     * This method contains the actual logic of the library.
     */
    void checkIndividualThreshHoldedSensor( thssPtr aSensor, unsigned long timeStamp, SensorValueRequestedCallback aCallBack, SensorValueTransmission transmissionCallback ) {
      // determine whether node should be checked in first place!!!
      if ( aSensor->lastChecked <= timeStamp ) {
        aSensor->measureCounter++;
        float value;
        aCallBack( aSensor->sensor_id, aSensor->sensor_type, &value );
        aSensor->lastChecked = timeStamp + ( (unsigned long)aSensor->readingInterval * 1000 );
    
        float diffMeasurement = abs( aSensor->lastValue - value );
    
        bool sendValues = false;
    
        if ( diffMeasurement >= aSensor->threshHold || aSensor->measureCounter == aSensor->forcedTransmissionInterval ) { // transmit value
          aSensor->lastValue = value;
          aSensor->measureCounter = 0;
          transmissionCallback(  aSensor->child_id,  aSensor->sensor_id, aSensor->sensor_type, aSensor->lastValue );
        }
      }
    }
    
    /**
     * Checks all defined sensors on: if they need to be checked and if their value needs to be send to the gateway:
     * aCallBack: pointer to a function that can determine the sensors current value (logic should be implemented in the main sketch, separation of concern)
     * transmissionCallback: pointer to the function the deals with sending a value of a sensor to the gateway (logic should be implemnted by the main sketch, separation of concern)
     */
    void checkThresholdedSensors( SensorValueRequestedCallback aCallBack, SensorValueTransmission transmissionCallback ) {
      unsigned long timeStamp = millis();
      if ( thresHoldedSensorsrootNode != NULL ) {
        thssPtr node = thresHoldedSensorsrootNode;
        checkIndividualThreshHoldedSensor( node, timeStamp, aCallBack, transmissionCallback );
        while ( node->nextNode != NULL ) {
          node = node->nextNode;
          checkIndividualThreshHoldedSensor( node, timeStamp, aCallBack, transmissionCallback );
        }
      }
    }
    
    #endif
    

    In your setup code you add:

      registerThresholdedSensor( CHILD_ID_TEMP, DHT_SENSOR, TEMPERATURE_SENSOR, 0.5, 30, 20 ); // every 30 seconds report at least every 10 minutes
      registerThresholdedSensor( CHILD_ID_HUMIDITY, DHT_SENSOR, HUMIDTY_SENSOR, 1.0, 60, 10 ); // every 60 seconds
    

    Add this line in the presentation method:

      checkThresholdedSensors( checkThrsedSensor, reportSensorValue ); // Necessary call for intializing ThresholdUtil
    

    Now implement the call back function's.

    float lastHumidity;
    
    /**
     * Callback method called by the Threshold util library whenever the library needs a new value for a specific sensor
     */
    void checkThrsedSensor( uint8_t aSensorId, ThreshHoldedSensorType aType, float *value ) {
      if ( aSensorId == DHT_SENSOR  ) {
        if ( aType == TEMPERATURE_SENSOR ) {
          lastHumidity = dht.readHumidity();
          *value = dht.readTemperature();;
        }
        else {
          // no need to read Humidity. We just read it when we read temp value. Haven't found a decent solution to support this from within the library
          *value = lastHumidity;
        }
      }
    }
    /**
     * Threshholded sensor's value needs to be reported to the gateway.
     */
    void reportSensorValue( uint8_t child_id, uint8_t sensor_id, ThreshHoldedSensorType sensor_type, float value ) {
      if ( child_id == CHILD_ID_TEMP ) {
        send( TemperatureMsg.set( value, 1), true );
      }
      else {
        send( HumidityMsg.set( value, 1), true );
      }
    

    And add this into your sketches main loop:

        checkThresholdedSensors( checkThrsedSensor, reportSensorValue );
    

    The library will only report changes when temperature differs 0.5 degrees from last send values. Or if the threshold isn't reached it sends the temperature every 5 minutes. It's the same for humidity, but that has a threshold of 1 %.

    Hope this helps. I will put the code on my github with examples when I've tested it enough.


  • Contest Winner

    @sincze it's on my github with a non MySensors example, that should explain how you use the library


  • MySensors Evangelist

    @TheoL tnx. looks pretty straight forward. and indeed... cheap sensors.... hahaha. Well I've used some calibration variables in my script to keep the sensor in line with my crestra 320tx.
    So what I can do is remove the sleep thing from my sketch and just implement your library in the loop and we are all good to go. excellent. Just one question. As this is a repeater node... would leaving the delay( 5000 ); (mentioned in your example sketch on github) in the void loop a good idea??


  • Contest Winner

    @sincze You shouldn't use delay with MySensors. Just use a wait instead. A delay will stall the execution of an Arduino. Causing the node to miss messages. A wait will stall the sketch but still handles incomming requests. This is the same for all node's regarding if they're a repeater or not.


  • Contest Winner

    @sincze What's really important is adding the * in front of the value. Otherwise the library won't be able to get your values. It's in this part:

    void checkThrsedSensor( uint8_t aSensorId, ThreshHoldedSensorType aType, float *value ) {
      if ( aSensorId == DHT_SENSOR  ) {
        if ( aType == TEMPERATURE_SENSOR ) {
          lastHumidity = dht.readHumidity();
          *value = dht.readTemperature();;
        }
        else {
          // no need to read Humidity. We just read it when we read temp value. Haven't found a decent solution to support this from within the library
          *value = lastHumidity;
        }
      }
    }
    

  • MySensors Evangelist

    Thanks. I did not know for sure if this was still the case in 2.0.0.
    wait(sleepTimer); added at the bottom of loop() function.

    now lets rewrite the sketch with your library.


 

485
Online

7.9k
Users

8.7k
Topics

93.6k
Posts