mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 11:16:34 +00:00
Merge branch 'development' into pre-release-9.4.0
This commit is contained in:
commit
9e008008c3
13
CHANGELOG.md
13
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)
|
||||
|
@ -1,6 +1,6 @@
|
||||

|
||||
|
||||
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._
|
||||
|
||||
[](https://github.com/arendst/Tasmota/releases/latest)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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 };
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<nitems(names); i++) {
|
||||
char *p = names[i];
|
||||
if (p) {
|
||||
size_t len = strlen(p);
|
||||
if (len > 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<endpoints_max; i++) {
|
||||
uint8_t endpoint = device.endpoints[i];
|
||||
if (0x00 == endpoint) { break; }
|
||||
buf.add8(endpoint);
|
||||
hibernateDeviceConfiguration(buf, device.data, endpoint);
|
||||
buf.add8(0xFF); // end of configuration
|
||||
}
|
||||
buf.add8(0xFF); // end of endpoints
|
||||
|
||||
// update overall length
|
||||
buf.set8(0, buf.len());
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
SBuffer hibernateDevices(void) {
|
||||
SBuffer buf(2048);
|
||||
|
||||
size_t devices_size = zigbee_devices.devicesSize();
|
||||
#ifdef ESP32
|
||||
if (devices_size > 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<buf.len(); i++) Serial.printf("%02X ", buf.get8(i));
|
||||
// Serial.printf("\n");
|
||||
} else {
|
||||
hydrateDevices(buf, version);
|
||||
zigbee_devices.clean(); // don't write back to Flash what we just loaded
|
||||
}
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device information in %s"), PSTR("Flash"));
|
||||
}
|
||||
#ifdef ESP32
|
||||
free(spi_buffer);
|
||||
#endif // ESP32
|
||||
}
|
||||
|
||||
void saveZigbeeDevices(void) {
|
||||
#ifdef USE_ZIGBEE_EZSP
|
||||
if (zigbee.eeprom_ready) {
|
||||
if (hibernateDevicesInEEPROM()) {
|
||||
return; // saved in EEPROM successful, non need to write in Flash
|
||||
}
|
||||
}
|
||||
#endif
|
||||
SBuffer buf = hibernateDevices();
|
||||
size_t buf_len = buf.len();
|
||||
if (buf_len > 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
|
@ -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
|
||||
|
@ -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
|
505
tasmota/xdrv_23_zigbee_4c_devices.ino
Normal file
505
tasmota/xdrv_23_zigbee_4c_devices.ino
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<nitems(names); i++) {
|
||||
char *p = names[i];
|
||||
if (p) {
|
||||
size_t len = strlen(p);
|
||||
if (len > 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<endpoints_max; i++) {
|
||||
uint8_t endpoint = device.endpoints[i];
|
||||
if (0x00 == endpoint) { break; }
|
||||
buf.add8(endpoint);
|
||||
hibernateDeviceConfiguration(buf, device.data, endpoint);
|
||||
buf.add8(0xFF); // end of configuration
|
||||
}
|
||||
buf.add8(0xFF); // end of endpoints
|
||||
|
||||
// update overall length
|
||||
buf.set8(0, buf.len());
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Write Devices in EEPROM/File/Flash
|
||||
*
|
||||
* Writes the preamble and all devices in the Univ_Write_File structure.
|
||||
* Does not close the file at the end.
|
||||
* Returns true if succesful.
|
||||
* In case of problem, the output file is left untouched
|
||||
\*********************************************************************************************/
|
||||
// EEPROM variant that writes one item at a time and is not limited to 2KB
|
||||
bool hibernateDevices(Univ_Write_File & write_data);
|
||||
bool hibernateDevices(Univ_Write_File & write_data) {
|
||||
// first prefix is number of devices
|
||||
uint8_t devices_size = zigbee_devices.devicesSize();
|
||||
if (devices_size > 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 <hex>` 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
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
111
tasmota/xdsp_08_ILI9488_UD.ino
Normal file
111
tasmota/xdsp_08_ILI9488_UD.ino
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user