From 96eac4a54723a86adaf876f4b475683384dac6c6 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 18 Jan 2021 16:10:17 +0100 Subject: [PATCH 01/19] Use solo1-release_v3.3-7e63061fa --- platformio_override_sample.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index 4db6a41b3..deff8272a 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -165,7 +165,7 @@ lib_extra_dirs = ; *** EXPERIMENTAL Tasmota version for ESP32solo1 (used in some Xiaomi devices) [env:tasmota32solo1] extends = env:tasmota32 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/raw/framework-arduinoespressif32/framework-arduinoespressif32-release_v3.3-solo1-4b325f52e.tar.gz +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/raw/framework-arduinoespressif32/framework-arduinoespressif32-solo1-release_v3.3-7e63061fa.tar.gz platformio/tool-mklittlefs @ ~1.203.200522 platformio/tool-esptoolpy @ ~1.30000.0 build_unflags = ${esp32_defaults.build_unflags} From 57a4153a418d6daebc5d407f700caa611de9d05f Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 18 Jan 2021 18:31:19 +0100 Subject: [PATCH 02/19] Zigbee fix bad JSON for `ZbStatus0` --- tasmota/xdrv_23_zigbee_2a_devices_impl.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino index ffb5a72df..7d850ef5a 100644 --- a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino +++ b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino @@ -767,7 +767,7 @@ String Z_Devices::dumpCoordinator(void) const { attr_list.addAttributePMEM(PSTR("IEEEAddr")).setHex64(localIEEEAddr); attr_list.addAttributePMEM(PSTR("TotalDevices")).setUInt(zigbee_devices.devicesSize()); - return attr_list.toString(); + return attr_list.toString(true); } // If &device == nullptr, then dump all From 1b1b1ed83cdba93535b01449cacbb1100d94912d Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 18 Jan 2021 19:21:51 +0100 Subject: [PATCH 03/19] build gz only for ESP8266 --- pio-tools/gzip-firmware.py | 42 ++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/pio-tools/gzip-firmware.py b/pio-tools/gzip-firmware.py index e29c05576..bb1759123 100644 --- a/pio-tools/gzip-firmware.py +++ b/pio-tools/gzip-firmware.py @@ -3,26 +3,32 @@ import os import shutil import gzip -OUTPUT_DIR = "build_output{}".format(os.path.sep) +platform = env.PioPlatform() +board = env.BoardConfig() +mcu = board.get("build.mcu", "esp32") +# gzip only for ESP8266 +if env["PIOPLATFORM"] != "espressif32": -def bin_gzip(source, target, env): - variant = str(target[0]).split(os.path.sep)[2] - - # create string with location and file names based on variant - bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant) - gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant) + OUTPUT_DIR = "build_output{}".format(os.path.sep) - # check if new target files exist and remove if necessary - if os.path.isfile(gzip_file): os.remove(gzip_file) + def bin_gzip(source, target, env): + variant = str(target[0]).split(os.path.sep)[2] - # write gzip firmware file - with open(bin_file,"rb") as fp: - with gzip.open(gzip_file, "wb", compresslevel = 9) as f: - shutil.copyfileobj(fp, f) - - ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size - GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size + # create string with location and file names based on variant + bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant) + gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant) - print("Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format((GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100, ORG_FIRMWARE_SIZE, GZ_FIRMWARE_SIZE)) + # check if new target files exist and remove if necessary + if os.path.isfile(gzip_file): os.remove(gzip_file) -env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip]) + # write gzip firmware file + with open(bin_file,"rb") as fp: + with gzip.open(gzip_file, "wb", compresslevel = 9) as f: + shutil.copyfileobj(fp, f) + + ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size + GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size + + print("Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format((GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100, ORG_FIRMWARE_SIZE, GZ_FIRMWARE_SIZE)) + + env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip]) From 081f097b60b037e554d33a3a90e8b48460cccd3f Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 18 Jan 2021 20:39:14 +0100 Subject: [PATCH 04/19] Zigbee increase timeouts for EZSP --- tasmota/xdrv_23_zigbee_7_0_statemachine.ino | 64 ++++++++++----------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino index cc23c7a92..b2a01b6eb 100644 --- a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino @@ -789,38 +789,38 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { // configure EFR32 ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredCoord) - ZI_SEND(ZBS_SET_ADDR_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK) // Address table size - ZI_SEND(ZBS_SET_MCAST_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_STK_PROF) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_SEC_LEVEL) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_MAX_DEVICES) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_INDIRECT_TMO) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_TC_CACHE) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_ROUTE_TBL) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_KEY_TBL) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_PANID_CNFLCT) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_ZDO_REQ) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_NETWORKS) ZI_WAIT_RECV(500, ZBR_SET_OK) - ZI_SEND(ZBS_SET_PACKET_BUF) ZI_WAIT_RECV(500, ZBR_SET_OK2) + ZI_SEND(ZBS_SET_ADDR_TABLE) ZI_WAIT_RECV(2500, ZBR_SET_OK) // Address table size + ZI_SEND(ZBS_SET_MCAST_TABLE) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_STK_PROF) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_SEC_LEVEL) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_MAX_DEVICES) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_INDIRECT_TMO) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_TC_CACHE) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_ROUTE_TBL) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_KEY_TBL) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_PANID_CNFLCT) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_ZDO_REQ) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_NETWORKS) ZI_WAIT_RECV(2500, ZBR_SET_OK) + ZI_SEND(ZBS_SET_PACKET_BUF) ZI_WAIT_RECV(2500, ZBR_SET_OK2) // read configuration // TODO - not sure it's useful - //ZI_SEND(ZBS_GET_APS_UNI) ZI_WAIT_RECV_FUNC(500, ZBR_GET_OK, &EZ_ReadAPSUnicastMessage) + //ZI_SEND(ZBS_GET_APS_UNI) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_OK, &EZ_ReadAPSUnicastMessage) // add endpoint 0x01 and 0x0B - ZI_SEND(ZBS_ADD_ENDPOINT1) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT) - ZI_SEND(ZBS_ADD_ENDPOINTB) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT) + ZI_SEND(ZBS_ADD_ENDPOINT1) ZI_WAIT_RECV(2500, ZBR_ADD_ENDPOINT) + ZI_SEND(ZBS_ADD_ENDPOINTB) ZI_WAIT_RECV(2500, ZBR_ADD_ENDPOINT) // set Concentrator - ZI_SEND(ZBS_SET_CONCENTRATOR) ZI_WAIT_RECV(500, ZBR_SET_CONCENTRATOR) + ZI_SEND(ZBS_SET_CONCENTRATOR) ZI_WAIT_RECV(2500, ZBR_SET_CONCENTRATOR) // setInitialSecurityState - ZI_SEND(ZBS_SET_POLICY_00) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) - ZI_SEND(ZBS_SET_POLICY_02) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) - ZI_SEND(ZBS_SET_POLICY_03) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) - // ZI_SEND(ZBS_SET_POLICY_04) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) - ZI_SEND(ZBS_SET_POLICY_05) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) - ZI_SEND(ZBS_SET_POLICY_06) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_00) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_02) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_03) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX) + // ZI_SEND(ZBS_SET_POLICY_04) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_05) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX) + ZI_SEND(ZBS_SET_POLICY_06) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX) // Decide whether we try 'networkInit()' to restore configuration, or create a new network ZI_CALL(&EZ_GotoIfResetConfig, ZIGBEE_LABEL_CONFIGURE_EZSP) // goto ZIGBEE_LABEL_CONFIGURE_EZSP if reset_config is set @@ -830,12 +830,12 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { // Try networkInit to restore settings, and check if network comes up ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_BAD_CONFIG) // ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_BAD_CONFIG) - ZI_SEND(ZBS_NETWORK_INIT) ZI_WAIT_RECV(500, ZBR_NETWORK_INIT) + ZI_SEND(ZBS_NETWORK_INIT) ZI_WAIT_RECV(2500, ZBR_NETWORK_INIT) ZI_WAIT_RECV(1500, ZBR_NETWORK_UP) // wait for network to start // check if configuration is ok - ZI_SEND(ZBS_GET_KEY_NWK) ZI_WAIT_RECV_FUNC(500, ZBR_GET_KEY_NWK, &EZ_CheckKeyNWK) - ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(500, ZBR_GET_EUI64, &EZ_GetEUI64) - ZI_SEND(ZBS_GET_NETW_PARM) ZI_WAIT_RECV_FUNC(500, ZBR_CHECK_NETW_PARM, &EZ_NetworkParameters) + ZI_SEND(ZBS_GET_KEY_NWK) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_KEY_NWK, &EZ_CheckKeyNWK) + ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_EUI64, &EZ_GetEUI64) + ZI_SEND(ZBS_GET_NETW_PARM) ZI_WAIT_RECV_FUNC(2500, ZBR_CHECK_NETW_PARM, &EZ_NetworkParameters) // all ok, proceed to next step ZI_GOTO(ZIGBEE_LABEL_NETWORK_CONFIGURED) @@ -851,9 +851,9 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) // set encryption keys - ZI_SEND(ZBS_SET_SECURITY) ZI_WAIT_RECV(500, ZBR_SET_SECURITY) + ZI_SEND(ZBS_SET_SECURITY) ZI_WAIT_RECV(2500, ZBR_SET_SECURITY) // formNetwork - ZI_SEND(ZBS_FORM_NETWORK) ZI_WAIT_RECV(500, ZBR_FORM_NETWORK) + ZI_SEND(ZBS_FORM_NETWORK) ZI_WAIT_RECV(2500, ZBR_FORM_NETWORK) ZI_WAIT_RECV(5000, ZBR_NETWORK_UP) // wait for network to start ZI_LABEL(ZIGBEE_LABEL_NETWORK_CONFIGURED) @@ -861,11 +861,11 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) // Query device information - ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(500, ZBR_GET_EUI64, &EZ_GetEUI64) - ZI_SEND(ZBS_GET_NODEID) ZI_WAIT_RECV_FUNC(500, ZBR_GET_NODEID, &EZ_GetNodeId) + ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_EUI64, &EZ_GetEUI64) + ZI_SEND(ZBS_GET_NODEID) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_NODEID, &EZ_GetNodeId) // auto-register multicast group 0x0000 ZI_LOG(LOG_LEVEL_INFO, kZigbeeGroup0) - ZI_SEND(ZBS_SET_MCAST_ENTRY) ZI_WAIT_RECV(500, ZBR_SET_MCAST_ENTRY) + ZI_SEND(ZBS_SET_MCAST_ENTRY) ZI_WAIT_RECV(2500, ZBR_SET_MCAST_ENTRY) // ZI_LABEL(ZIGBEE_LABEL_READY) ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted) From cdc9d8dfc9082e39ef06ad33e0921c4913782db0 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Mon, 18 Jan 2021 21:28:12 +0100 Subject: [PATCH 05/19] Zigbee send ack to command (#10624) * Zigbee send ack to command * Fix cluster Co-authored-by: Stephan Hadinger --- tasmota/xdrv_23_zigbee_5_converters.ino | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index 674f6fdd0..4af809b5c 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -1673,6 +1673,27 @@ void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) { } } } + // Send Default Response to acknowledge the attribute reporting + if (0 == _frame_control.b.disable_def_resp) { + // the device expects a default response + SBuffer buf(2); + buf.add8(_cmd_id); + buf.add8(0x00); // Status = OK + + ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({ + _srcaddr, + 0x0000, + _cluster_id, + _srcendpoint, + ZCL_DEFAULT_RESPONSE, + _manuf_code, + false /* not cluster specific */, + false /* noresponse */, + true /* direct no retry */, + _transact_seq, /* zcl transaction id */ + buf.getBuffer(), buf.len() + })); + } } // ====================================================================== From 2f139d0e47bf7e353fdf47d10b38c101cd8a1558 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 18 Jan 2021 21:48:04 +0100 Subject: [PATCH 06/19] More PROGMEM --- .../jsmn-shadinger-1.0/src/JsonGenerator.h | 4 +- tasmota/settings.ino | 38 ++--- tasmota/support.ino | 13 +- tasmota/support_command.ino | 6 +- tasmota/support_network.ino | 2 +- tasmota/support_rtc.ino | 18 ++- tasmota/support_tasmota.ino | 12 +- tasmota/support_wifi.ino | 4 +- tasmota/tasmota.ino | 4 +- tasmota/xdrv_01_webserver.ino | 145 +++++++++--------- tasmota/xdrv_02_mqtt.ino | 50 +++--- tasmota/xdrv_04_light.ino | 2 +- tasmota/xdrv_07_domoticz.ino | 4 +- tasmota/xdrv_09_timers.ino | 6 +- tasmota/xdrv_10_rules.ino | 22 +-- tasmota/xdrv_12_home_assistant.ino | 6 +- tasmota/xdrv_16_tuyamcu.ino | 2 +- tasmota/xdrv_20_hue.ino | 56 +++---- tasmota/xdrv_23_zigbee_1z_libs.ino | 6 +- tasmota/xdrv_23_zigbee_2a_devices_impl.ino | 2 +- tasmota/xdrv_23_zigbee_3_hue.ino | 16 +- tasmota/xdrv_23_zigbee_A_impl.ino | 2 +- tasmota/xdrv_38_ping.ino | 2 +- tasmota/xdrv_40_telegram.ino | 4 +- tasmota/xdrv_50_filesystem.ino | 28 ++-- tasmota/xsns_34_hx711.ino | 2 +- 26 files changed, 234 insertions(+), 222 deletions(-) diff --git a/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h b/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h index 2f27b846b..1d6c0f969 100644 --- a/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h +++ b/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h @@ -32,7 +32,7 @@ extern String EscapeJSONString(const char *str); class JsonGeneratorArray { public: - JsonGeneratorArray(): val("[]") {} // start with empty array + JsonGeneratorArray(): val(F("[]")) {} // start with empty array void add(uint32_t uval32); void add(int32_t uval32); @@ -53,7 +53,7 @@ protected: class JsonGeneratorObject { public: - JsonGeneratorObject(): val("{}") {} // start with empty object + JsonGeneratorObject(): val(F("{}")) {} // start with empty object void add(const char* key, uint32_t uval32); void add(const char* key, int32_t uval32); diff --git a/tasmota/settings.ino b/tasmota/settings.ino index b2d438df6..0ebdac4c7 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -812,10 +812,10 @@ void SettingsDefaultSet2(void) flag3.use_wifi_rescan |= WIFI_SCAN_REGULARLY; Settings.wifi_output_power = 170; Settings.param[P_ARP_GRATUITOUS] = WIFI_ARP_INTERVAL; - ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS); - ParseIp(&Settings.ip_address[1], WIFI_GATEWAY); - ParseIp(&Settings.ip_address[2], WIFI_SUBNETMASK); - ParseIp(&Settings.ip_address[3], WIFI_DNS); + ParseIp(&Settings.ip_address[0], PSTR(WIFI_IP_ADDRESS)); + ParseIp(&Settings.ip_address[1], PSTR(WIFI_GATEWAY)); + ParseIp(&Settings.ip_address[2], PSTR(WIFI_SUBNETMASK)); + ParseIp(&Settings.ip_address[3], PSTR(WIFI_DNS)); Settings.sta_config = WIFI_CONFIG_TOOL; // Settings.sta_active = 0; SettingsUpdateText(SET_STASSID1, PSTR(STA_SSID1)); @@ -865,22 +865,22 @@ void SettingsDefaultSet2(void) flag3.grouptopic_mode |= MQTT_GROUPTOPIC_FORMAT; SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST); Settings.mqtt_port = MQTT_PORT; - SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID); - SettingsUpdateText(SET_MQTT_USER, MQTT_USER); - SettingsUpdateText(SET_MQTT_PWD, MQTT_PASS); - SettingsUpdateText(SET_MQTT_TOPIC, MQTT_TOPIC); - SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); - SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); - SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); - SettingsUpdateText(SET_MQTT_FULLTOPIC, MQTT_FULLTOPIC); + SettingsUpdateText(SET_MQTT_CLIENT, PSTR(MQTT_CLIENT_ID)); + SettingsUpdateText(SET_MQTT_USER, PSTR(MQTT_USER)); + SettingsUpdateText(SET_MQTT_PWD, PSTR(MQTT_PASS)); + SettingsUpdateText(SET_MQTT_TOPIC, PSTR(MQTT_TOPIC)); + SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, PSTR(MQTT_BUTTON_TOPIC)); + SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, PSTR(MQTT_SWITCH_TOPIC)); + SettingsUpdateText(SET_MQTT_GRP_TOPIC, PSTR(MQTT_GRPTOPIC)); + SettingsUpdateText(SET_MQTT_FULLTOPIC, PSTR(MQTT_FULLTOPIC)); Settings.mqtt_retry = MQTT_RETRY_SECS; - SettingsUpdateText(SET_MQTTPREFIX1, SUB_PREFIX); - SettingsUpdateText(SET_MQTTPREFIX2, PUB_PREFIX); - SettingsUpdateText(SET_MQTTPREFIX3, PUB_PREFIX2); - SettingsUpdateText(SET_STATE_TXT1, MQTT_STATUS_OFF); - SettingsUpdateText(SET_STATE_TXT2, MQTT_STATUS_ON); - SettingsUpdateText(SET_STATE_TXT3, MQTT_CMND_TOGGLE); - SettingsUpdateText(SET_STATE_TXT4, MQTT_CMND_HOLD); + SettingsUpdateText(SET_MQTTPREFIX1, PSTR(SUB_PREFIX)); + SettingsUpdateText(SET_MQTTPREFIX2, PSTR(PUB_PREFIX)); + SettingsUpdateText(SET_MQTTPREFIX3, PSTR(PUB_PREFIX2)); + SettingsUpdateText(SET_STATE_TXT1, PSTR(MQTT_STATUS_OFF)); + SettingsUpdateText(SET_STATE_TXT2, PSTR(MQTT_STATUS_ON)); + SettingsUpdateText(SET_STATE_TXT3, PSTR(MQTT_CMND_TOGGLE)); + SettingsUpdateText(SET_STATE_TXT4, PSTR(MQTT_CMND_HOLD)); memcpy_P(Settings.mqtt_fingerprint[0], default_fingerprint1, sizeof(default_fingerprint1)); memcpy_P(Settings.mqtt_fingerprint[1], default_fingerprint2, sizeof(default_fingerprint2)); Settings.tele_period = TELE_PERIOD; diff --git a/tasmota/support.ino b/tasmota/support.ino index 0d5ce5b94..dace03c58 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -384,7 +384,7 @@ char* Uint64toHex(uint64_t value, char *str, uint16_t bits) char* dtostrfd(double number, unsigned char prec, char *s) { if ((isnan(number)) || (isinf(number))) { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript) - strcpy(s, "null"); + strcpy_P(s, PSTR("null")); return s; } else { return dtostrf(number, 1, prec, s); @@ -661,10 +661,13 @@ bool ValidIpAddress(const char* str) return (*p == '\0'); } -bool ParseIp(uint32_t* addr, const char* str) +bool ParseIp(uint32_t* addr, const char* str_p) { uint8_t *part = (uint8_t*)addr; uint8_t i; + char str_r[strlen_P(str_p)+1]; + char * str = &str_r[0]; + strcpy_P(str, str_p); *addr = 0; for (i = 0; i < 4; i++) { @@ -861,7 +864,7 @@ float ConvertPressureForSeaLevel(float pressure) String PressureUnit(void) { - return (Settings.flag.pressure_conversion) ? String(D_UNIT_MILLIMETER_MERCURY) : String(D_UNIT_PRESSURE); + return (Settings.flag.pressure_conversion) ? String(F(D_UNIT_MILLIMETER_MERCURY)) : String(F(D_UNIT_PRESSURE)); } float ConvertSpeed(float s) @@ -1024,11 +1027,11 @@ String GetSerialConfig(void) { // b00000x00 - 1 or 2 stop bits // b000xx000 - None, Even or Odd parity - const char kParity[] = "NEOI"; + const static char kParity[] PROGMEM = "NEOI"; char config[4]; config[0] = '5' + (Settings.serial_config & 0x3); - config[1] = kParity[(Settings.serial_config >> 3) & 0x3]; + config[1] = pgm_read_byte(&kParity[(Settings.serial_config >> 3) & 0x3]); config[2] = '1' + ((Settings.serial_config >> 2) & 0x1); config[3] = '\0'; return String(config); diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 1c5e32474..145d86ef2 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -110,7 +110,7 @@ void ResponseCmndStateText(uint32_t value) void ResponseCmndDone(void) { - ResponseCmndChar(D_JSON_DONE); + ResponseCmndChar(PSTR(D_JSON_DONE)); } void ResponseCmndIdxChar(const char* value) @@ -352,7 +352,7 @@ void CmndBacklog(void) #else TasmotaGlobal.backlog_pointer = TasmotaGlobal.backlog_index; #endif - ResponseCmndChar(blflag ? D_JSON_EMPTY : D_JSON_ABORTED); + ResponseCmndChar(blflag ? PSTR(D_JSON_EMPTY) : PSTR(D_JSON_ABORTED)); } } @@ -1282,7 +1282,7 @@ void CmndTemplate(void) if (Settings.module != USER_MODULE) { ModuleDefault(Settings.module); } - SettingsUpdateText(SET_TEMPLATE_NAME, "Merged"); + SettingsUpdateText(SET_TEMPLATE_NAME, PSTR("Merged")); uint32_t j = 0; for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) { if (6 == i) { j = 9; } diff --git a/tasmota/support_network.ino b/tasmota/support_network.ino index b2bb73bf0..e2b24d782 100644 --- a/tasmota/support_network.ino +++ b/tasmota/support_network.ino @@ -38,7 +38,7 @@ void StartMdns(void) { // mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START]; MDNS.end(); // close existing or MDNS.begin will fail Mdns.begun = (uint8_t)MDNS.begin(TasmotaGlobal.hostname); - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Mdns.begun) ? D_INITIALIZED : D_FAILED); + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Mdns.begun) ? PSTR(D_INITIALIZED) : PSTR(D_FAILED)); // } } } diff --git a/tasmota/support_rtc.ino b/tasmota/support_rtc.ino index 08f7cfc3d..2f2403e7a 100644 --- a/tasmota/support_rtc.ino +++ b/tasmota/support_rtc.ino @@ -33,8 +33,8 @@ const uint32_t MINS_PER_HOUR = 60UL; Ticker TickerRtc; -static const uint8_t kDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // API starts months from 1, this array starts from 0 -static const char kMonthNamesEnglish[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; +static const uint8_t kDaysInMonth[] PROGMEM = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // API starts months from 1, this array starts from 0 +static const char kMonthNamesEnglish[] PROGMEM = "JanFebMarAprMayJunJulAugSepOctNovDec"; struct RTC { uint32_t utc_time = 0; @@ -88,7 +88,9 @@ String GetBuildDateAndTime(void) // "2017-03-07T11:08:02" - ISO8601:2004 char bdt[21]; char *p; - char mdate[] = __DATE__; // "Mar 7 2017" + static const char mdate_P[] PROGMEM = __DATE__; // "Mar 7 2017" + char mdate[strlen_P(mdate_P)+1]; // copy on stack first + strcpy_P(mdate, mdate_P); char *smonth = mdate; int day = 0; int year = 0; @@ -107,8 +109,10 @@ String GetBuildDateAndTime(void) year = atoi(str); } } - int month = (strstr(kMonthNamesEnglish, smonth) -kMonthNamesEnglish) /3 +1; - snprintf_P(bdt, sizeof(bdt), PSTR("%d" D_YEAR_MONTH_SEPARATOR "%02d" D_MONTH_DAY_SEPARATOR "%02d" D_DATE_TIME_SEPARATOR "%s"), year, month, day, __TIME__); + char MonthNamesEnglish[sizeof(kMonthNamesEnglish)]; + strcpy_P(MonthNamesEnglish, kMonthNamesEnglish); + int month = (strstr(MonthNamesEnglish, smonth) -MonthNamesEnglish) /3 +1; + snprintf_P(bdt, sizeof(bdt), PSTR("%d" D_YEAR_MONTH_SEPARATOR "%02d" D_MONTH_DAY_SEPARATOR "%02d" D_DATE_TIME_SEPARATOR "%s"), year, month, day, PSTR(__TIME__)); return String(bdt); // 2017-03-07T11:08:02 } @@ -290,7 +294,7 @@ void BreakTime(uint32_t time_input, TIME_T &tm) month_length = 28; } } else { - month_length = kDaysInMonth[month]; + month_length = pgm_read_byte(&kDaysInMonth[month]); } if (time >= month_length) { @@ -326,7 +330,7 @@ uint32_t MakeTime(TIME_T &tm) if ((2 == i) && LEAP_YEAR(tm.year)) { seconds += SECS_PER_DAY * 29; } else { - seconds += SECS_PER_DAY * kDaysInMonth[i-1]; // monthDay array starts from 0 + seconds += SECS_PER_DAY * pgm_read_byte(&kDaysInMonth[i-1]); // monthDay array starts from 0 } } seconds+= (tm.day_of_month - 1) * SECS_PER_DAY; diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 64bc6e09d..9cac3fa37 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -20,10 +20,12 @@ const char kSleepMode[] PROGMEM = "Dynamic|Normal"; const char kPrefixes[] PROGMEM = D_CMND "|" D_STAT "|" D_TELE; -char* Format(char* output, const char* input, int size) +char* Format(char* output, const char* input_p, int size) { char *token; uint32_t digits = 0; + char input[strlen_P(input_p)+1]; // copy from PMEM to RAM + strcpy_P(input, input_p); if (strchr(input, '%') != nullptr) { strlcpy(output, input, size); @@ -102,7 +104,7 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi fulltopic += TasmotaGlobal.mqtt_client; fulltopic += F("_fb"); // cmnd/_fb } else { - fulltopic += topic; // cmnd/ + fulltopic += (const __FlashStringHelper *)topic; // cmnd/ } } else { fulltopic = SettingsText(SET_MQTT_FULLTOPIC); @@ -118,7 +120,7 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi } fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), SettingsText(SET_MQTTPREFIX1 + prefix)); - fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic); + fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), (const __FlashStringHelper *)topic); fulltopic.replace(F("%hostname%"), TasmotaGlobal.hostname); String token_id = WiFi.macAddress(); token_id.replace(":", ""); @@ -486,7 +488,7 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state) #endif // USE_DOMOTICZ result = !Settings.flag3.button_switch_force_local; // SetOption61 - Force local operation when button/switch topic is set } else { - Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); + Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? PSTR("Switch") : PSTR("Button"), device, state); result = XdrvRulesProcess(); } #ifdef USE_PWM_DIMMER @@ -742,7 +744,7 @@ void TempHumDewShow(bool json, bool pass_on, const char *types, float f_temperat String GetSwitchText(uint32_t i) { String switch_text = SettingsText(SET_SWITCH_TXT1 + i); if ('\0' == switch_text[0]) { - switch_text = D_JSON_SWITCH + String(i +1); + switch_text = F(D_JSON_SWITCH) + String(i +1); } return switch_text; } diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index 15f5e10c2..68208b941 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -176,7 +176,7 @@ void WiFiSetSleepMode(void) void WifiBegin(uint8_t flag, uint8_t channel) { - const char kWifiPhyMode[] = " bgnl"; + const static char kWifiPhyMode[] PROGMEM = " bgnl"; #ifdef USE_EMULATION UdpDisconnect(); @@ -218,7 +218,7 @@ void WifiBegin(uint8_t flag, uint8_t channel) WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active)); } AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " 11%c " D_AS " %s..."), - Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, kWifiPhyMode[WiFi.getPhyMode() & 0x3], TasmotaGlobal.hostname); + Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, pgm_read_byte(&kWifiPhyMode[WiFi.getPhyMode() & 0x3]), TasmotaGlobal.hostname); #if LWIP_IPV6 for (bool configured = false; !configured;) { diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 2407e61b1..18995cd47 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -302,7 +302,7 @@ void setup(void) { snprintf_P(TasmotaGlobal.version, sizeof(TasmotaGlobal.version), PSTR("%s.%d"), TasmotaGlobal.version, VERSION & 0xff); } // Thehackbox inserts "release" or "commit number" before compiling using sed -i -e 's/PSTR("(%s)")/PSTR("(85cff52-%s)")/g' tasmota.ino - snprintf_P(TasmotaGlobal.image_name, sizeof(TasmotaGlobal.image_name), PSTR("(%s)"), CODE_IMAGE_STR); // Results in (85cff52-tasmota) or (release-tasmota) + snprintf_P(TasmotaGlobal.image_name, sizeof(TasmotaGlobal.image_name), PSTR("(%s)"), PSTR(CODE_IMAGE_STR)); // Results in (85cff52-tasmota) or (release-tasmota) Format(TasmotaGlobal.mqtt_client, SettingsText(SET_MQTT_CLIENT), sizeof(TasmotaGlobal.mqtt_client)); Format(TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_TOPIC), sizeof(TasmotaGlobal.mqtt_topic)); @@ -321,7 +321,7 @@ void setup(void) { SetPowerOnState(); AddLog_P(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_CORE_RELEASE "(%s)"), - PROJECT, SettingsText(SET_DEVICENAME), TasmotaGlobal.version, TasmotaGlobal.image_name, GetBuildDateAndTime().c_str()); + PSTR(PROJECT), SettingsText(SET_DEVICENAME), TasmotaGlobal.version, TasmotaGlobal.image_name, GetBuildDateAndTime().c_str()); #ifdef FIRMWARE_MINIMAL AddLog_P(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION)); #endif // FIRMWARE_MINIMAL diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 42cbf1af4..b43bc573f 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -376,9 +376,10 @@ struct WEB { } Web; // Helper function to avoid code duplication (saves 4k Flash) +// arg can be in PROGMEM static void WebGetArg(const char* arg, char* out, size_t max) { - String s = Webserver->arg(arg); + String s = Webserver->arg((const __FlashStringHelper *)arg); strlcpy(out, s.c_str(), max); // out[max-1] = '\0'; // Ensure terminating NUL } @@ -460,7 +461,7 @@ void StartWebserver(int type, IPAddress ipweb) WebServer_on(uri, line.handler, pgm_read_byte(&line.method)); } Webserver->onNotFound(HandleNotFound); - Webserver->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop); // this call requires 2 functions so we keep a direct call + Webserver->on(F("/u2"), HTTP_POST, HandleUploadDone, HandleUploadLoop); // this call requires 2 functions so we keep a direct call #ifndef FIRMWARE_MINIMAL XdrvCall(FUNC_WEB_ADD_HANDLER); XsnsCall(FUNC_WEB_ADD_HANDLER); @@ -691,7 +692,7 @@ void WSContentStart_P(const char* title, bool auth) WSContentBegin(200, CT_HTML); if (title != nullptr) { - WSContentSend_P(HTTP_HEADER1, D_HTML_LANGUAGE, SettingsText(SET_DEVICENAME), title); + WSContentSend_P(HTTP_HEADER1, PSTR(D_HTML_LANGUAGE), SettingsText(SET_DEVICENAME), title); } } @@ -767,7 +768,7 @@ void WSContentButton(uint32_t title_index) WSContentSend_P(PSTR("

