MySensors Library & Examples  2.3.2
WaterMeterPulseSensor.ino
1 /*
2  * The MySensors Arduino library handles the wireless radio link and protocol
3  * between your home built sensors/actuators and HA controller of choice.
4  * The sensors forms a self healing radio network with optional repeaters. Each
5  * repeater and gateway builds a routing tables in EEPROM which keeps track of the
6  * network topology allowing messages to be routed to nodes.
7  *
8  * Created by Henrik Ekblad <[email protected]>
9  * Copyright (C) 2013-2019 Sensnology AB
10  * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
11  *
12  * Documentation: http://www.mysensors.org
13  * Support Forum: http://forum.mysensors.org
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * version 2 as published by the Free Software Foundation.
18  *
19  *******************************
20  *
21  * REVISION HISTORY
22  * Version 1.0 - Henrik Ekblad
23  * Version 1.1 - GizMoCuz
24  *
25  * DESCRIPTION
26  * Use this sensor to measure volume and flow of your house water meter.
27  * You need to set the correct pulsefactor of your meter (pulses per m3).
28  * The sensor starts by fetching current volume reading from gateway (VAR 1).
29  * Reports both volume and flow back to gateway.
30  *
31  * Unfortunately millis() won't increment when the Arduino is in
32  * sleepmode. So we cannot make this sensor sleep if we also want
33  * to calculate/report flow.
34  * http://www.mysensors.org/build/pulse_water
35  */
36 
37 // Enable debug prints to serial monitor
38 #define MY_DEBUG
39 
40 // Enable and select radio type attached
41 #define MY_RADIO_RF24
42 //#define MY_RADIO_NRF5_ESB
43 //#define MY_RADIO_RFM69
44 //#define MY_RADIO_RFM95
45 
46 #include <MySensors.h>
47 
48 #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!)
49 
50 #define PULSE_FACTOR 1000 // Number of blinks per m3 of your meter (One rotation/liter)
51 
52 #define SLEEP_MODE false // flowvalue can only be reported when sleep mode is false.
53 
54 #define MAX_FLOW 40 // Max flow (l/min) value to report. This filters outliers.
55 
56 #define CHILD_ID 1 // Id of the sensor child
57 
58 uint32_t SEND_FREQUENCY =
59  30000; // Minimum time between send (in milliseconds). We don't want to spam the gateway.
60 
61 MyMessage flowMsg(CHILD_ID,V_FLOW);
62 MyMessage volumeMsg(CHILD_ID,V_VOLUME);
63 MyMessage lastCounterMsg(CHILD_ID,V_VAR1);
64 
65 double ppl = ((double)PULSE_FACTOR)/1000; // Pulses per liter
66 
67 volatile uint32_t pulseCount = 0;
68 volatile uint32_t lastBlink = 0;
69 volatile double flow = 0;
70 bool pcReceived = false;
71 uint32_t oldPulseCount = 0;
72 double oldflow = 0;
73 double oldvolume =0;
74 uint32_t lastSend =0;
75 uint32_t lastPulse =0;
76 
77 #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
78 #define IRQ_HANDLER_ATTR ICACHE_RAM_ATTR
79 #else
80 #define IRQ_HANDLER_ATTR
81 #endif
82 
83 void IRQ_HANDLER_ATTR onPulse()
84 {
85  if (!SLEEP_MODE) {
86  uint32_t newBlink = micros();
87  uint32_t interval = newBlink-lastBlink;
88 
89  if (interval!=0) {
90  lastPulse = millis();
91  if (interval<500000L) {
92  // Sometimes we get interrupt on RISING, 500000 = 0.5 second debounce ( max 120 l/min)
93  return;
94  }
95  flow = (60000000.0 /interval) / ppl;
96  }
97  lastBlink = newBlink;
98  }
99  pulseCount++;
100 }
101 
102 void setup()
103 {
104  // initialize our digital pins internal pullup resistor so one pulse switches from high to low (less distortion)
105  pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
106 
107  pulseCount = oldPulseCount = 0;
108 
109  // Fetch last known pulse count value from gw
110  request(CHILD_ID, V_VAR1);
111 
112  lastSend = lastPulse = millis();
113 
114  attachInterrupt(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), onPulse, FALLING);
115 }
116 
118 {
119  // Send the sketch version information to the gateway and Controller
120  sendSketchInfo("Water Meter", "1.1");
121 
122  // Register this device as Water flow sensor
123  present(CHILD_ID, S_WATER);
124 }
125 
126 void loop()
127 {
128  uint32_t currentTime = millis();
129 
130  // Only send values at a maximum frequency or woken up from sleep
131  if (SLEEP_MODE || (currentTime - lastSend > SEND_FREQUENCY)) {
132  lastSend=currentTime;
133 
134  if (!pcReceived) {
135  //Last Pulsecount not yet received from controller, request it again
136  request(CHILD_ID, V_VAR1);
137  return;
138  }
139 
140  if (!SLEEP_MODE && flow != oldflow) {
141  oldflow = flow;
142 
143  Serial.print("l/min:");
144  Serial.println(flow);
145 
146  // Check that we don't get unreasonable large flow value.
147  // could happen when long wraps or false interrupt triggered
148  if (flow<((uint32_t)MAX_FLOW)) {
149  send(flowMsg.set(flow, 2)); // Send flow value to gw
150  }
151  }
152 
153  // No Pulse count received in 2min
154  if(currentTime - lastPulse > 120000) {
155  flow = 0;
156  }
157 
158  // Pulse count has changed
159  if ((pulseCount != oldPulseCount)||(!SLEEP_MODE)) {
160  oldPulseCount = pulseCount;
161 
162  Serial.print("pulsecount:");
163  Serial.println(pulseCount);
164 
165  send(lastCounterMsg.set(pulseCount)); // Send pulsecount value to gw in VAR1
166 
167  double volume = ((double)pulseCount/((double)PULSE_FACTOR));
168  if ((volume != oldvolume)||(!SLEEP_MODE)) {
169  oldvolume = volume;
170 
171  Serial.print("volume:");
172  Serial.println(volume, 3);
173 
174  send(volumeMsg.set(volume, 3)); // Send volume value to gw
175  }
176  }
177  }
178  if (SLEEP_MODE) {
179  sleep(SEND_FREQUENCY, false);
180  }
181 }
182 
183 void receive(const MyMessage &message)
184 {
185  if (message.getType()==V_VAR1) {
186  uint32_t gwPulseCount=message.getULong();
187  pulseCount += gwPulseCount;
188  flow=oldflow=0;
189  Serial.print("Received last pulse count from gw:");
190  Serial.println(pulseCount);
191  pcReceived = true;
192  }
193 }
194 
bool request(const uint8_t childSensorId, const uint8_t variableType, const uint8_t destination=GATEWAY_ADDRESS)
MyMessage is used to create, manipulate, send and read MySensors messages.
Definition: MyMessage.h:289
bool sendSketchInfo(const char *name, const char *version, const bool requestEcho=false)
API declaration for MySensors.
bool send(MyMessage &msg, const bool requestEcho=false)
bool present(const uint8_t sensorId, const mysensors_sensor_t sensorType, const char *description="", const bool requestEcho=false)
void setup()
Called after node initialises but before main loop.
int8_t sleep(const uint32_t sleepingMS, const bool smartSleep=false)
void receive(const MyMessage &message)
Callback for incoming messages.
void presentation()
Node presentation.
uint8_t getType(void) const
Get message type.
#define IRQ_HANDLER_ATTR
ESP8266/ESP32 IRQ handlers need to be stored in IRAM.
Definition: MyHwHAL.h:48
MyMessage & set(const void *payload, const size_t length)
Set entire payload.
void loop()
Main loop.
uint32_t getULong(void) const
Get unsigned 32-bit integer payload.