I have a pump station with a water meter.
It runs on an Arduino UNO; receives its start via MQTT; and reports water flow rate and current volume via MQTT.
This set-up has been working for two year.
A few days ago, I noticed that the volume counter is reset during the pump operation at random intervals... today 16 times, resulting incorrect water volumes being reported.
For simplicity sake I have shortened the code to say:
in setup() -> attach the ISR called pulse_count_isr()
in loop() I call water_flow_measure()
Here the functions:
/*
* ---------------------------------------------|----------------------------------
* @brief Add flowmeter pulse via interrupt routine
* ---------------------------------------------|----------------------------------
*/
void pulse_count_isr ()
{
g_meter_pulse_counter++;
} /* void pulse_count_isr () */
/*
* ---------------------------------------------|----------------------------------
* @brief Measures water flow and volume
* ---------------------------------------------|----------------------------------
*/
void water_flow_measure ()
{
// last time we took a count
static uint32_t time_flow_read_previously = 1;
static uint16_t flow_litres_per_minute = 0;
if ((millis () - time_flow_read_previously) > 1000)
{
// process counters once per second
// disable interrupt while calculating flow rate
// keep the interrupt detach -> attach period as short as possible
detachInterrupt (digitalPinToInterrupt(PIN_FLOW_SENSOR));
g_water_flow_rate = ((1000.0 / (millis () - time_flow_read_previously))
* g_meter_pulse_counter) / FLOW_FREQUENCY;
time_flow_read_previously = millis ();
flow_litres_per_minute = (g_water_flow_rate / 60) * 1000;
g_litres_total += flow_litres_per_minute;
if (0 != int (g_water_flow_rate))
{
// only if g_water_flow_rate is not zero
water_meter_read (); // ... care about meter and flow updates
}
g_meter_pulse_counter = 0; // reset to start incrementing again
// attach the interrupt again
attachInterrupt (digitalPinToInterrupt(PIN_FLOW_SENSOR), pulse_count_isr, RISING);
}
} /* void water_flow_measure () */
/*
* ---------------------------------------------|----------------------------------
* @brief Outputs flow and volume every 5 seconds
* function is only called when flow rate is not zero
* ---------------------------------------------|----------------------------------
*/
void water_meter_read ()
{
uint32_t time_now = millis (); // memorise the time
static const uint32_t PUB_METER_INTERVAL = 5000; // 5 s
static uint32_t time_meter_last_published = 0; // time stamp when flow was last published
static uint16_t meter_read_last_value = 0; // last meter as int
// publish flow at defined interval
if (time_now - time_meter_last_published > PUB_METER_INTERVAL)
{
time_meter_last_published = time_now; // update time_meter_last_published here
g_meter_current = g_litres_total / 1000;// get litres
if (g_meter_current != meter_read_last_value)
{
// only publish meter if it has changed
static char charBuf[7]; // char for meter
//dtostrf(floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, charBuffer);
dtostrf (g_meter_current, 6, 0, charBuf); // convert meter to char
// space leading value due to formatting hence the deblank()
message_publish (MQTT_PUB_WATER_METER, deblank (charBuf));
charBuf[0] = '\0'; // reset the char buffer
dtostrf (int(g_water_flow_rate), 6, 0, charBuf); // convert float to char
message_publish (MQTT_PUB_WATER_FLOW, deblank (charBuf));
charBuf[0] = '\0'; // reset the char buffer
meter_read_last_value = g_meter_current;
}
}
} /* void water_meter_read () */
In essence: I detach the interrupt to do house-keeping and sending data yadda yadda;
And yes, I loose a few litres with this approach (which is admittedly sub-optimal), but is has worked to my satisfaction.
Now, here the interesting bit...
According to the code, it should report every 5 seconds; however, the log shows the weirdest of update intervals, ranging from 11 ms to 33 s.
While the code has worked for two years, and it is strange it shows potential weaknesses now, something seems wrong with my approach --- I just don't see it.
Any hints appreciated. Thanks.