"
// xdrv_01_mqtt.ino
-#define D_FINGERPRINT "TLS-Fingerabdruck wird verifiziert…"
-#define D_TLS_CONNECT_FAILED_TO "TLS-Verbindung fehlgeschlagen an"
+#define D_FINGERPRINT "TLS-Fingerabdruck wird verifiziert"
+#define D_TLS_CONNECT_FAILED_TO "TLS-Verbindung fehlgeschlagen"
#define D_RETRY_IN "Erneuter Versuch in"
#define D_VERIFIED "verifiziert mit Fingerabdruck"
#define D_INSECURE "unsichere Verbindung aufgrund ungültigen Fingerabdrucks"
@@ -445,7 +446,7 @@
#define D_3_RESPONSE_PACKETS_SENT "3 Antwortpakete gesendet"
// xdrv_07_domoticz.ino
-#define D_DOMOTICZ_PARAMETERS "Domoticz-Einstellungen"
+#define D_DOMOTICZ_PARAMETERS "Domoticz"
#define D_DOMOTICZ_IDX "Idx"
#define D_DOMOTICZ_KEY_IDX "Key Idx"
#define D_DOMOTICZ_SWITCH_IDX "Switch Idx"
@@ -463,8 +464,8 @@
#define D_DOMOTICZ_UPDATE_TIMER "Zeitplan-Update"
// xdrv_09_timers.ino
-#define D_CONFIGURE_TIMER "Zeitplaneinstellungen"
-#define D_TIMER_PARAMETERS "Zeitplaneinstellungen"
+#define D_CONFIGURE_TIMER "Zeitplan"
+#define D_TIMER_PARAMETERS "Zeitplan"
#define D_TIMER_ENABLE "Zeitpläne aktivieren"
#define D_TIMER_ARM "Aktiv"
#define D_TIMER_TIME "Uhrzeit"
@@ -474,12 +475,12 @@
#define D_TIMER_ACTION "Aktion"
// xdrv_10_knx.ino
-#define D_CONFIGURE_KNX "KNX-Einstellungen"
-#define D_KNX_PARAMETERS "KNX-Parameter"
+#define D_CONFIGURE_KNX "KNX"
+#define D_KNX_PARAMETERS "Parameter"
#define D_KNX_GENERAL_CONFIG "Allgemein"
#define D_KNX_PHYSICAL_ADDRESS "Physikalische Adresse"
-#define D_KNX_PHYSICAL_ADDRESS_NOTE "(Muss einmalig im KNX-Netzwerk sein)"
-#define D_KNX_ENABLE "KNX aktivieren"
+#define D_KNX_PHYSICAL_ADDRESS_NOTE "Muss eindeutig im KNX-Netzwerk sein"
+#define D_KNX_ENABLE "aktivieren"
#define D_KNX_GROUP_ADDRESS_TO_WRITE "Daten zum Senden an Gruppenadresse"
#define D_ADD "Hinzufügen"
#define D_DELETE "Löschen"
@@ -490,7 +491,7 @@
#define D_KNX_COMMAND_READ "Lesen"
#define D_KNX_COMMAND_OTHER "Andere"
#define D_SENT_TO "gesendet an"
-#define D_KNX_WARNING "Die Gruppenadresse (0/0/0) ist reserviert und kann nicht verwendet werden"
+#define D_KNX_WARNING "Die Gruppenadresse (0/0/0) ist reserviert"
#define D_KNX_ENHANCEMENT "Erweiterte Kommunikation"
#define D_KNX_TX_SLOT "KNX TX"
#define D_KNX_RX_SLOT "KNX RX"
@@ -498,15 +499,15 @@
#define D_KNX_RX_SCENE "KNX SCENE RX"
// xdrv_23_zigbee
-#define D_ZIGBEE_PERMITJOIN_ACTIVE "Gerätekopplung erlaubt"
-#define D_ZIGBEE_MAPPING_TITLE "Tasmota Zigbee Karte"
+#define D_ZIGBEE_PERMITJOIN_ACTIVE "Kopplung erlaubt"
+#define D_ZIGBEE_MAPPING_TITLE "Zigbee Karte"
#define D_ZIGBEE_NOT_STARTED "Zigbee nicht gestartet"
#define D_ZIGBEE_MAPPING_IN_PROGRESS_SEC "Karte in Erstellung (%d s․ verbleibend)"
#define D_ZIGBEE_MAPPING_NOT_PRESENT "Keine Karte"
-#define D_ZIGBEE_MAP_REFRESH "Zigbee Karte erneuern"
-#define D_ZIGBEE_MAP "Zigbee Karte"
-#define D_ZIGBEE_PERMITJOIN "Zigbee Kopplung ein"
-#define D_ZIGBEE_GENERATE_KEY "Erzeuge zufälligen Zigbee Netzwerkschlüssel"
+#define D_ZIGBEE_MAP_REFRESH "Karte erneuern"
+#define D_ZIGBEE_MAP "Karte"
+#define D_ZIGBEE_PERMITJOIN "Kopplung ein"
+#define D_ZIGBEE_GENERATE_KEY "Erzeuge zufälligen Netzwerkschlüssel"
#define D_ZIGBEE_UNKNOWN_DEVICE "Unbekanntes Gerät"
#define D_ZIGBEE_UNKNOWN_ATTRIBUTE "Unbekanntes Attribut"
#define D_ZIGBEE_UNKNOWN_ENDPOINT "Unkbekannter Endpunkt"
@@ -516,19 +517,19 @@
#define D_ZIGBEE_TOO_MANY_CLUSTERS "Nur eine Cluster-ID pro Kommando"
#define D_ZIGBEE_CONFLICTING_ENDPOINTS "Kollidierende Endpunkte"
#define D_ZIGBEE_WRONG_DELIMITER "Falscher Delimeter für Payload"
-#define D_ZIGBEE_UNRECOGNIZED_COMMAND "Unerkanntes Zigbee Kommando: %s"
+#define D_ZIGBEE_UNRECOGNIZED_COMMAND "Unerkanntes Kommando: %s"
#define D_ZIGBEE_TOO_MANY_COMMANDS "Nur 1 Kommando zulässig (%d)"
#define D_ZIGBEE_NO_ATTRIBUTE "Kein Attribut in der Liste"
#define D_ZIGBEE_UNSUPPORTED_ATTRIBUTE_TYPE "Nicht unterstützter Attributtyp"
#define D_ZIGBEE_JSON_REQUIRED "Konfiguration muss JSON-basiert sein"
#define D_ZIGBEE_RESET_1_OR_2 "1 oder 2 für Reset"
-#define D_ZIGBEE_EEPROM_FOUND_AT_ADDRESS "ZBBridge EEPROM gefunden an Adresse"
-#define D_ZIGBEE_RANDOMIZING_ZBCONFIG "Zufällige Zigbee Parameter erstellt, Überprüfung mit 'ZbConfig'"
+#define D_ZIGBEE_EEPROM_FOUND_AT_ADDRESS "EEPROM gefunden an Adresse"
+#define D_ZIGBEE_RANDOMIZING_ZBCONFIG "Zufällige Parameter erstellt, Überprüfung mit 'ZbConfig'"
// xdrv_89_dali.ino
#define D_SENSOR_DALI_RX "Dali RX"
#define D_SENSOR_DALI_TX "Dali TX"
-#define D_CONFIGURE_DALI "DALI-Einstellungen"
+#define D_CONFIGURE_DALI "Dali Einstellungen"
// xdrv_03_energy.ino
#define D_ENERGY_TODAY "Energie heute"
@@ -571,10 +572,10 @@
#define D_THERMOSTAT_AUTOTUNE_HYBRID "Autotune (Hybrid)"
// xdrv_79_esp32_ble.ino
-#define D_CONFIGURE_BLE "BLE-Einstellungen"
-#define D_BLE_PARAMETERS "Bluetooth-Einstellungen"
-#define D_MQTT_BLE_ENABLE "Bluetooth aktivieren"
-#define D_MQTT_BLE_ACTIVESCAN "Aktiv scannen (*)"
+#define D_CONFIGURE_BLE "BLE"
+#define D_BLE_PARAMETERS "BLE Parameter"
+#define D_BLE_ENABLE "BLE aktivieren"
+#define D_BLE_ACTIVESCAN "Aktiv scannen (*)"
#define D_BLE_DEVICES "Erkannte Geräte"
#define D_BLE_REMARK "Mit (*) markierte Geräte werden nicht gespeichert."
diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h
index cfe3e1a39cd4..4a75cae60c70 100644
--- a/tasmota/language/el_GR.h
+++ b/tasmota/language/el_GR.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Ποιότητα αέρα"
#define D_AP "AP" // Access Point
#define D_AS "ως"
+#define D_AT "at"
#define D_AUTO "ΑΥΤΟΜΑΤΟ"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h
index f379bd8e410d..19312fd4e3a0 100644
--- a/tasmota/language/en_GB.h
+++ b/tasmota/language/en_GB.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Air quality"
#define D_AP "AP" // Access Point
#define D_AS "as"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -280,16 +281,16 @@
#define D_CONSOLE "Console"
#define D_CONFIRM_RESTART "Confirm Restart"
-#define D_CONFIGURE_MODULE "Configure Module"
-#define D_CONFIGURE_WIFI "Configure WiFi"
-#define D_CONFIGURE_MQTT "Configure MQTT"
-#define D_CONFIGURE_DOMOTICZ "Configure Domoticz"
-#define D_CONFIGURE_LOGGING "Configure Logging"
-#define D_CONFIGURE_OTHER "Configure Other"
+#define D_CONFIGURE_MODULE "Module"
+#define D_CONFIGURE_WIFI "WiFi"
+#define D_CONFIGURE_MQTT "MQTT"
+#define D_CONFIGURE_DOMOTICZ "Domoticz"
+#define D_CONFIGURE_LOGGING "Logging"
+#define D_CONFIGURE_OTHER "Other"
#define D_CONFIRM_RESET_CONFIGURATION "Confirm Reset Configuration"
-#define D_RESET_CONFIGURATION "Reset Configuration"
-#define D_BACKUP_CONFIGURATION "Backup Configuration"
-#define D_RESTORE_CONFIGURATION "Restore Configuration"
+#define D_RESET_CONFIGURATION "Reset"
+#define D_BACKUP_CONFIGURATION "Backup"
+#define D_RESTORE_CONFIGURATION "Restore"
#define D_START_RESTORE "Start restore"
#define D_MAIN_MENU "Main Menu"
@@ -356,7 +357,7 @@
#define D_SINGLE_DEVICE "single device"
#define D_MULTI_DEVICE "multi device"
-#define D_CONFIGURE_TEMPLATE "Configure Template"
+#define D_CONFIGURE_TEMPLATE "Template"
#define D_TEMPLATE_PARAMETERS "Template parameters"
#define D_TEMPLATE_NAME "Name"
#define D_BASE_TYPE "Based on"
@@ -386,10 +387,10 @@
#define D_FLASH_CHIP_SIZE "Flash Size"
#define D_FREE_PROGRAM_SPACE "Free Program Space"
-#define D_UPGRADE_BY_WEBSERVER "Upgrade by web server"
+#define D_UPGRADE_BY_WEBSERVER "Use web server"
#define D_OTA_URL "OTA Url"
#define D_START_UPGRADE "Start upgrade"
-#define D_UPGRADE_BY_FILE_UPLOAD "Upgrade by file upload"
+#define D_UPGRADE_BY_FILE_UPLOAD "Use file upload"
#define D_UPLOAD_FACTORY "Switching to safeboot partition"
#define D_UPLOAD_STARTED "Upload started"
#define D_UPGRADE_STARTED "Upgrade started"
@@ -463,7 +464,7 @@
#define D_DOMOTICZ_UPDATE_TIMER "Update timer"
// xdrv_09_timers.ino
-#define D_CONFIGURE_TIMER "Configure Timer"
+#define D_CONFIGURE_TIMER "Timer"
#define D_TIMER_PARAMETERS "Timer parameters"
#define D_TIMER_ENABLE "Enable Timers"
#define D_TIMER_ARM "Enable"
@@ -474,7 +475,7 @@
#define D_TIMER_ACTION "Action"
// xdrv_10_knx.ino
-#define D_CONFIGURE_KNX "Configure KNX"
+#define D_CONFIGURE_KNX "KNX"
#define D_KNX_PARAMETERS "KNX Parameters"
#define D_KNX_GENERAL_CONFIG "General"
#define D_KNX_PHYSICAL_ADDRESS "Physical Address"
@@ -528,7 +529,7 @@
// xdrv_89_dali.ino
#define D_SENSOR_DALI_RX "Dali RX"
#define D_SENSOR_DALI_TX "Dali TX"
-#define D_CONFIGURE_DALI "Config DALI"
+#define D_CONFIGURE_DALI "DALI"
// xdrv_03_energy.ino
#define D_ENERGY_TODAY "Energy Today"
@@ -546,7 +547,7 @@
#define D_DOMOTICZ_SHUTTER "Shutter"
// xdrv_28_pcf8574.ino
-#define D_CONFIGURE_PCF8574 "Configure PCF8574"
+#define D_CONFIGURE_PCF8574 "PCF8574"
#define D_PCF8574_PARAMETERS "PCF8574 parameters"
#define D_INVERT_PORTS "Invert Ports"
#define D_DEVICE "Device"
@@ -571,10 +572,10 @@
#define D_THERMOSTAT_AUTOTUNE_HYBRID "Autotune (Hybrid)"
// xdrv_79_esp32_ble.ino
-#define D_CONFIGURE_BLE "Configure BLE"
+#define D_CONFIGURE_BLE "BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
@@ -627,7 +628,7 @@
#define D_HX_CAL_DONE "Calibrated"
#define D_HX_CAL_FAIL "Calibration failed"
#define D_RESET_HX711 "Reset Scale"
-#define D_CONFIGURE_HX711 "Configure Scale"
+#define D_CONFIGURE_HX711 "Scale"
#define D_HX711_PARAMETERS "Scale parameters"
#define D_ITEM_WEIGHT "Item weight"
#define D_REFERENCE_WEIGHT "Reference weight"
diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h
index ff49b30587ec..3d4ad847ea45 100644
--- a/tasmota/language/es_ES.h
+++ b/tasmota/language/es_ES.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Calidad del Aire"
#define D_AP "AP" // Access Point
#define D_AS "como"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Bat" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h
index 1b07f5e8ba57..e87a1193cbb5 100644
--- a/tasmota/language/fr_FR.h
+++ b/tasmota/language/fr_FR.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Qualité de l'Air"
#define D_AP "AP" // Access Point
#define D_AS "comme"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h
index 189e2297a952..e00a178c2a5a 100644
--- a/tasmota/language/fy_NL.h
+++ b/tasmota/language/fy_NL.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Luchtkwaliteit"
#define D_AP "AP" // Access Point
#define D_AS "als"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h
index 6a04467ac2a1..1179797b6912 100644
--- a/tasmota/language/he_HE.h
+++ b/tasmota/language/he_HE.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "איכות אוויר"
#define D_AP "AP" // Access Point
#define D_AS "-כ"
+#define D_AT "at"
#define D_AUTO "אוטומטי"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h
index 9f07ff5cc27b..64b10fb22b96 100644
--- a/tasmota/language/hu_HU.h
+++ b/tasmota/language/hu_HU.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Levegőminőség"
#define D_AP "AP" // Access Point
#define D_AS "mint"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h
index 1c614468e4e0..00254ca7a2cd 100644
--- a/tasmota/language/it_IT.h
+++ b/tasmota/language/it_IT.h
@@ -28,7 +28,7 @@
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
* Use online command Prefix to translate cmnd, stat and tele.
*
- * Updated until v9.4.0.1 - Last update 15.11.2024
+ * Updated until v9.4.0.1 - Last update 07.12.2024
\*********************************************************************/
#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Qualità dell'aria"
#define D_AP "AP" // Access Point
#define D_AS "come"
+#define D_AT "in"
#define D_AUTO "AUTO"
#define D_BATTERY "Batteria"
#define D_BATT "Batt" // Short for Battery
@@ -386,10 +387,10 @@
#define D_FLASH_CHIP_SIZE "Dimensione flash"
#define D_FREE_PROGRAM_SPACE "Memoria libera programma"
-#define D_UPGRADE_BY_WEBSERVER "Aggiornamento via server web"
+#define D_UPGRADE_BY_WEBSERVER "Aggiorna via server web"
#define D_OTA_URL "URL OTA"
#define D_START_UPGRADE "Esegui aggiornamento"
-#define D_UPGRADE_BY_FILE_UPLOAD "Aggiornamento tramite file locale"
+#define D_UPGRADE_BY_FILE_UPLOAD "Aggiorna tramite file locale"
#define D_UPLOAD_FACTORY "Passaggio a partizione avvio sicuro"
#define D_UPLOAD_STARTED "Caricamento..."
#define D_UPGRADE_STARTED "Aggiornamento..."
@@ -571,10 +572,10 @@
#define D_THERMOSTAT_AUTOTUNE_HYBRID "Regolazione automatica (ibrida)"
// xdrv_79_esp32_ble.ino
-#define D_CONFIGURE_BLE "Configura BLE"
+#define D_CONFIGURE_BLE "BLE"
#define D_BLE_PARAMETERS "Impostazioni Bluetooth"
-#define D_MQTT_BLE_ENABLE "Abilita Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Abilita scansione attiva (*)"
+#define D_BLE_ENABLE "Abilita Bluetooth"
+#define D_BLE_ACTIVESCAN "Abilita scansione attiva (*)"
#define D_BLE_DEVICES "Scansione dispositivi"
#define D_BLE_REMARK "gli elementi segnati con (*) non sono memorizzati in config"
diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h
index d47bee94792a..314f246bc4d9 100644
--- a/tasmota/language/ko_KO.h
+++ b/tasmota/language/ko_KO.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "공기질"
#define D_AP "AP" // Access Point
#define D_AS "as"
+#define D_AT "at"
#define D_AUTO "자동"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h
index dac4c6eee3e4..9a27938c9f93 100644
--- a/tasmota/language/nl_NL.h
+++ b/tasmota/language/nl_NL.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Lucht kwaliteit"
#define D_AP "AP" // Access Point
#define D_AS "als"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h
index e48f1e00dacf..c1c9650adda0 100644
--- a/tasmota/language/pl_PL.h
+++ b/tasmota/language/pl_PL.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Jakość powietrza"
#define D_AP "AP" // Access Point
#define D_AS "jak"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Bateria"
#define D_BATT "Bat" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Konfiguracja BLE"
#define D_BLE_PARAMETERS "Ustawienia Bluetooth"
-#define D_MQTT_BLE_ENABLE "Załącz Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Załącz Aktywne Skanowanie(*)"
+#define D_BLE_ENABLE "Załącz Bluetooth"
+#define D_BLE_ACTIVESCAN "Załącz Aktywne Skanowanie(*)"
#define D_BLE_DEVICES "Znalezione Urządzenia"
#define D_BLE_REMARK "rzeczy oznaczone (*) nie są zapisywane w konfiguracji"
diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h
index 4dfaf2058113..a940abfd6d8b 100644
--- a/tasmota/language/pt_BR.h
+++ b/tasmota/language/pt_BR.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Qualidade do ar"
#define D_AP "Ponto de acesso" // Ponto de Acesso
#define D_AS "como"
+#define D_AT "at"
#define D_AUTO "Auto"
#define D_BATTERY "Battery"
#define D_BATT "Bat" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h
index 4735ffe3f7cc..fe4a4714d194 100644
--- a/tasmota/language/pt_PT.h
+++ b/tasmota/language/pt_PT.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Qualidade do Ar"
#define D_AP "AP" // Ponto de Acesso
#define D_AS "como"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h
index 96a0f506dee7..3408016a9114 100644
--- a/tasmota/language/ro_RO.h
+++ b/tasmota/language/ro_RO.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Calitatea aerului"
#define D_AP "AP" // Access Point
#define D_AS "as"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h
index 0a70d129cff4..ef655916db74 100644
--- a/tasmota/language/ru_RU.h
+++ b/tasmota/language/ru_RU.h
@@ -57,6 +57,7 @@
#define D_AIR_QUALITY "Качество воздуха"
#define D_AP "AP" // Access Point
#define D_AS "как"
+#define D_AT "at"
#define D_AUTO "АВТО"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -574,8 +575,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h
index 91ba8d635a58..a906456cb124 100644
--- a/tasmota/language/sk_SK.h
+++ b/tasmota/language/sk_SK.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Kvalita vzduchu"
#define D_AP "AP" // Access Point
#define D_AS "ako"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h
index 8bf23eb8da24..0d160b4f2777 100644
--- a/tasmota/language/sv_SE.h
+++ b/tasmota/language/sv_SE.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Luftkvalitet"
#define D_AP "AP" // Access Point
#define D_AS "som"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h
index d73fbd1abb90..320c058cfd04 100644
--- a/tasmota/language/tr_TR.h
+++ b/tasmota/language/tr_TR.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Hava Kalitesi"
#define D_AP "AP" // Access Point
#define D_AS "as"
+#define D_AT "at"
#define D_AUTO "OTOMATIK"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h
index 0cf2ac6909d6..7d64719579e3 100644
--- a/tasmota/language/uk_UA.h
+++ b/tasmota/language/uk_UA.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Якість повітря"
#define D_AP "Точка доступу" // Access Point
#define D_AS "як"
+#define D_AT "at"
#define D_AUTO "АВТО"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h
index 4b625a7c030c..42c68305dcce 100644
--- a/tasmota/language/vi_VN.h
+++ b/tasmota/language/vi_VN.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "Chất lượng không khí"
#define D_AP "Mạng wifi" // Access Point
#define D_AS "với tên gọi"
+#define D_AT "at"
#define D_AUTO "AUTO"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h
index 70d2c5d971ec..cd5692717060 100644
--- a/tasmota/language/zh_CN.h
+++ b/tasmota/language/zh_CN.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "空气质量"
#define D_AP "AP" // Access Point
#define D_AS "名称:"
+#define D_AT "at"
#define D_AUTO "自动"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h
index 2b721c2ab5fa..29125d6ea3d9 100644
--- a/tasmota/language/zh_TW.h
+++ b/tasmota/language/zh_TW.h
@@ -56,6 +56,7 @@
#define D_AIR_QUALITY "空氣品質"
#define D_AP "存取點" // Access Point
#define D_AS "名稱:"
+#define D_AT "at"
#define D_AUTO "自動"
#define D_BATTERY "Battery"
#define D_BATT "電池" // Short for Battery
@@ -573,8 +574,8 @@
// xdrv_79_esp32_ble.ino
#define D_CONFIGURE_BLE "Configure BLE"
#define D_BLE_PARAMETERS "Bluetooth Settings"
-#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
-#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_ENABLE "Enable Bluetooth"
+#define D_BLE_ACTIVESCAN "Enable Active Scan(*)"
#define D_BLE_DEVICES "Devices Seen"
#define D_BLE_REMARK "items marked (*) are not stored in config"
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index d6822db2fd2a..12a1ec8f9d31 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -211,7 +211,7 @@
#define COLOR_TIMER_TAB_TEXT "#fff" // [WebColor17] Config timer tab text color - White
#define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark gray
#define COLOR_TITLE_TEXT "#000" // [WebColor19] Title text color - Whiteish
-#define COLOR_BUTTON_OFF "#08405e" // [WebColor20] Button color when off - Darkest blueish
+#define COLOR_BUTTON_OFF "#a1d9f7" // [WebColor20] Button color when off - Light blue
*/
// Dark theme
// WebColor {"WebColor":["#eaeaea","#252525","#4f4f4f","#000","#ddd","#65c115","#1f1f1f","#ff5661","#008000","#faffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#faffff","#999","#eaeaea","#08405e"]}
@@ -234,7 +234,7 @@
#define COLOR_TIMER_TAB_TEXT "#faffff" // [WebColor17] Config timer tab text color - Very pale (mostly white) cyan.
#define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark gray
#define COLOR_TITLE_TEXT "#eaeaea" // [WebColor19] Title text color - Very light gray
-#define COLOR_BUTTON_OFF "#08405e" // [WebColor20] Button color when off - Darkest blueish
+#define COLOR_BUTTON_OFF "#08405e" // [WebColor20] Button color when off - Darkest blueish
// -- KNX -----------------------------------------
#define KNX_ENABLED false // [Knx_Enabled] Enable KNX protocol
@@ -562,14 +562,7 @@
// #define MAGICSWITCH_MASKING_WINDOW_LEN 5 // Overridable masking window (in number of 50ms loops)
// -- Optional light modules ----------------------
-#define USE_LIGHT // Add support for light control
-#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by //
-// #define USE_WS2812_DMA // ESP8266 only, DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow
- #define USE_WS2812_RMT 0 // ESP32 only, hardware RMT support (default). Specify the RMT channel 0..7. This should be preferred to software bit bang.
-// #define USE_WS2812_I2S 0 // ESP32 only, hardware I2S support. Specify the I2S channel 0..2. This is exclusive from RMT. By default, prefer RMT support
-// #define USE_WS2812_INVERTED // Use inverted data signal
- #define USE_WS2812_HARDWARE NEO_HW_WS2812 // Hardware type (NEO_HW_WS2812, NEO_HW_WS2812X, NEO_HW_WS2813, NEO_HW_SK6812, NEO_HW_LC8812, NEO_HW_APA106, NEO_HW_P9813)
- #define USE_WS2812_CTYPE NEO_GRB // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW)
+#define USE_LIGHT // Add support for light control
#define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas
#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code)
#define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code)
@@ -583,6 +576,18 @@
#define USE_DGR_LIGHT_SEQUENCE // Add support for device group light sequencing (requires USE_DEVICE_GROUPS) (+0k2 code)
//#define USE_LSC_MCSL // Add support for GPE Multi color smart light as sold by Action in the Netherlands (+1k1 code)
+// -- Optional adressable leds ----------------------
+#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by //
+// -------- below is for ESP8266 only
+// #define USE_WS2812_DMA // ESP8266 only, DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow
+ #define USE_WS2812_RMT 0 // ESP32 only, hardware RMT support (default). Specify the RMT channel 0..7. This should be preferred to software bit bang.
+// #define USE_WS2812_I2S 0 // ESP32 only, hardware I2S support. Specify the I2S channel 0..2. This is exclusive from RMT. By default, prefer RMT support
+// #define USE_WS2812_INVERTED // Use inverted data signal
+ #define USE_WS2812_HARDWARE NEO_HW_WS2812 // Hardware type (NEO_HW_WS2812, NEO_HW_WS2812X, NEO_HW_WS2813, NEO_HW_SK6812, NEO_HW_LC8812, NEO_HW_APA106, NEO_HW_P9813)
+ #define USE_WS2812_CTYPE NEO_GRB // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW)
+// -------- below if for ESP32x only -- ESP32 uses a lightweight library instead of NeoPixelBus
+ // #define USE_WS2812_FORCE_NEOPIXELBUS // this option forces to use NeoPixelBus (like ESP866), which disables Berry support and limits features -- DO NOT USE unless you have a good reason
+
// #define USE_LIGHT_ARTNET // Add support for DMX/ArtNet via UDP on port 6454 (+3.5k code)
#define USE_LIGHT_ARTNET_MCAST 239,255,25,54 // Multicast address used to listen: 239.255.25.54
@@ -775,7 +780,7 @@
#define MTX_ADDRESS7 0x00 // [DisplayAddress7] I2C address of seventh 8x8 matrix module
#define MTX_ADDRESS8 0x00 // [DisplayAddress8] I2C address of eigth 8x8 matrix module
#define USE_DISPLAY_SEVENSEG // [DisplayModel 11] [I2cDriver47] Enable sevenseg display (I2C 0x70-0x77) (<+11k code)
-// #define USE_DISPLAY_SEVENSEG_COMMON_ANODE // Enable support for common anode sevenseg displays
+// #define USE_DISPLAY_SEVENSEG_COMMON_ANODE // Enable support for common anode sevenseg displays
// Multiple sevenseg displays are logically arranged vertically with MTX_ADDRESS1 at y=0,
// MTX_ADDRESS2 at y=1, up to MTX_ADDRESS8 at y=7
// Command: DisplayText [yn]8888
@@ -783,7 +788,7 @@
// Each segment may be address Command: DisplayText [xn]m
// where n is 0..4 (4 digits and middle :) and m is decimal for bitmap of which segment to turn on.
// Reference: https://cdn-learn.adafruit.com/downloads/pdf/adafruit-led-backpack.pdf
- // #define SEVENSEG_ADDRESS1 0x70 // No longer used. Use MTX_ADDRESS1 - MTX_ADDRESS8 instead to specify I2C address of sevenseg displays
+// #define SEVENSEG_ADDRESS1 0x70 // No longer used. Use MTX_ADDRESS1 - MTX_ADDRESS8 instead to specify I2C address of sevenseg displays
// #define USE_DISPLAY_SH1106 // [DisplayModel 7] [I2cDriver6] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D)
// #define USE_DISPLAY_TM1650 // [DisplayModel 20] [I2cDriver74] Enable TM1650 display (I2C addresses 0x24 - 0x27 and 0x34 - 0x37)
// #define USE_DT_VARS // Display variables that are exposed in JSON MQTT strings e.g. in TelePeriod messages.
@@ -793,10 +798,12 @@
#endif // USE_I2C
-// #define USE_DISPLAY // Add I2C/TM1637/MAX7219 Display Support (+2k code)
-// #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637 Seven Segment Display Module (4-6 digits)
-// #define USE_DISPLAY_MAX7219 // [DisplayModel 15] Enable MAX7219 Seven Segment Display Module (8 digits)
-// #define USE_DISPLAY_MAX7219_MATRIX // [DisplayModel 19] Enable MAX7219 8x8 Matrix Display
+//#define USE_DISPLAY // Add I2C/TM1637/MAX7219 Display Support (+2k code)
+// #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637 Seven Segment Display Module (4-6 digits)
+// #define USE_DISPLAY_MAX7219 // [DisplayModel 15] Enable MAX7219 Seven Segment Display Module (8 digits)
+// #define USE_DISPLAY_MAX7219_MATRIX // [DisplayModel 19] Enable MAX7219 8x8 Matrix Display
+// #define USE_DISPLAY_TM1640 // [DisplayModel 13] Enable TM1640 module Seven Segment Display Module (stub)
+// #define USE_IOTTIMER // Enable TM1640 based IotTimer
// -- Universal Display Driver ---------------------------------
// #define USE_UNIVERSAL_DISPLAY // New universal display driver for both I2C and SPI
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index 94b611e1ae3e..923bd7556064 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -409,6 +409,7 @@ void setup(void) {
#endif // DISABLE_ESP32_BROWNOUT
#ifndef FIRMWARE_SAFEBOOT
+#ifndef DISABLE_PSRAMCHECK
#ifndef CORE32SOLO1
// restore GPIO5/18 or 16/17 if no PSRAM is found which may be used by Ethernet among others
if (!FoundPSRAM()) {
@@ -422,6 +423,7 @@ void setup(void) {
}
}
#endif // CORE32SOLO1
+#endif // DISABLE_PSRAMCHECK
#endif // FIRMWARE_SAFEBOOT
#endif // CONFIG_IDF_TARGET_ESP32
#endif // ESP32
diff --git a/tasmota/tasmota_support/support.ino b/tasmota/tasmota_support/support.ino
index 1c8609a5e9b0..4caa4bdf4d6e 100755
--- a/tasmota/tasmota_support/support.ino
+++ b/tasmota/tasmota_support/support.ino
@@ -832,12 +832,29 @@ int32_t UpdateDevicesPresent(int32_t change) {
else if (devices_present >= POWER_SIZE) { // Support up to uint32_t as bitmask
difference = devices_present - POWER_SIZE;
devices_present = POWER_SIZE;
- AddLog(LOG_LEVEL_DEBUG, PSTR("APP: Max number of devices reached"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("APP: Max 32 devices supported"));
}
TasmotaGlobal.devices_present = devices_present;
+
+// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DVC: DevicesPresent %d, Change %d"), TasmotaGlobal.devices_present, change);
+
return difference;
}
+void DevicesPresentNonDisplayOrLight(uint32_t &devices_claimed) {
+ uint32_t display_and_lights = 0;
+#ifdef USE_LIGHT
+ display_and_lights += LightDevices(); // Skip light(s)
+#endif // USE_LIGHT
+#ifdef USE_DISPLAY
+ display_and_lights += DisplayDevices(); // Skip display
+#endif // USE_DISPLAY
+ uint32_t devices_present = TasmotaGlobal.devices_present - display_and_lights;
+ if (devices_claimed > devices_present) {
+ devices_claimed = devices_present; // Reduce amount of claimed devices
+ }
+}
+
char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option)
{
strncpy_P(dest, S_RSLT_POWER, size); // POWER
@@ -1134,6 +1151,8 @@ int GetCommandCode(char* destination, size_t destination_size, const char* needl
bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void), const uint8_t *synonyms = nullptr);
bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void), const uint8_t *synonyms) {
+ SHOW_FREE_MEM(PSTR("DecodeCommand"));
+
GetTextIndexed(XdrvMailbox.command, CMDSZ, 0, haystack); // Get prefix if available
int prefix_length = strlen(XdrvMailbox.command);
if (prefix_length) {
@@ -1953,7 +1972,7 @@ uint32_t JsonParsePath(JsonParserObject *jobj, const char *spath, char delim, fl
uint32_t res = 0;
const char *cp = spath;
#ifdef DEBUG_JSON_PARSE_PATH
- AddLog(LOG_LEVEL_INFO, PSTR("JSON: parsing json key: %s from json: %s"), cp, spath);
+ AddLog(LOG_LEVEL_INFO, PSTR("JSON: parsing json key: %s from json: %s"), cp, jpath);
#endif
JsonParserObject obj = *jobj;
JsonParserObject lastobj = obj;
@@ -2640,11 +2659,12 @@ void AddLogData(uint32_t loglevel, const char* log_data, const char* log_data_pa
// Each entry has this format: [index][loglevel][log data]['\1']
// Truncate log messages longer than MAX_LOGSZ which is the log buffer size minus 64 spare
+ char *too_long = nullptr;
uint32_t log_data_len = strlen(log_data) + strlen(log_data_payload) + strlen(log_data_retained);
- char too_long[TOPSZ];
if (log_data_len > MAX_LOGSZ) {
- snprintf_P(too_long, sizeof(too_long) - 20, PSTR("%s%s"), log_data, log_data_payload); // 20 = strlen("... 123456 truncated")
- snprintf_P(too_long, sizeof(too_long), PSTR("%s... %d truncated"), too_long, log_data_len);
+ too_long = (char*)malloc(TOPSZ); // Use heap in favour of stack
+ snprintf_P(too_long, TOPSZ - 20, PSTR("%s%s"), log_data, log_data_payload); // 20 = strlen("... 123456 truncated")
+ snprintf_P(too_long, TOPSZ, PSTR("%s... %d truncated"), too_long, log_data_len);
log_data = too_long;
log_data_payload = empty;
log_data_retained = empty;
@@ -2665,6 +2685,7 @@ void AddLogData(uint32_t loglevel, const char* log_data, const char* log_data_pa
}
snprintf_P(TasmotaGlobal.log_buffer, LOG_BUFFER_SIZE, PSTR("%s%c%c%s%s%s%s\1"),
TasmotaGlobal.log_buffer, TasmotaGlobal.log_buffer_pointer++, '0'+loglevel, mxtime, log_data, log_data_payload, log_data_retained);
+ if (too_long) { free(too_long); }
TasmotaGlobal.log_buffer_pointer &= 0xFF;
if (!TasmotaGlobal.log_buffer_pointer) {
TasmotaGlobal.log_buffer_pointer++; // Index 0 is not allowed as it is the end of char string
diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino
index 2c25cdd2884c..727da327391e 100644
--- a/tasmota/tasmota_support/support_command.ino
+++ b/tasmota/tasmota_support/support_command.ino
@@ -348,6 +348,13 @@ void ExecuteCommand(const char *cmnd, uint32_t source)
CommandHandler(stopic, svalue, strlen(svalue));
}
+bool GetFallbackTopicFlag(char* topicBuf) {
+ // Use this function to free CommandHandler stack space from TOPSZ
+ char stemp1[TOPSZ];
+ GetFallbackTopic_P(stemp1, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/
+ return (!strncmp(topicBuf, stemp1, strlen(stemp1)));
+}
+
/********************************************************************************************/
// topicBuf: /power1 dataBuf: toggle = Console command
@@ -369,9 +376,7 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) {
}
}
- char stemp1[TOPSZ];
- GetFallbackTopic_P(stemp1, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/
- TasmotaGlobal.fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1)));
+ TasmotaGlobal.fallback_topic_flag = GetFallbackTopicFlag(topicBuf);
char *type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type)
@@ -416,11 +421,13 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) {
}
Response_P(PSTR("_1")); // Signal error message for either Command Error or Command Unknown
- char number[12];
- char command_line[64];
- snprintf_P(command_line, sizeof(command_line), PSTR("%s%s%s%s"),
+ char stemp1[16];
+// char command_line[64];
+// snprintf_P(command_line, sizeof(command_line), PSTR("%s%s%s%s"),
+ char *command_line = (char*)malloc(64); // Use heap in favour of stack
+ snprintf_P(command_line, 64, PSTR("%s%s%s%s"),
type,
- (index != 1) ? itoa(index, number, 10) : "",
+ (index != 1) ? itoa(index, stemp1, 10) : "",
(data_len) ? " " : "",
(data_len) ? (binary_data) ? HexToString((uint8_t*)dataBuf, data_len).c_str() : EscapeJSONString(dataBuf).c_str() : "");
@@ -483,6 +490,7 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) {
}
ResponseAppend_P(PSTR(",\"Input\":\"%s\"}"), command_line);
}
+ free(command_line);
if (ResponseLength()) {
if (TasmotaGlobal.no_mqtt_response){ // If it is activated, Tasmota will not publish MQTT messages, but it will proccess event trigger rules
diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino
index 90a1717878eb..d8cceedc4b2c 100644
--- a/tasmota/tasmota_support/support_tasmota.ino
+++ b/tasmota/tasmota_support/support_tasmota.ino
@@ -466,7 +466,11 @@ void SetPowerOnState(void)
for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) {
#ifdef ESP8266
if (!Settings->flag3.no_power_feedback && // SetOption63 - Don't scan relay power state at restart - #5594 and #5663
- !TasmotaGlobal.power_on_delay) { // SetOption47 - Delay switching relays to reduce power surge at power on
+ !TasmotaGlobal.power_on_delay // SetOption47 - Delay switching relays to reduce power surge at power on
+#ifdef USE_SHUTTER
+ && !Settings->flag3.shutter_mode // SetOption80 - Enable shutter support
+#endif // USE_SHUTTER
+ ) {
if ((port < MAX_RELAYS) && PinUsed(GPIO_REL1, port)) {
if (bitRead(TasmotaGlobal.rel_bistable, port)) {
port++; // Skip both bistable relays as always 0
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino
index 86a88d6d4c99..6e2383bd4d56 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino
@@ -266,6 +266,9 @@ const char HTTP_HEAD_STYLE3[] PROGMEM =
"%s " // Module name
"%s "; // Device name
+const char HTTP_MENU_HEAD[] PROGMEM =
+ "
%s ";
+
const char HTTP_MSG_SLIDER_SHUTTER[] PROGMEM =
""
"%s "
@@ -481,13 +484,11 @@ ESP8266WebServer *Webserver;
struct WEB {
String chunk_buffer = ""; // Could be max 2 * CHUNKED_BUFFER_SIZE
uint32_t upload_size = 0;
+ uint32_t light_shutter_button_mask;
+ uint32_t buttons_non_light_non_shutter;
uint32_t slider_update_time = 0;
int slider[LST_MAX];
-#ifdef ESP8266
- int8_t shutter_slider[MAX_SHUTTERS];
-#else // ESP32
int8_t shutter_slider[16]; // MAX_SHUTTERS_ESP32
-#endif // ESP8266
uint16_t upload_error = 0;
uint8_t state = HTTP_OFF;
uint8_t upload_file_type;
@@ -984,11 +985,12 @@ void WSContentSendStyle_P(const char* formatP, ...) {
WebColor(COL_TEXT_WARNING),
#endif
WebColor(COL_TITLE),
- (Web.initial_config) ? "" : (Settings->flag5.gui_module_name) ? "" : ModuleName().c_str(), SettingsTextEscaped(SET_DEVICENAME).c_str());
+ (Web.initial_config) ? "" : (Settings->flag5.gui_module_name) ? "" : ModuleName().c_str(), // SetOption141 - (GUI) Disable display of GUI module name (1)
+ (Settings->flag6.gui_device_name) ? "" : SettingsTextEscaped(SET_DEVICENAME).c_str()); // SetOption163 - (GUI) Disable display of GUI device name (1)
// SetOption53 - Show hostname and IP address in GUI main menu
#if (RESTART_AFTER_INITIAL_WIFI_CONFIG)
- if (Settings->flag3.gui_hostname_ip) { // SetOption53 - (GUI) Show hostname and IP address in GUI main menu
+ if (Settings->flag3.gui_hostname_ip) { // SetOption53 - (GUI) Show hostname and IP address in GUI main menu
#else
if ( Settings->flag3.gui_hostname_ip || ( (WiFi.getMode() == WIFI_AP_STA) && (!Web.initial_config) ) ) {
#endif
@@ -1258,31 +1260,37 @@ int32_t IsShutterWebButton(uint32_t idx) {
/*-------------------------------------------------------------------------------------------*/
-void WebGetDeviceCounts(uint32_t &buttons_non_light, uint32_t &buttons_non_light_non_shutter, uint32_t &shutter_button) {
- buttons_non_light = TasmotaGlobal.devices_present;
+void WebGetDeviceCounts(void) {
+ Web.buttons_non_light_non_shutter = TasmotaGlobal.devices_present;
+ Web.light_shutter_button_mask = 0; // Bitmask for each light and/or shutter button
#ifdef USE_LIGHT
// Chk for reduced toggle buttons used by lights
if (TasmotaGlobal.light_type) {
- // Find and skip light buttons (Lights are controlled by the last TasmotaGlobal.devices_present (or 2))
- buttons_non_light = LightDevice() -1;
+ // Find and skip light buttons
+ uint32_t light_device = LightDevice();
+ uint32_t light_devices = LightDevices();
+ for (uint32_t button_idx = light_device; button_idx < (light_device + light_devices); button_idx++) {
+ Web.buttons_non_light_non_shutter--;
+ Web.light_shutter_button_mask |= (1 << (button_idx -1)); // Set button bit in bitmask
+ }
}
#endif // USE_LIGHT
- buttons_non_light_non_shutter = buttons_non_light;
- shutter_button = 0; // Bitmask for each button
#ifdef USE_SHUTTER
// Chk for reduced toggle buttons used by shutters
- // Find and skip dedicated shutter buttons
- if (buttons_non_light && Settings->flag3.shutter_mode) { // SetOption80 - Enable shutter support
- for (uint32_t button_idx = 1; button_idx <= buttons_non_light; button_idx++) {
+ if (Settings->flag3.shutter_mode) { // SetOption80 - Enable shutter support
+ // Find and skip dedicated shutter buttons
+ for (uint32_t button_idx = 1; button_idx <= TasmotaGlobal.devices_present; button_idx++) {
if (IsShutterWebButton(button_idx) != 0) {
- buttons_non_light_non_shutter--;
- shutter_button |= (1 << (button_idx -1)); // Set button bit in bitmask
+ Web.buttons_non_light_non_shutter--;
+ Web.light_shutter_button_mask |= (1 << (button_idx -1)); // Set button bit in bitmask
}
}
}
#endif // USE_SHUTTER
+
+// AddLog(LOG_LEVEL_DEBUG, PSTR("HTP: DP %d, BNLNS %d, SB %08X"), TasmotaGlobal.devices_present, Web.buttons_non_light_non_shutter, Web.light_shutter_button_mask);
}
#ifdef USE_LIGHT
@@ -1365,14 +1373,9 @@ void HandleRoot(void) {
#ifndef FIRMWARE_MINIMAL
if (TasmotaGlobal.devices_present) {
- uint32_t buttons_non_light;
- uint32_t buttons_non_light_non_shutter;
- uint32_t shutter_button;
- WebGetDeviceCounts(buttons_non_light, buttons_non_light_non_shutter, shutter_button);
- uint32_t button_idx = 1;
-
- if (buttons_non_light_non_shutter) { // Any non light AND non shutter button
- // Display toggle buttons
+ WebGetDeviceCounts();
+
+ if (Web.buttons_non_light_non_shutter) { // Any non light AND non shutter button - Show toggle buttons
WSContentSend_P(HTTP_TABLE100); // "
"
WSContentSend_P(PSTR(""));
@@ -1391,18 +1394,14 @@ void HandleRoot(void) {
#endif // USE_SONOFF_IFAN
const uint32_t max_columns = 8;
- uint32_t rows = buttons_non_light_non_shutter / max_columns;
- if (buttons_non_light_non_shutter % max_columns) { rows++; }
- uint32_t cols = buttons_non_light_non_shutter / rows;
- if (buttons_non_light_non_shutter % rows) { cols++; }
+ uint32_t rows = Web.buttons_non_light_non_shutter / max_columns;
+ if (Web.buttons_non_light_non_shutter % max_columns) { rows++; }
+ uint32_t cols = Web.buttons_non_light_non_shutter / rows;
+ if (Web.buttons_non_light_non_shutter % rows) { cols++; }
uint32_t button_ptr = 0;
- for (button_idx = 1; button_idx <= buttons_non_light; button_idx++) {
-
-#ifdef USE_SHUTTER
- if (bitRead(shutter_button, button_idx -1)) { continue; } // Skip non-sequential shutter button
-#endif // USE_SHUTTER
-
+ for (uint32_t button_idx = 1; button_idx <= TasmotaGlobal.devices_present; button_idx++) {
+ if (bitRead(Web.light_shutter_button_mask, button_idx -1)) { continue; } // Skip non-sequential light and/or shutter button
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), button_idx);
WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / cols, button_idx, button_idx,
@@ -1419,30 +1418,29 @@ void HandleRoot(void) {
}
#ifdef USE_SHUTTER
- if (shutter_button) { // Any button bit set
- WSContentSend_P(HTTP_TABLE100); // ""
-
- int32_t ShutterWebButton;
- uint32_t shutter_button_idx = 1;
- for (uint32_t shutter_idx = 0; shutter_idx < TasmotaGlobal.shutters_present ; shutter_idx++) {
- while ((0 == shutter_button & (1 << (shutter_button_idx -1)))) { shutter_button_idx++; }
-
+ if (TasmotaGlobal.shutters_present) { // Any shutter present - Show shutter buttons and slider
+ WSContentSend_P(HTTP_TABLE100); // ""
+ uint32_t shutter_button_idx;
+ uint32_t shutter_button_idx_temp;
+ for (uint32_t shutter_idx = 0; shutter_idx < TasmotaGlobal.shutters_present; shutter_idx++) {
WSContentSend_P(PSTR(""));
- shutter_button_idx++; // Left button is next button first (down)
+ uint32_t shutter_options = ShutterGetOptions(shutter_idx);
+ shutter_button_idx = ShutterGetStartRelay(shutter_idx) +1; // Left button is next button first (down)
for (uint32_t j = 0; j < 2; j++) {
- ShutterWebButton = IsShutterWebButton(shutter_button_idx);
- WSContentSend_P(HTTP_DEVICE_CONTROL, 15, shutter_button_idx, shutter_button_idx,
- ((ShutterGetOptions(abs(ShutterWebButton)-1) & 2) /* is locked */ ? "-" :
- ((ShutterGetOptions(abs(ShutterWebButton)-1) & 8) /* invert web buttons */ ? ((ShutterWebButton>0) ? "▼" : "▲") : ((ShutterWebButton>0) ? "▲" : "▼"))),
+ shutter_button_idx_temp = (shutter_options & 1) ? shutter_button_idx + (j * 2) - 1 : shutter_button_idx; // Invert index
+// AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: j %d, shutter_idx %d, shutter_button_idx %d, shutter_idx %d, shutter_button_idx_temp %d"), j, shutter_idx, shutter_button_idx, shutter_idx, shutter_button_idx_temp);
+ WSContentSend_P(HTTP_DEVICE_CONTROL, 15, shutter_button_idx_temp, shutter_button_idx_temp,
+ ((shutter_options & 2) ? "-" : // Is locked
+ ((shutter_options & 1) ? (j ? "▼" : "▲") : (j ? "▲" : "▼"))), // Invert web buttons
"");
- if (1 == j) { break; }
+ if (1 == j) { break; } // Both buttons shown
- shutter_button_idx--; // Right button is previous button (up)
+ shutter_button_idx--; // Right button is previous button (up)
bool set_button = ((shutter_button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(shutter_button_idx -1)));
snprintf_P(stemp, sizeof(stemp), PSTR("Shutter %d"), shutter_idx +1);
uint32_t shutter_real_to_percent_position = ShutterRealToPercentPosition(-9999, shutter_idx);
- Web.shutter_slider[shutter_idx] = (ShutterGetOptions(shutter_idx) & 1) ? (100 - shutter_real_to_percent_position) : shutter_real_to_percent_position;
+ Web.shutter_slider[shutter_idx] = (shutter_options & 1) ? (100 - shutter_real_to_percent_position) : shutter_real_to_percent_position;
WSContentSend_P(HTTP_MSG_SLIDER_SHUTTER,
(set_button) ? HtmlEscape(GetWebButton(shutter_button_idx -1)).c_str() : stemp,
shutter_idx +1,
@@ -1450,30 +1448,28 @@ void HandleRoot(void) {
shutter_idx +1);
}
WSContentSend_P(PSTR(" "));
- shutter_button_idx += 2;
-
}
WSContentSend_P(PSTR("
"));
-
- if (1 == button_idx) {
- button_idx = shutter_button_idx;
- }
}
#endif // USE_SHUTTER
#ifdef USE_LIGHT
- if (TasmotaGlobal.light_type) {
- WSContentSend_P(HTTP_TABLE100); // ""
+ if (TasmotaGlobal.light_type) { // Any light - Show light button and slider(s)
+ uint32_t light_device = LightDevice();
+ uint32_t light_devices = LightDevices();
+ uint32_t button_idx = light_device;
+
+ WSContentSend_P(HTTP_TABLE100); // ""
uint8_t light_subtype = TasmotaGlobal.light_type &7;
if (!Settings->flag3.pwm_multi_channels) { // SetOption68 0 - Enable multi-channels PWM instead of Color PWM
- bool split_white = ((LST_RGBW <= light_subtype) && (TasmotaGlobal.devices_present > 1) && (Settings->param[P_RGB_REMAP] & 128)); // Only on RGBW or RGBCW and SetOption37 128
+ bool split_white = ((LST_RGBW <= light_subtype) && (light_devices > 1) && (Settings->param[P_RGB_REMAP] & 128)); // Only on RGBW or RGBCW and SetOption37 128
if ((LST_COLDWARM == light_subtype) || ((LST_RGBCW == light_subtype) && !split_white)) {
WebSliderColdWarm();
}
- if (light_subtype > 2) { // No W or CW
+ if (light_subtype > 2) { // No W or CW
uint16_t hue;
uint8_t sat;
LightGetHSB(&hue, &sat, nullptr);
@@ -1541,7 +1537,7 @@ void HandleRoot(void) {
uint32_t width = 100;
WSContentSend_P(PSTR(""));
- if (button_idx <= TasmotaGlobal.devices_present) {
+ if (button_idx < (light_device + light_devices)) {
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
char first[2];
snprintf_P(first, sizeof(first), PSTR("%s"), PSTR(D_BUTTON_TOGGLE));
@@ -1567,9 +1563,8 @@ void HandleRoot(void) {
WSContentSend_P(PSTR(" "));
}
} else { // Settings->flag3.pwm_multi_channels - SetOption68 1 - Enable multi-channels PWM instead of Color PWM
- uint32_t pwm_channels = TasmotaGlobal.devices_present - buttons_non_light;
- stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; // d0
- for (uint32_t i = 0; i < pwm_channels; i++) {
+ stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; // e0
+ for (uint32_t i = 0; i < light_devices; i++) {
bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1)));
char first[2];
snprintf_P(first, sizeof(first), PSTR("%s"), PSTR(D_BUTTON_TOGGLE));
@@ -1689,7 +1684,7 @@ bool HandleRootStatusRefresh(void) {
char svalue[32]; // Command and number parameter
char webindex[5]; // WebGetArg name
- WebGetArg(PSTR("o"), tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed
+ WebGetArg(PSTR("o"), tmp, sizeof(tmp)); // 1 - 32 Device number for button Toggle or Fanspeed
if (strlen(tmp)) {
ShowWebSource(SRC_WEBGUI);
uint32_t device = atoi(tmp);
@@ -1729,9 +1724,9 @@ bool HandleRootStatusRefresh(void) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp);
ExecuteWebCommand(svalue);
}
- uint32_t light_device = LightDevice(); // Channel number offset
- uint32_t pwm_channels = (TasmotaGlobal.light_type & 7) > LST_MAX ? LST_MAX : (TasmotaGlobal.light_type & 7);
- for (uint32_t j = 0; j < pwm_channels; j++) {
+ uint32_t light_device = LightDevice(); // Channel number offset
+ uint32_t light_devices = LightDevices(); // Number of channels
+ for (uint32_t j = 0; j < light_devices; j++) {
snprintf_P(webindex, sizeof(webindex), PSTR("e%d"), j +1);
WebGetArg(webindex, tmp, sizeof(tmp)); // 0 - 100 percent
if (strlen(tmp)) {
@@ -1891,33 +1886,36 @@ bool HandleRootStatusRefresh(void) {
XsnsXdrvCall(FUNC_WEB_SENSOR);
WSContentSend_P(PSTR("
"));
- if (!Settings->flag6.gui_no_state_text) { // SetOption161 - (GUI) Disable display of state text (1)
- bool show_state = (TasmotaGlobal.devices_present);
+ if (!Settings->flag6.gui_no_state_text) { // SetOption161 - (GUI) Disable display of state text (1)
+ if (!Web.buttons_non_light_non_shutter) { // Might still be zero on restart so chk if we have at least one
+ WebGetDeviceCounts();
+ }
+ if ((Web.buttons_non_light_non_shutter > 0) &&
+ ( Web.buttons_non_light_non_shutter <= 8)) { // We need at least one non light AND non shutter button
+ WSContentSend_P(PSTR("{t}"));
#ifdef USE_SONOFF_IFAN
- if (IsModuleIfan()) { show_state = false; }
+ if (IsModuleIfan()) {
+ WSContentSend_P(HTTP_DEVICE_STATE, 36, (bitRead(TasmotaGlobal.power, 0)) ? PSTR("bold") : PSTR("normal"), 54, GetStateText(bitRead(TasmotaGlobal.power, 0)));
+ uint32_t fanspeed = GetFanspeed();
+ snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed);
+ WSContentSend_P(HTTP_DEVICE_STATE, 64, (fanspeed) ? PSTR("bold") : PSTR("normal"), 54, (fanspeed) ? svalue : GetStateText(0));
+ } else {
#endif // USE_SONOFF_IFAN
- if (show_state) {
- uint32_t buttons_non_light;
- uint32_t buttons_non_light_non_shutter;
- uint32_t shutter_button;
- WebGetDeviceCounts(buttons_non_light, buttons_non_light_non_shutter, shutter_button);
-
- if (buttons_non_light_non_shutter <= 8) { // Any non light AND non shutter button
- WSContentSend_P(PSTR("{t} "));
- uint32_t cols = buttons_non_light_non_shutter;
+ uint32_t cols = Web.buttons_non_light_non_shutter;
uint32_t fontsize = (cols < 5) ? 70 - (cols * 8) : 32;
- for (uint32_t idx = 1; idx <= buttons_non_light; idx++) {
-
-#ifdef USE_SHUTTER
- if (bitRead(shutter_button, idx -1)) { continue; } // Skip non-sequential shutter button
-#endif // USE_SHUTTER
-
- snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(TasmotaGlobal.power, idx -1));
- WSContentSend_P(HTTP_DEVICE_STATE, 100 / cols, (bitRead(TasmotaGlobal.power, idx -1)) ? PSTR("bold") : PSTR("normal"), fontsize,
- (cols < 5) ? GetStateText(bitRead(TasmotaGlobal.power, idx -1)) : svalue);
+ uint32_t button_ptr = 0;
+ for (uint32_t button_idx = 1; button_idx <= TasmotaGlobal.devices_present; button_idx++) {
+ if (bitRead(Web.light_shutter_button_mask, button_idx -1)) { continue; } // Skip non-sequential shutter button
+ bool power_state = bitRead(TasmotaGlobal.power, button_idx -1);
+ snprintf_P(svalue, sizeof(svalue), PSTR("%d"), power_state);
+ WSContentSend_P(HTTP_DEVICE_STATE, 100 / cols, (power_state) ? PSTR("bold") : PSTR("normal"), fontsize, (cols < 5) ? GetStateText(power_state) : svalue);
+ button_ptr++;
+ if (button_ptr >= Web.buttons_non_light_non_shutter) { break; }
}
- WSContentSend_P(PSTR("
"));
+#ifdef USE_SONOFF_IFAN
}
+#endif // USE_SONOFF_IFAN
+ WSContentSend_P(PSTR("
"));
}
}
@@ -1946,6 +1944,7 @@ void HandleConfiguration(void) {
WSContentStart_P(PSTR(D_CONFIGURATION));
WSContentSendStyle();
+ WSContentSend_P(HTTP_MENU_HEAD, D_CONFIGURATION);
WSContentButton(BUTTON_MODULE);
WSContentButton(BUTTON_WIFI);
@@ -2828,13 +2827,14 @@ void HandleInformation(void) {
// }1 =
// }2 =
WSContentSend_P(HTTP_SCRIPT_INFO_BEGIN);
+ WSContentSend_P(HTTP_MENU_HEAD, D_INFORMATION);
WSContentSend_P(PSTR(""));
WSContentSend_P(PSTR(D_PROGRAM_VERSION "}2%s %s %s"), TasmotaGlobal.version, TasmotaGlobal.image_name, GetCodeCores().c_str());
WSContentSend_P(PSTR("}1" D_BUILD_DATE_AND_TIME "}2%s"), GetBuildDateAndTime().c_str());
WSContentSend_P(PSTR("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_CORE_RELEASE "/%s"), ESP.getSdkVersion());
WSContentSend_P(PSTR("}1" D_UPTIME "}2%s"), GetUptime().c_str());
#ifdef ESP8266
- WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d at 0x%X"), Settings->save_flag, GetSettingsAddress());
+ WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d " D_AT " 0x%X"), Settings->save_flag, GetSettingsAddress());
#endif // ESP8266
#ifdef ESP32
WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d"), Settings->save_flag);
@@ -3114,6 +3114,8 @@ void HandleUpgradeFirmware(void) {
WSContentStart_P(PSTR(D_FIRMWARE_UPGRADE));
WSContentSendStyle();
+ WSContentSend_P(HTTP_MENU_HEAD, D_FIRMWARE_UPGRADE);
+
WSContentSend_P(HTTP_FORM_UPG, SettingsTextEscaped(SET_OTAURL).c_str());
#ifdef ESP32
if (EspSingleOtaPartition() && !EspRunningFactoryPartition()) {
@@ -3651,6 +3653,7 @@ void HandleManagement(void) {
WSContentStart_P(PSTR(D_MANAGEMENT));
WSContentSendStyle();
+ WSContentSend_P(HTTP_MENU_HEAD, D_MANAGEMENT);
WSContentButton(BUTTON_CONSOLE);
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino b/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino
index 9fe1517a9da6..3d2d998e8450 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino
@@ -566,7 +566,8 @@ bool MqttPublishLib(const char* topic, const uint8_t* payload, unsigned int plen
MqttClient.endPublish();
- yield(); // #3313
+// yield(); // #3313
+ delay(0);
return true;
}
@@ -701,15 +702,14 @@ void MqttPublishLoggingAsync(bool refresh) {
void MqttPublishPayload(const char* topic, const char* payload, uint32_t binary_length, bool retained) {
// Publish payload string or binary when binary_length set with optional retained
-
- SHOW_FREE_MEM(PSTR("MqttPublish"));
+ SHOW_FREE_MEM(PSTR("MqttPublishPayload"));
bool binary_data = (binary_length > 0);
if (!binary_data) {
binary_length = strlen(payload);
}
- if (Settings->flag4.mqtt_no_retain) { // SetOption104 - Disable all MQTT retained messages, some brokers don't support it: AWS IoT, Losant
+ if (Settings->flag4.mqtt_no_retain) { // SetOption104 - Disable all MQTT retained messages, some brokers don't support it: AWS IoT, Losant
retained = false; // Some brokers don't support retained, they will disconnect if received
}
@@ -793,14 +793,30 @@ void MqttPublishPayloadPrefixTopic_P(uint32_t prefix, const char* subtopic, cons
prefix 5 = stat using subtopic or RESULT
prefix 6 = tele using subtopic or RESULT
*/
- char romram[64];
+ SHOW_FREE_MEM(PSTR("MqttPublishPayloadPrefixTopic_P"));
+/*
+ char romram[64]; // Claim 64 bytes from 4k stack
snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings->flag.mqtt_response) ? S_RSLT_RESULT : subtopic); // SetOption4 - Switch between MQTT RESULT or COMMAND
UpperCase(romram, romram);
prefix &= 3;
- char stopic[TOPSZ];
+ char stopic[TOPSZ]; // Claim TOPSZ bytes from 4k stack
GetTopic_P(stopic, prefix, TasmotaGlobal.mqtt_topic, romram);
MqttPublishPayload(stopic, payload, binary_length, retained);
+*/
+ // Reduce important stack usage by 200 bytes but adding 52 bytes code
+ char *romram = (char*)malloc(64); // Claim 64 bytes from 20k heap
+ strcpy_P(romram, ((prefix > 3) && !Settings->flag.mqtt_response) ? S_RSLT_RESULT : subtopic);
+ UpperCase(romram, romram);
+
+ prefix &= 3;
+ char *htopic = (char*)malloc(TOPSZ); // Claim TOPSZ bytes from 16k heap
+ GetTopic_P(htopic, prefix, TasmotaGlobal.mqtt_topic, romram);
+ char stopic[strlen_P(htopic) +1]; // Claim only strlen_P bytes from 4k stack
+ strcpy_P(stopic, htopic);
+ free(htopic); // Free 16k heap from TOPSZ bytes
+ free(romram); // Free 16k heap from 64 bytes
+ MqttPublishPayload(stopic, payload, binary_length, retained);
#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT)
if ((prefix > 0) && (Settings->flag4.awsiot_shadow) && (Mqtt.connected)) { // placeholder for SetOptionXX
@@ -856,6 +872,8 @@ void MqttPublishPayloadPrefixTopicRulesProcess_P(uint32_t prefix, const char* su
void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained) {
// Publish //> default ResponseData string with optional retained
+ SHOW_FREE_MEM(PSTR("MqttPublishPrefixTopic_P"));
+
MqttPublishPayloadPrefixTopic_P(prefix, subtopic, ResponseData(), 0, retained);
}
@@ -867,6 +885,8 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic) {
void MqttPublishPrefixTopicRulesProcess_P(uint32_t prefix, const char* subtopic, bool retained) {
// Publish //> default ResponseData string with optional retained
// then process rules
+ SHOW_FREE_MEM(PSTR("MqttPublishPrefixTopicRulesProcess_P"));
+
MqttPublishPrefixTopic_P(prefix, subtopic, retained);
XdrvRulesProcess(0);
}
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino
index a26644832b07..aae18311e74d 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino
@@ -278,7 +278,9 @@ void EnergyUpdateToday(void) {
int32_t delta = Energy->kWhtoday_delta[i] / 1000;
delta_sum_balanced += delta;
Energy->kWhtoday_delta[i] -= (delta * 1000);
- Energy->kWhtoday[i] += delta;
+ if (!Settings->flag6.no_export_energy_today || (delta > 0)) { // SetOption162 - (Energy) Do not add export energy to energy today (1)
+ Energy->kWhtoday[i] += delta;
+ }
if (delta < 0) { // Export energy
Energy->kWhtoday_export[i] += (delta *-1);
if (Energy->kWhtoday_export[i] > 100) {
@@ -368,14 +370,14 @@ void EnergyUpdateTotal(void) {
}
}
- if ((Energy->total[i] < (Energy->import_active[i] - 0.01f)) && // We subtract a little offset of 10Wh to avoid continuous updates
- Settings->flag3.hardware_energy_total) { // SetOption72 - Enable hardware energy total counter as reference (#6561)
+ if (Settings->flag3.hardware_energy_total && // SetOption72 - Enable hardware energy total counter as reference (#6561)
+ fabs(Energy->total[i] - Energy->import_active[i]) > 0.01f) { // to avoid continuous updates, check for difference of min 10Wh
// The following calculation allows total usage (Energy->import_active[i]) up to +/-2147483.647 kWh
RtcSettings.energy_kWhtotal_ph[i] = (int32_t)((Energy->import_active[i] * 1000) - ((Energy->kWhtoday_offset[i] + Energy->kWhtoday[i]) / 100));
Settings->energy_kWhtotal_ph[i] = RtcSettings.energy_kWhtotal_ph[i];
Energy->total[i] = Energy->import_active[i];
Settings->energy_kWhtotal_time = (!Energy->kWhtoday_offset[i]) ? LocalTime() : Midnight();
- // AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value"));
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: EnergyTotal updated with hardware value"));
}
}
@@ -407,7 +409,7 @@ void Energy200ms(void) {
}
bool midnight = (LocalTime() == Midnight());
- if (midnight || (RtcTime.day_of_year > Settings->energy_kWhdoy)) {
+ if ((midnight || RtcTime.day_of_year != Settings->energy_kWhdoy) && TasmotaGlobal.uptime > 10) {
Energy->kWhtoday_offset_init = true;
Settings->energy_kWhdoy = RtcTime.day_of_year;
@@ -1472,6 +1474,10 @@ void EnergyShow(bool json) {
WSContentSend_PD(HTTP_SNS_CURRENT, WebEnergyFmt(Energy->current, Settings->flag2.current_resolution));
}
WSContentSend_PD(HTTP_SNS_POWER, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution));
+// if (abs(negative_phases) != Energy->phase_count) { // Provide total power if producing power (PV) and multi phase
+ if (Energy->phase_count > 1) { // Provide total power if multi phase
+ WSContentSend_PD(HTTP_SNS_POWER_TOTAL, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution, 3));
+ }
if (!Energy->type_dc) {
if (Energy->current_available && Energy->voltage_available) {
WSContentSend_PD(HTTP_SNS_POWERUSAGE_APPARENT, WebEnergyFmt(apparent_power, Settings->flag2.wattage_resolution));
@@ -1479,9 +1485,6 @@ void EnergyShow(bool json) {
WSContentSend_PD(HTTP_SNS_POWER_FACTOR, WebEnergyFmt(power_factor, 2));
}
}
- if (abs(negative_phases) != Energy->phase_count) { // Provide total power if producing power (PV) and multi phase
- WSContentSend_PD(HTTP_SNS_POWER_TOTAL, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution, 3));
- }
WSContentSend_PD(HTTP_SNS_ENERGY_TODAY, WebEnergyFmt(Energy->daily, Settings->flag2.energy_resolution, 2));
WSContentSend_PD(HTTP_SNS_ENERGY_YESTERDAY, WebEnergyFmt(energy_yesterday_ph, Settings->flag2.energy_resolution, 2));
WSContentSend_PD(HTTP_SNS_ENERGY_TOTAL, WebEnergyFmt(Energy->total, Settings->flag2.energy_resolution, 2));
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino b/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino
index 6b742332907f..44d6515e34f6 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino
@@ -543,9 +543,11 @@ void EnergyUpdateToday(void) {
int32_t delta = Energy->kWhtoday_delta[i] / 1000;
delta_sum_balanced += delta;
Energy->kWhtoday_delta[i] -= (delta * 1000);
- Energy->kWhtoday[i] += delta;
+ if (!Settings->flag6.no_export_energy_today || (delta > 0)) { // SetOption162 - (Energy) Do not add export energy to energy today (1)
+ Energy->kWhtoday[i] += delta;
+ }
if (delta < 0) { // Export energy
- RtcEnergySettings.energy_export_kWh[i] += ((float)(delta / 100) *-1) / 1000;
+ RtcEnergySettings.energy_export_kWh[i] += (((float)delta / 100) *-1) / 1000;
}
}
@@ -624,14 +626,14 @@ void EnergyUpdateTotal(void) {
}
}
- if ((Energy->total[i] < (Energy->import_active[i] - 0.01f)) && // We subtract a little offset of 10Wh to avoid continuous updates
- Settings->flag3.hardware_energy_total) { // SetOption72 - Enable hardware energy total counter as reference (#6561)
+ if (Settings->flag3.hardware_energy_total && // SetOption72 - Enable hardware energy total counter as reference (#6561)
+ fabs(Energy->total[i] - Energy->import_active[i]) > 0.01f) { // to avoid continuous updates, check for difference of min 10Wh
// The following calculation allows total usage (Energy->import_active[i]) up to +/-2147483.647 kWh
RtcEnergySettings.energy_total_kWh[i] = Energy->import_active[i] - (Energy->energy_today_offset_kWh[i] + ((float)Energy->kWhtoday[i] / 100000));
Energy->Settings.energy_total_kWh[i] = RtcEnergySettings.energy_total_kWh[i];
Energy->total[i] = Energy->import_active[i];
Energy->Settings.energy_kWhtotal_time = (!Energy->energy_today_offset_kWh[i]) ? LocalTime() : Midnight();
- // AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value"));
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: EnergyTotal updated with hardware value"));
}
}
@@ -663,7 +665,7 @@ void Energy200ms(void) {
}
bool midnight = (LocalTime() == Midnight());
- if (midnight || (RtcTime.day_of_year > Energy->Settings.energy_kWhdoy)) {
+ if ((midnight || RtcTime.day_of_year != Energy->Settings.energy_kWhdoy) && TasmotaGlobal.uptime > 10) {
Energy->kWhtoday_offset_init = true;
Energy->Settings.energy_kWhdoy = RtcTime.day_of_year;
@@ -1862,6 +1864,10 @@ void EnergyShow(bool json) {
WSContentSend_PD(HTTP_SNS_CURRENT, WebEnergyFmt(Energy->current, Settings->flag2.current_resolution));
}
WSContentSend_PD(HTTP_SNS_POWER, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution));
+// if (abs(negative_phases) != Energy->phase_count) { // Provide total power if producing power (PV) and multi phase
+ if (Energy->phase_count > 1) { // Provide total power if multi phase
+ WSContentSend_PD(HTTP_SNS_POWER_TOTAL, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution, 3));
+ }
if (!Energy->type_dc) {
if (Energy->current_available && Energy->voltage_available) {
WSContentSend_PD(HTTP_SNS_POWERUSAGE_APPARENT, WebEnergyFmt(apparent_power, Settings->flag2.wattage_resolution));
@@ -1869,9 +1875,6 @@ void EnergyShow(bool json) {
WSContentSend_PD(HTTP_SNS_POWER_FACTOR, WebEnergyFmt(power_factor, 2));
}
}
- if (abs(negative_phases) != Energy->phase_count) { // Provide total power if producing power (PV) and multi phase
- WSContentSend_PD(HTTP_SNS_POWER_TOTAL, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution, 3));
- }
WSContentSend_PD(HTTP_SNS_ENERGY_TODAY, WebEnergyFmt(Energy->daily_kWh, Settings->flag2.energy_resolution, 2));
WSContentSend_PD(HTTP_SNS_ENERGY_YESTERDAY, WebEnergyFmt(energy_yesterday_kWh, Settings->flag2.energy_resolution, 2));
WSContentSend_PD(HTTP_SNS_ENERGY_TOTAL, WebEnergyFmt(Energy->total, Settings->flag2.energy_resolution, 2));
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino b/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino
index 047da5b43b5d..638b5bb6d4ff 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino
@@ -236,6 +236,7 @@ struct LIGHT {
uint8_t random = 0;
uint8_t subtype = 0; // LST_ subtype
uint8_t device = 0;
+ uint8_t devices = 0;
uint8_t old_power = 1;
uint8_t wakeup_active = 0; // 0=inctive, 1=on-going, 2=about to start, 3=will be triggered next cycle
uint8_t fixed_color_index = 1;
@@ -293,6 +294,10 @@ uint8_t LightDevice(void)
return Light.device; // Make external
}
+uint32_t LightDevices(void) {
+ return Light.devices; // Make external
+}
+
static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) {
return (a < b && a < c) ? a : (b < c) ? b : c;
}
@@ -1249,6 +1254,8 @@ void LightInit(void)
Light.fade_initialized = true; // consider fade intialized starting from black
}
+ Light.devices = TasmotaGlobal.devices_present - Light.device +1; // Last time that devices_present is not increments by display
+
LightUpdateColorMapping();
}
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino b/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino
index d0d3b61ff05e..14204476665d 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino
@@ -386,7 +386,7 @@ bool ArtNetStart(void) {
Settings->light_rotation = 0;
Ws2812InitStrip();
} else {
- Ws2812Clear();
+ Ws2812Clear(true);
}
}
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino b/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino
index d43b93aaf072..dae5cfd95dd2 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino
@@ -1687,14 +1687,19 @@ float evaluateExpression(const char * expression, unsigned int len) {
while (index < operators_size) {
if (priority == pgm_read_byte(kExpressionOperatorsPriorities + operators[index])) { // Need to calculate the operator first
// Get current object value and remove the next object with current operator
+
+// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: index %d, v1 '%4_f', v2 '%4_f', op %d"), index, &object_values[index], &object_values[index + 1], operators[index]);
+
va = calculateTwoValues(object_values[index], object_values[index + 1], operators[index]);
uint32_t i = index;
while (i <= operators_size) {
- operators[i++] = operators[i]; // operators.remove(index)
+// operators[i++] = operators[i]; // operators.remove(index) - Fails on ESP32 (#22636)
+ operators[i] = operators[i +1]; // operators.remove(index)
+ i++;
object_values[i] = object_values[i +1]; // object_values.remove(index + 1)
}
operators_size--;
- object_values[index] = va; // Replace the current value with the result
+ object_values[index] = va; // Replace the current value with the result
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Intermediate '%4_f'"), &object_values[index]);
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_11_knx.ino b/tasmota/tasmota_xdrv_driver/xdrv_11_knx.ino
index d40c11f34a45..5b537620ff65 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_11_knx.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_11_knx.ino
@@ -52,19 +52,14 @@ uint8_t Settings->knx_CB_param[MAX_KNX_CB] Type of Output (set relay,
#include // KNX Library
-bool knx_started = false;
+#define TOGGLE_INHIBIT_TIME 15 // 15*50mseg = 750mseg (inhibit time for not toggling again relays by a KNX toggle command)
-address_t KNX_physs_addr; // Physical KNX address of this device
-address_t KNX_addr; // KNX Address converter variable
+#ifndef KNX_ENHANCEMENT_REPEAT
+#define KNX_ENHANCEMENT_REPEAT 3
+#endif
#define KNX_Empty 255
-#define TOGGLE_INHIBIT_TIME 15 // 15*50mseg = 750mseg (inhibit time for not toggling again relays by a KNX toggle command)
-
-float last_temp;
-float last_hum;
-uint8_t toggle_inhibit;
-
typedef struct __device_parameters
{
uint8_t type; // PARAMETER_ID. Used as type of GA = relay, button, sensor, etc, (INPUTS)
@@ -116,13 +111,13 @@ device_parameters_t device_param[] = {
{ KNX_SLOT3 , false, false, KNX_Empty },
{ KNX_SLOT4 , false, false, KNX_Empty },
{ KNX_SLOT5 , false, false, KNX_Empty },
+ { KNX_SCENE , false, false, KNX_Empty },
+ { KNX_DIMMER , false, false, KNX_Empty },
+ { KNX_COLOUR , false, false, KNX_Empty },
{ KNX_SLOT6 , false, false, KNX_Empty },
{ KNX_SLOT7 , false, false, KNX_Empty },
{ KNX_SLOT8 , false, false, KNX_Empty },
{ KNX_SLOT9 , false, false, KNX_Empty },
- { KNX_SCENE , false, false, KNX_Empty },
- { KNX_DIMMER , false, false, KNX_Empty },
- { KNX_COLOUR , false, false, KNX_Empty },
{ KNX_Empty, false, false, KNX_Empty}
};
@@ -158,13 +153,13 @@ const char * device_param_ga[] = {
D_KNX_TX_SLOT " 3",
D_KNX_TX_SLOT " 4",
D_KNX_TX_SLOT " 5",
+ D_KNX_TX_SCENE ,
+ D_BRIGHTLIGHT ,
+ D_COLOR ,
D_KNX_TX_SLOT " 6",
D_KNX_TX_SLOT " 7",
D_KNX_TX_SLOT " 8",
D_KNX_TX_SLOT " 9",
- D_KNX_TX_SCENE ,
- D_BRIGHTLIGHT ,
- D_COLOR ,
nullptr
};
@@ -200,14 +195,51 @@ const char *device_param_cb[] = {
D_KNX_RX_SLOT " 3",
D_KNX_RX_SLOT " 4",
D_KNX_RX_SLOT " 5",
+ D_KNX_RX_SCENE ,
+ D_BRIGHTLIGHT ,
+ D_COLOR ,
D_KNX_RX_SLOT " 6",
D_KNX_RX_SLOT " 7",
D_KNX_RX_SLOT " 8",
D_KNX_RX_SLOT " 9",
- D_KNX_RX_SCENE ,
- D_BRIGHTLIGHT ,
- D_COLOR ,
-nullptr
+ nullptr
+};
+
+uint8_t knx_slot_xref[] = {
+ KNX_SLOT1,
+ KNX_SLOT2,
+ KNX_SLOT3,
+ KNX_SLOT4,
+ KNX_SLOT5,
+ KNX_SLOT6,
+ KNX_SLOT7,
+ KNX_SLOT8,
+ KNX_SLOT9
+};
+
+uint8_t knx_select_nice_list[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ KNX_TEMPERATURE -1,
+ KNX_HUMIDITY -1,
+ KNX_ENERGY_VOLTAGE -1,
+ KNX_ENERGY_CURRENT -1,
+ KNX_ENERGY_POWER -1,
+ KNX_ENERGY_POWERFACTOR -1,
+ KNX_ENERGY_DAILY -1,
+ KNX_ENERGY_YESTERDAY -1,
+ KNX_ENERGY_TOTAL -1,
+ KNX_SLOT1 -1,
+ KNX_SLOT2 -1,
+ KNX_SLOT3 -1,
+ KNX_SLOT4 -1,
+ KNX_SLOT5 -1,
+ KNX_SLOT6 -1,
+ KNX_SLOT7 -1,
+ KNX_SLOT8 -1,
+ KNX_SLOT9 -1,
+ KNX_SCENE -1,
+ KNX_DIMMER -1,
+ KNX_COLOUR -1
};
// Commands
@@ -224,19 +256,27 @@ nullptr
#define D_CMND_KNXTXDOUBLE "Tx_Double" // 4 bytes float (DPT14)
#define D_CMND_KNXTXBYTE "Tx_Byte" // 1 byte unsigned (DPT5)
-
const char kKnxCommands[] PROGMEM = D_PRFX_KNX "|" // Prefix
- D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB "|" D_CMND_KNXTXSCENE "|"
+ D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|"
+ D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB "|" D_CMND_KNXTXSCENE "|"
D_CMND_KNXTXFLOAT "|" D_CMND_KNXTXDOUBLE "|" D_CMND_KNXTXBYTE;
void (* const KnxCommand[])(void) PROGMEM = {
- &CmndKnxTxCmnd, &CmndKnxTxVal, &CmndKnxEnabled, &CmndKnxEnhanced, &CmndKnxPa, &CmndKnxGa, &CmndKnxCb, &CmndKnxTxScene,
+ &CmndKnxTxCmnd, &CmndKnxTxVal, &CmndKnxEnabled, &CmndKnxEnhanced,
+ &CmndKnxPa, &CmndKnxGa, &CmndKnxCb, &CmndKnxTxScene,
&CmndKnxTxFloat, &CmndKnxTxVal, &CmndKnxTxByte};
-#ifndef KNX_ENHANCEMENT_REPEAT
-#define KNX_ENHANCEMENT_REPEAT 3
-#endif
+ address_t KNX_physs_addr; // Physical KNX address of this device
+ address_t KNX_addr; // KNX Address converter variable
+
+struct Knx_t {
+ float last_temp;
+ float last_hum;
+ uint8_t toggle_inhibit;
+ bool started = false;
+} Knx;
+/*********************************************************************************************/
void KNX_Send_1bit(address_t const &receiver, uint8_t value, knx_command_type_t ct)
{
@@ -312,6 +352,7 @@ void KNX_Send_6byte_color(address_t const &receiver, uint8_t* color, knx_command
#define KNX_WRITE_6BYTE_COLOR(r,rgbw) KNX_Send_6byte_color((r),(rgbw),KNX_CT_WRITE)
#define KNX_ANSWER_6BYTE_COLOR(r,rgbw) KNX_Send_6byte_color((r),(rgbw),KNX_CT_ANSWER)
+/*********************************************************************************************/
uint8_t KNX_GA_Search( uint8_t param, uint8_t start = 0 )
{
@@ -550,6 +591,7 @@ bool KNX_CONFIG_NOT_MATCH(void)
return false;
}
+/*********************************************************************************************/
void KNXStart(void)
{
@@ -711,41 +753,46 @@ void KNX_CB_Action(message_t const &msg, void *arg)
}
else if (chan->type < 17) // Toggle Relays
{
- if (!toggle_inhibit) {
+ if (!Knx.toggle_inhibit) {
ExecuteCommandPower((chan->type) -8, POWER_TOGGLE, SRC_KNX);
if (Settings->flag.knx_enable_enhancement) {
- toggle_inhibit = TOGGLE_INHIBIT_TIME;
+ Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME;
}
}
}
#if defined(USE_RULES) || defined(USE_SCRIPT)
- else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT9)) // KNX RX SLOTs (write command)
+ else if (((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) ||
+ ((chan->type >= KNX_SLOT6) && (chan->type <= KNX_SLOT9))) // KNX RX SLOTs (write command)
{
- if (!toggle_inhibit) {
+ if (!Knx.toggle_inhibit) {
+ uint32_t slot_offset = KNX_SLOT1;
+ if (chan->type >= KNX_SLOT6) {
+ slot_offset = KNX_SLOT6;
+ }
char command[35]; //4294967295.00 13chars + 17
if (msg.data_len == 1) {
// Command received
- snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - KNX_SLOT1 + 1 ), msg.data[0]);
+ snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - slot_offset + 1 ), msg.data[0]);
} else {
// Value received
- snprintf_P(command, sizeof(command), PSTR("event KNXRX_VAL%d=%s"), ((chan->type) - KNX_SLOT1 + 1 ), tempchar);
+ snprintf_P(command, sizeof(command), PSTR("event KNXRX_VAL%d=%s"), ((chan->type) - slot_offset + 1 ), tempchar);
}
ExecuteCommand(command, SRC_KNX);
if (Settings->flag.knx_enable_enhancement) {
- toggle_inhibit = TOGGLE_INHIBIT_TIME;
+ Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME;
}
}
}
else if (chan->type == KNX_SCENE) // KNX RX SCENE SLOT (write command)
{
- if (!toggle_inhibit) {
+ if (!Knx.toggle_inhibit) {
char command[25];
// Value received
snprintf_P(command, sizeof(command), PSTR("event KNX_SCENE=%s"), tempchar);
ExecuteCommand(command, SRC_KNX);
if (Settings->flag.knx_enable_enhancement) {
- toggle_inhibit = TOGGLE_INHIBIT_TIME;
+ Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME;
}
}
}
@@ -753,25 +800,25 @@ void KNX_CB_Action(message_t const &msg, void *arg)
#ifdef USE_LIGHT
else if (chan->type == KNX_DIMMER) // KNX RX DIMMER SLOT (write command)
{
- if (!toggle_inhibit) {
+ if (!Knx.toggle_inhibit) {
char command[25];
// Value received
snprintf_P(command, sizeof(command), PSTR("Dimmer %s"), tempchar);
ExecuteCommand(command, SRC_KNX);
if (Settings->flag.knx_enable_enhancement) {
- toggle_inhibit = TOGGLE_INHIBIT_TIME;
+ Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME;
}
}
}
else if (chan->type == KNX_COLOUR) // KNX RX COLOUR_RGB/RGBW SLOT (write command)
{
- if (!toggle_inhibit) {
+ if (!Knx.toggle_inhibit) {
char command[25];
// Value received
snprintf_P(command, sizeof(command), PSTR("Color #%s"), tempchar);
ExecuteCommand(command, SRC_KNX);
if (Settings->flag.knx_enable_enhancement) {
- toggle_inhibit = TOGGLE_INHIBIT_TIME;
+ Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME;
}
}
}
@@ -784,17 +831,17 @@ void KNX_CB_Action(message_t const &msg, void *arg)
else if (chan->type == KNX_TEMPERATURE) // Reply Temperature
{
#ifdef KNX_USE_DPT9
- KNX_ANSWER_2BYTE_FLOAT(msg.received_on, last_temp);
+ KNX_ANSWER_2BYTE_FLOAT(msg.received_on, Knx.last_temp);
#else
- KNX_ANSWER_4BYTE_FLOAT(msg.received_on, last_temp);
+ KNX_ANSWER_4BYTE_FLOAT(msg.received_on, Knx.last_temp);
#endif // KNX_USE_DPT9
}
else if (chan->type == KNX_HUMIDITY) // Reply Humidity
{
#ifdef KNX_USE_DPT9
- KNX_ANSWER_2BYTE_FLOAT(msg.received_on, last_hum);
+ KNX_ANSWER_2BYTE_FLOAT(msg.received_on, Knx.last_hum);
#else
- KNX_ANSWER_4BYTE_FLOAT(msg.received_on, last_hum);
+ KNX_ANSWER_4BYTE_FLOAT(msg.received_on, Knx.last_hum);
#endif // KNX_USE_DPT9
}
#if defined(USE_ENERGY_SENSOR)
@@ -830,14 +877,19 @@ void KNX_CB_Action(message_t const &msg, void *arg)
#if defined(USE_RULES) || defined(USE_SCRIPT)
- else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT9)) // KNX RX SLOTs (read command)
+ else if (((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) ||
+ ((chan->type >= KNX_SLOT6) && (chan->type <= KNX_SLOT9))) // KNX RX SLOTs (read command)
{
- if (!toggle_inhibit) {
+ if (!Knx.toggle_inhibit) {
+ uint32_t slot_offset = KNX_SLOT1;
+ if (chan->type >= KNX_SLOT6) {
+ slot_offset = KNX_SLOT6;
+ }
char command[25];
- snprintf_P(command, sizeof(command), PSTR("event KNXRX_REQ%d"), ((chan->type) - KNX_SLOT1 + 1 ) );
+ snprintf_P(command, sizeof(command), PSTR("event KNXRX_REQ%d"), ((chan->type) - slot_offset + 1 ) );
ExecuteCommand(command, SRC_KNX);
if (Settings->flag.knx_enable_enhancement) {
- toggle_inhibit = TOGGLE_INHIBIT_TIME;
+ Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME;
}
}
}
@@ -955,10 +1007,10 @@ void KnxSensor(uint8_t sensor_type, float value)
{
if (sensor_type == KNX_TEMPERATURE)
{
- last_temp = value;
+ Knx.last_temp = value;
} else if (sensor_type == KNX_HUMIDITY)
{
- last_hum = value;
+ Knx.last_hum = value;
}
if (!(Settings->flag.knx_enabled)) { return; }
@@ -1149,9 +1201,9 @@ void HandleKNXConfiguration(void)
WSContentSend_P(HTTP_FORM_KNX2);
for (uint32_t i = 0; i < KNX_MAX_device_param ; i++)
{
- if ( device_param[i].show )
+ if ( device_param[knx_select_nice_list[i]].show )
{
- WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_ga[i]);
+ WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[knx_select_nice_list[i]].type, device_param_ga[knx_select_nice_list[i]]);
}
}
WSContentSend_P(PSTR(" -> "));
@@ -1176,9 +1228,9 @@ void HandleKNXConfiguration(void)
// Check How many Relays are available and add: RelayX and TogleRelayX
if ( (i > 8) && (i < 16) ) { j=i-8; } else { j=i; }
if ( i == 8 ) { j = 0; }
- if ( device_param[j].show )
+ if ( device_param[knx_select_nice_list[j]].show )
{
- WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_cb[i]);
+ WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[knx_select_nice_list[i]].type, device_param_cb[knx_select_nice_list[i]]);
}
}
WSContentSend_P(PSTR(" "));
@@ -1254,20 +1306,21 @@ void KNX_Save_Settings(void)
void CmndKnxTxCmnd(void)
{
+ // KNX_WRITE_1BIT
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings->flag.knx_enabled) {
// XdrvMailbox.index <- KNX SLOT to use
// XdrvMailbox.payload <- data to send
// Search all the registered GA that has that output (variable: KNX SLOTx) as parameter
- uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1);
+ uint8_t i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1]);
while ( i != KNX_Empty ) {
KNX_addr.value = Settings->knx_GA_addr[i];
KNX_WRITE_1BIT(KNX_addr, !(XdrvMailbox.payload == 0));
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d/%d/%d"),
- device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0),
+ device_param_ga[knx_slot_xref[XdrvMailbox.index -1] -1], !(XdrvMailbox.payload == 0),
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
- i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1);
+ i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1], i + 1);
}
ResponseCmndIdxChar (XdrvMailbox.data );
}
@@ -1275,24 +1328,23 @@ void CmndKnxTxCmnd(void)
void CmndKnxTxVal(void)
{
+ // KNX_WRITE_4BYTE_FLOAT
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings->flag.knx_enabled) {
// XdrvMailbox.index <- KNX SLOT to use
// XdrvMailbox.payload <- data to send
// Search all the registered GA that has that output (variable: KNX SLOTx) as parameter
- uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1);
+ uint8_t i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1]);
while ( i != KNX_Empty ) {
KNX_addr.value = Settings->knx_GA_addr[i];
float tempvar = CharToFloat(XdrvMailbox.data);
- dtostrfd(tempvar,2,XdrvMailbox.data);
-
KNX_WRITE_4BYTE_FLOAT(KNX_addr, tempvar);
- AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d/%d/%d"),
- device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data,
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %2_f " D_SENT_TO " %d/%d/%d"),
+ device_param_ga[knx_slot_xref[XdrvMailbox.index -1] -1], &tempvar,
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
- i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1);
+ i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1], i + 1);
}
ResponseCmndIdxChar (XdrvMailbox.data );
}
@@ -1301,24 +1353,23 @@ void CmndKnxTxVal(void)
void CmndKnxTxFloat(void)
{
+ // KNX_WRITE_2BYTE_FLOAT
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings->flag.knx_enabled) {
// XdrvMailbox.index <- KNX SLOT to use
// XdrvMailbox.payload <- data to send
// Search all the registered GA that has that output (variable: KNX SLOTx) as parameter
- uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1);
+ uint8_t i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1]);
while ( i != KNX_Empty ) {
KNX_addr.value = Settings->knx_GA_addr[i];
float tempvar = CharToFloat(XdrvMailbox.data);
- dtostrfd(tempvar,2,XdrvMailbox.data);
-
KNX_WRITE_2BYTE_FLOAT(KNX_addr, tempvar);
- AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d/%d/%d (2 bytes float)"),
- device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data,
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %2_f " D_SENT_TO " %d/%d/%d (2 bytes float)"),
+ device_param_ga[knx_slot_xref[XdrvMailbox.index -1] -1], &tempvar,
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
- i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1);
+ i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1], i + 1);
}
ResponseCmndIdxChar (XdrvMailbox.data );
}
@@ -1326,24 +1377,23 @@ void CmndKnxTxFloat(void)
void CmndKnxTxByte(void)
{
+ // KNX_WRITE_1BYTE_UINT
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings->flag.knx_enabled) {
// XdrvMailbox.index <- KNX SLOT to use
// XdrvMailbox.payload <- data to send
// Search all the registered GA that has that output (variable: KNX SLOTx) as parameter
- uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1);
+ uint8_t i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1]);
while ( i != KNX_Empty ) {
KNX_addr.value = Settings->knx_GA_addr[i];
uint8_t tempvar = TextToInt(XdrvMailbox.data);
- dtostrfd(tempvar,0,XdrvMailbox.data);
-
KNX_WRITE_1BYTE_UINT(KNX_addr, tempvar);
- AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d/%d/%d (1 byte unsigned)"),
- device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data,
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d/%d/%d (1 byte unsigned)"),
+ device_param_ga[knx_slot_xref[XdrvMailbox.index -1] -1], tempvar,
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
- i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1);
+ i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1], i + 1);
}
ResponseCmndIdxChar (XdrvMailbox.data );
}
@@ -1358,12 +1408,10 @@ void CmndKnxTxScene(void)
KNX_addr.value = Settings->knx_GA_addr[i];
uint8_t tempvar = TextToInt(XdrvMailbox.data);
- dtostrfd(tempvar,0,XdrvMailbox.data);
-
KNX_WRITE_1BYTE_UINT(KNX_addr, tempvar);
- AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d/%d/%d"),
- device_param_ga[KNX_SCENE-1], XdrvMailbox.data,
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d/%d/%d"),
+ device_param_ga[KNX_SCENE-1], tempvar,
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
ResponseCmndIdxChar (XdrvMailbox.data);
}
@@ -1374,6 +1422,9 @@ void CmndKnxEnabled(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
Settings->flag.knx_enabled = XdrvMailbox.payload;
+ if (!Settings->flag.knx_enabled) {
+ Knx.started = false;
+ }
}
ResponseCmndChar (GetStateText(Settings->flag.knx_enabled) );
}
@@ -1522,8 +1573,8 @@ bool Xdrv11(uint32_t function)
if (!TasmotaGlobal.global_state.network_down) { knx.loop(); } // Process knx events
break;
case FUNC_EVERY_50_MSECOND:
- if (toggle_inhibit) {
- toggle_inhibit--;
+ if (Knx.toggle_inhibit) {
+ Knx.toggle_inhibit--;
}
break;
case FUNC_ANY_KEY:
@@ -1546,13 +1597,13 @@ bool Xdrv11(uint32_t function)
KNX_INIT();
break;
case FUNC_NETWORK_UP:
- if (!knx_started && Settings->flag.knx_enabled) { // CMND_KNX_ENABLED
+ if (!Knx.started && Settings->flag.knx_enabled) { // CMND_KNX_ENABLED
KNXStart();
- knx_started = true;
+ Knx.started = true;
}
break;
case FUNC_NETWORK_DOWN:
- knx_started = false;
+ Knx.started = false;
break;
// case FUNC_SET_POWER:
// break;
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_13_display.ino b/tasmota/tasmota_xdrv_driver/xdrv_13_display.ino
index 4ef3786f6c3d..0153e0f3074c 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_13_display.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_13_display.ino
@@ -269,6 +269,12 @@ bool disp_subscribed = false;
/*********************************************************************************************/
+uint32_t DisplayDevices(void) {
+ return (disp_device) ? 1 : 0;
+}
+
+/*********************************************************************************************/
+
void DisplayClear(void) {
if (renderer) {
renderer->fillScreen(bg_color);
@@ -1846,57 +1852,53 @@ void DisplayLocalSensor(void)
\*********************************************************************************************/
void DisplayInitDriver(void) {
+ uint32_t display_model = Settings->display_model;
+ Settings->display_model = 0; // Test if any display_model is available
XdspCall(FUNC_DISPLAY_INIT_DRIVER);
+ if (Settings->display_model && display_model) { // If any model found keep using user configured one for backward compatibility
+ Settings->display_model = display_model;
+ }
+ if (!Settings->display_model) { return; }
-// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Display model %d"), Settings->display_model);
+// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DSP: Model %d"), Settings->display_model);
- if (Settings->display_model) {
-// ApplyDisplayDimmer(); // Not allowed here. Way too early in init sequence. Global power state has not been set at this point in time
+// ApplyDisplayDimmer(); // Not allowed here. Way too early in init sequence. Global power state has not been set at this point in time
#ifdef USE_MULTI_DISPLAY
- Set_display(0);
+ Set_display(0);
#endif // USE_MULTI_DISPLAY
- if (renderer) {
- renderer->setTextFont(Settings->display_font);
- renderer->setTextSize(Settings->display_size);
- // force opaque mode
- renderer->setDrawMode(0);
+ if (renderer) {
+ renderer->setTextFont(Settings->display_font);
+ renderer->setTextSize(Settings->display_size);
+ // force opaque mode
+ renderer->setDrawMode(0);
- for (uint32_t cnt = 0; cnt < (MAX_INDEXCOLORS - PREDEF_INDEXCOLORS); cnt++) {
- index_colors[cnt] = 0;
- }
+ for (uint32_t cnt = 0; cnt < (MAX_INDEXCOLORS - PREDEF_INDEXCOLORS); cnt++) {
+ index_colors[cnt] = 0;
}
+ }
#ifdef USE_DT_VARS
- free_dt_vars();
+ free_dt_vars();
#endif
#ifdef USE_UFILESYS
- Display_Text_From_File(DISP_BATCH_FILE);
+ Display_Text_From_File(DISP_BATCH_FILE);
#endif
#ifdef USE_GRAPH
- for (uint8_t count = 0; count < NUM_GRAPHS; count++) { graph[count] = 0; }
+ for (uint8_t count = 0; count < NUM_GRAPHS; count++) { graph[count] = 0; }
#endif
- UpdateDevicesPresent(1);
- if (!PinUsed(GPIO_BACKLIGHT)) {
- if ((LT_PWM1 == TasmotaGlobal.light_type) && // Single PWM light channel
- ((4 == Settings->display_model) || // ILI9341 legacy
- (17 == Settings->display_model)) // Universal
- ) {
- UpdateDevicesPresent(-1); // Assume PWM channel is used for backlight
- }
- }
- disp_device = TasmotaGlobal.devices_present;
+ UpdateDevicesPresent(1);
+ disp_device = TasmotaGlobal.devices_present;
#ifndef USE_DISPLAY_MODES1TO5
- Settings->display_mode = 0;
+ Settings->display_mode = 0;
#else
- DisplayLogBufferInit();
+ DisplayLogBufferInit();
#endif // USE_DISPLAY_MODES1TO5
- }
}
void DisplaySetPower(void) {
@@ -2169,7 +2171,7 @@ void CmndDisplayText(void) {
void CmndDisplayClear(void) {
DisplayClear();
- ResponseCmndChar(XdrvMailbox.data);
+ ResponseCmndDone();
}
void CmndDisplayNumber(void) {
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_27_esp32_shutter.ino b/tasmota/tasmota_xdrv_driver/xdrv_27_esp32_shutter.ino
index 2be8c6f9d9ed..a26d1a6b1a05 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_27_esp32_shutter.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_27_esp32_shutter.ino
@@ -126,7 +126,10 @@ const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|"
D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|"
D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|"
D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPTOGGLEDIR "|" D_CMND_SHUTTER_STOPPOSITION "|" D_CMND_SHUTTER_INCDEC "|"
- D_CMND_SHUTTER_UNITTEST "|" D_CMND_SHUTTER_TILTCONFIG "|" D_CMND_SHUTTER_SETTILT "|" D_CMND_SHUTTER_TILTINCDEC "|" D_CMND_SHUTTER_MOTORSTOP "|" D_CMND_SHUTTER_SETUP "|"
+#ifdef SHUTTER_UNITTEST
+ D_CMND_SHUTTER_UNITTEST "|"
+#endif // SHUTTER_UNITTEST
+ D_CMND_SHUTTER_TILTCONFIG "|" D_CMND_SHUTTER_SETTILT "|" D_CMND_SHUTTER_TILTINCDEC "|" D_CMND_SHUTTER_MOTORSTOP "|" D_CMND_SHUTTER_SETUP "|"
D_CMD_SHUTTER_EXTRASTOPRELAY;
void (* const ShutterCommand[])(void) PROGMEM = {
@@ -135,7 +138,10 @@ void (* const ShutterCommand[])(void) PROGMEM = {
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay,
&CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons,
&CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopToggleDir, &CmndShutterStopPosition, &CmndShutterIncDec,
- &CmndShutterUnitTest,&CmndShutterTiltConfig,&CmndShutterSetTilt,&CmndShutterTiltIncDec,&CmndShutterMotorStop,&CmndShutterSetup,&CmndShutterExtraStopPulseRelay
+#ifdef SHUTTER_UNITTEST
+ &CmndShutterUnitTest,
+#endif // SHUTTER_UNITTEST
+ &CmndShutterTiltConfig, &CmndShutterSetTilt, &CmndShutterTiltIncDec, &CmndShutterMotorStop, &CmndShutterSetup, &CmndShutterExtraStopPulseRelay
};
const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d,\"Target\":%d,\"Tilt\":%d}";
@@ -2268,6 +2274,62 @@ void CmndShutterToggleDir(void)
ShutterToggle(true);
}
+#ifdef SHUTTER_UNITTEST
+void CmndShutterUnitTest(void) {
+ int16_t input_percent[10] = {-5,0,10,26,35,55,80,99,100,105};
+ int16_t output_percent[10] = {0,0,10,26,35,55,80,99,100,100};
+ uint32_t result_percent[2][2][10] = {{{0,0,24000,62400,84000,132000,192000,237600,240000,240000},
+ {0,0,360000,936000,1260000,1980000,2880000,3564000,3600000,3600000}},
+ {{0,0,76296,100000,113333,174299,205795,237983,240000,240000},
+ {0,0,1144444,1500000,1700000,2614488,3086929,3569748,3600000,3600000}}};
+
+ uint32_t result = 0;
+ char svalue[50]; // Command and number parameter
+ ShutterSettings.shuttercoeff[0][0] = 0;
+ for (uint8_t i=0; i<2 ; i++){
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12);
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ ShutterInit();
+ for (uint8_t j=0; j<2 ; j++){
+ for (uint8_t k=0; k<10 ; k++){
+ result += (result_percent[i][j][k] == ShutterPercentToRealPosition(input_percent[k] , 0) ? 0 : 1);
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition error %d: %d <-> %d"),result, ShutterPercentToRealPosition(input_percent[k] , 0), result_percent[i][j][k]);
+ }
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180);
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ }
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210");
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ }
+ if (!result){
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: PASS"));
+ } else {
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: FAIL"));
+ }
+ ShutterSettings.shuttercoeff[0][0] = 0;
+ for (uint8_t i=0; i<2 ; i++){
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12);
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ ShutterInit();
+ for (uint8_t j=0; j<2 ; j++){
+ for (uint8_t k=0; k<10 ; k++){
+ result += (output_percent[k] == ShutterRealToPercentPosition(result_percent[i][j][k] , 0) ? 0 : 1);
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition error %d: %d <-> %d"),result, ShutterRealToPercentPosition(result_percent[i][j][k] , 0), output_percent[k]);
+ }
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180);
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ }
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210");
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ }
+ if (!result){
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: PASS"));
+ } else {
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: FAIL"));
+ }
+}
+#endif // SHUTTER_UNITTEST
+
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
@@ -2285,6 +2347,13 @@ bool Xdrv27(uint32_t function)
char stemp1[10];
power_t save_powermatrix;
switch (function) {
+ case FUNC_EVERY_50_MSECOND:
+ ShutterUpdatePosition();
+ break;
+ case FUNC_EVERY_SECOND:
+ //case FUNC_EVERY_250_MSECOND:
+ ShutterReportPosition(false, MAX_SHUTTERS_ESP32);
+ break;
case FUNC_RESTORE_SETTINGS:
result = ShutterSettingsRestore();
break;
@@ -2293,18 +2362,13 @@ bool Xdrv27(uint32_t function)
break;
case FUNC_PRE_INIT:
ShutterSettingsLoad(0);
+ break;
+ case FUNC_INIT:
ShutterInit();
break;
case FUNC_RESET_SETTINGS:
ShutterSettingsLoad(1);
break;
- case FUNC_EVERY_50_MSECOND:
- ShutterUpdatePosition();
- break;
- case FUNC_EVERY_SECOND:
- //case FUNC_EVERY_250_MSECOND:
- ShutterReportPosition(false, MAX_SHUTTERS_ESP32);
- break;
case FUNC_COMMAND:
for (uint8_t i = counter; i <= counterend; i++) {
XdrvMailbox.index = i;
@@ -2319,7 +2383,7 @@ bool Xdrv27(uint32_t function)
}
break;
case FUNC_JSON_APPEND:
- if (!ShutterGlobal.sensor_data_reported) {
+ if (!ShutterGlobal.sensor_data_reported || TasmotaGlobal.tele_period == 0) {
ShutterGlobal.sensor_data_reported = true;
for (uint8_t i = 0; i < TasmotaGlobal.shutters_present; i++) {
ResponseAppend_P(",");
@@ -2391,64 +2455,5 @@ bool Xdrv27(uint32_t function)
return result;
}
-#endif //USE_SHUTTER
-
-#ifdef SHUTTER_UNITTEST
-void CmndShutterUnitTest(void) {
- int16_t input_percent[10] = {-5,0,10,26,35,55,80,99,100,105};
- int16_t output_percent[10] = {0,0,10,26,35,55,80,99,100,100};
- uint32_t result_percent[2][2][10] = {{{0,0,24000,62400,84000,132000,192000,237600,240000,240000},
- {0,0,360000,936000,1260000,1980000,2880000,3564000,3600000,3600000}},
- {{0,0,76296,100000,113333,174299,205795,237983,240000,240000},
- {0,0,1144444,1500000,1700000,2614488,3086929,3569748,3600000,3600000}}};
-
- uint32_t result = 0;
- char svalue[50]; // Command and number parameter
- ShutterSettings.shuttercoeff[0][0] = 0;
- for (uint8_t i=0; i<2 ; i++){
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12);
- ExecuteCommand(svalue, SRC_SHUTTER);
- ShutterInit();
- for (uint8_t j=0; j<2 ; j++){
- for (uint8_t k=0; k<10 ; k++){
- result += (result_percent[i][j][k] == ShutterPercentToRealPosition(input_percent[k] , 0) ? 0 : 1);
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition error %d: %d <-> %d"),result, ShutterPercentToRealPosition(input_percent[k] , 0), result_percent[i][j][k]);
- }
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180);
- ExecuteCommand(svalue, SRC_SHUTTER);
- }
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210");
- ExecuteCommand(svalue, SRC_SHUTTER);
- }
- if (!result){
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: PASS"));
- } else {
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: FAIL"));
- }
- ShutterSettings.shuttercoeff[0][0] = 0;
- for (uint8_t i=0; i<2 ; i++){
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12);
- ExecuteCommand(svalue, SRC_SHUTTER);
- ShutterInit();
- for (uint8_t j=0; j<2 ; j++){
- for (uint8_t k=0; k<10 ; k++){
- result += (output_percent[k] == ShutterRealToPercentPosition(result_percent[i][j][k] , 0) ? 0 : 1);
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition error %d: %d <-> %d"),result, ShutterRealToPercentPosition(result_percent[i][j][k] , 0), output_percent[k]);
- }
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180);
- ExecuteCommand(svalue, SRC_SHUTTER);
- }
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210");
- ExecuteCommand(svalue, SRC_SHUTTER);
- }
- if (!result){
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: PASS"));
- } else {
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: FAIL"));
- }
-}
-#else
-void CmndShutterUnitTest(void) {}
-#endif // SHUTTER_UNITTEST
-
+#endif // USE_SHUTTER
#endif // ESP32
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino
index da7c7a138b0c..28292483951e 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino
@@ -19,6 +19,7 @@
#ifdef ESP8266
#ifdef USE_SHUTTER
+//#if defined(USE_SHUTTER) && !defined(USE_UFILESYS)
/*********************************************************************************************\
* Shutter or Blind support using two consecutive relays
\*********************************************************************************************/
@@ -65,7 +66,10 @@ const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|"
D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|"
D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|"
D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPTOGGLEDIR "|" D_CMND_SHUTTER_STOPPOSITION "|" D_CMND_SHUTTER_INCDEC "|"
- D_CMND_SHUTTER_UNITTEST "|" D_CMND_SHUTTER_TILTCONFIG "|" D_CMND_SHUTTER_SETTILT "|" D_CMND_SHUTTER_TILTINCDEC "|" D_CMND_SHUTTER_MOTORSTOP;
+#ifdef SHUTTER_UNITTEST
+ D_CMND_SHUTTER_UNITTEST "|"
+#endif // SHUTTER_UNITTEST
+ D_CMND_SHUTTER_TILTCONFIG "|" D_CMND_SHUTTER_SETTILT "|" D_CMND_SHUTTER_TILTINCDEC "|" D_CMND_SHUTTER_MOTORSTOP;
void (* const ShutterCommand[])(void) PROGMEM = {
&CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterToggleDir, &CmndShutterStop, &CmndShutterPosition,
@@ -73,7 +77,10 @@ void (* const ShutterCommand[])(void) PROGMEM = {
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay,
&CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons,
&CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopToggleDir, &CmndShutterStopPosition, &CmndShutterIncDec,
- &CmndShutterUnitTest,&CmndShutterTiltConfig,&CmndShutterSetTilt,&CmndShutterTiltIncDec,&CmndShutterMotorStop};
+#ifdef SHUTTER_UNITTEST
+ &CmndShutterUnitTest,
+#endif // SHUTTER_UNITTEST
+ &CmndShutterTiltConfig, &CmndShutterSetTilt, &CmndShutterTiltIncDec, &CmndShutterMotorStop};
const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d,\"Target\":%d,\"Tilt\":%d}";
const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}";
@@ -1875,6 +1882,62 @@ void CmndShutterMotorStop(void)
}
}
+#ifdef SHUTTER_UNITTEST
+void CmndShutterUnitTest(void) {
+ int16_t input_percent[10] = {-5,0,10,26,35,55,80,99,100,105};
+ int16_t output_percent[10] = {0,0,10,26,35,55,80,99,100,100};
+ uint32_t result_percent[2][2][10] = {{{0,0,24000,62400,84000,132000,192000,237600,240000,240000},
+ {0,0,360000,936000,1260000,1980000,2880000,3564000,3600000,3600000}},
+ {{0,0,76296,100000,113333,174299,205795,237983,240000,240000},
+ {0,0,1144444,1500000,1700000,2614488,3086929,3569748,3600000,3600000}}};
+
+ uint32_t result = 0;
+ char svalue[50]; // Command and number parameter
+ Settings->shuttercoeff[0][0] = 0;
+ for (uint8_t i=0; i<2 ; i++){
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12);
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ ShutterInit();
+ for (uint8_t j=0; j<2 ; j++){
+ for (uint8_t k=0; k<10 ; k++){
+ result += (result_percent[i][j][k] == ShutterPercentToRealPosition(input_percent[k] , 0) ? 0 : 1);
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition error %d: %d <-> %d"),result, ShutterPercentToRealPosition(input_percent[k] , 0), result_percent[i][j][k]);
+ }
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180);
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ }
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210");
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ }
+ if (!result){
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: PASS"));
+ } else {
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: FAIL"));
+ }
+ Settings->shuttercoeff[0][0] = 0;
+ for (uint8_t i=0; i<2 ; i++){
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12);
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ ShutterInit();
+ for (uint8_t j=0; j<2 ; j++){
+ for (uint8_t k=0; k<10 ; k++){
+ result += (output_percent[k] == ShutterRealToPercentPosition(result_percent[i][j][k] , 0) ? 0 : 1);
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition error %d: %d <-> %d"),result, ShutterRealToPercentPosition(result_percent[i][j][k] , 0), output_percent[k]);
+ }
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180);
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ }
+ snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210");
+ ExecuteCommand(svalue, SRC_SHUTTER);
+ }
+ if (!result){
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: PASS"));
+ } else {
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: FAIL"));
+ }
+}
+#endif // SHUTTER_UNITTEST
+
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
@@ -1892,9 +1955,6 @@ bool Xdrv27(uint32_t function)
char stemp1[10];
power_t save_powermatrix;
switch (function) {
- case FUNC_PRE_INIT:
- ShutterInit();
- break;
case FUNC_EVERY_50_MSECOND:
ShutterUpdatePosition();
break;
@@ -1902,6 +1962,9 @@ bool Xdrv27(uint32_t function)
//case FUNC_EVERY_250_MSECOND:
ShutterReportPosition(false, MAX_SHUTTERS);
break;
+ case FUNC_INIT:
+ ShutterInit();
+ break;
case FUNC_COMMAND:
for (uint8_t i = counter; i <= counterend; i++) {
XdrvMailbox.index = i;
@@ -1911,7 +1974,7 @@ bool Xdrv27(uint32_t function)
}
break;
case FUNC_JSON_APPEND:
- if (!sensor_data_reported) {
+ if (!sensor_data_reported || TasmotaGlobal.tele_period == 0) {
sensor_data_reported = true;
for (uint8_t i = 0; i < TasmotaGlobal.shutters_present; i++) {
uint8_t position = ShutterRealToPercentPosition(Shutter[i].real_position, i);
@@ -1974,64 +2037,5 @@ bool Xdrv27(uint32_t function)
return result;
}
-#endif //USE_SHUTTER
-
-#ifdef SHUTTER_UNITTEST
-void CmndShutterUnitTest(void) {
- int16_t input_percent[10] = {-5,0,10,26,35,55,80,99,100,105};
- int16_t output_percent[10] = {0,0,10,26,35,55,80,99,100,100};
- uint32_t result_percent[2][2][10] = {{{0,0,24000,62400,84000,132000,192000,237600,240000,240000},
- {0,0,360000,936000,1260000,1980000,2880000,3564000,3600000,3600000}},
- {{0,0,76296,100000,113333,174299,205795,237983,240000,240000},
- {0,0,1144444,1500000,1700000,2614488,3086929,3569748,3600000,3600000}}};
-
- uint32_t result = 0;
- char svalue[50]; // Command and number parameter
- Settings->shuttercoeff[0][0] = 0;
- for (uint8_t i=0; i<2 ; i++){
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12);
- ExecuteCommand(svalue, SRC_SHUTTER);
- ShutterInit();
- for (uint8_t j=0; j<2 ; j++){
- for (uint8_t k=0; k<10 ; k++){
- result += (result_percent[i][j][k] == ShutterPercentToRealPosition(input_percent[k] , 0) ? 0 : 1);
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition error %d: %d <-> %d"),result, ShutterPercentToRealPosition(input_percent[k] , 0), result_percent[i][j][k]);
- }
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180);
- ExecuteCommand(svalue, SRC_SHUTTER);
- }
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210");
- ExecuteCommand(svalue, SRC_SHUTTER);
- }
- if (!result){
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: PASS"));
- } else {
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: FAIL"));
- }
- Settings->shuttercoeff[0][0] = 0;
- for (uint8_t i=0; i<2 ; i++){
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12);
- ExecuteCommand(svalue, SRC_SHUTTER);
- ShutterInit();
- for (uint8_t j=0; j<2 ; j++){
- for (uint8_t k=0; k<10 ; k++){
- result += (output_percent[k] == ShutterRealToPercentPosition(result_percent[i][j][k] , 0) ? 0 : 1);
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition error %d: %d <-> %d"),result, ShutterRealToPercentPosition(result_percent[i][j][k] , 0), output_percent[k]);
- }
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180);
- ExecuteCommand(svalue, SRC_SHUTTER);
- }
- snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210");
- ExecuteCommand(svalue, SRC_SHUTTER);
- }
- if (!result){
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: PASS"));
- } else {
- AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: FAIL"));
- }
-}
-#else
-void CmndShutterUnitTest(void) {}
-#endif
-
+#endif // USE_SHUTTER
#endif // ESP8266
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino b/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino
index 874cbd772b24..b0f4420ce5e3 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino
@@ -374,6 +374,7 @@ void Pcf8574Power(void) {
rpower >>= Pcf8574.relay_offset;
relay_max = Pcf8574.relay_max;
}
+ DevicesPresentNonDisplayOrLight(relay_max); // Skip display and/or light(s)
for (uint32_t index = 0; index < relay_max; index++) {
power_t state = rpower &1;
if (Pcf8574PinUsed(GPIO_REL1, index)) {
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_31_tasmota_client.ino b/tasmota/tasmota_xdrv_driver/xdrv_31_tasmota_client.ino
index 345fc9dbac82..ef0a5b4d32fb 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_31_tasmota_client.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_31_tasmota_client.ino
@@ -444,8 +444,10 @@ void TasmotaClient_Show(void) {
char buffer[250]; // Keep size below 255 to stay within 8-bits index and len
uint8_t len = TasmotaClient_receiveData(buffer, sizeof(buffer) -1);
- buffer[len] = '\0';
- ResponseAppend_P(PSTR(",\"TasmotaClient\":%s"), buffer);
+ if (len) {
+ buffer[len] = '\0';
+ ResponseAppend_P(PSTR(",\"TasmotaClient\":%s"), buffer);
+ }
}
}
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino
index 27c97ab857cf..84203d405c48 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino
@@ -1603,7 +1603,7 @@ void UfsEditor(void) {
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("UFS: UfsEditor: read=%d"), l);
if (l < 0) { break; }
buf[l] = '\0';
- WSContentSend_P(PSTR("%s"), buf);
+ WSContentSend_P(PSTR("%s"), HtmlEscape((char*)buf).c_str());
filelen -= l;
}
fp.close();
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino
index 4c1f0f711e39..97eb571e4b62 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino
@@ -23,13 +23,6 @@
#include
#include "esp8266toEsp32.h"
-#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)
-#if ESP_IDF_VERSION_MAJOR >= 5
-#include
-#else
-#include
-#endif
-#endif
/*********************************************************************************************\
* Native functions mapped to Berry functions
*
@@ -40,9 +33,10 @@
extern "C" {
#include "berry/include/be_gpio_defines.h"
-#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)
+#ifdef SOC_DAC_SUPPORTED
#include "soc/dac_channel.h"
-#endif // defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)
+ #include
+#endif // SOC_DAC_SUPPORTED
// virtual member
int gp_member(bvm *vm);
@@ -65,7 +59,7 @@ extern "C" {
// synthetic mode
if (-1 == mode) {
// DAC
-#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)
+#ifdef SOC_DAC_SUPPORTED
if (pin != DAC_CHAN0_GPIO_NUM && pin != DAC_CHAN1_GPIO_NUM) {
be_raisef(vm, "value_error", "DAC only supported on GPIO%i-%i", DAC_CHAN0_GPIO_NUM, DAC_CHAN1_GPIO_NUM);
}
@@ -113,7 +107,7 @@ extern "C" {
int gp_dac_voltage(bvm *vm);
int gp_dac_voltage(bvm *vm) {
-#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)
+#ifdef SOC_DAC_SUPPORTED
int32_t argc = be_top(vm); // Get the number of arguments
if (argc == 2 && be_isint(vm, 1) && be_isnumber(vm, 2)) {
int32_t pin = be_toint(vm, 1);
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino
index 0984cb14b3ce..555f4c44de62 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino
@@ -24,22 +24,8 @@
#ifdef USE_WS2812
-#include
-
-enum {
- ws2812_grb = 1,
- sk6812_grbw = 2,
-
- neopixel_type_end
-};
-
-#ifdef CONFIG_IDF_TARGET_ESP32C2
-typedef NeoPixelBus neopixel_ws2812_grb_t;
-typedef NeoPixelBus neopixel_sk6812_grbw_t;
-#else
-typedef NeoPixelBus neopixel_ws2812_grb_t;
-typedef NeoPixelBus neopixel_sk6812_grbw_t;
-#endif
+#include "TasmotaLED.h"
+#include "TasmotaLEDPusher.h"
/*********************************************************************************************\
* Functions from Tasmota WS2812 driver
@@ -86,93 +72,65 @@ extern "C" {
// # 22 : ShiftLeft (rot:int [, first:int, last:int]) -> void
// # 23 : ShiftRight (rot:int [, first:int, last:int]) -> void
- void * be_get_neopixelbus(bvm *vm) {
+ void * be_get_tasmotaled(bvm *vm) {
be_getmember(vm, 1, "_p");
void * strip = (void*) be_tocomptr(vm, -1);
be_pop(vm, 1);
- if (strip == nullptr) {
- be_raise(vm, "internal_error", "neopixelbus object not initialized");
- }
return strip;
}
- int32_t be_get_leds_type(bvm *vm) {
- be_getmember(vm, 1, "_t");
- int32_t type = be_toint(vm, -1);
- be_pop(vm, 1);
- if (type < 0 || type >= neopixel_type_end) {
- be_raise(vm, "internal_error", "invalid leds type");
- }
- return type;
- }
- int be_neopixelbus_call_native(bvm *vm);
- int be_neopixelbus_call_native(bvm *vm) {
+ int be_tasmotaled_call_native(bvm *vm);
+ int be_tasmotaled_call_native(bvm *vm) {
int32_t argc = be_top(vm); // Get the number of arguments
if (argc >= 2 && be_isint(vm, 2)) {
int32_t cmd = be_toint(vm, 2);
if (0 == cmd) { // 00 : ctor (leds:int, gpio:int) -> void
- if ((argc != 2) && !(argc >= 6 && be_isint(vm, 3) && be_isint(vm, 4) && be_isint(vm, 5) && be_isint(vm, 6))) {
- be_raise(vm, "value_error", "bad arguments for neopixelbus:ctor");
+ if ((argc != 2) && !(argc >= 5 && be_isint(vm, 3) && be_isint(vm, 4) && be_isint(vm, 5))) {
+ be_raise(vm, "value_error", "bad arguments for tasmotaled:ctor");
}
int32_t leds = -1;
int32_t gpio = -1;
- int32_t neopixel_type = 0;
- int32_t rmt = 0;
- void * strip = nullptr;
+ int32_t led_type = 0;
+ int32_t hardware = 0;
+ if (argc >= 6 && be_isint(vm, 6)) {
+ hardware = be_toint(vm, 6) & 0xFF0000; // remove the low 16 bits to avoid any interference with legacy parameter for RMT channels
+ }
+ TasmotaLED * strip = nullptr;
if (argc > 2) {
leds = be_toint(vm, 3);
gpio = be_toint(vm, 4);
- neopixel_type = be_toint(vm, 5);
- rmt = be_toint(vm, 6);
+ led_type = be_toint(vm, 5);
}
if (-1 == gpio) {
// if GPIO is '-1'
- neopixel_type = 0;
- Ws2812InitStrip(); // ensure the NeoPixelbus object is initialized, because Berry code runs before the driver is initialized
- strip = Ws2812GetStrip();
+ led_type = 0;
+ Ws2812InitStrip(); // ensure the tasmotaled object is initialized, because Berry code runs before the driver is initialized
} else {
- // allocate a new RMT
- if (neopixel_type < 1) { neopixel_type = 1; }
- if (neopixel_type >= neopixel_type_end) { neopixel_type = neopixel_type_end - 1; }
- if (rmt < 0) { rmt = 0; }
- if (rmt >= MAX_RMT) { rmt = MAX_RMT - 1; }
-
- switch (neopixel_type) {
- case ws2812_grb: strip = new neopixel_ws2812_grb_t(leds, gpio, (NeoBusChannel) rmt);
- break;
- case sk6812_grbw: strip = new neopixel_sk6812_grbw_t(leds, gpio, (NeoBusChannel) rmt);
- break;
+ if (led_type < 1) { led_type = 1; }
+ TasmotaLEDPusher * pusher = TasmotaLEDPusher::Create(hardware, gpio);
+ if (pusher == nullptr) {
+ be_raise(vm, "value_error", "LED interface not supported");
}
+ strip = new TasmotaLED(led_type, leds);
+ strip->SetPusher(pusher);
}
- // store type in attribute `_t`
- be_pushint(vm, neopixel_type);
- be_setmember(vm, 1, "_t");
- be_pop(vm, 1);
-
- be_pushcomptr(vm, (void*) strip);
+ be_pushcomptr(vm, (void*) strip); // if native driver, it is NULL
be_setmember(vm, 1, "_p");
be_pop(vm, 1);
be_pushnil(vm);
} else {
- // all other commands need a valid neopixelbus pointer
- int32_t leds_type = be_get_leds_type(vm);
- const void * s = be_get_neopixelbus(vm); // raises an exception if pointer is invalid
- // initialize all possible variants
- neopixel_ws2812_grb_t * s_ws2812_grb = (leds_type == ws2812_grb) ? (neopixel_ws2812_grb_t*) s : nullptr;
- neopixel_sk6812_grbw_t * s_sk6812_grbw = (leds_type == sk6812_grbw) ? (neopixel_sk6812_grbw_t*) s : nullptr;
- // detect native driver
- bool native = (leds_type == 0);
-
+ // all other commands need a valid tasmotaled pointer
+ TasmotaLED * strip = (TasmotaLED*) be_get_tasmotaled(vm); // raises an exception if pointer is invalid
+ // detect native driver means strip == nullptr
be_pushnil(vm); // push a default `nil` return value
switch (cmd) {
case 1: // # 01 : begin void -> void
- if (native) Ws2812Begin();
- if (s_ws2812_grb) s_ws2812_grb->Begin();
- if (s_sk6812_grbw) s_sk6812_grbw->Begin();
+ if (strip) strip->Begin();
+ else Ws2812Begin();
break;
case 2: // # 02 : show void -> void
{
@@ -195,49 +153,41 @@ extern "C" {
}
}
uint32_t pixels_size; // number of bytes to push
- if (native) { Ws2812Show(); pixels_size = Ws2812PixelsSize(); }
- if (s_ws2812_grb) { s_ws2812_grb->Show(); pixels_size = s_ws2812_grb->PixelsSize(); }
- if (s_sk6812_grbw) { s_sk6812_grbw->Show(); pixels_size = s_sk6812_grbw->PixelsSize(); }
-
- // Wait for RMT/I2S to complete fixes distortion due to analogRead
- // 1ms is needed for 96 bytes
- SystemBusyDelay((pixels_size + 95) / 96);
+ bool update_completed = false;
+ if (strip) {
+ strip->Show();
+ pixels_size = strip->PixelCount() * strip->PixelSize();
+ update_completed = strip->CanShow();
+ } else {
+ Ws2812Show();
+ pixels_size = Ws2812PixelsSize();
+ update_completed =Ws2812CanShow();
+ }
}
break;
case 3: // # 03 : CanShow void -> bool
- if (native) be_pushbool(vm, Ws2812CanShow());
- if (s_ws2812_grb) be_pushbool(vm, s_ws2812_grb->CanShow());
- if (s_sk6812_grbw) be_pushbool(vm, s_sk6812_grbw->CanShow());
+ if (strip) be_pushbool(vm, strip->CanShow());
+ else be_pushbool(vm, Ws2812CanShow());
break;
case 4: // # 04 : IsDirty void -> bool
- if (native) be_pushbool(vm, Ws2812IsDirty());
- if (s_ws2812_grb) be_pushbool(vm, s_ws2812_grb->IsDirty());
- if (s_sk6812_grbw) be_pushbool(vm, s_sk6812_grbw->IsDirty());
+ if (strip) be_pushbool(vm, strip->IsDirty());
+ else be_pushbool(vm, Ws2812IsDirty());
break;
case 5: // # 05 : Dirty void -> void
- if (native) Ws2812Dirty();
- if (s_ws2812_grb) s_ws2812_grb->Dirty();
- if (s_sk6812_grbw) s_sk6812_grbw->Dirty();
+ if (strip) strip->Dirty();
+ else Ws2812Dirty();
break;
case 6: // # 06 : Pixels void -> bytes() (mapped to the buffer)
- {
- uint8_t * pixels;
- if (native) pixels = Ws2812Pixels();
- if (s_ws2812_grb) pixels = s_ws2812_grb->Pixels();
- if (s_sk6812_grbw) pixels = s_sk6812_grbw->Pixels();
-
- be_pushcomptr(vm, pixels);
- }
+ if (strip) be_pushcomptr(vm, strip->Pixels());
+ else be_pushcomptr(vm, Ws2812Pixels());
break;
case 7: // # 07 : PixelSize void -> int
- if (native) be_pushint(vm, Ws2812PixelSize());
- if (s_ws2812_grb) be_pushint(vm, s_ws2812_grb->PixelSize());
- if (s_sk6812_grbw) be_pushint(vm, s_sk6812_grbw->PixelSize());
+ if (strip) be_pushint(vm, strip->PixelSize());
+ else be_pushint(vm, Ws2812PixelSize());
break;
case 8: // # 08 : PixelCount void -> int
- if (native) be_pushint(vm, Ws2812PixelCount());
- if (s_ws2812_grb) be_pushint(vm, s_ws2812_grb->PixelCount());
- if (s_sk6812_grbw) be_pushint(vm, s_sk6812_grbw->PixelCount());
+ if (strip) be_pushint(vm, strip->PixelCount());
+ else be_pushint(vm, Ws2812PixelCount());
break;
case 9: // # 09 : ClearTo (color:??) -> void
{
@@ -252,44 +202,37 @@ extern "C" {
if (from < 0) { from = 0; }
if (len < 0) { len = 0; }
- if (native) Ws2812ClearTo(r, g, b, w, from, from + len - 1);
- if (s_ws2812_grb) s_ws2812_grb->ClearTo(RgbColor(r, g, b), from, from + len - 1);
- if (s_sk6812_grbw) s_sk6812_grbw->ClearTo(RgbwColor(r, g, b, w), from, from + len - 1);
+ if (strip) strip->ClearTo(rgbw, from, from + len - 1);
+ else Ws2812ClearTo(r, g, b, w, from, from + len - 1);
} else {
- if (native) Ws2812ClearTo(r, g, b, w, -1, -1);
- if (s_ws2812_grb) s_ws2812_grb->ClearTo(RgbColor(r, g, b));
- if (s_sk6812_grbw) s_sk6812_grbw->ClearTo(RgbwColor(r, g, b, w));
+ if (strip) strip->ClearTo(rgbw);
+ else Ws2812ClearTo(r, g, b, w, -1, -1);
}
}
break;
- case 10: // # 10 : SetPixelColor (idx:int, color:??) -> void
+ case 10: // # 10 : SetPixelColor (idx:int, color:int wrgb) -> void
{
int32_t idx = be_toint(vm, 3);
- uint32_t rgbw = be_toint(vm, 4);
- uint8_t w = (rgbw >> 24) & 0xFF;
- uint8_t r = (rgbw >> 16) & 0xFF;
- uint8_t g = (rgbw >> 8) & 0xFF;
- uint8_t b = (rgbw ) & 0xFF;
- if (native) Ws2812SetPixelColor(idx, r, g, b, w);
- if (s_ws2812_grb) s_ws2812_grb->SetPixelColor(idx, RgbColor(r, g, b));
- if (s_sk6812_grbw) s_sk6812_grbw->SetPixelColor(idx, RgbwColor(r, g, b, w));
+ uint32_t wrgb = be_toint(vm, 4);
+ if (strip) {
+ strip->SetPixelColor(idx, wrgb);
+ } else {
+ uint8_t w = (wrgb >> 24) & 0xFF;
+ uint8_t r = (wrgb >> 16) & 0xFF;
+ uint8_t g = (wrgb >> 8) & 0xFF;
+ uint8_t b = (wrgb ) & 0xFF;
+ Ws2812SetPixelColor(idx, r, g, b, w);
+ }
}
break;
- case 11: // # 11 : GetPixelColor (idx:int) -> color:??
+ case 11: // # 11 : GetPixelColor (idx:int) -> color:int wrgb
{
int32_t idx = be_toint(vm, 3);
-
- if (native) {
+ if (strip) {
+ be_pushint(vm, strip->GetPixelColor(idx));
+ } else {
be_pushint(vm, Ws2812GetPixelColor(idx));
}
- if (s_ws2812_grb) {
- RgbColor rgb = s_ws2812_grb->GetPixelColor(idx);
- be_pushint(vm, (rgb.R << 16) | (rgb.G << 8) | rgb.B);
- }
- if (s_sk6812_grbw) {
- RgbwColor rgbw = s_sk6812_grbw->GetPixelColor(idx);
- be_pushint(vm, (rgbw.W << 24) | (rgbw.R << 16) | (rgbw.G << 8) | rgbw.B);
- }
}
break;
default:
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino
index 24835b64f4c7..5c17dc13c42e 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino
@@ -21,34 +21,39 @@
#ifdef USE_BERRY_ULP
#include
-#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
+// #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_ULP_COPROC_TYPE_LP_CORE)
+#if defined(CONFIG_ULP_COPROC_ENABLED)
#if defined(CONFIG_IDF_TARGET_ESP32)
#include "esp32/ulp.h"
#endif // esp32
-#if ESP_IDF_VERSION_MAJOR < 5
- #if defined(CONFIG_IDF_TARGET_ESP32S2)
- #include "esp32s2/ulp.h"
- #include "esp32s2/ulp_riscv.h"
- #include "esp32s2/ulp_riscv_adc.h"
- #endif // s2
- #if defined(CONFIG_IDF_TARGET_ESP32S3)
- #include "esp32s3/ulp.h"
- #include "esp32s3/ulp_riscv.h"
- #include "esp32s3/ulp_riscv_adc.h"
- #endif //s3
-#endif // ESP_IDF_VERSION_MAJOR < 5
+// #if ESP_IDF_VERSION_MAJOR < 5
+// #if defined(CONFIG_IDF_TARGET_ESP32S2)
+// #include "esp32s2/ulp.h"
+// #include "esp32s2/ulp_riscv.h"
+// #include "esp32s2/ulp_riscv_adc.h"
+// #endif // s2
+// #if defined(CONFIG_IDF_TARGET_ESP32S3)
+// #include "esp32s3/ulp.h"
+// #include "esp32s3/ulp_riscv.h"
+// #include "esp32s3/ulp_riscv_adc.h"
+// #endif //s3
+// #endif // ESP_IDF_VERSION_MAJOR < 5
#include "driver/rtc_io.h"
#include "driver/gpio.h"
-#if ESP_IDF_VERSION_MAJOR >= 5
+// #if ESP_IDF_VERSION_MAJOR >= 5
#include "esp_adc/adc_oneshot.h"
#include "ulp_adc.h"
- #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
+ #if defined(CONFIG_ULP_COPROC_TYPE_RISCV) // S2 or S3
#include "ulp_riscv.h"
#endif // defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
-#else
- #include "driver/adc.h"
-#endif // ESP_IDF_VERSION_MAJOR >= 5
+// #else
+// #include "driver/adc.h"
+// #endif // ESP_IDF_VERSION_MAJOR >= 5
+#ifdef CONFIG_ULP_COPROC_TYPE_LP_CORE
+ #include "ulp_lp_core.h"
+ ulp_lp_core_cfg_t be_ulp_lp_core_cfg;
+#endif //CONFIG_ULP_COPROC_TYPE_LP_CORE
#include "sdkconfig.h"
@@ -60,14 +65,21 @@ extern "C" {
void be_ULP_run(int32_t entry) {
#if defined(CONFIG_IDF_TARGET_ESP32)
ulp_run(entry); // entry point should be at the beginning of program
-#else // S2 or S3
+#elif defined(CONFIG_ULP_COPROC_TYPE_RISCV) // S2 or S3
ulp_riscv_run();
+#else // lp_core
+ int err = ulp_lp_core_run(&be_ulp_lp_core_cfg);
#endif
}
// `ULP.wake_period(period_index:int, period_us:int) -> nil`
void be_ULP_wake_up_period(int32_t period_index, int32_t period_us) {
+#ifdef CONFIG_ULP_COPROC_TYPE_LP_CORE
+ be_ulp_lp_core_cfg.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER;
+ be_ulp_lp_core_cfg.lp_timer_sleep_duration_us = period_us;
+#else
ulp_set_wakeup_period(period_index, period_us);
+#endif //CONFIG_ULP_COPROC_TYPE_LP_CORE
}
// `ULP.set_mem(position:int, value:int) -> value:int`
@@ -102,7 +114,6 @@ extern "C" {
// enums: channel 0-7, attenuation 0-3, width 0-3
void be_ULP_adc_config(struct bvm *vm, int32_t channel, int32_t attenuation, int32_t width) {
#if defined(CONFIG_IDF_TARGET_ESP32)
-#if ESP_IDF_VERSION_MAJOR >= 5
ulp_adc_cfg_t cfg = {
.adc_n = ADC_UNIT_1,
.channel = (adc_channel_t)channel,
@@ -111,20 +122,7 @@ extern "C" {
.ulp_mode = ADC_ULP_MODE_FSM,
};
esp_err_t err = ulp_adc_init(&cfg);
-#else
- esp_err_t err = adc1_config_channel_atten((adc1_channel_t)channel, (adc_atten_t)attenuation);
- err += adc1_config_width((adc_bits_width_t)width);
-#endif // ESP_IDF_VERSION_MAJOR >= 5
- if (err != ESP_OK) {
- be_raisef(vm, "ulp_adc_config_error", "ULP: invalid code err=%i", err);
- }
-#if ESP_IDF_VERSION_MAJOR < 5
- else {
- adc1_ulp_enable();
- }
-#endif // ESP_IDF_VERSION_MAJOR < 5
-#else // S2 or S3
-#if ESP_IDF_VERSION_MAJOR >= 5
+#elif defined(CONFIG_ULP_COPROC_TYPE_RISCV) // S2 or S3
ulp_adc_cfg_t cfg = {
.adc_n = ADC_UNIT_1,
.channel = (adc_channel_t)channel,
@@ -134,17 +132,18 @@ extern "C" {
};
esp_err_t err = ulp_adc_init(&cfg);
#else
- ulp_riscv_adc_cfg_t cfg = {
- .channel = (adc_channel_t)channel,
- .atten = (adc_atten_t)attenuation,
- .width = (adc_bits_width_t)width
- };
- esp_err_t err = ulp_riscv_adc_init(&cfg);
-#endif // ESP_IDF_VERSION_MAJOR >= 5
+ // esp_err_t err = lp_core_lp_adc_init(ADC_UNIT_1);
+ // const lp_core_lp_adc_chan_cfg_t config = {
+ // .atten = (adc_atten_t)attenuation,
+ // .bitwidth = (adc_bitwidth_t)width,
+ // };
+ // err += lp_core_lp_adc_config_channel(ADC_UNIT_1, (adc_channel_t)channel, &config);
+ be_raisef(vm, "ulp_adc_config_error", "ULP: not supported before ESP-IDF 5.4");
+ esp_err_t err = ESP_FAIL;
+#endif
if (err != ESP_OK) {
be_raisef(vm, "ulp_adc_config_error", "ULP: invalid code err=%i", err);
}
-#endif
}
/**
@@ -156,8 +155,10 @@ extern "C" {
void be_ULP_load(struct bvm *vm, const uint8_t *buf, size_t size) {
#if defined(CONFIG_IDF_TARGET_ESP32)
esp_err_t err = ulp_load_binary(0, buf, size / 4); // FSM type only, specific header, size in long words
-#else // S2 or S3
+#elif defined(CONFIG_ULP_COPROC_TYPE_RISCV) // S2 or S3
esp_err_t err = ulp_riscv_load_binary(buf, size); // there are no header bytes, just load and hope for a valid binary - size in bytes
+#else
+ esp_err_t err = ulp_lp_core_load_binary(buf,size); // check valid size, size in bytes
#endif // defined(CONFIG_IDF_TARGET_ESP32)
if (err != ESP_OK) {
be_raisef(vm, "ulp_load_error", "ULP: invalid code err=%i", err);
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino b/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino
index 809ec86ac5f3..3e20116a2592 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino
@@ -31,15 +31,28 @@ struct Shift595 {
uint8_t pinOE;
uint8_t outputs;
uint8_t first;
- bool connected = false;
} *Shift595 = nullptr;
+/*********************************************************************************************\
+ * Low level Shift 74x595
+\*********************************************************************************************/
+
void Shift595ConfigurePin(uint8_t pin, uint8_t value = 0) {
pinMode(pin, OUTPUT);
digitalWrite(pin, value);
}
-void Shift595Init(void) {
+void Shift595LatchPin(uint8_t pin) {
+ digitalWrite(pin, 1);
+ digitalWrite(pin, 0);
+}
+
+/*********************************************************************************************\
+ * FUNC_SETUP_RING2 at T +1
+ * Claim devices_present
+\*********************************************************************************************/
+
+void Shift595ModuleInit(void) {
if (PinUsed(GPIO_SHIFT595_SRCLK) && PinUsed(GPIO_SHIFT595_RCLK) && PinUsed(GPIO_SHIFT595_SER)) {
Shift595 = (struct Shift595*)calloc(1, sizeof(struct Shift595));
if (Shift595) {
@@ -50,45 +63,70 @@ void Shift595Init(void) {
Shift595ConfigurePin(Shift595->pinSRCLK);
Shift595ConfigurePin(Shift595->pinRCLK);
Shift595ConfigurePin(Shift595->pinSER);
+#ifdef ESP32
+ // Release hold on clocks (if set before restart)
+ gpio_hold_dis((gpio_num_t)Shift595->pinSRCLK);
+ gpio_hold_dis((gpio_num_t)Shift595->pinRCLK);
+#endif
if (PinUsed(GPIO_SHIFT595_OE)) {
Shift595->pinOE = Pin(GPIO_SHIFT595_OE);
- Shift595ConfigurePin(Shift595->pinOE, 1);
+ // Fix relay toggle at restart
+ // On power ON set all outputs to 3-state (3-state converted to OFF by ULN2803 relay drivers)
+ Shift595ConfigurePin(Shift595->pinOE, ResetReasonPowerOn());
}
- Shift595->first = TasmotaGlobal.devices_present;
- Shift595->outputs = Settings->shift595_device_count * 8;
+ Shift595->first = TasmotaGlobal.devices_present; // devices_present offset
+ Shift595->outputs = Settings->shift595_device_count * 8; // Max number of outputs present
UpdateDevicesPresent(Shift595->outputs);
-
- Shift595->connected = true;
- AddLog(LOG_LEVEL_DEBUG, PSTR("595: Controlling relays POWER%d to POWER%d"), Shift595->first + 1, Shift595->first + Shift595->outputs);
}
}
}
-void Shift595LatchPin(uint8_t pin) {
- digitalWrite(pin, 1);
- digitalWrite(pin, 0);
-}
+/*********************************************************************************************\
+ * FUNC_SET_POWER at T +2
+ * Reduce devices_present with display and/or lights not known before
+ * Add offset for previous defined relays
+\*********************************************************************************************/
void Shift595SwitchRelay(void) {
- if (Shift595 && Shift595->connected == true) {
- for (uint32_t i = 0; i < Shift595->outputs; i++) {
- uint8_t relay_state = bitRead(XdrvMailbox.index, Shift595->first + Shift595->outputs -1 -i);
- digitalWrite(Shift595->pinSER, Settings->flag5.shift595_invert_outputs ? !relay_state : relay_state);
- Shift595LatchPin(Shift595->pinSRCLK);
+ // XdrvMailbox.index = 32-bit rpower bit mask
+ // Use relative and sequential relay indexes
+ power_t rpower = XdrvMailbox.index;
+ uint32_t relay_max = Shift595->outputs; // Total number of outputs
+ DevicesPresentNonDisplayOrLight(relay_max); // Skip display and/or light(s)
+ uint32_t relay_offset = Shift595->outputs - relay_max + Shift595->first;
+
+ static bool first = false;
+ if (!first) {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("595: Output 1 to %d use POWER%d to POWER%d"), Shift595->outputs - relay_offset, Shift595->first + 1, relay_max);
+ first = true;
+ }
+
+ uint32_t power_bit = relay_max -1; // Start at highest non display and/or light power bit
+ for (uint32_t i = 0; i < Shift595->outputs; i++) { // We need to set all shift outputs even if not used
+ uint32_t relay_state = 0; // Unused state
+ if (i >= relay_offset) {
+ relay_state = bitRead(rpower, power_bit); // Shift-in from high to low
+ power_bit--;
}
+ digitalWrite(Shift595->pinSER, Settings->flag5.shift595_invert_outputs ? !relay_state : relay_state); // SetOption133 - (Shift595) Invert outputs of 74x595 shift registers
+ Shift595LatchPin(Shift595->pinSRCLK);
+ }
- Shift595LatchPin(Shift595->pinRCLK);
+ Shift595LatchPin(Shift595->pinRCLK);
- if (PinUsed(GPIO_SHIFT595_OE)) {
- digitalWrite(Shift595->pinOE, 0);
- }
+ if (PinUsed(GPIO_SHIFT595_OE)) {
+ digitalWrite(Shift595->pinOE, 0);
}
}
+/*********************************************************************************************\
+ * Commands
+\*********************************************************************************************/
+
void CmndShift595Devices(void) {
- if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 3)) {
+ if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload * 8 <= MAX_RELAYS_SET - Shift595->first)) {
Settings->shift595_device_count = (1 == XdrvMailbox.payload) ? SHIFT595_DEVICE_COUNT : XdrvMailbox.payload;
TasmotaGlobal.restart_flag = 2;
}
@@ -102,13 +140,20 @@ void CmndShift595Devices(void) {
bool Xdrv60(uint32_t function) {
bool result = false;
- if (FUNC_PRE_INIT == function) {
- Shift595Init();
+ if (FUNC_SETUP_RING2 == function) {
+ Shift595ModuleInit();
} else if (Shift595) {
switch (function) {
case FUNC_SET_POWER:
Shift595SwitchRelay();
break;
+#ifdef ESP32
+ case FUNC_SAVE_BEFORE_RESTART:
+ // Set hold on clocks to disable relay click on restart
+ gpio_hold_en((gpio_num_t)Shift595->pinSRCLK);
+ gpio_hold_en((gpio_num_t)Shift595->pinRCLK);
+ break;
+#endif // ESP32
case FUNC_COMMAND:
result = DecodeCommand(kShift595Commands, Shift595Command);
break;
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino b/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino
index 8cb1196ac06f..6afa820e4c20 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino
@@ -26,6 +26,7 @@
* Supported template fields:
* NAME - Template name
* BASE - Optional. 0 = use relative buttons and switches (default), 1 = use absolute buttons and switches
+ * IOCON - Optional. IOCON I/O Expander configuration register (bitmap: 0 MIRROR 0 DISSLW HAEN ODR INTPOL 0. Default 0b01011000 = 0x58)
* GPIO - Sequential list of pin 1 and up with configured GPIO function
* Function Code Description
* ------------------- -------- ----------------------------------------
@@ -60,6 +61,9 @@
*
* Buttons and relays B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8
* {"NAME":"MCP23017 A=B1-8, B=R1-8","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231]}
+ *
+ * Buttons and relays with open-drain INT B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8
+ * {"NAME":"MCP23017 A=B1-8, B=R1-8","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231],"IOCON":0x5C}
*
* Buttons, relays, buttons and relays B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8 B9 B10B11B12B13B14B15B16R9 R10 R11 R12 R13 R14 R15 R16
* {"NAME":"MCP23017 A=B1-8, B=R1-8, C=B9-16, D=R9-16","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231,40,41,42,43,44,45,46,47,232,233,234,235,236,237,238,239]}
@@ -90,6 +94,8 @@
* MCP23017 support
\*********************************************************************************************/
+#define D_JSON_IOCON "IOCON"
+
enum MCP23S08GPIORegisters {
MCP23X08_IODIR = 0x00,
MCP23X08_IPOL = 0x01,
@@ -140,11 +146,25 @@ typedef struct {
uint8_t olatb;
uint8_t address;
uint8_t interface;
- uint8_t pins; // 8 (MCP23x08) or 16 (MCP23x17)
+ uint8_t pins; // 8 (MCP23x08) or 16 (MCP23x17)
int8_t pin_cs;
int8_t pin_int;
} tMcp23xDevice;
+typedef union { // Restricted by MISRA-C Rule 18.4 but so useful...
+ uint8_t reg; // Allow bit manipulation using template IOCON
+ struct {
+ uint8_t spare0 : 1; // 0 Unimplemented
+ uint8_t INTPOL : 1; // (0) INT pin active-low. (1) active-high
+ uint8_t ODR : 1; // (0) INT pin active driver output. (1) Open-drain output (overrides INTPOL)
+ uint8_t HAEN : 1; // (1) Hardware Address enabled (MCS23S17 only)
+ uint8_t DISSLW : 1; // (0) SDA output slew rate disabled
+ uint8_t SEQOP : 1; // 0 Sequential operation enabled, address pointer increments
+ uint8_t MIRROR : 1; // (1) INT pins are internally connected
+ uint8_t BANK : 1; // 0 Registers are in the same bank (addresses are sequential) (MCS23x17 only)
+ };
+} tIOCON;
+
struct MCP230 {
tMcp23xDevice device[MCP23XXX_MAX_DEVICES];
uint32_t relay_inverted;
@@ -156,6 +176,7 @@ struct MCP230 {
uint8_t relay_offset;
uint8_t button_max;
uint8_t switch_max;
+ tIOCON iocon;
int8_t button_offset;
int8_t switch_offset;
bool base;
@@ -339,15 +360,15 @@ void MCP23xPinMode(uint8_t pin, uint8_t flags) {
}
switch (flags) {
case INPUT:
- MCP23xUpdate(pin, true, iodir);
- MCP23xUpdate(pin, false, gppu);
+ MCP23xUpdate(pin, true, iodir); // Pin is configured as an input
+ MCP23xUpdate(pin, false, gppu); // Pull-up disabled
break;
case INPUT_PULLUP:
- MCP23xUpdate(pin, true, iodir);
- MCP23xUpdate(pin, true, gppu);
+ MCP23xUpdate(pin, true, iodir); // Pin is configured as an input
+ MCP23xUpdate(pin, true, gppu); // Pull-up enabled
break;
case OUTPUT:
- MCP23xUpdate(pin, false, iodir);
+ MCP23xUpdate(pin, false, iodir); // Pin is configured as an output
break;
}
@@ -371,21 +392,21 @@ void MCP23xPinInterruptMode(uint8_t pin, uint8_t interrupt_mode) {
}
switch (interrupt_mode) {
case MCP23XXX_CHANGE:
- MCP23xUpdate(pin, true, gpinten);
- MCP23xUpdate(pin, false, intcon);
+ MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event
+ MCP23xUpdate(pin, false, intcon); // Pin value is compared against the previous pin value
break;
case MCP23XXX_RISING:
- MCP23xUpdate(pin, true, gpinten);
- MCP23xUpdate(pin, true, intcon);
- MCP23xUpdate(pin, true, defval);
+ MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event
+ MCP23xUpdate(pin, true, intcon); // Controls how the associated pin value is compared for interrupt-on-change
+ MCP23xUpdate(pin, false, defval); // If the associated pin level is the opposite from the register bit, an interrupt occurs.
break;
case MCP23XXX_FALLING:
- MCP23xUpdate(pin, true, gpinten);
- MCP23xUpdate(pin, true, intcon);
- MCP23xUpdate(pin, false, defval);
+ MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event
+ MCP23xUpdate(pin, true, intcon); // Controls how the associated pin value is compared for interrupt-on-change
+ MCP23xUpdate(pin, true, defval); // If the associated pin level is the opposite from the register bit, an interrupt occurs.
break;
case MCP23XXX_NO_INTERRUPT:
- MCP23xUpdate(pin, false, gpinten);
+ MCP23xUpdate(pin, false, gpinten); // Disable GPIO input pin for interrupt-on-change event
break;
}
}
@@ -394,7 +415,7 @@ void MCP23xSetPinModes(uint8_t pin, uint8_t flags) {
// pin 0 - 63
MCP23xPinMode(pin, flags);
if (Mcp23x.device[Mcp23x.chip].pin_int > -1) { // Mcp23x.chip is updated by call to MCP23xPinMode
- MCP23xPinInterruptMode(pin, MCP23XXX_CHANGE);
+ MCP23xPinInterruptMode(pin, (Mcp23x.iocon.ODR) ? MCP23XXX_FALLING : MCP23XXX_CHANGE);
}
}
@@ -457,6 +478,15 @@ uint32_t MCP23xGetPin(uint32_t lpin) {
/*********************************************************************************************/
+bool MCP23xAddItem(uint8_t &item) {
+ if (item >= MAX_RELAYS_SET) { // MAX_RELAYS_SET = MAX_SWITCHES_SET = MAX_KEYS_SET = 32
+ AddLog(LOG_LEVEL_INFO, PSTR("MCP: Max reached"));
+ return false;
+ }
+ item++;
+ return true;
+}
+
String MCP23xTemplateLoadFile(void) {
String mcptmplt = "";
#ifdef USE_UFILESYS
@@ -497,7 +527,7 @@ bool MCP23xLoadTemplate(void) {
}
val = root[PSTR(D_JSON_NAME)];
if (val) {
- AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Base %d, Template '%s'"), Mcp23x.base, val.getStr());
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: IOCON 0x%02X, Base %d, Template '%s'"), Mcp23x.iocon, Mcp23x.base, val.getStr());
}
JsonParserArray arr = root[PSTR(D_JSON_GPIO)];
if (arr) {
@@ -507,44 +537,36 @@ bool MCP23xLoadTemplate(void) {
if (!val) { break; }
uint16_t mpin = val.getUInt();
if (mpin) { // Above GPIO_NONE
- if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES_SET))) {
- Mcp23x.switch_max++;
+ if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES_SET)) && MCP23xAddItem(Mcp23x.switch_max)) {
MCP23xSetPinModes(pin, INPUT_PULLUP);
}
- else if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES_SET))) {
+ else if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES_SET)) && MCP23xAddItem(Mcp23x.switch_max)) {
mpin -= (AGPIO(GPIO_SWT1_NP) - AGPIO(GPIO_SWT1));
- Mcp23x.switch_max++;
MCP23xSetPinModes(pin, INPUT);
}
- else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS_SET))) {
- Mcp23x.button_max++;
+ else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
MCP23xSetPinModes(pin, INPUT_PULLUP);
}
- else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS_SET))) {
+ else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
mpin -= (AGPIO(GPIO_KEY1_NP) - AGPIO(GPIO_KEY1));
- Mcp23x.button_max++;
MCP23xSetPinModes(pin, INPUT);
}
- else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS_SET))) {
+ else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV));
mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1));
- Mcp23x.button_max++;
MCP23xSetPinModes(pin, INPUT_PULLUP);
}
- else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS_SET))) {
+ else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) {
bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV_NP));
mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1));
- Mcp23x.button_max++;
MCP23xSetPinModes(pin, INPUT);
}
- else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET))) {
- Mcp23x.relay_max++;
+ else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET)) && MCP23xAddItem(Mcp23x.relay_max)) {
MCP23xPinMode(pin, OUTPUT);
}
- else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS_SET))) {
+ else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS_SET)) && MCP23xAddItem(Mcp23x.relay_max)) {
bitSet(Mcp23x.relay_inverted, mpin - AGPIO(GPIO_REL1_INV));
mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1));
- Mcp23x.relay_max++;
MCP23xPinMode(pin, OUTPUT);
}
else if (mpin == AGPIO(GPIO_OUTPUT_HI)) {
@@ -558,14 +580,9 @@ bool MCP23xLoadTemplate(void) {
else { mpin = 0; }
Mcp23x_gpio_pin[pin] = mpin;
}
- if ((Mcp23x.switch_max >= MAX_SWITCHES_SET) ||
- (Mcp23x.button_max >= MAX_KEYS_SET) ||
- (Mcp23x.relay_max >= MAX_RELAYS_SET)) {
- AddLog(LOG_LEVEL_INFO, PSTR("MCP: Max reached (S%d/B%d/R%d)"), Mcp23x.switch_max, Mcp23x.button_max, Mcp23x.relay_max);
- break;
- }
}
Mcp23x.max_pins = pin; // Max number of configured pins
+ AddLog(LOG_LEVEL_INFO, PSTR("MCP: Pins %d (S%d/B%d/R%d)"), Mcp23x.max_pins, Mcp23x.switch_max, Mcp23x.button_max, Mcp23x.relay_max);
}
// AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Pins %d, Mcp23x_gpio_pin %*_V"), Mcp23x.max_pins, Mcp23x.max_pins, (uint8_t*)Mcp23x_gpio_pin);
@@ -582,6 +599,10 @@ uint32_t MCP23xTemplateGpio(void) {
JsonParserObject root = parser.getRootObject();
if (!root) { return 0; }
+ JsonParserToken val = root[PSTR(D_JSON_IOCON)];
+ if (val) {
+ Mcp23x.iocon.reg = val.getUInt() & 0x5E; // Only allow 0 MIRROR 0 DISSLW HAEN ODR INTPOL 0
+ }
JsonParserArray arr = root[PSTR(D_JSON_GPIO)];
if (arr.isArray()) {
return arr.size(); // Number of requested pins
@@ -590,9 +611,10 @@ uint32_t MCP23xTemplateGpio(void) {
}
void MCP23xModuleInit(void) {
+ Mcp23x.iocon.reg = 0b01011000; // Default 0x58 = Enable INT mirror, Disable Slew rate, HAEN pins for addressing
int32_t pins_needed = MCP23xTemplateGpio();
if (!pins_needed) {
- AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Invalid template"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: Invalid template"));
return;
}
@@ -606,7 +628,8 @@ void MCP23xModuleInit(void) {
#endif
while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && PinUsed(GPIO_MCP23SXX_CS, Mcp23x.max_devices)) {
Mcp23x.chip = Mcp23x.max_devices;
- Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, Mcp23x.chip)) ? Pin(GPIO_MCP23XXX_INT, Mcp23x.chip) : -1;
+ uint32_t pin_int = (Mcp23x.iocon.ODR) ? 0 : Mcp23x.chip; // INT ODR pins are open-drain outputs and supposedly connected together to one GPIO
+ Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, pin_int)) ? Pin(GPIO_MCP23XXX_INT, pin_int) : -1;
Mcp23x.device[Mcp23x.chip].pin_cs = Pin(GPIO_MCP23SXX_CS, Mcp23x.max_devices);
digitalWrite(Mcp23x.device[Mcp23x.chip].pin_cs, 1);
pinMode(Mcp23x.device[Mcp23x.chip].pin_cs, OUTPUT);
@@ -619,12 +642,14 @@ void MCP23xModuleInit(void) {
if (0x00 == buffer) { // MCP23S08
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S08 found at CS%d"), Mcp23x.chip +1);
Mcp23x.device[Mcp23x.chip].pins = 8;
- MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
+// MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing
+ MCP23xWrite(MCP23X08_IOCON, Mcp23x.iocon.reg & 0x3E);
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT);
} else if (0x80 == buffer) { // MCP23S17
AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S17 found at CS%d"), Mcp23x.chip +1);
Mcp23x.device[Mcp23x.chip].pins = 16;
- MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
+// MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
+ MCP23xWrite(MCP23X17_IOCONA, Mcp23x.iocon.reg);
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA);
Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB);
}
@@ -641,8 +666,9 @@ void MCP23xModuleInit(void) {
uint8_t mcp23xxx_address = MCP23XXX_ADDR_START;
while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && (mcp23xxx_address < MCP23XXX_ADDR_END)) {
Mcp23x.chip = Mcp23x.max_devices;
+ uint32_t pin_int = (Mcp23x.iocon.ODR) ? 0 : Mcp23x.chip; // INT pins are open-drain outputs and supposedly connected together to one GPIO
if (I2cSetDevice(mcp23xxx_address)) {
- Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, Mcp23x.chip)) ? Pin(GPIO_MCP23XXX_INT, Mcp23x.chip) : -1;
+ Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, pin_int)) ? Pin(GPIO_MCP23XXX_INT, pin_int) : -1;
Mcp23x.device[Mcp23x.chip].interface = MCP23X_I2C;
Mcp23x.device[Mcp23x.chip].address = mcp23xxx_address;
@@ -652,7 +678,8 @@ void MCP23xModuleInit(void) {
if (0x00 == buffer) {
I2cSetActiveFound(mcp23xxx_address, "MCP23008");
Mcp23x.device[Mcp23x.chip].pins = 8;
- MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing
+// MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing
+ MCP23xWrite(MCP23X08_IOCON, Mcp23x.iocon.reg & 0x3E);
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT);
Mcp23x.max_devices++;
}
@@ -660,7 +687,8 @@ void MCP23xModuleInit(void) {
I2cSetActiveFound(mcp23xxx_address, "MCP23017");
Mcp23x.device[Mcp23x.chip].pins = 16;
MCP23xWrite(MCP23X08_IOCON, 0x00); // Reset bank mode to 0 (MCP23X17_GPINTENB)
- MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
+// MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing
+ MCP23xWrite(MCP23X17_IOCONA, Mcp23x.iocon.reg);
Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA);
Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB);
Mcp23x.max_devices++;
@@ -691,6 +719,8 @@ void MCP23xModuleInit(void) {
return;
}
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: INT open-drain %d"), Mcp23x.iocon.ODR);
+
Mcp23x.relay_offset = TasmotaGlobal.devices_present;
Mcp23x.relay_max -= UpdateDevicesPresent(Mcp23x.relay_max);
@@ -745,8 +775,10 @@ void MCP23xInit(void) {
} else {
gpio = MCP23xRead16(MCP23X17_GPIOA); // Clear MCP23x17 interrupt
}
+ if (Mcp23x.iocon.ODR && Mcp23x.chip) { continue; }
+// pinMode(Mcp23x.device[Mcp23x.chip].pin_int, (Mcp23x.iocon.ODR) ? INPUT_PULLUP : INPUT);
pinMode(Mcp23x.device[Mcp23x.chip].pin_int, INPUT_PULLUP);
- attachInterrupt(Mcp23x.device[Mcp23x.chip].pin_int, MCP23xInputIsr, CHANGE);
+ attachInterrupt(Mcp23x.device[Mcp23x.chip].pin_int, MCP23xInputIsr, (Mcp23x.iocon.ODR) ? FALLING : CHANGE);
}
}
}
@@ -762,6 +794,7 @@ void MCP23xPower(void) {
rpower >>= Mcp23x.relay_offset;
relay_max = Mcp23x.relay_max;
}
+ DevicesPresentNonDisplayOrLight(relay_max); // Skip display and/or light(s)
for (uint32_t index = 0; index < relay_max; index++) {
power_t state = rpower &1;
if (MCP23xPinUsed(GPIO_REL1, index)) {
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_69_pca9557.ino b/tasmota/tasmota_xdrv_driver/xdrv_69_pca9557.ino
index 9ee9c1c8753f..5d11d31603b2 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_69_pca9557.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_69_pca9557.ino
@@ -360,7 +360,7 @@ uint32_t PCA9557TemplateGpio(void) {
void PCA9557ModuleInit(void) {
int32_t pins_needed = PCA9557TemplateGpio();
if (!pins_needed) {
- AddLog(LOG_LEVEL_DEBUG, PSTR("PCA: Invalid template"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PCA: Invalid template"));
return;
}
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_71_magic_switch.ino b/tasmota/tasmota_xdrv_driver/xdrv_71_magic_switch.ino
index a0c097b28070..46bc1e062634 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_71_magic_switch.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_71_magic_switch.ino
@@ -105,6 +105,12 @@ void MagicSwitchLoop()
}
}
+void MagicSwitchSetPower(void) {
+ // It can happen that on relay switch, disturbances on the mains is falsy see as a MagicSwitch pulse
+ // This restart the masking windows on every power change to avoid that effect
+ MagicSwitch->switch_state = MAGICSWITCH_MASKING_WINDOW_LEN;
+}
+
/********************************************************************************************************
* Driver initialisation
*/
@@ -173,6 +179,9 @@ bool Xdrv71(uint32_t function) {
//case FUNC_EVERY_250_MSECOND:
MagicSwitchLoop();
break;
+ case FUNC_SET_POWER:
+ MagicSwitchSetPower();
+ break;
case FUNC_ADD_SWITCH:
result = MagicSwitchAddSwitch();
break;
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino b/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino
index 2c5aa816dd12..81a6f779a8e8 100644
--- a/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino
+++ b/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino
@@ -108,6 +108,11 @@
BLEEnableUnsaved
*0/1 - if BLE is disabled, this can be used to enable BLE without
it being saved - useful as the last command in autoexec.bat
+ BLEFilterNames
+ BLEFilterNames0 - clear filter list
+ BLEFilterNames1 - , - set one or more device names
+ BLEMinRssiLevel
+ BLEMinRssiLevel - Sets the minimum allowable RSSI level for detected devices
Other drivers can add callbacks to receive advertisements
Other drivers can add 'operations' to be performed and receive callbacks from the operation's success or failure
@@ -134,6 +139,8 @@ i.e. the Bluetooth of the ESP can be shared without conflict.
*/
#define BLE_ESP32_ALIASES
+#define BLE_ESP32_FILTER_BY_NAME
+#define BLE_ESP32_FILTER_BY_RSSI
// uncomment for more diagnostic/information messages - + more flash use.
//#define BLE_ESP32_DEBUG
@@ -472,6 +479,12 @@ std::deque scancompleteCallbacks;
std::deque aliases;
#endif
+#ifdef BLE_ESP32_FILTER_BY_NAME
+std::vector bleFilterNames;
+#endif
+#ifdef BLE_ESP32_FILTER_BY_RSSI
+int minRSSI = -100;
+#endif
/*********************************************************************************************\
* constants
@@ -480,7 +493,7 @@ std::deque aliases;
#define D_CMND_BLE "BLE"
const char kBLE_Commands[] PROGMEM = D_CMND_BLE "|"
- "Period|Adv|Op|Mode|Details|Scan|Alias|Name|Debug|Devices|MaxAge|AddrFilter|EnableUnsaved";
+ "Period|Adv|Op|Mode|Details|Scan|Alias|Name|Debug|Devices|MaxAge|AddrFilter|EnableUnsaved|FilterNames|MinRssiLevel";
static void CmndBLEPeriod(void);
static void CmndBLEAdv(void);
@@ -495,6 +508,8 @@ static void CmndBLEDevices(void);
static void CmndBLEMaxAge(void);
static void CmndBLEAddrFilter(void);
static void CmndBLEEnableUnsaved(void);
+static void CmndBleFilterNames(void);
+static void CmndSetMinRSSI(void);
void (*const BLE_Commands[])(void) PROGMEM = {
&BLE_ESP32::CmndBLEPeriod,
@@ -509,7 +524,9 @@ void (*const BLE_Commands[])(void) PROGMEM = {
&BLE_ESP32::CmndBLEDevices,
&BLE_ESP32::CmndBLEMaxAge,
&BLE_ESP32::CmndBLEAddrFilter,
- &BLE_ESP32::CmndBLEEnableUnsaved
+ &BLE_ESP32::CmndBLEEnableUnsaved,
+ &BLE_ESP32::CmndBleFilterNames,
+ &BLE_ESP32::CmndSetMinRSSI
};
const char *successStates[] PROGMEM = {
@@ -1128,7 +1145,24 @@ void ReverseMAC(uint8_t _mac[]){
}
-
+/**
+ * @brief Search for device name in filer list
+ *
+ * @param deviceName device name string
+ */
+#ifdef BLE_ESP32_FILTER_BY_NAME
+bool isDeviceInFilter(const String& deviceName) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Device chcked in filter %s"), deviceName);
+#endif
+ for (const auto& filterName : bleFilterNames) {
+ if (deviceName == filterName) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
/*********************************************************************************************\
* Advertisment details
@@ -1372,9 +1406,24 @@ class BLEAdvCallbacks: public NimBLEScanCallbacks {
BLEAdvertisment.name[sizeof(BLEAdvertisment.name)-1] = 0;
}
+ int filter = 0;
+#ifdef BLE_ESP32_FILTER_BY_NAME
+ if (!bleFilterNames.empty()) {
+ if (!advertisedDevice->haveName() || !isDeviceInFilter(namestr))
+ {
+ filter = 1;
+ }
+ }
+#endif
+
+#ifdef BLE_ESP32_FILTER_BY_RSSI
+ if (advertisedDevice->getRSSI() < minRSSI) {
+ filter = 1;
+ }
+#endif
// log this device safely
- if (BLEAdvertisment.addrtype <= BLEAddressFilter){
+ if ((BLEAdvertisment.addrtype <= BLEAddressFilter) && (0 == filter) ){
addSeenDevice(BLEAdvertisment.addr, BLEAdvertisment.addrtype, BLEAdvertisment.name, BLEAdvertisment.RSSI);
}
@@ -2833,6 +2882,58 @@ void CmndBLEDetails(void){
}
+void CmndBleFilterNames(void) {
+#ifdef BLE_ESP32_FILTER_BY_NAME
+ int op = XdrvMailbox.index;
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Name %d %s"), op, XdrvMailbox.data);
+#endif
+
+ switch(op){
+ case 0:{
+ bleFilterNames.clear();
+ ResponseCmndDone();
+ } break;
+ case 1:{
+ if (XdrvMailbox.data_len) {
+ String filters = XdrvMailbox.data;
+ bleFilterNames.clear();
+
+ int start = 0;
+ int end = filters.indexOf(',');
+ while (end != -1) {
+ bleFilterNames.push_back(filters.substring(start, end));
+ start = end + 1;
+ end = filters.indexOf(',', start);
+ }
+ bleFilterNames.push_back(filters.substring(start));
+
+ Response_P(PSTR("{\"BLEFilterNames\":\"%s\"}"), filters.c_str());
+ } else {
+ String filterList;
+ for (const auto& name : bleFilterNames) {
+ if (!filterList.isEmpty()) {
+ filterList += ", ";
+ }
+ filterList += name;
+ }
+
+ Response_P(PSTR("{\"BLEFilterNames\":\"%s\"}"), filterList.c_str());
+ }
+ } break;
+ }
+#endif
+}
+
+void CmndSetMinRSSI(void) {
+#ifdef BLE_ESP32_FILTER_BY_RSSI
+ if (XdrvMailbox.data_len) {
+ minRSSI = atoi(XdrvMailbox.data);
+ }
+ Response_P(PSTR("{\"MinRSSI\":\"%d\"}"), minRSSI);
+#endif
+}
+
void CmndBLEAlias(void){
#ifdef BLE_ESP32_ALIASES
int op = XdrvMailbox.index;
@@ -3472,8 +3573,8 @@ const char HTTP_BTN_MENU_BLE[] PROGMEM =
const char HTTP_FORM_BLE[] PROGMEM =
" " D_BLE_PARAMETERS " "
"