MySensors Library & Examples  2.3.2-36-g2e00bf6
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-2020 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 //#define MY_PJON
46 
47 #include <MySensors.h>
48 
49 #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!)
50 
51 #define PULSE_FACTOR 1000 // Number of blinks per m3 of your meter (One rotation/liter)
52 
53 #define SLEEP_MODE false // flowvalue can only be reported when sleep mode is false.
54 
55 #define MAX_FLOW 40 // Max flow (l/min) value to report. This filters outliers.
56 
57 #define CHILD_ID 1 // Id of the sensor child
58 
59 uint32_t SEND_FREQUENCY =
60  30000; // Minimum time between send (in milliseconds). We don't want to spam the gateway.
61 
62 MyMessage flowMsg(CHILD_ID,V_FLOW);
63 MyMessage volumeMsg(CHILD_ID,V_VOLUME);
64 MyMessage lastCounterMsg(CHILD_ID,V_VAR1);
65 
66 double ppl = ((double)PULSE_FACTOR)/1000; // Pulses per liter
67 
68 volatile uint32_t pulseCount = 0;
69 volatile uint32_t lastBlink = 0;
70 volatile double flow = 0;
71 bool pcReceived = false;
72 uint32_t oldPulseCount = 0;
73 double oldflow = 0;
74 double oldvolume =0;
75 uint32_t lastSend =0;
76 uint32_t lastPulse =0;
77 
78 #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
79 #define IRQ_HANDLER_ATTR ICACHE_RAM_ATTR
80 #else
81 #define IRQ_HANDLER_ATTR
82 #endif
83 
84 void IRQ_HANDLER_ATTR onPulse()
85 {
86  if (!SLEEP_MODE) {
87  uint32_t newBlink = micros();
88  uint32_t interval = newBlink-lastBlink;
89 
90  if (interval!=0) {
91  lastPulse = millis();
92  if (interval<500000L) {
93  // Sometimes we get interrupt on RISING, 500000 = 0.5 second debounce ( max 120 l/min)
94  return;
95  }
96  flow = (60000000.0 /interval) / ppl;
97  }
98  lastBlink = newBlink;
99  }
100  pulseCount++;
101 }
102 
103 void setup()
104 {
105  // initialize our digital pins internal pullup resistor so one pulse switches from high to low (less distortion)
106  pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
107 
108  pulseCount = oldPulseCount = 0;
109 
110  // Fetch last known pulse count value from gw
111  request(CHILD_ID, V_VAR1);
112 
113  lastSend = lastPulse = millis();
114 
115  attachInterrupt(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), onPulse, FALLING);
116 }
117 
119 {
120  // Send the sketch version information to the gateway and Controller
121  sendSketchInfo("Water Meter", "1.1");
122 
123  // Register this device as Water flow sensor
124  present(CHILD_ID, S_WATER);
125 }
126 
127 void loop()
128 {
129  uint32_t currentTime = millis();
130 
131  // Only send values at a maximum frequency or woken up from sleep
132  if (SLEEP_MODE || (currentTime - lastSend > SEND_FREQUENCY)) {
133  lastSend=currentTime;
134 
135  if (!pcReceived) {
136  //Last Pulsecount not yet received from controller, request it again
137  request(CHILD_ID, V_VAR1);
138  return;
139  }
140 
141  if (!SLEEP_MODE && flow != oldflow) {
142  oldflow = flow;
143 
144  Serial.print("l/min:");
145  Serial.println(flow);
146 
147  // Check that we don't get unreasonable large flow value.
148  // could happen when long wraps or false interrupt triggered
149  if (flow<((uint32_t)MAX_FLOW)) {
150  send(flowMsg.set(flow, 2)); // Send flow value to gw
151  }
152  }
153 
154  // No Pulse count received in 2min
155  if(currentTime - lastPulse > 120000) {
156  flow = 0;
157  }
158 
159  // Pulse count has changed
160  if ((pulseCount != oldPulseCount)||(!SLEEP_MODE)) {
161  oldPulseCount = pulseCount;
162 
163  Serial.print("pulsecount:");
164  Serial.println(pulseCount);
165 
166  send(lastCounterMsg.set(pulseCount)); // Send pulsecount value to gw in VAR1
167 
168  double volume = ((double)pulseCount/((double)PULSE_FACTOR));
169  if ((volume != oldvolume)||(!SLEEP_MODE)) {
170  oldvolume = volume;
171 
172  Serial.print("volume:");
173  Serial.println(volume, 3);
174 
175  send(volumeMsg.set(volume, 3)); // Send volume value to gw
176  }
177  }
178  }
179  if (SLEEP_MODE) {
180  sleep(SEND_FREQUENCY, false);
181  }
182 }
183 
184 void receive(const MyMessage &message)
185 {
186  if (message.getType()==V_VAR1) {
187  uint32_t gwPulseCount=message.getULong();
188  pulseCount += gwPulseCount;
189  flow=oldflow=0;
190  Serial.print("Received last pulse count from gw:");
191  Serial.println(pulseCount);
192  pcReceived = true;
193  }
194 }
195 
sendSketchInfo
bool sendSketchInfo(const char *name, const char *version, const bool requestEcho=false)
receive
void receive(const MyMessage &message)
Callback for incoming messages.
Definition: WaterMeterPulseSensor.ino:184
loop
void loop()
Main loop.
Definition: WaterMeterPulseSensor.ino:127
MyMessage::getULong
uint32_t getULong(void) const
Get unsigned 32-bit integer payload.
presentation
void presentation()
Node presentation.
Definition: WaterMeterPulseSensor.ino:118
MyMessage::set
MyMessage & set(const void *payload, const size_t length)
Set entire payload.
send
bool send(MyMessage &msg, const bool requestEcho=false)
MyMessage::getType
uint8_t getType(void) const
Get message type.
request
bool request(const uint8_t childSensorId, const uint8_t variableType, const uint8_t destination=GATEWAY_ADDRESS)
present
bool present(const uint8_t sensorId, const mysensors_sensor_t sensorType, const char *description="", const bool requestEcho=false)
setup
void setup()
Called after node initialises but before main loop.
Definition: WaterMeterPulseSensor.ino:103
sleep
int8_t sleep(const uint32_t sleepingMS, const bool smartSleep=false)
IRQ_HANDLER_ATTR
#define IRQ_HANDLER_ATTR
ESP8266/ESP32 IRQ handlers need to be stored in IRAM.
Definition: MyHwHAL.h:48
MySensors.h
API declaration for MySensors.
MyMessage
MyMessage is used to create, manipulate, send and read MySensors messages.
Definition: MyMessage.h:289