MultiSensor - relays, temp, hum, PIR, gesture with various controller overrides
-
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 } } -
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 } }@Ben-Andrewes So with the code rewrite, what does it show your memory usage at?
-
@Ben-Andrewes So with the code rewrite, what does it show your memory usage at?
@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
-
@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
@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.
-
@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.
@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) #endifI 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...
-
@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) #endifI 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...
@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.
-
@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) #endifI 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...
@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
-
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 } }@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.
-
@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.
@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?
-
@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
@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.