Tasmota/tasmota/xsns_96_flowratemeter.ino

269 lines
8.9 KiB
Arduino
Raw Normal View History

2022-04-22 07:40:29 +01:00
/*
2022-04-27 15:49:16 +01:00
xsns_96_flowratemeter.ino - flowratemeter support for Tasmota
- up to two flowratemeter YF-DN50 and similary
- flow rate frequencies f = 1 Hz up to 5 kHz
- uses the FreqRes resolution
2022-04-22 07:40:29 +01:00
Copyright (C) 2022 Norbert Richter
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2022-04-27 15:49:16 +01:00
#ifdef USE_FLOWRATEMETER
2022-04-22 07:40:29 +01:00
#define XSNS_96 96
2022-04-27 16:50:41 +01:00
#define FLOWRATEMETER_WEIGHT_AVG_SAMPLE 20 // number of samples for smooth weigted average
2022-04-27 15:49:16 +01:00
#define FLOWRATEMETER_MIN_FREQ 1 // Hz
2022-04-22 07:40:29 +01:00
2022-04-27 15:49:16 +01:00
#define D_JSON_FLOWRATEMETER_RATE "Rate"
#define D_JSON_FLOWRATEMETER_VALUE "Value"
#define D_JSON_FLOWRATEMETER_UNIT "Unit"
#define D_JSON_FLOWRATEMETER_VALUE_AVG "average"
#define D_JSON_FLOWRATEMETER_VALUE_RAW "raw"
2022-04-22 07:40:29 +01:00
2022-04-27 16:50:41 +01:00
2022-04-22 07:40:29 +01:00
#ifdef USE_WEBSERVER
2022-04-27 15:49:16 +01:00
const char HTTP_SNS_FLOWRATEMETER[] PROGMEM =
"{s}" D_FLOWRATEMETER_NAME "-%d{m}%*_f %s{e}"
2022-04-22 07:40:29 +01:00
;
#endif // USE_WEBSERVER
2022-04-27 15:49:16 +01:00
int32_t flowratemeter_period[MAX_FLOWRATEMETER] = {0};
float flowratemeter_period_avg[MAX_FLOWRATEMETER] = {0};
uint32_t flowratemeter_count[MAX_FLOWRATEMETER] = {0};
volatile uint32_t flowratemeter_last_irq[MAX_FLOWRATEMETER] = {0};
2022-04-22 07:40:29 +01:00
2022-04-27 15:49:16 +01:00
bool flowratemeter_valuesread = false;
bool flowratemeter_raw_value = false;
2022-04-22 07:40:29 +01:00
2022-04-27 16:50:41 +01:00
void IRAM_ATTR FlowRateMeterIR(uint16_t irq)
2022-04-22 07:40:29 +01:00
{
uint32_t time = micros();
2022-04-27 15:49:16 +01:00
#if defined(ESP8266)
uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
2022-04-27 07:32:09 +01:00
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);
2022-04-27 15:49:16 +01:00
#endif
if (irq < MAX_FLOWRATEMETER) {
if ((time - flowratemeter_last_irq[irq]) < (1000000 / FLOWRATEMETER_MIN_FREQ)) {
flowratemeter_period[irq] = time - flowratemeter_last_irq[irq];
2022-04-22 07:40:29 +01:00
} else {
2022-04-27 15:49:16 +01:00
flowratemeter_period[irq] = 0;
2022-04-22 07:40:29 +01:00
}
2022-04-27 15:49:16 +01:00
flowratemeter_valuesread = true;
flowratemeter_last_irq[irq] = time;
2022-04-22 07:40:29 +01:00
}
}
2022-04-27 13:03:05 +01:00
// GPIO_STATUS is always 0 (?), so can only determine the IR source using this way
2022-04-27 16:50:41 +01:00
void IRAM_ATTR FlowRateMeter1IR(void)
2022-04-22 07:40:29 +01:00
{
2022-04-27 16:50:41 +01:00
FlowRateMeterIR(0);
2022-04-22 07:40:29 +01:00
}
2022-04-27 16:50:41 +01:00
void IRAM_ATTR FlowRateMeter2IR(void)
2022-04-22 07:40:29 +01:00
{
2022-04-27 16:50:41 +01:00
FlowRateMeterIR(1);
2022-04-22 07:40:29 +01:00
}
2022-04-27 16:50:41 +01:00
void FlowRateMeterRead(void)
2022-04-22 07:40:29 +01:00
{
2022-04-27 15:49:16 +01:00
for (uint32_t i = 0; i < MAX_FLOWRATEMETER; i++) {
if ((micros() - flowratemeter_last_irq[i]) >= (1000000 / FLOWRATEMETER_MIN_FREQ)) {
flowratemeter_period[i] = 0;
flowratemeter_period_avg[i] = 0;
2022-04-22 07:40:29 +01:00
}
// exponentially weighted average
2022-04-27 15:49:16 +01:00
if (flowratemeter_count[i] <= FLOWRATEMETER_WEIGHT_AVG_SAMPLE) {
flowratemeter_count[i]++;
2022-04-22 07:40:29 +01:00
}
2022-04-27 15:49:16 +01:00
flowratemeter_period_avg[i] -= flowratemeter_period_avg[i] / flowratemeter_count[i];
flowratemeter_period_avg[i] += float(flowratemeter_period[i]) / flowratemeter_count[i];
2022-04-22 07:40:29 +01:00
}
}
2022-04-27 16:50:41 +01:00
void FlowRateMeterInit(void)
2022-04-22 07:40:29 +01:00
{
2022-04-27 16:50:41 +01:00
void (* irq_service[MAX_FLOWRATEMETER])(void)= {FlowRateMeter1IR, FlowRateMeter2IR};
2022-04-22 07:40:29 +01:00
2022-04-27 15:49:16 +01:00
flowratemeter_valuesread = false;
for (uint32_t i = 0; i < MAX_FLOWRATEMETER; i++) {
pinMode(Pin(GPIO_FLOWRATEMETER_IN, i), INPUT);
attachInterrupt(Pin(GPIO_FLOWRATEMETER_IN, i), irq_service[i], RISING);
2022-04-22 07:40:29 +01:00
}
}
2022-04-27 16:50:41 +01:00
void FlowRateMeterShow(bool json)
2022-04-22 07:40:29 +01:00
{
2022-04-27 13:03:05 +01:00
if (json) {
2022-04-27 15:49:16 +01:00
ResponseAppend_P(PSTR(",\"" D_FLOWRATEMETER_NAME "\":{\"" D_JSON_FLOWRATEMETER_RATE "\":["));
2022-04-27 13:03:05 +01:00
}
2022-04-27 15:49:16 +01:00
for (uint32_t i = 0; i < MAX_FLOWRATEMETER; i++) {
float flowratemeter_rate_avg_float = 0;
2022-04-22 07:40:29 +01:00
2022-04-27 15:49:16 +01:00
if (flowratemeter_period[i]) {
flowratemeter_rate_avg_float =
((Settings->SensorBits1.flowratemeter_unit ? (1000000.0 / 1000.0) : (1000000 / 60.0)) / 2.0)
/ (flowratemeter_raw_value ? flowratemeter_period[i] : flowratemeter_period_avg[i])
* (Settings->flowratemeter_calibration[i] ? (float)Settings->flowratemeter_calibration[i] : 1000.0);
2022-04-22 07:40:29 +01:00
}
2022-04-27 15:49:16 +01:00
if (PinUsed(GPIO_FLOWRATEMETER_IN, i)) {
2022-04-22 07:40:29 +01:00
if (json) {
2022-04-27 13:03:05 +01:00
ResponseAppend_P(PSTR("%s%*_f"),
i ? PSTR(",") : PSTR(""),
2022-04-27 15:49:16 +01:00
Settings->flag2.frequency_resolution, &flowratemeter_rate_avg_float
2022-04-22 07:40:29 +01:00
);
2022-04-27 13:03:05 +01:00
2022-04-22 07:40:29 +01:00
#ifdef USE_WEBSERVER
} else {
2022-04-27 15:49:16 +01:00
WSContentSend_PD(HTTP_SNS_FLOWRATEMETER,
2022-04-22 07:40:29 +01:00
i+1,
2022-04-27 15:49:16 +01:00
Settings->flag2.frequency_resolution, &flowratemeter_rate_avg_float,
Settings->SensorBits1.flowratemeter_unit ? D_UNIT_CUBICMETER_PER_HOUR : D_UNIT_LITER_PER_MINUTE
2022-04-22 07:40:29 +01:00
);
#endif // USE_WEBSERVER
}
}
2022-04-26 13:52:26 +01:00
}
if (json) {
2022-04-27 15:49:16 +01:00
ResponseAppend_P(PSTR("],\"" D_JSON_FLOWRATEMETER_VALUE "\":\"%s\""),
flowratemeter_raw_value ? PSTR(D_JSON_FLOWRATEMETER_VALUE_RAW) : PSTR(D_JSON_FLOWRATEMETER_VALUE_AVG)
2022-04-27 13:03:05 +01:00
);
2022-04-27 15:49:16 +01:00
ResponseAppend_P(PSTR(",\"" D_JSON_FLOWRATEMETER_UNIT "\":\"%s\"}"),
2022-04-27 16:25:20 +01:00
Settings->SensorBits1.flowratemeter_unit ? PSTR(D_UNIT_CUBICMETER_PER_HOUR) : PSTR(D_UNIT_LITER_PER_MINUTE)
2022-04-26 13:52:26 +01:00
);
2022-04-22 07:40:29 +01:00
}
}
/*********************************************************************************************\
* Supported commands for Sensor96:
*
* Sensor96 - Show current settings
2022-04-27 07:23:27 +01:00
* Sensor96 0 0|1 - Show flow value in l/min (0) or m³/h (1)
2022-04-22 07:40:29 +01:00
* Sensor96 1 <correction-factor> - Set sensor 1 factor (x 1000) - to set to 0.2 enter 'Sensor96 1 200'
* Sensor96 2 <correction-factor> - Set sensor 2 factor (x 1000)
2022-04-27 13:03:05 +01:00
* Sensor96 9 0|1 - Value mode: Switch between displaying avg(0) / raw(1) readings (not permanently)
2022-04-22 07:40:29 +01:00
*
* Flowmeter calibration:
* - get the current displayed flow rate (D)
* - get the current <correction-factor> (c)
* - measure the real flow rate (M)
* - new <correction-factor> = M / (c * D)
*
* Example:
* - displayed flow rate = 254.39 l/min (D)
* - current <correction-factor> = 1.0 (c)
* - real flow rate = 83.42 l/min (M)
*
* new <correction-factor> = M / (c * D) = 83.42 / (1 * 254.39) = 0.328
* Cmd: Sensor96 x 328
\*********************************************************************************************/
2022-04-27 16:50:41 +01:00
bool FlowRateMeterCommand(void) {
2022-04-22 07:40:29 +01:00
bool show_parms = true;
char argument[XdrvMailbox.data_len];
long value = 0;
for (uint32_t ca = 0; ca < XdrvMailbox.data_len; ca++) {
if ((' ' == XdrvMailbox.data[ca]) || ('=' == XdrvMailbox.data[ca])) { XdrvMailbox.data[ca] = ','; }
}
bool any_value = (strchr(XdrvMailbox.data, ',') != nullptr);
if (any_value) {
value = strtol(ArgV(argument, 2), nullptr, 10);
}
switch (XdrvMailbox.payload) {
2022-04-27 07:23:27 +01:00
case 0: // Unit
2022-04-22 07:40:29 +01:00
if (any_value) {
2022-04-27 15:49:16 +01:00
Settings->SensorBits1.flowratemeter_unit = value & 1;
2022-04-27 07:23:27 +01:00
ResponseCmndNumber(value & 1);
2022-04-22 07:40:29 +01:00
show_parms = false;
}
break;
2022-04-27 07:23:27 +01:00
case 1: // Sensor calibration value
case 2:
2022-04-22 07:40:29 +01:00
if (any_value) {
2022-04-27 15:49:16 +01:00
Settings->flowratemeter_calibration[XdrvMailbox.payload - 1] = value;
2022-04-27 07:23:27 +01:00
ResponseCmndNumber(value);
2022-04-22 07:40:29 +01:00
show_parms = false;
}
break;
2022-04-27 13:03:05 +01:00
case 9: // avg/raw values
if (any_value) {
2022-04-27 15:49:16 +01:00
flowratemeter_raw_value = value & 1;
2022-04-27 13:03:05 +01:00
ResponseCmndNumber(value & 1);
show_parms = false;
}
break;
2022-04-22 07:40:29 +01:00
}
if (show_parms) {
2022-04-27 13:03:05 +01:00
Response_P(PSTR("{\"Sensor%d\":{\"" D_JSON_POWERFACTOR "\":["), XSNS_96);
2022-04-27 15:49:16 +01:00
for (uint32_t i = 0; i < MAX_FLOWRATEMETER; i++) {
float flowratemeter_factor = Settings->flowratemeter_calibration[i] ? (float)Settings->flowratemeter_calibration[i] / 1000 : 1;
ResponseAppend_P(PSTR("%s%3_f"), i ? PSTR(",") : PSTR(""), &flowratemeter_factor);
2022-04-22 07:40:29 +01:00
}
2022-04-27 15:49:16 +01:00
ResponseAppend_P(PSTR("],\"" D_JSON_FLOWRATEMETER_VALUE "\":\"%s\""),
flowratemeter_raw_value ? PSTR(D_JSON_FLOWRATEMETER_VALUE_RAW) : PSTR(D_JSON_FLOWRATEMETER_VALUE_AVG)
2022-04-27 13:03:05 +01:00
);
2022-04-27 15:49:16 +01:00
ResponseAppend_P(PSTR(",\"" D_JSON_FLOWRATEMETER_UNIT "\":\"%s\"}}"),
2022-04-27 16:25:20 +01:00
Settings->SensorBits1.flowratemeter_unit ? PSTR(D_UNIT_CUBICMETER_PER_HOUR) : PSTR(D_UNIT_LITER_PER_MINUTE)
2022-04-22 07:40:29 +01:00
);
}
return true;
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xsns96(uint8_t function)
{
bool result = false;
2022-04-27 15:49:16 +01:00
if (PinUsed(GPIO_FLOWRATEMETER_IN, GPIO_ANY)) {
2022-04-22 07:40:29 +01:00
switch (function) {
case FUNC_INIT:
2022-04-27 16:50:41 +01:00
FlowRateMeterInit();
2022-04-22 07:40:29 +01:00
break;
case FUNC_EVERY_250_MSECOND:
2022-04-27 16:50:41 +01:00
FlowRateMeterRead();
2022-04-22 07:40:29 +01:00
break;
case FUNC_COMMAND_SENSOR:
if (XSNS_96 == XdrvMailbox.index) {
2022-04-27 16:50:41 +01:00
result = FlowRateMeterCommand();
2022-04-22 07:40:29 +01:00
}
break;
case FUNC_JSON_APPEND:
2022-04-27 16:50:41 +01:00
FlowRateMeterShow(true);
2022-04-22 07:40:29 +01:00
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
2022-04-27 16:50:41 +01:00
FlowRateMeterShow(false);
2022-04-22 07:40:29 +01:00
break;
#endif // USE_WEBSERVER
}
}
return result;
}
2022-04-27 15:49:16 +01:00
#endif // USE_FLOWRATEMETER