Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. Development
  3. MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides

MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides

Scheduled Pinned Locked Moved Development
19 Posts 3 Posters 5.3k Views 4 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    Ben Andrewes
    wrote on last edited by Ben Andrewes
    #1

    This is my first real effort at making a sensor... It is also my first time programming Arduinos - I learnt BASIC at school 30yrs ago and use VBA at work but that is it so be kind please... :) I am grateful for any suggestions on how to improve it and cut out any lines I don't need

    I already use RFLink and Domoticz to do some controlling around my house but with MySensors I thought I could add to that. The aim is to create one lot of code that can then be used on about 10 separate idenitcal nodes around the house. That way I can debug the code before deployment rather than having to debug every node.

    I decided I needed temp, humidity, a couple of relays, a PIR, and (on a whim) a gesture sensor. I used a dht11 for the temp/hum and apds-9960 for the gestures. I wanted lots of possibilities for sending command to Domoticz which will then handle the logic e.g. using the gesture sensor, if I swipe up I'll get Domoticz to switch on a relay.

    I've also made it so that there is a separate switch that can deactivate motion sensing from the controller (don't know why, just seemed like a good idea. In addition I made a temperature adjustment child by declaring a dimmer switch then using the value to calculate an adjustment where 50% dimmed is 0 degrees offset. 0% = -5 degrees, 25% = -2.5 degrees, 50% = no offset, 75% = +2.5 degrees, 76% = +2.6 degrees etc.

    Fianlly I thought that dealing with the status of the relays, the PIR, and temp offset might be different depending on the sensor (e.g. I might want a relay connected to a fridge to default to "On" if the node rebooted for any reason, but e.g. the light in the garage to default to off, I might also want the sensor to get the last value from eeprom as in the relay example sketch or even query Domoticz to find the current state according to it).

    To do this I added a StoreXState variable that can be 1,2, or 3 and after a power cycle, it decides if the "Motion Enable/disable", Relays, & Temp Offset states are either default, got from EEPROM, or requested from Domoticz.

    I'm pretty pleased with it as a first attempt, so thought I'd share the code. A few things:

    • there are loads of Serial Prints so I could see what was happening
    • if debug is defined then it uses loads of memory (like 99%) on a pro mini and I got really odd behaviour from the node
    • I've borrowed heavily from other people's code, thank you to them
    • it is currently a jumble of wires on my desk so no pics yet
    • massive thanks to the MySensors team
    /**
       The MySensors Arduino library handles the wireless radio link and protocol
       between your home built sensors/actuators and HA controller of choice.
       The sensors forms a self healing radio network with optional repeaters. Each
       repeater and gateway builds a routing tables in EEPROM which keeps track of the
       network topology allowing messages to be routed to nodes.
    
       Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
       Copyright (C) 2013-2015 Sensnology AB
       Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
    
       Documentation: http://www.mysensors.org
       Support Forum: http://forum.mysensors.org
    
       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.
    
     *******************************
    
    Hardware Connections:
    
    IMPORTANT: The APDS-9960 can only accept 3.3V!
     
     Arduino Pin  APDS-9960 Board  Function
     
     3.3V         VCC              Power
     GND          GND              Ground
     A4           SDA              I2C Data
     A5           SCL              I2C Clock
     2            INT              Interrupt
    */
    
    // Enable debug prints to serial monitor
    //#define MY_DEBUG
    
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    //#define MY_RADIO_RFM69
    
    // Enable repeater functionality for this node
    #define MY_REPEATER_FEATURE
    #define MY_NODE_ID 103
    
    #include <Wire.h>
    #include <SparkFun_APDS9960.h>
    #include <SPI.h>
    #include <DHT.h>
    #include <MySensors.h>
    
    #define RELAY_1_PIN  5  // Arduino Digital I/O pin number for first relay 
    #define RELAY_2_PIN  6  // Arduino Digital I/O pin number for second relay 
    
    #define RELAY_ON 0  // GPIO value to write to turn on attached relay - domoticz needs 0 for on, 1 for off
    #define RELAY_OFF 1 // GPIO value to write to turn off attached relay
    
    
    #define CHILD_ID_RELAY1 1
    #define CHILD_ID_RELAY2 2
    #define CHILD_ID_HUM 3
    #define CHILD_ID_TEMP 4
    #define CHILD_ID_MOT 5   // Id of the sensor child
    #define CHILD_ID_TEMP_OFFSET 20
    #define CHILD_ID_MotionOnOff 21
    #define CHILD_ID_GESTUREUD 30  //up down gesture
    #define CHILD_ID_GESTURELR 31 //left right gesture
    #define CHILD_ID_GESTURENF 32 //near far gesture
    #define GestureUp 1 //command to send on gesture, 0 = off, 1 = on
    #define GestureDown 0 //command to send on gesture, 0 = off, 1 = on
    #define GestureLeft 0 //command to send on gesture, 0 = off, 1 = on
    #define GestureRight 1 //command to send on gesture, 0 = off, 1 = on
    #define GestureNear 0 //command to send on gesture, 0 = off, 1 = on
    #define GestureFar 1 //command to send on gesture, 0 = off, 1 = on
    #define GestureSensitivity  0  // value between 0 & 3, 0 most sensitive, 3 least
    
    
    #define HUMIDITY_SENSOR_DIGITAL_PIN 7
    
    #define APDS9960_INT    2  // for the APDS-9960 - Needs to be an interrupt pin
    
    #define DIGITAL_INPUT_SENSOR 3   // The digital input you attached your motion sensor.  (Only 2 and 3 generates interrupt!)
    #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // for the PIR - Usually the interrupt = pin -2 (on uno/nano anyway)
    
    SparkFun_APDS9960 apds = SparkFun_APDS9960();
    volatile int isr_flag = 0; //handles the interrupt process for gesture sensor
    
    
    
    
    unsigned long SLEEP_TIME = 120000; // Sleep time between reads of temp/hum (in milliseconds)
    unsigned long lastRefreshTime = 0; // Use this to implement a non-blocking delay function
    unsigned long RefreshCyclesUntilForcedUpdate = 5; // use to force a temp/hum reading after 'n' SLEEP_TIME cycles even if value hasn't changed, set v.high (e.g. 10,000) to effectively disable
    unsigned long ForceUpdate = RefreshCyclesUntilForcedUpdate * SLEEP_TIME; // Force update read after this amount of time
    unsigned long lastForceUpdateTime = 0; // Use this to implement a non-blocking delay function that forces update even if temp hasn't changed
    
    DHT dht;
    float lastTemp;
    float lastHum;
    
    boolean metric = true;
    
    bool lastTripped = 0;   // Used to store last motion sensor value
    boolean FirstBoot = true;
    
    // the below allow you decide if the default state of certain sensors is stored in EEPROM or Controller or always start in a certain state
    // 1 = EEPROM 2 = Controller (e.g. Domoticz), 3 or any other number then they start as with default values
    // If option 1 or 3 is selected then we will also send a message to the controller to tell it the states
    
    // unsigned long WaitBetweenSendingMessages = 150; //how long to wait between sending messages and presenting nodes
    int StoreTempOffset = 2;
    int StoreMotionOnOff = 2;
    int StoreRelayState = 2;
    
    // below are the default states that you can set to be loaded at boot each time
    // these will be reverted to in EEPROM values are corrupted
    bool MotionON = 0;      //enable/disable the PIR reports (1=enabled 0=disabled)
    bool RelayState = 0;    // default state for relays (1 = on, 0 = off)
    
    int TempOffset = 50; // a value of 50 means that initial offset is zero when first loaded.  Tempoffset can be between 0-100
    volatile float ActualTempOffset = (TempOffset / 10.0) - 5.0;  //Adjust temp offset to a decimal between +/- 5 degrees
    
    MyMessage msgHum(CHILD_ID_HUM, V_HUM);
    MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
    MyMessage msgMot(CHILD_ID_MOT, V_TRIPPED);
    MyMessage msgTempOffset(CHILD_ID_TEMP_OFFSET, V_PERCENTAGE);
    MyMessage msgMotOnOff(CHILD_ID_MotionOnOff, V_STATUS);
    MyMessage msgRelay1(1, V_STATUS);
    MyMessage msgRelay2(2, V_STATUS);
    MyMessage msgGestureUpDown(CHILD_ID_GESTUREUD, V_STATUS);
    MyMessage msgGestureLeftRight(CHILD_ID_GESTURELR, V_STATUS);
    MyMessage msgGestureNearFar(CHILD_ID_GESTURENF, V_STATUS);
    
    void before()
    {
    
      // Then set relay pins in output mode
      pinMode(RELAY_1_PIN , OUTPUT);
      pinMode(RELAY_2_PIN , OUTPUT);
      
    
      
      pinMode(DIGITAL_INPUT_SENSOR, INPUT);      // sets the motion sensor digital pin as input
    
    
    
    
    }
    
    void setup()
    {
      // Set gesture sensor interrupt pin as input
      pinMode(APDS9960_INT, INPUT);
        
        //define sensitivity of gesture sensor
        apds.setGestureGain( GestureSensitivity );
      
      dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
      // metric = getConfig().isMetric;
    
      // Initialize interrupt service routine for gestures
      
     attachInterrupt(digitalPinToInterrupt(APDS9960_INT), interruptRoutine, FALLING);
    
    
      // Initialize APDS-9960 (configure I2C and initial values)
      if ( apds.init() ) {
        Serial.println(F("APDS-9960 initialization complete"));
      } else {
        Serial.println(F("Something went wrong during APDS-9960 init!"));
      }
      
      // Start running the APDS-9960 gesture sensor engine
      if ( apds.enableGestureSensor(true) ) {
        Serial.println(F("Gesture sensor is now running"));
      } else {
        Serial.println(F("Something went wrong during gesture sensor init!"));
      }
    
    }
    
    void presentation()
    {
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo("MyMultiSensor", "1.4");
    
    
      // Register all sensors to gw (they will be created as child devices)
      present(CHILD_ID_RELAY1, S_BINARY, "Relay 1");
      wait(100);
      present(CHILD_ID_RELAY2, S_BINARY, "Relay 2");
      wait(100);
      present(CHILD_ID_HUM, S_HUM, "Humidity");
      wait(100);
      present(CHILD_ID_TEMP, S_TEMP, "Temperature");
      wait(100);
      present(CHILD_ID_MOT, S_MOTION, "PIR");
      wait(100);
      present(CHILD_ID_TEMP_OFFSET, S_DIMMER, "Temp Calibration");
      wait(100);
      present(CHILD_ID_MotionOnOff, S_BINARY, "PIR Enable/Disable");
      wait(100);
      present(CHILD_ID_GESTUREUD, S_BINARY, "Gesture Up/Down");
      wait(100);
      present(CHILD_ID_GESTURELR, S_BINARY, "Gesture Left/Right");
      wait(100);
      present(CHILD_ID_GESTURENF, S_BINARY, "Gesture Near/Far");
      wait(100);  
     
    }
    
    void firstboot()
    {
      
      }
      
      
    void loop()
    {
      if(FirstBoot)
      {
      Serial.println("FirstBoot sequence started");
      // Set relay to correct state (using eeprom/default/domoticz value)
      switch (StoreRelayState) {
        case 1:
          if (loadState(CHILD_ID_RELAY1) == 1)  {
            digitalWrite(RELAY_1_PIN , RELAY_ON);  
          }
          else
          {
            digitalWrite(RELAY_1_PIN , RELAY_OFF);  
          }
          if (loadState(CHILD_ID_RELAY2) == 1)  {
            digitalWrite(RELAY_2_PIN , RELAY_ON);  
          }
          else
          {
            digitalWrite(RELAY_2_PIN , RELAY_OFF);  
          }
           
            Serial.print("Relay 1 loadstate from EEPROM: ");
            Serial.println(loadState(CHILD_ID_RELAY1));
            Serial.print("Relay 2 loadstate from EEPROM: ");
            Serial.println(loadState(CHILD_ID_RELAY2));
            //best let domoticz know
            send(msgRelay1.set(RelayState));
            wait(100);
            send(msgRelay2.set(RelayState));
            wait(100);
            
            Serial.print("Default Relay state sent to Domoticz: ");
            Serial.println(RelayState);
        break;
          
        case 2: 
            //get Relay Statuses from Domoticz
              Serial.println("Relay status requested from Domoticz");          
              request(CHILD_ID_RELAY1, V_STATUS);
              wait(1000);
              request(CHILD_ID_RELAY2, V_STATUS);
              wait(1000);
              
        break;
        default:
            digitalWrite(RELAY_1_PIN ,  RelayState ? RELAY_ON : RELAY_OFF);
            digitalWrite(RELAY_2_PIN ,  RelayState ? RELAY_ON : RELAY_OFF);
            Serial.print("Relays set to default boot state: ");
            Serial.println(RelayState);
            //
            send(msgRelay1.set(RelayState));
            wait(100);
            send(msgRelay2.set(RelayState));
            wait(100);
            Serial.print("Default Relay state sent to Domoticz: ");
            Serial.println(RelayState);
        break;
        }
      
      // Set tempoffset to correct state (using eeprom/default/domoticz value) - needs to be 0 - 100 otherwise set to no offset
    
        switch (StoreTempOffset)  {
          case 1:
            if (loadState(CHILD_ID_TEMP_OFFSET) >100) {
              ActualTempOffset = 5.0;
            }
            else
            {
              TempOffset = loadState(CHILD_ID_TEMP_OFFSET);
              ActualTempOffset = (TempOffset / 10.0) - 5.0;
            }
              Serial.print("Temp Offset retrieved from eeprom: ");
              Serial.println(ActualTempOffset);
              //let Domoticz know the value
              send(msgTempOffset.set(TempOffset, 1));
              wait(100);
              Serial.print("tempOffset from sensor sent to Domoticz: ");
              Serial.println(TempOffset);
          break;
          case 2:
            //get TempOffSet from Domoticz 
              Serial.println("TempOffset value requested from Domoticz");         
              request(CHILD_ID_TEMP_OFFSET, V_PERCENTAGE);
              wait(1000);
              
          break;
          default:
              Serial.print("Default boot temperature offset is: ");
              Serial.println(ActualTempOffset);
              //let Domoticz know the value
              send(msgTempOffset.set(TempOffset, 1));
              wait(100);
              Serial.print("tempOffset from sensor sent to Domoticz: ");
              Serial.println(TempOffset);    
          break;
        }
    
        // Set PIR enable/disable (using eeprom/default/domoticz value)  
        switch(StoreMotionOnOff) {
          case 1:
            if (loadState(CHILD_ID_MotionOnOff) == 1 || loadState(CHILD_ID_MotionOnOff) == 0) { //check for odd values in EEPROM
              MotionON = loadState(CHILD_ID_MotionOnOff);  
            }
            else {
              Serial.print("Error in PIR Enable/Disable Value in EEPROM");
              Serial.println("Default value used instead");
            } 
            //Let Domoticz know
              send(msgMotOnOff.set(MotionON, 1));
              wait(100);
              Serial.print("PIR ENABLE status from sensor sent to Domoticz: ");
              Serial.println(MotionON);  
          break;
           
          case 2:
            //get PIR enable status from Domoticz
              Serial.println("MotionOnOff status requested from Domoticz");          
              request(CHILD_ID_MotionOnOff, V_STATUS);
              wait(1000);
    
    
          break;
              
    
          default:
              send(msgMotOnOff.set(MotionON, 1));
              wait(100);
              Serial.print("Default PIR ENABLE status used and sent to Domoticz: ");
              Serial.println(MotionON); 
          break;
        }
      wait(2000);
      Serial.println("First boot checks completed");
      FirstBoot = false;
      }
    
      //check if gesture sensor interrupt is triggered
        if( isr_flag == 1 ) {
        detachInterrupt(digitalPinToInterrupt(APDS9960_INT));
        handleGesture();
        isr_flag = 0;
        attachInterrupt(digitalPinToInterrupt(APDS9960_INT), interruptRoutine, FALLING);
    
      }
      
      // Read digital motion value
      if (MotionON) {
        bool tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH;
        if (lastTripped != tripped) {
          Serial.print("New Motion State: ");
          Serial.println(tripped);
          // Send tripped value to gw
          send(msgMot.set(tripped ? "1" : "0"));
          lastTripped = tripped;
        }
      }
    
    
    
      boolean needRefresh = (millis() - lastRefreshTime) > SLEEP_TIME;
      if (needRefresh)
      {
        Serial.println("I'm alive");
        lastRefreshTime = millis();
    
        float temperature = dht.getTemperature() + ActualTempOffset;
        if (isnan(temperature)) {
          Serial.println("Failed reading temperature from DHT");
        }
        else if (temperature != lastTemp  ) {
          lastTemp = temperature;
          if (!metric) {
            temperature = dht.toFahrenheit(temperature);
          }
          send(msgTemp.set(temperature, 1));
          Serial.print("T: ");
          Serial.println(temperature);
        }
        else if (millis() - lastForceUpdateTime > ForceUpdate) {
          lastTemp = temperature;
          lastForceUpdateTime = millis();
          if (!metric) {
            temperature = dht.toFahrenheit(temperature);
          }
          send(msgTemp.set(temperature, 1));
          Serial.print("T: ");
          Serial.println(temperature);
        }
    
    
        float humidity = dht.getHumidity();
        if (isnan(humidity)) {
          Serial.println("Failed reading humidity from DHT");
        }
        else if (humidity != lastHum) {
          lastHum = humidity;
          send(msgHum.set(humidity, 1));
          Serial.print("H: ");
          Serial.println(humidity);
        }
        else if (millis() - lastForceUpdateTime > ForceUpdate) {
          lastHum = humidity;
          lastForceUpdateTime = millis();
          send(msgHum.set(humidity, 1));
          Serial.print("H: ");
          Serial.println(humidity);
        }
      }
    
    }
    
    void interruptRoutine() {
      //Serial.println("Interrupt Routine started");
      isr_flag = 1;
    }
    
    void handleGesture() {
    
     
      
        if ( apds.isGestureAvailable() ) {
    
    
        switch ( apds.readGesture() ) {
          case DIR_UP:
            Serial.println("UP");
            send(msgGestureUpDown.set(GestureUp));
            break;
          case DIR_DOWN:
            Serial.println("DOWN");
            send(msgGestureUpDown.set(GestureDown));
            break;
          case DIR_LEFT:
            Serial.println("LEFT");
            send(msgGestureLeftRight.set(GestureLeft));
            break;
          case DIR_RIGHT:
            Serial.println("RIGHT");
            send(msgGestureLeftRight.set(GestureRight));
            break;
          case DIR_NEAR:
            Serial.println("NEAR");
            send(msgGestureNearFar.set(GestureNear));
            break;
          case DIR_FAR:
            Serial.println("FAR");
            send(msgGestureNearFar.set(GestureFar));
            break;
          default:
            Serial.println("NONE");
        }
      
       }
      
    }
    
    void receive(const MyMessage &message)
    {
     //For debugging just checking why controller sent a msg e.g. was it in response to a request by node  
      switch (message.getCommand()) {                                 // message.getCommand will give us the command type of the incomming message
          case C_SET: 
            Serial.println("msg sent by controller");
          break;
          case C_REQ:
            Serial.println("msg state sent by controller in response to request");
          break;
          default:
            Serial.println("msg isn't C_SET or C_REQ so what you gonna do?");
          break;
       }
      // We only expect V_STATUS (for relays) or V_Percentage (for temp offset) message from controller. But we better check anyway.
      if (message.type == V_STATUS) {
        // Change relay state
    
        if (message.sensor == 1 ) {
          
          digitalWrite(RELAY_1_PIN , message.getBool() ? RELAY_ON : RELAY_OFF);
          // Store state in eeprom
          saveState(message.sensor, message.getBool());
          // Write some debug info
          Serial.print("Incoming Relay 1 State: ");
          Serial.println(message.getBool());
        }
        else if (message.sensor == 2) {
         
          digitalWrite(RELAY_2_PIN , message.getBool() ? RELAY_ON : RELAY_OFF);
          // Store state in eeprom
          saveState(message.sensor, message.getBool());
          // Write some debug info
          Serial.print("Incoming Relay 2 State: ");
          Serial.println(message.getBool());
        }
        else if (message.sensor == CHILD_ID_MotionOnOff) {
    
            if (message.getBool() == 1) {
              MotionON = 1;
              saveState(CHILD_ID_MotionOnOff, MotionON);
            }
            else
            {
              MotionON = 0;
            }
        Serial.print("Motion Sensor on/off state: ");
        Serial.println(MotionON);
        saveState(CHILD_ID_MotionOnOff, MotionON);
        
        }
      }
      else if (message.type == V_PERCENTAGE)  {
        int TempOffset = atoi(message.data);
        ActualTempOffset = (TempOffset / 10.0) - 5.0;
        Serial.print("Temp Offset value: ");
        Serial.println(ActualTempOffset);
        saveState(CHILD_ID_TEMP_OFFSET, TempOffset);
    
      }
      
    
    }
    
    
    dbemowskD 1 Reply Last reply
    1
    • B Ben Andrewes

      This is my first real effort at making a sensor... It is also my first time programming Arduinos - I learnt BASIC at school 30yrs ago and use VBA at work but that is it so be kind please... :) I am grateful for any suggestions on how to improve it and cut out any lines I don't need

      I already use RFLink and Domoticz to do some controlling around my house but with MySensors I thought I could add to that. The aim is to create one lot of code that can then be used on about 10 separate idenitcal nodes around the house. That way I can debug the code before deployment rather than having to debug every node.

      I decided I needed temp, humidity, a couple of relays, a PIR, and (on a whim) a gesture sensor. I used a dht11 for the temp/hum and apds-9960 for the gestures. I wanted lots of possibilities for sending command to Domoticz which will then handle the logic e.g. using the gesture sensor, if I swipe up I'll get Domoticz to switch on a relay.

      I've also made it so that there is a separate switch that can deactivate motion sensing from the controller (don't know why, just seemed like a good idea. In addition I made a temperature adjustment child by declaring a dimmer switch then using the value to calculate an adjustment where 50% dimmed is 0 degrees offset. 0% = -5 degrees, 25% = -2.5 degrees, 50% = no offset, 75% = +2.5 degrees, 76% = +2.6 degrees etc.

      Fianlly I thought that dealing with the status of the relays, the PIR, and temp offset might be different depending on the sensor (e.g. I might want a relay connected to a fridge to default to "On" if the node rebooted for any reason, but e.g. the light in the garage to default to off, I might also want the sensor to get the last value from eeprom as in the relay example sketch or even query Domoticz to find the current state according to it).

      To do this I added a StoreXState variable that can be 1,2, or 3 and after a power cycle, it decides if the "Motion Enable/disable", Relays, & Temp Offset states are either default, got from EEPROM, or requested from Domoticz.

      I'm pretty pleased with it as a first attempt, so thought I'd share the code. A few things:

      • there are loads of Serial Prints so I could see what was happening
      • if debug is defined then it uses loads of memory (like 99%) on a pro mini and I got really odd behaviour from the node
      • I've borrowed heavily from other people's code, thank you to them
      • it is currently a jumble of wires on my desk so no pics yet
      • massive thanks to the MySensors team
      /**
         The MySensors Arduino library handles the wireless radio link and protocol
         between your home built sensors/actuators and HA controller of choice.
         The sensors forms a self healing radio network with optional repeaters. Each
         repeater and gateway builds a routing tables in EEPROM which keeps track of the
         network topology allowing messages to be routed to nodes.
      
         Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
         Copyright (C) 2013-2015 Sensnology AB
         Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
      
         Documentation: http://www.mysensors.org
         Support Forum: http://forum.mysensors.org
      
         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.
      
       *******************************
      
      Hardware Connections:
      
      IMPORTANT: The APDS-9960 can only accept 3.3V!
       
       Arduino Pin  APDS-9960 Board  Function
       
       3.3V         VCC              Power
       GND          GND              Ground
       A4           SDA              I2C Data
       A5           SCL              I2C Clock
       2            INT              Interrupt
      */
      
      // Enable debug prints to serial monitor
      //#define MY_DEBUG
      
      // Enable and select radio type attached
      #define MY_RADIO_NRF24
      //#define MY_RADIO_RFM69
      
      // Enable repeater functionality for this node
      #define MY_REPEATER_FEATURE
      #define MY_NODE_ID 103
      
      #include <Wire.h>
      #include <SparkFun_APDS9960.h>
      #include <SPI.h>
      #include <DHT.h>
      #include <MySensors.h>
      
      #define RELAY_1_PIN  5  // Arduino Digital I/O pin number for first relay 
      #define RELAY_2_PIN  6  // Arduino Digital I/O pin number for second relay 
      
      #define RELAY_ON 0  // GPIO value to write to turn on attached relay - domoticz needs 0 for on, 1 for off
      #define RELAY_OFF 1 // GPIO value to write to turn off attached relay
      
      
      #define CHILD_ID_RELAY1 1
      #define CHILD_ID_RELAY2 2
      #define CHILD_ID_HUM 3
      #define CHILD_ID_TEMP 4
      #define CHILD_ID_MOT 5   // Id of the sensor child
      #define CHILD_ID_TEMP_OFFSET 20
      #define CHILD_ID_MotionOnOff 21
      #define CHILD_ID_GESTUREUD 30  //up down gesture
      #define CHILD_ID_GESTURELR 31 //left right gesture
      #define CHILD_ID_GESTURENF 32 //near far gesture
      #define GestureUp 1 //command to send on gesture, 0 = off, 1 = on
      #define GestureDown 0 //command to send on gesture, 0 = off, 1 = on
      #define GestureLeft 0 //command to send on gesture, 0 = off, 1 = on
      #define GestureRight 1 //command to send on gesture, 0 = off, 1 = on
      #define GestureNear 0 //command to send on gesture, 0 = off, 1 = on
      #define GestureFar 1 //command to send on gesture, 0 = off, 1 = on
      #define GestureSensitivity  0  // value between 0 & 3, 0 most sensitive, 3 least
      
      
      #define HUMIDITY_SENSOR_DIGITAL_PIN 7
      
      #define APDS9960_INT    2  // for the APDS-9960 - Needs to be an interrupt pin
      
      #define DIGITAL_INPUT_SENSOR 3   // The digital input you attached your motion sensor.  (Only 2 and 3 generates interrupt!)
      #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // for the PIR - Usually the interrupt = pin -2 (on uno/nano anyway)
      
      SparkFun_APDS9960 apds = SparkFun_APDS9960();
      volatile int isr_flag = 0; //handles the interrupt process for gesture sensor
      
      
      
      
      unsigned long SLEEP_TIME = 120000; // Sleep time between reads of temp/hum (in milliseconds)
      unsigned long lastRefreshTime = 0; // Use this to implement a non-blocking delay function
      unsigned long RefreshCyclesUntilForcedUpdate = 5; // use to force a temp/hum reading after 'n' SLEEP_TIME cycles even if value hasn't changed, set v.high (e.g. 10,000) to effectively disable
      unsigned long ForceUpdate = RefreshCyclesUntilForcedUpdate * SLEEP_TIME; // Force update read after this amount of time
      unsigned long lastForceUpdateTime = 0; // Use this to implement a non-blocking delay function that forces update even if temp hasn't changed
      
      DHT dht;
      float lastTemp;
      float lastHum;
      
      boolean metric = true;
      
      bool lastTripped = 0;   // Used to store last motion sensor value
      boolean FirstBoot = true;
      
      // the below allow you decide if the default state of certain sensors is stored in EEPROM or Controller or always start in a certain state
      // 1 = EEPROM 2 = Controller (e.g. Domoticz), 3 or any other number then they start as with default values
      // If option 1 or 3 is selected then we will also send a message to the controller to tell it the states
      
      // unsigned long WaitBetweenSendingMessages = 150; //how long to wait between sending messages and presenting nodes
      int StoreTempOffset = 2;
      int StoreMotionOnOff = 2;
      int StoreRelayState = 2;
      
      // below are the default states that you can set to be loaded at boot each time
      // these will be reverted to in EEPROM values are corrupted
      bool MotionON = 0;      //enable/disable the PIR reports (1=enabled 0=disabled)
      bool RelayState = 0;    // default state for relays (1 = on, 0 = off)
      
      int TempOffset = 50; // a value of 50 means that initial offset is zero when first loaded.  Tempoffset can be between 0-100
      volatile float ActualTempOffset = (TempOffset / 10.0) - 5.0;  //Adjust temp offset to a decimal between +/- 5 degrees
      
      MyMessage msgHum(CHILD_ID_HUM, V_HUM);
      MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
      MyMessage msgMot(CHILD_ID_MOT, V_TRIPPED);
      MyMessage msgTempOffset(CHILD_ID_TEMP_OFFSET, V_PERCENTAGE);
      MyMessage msgMotOnOff(CHILD_ID_MotionOnOff, V_STATUS);
      MyMessage msgRelay1(1, V_STATUS);
      MyMessage msgRelay2(2, V_STATUS);
      MyMessage msgGestureUpDown(CHILD_ID_GESTUREUD, V_STATUS);
      MyMessage msgGestureLeftRight(CHILD_ID_GESTURELR, V_STATUS);
      MyMessage msgGestureNearFar(CHILD_ID_GESTURENF, V_STATUS);
      
      void before()
      {
      
        // Then set relay pins in output mode
        pinMode(RELAY_1_PIN , OUTPUT);
        pinMode(RELAY_2_PIN , OUTPUT);
        
      
        
        pinMode(DIGITAL_INPUT_SENSOR, INPUT);      // sets the motion sensor digital pin as input
      
      
      
      
      }
      
      void setup()
      {
        // Set gesture sensor interrupt pin as input
        pinMode(APDS9960_INT, INPUT);
          
          //define sensitivity of gesture sensor
          apds.setGestureGain( GestureSensitivity );
        
        dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
        // metric = getConfig().isMetric;
      
        // Initialize interrupt service routine for gestures
        
       attachInterrupt(digitalPinToInterrupt(APDS9960_INT), interruptRoutine, FALLING);
      
      
        // Initialize APDS-9960 (configure I2C and initial values)
        if ( apds.init() ) {
          Serial.println(F("APDS-9960 initialization complete"));
        } else {
          Serial.println(F("Something went wrong during APDS-9960 init!"));
        }
        
        // Start running the APDS-9960 gesture sensor engine
        if ( apds.enableGestureSensor(true) ) {
          Serial.println(F("Gesture sensor is now running"));
        } else {
          Serial.println(F("Something went wrong during gesture sensor init!"));
        }
      
      }
      
      void presentation()
      {
        // Send the sketch version information to the gateway and Controller
        sendSketchInfo("MyMultiSensor", "1.4");
      
      
        // Register all sensors to gw (they will be created as child devices)
        present(CHILD_ID_RELAY1, S_BINARY, "Relay 1");
        wait(100);
        present(CHILD_ID_RELAY2, S_BINARY, "Relay 2");
        wait(100);
        present(CHILD_ID_HUM, S_HUM, "Humidity");
        wait(100);
        present(CHILD_ID_TEMP, S_TEMP, "Temperature");
        wait(100);
        present(CHILD_ID_MOT, S_MOTION, "PIR");
        wait(100);
        present(CHILD_ID_TEMP_OFFSET, S_DIMMER, "Temp Calibration");
        wait(100);
        present(CHILD_ID_MotionOnOff, S_BINARY, "PIR Enable/Disable");
        wait(100);
        present(CHILD_ID_GESTUREUD, S_BINARY, "Gesture Up/Down");
        wait(100);
        present(CHILD_ID_GESTURELR, S_BINARY, "Gesture Left/Right");
        wait(100);
        present(CHILD_ID_GESTURENF, S_BINARY, "Gesture Near/Far");
        wait(100);  
       
      }
      
      void firstboot()
      {
        
        }
        
        
      void loop()
      {
        if(FirstBoot)
        {
        Serial.println("FirstBoot sequence started");
        // Set relay to correct state (using eeprom/default/domoticz value)
        switch (StoreRelayState) {
          case 1:
            if (loadState(CHILD_ID_RELAY1) == 1)  {
              digitalWrite(RELAY_1_PIN , RELAY_ON);  
            }
            else
            {
              digitalWrite(RELAY_1_PIN , RELAY_OFF);  
            }
            if (loadState(CHILD_ID_RELAY2) == 1)  {
              digitalWrite(RELAY_2_PIN , RELAY_ON);  
            }
            else
            {
              digitalWrite(RELAY_2_PIN , RELAY_OFF);  
            }
             
              Serial.print("Relay 1 loadstate from EEPROM: ");
              Serial.println(loadState(CHILD_ID_RELAY1));
              Serial.print("Relay 2 loadstate from EEPROM: ");
              Serial.println(loadState(CHILD_ID_RELAY2));
              //best let domoticz know
              send(msgRelay1.set(RelayState));
              wait(100);
              send(msgRelay2.set(RelayState));
              wait(100);
              
              Serial.print("Default Relay state sent to Domoticz: ");
              Serial.println(RelayState);
          break;
            
          case 2: 
              //get Relay Statuses from Domoticz
                Serial.println("Relay status requested from Domoticz");          
                request(CHILD_ID_RELAY1, V_STATUS);
                wait(1000);
                request(CHILD_ID_RELAY2, V_STATUS);
                wait(1000);
                
          break;
          default:
              digitalWrite(RELAY_1_PIN ,  RelayState ? RELAY_ON : RELAY_OFF);
              digitalWrite(RELAY_2_PIN ,  RelayState ? RELAY_ON : RELAY_OFF);
              Serial.print("Relays set to default boot state: ");
              Serial.println(RelayState);
              //
              send(msgRelay1.set(RelayState));
              wait(100);
              send(msgRelay2.set(RelayState));
              wait(100);
              Serial.print("Default Relay state sent to Domoticz: ");
              Serial.println(RelayState);
          break;
          }
        
        // Set tempoffset to correct state (using eeprom/default/domoticz value) - needs to be 0 - 100 otherwise set to no offset
      
          switch (StoreTempOffset)  {
            case 1:
              if (loadState(CHILD_ID_TEMP_OFFSET) >100) {
                ActualTempOffset = 5.0;
              }
              else
              {
                TempOffset = loadState(CHILD_ID_TEMP_OFFSET);
                ActualTempOffset = (TempOffset / 10.0) - 5.0;
              }
                Serial.print("Temp Offset retrieved from eeprom: ");
                Serial.println(ActualTempOffset);
                //let Domoticz know the value
                send(msgTempOffset.set(TempOffset, 1));
                wait(100);
                Serial.print("tempOffset from sensor sent to Domoticz: ");
                Serial.println(TempOffset);
            break;
            case 2:
              //get TempOffSet from Domoticz 
                Serial.println("TempOffset value requested from Domoticz");         
                request(CHILD_ID_TEMP_OFFSET, V_PERCENTAGE);
                wait(1000);
                
            break;
            default:
                Serial.print("Default boot temperature offset is: ");
                Serial.println(ActualTempOffset);
                //let Domoticz know the value
                send(msgTempOffset.set(TempOffset, 1));
                wait(100);
                Serial.print("tempOffset from sensor sent to Domoticz: ");
                Serial.println(TempOffset);    
            break;
          }
      
          // Set PIR enable/disable (using eeprom/default/domoticz value)  
          switch(StoreMotionOnOff) {
            case 1:
              if (loadState(CHILD_ID_MotionOnOff) == 1 || loadState(CHILD_ID_MotionOnOff) == 0) { //check for odd values in EEPROM
                MotionON = loadState(CHILD_ID_MotionOnOff);  
              }
              else {
                Serial.print("Error in PIR Enable/Disable Value in EEPROM");
                Serial.println("Default value used instead");
              } 
              //Let Domoticz know
                send(msgMotOnOff.set(MotionON, 1));
                wait(100);
                Serial.print("PIR ENABLE status from sensor sent to Domoticz: ");
                Serial.println(MotionON);  
            break;
             
            case 2:
              //get PIR enable status from Domoticz
                Serial.println("MotionOnOff status requested from Domoticz");          
                request(CHILD_ID_MotionOnOff, V_STATUS);
                wait(1000);
      
      
            break;
                
      
            default:
                send(msgMotOnOff.set(MotionON, 1));
                wait(100);
                Serial.print("Default PIR ENABLE status used and sent to Domoticz: ");
                Serial.println(MotionON); 
            break;
          }
        wait(2000);
        Serial.println("First boot checks completed");
        FirstBoot = false;
        }
      
        //check if gesture sensor interrupt is triggered
          if( isr_flag == 1 ) {
          detachInterrupt(digitalPinToInterrupt(APDS9960_INT));
          handleGesture();
          isr_flag = 0;
          attachInterrupt(digitalPinToInterrupt(APDS9960_INT), interruptRoutine, FALLING);
      
        }
        
        // Read digital motion value
        if (MotionON) {
          bool tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH;
          if (lastTripped != tripped) {
            Serial.print("New Motion State: ");
            Serial.println(tripped);
            // Send tripped value to gw
            send(msgMot.set(tripped ? "1" : "0"));
            lastTripped = tripped;
          }
        }
      
      
      
        boolean needRefresh = (millis() - lastRefreshTime) > SLEEP_TIME;
        if (needRefresh)
        {
          Serial.println("I'm alive");
          lastRefreshTime = millis();
      
          float temperature = dht.getTemperature() + ActualTempOffset;
          if (isnan(temperature)) {
            Serial.println("Failed reading temperature from DHT");
          }
          else if (temperature != lastTemp  ) {
            lastTemp = temperature;
            if (!metric) {
              temperature = dht.toFahrenheit(temperature);
            }
            send(msgTemp.set(temperature, 1));
            Serial.print("T: ");
            Serial.println(temperature);
          }
          else if (millis() - lastForceUpdateTime > ForceUpdate) {
            lastTemp = temperature;
            lastForceUpdateTime = millis();
            if (!metric) {
              temperature = dht.toFahrenheit(temperature);
            }
            send(msgTemp.set(temperature, 1));
            Serial.print("T: ");
            Serial.println(temperature);
          }
      
      
          float humidity = dht.getHumidity();
          if (isnan(humidity)) {
            Serial.println("Failed reading humidity from DHT");
          }
          else if (humidity != lastHum) {
            lastHum = humidity;
            send(msgHum.set(humidity, 1));
            Serial.print("H: ");
            Serial.println(humidity);
          }
          else if (millis() - lastForceUpdateTime > ForceUpdate) {
            lastHum = humidity;
            lastForceUpdateTime = millis();
            send(msgHum.set(humidity, 1));
            Serial.print("H: ");
            Serial.println(humidity);
          }
        }
      
      }
      
      void interruptRoutine() {
        //Serial.println("Interrupt Routine started");
        isr_flag = 1;
      }
      
      void handleGesture() {
      
       
        
          if ( apds.isGestureAvailable() ) {
      
      
          switch ( apds.readGesture() ) {
            case DIR_UP:
              Serial.println("UP");
              send(msgGestureUpDown.set(GestureUp));
              break;
            case DIR_DOWN:
              Serial.println("DOWN");
              send(msgGestureUpDown.set(GestureDown));
              break;
            case DIR_LEFT:
              Serial.println("LEFT");
              send(msgGestureLeftRight.set(GestureLeft));
              break;
            case DIR_RIGHT:
              Serial.println("RIGHT");
              send(msgGestureLeftRight.set(GestureRight));
              break;
            case DIR_NEAR:
              Serial.println("NEAR");
              send(msgGestureNearFar.set(GestureNear));
              break;
            case DIR_FAR:
              Serial.println("FAR");
              send(msgGestureNearFar.set(GestureFar));
              break;
            default:
              Serial.println("NONE");
          }
        
         }
        
      }
      
      void receive(const MyMessage &message)
      {
       //For debugging just checking why controller sent a msg e.g. was it in response to a request by node  
        switch (message.getCommand()) {                                 // message.getCommand will give us the command type of the incomming message
            case C_SET: 
              Serial.println("msg sent by controller");
            break;
            case C_REQ:
              Serial.println("msg state sent by controller in response to request");
            break;
            default:
              Serial.println("msg isn't C_SET or C_REQ so what you gonna do?");
            break;
         }
        // We only expect V_STATUS (for relays) or V_Percentage (for temp offset) message from controller. But we better check anyway.
        if (message.type == V_STATUS) {
          // Change relay state
      
          if (message.sensor == 1 ) {
            
            digitalWrite(RELAY_1_PIN , message.getBool() ? RELAY_ON : RELAY_OFF);
            // Store state in eeprom
            saveState(message.sensor, message.getBool());
            // Write some debug info
            Serial.print("Incoming Relay 1 State: ");
            Serial.println(message.getBool());
          }
          else if (message.sensor == 2) {
           
            digitalWrite(RELAY_2_PIN , message.getBool() ? RELAY_ON : RELAY_OFF);
            // Store state in eeprom
            saveState(message.sensor, message.getBool());
            // Write some debug info
            Serial.print("Incoming Relay 2 State: ");
            Serial.println(message.getBool());
          }
          else if (message.sensor == CHILD_ID_MotionOnOff) {
      
              if (message.getBool() == 1) {
                MotionON = 1;
                saveState(CHILD_ID_MotionOnOff, MotionON);
              }
              else
              {
                MotionON = 0;
              }
          Serial.print("Motion Sensor on/off state: ");
          Serial.println(MotionON);
          saveState(CHILD_ID_MotionOnOff, MotionON);
          
          }
        }
        else if (message.type == V_PERCENTAGE)  {
          int TempOffset = atoi(message.data);
          ActualTempOffset = (TempOffset / 10.0) - 5.0;
          Serial.print("Temp Offset value: ");
          Serial.println(ActualTempOffset);
          saveState(CHILD_ID_TEMP_OFFSET, TempOffset);
      
        }
        
      
      }
      
      
      dbemowskD Offline
      dbemowskD Offline
      dbemowsk
      wrote on last edited by
      #2

      @Ben-Andrewes If you have loads of serial prints, that is probably why you have 99% memory usage. I bet if you pull those out, or at least use a define to be able to turn them on and off like, or even with MY_DEBUG or the like, you can see the difference when shutting them off at compile time.

      Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
      Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

      B 1 Reply Last reply
      1
      • dbemowskD dbemowsk

        @Ben-Andrewes If you have loads of serial prints, that is probably why you have 99% memory usage. I bet if you pull those out, or at least use a define to be able to turn them on and off like, or even with MY_DEBUG or the like, you can see the difference when shutting them off at compile time.

        B Offline
        B Offline
        Ben Andrewes
        wrote on last edited by
        #3

        @dbemowsk Sounds like a good idea, but I'm not sure how... does that mean I'd do something like

        #define serialmessages

        then around each Serial.print

        do some sort of if clause?

        Thanks in advance for any advice

        dbemowskD 1 Reply Last reply
        0
        • B Ben Andrewes

          @dbemowsk Sounds like a good idea, but I'm not sure how... does that mean I'd do something like

          #define serialmessages

          then around each Serial.print

          do some sort of if clause?

          Thanks in advance for any advice

          dbemowskD Offline
          dbemowskD Offline
          dbemowsk
          wrote on last edited by dbemowsk
          #4

          @Ben-Andrewes This is really easy. The code for the debug is like this:

          //define this at the top of your sketch
          #define MY_DEBUG
          
          ....other code....
          #ifdef MY_DEBUG
          ...your debug code here....
          #endif
          

          Then if you comment out the define at the top of the sketch, all of the debug code is ignored when compiling. Change MY_DEBUG to whatever name you want if you want to only block out certain bits of code at compile time.

          Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
          Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

          1 Reply Last reply
          1
          • B Offline
            B Offline
            Ben Andrewes
            wrote on last edited by
            #5

            Thanks for the help , much appreciated. I added 3 #defines so I have a bit of control over what gets cut out i.e. the verbose stuff is really only useful at development stage but I guess if I start getting weird sensor readings then it will be helpful to have a serial reading of what the sensors are reporting. If I comment out all 4 defines then I use less than half the available memory

            #define MY_DEBUG //mysensors protocol debug prints
            #define SerialPrints //all the verbose messages that state what the program is doing at certain points e.g. "Requesting message from Domoticz"
            #define ImAlive  // Heartbeat in main loop
            #define SerialReadings  //Sensor readings e.g. temp and certain messages from Domoticz e.g. if turning a relay on
            

            I might be starting to get a bit of an idea of how this stuff works... :)

            dbemowskD 1 Reply Last reply
            0
            • B Ben Andrewes

              Thanks for the help , much appreciated. I added 3 #defines so I have a bit of control over what gets cut out i.e. the verbose stuff is really only useful at development stage but I guess if I start getting weird sensor readings then it will be helpful to have a serial reading of what the sensors are reporting. If I comment out all 4 defines then I use less than half the available memory

              #define MY_DEBUG //mysensors protocol debug prints
              #define SerialPrints //all the verbose messages that state what the program is doing at certain points e.g. "Requesting message from Domoticz"
              #define ImAlive  // Heartbeat in main loop
              #define SerialReadings  //Sensor readings e.g. temp and certain messages from Domoticz e.g. if turning a relay on
              

              I might be starting to get a bit of an idea of how this stuff works... :)

              dbemowskD Offline
              dbemowskD Offline
              dbemowsk
              wrote on last edited by
              #6

              @Ben-Andrewes I think you got it. Great job.

              Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
              Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

              1 Reply Last reply
              0
              • BulldogLowellB Offline
                BulldogLowellB Offline
                BulldogLowell
                Contest Winner
                wrote on last edited by
                #7

                @Ben-Andrewes said in MultiSensor - temp, hum, PIR, gesture with various controller overrides:

                you do it here:

                Serial.println(F("APDS-9960 initialization complete"));
                

                but not here:

                Serial.println("Relay status requested from Domoticz");
                

                You shouuld wrap every every every string constant in the F Macro

                dbemowskD B 2 Replies Last reply
                1
                • BulldogLowellB BulldogLowell

                  @Ben-Andrewes said in MultiSensor - temp, hum, PIR, gesture with various controller overrides:

                  you do it here:

                  Serial.println(F("APDS-9960 initialization complete"));
                  

                  but not here:

                  Serial.println("Relay status requested from Domoticz");
                  

                  You shouuld wrap every every every string constant in the F Macro

                  dbemowskD Offline
                  dbemowskD Offline
                  dbemowsk
                  wrote on last edited by
                  #8

                  @BulldogLowell I was unaware of this, good call though. I looked it up because I wasn't sure what the F() method was. Here's a link for others that may be curious.
                  https://playground.arduino.cc/Learning/Memory

                  Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
                  Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

                  1 Reply Last reply
                  2
                  • BulldogLowellB BulldogLowell

                    @Ben-Andrewes said in MultiSensor - temp, hum, PIR, gesture with various controller overrides:

                    you do it here:

                    Serial.println(F("APDS-9960 initialization complete"));
                    

                    but not here:

                    Serial.println("Relay status requested from Domoticz");
                    

                    You shouuld wrap every every every string constant in the F Macro

                    B Offline
                    B Offline
                    Ben Andrewes
                    wrote on last edited by
                    #9

                    @BulldogLowell I didn't even notice the F() thing as I copied that bit of code from the sparkfun example. Thanks for the pointer though - I've update all the Serialprints to include it.

                    I guess that I am so used to not even thinking about memory limits with the GB of mem in everyday device that it hadn't even crossed my mind that I could (and should) choose where to store stuff. 30 yrs ago with my Spectrum 48k it was something to be careful of... The pro mini has almost the same amount memory!

                    1 Reply Last reply
                    0
                    • B Offline
                      B Offline
                      Ben Andrewes
                      wrote on last edited by
                      #10

                      I've put the updated code below in case it is useful to anyone..

                      I also found that the relays were switching on and off during first boot which was a pain, again my solution is below (I couldn't find a reference to this problem in the forums). As the relays are active low, I set the relay pins to high before setting them as outputs

                        digitalWrite(RELAY_1_PIN , HIGH);  //stops relays cycling state during boot
                        digitalWrite(RELAY_2_PIN , HIGH);  //stops relays cycling state during boot
                        
                        pinMode(RELAY_1_PIN , OUTPUT);
                        pinMode(RELAY_2_PIN , OUTPUT);
                      
                      /**
                         The MySensors Arduino library handles the wireless radio link and protocol
                         between your home built sensors/actuators and HA controller of choice.
                         The sensors forms a self healing radio network with optional repeaters. Each
                         repeater and gateway builds a routing tables in EEPROM which keeps track of the
                         network topology allowing messages to be routed to nodes.
                      
                         Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
                         Copyright (C) 2013-2015 Sensnology AB
                         Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
                      
                         Documentation: http://www.mysensors.org
                         Support Forum: http://forum.mysensors.org
                      
                         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.
                      
                       *******************************
                      
                      Hardware Connections:
                      
                      IMPORTANT: The APDS-9960 can only accept 3.3V!
                       
                       Arduino Pin  APDS-9960 Board  Function
                       
                       3.3V         VCC              Power
                       GND          GND              Ground
                       A4           SDA              I2C Data
                       A5           SCL              I2C Clock
                       2            INT              Interrupt
                      */
                      
                      // Enable debug prints to serial monitor
                      // #define MY_DEBUG //mysensors protocol debug prints
                      // #define SerialPrints //all the verbose messages that state what the program is doing at certain points e.g. "Requesting message from Domoticz"
                      // #define ImAlive  // Heartbeat in main loop
                      #define SerialReadings  //Sensor readings e.g. temp and certain messages from Domoticz e.g. if turning a relay on
                      
                      // Enable and select radio type attached
                      #define MY_RADIO_NRF24
                      //#define MY_RADIO_RFM69
                      
                      // Enable repeater functionality for this node
                      #define MY_REPEATER_FEATURE
                      #define MY_NODE_ID 103
                      
                      #include <Wire.h>
                      #include <SparkFun_APDS9960.h>
                      #include <SPI.h>
                      #include <DHT.h>
                      #include <MySensors.h>
                      
                      #define RELAY_1_PIN  5  // Arduino Digital I/O pin number for first relay 
                      #define RELAY_2_PIN  6  // Arduino Digital I/O pin number for second relay 
                      
                      #define RELAY_ON 0  // GPIO value to write to turn on attached relay - domoticz needs 0 for on, 1 for off
                      #define RELAY_OFF 1 // GPIO value to write to turn off attached relay
                      
                      
                      #define CHILD_ID_RELAY1 1
                      #define CHILD_ID_RELAY2 2
                      #define CHILD_ID_HUM 3
                      #define CHILD_ID_TEMP 4
                      #define CHILD_ID_MOT 5   // Id of the sensor child
                      #define CHILD_ID_TEMP_OFFSET 20
                      #define CHILD_ID_MotionOnOff 21
                      #define CHILD_ID_GESTUREUD 30  //up down gesture
                      #define CHILD_ID_GESTURELR 31 //left right gesture
                      #define CHILD_ID_GESTURENF 32 //near far gesture
                      #define GestureUp 1 //command to send on gesture, 0 = off, 1 = on
                      #define GestureDown 0 //command to send on gesture, 0 = off, 1 = on
                      #define GestureLeft 0 //command to send on gesture, 0 = off, 1 = on
                      #define GestureRight 1 //command to send on gesture, 0 = off, 1 = on
                      #define GestureNear 0 //command to send on gesture, 0 = off, 1 = on
                      #define GestureFar 1 //command to send on gesture, 0 = off, 1 = on
                      #define GestureSensitivity  0  // value between 0 & 3, 0 most sensitive, 3 least
                      
                      
                      #define HUMIDITY_SENSOR_DIGITAL_PIN 7
                      
                      #define APDS9960_INT    2  // for the APDS-9960 - Needs to be an interrupt pin
                      
                      #define DIGITAL_INPUT_SENSOR 3   // The digital input you attached your motion sensor.  (Only 2 and 3 generates interrupt!)
                      #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // for the PIR - Usually the interrupt = pin -2 (on uno/nano anyway)
                      
                      SparkFun_APDS9960 apds = SparkFun_APDS9960();
                      volatile int isr_flag = 0; //handles the interrupt process for gesture sensor
                      
                      
                      
                      
                      unsigned long SLEEP_TIME = 120000; // Sleep time between reads of temp/hum (in milliseconds)
                      unsigned long lastRefreshTime = 0; // Use this to implement a non-blocking delay function
                      unsigned long RefreshCyclesUntilForcedUpdate = 5; // use to force a temp/hum reading after 'n' SLEEP_TIME cycles even if value hasn't changed, set v.high (e.g. 10,000) to effectively disable
                      unsigned long ForceUpdate = RefreshCyclesUntilForcedUpdate * SLEEP_TIME; // Force update read after this amount of time
                      unsigned long lastForceUpdateTime = 0; // Use this to implement a non-blocking delay function that forces update even if temp hasn't changed
                      
                      DHT dht;
                      float lastTemp;
                      float lastHum;
                      
                      boolean metric = true;
                      
                      bool lastTripped = 0;   // Used to store last motion sensor value
                      boolean FirstBoot = true;
                      
                      // the below allow you decide if the default state of certain sensors is stored in EEPROM or Controller or always start in a certain state
                      // 1 = EEPROM 2 = Controller (e.g. Domoticz), 3 or any other number then they start as with default values
                      // If option 1 or 3 is selected then we will also send a message to the controller to tell it the states
                      
                      // unsigned long WaitBetweenSendingMessages = 150; //how long to wait between sending messages and presenting nodes
                      int StoreTempOffset = 2;
                      int StoreMotionOnOff = 2;
                      int StoreRelayState = 2;
                      
                      // below are the default states that you can set to be loaded at boot each time
                      // these will be reverted to in EEPROM values are corrupted
                      bool MotionON = 0;      //enable/disable the PIR reports (1=enabled 0=disabled)
                      bool RelayState = 0;    // default state for relays (1 = on, 0 = off)
                      
                      int TempOffset = 50; // a value of 50 means that initial offset is zero when first loaded.  Tempoffset can be between 0-100
                      volatile float ActualTempOffset = (TempOffset / 10.0) - 5.0;  //Adjust temp offset to a decimal between +/- 5 degrees
                      
                      MyMessage msgHum(CHILD_ID_HUM, V_HUM);
                      MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
                      MyMessage msgMot(CHILD_ID_MOT, V_TRIPPED);
                      MyMessage msgTempOffset(CHILD_ID_TEMP_OFFSET, V_PERCENTAGE);
                      MyMessage msgMotOnOff(CHILD_ID_MotionOnOff, V_STATUS);
                      MyMessage msgRelay1(1, V_STATUS);
                      MyMessage msgRelay2(2, V_STATUS);
                      MyMessage msgGestureUpDown(CHILD_ID_GESTUREUD, V_STATUS);
                      MyMessage msgGestureLeftRight(CHILD_ID_GESTURELR, V_STATUS);
                      MyMessage msgGestureNearFar(CHILD_ID_GESTURENF, V_STATUS);
                      
                      void before()
                      {
                      
                        // Then set relay pins in output mode
                        digitalWrite(RELAY_1_PIN , HIGH);
                        digitalWrite(RELAY_2_PIN , HIGH);
                        
                        pinMode(RELAY_1_PIN , OUTPUT);
                        pinMode(RELAY_2_PIN , OUTPUT);
                        
                      
                        
                        pinMode(DIGITAL_INPUT_SENSOR, INPUT);      // sets the motion sensor digital pin as input
                      
                      
                      
                      
                      }
                      
                      void setup()
                      {
                        // Set gesture sensor interrupt pin as input
                        pinMode(APDS9960_INT, INPUT);
                          
                          //define sensitivity of gesture sensor
                          apds.setGestureGain( GestureSensitivity );
                        
                        dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
                        // metric = getConfig().isMetric;
                      
                        // Initialize interrupt service routine for gestures
                        
                       attachInterrupt(digitalPinToInterrupt(APDS9960_INT), interruptRoutine, FALLING);
                      
                      
                        // Initialize APDS-9960 (configure I2C and initial values)
                      //Do not get rid of these if statements as they initialise the gesture sensing 
                        if ( apds.init() ) {
                          Serial.println(F("APDS-9960 initialization complete"));
                        } else {
                          Serial.println(F("Something went wrong during APDS-9960 init!"));
                        }
                      
                        
                        // Start running the APDS-9960 gesture sensor engine
                        if ( apds.enableGestureSensor(true) ) {
                          Serial.println(F("Gesture sensor is now running"));
                        } else {
                          Serial.println(F("Something went wrong during gesture sensor init!"));
                        }
                      
                      }
                      
                      void presentation()
                      {
                        // Send the sketch version information to the gateway and Controller
                        sendSketchInfo("MyMultiSensor", "1.4");
                      
                      
                        // Register all sensors to gw (they will be created as child devices)
                        present(CHILD_ID_RELAY1, S_BINARY, "Relay 1");
                        wait(100);
                        present(CHILD_ID_RELAY2, S_BINARY, "Relay 2");
                        wait(100);
                        present(CHILD_ID_HUM, S_HUM, "Humidity");
                        wait(100);
                        present(CHILD_ID_TEMP, S_TEMP, "Temperature");
                        wait(100);
                        present(CHILD_ID_MOT, S_MOTION, "PIR");
                        wait(100);
                        present(CHILD_ID_TEMP_OFFSET, S_DIMMER, "Temp Calibration");
                        wait(100);
                        present(CHILD_ID_MotionOnOff, S_BINARY, "PIR Enable/Disable");
                        wait(100);
                        present(CHILD_ID_GESTUREUD, S_BINARY, "Gesture Up/Down");
                        wait(100);
                        present(CHILD_ID_GESTURELR, S_BINARY, "Gesture Left/Right");
                        wait(100);
                        present(CHILD_ID_GESTURENF, S_BINARY, "Gesture Near/Far");
                        wait(100);  
                       
                      }
                      
                      void firstboot()
                      {
                        
                        }
                        
                        
                      void loop()
                      {
                        if(FirstBoot)
                        {
                      #ifdef SerialPrints
                        Serial.println(F("FirstBoot sequence started"));
                      #endif
                        // Set relay to correct state (using eeprom/default/domoticz value)
                        switch (StoreRelayState) {
                          case 1:
                            if (loadState(CHILD_ID_RELAY1) == 1)  {
                              digitalWrite(RELAY_1_PIN , RELAY_ON);  
                            }
                            else
                            {
                              digitalWrite(RELAY_1_PIN , RELAY_OFF);  
                            }
                            if (loadState(CHILD_ID_RELAY2) == 1)  {
                              digitalWrite(RELAY_2_PIN , RELAY_ON);  
                            }
                            else
                            {
                              digitalWrite(RELAY_2_PIN , RELAY_OFF);  
                            }
                      
                            #ifdef SerialPrints
                              Serial.print(F("Relay 1 loadstate from EEPROM: "));
                              Serial.println(loadState(CHILD_ID_RELAY1));
                              Serial.print(F("Relay 2 loadstate from EEPROM: "));
                              Serial.println(loadState(CHILD_ID_RELAY2));
                            #endif
                              //best let domoticz know
                              send(msgRelay1.set(RelayState));
                              wait(100);
                              send(msgRelay2.set(RelayState));
                              wait(100);
                      
                            #ifdef SerialPrints
                              Serial.print(F("Default Relay state sent to Domoticz: "));
                              Serial.println(RelayState);
                            #endif
                          break;
                            
                          case 2: 
                              //get Relay Statuses from Domoticz
                              #ifdef SerialPrints          
                                Serial.println(F("Relay status requested from Domoticz"));          
                              #endif
                                request(CHILD_ID_RELAY1, V_STATUS);
                                wait(1000);
                                request(CHILD_ID_RELAY2, V_STATUS);
                                wait(1000);
                                
                          break;
                          default:
                              digitalWrite(RELAY_1_PIN ,  RelayState ? RELAY_ON : RELAY_OFF);
                              digitalWrite(RELAY_2_PIN ,  RelayState ? RELAY_ON : RELAY_OFF);
                            #ifdef SerialPrints        
                              Serial.print(F("Relays set to default boot state: "));
                              Serial.println(RelayState);
                            #endif
                              send(msgRelay1.set(RelayState));
                              wait(100);
                              send(msgRelay2.set(RelayState));
                              wait(100);
                            #ifdef SerialPrints
                              Serial.print(F("Default Relay state sent to Domoticz: "));
                              Serial.println(RelayState);
                            #endif
                          break;
                          }
                        
                        // Set tempoffset to correct state (using eeprom/default/domoticz value) - needs to be 0 - 100 otherwise set to no offset
                      
                          switch (StoreTempOffset)  {
                            case 1:
                              if (loadState(CHILD_ID_TEMP_OFFSET) >100) {
                                ActualTempOffset = 5.0;
                              }
                              else
                              {
                                TempOffset = loadState(CHILD_ID_TEMP_OFFSET);
                                ActualTempOffset = (TempOffset / 10.0) - 5.0;
                              }
                              #ifdef SerialPrints
                                Serial.print(F("Temp Offset retrieved from eeprom: "));
                                Serial.println(ActualTempOffset);
                              #endif
                                //let Domoticz know the value
                                send(msgTempOffset.set(TempOffset, 1));
                                wait(100);
                              #ifdef SerialPrints
                                Serial.print(F("tempOffset from sensor sent to Domoticz: "));
                                Serial.println(TempOffset);
                              #endif
                            break;
                            case 2:
                              //get TempOffSet from Domoticz 
                              #ifdef SerialPrints
                                Serial.println(F("TempOffset value requested from Domoticz"));         
                              #endif
                                request(CHILD_ID_TEMP_OFFSET, V_PERCENTAGE);
                                wait(1000);
                                
                            break;
                            default:
                              #ifdef SerialPrints
                                Serial.print(F("Default boot temperature offset is: "));
                                Serial.println(ActualTempOffset);
                              #endif
                                //let Domoticz know the value
                                send(msgTempOffset.set(TempOffset, 1));
                                wait(100);
                              #ifdef SerialPrints
                                Serial.print(F("tempOffset from sensor sent to Domoticz: "));
                                Serial.println(TempOffset);    
                              #endif
                            break;
                          }
                      
                          // Set PIR enable/disable (using eeprom/default/domoticz value)  
                          switch(StoreMotionOnOff) {
                            case 1:
                              if (loadState(CHILD_ID_MotionOnOff) == 1 || loadState(CHILD_ID_MotionOnOff) == 0) { //check for odd values in EEPROM
                                MotionON = loadState(CHILD_ID_MotionOnOff);  
                              }
                              else {
                              #ifdef SerialPrints
                                Serial.print(F("Error in PIR Enable/Disable Value in EEPROM"));
                                Serial.println(F("Default value used instead"));
                              #endif
                              } 
                              //Let Domoticz know
                                send(msgMotOnOff.set(MotionON, 1));
                                wait(100);
                              #ifdef SerialPrints  
                                Serial.print(F("PIR ENABLE status from sensor sent to Domoticz: "));
                                Serial.println(MotionON);  
                              #endif  
                            break;
                             
                            case 2:
                              //get PIR enable status from Domoticz
                              #ifdef SerialPrints
                                Serial.println(F("MotionOnOff status requested from Domoticz"));          
                              #endif
                                request(CHILD_ID_MotionOnOff, V_STATUS);
                                wait(1000);
                      
                      
                            break;
                                
                      
                            default:
                                send(msgMotOnOff.set(MotionON, 1));
                                wait(100);
                              #ifdef SerialPrints
                                Serial.print(F("Default PIR ENABLE status used and sent to Domoticz: "));
                                Serial.println(MotionON); 
                              #endif
                            break;
                          }
                        wait(2000);
                      #ifdef SerialPrints
                        Serial.println("First boot checks completed");
                      #endif 
                        #ifdef ImAlive
                          Serial.println(F("I'm alive"));
                        #endif 
                        FirstBoot = false;
                        }
                      
                        //check if gesture sensor interrupt is triggered
                          if( isr_flag == 1 ) {
                          detachInterrupt(digitalPinToInterrupt(APDS9960_INT));
                          handleGesture();
                          isr_flag = 0;
                          attachInterrupt(digitalPinToInterrupt(APDS9960_INT), interruptRoutine, FALLING);
                      
                        }
                        
                        // Read digital motion value
                        if (MotionON) {
                          bool tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH;
                          if (lastTripped != tripped) {
                          #ifdef SerialReadings
                            Serial.print(F("New Motion State: "));
                            Serial.println(tripped);
                          #endif
                            // Send tripped value to gw
                            send(msgMot.set(tripped ? "1" : "0"));
                            lastTripped = tripped;
                          }
                        }
                      
                      
                      
                        boolean needRefresh = (millis() - lastRefreshTime) > SLEEP_TIME;
                        if (needRefresh)
                        {
                        #ifdef ImAlive
                          Serial.println(F("I'm alive"));
                        #endif
                          lastRefreshTime = millis();
                      
                          float temperature = dht.getTemperature() + ActualTempOffset;
                          if (isnan(temperature)) {
                          #ifdef SerialReadings
                            Serial.println(F("Failed reading temperature from DHT"));
                          #endif
                          }
                          else if (temperature != lastTemp  ) {
                            lastTemp = temperature;
                            if (!metric) {
                              temperature = dht.toFahrenheit(temperature);
                            }
                            send(msgTemp.set(temperature, 1));
                          #ifdef SerialReadings
                            Serial.print(F("T: "));
                            Serial.println(temperature);
                          #endif
                          }
                          else if (millis() - lastForceUpdateTime > ForceUpdate) {
                            lastTemp = temperature;
                            lastForceUpdateTime = millis();
                            if (!metric) {
                              temperature = dht.toFahrenheit(temperature);
                            }
                            send(msgTemp.set(temperature, 1));
                          #ifdef SerialReadings
                            Serial.print("T: ");
                            Serial.println(temperature);
                          #endif
                          }
                      
                      
                          float humidity = dht.getHumidity();
                          if (isnan(humidity)) {
                          #ifdef SerialReadings
                            Serial.println(F("Failed reading humidity from DHT"));
                          #endif
                          }
                          else if (humidity != lastHum) {
                            lastHum = humidity;
                            send(msgHum.set(humidity, 1));
                          #ifdef SerialReadings  
                            Serial.print(F("H: "));
                            Serial.println(humidity);
                          #endif
                          }
                          else if (millis() - lastForceUpdateTime > ForceUpdate) {
                            lastHum = humidity;
                            lastForceUpdateTime = millis();
                            send(msgHum.set(humidity, 1));
                          #ifdef SerialReadings
                            Serial.print(F("H: "));
                            Serial.println(humidity);
                          #endif
                          }
                        }
                      
                      }
                      
                      void interruptRoutine() {
                      #ifdef SerialPrints  
                        Serial.println("Interrupt Routine started");
                      #endif
                        isr_flag = 1;
                      }
                      
                      void handleGesture() {
                      
                       
                        
                          if ( apds.isGestureAvailable() ) {
                      
                      
                          switch ( apds.readGesture() ) {
                            case DIR_UP:
                            #ifdef SerialReadings
                              Serial.println(F("UP"));
                            #endif
                              send(msgGestureUpDown.set(GestureUp));
                              break;
                            case DIR_DOWN:
                            #ifdef SerialReadings
                              Serial.println(F("DOWN"));
                            #endif
                              send(msgGestureUpDown.set(GestureDown));
                              break;
                            case DIR_LEFT:
                            #ifdef SerialReadings
                              Serial.println(F("LEFT"));
                            #endif
                              send(msgGestureLeftRight.set(GestureLeft));
                              break;
                            case DIR_RIGHT:
                            #ifdef SerialReadings
                              Serial.println(F("RIGHT"));
                            #endif
                              send(msgGestureLeftRight.set(GestureRight));
                              break;
                            case DIR_NEAR:
                            #ifdef SerialReadings
                              Serial.println(F("NEAR"));
                            #endif
                              send(msgGestureNearFar.set(GestureNear));
                              break;
                            case DIR_FAR:
                            #ifdef SerialReadings
                              Serial.println(F("FAR"));
                            #endif
                              send(msgGestureNearFar.set(GestureFar));
                              break;
                            default:
                            #ifdef SerialReadings
                              Serial.println(F("NONE"));
                            #endif
                            break;
                          }
                        
                         }
                        
                      }
                      
                      void receive(const MyMessage &message)
                      {
                       //For debugging just checking why controller sent a msg e.g. was it in response to a request by node  
                      #ifdef SerialPrints
                        switch (message.getCommand()) {                                 // message.getCommand will give us the command type of the incomming message
                            case C_SET: 
                              Serial.println(F("msg sent by controller"));
                            break;
                            case C_REQ:
                              Serial.println(F("msg state sent by controller in response to request"));
                            break;
                            default:
                              Serial.println(F("msg isn't C_SET or C_REQ so what you gonna do?"));
                            break;
                        }
                      #endif
                        // We only expect V_STATUS (for relays) or V_Percentage (for temp offset) message from controller. But we better check anyway.
                        if (message.type == V_STATUS) {
                          // Change relay state
                      
                          if (message.sensor == 1 ) {
                            
                            digitalWrite(RELAY_1_PIN , message.getBool() ? RELAY_ON : RELAY_OFF);
                            // Store state in eeprom
                            saveState(message.sensor, message.getBool());
                            // Write some debug info
                          #ifdef SerialReadings
                            Serial.print(F("Incoming Relay 1 State: "));
                            Serial.println(message.getBool());
                          #endif
                          }
                          else if (message.sensor == 2) {
                           
                            digitalWrite(RELAY_2_PIN , message.getBool() ? RELAY_ON : RELAY_OFF);
                            // Store state in eeprom
                            saveState(message.sensor, message.getBool());
                            // Write some debug info
                          #ifdef SerialReadings
                            Serial.print(F("Incoming Relay 2 State: "));
                            Serial.println(message.getBool());
                          #endif
                          }
                          else if (message.sensor == CHILD_ID_MotionOnOff) {
                      
                              if (message.getBool() == 1) {
                                MotionON = 1;
                                saveState(CHILD_ID_MotionOnOff, MotionON);
                              }
                              else
                              {
                                MotionON = 0;
                              }
                              #ifdef SerialReadings
                                Serial.print(F("Motion Sensor on/off state from Domoticz: "));
                                Serial.println(MotionON);
                              #endif
                                saveState(CHILD_ID_MotionOnOff, MotionON);
                          
                          }
                        }
                        else if (message.type == V_PERCENTAGE)  {
                          int TempOffset = atoi(message.data);
                          ActualTempOffset = (TempOffset / 10.0) - 5.0;
                        #ifdef SerialReadings
                          Serial.print(F("Temp Offset value from Domoticz: "));
                          Serial.println(ActualTempOffset);
                          saveState(CHILD_ID_TEMP_OFFSET, TempOffset);
                        #endif
                        }
                        
                      
                      }
                      
                      
                      dbemowskD BulldogLowellB 2 Replies Last reply
                      0
                      • B Ben Andrewes

                        I've put the updated code below in case it is useful to anyone..

                        I also found that the relays were switching on and off during first boot which was a pain, again my solution is below (I couldn't find a reference to this problem in the forums). As the relays are active low, I set the relay pins to high before setting them as outputs

                          digitalWrite(RELAY_1_PIN , HIGH);  //stops relays cycling state during boot
                          digitalWrite(RELAY_2_PIN , HIGH);  //stops relays cycling state during boot
                          
                          pinMode(RELAY_1_PIN , OUTPUT);
                          pinMode(RELAY_2_PIN , OUTPUT);
                        
                        /**
                           The MySensors Arduino library handles the wireless radio link and protocol
                           between your home built sensors/actuators and HA controller of choice.
                           The sensors forms a self healing radio network with optional repeaters. Each
                           repeater and gateway builds a routing tables in EEPROM which keeps track of the
                           network topology allowing messages to be routed to nodes.
                        
                           Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
                           Copyright (C) 2013-2015 Sensnology AB
                           Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
                        
                           Documentation: http://www.mysensors.org
                           Support Forum: http://forum.mysensors.org
                        
                           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.
                        
                         *******************************
                        
                        Hardware Connections:
                        
                        IMPORTANT: The APDS-9960 can only accept 3.3V!
                         
                         Arduino Pin  APDS-9960 Board  Function
                         
                         3.3V         VCC              Power
                         GND          GND              Ground
                         A4           SDA              I2C Data
                         A5           SCL              I2C Clock
                         2            INT              Interrupt
                        */
                        
                        // Enable debug prints to serial monitor
                        // #define MY_DEBUG //mysensors protocol debug prints
                        // #define SerialPrints //all the verbose messages that state what the program is doing at certain points e.g. "Requesting message from Domoticz"
                        // #define ImAlive  // Heartbeat in main loop
                        #define SerialReadings  //Sensor readings e.g. temp and certain messages from Domoticz e.g. if turning a relay on
                        
                        // Enable and select radio type attached
                        #define MY_RADIO_NRF24
                        //#define MY_RADIO_RFM69
                        
                        // Enable repeater functionality for this node
                        #define MY_REPEATER_FEATURE
                        #define MY_NODE_ID 103
                        
                        #include <Wire.h>
                        #include <SparkFun_APDS9960.h>
                        #include <SPI.h>
                        #include <DHT.h>
                        #include <MySensors.h>
                        
                        #define RELAY_1_PIN  5  // Arduino Digital I/O pin number for first relay 
                        #define RELAY_2_PIN  6  // Arduino Digital I/O pin number for second relay 
                        
                        #define RELAY_ON 0  // GPIO value to write to turn on attached relay - domoticz needs 0 for on, 1 for off
                        #define RELAY_OFF 1 // GPIO value to write to turn off attached relay
                        
                        
                        #define CHILD_ID_RELAY1 1
                        #define CHILD_ID_RELAY2 2
                        #define CHILD_ID_HUM 3
                        #define CHILD_ID_TEMP 4
                        #define CHILD_ID_MOT 5   // Id of the sensor child
                        #define CHILD_ID_TEMP_OFFSET 20
                        #define CHILD_ID_MotionOnOff 21
                        #define CHILD_ID_GESTUREUD 30  //up down gesture
                        #define CHILD_ID_GESTURELR 31 //left right gesture
                        #define CHILD_ID_GESTURENF 32 //near far gesture
                        #define GestureUp 1 //command to send on gesture, 0 = off, 1 = on
                        #define GestureDown 0 //command to send on gesture, 0 = off, 1 = on
                        #define GestureLeft 0 //command to send on gesture, 0 = off, 1 = on
                        #define GestureRight 1 //command to send on gesture, 0 = off, 1 = on
                        #define GestureNear 0 //command to send on gesture, 0 = off, 1 = on
                        #define GestureFar 1 //command to send on gesture, 0 = off, 1 = on
                        #define GestureSensitivity  0  // value between 0 & 3, 0 most sensitive, 3 least
                        
                        
                        #define HUMIDITY_SENSOR_DIGITAL_PIN 7
                        
                        #define APDS9960_INT    2  // for the APDS-9960 - Needs to be an interrupt pin
                        
                        #define DIGITAL_INPUT_SENSOR 3   // The digital input you attached your motion sensor.  (Only 2 and 3 generates interrupt!)
                        #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // for the PIR - Usually the interrupt = pin -2 (on uno/nano anyway)
                        
                        SparkFun_APDS9960 apds = SparkFun_APDS9960();
                        volatile int isr_flag = 0; //handles the interrupt process for gesture sensor
                        
                        
                        
                        
                        unsigned long SLEEP_TIME = 120000; // Sleep time between reads of temp/hum (in milliseconds)
                        unsigned long lastRefreshTime = 0; // Use this to implement a non-blocking delay function
                        unsigned long RefreshCyclesUntilForcedUpdate = 5; // use to force a temp/hum reading after 'n' SLEEP_TIME cycles even if value hasn't changed, set v.high (e.g. 10,000) to effectively disable
                        unsigned long ForceUpdate = RefreshCyclesUntilForcedUpdate * SLEEP_TIME; // Force update read after this amount of time
                        unsigned long lastForceUpdateTime = 0; // Use this to implement a non-blocking delay function that forces update even if temp hasn't changed
                        
                        DHT dht;
                        float lastTemp;
                        float lastHum;
                        
                        boolean metric = true;
                        
                        bool lastTripped = 0;   // Used to store last motion sensor value
                        boolean FirstBoot = true;
                        
                        // the below allow you decide if the default state of certain sensors is stored in EEPROM or Controller or always start in a certain state
                        // 1 = EEPROM 2 = Controller (e.g. Domoticz), 3 or any other number then they start as with default values
                        // If option 1 or 3 is selected then we will also send a message to the controller to tell it the states
                        
                        // unsigned long WaitBetweenSendingMessages = 150; //how long to wait between sending messages and presenting nodes
                        int StoreTempOffset = 2;
                        int StoreMotionOnOff = 2;
                        int StoreRelayState = 2;
                        
                        // below are the default states that you can set to be loaded at boot each time
                        // these will be reverted to in EEPROM values are corrupted
                        bool MotionON = 0;      //enable/disable the PIR reports (1=enabled 0=disabled)
                        bool RelayState = 0;    // default state for relays (1 = on, 0 = off)
                        
                        int TempOffset = 50; // a value of 50 means that initial offset is zero when first loaded.  Tempoffset can be between 0-100
                        volatile float ActualTempOffset = (TempOffset / 10.0) - 5.0;  //Adjust temp offset to a decimal between +/- 5 degrees
                        
                        MyMessage msgHum(CHILD_ID_HUM, V_HUM);
                        MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
                        MyMessage msgMot(CHILD_ID_MOT, V_TRIPPED);
                        MyMessage msgTempOffset(CHILD_ID_TEMP_OFFSET, V_PERCENTAGE);
                        MyMessage msgMotOnOff(CHILD_ID_MotionOnOff, V_STATUS);
                        MyMessage msgRelay1(1, V_STATUS);
                        MyMessage msgRelay2(2, V_STATUS);
                        MyMessage msgGestureUpDown(CHILD_ID_GESTUREUD, V_STATUS);
                        MyMessage msgGestureLeftRight(CHILD_ID_GESTURELR, V_STATUS);
                        MyMessage msgGestureNearFar(CHILD_ID_GESTURENF, V_STATUS);
                        
                        void before()
                        {
                        
                          // Then set relay pins in output mode
                          digitalWrite(RELAY_1_PIN , HIGH);
                          digitalWrite(RELAY_2_PIN , HIGH);
                          
                          pinMode(RELAY_1_PIN , OUTPUT);
                          pinMode(RELAY_2_PIN , OUTPUT);
                          
                        
                          
                          pinMode(DIGITAL_INPUT_SENSOR, INPUT);      // sets the motion sensor digital pin as input
                        
                        
                        
                        
                        }
                        
                        void setup()
                        {
                          // Set gesture sensor interrupt pin as input
                          pinMode(APDS9960_INT, INPUT);
                            
                            //define sensitivity of gesture sensor
                            apds.setGestureGain( GestureSensitivity );
                          
                          dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
                          // metric = getConfig().isMetric;
                        
                          // Initialize interrupt service routine for gestures
                          
                         attachInterrupt(digitalPinToInterrupt(APDS9960_INT), interruptRoutine, FALLING);
                        
                        
                          // Initialize APDS-9960 (configure I2C and initial values)
                        //Do not get rid of these if statements as they initialise the gesture sensing 
                          if ( apds.init() ) {
                            Serial.println(F("APDS-9960 initialization complete"));
                          } else {
                            Serial.println(F("Something went wrong during APDS-9960 init!"));
                          }
                        
                          
                          // Start running the APDS-9960 gesture sensor engine
                          if ( apds.enableGestureSensor(true) ) {
                            Serial.println(F("Gesture sensor is now running"));
                          } else {
                            Serial.println(F("Something went wrong during gesture sensor init!"));
                          }
                        
                        }
                        
                        void presentation()
                        {
                          // Send the sketch version information to the gateway and Controller
                          sendSketchInfo("MyMultiSensor", "1.4");
                        
                        
                          // Register all sensors to gw (they will be created as child devices)
                          present(CHILD_ID_RELAY1, S_BINARY, "Relay 1");
                          wait(100);
                          present(CHILD_ID_RELAY2, S_BINARY, "Relay 2");
                          wait(100);
                          present(CHILD_ID_HUM, S_HUM, "Humidity");
                          wait(100);
                          present(CHILD_ID_TEMP, S_TEMP, "Temperature");
                          wait(100);
                          present(CHILD_ID_MOT, S_MOTION, "PIR");
                          wait(100);
                          present(CHILD_ID_TEMP_OFFSET, S_DIMMER, "Temp Calibration");
                          wait(100);
                          present(CHILD_ID_MotionOnOff, S_BINARY, "PIR Enable/Disable");
                          wait(100);
                          present(CHILD_ID_GESTUREUD, S_BINARY, "Gesture Up/Down");
                          wait(100);
                          present(CHILD_ID_GESTURELR, S_BINARY, "Gesture Left/Right");
                          wait(100);
                          present(CHILD_ID_GESTURENF, S_BINARY, "Gesture Near/Far");
                          wait(100);  
                         
                        }
                        
                        void firstboot()
                        {
                          
                          }
                          
                          
                        void loop()
                        {
                          if(FirstBoot)
                          {
                        #ifdef SerialPrints
                          Serial.println(F("FirstBoot sequence started"));
                        #endif
                          // Set relay to correct state (using eeprom/default/domoticz value)
                          switch (StoreRelayState) {
                            case 1:
                              if (loadState(CHILD_ID_RELAY1) == 1)  {
                                digitalWrite(RELAY_1_PIN , RELAY_ON);  
                              }
                              else
                              {
                                digitalWrite(RELAY_1_PIN , RELAY_OFF);  
                              }
                              if (loadState(CHILD_ID_RELAY2) == 1)  {
                                digitalWrite(RELAY_2_PIN , RELAY_ON);  
                              }
                              else
                              {
                                digitalWrite(RELAY_2_PIN , RELAY_OFF);  
                              }
                        
                              #ifdef SerialPrints
                                Serial.print(F("Relay 1 loadstate from EEPROM: "));
                                Serial.println(loadState(CHILD_ID_RELAY1));
                                Serial.print(F("Relay 2 loadstate from EEPROM: "));
                                Serial.println(loadState(CHILD_ID_RELAY2));
                              #endif
                                //best let domoticz know
                                send(msgRelay1.set(RelayState));
                                wait(100);
                                send(msgRelay2.set(RelayState));
                                wait(100);
                        
                              #ifdef SerialPrints
                                Serial.print(F("Default Relay state sent to Domoticz: "));
                                Serial.println(RelayState);
                              #endif
                            break;
                              
                            case 2: 
                                //get Relay Statuses from Domoticz
                                #ifdef SerialPrints          
                                  Serial.println(F("Relay status requested from Domoticz"));          
                                #endif
                                  request(CHILD_ID_RELAY1, V_STATUS);
                                  wait(1000);
                                  request(CHILD_ID_RELAY2, V_STATUS);
                                  wait(1000);
                                  
                            break;
                            default:
                                digitalWrite(RELAY_1_PIN ,  RelayState ? RELAY_ON : RELAY_OFF);
                                digitalWrite(RELAY_2_PIN ,  RelayState ? RELAY_ON : RELAY_OFF);
                              #ifdef SerialPrints        
                                Serial.print(F("Relays set to default boot state: "));
                                Serial.println(RelayState);
                              #endif
                                send(msgRelay1.set(RelayState));
                                wait(100);
                                send(msgRelay2.set(RelayState));
                                wait(100);
                              #ifdef SerialPrints
                                Serial.print(F("Default Relay state sent to Domoticz: "));
                                Serial.println(RelayState);
                              #endif
                            break;
                            }
                          
                          // Set tempoffset to correct state (using eeprom/default/domoticz value) - needs to be 0 - 100 otherwise set to no offset
                        
                            switch (StoreTempOffset)  {
                              case 1:
                                if (loadState(CHILD_ID_TEMP_OFFSET) >100) {
                                  ActualTempOffset = 5.0;
                                }
                                else
                                {
                                  TempOffset = loadState(CHILD_ID_TEMP_OFFSET);
                                  ActualTempOffset = (TempOffset / 10.0) - 5.0;
                                }
                                #ifdef SerialPrints
                                  Serial.print(F("Temp Offset retrieved from eeprom: "));
                                  Serial.println(ActualTempOffset);
                                #endif
                                  //let Domoticz know the value
                                  send(msgTempOffset.set(TempOffset, 1));
                                  wait(100);
                                #ifdef SerialPrints
                                  Serial.print(F("tempOffset from sensor sent to Domoticz: "));
                                  Serial.println(TempOffset);
                                #endif
                              break;
                              case 2:
                                //get TempOffSet from Domoticz 
                                #ifdef SerialPrints
                                  Serial.println(F("TempOffset value requested from Domoticz"));         
                                #endif
                                  request(CHILD_ID_TEMP_OFFSET, V_PERCENTAGE);
                                  wait(1000);
                                  
                              break;
                              default:
                                #ifdef SerialPrints
                                  Serial.print(F("Default boot temperature offset is: "));
                                  Serial.println(ActualTempOffset);
                                #endif
                                  //let Domoticz know the value
                                  send(msgTempOffset.set(TempOffset, 1));
                                  wait(100);
                                #ifdef SerialPrints
                                  Serial.print(F("tempOffset from sensor sent to Domoticz: "));
                                  Serial.println(TempOffset);    
                                #endif
                              break;
                            }
                        
                            // Set PIR enable/disable (using eeprom/default/domoticz value)  
                            switch(StoreMotionOnOff) {
                              case 1:
                                if (loadState(CHILD_ID_MotionOnOff) == 1 || loadState(CHILD_ID_MotionOnOff) == 0) { //check for odd values in EEPROM
                                  MotionON = loadState(CHILD_ID_MotionOnOff);  
                                }
                                else {
                                #ifdef SerialPrints
                                  Serial.print(F("Error in PIR Enable/Disable Value in EEPROM"));
                                  Serial.println(F("Default value used instead"));
                                #endif
                                } 
                                //Let Domoticz know
                                  send(msgMotOnOff.set(MotionON, 1));
                                  wait(100);
                                #ifdef SerialPrints  
                                  Serial.print(F("PIR ENABLE status from sensor sent to Domoticz: "));
                                  Serial.println(MotionON);  
                                #endif  
                              break;
                               
                              case 2:
                                //get PIR enable status from Domoticz
                                #ifdef SerialPrints
                                  Serial.println(F("MotionOnOff status requested from Domoticz"));          
                                #endif
                                  request(CHILD_ID_MotionOnOff, V_STATUS);
                                  wait(1000);
                        
                        
                              break;
                                  
                        
                              default:
                                  send(msgMotOnOff.set(MotionON, 1));
                                  wait(100);
                                #ifdef SerialPrints
                                  Serial.print(F("Default PIR ENABLE status used and sent to Domoticz: "));
                                  Serial.println(MotionON); 
                                #endif
                              break;
                            }
                          wait(2000);
                        #ifdef SerialPrints
                          Serial.println("First boot checks completed");
                        #endif 
                          #ifdef ImAlive
                            Serial.println(F("I'm alive"));
                          #endif 
                          FirstBoot = false;
                          }
                        
                          //check if gesture sensor interrupt is triggered
                            if( isr_flag == 1 ) {
                            detachInterrupt(digitalPinToInterrupt(APDS9960_INT));
                            handleGesture();
                            isr_flag = 0;
                            attachInterrupt(digitalPinToInterrupt(APDS9960_INT), interruptRoutine, FALLING);
                        
                          }
                          
                          // Read digital motion value
                          if (MotionON) {
                            bool tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH;
                            if (lastTripped != tripped) {
                            #ifdef SerialReadings
                              Serial.print(F("New Motion State: "));
                              Serial.println(tripped);
                            #endif
                              // Send tripped value to gw
                              send(msgMot.set(tripped ? "1" : "0"));
                              lastTripped = tripped;
                            }
                          }
                        
                        
                        
                          boolean needRefresh = (millis() - lastRefreshTime) > SLEEP_TIME;
                          if (needRefresh)
                          {
                          #ifdef ImAlive
                            Serial.println(F("I'm alive"));
                          #endif
                            lastRefreshTime = millis();
                        
                            float temperature = dht.getTemperature() + ActualTempOffset;
                            if (isnan(temperature)) {
                            #ifdef SerialReadings
                              Serial.println(F("Failed reading temperature from DHT"));
                            #endif
                            }
                            else if (temperature != lastTemp  ) {
                              lastTemp = temperature;
                              if (!metric) {
                                temperature = dht.toFahrenheit(temperature);
                              }
                              send(msgTemp.set(temperature, 1));
                            #ifdef SerialReadings
                              Serial.print(F("T: "));
                              Serial.println(temperature);
                            #endif
                            }
                            else if (millis() - lastForceUpdateTime > ForceUpdate) {
                              lastTemp = temperature;
                              lastForceUpdateTime = millis();
                              if (!metric) {
                                temperature = dht.toFahrenheit(temperature);
                              }
                              send(msgTemp.set(temperature, 1));
                            #ifdef SerialReadings
                              Serial.print("T: ");
                              Serial.println(temperature);
                            #endif
                            }
                        
                        
                            float humidity = dht.getHumidity();
                            if (isnan(humidity)) {
                            #ifdef SerialReadings
                              Serial.println(F("Failed reading humidity from DHT"));
                            #endif
                            }
                            else if (humidity != lastHum) {
                              lastHum = humidity;
                              send(msgHum.set(humidity, 1));
                            #ifdef SerialReadings  
                              Serial.print(F("H: "));
                              Serial.println(humidity);
                            #endif
                            }
                            else if (millis() - lastForceUpdateTime > ForceUpdate) {
                              lastHum = humidity;
                              lastForceUpdateTime = millis();
                              send(msgHum.set(humidity, 1));
                            #ifdef SerialReadings
                              Serial.print(F("H: "));
                              Serial.println(humidity);
                            #endif
                            }
                          }
                        
                        }
                        
                        void interruptRoutine() {
                        #ifdef SerialPrints  
                          Serial.println("Interrupt Routine started");
                        #endif
                          isr_flag = 1;
                        }
                        
                        void handleGesture() {
                        
                         
                          
                            if ( apds.isGestureAvailable() ) {
                        
                        
                            switch ( apds.readGesture() ) {
                              case DIR_UP:
                              #ifdef SerialReadings
                                Serial.println(F("UP"));
                              #endif
                                send(msgGestureUpDown.set(GestureUp));
                                break;
                              case DIR_DOWN:
                              #ifdef SerialReadings
                                Serial.println(F("DOWN"));
                              #endif
                                send(msgGestureUpDown.set(GestureDown));
                                break;
                              case DIR_LEFT:
                              #ifdef SerialReadings
                                Serial.println(F("LEFT"));
                              #endif
                                send(msgGestureLeftRight.set(GestureLeft));
                                break;
                              case DIR_RIGHT:
                              #ifdef SerialReadings
                                Serial.println(F("RIGHT"));
                              #endif
                                send(msgGestureLeftRight.set(GestureRight));
                                break;
                              case DIR_NEAR:
                              #ifdef SerialReadings
                                Serial.println(F("NEAR"));
                              #endif
                                send(msgGestureNearFar.set(GestureNear));
                                break;
                              case DIR_FAR:
                              #ifdef SerialReadings
                                Serial.println(F("FAR"));
                              #endif
                                send(msgGestureNearFar.set(GestureFar));
                                break;
                              default:
                              #ifdef SerialReadings
                                Serial.println(F("NONE"));
                              #endif
                              break;
                            }
                          
                           }
                          
                        }
                        
                        void receive(const MyMessage &message)
                        {
                         //For debugging just checking why controller sent a msg e.g. was it in response to a request by node  
                        #ifdef SerialPrints
                          switch (message.getCommand()) {                                 // message.getCommand will give us the command type of the incomming message
                              case C_SET: 
                                Serial.println(F("msg sent by controller"));
                              break;
                              case C_REQ:
                                Serial.println(F("msg state sent by controller in response to request"));
                              break;
                              default:
                                Serial.println(F("msg isn't C_SET or C_REQ so what you gonna do?"));
                              break;
                          }
                        #endif
                          // We only expect V_STATUS (for relays) or V_Percentage (for temp offset) message from controller. But we better check anyway.
                          if (message.type == V_STATUS) {
                            // Change relay state
                        
                            if (message.sensor == 1 ) {
                              
                              digitalWrite(RELAY_1_PIN , message.getBool() ? RELAY_ON : RELAY_OFF);
                              // Store state in eeprom
                              saveState(message.sensor, message.getBool());
                              // Write some debug info
                            #ifdef SerialReadings
                              Serial.print(F("Incoming Relay 1 State: "));
                              Serial.println(message.getBool());
                            #endif
                            }
                            else if (message.sensor == 2) {
                             
                              digitalWrite(RELAY_2_PIN , message.getBool() ? RELAY_ON : RELAY_OFF);
                              // Store state in eeprom
                              saveState(message.sensor, message.getBool());
                              // Write some debug info
                            #ifdef SerialReadings
                              Serial.print(F("Incoming Relay 2 State: "));
                              Serial.println(message.getBool());
                            #endif
                            }
                            else if (message.sensor == CHILD_ID_MotionOnOff) {
                        
                                if (message.getBool() == 1) {
                                  MotionON = 1;
                                  saveState(CHILD_ID_MotionOnOff, MotionON);
                                }
                                else
                                {
                                  MotionON = 0;
                                }
                                #ifdef SerialReadings
                                  Serial.print(F("Motion Sensor on/off state from Domoticz: "));
                                  Serial.println(MotionON);
                                #endif
                                  saveState(CHILD_ID_MotionOnOff, MotionON);
                            
                            }
                          }
                          else if (message.type == V_PERCENTAGE)  {
                            int TempOffset = atoi(message.data);
                            ActualTempOffset = (TempOffset / 10.0) - 5.0;
                          #ifdef SerialReadings
                            Serial.print(F("Temp Offset value from Domoticz: "));
                            Serial.println(ActualTempOffset);
                            saveState(CHILD_ID_TEMP_OFFSET, TempOffset);
                          #endif
                          }
                          
                        
                        }
                        
                        
                        dbemowskD Offline
                        dbemowskD Offline
                        dbemowsk
                        wrote on last edited by
                        #11

                        @Ben-Andrewes So with the code rewrite, what does it show your memory usage at?

                        Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
                        Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

                        B 1 Reply Last reply
                        1
                        • dbemowskD dbemowsk

                          @Ben-Andrewes So with the code rewrite, what does it show your memory usage at?

                          B Offline
                          B Offline
                          Ben Andrewes
                          wrote on last edited by
                          #12

                          @dbemowsk with all the Serialprints & debugs etc defined, it is 78% of program storage & 65% of variable storage but importantly no warnings from the IDE that memory is low

                          if I comment out all of the defines then it is 49% & 59%. Good result either way.

                          One thing that I'd like to do is to call separate subroutines for e.g. firstboot but I had all sorts of strange results when I didn't just include it in the main loop(). I think this might be because the loop is still, um, looping while these subroutines run - do you think I am right in thinking this is the problem? If so, is there a way around it?

                          Cheers

                          Ben

                          dbemowskD 1 Reply Last reply
                          0
                          • B Ben Andrewes

                            @dbemowsk with all the Serialprints & debugs etc defined, it is 78% of program storage & 65% of variable storage but importantly no warnings from the IDE that memory is low

                            if I comment out all of the defines then it is 49% & 59%. Good result either way.

                            One thing that I'd like to do is to call separate subroutines for e.g. firstboot but I had all sorts of strange results when I didn't just include it in the main loop(). I think this might be because the loop is still, um, looping while these subroutines run - do you think I am right in thinking this is the problem? If so, is there a way around it?

                            Cheers

                            Ben

                            dbemowskD Offline
                            dbemowskD Offline
                            dbemowsk
                            wrote on last edited by
                            #13

                            @Ben-Andrewes Sounds like great results. I need to go through my weather station sketch and do this for the same reasons. Thanks for the heads up on the results.

                            Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
                            Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

                            B 1 Reply Last reply
                            1
                            • dbemowskD dbemowsk

                              @Ben-Andrewes Sounds like great results. I need to go through my weather station sketch and do this for the same reasons. Thanks for the heads up on the results.

                              B Offline
                              B Offline
                              Ben Andrewes
                              wrote on last edited by
                              #14

                              @dbemowsk Just looked at your weather station - ace work.

                              I was intrigued by this:

                              #ifdef  DEBUG_ON
                                #define DEBUG_PRINT(x)   Serial.print(x)
                                #define DEBUG_PRINTLN(x) Serial.println(x)
                                #define SERIAL_START(x)  Serial.begin(x)
                                #else
                                #define DEBUG_PRINT(x)
                                #define DEBUG_PRINTLN(x)
                                #define SERIAL_START(x)
                              #endif
                              

                              I think I understand that if DEBUG_ON is defined then wherever you have e.g.
                              DEBUG_PRINT("Hello")
                              The compiler will turn this into serial.print("Hello")

                              My question is what happens if DEBUG_ON is not defined? How does the compiler treat lines such as DEBUG_PRINT("Hello")?

                              Is it that because in the #else part DEBUG_PRINT is defined but not actually defined as anything then the compiler will delete the DEBUG_PRINT("Hello") completely?

                              Sorry for all the questions but I am trying to get my head around compiling vs programming and then how I can use it to make things more efficient...

                              dbemowskD 2 Replies Last reply
                              0
                              • B Ben Andrewes

                                @dbemowsk Just looked at your weather station - ace work.

                                I was intrigued by this:

                                #ifdef  DEBUG_ON
                                  #define DEBUG_PRINT(x)   Serial.print(x)
                                  #define DEBUG_PRINTLN(x) Serial.println(x)
                                  #define SERIAL_START(x)  Serial.begin(x)
                                  #else
                                  #define DEBUG_PRINT(x)
                                  #define DEBUG_PRINTLN(x)
                                  #define SERIAL_START(x)
                                #endif
                                

                                I think I understand that if DEBUG_ON is defined then wherever you have e.g.
                                DEBUG_PRINT("Hello")
                                The compiler will turn this into serial.print("Hello")

                                My question is what happens if DEBUG_ON is not defined? How does the compiler treat lines such as DEBUG_PRINT("Hello")?

                                Is it that because in the #else part DEBUG_PRINT is defined but not actually defined as anything then the compiler will delete the DEBUG_PRINT("Hello") completely?

                                Sorry for all the questions but I am trying to get my head around compiling vs programming and then how I can use it to make things more efficient...

                                dbemowskD Offline
                                dbemowskD Offline
                                dbemowsk
                                wrote on last edited by
                                #15

                                @Ben-Andrewes said in MultiSensor - temp, hum, PIR, gesture with various controller overrides:

                                My question is what happens if DEBUG_ON is not defined? How does the compiler treat lines such as DEBUG_PRINT("Hello")?

                                In a sense it is ignored. It still defines DEBUG_PRINT(), DEBUG_PRINTLN() and SERIAL_START() because of the #else, but they do nothing if DEBUG_ON is not defined. You can then put DEBUG_PRINT{LN}(x) statements in your code without the need to do the #ifdef DEBUG_ON every time you want to print something for debugging. Makes for a lot cleaner code.

                                I cannot take credit for that bit of code though. That came from the rain gauge sketch in the build section of the site, but I liked it a lot. I should really use it for a bunch of different/separate debugs in that sketch that would allow me to turn on/off separate debugging code for each different sensor.

                                Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
                                Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

                                1 Reply Last reply
                                1
                                • B Ben Andrewes

                                  @dbemowsk Just looked at your weather station - ace work.

                                  I was intrigued by this:

                                  #ifdef  DEBUG_ON
                                    #define DEBUG_PRINT(x)   Serial.print(x)
                                    #define DEBUG_PRINTLN(x) Serial.println(x)
                                    #define SERIAL_START(x)  Serial.begin(x)
                                    #else
                                    #define DEBUG_PRINT(x)
                                    #define DEBUG_PRINTLN(x)
                                    #define SERIAL_START(x)
                                  #endif
                                  

                                  I think I understand that if DEBUG_ON is defined then wherever you have e.g.
                                  DEBUG_PRINT("Hello")
                                  The compiler will turn this into serial.print("Hello")

                                  My question is what happens if DEBUG_ON is not defined? How does the compiler treat lines such as DEBUG_PRINT("Hello")?

                                  Is it that because in the #else part DEBUG_PRINT is defined but not actually defined as anything then the compiler will delete the DEBUG_PRINT("Hello") completely?

                                  Sorry for all the questions but I am trying to get my head around compiling vs programming and then how I can use it to make things more efficient...

                                  dbemowskD Offline
                                  dbemowskD Offline
                                  dbemowsk
                                  wrote on last edited by dbemowsk
                                  #16

                                  @Ben-Andrewes said in MultiSensor - temp, hum, PIR, gesture with various controller overrides:

                                  Sorry for all the questions but I am trying to get my head around compiling vs programming and then how I can use it to make things more efficient...

                                  No need to be sorry, we all learn, even me. When I first saw that bit of code, I too thought it was genius.

                                  My understanding of things is that lines that start with a hashtag are dealt with by the compiler and are not compiled into the program, to a point. Here is an example.

                                  In the code below, the LED_PIN value is processed by the compiler and is good for static values that don't need to change. The value 5 is not stored in program memory and tied to a variable. If you scatter digitalWrites() throughout your code, allyou have to do is change your #define and you are good to go.

                                  #define LED_PIN 5
                                  
                                  pinmode(LED_PIN, OUTPUT);
                                  
                                  ... other code ...
                                  
                                  digitalWrite(LED_PIN, HIGH);
                                  

                                  The code below essentially does the same thing as the previous bit, but if you scatter any digitalWrite() lines in other places in your code and want to make a change, you have to find all of the lines that write to pin 5 and change them. Plus, it is MUCH harder to read and decipher.

                                  pinmode(5, OUTPUT);
                                  
                                  ... other code ...
                                  
                                  digitalWrite(5, HIGH);
                                  

                                  This code is similar. For scattered digitalWrites(), you only need to change the value in one place just like the #define, but uses a variable that is compiled and stored in memory.

                                  INT led_pin = 5;
                                  
                                  pinmode(led_pin, OUTPUT);
                                  
                                  ... other code ...
                                  
                                  digitalWrite(led_pin, HIGH);
                                  

                                  All three bits of code will do the same thing. I don't know about other people, but for variables used by the compiler as in a #define, I always make the variable name all upper case. For program variables such as the last example, I try to use lower case. It makes it easier for me to tell which is which.

                                  For this type of situation in my example, I think it is best to use compiler variables. They get rid of the needless use of variable storage space, even if the sketch is small. IMHO, program variables should be used for values that may/will change in your sketch

                                  Hope that helps clear your confusion

                                  Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
                                  Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

                                  BulldogLowellB 1 Reply Last reply
                                  1
                                  • B Ben Andrewes

                                    I've put the updated code below in case it is useful to anyone..

                                    I also found that the relays were switching on and off during first boot which was a pain, again my solution is below (I couldn't find a reference to this problem in the forums). As the relays are active low, I set the relay pins to high before setting them as outputs

                                      digitalWrite(RELAY_1_PIN , HIGH);  //stops relays cycling state during boot
                                      digitalWrite(RELAY_2_PIN , HIGH);  //stops relays cycling state during boot
                                      
                                      pinMode(RELAY_1_PIN , OUTPUT);
                                      pinMode(RELAY_2_PIN , OUTPUT);
                                    
                                    /**
                                       The MySensors Arduino library handles the wireless radio link and protocol
                                       between your home built sensors/actuators and HA controller of choice.
                                       The sensors forms a self healing radio network with optional repeaters. Each
                                       repeater and gateway builds a routing tables in EEPROM which keeps track of the
                                       network topology allowing messages to be routed to nodes.
                                    
                                       Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
                                       Copyright (C) 2013-2015 Sensnology AB
                                       Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
                                    
                                       Documentation: http://www.mysensors.org
                                       Support Forum: http://forum.mysensors.org
                                    
                                       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.
                                    
                                     *******************************
                                    
                                    Hardware Connections:
                                    
                                    IMPORTANT: The APDS-9960 can only accept 3.3V!
                                     
                                     Arduino Pin  APDS-9960 Board  Function
                                     
                                     3.3V         VCC              Power
                                     GND          GND              Ground
                                     A4           SDA              I2C Data
                                     A5           SCL              I2C Clock
                                     2            INT              Interrupt
                                    */
                                    
                                    // Enable debug prints to serial monitor
                                    // #define MY_DEBUG //mysensors protocol debug prints
                                    // #define SerialPrints //all the verbose messages that state what the program is doing at certain points e.g. "Requesting message from Domoticz"
                                    // #define ImAlive  // Heartbeat in main loop
                                    #define SerialReadings  //Sensor readings e.g. temp and certain messages from Domoticz e.g. if turning a relay on
                                    
                                    // Enable and select radio type attached
                                    #define MY_RADIO_NRF24
                                    //#define MY_RADIO_RFM69
                                    
                                    // Enable repeater functionality for this node
                                    #define MY_REPEATER_FEATURE
                                    #define MY_NODE_ID 103
                                    
                                    #include <Wire.h>
                                    #include <SparkFun_APDS9960.h>
                                    #include <SPI.h>
                                    #include <DHT.h>
                                    #include <MySensors.h>
                                    
                                    #define RELAY_1_PIN  5  // Arduino Digital I/O pin number for first relay 
                                    #define RELAY_2_PIN  6  // Arduino Digital I/O pin number for second relay 
                                    
                                    #define RELAY_ON 0  // GPIO value to write to turn on attached relay - domoticz needs 0 for on, 1 for off
                                    #define RELAY_OFF 1 // GPIO value to write to turn off attached relay
                                    
                                    
                                    #define CHILD_ID_RELAY1 1
                                    #define CHILD_ID_RELAY2 2
                                    #define CHILD_ID_HUM 3
                                    #define CHILD_ID_TEMP 4
                                    #define CHILD_ID_MOT 5   // Id of the sensor child
                                    #define CHILD_ID_TEMP_OFFSET 20
                                    #define CHILD_ID_MotionOnOff 21
                                    #define CHILD_ID_GESTUREUD 30  //up down gesture
                                    #define CHILD_ID_GESTURELR 31 //left right gesture
                                    #define CHILD_ID_GESTURENF 32 //near far gesture
                                    #define GestureUp 1 //command to send on gesture, 0 = off, 1 = on
                                    #define GestureDown 0 //command to send on gesture, 0 = off, 1 = on
                                    #define GestureLeft 0 //command to send on gesture, 0 = off, 1 = on
                                    #define GestureRight 1 //command to send on gesture, 0 = off, 1 = on
                                    #define GestureNear 0 //command to send on gesture, 0 = off, 1 = on
                                    #define GestureFar 1 //command to send on gesture, 0 = off, 1 = on
                                    #define GestureSensitivity  0  // value between 0 & 3, 0 most sensitive, 3 least
                                    
                                    
                                    #define HUMIDITY_SENSOR_DIGITAL_PIN 7
                                    
                                    #define APDS9960_INT    2  // for the APDS-9960 - Needs to be an interrupt pin
                                    
                                    #define DIGITAL_INPUT_SENSOR 3   // The digital input you attached your motion sensor.  (Only 2 and 3 generates interrupt!)
                                    #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // for the PIR - Usually the interrupt = pin -2 (on uno/nano anyway)
                                    
                                    SparkFun_APDS9960 apds = SparkFun_APDS9960();
                                    volatile int isr_flag = 0; //handles the interrupt process for gesture sensor
                                    
                                    
                                    
                                    
                                    unsigned long SLEEP_TIME = 120000; // Sleep time between reads of temp/hum (in milliseconds)
                                    unsigned long lastRefreshTime = 0; // Use this to implement a non-blocking delay function
                                    unsigned long RefreshCyclesUntilForcedUpdate = 5; // use to force a temp/hum reading after 'n' SLEEP_TIME cycles even if value hasn't changed, set v.high (e.g. 10,000) to effectively disable
                                    unsigned long ForceUpdate = RefreshCyclesUntilForcedUpdate * SLEEP_TIME; // Force update read after this amount of time
                                    unsigned long lastForceUpdateTime = 0; // Use this to implement a non-blocking delay function that forces update even if temp hasn't changed
                                    
                                    DHT dht;
                                    float lastTemp;
                                    float lastHum;
                                    
                                    boolean metric = true;
                                    
                                    bool lastTripped = 0;   // Used to store last motion sensor value
                                    boolean FirstBoot = true;
                                    
                                    // the below allow you decide if the default state of certain sensors is stored in EEPROM or Controller or always start in a certain state
                                    // 1 = EEPROM 2 = Controller (e.g. Domoticz), 3 or any other number then they start as with default values
                                    // If option 1 or 3 is selected then we will also send a message to the controller to tell it the states
                                    
                                    // unsigned long WaitBetweenSendingMessages = 150; //how long to wait between sending messages and presenting nodes
                                    int StoreTempOffset = 2;
                                    int StoreMotionOnOff = 2;
                                    int StoreRelayState = 2;
                                    
                                    // below are the default states that you can set to be loaded at boot each time
                                    // these will be reverted to in EEPROM values are corrupted
                                    bool MotionON = 0;      //enable/disable the PIR reports (1=enabled 0=disabled)
                                    bool RelayState = 0;    // default state for relays (1 = on, 0 = off)
                                    
                                    int TempOffset = 50; // a value of 50 means that initial offset is zero when first loaded.  Tempoffset can be between 0-100
                                    volatile float ActualTempOffset = (TempOffset / 10.0) - 5.0;  //Adjust temp offset to a decimal between +/- 5 degrees
                                    
                                    MyMessage msgHum(CHILD_ID_HUM, V_HUM);
                                    MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
                                    MyMessage msgMot(CHILD_ID_MOT, V_TRIPPED);
                                    MyMessage msgTempOffset(CHILD_ID_TEMP_OFFSET, V_PERCENTAGE);
                                    MyMessage msgMotOnOff(CHILD_ID_MotionOnOff, V_STATUS);
                                    MyMessage msgRelay1(1, V_STATUS);
                                    MyMessage msgRelay2(2, V_STATUS);
                                    MyMessage msgGestureUpDown(CHILD_ID_GESTUREUD, V_STATUS);
                                    MyMessage msgGestureLeftRight(CHILD_ID_GESTURELR, V_STATUS);
                                    MyMessage msgGestureNearFar(CHILD_ID_GESTURENF, V_STATUS);
                                    
                                    void before()
                                    {
                                    
                                      // Then set relay pins in output mode
                                      digitalWrite(RELAY_1_PIN , HIGH);
                                      digitalWrite(RELAY_2_PIN , HIGH);
                                      
                                      pinMode(RELAY_1_PIN , OUTPUT);
                                      pinMode(RELAY_2_PIN , OUTPUT);
                                      
                                    
                                      
                                      pinMode(DIGITAL_INPUT_SENSOR, INPUT);      // sets the motion sensor digital pin as input
                                    
                                    
                                    
                                    
                                    }
                                    
                                    void setup()
                                    {
                                      // Set gesture sensor interrupt pin as input
                                      pinMode(APDS9960_INT, INPUT);
                                        
                                        //define sensitivity of gesture sensor
                                        apds.setGestureGain( GestureSensitivity );
                                      
                                      dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
                                      // metric = getConfig().isMetric;
                                    
                                      // Initialize interrupt service routine for gestures
                                      
                                     attachInterrupt(digitalPinToInterrupt(APDS9960_INT), interruptRoutine, FALLING);
                                    
                                    
                                      // Initialize APDS-9960 (configure I2C and initial values)
                                    //Do not get rid of these if statements as they initialise the gesture sensing 
                                      if ( apds.init() ) {
                                        Serial.println(F("APDS-9960 initialization complete"));
                                      } else {
                                        Serial.println(F("Something went wrong during APDS-9960 init!"));
                                      }
                                    
                                      
                                      // Start running the APDS-9960 gesture sensor engine
                                      if ( apds.enableGestureSensor(true) ) {
                                        Serial.println(F("Gesture sensor is now running"));
                                      } else {
                                        Serial.println(F("Something went wrong during gesture sensor init!"));
                                      }
                                    
                                    }
                                    
                                    void presentation()
                                    {
                                      // Send the sketch version information to the gateway and Controller
                                      sendSketchInfo("MyMultiSensor", "1.4");
                                    
                                    
                                      // Register all sensors to gw (they will be created as child devices)
                                      present(CHILD_ID_RELAY1, S_BINARY, "Relay 1");
                                      wait(100);
                                      present(CHILD_ID_RELAY2, S_BINARY, "Relay 2");
                                      wait(100);
                                      present(CHILD_ID_HUM, S_HUM, "Humidity");
                                      wait(100);
                                      present(CHILD_ID_TEMP, S_TEMP, "Temperature");
                                      wait(100);
                                      present(CHILD_ID_MOT, S_MOTION, "PIR");
                                      wait(100);
                                      present(CHILD_ID_TEMP_OFFSET, S_DIMMER, "Temp Calibration");
                                      wait(100);
                                      present(CHILD_ID_MotionOnOff, S_BINARY, "PIR Enable/Disable");
                                      wait(100);
                                      present(CHILD_ID_GESTUREUD, S_BINARY, "Gesture Up/Down");
                                      wait(100);
                                      present(CHILD_ID_GESTURELR, S_BINARY, "Gesture Left/Right");
                                      wait(100);
                                      present(CHILD_ID_GESTURENF, S_BINARY, "Gesture Near/Far");
                                      wait(100);  
                                     
                                    }
                                    
                                    void firstboot()
                                    {
                                      
                                      }
                                      
                                      
                                    void loop()
                                    {
                                      if(FirstBoot)
                                      {
                                    #ifdef SerialPrints
                                      Serial.println(F("FirstBoot sequence started"));
                                    #endif
                                      // Set relay to correct state (using eeprom/default/domoticz value)
                                      switch (StoreRelayState) {
                                        case 1:
                                          if (loadState(CHILD_ID_RELAY1) == 1)  {
                                            digitalWrite(RELAY_1_PIN , RELAY_ON);  
                                          }
                                          else
                                          {
                                            digitalWrite(RELAY_1_PIN , RELAY_OFF);  
                                          }
                                          if (loadState(CHILD_ID_RELAY2) == 1)  {
                                            digitalWrite(RELAY_2_PIN , RELAY_ON);  
                                          }
                                          else
                                          {
                                            digitalWrite(RELAY_2_PIN , RELAY_OFF);  
                                          }
                                    
                                          #ifdef SerialPrints
                                            Serial.print(F("Relay 1 loadstate from EEPROM: "));
                                            Serial.println(loadState(CHILD_ID_RELAY1));
                                            Serial.print(F("Relay 2 loadstate from EEPROM: "));
                                            Serial.println(loadState(CHILD_ID_RELAY2));
                                          #endif
                                            //best let domoticz know
                                            send(msgRelay1.set(RelayState));
                                            wait(100);
                                            send(msgRelay2.set(RelayState));
                                            wait(100);
                                    
                                          #ifdef SerialPrints
                                            Serial.print(F("Default Relay state sent to Domoticz: "));
                                            Serial.println(RelayState);
                                          #endif
                                        break;
                                          
                                        case 2: 
                                            //get Relay Statuses from Domoticz
                                            #ifdef SerialPrints          
                                              Serial.println(F("Relay status requested from Domoticz"));          
                                            #endif
                                              request(CHILD_ID_RELAY1, V_STATUS);
                                              wait(1000);
                                              request(CHILD_ID_RELAY2, V_STATUS);
                                              wait(1000);
                                              
                                        break;
                                        default:
                                            digitalWrite(RELAY_1_PIN ,  RelayState ? RELAY_ON : RELAY_OFF);
                                            digitalWrite(RELAY_2_PIN ,  RelayState ? RELAY_ON : RELAY_OFF);
                                          #ifdef SerialPrints        
                                            Serial.print(F("Relays set to default boot state: "));
                                            Serial.println(RelayState);
                                          #endif
                                            send(msgRelay1.set(RelayState));
                                            wait(100);
                                            send(msgRelay2.set(RelayState));
                                            wait(100);
                                          #ifdef SerialPrints
                                            Serial.print(F("Default Relay state sent to Domoticz: "));
                                            Serial.println(RelayState);
                                          #endif
                                        break;
                                        }
                                      
                                      // Set tempoffset to correct state (using eeprom/default/domoticz value) - needs to be 0 - 100 otherwise set to no offset
                                    
                                        switch (StoreTempOffset)  {
                                          case 1:
                                            if (loadState(CHILD_ID_TEMP_OFFSET) >100) {
                                              ActualTempOffset = 5.0;
                                            }
                                            else
                                            {
                                              TempOffset = loadState(CHILD_ID_TEMP_OFFSET);
                                              ActualTempOffset = (TempOffset / 10.0) - 5.0;
                                            }
                                            #ifdef SerialPrints
                                              Serial.print(F("Temp Offset retrieved from eeprom: "));
                                              Serial.println(ActualTempOffset);
                                            #endif
                                              //let Domoticz know the value
                                              send(msgTempOffset.set(TempOffset, 1));
                                              wait(100);
                                            #ifdef SerialPrints
                                              Serial.print(F("tempOffset from sensor sent to Domoticz: "));
                                              Serial.println(TempOffset);
                                            #endif
                                          break;
                                          case 2:
                                            //get TempOffSet from Domoticz 
                                            #ifdef SerialPrints
                                              Serial.println(F("TempOffset value requested from Domoticz"));         
                                            #endif
                                              request(CHILD_ID_TEMP_OFFSET, V_PERCENTAGE);
                                              wait(1000);
                                              
                                          break;
                                          default:
                                            #ifdef SerialPrints
                                              Serial.print(F("Default boot temperature offset is: "));
                                              Serial.println(ActualTempOffset);
                                            #endif
                                              //let Domoticz know the value
                                              send(msgTempOffset.set(TempOffset, 1));
                                              wait(100);
                                            #ifdef SerialPrints
                                              Serial.print(F("tempOffset from sensor sent to Domoticz: "));
                                              Serial.println(TempOffset);    
                                            #endif
                                          break;
                                        }
                                    
                                        // Set PIR enable/disable (using eeprom/default/domoticz value)  
                                        switch(StoreMotionOnOff) {
                                          case 1:
                                            if (loadState(CHILD_ID_MotionOnOff) == 1 || loadState(CHILD_ID_MotionOnOff) == 0) { //check for odd values in EEPROM
                                              MotionON = loadState(CHILD_ID_MotionOnOff);  
                                            }
                                            else {
                                            #ifdef SerialPrints
                                              Serial.print(F("Error in PIR Enable/Disable Value in EEPROM"));
                                              Serial.println(F("Default value used instead"));
                                            #endif
                                            } 
                                            //Let Domoticz know
                                              send(msgMotOnOff.set(MotionON, 1));
                                              wait(100);
                                            #ifdef SerialPrints  
                                              Serial.print(F("PIR ENABLE status from sensor sent to Domoticz: "));
                                              Serial.println(MotionON);  
                                            #endif  
                                          break;
                                           
                                          case 2:
                                            //get PIR enable status from Domoticz
                                            #ifdef SerialPrints
                                              Serial.println(F("MotionOnOff status requested from Domoticz"));          
                                            #endif
                                              request(CHILD_ID_MotionOnOff, V_STATUS);
                                              wait(1000);
                                    
                                    
                                          break;
                                              
                                    
                                          default:
                                              send(msgMotOnOff.set(MotionON, 1));
                                              wait(100);
                                            #ifdef SerialPrints
                                              Serial.print(F("Default PIR ENABLE status used and sent to Domoticz: "));
                                              Serial.println(MotionON); 
                                            #endif
                                          break;
                                        }
                                      wait(2000);
                                    #ifdef SerialPrints
                                      Serial.println("First boot checks completed");
                                    #endif 
                                      #ifdef ImAlive
                                        Serial.println(F("I'm alive"));
                                      #endif 
                                      FirstBoot = false;
                                      }
                                    
                                      //check if gesture sensor interrupt is triggered
                                        if( isr_flag == 1 ) {
                                        detachInterrupt(digitalPinToInterrupt(APDS9960_INT));
                                        handleGesture();
                                        isr_flag = 0;
                                        attachInterrupt(digitalPinToInterrupt(APDS9960_INT), interruptRoutine, FALLING);
                                    
                                      }
                                      
                                      // Read digital motion value
                                      if (MotionON) {
                                        bool tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH;
                                        if (lastTripped != tripped) {
                                        #ifdef SerialReadings
                                          Serial.print(F("New Motion State: "));
                                          Serial.println(tripped);
                                        #endif
                                          // Send tripped value to gw
                                          send(msgMot.set(tripped ? "1" : "0"));
                                          lastTripped = tripped;
                                        }
                                      }
                                    
                                    
                                    
                                      boolean needRefresh = (millis() - lastRefreshTime) > SLEEP_TIME;
                                      if (needRefresh)
                                      {
                                      #ifdef ImAlive
                                        Serial.println(F("I'm alive"));
                                      #endif
                                        lastRefreshTime = millis();
                                    
                                        float temperature = dht.getTemperature() + ActualTempOffset;
                                        if (isnan(temperature)) {
                                        #ifdef SerialReadings
                                          Serial.println(F("Failed reading temperature from DHT"));
                                        #endif
                                        }
                                        else if (temperature != lastTemp  ) {
                                          lastTemp = temperature;
                                          if (!metric) {
                                            temperature = dht.toFahrenheit(temperature);
                                          }
                                          send(msgTemp.set(temperature, 1));
                                        #ifdef SerialReadings
                                          Serial.print(F("T: "));
                                          Serial.println(temperature);
                                        #endif
                                        }
                                        else if (millis() - lastForceUpdateTime > ForceUpdate) {
                                          lastTemp = temperature;
                                          lastForceUpdateTime = millis();
                                          if (!metric) {
                                            temperature = dht.toFahrenheit(temperature);
                                          }
                                          send(msgTemp.set(temperature, 1));
                                        #ifdef SerialReadings
                                          Serial.print("T: ");
                                          Serial.println(temperature);
                                        #endif
                                        }
                                    
                                    
                                        float humidity = dht.getHumidity();
                                        if (isnan(humidity)) {
                                        #ifdef SerialReadings
                                          Serial.println(F("Failed reading humidity from DHT"));
                                        #endif
                                        }
                                        else if (humidity != lastHum) {
                                          lastHum = humidity;
                                          send(msgHum.set(humidity, 1));
                                        #ifdef SerialReadings  
                                          Serial.print(F("H: "));
                                          Serial.println(humidity);
                                        #endif
                                        }
                                        else if (millis() - lastForceUpdateTime > ForceUpdate) {
                                          lastHum = humidity;
                                          lastForceUpdateTime = millis();
                                          send(msgHum.set(humidity, 1));
                                        #ifdef SerialReadings
                                          Serial.print(F("H: "));
                                          Serial.println(humidity);
                                        #endif
                                        }
                                      }
                                    
                                    }
                                    
                                    void interruptRoutine() {
                                    #ifdef SerialPrints  
                                      Serial.println("Interrupt Routine started");
                                    #endif
                                      isr_flag = 1;
                                    }
                                    
                                    void handleGesture() {
                                    
                                     
                                      
                                        if ( apds.isGestureAvailable() ) {
                                    
                                    
                                        switch ( apds.readGesture() ) {
                                          case DIR_UP:
                                          #ifdef SerialReadings
                                            Serial.println(F("UP"));
                                          #endif
                                            send(msgGestureUpDown.set(GestureUp));
                                            break;
                                          case DIR_DOWN:
                                          #ifdef SerialReadings
                                            Serial.println(F("DOWN"));
                                          #endif
                                            send(msgGestureUpDown.set(GestureDown));
                                            break;
                                          case DIR_LEFT:
                                          #ifdef SerialReadings
                                            Serial.println(F("LEFT"));
                                          #endif
                                            send(msgGestureLeftRight.set(GestureLeft));
                                            break;
                                          case DIR_RIGHT:
                                          #ifdef SerialReadings
                                            Serial.println(F("RIGHT"));
                                          #endif
                                            send(msgGestureLeftRight.set(GestureRight));
                                            break;
                                          case DIR_NEAR:
                                          #ifdef SerialReadings
                                            Serial.println(F("NEAR"));
                                          #endif
                                            send(msgGestureNearFar.set(GestureNear));
                                            break;
                                          case DIR_FAR:
                                          #ifdef SerialReadings
                                            Serial.println(F("FAR"));
                                          #endif
                                            send(msgGestureNearFar.set(GestureFar));
                                            break;
                                          default:
                                          #ifdef SerialReadings
                                            Serial.println(F("NONE"));
                                          #endif
                                          break;
                                        }
                                      
                                       }
                                      
                                    }
                                    
                                    void receive(const MyMessage &message)
                                    {
                                     //For debugging just checking why controller sent a msg e.g. was it in response to a request by node  
                                    #ifdef SerialPrints
                                      switch (message.getCommand()) {                                 // message.getCommand will give us the command type of the incomming message
                                          case C_SET: 
                                            Serial.println(F("msg sent by controller"));
                                          break;
                                          case C_REQ:
                                            Serial.println(F("msg state sent by controller in response to request"));
                                          break;
                                          default:
                                            Serial.println(F("msg isn't C_SET or C_REQ so what you gonna do?"));
                                          break;
                                      }
                                    #endif
                                      // We only expect V_STATUS (for relays) or V_Percentage (for temp offset) message from controller. But we better check anyway.
                                      if (message.type == V_STATUS) {
                                        // Change relay state
                                    
                                        if (message.sensor == 1 ) {
                                          
                                          digitalWrite(RELAY_1_PIN , message.getBool() ? RELAY_ON : RELAY_OFF);
                                          // Store state in eeprom
                                          saveState(message.sensor, message.getBool());
                                          // Write some debug info
                                        #ifdef SerialReadings
                                          Serial.print(F("Incoming Relay 1 State: "));
                                          Serial.println(message.getBool());
                                        #endif
                                        }
                                        else if (message.sensor == 2) {
                                         
                                          digitalWrite(RELAY_2_PIN , message.getBool() ? RELAY_ON : RELAY_OFF);
                                          // Store state in eeprom
                                          saveState(message.sensor, message.getBool());
                                          // Write some debug info
                                        #ifdef SerialReadings
                                          Serial.print(F("Incoming Relay 2 State: "));
                                          Serial.println(message.getBool());
                                        #endif
                                        }
                                        else if (message.sensor == CHILD_ID_MotionOnOff) {
                                    
                                            if (message.getBool() == 1) {
                                              MotionON = 1;
                                              saveState(CHILD_ID_MotionOnOff, MotionON);
                                            }
                                            else
                                            {
                                              MotionON = 0;
                                            }
                                            #ifdef SerialReadings
                                              Serial.print(F("Motion Sensor on/off state from Domoticz: "));
                                              Serial.println(MotionON);
                                            #endif
                                              saveState(CHILD_ID_MotionOnOff, MotionON);
                                        
                                        }
                                      }
                                      else if (message.type == V_PERCENTAGE)  {
                                        int TempOffset = atoi(message.data);
                                        ActualTempOffset = (TempOffset / 10.0) - 5.0;
                                      #ifdef SerialReadings
                                        Serial.print(F("Temp Offset value from Domoticz: "));
                                        Serial.println(ActualTempOffset);
                                        saveState(CHILD_ID_TEMP_OFFSET, TempOffset);
                                      #endif
                                      }
                                      
                                    
                                    }
                                    
                                    
                                    BulldogLowellB Offline
                                    BulldogLowellB Offline
                                    BulldogLowell
                                    Contest Winner
                                    wrote on last edited by
                                    #17

                                    @Ben-Andrewes said in MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides:

                                    #ifdef SerialPrints
                                      Serial.println("First boot checks completed");
                                    

                                    whoopsie... you missed a few.

                                    as a side note,

                                    if you have two identical string constants... the compiler is smart enough to evaluate them as such and store only one and access it accordingly.

                                    also, the following expression means wherever preprocessor sees an argument wrapped in DEBUG_PRINT(), it should replace it with that argument wrapped in Serial.print():

                                    #define DEBUG_PRINT(x)   Serial.print(x)
                                    

                                    this expression, on the other hand, means wherever preprocessor sees an argument wrapped in DEBUG_PRINT(), it should replace it with nothing:

                                    #define DEBUG_PRINT(x)
                                    

                                    @dbemowsk said in MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides:

                                    @Ben-Andrewes said in MultiSensor - temp, hum, PIR, gesture with various controller overrides:

                                    Sorry for all the questions but I am trying to get my head around compiling vs programming and then how I can use it to make things more efficient...

                                    No need to be sorry, we all learn, even me. When I first saw that bit of code, I too thought it was genius.

                                    My understanding of things is that lines that start with a hashtag are dealt with by the compiler and are not compiled into the program, to a point. Here is an example.

                                    they are actually preprocessor directives, and that happens before compilation.

                                    dbemowskD 1 Reply Last reply
                                    1
                                    • BulldogLowellB BulldogLowell

                                      @Ben-Andrewes said in MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides:

                                      #ifdef SerialPrints
                                        Serial.println("First boot checks completed");
                                      

                                      whoopsie... you missed a few.

                                      as a side note,

                                      if you have two identical string constants... the compiler is smart enough to evaluate them as such and store only one and access it accordingly.

                                      also, the following expression means wherever preprocessor sees an argument wrapped in DEBUG_PRINT(), it should replace it with that argument wrapped in Serial.print():

                                      #define DEBUG_PRINT(x)   Serial.print(x)
                                      

                                      this expression, on the other hand, means wherever preprocessor sees an argument wrapped in DEBUG_PRINT(), it should replace it with nothing:

                                      #define DEBUG_PRINT(x)
                                      

                                      @dbemowsk said in MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides:

                                      @Ben-Andrewes said in MultiSensor - temp, hum, PIR, gesture with various controller overrides:

                                      Sorry for all the questions but I am trying to get my head around compiling vs programming and then how I can use it to make things more efficient...

                                      No need to be sorry, we all learn, even me. When I first saw that bit of code, I too thought it was genius.

                                      My understanding of things is that lines that start with a hashtag are dealt with by the compiler and are not compiled into the program, to a point. Here is an example.

                                      they are actually preprocessor directives, and that happens before compilation.

                                      dbemowskD Offline
                                      dbemowskD Offline
                                      dbemowsk
                                      wrote on last edited by
                                      #18

                                      @BulldogLowell Thanks, I appreciate the clarification. I revert back to my previous statement, "we all learn, even me". So I assume that my other comments in the post are correct except for the fact that they are preprocessor directives, correct?

                                      Vera Plus running UI7 with MySensors, Sonoffs and 1-Wire devices
                                      Visit my website for more Bits, Bytes and Ramblings from me: http://dan.bemowski.info/

                                      1 Reply Last reply
                                      0
                                      • dbemowskD dbemowsk

                                        @Ben-Andrewes said in MultiSensor - temp, hum, PIR, gesture with various controller overrides:

                                        Sorry for all the questions but I am trying to get my head around compiling vs programming and then how I can use it to make things more efficient...

                                        No need to be sorry, we all learn, even me. When I first saw that bit of code, I too thought it was genius.

                                        My understanding of things is that lines that start with a hashtag are dealt with by the compiler and are not compiled into the program, to a point. Here is an example.

                                        In the code below, the LED_PIN value is processed by the compiler and is good for static values that don't need to change. The value 5 is not stored in program memory and tied to a variable. If you scatter digitalWrites() throughout your code, allyou have to do is change your #define and you are good to go.

                                        #define LED_PIN 5
                                        
                                        pinmode(LED_PIN, OUTPUT);
                                        
                                        ... other code ...
                                        
                                        digitalWrite(LED_PIN, HIGH);
                                        

                                        The code below essentially does the same thing as the previous bit, but if you scatter any digitalWrite() lines in other places in your code and want to make a change, you have to find all of the lines that write to pin 5 and change them. Plus, it is MUCH harder to read and decipher.

                                        pinmode(5, OUTPUT);
                                        
                                        ... other code ...
                                        
                                        digitalWrite(5, HIGH);
                                        

                                        This code is similar. For scattered digitalWrites(), you only need to change the value in one place just like the #define, but uses a variable that is compiled and stored in memory.

                                        INT led_pin = 5;
                                        
                                        pinmode(led_pin, OUTPUT);
                                        
                                        ... other code ...
                                        
                                        digitalWrite(led_pin, HIGH);
                                        

                                        All three bits of code will do the same thing. I don't know about other people, but for variables used by the compiler as in a #define, I always make the variable name all upper case. For program variables such as the last example, I try to use lower case. It makes it easier for me to tell which is which.

                                        For this type of situation in my example, I think it is best to use compiler variables. They get rid of the needless use of variable storage space, even if the sketch is small. IMHO, program variables should be used for values that may/will change in your sketch

                                        Hope that helps clear your confusion

                                        BulldogLowellB Offline
                                        BulldogLowellB Offline
                                        BulldogLowell
                                        Contest Winner
                                        wrote on last edited by
                                        #19

                                        @dbemowsk said in MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides:

                                        ... I think it is best to use compiler variables. They get rid of the needless use of variable storage space, even if the sketch is small. IMHO, program variables should be used for values that may/will change in your sketch

                                        The problem with using the #define directive for a constant is that they are not strongly typed. That is, suppose you are making a comparison, you may not realize that you are comparing a signed to unsigned number.

                                        You can see this if you compile this example with verbose on:

                                        #define LIMIT 6
                                        const unsigned int limit = 6;
                                        
                                        void setup() 
                                        {
                                          int myVar = 5;
                                          if(myVar > LIMIT){};
                                          if(myVar > limit){};
                                        }
                                        
                                        void loop() {}
                                        

                                        in C++, it is generally preferred to use the const keyword over a #define directive; both methods will store the "variable" in FLASH, saving SRAM as you pointed out.

                                        There is (of course) a lot of discourse on this topic (see Stack Overflow).

                                        So there are benefits to using #define. A #define at the top of a program does usually get seen by the programmer (or you, later on, after a hiatus) where they/you may not notice a regular definition on a quick glance #define sort of sticks out in the crowd.

                                        Perhaps that is why you see so much of the #define directive in Arduino programming.

                                        1 Reply Last reply
                                        0
                                        Reply
                                        • Reply as topic
                                        Log in to reply
                                        • Oldest to Newest
                                        • Newest to Oldest
                                        • Most Votes


                                        22

                                        Online

                                        11.7k

                                        Users

                                        11.2k

                                        Topics

                                        113.0k

                                        Posts


                                        Copyright 2019 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
                                        • Login

                                        • Don't have an account? Register

                                        • Login or register to search.
                                        • First post
                                          Last post
                                        0
                                        • MySensors
                                        • OpenHardware.io
                                        • Categories
                                        • Recent
                                        • Tags
                                        • Popular