"), GetTextIndexed(action, sizeof(action), title_index, kButtonAction), GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm), - (!title_index) ? "rst" : "non", + (!title_index) ? PSTR("rst") : PSTR("non"), GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); } else { WSContentSend_P(PSTR("

"), @@ -866,8 +867,8 @@ void HandleWifiLogin(void) void WebSliderColdWarm(void) { WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Cold Warm - "a", // a - Unique HTML id - "#eff", "#f81", // 6500k in RGB (White) to 2500k in RGB (Warm Yellow) + PSTR("a"), // a - Unique HTML id + PSTR("#eff"), PSTR("#f81"), // 6500k in RGB (White) to 2500k in RGB (Warm Yellow) 1, // sl1 153, 500, // Range color temperature LightGetColorTemp(), @@ -879,17 +880,17 @@ void HandleRoot(void) { if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the page. - if (Webserver->hasArg("rst")) { + if (Webserver->hasArg(F("rst"))) { WebRestart(0); return; } if (WifiIsInManagerMode()) { #ifndef FIRMWARE_MINIMAL - if (strlen(SettingsText(SET_WEBPWD)) && !(Webserver->hasArg("USER1")) && !(Webserver->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) { + if (strlen(SettingsText(SET_WEBPWD)) && !(Webserver->hasArg(F("USER1"))) && !(Webserver->hasArg(F("PASS1"))) && HTTP_MANAGER_RESET_ONLY != Web.state) { HandleWifiLogin(); } else { - if (!strlen(SettingsText(SET_WEBPWD)) || (((Webserver->arg("USER1") == WEB_USERNAME ) && (Webserver->arg("PASS1") == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { + if (!strlen(SettingsText(SET_WEBPWD)) || (((Webserver->arg(F("USER1")) == WEB_USERNAME ) && (Webserver->arg(F("PASS1")) == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { HandleWifiConfiguration(); } else { // wrong user and pass @@ -936,8 +937,8 @@ void HandleRoot(void) LightGetHSB(&hue, &sat, nullptr); WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Hue - "b", // b - Unique HTML id - "#800", PSTR("#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800"), // Hue colors + PSTR("b"), // b - Unique HTML id + PSTR("#800"), PSTR("#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800"), // Hue colors 2, // sl2 - Unique range HTML id - Used as source for Saturation end color 1, 359, // Range valid Hue hue, @@ -951,7 +952,7 @@ void HandleRoot(void) snprintf_P(stemp, sizeof(stemp), PSTR("#%02X%02X%02X"), red, green, blue); // Saturation end color WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Saturation - "s", // s - Unique HTML id related to eb('s').style.background='linear-gradient(to right,rgb('+sl+'%%,'+sl+'%%,'+sl+'%%),hsl('+eb('sl2').value+',100%%,50%%))'; + PSTR("s"), // s - Unique HTML id related to eb('s').style.background='linear-gradient(to right,rgb('+sl+'%%,'+sl+'%%,'+sl+'%%),hsl('+eb('sl2').value+',100%%,50%%))'; scolor, stemp, // Brightness to max current color 3, // sl3 - Unique range HTML id - Not used 0, 100, // Range 0 to 100% @@ -960,8 +961,8 @@ void HandleRoot(void) } WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Brightness - Black to White - "c", // c - Unique HTML id - "#000", "#fff", // Black to White + PSTR("c"), // c - Unique HTML id + PSTR("#000"), PSTR("#fff"), // Black to White 4, // sl4 - Unique range HTML id - Used as source for Saturation begin color Settings.flag3.slider_dimmer_stay_on, 100, // Range 0/1 to 100% (SetOption77 - Do not power off if slider moved to far left) Settings.light_dimmer, @@ -972,8 +973,8 @@ void HandleRoot(void) WebSliderColdWarm(); } WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // White brightness - Black to White - "f", // f - Unique HTML id - "#000", "#fff", // Black to White + PSTR("f"), // f - Unique HTML id + PSTR("#000"), PSTR("#fff"), // Black to White 5, // sl5 - Unique range HTML id - Not used Settings.flag3.slider_dimmer_stay_on, 100, // Range 0/1 to 100% (SetOption77 - Do not power off if slider moved to far left) LightGetDimmer(2), @@ -987,7 +988,7 @@ void HandleRoot(void) WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Channel brightness - Black to White stemp, // e1 to e5 - Unique HTML id - "#000", "#fff", // Black to White + PSTR("#000"), PSTR("#fff"), // Black to White i+1, // sl1 to sl5 - Unique range HTML id - Not used 1, 100, // Range 1 to 100% changeUIntScale(Settings.light_color[i], 0, 255, 0, 100), @@ -1008,7 +1009,7 @@ void HandleRoot(void) #ifdef USE_SONOFF_IFAN if (IsModuleIfan()) { WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1, - (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : D_BUTTON_TOGGLE, + (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : PSTR(D_BUTTON_TOGGLE), ""); for (uint32_t i = 0; i < MaxFanspeed(); i++) { snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i); @@ -1031,7 +1032,7 @@ void HandleRoot(void) #endif // USE_SHUTTER snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / TasmotaGlobal.devices_present, idx, - (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (TasmotaGlobal.devices_present < 5) ? D_BUTTON_TOGGLE : "", + (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (TasmotaGlobal.devices_present < 5) ? PSTR(D_BUTTON_TOGGLE) : "", (set_button) ? "" : (TasmotaGlobal.devices_present > 1) ? stemp : ""); } #ifdef USE_SONOFF_IFAN @@ -1107,7 +1108,7 @@ bool HandleRootStatusRefresh(void) char svalue[32]; // Command and number parameter char webindex[5]; // WebGetArg name - WebGetArg("o", tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed + WebGetArg(PSTR("o"), tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed if (strlen(tmp)) { ShowWebSource(SRC_WEBGUI); uint32_t device = atoi(tmp); @@ -1153,12 +1154,12 @@ bool HandleRootStatusRefresh(void) #endif // USE_TUYA_MCU } #ifdef USE_LIGHT - WebGetArg("d0", tmp, sizeof(tmp)); // 0 - 100 Dimmer value + WebGetArg(PSTR("d0"), tmp, sizeof(tmp)); // 0 - 100 Dimmer value if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } - WebGetArg("w0", tmp, sizeof(tmp)); // 0 - 100 White value + WebGetArg(PSTR("w0"), tmp, sizeof(tmp)); // 0 - 100 White value if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); @@ -1173,17 +1174,17 @@ bool HandleRootStatusRefresh(void) ExecuteWebCommand(svalue, SRC_WEBGUI); } } - WebGetArg("t0", tmp, sizeof(tmp)); // 153 - 500 Color temperature + WebGetArg(PSTR("t0"), tmp, sizeof(tmp)); // 153 - 500 Color temperature if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } - WebGetArg("h0", tmp, sizeof(tmp)); // 0 - 359 Hue value + WebGetArg(PSTR("h0"), tmp, sizeof(tmp)); // 0 - 359 Hue value if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "1 %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } - WebGetArg("n0", tmp, sizeof(tmp)); // 0 - 99 Saturation value + WebGetArg(PSTR("n0"), tmp, sizeof(tmp)); // 0 - 99 Saturation value if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "2 %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); @@ -1200,19 +1201,19 @@ bool HandleRootStatusRefresh(void) } #endif // USE_SHUTTER #ifdef USE_SONOFF_RF - WebGetArg("k", tmp, sizeof(tmp)); // 1 - 16 Pre defined RF keys + WebGetArg(PSTR("k"), tmp, sizeof(tmp)); // 1 - 16 Pre defined RF keys if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } #endif // USE_SONOFF_RF #ifdef USE_ZIGBEE - WebGetArg("zbj", tmp, sizeof(tmp)); + WebGetArg(PSTR("zbj"), tmp, sizeof(tmp)); if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR("ZbPermitJoin")); ExecuteWebCommand(svalue, SRC_WEBGUI); } - WebGetArg("zbr", tmp, sizeof(tmp)); + WebGetArg(PSTR("zbr"), tmp, sizeof(tmp)); if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR("ZbMap")); ExecuteWebCommand(svalue, SRC_WEBGUI); @@ -1315,7 +1316,7 @@ void WSContentSendNiceLists(uint32_t option) { char stemp[30]; // Template number and Sensor name for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { // GPIO: }2'0'>None (0)}3}2'17'>Button1 (17)}3... if (option && (1 == i)) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), D_SENSOR_USER); // }2'255'>User}3 + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), PSTR(D_SENSOR_USER)); // }2'255'>User}3 } uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0; uint32_t midx = BGPIO(ridx); @@ -1356,7 +1357,7 @@ void WSContentSendAdcNiceList(uint32_t option) { WSContentSend_P(PSTR("os=\"")); for (uint32_t i = 0; i < ARRAY_SIZE(kAdcNiceList); i++) { // GPIO: }2'0'>None}3}2'17'>Analog}3... if (option && (1 == i)) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), D_SENSOR_USER); // }2'15'>User}3 + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), PSTR(D_SENSOR_USER)); // }2'15'>User}3 } uint32_t ridx = pgm_read_word(kAdcNiceList + i) & 0xFFE0; uint32_t midx = BGPIO(ridx); @@ -1372,7 +1373,7 @@ void HandleTemplateConfiguration(void) { if (!HttpCheckPriviledgedAccess()) { return; } - if (Webserver->hasArg("save")) { + if (Webserver->hasArg(F("save"))) { TemplateSaveSettings(); WebRestart(1); return; @@ -1380,7 +1381,7 @@ void HandleTemplateConfiguration(void) char stemp[30]; // Template number and Sensor name - WebGetArg("t", stemp, sizeof(stemp)); // 0 - 69 Template number + WebGetArg(PSTR("t"), stemp, sizeof(stemp)); // 0 - 69 Template number if (strlen(stemp)) { uint32_t module = atoi(stemp); uint32_t module_save = Settings.module; @@ -1474,7 +1475,7 @@ void TemplateSaveSettings(void) char tmp[TOPSZ]; // WebGetArg NAME and GPIO/BASE/FLAG byte value char svalue[300]; // Template command string - WebGetArg("s1", tmp, sizeof(tmp)); // NAME + WebGetArg(PSTR("s1"), tmp, sizeof(tmp)); // NAME snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp); uint32_t j = 0; @@ -1492,7 +1493,7 @@ void TemplateSaveSettings(void) uint32_t state = Webserver->hasArg(webindex) << i; // FLAG flag += state; } - WebGetArg("g99", tmp, sizeof(tmp)); // BASE + WebGetArg(PSTR("g99"), tmp, sizeof(tmp)); // BASE uint32_t base = atoi(tmp) +1; snprintf_P(svalue, sizeof(svalue), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), svalue, flag, base); @@ -1505,7 +1506,7 @@ void HandleModuleConfiguration(void) { if (!HttpCheckPriviledgedAccess()) { return; } - if (Webserver->hasArg("save")) { + if (Webserver->hasArg(F("save"))) { ModuleSaveSettings(); WebRestart(1); return; @@ -1572,7 +1573,7 @@ void ModuleSaveSettings(void) { char tmp[8]; // WebGetArg numbers only - WebGetArg("g99", tmp, sizeof(tmp)); + WebGetArg(PSTR("g99"), tmp, sizeof(tmp)); uint32_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); Settings.last_module = Settings.module; Settings.module = new_module; @@ -1624,7 +1625,7 @@ void HandleWifiConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_WIFI)); - if (Webserver->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) { + if (Webserver->hasArg(F("save")) && HTTP_MANAGER_RESET_ONLY != Web.state) { WifiSaveSettings(); WebRestart(2); return; @@ -1639,7 +1640,7 @@ void HandleWifiConfiguration(void) #endif // USE_ENHANCED_GUI_WIFI_SCAN if (HTTP_MANAGER_RESET_ONLY != Web.state) { - if (Webserver->hasArg("scan")) { + if (Webserver->hasArg(F("scan"))) { #ifdef USE_EMULATION UdpDisconnect(); #endif // USE_EMULATION @@ -1776,20 +1777,20 @@ void WifiSaveSettings(void) { char tmp[TOPSZ]; // Max length is currently 150 - WebGetArg("h", tmp, sizeof(tmp)); + WebGetArg(PSTR("h"), tmp, sizeof(tmp)); SettingsUpdateText(SET_HOSTNAME, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp); if (strchr(SettingsText(SET_HOSTNAME), '%') != nullptr) { SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); } - WebGetArg("c", tmp, sizeof(tmp)); + WebGetArg(PSTR("c"), tmp, sizeof(tmp)); SettingsUpdateText(SET_CORS, (!strlen(tmp)) ? CORS_DOMAIN : tmp); - WebGetArg("s1", tmp, sizeof(tmp)); + WebGetArg(PSTR("s1"), tmp, sizeof(tmp)); SettingsUpdateText(SET_STASSID1, (!strlen(tmp)) ? STA_SSID1 : tmp); - WebGetArg("s2", tmp, sizeof(tmp)); + WebGetArg(PSTR("s2"), tmp, sizeof(tmp)); SettingsUpdateText(SET_STASSID2, (!strlen(tmp)) ? STA_SSID2 : tmp); - WebGetArg("p1", tmp, sizeof(tmp)); + WebGetArg(PSTR("p1"), tmp, sizeof(tmp)); SettingsUpdateText(SET_STAPWD1, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD1) : tmp); - WebGetArg("p2", tmp, sizeof(tmp)); + WebGetArg(PSTR("p2"), tmp, sizeof(tmp)); SettingsUpdateText(SET_STAPWD2, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD2) : tmp); AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"), SettingsText(SET_HOSTNAME), SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), SettingsText(SET_CORS)); @@ -1839,19 +1840,19 @@ void LoggingSaveSettings(void) { char tmp[TOPSZ]; // Max length is currently 33 - WebGetArg("l0", tmp, sizeof(tmp)); + WebGetArg(PSTR("l0"), tmp, sizeof(tmp)); SetSeriallog((!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp)); - WebGetArg("l1", tmp, sizeof(tmp)); + WebGetArg(PSTR("l1"), tmp, sizeof(tmp)); Settings.weblog_level = (!strlen(tmp)) ? WEB_LOG_LEVEL : atoi(tmp); - WebGetArg("l2", tmp, sizeof(tmp)); + WebGetArg(PSTR("l2"), tmp, sizeof(tmp)); Settings.mqttlog_level = (!strlen(tmp)) ? MQTT_LOG_LEVEL : atoi(tmp); - WebGetArg("l3", tmp, sizeof(tmp)); + WebGetArg(PSTR("l3"), tmp, sizeof(tmp)); SetSyslog((!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp)); - WebGetArg("lh", tmp, sizeof(tmp)); + WebGetArg(PSTR("lh"), tmp, sizeof(tmp)); SettingsUpdateText(SET_SYSLOG_HOST, (!strlen(tmp)) ? SYS_LOG_HOST : tmp); - WebGetArg("lp", tmp, sizeof(tmp)); + WebGetArg(PSTR("lp"), tmp, sizeof(tmp)); Settings.syslog_port = (!strlen(tmp)) ? SYS_LOG_PORT : atoi(tmp); - WebGetArg("lt", tmp, sizeof(tmp)); + WebGetArg(PSTR("lt"), tmp, sizeof(tmp)); Settings.tele_period = (!strlen(tmp)) ? TELE_PERIOD : atoi(tmp); if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) { Settings.tele_period = 10; // Do not allow periods < 10 seconds @@ -1868,7 +1869,7 @@ void HandleOtherConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_OTHER)); - if (Webserver->hasArg("save")) { + if (Webserver->hasArg(F("save"))) { OtherSaveSettings(); WebRestart(1); return; @@ -1932,15 +1933,15 @@ void OtherSaveSettings(void) char friendlyname[TOPSZ]; char message[MAX_LOGSZ]; - WebGetArg("dn", tmp, sizeof(tmp)); + WebGetArg(PSTR("dn"), tmp, sizeof(tmp)); SettingsUpdateText(SET_DEVICENAME, (!strlen(tmp)) ? "" : (!strcmp(tmp,"1")) ? SettingsText(SET_FRIENDLYNAME1) : tmp); - WebGetArg("wp", tmp, sizeof(tmp)); + WebGetArg(PSTR("wp"), tmp, sizeof(tmp)); SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp); - Settings.flag.mqtt_enabled = Webserver->hasArg("b1"); // SetOption3 - Enable MQTT + Settings.flag.mqtt_enabled = Webserver->hasArg(F("b1")); // SetOption3 - Enable MQTT #ifdef USE_EMULATION UdpDisconnect(); #if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE) - WebGetArg("b2", tmp, sizeof(tmp)); + WebGetArg(PSTR("b2"), tmp, sizeof(tmp)); Settings.flag2.emulation = (!strlen(tmp)) ? 0 : atoi(tmp); #endif // USE_EMULATION_WEMO || USE_EMULATION_HUE #endif // USE_EMULATION @@ -1951,14 +1952,14 @@ void OtherSaveSettings(void) snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i); WebGetArg(webindex, tmp, sizeof(tmp)); snprintf_P(friendlyname, sizeof(friendlyname), PSTR(FRIENDLY_NAME"%d"), i +1); - SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp); + SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : PSTR(FRIENDLY_NAME) : tmp); snprintf_P(message, sizeof(message), PSTR("%s%s %s"), message, (i) ? "," : "", SettingsText(SET_FRIENDLYNAME1 +i)); } AddLogData(LOG_LEVEL_INFO, message); - WebGetArg("t1", tmp, sizeof(tmp)); + WebGetArg(PSTR("t1"), tmp, sizeof(tmp)); if (strlen(tmp)) { // {"NAME":"12345678901234","GPIO":[255,255,255,255,255,255,255,255,255,255,255,255,255],"FLAG":255,"BASE":255} - snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (Webserver->hasArg("t2")) ? "; " D_CMND_MODULE " 0" : ""); + snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (Webserver->hasArg(F("t2"))) ? PSTR("; " D_CMND_MODULE " 0") : ""); ExecuteWebCommand(message, SRC_WEBGUI); } } @@ -2034,7 +2035,7 @@ void HandleRestoreConfiguration(void) WSContentStart_P(PSTR(D_RESTORE_CONFIGURATION)); WSContentSendStyle(); WSContentSend_P(HTTP_FORM_RST); - WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE); + WSContentSend_P(HTTP_FORM_RST_UPG, PSTR(D_RESTORE)); if (WifiIsInManagerMode()) { WSContentSpaceButton(BUTTON_MAIN); } else { @@ -2249,7 +2250,7 @@ void HandleUpgradeFirmware(void) { WSContentStart_P(PSTR(D_FIRMWARE_UPGRADE)); WSContentSendStyle(); WSContentSend_P(HTTP_FORM_UPG, SettingsText(SET_OTAURL)); - WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE); + WSContentSend_P(HTTP_FORM_RST_UPG, PSTR(D_UPGRADE)); WSContentSpaceButton(BUTTON_MAIN); WSContentStop(); @@ -2265,7 +2266,7 @@ void HandleUpgradeFirmwareStart(void) { WifiConfigCounter(); char otaurl[TOPSZ]; - WebGetArg("o", otaurl, sizeof(otaurl)); + WebGetArg(PSTR("o"), otaurl, sizeof(otaurl)); if (strlen(otaurl)) { snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl); ExecuteWebCommand(command, SRC_WEBGUI); @@ -2668,9 +2669,9 @@ void HandleHttpCommand(void) if (!WebAuthenticate()) { // Prefer authorization via HTTP header (Basic auth), if it fails, use legacy method via GET parameters char tmp1[33]; - WebGetArg("user", tmp1, sizeof(tmp1)); + WebGetArg(PSTR("user"), tmp1, sizeof(tmp1)); char tmp2[strlen(SettingsText(SET_WEBPWD)) + 1]; - WebGetArg("password", tmp2, sizeof(tmp2)); + WebGetArg(PSTR("password"), tmp2, sizeof(tmp2)); if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) { WSContentBegin(401, CT_JSON); @@ -2681,7 +2682,7 @@ void HandleHttpCommand(void) } WSContentBegin(200, CT_JSON); - String svalue = Webserver->arg("cmnd"); + String svalue = Webserver->arg(F("cmnd")); if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { uint32_t curridx = TasmotaGlobal.log_buffer_pointer; TasmotaGlobal.templog_level = LOG_LEVEL_INFO; @@ -2717,7 +2718,7 @@ void HandleConsole(void) { if (!HttpCheckPriviledgedAccess()) { return; } - if (Webserver->hasArg("c2")) { // Console refresh requested + if (Webserver->hasArg(F("c2"))) { // Console refresh requested HandleConsoleRefresh(); return; } @@ -2734,14 +2735,14 @@ void HandleConsole(void) void HandleConsoleRefresh(void) { - String svalue = Webserver->arg("c1"); + String svalue = Webserver->arg(F("c1")); if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str()); ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE); } char stmp[8]; - WebGetArg("c2", stmp, sizeof(stmp)); + WebGetArg(PSTR("c2"), stmp, sizeof(stmp)); uint32_t index = 0; // Initial start, dump all if (strlen(stmp)) { index = atoi(stmp); } @@ -2758,7 +2759,7 @@ void HandleConsoleRefresh(void) if (len > sizeof(TasmotaGlobal.mqtt_data) -2) { len = sizeof(TasmotaGlobal.mqtt_data); } char stemp[len +1]; strlcpy(stemp, line, len); - WSContentSend_P(PSTR("%s%s"), (cflg) ? "\n" : "", stemp); + WSContentSend_P(PSTR("%s%s"), (cflg) ? PSTR("\n") : "", stemp); cflg = true; } WSContentSend_P(PSTR("}1")); @@ -2776,14 +2777,14 @@ void HandleNotFound(void) #ifdef USE_EMULATION #ifdef USE_EMULATION_HUE String path = Webserver->uri(); - if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) { + if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith(F("/api")))) { HandleHueApi(&path); } else #endif // USE_EMULATION_HUE #endif // USE_EMULATION { WSContentBegin(404, CT_PLAIN); - WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), Webserver->uri().c_str(), (Webserver->method() == HTTP_GET) ? "GET" : "POST", Webserver->args()); + WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), Webserver->uri().c_str(), (Webserver->method() == HTTP_GET) ? PSTR("GET") : PSTR("POST"), Webserver->args()); for (uint32_t i = 0; i < Webserver->args(); i++) { WSContentSend_P(PSTR(" %s: %s\n"), Webserver->argName(i).c_str(), Webserver->arg(i).c_str()); } @@ -2978,7 +2979,7 @@ void CmndWebServer(void) } if (Settings.webserver) { Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str()); + (2 == Settings.webserver) ? PSTR(D_ADMIN) : PSTR(D_USER), NetworkHostname(), NetworkAddress().toString().c_str()); } else { ResponseCmndStateText(0); } diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index 6324a584b..bf4a9e6f9 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -156,7 +156,7 @@ void MqttInit(void) { // Detect AWS IoT and set default parameters String host = String(SettingsText(SET_MQTT_HOST)); - if (host.indexOf(".iot.") && host.endsWith(".amazonaws.com")) { // look for ".iot." and ".amazonaws.com" in the domain name + if (host.indexOf(F(".iot.")) && host.endsWith(F(".amazonaws.com"))) { // look for ".iot." and ".amazonaws.com" in the domain name Settings.flag4.mqtt_no_retain = true; } @@ -527,10 +527,10 @@ void MqttConnected(void) { if (Settings.webserver) { #if LWIP_IPV6 Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str(), WifiGetIPv6().c_str()); + (2 == Settings.webserver) ? PSTR(D_ADMIN) : PSTR(D_USER), NetworkHostname(), NetworkAddress().toString().c_str(), WifiGetIPv6().c_str()); #else Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str()); + (2 == Settings.webserver) ? PSTR(D_ADMIN) : PSTR(D_USER), NetworkHostname(), NetworkAddress().toString().c_str()); #endif // LWIP_IPV6 = 1 MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); } @@ -787,7 +787,7 @@ void CmndMqttFingerprint(void) { void CmndMqttUser(void) { if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data); + SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_USER) : XdrvMailbox.data); TasmotaGlobal.restart_flag = 2; } ResponseCmndChar(SettingsText(SET_MQTT_USER)); @@ -795,7 +795,7 @@ void CmndMqttUser(void) { void CmndMqttPassword(void) { if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data); + SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_PASS) : XdrvMailbox.data); ResponseCmndChar(SettingsText(SET_MQTT_PWD)); TasmotaGlobal.restart_flag = 2; } else { @@ -852,7 +852,7 @@ void CmndStateText(void) { void CmndMqttClient(void) { if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data); + SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_CLIENT_ID) : XdrvMailbox.data); TasmotaGlobal.restart_flag = 2; } ResponseCmndChar(SettingsText(SET_MQTT_CLIENT)); @@ -882,7 +882,7 @@ void CmndPrefix(void) { if (XdrvMailbox.data_len > 0) { MakeValidMqtt(0, XdrvMailbox.data); SettingsUpdateText(SET_MQTTPREFIX1 + XdrvMailbox.index -1, - (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? SUB_PREFIX : (2==XdrvMailbox.index) ? PUB_PREFIX : PUB_PREFIX2 : XdrvMailbox.data); + (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? PSTR(SUB_PREFIX) : (2==XdrvMailbox.index) ? PSTR(PUB_PREFIX) : PSTR(PUB_PREFIX2) : XdrvMailbox.data); TasmotaGlobal.restart_flag = 2; } ResponseCmndIdxChar(SettingsText(SET_MQTTPREFIX1 + XdrvMailbox.index -1)); @@ -918,7 +918,7 @@ void CmndGroupTopic(void) { uint32_t settings_text_index = (1 == XdrvMailbox.index) ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2; MakeValidMqtt(0, XdrvMailbox.data); if (!strcmp(XdrvMailbox.data, TasmotaGlobal.mqtt_client)) { SetShortcutDefault(); } - SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); + SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_GRPTOPIC) : XdrvMailbox.data); // Eliminate duplicates, have at least one and fill from index 1 char stemp[MAX_GROUP_TOPICS][TOPSZ]; @@ -940,7 +940,7 @@ void CmndGroupTopic(void) { } } if (0 == read_index) { - SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); + SettingsUpdateText(SET_MQTT_GRP_TOPIC, PSTR(MQTT_GRPTOPIC)); } else { uint32_t write_index = 0; uint32_t real_index = SET_MQTT_GRP_TOPIC; @@ -998,7 +998,7 @@ void CmndSwitchTopic(void) { switch (Shortcut()) { case SC_CLEAR: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, ""); break; case SC_DEFAULT: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, TasmotaGlobal.mqtt_topic); break; - case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); break; + case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, PSTR(MQTT_SWITCH_TOPIC)); break; default: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, XdrvMailbox.data); } } @@ -1266,7 +1266,7 @@ void HandleMqttConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_MQTT)); - if (Webserver->hasArg("save")) { + if (Webserver->hasArg(F("save"))) { MqttSaveSettings(); WebRestart(1); return; @@ -1282,11 +1282,11 @@ void HandleMqttConfiguration(void) #ifdef USE_MQTT_TLS Mqtt.mqtt_tls ? PSTR(" checked") : "", // SetOption102 - Enable MQTT TLS #endif // USE_MQTT_TLS - Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT)); + Format(str, PSTR(MQTT_CLIENT_ID), sizeof(str)), PSTR(MQTT_CLIENT_ID), SettingsText(SET_MQTT_CLIENT)); WSContentSend_P(HTTP_FORM_MQTT2, (!strlen(SettingsText(SET_MQTT_USER))) ? "0" : SettingsText(SET_MQTT_USER), - Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, SettingsText(SET_MQTT_TOPIC), - MQTT_FULLTOPIC, MQTT_FULLTOPIC, SettingsText(SET_MQTT_FULLTOPIC)); + Format(str, PSTR(MQTT_TOPIC), sizeof(str)), PSTR(MQTT_TOPIC), SettingsText(SET_MQTT_TOPIC), + PSTR(MQTT_FULLTOPIC), PSTR(MQTT_FULLTOPIC), SettingsText(SET_MQTT_FULLTOPIC)); WSContentSend_P(HTTP_FORM_END); WSContentSpaceButton(BUTTON_CONFIGURATION); WSContentStop(); @@ -1298,10 +1298,10 @@ void MqttSaveSettings(void) char stemp[TOPSZ]; char stemp2[TOPSZ]; - WebGetArg("mt", tmp, sizeof(tmp)); + WebGetArg(PSTR("mt"), tmp, sizeof(tmp)); strlcpy(stemp, (!strlen(tmp)) ? MQTT_TOPIC : tmp, sizeof(stemp)); MakeValidMqtt(0, stemp); - WebGetArg("mf", tmp, sizeof(tmp)); + WebGetArg(PSTR("mf"), tmp, sizeof(tmp)); strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2)); MakeValidMqtt(1, stemp2); if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) { @@ -1310,18 +1310,18 @@ void MqttSaveSettings(void) } SettingsUpdateText(SET_MQTT_TOPIC, stemp); SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp2); - WebGetArg("mh", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp); - WebGetArg("ml", tmp, sizeof(tmp)); + WebGetArg(PSTR("mh"), tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? PSTR(MQTT_HOST) : (!strcmp(tmp,"0")) ? "" : tmp); + WebGetArg(PSTR("ml"), tmp, sizeof(tmp)); Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp); #ifdef USE_MQTT_TLS - Settings.flag4.mqtt_tls = Webserver->hasArg("b3"); // SetOption102 - Enable MQTT TLS + Settings.flag4.mqtt_tls = Webserver->hasArg(F("b3")); // SetOption102 - Enable MQTT TLS #endif - WebGetArg("mc", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp); - WebGetArg("mu", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp); - WebGetArg("mp", tmp, sizeof(tmp)); + WebGetArg(PSTR("mc"), tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? PSTR(MQTT_CLIENT_ID) : tmp); + WebGetArg(PSTR("mu"), tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? PSTR(MQTT_USER) : (!strcmp(tmp,"0")) ? "" : tmp); + WebGetArg(PSTR("mp"), tmp, sizeof(tmp)); SettingsUpdateText(SET_MQTT_PWD, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? SettingsText(SET_MQTT_PWD) : tmp); AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_USER), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index 90d94ffb7..7e0907dd1 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -2625,7 +2625,7 @@ void CmndWakeup(void) Light.wakeup_active = 3; Settings.light_scheme = LS_WAKEUP; LightPowerOn(); - ResponseCmndChar(D_JSON_STARTED); + ResponseCmndChar(PSTR(D_JSON_STARTED)); } void CmndColorTemperature(void) diff --git a/tasmota/xdrv_07_domoticz.ino b/tasmota/xdrv_07_domoticz.ino index 82b769963..b982a58b1 100644 --- a/tasmota/xdrv_07_domoticz.ino +++ b/tasmota/xdrv_07_domoticz.ino @@ -546,7 +546,7 @@ void HandleDomoticzConfiguration(void) { AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_DOMOTICZ)); - if (Webserver->hasArg("save")) { + if (Webserver->hasArg(F("save"))) { DomoticzSaveSettings(); WebRestart(1); return; @@ -605,7 +605,7 @@ void DomoticzSaveSettings(void) { Settings.domoticz_sensor_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]); } - WebGetArg("ut", tmp, sizeof(tmp)); + WebGetArg(PSTR("ut"), tmp, sizeof(tmp)); Settings.domoticz_update_timer = (!strlen(tmp)) ? DOMOTICZ_UPDATE_TIMER : atoi(tmp); AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"), diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/xdrv_09_timers.ino index 0f6292e13..a33204906 100644 --- a/tasmota/xdrv_09_timers.ino +++ b/tasmota/xdrv_09_timers.ino @@ -841,7 +841,7 @@ void HandleTimerConfiguration(void) AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_TIMER)); - if (Webserver->hasArg("save")) { + if (Webserver->hasArg(F("save"))) { TimerSaveSettings(); HandleConfiguration(); return; @@ -883,8 +883,8 @@ void TimerSaveSettings(void) char message[32 + (MAX_TIMERS *11)]; // MQT: Timers 0,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000 Timer timer; - Settings.flag3.timers_enable = Webserver->hasArg("e0"); // CMND_TIMERS - WebGetArg("t0", tmp, sizeof(tmp)); + Settings.flag3.timers_enable = Webserver->hasArg(F("e0")); // CMND_TIMERS + WebGetArg(PSTR("t0"), tmp, sizeof(tmp)); char *p = tmp; snprintf_P(message, sizeof(message), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable); // CMND_TIMERS for (uint32_t i = 0; i < MAX_TIMERS; i++) { diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index 637a3b963..31b8e9f46 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -416,7 +416,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all // Step1: Analyse rule String rule_expr = rule; // "TELE-INA219#CURRENT>0.100" if (Rules.teleperiod) { - int ppos = rule_expr.indexOf("TELE-"); // "TELE-INA219#CURRENT>0.100" or "INA219#CURRENT>0.100" + int ppos = rule_expr.indexOf(F("TELE-")); // "TELE-INA219#CURRENT>0.100" or "INA219#CURRENT>0.100" if (ppos == -1) { return false; } // No pre-amble in rule rule_expr = rule.substring(5); // "INA219#CURRENT>0.100" or "SYSTEM#BOOT" } @@ -494,7 +494,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all // Step2: Search rule_name int pos; int rule_name_idx = 0; - if ((pos = rule_name.indexOf("[")) > 0) { // "SUBTYPE1#CURRENT[1]" + if ((pos = rule_name.indexOf(F("["))) > 0) { // "SUBTYPE1#CURRENT[1]" rule_name_idx = rule_name.substring(pos +1).toInt(); if ((rule_name_idx < 1) || (rule_name_idx > 6)) { // Allow indexes 1 to 6 rule_name_idx = 1; @@ -515,7 +515,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all } String subtype; uint32_t i = 0; - while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT" + while ((pos = rule_name.indexOf(F("#"))) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT" subtype = rule_name.substring(0, pos); obj = obj[subtype.c_str()].getObject(); if (!obj) { return false; } // not found @@ -686,14 +686,14 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) String rule = rules; rule.toUpperCase(); // "ON INA219#CURRENT>0.100 DO BACKLOG DIMMER 10;COLOR 100000 ENDON" - if (!rule.startsWith("ON ")) { return serviced; } // Bad syntax - Nothing to start on + if (!rule.startsWith(F("ON "))) { return serviced; } // Bad syntax - Nothing to start on - int pevt = rule.indexOf(" DO "); + int pevt = rule.indexOf(F(" DO ")); if (pevt == -1) { return serviced; } // Bad syntax - Nothing to do String event_trigger = rule.substring(3, pevt); // "INA219#CURRENT>0.100" - plen = rule.indexOf(" ENDON"); - plen2 = rule.indexOf(" BREAK"); + plen = rule.indexOf(F(" ENDON")); + plen2 = rule.indexOf(F(" BREAK")); if ((plen == -1) && (plen2 == -1)) { return serviced; } // Bad syntax - No ENDON neither BREAK if (plen == -1) { plen = 9999; } @@ -717,10 +717,10 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) // if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race exception // Use Backlog with event to prevent rule event loop exception unless IF is used which uses an implicit backlog - if ((ucommand.indexOf("IF ") == -1) && - (ucommand.indexOf("EVENT ") != -1) && - (ucommand.indexOf("BACKLOG ") == -1)) { - commands = "backlog " + commands; + if ((ucommand.indexOf(F("IF ")) == -1) && + (ucommand.indexOf(F("EVENT ")) != -1) && + (ucommand.indexOf(F("BACKLOG ")) == -1)) { + commands = F("backlog ") + commands; } RulesVarReplace(commands, F("%VALUE%"), Rules.event_value); diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index bf4664886..0120615e0 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -294,7 +294,7 @@ void NewHAssDiscovery(void) for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) { char fname[TOPSZ]; snprintf_P(fname, sizeof(fname), PSTR("\"%s\""), EscapeJSONString(SettingsText(SET_FRIENDLYNAME1 +i)).c_str()); - snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%s"), stemp2, (i > 0 ? "," : ""), (i < maxfn) ? fname : "null"); + snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%s"), stemp2, (i > 0 ? "," : ""), (i < maxfn) ? fname : PSTR("null")); } stemp3[0] = '\0'; @@ -304,7 +304,7 @@ void NewHAssDiscovery(void) char sname[TOPSZ]; snprintf_P(sname, sizeof(sname), PSTR("\"%s\""), GetSwitchText(i).c_str()); snprintf_P(stemp3, sizeof(stemp3), PSTR("%s%s%d"), stemp3, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? Settings.switchmode[i] : -1); - snprintf_P(stemp4, sizeof(stemp4), PSTR("%s%s%s"), stemp4, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? sname : "null"); + snprintf_P(stemp4, sizeof(stemp4), PSTR("%s%s%s"), stemp4, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? sname : PSTR("null")); } stemp5[0] = '\0'; @@ -338,7 +338,7 @@ void NewHAssDiscovery(void) if (!Settings.flag.hass_discovery) { // HassDiscoveryRelays(relays) Response_P(HASS_DISCOVER_DEVICE, WiFi.localIP().toString().c_str(), SettingsText(SET_DEVICENAME), stemp2, TasmotaGlobal.hostname, unique_id, ModuleName().c_str(), TuyaMod, iFanMod, GetStateText(0), GetStateText(1), GetStateText(2), GetStateText(3), - TasmotaGlobal.version, TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_FULLTOPIC), SUB_PREFIX, PUB_PREFIX, PUB_PREFIX2, Hass.RelLst, stemp3, stemp4, + TasmotaGlobal.version, TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_FULLTOPIC), PSTR(SUB_PREFIX), PSTR(PUB_PREFIX), PSTR(PUB_PREFIX2), Hass.RelLst, stemp3, stemp4, stemp5, Settings.flag.mqtt_response, Settings.flag.button_swap, Settings.flag.button_single, Settings.flag.decimal_text, Settings.flag.not_power_linked, Settings.flag.hass_light, Settings.flag3.pwm_multi_channels, Settings.flag3.mqtt_buttons, Settings.flag4.alexa_ct_range, Settings.flag5.mqtt_switches, Settings.flag5.fade_fixed_duration, light_controller.isCTRGBLinked(), Light.subtype, stemp6); diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino index a291d2e11..7535d29c5 100644 --- a/tasmota/xdrv_16_tuyamcu.ino +++ b/tasmota/xdrv_16_tuyamcu.ino @@ -1295,7 +1295,7 @@ void TuyaSensorsShow(bool json) GetTextIndexed(sname, sizeof(sname), (sensor-71), kTuyaSensors); ResponseAppend_P(PSTR("\"%s\":%s"), sname, - (Tuya.SensorsValid[sensor-71] ? dtostrfd(Tuya.Sensors[sensor-71], res, tempval) : "null")); + (Tuya.SensorsValid[sensor-71] ? dtostrfd(Tuya.Sensors[sensor-71], res, tempval) : PSTR("null"))); added = true; } #ifdef USE_WEBSERVER diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index 4da625847..013e22a98 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -429,9 +429,9 @@ void HandleUpnpSetupHue(void) { AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_HUE_BRIDGE_SETUP)); String description_xml = Decompress(HUE_DESCRIPTION_XML_COMPRESSED,HUE_DESCRIPTION_XML_SIZE); - description_xml.replace("{x1", WiFi.localIP().toString()); - description_xml.replace("{x2", HueUuid()); - description_xml.replace("{x3", HueSerialnumber()); + description_xml.replace(F("{x1"), WiFi.localIP().toString()); + description_xml.replace(F("{x2"), HueUuid()); + description_xml.replace(F("{x3"), HueSerialnumber()); WSSend(200, CT_XML, description_xml); } @@ -439,19 +439,19 @@ void HueNotImplemented(String *path) { AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str()); - WSSend(200, CT_JSON, "{}"); + WSSend(200, CT_JSON, PSTR("{}")); } void HueConfigResponse(String *response) { *response += Decompress(HueConfigResponse_JSON, HueConfigResponse_JSON_SIZE); - response->replace("{ma", WiFi.macAddress()); - response->replace("{ip", WiFi.localIP().toString()); - response->replace("{ms", WiFi.subnetMask().toString()); - response->replace("{gw", WiFi.gatewayIP().toString()); - response->replace("{br", HueBridgeId()); - response->replace("{dt", GetDateAndTime(DT_UTC)); - response->replace("{id", GetHueUserId()); + response->replace(F("{ma"), WiFi.macAddress()); + response->replace(F("{ip"), WiFi.localIP().toString()); + response->replace(F("{ms"), WiFi.subnetMask().toString()); + response->replace(F("{gw"), WiFi.gatewayIP().toString()); + response->replace(F("{br"), HueBridgeId()); + response->replace(F("{dt"), GetDateAndTime(DT_UTC)); + response->replace(F("{id"), GetHueUserId()); } void HueConfig(String *path) @@ -534,13 +534,13 @@ void HueLightStatus1(uint8_t device, String *response) char * buf = (char*) malloc(buf_size); // temp buffer for strings, avoid stack UnishoxStrings msg(HUE_LIGHTS); - snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), (TasmotaGlobal.power & (1 << (device-1))) ? "true" : "false"); + snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), (TasmotaGlobal.power & (1 << (device-1))) ? PSTR("true") : PSTR("false")); // Brightness for all devices with PWM if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { // force dimmer for 1st gen Echo snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri); } if (LST_COLDWARM <= local_light_subtype) { - snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, g_gotct ? "ct" : "hs"); + snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, g_gotct ? PSTR("ct") : PSTR("hs")); } if (LST_RGB <= local_light_subtype) { // colors if (prev_x_str[0] && prev_y_str[0]) { @@ -680,7 +680,7 @@ void HueGlobalConfig(String *path) { #endif // USE_ZIGBEE response += F("},\"groups\":{},\"schedules\":{},\"config\":"); HueConfigResponse(&response); - response += "}"; + response += F("}"); WSSend(200, CT_JSON, response); } @@ -700,7 +700,7 @@ void CheckHue(String * response, bool &appending) { for (uint32_t i = 1; i <= maxhue; i++) { if (HueActive(i)) { if (appending) { *response += ","; } - *response += "\""; + *response += F("\""); *response += EncodeLightId(i); *response += F("\":{\"state\":"); HueLightStatus1(i, response); @@ -735,7 +735,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { on = hue_on.getBool(); snprintf_P(buf, buf_size, msg[HUE_RESP_ON], - device_id, on ? "true" : "false"); + device_id, on ? PSTR("true") : PSTR("false")); #ifdef USE_SHUTTER if (ShutterState(device)) { @@ -778,7 +778,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { if (resp) { response += ","; } snprintf_P(buf, buf_size, msg[HUE_RESP_NUM], - device_id, "bri", bri); + device_id, PSTR("bri"), bri); response += buf; if (LST_SINGLE <= Light.subtype) { // extend bri value if set to max @@ -824,7 +824,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { if (resp) { response += ","; } snprintf_P(buf, buf_size, msg[HUE_RESP_NUM], - device_id, "hue", hue); + device_id, PSTR("hue"), hue); response += buf; if (LST_RGB <= Light.subtype) { // change range from 0..65535 to 0..360 @@ -843,7 +843,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { if (resp) { response += ","; } snprintf_P(buf, buf_size, msg[HUE_RESP_NUM], - device_id, "sat", sat); + device_id, PSTR("sat"), sat); response += buf; if (LST_RGB <= Light.subtype) { // extend sat value if set to max @@ -862,7 +862,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { if (resp) { response += ","; } snprintf_P(buf, buf_size, msg[HUE_RESP_NUM], - device_id, "ct", ct); + device_id, PSTR("ct"), ct); response += buf; if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { g_gotct = true; @@ -923,7 +923,7 @@ void HueLights(String *path) path->remove(0,path->indexOf(F("/lights"))); // Remove until /lights if (path->endsWith(F("/lights"))) { // Got /lights - response = "{"; + response = F("{"); bool appending = false; #ifdef USE_LIGHT CheckHue(&response, appending); @@ -934,7 +934,7 @@ void HueLights(String *path) #ifdef USE_SCRIPT_HUE Script_Check_Hue(&response); #endif - response += "}"; + response += F("}"); } else if (path->endsWith(F("/state"))) { // Got ID/state path->remove(0,8); // Remove /lights/ @@ -992,7 +992,7 @@ void HueLights(String *path) #endif // USE_LIGHT } else { - response = "{}"; + response = F("{}"); code = 406; } exit: @@ -1005,24 +1005,24 @@ void HueGroups(String *path) /* * http://tasmota/api/username/groups?1={"name":"Woonkamer","lights":[],"type":"Room","class":"Living room"}) */ - String response = "{}"; + String response(F("{}")); uint8_t maxhue = (TasmotaGlobal.devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : TasmotaGlobal.devices_present; //AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " HueGroups (%s)"), path->c_str()); - if (path->endsWith("/0")) { + if (path->endsWith(F("/0"))) { UnishoxStrings msg(HUE_LIGHTS); response = msg[HUE_GROUP0_STATUS_JSON]; String lights = F("\"1\""); for (uint32_t i = 2; i <= maxhue; i++) { - lights += ",\""; + lights += F(",\""); lights += EncodeLightId(i); - lights += "\""; + lights += F("\""); } #ifdef USE_ZIGBEE ZigbeeHueGroups(&response); #endif // USE_ZIGBEE - response.replace("{l1", lights); + response.replace(F("{l1"), lights); #ifdef USE_LIGHT HueLightStatus1(1, &response); #endif // USE_LIGHT diff --git a/tasmota/xdrv_23_zigbee_1z_libs.ino b/tasmota/xdrv_23_zigbee_1z_libs.ino index 4e018a21e..a228798e2 100644 --- a/tasmota/xdrv_23_zigbee_1z_libs.ino +++ b/tasmota/xdrv_23_zigbee_1z_libs.ino @@ -579,7 +579,7 @@ String Z_attribute::toString(bool prefix_comma) const { // value part switch (type) { case Za_type::Za_none: - res += "null"; + res += F("null"); break; case Za_type::Za_bool: res += val.uval32 ? F("true") : F("false"); @@ -638,7 +638,9 @@ String Z_attribute::toString(bool prefix_comma) const { if (val.arrval) { res += val.arrval->toString(); } else { - res += "[]"; + // res += '['; + // res += ']'; + res += F("[]"); } break; } diff --git a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino index 7d850ef5a..55a98a748 100644 --- a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino +++ b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino @@ -556,7 +556,7 @@ void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_l if (Settings.flag5.zb_received_as_subtopic) GetTopic_P(stopic, TELE, subtopic, json_prefix); else - GetTopic_P(stopic, TELE, subtopic, D_RSLT_SENSOR); + GetTopic_P(stopic, TELE, subtopic, PSTR(D_RSLT_SENSOR)); MqttPublish(stopic, Settings.flag.mqtt_sensor_retain); } else { MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index a77c8b67f..d71a8c3a9 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -62,13 +62,13 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri const size_t buf_size = 256; char * buf = (char*) malloc(buf_size); // temp buffer for strings, avoid stack - snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), power ? "true" : "false"); + snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), power ? PSTR("true") : PSTR("false")); // Brightness for all devices with PWM if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { // force dimmer for 1st gen Echo snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri); } if (LST_COLDWARM <= local_light_subtype) { - snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, (0 == colormode) ? "hs" : (1 == colormode) ? "xy" : "ct"); + snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, (0 == colormode) ? PSTR("hs") : (1 == colormode) ? PSTR("xy") : PSTR("ct")); } if (LST_RGB <= local_light_subtype) { // colors if (prev_x_str[0] && prev_y_str[0]) { @@ -83,7 +83,7 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { // white temp snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284); } - snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE, buf, reachable ? "true" : "false"); + snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE, buf, reachable ? PSTR("true") : PSTR("false")); *response += buf; free(buf); @@ -233,7 +233,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { on = hue_on.getBool(); snprintf_P(buf, buf_size, msg[HUE_RESP_ON], - device_id, on ? "true" : "false"); + device_id, on ? PSTR("true") : PSTR("false")); if (on) { ZigbeeHuePower(shortaddr, 0x01); @@ -252,7 +252,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { if (resp) { response += ","; } snprintf_P(buf, buf_size, msg[HUE_RESP_NUM], - device_id, "bri", bri); + device_id, PSTR("bri"), bri); response += buf; if (LST_SINGLE <= bulbtype) { // extend bri value if set to max @@ -293,7 +293,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { if (resp) { response += ","; } snprintf_P(buf, buf_size, msg[HUE_RESP_NUM], - device_id, "hue", hue); + device_id, PSTR("hue"), hue); response += buf; if (LST_RGB <= bulbtype) { // change range from 0..65535 to 0..360 @@ -311,7 +311,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { if (resp) { response += ","; } snprintf_P(buf, buf_size, msg[HUE_RESP_NUM], - device_id, "sat", sat); + device_id, PSTR("sat"), sat); response += buf; if (LST_RGB <= bulbtype) { // extend sat value if set to max @@ -332,7 +332,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { if (resp) { response += ","; } snprintf_P(buf, buf_size, msg[HUE_RESP_NUM], - device_id, "ct", ct); + device_id, PSTR("ct"), ct); response += buf; if ((LST_COLDWARM == bulbtype) || (LST_RGBW <= bulbtype)) { ZigbeeHueCT(shortaddr, ct); diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index ec9a454ac..673f1ff8d 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -2092,7 +2092,7 @@ void ZigbeeMapRefresh(void) { if ((!zigbee.init_phase) && (!zigbee.mapping_in_progress)) { ZigbeeMapAllDevices(); } - Webserver->sendHeader("Location","/zbm"); // Add a header to respond with a new location for the browser to go to the home page again + Webserver->sendHeader(F("Location"),F("/zbm")); // Add a header to respond with a new location for the browser to go to the home page again Webserver->send(302); } diff --git a/tasmota/xdrv_38_ping.ino b/tasmota/xdrv_38_ping.ino index 3f87a54bb..0974697fe 100644 --- a/tasmota/xdrv_38_ping.ino +++ b/tasmota/xdrv_38_ping.ino @@ -303,7 +303,7 @@ void PingResponsePoll(void) { ",\"AvgTime\":%d" "}}}"), ping->hostname.c_str(), - success ? "true" : "false", + success ? PSTR("true") : PSTR("false"), ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, ip >> 24, success, ping->timeout_count, diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/xdrv_40_telegram.ino index ec230e792..0ceefa3a7 100644 --- a/tasmota/xdrv_40_telegram.ino +++ b/tasmota/xdrv_40_telegram.ino @@ -434,14 +434,14 @@ void CmndTmChatId(void) { void CmndTmSend(void) { if (!Telegram.send_enable || !strlen(SettingsText(SET_TELEGRAM_CHATID))) { - ResponseCmndChar(D_JSON_FAILED); + ResponseCmndChar(PSTR(D_JSON_FAILED)); return; } if (XdrvMailbox.data_len > 0) { String message = XdrvMailbox.data; String chat_id = SettingsText(SET_TELEGRAM_CHATID); if (!TelegramSendMessage(chat_id.toInt(), message)) { - ResponseCmndChar(D_JSON_FAILED); + ResponseCmndChar(PSTR(D_JSON_FAILED)); return; } } diff --git a/tasmota/xdrv_50_filesystem.ino b/tasmota/xdrv_50_filesystem.ino index fb12e3b9d..dfca83cef 100644 --- a/tasmota/xdrv_50_filesystem.ino +++ b/tasmota/xdrv_50_filesystem.ino @@ -391,7 +391,7 @@ void UFSDelete(void) { result = (ufs_type && ufsp->remove(XdrvMailbox.data)); } if (!result) { - ResponseCmndChar(D_JSON_FAILED); + ResponseCmndChar(PSTR(D_JSON_FAILED)); } else { ResponseCmndDone(); } @@ -454,8 +454,8 @@ void UfsDirectory(void) { strcpy(ufs_path, "/"); - if (Webserver->hasArg("download")) { - String stmp = Webserver->arg("download"); + if (Webserver->hasArg(F("download"))) { + String stmp = Webserver->arg(F("download")); char *cp = (char*)stmp.c_str(); if (UfsDownloadFile(cp)) { // is directory @@ -465,8 +465,8 @@ void UfsDirectory(void) { } } - if (Webserver->hasArg("dir")) { - String stmp = Webserver->arg("dir"); + if (Webserver->hasArg(F("dir"))) { + String stmp = Webserver->arg(F("dir")); ufs_dir = atoi(stmp.c_str()); if (ufs_dir == 1) { dfsp = ufsp; @@ -477,8 +477,8 @@ void UfsDirectory(void) { } } - if (Webserver->hasArg("delete")) { - String stmp = Webserver->arg("delete"); + if (Webserver->hasArg(F("delete"))) { + String stmp = Webserver->arg(F("delete")); char *cp = (char*)stmp.c_str(); dfsp->remove(cp); } @@ -498,7 +498,7 @@ void UfsDirectory(void) { } WSContentSend_P(UFS_FORM_FILE_UPGc2); - WSContentSend_P(UFS_FORM_FILE_UPG, D_SCRIPT_UPLOAD); + WSContentSend_P(UFS_FORM_FILE_UPG, PSTR(D_SCRIPT_UPLOAD)); WSContentSend_P(UFS_FORM_SDC_DIRa); if (ufs_type) { @@ -516,7 +516,7 @@ void UfsListDir(char *path, uint8_t depth) { char name[32]; char npath[128]; char format[12]; - sprintf(format, "%%-%ds", 24 - depth); + sprintf(format, PSTR("%%-%ds"), 24 - depth); File dir = dfsp->open(path, UFS_FILE_READ); if (dir) { @@ -533,7 +533,7 @@ void UfsListDir(char *path, uint8_t depth) { break; } } - WSContentSend_P(UFS_FORM_SDC_DIRd, npath, path, ".."); + WSContentSend_P(UFS_FORM_SDC_DIRd, npath, path, PSTR("..")); } char *ep; while (true) { @@ -764,13 +764,13 @@ bool Xdrv50(uint8_t function) { #ifdef USE_WEBSERVER case FUNC_WEB_ADD_MANAGEMENT_BUTTON: if (ufs_type) { - WSContentSend_PD(UFS_WEB_DIR, D_MANAGE_FILE_SYSTEM); + WSContentSend_PD(UFS_WEB_DIR, PSTR(D_MANAGE_FILE_SYSTEM)); } break; case FUNC_WEB_ADD_HANDLER: - Webserver->on("/ufsd", UfsDirectory); - Webserver->on("/ufsu", HTTP_GET, UfsDirectory); - Webserver->on("/ufsu", HTTP_POST,[](){Webserver->sendHeader("Location","/ufsu");Webserver->send(303);}, HandleUploadLoop); + Webserver->on(F("/ufsd"), UfsDirectory); + Webserver->on(F("/ufsu"), HTTP_GET, UfsDirectory); + Webserver->on(F("/ufsu"), HTTP_POST,[](){Webserver->sendHeader(F("Location"),F("/ufsu"));Webserver->send(303);}, HandleUploadLoop); break; #endif // USE_WEBSERVER } diff --git a/tasmota/xsns_34_hx711.ino b/tasmota/xsns_34_hx711.ino index 4f22d7cca..7dc9da3d7 100644 --- a/tasmota/xsns_34_hx711.ino +++ b/tasmota/xsns_34_hx711.ino @@ -241,7 +241,7 @@ bool HxCommand(void) break; case 7: // WeightSave Settings.energy_frequency_calibration = Hx.weight; - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, D_JSON_DONE); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, PSTR(D_JSON_DONE)); break; case 8: // Json on weight change if (strchr(XdrvMailbox.data, ',') != nullptr) { From b55fdcef75ca397ed25ee3fe0e6b638f951628c5 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 18 Jan 2021 22:32:59 +0100 Subject: [PATCH 07/19] Fix compilation ESP32 --- tasmota/xdrv_10_rules.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index 31b8e9f46..4a6d3c9c1 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -720,7 +720,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) if ((ucommand.indexOf(F("IF ")) == -1) && (ucommand.indexOf(F("EVENT ")) != -1) && (ucommand.indexOf(F("BACKLOG ")) == -1)) { - commands = F("backlog ") + commands; + commands = String(F("backlog ")) + commands; } RulesVarReplace(commands, F("%VALUE%"), Rules.event_value); From 3265236fe1943a08fef3b2f36db193dfa94652be Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 18 Jan 2021 22:37:36 +0100 Subject: [PATCH 08/19] Fix compilation ESP32 --- tasmota/support_tasmota.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 9cac3fa37..40d5836ad 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -744,7 +744,8 @@ void TempHumDewShow(bool json, bool pass_on, const char *types, float f_temperat String GetSwitchText(uint32_t i) { String switch_text = SettingsText(SET_SWITCH_TXT1 + i); if ('\0' == switch_text[0]) { - switch_text = F(D_JSON_SWITCH) + String(i +1); + switch_text = F(D_JSON_SWITCH); + switch_text += String(i+1); } return switch_text; } From 61bf455747f08c496c5130b6b312820518dc66ae Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 19 Jan 2021 08:50:52 +0100 Subject: [PATCH 09/19] gz not build anymore for ESP32 --- .github/workflows/Tasmota_build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/Tasmota_build.yml b/.github/workflows/Tasmota_build.yml index 9e8f61219..6cf253537 100644 --- a/.github/workflows/Tasmota_build.yml +++ b/.github/workflows/Tasmota_build.yml @@ -1603,8 +1603,6 @@ jobs: [ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/ [ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/ - rm ./firmware/tasmota32/*.gz - rm ./firmware/tasmota32/languages/*.gz [ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/ [ ! -f ./tools/Esptool/Odroid_go/*.* ] || mv ./tools/Esptool/Odroid_go/*.* ./firmware/tasmota32/Odroid_go_needed_files/ [ ! -f ./FIRMWARE.md ] || mv -f ./FIRMWARE.md ./README.md From 86f4e8ab74f874b24609097cd66686d512430380 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 19 Jan 2021 08:51:48 +0100 Subject: [PATCH 10/19] no gz build for ESP32 --- .github/workflows/Tasmota_build_master.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/Tasmota_build_master.yml b/.github/workflows/Tasmota_build_master.yml index 8c00d25e3..b2b68c477 100644 --- a/.github/workflows/Tasmota_build_master.yml +++ b/.github/workflows/Tasmota_build_master.yml @@ -1603,8 +1603,6 @@ jobs: [ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/ [ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/ [ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/ - rm ./firmware/tasmota32/*.gz - rm ./firmware/tasmota32/languages/*.gz [ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/ [ ! -f ./tools/Esptool/Odroid_go/*.* ] || mv ./tools/Esptool/Odroid_go/*.* ./firmware/tasmota32/Odroid_go_needed_files/ [ ! -f ./FIRMWARE.md ] || mv -f ./RELEASENOTES.md ./README.md From ec74dc3a51f46dedda7a0c6cafd97c1c82e35390 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Jan 2021 11:54:49 +0100 Subject: [PATCH 11/19] Make zbbridge transfer more verbose Make zbbridge transfer more verbose (#10413) --- tasmota/xdrv_23_zigbee_9a_upload.ino | 90 ++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 26 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_9a_upload.ino b/tasmota/xdrv_23_zigbee_9a_upload.ino index b946848a3..d8157c127 100644 --- a/tasmota/xdrv_23_zigbee_9a_upload.ino +++ b/tasmota/xdrv_23_zigbee_9a_upload.ino @@ -81,6 +81,13 @@ char ZigbeeUploadFlashRead(void) { ZbUpload.byte_counter++; if (ZbUpload.byte_counter > ZbUpload.ota_size) { + +// static bool padding = true; +// if (padding) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Start padding from %d"), ZbUpload.byte_counter); +// padding = false; +// } + // When the source device reaches the last XModem data block, it should be padded to 128 bytes // of data using SUB (ASCII 0x1A) characters. data = XM_SUB; @@ -107,8 +114,8 @@ struct XMODEM { uint32_t delay = 0; uint32_t flush_delay = 0xFFFFFFFF; uint32_t filepos = 0; + uint32_t packet_no = 1; int crcBuf = 0; - uint8_t packetNo = 1; uint8_t checksumBuf = 0; bool oldChecksum; } XModem; @@ -142,6 +149,11 @@ char XModemWaitACK(void) if (i > 200) { return -1; } } in_char = ZigbeeSerial->read(); + +// if (in_char != XM_ACK) { +// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("XMD: Rcvd3 0x%02X"), in_char); +// } + if (XM_CAN == in_char) { return XM_CAN; } } while ((in_char != XM_NAK) && (in_char != XM_ACK) && (in_char != 'C')); return in_char; @@ -162,10 +174,12 @@ bool XModemSendPacket(uint32_t packet_no) { XModem.checksumBuf = 0x00; XModem.crcBuf = 0x00; + uint8_t packet_num = packet_no; + // Try to send packet, so header first ZigbeeSerial->write(XM_SOH); - ZigbeeSerial->write(packet_no); - ZigbeeSerial->write(~packet_no); + ZigbeeSerial->write(packet_num); + ZigbeeSerial->write(~packet_num); for (uint32_t i = 0; i < XMODEM_PACKET_SIZE; i++) { in_char = ZigbeeUploadFlashRead(); XModemOutputByte(in_char); @@ -220,6 +234,13 @@ bool ZigbeeUploadBootloaderPrompt(void) { yield(); char bootloader_byte = ZigbeeSerial->read(); + // [cr][lf] + // Gecko Bootloader v1.A.3 or Gecko Bootloader v1.9.1.04[cr][lf] + // 1. upload gbl[cr][lf] + // 2. run[cr][lf] + // 3. ebl info[cr][lf] + // BL > + if (((uint8_t)bootloader_byte >=0) && (buf_len < sizeof(serial_buffer) -2)) { serial_buffer[buf_len++] = bootloader_byte; } @@ -332,17 +353,18 @@ bool ZigbeeUploadXmodem(void) { } } else { // After the bootloader receives a carriage return from the target device, it displays a menu - // Gecko Bootloader v1.A.3 - // 1. upload gbl - // 2. run - // 3. ebl info + // [cr][lf] + // Gecko Bootloader v1.A.3 or Gecko Bootloader v1.9.1.04[cr][lf] + // 1. upload gbl[cr][lf] + // 2. run[cr][lf] + // 3. ebl info[cr][lf] // BL > if (ZigbeeUploadBootloaderPrompt()) { AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Init sync")); ZigbeeSerial->flush(); ZigbeeSerial->write('1'); // upload ebl if (TasmotaGlobal.sleep > 0) { - TasmotaGlobal.sleep = 1; // Speed up loop used for xmodem upload + TasmotaGlobal.sleep = 1; // Speed up loop used for xmodem upload } XModem.timeout = millis() + (XMODEM_SYNC_TIMEOUT * 1000); ZbUpload.ota_step = ZBU_SYNC; @@ -358,11 +380,17 @@ bool ZigbeeUploadXmodem(void) { } // Wait for either C or NACK as a sync packet. Determines protocol details, checksum algorithm. if (ZigbeeSerial->available()) { + // [cr][lf] + // begin upload[cr][lf] + // C char xmodem_sync = ZigbeeSerial->read(); + +// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("XMD: Rcvd2 0x%02X"), xmodem_sync); + if (('C' == xmodem_sync) || (XM_NAK == xmodem_sync)) { // Determine which checksum algorithm to use XModem.oldChecksum = (xmodem_sync == XM_NAK); - XModem.packetNo = 1; + XModem.packet_no = 1; ZbUpload.byte_counter = 0; ZbUpload.ota_step = ZBU_UPLOAD; AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Init packet send")); @@ -372,12 +400,15 @@ bool ZigbeeUploadXmodem(void) { } case ZBU_UPLOAD: { // *** Handle file upload using XModem - upload if (ZigbeeUploadAvailable()) { - if (!XModemSendPacket(XModem.packetNo)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Packet send failed")); + if (ZbUpload.byte_counter && !(ZbUpload.byte_counter % 10240)) { // Show progress every 10kB + AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Progress %d kB"), ZbUpload.byte_counter / 1024); + } + if (!XModemSendPacket(XModem.packet_no)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Packet %d send failed"), XModem.packet_no); ZbUpload.ota_step = ZBU_ERROR; return true; } - XModem.packetNo++; + XModem.packet_no++; } else { // Once the last block is ACKed by the target, the transfer should be finalized by an // EOT (ASCII 0x04) packet from the source. Once this packet is confirmed via XModem ACK @@ -385,6 +416,7 @@ bool ZigbeeUploadXmodem(void) { ZigbeeSerial->write(XM_EOT); XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EOT ACK ZbUpload.ota_step = ZBU_EOT; + AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Transferred %d bytes"), ZbUpload.ota_size); } break; } @@ -401,7 +433,12 @@ bool ZigbeeUploadXmodem(void) { } if (ZigbeeSerial->available()) { char xmodem_ack = XModemWaitACK(); - if (XM_ACK == xmodem_ack) { + if (XM_CAN == xmodem_ack) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Transfer invalid")); + ZbUpload.ota_step = ZBU_ERROR; + return true; + } + else if (XM_ACK == xmodem_ack) { AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: " D_SUCCESSFUL)); XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EBL prompt ZbUpload.byte_counter = 0; @@ -418,28 +455,33 @@ bool ZigbeeUploadXmodem(void) { } else { // After an image successfully uploads, the XModem transaction completes and the bootloader displays // ‘Serial upload complete’ before redisplaying the menu - // Serial upload complete - // Gecko Bootloader v1.A.3 - // 1. upload gbl - // 2. run - // 3. ebl info + // + // [cr][lf] + // Serial upload complete[cr][lf] + // [cr][lf] + // Gecko Bootloader v1.A.3 or Gecko Bootloader v1.9.1.04[cr][lf] + // 1. upload gbl[cr][lf] + // 2. run[cr][lf] + // 3. ebl info[cr][lf] // BL > if (ZigbeeUploadBootloaderPrompt()) { ZbUpload.state = ZBU_COMPLETE; ZbUpload.ota_step = ZBU_DONE; + AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: " D_RESTARTING)); } } break; } case ZBU_ERROR: ZbUpload.state = ZBU_ERROR; + AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: " D_FAILED)); case ZBU_DONE: { // *** Clean up and restart to disable bootloader and use new firmware - AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: " D_RESTARTING)); ZigbeeUploadSetBootloader(1); // Disable bootloader and reset MCU - should happen at restart if (1 == TasmotaGlobal.sleep) { TasmotaGlobal.sleep = Settings.sleep; // Restore loop sleep } // TasmotaGlobal.restart_flag = 2; // Restart to disable bootloader and use new firmware + if (ZbUpload.buffer) { free(ZbUpload.buffer); } ZbUpload.ota_step = ZBU_FINISH; // Never return to zero without a restart to get a sane Zigbee environment break; } @@ -472,10 +514,6 @@ void ZigbeeUploadStep1Done(uint32_t data, size_t size) { ZbUpload.state = ZBU_UPLOAD; // Signal upload done and ready for delayed upload to MCU EFR32 } -bool ZigbeeUploadFinish(void) { - return (ZBU_FINISH == ZbUpload.ota_step); -} - #define WEB_HANDLE_ZIGBEE_XFER "zx" const char HTTP_SCRIPT_XFER_STATE[] PROGMEM = @@ -500,12 +538,12 @@ void HandleZigbeeXfer(void) { if (!HttpCheckPriviledgedAccess()) { return; } if (Webserver->hasArg("z")) { // Status refresh requested - WSContentBegin(200, CT_PLAIN); - WSContentSend_P(PSTR("%d"), ZbUpload.state); - WSContentEnd(); if (ZBU_ERROR == ZbUpload.state) { Web.upload_error = 7; // Upload aborted (xmodem transfer failed) } + WSContentBegin(200, CT_PLAIN); + WSContentSend_P(PSTR("%d"), ZbUpload.state); + WSContentEnd(); return; } From 865b25dff9dd4230b1d69fe3d6567c8e10a98952 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Jan 2021 12:23:01 +0100 Subject: [PATCH 12/19] Remove disable messages --- tasmota/xdrv_01_webserver.ino | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index b43bc573f..33d4ebce8 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -2148,8 +2148,6 @@ void HandleInformation(void) #ifdef USE_EMULATION WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions)); -#else - WSContentSend_P(PSTR("}1" D_EMULATION "}2" D_DISABLED)); #endif // USE_EMULATION #ifdef USE_DISCOVERY @@ -2161,8 +2159,6 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED)); #endif // WEBSERVER_ADVERTISE } -#else - WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2" D_DISABLED)); #endif // USE_DISCOVERY WSContentSend_P(PSTR("}1}2 ")); // Empty line From 9a11d3613bc87c1ba9be7ed56ee3615f794348dc Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Jan 2021 12:26:52 +0100 Subject: [PATCH 13/19] Remove disable messages --- tasmota/xdrv_01_webserver.ino | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 33d4ebce8..26a720a2d 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -2127,7 +2127,7 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port); #ifdef USE_MQTT_TLS WSContentSend_P(PSTR("}1" D_MQTT_TLS_ENABLE "}2%s"), Settings.flag4.mqtt_tls ? PSTR(D_ENABLED) : PSTR(D_DISABLED)); -#endif // USE_MQTT_TLS +#endif // USE_MQTT_TLS WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER)); WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), TasmotaGlobal.mqtt_client); WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC)); @@ -2144,12 +2144,13 @@ void HandleInformation(void) } else { WSContentSend_P(PSTR("}1" D_MQTT "}2" D_DISABLED)); } - WSContentSend_P(PSTR("}1}2 ")); // Empty line +#if defined(USE_EMULATION) || defined(USE_DISCOVERY) + WSContentSend_P(PSTR("}1}2 ")); // Empty line +#endif // USE_EMULATION or USE_DISCOVERY #ifdef USE_EMULATION WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions)); -#endif // USE_EMULATION - +#endif // USE_EMULATION #ifdef USE_DISCOVERY WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2%s"), (Settings.flag3.mdns_enabled) ? D_ENABLED : D_DISABLED); // SetOption55 - Control mDNS service if (Settings.flag3.mdns_enabled) { // SetOption55 - Control mDNS service @@ -2157,9 +2158,9 @@ void HandleInformation(void) WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_WEB_SERVER)); #else WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED)); -#endif // WEBSERVER_ADVERTISE +#endif // WEBSERVER_ADVERTISE } -#endif // USE_DISCOVERY +#endif // USE_DISCOVERY WSContentSend_P(PSTR("}1}2 ")); // Empty line WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP_getChipId()); From 6fe37d148a8b5a8603855e9bb555f5ba300b5095 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 19 Jan 2021 12:34:36 +0100 Subject: [PATCH 14/19] use esp32-1.0.5-rc6.zip for ESP32 --- platformio_tasmota32.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini index 717ca03a5..7053ac3af 100644 --- a/platformio_tasmota32.ini +++ b/platformio_tasmota32.ini @@ -92,7 +92,7 @@ build_flags = ${esp_defaults.build_flags} [core32] platform = espressif32 @ 2.1.0 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc4/esp32-1.0.5-rc4.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc6/esp32-1.0.5-rc6.zip platformio/tool-mklittlefs @ ~1.203.200522 build_unflags = ${esp32_defaults.build_unflags} build_flags = ${esp32_defaults.build_flags} From cba145136e58906b87e98c2510549ba6c3eb6ee1 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 19 Jan 2021 12:35:33 +0100 Subject: [PATCH 15/19] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e542181b9..0de14bbf8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,7 +7,7 @@ - [ ] Only relevant files were touched - [ ] Only one feature/fix was added per PR and the code change compiles without warnings - [ ] The code change is tested and works on Tasmota core ESP8266 V.2.7.4.9 - - [ ] The code change is tested and works on Tasmota core ESP32 V.1.0.5-rc4 + - [ ] The code change is tested and works on Tasmota core ESP32 V.1.0.5-rc6 - [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). _NOTE: The code change must pass CI tests. **Your PR cannot be merged unless tests pass**_ From f607152aa17cd5aa2e472b35239edc160b6474da Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 19 Jan 2021 13:37:53 +0100 Subject: [PATCH 16/19] Zigbee add EZSP 6.7.8 as Release Candidate firmware --- .../{ => archive}/ncp-uart-sw_6.5.5_115200.ota | Bin .../ncp-uart-sw_6.7.8_115200.ota | Bin 0 -> 190756 bytes tools/fw_SonoffZigbeeBridge_ezsp/readme.txt | 7 +++++-- 3 files changed, 5 insertions(+), 2 deletions(-) rename tools/fw_SonoffZigbeeBridge_ezsp/{ => archive}/ncp-uart-sw_6.5.5_115200.ota (100%) create mode 100644 tools/fw_SonoffZigbeeBridge_ezsp/ncp-uart-sw_6.7.8_115200.ota diff --git a/tools/fw_SonoffZigbeeBridge_ezsp/ncp-uart-sw_6.5.5_115200.ota b/tools/fw_SonoffZigbeeBridge_ezsp/archive/ncp-uart-sw_6.5.5_115200.ota similarity index 100% rename from tools/fw_SonoffZigbeeBridge_ezsp/ncp-uart-sw_6.5.5_115200.ota rename to tools/fw_SonoffZigbeeBridge_ezsp/archive/ncp-uart-sw_6.5.5_115200.ota diff --git a/tools/fw_SonoffZigbeeBridge_ezsp/ncp-uart-sw_6.7.8_115200.ota b/tools/fw_SonoffZigbeeBridge_ezsp/ncp-uart-sw_6.7.8_115200.ota new file mode 100644 index 0000000000000000000000000000000000000000..9bc81b544d7c55f82768b8088df51bed51e41052 GIT binary patch literal 190756 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%0AUWU5O5bd-u5 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%0A-Q>YP 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 sW6n1`oUq{i|Fdm+Z@U|3w^Spz8MKhX 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 literal 0 HcmV?d00001 diff --git a/tools/fw_SonoffZigbeeBridge_ezsp/readme.txt b/tools/fw_SonoffZigbeeBridge_ezsp/readme.txt index da6716cdd..f8a4b0bb2 100644 --- a/tools/fw_SonoffZigbeeBridge_ezsp/readme.txt +++ b/tools/fw_SonoffZigbeeBridge_ezsp/readme.txt @@ -3,8 +3,7 @@ ## EmberZNet NCP UART EZSP firmware signed for Sonoff ZBBridge - `ncp-uart-sw_6.7.6_115200.ota` - recommended stable version for EZSP v6, EZSP v7, and EZSP v8 compatible hosts. -- `ncp-uart-sw-6.8.0.1_115200.ota` - latest cutting-edge version. largely untested and only for experimentation with EZSP v8 compatible hosts. -- `ncp-uart-sw_6.5.5_115200.ota` - legacy version for EZSP v4, EZSP v5, EZSP v6, or EZSP v7 compatible hosts. +- `ncp-uart-sw_6.7.8_115200.ota` - release candidate, supposedly fixing IKEA battery drain issue. ## EmberZNet and EZSP Protocol Versions @@ -13,3 +12,7 @@ Silicon Labs do not currently have a consolidated list of changes by EmberZNet S The largest change was between EZSP v4 (first added in EmberZNet 4.7.2 SDK) and EZSP v5 that was added in EmberZNet 5.9.0 SDK which requires the Legacy Frame ID 0xFF. The change from EZSP v5 to EZSP v6 was done in EmberZNet 6.0.0 SDK. The change from EZSP v6 to EZSP v7 was in EmberZNet 6.4.0 SDK. EmberZNet 6.7.0 SDK added EZSP v8 (and Secure EZSP Protocol Version 2). Perhaps more important to know is that EZSP v5, v6 and v7 (EmberZNet 6.6.x.x) use the same framing format, but EmberZNet 6.7.x.x/EZSP v8 introduced new framing format and expanded command id field from 8 bits to 16 bits. + +## Archived Versions + +- `ncp-uart-sw_6.5.5_115200.ota` - legacy version for EZSP v4, EZSP v5, EZSP v6, or EZSP v7 compatible hosts. \ No newline at end of file From c5984875d65ae3755aeff95d577ebc75aff0bc04 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Jan 2021 13:57:30 +0100 Subject: [PATCH 17/19] Remove upload watchdog --- tasmota/xdrv_01_webserver.ino | 3 --- 1 file changed, 3 deletions(-) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 26a720a2d..34d3947f6 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -2341,7 +2341,6 @@ void UploadServices(uint32_t start_service) { if (start_service) { // AddLog_P(LOG_LEVEL_DEBUG, PSTR("UPL: Services enabled")); - TasmotaGlobal.restart_flag = 0; /* MqttRetryCounter(0); */ @@ -2376,7 +2375,6 @@ void UploadServices(uint32_t start_service) { MqttDisconnect(); } */ - TasmotaGlobal.restart_flag = 120; // Set restart watchdog after 2 minutes } } @@ -2429,7 +2427,6 @@ void HandleUploadLoop(void) { Web.upload_error = 2; return; } - TasmotaGlobal.restart_flag = 0; } #endif // USE_UFILESYS } From f5f6c6e5a1675587d551c07e3eceb1011241ee61 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Jan 2021 15:26:02 +0100 Subject: [PATCH 18/19] Enable new BLE driver for odroid --- tasmota/tasmota_configurations_ESP32.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h index 0a507dc2b..bb23c6663 100644 --- a/tasmota/tasmota_configurations_ESP32.h +++ b/tasmota/tasmota_configurations_ESP32.h @@ -57,6 +57,7 @@ #define USE_SPI #define USE_DISPLAY // Add SPI Display Support (+2k code) #define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code) +#define USE_BLE_ESP32 // Enable new BLE driver #define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash) #endif // FIRMWARE_ODROID_GO From c16fb465fb37392e114b181dba40ed1ef7b22a1f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 19 Jan 2021 16:23:16 +0100 Subject: [PATCH 19/19] Add correct log info --- tasmota/xdrv_52_BLE_ESP32.ino | 234 +++++++++--------- tasmota/xsns_62_MI_ESP32_BLE_ESP32.ino | 314 ++++++++++++------------- 2 files changed, 274 insertions(+), 274 deletions(-) diff --git a/tasmota/xdrv_52_BLE_ESP32.ino b/tasmota/xdrv_52_BLE_ESP32.ino index 309934d3e..cfd176a8d 100644 --- a/tasmota/xdrv_52_BLE_ESP32.ino +++ b/tasmota/xdrv_52_BLE_ESP32.ino @@ -599,7 +599,7 @@ int addSeenDevice(const uint8_t *mac, uint8_t addrtype, const char *name, int8_t int total = seenDevices.size(); if (total < MAX_BLE_DEVICES_LOGGED){ #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("new seendev slot %d"), total); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: New seendev slot %d"), total); #endif BLE_ESP32::BLE_simple_device_t* dev = new BLE_ESP32::BLE_simple_device_t; freeDevices.push_back(dev); @@ -667,10 +667,10 @@ int deleteSeenDevices(int ageS = 0){ dump(addr, 20, dev->mac, 6); const char *alias = getAlias(dev->mac); if (!filter){ - AddLog_P(LOG_LEVEL_INFO,PSTR("delete device %s(%s) by age lastseen %u + maxage %u < now %u."), + AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: Delete device %s(%s) by age lastseen %u + maxage %u < now %u."), addr, alias, lastseenS, ageS, nowS); } else { - AddLog_P(LOG_LEVEL_INFO,PSTR("delete device %s(%s) by addrtype filter %d > %d."), + AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: Delete device %s(%s) by addrtype filter %d > %d."), addr, alias, dev->addrtype, BLEAddressFilter); } #endif @@ -682,7 +682,7 @@ int deleteSeenDevices(int ageS = 0){ } if (res){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_INFO,PSTR("BLE deleted %d devices"), res); + AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: Deleted %d devices"), res); #endif } return res; @@ -820,7 +820,7 @@ int getSeenDevicesToJson(char *dest, int maxlen){ } // deliberate test of SafeAddLog_P from main thread... - //AddLog_P(LOG_LEVEL_INFO,PSTR("getSeen %d"), seenDevices.size()); + //AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: getSeen %d"), seenDevices.size()); int len; @@ -1184,17 +1184,17 @@ void postAdvertismentDetails(){ class BLESensorCallback : public NimBLEClientCallbacks { void onConnect(NimBLEClient* pClient) { #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("onConnect %s"), ((std::string)pClient->getPeerAddress()).c_str()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: onConnect %s"), ((std::string)pClient->getPeerAddress()).c_str()); #endif } void onDisconnect(NimBLEClient* pClient) { #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("onDisconnect %s"), ((std::string)pClient->getPeerAddress()).c_str()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: onDisconnect %s"), ((std::string)pClient->getPeerAddress()).c_str()); #endif } bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) { #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("onConnParamsUpdateRequest %s"), ((std::string)pClient->getPeerAddress()).c_str()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: onConnParamsUpdateRequest %s"), ((std::string)pClient->getPeerAddress()).c_str()); #endif // if(params->itvl_min < 24) { /** 1.25ms units */ @@ -1315,7 +1315,7 @@ class BLEAdvCallbacks: public NimBLEAdvertisedDeviceCallbacks { } } catch(const std::exception& e){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("exception in advertismentCallbacks")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: exception in advertismentCallbacks")); #endif } } @@ -1334,18 +1334,18 @@ static BLESensorCallback BLESensorCB; static void BLEscanEndedCB(NimBLEScanResults results){ #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("Scan ended")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: Scan ended")); #endif for (int i = 0; i < scancompleteCallbacks.size(); i++){ try { SCANCOMPLETE_CALLBACK *pFn = scancompleteCallbacks[i]; int callbackres = pFn(results); #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("scancompleteCallbacks %d %d"), i, callbackres); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: scancompleteCallbacks %d %d"), i, callbackres); #endif } catch(const std::exception& e){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("exception in operationsCallbacks")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: exception in operationsCallbacks")); #endif } } @@ -1367,21 +1367,21 @@ static void BLEGenNotifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, ui if (!pRemoteCharacteristic){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("Notify: no remote char!!??")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: Notify: no remote char!!??")); #endif return; } #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("Notified length: %u"),length); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: Notified length: %u"),length); #endif // find the operation this is associated with NimBLERemoteService *pSvc = pRemoteCharacteristic->getRemoteService(); if (!pSvc){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("Notify: no remote service found")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Notify: no remote service found")); #endif return; } @@ -1389,7 +1389,7 @@ static void BLEGenNotifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, ui pRClient = pSvc->getClient(); if (!pRClient){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("Notify: no remote client!!??")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Notify: no remote client!!??")); #endif return; } @@ -1404,7 +1404,7 @@ static void BLEGenNotifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, ui generic_sensor_t *op = currentOperations[i]; if (!op){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("Notify: null op in currentOperations!!??")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Notify: null op in currentOperations!!??")); #endif } else { if (devaddr == op->addr){ @@ -1420,7 +1420,7 @@ static void BLEGenNotifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, ui if (!thisop){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("no op for notify")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: no op for notify")); #endif return; } @@ -1524,7 +1524,7 @@ static void BLEOperationTask(void *pvParameters); static void BLEStartOperationTask(){ if (BLERunning == false){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Start operations"),D_CMND_BLE); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: %s: Start operations"),D_CMND_BLE); #endif BLERunning = true; @@ -1547,25 +1547,25 @@ static void BLEStartOperationTask(){ static void BLETaskStopStartNimBLE(NimBLEClient **ppClient, bool start = true){ if (*ppClient){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLETask:Stopping NimBLE")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Task:Stopping NimBLE")); (*ppClient)->setClientCallbacks(nullptr, false); try { if ((*ppClient)->isConnected()){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_INFO,PSTR("disconnecting connected client")); + AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: disconnecting connected client")); #endif (*ppClient)->disconnect(); } NimBLEDevice::deleteClient((*ppClient)); (*ppClient) = nullptr; #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_INFO,PSTR("deleted client")); + AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: deleted client")); #endif } catch(const std::exception& e){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("Stopping NimBLE:exception in delete client")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Stopping NimBLE:exception in delete client")); #endif } @@ -1582,7 +1582,7 @@ static void BLETaskStopStartNimBLE(NimBLEClient **ppClient, bool start = true){ BLERunningScan = 0; if (start){ - AddLog_P(LOG_LEVEL_INFO,PSTR("BLETask:Starting NimBLE")); + AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: BLETask:Starting NimBLE")); NimBLEDevice::init("BLE_ESP32"); *ppClient = NimBLEDevice::createClient(); @@ -1623,7 +1623,7 @@ int BLETaskStartScan(int time){ } #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: Startscan")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: Startscan")); #endif //vTaskDelay(500/ portTICK_PERIOD_MS); ble32Scan->setActiveScan(BLEScanActiveMode ? 1: 0); @@ -1652,7 +1652,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe *pCurrentOperation = nextOperation(&queuedOperations); if (*pCurrentOperation){ #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: new currentOperation")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: new currentOperation")); #endif BLEOpCount++; generic_sensor_t* temp = *pCurrentOperation; @@ -1673,7 +1673,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe diff = diff/1000; if (diff > 20000){ // 20s #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: notify timeout")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: notify timeout")); #endif (*pCurrentOperation)->state = GEN_STATE_FAILED_NOTIFYTIMEOUT; (*pCurrentOperation)->notifytimer = 0; @@ -1690,7 +1690,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe (*pCurrentOperation)->state = GEN_STATE_NOTIFIED; // just stay here until this is removed by the main thread #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: notify operation complete")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: notify operation complete")); #endif BLE_ESP32::BLETaskRunTaskDoneOperation(pCurrentOperation, ppClient); pClient = *ppClient; @@ -1701,7 +1701,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe case GEN_STATE_NOTIFIED: // - may have completed DURING our read/write to get here // just stay here until this is removed by the main thread #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: operation complete")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: operation complete")); #endif BLE_ESP32::BLETaskRunTaskDoneOperation(pCurrentOperation, ppClient); pClient = *ppClient; @@ -1721,7 +1721,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe if ((*pCurrentOperation)->state <= GEN_STATE_FAILED){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLETask: op failed %d"), (*pCurrentOperation)->state); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: BLETask: op failed %d"), (*pCurrentOperation)->state); #endif BLE_ESP32::BLETaskRunTaskDoneOperation(pCurrentOperation, ppClient); pClient = *ppClient; @@ -1735,7 +1735,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe if (pClient->isConnected()){ // don't do anything if we are still connected #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: still connected")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: still connected")); #endif return; } @@ -1755,7 +1755,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe op->state = GEN_STATE_STARTED; #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLETask: attempt connect %s"), ((std::string)op->addr).c_str()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: attempt connect %s"), ((std::string)op->addr).c_str()); #endif if (!op->serviceUUID.bitSize()){ @@ -1766,7 +1766,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe if (pClient->connect(op->addr, true)) { #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("connected %s -> getservice"), ((std::string)op->addr).c_str()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: connected %s -> getservice"), ((std::string)op->addr).c_str()); #endif NimBLERemoteService *pService = pClient->getService(op->serviceUUID); int waitNotify = false; @@ -1775,7 +1775,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe if (pService != nullptr) { #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("got service")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: got service")); #endif // pre-set to fail if no operations requested //newstate = GEN_STATE_FAILED_NOREADWRITE; @@ -1792,13 +1792,13 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe pService->getCharacteristic(op->notificationCharacteristicUUID); if (pNCharacteristic != nullptr) { #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("got notify characteristic")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: got notify characteristic")); #endif op->notifylen = 0; if(pNCharacteristic->canNotify()) { if(pNCharacteristic->subscribe(true, BLE_ESP32::BLEGenNotifyCB)) { #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("subscribe for notify")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: subscribe for notify")); #endif uint64_t now = esp_timer_get_time(); op->notifytimer = now; @@ -1808,7 +1808,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe waitNotify = true; } else { #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("failed subscribe for notify")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: failed subscribe for notify")); #endif newstate = GEN_STATE_FAILED_NOTIFY; } @@ -1816,7 +1816,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe if(pNCharacteristic->canIndicate()) { if(pNCharacteristic->subscribe(false, BLE_ESP32::BLEGenNotifyCB)) { #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("subscribe for indicate")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: subscribe for indicate")); #endif notifystate = GEN_STATE_WAITINDICATE; uint64_t now = esp_timer_get_time(); @@ -1824,21 +1824,21 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe waitNotify = true; } else { #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("failed subscribe for indicate")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: failed subscribe for indicate")); #endif newstate = GEN_STATE_FAILED_INDICATE; } } else { newstate = GEN_STATE_FAILED_CANTNOTIFYORINDICATE; #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("characteristic can't notify")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: characteristic can't notify")); #endif } } } else { newstate = GEN_STATE_FAILED_NONOTIFYCHAR; #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("notify characteristic not found")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: notify characteristic not found")); #endif } @@ -1855,7 +1855,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe pCharacteristic = pService->getCharacteristic(op->characteristicUUID); if (pCharacteristic != nullptr) { #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("got read/write characteristic")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: got read/write characteristic")); #endif newstate = GEN_STATE_FAILED_NOREADWRITE; // overwritten on failure @@ -1875,12 +1875,12 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe if (op->readmodifywritecallback){ READ_CALLBACK *pFn = (READ_CALLBACK *)op->readmodifywritecallback; #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("read characteristic with readmodifywritecallback")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: read characteristic with readmodifywritecallback")); #endif pFn(op); } else { #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("read characteristic")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: read characteristic")); #endif } @@ -1896,12 +1896,12 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe if (!pCharacteristic->writeValue(op->dataToWrite, op->writelen, true)){ newstate = GEN_STATE_FAILED_WRITE; #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("characteristic write fail")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: characteristic write fail")); #endif } else { if (!waitNotify) newstate = GEN_STATE_WRITEDONE; #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("write characteristic")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: write characteristic")); #endif } } else { @@ -1913,7 +1913,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe } else { newstate = GEN_STATE_FAILED_NO_RW_CHAR; #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("r/w characteristic not found")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: r/w characteristic not found")); #endif } } @@ -1934,7 +1934,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe newstate = GEN_STATE_FAILED_NOSERVICE; // failed to get a service #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("failed - svc not on device?")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: failed - svc not on device?")); #endif } @@ -1946,14 +1946,14 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe switch (rc){ case (0x0200+BLE_ERR_CONN_LIMIT ): #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("Hit connection limit? - restarting NimBLE")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Hit connection limit? - restarting NimBLE")); #endif BLERestartNimBLE = 1; BLERestartBLEReason = BLE_RESTART_BLE_REASON_CONN_LIMIT; break; case (0x0200+BLE_ERR_ACL_CONN_EXISTS): #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("Connection exists? - restarting NimBLE")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Connection exists? - restarting NimBLE")); #endif BLERestartNimBLE = 1; BLERestartBLEReason = BLE_RESTART_BLE_REASON_CONN_EXISTS; @@ -1963,7 +1963,7 @@ static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOpe // failed to connect #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("failed to connect to device %d"), rc); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: failed to connect to device %d"), rc); #endif } op->state = newstate; @@ -1979,7 +1979,7 @@ static void BLETaskRunTaskDoneOperation(BLE_ESP32::generic_sensor_t** op, NimBLE try { if ((*ppClient)->isConnected()){ #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("runTaskDoneOperation: disconnecting connected client")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: runTaskDoneOperation: disconnecting connected client")); #endif (*ppClient)->disconnect(); // wait for 1/2 second after disconnect @@ -1990,7 +1990,7 @@ static void BLETaskRunTaskDoneOperation(BLE_ESP32::generic_sensor_t** op, NimBLE //(*ppClient)->disconnect(); // we will stall here forever!!! - as testing #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE wait discon%d"), waits); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: wait discon%d"), waits); #endif vTaskDelay(500/ portTICK_PERIOD_MS); } @@ -1999,11 +1999,11 @@ static void BLETaskRunTaskDoneOperation(BLE_ESP32::generic_sensor_t** op, NimBLE int conn_id = (*ppClient)->getConnId(); ble_gap_conn_broken(conn_id, -1); #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE wait discon%d - kill connection"), waits); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: wait discon%d - kill connection"), waits); #endif } if (waits == 60){ - AddLog_P(LOG_LEVEL_ERROR,PSTR(">60s waiting -> BLE Failed, restart Tasmota %d"), waits); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: >60s waiting -> BLE Failed, restart Tasmota %d"), waits); BLEStop = 1; BLEStopAt = esp_timer_get_time(); @@ -2015,7 +2015,7 @@ static void BLETaskRunTaskDoneOperation(BLE_ESP32::generic_sensor_t** op, NimBLE } } catch(const std::exception& e){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("runTaskDoneOperation: exception in disconnect")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: runTaskDoneOperation: exception in disconnect")); #endif } @@ -2035,7 +2035,7 @@ static void BLETaskRunTaskDoneOperation(BLE_ESP32::generic_sensor_t** op, NimBLE // by adding it to this list, this will cause it to be sent to MQTT #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("runTaskDoneOperation: add to completedOperations")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: runTaskDoneOperation: add to completedOperations")); #endif addOperation(&completedOperations, op); return; @@ -2096,7 +2096,7 @@ static void BLEOperationTask(void *pvParameters){ BLERestartNimBLE = 0; BLERestartTasmota = 10; BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_RESTARTING_BLE_TIMEOUT; - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLETask: Restart NimBLE - restart Tasmota in 10 if not complt")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: BLETask: Restart NimBLE - restart Tasmota in 10 if not complt")); BLE_ESP32::BLETaskStopStartNimBLE(&pClient); BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_UNKNOWN; BLERestartTasmota = 0; @@ -2110,7 +2110,7 @@ static void BLEOperationTask(void *pvParameters){ vTaskDelay(100/ portTICK_PERIOD_MS); #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLEOperationTask: Left task")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: BLEOperationTask: Left task")); #endif deleteSeenDevices(); @@ -2192,11 +2192,11 @@ static void BLEEverySecond(bool restart){ if (!BLERestartTasmotaReason) BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_UNKNOWN; snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("{\"reboot\":\"%s\"}"), BLERestartTasmotaReason); MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain); - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE Failure! Restarting Tasmota in %d seconds because %s"), BLERestartTasmota, BLERestartTasmotaReason); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Failure! Restarting Tasmota in %d seconds because %s"), BLERestartTasmota, BLERestartTasmotaReason); } if (!BLERestartTasmota){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE Failure! Restarting Tasmota because %s"), BLERestartTasmotaReason); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Failure! Restarting Tasmota because %s"), BLERestartTasmotaReason); // just a normal restart TasmotaGlobal.restart_flag = 1; } @@ -2205,7 +2205,7 @@ static void BLEEverySecond(bool restart){ if (BLERestartBLEReason){ // just use the ptr as the trigger to send MQTT snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("{\"blerestart\":\"%s\"}"), BLERestartBLEReason); MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain); - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE Failure! Restarting BLE Stack because %s"), BLERestartBLEReason); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Failure! Restarting BLE Stack because %s"), BLERestartBLEReason); BLERestartBLEReason = nullptr; } @@ -2250,9 +2250,9 @@ int addOperation(std::deque *ops, generic_sensor_t** op){ } } if (res){ - //AddLog_P(LOG_LEVEL_DEBUG,PSTR("added operation")); + //AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: added operation")); } else { - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE op - no room")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: op - no room")); } return res; } @@ -2260,7 +2260,7 @@ int addOperation(std::deque *ops, generic_sensor_t** op){ int newOperation(BLE_ESP32::generic_sensor_t** op){ if (!op) { - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE op inv in newOperation")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: op inv in newOperation")); return 0; } @@ -2307,7 +2307,7 @@ int extQueueOperation(BLE_ESP32::generic_sensor_t** op){ int res = addOperation(&queuedOperations, op); if (!res){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("extQueueOperation: op added id %d failed"), (lastopid-1)); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: extQueueOperation: op added id %d failed"), (lastopid-1)); } return res; } @@ -2427,7 +2427,7 @@ static int StartBLE(void) { BLE_ESP32::BLEStartOperationTask(); return 1; } - AddLog_P(LOG_LEVEL_ERROR,PSTR("StartBLE - wait as BLEStop==1")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: StartBLE - wait as BLEStop==1")); return 0; } @@ -2436,16 +2436,16 @@ static int StopBLE(void){ if (BLERunning){ if (BLEStop != 1){ BLEStop = 1; - AddLog_P(LOG_LEVEL_INFO,PSTR("StopBLE - BLEStop->1")); + AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: StopBLE - BLEStop->1")); BLEStopAt = esp_timer_get_time(); // give a little time for it to stop. vTaskDelay(1000/ portTICK_PERIOD_MS); return 1; } - AddLog_P(LOG_LEVEL_ERROR,PSTR("StopBLE - wait as BLEStop==1")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: StopBLE - wait as BLEStop==1")); return 0; } else { - AddLog_P(LOG_LEVEL_ERROR,PSTR("StopBLE - was not running")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: StopBLE - was not running")); return 1; } } @@ -2686,7 +2686,7 @@ void CmndBLEDetails(void){ void CmndBLEAlias(void){ #ifdef BLE_ESP32_ALIASES int op = XdrvMailbox.index; - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("Alias %d %s"), op, XdrvMailbox.data); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: Alias %d %s"), op, XdrvMailbox.data); int res = -1; switch(op){ @@ -2705,7 +2705,7 @@ void CmndBLEAlias(void){ char *mac = p; int len = fromHex(addr, p, sizeof(addr)); if (len != 6){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("Alias invalid mac %s"), p); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Alias invalid mac %s"), p); ResponseCmndChar("invalidmac"); return; } @@ -2726,7 +2726,7 @@ void CmndBLEAlias(void){ return; } - AddLog_P(LOG_LEVEL_ERROR,PSTR("Add Alias mac %s = name %s"), mac, p); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Add Alias mac %s = name %s"), mac, p); if (addAlias( addr, name )){ added++; } @@ -2734,7 +2734,7 @@ void CmndBLEAlias(void){ } while (p); if (added){ - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("Added %d Aliases"), added); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: Added %d Aliases"), added); BLEAliasListResp(); } else { BLEAliasListResp(); @@ -2742,7 +2742,7 @@ void CmndBLEAlias(void){ return; } break; case 2:{ // clear - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLEAlias clearing %d"), aliases.size()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: Alias clearing %d"), aliases.size()); for (int i = aliases.size()-1; i >= 0; i--){ BLE_ESP32::ble_alias_t *alias = aliases[i]; aliases.pop_back(); @@ -2773,14 +2773,14 @@ void CmndBLEName(void) { if (addrres){ if (addrres == 2){ - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE addr used alias: %s"), p); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: addr used alias: %s"), p); } //#ifdef EQ3_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE cmd addr: %s -> %s"), p, addr.toString().c_str()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: cmd addr: %s -> %s"), p, addr.toString().c_str()); //#endif } else { - AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE addr invalid: %s"), p); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: addr invalid: %s"), p); ResponseCmndIdxChar(PSTR("invalidaddr")); return; } @@ -2789,11 +2789,11 @@ void CmndBLEName(void) { // ALWAYS use this function to create a new one. int res = BLE_ESP32::newOperation(&op); if (!res){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("Can't get a newOperation")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Can't get a newOperation")); ResponseCmndChar(PSTR("FAIL")); return; } else { - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("got a newOperation from BLE")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: got a newOperation from BLE")); } op->addr = addr; @@ -2804,21 +2804,21 @@ void CmndBLEName(void) { char *name = strtok(nullptr, " "); bool write = false; if (name && *name){ - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("write name %s"), name); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: write name %s"), name); op->writelen = strlen(name); memcpy(op->dataToWrite, name, op->writelen); write = true; } else { - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("read name")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: read name")); op->readlen = 1; } res = BLE_ESP32::extQueueOperation(&op); - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("queue res %d"), res); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: queue res %d"), res); if (!res){ // if it fails to add to the queue, do please delete it BLE_ESP32::freeOperation(&op); - AddLog_P(LOG_LEVEL_ERROR,PSTR("Failed to queue new operation - deleted")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Failed to queue new operation - deleted")); ResponseCmndChar(PSTR("QUEUEFAIL")); return; } @@ -2856,7 +2856,7 @@ void CmndBLEOperation(void){ int op = XdrvMailbox.index; - //AddLog_P(LOG_LEVEL_INFO,PSTR("op %d"), op); + //AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: op %d"), op); int res = -1; @@ -2864,7 +2864,7 @@ void CmndBLEOperation(void){ switch(op) { case 0: #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("preview")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: preview")); #endif BLEPostMQTTTrigger = 1; break; @@ -2875,7 +2875,7 @@ void CmndBLEOperation(void){ int opres = BLE_ESP32::newOperation(&prepOperation); if (!opres){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("Could not create new operation")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Could not create new operation")); #endif ResponseCmndChar("FailCreate"); return; @@ -2933,14 +2933,14 @@ void CmndBLEOperation(void){ // this means you could retry with another BLEOp10. // it WOULD be deleted if you sent another BELOP1 #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("Could not queue new operation")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Could not queue new operation")); #endif ResponseCmndChar("FailQueue"); return; } else { // NOTE: prepOperation has been set to null if we queued sucessfully. #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("Operations queued:%d"), queuedOperations.size()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: Operations queued:%d"), queuedOperations.size()); #endif char temp[40]; sprintf(temp, "{\"opid\":%d,\"u\":%d}", lastopid-1, u); @@ -2969,13 +2969,13 @@ void CmndBLEOperation(void){ // this means you could retry with another BLEOp10. // it WOULD be deleted if you sent another BELOP1 #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("Could not queue new operation")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Could not queue new operation")); #endif ResponseCmndChar("FailQueue"); } else { // NOTE: prepOperation has been set to null if we queued sucessfully. #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("Operations queued:%d"), queuedOperations.size()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: Operations queued:%d"), queuedOperations.size()); #endif char temp[40]; sprintf(temp, "{\"opid\":%d,\"u\":%d}", lastopid-1, u); @@ -3026,20 +3026,20 @@ static void BLEPostMQTT(bool onlycompleted) { if (prepOperation || completedOperations.size() || queuedOperations.size() || currentOperations.size()){ #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("some to show")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: some to show")); #endif if (prepOperation && !onlycompleted){ std::string out = BLETriggerResponse(prepOperation); snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str()); MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain); #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("prep sent %s"), out.c_str()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: prep sent %s"), out.c_str()); #endif } if (queuedOperations.size() && !onlycompleted){ #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("queued %d"), queuedOperations.size()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: queued %d"), queuedOperations.size()); #endif for (int i = 0; i < queuedOperations.size(); i++){ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEPost1"); @@ -3053,7 +3053,7 @@ static void BLEPostMQTT(bool onlycompleted) { snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str()); MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain); #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("queued %d sent %s"), i, out.c_str()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: queued %d sent %s"), i, out.c_str()); #endif //break; } @@ -3062,7 +3062,7 @@ static void BLEPostMQTT(bool onlycompleted) { if (currentOperations.size() && !onlycompleted){ #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("current %d"), currentOperations.size()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: current %d"), currentOperations.size()); #endif for (int i = 0; i < currentOperations.size(); i++){ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEPost2"); @@ -3075,7 +3075,7 @@ static void BLEPostMQTT(bool onlycompleted) { snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str()); MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain); #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("curr %d sent %s"), i, out.c_str()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: curr %d sent %s"), i, out.c_str()); #endif //break; } @@ -3084,7 +3084,7 @@ static void BLEPostMQTT(bool onlycompleted) { if (completedOperations.size()){ #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("completed %d"), completedOperations.size()); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: completed %d"), completedOperations.size()); #endif do { generic_sensor_t *toSend = nextOperation(&completedOperations); @@ -3092,7 +3092,7 @@ static void BLEPostMQTT(bool onlycompleted) { break; // break from while loop } else { #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE:completedOperation removed")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: completedOperation removed")); #endif std::string out = BLETriggerResponse(toSend); snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str()); @@ -3154,7 +3154,7 @@ static void mainThreadBLETimeouts() { static void mainThreadOpCallbacks() { if (completedOperations.size()){ - //AddLog_P(LOG_LEVEL_INFO,PSTR("completed %d"), completedOperations.size()); + //AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: completed %d"), completedOperations.size()); TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEMainCB"); // find this operation in currentOperations, and remove it. @@ -3169,11 +3169,11 @@ static void mainThreadOpCallbacks() { OPCOMPLETE_CALLBACK *pFn = (OPCOMPLETE_CALLBACK *)(op->completecallback); callbackres = pFn(op); #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("op->completecallback %d"), callbackres); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: op->completecallback %d"), callbackres); #endif } catch(const std::exception& e){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("exception in op->completecallback")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: exception in op->completecallback")); #endif } } @@ -3184,14 +3184,14 @@ static void mainThreadOpCallbacks() { OPCOMPLETE_CALLBACK *pFn = operationsCallbacks[i]; callbackres = pFn(op); #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("operationsCallbacks %d %d"), i, callbackres); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: operationsCallbacks %d %d"), i, callbackres); #endif if (callbackres){ break; // this callback ate the op. } } catch(const std::exception& e){ #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_ERROR,PSTR("exception in operationsCallbacks")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: exception in operationsCallbacks")); #endif } } @@ -3200,7 +3200,7 @@ static void mainThreadOpCallbacks() { // if some callback told us not to send on MQTT, then remove from completed and delete the data if (callbackres){ #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("callbackres true -> delete op")); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("BLE: callbackres true -> delete op")); #endif completedOperations.erase(completedOperations.begin() + i); delete op; @@ -3214,7 +3214,7 @@ static void BLEShow(bool json) { if (json){ #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("show json %d"),json); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: show json %d"),json); #endif uint32_t totalCount = BLEAdvertisment.totalCount; uint32_t deviceCount = seenDevices.size(); @@ -3262,7 +3262,7 @@ static void BLEDiag() uint32_t totalCount = BLEAdvertisment.totalCount; uint32_t deviceCount = seenDevices.size(); #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE:scans:%u,advertisements:%u,devices:%u,resets:%u,BLEStop:%d,BLERunning:%d,BLERunningScan:%d,BLELoopCount:%u,BLEOpCount:%u"), BLEScanCount, totalCount, deviceCount, BLEResets, BLEStop, BLERunning, BLERunningScan, BLELoopCount, BLEOpCount); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: scans:%u,advertisements:%u,devices:%u,resets:%u,BLEStop:%d,BLERunning:%d,BLERunningScan:%d,BLELoopCount:%u,BLEOpCount:%u"), BLEScanCount, totalCount, deviceCount, BLEResets, BLEStop, BLERunning, BLERunningScan, BLELoopCount, BLEOpCount); #endif } @@ -3372,12 +3372,12 @@ void HandleBleConfiguration(void) { #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG, PSTR("HandleBleConfiguration")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("BLE: HandleBleConfiguration")); #endif if (!HttpCheckPriviledgedAccess()) { #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG, PSTR("!HttpCheckPriviledgedAccess()")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("BLE: !HttpCheckPriviledgedAccess()")); #endif return; } @@ -3390,12 +3390,12 @@ void HandleBleConfiguration(void) WebGetArg("en", tmp, sizeof(tmp)); #ifdef BLE_ESP32_DEBUG - if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("arg en is %s"), tmp); + if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("BLE: arg en is %s"), tmp); #endif if (Webserver->hasArg("save")) { #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG, PSTR("BLE SETTINGS SAVE")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("BLE: SETTINGS SAVE")); #endif Settings.flag5.mi32_enable = Webserver->hasArg("e0"); // BLEScanActiveMode = (Webserver->hasArg("e1")?1:0); // @@ -3405,7 +3405,7 @@ void HandleBleConfiguration(void) return; } #ifdef BLE_ESP32_DEBUG - AddLog_P(LOG_LEVEL_DEBUG, PSTR("!SAVE")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("BLE: !SAVE")); #endif char str[TOPSZ]; @@ -3460,7 +3460,7 @@ void HandleBleConfiguration(void) \*********************************************************************************************/ int ExtStopBLE(){ - AddLog_P(LOG_LEVEL_INFO, PSTR("Stopping BLE if active - upgrade starting?")); + AddLog_P(LOG_LEVEL_INFO, PSTR("BLE: Stopping if active")); BLE_ESP32::BLEMode = BLE_ESP32::BLEModeDisabled; BLE_ESP32::StopBLE(); return 0; @@ -3548,13 +3548,13 @@ int myAdvertCallback(BLE_ESP32::ble_advertisment_t *pStruct) { // this one is used to demonstrate processing ALL operations int myOpCallback(BLE_ESP32::generic_sensor_t *pStruct){ - AddLog_P(LOG_LEVEL_INFO,PSTR("myOpCallback")); + AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: myOpCallback")); return 0; // return true to block MQTT broadcast } // this one is used to demonstrate processing of ONE specific operation int myOpCallback2(BLE_ESP32::generic_sensor_t *pStruct){ - AddLog_P(LOG_LEVEL_INFO,PSTR("myOpCallback2")); + AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: myOpCallback2")); return 1; // return true to block MQTT broadcast } #endif @@ -3577,7 +3577,7 @@ void sendExample(){ BLE_ESP32::generic_sensor_t *op = nullptr; int res = BLE_ESP32::newOperation(&op); if (!res){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("Could not create new operation")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Could not create new operation")); return; } strncpy(op->MAC, "001A22092EE0", sizeof(op->MAC)); @@ -3592,7 +3592,7 @@ void sendExample(){ if (!res){ // if it fails to add to the queue, do please delete it BLE_ESP32::freeOperation(&op); - AddLog_P(LOG_LEVEL_ERROR,PSTR("Failed to queue new operation - deleted")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("BLE: Failed to queue new operation - deleted")); return; } diff --git a/tasmota/xsns_62_MI_ESP32_BLE_ESP32.ino b/tasmota/xsns_62_MI_ESP32_BLE_ESP32.ino index 832aae2e7..c6a3e3111 100644 --- a/tasmota/xsns_62_MI_ESP32_BLE_ESP32.ino +++ b/tasmota/xsns_62_MI_ESP32_BLE_ESP32.ino @@ -97,15 +97,15 @@ struct { struct { // the slot currently having it's battery read // set to 0 to start a battery read cycle - uint8_t slot = 255; - uint8_t active = 0; + uint8_t slot = 255; + uint8_t active = 0; } batteryreader; struct { // the slot currently having it's battery read // set to 0 to start a battery read cycle - uint8_t slot = 255; - uint8_t active = 0; + uint8_t slot = 255; + uint8_t active = 0; } sensorreader; struct { @@ -246,7 +246,7 @@ struct PVVXPacket_t { uint16_t battery_mv; // mV uint8_t battery_level; // 0..100 % uint8_t counter; // measurement count - uint8_t flags; + uint8_t flags; }; #pragma pack(0) @@ -362,7 +362,7 @@ void (*const MI32_Commands[])(void) PROGMEM = { #define MI_MI32_TYPES 13 //count this manually -const uint16_t kMI32DeviceID[MI_MI32_TYPES]={ +const uint16_t kMI32DeviceID[MI_MI32_TYPES]={ 0x0000, // Unkown 0x0098, // Flora 0x01aa, // MJ_HT_V1 @@ -419,8 +419,8 @@ const char *MHOC303_TimeChar = LYWSD02_TimeChar; const char *MHOC401_Svc = LYWSD02_Svc; const char *MHOC401_BattNotifyChar = LYWSD02_BattNotifyChar; -const char CGD1_Svc[] PROGMEM = "180F"; -const char CGD1_BattChar[] PROGMEM = "2A19"; +const char CGD1_Svc[] PROGMEM = "180F"; +const char CGD1_BattChar[] PROGMEM = "2A19"; const char FLORA_Svc[] PROGMEM = "00001204-0000-1000-8000-00805F9B34FB"; const char FLORA_BattChar[] PROGMEM = "00001A02-0000-1000-8000-00805F9B34FB"; @@ -443,7 +443,7 @@ enum MI32_MI_OP_TYPES { enum MI32_MI_KEY_REQ { - KEY_REQUIREMENT_UNKNOWN = 0, // we don't know if a key is needed + KEY_REQUIREMENT_UNKNOWN = 0, // we don't know if a key is needed KEY_NOT_REQUIRED = 1, // we got an unencrypted payload KEY_REQUIRED_BUT_NOT_FOUND = 2, // we got an encrypted packet, but had not key KEY_REQUIRED_AND_FOUND = 3, // we got an encrypted packet, and could decrypt @@ -490,7 +490,7 @@ int toggleUnit(BLE_ESP32::generic_sensor_t *op){ bool MI32Operation(int slot, int optype, const char *svc, const char *charactistic, const char *notifychar = nullptr, const uint8_t *data = nullptr, int datalen = 0, uint8_t *addr = nullptr ) { if (!svc || !svc[0]){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("MI32Op: inv svc")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: Op inv svc")); return 0; } @@ -499,17 +499,17 @@ bool MI32Operation(int slot, int optype, const char *svc, const char *charactist // ALWAYS use this function to create a new one. int res = BLE_ESP32::newOperation(&op); if (!res){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("Can't get a newOperation from BLE")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("M32: Can't get a newOperation")); return 0; } else { - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("got a newOperation from BLE")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: Got a newOperation")); } if (slot >= 0){ op->addr = NimBLEAddress(MIBLEsensors[slot].MAC); } else { if (!addr){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("no addr")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: No addr")); BLE_ESP32::freeOperation(&op); return 0; } @@ -521,7 +521,7 @@ bool MI32Operation(int slot, int optype, const char *svc, const char *charactist if (!op->serviceUUID.bitSize()){ BLE_ESP32::freeOperation(&op); - AddLog_P(LOG_LEVEL_ERROR,PSTR("MI: Bad service string %s"), svc); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: MI Bad service string %s"), svc); return 0; } @@ -531,7 +531,7 @@ bool MI32Operation(int slot, int optype, const char *svc, const char *charactist op->characteristicUUID = NimBLEUUID(charactistic); if (!op->characteristicUUID.bitSize()){ BLE_ESP32::freeOperation(&op); - AddLog_P(LOG_LEVEL_ERROR,PSTR("MI: Bad characteristic string %s"), charactistic); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: MI Bad characteristic string %s"), charactistic); return 0; } } @@ -539,10 +539,10 @@ bool MI32Operation(int slot, int optype, const char *svc, const char *charactist op->notificationCharacteristicUUID = NimBLEUUID(notifychar); if (!op->notificationCharacteristicUUID.bitSize()){ BLE_ESP32::freeOperation(&op); - AddLog_P(LOG_LEVEL_ERROR,PSTR("MI: Bad notifycharacteristic string %s"), notifychar); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: MI Bad notifycharacteristic string %s"), notifychar); return 0; } - } + } if (data && datalen) { op->writelen = datalen; @@ -564,13 +564,13 @@ bool MI32Operation(int slot, int optype, const char *svc, const char *charactist uint32_t context = (optype << 24) | (MIBLEsensors[slot].type << 16) | slot; op->context = (void *)context; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI s:%d op:%s"), slot, BLE_ESP32::BLETriggerResponse(op).c_str()); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: MI s:%d op:%s"), slot, BLE_ESP32::BLETriggerResponse(op).c_str()); res = BLE_ESP32::extQueueOperation(&op); if (!res){ // if it fails to add to the queue, do please delete it BLE_ESP32::freeOperation(&op); - AddLog_P(LOG_LEVEL_ERROR,PSTR("Failed to queue new operation - deleted")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: Failed to queue new operation - deleted")); } return res; @@ -591,10 +591,10 @@ int genericBatReadFn(int slot){ break; // these read a characteristic - case MI_FLORA: + case MI_FLORA: res = MI32Operation(slot, OP_BATT_READ, FLORA_Svc, FLORA_BattChar); break; - case MI_LYWSD02: + case MI_LYWSD02: res = MI32Operation(slot, OP_BATT_READ, LYWSD02_Svc, LYWSD02_BattChar); break; case MI_CGD1: @@ -611,9 +611,9 @@ int genericBatReadFn(int slot){ break; } if (res > 0){ - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("Req batt read slot %d type %d queued"), slot, MIBLEsensors[slot].type); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO, PSTR("M32: Req batt read slot %d type %d queued"), slot, MIBLEsensors[slot].type); } else { - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("Req batt read slot %d type %d non-queued res %d"), slot, MIBLEsensors[slot].type, res); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO, PSTR("M32: Req batt read slot %d type %d non-queued res %d"), slot, MIBLEsensors[slot].type, res); } return res; } @@ -627,17 +627,17 @@ int genericSensorReadFn(int slot, int force){ so although the characteristic seems to exist, it does not work? further dev required with sensor to hand. case MI_LYWSD02: - // don't read if key present and we've decoded at least one advert + // don't read if key present and we've decoded at least one advert if (MIBLEsensors[slot].needkey == KEY_REQUIRED_AND_FOUND) return -2; res = MI32Operation(slot, OP_READ_HT_LY, LYWSD02_Svc, nullptr, LYWSD02_BattNotifyChar); break;*/ case MI_LYWSD03MMC: - // don't read if key present and we've decoded at least one advert + // don't read if key present and we've decoded at least one advert if (MIBLEsensors[slot].needkey == KEY_REQUIRED_AND_FOUND && !force) return -2; res = MI32Operation(slot, OP_READ_HT_LY, LYWSD03_Svc, nullptr, LYWSD03_BattNotifyChar); break; case MI_MHOC401: - // don't read if key present and we've decoded at least one advert + // don't read if key present and we've decoded at least one advert if (MIBLEsensors[slot].needkey == KEY_REQUIRED_AND_FOUND && !force) return -2; res = MI32Operation(slot, OP_READ_HT_LY, MHOC401_Svc, nullptr, MHOC401_BattNotifyChar); break; @@ -653,22 +653,22 @@ int genericSensorReadFn(int slot, int force){ // called once per second int readOneSensor(){ if (MI32.sensorreader.active){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("readOneSensor - already active reading %d"), MI32.sensorreader.slot-1); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: readOneSensor - already active reading %d"), MI32.sensorreader.slot-1); return 0; } // loop if the sensor at the slot does not need to be read // i.e. drop out of loop when we start a read, or hit the end - int res = -1; + int res = -1; do { // MI32.sensorreader.slot is reset to zero to trigger a read sequence if (MI32.sensorreader.slot >= MIBLEsensors.size()){ - //AddLog_P(LOG_LEVEL_DEBUG,PSTR("readOneSensor past end of slots - %d > %d"), MI32.sensorreader.slot, MIBLEsensors.size()); + //AddLog_P(LOG_LEVEL_DEBUG, PSTR("BLE: readOneSensor past end of slots - %d > %d"), MI32.sensorreader.slot, MIBLEsensors.size()); return 0; } res = genericSensorReadFn(MI32.sensorreader.slot, 0); - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("genericSensorReadFn slot %d res %d"), MI32.sensorreader.slot, res); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: genericSensorReadFn slot %d res %d"), MI32.sensorreader.slot, res); // if this sensor in this slot does not need to be read via notify, just move on top the next one if (res < 0){ @@ -680,7 +680,7 @@ int readOneSensor(){ if (res == 0){ // can't read at the moment (no operations available?) - AddLog_P(LOG_LEVEL_DEBUG,PSTR("readOneSensor no ops available slot %d res %d"), MI32.sensorreader.slot, res); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: readOneSensor no ops available slot %d res %d"), MI32.sensorreader.slot, res); return 0; } @@ -689,7 +689,7 @@ int readOneSensor(){ // and make it wait until the read/notify is complete // this is cleared in the response callback. MI32.sensorreader.active = 1; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("readOneSensor reading for slot %d res %d"), MI32.sensorreader.slot-1, res); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: readOneSensor reading for slot %d res %d"), MI32.sensorreader.slot-1, res); // started one return 1; @@ -714,7 +714,7 @@ int readOneBat(){ if (res < 0){ MI32.batteryreader.slot++; if (MI32.batteryreader.slot >= MIBLEsensors.size()){ - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("Batt loop complete at %d"), MI32.batteryreader.slot); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO, PSTR("M32: Batt loop complete at %d"), MI32.batteryreader.slot); } return 0; } @@ -730,7 +730,7 @@ int readOneBat(){ // this is cleared in the response callback. MI32.batteryreader.active = 1; if (MI32.batteryreader.slot >= MIBLEsensors.size()){ - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("Batt loop will complete at %d"), MI32.batteryreader.slot); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO, PSTR("M32: Batt loop will complete at %d"), MI32.batteryreader.slot); } // started one return 1; @@ -816,7 +816,7 @@ int genericTimeWriteFn(int slot){ int genericOpCompleteFn(BLE_ESP32::generic_sensor_t *op){ uint32_t context = (uint32_t) op->context; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI op complete context %x"), context); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: MI op complete context %x"), context); int opType = context >> 24; int devType = (context >> 16) & 0xff; @@ -832,12 +832,12 @@ int genericOpCompleteFn(BLE_ESP32::generic_sensor_t *op){ bool fail = false; if (op->addr != addr){ // slot changed during operation? - AddLog_P(LOG_LEVEL_ERROR,PSTR("Slot mac changed during an operation")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: Slot mac changed during an operation")); fail = true; } if (op->state <= GEN_STATE_FAILED){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("operation failed %d for %s"), op->state, slotMAC); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: Operation failed %d for %s"), op->state, slotMAC); fail = true; } @@ -857,7 +857,7 @@ int genericOpCompleteFn(BLE_ESP32::generic_sensor_t *op){ switch(opType){ case OP_TIME_WRITE: - AddLog_P(LOG_LEVEL_DEBUG,PSTR("Time write for %s complete"), slotMAC); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: Time write for %s complete"), slotMAC); return 0; // nothing to do case OP_BATT_READ:{ uint8_t *data = nullptr; @@ -876,33 +876,33 @@ int genericOpCompleteFn(BLE_ESP32::generic_sensor_t *op){ // allow another... MI32.batteryreader.active = 0; - AddLog_P(LOG_LEVEL_INFO,PSTR("batt read slot %d done state %x"), slot, op->state); + AddLog_P(LOG_LEVEL_INFO, PSTR("M32: Batt read slot %d done state %x"), slot, op->state); } return 0; case OP_UNIT_WRITE: // nothing more to do? - AddLog_P(LOG_LEVEL_DEBUG,PSTR("Unit write for %s complete"), slotMAC); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: Unit write for %s complete"), slotMAC); return 0; case OP_UNIT_READ: { - uint8_t currUnit = op->dataRead[0]; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("Unit read for %s complete %d"), slotMAC, currUnit); + uint8_t currUnit = op->dataRead[0]; + AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: Unit read for %s complete %d"), slotMAC, currUnit); } return 0; case OP_UNIT_TOGGLE: { - uint8_t currUnit = op->dataToWrite[0]; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("Unit toggle for %s complete %d->%d; datasize was %d"), slotMAC, op->dataRead[0], op->dataToWrite[0], op->readlen); + uint8_t currUnit = op->dataToWrite[0]; + AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: Unit toggle for %s complete %d->%d; datasize was %d"), slotMAC, op->dataRead[0], op->dataToWrite[0], op->readlen); } return 0; case OP_READ_HT_LY: { // allow another... MI32.sensorreader.active = 0; MI32notifyHT_LY(slot, (char*)op->dataNotify, op->notifylen); - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("HT_LY notify for %s complete"), slotMAC); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: HT_LY notify for %s complete"), slotMAC); } return 0; default: - AddLog_P(LOG_LEVEL_ERROR,PSTR("OpType %d not recognised?"), opType); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: OpType %d not recognised?"), opType); return 0; } @@ -914,7 +914,7 @@ int MI32advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) // we will try not to use this... BLEAdvertisedDevice *advertisedDevice = pStruct->advertisedDevice; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); + // AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); int RSSI = pStruct->RSSI; const uint8_t *addr = pStruct->addr; if(MI32isInBlockList(addr) == true) return 0; @@ -937,14 +937,14 @@ int MI32advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) char temp[60]; BLE_ESP32::dump(temp, 13, addr, 6); - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("MI:%s svc[0] UUID (%x)"), temp, UUID); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("M32: MI:%s svc[0] UUID (%x)"), temp, UUID); std::string ServiceDataStr = advertisedDevice->getServiceData(0); - + uint32_t ServiceDataLength = ServiceDataStr.length(); const uint8_t *ServiceData = (const uint8_t *)ServiceDataStr.data(); BLE_ESP32::dump(temp, 60, ServiceData, ServiceDataLength); - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("MI:%s"), temp); - + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("M32: MI:%s"), temp); + if (UUID){ // this will take and keep the mutex until the function is over @@ -1041,14 +1041,14 @@ int MI32AddKey(char* payload, char* key = nullptr){ bool unknownKey = true; for(uint32_t i=0; i 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: search key for MAC: %02x%02x%02x%02x%02x%02x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: Search key for MAC: %02x%02x%02x%02x%02x%02x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); for(uint32_t i=0; i 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: decryption Key found")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Decryption Key found")); foundNoKey = false; break; } } if(foundNoKey){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: no Key found !!")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: No Key found")); return -2; // indicates needs key } @@ -1098,7 +1098,7 @@ int MIDecryptPayload(const uint8_t *macin, const uint8_t *nonce, uint32_t tag, u // returns 1 if matched, else 0 int ret = br_ccm_check_tag(&ctx, &tag); - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: Err:%i, Decrypted : %02x %02x %02x %02x %02x"), ret, payload[1],payload[2],payload[3],payload[4],payload[5]); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Err:%i, Decrypted : %02x %02x %02x %02x %02x"), ret, payload[1],payload[2],payload[3],payload[4],payload[5]); return ret-1; // -> -1=fail, 0=success } @@ -1168,7 +1168,7 @@ int MIParsePacket(const uint8_t* slotmac, struct mi_beacon_data_t *parsed, const parsed->devicetype = *((uint16_t *)(data + byteindex)); byteindex += 2; parsed->framecnt = data[byteindex]; - //if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI frame %d"), parsed->framecnt); + //if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: MI frame %d"), parsed->framecnt); byteindex++; @@ -1184,7 +1184,7 @@ int MIParsePacket(const uint8_t* slotmac, struct mi_beacon_data_t *parsed, const byteindex += 6; } - int decres = 1; + int decres = 1; // everything after MAC is encrypted if specified? if (parsed->framedata.isencrypted){ if (len < byteindex + 3+4+1){ @@ -1217,17 +1217,17 @@ int MIParsePacket(const uint8_t* slotmac, struct mi_beacon_data_t *parsed, const break; case 0: // suceeded parsed->needkey = KEY_REQUIRED_AND_FOUND; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI payload decrypted")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Payload decrypted")); break; case -1: // key failed to work parsed->needkey = KEY_REQUIRED_AND_INVALID; - AddLog_P(LOG_LEVEL_ERROR,PSTR("MI payload decrypt failed")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("M32: Payload decrypt failed")); parsed->payloadpresent = 0; return 0; break; case -2: // key not present parsed->needkey = KEY_REQUIRED_BUT_NOT_FOUND; - AddLog_P(LOG_LEVEL_ERROR,PSTR("MI payload encrypted but no key")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("M32: Payload encrypted but no key")); parsed->payloadpresent = 0; return 0; break; @@ -1262,7 +1262,7 @@ int MIParsePacket(const uint8_t* slotmac, struct mi_beacon_data_t *parsed, const } if ((len - byteindex) == 0){ - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI no payload")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: No payload")); parsed->payload.size = 0; parsed->payloadpresent = 0; return 0; @@ -1271,14 +1271,14 @@ int MIParsePacket(const uint8_t* slotmac, struct mi_beacon_data_t *parsed, const // we have payload which did not need decrypt. if (decres == 1){ parsed->needkey = KEY_NOT_REQUIRED; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI payload unencrypted")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Payload unencrypted")); } // already decrypted if required parsed->payloadpresent = 1; memcpy(&parsed->payload, (data + byteindex), (len - byteindex)); if (parsed->payload.size != (len - byteindex) - 3){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI payload length mismatch")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Payload length mismatch")); } return 1; @@ -1319,7 +1319,7 @@ void MI32nullifyEndOfMQTT_DATA(){ */ uint32_t MIBLEgetSensorSlot(const uint8_t *mac, uint16_t _type, uint8_t counter){ - //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("%s: will test ID-type: %x"),D_CMND_MI32, _type); + //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: will test ID-type: %x"),D_CMND_MI32, _type); bool _success = false; for (uint32_t i=0; i < MI_MI32_TYPES; i++){ // i < sizeof(kMI32DeviceID) gives compiler warning if(_type == kMI32DeviceID[i]){ @@ -1328,40 +1328,40 @@ uint32_t MIBLEgetSensorSlot(const uint8_t *mac, uint16_t _type, uint8_t counter) break; } else { - //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("%s: ID-type is not: %x"),D_CMND_MI32,kMI32DeviceID[i]); + //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: ID-type is not: %x"),D_CMND_MI32,kMI32DeviceID[i]); } } if(!_success) { _type = 1; // unknown } - //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("%s: vector size %u"),D_CMND_MI32, MIBLEsensors.size()); + //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: vector size %u"),D_CMND_MI32, MIBLEsensors.size()); for(uint32_t i=0; i 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("%s: slot: %u/%u - ign repeat"),D_CMND_MI32, i, MIBLEsensors.size()); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: slot: %u/%u - ign repeat"),D_CMND_MI32, i, MIBLEsensors.size()); //return 0xff; // packet received before, stop here } - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI frame %d, last %d"), counter, MIBLEsensors[i].lastCnt); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Frame %d, last %d"), counter, MIBLEsensors[i].lastCnt); MIBLEsensors[i].lastCnt = counter; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("%s: slot: %u/%u"),D_CMND_MI32, i, MIBLEsensors.size()); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: slot: %u/%u"),D_CMND_MI32, i, MIBLEsensors.size()); if (MIBLEsensors[i].type != _type){ // this happens on incorrectly configured pvvx ATC firmware - AddLog_P(LOG_LEVEL_ERROR,PSTR("%s: slot: %u - device type 0x%04x(%s) -> 0x%04x(%s) - check device is only sending one type of advert."),D_CMND_MI32, i, + AddLog_P(LOG_LEVEL_ERROR,PSTR("M32: %s: slot: %u - device type 0x%04x(%s) -> 0x%04x(%s) - check device is only sending one type of advert."),D_CMND_MI32, i, kMI32DeviceID[MIBLEsensors[i].type-1], kMI32DeviceType[MIBLEsensors[i].type-1], kMI32DeviceID[_type-1], kMI32DeviceType[_type-1]); MIBLEsensors[i].type = _type; } return i; } - //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("%s: i: %x %x %x %x %x %x"),D_CMND_MI32, MIBLEsensors[i].MAC[5], MIBLEsensors[i].MAC[4],MIBLEsensors[i].MAC[3],MIBLEsensors[i].MAC[2],MIBLEsensors[i].MAC[1],MIBLEsensors[i].MAC[0]); - //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("%s: n: %x %x %x %x %x %x"),D_CMND_MI32, mac[5], mac[4], mac[3],mac[2],mac[1],mac[0]); + //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: i: %x %x %x %x %x %x"),D_CMND_MI32, MIBLEsensors[i].MAC[5], MIBLEsensors[i].MAC[4],MIBLEsensors[i].MAC[3],MIBLEsensors[i].MAC[2],MIBLEsensors[i].MAC[1],MIBLEsensors[i].MAC[0]); + //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: n: %x %x %x %x %x %x"),D_CMND_MI32, mac[5], mac[4], mac[3],mac[2],mac[1],mac[0]); } - //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("%s: new sensor -> slot: %u"),D_CMND_MI32, MIBLEsensors.size()); - //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("%s: found new sensor"),D_CMND_MI32); + //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: new sensor -> slot: %u"),D_CMND_MI32, MIBLEsensors.size()); + //AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: found new sensor"),D_CMND_MI32); mi_sensor_t _newSensor; memset(&_newSensor, 0 , sizeof(_newSensor)); memcpy(_newSensor.MAC, mac, 6); @@ -1411,7 +1411,7 @@ uint32_t MIBLEgetSensorSlot(const uint8_t *mac, uint16_t _type, uint8_t counter) break; } MIBLEsensors.push_back(_newSensor); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: new %s at slot: %u"),D_CMND_MI32, kMI32DeviceType[_type-1],MIBLEsensors.size()-1); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s: new %s at slot: %u"),D_CMND_MI32, kMI32DeviceType[_type-1],MIBLEsensors.size()-1); MI32.mode.shallShowStatusInfo = 1; return MIBLEsensors.size()-1; }; @@ -1444,7 +1444,7 @@ void MI32StatusInfo() { int MI32scanCompleteCallback(NimBLEScanResults results){ // we actually don't need to do anything here.... - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: scancomplete")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Scan complete")); return 0; } @@ -1473,7 +1473,7 @@ void MI32Init(void) { // note: for operations, we will set individual callbacks in the operations we request //void registerForOpCallbacks(const char *tag, BLE_ESP32::OPCOMPLETE_CALLBACK* pFn); - AddLog_P(LOG_LEVEL_INFO,PSTR("MI32: init: request callbacks")); + AddLog_P(LOG_LEVEL_INFO,PSTR("M32: init: request callbacks")); MI32.period = Settings.tele_period; MI32.mode.init = 1; return; @@ -1496,19 +1496,19 @@ int MIParseBatt(int slot, uint8_t *data, int len){ MIBLEsensors[slot].bat = value; if(MIBLEsensors[slot].type==MI_FLORA){ if (len < 7){ - AddLog_P(LOG_LEVEL_ERROR,PSTR("FLORA: not enough bytes read for firmware?")); + AddLog_P(LOG_LEVEL_ERROR,PSTR("M32: FLORA: not enough bytes read for firmware?")); } else { memcpy(MIBLEsensors[slot].firmware, data+2, 5); MIBLEsensors[slot].firmware[5] = '\0'; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: FLORA Firmware: %s"),D_CMND_MI32,MIBLEsensors[slot].firmware); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s: FLORA Firmware: %s"),D_CMND_MI32,MIBLEsensors[slot].firmware); } } MIBLEsensors[slot].eventType.bat = 1; MIBLEsensors[slot].shallSendMQTT = 1; MI32.mode.shallTriggerTele = 1; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("Batt read for %s complete %d"), slotMAC, value); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Batt read for %s complete %d"), slotMAC, value); } else { - AddLog_P(LOG_LEVEL_ERROR,PSTR("Batt read for %s complete but out of range 1-101 (%d)"), slotMAC, value); + AddLog_P(LOG_LEVEL_ERROR,PSTR("M32: Batt read for %s complete but out of range 1-101 (%d)"), slotMAC, value); } return 0; @@ -1534,13 +1534,13 @@ void MI32ParseATCPacket(const uint8_t * _buf, uint32_t length, const uint8_t *ad //uint16_t battery_mv; // mV //uint8_t battery_level; // 0..100 % //uint8_t counter; // measurement count - //uint8_t flags; + //uint8_t flags; uint32_t _slot = MIBLEgetSensorSlot(addr, 0x0a1c, ppv_packet->counter); // This must be a hard-coded fake ID if(_slot==0xff) return; if ((_slot >= 0) && (_slot < MIBLEsensors.size())){ - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s:pvvx at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s:pvvx at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); MIBLEsensors[_slot].RSSI=RSSI; MIBLEsensors[_slot].needkey=KEY_NOT_REQUIRED; @@ -1557,7 +1557,7 @@ void MI32ParseATCPacket(const uint8_t * _buf, uint32_t length, const uint8_t *ad } return; } else { - AddLog_P(LOG_LEVEL_ERROR, PSTR("PVVX packet mac mismatch - ignored?")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: PVVX packet mac mismatch - ignored?")); return; } } @@ -1571,9 +1571,9 @@ void MI32ParseATCPacket(const uint8_t * _buf, uint32_t length, const uint8_t *ad if (memcmp(addrrev, _packet->MAC, 6)){ MI32_ReverseMAC(_packet->MAC); if (!memcmp(addrrev, _packet->MAC, 6)){ - AddLog_P(LOG_LEVEL_ERROR, PSTR("ATC packet with reversed MAC addr?")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: ATC packet with reversed MAC addr?")); } else { - AddLog_P(LOG_LEVEL_ERROR, PSTR("ATC packet with MAC addr mismatch - is this mesh?")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: ATC packet with MAC addr mismatch - is this mesh?")); memcpy(addrrev, _packet->MAC, 6); } addr = addrrev; @@ -1584,7 +1584,7 @@ void MI32ParseATCPacket(const uint8_t * _buf, uint32_t length, const uint8_t *ad if(_slot==0xff) return; if ((_slot >= 0) && (_slot < MIBLEsensors.size())){ - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); MIBLEsensors[_slot].RSSI=RSSI; MIBLEsensors[_slot].needkey=KEY_NOT_REQUIRED; @@ -1606,17 +1606,17 @@ void MI32ParseATCPacket(const uint8_t * _buf, uint32_t length, const uint8_t *ad //////////////////////////////////////////////////////////// // this SHOULD parse any MI payload. int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){ - struct mi_beacon_data_payload_data_t *pld = + struct mi_beacon_data_payload_data_t *pld = (struct mi_beacon_data_payload_data_t *) &parsed->payload.data; int res = 1; - + if (!parsed->payloadpresent){ return 0; } char tmp[20]; BLE_ESP32::dump(tmp, 20, (uint8_t*)&(parsed->payload), parsed->payload.size+3); - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("MI%d payload %s"), _slot, tmp); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: MI%d payload %s"), _slot, tmp); switch(parsed->payload.type){ case 0x01: // button press @@ -1624,7 +1624,7 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){ MIBLEsensors[_slot].eventType.Btn = 1; MI32.mode.shallTriggerTele = 1; break; - case 0x02: + case 0x02: res = 0; break; case 0x03: {// motion? 1 byte @@ -1636,22 +1636,22 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){ if(_tempFloat<60){ MIBLEsensors[_slot].temp=_tempFloat; MIBLEsensors[_slot].eventType.temp = 1; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode 4: temp updated")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 4: temp updated")); } else { - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode 4: temp ignored > 60 (%f)"), _tempFloat); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 4: temp ignored > 60 (%f)"), _tempFloat); } - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 4: U16: %u Temp"), _beacon.temp ); + // AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Mode 4: U16: %u Temp"), _beacon.temp ); } break; case 0x06: { float _tempFloat=(float)(pld->hum)/10.0f; if(_tempFloat<101){ MIBLEsensors[_slot].hum=_tempFloat; MIBLEsensors[_slot].eventType.hum = 1; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode 6: hum updated")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 6: hum updated")); } else { - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode 6: hum ignored > 101 (%f)"), _tempFloat); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 6: hum ignored > 101 (%f)"), _tempFloat); } - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 6: U16: %u Hum"), _beacon.hum); + // AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Mode 6: U16: %u Hum"), _beacon.hum); } break; case 0x07: MIBLEsensors[_slot].lux=pld->lux & 0x00ffffff; @@ -1659,19 +1659,19 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){ MIBLEsensors[_slot].eventType.noMotion = 1; } MIBLEsensors[_slot].eventType.lux = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); + // AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); break; case 0x08: MIBLEsensors[_slot].moisture=pld->moist; MIBLEsensors[_slot].eventType.moist = 1; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode 8: moisture updated")); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 8: U8: %u Moisture"), _beacon.moist); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 8: moisture updated")); + // AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Mode 8: U8: %u Moisture"), _beacon.moist); break; case 0x09: // 'conductivity' MIBLEsensors[_slot].fertility=pld->fert; MIBLEsensors[_slot].eventType.fert = 1; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode 9: fertility updated")); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 9: U16: %u Fertility"), _beacon.fert); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 9: fertility updated")); + // AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Mode 9: U16: %u Fertility"), _beacon.fert); break; case 0x0a: if(MI32.option.ignoreBogusBattery){ @@ -1683,31 +1683,31 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){ if(pld->bat<101){ MIBLEsensors[_slot].bat = pld->bat; MIBLEsensors[_slot].eventType.bat = 1; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode a: bat updated")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode a: bat updated")); } else { MIBLEsensors[_slot].bat = 100; MIBLEsensors[_slot].eventType.bat = 1; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode a: bat > 100 (%d)"), pld->bat); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode a: bat > 100 (%d)"), pld->bat); } - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode a: U8: %u %%"), _beacon.bat); + // AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Mode a: U8: %u %%"), _beacon.bat); break; case 0x0d:{ float _tempFloat=(float)(pld->HT.temp)/10.0f; if(_tempFloat < 60){ MIBLEsensors[_slot].temp = _tempFloat; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode d: temp updated")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode d: temp updated")); } else { - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode d: temp ignored > 60 (%f)"), _tempFloat); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode d: temp ignored > 60 (%f)"), _tempFloat); } _tempFloat=(float)(pld->HT.hum)/10.0f; if(_tempFloat < 100){ MIBLEsensors[_slot].hum = _tempFloat; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode d: hum updated")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode d: hum updated")); } else { - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("Mode d: hum ignored > 100 (%f)"), _tempFloat); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode d: hum ignored > 100 (%f)"), _tempFloat); } MIBLEsensors[_slot].eventType.tempHum = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); + // AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); } break; case 0x0f: if (parsed->payload.ten != 0) break; @@ -1718,7 +1718,7 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){ MIBLEsensors[_slot].eventType.lux = 1; MIBLEsensors[_slot].NMT = 0; MI32.mode.shallTriggerTele = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("PIR: primary"),MIBLEsensors[_slot].lux ); + // AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: PIR: primary"),MIBLEsensors[_slot].lux ); break; case 0x10:{ // 'formaldehide' const uint16_t f = uint16_t(parsed->payload.data[0]) | (uint16_t(parsed->payload.data[1]) << 8); @@ -1742,11 +1742,11 @@ int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){ MIBLEsensors[_slot].NMT = pld->NMT; MIBLEsensors[_slot].eventType.NMT = 1; MI32.mode.shallTriggerTele = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 17: NMT: %u seconds"), _beacon.NMT); + // AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Mode 17: NMT: %u seconds"), _beacon.NMT); } break; default: { - AddLog_P(LOG_LEVEL_DEBUG,PSTR("Unknown MI pld")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Unknown MI pld")); res = 0; } break; } @@ -1771,7 +1771,7 @@ void MI32ParseResponse(const uint8_t *buf, uint16_t bufsize, const uint8_t* addr MI32_ReverseMAC(addrrev); if (memcmp(addrrev, parsed.macdata.mac, 6)){ - AddLog_P(LOG_LEVEL_ERROR, PSTR("MI packet with MAC addr mismatch - is this mesh?")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: MI packet with MAC addr mismatch - is this mesh?")); memcpy(addrrev, parsed.macdata.mac, 6); MI32_ReverseMAC(addrrev); addr = addrrev; @@ -1781,11 +1781,11 @@ void MI32ParseResponse(const uint8_t *buf, uint16_t bufsize, const uint8_t* addr if(_slot==0xff) return; if ((_slot >= 0) && (_slot < MIBLEsensors.size())){ if (parsed.needkey != KEY_REQUIREMENT_UNKNOWN){ - MIBLEsensors[_slot].needkey = parsed.needkey; + MIBLEsensors[_slot].needkey = parsed.needkey; } MIBLEsensors[_slot].RSSI=RSSI; if (!res){ // - if the payload is not valid - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("MIParsePacket returned %d"), res); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: MIParsePacket returned %d"), res); return; } else { } @@ -1806,7 +1806,7 @@ void MI32removeMIBLEsensor(uint8_t* MAC){ TasAutoMutex localmutex(&slotmutex, "Mi32Rem"); MIBLEsensors.erase( std::remove_if( MIBLEsensors.begin() , MIBLEsensors.end(), [MAC]( mi_sensor_t _sensor )->bool - { return (memcmp(_sensor.MAC,MAC,6) == 0); } + { return (memcmp(_sensor.MAC,MAC,6) == 0); } ), end( MIBLEsensors ) ); } /***********************************************************************\ @@ -1814,14 +1814,14 @@ void MI32removeMIBLEsensor(uint8_t* MAC){ \***********************************************************************/ void MI32notifyHT_LY(int slot, char *_buf, int len){ - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); // the value 0b00 is 28.16 C? if(_buf[0] != 0 || _buf[1] != 0){ memcpy(&LYWSD0x_HT,(void *)_buf,sizeof(LYWSD0x_HT)); - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u, V: %u"),D_CMND_MI32,LYWSD0x_HT.temp,LYWSD0x_HT.hum, LYWSD0x_HT.volt); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: %s: T * 100: %u, H: %u, V: %u"),D_CMND_MI32,LYWSD0x_HT.temp,LYWSD0x_HT.hum, LYWSD0x_HT.volt); uint32_t _slot = slot; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("MIBLE: Sensor slot: %u"), _slot); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: MIBLE: Sensor slot: %u"), _slot); static float _tempFloat; _tempFloat=(float)(LYWSD0x_HT.temp)/100.0f; if(_tempFloat<60){ @@ -1831,7 +1831,7 @@ void MI32notifyHT_LY(int slot, char *_buf, int len){ _tempFloat=(float)LYWSD0x_HT.hum; if(_tempFloat<100){ MIBLEsensors[_slot].hum = _tempFloat; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("LYWSD0x: hum updated")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: LYWSD0x: hum updated")); } MIBLEsensors[_slot].eventType.tempHum = 1; if (MIBLEsensors[_slot].type == MI_LYWSD03MMC || MIBLEsensors[_slot].type == MI_MHOC401){ @@ -1875,17 +1875,17 @@ void MI32Every50mSecond(){ void MI32EverySecond(bool restart){ -// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("MI32: onesec")); +// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("M32: onesec")); MI32TimeoutSensors(); MI32ShowSomeSensors(); - // read a battery if + // read a battery if // MI32.batteryreader.slot < filled and !MI32.batteryreader.active readOneBat(); - // read a sensor if + // read a sensor if // MI32.sensorreader.slot < filled and !MI32.sensorreader.active // for sensors which need to get data through notify... readOneSensor(); @@ -1893,7 +1893,7 @@ void MI32EverySecond(bool restart){ if (MI32.secondsCounter >= MI32.period){ // only if we finished the last read if (MI32.sensorreader.slot >= MIBLEsensors.size()){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("kick off readOneSensor")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Kick off readOneSensor")); // kick off notification sensor reading every period. MI32.sensorreader.slot = 0; MI32.secondsCounter = 0; @@ -1903,11 +1903,11 @@ void MI32EverySecond(bool restart){ if (MI32.secondsCounter2 >= MI32.period){ if (MI32.mqttCurrentSlot >= MIBLEsensors.size()){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("kick off tele sending")); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Kick off tele sending")); MI32.mqttCurrentSlot = 0; MI32.secondsCounter2 = 0; } else { - AddLog_P(LOG_LEVEL_DEBUG,PSTR("hit tele time, restarted but not finished last - lost from slot %d")+MI32.mqttCurrentSlot); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Hit tele time, restarted but not finished last - lost from slot %d")+MI32.mqttCurrentSlot); MI32.mqttCurrentSlot = 0; MI32.secondsCounter2 = 0; } @@ -1968,15 +1968,15 @@ void CmndMi32Time(void) { if (MIBLEsensors.size() > slot) { int res = genericTimeWriteFn(slot); if (res > 0){ - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("MI32: will set Time")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: will set Time")); ResponseCmndNumber(slot); return; } if (res < 0) { - AddLog_P(LOG_LEVEL_ERROR, PSTR("MI32: cannot set Time on sensor type")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: cannot set Time on sensor type")); } if (res == 0) { - AddLog_P(LOG_LEVEL_ERROR, PSTR("MI32: cannot set Time right now")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: cannot set Time right now")); } } } @@ -2009,15 +2009,15 @@ void CmndMi32Unit(void) { // TOGGLE unit? int res = genericUnitWriteFn(slot, -1); if (res > 0){ - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("MI32: will toggle Unit")); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: will toggle Unit")); ResponseCmndNumber(slot); return; } if (res < 0) { - AddLog_P(LOG_LEVEL_ERROR, PSTR("MI32: cannot toggle Unit on sensor type")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: cannot toggle Unit on sensor type")); } if (res == 0) { - AddLog_P(LOG_LEVEL_ERROR, PSTR("MI32: cannot toggle Unit right now")); + AddLog_P(LOG_LEVEL_ERROR, PSTR("M32: cannot toggle Unit right now")); } } } @@ -2058,7 +2058,7 @@ void CmndMi32Block(void){ } break; default: case 1: - break; + break; } MI32BlockListResp(); return; @@ -2076,7 +2076,7 @@ void CmndMi32Block(void){ case 0: { //TasAutoMutex localmutex(&slotmutex, "Mi32Block2"); MIBLEBlockList.erase( std::remove_if( begin( MIBLEBlockList ), end( MIBLEBlockList ), [_MACasBytes]( MAC_t& _entry )->bool - { return (memcmp(_entry.buf,_MACasBytes.buf,6) == 0); } + { return (memcmp(_entry.buf,_MACasBytes.buf,6) == 0); } ), end( MIBLEBlockList ) ); } break; case 1: { @@ -2091,8 +2091,8 @@ void CmndMi32Block(void){ MIBLEBlockList.push_back(_MACasBytes); MI32removeMIBLEsensor(_MACasBytes.buf); } - // AddLog_P(LOG_LEVEL_INFO,PSTR("MI32: size of ilist: %u"), MIBLEBlockList.size()); - } break; + // AddLog_P(LOG_LEVEL_INFO,PSTR("M32: size of ilist: %u"), MIBLEBlockList.size()); + } break; } MI32BlockListResp(); } @@ -2126,7 +2126,7 @@ void MI32KeyListResp(){ ToHex_P(MIBLEbindKeys[i].MAC,6,tmp,20,0); char key[16*2+1]; ToHex_P(MIBLEbindKeys[i].key,16,key,33,0); - + ResponseAppend_P(PSTR("\"%s\":\"%s\""), tmp, key); } ResponseAppend_P(PSTR("}}")); @@ -2136,7 +2136,7 @@ void MI32KeyListResp(){ void CmndMi32Keys(void){ #ifdef BLE_ESP32_ALIASES int op = XdrvMailbox.index; - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("key %d %s"), op, XdrvMailbox.data); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Key %d %s"), op, XdrvMailbox.data); int res = -1; switch(op){ @@ -2175,7 +2175,7 @@ void CmndMi32Keys(void){ return; } - AddLog_P(LOG_LEVEL_ERROR,PSTR("Add key mac %s = key %s"), mac, key); + AddLog_P(LOG_LEVEL_ERROR,PSTR("M32: Add key mac %s = key %s"), mac, key); char tmp[20]; // convert mac back to string ToHex_P(addr,6,tmp,20,0); @@ -2186,7 +2186,7 @@ void CmndMi32Keys(void){ } while (p); if (added){ - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("Added %d Keys"), added); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Added %d Keys"), added); MI32KeyListResp(); } else { MI32KeyListResp(); @@ -2194,7 +2194,7 @@ void CmndMi32Keys(void){ return; } break; case 2:{ // clear - if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32Keys clearing %d"), MIBLEbindKeys.size()); + if (BLE_ESP32::BLEDebugMode > 0) AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Keys clearing %d"), MIBLEbindKeys.size()); for (int i = MIBLEbindKeys.size()-1; i >= 0; i--){ MIBLEbindKeys.pop_back(); } @@ -2232,7 +2232,7 @@ const char HTTP_MI32_HL[] PROGMEM = "{s}
{m}
{e}"; const char HTTP_NEEDKEY[] PROGMEM = "{s}%s %s{m} {e}"; - + const char HTTP_PAIRING[] PROGMEM = "{s}%s Pair Button Pressed{m} {e}"; @@ -2246,10 +2246,10 @@ const char HTTP_MI_KEY_STYLE[] PROGMEM = ""; #define D_MI32_KEY "MI32 Set Key" void HandleMI32Key(){ - AddLog_P(LOG_LEVEL_DEBUG, PSTR("HandleMI32Key hit")); - if (!HttpCheckPriviledgedAccess()) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("!HttpCheckPriviledgedAccess()")); - return; + AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: HandleMI32Key hit")); + if (!HttpCheckPriviledgedAccess()) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: !HttpCheckPriviledgedAccess()")); + return; } WSContentStart_P(PSTR(D_MI32_KEY)); WSContentSendStyle_P(HTTP_MI_KEY_STYLE); @@ -2286,13 +2286,13 @@ void MI32TimeoutSensors(){ // so block for as long as it takes. // PROBLEM: when we take this, it hangs the BLE loop. - // BUT, devicePresent uses the + // BUT, devicePresent uses the // remove devices for which the adverts have timed out for (int i = MIBLEsensors.size()-1; i >= 0 ; i--) { //if (MIBLEsensors[i].MAC[2] || MIBLEsensors[i].MAC[3] || MIBLEsensors[i].MAC[4] || MIBLEsensors[i].MAC[5]){ if (!BLE_ESP32::devicePresent(MIBLEsensors[i].MAC)){ uint8_t *mac = MIBLEsensors[i].MAC; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("MI32: dev no longer present MAC: %02x%02x%02x%02x%02x%02x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Dev no longer present MAC: %02x%02x%02x%02x%02x%02x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); TasAutoMutex localmutex(&slotmutex, "Mi32Timeout"); MIBLEsensors.erase(MIBLEsensors.begin() + i); } @@ -2313,7 +2313,7 @@ void MI32GetOneSensorJson(int slot){ ResponseAppend_P(PSTR("\"MAC\":\"%02x%02x%02x%02x%02x%02x\""), p->MAC[0], p->MAC[1], p->MAC[2], p->MAC[3], p->MAC[4], p->MAC[5]); - + if((!MI32.mode.triggeredTele && !MI32.option.minimalSummary)||MI32.mode.triggeredTele){ bool tempHumSended = false; if(p->feature.tempHum){ @@ -2497,7 +2497,7 @@ void MI32ShowSomeSensors(){ } ResponseAppend_P(PSTR("}")); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - //AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: show some %d %s"),D_CMND_MI32, MI32.mqttCurrentSlot, TasmotaGlobal.mqtt_data); + //AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s: show some %d %s"),D_CMND_MI32, MI32.mqttCurrentSlot, TasmotaGlobal.mqtt_data); #ifdef USE_RULES RulesTeleperiod(); // Allow rule based HA messages @@ -2547,7 +2547,7 @@ void MI32ShowTriggeredSensors(){ if (cnt){ // if we got one, then publish ResponseAppend_P(PSTR("}")); MqttPublishPrefixTopic_P(STAT, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: triggered %d %s"),D_CMND_MI32, sensor, TasmotaGlobal.mqtt_data); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s: triggered %d %s"),D_CMND_MI32, sensor, TasmotaGlobal.mqtt_data); #ifdef USE_RULES RulesTeleperiod(); // Allow rule based HA messages @@ -2565,7 +2565,7 @@ void MI32Show(bool json) // don't detect half-added ones here int numsensors = MIBLEsensors.size(); - if (json) { + if (json) { // TELE JSON messages now do nothing here, apart from set MI32.mqttCurrentSlot // which will trigger send next second of up to 4 sensors, then the next four in the next second, etc. //MI32.mqttCurrentSlot = 0; @@ -2576,7 +2576,7 @@ void MI32Show(bool json) static uint16_t _counter = 0; int32_t i = _page * MI32.perPage; uint32_t j = i + MI32.perPage; - + if (j+1 > numsensors){ j = numsensors; }