From 9f1ff8af660728c3b6d658b3db04913c06a60b56 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 7 Jun 2021 10:17:09 +0200 Subject: [PATCH 1/5] C3 build changes --- platformio_tasmota_cenv_sample.ini | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/platformio_tasmota_cenv_sample.ini b/platformio_tasmota_cenv_sample.ini index 39af35128..f6115d257 100644 --- a/platformio_tasmota_cenv_sample.ini +++ b/platformio_tasmota_cenv_sample.ini @@ -60,12 +60,16 @@ platform_packages = framework-arduinoespressif32 @ https://github.com/ tasmota/toolchain-riscv32 platformio/tool-mklittlefs @ ~1.203.200522 build_unflags = ${esp32_defaults.build_unflags} -mtarget-align -build_flags = ${esp32_defaults.build_flags} -DFIRMWARE_LITE +build_flags = ${esp32_defaults.build_flags} + ;-DFIRMWARE_LITE -I$PROJECT_DIR/include -include "fix_esp32c3.h" ;-DESP32_STAGE=true -lib_extra_dirs = lib/libesp32 +;lib_extra_dirs = lib/libesp32 lib_ignore = + NeoPixelBus + rc-switch + OneWire NimBLE-Arduino Micro-RTSP From dbf433f151355a5845356b94c312207d91b8cf09 Mon Sep 17 00:00:00 2001 From: Yury Sannikov Date: Mon, 7 Jun 2021 11:31:49 +0300 Subject: [PATCH 2/5] Force set boiler setpoint tmp along with CH flag set --- tasmota/xsns_69_opentherm.ino | 6 ++++++ tasmota/xsns_69_opentherm_protocol.ino | 15 +++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/tasmota/xsns_69_opentherm.ino b/tasmota/xsns_69_opentherm.ino index 705a76395..d4f9624c0 100644 --- a/tasmota/xsns_69_opentherm.ino +++ b/tasmota/xsns_69_opentherm.ino @@ -108,6 +108,9 @@ typedef struct OT_BOILER_STATUS_T // Boiler desired values float m_boilerSetpoint; float m_hotWaterSetpoint; + // This flag is set when Master require a CH to be on + // and forces the OpenThermMessageID::TSet to be sent to the boiler + bool m_forceSetpointSet; } OT_BOILER_STATUS; @@ -595,6 +598,9 @@ bool Xsns69(uint8_t function) case FUNC_JSON_APPEND: sns_opentherm_stat(1); break; + case FUNC_SAVE_AT_MIDNIGHT: + sns_opentherm_protocol_reset(); + break; #ifdef USE_WEBSERVER case FUNC_WEB_SENSOR: sns_opentherm_stat(0); diff --git a/tasmota/xsns_69_opentherm_protocol.ino b/tasmota/xsns_69_opentherm_protocol.ino index 780cbba58..7c6a3a521 100644 --- a/tasmota/xsns_69_opentherm_protocol.ino +++ b/tasmota/xsns_69_opentherm_protocol.ino @@ -258,7 +258,11 @@ unsigned long sns_opentherm_set_slave_flags(struct OpenThermCommandT *self, stru AddLog(LOG_LEVEL_INFO, PSTR("[OTH]: Central Heating transitioning from %s to %s"), self->m_results[1].m_bool ? "on" : "off", - status->m_enableCentralHeating ? "on" : "off"); + centralHeatingIsOn ? "on" : "off"); + + if (centralHeatingIsOn) { + status->m_forceSetpointSet = true; + } } self->m_results[1].m_bool = centralHeatingIsOn; @@ -302,14 +306,17 @@ unsigned long sns_opentherm_set_boiler_temperature(struct OpenThermCommandT *sel // wearing out Boiler flash memory. float diff = abs(status->m_boilerSetpoint - self->m_results[0].m_float); // Ignore small changes in the boiler setpoint temperature - if (diff < OPENTHERM_BOILER_SETPOINT_TOLERANCE) + if (diff < OPENTHERM_BOILER_SETPOINT_TOLERANCE && !status->m_forceSetpointSet) { return -1; } AddLog(LOG_LEVEL_INFO, - PSTR("[OTH]: Setting Boiler Temp. Old: %d, New: %d"), + PSTR("[OTH]: Setting Boiler Temp. Old: %d, New: %d, Force: %s"), (int)self->m_results[0].m_float, - (int)status->m_boilerSetpoint); + (int)status->m_boilerSetpoint, + status->m_forceSetpointSet ? "Y" : "N"); + + status->m_forceSetpointSet = false; self->m_results[0].m_float = status->m_boilerSetpoint; unsigned int data = OpenTherm::temperatureToData(status->m_boilerSetpoint); From 311e7ad661fed130d8b6d5a4b042484facbd9dac Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 7 Jun 2021 10:38:20 +0200 Subject: [PATCH 3/5] Only version lite with c3 --- platformio_tasmota_cenv_sample.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio_tasmota_cenv_sample.ini b/platformio_tasmota_cenv_sample.ini index f6115d257..c92168356 100644 --- a/platformio_tasmota_cenv_sample.ini +++ b/platformio_tasmota_cenv_sample.ini @@ -61,11 +61,11 @@ platform_packages = framework-arduinoespressif32 @ https://github.com/ platformio/tool-mklittlefs @ ~1.203.200522 build_unflags = ${esp32_defaults.build_unflags} -mtarget-align build_flags = ${esp32_defaults.build_flags} - ;-DFIRMWARE_LITE + -DFIRMWARE_LITE -I$PROJECT_DIR/include -include "fix_esp32c3.h" ;-DESP32_STAGE=true -;lib_extra_dirs = lib/libesp32 +lib_extra_dirs = lib/libesp32 lib_ignore = NeoPixelBus rc-switch From 3a5f8869b93150e83f54fdf8ed5d700212a2b275 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 7 Jun 2021 19:07:44 +0200 Subject: [PATCH 4/5] Berry updated `partition` module --- tasmota/berry/modules/partition.be | 264 ++++++++++++++++++++++++++++- 1 file changed, 260 insertions(+), 4 deletions(-) diff --git a/tasmota/berry/modules/partition.be b/tasmota/berry/modules/partition.be index f5df5e549..b5d90aa4f 100644 --- a/tasmota/berry/modules/partition.be +++ b/tasmota/berry/modules/partition.be @@ -7,6 +7,8 @@ partition = module('partition') import flash import string +#- remove trailing NULL chars from a buffer before converting to string -# +#- Berry strings can contain NULL, but this messes up C-Berry interface -# def remove_trailing_zeroes(b) while size(b) > 0 if b.get(size(b)-1,1) == 0 @@ -18,8 +20,56 @@ def remove_trailing_zeroes(b) return b end + +#------------------------------------------------------------- + - Simple CRC32 imple + - + - adapted from Python https://rosettacode.org/wiki/CRC-32#Python + -------------------------------------------------------------# +def crc32_create_table() + var a = [] + for i:0..255 + var k = i + for j:0..7 + if k & 1 + k = (k >> 1) & 0x7FFFFFFF + k ^= 0xedb88320 + else + k = (k >> 1) & 0x7FFFFFFF + end + end + a.push(k) + end + return a +end + +crc32_table = crc32_create_table() + +def crc32_update(buf, crc) + crc ^= 0xffffffff + for k:0..size(buf)-1 + crc = (crc >> 8 & 0x00FFFFFF) ^ crc32_table[(crc & 0xff) ^ buf[k]] + end + return crc ^ 0xffffffff +end + +#- crc32 for ota_seq as 32 bits unsigned, with init vector -1 -# +def crc32_ota_seq(seq) + return crc32_update(bytes().add(seq, 4), 0xFFFFFFFF) +end + #------------------------------------------------------------- - Class for a partition table entry + - + typedef struct { + uint16_t magic; + uint8_t type; + uint8_t subtype; + uint32_t offset; + uint32_t size; + uint8_t label[16]; + uint32_t flags; + } esp_partition_info_t_simplified; -------------------------------------------------------------# class Partition_info var type @@ -62,8 +112,33 @@ class Partition_info def tostring() import string - return string.format("", - self.type, self.subtype, self.start, self.size, + var type_s = "" + var subtype_s = "" + if self.type == 0 type_s = "app" + if self.subtype == 0 subtype_s = "factory" + elif self.subtype >= 0x10 && self.subtype < 0x20 subtype_s = "ota" + str(self.subtype - 0x10) + elif self.subtype == 0x20 subtype_s = "test" + end + elif self.type == 1 type_s = "data" + if self.subtype == 0x00 subtype_s = "otadata" + elif self.subtype == 0x01 subtype_s = "phy" + elif self.subtype == 0x02 subtype_s = "nvs" + elif self.subtype == 0x03 subtype_s = "coredump" + elif self.subtype == 0x04 subtype_s = "nvskeys" + elif self.subtype == 0x05 subtype_s = "efuse_em" + elif self.subtype == 0x80 subtype_s = "esphttpd" + elif self.subtype == 0x81 subtype_s = "fat" + elif self.subtype == 0x82 subtype_s = "spiffs" + end + end + + #- reformat strings -# + if type_s != "" type_s = " (" + type_s + ")" end + if subtype_s != "" subtype_s = " (" + subtype_s + ")" end + return string.format("", + self.type, type_s, + self.subtype, subtype_s, + self.start, self.size, self.label, self.flags) end @@ -84,6 +159,150 @@ class Partition_info end + +#------------------------------------------------------------- + - OTA Data + - + - Selection of the active OTA partition + - + typedef struct { + uint32_t ota_seq; + uint8_t seq_label[20]; + uint32_t ota_state; + uint32_t crc; /* CRC32 of ota_seq field only */ + } esp_ota_select_entry_t; + + - Excerp from esp_ota_ops.c + esp32_idf use two sector for store information about which partition is running + it defined the two sector as ota data partition,two structure esp_ota_select_entry_t is saved in the two sector + named data in first sector as otadata[0], second sector data as otadata[1] + e.g. + if otadata[0].ota_seq == otadata[1].ota_seq == 0xFFFFFFFF,means ota info partition is in init status + so it will boot factory application(if there is),if there's no factory application,it will boot ota[0] application + if otadata[0].ota_seq != 0 and otadata[1].ota_seq != 0,it will choose a max seq ,and get value of max_seq%max_ota_app_number + and boot a subtype (mask 0x0F) value is (max_seq - 1)%max_ota_app_number,so if want switch to run ota[x],can use next formulas. + for example, if otadata[0].ota_seq = 4, otadata[1].ota_seq = 5, and there are 8 ota application, + current running is (5-1)%8 = 4,running ota[4],so if we want to switch to run ota[7], + we should add otadata[0].ota_seq (is 4) to 4 ,(8-1)%8=7,then it will boot ota[7] + if A=(B - C)%D + then B=(A + C)%D + D*n ,n= (0,1,2...) + so current ota app sub type id is x , dest bin subtype is y,total ota app count is n + seq will add (x + n*1 + 1 - seq)%n + -------------------------------------------------------------# +class Partition_otadata + var maxota #- number of highest OTA partition, default 1 (double ota0/ota1) -# + var offset #- offset of the otadata partition (0x2000 in length), default 0xE000 -# + var active_otadata #- which otadata block is active, 0 or 1, i.e. 0xE000 or 0xF000 -# + var seq0 #- ota_seq of first block -# + var seq1 #- ota_seq of second block -# + + def init(maxota, offset) + self.maxota = maxota + if self.maxota == nil self.maxota = 1 end + self.offset = offset + if self.offset == nil self.offset = 0xE000 end + self.active_otadata = 0 + self.load() + end + + #- update ota_max, needs to recompute everything -# + def set_ota_max(n) + self.maxota = n + end + + def set_active(n) + var seq_max = 0 #- current highest seq number -# + var block_act = 0 #- block number containing the highest seq number -# + + if self.seq0 != nil + seq_max = self.seq0 + block_act = 0 + end + if self.seq1 != nil && self.seq1 > seq_max + seq_max = self.seq1 + block_act = 1 + end + + #- compute the next sequence number -# + var actual_ota = (seq_max - 1) % (self.maxota + 1) + if actual_ota != n #- change only if different -# + if n > actual_ota seq_max += n - actual_ota + else seq_max += (self.maxota + 1) - actual_ota + n + end + + #- update internal structure -# + if block_act == 1 #- current block is 1, so update block 0 -# + self.seq0 = seq_max + else #- or write to block 1 -# + self.seq1 = seq_max + end + self._validate() + end + + end + + #- load otadata -# + def load() + var otadata0 = flash.read(0xE000, 32) + var otadata1 = flash.read(0xF000, 32) + self.seq0 = otadata0.get(0, 4) #- ota_seq for block 1 -# + self.seq1 = otadata1.get(0, 4) #- ota_seq for block 2 -# + var valid0 = otadata0.get(28, 4) == crc32_ota_seq(self.seq0) #- is CRC32 valid? -# + var valid1 = otadata1.get(28, 4) == crc32_ota_seq(self.seq1) #- is CRC32 valid? -# + if !valid0 self.seq0 = nil end + if !valid1 self.seq1 = nil end + + self._validate() + end + + #- internally used, validate data -# + def _validate() + self.active_otadata = 0 #- if none is valid, default to OTA0 -# + if self.seq0 != nil + self.active_otadata = (self.seq0 - 1) % (self.maxota + 1) + end + if self.seq1 != nil && (self.seq0 == nil || self.seq1 > self.seq0) + self.active_otadata = (self.seq1 - 1) % (self.maxota + 1) + end + end + + def save() + #- check the block number to save, 0 or 1. Choose the highest ota_seq -# + var block_to_save = -1 #- invalid -# + var seq_to_save = -1 #- invalid value -# + + # check seq0 + if self.seq0 != nil + seq_to_save = self.seq0 + block_to_save = 0 + end + if (self.seq1 != nil) && (self.seq1 > seq_to_save) + seq_to_save = self.seq1 + block_to_save = 1 + end + # if none was good + if block_to_save < 0 block_to_save = 0 end + if seq_to_save < 0 seq_to_save = 1 end + + var offset_to_save = self.offset + 0x1000 * block_to_save #- default 0xE000 or 0xF000 -# + + var bytes_to_save = bytes() + bytes_to_save.add(seq_to_save, 4) + bytes_to_save += bytes("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + bytes_to_save.add(crc32_ota_seq(seq_to_save), 4) + + #- erase flash area and write -# + flash.erase(offset_to_save, 0x1000) + flash.write(offset_to_save, bytes_to_save) + end + + def tostring() + return string.format("", + self.active_otadata, self.seq0, self.seq1, self.maxota) + end + +end + #------------------------------------------------------------- - Class for a partition table entry -------------------------------------------------------------# @@ -91,11 +310,13 @@ class Partition var raw #- raw bytes of the partition table in flash -# var md5 #- md5 hash of partition list -# var slots + var otadata #- instance of Partition_otadata() -# def init() self.slots = [] self.load() self.parse() + self.load_otadata() end def load() @@ -121,6 +342,38 @@ class Partition end end + #- compute the highest ota partition -# + def ota_max() + var ota_max = 0 + for slot:self.slots + if slot.type == 0 && (slot.subtype >= 0x10 && slot.subtype < 0x20) + var ota_num = slot.subtype - 0x10 + if ota_num > ota_max ota_max = ota_num end + end + end + return ota_max + end + + def load_otadata() + #- look for otadata partition offset, and max_ota -# + var otadata_offset = 0xE000 #- default value -# + var ota_max = self.ota_max() + for slot:self.slots + if slot.type == 1 && slot.subtype == 0 #- otadata -# + otadata_offset = slot.start + end + end + + self.otadata = Partition_otadata(ota_max, otadata_offset) + end + + #- change the active partition -# + def set_active(n) + if n < 0 || n > self.ota_max() raise "value_error", "Invalid ota partition number" end + self.otadata.set_ota_max(self.ota_max()) #- update ota_max if it changed -# + self.otadata.set_active(n) + end + #- convert to human readble -# def tostring() var ret = " Date: Tue, 8 Jun 2021 16:04:20 +0200 Subject: [PATCH 5/5] Use correct template for Home Assistant light --- tasmota/xdrv_12_home_assistant.ino | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index 323c1b2c5..4a5fbeaf7 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -65,10 +65,12 @@ const char HASS_DISCOVER_SENSOR_LWT[] PROGMEM = const char HASS_DISCOVER_RELAY[] PROGMEM = ",\"cmd_t\":\"%s\"," // cmnd/dualr2/POWER2 - "\"val_tpl\":\"{{value_json.%s}}\"," // POWER2 "\"pl_off\":\"%s\"," // OFF "\"pl_on\":\"%s\""; // ON +const char HASS_DISCOVER_RELAY_TEMPLATE[] PROGMEM = + ",\"val_tpl\":\"{{value_json.%s}}\""; // POWER2 + const char HASS_DISCOVER_BIN_SWITCH[] PROGMEM = ",\"val_tpl\":\"{{value_json.%s}}\"," // STATE "\"frc_upd\":true," // In ON/OFF case, enable force_update to make automations work @@ -81,6 +83,9 @@ const char HASS_DISCOVER_BIN_PIR[] PROGMEM = "\"pl_on\":\"%s\"," // ON "\"off_dly\":1"; // Switchmode13 and Switchmode14 doesn't transmit an OFF state. +const char HASS_DISCOVER_LIGHT_TEMPLATE[] PROGMEM = + ",\"stat_val_tpl\":\"{{value_json.%s}}\""; // POWER2 + const char HASS_DISCOVER_BASE_LIGHT[] PROGMEM = ",\"bri_cmd_t\":\"%s\"," // cmnd/led2/Dimmer "\"bri_stat_t\":\"%s\"," // stat/led2/RESULT @@ -489,7 +494,12 @@ void HAssAnnounceRelayLight(void) GetTopic_P(availability_topic, TELE, TasmotaGlobal.mqtt_topic, S_LWT); Response_P(HASS_DISCOVER_BASE, name, state_topic); TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); + TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); + if (is_topic_light) { + TryResponseAppend_P(HASS_DISCOVER_LIGHT_TEMPLATE, value_template); + } else { + TryResponseAppend_P(HASS_DISCOVER_RELAY_TEMPLATE, value_template); + } TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); #ifdef USE_LIGHT