Add optional DS18x20 arithmetic mean

Add command ``SetOption126 1`` to enable DS18x20 arithmetic mean over teleperiod for JSON temperature based on (#11472)
This commit is contained in:
Theo Arends 2021-04-02 11:43:31 +02:00
parent c93185172a
commit b0689af803
6 changed files with 92 additions and 60 deletions

View File

@ -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 ``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 - 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 ``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 TM1638 seven segment display by Ajith Vasudevan (#11031)
- Support for MAX7219 seven segment display by Ajith Vasudevan (#11387) - Support for MAX7219 seven segment display by Ajith Vasudevan (#11387)
- Support for Frequency monitoring and zero-cross detection on CSE7761 (Sonoff Dual R3) - Support for Frequency monitoring and zero-cross detection on CSE7761 (Sonoff Dual R3)

View File

@ -82,6 +82,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
### Added ### 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 ``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 ``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 ``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 - 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) - Support for SML VBUS [#11125](https://github.com/arendst/Tasmota/issues/11125)

View File

@ -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_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 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 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 spare13 : 1; // bit 13
uint32_t spare14 : 1; // bit 14 uint32_t spare14 : 1; // bit 14
uint32_t spare15 : 1; // bit 15 uint32_t spare15 : 1; // bit 15

View File

@ -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 }; 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 address[8];
uint8_t index; uint8_t index;
uint8_t valid; uint8_t valid;
float temperature;
} ds18x20_sensor[DS18X20_MAX_SENSORS]; } ds18x20_sensor[DS18X20_MAX_SENSORS];
struct { struct {
@ -351,6 +353,7 @@ void Ds18x20Convert(void) {
} }
bool Ds18x20Read(uint8_t sensor) { bool Ds18x20Read(uint8_t sensor) {
float temperature;
uint8_t data[9]; uint8_t data[9];
int8_t sign = 1; int8_t sign = 1;
@ -367,10 +370,8 @@ bool Ds18x20Read(uint8_t sensor) {
switch(ds18x20_sensor[index].address[0]) { switch(ds18x20_sensor[index].address[0]) {
case DS18S20_CHIPID: { case DS18S20_CHIPID: {
int16_t tempS = (((data[1] << 8) | (data[0] & 0xFE)) << 3) | ((0x10 - data[6]) & 0x0F); int16_t tempS = (((data[1] << 8) | (data[0] & 0xFE)) << 3) | ((0x10 - data[6]) & 0x0F);
ds18x20_sensor[index].temperature = ConvertTemp(tempS * 0.0625 - 0.250); temperature = ConvertTemp(tempS * 0.0625 - 0.250);
break;
ds18x20_sensor[index].valid = SENSOR_MAX_MISS;
return true;
} }
case DS1822_CHIPID: case DS1822_CHIPID:
case DS18B20_CHIPID: { case DS18B20_CHIPID: {
@ -393,19 +394,26 @@ bool Ds18x20Read(uint8_t sensor) {
temp12 = (~temp12) +1; temp12 = (~temp12) +1;
sign = -1; sign = -1;
} }
ds18x20_sensor[index].temperature = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16 temperature = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16
ds18x20_sensor[index].valid = SENSOR_MAX_MISS; break;
return true;
} }
case MAX31850_CHIPID: { case MAX31850_CHIPID: {
int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC);
ds18x20_sensor[index].temperature = ConvertTemp(temp14 * 0.0625); // Divide by 16 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; ds18x20_sensor[index].valid = SENSOR_MAX_MISS;
return true; return true;
} }
} }
}
}
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR));
return false; return false;
} }
@ -475,6 +483,12 @@ void Ds18x20Show(bool json) {
Ds18x20Name(i); Ds18x20Name(i);
if (json) { 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]; char address[17];
for (uint32_t j = 0; j < 6; j++) { for (uint32_t j = 0; j < 6; j++) {
sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); // Skip sensor type and crc sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); // Skip sensor type and crc

View File

@ -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_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID };
uint8_t ds18x20_address[DS18X20_MAX_SENSORS][8]; struct {
uint8_t ds18x20_index[DS18X20_MAX_SENSORS]; float temp_sum;
uint8_t ds18x20_valid[DS18X20_MAX_SENSORS]; uint16_t numread;
uint8_t ds18x20_sensors = 0; uint8_t address[8];
char ds18x20_types[17]; 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)); ds = new OneWire(Pin(GPIO_DSB));
Ds18x20Search(); 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) { void Ds18x20Search(void) {
@ -67,30 +74,30 @@ void Ds18x20Search(void) {
ds->reset_search(); ds->reset_search();
for (num_sensors = 0; num_sensors < DS18X20_MAX_SENSORS; num_sensors) { 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(); ds->reset_search();
break; break;
} }
// If CRC Ok and Type DS18S20, DS1822, DS18B20 or MAX31850 // If CRC Ok and Type DS18S20, DS1822, DS18B20 or MAX31850
if ((OneWire::crc8(ds18x20_address[num_sensors], 7) == ds18x20_address[num_sensors][7]) && if ((OneWire::crc8(ds18x20_sensor[num_sensors].address, 7) == ds18x20_sensor[num_sensors].address[7]) &&
((ds18x20_address[num_sensors][0]==DS18S20_CHIPID) || ((ds18x20_sensor[num_sensors].address[0] == DS18S20_CHIPID) ||
(ds18x20_address[num_sensors][0]==DS1822_CHIPID) || (ds18x20_sensor[num_sensors].address[0] == DS1822_CHIPID) ||
(ds18x20_address[num_sensors][0]==DS18B20_CHIPID) || (ds18x20_sensor[num_sensors].address[0] == DS18B20_CHIPID) ||
(ds18x20_address[num_sensors][0]==MAX31850_CHIPID))) { (ds18x20_sensor[num_sensors].address[0] == MAX31850_CHIPID))) {
num_sensors++; num_sensors++;
} }
} }
for (uint32_t i = 0; i < num_sensors; i++) { 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 i = 0; i < num_sensors; i++) {
for (uint32_t j = i + 1; j < num_sensors; j++) { 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]])) { if (uint32_t(ds18x20_sensor[ds18x20_sensor[i].index].address) > uint32_t(ds18x20_sensor[ds18x20_sensor[j].index].address)) {
std::swap(ds18x20_index[i], ds18x20_index[j]); std::swap(ds18x20_sensor[i].index, ds18x20_sensor[j].index);
} }
} }
} }
ds18x20_sensors = num_sensors; DS18X20Data.sensors = num_sensors;
} }
void Ds18x20Convert(void) { void Ds18x20Convert(void) {
@ -100,29 +107,28 @@ void Ds18x20Convert(void) {
// delay(750); // 750ms should be enough for 12bit conv // 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]; uint8_t data[12];
int8_t sign = 1; int8_t sign = 1;
t = NAN; t = NAN;
uint8_t index = ds18x20_index[sensor]; uint8_t index = ds18x20_sensor[sensor].index;
if (ds18x20_valid[index]) { ds18x20_valid[index]--; } if (ds18x20_sensor[index].valid) { ds18x20_sensor[index].valid--; }
ds->reset(); ds->reset();
ds->select(ds18x20_address[index]); ds->select(ds18x20_sensor[index].address);
ds->write(W1_READ_SCRATCHPAD); // Read Scratchpad ds->write(W1_READ_SCRATCHPAD); // Read Scratchpad
for (uint32_t i = 0; i < 9; i++) { for (uint32_t i = 0; i < 9; i++) {
data[i] = ds->read(); data[i] = ds->read();
} }
if (OneWire::crc8(data, 8) == data[8]) { if (OneWire::crc8(data, 8) == data[8]) {
switch(ds18x20_address[index][0]) { switch(ds18x20_sensor[index].address[0]) {
case DS18S20_CHIPID: { case DS18S20_CHIPID: {
int16_t tempS = (((data[1] << 8) | (data[0] & 0xFE)) << 3) | ((0x10 - data[6]) & 0x0F); int16_t tempS = (((data[1] << 8) | (data[0] & 0xFE)) << 3) | ((0x10 - data[6]) & 0x0F);
t = ConvertTemp(tempS * 0.0625 - 0.250); t = ConvertTemp(tempS * 0.0625 - 0.250);
ds18x20_valid[index] = SENSOR_MAX_MISS; ds18x20_sensor[index].valid = SENSOR_MAX_MISS;
return true; return true;
} }
case DS1822_CHIPID: case DS1822_CHIPID:
@ -133,13 +139,13 @@ bool Ds18x20Read(uint8_t sensor, float &t)
sign = -1; sign = -1;
} }
t = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16 t = ConvertTemp(sign * temp12 * 0.0625); // Divide by 16
ds18x20_valid[index] = SENSOR_MAX_MISS; ds18x20_sensor[index].valid = SENSOR_MAX_MISS;
return true; return true;
} }
case MAX31850_CHIPID: { case MAX31850_CHIPID: {
int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC);
t = ConvertTemp(temp14 * 0.0625); // Divide by 16 t = ConvertTemp(temp14 * 0.0625); // Divide by 16
ds18x20_valid[index] = SENSOR_MAX_MISS; ds18x20_sensor[index].valid = SENSOR_MAX_MISS;
return true; return true;
} }
} }
@ -148,34 +154,32 @@ bool Ds18x20Read(uint8_t sensor, float &t)
return false; return false;
} }
void Ds18x20Name(uint8_t sensor) void Ds18x20Name(uint8_t sensor) {
{
uint8_t index = sizeof(ds18x20_chipids); uint8_t index = sizeof(ds18x20_chipids);
while (index) { 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; break;
} }
index--; index--;
} }
GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types); GetTextIndexed(DS18X20Data.name, sizeof(DS18X20Data.name), index, kDs18x20Types);
if (ds18x20_sensors > 1) { if (DS18X20Data.sensors > 1) {
#ifdef DS18x20_USE_ID_AS_NAME #ifdef DS18x20_USE_ID_AS_NAME
char address[17]; char address[17];
for (uint32_t j = 0; j < 3; j++) { 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 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 #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 #endif
} }
} }
/********************************************************************************************/ /********************************************************************************************/
void Ds18x20EverySecond(void) void Ds18x20EverySecond(void) {
{ if (!DS18X20Data.sensors) { return; }
if (!ds18x20_sensors) { return; }
if (TasmotaGlobal.uptime & 1) { if (TasmotaGlobal.uptime & 1) {
// 2mS // 2mS
@ -183,32 +187,44 @@ void Ds18x20EverySecond(void)
Ds18x20Convert(); // Start Conversion, takes up to one second Ds18x20Convert(); // Start Conversion, takes up to one second
} else { } else {
float t; float t;
for (uint32_t i = 0; i < ds18x20_sensors; i++) { for (uint32_t i = 0; i < DS18X20Data.sensors; i++) {
// 12mS per device // 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); 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; float t;
uint8_t dsxflg = 0; 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 if (Ds18x20Read(i, t)) { // Check if read failed
Ds18x20Name(i); Ds18x20Name(i);
if (json) { 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]; char address[17];
for (uint32_t j = 0; j < 6; j++) { 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}"), 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++; dsxflg++;
#ifdef USE_DOMOTICZ #ifdef USE_DOMOTICZ
if ((0 == TasmotaGlobal.tele_period) && (1 == dsxflg)) { if ((0 == TasmotaGlobal.tele_period) && (1 == dsxflg)) {
@ -222,7 +238,7 @@ void Ds18x20Show(bool json)
#endif // USE_KNX #endif // USE_KNX
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
} else { } else {
WSContentSend_Temp(ds18x20_types, t); WSContentSend_Temp(DS18X20Data.name, t);
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
} }
} }
@ -233,8 +249,7 @@ void Ds18x20Show(bool json)
* Interface * Interface
\*********************************************************************************************/ \*********************************************************************************************/
bool Xsns05(uint8_t function) bool Xsns05(uint8_t function) {
{
bool result = false; bool result = false;
if (PinUsed(GPIO_DSB)) { if (PinUsed(GPIO_DSB)) {

View File

@ -180,7 +180,8 @@ a_setoption = [[
"(Wiegand) switch tag number output to hex format (1)", "(Wiegand) switch tag number output to hex format (1)",
"(Wiegand) send key pad stroke as single char (0) or one tag (ending char #) (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)", "(Zigbee) Hide bridge topic from zigbee topic (use with SetOption89) (1)",
"","","","", "(DS18x20) Enable arithmetic mean over teleperiod for JSON temperature (1)",
"","","",
"","","","", "","","","",
"","","","", "","","","",
"","","","", "","","","",