From b0689af80360b6b13352daf28cc320be421b3792 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 2 Apr 2021 11:43:31 +0200 Subject: [PATCH] Add optional DS18x20 arithmetic mean Add command ``SetOption126 1`` to enable DS18x20 arithmetic mean over teleperiod for JSON temperature based on (#11472) --- CHANGELOG.md | 1 + RELEASENOTES.md | 1 + tasmota/settings.h | 2 +- tasmota/xsns_05_ds18x20.ino | 38 +++++++---- tasmota/xsns_05_ds18x20_esp32.ino | 107 +++++++++++++++++------------- tools/decode-status.py | 3 +- 6 files changed, 92 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b3f67777..9c5c5d335 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Commands ``MqttKeepAlive 1..100`` to set Mqtt Keep Alive timer (default 30) and ``MqttTimeout 1..100`` to set Mqtt Socket Timeout (default 4) (#5341) - Commands ``DisplayType`` to select sub-modules where implemented and ``DisplayInvert`` to select inverted display where implemented - Command ``SerialBuffer 256..520`` to change hardware serial receive buffer size from default (256) to max local buffer size (520) (#11448) +- Command ``SetOption126 1`` to enable DS18x20 arithmetic mean over teleperiod for JSON temperature based on (#11472) - Support for TM1638 seven segment display by Ajith Vasudevan (#11031) - Support for MAX7219 seven segment display by Ajith Vasudevan (#11387) - Support for Frequency monitoring and zero-cross detection on CSE7761 (Sonoff Dual R3) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 866277a14..98644aa73 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -82,6 +82,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota ### Added - Command ``Sensor80 1 <0..7>`` to control MFRC522 RFID antenna gain from 18dB (0) to 48dB (7) [#11073](https://github.com/arendst/Tasmota/issues/11073) - Command ``SerialBuffer 256..520`` to change hardware serial receive buffer size from default (256) to max local buffer size (520) [#11448](https://github.com/arendst/Tasmota/issues/11448) +- Command ``SetOption126 1`` to enable DS18x20 arithmetic mean over teleperiod for JSON temperature based on [#11472](https://github.com/arendst/Tasmota/issues/11472) - Commands ``MqttKeepAlive 1..100`` to set Mqtt Keep Alive timer (default 30) and ``MqttTimeout 1..100`` to set Mqtt Socket Timeout (default 4) [#5341](https://github.com/arendst/Tasmota/issues/5341) - Commands ``DisplayType`` to select sub-modules where implemented and ``DisplayInvert`` to select inverted display where implemented - Support for SML VBUS [#11125](https://github.com/arendst/Tasmota/issues/11125) diff --git a/tasmota/settings.h b/tasmota/settings.h index 804f47835..7d254e6d3 100644 --- a/tasmota/settings.h +++ b/tasmota/settings.h @@ -151,7 +151,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t wiegand_hex_output : 1; // bit 9 (v9.3.1.1) - SetOption123 - (Wiegand) switch tag number output to hex format (1) uint32_t wiegand_keypad_to_tag : 1; // bit 10 (v9.3.1.1) - SetOption124 - (Wiegand) send key pad stroke as single char (0) or one tag (ending char #) (1) uint32_t zigbee_hide_bridge_topic : 1; // bit 11 (v9.3.1.1) - SetOption125 - (Zigbee) Hide bridge topic from zigbee topic (use with SetOption89) (1) - uint32_t spare12 : 1; // bit 12 + uint32_t ds18x20_mean : 1; // bit 12 (v9.3.1.2) - SetOption126 - (DS18x20) Enable arithmetic mean over teleperiod for JSON temperature (1) uint32_t spare13 : 1; // bit 13 uint32_t spare14 : 1; // bit 14 uint32_t spare15 : 1; // bit 15 diff --git a/tasmota/xsns_05_ds18x20.ino b/tasmota/xsns_05_ds18x20.ino index ecb107e43..68c2cbd83 100644 --- a/tasmota/xsns_05_ds18x20.ino +++ b/tasmota/xsns_05_ds18x20.ino @@ -47,11 +47,13 @@ const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850"; uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID }; -struct DS18X20STRUCT { +struct { + float temperature; + float temp_sum; + uint16_t numread; uint8_t address[8]; uint8_t index; uint8_t valid; - float temperature; } ds18x20_sensor[DS18X20_MAX_SENSORS]; struct { @@ -351,6 +353,7 @@ void Ds18x20Convert(void) { } bool Ds18x20Read(uint8_t sensor) { + float temperature; uint8_t data[9]; int8_t sign = 1; @@ -367,10 +370,8 @@ bool Ds18x20Read(uint8_t sensor) { switch(ds18x20_sensor[index].address[0]) { case DS18S20_CHIPID: { int16_t tempS = (((data[1] << 8) | (data[0] & 0xFE)) << 3) | ((0x10 - data[6]) & 0x0F); - ds18x20_sensor[index].temperature = ConvertTemp(tempS * 0.0625 - 0.250); - - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; + temperature = ConvertTemp(tempS * 0.0625 - 0.250); + break; } case DS1822_CHIPID: case DS18B20_CHIPID: { @@ -393,17 +394,24 @@ bool Ds18x20Read(uint8_t sensor) { temp12 = (~temp12) +1; sign = -1; } - ds18x20_sensor[index].temperature = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16 - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; + temperature = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16 + break; } case MAX31850_CHIPID: { int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); - ds18x20_sensor[index].temperature = ConvertTemp(temp14 * 0.0625); // Divide by 16 - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; + temperature = ConvertTemp(temp14 * 0.0625); // Divide by 16 + break; } } + ds18x20_sensor[index].temperature = temperature; + if (Settings.flag5.ds18x20_mean) { + if (ds18x20_sensor[index].numread++ == 0) { + ds18x20_sensor[index].temp_sum = 0; + } + ds18x20_sensor[index].temp_sum += temperature; + } + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; } } AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); @@ -475,6 +483,12 @@ void Ds18x20Show(bool json) { Ds18x20Name(i); if (json) { + if (Settings.flag5.ds18x20_mean) { + if ((0 == TasmotaGlobal.tele_period) && ds18x20_sensor[index].numread) { + ds18x20_sensor[index].temperature = ds18x20_sensor[index].temp_sum / ds18x20_sensor[index].numread; + ds18x20_sensor[index].numread = 0; + } + } char address[17]; for (uint32_t j = 0; j < 6; j++) { sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); // Skip sensor type and crc diff --git a/tasmota/xsns_05_ds18x20_esp32.ino b/tasmota/xsns_05_ds18x20_esp32.ino index 4f591b36a..2f929dab7 100644 --- a/tasmota/xsns_05_ds18x20_esp32.ino +++ b/tasmota/xsns_05_ds18x20_esp32.ino @@ -42,11 +42,18 @@ const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850"; uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID }; -uint8_t ds18x20_address[DS18X20_MAX_SENSORS][8]; -uint8_t ds18x20_index[DS18X20_MAX_SENSORS]; -uint8_t ds18x20_valid[DS18X20_MAX_SENSORS]; -uint8_t ds18x20_sensors = 0; -char ds18x20_types[17]; +struct { + float temp_sum; + uint16_t numread; + uint8_t address[8]; + uint8_t index; + uint8_t valid; +} ds18x20_sensor[DS18X20_MAX_SENSORS]; + +struct { + char name[17]; + uint8_t sensors = 0; +} DS18X20Data; /********************************************************************************************/ @@ -58,7 +65,7 @@ void Ds18x20Init(void) { ds = new OneWire(Pin(GPIO_DSB)); Ds18x20Search(); - AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), DS18X20Data.sensors); } void Ds18x20Search(void) { @@ -67,30 +74,30 @@ void Ds18x20Search(void) { ds->reset_search(); for (num_sensors = 0; num_sensors < DS18X20_MAX_SENSORS; num_sensors) { - if (!ds->search(ds18x20_address[num_sensors])) { + if (!ds->search(ds18x20_sensor[num_sensors].address)) { ds->reset_search(); break; } // If CRC Ok and Type DS18S20, DS1822, DS18B20 or MAX31850 - if ((OneWire::crc8(ds18x20_address[num_sensors], 7) == ds18x20_address[num_sensors][7]) && - ((ds18x20_address[num_sensors][0]==DS18S20_CHIPID) || - (ds18x20_address[num_sensors][0]==DS1822_CHIPID) || - (ds18x20_address[num_sensors][0]==DS18B20_CHIPID) || - (ds18x20_address[num_sensors][0]==MAX31850_CHIPID))) { + if ((OneWire::crc8(ds18x20_sensor[num_sensors].address, 7) == ds18x20_sensor[num_sensors].address[7]) && + ((ds18x20_sensor[num_sensors].address[0] == DS18S20_CHIPID) || + (ds18x20_sensor[num_sensors].address[0] == DS1822_CHIPID) || + (ds18x20_sensor[num_sensors].address[0] == DS18B20_CHIPID) || + (ds18x20_sensor[num_sensors].address[0] == MAX31850_CHIPID))) { num_sensors++; } } for (uint32_t i = 0; i < num_sensors; i++) { - ds18x20_index[i] = i; + ds18x20_sensor[i].index = i; } for (uint32_t i = 0; i < num_sensors; i++) { for (uint32_t j = i + 1; j < num_sensors; j++) { - if (uint32_t(ds18x20_address[ds18x20_index[i]]) > uint32_t(ds18x20_address[ds18x20_index[j]])) { - std::swap(ds18x20_index[i], ds18x20_index[j]); + if (uint32_t(ds18x20_sensor[ds18x20_sensor[i].index].address) > uint32_t(ds18x20_sensor[ds18x20_sensor[j].index].address)) { + std::swap(ds18x20_sensor[i].index, ds18x20_sensor[j].index); } } } - ds18x20_sensors = num_sensors; + DS18X20Data.sensors = num_sensors; } void Ds18x20Convert(void) { @@ -100,29 +107,28 @@ void Ds18x20Convert(void) { // delay(750); // 750ms should be enough for 12bit conv } -bool Ds18x20Read(uint8_t sensor, float &t) -{ +bool Ds18x20Read(uint8_t sensor, float &t) { uint8_t data[12]; int8_t sign = 1; t = NAN; - uint8_t index = ds18x20_index[sensor]; - if (ds18x20_valid[index]) { ds18x20_valid[index]--; } + uint8_t index = ds18x20_sensor[sensor].index; + if (ds18x20_sensor[index].valid) { ds18x20_sensor[index].valid--; } ds->reset(); - ds->select(ds18x20_address[index]); + ds->select(ds18x20_sensor[index].address); ds->write(W1_READ_SCRATCHPAD); // Read Scratchpad for (uint32_t i = 0; i < 9; i++) { data[i] = ds->read(); } if (OneWire::crc8(data, 8) == data[8]) { - switch(ds18x20_address[index][0]) { + switch(ds18x20_sensor[index].address[0]) { case DS18S20_CHIPID: { int16_t tempS = (((data[1] << 8) | (data[0] & 0xFE)) << 3) | ((0x10 - data[6]) & 0x0F); t = ConvertTemp(tempS * 0.0625 - 0.250); - ds18x20_valid[index] = SENSOR_MAX_MISS; + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; return true; } case DS1822_CHIPID: @@ -133,13 +139,13 @@ bool Ds18x20Read(uint8_t sensor, float &t) sign = -1; } t = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16 - ds18x20_valid[index] = SENSOR_MAX_MISS; + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; return true; } case MAX31850_CHIPID: { int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); t = ConvertTemp(temp14 * 0.0625); // Divide by 16 - ds18x20_valid[index] = SENSOR_MAX_MISS; + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; return true; } } @@ -148,34 +154,32 @@ bool Ds18x20Read(uint8_t sensor, float &t) return false; } -void Ds18x20Name(uint8_t sensor) -{ +void Ds18x20Name(uint8_t sensor) { uint8_t index = sizeof(ds18x20_chipids); while (index) { - if (ds18x20_address[ds18x20_index[sensor]][0] == ds18x20_chipids[index]) { + if (ds18x20_sensor[ds18x20_sensor[sensor].index].address[0] == ds18x20_chipids[index]) { break; } index--; } - GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types); - if (ds18x20_sensors > 1) { + GetTextIndexed(DS18X20Data.name, sizeof(DS18X20Data.name), index, kDs18x20Types); + if (DS18X20Data.sensors > 1) { #ifdef DS18x20_USE_ID_AS_NAME char address[17]; for (uint32_t j = 0; j < 3; j++) { sprintf(address+2*j, "%02X", ds18x20_sensor[ds18x20_sensor[sensor].index].address[3-j]); // Only last 3 bytes } - snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s%c%s"), ds18x20_types, IndexSeparator(), address); + snprintf_P(DS18X20Data.name, sizeof(DS18X20Data.name), PSTR("%s%c%s"), DS18X20Data.name, IndexSeparator(), address); #else - snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s%c%d"), ds18x20_types, IndexSeparator(), sensor +1); + snprintf_P(DS18X20Data.name, sizeof(DS18X20Data.name), PSTR("%s%c%d"), DS18X20Data.name, IndexSeparator(), sensor +1); #endif } } /********************************************************************************************/ -void Ds18x20EverySecond(void) -{ - if (!ds18x20_sensors) { return; } +void Ds18x20EverySecond(void) { + if (!DS18X20Data.sensors) { return; } if (TasmotaGlobal.uptime & 1) { // 2mS @@ -183,32 +187,44 @@ void Ds18x20EverySecond(void) Ds18x20Convert(); // Start Conversion, takes up to one second } else { float t; - for (uint32_t i = 0; i < ds18x20_sensors; i++) { + for (uint32_t i = 0; i < DS18X20Data.sensors; i++) { // 12mS per device - if (!Ds18x20Read(i, t)) { // Read temperature + if (Ds18x20Read(i, t)) { // Read temperature + if (Settings.flag5.ds18x20_mean) { + if (ds18x20_sensor[i].numread++ == 0) { + ds18x20_sensor[i].temp_sum = 0; + } + ds18x20_sensor[i].temp_sum += t; + } + } else { Ds18x20Name(i); - AddLogMissed(ds18x20_types, ds18x20_valid[ds18x20_index[i]]); + AddLogMissed(DS18X20Data.name, ds18x20_sensor[ds18x20_sensor[i].index].valid); } } } } -void Ds18x20Show(bool json) -{ +void Ds18x20Show(bool json) { float t; uint8_t dsxflg = 0; - for (uint32_t i = 0; i < ds18x20_sensors; i++) { + for (uint32_t i = 0; i < DS18X20Data.sensors; i++) { if (Ds18x20Read(i, t)) { // Check if read failed Ds18x20Name(i); if (json) { + if (Settings.flag5.ds18x20_mean) { + if ((0 == TasmotaGlobal.tele_period) && ds18x20_sensor[i].numread) { + t = ds18x20_sensor[i].temp_sum / ds18x20_sensor[i].numread; + ds18x20_sensor[i].numread = 0; + } + } char address[17]; for (uint32_t j = 0; j < 6; j++) { - sprintf(address+2*j, "%02X", ds18x20_address[ds18x20_index[i]][6-j]); // Skip sensor type and crc + sprintf(address+2*j, "%02X", ds18x20_sensor[ds18x20_sensor[i].index].address[6-j]); // Skip sensor type and crc } ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%*_f}"), - ds18x20_types, address, Settings.flag2.temperature_resolution, &t); + DS18X20Data.name, address, Settings.flag2.temperature_resolution, &t); dsxflg++; #ifdef USE_DOMOTICZ if ((0 == TasmotaGlobal.tele_period) && (1 == dsxflg)) { @@ -222,7 +238,7 @@ void Ds18x20Show(bool json) #endif // USE_KNX #ifdef USE_WEBSERVER } else { - WSContentSend_Temp(ds18x20_types, t); + WSContentSend_Temp(DS18X20Data.name, t); #endif // USE_WEBSERVER } } @@ -233,8 +249,7 @@ void Ds18x20Show(bool json) * Interface \*********************************************************************************************/ -bool Xsns05(uint8_t function) -{ +bool Xsns05(uint8_t function) { bool result = false; if (PinUsed(GPIO_DSB)) { diff --git a/tools/decode-status.py b/tools/decode-status.py index 205419036..8cd2fbc8e 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -180,7 +180,8 @@ a_setoption = [[ "(Wiegand) switch tag number output to hex format (1)", "(Wiegand) send key pad stroke as single char (0) or one tag (ending char #) (1)", "(Zigbee) Hide bridge topic from zigbee topic (use with SetOption89) (1)", - "","","","", + "(DS18x20) Enable arithmetic mean over teleperiod for JSON temperature (1)", + "","","", "","","","", "","","","", "","","","",