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. General Discussion
  3. ISR Pulse Meter Question

ISR Pulse Meter Question

Scheduled Pinned Locked Moved General Discussion
12 Posts 4 Posters 1.1k Views 2 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.
  • K Offline
    K Offline
    killerfreek
    wrote on last edited by
    #1

    In the example code, I am trying to understand the sleep behaviour. In testing, if I set sleep time to 10 mins it works fine and counts pulses. Then every 10 minutes it sends the data to Domoticz.

    My question is that I understood an ISR would return to the main loop when done, in this case counting one pulse. If this is the case however, it would then send the data to gateway since the pulse count is different.

    Instead it seems to be just counting the pulses and returning back to sleep until the sleep timer runs out. Then if the pulse counts are different, sends the count to gw and repeats. Hence the 10 minute updates.

    I clearly don't understand sometging here, can anyone please explain? Many thanks.

    /*
     * 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-2018 Sensnology AB
     * Full contributor list: https://github.com/mysensors/MySensors/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
     * Version 1.1 - GizMoCuz
     *
     * DESCRIPTION
     * Use this sensor to measure volume and flow of your house water meter.
     * You need to set the correct pulsefactor of your meter (pulses per m3).
     * The sensor starts by fetching current volume reading from gateway (VAR 1).
     * Reports both volume and flow back to gateway.
     *
     * Unfortunately millis() won't increment when the Arduino is in
     * sleepmode. So we cannot make this sensor sleep if we also want
     * to calculate/report flow.
     * http://www.mysensors.org/build/pulse_water
     */
    
    // Enable debug prints to serial monitor
    #define MY_DEBUG
    
    // Enable and select radio type attached
    #define MY_RADIO_RF24
    //#define MY_RADIO_NRF5_ESB
    //#define MY_RADIO_RFM69
    //#define MY_RADIO_RFM95
    
    #include <MySensors.h>
    
    #define DIGITAL_INPUT_SENSOR 3                  // The digital input you attached your sensor.  (Only 2 and 3 generates interrupt!)
    
    #define PULSE_FACTOR 1000                       // Number of blinks per m3 of your meter (One rotation/liter)
    
    #define SLEEP_MODE false                        // flowvalue can only be reported when sleep mode is false.
    
    #define MAX_FLOW 40                             // Max flow (l/min) value to report. This filters outliers.
    
    #define CHILD_ID 1                              // Id of the sensor child
    
    uint32_t SEND_FREQUENCY =
        30000;           // Minimum time between send (in milliseconds). We don't want to spam the gateway.
    
    MyMessage flowMsg(CHILD_ID,V_FLOW);
    MyMessage volumeMsg(CHILD_ID,V_VOLUME);
    MyMessage lastCounterMsg(CHILD_ID,V_VAR1);
    
    double ppl = ((double)PULSE_FACTOR)/1000;        // Pulses per liter
    
    volatile uint32_t pulseCount = 0;
    volatile uint32_t lastBlink = 0;
    volatile double flow = 0;
    bool pcReceived = false;
    uint32_t oldPulseCount = 0;
    uint32_t newBlink = 0;
    double oldflow = 0;
    double volume =0;
    double oldvolume =0;
    uint32_t lastSend =0;
    uint32_t lastPulse =0;
    
    void setup()
    {
    	// initialize our digital pins internal pullup resistor so one pulse switches from high to low (less distortion)
    	pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
    
    	pulseCount = oldPulseCount = 0;
    
    	// Fetch last known pulse count value from gw
    	request(CHILD_ID, V_VAR1);
    
    	lastSend = lastPulse = millis();
    
    	attachInterrupt(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), onPulse, FALLING);
    }
    
    void presentation()
    {
    	// Send the sketch version information to the gateway and Controller
    	sendSketchInfo("Water Meter", "1.1");
    
    	// Register this device as Water flow sensor
    	present(CHILD_ID, S_WATER);
    }
    
    void loop()
    {
    	uint32_t currentTime = millis();
    
    	// Only send values at a maximum frequency or woken up from sleep
    	if (SLEEP_MODE || (currentTime - lastSend > SEND_FREQUENCY)) {
    		lastSend=currentTime;
    
    		if (!pcReceived) {
    			//Last Pulsecount not yet received from controller, request it again
    			request(CHILD_ID, V_VAR1);
    			return;
    		}
    
    		if (!SLEEP_MODE && flow != oldflow) {
    			oldflow = flow;
    
    			Serial.print("l/min:");
    			Serial.println(flow);
    
    			// Check that we don't get unreasonable large flow value.
    			// could happen when long wraps or false interrupt triggered
    			if (flow<((uint32_t)MAX_FLOW)) {
    				send(flowMsg.set(flow, 2));                   // Send flow value to gw
    			}
    		}
    
    		// No Pulse count received in 2min
    		if(currentTime - lastPulse > 120000) {
    			flow = 0;
    		}
    
    		// Pulse count has changed
    		if ((pulseCount != oldPulseCount)||(!SLEEP_MODE)) {
    			oldPulseCount = pulseCount;
    
    			Serial.print("pulsecount:");
    			Serial.println(pulseCount);
    
    			send(lastCounterMsg.set(pulseCount));                  // Send  pulsecount value to gw in VAR1
    
    			double volume = ((double)pulseCount/((double)PULSE_FACTOR));
    			if ((volume != oldvolume)||(!SLEEP_MODE)) {
    				oldvolume = volume;
    
    				Serial.print("volume:");
    				Serial.println(volume, 3);
    
    				send(volumeMsg.set(volume, 3));               // Send volume value to gw
    			}
    		}
    	}
    	if (SLEEP_MODE) {
    		sleep(SEND_FREQUENCY);
    	}
    }
    
    void receive(const MyMessage &message)
    {
    	if (message.type==V_VAR1) {
    		uint32_t gwPulseCount=message.getULong();
    		pulseCount += gwPulseCount;
    		flow=oldflow=0;
    		Serial.print("Received last pulse count from gw:");
    		Serial.println(pulseCount);
    		pcReceived = true;
    	}
    }
    
    void onPulse()
    {
    	if (!SLEEP_MODE) {
    		uint32_t newBlink = micros();
    		uint32_t interval = newBlink-lastBlink;
    
    		if (interval!=0) {
    			lastPulse = millis();
    			if (interval<500000L) {
    				// Sometimes we get interrupt on RISING,  500000 = 0.5 second debounce ( max 120 l/min)
    				return;
    			}
    			flow = (60000000.0 /interval) / ppl;
    		}
    		lastBlink = newBlink;
    	}
    	pulseCount++;
    }
    ``
    mfalkviddM 1 Reply Last reply
    0
    • K killerfreek

      In the example code, I am trying to understand the sleep behaviour. In testing, if I set sleep time to 10 mins it works fine and counts pulses. Then every 10 minutes it sends the data to Domoticz.

      My question is that I understood an ISR would return to the main loop when done, in this case counting one pulse. If this is the case however, it would then send the data to gateway since the pulse count is different.

      Instead it seems to be just counting the pulses and returning back to sleep until the sleep timer runs out. Then if the pulse counts are different, sends the count to gw and repeats. Hence the 10 minute updates.

      I clearly don't understand sometging here, can anyone please explain? Many thanks.

      /*
       * 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-2018 Sensnology AB
       * Full contributor list: https://github.com/mysensors/MySensors/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
       * Version 1.1 - GizMoCuz
       *
       * DESCRIPTION
       * Use this sensor to measure volume and flow of your house water meter.
       * You need to set the correct pulsefactor of your meter (pulses per m3).
       * The sensor starts by fetching current volume reading from gateway (VAR 1).
       * Reports both volume and flow back to gateway.
       *
       * Unfortunately millis() won't increment when the Arduino is in
       * sleepmode. So we cannot make this sensor sleep if we also want
       * to calculate/report flow.
       * http://www.mysensors.org/build/pulse_water
       */
      
      // Enable debug prints to serial monitor
      #define MY_DEBUG
      
      // Enable and select radio type attached
      #define MY_RADIO_RF24
      //#define MY_RADIO_NRF5_ESB
      //#define MY_RADIO_RFM69
      //#define MY_RADIO_RFM95
      
      #include <MySensors.h>
      
      #define DIGITAL_INPUT_SENSOR 3                  // The digital input you attached your sensor.  (Only 2 and 3 generates interrupt!)
      
      #define PULSE_FACTOR 1000                       // Number of blinks per m3 of your meter (One rotation/liter)
      
      #define SLEEP_MODE false                        // flowvalue can only be reported when sleep mode is false.
      
      #define MAX_FLOW 40                             // Max flow (l/min) value to report. This filters outliers.
      
      #define CHILD_ID 1                              // Id of the sensor child
      
      uint32_t SEND_FREQUENCY =
          30000;           // Minimum time between send (in milliseconds). We don't want to spam the gateway.
      
      MyMessage flowMsg(CHILD_ID,V_FLOW);
      MyMessage volumeMsg(CHILD_ID,V_VOLUME);
      MyMessage lastCounterMsg(CHILD_ID,V_VAR1);
      
      double ppl = ((double)PULSE_FACTOR)/1000;        // Pulses per liter
      
      volatile uint32_t pulseCount = 0;
      volatile uint32_t lastBlink = 0;
      volatile double flow = 0;
      bool pcReceived = false;
      uint32_t oldPulseCount = 0;
      uint32_t newBlink = 0;
      double oldflow = 0;
      double volume =0;
      double oldvolume =0;
      uint32_t lastSend =0;
      uint32_t lastPulse =0;
      
      void setup()
      {
      	// initialize our digital pins internal pullup resistor so one pulse switches from high to low (less distortion)
      	pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
      
      	pulseCount = oldPulseCount = 0;
      
      	// Fetch last known pulse count value from gw
      	request(CHILD_ID, V_VAR1);
      
      	lastSend = lastPulse = millis();
      
      	attachInterrupt(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), onPulse, FALLING);
      }
      
      void presentation()
      {
      	// Send the sketch version information to the gateway and Controller
      	sendSketchInfo("Water Meter", "1.1");
      
      	// Register this device as Water flow sensor
      	present(CHILD_ID, S_WATER);
      }
      
      void loop()
      {
      	uint32_t currentTime = millis();
      
      	// Only send values at a maximum frequency or woken up from sleep
      	if (SLEEP_MODE || (currentTime - lastSend > SEND_FREQUENCY)) {
      		lastSend=currentTime;
      
      		if (!pcReceived) {
      			//Last Pulsecount not yet received from controller, request it again
      			request(CHILD_ID, V_VAR1);
      			return;
      		}
      
      		if (!SLEEP_MODE && flow != oldflow) {
      			oldflow = flow;
      
      			Serial.print("l/min:");
      			Serial.println(flow);
      
      			// Check that we don't get unreasonable large flow value.
      			// could happen when long wraps or false interrupt triggered
      			if (flow<((uint32_t)MAX_FLOW)) {
      				send(flowMsg.set(flow, 2));                   // Send flow value to gw
      			}
      		}
      
      		// No Pulse count received in 2min
      		if(currentTime - lastPulse > 120000) {
      			flow = 0;
      		}
      
      		// Pulse count has changed
      		if ((pulseCount != oldPulseCount)||(!SLEEP_MODE)) {
      			oldPulseCount = pulseCount;
      
      			Serial.print("pulsecount:");
      			Serial.println(pulseCount);
      
      			send(lastCounterMsg.set(pulseCount));                  // Send  pulsecount value to gw in VAR1
      
      			double volume = ((double)pulseCount/((double)PULSE_FACTOR));
      			if ((volume != oldvolume)||(!SLEEP_MODE)) {
      				oldvolume = volume;
      
      				Serial.print("volume:");
      				Serial.println(volume, 3);
      
      				send(volumeMsg.set(volume, 3));               // Send volume value to gw
      			}
      		}
      	}
      	if (SLEEP_MODE) {
      		sleep(SEND_FREQUENCY);
      	}
      }
      
      void receive(const MyMessage &message)
      {
      	if (message.type==V_VAR1) {
      		uint32_t gwPulseCount=message.getULong();
      		pulseCount += gwPulseCount;
      		flow=oldflow=0;
      		Serial.print("Received last pulse count from gw:");
      		Serial.println(pulseCount);
      		pcReceived = true;
      	}
      }
      
      void onPulse()
      {
      	if (!SLEEP_MODE) {
      		uint32_t newBlink = micros();
      		uint32_t interval = newBlink-lastBlink;
      
      		if (interval!=0) {
      			lastPulse = millis();
      			if (interval<500000L) {
      				// Sometimes we get interrupt on RISING,  500000 = 0.5 second debounce ( max 120 l/min)
      				return;
      			}
      			flow = (60000000.0 /interval) / ppl;
      		}
      		lastBlink = newBlink;
      	}
      	pulseCount++;
      }
      ``
      mfalkviddM Offline
      mfalkviddM Offline
      mfalkvidd
      Mod
      wrote on last edited by mfalkvidd
      #2

      @killerfreek welcome to the forum :-)
      attachinterrupt connects the ISR with the interrupt. Sleep is called without interrupt, so sleep will not return until the set amount of time.

      It is possible to call sleep so it wakes up for every pulse. See https://www.mysensors.org/download/sensor_api_20#sleeping for documentation.

      K 1 Reply Last reply
      0
      • mfalkviddM mfalkvidd

        @killerfreek welcome to the forum :-)
        attachinterrupt connects the ISR with the interrupt. Sleep is called without interrupt, so sleep will not return until the set amount of time.

        It is possible to call sleep so it wakes up for every pulse. See https://www.mysensors.org/download/sensor_api_20#sleeping for documentation.

        K Offline
        K Offline
        killerfreek
        wrote on last edited by
        #3

        @mfalkvidd Many thanks!

        1 Reply Last reply
        1
        • B Offline
          B Offline
          barrydou
          wrote on last edited by barrydou
          #4

          Hello

          I'm asking exactly the same question.
          I mixed this sketch with another one for battery measurement (and added fonction to resend last volume for updating domoticz, software debounce, but that's not the main point).

          Each time I have a pulse, the loop start again, even if it was sleeping.

          I have also tried this simple code

          
          // Enable debug prints to serial monitor
          #define MY_DEBUG
          
          // Enable and select radio type attached
          #define MY_RADIO_RF24
          
          #include <MySensors.h>
          
          #define DIGITAL_INPUT_SENSOR 3                  // The digital input you attached your sensor.  (Only 2 and 3 generates interrupt!)
          
          #define CHILD_ID 1                              // Id of the sensor child
          
          unsigned long SEND_FREQUENCY =  60000;           // Minimum time between send (in milliseconds). We don't want to spam the gateway.
          
          volatile uint32_t pulseCount = 0;
          uint32_t oldPulseCount = 0;
          
          
          
          void setup()
          {
            // initialize our digital pins internal pullup resistor so one pulse switches from high to low (less distortion)
            pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
            pulseCount = oldPulseCount = 0;
            attachInterrupt(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), onPulse, FALLING);
          }
          
          void presentation()
          {
            // Send the sketch version information to the gateway and Controller
            sendSketchInfo("TEST", "1.1");
            // Register this device as Water flow sensor
            present(CHILD_ID, S_GAS);
          }
          
          void loop()
          {
          
            // Pulse count has changed
            if ((pulseCount != oldPulseCount)) {
              oldPulseCount = pulseCount;
          
              Serial.print("pulsecount:");
              Serial.println(pulseCount);
            } else {
              Serial.println("pulsecount not changed");
          
            }
          
            sleep(SEND_FREQUENCY);
          
          }
          
          void onPulse()
          {
            pulseCount++;
          }
          
          

          And here is the log

          18:33:44.593 -> 3229 MCO:BGN:INIT OK,TSP=1
          18:33:44.593 -> pulsecount not changed
          18:33:44.593 -> 3233 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
          18:33:44.593 -> 3239 TSF:TDI:TSL
          18:34:48.589 -> 3241 MCO:SLP:WUP=-1
          18:34:48.589 -> 3244 TSF:TRI:TSB
          18:34:48.635 -> pulsecount not changed
          18:34:48.635 -> 3250 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
          18:34:48.635 -> 3258 TSF:TDI:TSL
          18:34:57.719 -> 3260 MCO:SLP:WUP=-1
          18:34:57.719 -> 3262 TSF:TRI:TSB
          18:34:57.763 -> pulsecount:159
          18:34:57.763 -> 3270 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
          18:34:57.763 -> 3276 TSF:TDI:TSL
          18:34:58.212 -> 3280 MCO:SLP:WUP=-1
          18:34:58.212 -> 3282 TSF:TRI:TSB
          18:34:58.212 -> pulsecount:364
          18:34:58.212 -> 3289 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
          18:34:58.212 -> 3295 TSF:TDI:TSL
          18:35:04.813 -> 3299 MCO:SLP:WUP=-1
          18:35:04.813 -> 3301 TSF:TRI:TSB
          18:35:04.813 -> pulsecount:380
          18:35:04.813 -> 3307 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
          18:35:04.861 -> 3313 TSF:TDI:TSL
          18:35:05.211 -> 3315 MCO:SLP:WUP=-1
          18:35:05.211 -> 3319 TSF:TRI:TSB
          18:35:05.211 -> pulsecount:394
          18:35:05.211 -> 3325 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
          18:35:05.211 -> 3332 TSF:TDI:TSL
          18:35:10.827 -> 3334 MCO:SLP:WUP=-1
          18:35:10.827 -> 3336 TSF:TRI:TSB
          18:35:10.827 -> pulsecount:402
          18:35:10.827 -> 3344 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
          18:35:10.827 -> 3350 TSF:TDI:TSL
          18:36:14.864 -> 3352 MCO:SLP:WUP=-1
          18:36:14.864 -> 3354 TSF:TRI:TSB
          18:36:14.864 -> pulsecount not changed
          18:36:14.864 -> 3360 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
          18:36:14.864 -> 3368 TSF:TDI:TSL
          

          As you can see with timestamp, you don't get value every minute, but every minute AND each time ISR is run. The main loop continue don't wait the end of the time to continue. What do I miss ?

          Thank you for your help

          mfalkviddM 1 Reply Last reply
          0
          • B barrydou

            Hello

            I'm asking exactly the same question.
            I mixed this sketch with another one for battery measurement (and added fonction to resend last volume for updating domoticz, software debounce, but that's not the main point).

            Each time I have a pulse, the loop start again, even if it was sleeping.

            I have also tried this simple code

            
            // Enable debug prints to serial monitor
            #define MY_DEBUG
            
            // Enable and select radio type attached
            #define MY_RADIO_RF24
            
            #include <MySensors.h>
            
            #define DIGITAL_INPUT_SENSOR 3                  // The digital input you attached your sensor.  (Only 2 and 3 generates interrupt!)
            
            #define CHILD_ID 1                              // Id of the sensor child
            
            unsigned long SEND_FREQUENCY =  60000;           // Minimum time between send (in milliseconds). We don't want to spam the gateway.
            
            volatile uint32_t pulseCount = 0;
            uint32_t oldPulseCount = 0;
            
            
            
            void setup()
            {
              // initialize our digital pins internal pullup resistor so one pulse switches from high to low (less distortion)
              pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
              pulseCount = oldPulseCount = 0;
              attachInterrupt(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), onPulse, FALLING);
            }
            
            void presentation()
            {
              // Send the sketch version information to the gateway and Controller
              sendSketchInfo("TEST", "1.1");
              // Register this device as Water flow sensor
              present(CHILD_ID, S_GAS);
            }
            
            void loop()
            {
            
              // Pulse count has changed
              if ((pulseCount != oldPulseCount)) {
                oldPulseCount = pulseCount;
            
                Serial.print("pulsecount:");
                Serial.println(pulseCount);
              } else {
                Serial.println("pulsecount not changed");
            
              }
            
              sleep(SEND_FREQUENCY);
            
            }
            
            void onPulse()
            {
              pulseCount++;
            }
            
            

            And here is the log

            18:33:44.593 -> 3229 MCO:BGN:INIT OK,TSP=1
            18:33:44.593 -> pulsecount not changed
            18:33:44.593 -> 3233 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
            18:33:44.593 -> 3239 TSF:TDI:TSL
            18:34:48.589 -> 3241 MCO:SLP:WUP=-1
            18:34:48.589 -> 3244 TSF:TRI:TSB
            18:34:48.635 -> pulsecount not changed
            18:34:48.635 -> 3250 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
            18:34:48.635 -> 3258 TSF:TDI:TSL
            18:34:57.719 -> 3260 MCO:SLP:WUP=-1
            18:34:57.719 -> 3262 TSF:TRI:TSB
            18:34:57.763 -> pulsecount:159
            18:34:57.763 -> 3270 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
            18:34:57.763 -> 3276 TSF:TDI:TSL
            18:34:58.212 -> 3280 MCO:SLP:WUP=-1
            18:34:58.212 -> 3282 TSF:TRI:TSB
            18:34:58.212 -> pulsecount:364
            18:34:58.212 -> 3289 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
            18:34:58.212 -> 3295 TSF:TDI:TSL
            18:35:04.813 -> 3299 MCO:SLP:WUP=-1
            18:35:04.813 -> 3301 TSF:TRI:TSB
            18:35:04.813 -> pulsecount:380
            18:35:04.813 -> 3307 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
            18:35:04.861 -> 3313 TSF:TDI:TSL
            18:35:05.211 -> 3315 MCO:SLP:WUP=-1
            18:35:05.211 -> 3319 TSF:TRI:TSB
            18:35:05.211 -> pulsecount:394
            18:35:05.211 -> 3325 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
            18:35:05.211 -> 3332 TSF:TDI:TSL
            18:35:10.827 -> 3334 MCO:SLP:WUP=-1
            18:35:10.827 -> 3336 TSF:TRI:TSB
            18:35:10.827 -> pulsecount:402
            18:35:10.827 -> 3344 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
            18:35:10.827 -> 3350 TSF:TDI:TSL
            18:36:14.864 -> 3352 MCO:SLP:WUP=-1
            18:36:14.864 -> 3354 TSF:TRI:TSB
            18:36:14.864 -> pulsecount not changed
            18:36:14.864 -> 3360 MCO:SLP:MS=60000,SMS=0,I1=255,M1=255,I2=255,M2=255
            18:36:14.864 -> 3368 TSF:TDI:TSL
            

            As you can see with timestamp, you don't get value every minute, but every minute AND each time ISR is run. The main loop continue don't wait the end of the time to continue. What do I miss ?

            Thank you for your help

            mfalkviddM Offline
            mfalkviddM Offline
            mfalkvidd
            Mod
            wrote on last edited by
            #5

            @barrydou my guess is that the interrupt causes the sleep function to believe that 8 seconds has passed (which is the maximum time for the watchdog), so it deducts 8 seconds for each pulse.

            1 Reply Last reply
            0
            • B Offline
              B Offline
              barrydou
              wrote on last edited by
              #6

              Hi

              Thank you for your response.

              Does it means that if I sleep longer, 300 sec for example, and I debounce the switch on interrupt not get a huge increase on a single event, It will sleep 300sec with no pulse, 292 sec with one pule, 284 sec for two pulse (and so on :) )?

              Thank you again for your help and explanation

              mfalkviddM 1 Reply Last reply
              0
              • B barrydou

                Hi

                Thank you for your response.

                Does it means that if I sleep longer, 300 sec for example, and I debounce the switch on interrupt not get a huge increase on a single event, It will sleep 300sec with no pulse, 292 sec with one pule, 284 sec for two pulse (and so on :) )?

                Thank you again for your help and explanation

                mfalkviddM Offline
                mfalkviddM Offline
                mfalkvidd
                Mod
                wrote on last edited by mfalkvidd
                #7

                @barrydou almost. The sleep function will set the watchdog to 8 seconds and power down the node. When the node is woken up, sleep will assume that 8 seconds has passed.

                If the interrupt happens 7.999 seconds after the node powered down, assuming 8 seconds will yield an error of 0.001 seconds.

                If the interrupt happens 0.2 seconds after the node powered down, assuming 8 seconds will yield an error of 7.8 seconds.

                So it depends on how quickly the interrupts come. In your log you had about 16 pulses per second. That should yield an error of 8-1/16 seconds which is 7.9375 per pulse.

                1 Reply Last reply
                0
                • B Offline
                  B Offline
                  barrydou
                  wrote on last edited by
                  #8

                  Ok thank you very much for all the informations.

                  The high rate of interrupts is due to the way I generate the interrupt for the test : with a breadboard jumper manually connected on the pin, so it generates a lot of bounces.

                  In a normal behaviour, I will have a reed switch for my gas counter. So it should not occur so much.

                  I have not realised that the sleep function "hide" WDT for sleeping (but it's quite logic).
                  In fact, in my example, my high rate of interrupts causes the loop to start again. With one event and 6 bounces (so 7 interrupts), the wait period is considered elapsed, even if it was sleeping only from a few seconds.

                  I never realised that before :)

                  zboblamontZ 1 Reply Last reply
                  0
                  • B barrydou

                    Ok thank you very much for all the informations.

                    The high rate of interrupts is due to the way I generate the interrupt for the test : with a breadboard jumper manually connected on the pin, so it generates a lot of bounces.

                    In a normal behaviour, I will have a reed switch for my gas counter. So it should not occur so much.

                    I have not realised that the sleep function "hide" WDT for sleeping (but it's quite logic).
                    In fact, in my example, my high rate of interrupts causes the loop to start again. With one event and 6 bounces (so 7 interrupts), the wait period is considered elapsed, even if it was sleeping only from a few seconds.

                    I never realised that before :)

                    zboblamontZ Offline
                    zboblamontZ Offline
                    zboblamont
                    wrote on last edited by
                    #9

                    @barrydou I admit confusion, it sounds as if your objective is to derive a flow rate at the gas meter, which you will then pass to Domoticz, but puzzled why? Process monitoring rather than domestic consumption?
                    An RTC is the only sure way to get time accuracy, but a standard domestic gas meter reed pulses every 10 litres.
                    I just checked Domoticz log on a heating cycle, fastest single pulse would be somewhere around 21 secs, but this Node only sends in every 5 pulses to extend battery life.
                    The sketch used here sleeps the loop indefinitely (sleep=0) until triggered by the Reed.
                    One caution - I got crazy readings initially despite a debounce circuit, and eventually found the reed closes ca 5 seconds on a typical run, solved by a subroutine check for reed reset before going to sleep.
                    The physical and Domoticz meters are still in perfect sync, and the Node on the same 2AA after well over a year. I might go back to single pulses later for greater accuracy, but only out of curiosity.
                    The standard Domoticz Gas Meter groups readings into hourly and daily totals. For your own puzzle, the readings come in on timestamps, so perhaps NodeRed might be an alternative way to go to derive a flow rate...

                    1 Reply Last reply
                    1
                    • B Offline
                      B Offline
                      barrydou
                      wrote on last edited by barrydou
                      #10

                      No no, my intention is just to send the pulse count, every 10 or 15 minutes, with battery powered sensor (and resend the last value even if it not change, due to domoticz not reporting heartbeat, but that is another story :) )

                      Hourly and daily value are ok for me.

                      I was using this sketch as an example just to see how it work and if it could directly do the job.
                      I enabled sleep_mode.
                      I was doing that with a testing arduino, directly connected to radio with jumper, and using jumper to simulate reed.
                      Each time I was doing a pulse that way, the node send the information directly. I was not understanding why. So I run a minimal sketch to try to understand, and I found this thread ...

                      Finally with all those informations, i think i'll do something like that:

                      • The loop sleeps for 1 minute, or for the reed interrupt.
                      • When the loop restart, it checked if the reed interrupt occured with the return value of sleep
                        -- if yes, it increments pulse count, waits a few ms for debounce, and continues
                        -- if no it continues
                      • every 10 loops, a msg is send with the pulse count
                      • every 60 loops, the battery level is checked
                      • and then it sleeps again for 1 minute or if the reed interrupt occurs

                      Thank you again for this great help

                      zboblamontZ 1 Reply Last reply
                      1
                      • B barrydou

                        No no, my intention is just to send the pulse count, every 10 or 15 minutes, with battery powered sensor (and resend the last value even if it not change, due to domoticz not reporting heartbeat, but that is another story :) )

                        Hourly and daily value are ok for me.

                        I was using this sketch as an example just to see how it work and if it could directly do the job.
                        I enabled sleep_mode.
                        I was doing that with a testing arduino, directly connected to radio with jumper, and using jumper to simulate reed.
                        Each time I was doing a pulse that way, the node send the information directly. I was not understanding why. So I run a minimal sketch to try to understand, and I found this thread ...

                        Finally with all those informations, i think i'll do something like that:

                        • The loop sleeps for 1 minute, or for the reed interrupt.
                        • When the loop restart, it checked if the reed interrupt occured with the return value of sleep
                          -- if yes, it increments pulse count, waits a few ms for debounce, and continues
                          -- if no it continues
                        • every 10 loops, a msg is send with the pulse count
                        • every 60 loops, the battery level is checked
                        • and then it sleeps again for 1 minute or if the reed interrupt occurs

                        Thank you again for this great help

                        zboblamontZ Offline
                        zboblamontZ Offline
                        zboblamont
                        wrote on last edited by
                        #11

                        @barrydou Ok, went through much the same head-scratching, even if I still didn't fully understand the detail then, it pretty much worked.
                        There really is no need to insert a time element, Domoticz makes 5 minute accumulations then hourly then daily.
                        If you leave the loop to ONLY activate on a reed pulse, send in after X pulses (if you wish), and send in battery after YxX pulses, you should be good to go with long battery life.
                        Only issue I fell over was holding the reading originally as an INT caused chaos in Domoticz when it rolled over to a minus.
                        From memory changing it to a LONG worked but left trailing digits on the live meter update in Domoticz, but I ignored them anyway. One day I might look at it again, but it accurately records otherwise.

                        Looked back for the sketch, it is so early in the learning curve the sketch name doesn't bear the Node Number like the rest.

                        #include <T2WhisperNode.h>
                        
                        /**
                         * 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
                         *
                         * Interrupt driven binary switch example with dual interrupts
                         * Author: Patrick 'Anticimex' Fallberg
                         * Connect one button or door/window reed switch between
                         * digitial I/O pin 3 (BUTTON_PIN below) and GND and the other
                         * one in similar fashion on digital I/O pin 2.
                         * This example is designed to fit Arduino Nano/Pro Mini
                         *
                         */
                        
                        #include <MyConfig.h>
                        // Enable debug prints to serial monitor
                        //#define MY_DEBUG //Comment out once all working saving memory
                        
                        // Enable and select radio type attached
                        ////#define MY_RADIO_NRF24
                        #define MY_RADIO_RFM69
                        #define MY_RFM69_FREQUENCY RF69_433MHZ  // Define for frequency setting. Needed if you're radio module isn't 868Mhz (868Mhz is default in lib)
                        
                        #define MY_RFM69_NETWORKID 101  // Default is 100 in lib. Uncomment it and set your preferred network id if needed
                        #define MY_NODE_ID 4  //Manually set the node ID here. Comment out to auto assign
                        //A define should be added to provide minimum power output to provide decent RSSI to the Gateway if ATC not available
                        #include <SPI.h>
                        #include <MySensors.h>
                        #define SN "Gas"
                        #define SV "1"
                        #define FIRST_CHILD_ID 1
                        //#define SECOND_CHILD_ID 2
                        #define THIRD_CHILD_ID 3
                        #define REED_PIN 3   // Arduino Digital I/O pin for button/reed switch
                        #define BatteryOn 14
                        #define BatteryIn A6
                        
                        
                        // Change to V_LIGHT if you use S_LIGHT in presentation below
                        MyMessage msg(FIRST_CHILD_ID, V_VOLUME);
                        //MyMessage msg2(SECOND_CHILD_ID, V_VOLUME);
                        MyMessage msg3(THIRD_CHILD_ID, V_VOLTAGE);
                        
                        int GasCycle=0;
                        int batteryread=0;
                        long gasunit=906867L;
                        
                        unsigned long SLEEP_TIME = 0; // Sleep time between reports (in milliseconds)
                        
                        void setup(){
                          // Setup the buttons
                         pinMode(REED_PIN, INPUT);
                         pinMode(BatteryOn, OUTPUT);
                         pinMode(BatteryIn, INPUT);
                         analogReference(INTERNAL);
                        }
                        
                        void presentation(){
                          // Send the sketch version information to the gateway and Controller
                          sendSketchInfo(SN, SV);
                        
                          // Register binary input sensor to sensor_node (they will be created as child devices)
                          // You can use S_DOOR, S_MOTION or S_LIGHT here depending on your usage.
                          // If S_LIGHT is used, remember to update variable type you send in. See "msg" above.
                          present(FIRST_CHILD_ID, S_GAS);
                        //  present(SECOND_CHILD_ID, S_GAS);
                          present(THIRD_CHILD_ID, S_MULTIMETER);
                        }
                        
                        // Loop will respond then wait for interrupt to reloop
                        void loop(){ //Wait for Reed to release from closed
                          while(digitalRead(REED_PIN)==LOW){
                            sleep(1000);
                          }
                          
                          //Battery Read
                         if(batteryread==0){
                          digitalWrite(BatteryOn, HIGH);
                          int voltread = analogRead(BatteryIn);
                          float voltage = (7.272 * voltread) / 1024;
                          sleep(5);
                          digitalWrite(BatteryOn, LOW);
                          send(msg3.set(voltage,2));
                          batteryread=-100; 
                          sleep(50);
                        }
                        
                         //Increment variables
                          batteryread++;
                        //  gastotal++;
                          gasunit++;
                          GasCycle++;
                         
                        
                          if(GasCycle>=5){
                            // Short delay to allow setup to properly settle
                          sleep(5);
                            send(msg.set(gasunit));
                        //    send(msg2.set(gastotal));
                         sleep(100);
                         GasCycle=0;
                         }
                         sleep(100);// Time to settle down
                          // Sleep until something happens with the sensor
                          sleep(digitalPinToInterrupt(REED_PIN),LOW, SLEEP_TIME);
                        
                        }
                        

                        It still needs tidied, but butcher away with it if it helps. Good luck ;)

                        1 Reply Last reply
                        0
                        • B Offline
                          B Offline
                          barrydou
                          wrote on last edited by
                          #12

                          Hello

                          Here is my last code for my gas meter. If it could help somebody

                          /*
                           * 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-2018 Sensnology AB
                           * Full contributor list: https://github.com/mysensors/MySensors/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
                           * Version 1.1 - GizMoCuz
                           *
                           * DESCRIPTION
                           * Use this sensor to measure volume and flow of your house water meter.
                           * You need to set the correct pulsefactor of your meter (pulses per m3).
                           * The sensor starts by fetching current volume reading from gateway (VAR 1).
                           * Reports both volume and flow back to gateway.
                           *
                           * Unfortunately millis() won't increment when the Arduino is in
                           * sleepmode. So we cannot make this sensor sleep if we also want
                           * to calculate/report flow.
                           * http://www.mysensors.org/build/pulse_water
                           */
                          
                          // Enable debug prints to serial monitor
                          #define MY_DEBUG
                          
                          // Enable and select radio type attached
                          #define MY_RADIO_RF24
                          
                          #include <MySensors.h>
                          
                          #define DIGITAL_INPUT_SENSOR 3                  // The digital input you attached your sensor.  (Only 2 and 3 generates interrupt!)
                          #define CHILD_ID 1                              // Id of the sensor child
                          
                          unsigned long loopNumber = 0;
                          unsigned long lastLoopSend = 0;
                          
                          MyMessage volumeMsg(CHILD_ID, V_VOLUME);
                          MyMessage lastCounterMsg(CHILD_ID, V_VAR1);
                          
                          volatile uint32_t pulseCount = 0;
                          bool pcReceived = false;
                          double volume = 0;
                          
                          //=========================
                          // BATTERY MEASURER
                          // VOLTAGE DIVIDER SETUP
                          // 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
                          #define VBAT_PER_BITS 0.003363075
                          #define VMIN 2.2                                  //  Vmin (radio Min Volt)=1.9V (564v)
                          #define VMAX 3.2                                  //  Vmax = (2xAA bat)=3.0V (892v)
                          int batteryPcnt = 0;                              // Calc value for battery %
                          int batLoop = 0;                                  // Loop to help calc average
                          int batArray[4];                                  // Array to store value for average calc.
                          int BATTERY_SENSE_PIN = A0;                       // select the input pin for the battery sense point
                          //=========================
                          
                          
                          void setup() {
                            // initialize our digital pins internal pullup resistor so one pulse switches from high to low (less distortion)
                            pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
                          
                            pulseCount = 0;
                          
                            // Fetch last known pulse count value from gw
                            request(CHILD_ID, V_VAR1);
                          
                            //Battery
                            analogReference(INTERNAL);
                            Serial.print("With Battery VMax (100%) = "); Serial.print(VMAX); Serial.print("volts and Vmin (0%) = "); Serial.print(VMIN); Serial.println(" volts");
                            Serial.print("Battery Percent 25%/50%/75% calculates to: "); Serial.print(((VMAX - VMIN) / 4) + VMIN); Serial.print("/"); Serial.print(((VMAX - VMIN) / 2) + VMIN); Serial.print("/"); Serial.println(VMAX - ((VMAX - VMIN) / 4));
                            delay(1000);
                            int sensorValue = analogRead(BATTERY_SENSE_PIN);
                            delay(50);
                            float Vbat  = sensorValue * VBAT_PER_BITS;
                            int batteryPcnt = static_cast<int>(((Vbat - VMIN) / (VMAX - VMIN)) * 100.);
                            Serial.print("Current battery are measured to (please confirm!): "); Serial.print(batteryPcnt); Serial.print(" % - Or "); Serial.print(Vbat); Serial.println(" Volts");
                          
                          
                          }
                          
                          void presentation()
                          {
                            // Send the sketch version information to the gateway and Controller
                            sendSketchInfo("Gas Meter", "2.0");
                          
                            // Register this device as Water flow sensor
                            present(CHILD_ID, S_GAS);
                          }
                          
                          //=========================
                          // BATTERY MEASURER
                          void MeasureBattery() //The battery calculations
                          {
                            delay(500);
                            // Battery monitoring reading
                            int sensorValue = analogRead(BATTERY_SENSE_PIN);
                            delay(500);
                          
                            // Calculate the battery in %
                            float Vbat  = sensorValue * VBAT_PER_BITS;
                            int batteryPcnt = static_cast<int>(((Vbat - VMIN) / (VMAX - VMIN)) * 100.);
                            Serial.print("Battery percent: "); Serial.print(batteryPcnt); Serial.print(" %"); Serial.print("Battery Voltage: "); Serial.print(Vbat); Serial.println(" Volts");
                          
                            // Add it to array so we get an average of 3 (3x20min)
                            batArray[batLoop] = batteryPcnt;
                          
                            if (batLoop > 2) {
                              batteryPcnt = (batArray[0] + batArray[1] + batArray[2] + batArray[3]);
                              batteryPcnt = batteryPcnt / 4;
                          
                              if (batteryPcnt > 100) {
                                batteryPcnt = 100;
                              }
                              Serial.print("Battery Average (Send): "); Serial.print(batteryPcnt); Serial.println(" %");
                              sendBatteryLevel(batteryPcnt);
                              batLoop = 0;
                            }
                            else
                            {
                              batLoop++;
                            }
                          }
                          
                          void loop() {
                            if (!pcReceived) {
                              //Last Pulsecount not yet received from controller, request it again
                              request(CHILD_ID, V_VAR1);
                              wait(1000);
                              return;
                            }
                            Serial.print("loopNumer=");
                            Serial.println(loopNumber);
                          
                            if (loopNumber % 60 == 0) {
                              Serial.println("Measuring Battery");
                              //=========================
                              // BATTERY MEASURER
                              MeasureBattery();
                              //=========================
                            }
                            if (loopNumber % 10 == 0) {
                              Serial.println("Sending pulse Count");
                              volume = ((double)pulseCount / ((double)1000));
                              send(volumeMsg.set(volume,4));
                              send(lastCounterMsg.set(pulseCount));
                            }
                            Serial.println("I'm sleeping");
                            int8_t cause = sleep(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), FALLING, 60000);
                            Serial.print("WakeUp , cause:");
                            Serial.print(cause);
                            Serial.print("(pin interrupt :");
                            Serial.print(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR));
                            Serial.println(";-1=timer)");
                          
                            if (cause == digitalPinToInterrupt(DIGITAL_INPUT_SENSOR)) {
                              pulseCount++;
                              wait(100);
                            }
                            Serial.print("Pulsecount=");
                            Serial.println(pulseCount);
                          
                            loopNumber++;
                          
                          }
                          
                          void receive(const MyMessage &message)
                          {
                              if (message.type==V_VAR1) {
                                  uint32_t gwPulseCount=message.getULong();
                                  pulseCount += gwPulseCount;
                                  Serial.print("Received last pulse count from gw:");
                                  Serial.println(pulseCount);
                                  pcReceived = true;
                              }
                          }```
                          1 Reply Last reply
                          1
                          Reply
                          • Reply as topic
                          Log in to reply
                          • Oldest to Newest
                          • Newest to Oldest
                          • Most Votes


                          5

                          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