Can't get motion interrupt to work in my (combined temp, battery and motion) sketch



  • Hi everyone,

    I've been having some trouble combining the temp, battery and motion sketch.
    I put the code together and battery level, temperature and motion info gets sent on the regular intervals, but I can't get the motion to trigger an interrupt.
    The code is below.

    I've tried flashing the the regular motion sketch and alter the names of the motion pin to MOTION_INPUT_SENSOR and that works.
    Also, I'm using a HC-SR501 on a 3.3V pro mini, but as I said, it acts correctly when using the regular sketch.

    Could someone take a look at my code and see what I might have missed?

    Here's my sketch:

    /**
     * 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.
     *
     *******************************
     *
     * DESCRIPTION
     *
     * Example sketch showing how to send in DS1820B OneWire temperature readings back to the controller
     * http://www.mysensors.org/build/temp
     */
    
    // Include libraries
    #include <MySensor.h>
    #include <SPI.h>
    #include <DallasTemperature.h>
    #include <OneWire.h>
    
    // Define data
    #define CHILD_ID_TEMP 0              // Temp child ID
    #define CHILD_ID_MOTION 2            // MOTION child ID
    #define COMPARE_TEMP 1 // Send temperature only if changed? 1 = Yes 0 = No
    #define ONE_WIRE_BUS 4 // Pin where dallase sensor is connected 
    #define MAX_ATTACHED_DS18B20 16
    #define MOTION_INPUT_SENSOR 3   // The digital input you attached your motion sensor.  (Only 2 and 3 generates interrupt!)
    #define INTERRUPT MOTION_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
    
    // Initialize mysensor
    MySensor gw;
    
    // Initialize D_Temp
    OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
    DallasTemperature sensors(&oneWire); // Pass the oneWire reference to Dallas Temperature.
    
    // Variables
    unsigned long SLEEP_TIME = 20000; // Sleep time between reads (in milliseconds)
    float lastTemperature[MAX_ATTACHED_DS18B20];
    int numSensors = 0;
    int node_id = 13;
    int BATTERY_SENSE_PIN = A0;  // select the input pin for the battery sense point
    int oldBatteryPcnt = 0;
    boolean receivedConfig = false;
    boolean metric = true;
    
    // Initialize childs
    MyMessage msgMot(CHILD_ID_MOTION, V_TRIPPED);
    MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
    
    void setup()
    {
      // Start up the OneWire library
      sensors.begin();
      // requestTemperatures() will not block current thread
      sensors.setWaitForConversion(false);
    
      // use the 1.1 V internal reference
    #if defined(__AVR_ATmega2560__)
      analogReference(INTERNAL1V1);
    #else
      analogReference(INTERNAL);
    #endif
    
      // Startup and initialize MySensors library. Set callback for incoming messages.
      gw.begin(NULL, node_id);
    
      // Send the sketch version information to the gateway and Controller
      gw.sendSketchInfo("Temp en Motion sensor met batterij", "1.0");
    
      // Fetch the number of attached temperature sensors
      numSensors = sensors.getDeviceCount();
    
      // Present all sensors to controller
      for (int i = 0; i < numSensors && i < MAX_ATTACHED_DS18B20; i++) {
        gw.present(i, S_TEMP);
       }
         pinMode(MOTION_INPUT_SENSOR, INPUT);      // sets the motion sensor digital pin as input
      // Register all sensors to gw (they will be created as child devices)
      gw.present(CHILD_ID_MOTION, S_MOTION);
    
    }
    
    
    void loop()
    {
      // get the battery Voltage
      int sensorValue = analogRead(BATTERY_SENSE_PIN);
    #ifdef DEBUG
      Serial.println(sensorValue);
    #endif
    
      // 1M, 470K divider across battery and using internal ADC ref of 1.1V
      // Sense point is bypassed with 0.1 uF cap to reduce noise at that point
      // ((1e6+470e3)/470e3)*1.1 = Vmax = 3.44 Volts
      // 3.44/1023 = Volts per bit = 0.003363075
      float batteryV  = sensorValue * 0.003363075;
      int batteryPcnt = sensorValue / 10;
    
    #ifdef DEBUG
      Serial.print("Battery Voltage: ");
      Serial.print(batteryV);
      Serial.println(" V");
    
      Serial.print("Battery percent: ");
      Serial.print(batteryPcnt);
      Serial.println(" %");
    #endif
    
      if (oldBatteryPcnt != batteryPcnt) {
        // Power up radio after sleep
        gw.sendBatteryLevel(batteryPcnt);
        oldBatteryPcnt = batteryPcnt;
      }
    
      // Process incoming messages (like config from server)
      gw.process();
    
      // Fetch temperatures from Dallas sensors
      sensors.requestTemperatures();
    
      // query conversion time and sleep until conversion completed
      int16_t conversionTime = sensors.millisToWaitForConversion(sensors.getResolution());
      // sleep() call can be replaced by wait() call if node need to process incoming messages (or if node is repeater)
      gw.sleep(conversionTime);
    
      // Read temperatures and send them to controller
      for (int i = 0; i < numSensors && i < MAX_ATTACHED_DS18B20; i++) {
    
        // Fetch and round temperature to one decimal
        float temperature = static_cast<float>(static_cast<int>((gw.getConfig().isMetric ? sensors.getTempCByIndex(i) : sensors.getTempFByIndex(i)) * 10.)) / 10.;
    
        // Only send data if temperature has changed and no error
    #if COMPARE_TEMP == 1
        if (lastTemperature[i] != temperature && temperature != -127.00 && temperature != 85.00) {
    #else
        if (temperature != -127.00 && temperature != 85.00) {
    #endif
    
          // Send in the new temperature
          gw.send(msgTemp.setSensor(i).set(temperature, 1));
          // Save new temperatures for next compare
          lastTemperature[i] = temperature;
        }
          // Read digital motion value
      boolean tripped = digitalRead(MOTION_INPUT_SENSOR) == HIGH; 
            
      Serial.println(tripped);
      gw.send(msgMot.set(tripped?"1":"0"));  // Send tripped value to gw 
      }
      gw.sleep(SLEEP_TIME,CHANGE, SLEEP_TIME);
    }
    

    I hope you don't lose your way in the sketch, because it's kind of cobbled together from other sketches I found, so it isn't well structured.
    Thanks in advance!

    Just a quick edit: this is the altered standard sketch that does work with the interrupt:

    /**
     * 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.
     *
     *******************************
     *
     * REVISION HISTORY
     * Version 1.0 - Henrik Ekblad
     * 
     * DESCRIPTION
     * Motion Sensor example using HC-SR501 
     * http://www.mysensors.org/build/motion
     *
     */
    
    #include <MySensor.h>  
    #include <SPI.h>
    
    unsigned long SLEEP_TIME = 120000; // Sleep time between reports (in milliseconds)
    #define MOTION_INPUT_SENSOR 3   // The digital input you attached your motion sensor.  (Only 2 and 3 generates interrupt!)
    #define INTERRUPT MOTION_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
    #define CHILD_ID 1   // Id of the sensor child
    int node_id = 10;
    
    MySensor gw;
    // Initialize motion message
    MyMessage msg(CHILD_ID, V_TRIPPED);
    
    void setup()  
    {  
      gw.begin(NULL, node_id);
    
      // Send the sketch version information to the gateway and Controller
      gw.sendSketchInfo("Motion Sensor", "1.0");
    
      pinMode(MOTION_INPUT_SENSOR, INPUT);      // sets the motion sensor digital pin as input
      // Register all sensors to gw (they will be created as child devices)
      gw.present(CHILD_ID, S_MOTION);
      
    }
    
    void loop()     
    {     
      // Read digital motion value
      boolean tripped = digitalRead(MOTION_INPUT_SENSOR) == HIGH; 
            
      Serial.println(tripped);
      gw.send(msg.set(tripped?"1":"0"));  // Send tripped value to gw 
     
      // Sleep until interrupt comes in on motion sensor. Send update every two minute. 
      gw.sleep(INTERRUPT,CHANGE, SLEEP_TIME);
    }
    

  • Hero Member

    @BastienVH
    Look at the line

     gw.sleep(INTERRUPT,CHANGE, SLEEP_TIME);
    

    vs.

    gw.sleep(SLEEP_TIME,CHANGE, SLEEP_TIME);
    

    ;-)



  • @AWI
    Thank you so much!
    I've been at this the entire morning and I just couldn't see it...

    Now the interrupt works perfectly!
    I'm just gonna find a way to only send the motion status on interrupt and not the battery level and motion temp.
    I think it'll be with some if statement.

    I'm off to mess around with the cvode some more!


  • Hero Member

    @BastienVH The return value of gw.sleep() show the cause of the wake-up. You can use that in your "if statement".. have fun..


  • Plugin Developer

    @BastienVH

    Hi! It looks as if you might possible get a conflict of child ids, between temp sensors and motion. You're presenting the temp sensors in the loop, and if they are more than 2, one temp sensor and the motion sensor will be presented as child id 2. Don't know what would happen in that case, if it's allowed to overwrite the id at presentation in that way, or not.



  • Hi everyone,

    It's been a while but now I've received some more arduinos, I'm back in action with developing my sketches.
    After the last problem (everytime motion is detected, temp also gets updated and sent), I've been tinkering with my sketch and if-statements.

    At the moment, I've got this (migrated from dallas to DHT for testing purposes):

    /**
    #include <SPI.h>
    #include <MySensor.h>
    #include <DHT.h>
    
    #define DIGITAL_INPUT_MOTION 3   // The digital input you attached your motion sensor.  (Only 2 and 3 generates interrupt!)
    #define INTERRUPT DIGITAL_INPUT_MOTION-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
    #define CHILD_ID_HUM 0
    #define CHILD_ID_TEMP 1
    #define CHILD_ID_MOT 2
    #define HUMIDITY_SENSOR_DIGITAL_PIN 4
    unsigned long SLEEP_TIME = 15000; // Sleep time between reads (in milliseconds) - low for testing purposes
    
    MySensor gw;
    DHT dht;
    float lastTemp;
    float lastHum;
    boolean metric = true;
    MyMessage msgHum(CHILD_ID_HUM, V_HUM);
    MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
    MyMessage msgMot(CHILD_ID_MOT, V_TRIPPED);
    int node_id = 20;
    boolean lastTripped = false ;
    
    void setup()
    {
      gw.begin(NULL, node_id);
      dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
    
      // Send the Sketch Version Information to the Gateway
      gw.sendSketchInfo("Humidity and motion", "1.0");
    
      pinMode(DIGITAL_INPUT_MOTION, INPUT);      // sets the motion sensor digital pin as input
    
      // Register all sensors to gw (they will be created as child devices)
      gw.present(CHILD_ID_HUM, S_HUM);
      gw.present(CHILD_ID_TEMP, S_TEMP);
      gw.present(CHILD_ID_MOT, S_MOTION);
      metric = gw.getConfig().isMetric;
    }
    
    void loop()
    {
    // OPTION 1: work with if statement
       int wake;
      wake = gw.sleep(INTERRUPT, CHANGE, SLEEP_TIME);
    
       if (wake > 0) {
         Serial.println("wake by motion");
         Serial.println("reading motion");
         motion();
       }
    
       else {
         Serial.println("wake by timer");
         Serial.println("reading motion");
         motion();
         Serial.println("reading temp/hum");
         humTemp();
       }
    
    //OPTION 2: NO IF-STATEMENT (commented out at the moment)
      /*
      motion();
      Serial.println("read temp / hum");
      humTemp();
    */
      Serial.println("going to sleep now.");
      gw.sleep(INTERRUPT, CHANGE, SLEEP_TIME); //sleep a bit
    }
    
    void motion() {
      // Read digital motion value
      boolean tripped = digitalRead(DIGITAL_INPUT_MOTION);
      Serial.println(tripped);
      if (lastTripped != tripped) {
        gw.send(msgMot.set(tripped ? "1" : "0")); // Send tripped value to gw
        lastTripped = tripped;
      }
    }
    
    void humTemp() {
      gw.wait(dht.getMinimumSamplingPeriod());
    
      float temperature = dht.getTemperature();
      if (isnan(temperature)) {
        Serial.println("Failed reading temperature from DHT");
      } else if (temperature != lastTemp) {
        lastTemp = temperature;
        if (!metric) {
          temperature = dht.toFahrenheit(temperature);
        }
        gw.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;
        gw.send(msgHum.set(humidity, 1));
        Serial.print("H: ");
        Serial.println(humidity);
      }
    }
    

    Strange things are happening...

    If I use OPTION 2, without if, so temp is always checked, interrupt get triggered instantly as shown here and sends a 'deactivate'-message 0 when the sensors sets back to 0 after +- 8 sec:

    1
    send: 20-20-0-0 s=2,c=1,t=16,pt=0,l=1,sg=0,st=ok:1
    read temp / hum
    going to sleep now.
    0
    send: 20-20-0-0 s=2,c=1,t=16,pt=0,l=1,sg=0,st=ok:0
    read temp / hum
    going to sleep now.
    

    If I use OPTION 1 (preferred because no unnecessary temp measurement), the interrupt seems to only occur when the motion signal goes back to 0 (low), which is late and doesn't provide me with a 1-signal (meaning it will not communicate presence, but only when the built-in timer of =- 8 seconds has passed).

    wake by motion
    reading motion
    0
    going to sleep now.
    

    The use of wake comes from this topic and the API.
    Any suggestions or does someone see what I missed?

    Thanks alot!


  • Plugin Developer

    @BastienVH

    You have two calls to sleep function. You're checking the return value of the first call but not the second. So when waking after the second sleep, it will just go back to sleep again. If it wakes again, now it will check the return value and takeaction. Then it repeats in this way, every other wake doing nothing but sleeping again.

    Just remove the last call to sleep, if you're fine with going to sleep immediately in the loop.



  • @martinhjelmare
    Ok, I didn't know that declaring the int wake as the reply from gw.sleep would actually make the node go to sleep again.
    I've now setup my loop like this and it appears to work:

    void loop()
    {
      int wake = gw.sleep(INTERRUPT, CHANGE, SLEEP_TIME);
      Serial.println(wake);
      if (wake > 0) {
        Serial.println("wake by motion");
        Serial.println("reading motion");
        motion();
      }
      else {
        Serial.println("wake by timer");
        Serial.println("reading motion");
        motion();
        Serial.println("reading temp/hum");
        humTemp();
      }
      Serial.println("going to sleep now.");
    }
    

    Only thing is, I only get my first temp/hum/motion data AFTER the node has been in sleeping for SLEEPTIME.
    Could I put a command to read motion and temp/hum in my setup or is that bad coding ettiquette?

    PS: Thanks martin for being such a helpful member!
    I feel like your everyones personal debugger!


  • Plugin Developer

    @BastienVH

    Yes you could call your sensor functions in the end of setup. Another way would be to move the sleep call to the end of the loop, but then you should initialize wake outside the loop before setup and give it a value that will make it call the sensor functions, when the program enters the loop.



  • @martinhjelmare
    Great, thanks.

    The sketch now works as it should.
    Below is the finished sketch.
    I also added a counter so that after 10 consecutive interrupts from the motion sensor, the temp and hum get read and transmited. (motion-on generates an interrupt and motion-off generates an interrupt, so it's actually 5 motion triggers)
    Otherwise if there is a lot of movement during a certain timeframe (mornings), hum and temp won't be measured.

    Later I will edit the sketch to have all transmits together to save more battery power.

    Here's the sketch:

    /**
     * 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.
     *
     *******************************
     *
     * REVISION HISTORY
     * Version 1.0 - Henrik EKblad
     *
     * DESCRIPTION
     * This sketch provides an example how to implement a humidity/temperature
     * sensor using DHT11/DHT-22
     * http://www.mysensors.org/build/humidity
     */
    
    #include <SPI.h>
    #include <MySensor.h>
    #include <DHT.h>
    
    #define DIGITAL_INPUT_MOTION 3   // The digital input you attached your motion sensor.  (Only 2 and 3 generates interrupt!)
    #define INTERRUPT DIGITAL_INPUT_MOTION-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
    #define CHILD_ID_HUM 0
    #define CHILD_ID_TEMP 1
    #define CHILD_ID_MOT 2
    #define HUMIDITY_SENSOR_DIGITAL_PIN 4
    unsigned long SLEEP_TIME = 150000; // Sleep time between reads (in milliseconds)
    
    MySensor gw;
    DHT dht;
    float lastTemp;
    float lastHum;
    boolean metric = true;
    MyMessage msgHum(CHILD_ID_HUM, V_HUM);
    MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
    MyMessage msgMot(CHILD_ID_MOT, V_TRIPPED);
    int node_id = 20;
    boolean lastTripped = false ;
    int wake = 0;
    int motionCount = 0;
    
    void setup()
    {
      gw.begin();
      dht.setup(HUMIDITY_SENSOR_DIGITAL_PIN);
    
      // Send the Sketch Version Information to the Gateway
      gw.sendSketchInfo("Humidity and motion", "1.1");
    
      pinMode(DIGITAL_INPUT_MOTION, INPUT);      // sets the motion sensor digital pin as input
    
      // Register all sensors to gw (they will be created as child devices)
      gw.present(CHILD_ID_HUM, S_HUM);
      gw.present(CHILD_ID_TEMP, S_TEMP);
      gw.present(CHILD_ID_MOT, S_MOTION);
      metric = gw.getConfig().isMetric;
    }
    
    void loop()
    {
      Serial.println(wake);
      if (wake == 1 && motionCount <= 10) {
        Serial.println("wake by motion");
        Serial.println("reading motion");
        motion();
        motionCount = motionCount + 1;
        Serial.println(motionCount);
      }
      else {
        Serial.println("wake by timer");
        Serial.println("reading motion");
        motion();
        Serial.println("reading temp/hum");
        humTemp();
        motionCount = 0;
        Serial.println("motionCount is reset");
      }
      Serial.println("going to sleep now.");
      wake = gw.sleep(INTERRUPT, CHANGE, SLEEP_TIME);
    }
    
    void motion() {
      // Read digital motion value
      boolean tripped = digitalRead(DIGITAL_INPUT_MOTION);
      Serial.println(tripped);
      if (lastTripped != tripped) {
        gw.send(msgMot.set(tripped ? "1" : "0")); // Send tripped value to gw
        lastTripped = tripped;
      }
    }
    
    void humTemp() {
      gw.wait(dht.getMinimumSamplingPeriod());
    
      float temperature = dht.getTemperature();
      if (isnan(temperature)) {
        Serial.println("Failed reading temperature from DHT");
      } else if (temperature != lastTemp) {
        lastTemp = temperature;
        if (!metric) {
          temperature = dht.toFahrenheit(temperature);
        }
        gw.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;
        gw.send(msgHum.set(humidity, 1));
        Serial.print("H: ");
        Serial.println(humidity);
      }
    }
    

Log in to reply
 

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