diff --git a/CHANGELOG.md b/CHANGELOG.md index 025bd82e1..f7917e748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,20 @@ All notable changes to this project will be documented in this file. ## [Released] -## [9.4.0] 20210422 +## [9.4.0] 20210423 - Release Leslie +## [9.4.0.1] 20210423 +### Added +- Command ``Wifi 0/1`` for ESP8266 to turn wifi Off and On. When wifi is Off it is always returned On after a restart except for a wake-up from deepsleep (#11839) + +### Changed +- Zigbee refactored storage for device configuration and device last known data (#11838) + +### Fixed +- Command ``Power`` should not reset pulsetime (#11805) +- Teleperiod rule handling regression from v9.3.1.2 (#11851) + ## [9.3.1.4] 20210422 ### Added - Command ``TuyaTempSetRes 0..3`` to control Tuya Temperature Set Resolution (#11781) diff --git a/README.md b/README.md index 94fb33a4d..307274e5d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Tasmota logo](/tools/logo/TASMOTA_FullLogo_Vector.svg) -Alternative firmware for [ESP8266](https://en.wikipedia.org/wiki/ESP8266) based devices with **easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX**. +Alternative firmware for [ESP8266](https://en.wikipedia.org/wiki/ESP8266) and [ESP32](https://en.wikipedia.org/wiki/ESP32) based devices with **easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX**. _Written for PlatformIO with limited support for Arduino IDE._ [![GitHub version](https://img.shields.io/github/release/arendst/Tasmota.svg)](https://github.com/arendst/Tasmota/releases/latest) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 010a84890..13a5f20aa 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -83,10 +83,11 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota - Command ``SetOption126 1`` to enable DS18x20 arithmetic mean over teleperiod for JSON temperature based on [#11472](https://github.com/arendst/Tasmota/issues/11472) - Command ``Backlog0`` to allow execution of following commands without delay - Command ``TuyaTempSetRes 0..3`` to control Tuya Temperature Set Resolution [#11781](https://github.com/arendst/Tasmota/issues/11781) +- Command ``Wifi 0/1`` for ESP8266 to turn wifi Off and On. When wifi is Off it is always returned On after a restart except for a wake-up from deepsleep [#11839](https://github.com/arendst/Tasmota/issues/11839) - Commands ``MqttKeepAlive 1..100`` to set Mqtt Keep Alive timer (default 30) and ``MqttTimeout 1..100`` to set Mqtt Socket Timeout (default 4) [#5341](https://github.com/arendst/Tasmota/issues/5341) - Commands ``DisplayType`` to select sub-modules where implemented and ``DisplayInvert`` to select inverted display where implemented - Support for SML VBUS [#11125](https://github.com/arendst/Tasmota/issues/11125) -- Support for NEC and OPTOMA LCD/DLP Projector serial power control by Jan Bubík [#11145](https://github.com/arendst/Tasmota/issues/11145) +- Support for NEC and OPTOMA LCD/DLP Projector serial power control by Jan Bubík [#11145](https://github.com/arendst/Tasmota/issues/11145) - Support for XPT2046 touch screen digitizer on ILI9341 display by nonix [#11159](https://github.com/arendst/Tasmota/issues/11159) - Support for zigbee lumi.sensor_wleak [#11200](https://github.com/arendst/Tasmota/issues/11200) - Support for dummy energy monitor using user values set by commands ``VoltageSet``, ``CurrentSet``, ``PowerSet`` and ``FrequencySet``. Enable by selecting any GPIO as ``Option A2`` [#10640](https://github.com/arendst/Tasmota/issues/10640) @@ -126,7 +127,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota - Redesigned GUI by moving non-configuration buttons from ``Configuration`` to new submenu ``Consoles`` - In tasmota-sensors.bin enabled support for VL53L0X and disabled TSL2561 [#11711](https://github.com/arendst/Tasmota/issues/11711) - Add HLW8012/BL0937 average pulse calculation by Alex Lovett [#11722](https://github.com/arendst/Tasmota/issues/11722) -- ESP32 **tasmota32-knx**, **tasmota32-sensors** and **tasmota32-lite** binaries consolidated in **tasmota32.bin** binary +- Zigbee refactored storage for device configuration and device last known data [#11838](https://github.com/arendst/Tasmota/issues/11838) ### Fixed - PN532 on ESP32 Serial flush both Tx and Rx buffers [#10910](https://github.com/arendst/Tasmota/issues/10910) @@ -145,6 +146,8 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota - Alexa discovery for ZBBridge [#11576](https://github.com/arendst/Tasmota/issues/11576) - Telegram chat id incorrect size [#11660](https://github.com/arendst/Tasmota/issues/11660) - KNX energy yesterday [#11718](https://github.com/arendst/Tasmota/issues/11718) +- Command ``Power`` should not reset pulsetime [#11805](https://github.com/arendst/Tasmota/issues/11805) +- Teleperiod rule handling regression from v9.3.1.2 [#11851](https://github.com/arendst/Tasmota/issues/11851) ### Noted - ESP32 single core **tasmota32solo1.bin** binary can only be uploaded using the GUI as OTA upload will trigger the watchdog timer diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index d1897880a..681144edc 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -119,12 +119,12 @@ upload_port = COM4 lib_extra_dirs = ${library.lib_extra_dirs} ; *** ESP32 lib. ALWAYS needed for ESP32 !!! lib/libesp32 -; *** uncomment the following line if you want to use LVGL in a Tasmota32 build -; lib/libesp32_lvgl -; *** uncomment the following line if you want to use Bluetooth or Apple Homekit in a Tasmota32 build -; lib/libesp32_div -; *** uncomment the following line if you want to use Epaper driver epidy in your Tasmota32 build -; lib/libesp32_epdiy +; *** comment the following line if you dont use LVGL in a Tasmota32 build. Reduces compile time + lib/libesp32_lvgl +; *** comment the following line if you dont use Bluetooth or Apple Homekit in a Tasmota32 build. Reduces compile time + lib/libesp32_div +; *** uncomment the following line if you dont use Epaper driver epidy in your Tasmota32 build. Reduces compile time + lib/libesp32_epdiy [core32] ; Activate Stage Core32 by removing ";" in next 3 lines, if you want to override the standard core32 diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index e046703dd..b7147058b 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -784,6 +784,7 @@ #define USE_ZIGBEE_ZNP // Enable ZNP protocol, needed for CC2530 based devices // #define USE_ZIGBEE_EZSP // Enable EZSP protocol, needed for EFR32 EmberZNet based devices, like Sonoff Zigbee bridge // Note: USE_ZIGBEE_ZNP and USE_ZIGBEE_EZSP are mutually incompatible, you must select exactly one + // #define USE_ZIGBEE_EEPROM // Use the EEPROM from the Sonoff ZBBridge to save Zigbee configuration and data #define USE_ZIGBEE_CHANNEL 11 // Zigbee Channel (11-26) #define USE_ZIGBEE_TXRADIO_DBM 20 // Tx Radio power in dBm (only for EZSP, EFR32 can go up to 20 dBm) diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index a701203de..4cdfcdf6a 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -25,7 +25,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALBUFFER "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" D_CMND_SERIALDELIMITER "|" - D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" + D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_WIFI "|" D_CMND_DEVICENAME "|" D_CMND_FN "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_LEDPWM_ON "|" D_CMND_LEDPWM_OFF "|" D_CMND_LEDPWM_MODE "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM"|" D_CMND_SWITCHTEXT "|" @@ -41,7 +41,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix #endif // USE_DEVICE_GROUPS D_CMND_SENSOR "|" D_CMND_DRIVER #ifdef ESP32 - "|Info|" D_CMND_TOUCH_CAL "|" D_CMND_TOUCH_THRES "|" D_CMND_TOUCH_NUM "|" D_CMND_CPU_FREQUENCY "|" D_CMND_WIFI + "|Info|" D_CMND_TOUCH_CAL "|" D_CMND_TOUCH_THRES "|" D_CMND_TOUCH_NUM "|" D_CMND_CPU_FREQUENCY #endif // ESP32 ; @@ -53,7 +53,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = { &CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange, &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialBuffer, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, &CmndSerialDelimiter, - &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, + &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndWifi, &CmndDevicename, &CmndFriendlyname, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode, &CmndWifiPower, &CmndTempOffset, &CmndHumOffset, &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, &CmndSwitchText, @@ -69,7 +69,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = { #endif // USE_DEVICE_GROUPS &CmndSensor, &CmndDriver #ifdef ESP32 - , &CmndInfo, &CmndTouchCal, &CmndTouchThres, &CmndTouchNum, &CmndCpuFrequency, &CmndWifi + , &CmndInfo, &CmndTouchCal, &CmndTouchThres, &CmndTouchNum, &CmndCpuFrequency #endif // ESP32 }; @@ -2114,6 +2114,15 @@ void CmndWifiPower(void) ResponseCmndChar(WifiGetOutputPower().c_str()); } +void CmndWifi(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag4.network_wifi = XdrvMailbox.payload; + if (Settings.flag4.network_wifi) { WifiEnable(); } + } + ResponseCmndStateText(Settings.flag4.network_wifi); +} + #ifdef USE_I2C void CmndI2cScan(void) { @@ -2217,15 +2226,6 @@ void CmndInfo(void) { ResponseCmndDone(); } -void CmndWifi(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - Settings.flag4.network_wifi = XdrvMailbox.payload; - TasmotaGlobal.restart_flag = 2; - } - ResponseCmndStateText(Settings.flag4.network_wifi); -} - void CmndCpuFrequency(void) { if ((80 == XdrvMailbox.payload) || (160 == XdrvMailbox.payload) || (240 == XdrvMailbox.payload)) { setCpuFrequencyMhz(XdrvMailbox.payload); diff --git a/tasmota/support_esp.ino b/tasmota/support_esp.ino index 08c49807c..e03d1bdf2 100644 --- a/tasmota/support_esp.ino +++ b/tasmota/support_esp.ino @@ -79,24 +79,16 @@ void *special_realloc(void *ptr, size_t size) { } String GetDeviceHardware(void) { - char buff[10]; // esptool.py get_efuses uint32_t efuse1 = *(uint32_t*)(0x3FF00050); uint32_t efuse2 = *(uint32_t*)(0x3FF00054); // uint32_t efuse3 = *(uint32_t*)(0x3FF00058); // uint32_t efuse4 = *(uint32_t*)(0x3FF0005C); - bool is_8285 = ( (efuse1 & (1 << 4)) || (efuse2 & (1 << 16)) ); - if (is_8285 && (ESP.getFlashChipRealSize() > 1048576)) { - is_8285 = false; // ESP8285 can only have 1M flash + if (((efuse1 & (1 << 4)) || (efuse2 & (1 << 16))) && (ESP.getFlashChipRealSize() < 1048577)) { // ESP8285 can only have 1M flash + return F("ESP8285"); } - if (is_8285) { - strcpy_P(buff, PSTR("ESP8285")); - } else { - strcpy_P(buff, PSTR("ESP8266EX")); - } - - return String(buff); + return F("ESP8266EX"); } #endif @@ -561,7 +553,7 @@ typedef struct { uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG); uint32_t pkg_version = chip_ver & 0x7; - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HDW: ESP32 Model %d, Revision %d, Core %d, Package %d"), chip_info.model, chip_revision, chip_info.cores, chip_ver); +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HDW: ESP32 Model %d, Revision %d, Core %d, Package %d"), chip_info.model, chip_revision, chip_info.cores, chip_ver); switch (pkg_version) { case 0: @@ -599,7 +591,7 @@ typedef struct { uint32_t pkg_version = chip_ver & 0x7; // uint32_t pkg_version = esp_efuse_get_pkg_ver(); - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HDW: ESP32 Model %d, Revision %d, Core %d, Package %d"), chip_info.model, chip_revision, chip_info.cores, chip_ver); +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HDW: ESP32 Model %d, Revision %d, Core %d, Package %d"), chip_info.model, chip_revision, chip_info.cores, chip_ver); switch (pkg_version) { case 0: return F("ESP32-S2"); // Max 240MHz, Single core, QFN 7*7, ESP32-S2-WROOM, ESP32-S2-WROVER, ESP32-S2-Saola-1, ESP32-S2-Kaluga-1 @@ -627,7 +619,7 @@ typedef struct { uint32_t pkg_version = chip_ver & 0x7; // uint32_t pkg_version = esp_efuse_get_pkg_ver(); - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HDW: ESP32 Model %d, Revision %d, Core %d, Package %d"), chip_info.model, chip_revision, chip_info.cores, chip_ver); +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HDW: ESP32 Model %d, Revision %d, Core %d, Package %d"), chip_info.model, chip_revision, chip_info.cores, chip_ver); switch (pkg_version) { case 0: return F("ESP32-C3"); // Max 160MHz, Single core, QFN 5*5, ESP32-C3-WROOM-02, ESP32-C3-DevKitC-02 @@ -653,7 +645,7 @@ typedef struct { uint32_t pkg_version = chip_ver & 0x7; // uint32_t pkg_version = esp_efuse_get_pkg_ver(); - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HDW: ESP32 Model %d, Revision %d, Core %d, Package %d"), chip_info.model, chip_revision, chip_info.cores, chip_ver); +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("HDW: ESP32 Model %d, Revision %d, Core %d, Package %d"), chip_info.model, chip_revision, chip_info.cores, chip_ver); switch (pkg_version) { case 0: return F("ESP32-C6"); diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index bccf37f7f..07501638a 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -599,7 +599,9 @@ void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) } TasmotaGlobal.active_device = device; - SetPulseTimer((device -1) % MAX_PULSETIMERS, 0); + if (state != POWER_SHOW_STATE) { + SetPulseTimer((device -1) % MAX_PULSETIMERS, 0); + } static bool interlock_mutex = false; // Interlock power command pending power_t mask = 1 << (device -1); // Device to control @@ -1286,6 +1288,8 @@ void Every250mSeconds(void) if (Settings.flag4.network_wifi) { WifiCheck(TasmotaGlobal.wifi_state_flag); TasmotaGlobal.wifi_state_flag = WIFI_RESTART; + } else { + WifiDisable(); } break; case 3: // Every x.75 second @@ -1324,7 +1328,7 @@ void Every250mSeconds(void) StopWebserver(); } #ifdef USE_EMULATION - if (Settings.flag2.emulation) { UdpConnect(); } + if (Settings.flag2.emulation) { UdpConnect(); } #endif // USE_EMULATION #endif // USE_WEBSERVER diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index e85374eda..3f4b96ef8 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -567,6 +567,10 @@ RF_PRE_INIT() } #endif // WIFI_RF_PRE_INIT +void WifiEnable(void) { + Wifi.counter = 1; +} + void WifiConnect(void) { if (!Settings.flag4.network_wifi) { return; } @@ -623,6 +627,14 @@ void WifiShutdown(bool option = false) delay(100); // Flush anything in the network buffers. } +void WifiDisable(void) { + if (!TasmotaGlobal.global_state.wifi_down) { + WifiShutdown(); + WifiSetMode(WIFI_OFF); + } + TasmotaGlobal.global_state.wifi_down = 1; +} + void EspRestart(void) { ResetPwm(); diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h index e0ad51e8e..39df38727 100644 --- a/tasmota/tasmota.h +++ b/tasmota/tasmota.h @@ -311,7 +311,7 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FU FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR, FUNC_ANY_KEY, FUNC_ENERGY_EVERY_SECOND, FUNC_ENERGY_RESET, - FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, + FUNC_RULES_PROCESS, FUNC_TELEPERIOD_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_CONSOLE_BUTTON, FUNC_WEB_ADD_MANAGEMENT_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS, FUNC_SET_SCHEME, FUNC_HOTPLUG_SCAN, FUNC_DEVICE_GROUP_ITEM }; diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 02aef4f39..b56b89a22 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -140,7 +140,6 @@ struct { int16_t save_data_counter; // Counter and flag for config save to Flash RulesBitfield rules_flag; // Rule state flags (16 bits) - bool rule_teleperiod; // Process rule based on teleperiod data using prefix TELE- bool serial_local; // Handle serial locally bool fallback_topic_flag; // Use Topic or FallbackTopic bool backlog_nodelay; // Execute all backlog commands with no delay @@ -288,6 +287,17 @@ void setup(void) { UpdateQuickPowerCycle(true); } + if (ResetReason() != REASON_DEEP_SLEEP_AWAKE) { +#ifdef ESP8266 + Settings.flag4.network_wifi = 1; // Make sure we're in control +#endif +#ifdef ESP32 + if (!Settings.flag4.network_ethernet) { + Settings.flag4.network_wifi = 1; // Make sure we're in control + } +#endif + } + TasmotaGlobal.stop_flash_rotate = Settings.flag.stop_flash_rotate; // SetOption12 - Switch between dynamic or fixed slot flash save location TasmotaGlobal.save_data_counter = Settings.save_data; TasmotaGlobal.sleep = Settings.sleep; diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index 0ee06c35d..54c953c80 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -607,6 +607,7 @@ #define USE_ZIGBEE #undef USE_ZIGBEE_ZNP #define USE_ZIGBEE_EZSP +#define USE_ZIGBEE_EEPROM // EEPROM of Sonoff ZBBridge via I2C #define USE_TCP_BRIDGE #define USE_ZIGBEE_CHANNEL 11 // Zigbee Channel (11-26) #define USE_ZIGBEE_COALESCE_ATTR_TIMER 350 // timer to coalesce attribute values (in ms) diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h index 2eb37309d..bc0f9cd74 100644 --- a/tasmota/tasmota_globals.h +++ b/tasmota/tasmota_globals.h @@ -261,7 +261,8 @@ const uint16_t LOG_BUFFER_SIZE = 4000; // Max number of characters in lo #define TASM_FILE_DRIVER "/.drvset%03d" #define TASM_FILE_SENSOR "/.snsset%03d" #define TASM_FILE_TLSKEY "/tlskey" // TLS private key -#define TASM_FILE_ZIGBEE "/zb" // Zigbee settings blob as used by CC2530 on ESP32 +#define TASM_FILE_ZIGBEE "/zb" // Zigbee devices information blob +#define TASM_FILE_ZIGBEE_DATA "/zbdata" // Zigbee last known values of devices #define TASM_FILE_AUTOEXEC "/autoexec.bat" // Commands executed after restart #define TASM_FILE_CONFIG "/config.sys" // Settings executed after restart diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index d4ac6a66e..b2692f6b0 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -174,6 +174,7 @@ struct RULES { uint16_t last_minute = 60; uint16_t vars_event = 0; // Bitmask supporting MAX_RULE_VARS bits uint16_t mems_event = 0; // Bitmask supporting MAX_RULE_MEMS bits + bool teleperiod = false; bool busy = false; bool no_execute = false; // Don't actually execute rule commands @@ -420,7 +421,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all // Step1: Analyse rule String rule_expr = rule; // "TELE-INA219#CURRENT>0.100" - if (TasmotaGlobal.rule_teleperiod) { + if (Rules.teleperiod) { int ppos = rule_expr.indexOf(F("TELE-")); // "TELE-INA219#CURRENT>0.100" or "INA219#CURRENT>0.100" if (ppos == -1) { return false; } // No pre-amble in rule rule_expr = rule.substring(5); // "INA219#CURRENT>0.100" or "SYSTEM#BOOT" @@ -433,7 +434,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all // rule_param = "0.100" or "%VAR1%" #ifdef DEBUG_RULES -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("RUL-RM1: expr %s, name %s, param %s"), rule_expr.c_str(), rule_name.c_str(), rule_param.c_str()); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("RUL-RM1: Teleperiod %d, Expr %s, Name %s, Param %s"), Rules.teleperiod, rule_expr.c_str(), rule_name.c_str(), rule_param.c_str()); #endif char rule_svalue[80] = { 0 }; @@ -852,7 +853,7 @@ void RulesInit(void) bitWrite(Settings.rule_once, i, 0); } } - TasmotaGlobal.rule_teleperiod = false; + Rules.teleperiod = false; } void RulesEvery50ms(void) @@ -2339,6 +2340,11 @@ bool Xdrv10(uint8_t function) case FUNC_RULES_PROCESS: result = RulesProcess(); break; + case FUNC_TELEPERIOD_RULES_PROCESS: + Rules.teleperiod = true; + result = RulesProcess(); + Rules.teleperiod = false; + break; case FUNC_SAVE_BEFORE_RESTART: RulesSaveBeforeRestart(); break; diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 307b6f53d..e7c83435f 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -7886,13 +7886,14 @@ bool Xdrv10(uint8_t function) break; case FUNC_RULES_PROCESS: if (bitRead(Settings.rule_enabled, 0)) { - if (TasmotaGlobal.rule_teleperiod) { // Signal teleperiod event - if (TasmotaGlobal.mqtt_data[0]) { - Run_Scripter(">T", 2, TasmotaGlobal.mqtt_data); - } - } else { - Run_Scripter(">E", 2, TasmotaGlobal.mqtt_data); - result = glob_script_mem.event_handeled; + Run_Scripter(">E", 2, TasmotaGlobal.mqtt_data); + result = glob_script_mem.event_handeled; + } + break; + case FUNC_TELEPERIOD_RULES_PROCESS: + if (bitRead(Settings.rule_enabled, 0)) { + if (TasmotaGlobal.mqtt_data[0]) { + Run_Scripter(">T", 2, TasmotaGlobal.mqtt_data); } } break; diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index 429f09967..166d38bc0 100755 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -60,7 +60,7 @@ struct MULTI_DISP { uint8_t auto_draw; } displays[3]; uint8_t cur_display; -Renderer *Init_uDisplay(const char *desc); +Renderer *Init_uDisplay(const char *desc, int8_t cs); void Set_display(uint8_t index) { displays[index].display = renderer; @@ -585,7 +585,7 @@ void DisplayText(void) fp.read((uint8_t*)fdesc, size); fp.close(); Get_display(temp); - renderer = Init_uDisplay(fdesc); + renderer = Init_uDisplay(fdesc, -1); Set_display(temp); AddLog(LOG_LEVEL_INFO, PSTR("DSP: File descriptor loaded %x"),renderer); } diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index e41233b9c..c93ccbb75 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -992,7 +992,7 @@ void HueLights(String *path) code = 406; } exit: - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); + AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); WSSend(code, CT_APP_JSON, response); } diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino index ba01ded30..3e541aa7d 100644 --- a/tasmota/xdrv_23_zigbee_1_headers.ino +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -19,9 +19,9 @@ #ifdef USE_ZIGBEE -#ifdef USE_ZIGBEE_EZSP +#ifdef USE_ZIGBEE_EEPROM #include "Eeprom24C512.h" -#endif // USE_ZIGBEE_EZSP +#endif // USE_ZIGBEE_EEPROM // channels numbers for Zigbee radio energy scan #define USE_ZIGBEE_CHANNEL_MIN 11 @@ -100,9 +100,9 @@ const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98; // Unsupported ZNP versio class ZigbeeStatus { public: ZigbeeStatus() -#ifdef USE_ZIGBEE_EZSP +#ifdef USE_ZIGBEE_EEPROM : eeprom(USE_ZIGBEE_ZBBRIDGE_EEPROM) -#endif // USE_ZIGBEE_EZSP +#endif // USE_ZIGBEE_EEPROM {} bool active = true; // is Zigbee active for this device, i.e. GPIOs configured @@ -142,9 +142,9 @@ public: uint16_t ezsp_version = 0; #endif -#ifdef USE_ZIGBEE_EZSP +#ifdef USE_ZIGBEE_EEPROM Eeprom24C512 eeprom; // takes only 1 bytes of RAM -#endif // USE_ZIGBEE_EZSP +#endif // USE_ZIGBEE_EEPROM }; struct ZigbeeStatus zigbee; SBuffer *zigbee_buffer = nullptr; diff --git a/tasmota/xdrv_23_zigbee_4_persistence.ino b/tasmota/xdrv_23_zigbee_4_persistence.ino deleted file mode 100644 index 8e29cc61c..000000000 --- a/tasmota/xdrv_23_zigbee_4_persistence.ino +++ /dev/null @@ -1,449 +0,0 @@ -/* - xdrv_23_zigbee.ino - zigbee support for Tasmota - - Copyright (C) 2021 Theo Arends and Stephan Hadinger - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifdef USE_ZIGBEE - -// Ensure persistence of devices into Flash -// -// Structure: -// (from file info): -// uint16 - start address in Flash (offset) -// uint16 - length in bytes (makes sure parsing stops) -// -// First byte: -// 0x00 - Empty or V3 format -// 0x01-0xFE - Legacy format -// 0xFF - invalid -// -// -// V1 Legacy -// ========= -// File structure: -// uint8 - number of devices, 0=none, 0xFF=invalid entry (probably Flash was erased) -// -// [Array of devices] -// [Offset = 2] -// uint8 - length of device record -// uint16 - short address -// uint64 - long IEEE address -// uint8 - number of endpoints -// [Array of endpoints] -// uint8 - endpoint number -// uint16 - profileID of the endpoint -// Array of uint8 - clusters In codes, 0xFF end marker -// Array of uint8 - clusters Out codes, 0xFF end marker -// -// str - ModelID (null terminated C string, 32 chars max) -// str - Manuf (null terminated C string, 32 chars max) -// str - FriendlyName (null terminated C string, 32 chars max) -// reserved for extensions -// -- V2 -- -// int8_t - zigbee profile of the device -// -// ======================= -// v3 with version number -// File structure: -// -// uint8 - number of devices, 0=none, 0xFF=invalid entry (probably Flash was erased) -// -// [Array of devices] -// [Offset = 2] -// uint8 - length of device record -// uint16 - short address -// uint64 - long IEEE address -// -// str - ModelID (null terminated C string, 32 chars max) -// str - Manuf (null terminated C string, 32 chars max) -// str - FriendlyName (null terminated C string, 32 chars max) -// -// [Array of endpoints] -// uint8 - endpoint number, 0xFF marks the end of endpoints -// uint8[] - list of configuration bytes, 0xFF marks the end -// i.e. 0xFF-0xFF marks the end of the array of endpoints -// - - -// Memory footprint -#ifdef ESP8266 -const static uint16_t z_spi_start_sector = 0xFF; // Force last bank of first MB -const static uint8_t* z_spi_start = (uint8_t*) 0x402FF000; // 0x402FF000 -const static uint8_t* z_dev_start = z_spi_start + 0x0800; // 0x402FF800 - 2KB -const static size_t z_spi_len = 0x1000; // 4kb blocks -const static size_t z_block_offset = 0x0800; -const static size_t z_block_len = 0x0800; // 2kb -#endif // ESP8266 -#ifdef ESP32 -uint8_t* z_dev_start; -const static size_t z_spi_len = 0x1000; // 4kb blocks -const static size_t z_block_offset = 0x0000; // No offset needed -const static size_t z_block_len = 0x1000; // 4kb -#endif // ESP32 - -// Each entry consumes 8 bytes -class Z_Flashentry { -public: - uint32_t name; // simple 4 letters name. Currently 'zig1', 'zig2'. 0xFFFFFFFF if not entry - uint16_t len; // len of object in bytes, 0xFFFF if no entry - uint16_t start; // address of start, 0xFFFF if empty, must be aligned on 128 bytes boundaries -}; - -class Z_Flashdirectory { -public: - // 8 bytes header - uint32_t magic; // magic value 'Tsmt' to check that the block is initialized - uint32_t clock; // clock vector to discard entries that are made before this one. This should be incremented by 1 for each new entry (future anti-weavering) - // entries, 14*8 = 112 bytes - Z_Flashentry entries[14]; - uint32_t name; // simple 4 letters name. Currently 'skey', 'crt ', 'crt1', 'crt2' - uint16_t len; // len of object - uint16_t reserved; // align on 4 bytes boundary - // link to next entry, none for now, but may be used for anti-weavering - uint16_t next_dir; // 0xFFFF if none - uint16_t reserved1; // must be 0xFFFF - uint32_t reserved2; // must be 0xFFFFFFFF -}; - - -const static uint32_t ZIGB_NAME1 = 0x3167697A; // 'zig1' little endian -const static uint32_t ZIGB_NAME2 = 0x3267697A; // 'zig2' little endian, v2 -const static uint32_t ZIGB_DATA2 = 0x32746164; // 'dat2' little endian, v2 -const static size_t Z_MAX_FLASH = z_block_len - sizeof(Z_Flashentry); // 2040 - -bool hibernateDeviceConfiguration(SBuffer & buf, const class Z_Data_Set & data, uint8_t endpoint) { - bool found = false; - for (auto & elt : data) { - if (endpoint == elt.getEndpoint()) { - buf.add8(elt.getConfigByte()); - found = true; - } - } - return found; -} - -SBuffer hibernateDevicev2(const struct Z_Device &device) { - SBuffer buf(128); - - buf.add8(0x00); // overall length, will be updated later - buf.add16(device.shortaddr); - buf.add64(device.longaddr); - - char *names[3] = { device.modelId, device.manufacturerId, device.friendlyName }; - - for (uint32_t i=0; i 32) { len = 32; } // max 32 chars - buf.addBuffer(p, len); - } - buf.add8(0x00); // end of string marker - } - - // check if we need to write fake endpoint 0x00 - buf.add8(0x00); - if (hibernateDeviceConfiguration(buf, device.data, 0)) { - buf.add8(0xFF); // end of configuration - } else { - buf.setLen(buf.len()-1); // remove 1 byte header - } - // scan endpoints - for (uint32_t i=0; i 48) { devices_size = 48; } // arbitrarily limit to 48 devices on ESP32, we will go beyond when we remove the limit of 2kb buffer -#else - if (devices_size > 32) { devices_size = 32; } // arbitrarily limit to 32 devices, for now -#endif - buf.add8(devices_size); // number of devices - - for (uint32_t i = 0; i < devices_size; i++) { - const Z_Device & device = zigbee_devices.devicesAt(i); - const SBuffer buf_device = hibernateDevicev2(device); - buf.addBuffer(buf_device); - } - - return buf; -} - -// parse a single string from the saved data -// if something wrong happens, returns nullptr to ignore the string -// Index d is incremented to just after the string -const char * hydrateSingleString(const SBuffer & buf, uint32_t *d) { - size_t s_len = buf.strlen(*d); - const char * ptr = s_len ? buf.charptr(*d) : ""; - *d += s_len + 1; - return ptr; -} - -void hydrateSingleDevice(const SBuffer & buf_d, uint32_t version = 2) { - uint32_t d = 1; // index in device buffer - uint16_t shortaddr = buf_d.get16(d); d += 2; - uint64_t longaddr = buf_d.get64(d); d += 8; - size_t buf_len = buf_d.len(); - Z_Device & device = zigbee_devices.updateDevice(shortaddr, longaddr); // update device's addresses - - if (1 == version) { - uint32_t endpoints = buf_d.get8(d++); - for (uint32_t j = 0; j < endpoints; j++) { - uint8_t ep = buf_d.get8(d++); - // uint16_t ep_profile = buf_d.get16(d); d += 2; - device.addEndpoint(ep); - - // in clusters - while (d < buf_len) { // safe guard against overflow - uint8_t ep_cluster = buf_d.get8(d++); - if (0xFF == ep_cluster) { break; } // end of block - // ignore - } - // out clusters - while (d < buf_len) { // safe guard against overflow - uint8_t ep_cluster = buf_d.get8(d++); - if (0xFF == ep_cluster) { break; } // end of block - // ignore - } - } - } - - // ModelId - device.setModelId(hydrateSingleString(buf_d, &d)); - - // ManufID - device.setManufId(hydrateSingleString(buf_d, &d)); - - // FriendlyName - device.setFriendlyName(hydrateSingleString(buf_d, &d)); - - if (d >= buf_len) { return; } - - // Hue bulbtype - if present - if (1 == version) { - device.setLightChannels(buf_d.get8(d)); - d++; - } else if (2 == version) { - // v2 parser - while (d < buf_len) { - uint8_t ep = buf_d.get8(d++); - if (0xFF == ep) { break; } // ep 0xFF marks the end of the endpoints - if (ep > 240) { ep = 0xFF; } // ep == 0xFF means ignore - device.addEndpoint(ep); // it will ignore invalid endpoints - while (d < buf_len) { - uint8_t config_type = buf_d.get8(d++); - if (0xFF == config_type) { break; } // 0xFF marks the end of congiguration - uint8_t config = config_type & 0x0F; - Z_Data_Type type = (Z_Data_Type) (config_type >> 4); - // set the configuration - if (ep != 0xFF) { - Z_Data & z_data = device.data.getByType(type, ep); - if (&z_data != nullptr) { - z_data.setConfig(config); - Z_Data_Set::updateData(z_data); - } - } - } - } - } -} - -void hydrateDevices(const SBuffer &buf, uint32_t version) { - uint32_t buf_len = buf.len(); - if (buf_len <= 10) { return; } - - uint32_t k = 0; // byte index in global buffer - uint32_t num_devices = buf.get8(k++); - for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) { - uint32_t dev_record_len = buf.get8(k); - - SBuffer buf_d = buf.subBuffer(k, dev_record_len); - hydrateSingleDevice(buf_d, version); - - // next iteration - k += dev_record_len; - } -} - -// dump = true, only dump to logs, don't actually load -void loadZigbeeDevices(bool dump_only = false) { -#ifdef USE_ZIGBEE_EZSP - if (loadZigbeeDevicesFromEEPROM()) { - return; // we succesfully loaded from EEPROM, skip the read from Flash - } -#endif -#ifdef ESP32 - // first copy SPI buffer into ram - uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); - if (!spi_buffer) { - AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); - return; - } -#ifdef USE_UFILESYS - TfsLoadFile(TASM_FILE_ZIGBEE, spi_buffer, z_spi_len); -#endif - z_dev_start = spi_buffer; -#endif // ESP32 - Z_Flashentry flashdata; - memcpy_P(&flashdata, z_dev_start, sizeof(Z_Flashentry)); -// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Memory %d"), ESP_getFreeHeap()); - // AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len); - - // Check the signature - if ( ((flashdata.name == ZIGB_NAME1) || (flashdata.name == ZIGB_NAME2)) - && (flashdata.len > 0)) { - uint16_t buf_len = flashdata.len; - uint32_t version = (flashdata.name == ZIGB_NAME2) ? 2 : 1; - // parse what seems to be a valid entry - SBuffer buf(buf_len); - buf.addBuffer(z_dev_start + sizeof(Z_Flashentry), buf_len); - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device information in %s (%d bytes)"), PSTR("Flash"), buf_len); - - if (dump_only) { - size_t buf_len = buf.len(); - if (buf_len > 192) { buf_len = 192; } - AddLogBuffer(LOG_LEVEL_INFO, buf.getBuffer(), buf_len); - // Serial.printf(">> Buffer="); - // for (uint32_t i=0; i 2040) { - AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len); - return; - } - - // first copy SPI buffer into ram - uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); - if (!spi_buffer) { - AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); - return; - } - // copy the flash into RAM to make local change, and write back the whole buffer -#ifdef ESP8266 - ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); -#endif // ESP8266 -#ifdef ESP32 -#ifdef USE_UFILESYS - TfsLoadFile(TASM_FILE_ZIGBEE, spi_buffer, z_spi_len); -#endif -#endif // ESP32 - - Z_Flashentry *flashdata = (Z_Flashentry*)(spi_buffer + z_block_offset); - flashdata->name = ZIGB_NAME2; // v2 - flashdata->len = buf_len; - flashdata->start = 0; - - memcpy(spi_buffer + z_block_offset + sizeof(Z_Flashentry), buf.getBuffer(), buf_len); - - // buffer is now ready, write it back -#ifdef ESP8266 - if (ESP.flashEraseSector(z_spi_start_sector)) { - ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - } - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len); -#endif // ESP8266 -#ifdef ESP32 -#ifdef USE_UFILESYS - TfsSaveFile(TASM_FILE_ZIGBEE, spi_buffer, z_spi_len); -#endif - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data saved in %s (%d bytes)"), PSTR("Flash"), buf_len); -#endif // ESP32 - free(spi_buffer); -} - -// Erase the flash area containing the ZigbeeData -void eraseZigbeeDevices(void) { - zigbee_devices.clean(); // avoid writing data to flash after erase -#ifdef USE_ZIGBEE_EZSP - ZFS_Erase(); -#endif // USE_ZIGBEE_EZSP -#ifdef ESP8266 - // first copy SPI buffer into ram - uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); - if (!spi_buffer) { - AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); - return; - } - // copy the flash into RAM to make local change, and write back the whole buffer - ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - - // Fill the Zigbee area with 0xFF - memset(spi_buffer + z_block_offset, 0xFF, z_block_len); - - // buffer is now ready, write it back - if (ESP.flashEraseSector(z_spi_start_sector)) { - ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - } - - free(spi_buffer); - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased in %s"), PSTR("Flash")); -#endif // ESP8266 -#ifdef ESP32 -#ifdef USE_UFILESYS - TfsInitFile(TASM_FILE_ZIGBEE, z_block_len, 0xFF); -#endif - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (%d bytes)"), z_block_len); -#endif // ESP32 -} - -void restoreDumpAllDevices(void) { - for (const auto & device : zigbee_devices.getDevices()) { - const SBuffer buf = hibernateDevicev2(device); - if (buf.len() > 0) { - Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_RESTORE "\":\"ZbRestore %_B\"}"), &buf); - MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_DATA)); - } - } -} - -#endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_4a_nano_fs.ino b/tasmota/xdrv_23_zigbee_4a_nano_fs.ino index 54cbb3b54..488ba2fca 100644 --- a/tasmota/xdrv_23_zigbee_4a_nano_fs.ino +++ b/tasmota/xdrv_23_zigbee_4a_nano_fs.ino @@ -18,10 +18,23 @@ */ #ifdef USE_ZIGBEE -#ifdef USE_ZIGBEE_EZSP // #define Z_EEPROM_DEBUG + +const static uint32_t ZIGB_NAME1 = 0x3167697A; // 'zig1' little endian +const static uint32_t ZIGB_NAME2 = 0x3267697A; // 'zig2' little endian, v2 +const static uint32_t ZIGB_DATA2 = 0x32746164; // 'dat2' little endian, v2 +extern FS *dfsp; +extern "C" uint32_t _FS_end; +// Is it ok to write to bank 0x402FF000 +bool flash_valid(void) { + return (_FS_end > 0x40280000) && (_FS_end < 0x402FF000); +} + +void hydrateSingleDevice(const SBuffer & buf_d); + +#ifdef USE_ZIGBEE_EEPROM // The EEPROM is 64KB in size with individually writable bytes. // They are conveniently organized in pages of 128 bytes to accelerate // data transfer, but unlike flash memory, you don't need to erase an entire page. @@ -243,7 +256,11 @@ public: uint8_t blk_start; // if 0x00 then file does not exist uint8_t entry_idx; // entry number in the directory - ZFS_Write_File(uint32_t _name) : name(_name), cursor(0), length(0), blk_start(0) { findOrCreate(); } + ZFS_Write_File(void) : name(0), cursor(0), length(0), blk_start(0) {} + void init(uint32_t _name) { + name = _name; + findOrCreate(); + } inline bool valid(void) const { return blk_start != 0; } // does the file exist? @@ -453,5 +470,238 @@ int32_t ZFS_Write_File::close(void) { return length; } -#endif // USE_ZIGBEE_EZSP + +#endif // USE_ZIGBEE_EEPROM + + +/*********************************************************************************************\ + * + * Generic for Reading a file + * + * Can work in 3 modes: + * - if passed a filename, use the ZFS for EEPROM nano-fs + * - if passed a File* object, use this object + * - if passed a buffer, read from a binary buffer in RAM +\*********************************************************************************************/ + +class Univ_Read_File { +public: + // file info + uint16_t len = 0; + uint16_t cursor = 0; + bool is_valid = false; + + Univ_Read_File(void) {} + + // == EEPROM ================================================ +#ifdef USE_ZIGBEE_EEPROM + uint32_t eeprom_name = 0; + ZFS_File_Entry entry; + // uint16_t length; + // uint8_t blk_start; // if 0x00 then file does not exist + uint8_t entry_idx; // entry number in the directory + + void init(uint32_t _name) { + eeprom_name = _name; + if (ZFS::findFileEntry(eeprom_name, entry, &entry_idx)) { + len = ZFS::getLength(eeprom_name); + is_valid = (len > 0); + } + } +#endif // USE_ZIGBEE_EEPROM + + // == File ================================================ +#ifdef USE_UFILESYS + File * file = nullptr; + + void init(File * _file) { + file = _file; + is_valid = (bool) *file; + len = file->size(); + } +#endif + +#ifdef ESP8266 + // == Buffer ================================================ + // binary buffer + const uint8_t * buffer = nullptr; + void init(const uint8_t * buf, size_t buflen) { + buffer = buf; + len = buflen; + is_valid = (buffer != nullptr) && (len > 0); + } +#endif // ESP8266 + + + // ================================================== + inline bool valid(void) const { return is_valid; } // does the file exist? + + int32_t readBytes(uint8_t* buf, size_t buflen); + void close(void); +}; + +void Univ_Read_File::close(void) { +#ifdef USE_UFILESYS + if (file != nullptr) { + file->close(); + } +#endif // USE_UFILESYS + // don't do anything for ZFS read of buffer +} + +int32_t Univ_Read_File::readBytes(uint8_t* buf, size_t btr) { + if (!is_valid) { return -1; } +#ifdef USE_UFILESYS + if (file != nullptr) { + return file->read(buf, btr); + } +#endif // USE_UFILESYS +#ifdef USE_ZIGBEE_EEPROM + if (eeprom_name != 0) { + int32_t bytes_read = ZFS::readBytes(eeprom_name, buf, btr, cursor, btr); + if (bytes_read < 0) { return -1; } + cursor += bytes_read; + return bytes_read; + } +#endif // USE_ZIGBEE_EEPROM + +#ifdef ESP8266 + // binary buffer + if (buffer != nullptr) { + if (btr > len - cursor) { btr = len - cursor; } + memcpy_P(buf, buffer + cursor, btr); + cursor += btr; + return btr; + } +#endif // ESP8266 + + return -1; +} + + +/*********************************************************************************************\ + * + * Generic for Writing a file + * + * Can work in 3 modes: + * - if passed a filename, use the ZFS for EEPROM nano-fs + * - if passed a File* object, use this object + * - if passed a buffer, write to a binary buffer in RAM +\*********************************************************************************************/ + +class Univ_Write_File { +public: + // file info + bool is_valid = false; + + Univ_Write_File(void) {} + + // == EEPROM ================================================ +#ifdef USE_ZIGBEE_EEPROM + ZFS_Write_File eeprom_file; + + void init(uint32_t _name) { + eeprom_file.init(_name); + is_valid = eeprom_file.valid(); + } +#endif // USE_ZIGBEE_EEPROM + + // == File ================================================ +#ifdef USE_UFILESYS + File * file = nullptr; + + void init(File * _file) { + file = _file; + is_valid = (bool) *file; + } +#endif + +#ifdef ESP8266 + // == Buffer ================================================ + // binary buffer + size_t buflen = 0; + uint8_t * buffer = nullptr; + uint16_t cursor = 0; + void init(uint8_t * buf, size_t _buflen) { + buffer = buf; + buflen = _buflen; + is_valid = (buffer != nullptr) && (buflen > 0); + } +#endif // ESP8266 + + // ================================================== + inline bool valid(void) const { return is_valid; } // does the file exist? + + int32_t writeBytes(uint8_t* buf, size_t buflen); + int32_t getCursor(void); + void close(void); +}; + +void Univ_Write_File::close(void) { +#ifdef USE_UFILESYS + if (file != nullptr) { + file->close(); + } +#endif // USE_UFILESYS +#ifdef USE_ZIGBEE_EEPROM + if (eeprom_file.valid()) { + eeprom_file.close(); + } +#endif // USE_ZIGBEE_EEPROM + // binary buffer doesn't need a close +} + +int32_t Univ_Write_File::getCursor(void) { + if (!is_valid) { return -1; } + +#ifdef USE_UFILESYS + if (file != nullptr) { + return file->position(); + } +#endif // USE_UFILESYS +#ifdef USE_ZIGBEE_EEPROM + if (eeprom_file.valid()) { + return eeprom_file.length; + } +#endif // USE_ZIGBEE_EEPROM + +#ifdef ESP8266 + if (buffer != nullptr) { + return cursor; + } +#endif // ESP8266 + + return -1; +} + +int32_t Univ_Write_File::writeBytes(uint8_t* buf, size_t btw) { + if (!is_valid) { return -1; } + +#ifdef USE_UFILESYS + if (file != nullptr) { + return file->write(buf, btw); + } +#endif // USE_UFILESYS +#ifdef USE_ZIGBEE_EEPROM + if (eeprom_file.valid()) { + uint16_t length_before = eeprom_file.length; + eeprom_file.addBytes(buf, btw); + return eeprom_file.length - length_before; // compute the increase in size + } +#endif // USE_ZIGBEE_EEPROM + +#ifdef ESP8266 + if (buffer != nullptr) { + // binary buffer + if (btw > buflen - cursor) { btw = buflen - cursor; } + memcpy_P(buffer + cursor, buf, btw); + cursor += btw; + return btw; + } +#endif // ESP8266 + + return -1; +} + + #endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_4b_eeprom.ino b/tasmota/xdrv_23_zigbee_4b_data.ino similarity index 53% rename from tasmota/xdrv_23_zigbee_4b_eeprom.ino rename to tasmota/xdrv_23_zigbee_4b_data.ino index b42b26a75..080afe8a2 100644 --- a/tasmota/xdrv_23_zigbee_4b_eeprom.ino +++ b/tasmota/xdrv_23_zigbee_4b_data.ino @@ -74,14 +74,14 @@ bool hydrateDeviceData(class Z_Device & device, const SBuffer & buf, size_t star // negative means error // positive is the segment length -int32_t hydrateSingleDevice(const SBuffer & buf, size_t start, size_t len) { - uint8_t segment_len = buf.get8(start); - if ((segment_len < 4) || (start + segment_len > len)) { +int32_t hydrateSingleDeviceData(const SBuffer & buf) { + uint8_t segment_len = buf.len(); + if (segment_len < 4) { AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "invalid segment_len=%d"), segment_len); return -1; } // read shortaddr - uint16_t shortaddr = buf.get16(start + 1); + uint16_t shortaddr = buf.get16(0); if (shortaddr >= 0xFFF0) { AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "invalid shortaddr=0x%04X"), shortaddr); return -1; @@ -89,7 +89,7 @@ int32_t hydrateSingleDevice(const SBuffer & buf, size_t start, size_t len) { #ifdef Z_EEPROM_DEBUG { if (segment_len > 3) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData 0x%04X,%*_H"), shortaddr, segment_len+1-3, buf.buf(start+3)); + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData 0x%04X,%*_H"), shortaddr, buf.buf(2), buf.len() - 2); } } #endif @@ -98,62 +98,14 @@ int32_t hydrateSingleDevice(const SBuffer & buf, size_t start, size_t len) { if (&device != nullptr) { // parse the rest - bool ret = hydrateDeviceData(device, buf, start + 3, segment_len - 3); + bool ret = hydrateDeviceData(device, buf, 2, segment_len - 2); if (!ret) { return -1; } } - return segment_len + 1; + return segment_len; } -/*********************************************************************************************\ - * - * Hydrate data from the EEPROM - * -\*********************************************************************************************/ -// Parse the entire blob -// return true if ok -bool hydrateDevicesDataFromEEPROM(void) { -#ifdef USE_ZIGBEE_EZSP - if (!zigbee.eeprom_ready) { return false; } - int32_t file_length = ZFS::getLength(ZIGB_DATA2); - if (file_length > 0) { - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device data in EEPROM (%d bytes)"), file_length); - } else { - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device data in EEPROM")); - return false; - } - - const uint16_t READ_BUFFER = 192; - uint16_t cursor = 0x0000; // cursor in the file - bool read_more = true; - - SBuffer buf(READ_BUFFER); - while (read_more) { - buf.setLen(buf.size()); // set to max size and fill with zeros - int32_t bytes_read = ZFS::readBytes(ZIGB_DATA2, buf.getBuffer(), buf.size(), cursor, READ_BUFFER); -// #ifdef Z_EEPROM_DEBUG -// AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "readBytes buffer_len=%d, read_start=%d, read_len=%d, actual_read=%d"), buf.size(), cursor, length, bytes_read); -// #endif - if (bytes_read > 0) { - buf.setLen(bytes_read); // adjust to actual size - int32_t segment_len = hydrateSingleDevice(buf, 0, buf.len()); -// #ifdef Z_EEPROM_DEBUG -// AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "hydrateSingleDevice segment_len=%d"), segment_len); -// #endif - if (segment_len <= 0) { return false; } - - cursor += segment_len; - } else { - read_more = false; - } - } - return true; -#else // USE_ZIGBEE_EZSP - return false; -#endif // USE_ZIGBEE_EZSP -} - -SBuffer hibernateDeviceData(const struct Z_Device & device, bool mqtt = false) { +SBuffer hibernateDeviceData(const struct Z_Device & device) { SBuffer buf(192); // If we have zero information about the device, just skip ir @@ -183,45 +135,128 @@ SBuffer hibernateDeviceData(const struct Z_Device & device, bool mqtt = false) { { // skip first 3 bytes size_t buf_len = buf.len() - 3; - - if (mqtt) { - Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_DATA "\":\"ZbData 0x%04X,%*_H\"}"), device.shortaddr, buf_len, buf.buf(3)); - MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_DATA)); - } else { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData 0x%04X,%*_H"), device.shortaddr, buf_len, buf.buf(3)); - } + Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_DATA "\":\"ZbData 0x%04X,%*_H\"}"), device.shortaddr, buf_len, buf.buf(3)); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_DATA)); } } return buf; } +/*********************************************************************************************\ + * + * Hydrate data from the EEPROM + * +\*********************************************************************************************/ +// Parse the entire blob +// return true if ok +bool hydrateDevicesData(void) { + Univ_Read_File f; // universal reader + const char * storage_class = PSTR(""); +#ifdef USE_ZIGBEE_EEPROM + if (zigbee.eeprom_ready) { + f.init(ZIGB_DATA2); + storage_class = PSTR("EEPROM"); + } +#endif // USE_ZIGBEE_EEPROM + +#ifdef USE_UFILESYS + File file; + if (!f.valid() && dfsp) { + file = dfsp->open(TASM_FILE_ZIGBEE_DATA, "r"); + if (file) { + f.init(&file); + storage_class = PSTR("File System"); + } + } +#endif // USE_UFILESYS + + if (!f.valid() || f.len <= 0) { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device data")); + return false; + } + + uint32_t file_len = f.len; + + if (file_len > 0) { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device data in %s (%d bytes)"), storage_class, file_len); + } else { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device data in %s"), storage_class); + f.close(); + return false; + } + + while (1) { + uint8_t dev_record_len = 0; + int32_t ret = f.readBytes(&dev_record_len, sizeof(dev_record_len)); + if (ret <= 0) { + break; // finished + } + + SBuffer buf(dev_record_len); + buf.setLen(dev_record_len); + + ret = f.readBytes(buf.getBuffer(), dev_record_len); + if (ret != dev_record_len) { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Invalid device data information, aborting")); + f.close(); + return false; + } + int32_t segment_len = hydrateSingleDeviceData(buf); + if (segment_len <= 0) { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Invalid device data information, aborting")); + f.close(); + return false; + } + } + f.close(); + return true; +} + /*********************************************************************************************\ * * Hibernate data to the EEPROM * \*********************************************************************************************/ void hibernateAllData(void) { -#ifdef USE_ZIGBEE_EZSP if (Rtc.utc_time < START_VALID_TIME) { return; } - if (!zigbee.eeprom_ready) { return; } + Univ_Write_File f; + const char * storage_class = PSTR(""); - ZFS_Write_File write_data(ZIGB_DATA2); - // first prefix is number of devices - uint8_t device_num = zigbee_devices.devicesSize(); +#ifdef USE_ZIGBEE_EEPROM + if (!f.valid() && zigbee.eeprom_ready) { + f.init(ZIGB_DATA2); + storage_class = PSTR("EEPROM"); + } +#endif - for (const auto & device : zigbee_devices.getDevices()) { - // allocte a buffer for a single device - SBuffer buf = hibernateDeviceData(device, false); // simple log, no mqtt - if (buf.len() > 0) { - write_data.addBytes(buf.getBuffer(), buf.len()); +#ifdef USE_UFILESYS + File file; + if (!f.valid() && dfsp) { + file = dfsp->open(TASM_FILE_ZIGBEE_DATA, "w"); + if (file) { + f.init(&file); + storage_class = PSTR("File System"); } } - int32_t ret = write_data.close(); -#ifdef Z_EEPROM_DEBUG - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData - %d bytes written to EEPROM"), ret); #endif -#endif // USE_ZIGBEE_EZSP + + if (f.valid()) { + // first prefix is number of devices + uint8_t device_num = zigbee_devices.devicesSize(); + + for (const auto & device : zigbee_devices.getDevices()) { + // allocte a buffer for a single device + SBuffer buf = hibernateDeviceData(device); + if (buf.len() > 0) { + f.writeBytes(buf.getBuffer(), buf.len()); + } + } + size_t buf_len = f.getCursor(); + f.close(); + + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData - %d bytes written to %s"), buf_len, storage_class); + } } /*********************************************************************************************\ @@ -232,86 +267,17 @@ const uint32_t Z_SAVE_DATA_TIMER = 60 * 60 * 1000; // save data every 60 m // // Callback for setting the timer to save Zigbee Data in x seconds // -int32_t Z_Set_Save_Data_Timer_EEPROM(uint8_t value) { +int32_t Z_Set_Save_Data_Timer(uint8_t value) { zigbee_devices.setTimer(0x0000, 0, Z_SAVE_DATA_TIMER, 0, 0, Z_CAT_ALWAYS, 0 /* value */, &Z_SaveDataTimer); return 0; // continue } void Z_SaveDataTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { hibernateAllData(); - Z_Set_Save_Data_Timer_EEPROM(0); // set a new timer -} - -#ifdef USE_ZIGBEE_EZSP -/*********************************************************************************************\ - * Write Devices in EEPROM -\*********************************************************************************************/ -// EEPROM variant that writes one item at a time and is not limited to 2KB -bool hibernateDevicesInEEPROM(void) { - if (Rtc.utc_time < START_VALID_TIME) { return false; } - if (!zigbee.eeprom_ready) { return false; } - - ZFS_Write_File write_data(ZIGB_NAME2); - - // first prefix is number of devices - uint8_t devices_size = zigbee_devices.devicesSize(); - if (devices_size > 64) { devices_size = 64; } // arbitrarily limit to 64 devices in EEPROM instead of 32 in Flash - write_data.addBytes(&devices_size, sizeof(devices_size)); - - for (const auto & device : zigbee_devices.getDevices()) { - const SBuffer buf = hibernateDevicev2(device); - if (buf.len() > 0) { - write_data.addBytes(buf.getBuffer(), buf.len()); - } - } - int32_t ret = write_data.close(); - - if (ret < 0) { - AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Error writing Devices to EEPROM")); - return false; - } else { - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data saved in %s (%d bytes)"), PSTR("EEPROM"), ret); - } - return true; -} - - -// dump = true, only dump to logs, don't actually load -bool loadZigbeeDevicesFromEEPROM(void) { - if (!zigbee.eeprom_ready) { return false; } - uint16_t file_len = ZFS::getLength(ZIGB_NAME2); - - uint8_t num_devices = 0; - ZFS::readBytes(ZIGB_NAME2, &num_devices, sizeof(num_devices), 0, sizeof(num_devices)); - - if ((file_len < 10) || (num_devices == 0x00) || (num_devices == 0xFF)) { // No data - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device information in %s"), PSTR("EEPROM")); - return false; - } - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device information in %s (%d bytes)"), PSTR("EEPROM"), file_len); - - uint32_t k = 1; // byte index in global buffer - for (uint32_t i = 0; (i < num_devices) && (k < file_len); i++) { - uint8_t dev_record_len = 0; - int32_t ret = ZFS::readBytes(ZIGB_NAME2, &dev_record_len, 1, k, 1); - SBuffer buf(dev_record_len); - buf.setLen(dev_record_len); - ret = ZFS::readBytes(ZIGB_NAME2, buf.getBuffer(), dev_record_len, k, dev_record_len); - if (ret != dev_record_len) { - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "File too short when reading EEPROM")); - return false; - } - - hydrateSingleDevice(buf, 2); - - // next iteration - k += dev_record_len; - } - - zigbee_devices.clean(); // don't write back to Flash what we just loaded - return true; + Z_Set_Save_Data_Timer(0); // set a new timer } +#ifdef USE_ZIGBEE_EEPROM void ZFS_Erase(void) { if (zigbee.eeprom_present) { ZFS::erase(); @@ -319,6 +285,6 @@ void ZFS_Erase(void) { } } -#endif // USE_ZIGBEE_EZSP +#endif // USE_ZIGBEE_EEPROM #endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_4c_devices.ino b/tasmota/xdrv_23_zigbee_4c_devices.ino new file mode 100644 index 000000000..70bfc53c4 --- /dev/null +++ b/tasmota/xdrv_23_zigbee_4c_devices.ino @@ -0,0 +1,505 @@ +/* + xdrv_23_zigbee.ino - zigbee support for Tasmota + + Copyright (C) 2021 Theo Arends and Stephan Hadinger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ZIGBEE + +// Ensure persistence of devices into Flash +// +// Structure: +// (from file info): +// uint16 - start address in Flash (offset) +// uint16 - length in bytes (makes sure parsing stops) +// +// First byte: +// 0x00 - Empty or V3 format +// 0x01-0xFE - Legacy format +// 0xFF - invalid +// +// +// V1 Legacy +// ========= +// File structure: +// uint8 - number of devices, 0=none, 0xFF=invalid entry (probably Flash was erased) +// +// [Array of devices] +// [Offset = 2] +// uint8 - length of device record +// uint16 - short address +// uint64 - long IEEE address +// uint8 - number of endpoints +// [Array of endpoints] +// uint8 - endpoint number +// uint16 - profileID of the endpoint +// Array of uint8 - clusters In codes, 0xFF end marker +// Array of uint8 - clusters Out codes, 0xFF end marker +// +// str - ModelID (null terminated C string, 32 chars max) +// str - Manuf (null terminated C string, 32 chars max) +// str - FriendlyName (null terminated C string, 32 chars max) +// reserved for extensions +// -- V2 -- +// int8_t - zigbee profile of the device +// +// ======================= +// v3 with version number +// File structure: +// +// uint8 - number of devices, 0=none, 0xFF=invalid entry (probably Flash was erased) +// +// [Array of devices] +// [Offset = 2] +// uint8 - length of device record +// uint16 - short address +// uint64 - long IEEE address +// +// str - ModelID (null terminated C string, 32 chars max) +// str - Manuf (null terminated C string, 32 chars max) +// str - FriendlyName (null terminated C string, 32 chars max) +// +// [Array of endpoints] +// uint8 - endpoint number, 0xFF marks the end of endpoints +// uint8[] - list of configuration bytes, 0xFF marks the end +// i.e. 0xFF-0xFF marks the end of the array of endpoints +// + + +// Memory footprint +#ifdef ESP8266 +const static uint16_t z_spi_start_sector = 0xFF; // Force last bank of first MB +const static uint8_t* z_spi_start = (uint8_t*) 0x402FF000; // 0x402FF000 +const static uint8_t* z_dev_start = z_spi_start + 0x0800; // 0x402FF800 - 2KB +const static size_t z_spi_len = 0x1000; // 4kb blocks +const static size_t z_block_offset = 0x0800; +const static size_t z_block_len = 0x0800; // 2kb +#endif // ESP8266 +#ifdef ESP32 +uint8_t* z_dev_start; +const static size_t z_spi_len = 0x1000; // 4kb blocks +const static size_t z_block_offset = 0x0000; // No offset needed +const static size_t z_block_len = 0x1000; // 4kb +#endif // ESP32 + +// Each entry consumes 8 bytes +class Z_Flashentry { +public: + uint32_t name; // simple 4 letters name. Currently 'zig1', 'zig2'. 0xFFFFFFFF if not entry + uint16_t len; // len of object in bytes, 0xFFFF if no entry + uint16_t start; // address of start, 0xFFFF if empty, must be aligned on 128 bytes boundaries +}; + +class Z_Flashdirectory { +public: + // 8 bytes header + uint32_t magic; // magic value 'Tsmt' to check that the block is initialized + uint32_t clock; // clock vector to discard entries that are made before this one. This should be incremented by 1 for each new entry (future anti-weavering) + // entries, 14*8 = 112 bytes + Z_Flashentry entries[14]; + uint32_t name; // simple 4 letters name. Currently 'skey', 'crt ', 'crt1', 'crt2' + uint16_t len; // len of object + uint16_t reserved; // align on 4 bytes boundary + // link to next entry, none for now, but may be used for anti-weavering + uint16_t next_dir; // 0xFFFF if none + uint16_t reserved1; // must be 0xFFFF + uint32_t reserved2; // must be 0xFFFFFFFF +}; + +const static size_t Z_MAX_FLASH = z_block_len - sizeof(Z_Flashentry); // 2040 + +bool hibernateDeviceConfiguration(SBuffer & buf, const class Z_Data_Set & data, uint8_t endpoint) { + bool found = false; + for (auto & elt : data) { + if (endpoint == elt.getEndpoint()) { + buf.add8(elt.getConfigByte()); + found = true; + } + } + return found; +} + +/*********************************************************************************************\ + * hibernateDevice + * + * Transforms a single device into a SBuffer binary representation. + * Only supports v2 (not the legacy old one long forgotten) +\*********************************************************************************************/ +SBuffer hibernateDevice(const struct Z_Device &device) { + SBuffer buf(128); + + buf.add8(0x00); // overall length, will be updated later + buf.add16(device.shortaddr); + buf.add64(device.longaddr); + + char *names[3] = { device.modelId, device.manufacturerId, device.friendlyName }; + + for (uint32_t i=0; i 32) { len = 32; } // max 32 chars + buf.addBuffer(p, len); + } + buf.add8(0x00); // end of string marker + } + + // check if we need to write fake endpoint 0x00 + buf.add8(0x00); + if (hibernateDeviceConfiguration(buf, device.data, 0)) { + buf.add8(0xFF); // end of configuration + } else { + buf.setLen(buf.len()-1); // remove 1 byte header + } + // scan endpoints + for (uint32_t i=0; i 250) { devices_size = 250; } // arbitrarily limit to 250 devices in EEPROM instead of 32 in Flash + write_data.writeBytes(&devices_size, sizeof(devices_size)); + + for (const auto & device : zigbee_devices.getDevices()) { + const SBuffer buf = hibernateDevice(device); + if (buf.len() > 0) { + int32_t ret = write_data.writeBytes(buf.getBuffer(), buf.len()); + if (ret != buf.len()) { + AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Error writing Devices, written = %d, expected = %d"), ret, buf.len()); + return false; + } + } + } + return true; +} + +// parse a single string from the saved data +// if something wrong happens, returns nullptr to ignore the string +// Index d is incremented to just after the string +const char * hydrateSingleString(const SBuffer & buf, uint32_t *d) { + size_t s_len = buf.strlen(*d); + const char * ptr = s_len ? buf.charptr(*d) : ""; + *d += s_len + 1; + return ptr; +} + +/*********************************************************************************************\ + * hydrateSingleDevice + * + * Transforms a binary representation to a Zigbee device + * Supports only v2 +\*********************************************************************************************/ +void hydrateSingleDevice(const SBuffer & buf_d) { + uint32_t d = 1; // index in device buffer + uint16_t shortaddr = buf_d.get16(d); d += 2; + uint64_t longaddr = buf_d.get64(d); d += 8; + size_t buf_len = buf_d.len(); + Z_Device & device = zigbee_devices.updateDevice(shortaddr, longaddr); // update device's addresses + + // ModelId + device.setModelId(hydrateSingleString(buf_d, &d)); + + // ManufID + device.setManufId(hydrateSingleString(buf_d, &d)); + + // FriendlyName + device.setFriendlyName(hydrateSingleString(buf_d, &d)); + + if (d >= buf_len) { return; } + + // Hue bulbtype - if present + while (d < buf_len) { + uint8_t ep = buf_d.get8(d++); + if (0xFF == ep) { break; } // ep 0xFF marks the end of the endpoints + if (ep > 240) { ep = 0xFF; } // ep == 0xFF means ignore + device.addEndpoint(ep); // it will ignore invalid endpoints + while (d < buf_len) { + uint8_t config_type = buf_d.get8(d++); + if (0xFF == config_type) { break; } // 0xFF marks the end of congiguration + uint8_t config = config_type & 0x0F; + Z_Data_Type type = (Z_Data_Type) (config_type >> 4); + // set the configuration + if (ep != 0xFF) { + Z_Data & z_data = device.data.getByType(type, ep); + if (&z_data != nullptr) { + z_data.setConfig(config); + Z_Data_Set::updateData(z_data); + } + } + } + } +} + +/*********************************************************************************************\ + * loadZigbeeDevices + * + * Load device configuration from storage. + * Order of storage for loading is: 1/ EEPROM 2/ File system 3/ Flash (ESP8266 only) +\*********************************************************************************************/ +// dump = true, only dump to logs, don't actually load +bool loadZigbeeDevices(void) { + Univ_Read_File f; // universal reader + const char * storage_class = PSTR(""); + +#ifdef USE_ZIGBEE_EEPROM + if (zigbee.eeprom_ready) { + f.init(ZIGB_NAME2); + storage_class = PSTR("EEPROM"); + } +#endif // USE_ZIGBEE_EEPROM + +#ifdef USE_UFILESYS + File file; + if (!f.valid() && dfsp) { + file = dfsp->open(TASM_FILE_ZIGBEE, "r"); + if (file) { + uint32_t signature = 0x0000; + file.read((uint8_t*)&signature, 4); + if (signature == ZIGB_NAME2) { + // skip another 4 bytes + file.read((uint8_t*)&signature, 4); + } else { + file.seek(0); // seek back to beginning of file + } + f.init(&file); + storage_class = PSTR("File System"); + } + } +#endif // USE_UFILESYS + +#ifdef ESP8266 + if (!f.valid() && flash_valid()) { + // Read binary data from Flash + + Z_Flashentry flashdata; + memcpy_P(&flashdata, z_dev_start, sizeof(Z_Flashentry)); + // AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "z_dev_start %p"), z_dev_start); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len); + + // Check the signature + if ( ((flashdata.name == ZIGB_NAME1) || (flashdata.name == ZIGB_NAME2)) + && (flashdata.len > 0)) { + uint16_t buf_len = flashdata.len; + // uint32_t version = (flashdata.name == ZIGB_NAME2) ? 2 : 1; + f.init(z_dev_start + sizeof(Z_Flashentry), buf_len); + storage_class = PSTR("Flash"); + } + } +#endif // ESP8266 + + uint32_t file_len = 0; + uint8_t num_devices = 0; + if (f.valid()) { + file_len = f.len; + f.readBytes(&num_devices, sizeof(num_devices)); + } + if ((file_len < 10) || (num_devices == 0x00) || (num_devices == 0xFF)) { // No data + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device information")); + return false; + } + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device information found in %s (%d devices - %d bytes)"), storage_class, num_devices, file_len); + + uint32_t k = 1; // byte index in global buffer + for (uint32_t i = 0; (i < num_devices) && (k < file_len); i++) { + uint8_t dev_record_len = 0; + f.readBytes(&dev_record_len, sizeof(dev_record_len)); + // int32_t ret = ZFS::readBytes(ZIGB_NAME2, &dev_record_len, 1, k, 1); + if (dev_record_len == 0) { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Invalid device information, aborting")); + zigbee_devices.clean(); // don't write back to Flash what we just loaded + return false; + } + SBuffer buf(dev_record_len); + buf.setLen(dev_record_len); + buf.set8(0, dev_record_len); // push the first byte (len including this first byte) + int32_t ret = f.readBytes(buf.buf(1), dev_record_len - 1); + // ret = ZFS::readBytes(ZIGB_NAME2, buf.getBuffer(), dev_record_len, k, dev_record_len); + if (ret != dev_record_len - 1) { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Invalid device information, aborting")); + zigbee_devices.clean(); // don't write back to Flash what we just loaded + return false; + } + + hydrateSingleDevice(buf); + + // next iteration + k += dev_record_len; + } + + zigbee_devices.clean(); // don't write back to Flash what we just loaded + return true; +} + +/*********************************************************************************************\ + * saveZigbeeDevices + * + * Save device configuration from storage. + * Order of storage for saving is: 1/ EEPROM 2/ File system 3/ Flash (ESP8266 only) +\*********************************************************************************************/ +void saveZigbeeDevices(void) { + Univ_Write_File f; + const char * storage_class = PSTR(""); + +#ifdef USE_ZIGBEE_EEPROM + if (!f.valid() && zigbee.eeprom_ready) { + f.init(ZIGB_NAME2); + storage_class = PSTR("EEPROM"); + } +#endif + +#ifdef USE_UFILESYS + File file; + if (!f.valid() && dfsp) { + file = dfsp->open(TASM_FILE_ZIGBEE, "w"); + if (file) { + f.init(&file); + storage_class = PSTR("File System"); + } + } +#endif + +#if defined(ESP8266) + uint8_t *sbuffer = nullptr; + static const size_t max_flash_size = 2040; + if (!f.valid() && flash_valid()) { + sbuffer = (uint8_t*) malloc(max_flash_size); + f.init(sbuffer, max_flash_size); + storage_class = PSTR("Flash"); + } +#endif // defined(ESP8266) + + bool written = false; + size_t buf_len = 0; + if (f.valid()) { + written = hibernateDevices(f); + + buf_len = f.getCursor(); + f.close(); + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data saved in %s (%d bytes)"), storage_class, buf_len); + } + +#if defined(ESP8266) + if (written && sbuffer != nullptr) { + // first copy SPI buffer into ram + uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); + if (!spi_buffer) { + AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); + free(sbuffer); + return; + } + ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + + Z_Flashentry *flashdata = (Z_Flashentry*)(spi_buffer + z_block_offset); + flashdata->name = ZIGB_NAME2; // v2 + flashdata->len = buf_len; + flashdata->start = 0; + + memcpy(spi_buffer + z_block_offset + sizeof(Z_Flashentry), sbuffer, buf_len); + + // buffer is now ready, write it back + if (ESP.flashEraseSector(z_spi_start_sector)) { + ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len); + free(spi_buffer); + free(sbuffer); + } +#endif // defined(ESP8266) + +} + + +/*********************************************************************************************\ + * eraseZigbeeDevices + * + * Erase all storage locations: 1/ EEPROM, 2/ File system 3/ Flash (ESP8266 only if no filesystem) +\*********************************************************************************************/ +// Erase the flash area containing the ZigbeeData +void eraseZigbeeDevices(void) { + zigbee_devices.clean(); // avoid writing data to flash after erase +#ifdef USE_ZIGBEE_EEPROM + ZFS_Erase(); +#endif // USE_ZIGBEE_EEPROM + +#if defined(ESP8266) && !defined(USE_UFILESYS) + // first copy SPI buffer into ram + uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); + if (!spi_buffer) { + AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); + return; + } + // copy the flash into RAM to make local change, and write back the whole buffer + ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + + // Fill the Zigbee area with 0xFF + memset(spi_buffer + z_block_offset, 0xFF, z_block_len); + + // buffer is now ready, write it back + if (ESP.flashEraseSector(z_spi_start_sector)) { + ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + + free(spi_buffer); + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased in %s"), PSTR("Flash")); +#endif // defined(ESP8266) && !defined(USE_UFILESYS) + +#ifdef USE_UFILESYS + if (TfsDeleteFile(TASM_FILE_ZIGBEE)) { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased")); + } +#endif // USE_UFILESYS +} + +/*********************************************************************************************\ + * restoreDumpAllDevices + * + * Dump all devices in `ZbRestore ` format ready to copy/paste +\*********************************************************************************************/ +void restoreDumpAllDevices(void) { + for (const auto & device : zigbee_devices.getDevices()) { + const SBuffer buf = hibernateDevice(device); + if (buf.len() > 0) { + Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_RESTORE "\":\"ZbRestore %_B\"}"), &buf); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_DATA)); + } + } +} + +#endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino index a9afb9764..89bff56dd 100644 --- a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino @@ -481,8 +481,12 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted) ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted) ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages + ZI_CALL(&Z_Prepare_Storage, 0) ZI_CALL(&Z_Load_Devices, 0) + ZI_CALL(&Z_Load_Data, 0) + ZI_CALL(&Z_Set_Save_Data_Timer, 0) ZI_CALL(&Z_Query_Bulbs, 0) + ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) ZI_WAIT_FOREVER() ZI_GOTO(ZIGBEE_LABEL_READY) @@ -907,10 +911,10 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted) ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted) ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages - ZI_CALL(&Z_Prepare_EEPROM, 0) + ZI_CALL(&Z_Prepare_Storage, 0) ZI_CALL(&Z_Load_Devices, 0) - ZI_CALL(&Z_Load_Data_EEPROM, 0) - ZI_CALL(&Z_Set_Save_Data_Timer_EEPROM, 0) + ZI_CALL(&Z_Load_Data, 0) + ZI_CALL(&Z_Set_Save_Data_Timer, 0) ZI_CALL(&Z_Query_Bulbs, 0) ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index b6206a0f7..a738d5dc4 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -2017,15 +2017,15 @@ int32_t ZNP_Recv_Default(int32_t res, const SBuffer &buf) { // // Callback for loading preparing EEPROM, called by the state machine // -#ifdef USE_ZIGBEE_EZSP -int32_t Z_Prepare_EEPROM(uint8_t value) { +int32_t Z_Prepare_Storage(uint8_t value) { +#ifdef USE_ZIGBEE_EEPROM ZFS::initOrFormat(); +#endif return 0; // continue } -#endif // USE_ZIGBEE_EZSP // -// Callback for loading Zigbee configuration from Flash, called by the state machine +// Callback for loading Zigbee configuration, called by the state machine // int32_t Z_Load_Devices(uint8_t value) { // try to hidrate from known devices @@ -2036,8 +2036,8 @@ int32_t Z_Load_Devices(uint8_t value) { // // Callback for loading Zigbee data from EEPROM, called by the state machine // -int32_t Z_Load_Data_EEPROM(uint8_t value) { - hydrateDevicesDataFromEEPROM(); +int32_t Z_Load_Data(uint8_t value) { + hydrateDevicesData(); return 0; // continue } diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 00309085b..d1eb6d8bc 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -1291,12 +1291,6 @@ void CmndZbSave(void) { case 2: // save only data hibernateAllData(); break; - case -1: // dump configuration - loadZigbeeDevices(true); // dump only - break; - case -2: - hydrateDevicesDataFromEEPROM(); - break; #ifdef Z_EEPROM_DEBUG case -10: { // reinit EEPROM @@ -1569,7 +1563,7 @@ void CmndZbData(void) { if (strlen(XdrvMailbox.data) == 0) { // if empty, log values for all devices for (const auto & device : zigbee_devices.getDevices()) { - hibernateDeviceData(device, true); // simple log, no mqtt + hibernateDeviceData(device); } } else { // check if parameters contain a comma ',' @@ -1588,7 +1582,7 @@ void CmndZbData(void) { // non-JSON, export current data // ZbData 0x1234 // ZbData Device_Name - hibernateDeviceData(device, true); // mqtt + hibernateDeviceData(device); } } @@ -2183,9 +2177,7 @@ bool Xdrv23(uint8_t function) result = DecodeCommand(kZbCommands, ZigbeeCommand, kZbSynonyms); break; case FUNC_SAVE_BEFORE_RESTART: -#ifdef USE_ZIGBEE_EZSP hibernateAllData(); -#endif // USE_ZIGBEE_EZSP restoreDumpAllDevices(); break; } diff --git a/tasmota/xdrv_interface.ino b/tasmota/xdrv_interface.ino index 6dfab4f36..f508fd588 100644 --- a/tasmota/xdrv_interface.ino +++ b/tasmota/xdrv_interface.ino @@ -1080,14 +1080,12 @@ void XsnsDriverState(void) /*********************************************************************************************/ bool XdrvRulesProcess(bool teleperiod) { - TasmotaGlobal.rule_teleperiod = teleperiod; // Signal teleperiod event - bool rule_handled = XdrvCallDriver(10, FUNC_RULES_PROCESS); + bool rule_handled = XdrvCallDriver(10, (teleperiod) ? FUNC_TELEPERIOD_RULES_PROCESS : FUNC_RULES_PROCESS); #ifdef USE_BERRY // events are passed to both Rules engine AND Berry engine bool berry_handled = XdrvCallDriver(52, FUNC_RULES_PROCESS); rule_handled |= berry_handled; #endif - TasmotaGlobal.rule_teleperiod = false; return rule_handled; } diff --git a/tasmota/xdsp_08_ILI9488_UD.ino b/tasmota/xdsp_08_ILI9488_UD.ino new file mode 100644 index 000000000..743b59a9a --- /dev/null +++ b/tasmota/xdsp_08_ILI9488_UD.ino @@ -0,0 +1,111 @@ +/* + xdsp_08_ILI9488.ino - Display ILI9488 support for Tasmota + + Copyright (C) 2021 Theo Arends, Gerhard Mutz + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_ILI9488_UD +#ifdef USE_UNIVERSAL_DISPLAY + +#define XDSP_08 8 + +bool ili9488_init_done = false; + +Renderer *Init_uDisplay(const char *desc, int8_t cs); +void udisp_CheckTouch(void); + +/*********************************************************************************************/ + +const char ILI9488_DESC[] PROGMEM = +":H,ILI9488,480,320,16,SPI,1,*,*,*,*,*,*,*,10\n" +":S,2,1,1,0,40,20\n" +":I\n" +"E0,0F,00,03,09,08,16,0A,3F,78,4C,09,0A,08,16,1A,0F\n" +"E1,0F,00,16,19,03,0F,05,32,45,46,04,0E,0D,35,37,0F\n" +"C0,2,17,15\n" +"C1,1,41\n" +"C5,3,00,12,80\n" +"36,1,48\n" +"3A,1,66\n" +"B0,1,80\n" +"B1,1,A0\n" +"B4,1,02\n" +"B6,2,02,02\n" +"E9,1,00\n" +"F7,4,A9,51,2C,82\n" +"11,80\n" +"29,0\n" +":o,28\n" +":O,29\n" +":A,2A,2B,2C,16\n" +":R,36\n" +";:0,48,00,00,00\n" +":0,28,00,00,01\n" +":1,28,00,00,00\n" +":2,E8,00,00,03\n" +":3,88,00,00,02\n" +":P,18\n" +":i,20,21\n" +":TI1,38,*,*\n" +"#\n"; + +void ILI9488_InitDriver(void) { + if (PinUsed(GPIO_ILI9488_CS) && (TasmotaGlobal.spi_enabled & SPI_MOSI)) { + + renderer = Init_uDisplay(ILI9488_DESC, Pin(GPIO_ILI9488_CS)); + + if (!renderer) return; + + Settings.display_model = XDSP_08; + + ili9488_init_done = true; + } +} + +/*********************************************************************************************/ +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ +bool Xdsp08(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + ILI9488_InitDriver(); + } + else if (ili9488_init_done && (XDSP_08 == Settings.display_model)) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: +#ifdef USE_FT5206 + if (FT5206_found) { + udisp_CheckTouch(); + } +#endif + break; + } + } + return result; +} + +#endif // USE_UNIVERSAL_DISPLAY +#endif // USE_DISPLAY_ILI9488 +#endif // USE_DISPLAY +#endif // USE_SPI diff --git a/tasmota/xdsp_17_universal.ino b/tasmota/xdsp_17_universal.ino index 3bf8710b2..cad9c7933 100644 --- a/tasmota/xdsp_17_universal.ino +++ b/tasmota/xdsp_17_universal.ino @@ -40,8 +40,7 @@ extern FS *ffsp; //#define DSP_ROM_DESC /*********************************************************************************************/ -//#ifdef DSP_ROM_DESC -#if 1 +#ifdef DSP_ROM_DESC /* sample descriptor */ const char DSP_SAMPLE_DESC[] PROGMEM = ":H,SH1106,128,64,1,I2C,3c,*,*,*\n" @@ -71,19 +70,20 @@ const char DSP_SAMPLE_DESC[] PROGMEM = #endif // DSP_ROM_DESC /*********************************************************************************************/ -Renderer *Init_uDisplay(const char *desc) { +Renderer *Init_uDisplay(const char *desc, int8_t cs) { char *ddesc = 0; char *fbuff; uDisplay *udisp; - if (TasmotaGlobal.gpio_optiona.udisplay_driver) { + if (TasmotaGlobal.gpio_optiona.udisplay_driver || desc) { + Settings.display_model = XDSP_17; fbuff = (char*)calloc(DISPDESC_SIZE, 1); if (!fbuff) return 0; if (desc) { - memcpy(fbuff, desc, DISPDESC_SIZE - 1); + memcpy_P(fbuff, desc, DISPDESC_SIZE - 1); ddesc = fbuff; AddLog(LOG_LEVEL_INFO, PSTR("DSP: const char descriptor used")); } @@ -192,9 +192,22 @@ uDisplay *udisp; cp += 4; //; 7 params nr,cs,sclk,mosi,dc,bl,reset,miso //SPI,*,*,*,*,*,*,* + if (cs < 0) { + switch (*cp) { + case 1: + cs = Pin(GPIO_SPI_CS); + break; + case 2: + cs = Pin(GPIO_SPI_CS, 1); + break; + default: + cs = Pin(GPIO_SSPI_CS); + break; + } + } if (*cp == '1') { cp+=2; - replacepin(&cp, Pin(GPIO_SPI_CS)); + replacepin(&cp, cs); replacepin(&cp, Pin(GPIO_SPI_CLK)); replacepin(&cp, Pin(GPIO_SPI_MOSI)); replacepin(&cp, Pin(GPIO_SPI_DC)); @@ -203,7 +216,7 @@ uDisplay *udisp; replacepin(&cp, Pin(GPIO_SPI_MISO)); } else if (*cp == '2') { cp+=2; - replacepin(&cp, Pin(GPIO_SPI_CS, 1)); + replacepin(&cp, cs); replacepin(&cp, Pin(GPIO_SPI_CLK, 1)); replacepin(&cp, Pin(GPIO_SPI_MOSI, 1)); replacepin(&cp, Pin(GPIO_SPI_DC, 1)); @@ -213,7 +226,7 @@ uDisplay *udisp; } else { // soft spi pins cp+=2; - replacepin(&cp, Pin(GPIO_SSPI_CS)); + replacepin(&cp, cs); replacepin(&cp, Pin(GPIO_SSPI_SCLK)); replacepin(&cp, Pin(GPIO_SSPI_MOSI)); replacepin(&cp, Pin(GPIO_SSPI_DC)); @@ -443,7 +456,7 @@ bool Xdsp17(uint8_t function) bool result = false; if (FUNC_DISPLAY_INIT_DRIVER == function) { - Init_uDisplay(0); + Init_uDisplay(0, -1); } else if (udisp_init_done && (XDSP_17 == Settings.display_model)) { switch (function) {