2121 * REVISION HISTORY
2222 * Version 1.0 - Henrik Ekblad
2323 * Version 1.1 - GizMoCuz
24+ * Version 1.2 - Paolo Rendano
25+ * * factory reset
26+ * * automatic home assistant entities creation
27+ * * counter correction from home assistant using
28+ * service notify.mysensors
29+ * * fixed counter automatically incremented by 1
30+ * at each device restart (due to arduino library
31+ * interrupt bug)
32+ * * other tiny improvements
2433 *
2534 * DESCRIPTION
2635 * Use this sensor to measure volume and flow of your house water meter.
3645
3746// Enable debug prints to serial monitor
3847#define MY_DEBUG
48+ #define APP_DEBUG
49+
50+ // Enable factory reset to REINIT the board with a different ID
51+ // #define FORCE_FACTORY_RESET
52+
53+ // uncomment to rejoin to a previous assigned node id
54+ // #define MY_NODE_ID 58
3955
4056// Enable and select radio type attached
4157#define MY_RADIO_RF24
4258// #define MY_RADIO_NRF5_ESB
4359// #define MY_RADIO_RFM69
4460// #define MY_RADIO_RFM95
4561// #define MY_PJON
62+ #define MY_SPLASH_SCREEN_DISABLED
4663
4764#include < MySensors.h>
4865
49- #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!)
66+ // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!)
67+ #define DIGITAL_INPUT_SENSOR 3
68+
69+ // Arduino Uno/Nano: INTF0 for DIGITAL_INPUT_SENSOR = 2; INTF1 for DIGITAL_INPUT_SENSOR = 3
70+ // Arduino AtMega2560: INTF0->INTF7 see datasheet based on the input you want to attach
71+ #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
72+ #define DIGITAL_INPUT_SENSOR_INTF INTF1
73+ #endif
74+
75+ // Number of blinks per m3 of your meter (One rotation/liter)
76+ #define PULSE_FACTOR 1000 .0d
77+
78+ // flowvalue can only be reported when sleep mode is false.
79+ #define SLEEP_MODE false
80+
81+ // Max flow (l/min) value to report. This filters outliers.
82+ #define MAX_FLOW 40
5083
51- #define PULSE_FACTOR 1000 // Number of blinks per m3 of your meter (One rotation/liter)
84+ // Timeout (in milliseconds) to reset to 0 the flow
85+ // information (assuming no pulses if no flow)
86+ #define FLOW_RESET_TO_ZERO_TIMEOUT 120000
5287
53- #define SLEEP_MODE false // flowvalue can only be reported when sleep mode is false.
88+ // Id of the sensor child
89+ #define CHILD_ID 1
5490
55- #define MAX_FLOW 40 // Max flow (l/min) value to report. This filters outliers.
91+ // Id of the sensor child for counter pulse addition
92+ #define CHILD_ID_VAR1 2
5693
57- #define CHILD_ID 1 // Id of the sensor child
94+ // Minimum time between send (in milliseconds). We don't want to spam the gateway.
95+ #define SEND_FREQUENCY 30000
5896
59- uint32_t SEND_FREQUENCY =
60- 30000 ; // Minimum time between send (in milliseconds). We don't want to spam the gateway.
97+ // Save on board if the home assistant counters have been initialized
98+ // (sufficient condition to see the entity in hass)
99+ #define FIRST_VALUE_SENT_FLAG_POSITION 0
100+ #define FIRST_VALUE_SENT_FLAG_YES 1
101+ #define FIRST_VALUE_SENT_FLAG_NO 255
61102
62103MyMessage flowMsg (CHILD_ID,V_FLOW);
63104MyMessage volumeMsg (CHILD_ID,V_VOLUME);
64105MyMessage lastCounterMsg (CHILD_ID,V_VAR1);
106+ MyMessage volumeAdd (CHILD_ID_VAR1,V_TEXT);
65107
66- double ppl = ((double )PULSE_FACTOR)/1000 ; // Pulses per liter
108+ // Pulses per liter
109+ double ppl = ((double )PULSE_FACTOR)/1000 ;
67110
68111volatile uint32_t pulseCount = 0 ;
69112volatile uint32_t lastBlink = 0 ;
@@ -74,6 +117,7 @@ double oldflow = 0;
74117double oldvolume =0 ;
75118uint32_t lastSend =0 ;
76119uint32_t lastPulse =0 ;
120+ bool firstValuesMessageSent = false ;
77121
78122void IRQ_HANDLER_ATTR onPulse ()
79123{
@@ -84,7 +128,8 @@ void IRQ_HANDLER_ATTR onPulse()
84128 if (interval!=0 ) {
85129 lastPulse = millis ();
86130 if (interval<500000L ) {
87- // Sometimes we get interrupt on RISING, 500000 = 0.5 second debounce ( max 120 l/min)
131+ // Sometimes we get interrupt on RISING,
132+ // 500000 = 0.5 second debounce ( max 120 l/min)
88133 return ;
89134 }
90135 flow = (60000000.0 /interval) / ppl;
@@ -96,26 +141,51 @@ void IRQ_HANDLER_ATTR onPulse()
96141
97142void setup ()
98143{
99- // initialize our digital pins internal pullup resistor so one pulse switches from high to low (less distortion)
144+ #ifdef FORCE_FACTORY_RESET
145+ // got from mysensors clear e2p sketch
146+ for (uint16_t i=0 ; i<EEPROM_LOCAL_CONFIG_ADDRESS; i++) {
147+ hwWriteConfig (i,0xFF );
148+ }
149+ // reset state for this sensor
150+ forceFactoryReset ();
151+ Serial.println (" Factory reset complete" );
152+ while (true );
153+ #endif
154+
155+ // initialize our digital pins internal pullup resistor so one pulse
156+ // switches from high to low (less distortion)
100157 pinMode (DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
101158
102159 pulseCount = oldPulseCount = 0 ;
103160
161+ // initialize home assistant values
162+ checkAndFirstTimeInitValuesOnHomeAssistant ();
163+
104164 // Fetch last known pulse count value from gw
105165 request (CHILD_ID, V_VAR1);
106166
107167 lastSend = lastPulse = millis ();
108168
169+ // fix for arduino library bug calling ISR function
170+ // at startup see https://github.com/arduino/ArduinoCore-avr/issues/244
171+ #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
172+ EIFR |= (1 << DIGITAL_INPUT_SENSOR_INTF);
173+ #endif
174+
109175 attachInterrupt (digitalPinToInterrupt (DIGITAL_INPUT_SENSOR), onPulse, FALLING);
110176}
111177
112178void presentation ()
113179{
114180 // Send the sketch version information to the gateway and Controller
115- sendSketchInfo (" Water Meter" , " 1.1 " );
181+ sendSketchInfo (" Water Meter" , " 1.2 " );
116182
117183 // Register this device as Water flow sensor
118184 present (CHILD_ID, S_WATER);
185+
186+ // Add info entity to manage corrections on pulse counter
187+ // (on Home Assistant this can be hidden)
188+ present (CHILD_ID_VAR1, S_INFO);
119189}
120190
121191void loop ()
@@ -127,46 +197,51 @@ void loop()
127197 lastSend=currentTime;
128198
129199 if (!pcReceived) {
130- // Last Pulsecount not yet received from controller, request it again
200+ // Last Pulsecount not yet received from controller,
201+ // request it again
131202 request (CHILD_ID, V_VAR1);
132203 return ;
133204 }
134205
135206 if (!SLEEP_MODE && flow != oldflow) {
136207 oldflow = flow;
137-
208+ # ifdef APP_DEBUG
138209 Serial.print (" l/min:" );
139210 Serial.println (flow);
140-
211+ # endif
141212 // Check that we don't get unreasonable large flow value.
142213 // could happen when long wraps or false interrupt triggered
143214 if (flow<((uint32_t )MAX_FLOW)) {
144- send (flowMsg.set (flow, 2 )); // Send flow value to gw
215+ // Send flow value to gw
216+ send (flowMsg.set (flow, 2 ));
145217 }
146218 }
147219
148- // No Pulse count received in 2min
149- if (currentTime - lastPulse > 120000 ) {
220+ // No Pulse count received for a defined time
221+ if (currentTime - lastPulse > FLOW_RESET_TO_ZERO_TIMEOUT ) {
150222 flow = 0 ;
151223 }
152224
153225 // Pulse count has changed
154226 if ((pulseCount != oldPulseCount)||(!SLEEP_MODE)) {
155227 oldPulseCount = pulseCount;
156-
228+ # ifdef APP_DEBUG
157229 Serial.print (" pulsecount:" );
158230 Serial.println (pulseCount);
231+ #endif
232+ // Send pulsecount value to gw in VAR1
233+ send (lastCounterMsg.set (pulseCount));
159234
160- send (lastCounterMsg.set (pulseCount)); // Send pulsecount value to gw in VAR1
161-
162- double volume = ((double )pulseCount/((double )PULSE_FACTOR));
235+ double volume = pulseCount / PULSE_FACTOR;
163236 if ((volume != oldvolume)||(!SLEEP_MODE)) {
164237 oldvolume = volume;
165238
239+ #ifdef APP_DEBUG
166240 Serial.print (" volume:" );
167241 Serial.println (volume, 3 );
168-
169- send (volumeMsg.set (volume, 3 )); // Send volume value to gw
242+ #endif
243+ // Send volume value to gw
244+ send (volumeMsg.set (volume, 3 ));
170245 }
171246 }
172247 }
@@ -175,15 +250,74 @@ void loop()
175250 }
176251}
177252
253+ // clearing eeprom with mysensors sketch is not enough since this
254+ // doesn't cover the saved state values. Call this function once
255+ // to force factory reset
256+ void forceFactoryReset () {
257+ saveState (FIRST_VALUE_SENT_FLAG_POSITION, FIRST_VALUE_SENT_FLAG_NO);
258+ }
259+
260+ void checkAndFirstTimeInitValuesOnHomeAssistant () {
261+ // check local e2p if this sensor has already sent
262+ // initial value (0). If not will send the init value
263+ uint8_t state = loadState (FIRST_VALUE_SENT_FLAG_POSITION);
264+ if (state==FIRST_VALUE_SENT_FLAG_NO) {
265+ // never sent anything. No value in HASS is assumed
266+ #ifdef APP_DEBUG
267+ Serial.println (" First time init" );
268+ #endif
269+ firstValuesMessageSent = true ;
270+ // Send flow value to gw
271+ send (flowMsg.set (0 .0d, 2 ));
272+ // Send volume value to gw
273+ send (volumeMsg.set (0 .0d, 3 ));
274+ // Send pulsecount value to gw in VAR1
275+ send (lastCounterMsg.set ((uint32_t )0 ));
276+ // Send volumeAdd value to gw in V_TEXT
277+ send (volumeAdd.set (" " ));
278+ }
279+ }
280+
178281void receive (const MyMessage &message)
179282{
180283 if (message.getType ()==V_VAR1) {
284+ if (firstValuesMessageSent) {
285+ // ack saving value on board that HomeAssistant
286+ // got first init values. Will never be sent again
287+ saveState (FIRST_VALUE_SENT_FLAG_POSITION,
288+ FIRST_VALUE_SENT_FLAG_YES);
289+ }
290+
181291 uint32_t gwPulseCount=message.getULong ();
182292 pulseCount += gwPulseCount;
183- flow=oldflow=0 ;
293+ flow=oldflow=0 .0d;
294+
295+ #ifdef APP_DEBUG
184296 Serial.print (" Received last pulse count from gw:" );
185297 Serial.println (pulseCount);
298+ #endif
299+
186300 pcReceived = true ;
187301 }
188- }
189302
303+ // incoming message to correct pulsecount
304+ // used to add or remove pulses to the current reading
305+ if (message.getType ()==V_TEXT) {
306+ long val = atol (message.getString ());
307+ if ((val+(int32_t )pulseCount)<0 ) {
308+ // TODO: this is not covering all the ranges problems
309+ #ifdef APP_DEBUG
310+ Serial.println (" out of range. Won't add" );
311+ #endif
312+ }
313+ else {
314+ pulseCount+=val;
315+ // reset only if ok
316+ send (volumeAdd.set (" " ));
317+ #ifdef APP_DEBUG
318+ Serial.print (" New pulseCount: " );
319+ Serial.println (pulseCount);
320+ #endif
321+ }
322+ }
323+ }
0 commit comments