diff --git a/CHANGELOG.md b/CHANGELOG.md index 5563006e8bc0..1db752d6aeb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,23 +3,86 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - Development -## [14.3.0.7] +## [14.4.1.1] +### Added +- Command ``SetOption163 1`` to disable display of Device name in GUI header + +### Breaking Changed + +### Changed +- ESP32 disable PSRAM check (and on restart some relay toggles) with `#define DISABLE_PSRAMCHECK` (#21266) +- TLS disable ECDSA for MQTT to ensure we don't break fingerprints after #22649 + +### Fixed + +### Removed + + +## [Released] + +## [14.4.1] 20241215 +- Release Rudolph + +## [14.4.0.1] 20241215 +### Added +- MCP23XXX_DRV control register IOCON in template (#22622) +- ESP32 support for TLS ECDSA (#22649) + +### Changed +- Berry make Leds animate calls reentrant (#22643) +- SSL clean up remnants of old fingerprint algorithm (#22645) +- Display removed PWM control of backlight GPIO regression from v14.1.0 + +### Fixed +- ESP32 rules operation priority regression from v13.3.0.4 (#22636) +- GUI display power button regression from v14.3.0.5 (#15788) +- MCP23xxx, PCF8574 and Shift595 power control when a display is configured regression from v14.3.0.7 +- Display DisplayMode adds a display device while not configured +- GUI timing related divide by zero exception on screen updates + +## [14.4.0] 20241211 +- Release Rudolph + +## [14.3.0.7] 20241211 ### Added - Support for TM1640 based IoTTimer by Stefan Oskamp (#21376) - Command `SetOption161 1` to disable display of state text (#22515) +- ESP32 new BLE filters by name and minimum RSSI (#22530) +- ESP32 Hybrid compile take custom boards settings in account (#22542) +- ESP32 ULP lp_core to Berry ULP module (#22567) +- Shelly 1 Gen3 template {"NAME":"Shelly 1 Gen3","GPIO":[0,0,0,4736,0,224,0,0,1,1,192,0,0,0,0,0,0,0,0,576,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio3 10000,10000,4000"} +- Shelly 1PM Gen3 template {"NAME":"Shelly 1PM Gen3","GPIO":[0,32,0,4736,224,0,3200,8161,576,1,192,0,0,0,0,0,0,0,0,1,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio3 10000,10000,4000"} +- Shelly 2PM Gen3 template {"NAME":"Shelly 2PM Gen3","GPIO":[9472,3458,576,225,4736,224,640,608,1,1,193,0,0,0,0,0,0,0,192,32,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio4 10000,10000,4000"} +- Shelly i4 Gen3 template {"NAME":"Shelly i4 Gen3","GPIO":[0,0,0,4736,32,195,194,193,1,1,192,0,0,0,0,0,0,0,0,0,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio3 10000,10000,4000} +- Show Active Power Total with any multi-phase energy monitoring (#22579) +- Command `SetOption162 1` to disable adding export energy to energy today (#22578) +- ESP32 support for WPA2/3 Enterprise conditional in core v3.1.0.241206 (#22600) +- Support for Sonoff POWCT Energy Export Active (#22596) +- Improved auto-selection of LED hardware support (RMT, SPI) (#22618) ### Breaking Changed +- ESP32 ArtNet switches from GRB to RGB encoding (#22556) ### Changed - ESP32 max number of supported switches/buttons/relays from 28 to 32 - ESP32 max number of interlocks from 14 to 16 - ESP32 Platform from 2024.11.30 to 2024.11.31, Framework (Arduino Core) from v3.1.0.241030 to v3.1.0.241117 and IDF to 5.3.1.241024 (#22504) +- Prevent active BLE operations with unencrypted MI-format beacons (#22453) +- ESP32 replaced NeoPixelBus with TasmotaLED (#22556) +- ESP32 Platform from 2024.11.31 to 2024.12.30, Framework (Arduino Core) from v3.1.0.241117 to v3.1.0.241206 and IDF to 5.3.2 (#22600) +- RG-15 sensor name from RG-15 to RG15 (#22612) ### Fixed - ESP32 upgrade by file upload response based on file size (#22500) - Wrong GUI Module and Template drop down list indexes regression - -### Removed +- Use HTML escape on File System Edit File load (#22492) +- Magic switch applying masking window to any power change (#22535) +- Shift595 output offsets and restart relay toggles +- Shutter wrong power ON state (#22548) +- ESP32-C2 TasmotaLED from not present I2S to SPI (#22575) +- KNX Scenes index change regression from v14.2.0.4 (#22405) +- Add GUI submenu headers and refresh configuration button text (#22592) +- ESP8266 Device Group exception due to lack of stack space (#22271) ## [14.3.0.6] 20241116 ### Added @@ -49,6 +112,7 @@ All notable changes to this project will be documented in this file. - Support for MS5837 pressure and temperature sensor (#22376) - Berry add I2C read16/write16 supporting Little Endian (#22448) - Berry drivers for PCA9535 (generic and in SenseCAP D1) (#22451) +- Shelly DALI Dimmer Gen3 template {"NAME":"Shelly DALI Dimmer Gen3","GPIO":[34,4736,0,3840,11360,11392,128,129,0,1,576,0,0,0,0,0,0,0,0,1,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio1 10000,10000,4000} ### Changed - AHT1X/AHT2X/AHT3X ready for virtual I2C (#22427) @@ -118,8 +182,6 @@ All notable changes to this project will be documented in this file. ### Fixed - EQ3 TRV firmware version 1.46 fails if the default true is used in subscribe on the notify characteristic (#22328) -## [Released] - ## [14.3.0] 20241015 - Release Robert diff --git a/FIRMWARE.md b/FIRMWARE.md index 43f76f8d2815..0ca8e6c6fa94 100644 --- a/FIRMWARE.md +++ b/FIRMWARE.md @@ -14,11 +14,11 @@ If you like **Tasmota**, give it a star, or fork it and contribute! [![GitHub forks](https://img.shields.io/github/forks/arendst/Tasmota.svg?style=social&label=Fork)](https://github.com/arendst/Tasmota/network) [![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/tasmota) -See [CHANGELOG.md](https://github.com/arendst/Tasmota/blob/development/tasmota/CHANGELOG.md) for changes since last release. +See [CHANGELOG.md](https://github.com/arendst/Tasmota/blob/development/CHANGELOG.md) for changes since last release. ## Development -[![Dev Version](https://img.shields.io/badge/development%20version-v13.2.x.x-blue.svg)](https://github.com/arendst/Tasmota) +[![Dev Version](https://img.shields.io/badge/development%20version-v14.4.x.x-blue.svg)](https://github.com/arendst/Tasmota) [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://ota.tasmota.com/tasmota/) [![Tasmota CI](https://github.com/arendst/Tasmota/workflows/Tasmota%20CI/badge.svg)](https://github.com/arendst/Tasmota/actions?query=workflow%3A%22Tasmota+CI%22) [![Tasmota ESP32 CI](https://github.com/arendst/Tasmota/workflows/Tasmota%20ESP32%20CI/badge.svg)](https://github.com/arendst/Tasmota/actions?query=workflow%3A%22Tasmota+ESP32+CI%22) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c1707f58ef8f..9568d89be9ed 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -36,9 +36,9 @@ While fallback or downgrading is common practice it was never supported due to S This release will be supported from ESP8266/Arduino library Core version **2.7.8** due to reported security and stability issues on previous Core version. This will also support gzipped binaries. -This release will be supported from ESP32/Arduino library Core version **v3.1.0.240926**. +This release will be supported from ESP32/Arduino library Core version **v3.1.0.241206**. -Support of ESP8266 Core versions before 2.7.8 and ESP32 Core versions before v3.1.0.240926 have been removed. +Support of ESP8266 Core versions before 2.7.8 and ESP32 Core versions before v3.1.0.241206 have been removed. ## Support of TLS @@ -75,12 +75,12 @@ Latest released binaries can be downloaded from - http://ota.tasmota.com/tasmota/release Historical binaries can be downloaded from -- http://ota.tasmota.com/tasmota/release-14.3.0 +- http://ota.tasmota.com/tasmota/release-14.4.1 The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz`` ### ESP32, ESP32-C2, ESP32-C3, ESP32-C6, ESP32-S2 and ESP32-S3 based -The following binary downloads have been compiled with ESP32/Arduino library core version **v3.1.0.240926**. +The following binary downloads have been compiled with ESP32/Arduino library core version **v3.1.0.241206**. - **tasmota32.bin** = The Tasmota version with most drivers including additional sensors and KNX for 4M+ flash. **RECOMMENDED RELEASE BINARY** - **tasmota32solo1.bin** = The Tasmota version with most drivers including additional sensors and KNX for single core ESP32 and 4M+ flash. @@ -104,7 +104,7 @@ Latest released binaries can be downloaded from - https://ota.tasmota.com/tasmota32/release Historical binaries can be downloaded from -- https://ota.tasmota.com/tasmota32/release-14.3.0 +- https://ota.tasmota.com/tasmota32/release-14.4.1 The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasmota.com/tasmota32/release/tasmota32.bin`` @@ -114,67 +114,15 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm [Complete list](BUILDS.md) of available feature and sensors. -## Changelog v14.3.0.7 +## Changelog v14.4.1.1 ### Added -- Command `WebColor20` to control color of Button when Off -- Command `SetOption161 1` to disable display of state text (#22515) -- DALI support for short addresses (gear) and groups -- DALI command `DaliGear` to set max found gear to speed up scan response -- DALI command `DaliGroup` to add gear to groups -- DALI command `DaliTarget` to set light control broadcast, group number or gear number -- DALI command `DaliGroupSliders 0..16` to show GUI group sliders with feedback disabling `DaliLight` -- DALI inverted signal configuration using GPIO DALI RX_i/TX_i -- Support for I2C over Serial [#22444](https://github.com/arendst/Tasmota/issues/22444) -- Support KNX for scripts [#22429](https://github.com/arendst/Tasmota/issues/22429) -- Support deep sleep (standby) for VL53L0X [#22441](https://github.com/arendst/Tasmota/issues/22441) -- Support for Shelly DALI Dimmer Gen3 -- Support for HLK-LD2410S 24GHz smart wave motion sensor [#22253](https://github.com/arendst/Tasmota/issues/22253) -- Support for US AQI and EPA AQI in PMS5003x sensors [#22294](https://github.com/arendst/Tasmota/issues/22294) -- Support for MS5837 pressure and temperature sensor [#22376](https://github.com/arendst/Tasmota/issues/22376) -- Support for TM1640 based IoTTimer by Stefan Oskamp [#21376](https://github.com/arendst/Tasmota/issues/21376) -- HLK-LD2410 Engineering mode [#21880](https://github.com/arendst/Tasmota/issues/21880) -- Mitsubishi Electric HVAC Operation time for MiElHVAC [#22334](https://github.com/arendst/Tasmota/issues/22334) -- Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC [#22345](https://github.com/arendst/Tasmota/issues/22345) -- Mitsubishi Electric HVAC Compressor Frequency for MiElHVAC [#22347](https://github.com/arendst/Tasmota/issues/22347) -- Mitsubishi Electric HVAC Auto Clear Remote Temp for MiElHVAC [#22370](https://github.com/arendst/Tasmota/issues/22370) -- SolaxX1 Meter mode [#22330](https://github.com/arendst/Tasmota/issues/22330) -- ESP32 MI32 legacy add config operations [#22458](https://github.com/arendst/Tasmota/issues/22458) -- BLE track devices with RPA [#22300](https://github.com/arendst/Tasmota/issues/22300) -- Berry add I2C read16/write16 supporting Little Endian [#22448](https://github.com/arendst/Tasmota/issues/22448) -- Berry drivers for PCA9535 (generic and in SenseCAP D1) [#22451](https://github.com/arendst/Tasmota/issues/22451) -- HASPmota `haspmota.get_pages()` to get the sorted list of pages [#22358](https://github.com/arendst/Tasmota/issues/22358) +- Command ``SetOption163 1`` to disable display of Device name in GUI header ### Breaking Changed ### Changed -- ESP32 Platform from 2024.09.30 to 2024.11.31, Framework (Arduino Core) from v3.1.0.240926 to v3.1.0.241117 and IDF to 5.3.1.241024 [#22504](https://github.com/arendst/Tasmota/issues/22504) -- ESP32 LVGL library from v9.2.0 to v9.2.2 [#22385](https://github.com/arendst/Tasmota/issues/22385) -- Redesign GUI adding feedback to buttons, shutters and lights -- Use command `WebButton1` to change GUI shutter 1 name -- Unit (k)VAr(h) to (k)var(h) [#22435](https://github.com/arendst/Tasmota/issues/22435) -- AHT1X/AHT2X/AHT3X ready for virtual I2C [#22427](https://github.com/arendst/Tasmota/issues/22427) -- SGP4X ready for virtual I2C [#22427](https://github.com/arendst/Tasmota/issues/22427) -- SCD40 reduce logging levels [#22443](https://github.com/arendst/Tasmota/issues/22443) -- SCD40 ready for virtual I2C [#22443](https://github.com/arendst/Tasmota/issues/22443) -- Refactored `i2c_enabled` as array [#22387](https://github.com/arendst/Tasmota/issues/22387) -- DALI renamed commands `DaliCommission` to `DaliScan` and `DaliWeb` to `DaliLight` -- DALI set Tasmota light control as default -- Shutter optimized behavior to publish shutter data with sensor request [#22353](https://github.com/arendst/Tasmota/issues/22353) -- ESP32 max number of supported switches/buttons/relays from 28 to 32 -- ESP32 max number of interlocks from 14 to 16 -- HASPmota support for page delete and object updates [#22311](https://github.com/arendst/Tasmota/issues/22311) +- ESP32 disable PSRAM check (and on restart some relay toggles) with `#define DISABLE_PSRAMCHECK` [#21266](https://github.com/arendst/Tasmota/issues/21266) ### Fixed -- FUNC_COMMAND linked list command buffer corruption by shutter driver -- Prevent crashing when `display.ini` is missing end `#` [#22471](https://github.com/arendst/Tasmota/issues/22471) -- Alexa Hue with multiple devices [#22383](https://github.com/arendst/Tasmota/issues/22383) -- Mitsubishi Electric HVAC Standby Stage for MiElHVAC [#22430](https://github.com/arendst/Tasmota/issues/22430) -- EQ3 TRV firmware version 1.46 fails if the default true is used in subscribe on the notify characteristic [#22328](https://github.com/arendst/Tasmota/issues/22328) -- Ethernet on -DFRAMEWORK_ARDUINO_ITEAD framework regression from v14.3.0 [#22367](https://github.com/arendst/Tasmota/issues/22367) -- ESP32 Upgrade by file upload response based on file size [#22500](https://github.com/arendst/Tasmota/issues/22500) -- ESP32 Arduino Core IPv6 zones used by Matter [#22378](https://github.com/arendst/Tasmota/issues/22378) -- ESP32, ESP32-S2 and ESP32-S3 re-enable touch buttons [#22446](https://github.com/arendst/Tasmota/issues/22446) -- ESP32-S3 UART output mode for Tx [#22426](https://github.com/arendst/Tasmota/issues/22426) -- Matter provisioning with matter.js controller [#22470](https://github.com/arendst/Tasmota/issues/22470) ### Removed diff --git a/TEMPLATES.md b/TEMPLATES.md index e6ce340d4618..997e02d8022b 100644 --- a/TEMPLATES.md +++ b/TEMPLATES.md @@ -5,7 +5,7 @@ # Templates -Find below the available templates as of October 2024. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) +Find below the available templates as of December 2024. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) ## Adapter Board ``` diff --git a/lib/default/WiFiHelper/src/WiFiHelper_ESP32.cpp b/lib/default/WiFiHelper/src/WiFiHelper_ESP32.cpp index 601496f9d97b..6ecf3d7d22f8 100644 --- a/lib/default/WiFiHelper/src/WiFiHelper_ESP32.cpp +++ b/lib/default/WiFiHelper/src/WiFiHelper_ESP32.cpp @@ -34,12 +34,14 @@ ip_addr_t dns_save6[DNS_MAX_SERVERS] = {}; // IPv6 DNS servers #include "tasmota_options.h" #include "lwip/dns.h" +#if CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT wl_status_t WiFiHelper::begin(const char *wpa2_ssid, wpa2_auth_method_t method, const char *wpa2_identity, const char *wpa2_username, const char *wpa2_password, const char *ca_pem, const char *client_crt, const char *client_key, int ttls_phase2_type, int32_t channel, const uint8_t *bssid, bool connect) { WiFiHelper::scrubDNS(); wl_status_t ret = WiFi.begin(wpa2_ssid, method, wpa2_identity, wpa2_username, wpa2_password, ca_pem, client_crt, client_key, ttls_phase2_type, channel, bssid, connect); WiFiHelper::scrubDNS(); return ret; } +#endif // CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT wl_status_t WiFiHelper::begin(const char* ssid, const char *passphrase, int32_t channel, const uint8_t* bssid, bool connect) { WiFiHelper::scrubDNS(); diff --git a/lib/lib_basic/NeoPixelBus/src/internal/NeoEsp32RmtMethod_idf5.h b/lib/lib_basic/NeoPixelBus/src/internal/NeoEsp32RmtMethod_idf5.h index a86b05330a18..d26aece56617 100644 --- a/lib/lib_basic/NeoPixelBus/src/internal/NeoEsp32RmtMethod_idf5.h +++ b/lib/lib_basic/NeoPixelBus/src/internal/NeoEsp32RmtMethod_idf5.h @@ -69,7 +69,7 @@ typedef struct { rmt_symbol_word_t reset_code; } rmt_led_strip_encoder_t; -static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state) +static IRAM_ATTR size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state) { rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder; diff --git a/lib/lib_basic/TasmotaLED/library.json b/lib/lib_basic/TasmotaLED/library.json new file mode 100644 index 000000000000..8ee6079f5509 --- /dev/null +++ b/lib/lib_basic/TasmotaLED/library.json @@ -0,0 +1,17 @@ +{ + "name": "TasmotaLED", + "version": "0.1", + "keywords": [ + "ws2816", "sk6812", "leds" + ], + "description": "Lightweight implementation for adressable leds.", + "repository": + { + "type": "git", + "url": "https://github.com/arendst/Tasmota/lib/lib_basic/TasmotaLED" + }, + "frameworks": "arduino", + "platforms": [ + "espressif32" + ] +} diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp b/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp new file mode 100644 index 000000000000..d323c20ab13f --- /dev/null +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp @@ -0,0 +1,237 @@ +/* + TasmotaLED.cpp - Lightweight implementation for adressable leds. + + Copyright (C) 2024 Stephan Hadinger + + This library 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 . +*/ + +#include +#ifdef ESP32 + +#include "TasmotaLEDPusher.h" +#include "TasmotaLED.h" + +// DRAM_ATTR to force in IRAM because we use this in show loop +static const DRAM_ATTR uint8_t TASMOTALED_CHANNEL_ORDERS[6][3] = { + {1, 0, 2}, // GRB (0) + {2, 0, 1}, // GBR (1) + {0, 1, 2}, // RGB (2) + {0, 2, 1}, // RBG (3) + {2, 1, 0}, // BRG (4) + {1, 2, 0} // BGR (5) +}; + +static const TasmotaLED_Timing TasmotaLED_Timings[] = { + // WS2812 + // RmtBit0 0x00228010 RmtBit1 0x00128020 RmtReset 0x800207D0 + { + .T0H = 400, + .T0L = 850, + .T1H = 800, + .T1L = 450, + .Reset = 80000 // it is 50000 for WS2812, but for compatibility with SK6812, we raise to 80000 + }, + // SK6812 + // RmtBit0 0x0024800C RmtBit1 0x00188018 RmtReset 0x80020C80 + { + .T0H = 300, + .T0L = 900, + .T1H = 600, + .T1L = 600, + .Reset = 80000 + }, +}; + +// enable AddLog +extern void AddLog(uint32_t loglevel, PGM_P formatP, ...); +enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE}; + + +TasmotaLED::TasmotaLED(uint16_t type, uint16_t num_leds) : + _type(type), + _pixel_order((type >> 4) & 0x07), + _w_before(type & 0x08), + _timing((type >> 8) & 0xFF), + _started(false), + _dirty(true), + _raw_format(false), + _pixel_count(num_leds), + _buf_work(nullptr), + _buf_show(nullptr), + _pixel_matrix(&TASMOTALED_CHANNEL_ORDERS[0]), + _pusher(nullptr) +{ + if (_timing > (TasmotaLed_TimingEnd >> 8)) { + _timing = 0; + } + switch (_type & 0x0F) { + // case TasmotaLed_1_W: + // _pixel_size = 1; + // break; + case TasmotaLed_4_WRGB: + _pixel_size = 4; + break; + case TasmotaLed_3_RGB: + default: // fallback + _pixel_size = 3; + break; + } + + _pixel_matrix = &TASMOTALED_CHANNEL_ORDERS[_pixel_order]; + + _buf_work = new uint8_t[_pixel_count * _pixel_size]; + memset(_buf_work, 0, _pixel_count * _pixel_size); + _buf_show = new uint8_t[_pixel_count * _pixel_size]; + memset(_buf_show, 0, _pixel_count * _pixel_size); + // AddLog(LOG_LEVEL_DEBUG, "LED: type=0x%04X pixel_order=0x%02X _timing=%i ", _type, _pixel_order, _timing); +} + +TasmotaLED::~TasmotaLED() { + if (_pusher) { + delete _pusher; + _pusher = nullptr; + } + delete _buf_work; + _buf_work = nullptr; + delete _buf_show; + _buf_show = nullptr; +} + +// Color is passed as 0xWWRRGGBB and copied as WWRRGGBB in _buf_work +void TasmotaLED::ClearTo(uint32_t wrgb, int32_t first, int32_t last) { + // adjust first and last to be in range of 0 to _pixel_count-1 + if (first <0) { first += _pixel_count; } + if (last <0) { last += _pixel_count; } + if (first < 0) { first = 0; } + if (last >= _pixel_count) { last = _pixel_count - 1; } + if (first > last) { return; } + // adjust to pixel format + uint8_t b0 = (wrgb >> 24) & 0xFF; + uint8_t b1 = (wrgb >> 16) & 0xFF; + uint8_t b2 = (wrgb >> 8) & 0xFF; + uint8_t b3 = (wrgb ) & 0xFF; + + if ((b0 | b1 | b2 | b3) == 0) { + // special version for clearing to black + memset(_buf_work + first * _pixel_size, 0, (last - first + 1) * _pixel_size); + } else { + // fill sub-buffer with RRGGBB or WWRRGGBB (or raw) + uint8_t *buf = _buf_work + first * _pixel_size; + for (uint32_t i = first; i <= last; i++) { + if (_pixel_size == 4) { *buf++ = b0;} + *buf++ = b1; + *buf++ = b2; + *buf++ = b3; + } + } +} + +void TasmotaLED::Show(void) { + if (_pusher) { + _dirty = false; // we don't use the _dirty attribute and always show + + // copy the input buffer to the work buffer in format to be understood by LED strip + if (_raw_format) { + memmove(_buf_show, _buf_work, _pixel_count * _pixel_size); // copy buffer in next buffer so we start with the current content + } else { + uint8_t *buf_from = _buf_work; + uint8_t *buf_to = _buf_show; + if (_pixel_size == 3) { + // copying with swapping 512 pixels (1536 bytes) takes 124 microseconds to copy, so it's negligeable + for (uint32_t i = 0; i < _pixel_count; i++) { + buf_to[(*_pixel_matrix)[0]] = buf_from[0]; // R + buf_to[(*_pixel_matrix)[1]] = buf_from[1]; // G + buf_to[(*_pixel_matrix)[2]] = buf_from[2]; // B + buf_to += 3; + buf_from += 3; + } + } else if (_pixel_size == 4) { + for (uint32_t i = 0; i < _pixel_count; i++) { + if (_w_before) { *buf_to++ = buf_from[3]; } + buf_to[(*_pixel_matrix)[0]] = buf_from[0]; // R + buf_to[(*_pixel_matrix)[1]] = buf_from[1]; // G + buf_to[(*_pixel_matrix)[2]] = buf_from[2]; // B + if (!_w_before) { *buf_to++ = buf_from[3]; } + buf_to += 3; // one increment already happened + buf_from += 4; + } + } + } + _pusher->Push(_buf_show); // push to leds + } +} + +void TasmotaLED::SetPixelColor(int32_t index, uint32_t wrgb) { + if (index < 0) { index += _pixel_count; } + if ((index >= 0) && (index < _pixel_count)) { + uint8_t *buf = _buf_work + index * _pixel_size; + uint8_t b0 = (wrgb >> 24) & 0xFF; + uint8_t b1 = (wrgb >> 16) & 0xFF; + uint8_t b2 = (wrgb >> 8) & 0xFF; + uint8_t b3 = (wrgb ) & 0xFF; + + if (_pixel_size == 4) { *buf++ = b0;} + *buf++ = b1; + *buf++ = b2; + *buf++ = b3; + _dirty = true; + } +} + +uint32_t TasmotaLED::GetPixelColor(int32_t index) { + if (index < 0) { index += _pixel_count; } + if ((index >= 0) && (index < _pixel_count)) { + uint8_t *buf = _buf_work + index * _pixel_size; + uint32_t wrgb = 0; + if (_pixel_size == 4) { wrgb = (*buf++) << 24; } + wrgb |= (*buf++) << 16; + wrgb |= (*buf++) << 8; + wrgb |= (*buf++); + return wrgb; + } else { + return 0; + } +} + +void TasmotaLED::SetPusher(TasmotaLEDPusher *pusher) { + if (_pusher) { + delete _pusher; + } + _pusher = pusher; + _started = false; +} + +bool TasmotaLED::Begin(void) { + if (_pusher) { + if (_started) { + return true; + } else { + const TasmotaLED_Timing * timing = &TasmotaLED_Timings[_timing]; + // AddLog(LOG_LEVEL_DEBUG, "LED: T0H=%i T0L=%i T1H=%i T1L=%i Reset=%i", timing.T0H, timing.T0L, timing.T1H, timing.T1L, timing.Reset); + return _pusher->Begin(_pixel_count, _pixel_size, timing); + } + } else { + return false; + } +} + +bool TasmotaLED::CanShow(void) const { + if (_pusher) { + return _pusher->CanShow(); + } + return false; +} + +#endif // ESP32 diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLED.h b/lib/lib_basic/TasmotaLED/src/TasmotaLED.h new file mode 100644 index 000000000000..dd2fb8dc9392 --- /dev/null +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLED.h @@ -0,0 +1,129 @@ +/* + TasmotaLED.h - Lightweight implementation for adressable leds. + + Copyright (C) 2024 Stephan Hadinger + + This library 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 . +*/ + +#ifndef __TASMOTALED_H__ +#define __TASMOTALED_H__ + +enum TasmotaLEDTypesEncoding : uint16_t { + // bits 0..3 encode for number of bytes per pixel + TasmotaLed_1_W = 0x0, // 1 byte per pixel (not used yet) + TasmotaLed_3_RGB = 0x1, // 3 bytes per pixel + TasmotaLed_4_WRGB = 0x2, // 4 bytes per pixel + // bits 4..6 encode for pixel order + TasmotaLed_GRB = 0b000 << 4, + TasmotaLed_GBR = 0b001 << 4, + TasmotaLed_RGB = 0b010 << 4, + TasmotaLed_RBG = 0b011 << 4, + TasmotaLed_BRG = 0b100 << 4, + TasmotaLed_BGR = 0b101 << 4, + // bit 7 sets the position for W channel + TasmotaLed_xxxW = 0b0 << 7, // W channel after color + TasmotaLed_Wxxx = 0b1 << 7, // W channel before color + // bits 8..15 encode for timing specifics + TasmotaLed_WS2812 = 0 << 8, + TasmotaLed_SK6812 = 1 << 8, + TasmotaLed_TimingEnd = 2 << 8, +}; + +enum TasmotaLEDHardware : uint32_t { + // low-order bits are reserved for channels numbers and hardware flags - currenlty not useds + // bits 16..23 + TasmotaLed_HW_Default = 0x000000, + TasmotaLed_RMT = (1 << 0) << 16, + TasmotaLed_SPI = (1 << 1) << 16, + TasmotaLed_I2S = (1 << 2) << 16, + TasmotaLed_HW_None = 0xFF << 16, // indicates that the specified HW is not supported +}; + +// Below is the encoding for full strips +// We need to keep backwards compatibility so: +// 0 = WS2812 (GRB) +// 1 = SK6812 with White (GRBW) +enum TasmotaLEDTypes : uint16_t { + ws2812_grb = TasmotaLed_3_RGB | TasmotaLed_GRB | TasmotaLed_WS2812, // 1 for backwards compatibility + sk6812_grbw = TasmotaLed_4_WRGB | TasmotaLed_GRB | TasmotaLed_xxxW | TasmotaLed_SK6812, // 2 for backwards compatibility + sk6812_grb = TasmotaLed_3_RGB | TasmotaLed_GRB | TasmotaLed_SK6812, +}; + +#ifdef __cplusplus + +/*******************************************************************************************\ + * class TasmotaLED + * + * This class is a lightweight replacement for NeoPixelBus library with a smaller + * implementation focusing only on pushing a buffer to the leds. + * + * It supports: + * - RMT and I2S hardware support + * Possible enhancements could be considered with SPI and Serial + * - Led size of 3 bytes (GRB) and 4 bytes (GRBW) + * APIs take 0xRRGGBB or 0xRRGGBBWW as input + * but Internal buffers use GGRRBB and GGRRBBWW + * - Led type of WS2812 and SK6812 + * - There is no buffer swapping, the working buffer is copied to an internal + * buffer just before display, so you can keep a reference to the buffer + * and modify it without having to worry about the display + * - buffer is cleared at start + * - "Dirty" is kept for API compatibility with NeoPixelBus but is glbally ignored + * so any call to `Show()` pushes the pixels even if they haven't changed. + * Control for dirty pixels should be done by the caller if required. + * - We tried to keep as close as possible to NeoPixelBus method names to ease transition +\*******************************************************************************************/ +class TasmotaLEDPusher; // forward definition +class TasmotaLED { +public: + TasmotaLED(uint16_t type, uint16_t num_leds); + ~TasmotaLED(); + + bool Begin(void); + void SetPusher(TasmotaLEDPusher *pusher); // needs to be called before `Begin()`, sets the hardware implementation + void Show(void); // pushes the pixels to the LED strip + inline void SetRawFormat(bool raw_format) { _raw_format = raw_format; } + + void ClearTo(uint32_t rgbw, int32_t first = 0, int32_t last = -1); + void SetPixelColor(int32_t index, uint32_t wrgb); + uint32_t GetPixelColor(int32_t index); + + uint8_t GetType(void) const { return _type; } + uint16_t PixelCount(void) const { return _pixel_count; } + uint8_t PixelSize(void) const { return _pixel_size; } + inline uint8_t * Pixels(void) const { return _buf_work; } + inline bool IsDirty(void) const { return _dirty; } + inline void Dirty(void) { _dirty = true; } + + bool CanShow(void) const; + +protected: + uint16_t _type; // the composite type + uint8_t _pixel_order; // permutation between RGB and position of W + bool _w_before; // true if W channel comes first (4 channels only) + uint8_t _timing; // timing code for strip, 0=WS2812, 1=SK6812... + bool _started; // true if the hardware implementation is configured + bool _dirty; // for NeoPixelBus compatibility, but ignored by `Push()` + bool _raw_format; // if true, copy raw to leds, if false, convert from RGB to GRB or LED format + uint16_t _pixel_count; // how many pixels in the strip + uint8_t _pixel_size; // how many bytes per pixels, only 3 and 4 are supported + uint8_t *_buf_work; // buffer used to draw into, can be modified directly by the caller + uint8_t *_buf_show; // copy of the buffer used to push to leds, private to this class + const uint8_t (*_pixel_matrix)[3]; // pointer to the pixer_order_matrix + TasmotaLEDPusher *_pusher; // pixels pusher implementation based on hardware (RMT, I2S...) +}; + +#endif // __cplusplus +#endif // __TASMOTALED_H__ diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.cpp b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.cpp new file mode 100644 index 000000000000..19280a7dd9e3 --- /dev/null +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.cpp @@ -0,0 +1,98 @@ +/* + TasmotaLEDPusher.cpp - Implementation to push Leds via hardware acceleration + + Copyright (C) 2024 Stephan Hadinger + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef ESP32 + +#include "TasmotaLEDPusher.h" +#include "TasmotaLED.h" + +//************************************************************************************************************** +// enable AddLog support within a C++ library +extern void AddLog(uint32_t loglevel, PGM_P formatP, ...); +enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE}; +//************************************************************************************************************** + + +// convert to the appropriate hardware acceleration based on capacities of the SOC +uint32_t TasmotaLEDPusher::ResolveHardware(uint32_t hw_input) { + // Step 1. discard any unsupported hardware, and replace with TasmotaLed_HW_Default + uint32_t hw = hw_input & 0xFF0000; // discard bits 0..15 +#if !TASMOTALED_HARDWARE_RMT + hw &= ~TasmotaLed_RMT; // remove RMT flag if not supported by hardware +#endif // TASMOTALED_HARDWARE_RMT +#if !TASMOTALED_HARDWARE_SPI + hw &= ~TasmotaLed_SPI; // remove SPI flag if not supported by hardware +#endif // TASMOTALED_HARDWARE_SPI +#if !TASMOTALED_HARDWARE_I2S + hw &= ~TasmotaLed_I2S; // remove I2S flag if not supported by hardware +#endif // TASMOTALED_HARDWARE_I2S + + // Step 2. If TasmotaLed_HW_Default, find a suitable scheme, RMT preferred +#if TASMOTALED_HARDWARE_RMT + if (hw == TasmotaLed_HW_Default) { + hw |= TasmotaLed_RMT; + } +#endif // TASMOTALED_HARDWARE_RMT +#if TASMOTALED_HARDWARE_I2S + if (hw == TasmotaLed_HW_Default) { + hw |= TasmotaLed_I2S; + } +#endif // TASMOTALED_HARDWARE_I2S +#if TASMOTALED_HARDWARE_SPI + if (hw == TasmotaLed_HW_Default) { + hw |= TasmotaLed_SPI; + } +#endif // TASMOTALED_HARDWARE_SPI + return hw; +} + + +TasmotaLEDPusher * TasmotaLEDPusher::Create(uint32_t hw, int8_t gpio) { + TasmotaLEDPusher * pusher = nullptr; + + hw = TasmotaLEDPusher::ResolveHardware(hw); + +#if TASMOTALED_HARDWARE_RMT + if (pusher == nullptr && (hw & TasmotaLed_RMT)) { + pusher = new TasmotaLEDPusherRMT(gpio); + if (pusher->Initialized()) { + AddLog(LOG_LEVEL_DEBUG, "LED: RMT gpio %i", gpio); + } else { + AddLog(LOG_LEVEL_INFO, "LED: Error create %s bus failed %i err=%i", "RMT", gpio, pusher->Error()); + delete pusher; + pusher = nullptr; + } + } +#endif // TASMOTALED_HARDWARE_RMT +#if TASMOTALED_HARDWARE_SPI + if (pusher == nullptr && (hw & TasmotaLed_SPI)) { + pusher = new TasmotaLEDPusherSPI(gpio); + if (pusher->Initialized()) { + AddLog(LOG_LEVEL_DEBUG, "LED: SPI gpio %i", gpio); + } else { + AddLog(LOG_LEVEL_INFO, "LED: Error create %s bus failed %i err=%i", "SPI", gpio, pusher->Error()); + delete pusher; + pusher = nullptr; + } + } +#endif // TASMOTALED_HARDWARE_SPI + return pusher; +} + +#endif // ESP32 diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.h b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.h new file mode 100644 index 000000000000..89b402997934 --- /dev/null +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.h @@ -0,0 +1,166 @@ +/* + TasmotaLEDPusher.h - Abstract class for Leds pusher (RMT, SPI, I2S...) + + Copyright (C) 2024 Stephan Hadinger + + This library 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 . +*/ + +#ifndef __TASMOTALEDPUSHER_H__ +#define __TASMOTALEDPUSHER_H__ + +#include + +// Below are flags to enable of disable each hardware support: RMT, I2S, SPI +// By default, only enable RMT support, and SPI is used as fallback if no protocol works +// +// Use de defines below: +// #define TASMOTALED_HARDWARE_RMT 0/1 +// #define TASMOTALED_HARDWARE_I2S 0/1 +// #define TASMOTALED_HARDWARE_SPI 0/1 +// +#ifndef TASMOTALED_HARDWARE_RMT + #define TASMOTALED_HARDWARE_RMT 1 +#endif + +#ifndef TASMOTALED_HARDWARE_I2S + #define TASMOTALED_HARDWARE_I2S 0 +#endif + +#ifndef TASMOTALED_HARDWARE_SPI + #define TASMOTALED_HARDWARE_SPI 0 +#endif + +// Disable any hardware if not supported by the SOC +#if TASMOTALED_HARDWARE_RMT && !defined(SOC_RMT_SUPPORTED) + #undef TASMOTALED_HARDWARE_RMT + #define TASMOTALED_HARDWARE_RMT 0 +#endif + +#if TASMOTALED_HARDWARE_I2S && !defined(SOC_I2S_SUPPORTED) + #undef TASMOTALED_HARDWARE_I2S + #define TASMOTALED_HARDWARE_I2S 0 +#endif + +#if TASMOTALED_HARDWARE_SPI && !defined(SOC_GPSPI_SUPPORTED) + #undef TASMOTALED_HARDWARE_SPI + #define TASMOTALED_HARDWARE_SPI 0 +#endif + +// if no protocol is defined, use SPI as fallback +#if !TASMOTALED_HARDWARE_RMT && !TASMOTALED_HARDWARE_I2S && !TASMOTALED_HARDWARE_SPI + #undef TASMOTALED_HARDWARE_SPI + #define TASMOTALED_HARDWARE_SPI 1 +#endif + +// Timing structure for LEDS - in nanoseconds +// It is passed by TasmotaLed to the pushers +typedef struct TasmotaLED_Timing { + uint16_t T0H, T0L, T1H, T1L; + uint32_t Reset; +} TasmotaLED_Timing; + +/*******************************************************************************************\ + * class TasmotaLEDPusher + * + * This is an virtual abstract class for Leds pusher (RMT, SPI, I2S...) + * + * Below are interfaces for current implementations +\*******************************************************************************************/ +class TasmotaLEDPusher { +public: + TasmotaLEDPusher() : _initialized(false), _err(ESP_OK), _pixel_count(0), _pixel_size(0), _led_timing(nullptr) {}; + virtual ~TasmotaLEDPusher() {}; + + bool Initialized(void) const { return _initialized; } + esp_err_t Error(void) const { return _err; } + virtual bool Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) { + _pixel_count = pixel_count; + _pixel_size = pixel_size; + _led_timing = led_timing; + return true; + } + virtual bool Push(uint8_t *buf) = 0; + virtual bool CanShow(void) = 0; + + static uint32_t ResolveHardware(uint32_t hw); // convert to the appropriate hardware acceleration based on capacities of the SOC + static TasmotaLEDPusher * Create(uint32_t hw, int8_t gpio); // create instance for the provided type, or nullptr if failed + +protected: + bool _initialized; // did the hardware got correctly initialized + esp_err_t _err; + uint16_t _pixel_count; + uint16_t _pixel_size; + const TasmotaLED_Timing * _led_timing; +}; + +/*******************************************************************************************\ + * class TasmotaLEDPusherRMT + * + * Implementation based on RMT driver +\*******************************************************************************************/ +#if TASMOTALED_HARDWARE_RMT +#include "driver/rmt_tx.h" +class TasmotaLEDPusherRMT : public TasmotaLEDPusher { +public: + TasmotaLEDPusherRMT(int8_t pin); + ~TasmotaLEDPusherRMT(); + + bool Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) override; + + bool Push(uint8_t *buf) override; + bool CanShow(void) override; +protected: + int8_t _pin; + rmt_transmit_config_t _tx_config = {}; + rmt_channel_handle_t _channel = nullptr;; + rmt_encoder_handle_t _led_encoder = nullptr; +}; +#endif // TASMOTALED_HARDWARE_RMT + +/*******************************************************************************************\ + * class TasmotaLEDPusherSPI + * + * Implementation based on SPI driver, mandatory for C2 +\*******************************************************************************************/ +#if TASMOTALED_HARDWARE_SPI +#include + +typedef struct led_strip_spi_obj_t { + uint8_t * pixel_buf; + uint16_t strip_len; + uint8_t bytes_per_pixel; + spi_host_device_t spi_host; + spi_device_handle_t spi_device; + spi_transaction_t tx_conf; // transaction in process if any +} led_strip_spi_obj; + +class TasmotaLEDPusherSPI : public TasmotaLEDPusher { +public: + TasmotaLEDPusherSPI(int8_t pin); + ~TasmotaLEDPusherSPI(); + + bool Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) override; + + bool Push(uint8_t *buf) override; + bool CanShow(void) override; + +protected: + int8_t _pin; + struct led_strip_spi_obj_t _spi_strip = {};; + const bool _with_dma = true; +}; +#endif // TASMOTALED_HARDWARE_SPI + +#endif // __TASMOTALEDPUSHER_H__ diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherRMT.cpp b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherRMT.cpp new file mode 100644 index 000000000000..d6ca1219780f --- /dev/null +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherRMT.cpp @@ -0,0 +1,243 @@ +/* + TasmotaLEDPusherRMT.cpp - Implementation to push Leds via RMT channel + + Copyright (C) 2024 Stephan Hadinger + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef ESP32 + +#include "TasmotaLEDPusher.h" +#include "TasmotaLED.h" + +#if TASMOTALED_HARDWARE_RMT +#include +#include + +//************************************************************************************************************** +// enable AddLog support within a C++ library +extern void AddLog(uint32_t loglevel, PGM_P formatP, ...); +enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE}; +//************************************************************************************************************** + +/*******************************************************************************************\ + * Implementation for TasmotaLEDPusherRMT + * + * Code mostly copied from Tasmota patch to NeoPixelBus applied to support esp-idf 5.x + * itself inspired from esp-idf example for RMT encoder from + * https://github.com/espressif/esp-idf/tree/v5.3.1/examples/peripherals/rmt/ir_nec_transceiver +\*******************************************************************************************/ +#define RMT_LED_STRIP_RESOLUTION_HZ 40000000 // 40MHz resolution, steps of 25 nanoseconds + +// structure used to pass arguments to `rmt_new_led_strip_encoder` +// currently only the encoder resolution in Hz +typedef struct { + uint32_t resolution; /*!< Encoder resolution, in Hz */ +} led_strip_encoder_config_t; + +// structure used to store all the necessary information for the RMT encoder +typedef struct { + rmt_encoder_t base; + rmt_encoder_t *bytes_encoder; + rmt_encoder_t *copy_encoder; + int32_t state; + rmt_symbol_word_t reset_code; +} rmt_led_strip_encoder_t; + +static IRAM_ATTR size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state) +{ + rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); + rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder; + rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder; + rmt_encode_state_t session_state = RMT_ENCODING_RESET; + rmt_encode_state_t state = RMT_ENCODING_RESET; + size_t encoded_symbols = 0; + switch (led_encoder->state) { + case 0: // send RGB data + encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state); + if (session_state & RMT_ENCODING_COMPLETE) { + led_encoder->state = 1; // switch to next state when current encoding session finished + } + if (session_state & RMT_ENCODING_MEM_FULL) { + state = static_cast(static_cast(state) | static_cast(RMT_ENCODING_MEM_FULL)); + goto out; // yield if there's no free space for encoding artifacts + } + // fall-through + case 1: // send reset code + encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code, sizeof(led_encoder->reset_code), &session_state); + if (session_state & RMT_ENCODING_COMPLETE) { + led_encoder->state = RMT_ENCODING_RESET; // back to the initial encoding session + state = static_cast(static_cast(state) | static_cast(RMT_ENCODING_COMPLETE)); + } + if (session_state & RMT_ENCODING_MEM_FULL) { + state = static_cast(static_cast(state) | static_cast(RMT_ENCODING_MEM_FULL)); + goto out; // yield if there's no free space for encoding artifacts + } + } +out: + *ret_state = state; + return encoded_symbols; +} + +static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder) { + rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); + rmt_del_encoder(led_encoder->bytes_encoder); + rmt_del_encoder(led_encoder->copy_encoder); + delete led_encoder; + return ESP_OK; +} + +static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder) { + rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); + rmt_encoder_reset(led_encoder->bytes_encoder); + rmt_encoder_reset(led_encoder->copy_encoder); + led_encoder->state = RMT_ENCODING_RESET; + return ESP_OK; +} + +static esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder, rmt_symbol_word_t bit0, rmt_symbol_word_t bit1, rmt_symbol_word_t reset_code) { + static const char* TAG = "TASMOTA_RMT"; + esp_err_t ret = ESP_OK; + rmt_led_strip_encoder_t *led_encoder = NULL; + rmt_bytes_encoder_config_t bytes_encoder_config; + rmt_copy_encoder_config_t copy_encoder_config = {}; + + ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + led_encoder = new rmt_led_strip_encoder_t(); + ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder"); + led_encoder->base.encode = rmt_encode_led_strip; + led_encoder->base.del = rmt_del_led_strip_encoder; + led_encoder->base.reset = rmt_led_strip_encoder_reset; + led_encoder->reset_code = reset_code; + + bytes_encoder_config.bit0 = bit0; + bytes_encoder_config.bit1 = bit1; + bytes_encoder_config.flags.msb_first = 1; // WS2812 transfer bit order: G7...G0R7...R0B7...B0 - TODO: more checks + + ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed"); + ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed"); + + *ret_encoder = &led_encoder->base; + return ret; +err: + AddLog(LOG_LEVEL_INFO, "RMT: could not init led encoder"); + if (led_encoder) { + if (led_encoder->bytes_encoder) { rmt_del_encoder(led_encoder->bytes_encoder); } + if (led_encoder->copy_encoder) { rmt_del_encoder(led_encoder->copy_encoder); } + delete led_encoder; + } + return ret; +} + +TasmotaLEDPusherRMT::~TasmotaLEDPusherRMT() { + if (_channel) { + rmt_tx_wait_all_done(_channel, 10000 / portTICK_PERIOD_MS); + rmt_del_channel(_channel); + _channel = nullptr; + } + + if (_pin >= 0) { + gpio_matrix_out(_pin, 0x100, false, false); + pinMode(_pin, INPUT); + _pin = -1; + } +} + +TasmotaLEDPusherRMT::TasmotaLEDPusherRMT(int8_t pin) : _pin(pin) { + esp_err_t ret = ESP_OK; + rmt_tx_channel_config_t config = {}; + config.clk_src = RMT_CLK_SRC_DEFAULT; + config.gpio_num = static_cast(_pin); + config.mem_block_symbols = 192; // memory block size, 64 * 4 = 256 Bytes + config.resolution_hz = RMT_LED_STRIP_RESOLUTION_HZ; // 40 MHz tick resolution, i.e., 1 tick = 0.025 µs or 25 ns + config.trans_queue_depth = 4; // set the number of transactions that can pend in the background + config.flags.invert_out = false; // do not invert output signal + config.flags.with_dma = false; // do not need DMA backend + + _err = rmt_new_tx_channel(&config, &_channel); + if (_err == ESP_OK) { + _initialized = true; + } +} + +bool TasmotaLEDPusherRMT::Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) { + if (!_initialized) { return false; } + TasmotaLEDPusher::Begin(pixel_count, pixel_size, led_timing); + led_strip_encoder_config_t encoder_config = { + .resolution = RMT_LED_STRIP_RESOLUTION_HZ, + }; + + _tx_config.loop_count = 0; // no loop + + rmt_symbol_word_t RmtBit0 = { + .duration0 = (uint16_t) (led_timing->T0H * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000), + .level0 = 1, + .duration1 = (uint16_t) (led_timing->T0L * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000), + .level1 = 0, + }; + rmt_symbol_word_t RmtBit1 = { + .duration0 = (uint16_t) (led_timing->T1H * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000), + .level0 = 1, + .duration1 = (uint16_t) (led_timing->T1L * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000), + .level1 = 0, + }; + rmt_symbol_word_t RmtReset = { + .duration0 = (uint16_t) (led_timing->Reset * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000), + .level0 = 0, + .duration1 = 50 * (RMT_LED_STRIP_RESOLUTION_HZ / 1000000) / 1000, + .level1 = 1, + }; + // AddLog(LOG_LEVEL_INFO, "RMT: RmtBit0 0x%08X RmtBit1 0x%08X RmtReset 0x%08X", RmtBit0.val, RmtBit1.val, RmtReset.val); + _err = rmt_new_led_strip_encoder(&encoder_config, &_led_encoder, RmtBit0, RmtBit1, RmtReset); + if (_err != ESP_OK) { + // AddLog(LOG_LEVEL_INFO, "RMT: cannot initialize led strip encoder err=%i", ret); + return false; + } + _err = rmt_enable(_channel); + if (_err != ESP_OK) { + // AddLog(LOG_LEVEL_INFO, "RMT: cannot enable channel err=%i", ret); + return false; + } + return true; +} + +bool TasmotaLEDPusherRMT::CanShow(void) { + if (_channel && _initialized) { + return (ESP_OK == rmt_tx_wait_all_done(_channel, 0)); + } else { + return false; + } +} + +bool TasmotaLEDPusherRMT::Push(uint8_t *buf) { + if (!_initialized) { return false; } + + // wait for not actively sending data + // this will time out at 1 second, an arbitrarily long period of time + // and do nothing if this happens + esp_err_t ret = rmt_tx_wait_all_done(_channel, 1000 / portTICK_PERIOD_MS); + if (ESP_OK == ret) { + // now start the RMT transmit with the editing buffer before we swap + ret = rmt_transmit(_channel, _led_encoder, buf, _pixel_count * _pixel_size, &_tx_config); + if (ESP_OK != ret) { + AddLog(LOG_LEVEL_DEBUG, "RMT: cannot transmit err=%i", ret); + return false; + } + } + return true; +} + +#endif // TASMOTALED_HARDWARE_RMT +#endif // ESP32 diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherSPI.cpp b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherSPI.cpp new file mode 100644 index 000000000000..74366ebb87f8 --- /dev/null +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherSPI.cpp @@ -0,0 +1,192 @@ +/* + TasmotaLEDPusherRMT.cpp - Implementation to push Leds via SPI channel + + Copyright (C) 2024 Stephan Hadinger + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef ESP32 + +#include "TasmotaLEDPusher.h" +#include "TasmotaLED.h" + +#if TASMOTALED_HARDWARE_SPI +#include + +//************************************************************************************************************** +// enable AddLog support within a C++ library +extern void AddLog(uint32_t loglevel, PGM_P formatP, ...); +enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE}; +//************************************************************************************************************** + +/*******************************************************************************************\ + * Implementation for TasmotaLEDPusherSPI + * +\*******************************************************************************************/ + +#define LED_STRIP_SPI_DEFAULT_RESOLUTION (25 * 100 * 1000) // 2.5MHz resolution +#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4 + +#define SPI_BYTES_PER_COLOR_BYTE 3 +#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8) + +static void __led_strip_spi_bit(uint8_t data, uint8_t *buf) +{ + // Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110 + // So a color byte occupies 3 bytes of SPI. + buf[0] = (data & BIT(5) ? BIT(1) | BIT(0) : BIT(1)) | (data & BIT(6) ? BIT(4) | BIT(3) : BIT(4)) | (data & BIT(7) ? BIT(7) | BIT(6) : BIT(7)); + buf[1] = (BIT(0)) | (data & BIT(3) ? BIT(3) | BIT(2) : BIT(3)) | (data & BIT(4) ? BIT(6) | BIT(5) : BIT(6)); + buf[2] = (data & BIT(0) ? BIT(2) | BIT(1) : BIT(2)) | (data & BIT(1) ? BIT(5) | BIT(4) : BIT(5)) | (data & BIT(2) ? BIT(7) : 0x00); +} + +esp_err_t led_strip_spi_refresh(led_strip_spi_obj * spi_strip) +{ + spi_strip->tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE; + spi_strip->tx_conf.tx_buffer = spi_strip->pixel_buf; + spi_strip->tx_conf.rx_buffer = NULL; + spi_device_transmit(spi_strip->spi_device, &spi_strip->tx_conf); + return ESP_OK; +} + +void led_strip_transmit_buffer(led_strip_spi_obj * spi_strip, uint8_t * buffer_rgbw) { + // Timing for 512 pixels (extreme test) + // Copying to buffer: 418 us + // sending pixels: 16.2 ms + uint8_t * buf = buffer_rgbw; + uint8_t * pix_buf = spi_strip->pixel_buf; + for (int i = 0; i < spi_strip->strip_len; i++) { + // LED_PIXEL_FORMAT_GRB takes 72bits(9bytes) + __led_strip_spi_bit(*buf++, pix_buf); pix_buf += SPI_BYTES_PER_COLOR_BYTE; + __led_strip_spi_bit(*buf++, pix_buf); pix_buf += SPI_BYTES_PER_COLOR_BYTE; + __led_strip_spi_bit(*buf++, pix_buf); pix_buf += SPI_BYTES_PER_COLOR_BYTE; + if (spi_strip->bytes_per_pixel > 3) { + __led_strip_spi_bit(*buf++, pix_buf); pix_buf += SPI_BYTES_PER_COLOR_BYTE; + } + } + /* Refresh the strip to send data */ + led_strip_spi_refresh(spi_strip); +} + + +TasmotaLEDPusherSPI::~TasmotaLEDPusherSPI() { + if (_spi_strip.spi_device) { + spi_bus_remove_device(_spi_strip.spi_device); + } + if (_spi_strip.spi_host) { + spi_bus_free(_spi_strip.spi_host); + } + + if (_pin >= 0) { + gpio_matrix_out(_pin, 0x100, false, false); + pinMode(_pin, INPUT); + _pin = -1; + } +} + +TasmotaLEDPusherSPI::TasmotaLEDPusherSPI(int8_t pin) : _pin(pin) { + spi_host_device_t spi_host = SPI2_HOST; + spi_bus_config_t spi_bus_cfg = { + .mosi_io_num = _pin, + //Only use MOSI to generate the signal, set -1 when other pins are not used. + .miso_io_num = -1, + .sclk_io_num = -1, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = _pixel_count * _pixel_size * SPI_BYTES_PER_COLOR_BYTE, + }; + _err = spi_bus_initialize(spi_host, &spi_bus_cfg, _with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED); + if (_err == ESP_OK) { + _spi_strip.spi_host = spi_host; // confirmed working, so keep it's value to free it later + _initialized = true; + } +} + +bool TasmotaLEDPusherSPI::Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) { + if (!_initialized) { + return false; + } + TasmotaLEDPusher::Begin(pixel_count, pixel_size, led_timing); + _spi_strip.bytes_per_pixel = _pixel_size; + _spi_strip.strip_len = _pixel_count; + + uint32_t mem_caps = MALLOC_CAP_DEFAULT; + // spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT; + spi_device_interface_config_t spi_dev_cfg; + int clock_resolution_khz = 0; + + if (_with_dma) { // TODO + // DMA buffer must be placed in internal SRAM + mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA; + } + _spi_strip.pixel_buf = (uint8_t *)heap_caps_calloc(1, _pixel_count * _pixel_size * SPI_BYTES_PER_COLOR_BYTE, mem_caps); + if (_spi_strip.pixel_buf == nullptr) { + AddLog(LOG_LEVEL_INFO, PSTR("LED: Error no mem for spi strip")); + goto err; + } + + spi_dev_cfg = { + .command_bits = 0, + .address_bits = 0, + .dummy_bits = 0, + .mode = 0, + //set -1 when CS is not used + .clock_source = SPI_CLK_SRC_DEFAULT, // clk_src, + .clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION, + .spics_io_num = -1, + .queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE, + }; + _err = spi_bus_add_device(_spi_strip.spi_host, &spi_dev_cfg, &_spi_strip.spi_device); + if (_err != ESP_OK) { + // AddLog(LOG_LEVEL_INFO, "LED: Error failed to add spi device"); + goto err; + } + + _err = spi_device_get_actual_freq(_spi_strip.spi_device, &clock_resolution_khz); + if (_err != ESP_OK) { + // AddLog(LOG_LEVEL_INFO, "LED: Error failed to get spi frequency"); + goto err; + } + // TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution + // But now, let's fixed the resolution, the downside is, we don't support a clock source whose frequency is not multiple of LED_STRIP_SPI_DEFAULT_RESOLUTION + if (clock_resolution_khz != LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000) { + // AddLog(LOG_LEVEL_INFO, "LED: Error unsupported clock resolution: %dKHz", clock_resolution_khz); + goto err; + } + return true; +err: + if (_spi_strip.spi_device) { + spi_bus_remove_device(_spi_strip.spi_device); + } + if (_spi_strip.spi_host) { + spi_bus_free(_spi_strip.spi_host); + } + _initialized = false; + return false; +} + +bool TasmotaLEDPusherSPI::CanShow(void) { + return _initialized; // TODO +} + +bool TasmotaLEDPusherSPI::Push(uint8_t *buf) { + if (!_initialized) { return false; } + if (CanShow()) { + led_strip_transmit_buffer(&_spi_strip, buf); + } + return true; +} + +#endif // TASMOTALED_HARDWARE_SPI +#endif // ESP32 diff --git a/lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp b/lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp index cc2bbf49a3ef..77abc7428d67 100644 --- a/lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp +++ b/lib/lib_div/esp-knx-ip-0.5.2/src/esp-knx-ip.cpp @@ -558,6 +558,8 @@ void ESPKNXIP::__loop_knx() DEBUG_PRINTLN(F("")); + udp.flush(); + knx_ip_pkt_t *knx_pkt = (knx_ip_pkt_t *)buf; DEBUG_PRINT(F("ST: 0x")); diff --git a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp index afbe3602e187..b4bbe6e6a32b 100755 --- a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp +++ b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp @@ -195,6 +195,7 @@ void WiFiClientSecure_light::_clear() { _recvapp_buf = nullptr; _recvapp_len = 0; _insecure = false; // set to true when calling setPubKeyFingerprint() + _rsa_only = true; // for now we disable ECDSA by default _fingerprint_any = true; // by default accept all fingerprints _fingerprint1 = nullptr; _fingerprint2 = nullptr; @@ -767,59 +768,70 @@ extern "C" { xc->done_cert = true; // first cert already processed } -// **** Start patch Castellucci -/* - static void pubkeyfingerprint_pubkey_fingerprint(br_sha1_context *shactx, br_rsa_public_key rsakey) { - br_sha1_init(shactx); - br_sha1_update(shactx, "ssh-rsa", 7); // tag - br_sha1_update(shactx, rsakey.e, rsakey.elen); // exponent - br_sha1_update(shactx, rsakey.n, rsakey.nlen); // modulus - } -*/ - // If `compat` id false, adds a u32be length prefixed value to the sha1 state. - // If `compat` is true, the length will be omitted for compatibility with - // data from older versions of Tasmota. - static void sha1_update_len(br_sha1_context *shactx, const void *msg, uint32_t len, bool compat) { + // Add a data element with a u32be length prefix to the sha1 state. + static void sha1_update_len(br_sha1_context *shactx, const void *msg, uint32_t len) { uint8_t buf[] = {0, 0, 0, 0}; - if (!compat) { - buf[0] = (len >> 24) & 0xff; - buf[1] = (len >> 16) & 0xff; - buf[2] = (len >> 8) & 0xff; - buf[3] = (len >> 0) & 0xff; - br_sha1_update(shactx, buf, 4); // length - } + buf[0] = (len >> 24) & 0xff; + buf[1] = (len >> 16) & 0xff; + buf[2] = (len >> 8) & 0xff; + buf[3] = (len >> 0) & 0xff; + br_sha1_update(shactx, buf, 4); // length + br_sha1_update(shactx, msg, len); // message } - // Update the received fingerprint based on the certificate's public key. - // If `compat` is true, an insecure version of the fingerprint will be - // calcualted for compatibility with older versions of Tasmota. Normally, - // `compat` should be false. - static void pubkeyfingerprint_pubkey_fingerprint(br_x509_pubkeyfingerprint_context *xc, bool compat) { - br_rsa_public_key rsakey = xc->ctx.pkey.key.rsa; + // Calculate the received fingerprint based on the certificate's public key. + // The public exponent and modulus are length prefixed to avoid security + // vulnerabilities related to ambiguous serialization. Without this, an + // attacker can generate alternative public keys which result in the same + // fingerprint, but are trivial to crack. This works because RSA keys can be + // created with more than two primes, and most numbers, even large ones, can + // be easily factored. + static void pubkeyfingerprint_pubkey_fingerprint(br_x509_pubkeyfingerprint_context *xc) { + if (xc->ctx.pkey.key_type == BR_KEYTYPE_RSA) { + br_rsa_public_key rsakey = xc->ctx.pkey.key.rsa; + + br_sha1_context shactx; - br_sha1_context shactx; + br_sha1_init(&shactx); - br_sha1_init(&shactx); + // The tag string doesn't really matter, but it should differ depending on + // key type. For RSA it's a fixed string. + sha1_update_len(&shactx, "ssh-rsa", 7); // tag + sha1_update_len(&shactx, rsakey.e, rsakey.elen); // exponent + sha1_update_len(&shactx, rsakey.n, rsakey.nlen); // modulus - sha1_update_len(&shactx, "ssh-rsa", 7, compat); // tag - sha1_update_len(&shactx, rsakey.e, rsakey.elen, compat); // exponent - sha1_update_len(&shactx, rsakey.n, rsakey.nlen, compat); // modulus + br_sha1_out(&shactx, xc->pubkey_recv_fingerprint); // copy to fingerprint + } + #ifndef ESP8266 + else if (xc->ctx.pkey.key_type == BR_KEYTYPE_EC) { + br_ec_public_key eckey = xc->ctx.pkey.key.ec; + + br_sha1_context shactx; + + br_sha1_init(&shactx); - br_sha1_out(&shactx, xc->pubkey_recv_fingerprint); // copy to fingerprint + // The tag string doesn't really matter, but it should differ depending on + // key type. For ECDSA it's a fixed string. + sha1_update_len(&shactx, "ecdsa", 5); // tag + int32_t curve = eckey.curve; + sha1_update_len(&shactx, &curve, 4); // curve id as int32 + sha1_update_len(&shactx, "curve", 5); // tag2 + sha1_update_len(&shactx, eckey.q, eckey.qlen); // exponent + } + #endif + else { + // We don't support anything else, so just set the fingerprint to all zeros. + memset(xc->pubkey_recv_fingerprint, 0, 20); + } } -// **** End patch Castellucci // Callback when complete chain has been parsed. // Return 0 on validation success, !0 on validation error static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) { br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; - // set fingerprint status byte to zero - // FIXME: find a better way to pass this information - xc->pubkey_recv_fingerprint[20] = 0; - // Try matching using the the new fingerprint algorithm - pubkeyfingerprint_pubkey_fingerprint(xc, false); + pubkeyfingerprint_pubkey_fingerprint(xc); if (!xc->fingerprint_all) { if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) { return 0; @@ -832,7 +844,6 @@ extern "C" { // Default (no validation at all) or no errors in prior checks = success. return 0; } -// **** End patch Castellucci } // Return the public key from the validator (set by x509_minimal) @@ -869,20 +880,39 @@ extern "C" { ctx->fingerprint_all = fingerprint_all; } +#ifdef ESP8266 // We limit to a single cipher to reduce footprint // we reference it, don't put in PROGMEM static const uint16_t suites[] = { BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 }; +#else + // add more flexibility on ESP32 + static const uint16_t suites[] = { + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + }; + static const uint16_t suites_RSA_ONLY[] = { + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + }; +#endif // Default initializion for our SSL clients - static void br_ssl_client_base_init(br_ssl_client_context *cc) { + static void br_ssl_client_base_init(br_ssl_client_context *cc, bool _rsa_only) { br_ssl_client_zero(cc); // forbid SSL renegotiation, as we free the Private Key after handshake br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION); br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12); +#ifdef ESP8266 br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); +#else + if (_rsa_only) { + br_ssl_engine_set_suites(&cc->eng, suites_RSA_ONLY, (sizeof suites_RSA_ONLY) / (sizeof suites_RSA_ONLY[0])); + } else { + br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); + } +#endif br_ssl_client_set_default_rsapub(cc); br_ssl_engine_set_default_rsavrfy(&cc->eng); @@ -897,6 +927,9 @@ extern "C" { // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); // TODO +#ifndef ESP8266 + br_ssl_engine_set_ecdsa(&cc->eng, &br_ecdsa_i15_vrfy_asn1); +#endif } } @@ -927,7 +960,7 @@ bool WiFiClientSecure_light::_connectSSL(const char* hostName) { _ctx_present = true; _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - br_ssl_client_base_init(_sc.get()); + br_ssl_client_base_init(_sc.get(), _rsa_only); if (_alpn_names && _alpn_num > 0) { br_ssl_engine_set_protocol_names(_eng, _alpn_names, _alpn_num); } diff --git a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h index 6139059d8527..8ba7a33bad6d 100755 --- a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h +++ b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h @@ -20,9 +20,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#define DEBUG_ESP_SSL - #if __has_include("core_version.h") // ESP32 Stage has no core_version.h file. Disable include via PlatformIO Option #include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_7_1) #endif // ESP32_STAGE @@ -88,6 +85,10 @@ class WiFiClientSecure_light : public WiFiClient { _fingerprint2 = f2; _fingerprint_any = f_any; _insecure = true; + _rsa_only = true; // if fingerprint, we limit to RSA only + } + void setRSAOnly(bool rsa_only) { + _rsa_only = rsa_only; } const uint8_t * getRecvPubKeyFingerprint(void) { return _recv_fingerprint; @@ -153,14 +154,10 @@ class WiFiClientSecure_light : public WiFiClient { bool _fingerprint_any; // accept all fingerprints bool _insecure; // force fingerprint + bool _rsa_only; // restrict to RSA only key exchange (no ECDSA - enabled to force RSA fingerprints) const uint8_t *_fingerprint1; // fingerprint1 to be checked against const uint8_t *_fingerprint2; // fingerprint2 to be checked against -// **** Start patch Castellucci -/* uint8_t _recv_fingerprint[20]; // fingerprint received -*/ - uint8_t _recv_fingerprint[21]; // fingerprint received -// **** End patch Castellucci unsigned char *_recvapp_buf; size_t _recvapp_len; diff --git a/lib/libesp32/HttpClientLight/src/HttpClientLight.cpp b/lib/libesp32/HttpClientLight/src/HttpClientLight.cpp index 6c35df2544d8..1430e6a6eeb8 100644 --- a/lib/libesp32/HttpClientLight/src/HttpClientLight.cpp +++ b/lib/libesp32/HttpClientLight/src/HttpClientLight.cpp @@ -89,6 +89,7 @@ class BearSSLTraits : public TransportTraitsLight { BearSSL::WiFiClientSecure_light& wcs = static_cast(client); wcs.setPubKeyFingerprint(_fingerprint_any, _fingerprint_any, true); // allow all fingerprints + wcs.setRSAOnly(false); // although we use fingerprint, we allow ECDSA return true; } diff --git a/lib/libesp32/berry/default/be_modtab.c b/lib/libesp32/berry/default/be_modtab.c index e4f60edcc939..f6a695145432 100644 --- a/lib/libesp32/berry/default/be_modtab.c +++ b/lib/libesp32/berry/default/be_modtab.c @@ -152,7 +152,7 @@ BERRY_LOCAL const bntvmodule_t* const be_module_table[] = { &be_native_module(unishox), #endif // USE_UNISHOX_COMPRESSION -#ifdef USE_WS2812 +#if defined(USE_WS2812) && !defined(USE_WS2812_FORCE_NEOPIXELBUS) &be_native_module(animate), #endif // USE_WS2812 @@ -178,7 +178,7 @@ BERRY_LOCAL const bntvmodule_t* const be_module_table[] = { &be_native_module(partition_core), &be_native_module(crc), &be_native_module(crypto), -#if defined(USE_BERRY_ULP) && ((CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)) +#if defined(USE_BERRY_ULP) && defined(CONFIG_ULP_COPROC_ENABLED) &be_native_module(ULP), #endif // USE_BERRY_ULP #if defined(USE_BERRY_TF_LITE) @@ -293,7 +293,7 @@ BERRY_LOCAL bclass_array be_class_table = { #ifdef USE_BERRY_TCPSERVER &be_native_class(tcpserver), #endif // USE_BERRY_TCPSERVER -#ifdef USE_WS2812 +#if defined(USE_WS2812) && !defined(USE_WS2812_FORCE_NEOPIXELBUS) &be_native_class(Leds_ntv), &be_native_class(Leds), #endif // USE_WS2812 diff --git a/lib/libesp32/berry_animate/src/be_berry_leds_frame.cpp b/lib/libesp32/berry_animate/src/be_berry_leds_frame.cpp index 70917593faec..65ab158ccab6 100644 --- a/lib/libesp32/berry_animate/src/be_berry_leds_frame.cpp +++ b/lib/libesp32/berry_animate/src/be_berry_leds_frame.cpp @@ -45,10 +45,10 @@ extern "C" { uint32_t g2 = (color_b >> 8) & 0xFF; uint32_t b2 = (color_b ) & 0xFF; uint32_t a2 = (color_b >> 24) & 0xFF; - uint32_t r3 = changeUIntScale(alpha, 0, 255, r2, r); - uint32_t g3 = changeUIntScale(alpha, 0, 255, g2, g); - uint32_t b3 = changeUIntScale(alpha, 0, 255, b2, b); - uint32_t a3 = changeUIntScale(alpha, 0, 255, a2, a); + uint8_t r3 = changeUIntScale(alpha, 0, 255, r2, r); + uint8_t g3 = changeUIntScale(alpha, 0, 255, g2, g); + uint8_t b3 = changeUIntScale(alpha, 0, 255, b2, b); + uint8_t a3 = changeUIntScale(alpha, 0, 255, a2, a); uint32_t rgb = (a3 << 24) | (r3 << 16) | (g3 << 8) | b3; be_pushint(vm, rgb); be_return(vm); @@ -97,9 +97,9 @@ extern "C" { uint32_t fore_g = (fore_argb >> 8) & 0xFF; uint32_t back_b = (back_argb ) & 0xFF; uint32_t fore_b = (fore_argb ) & 0xFF; - uint32_t dest_r_new = changeUIntScale(fore_alpha, 0, 255, fore_r, back_r); - uint32_t dest_g_new = changeUIntScale(fore_alpha, 0, 255, fore_g, back_g); - uint32_t dest_b_new = changeUIntScale(fore_alpha, 0, 255, fore_b, back_b); + uint8_t dest_r_new = changeUIntScale(fore_alpha, 0, 255, fore_r, back_r); + uint8_t dest_g_new = changeUIntScale(fore_alpha, 0, 255, fore_g, back_g); + uint8_t dest_b_new = changeUIntScale(fore_alpha, 0, 255, fore_b, back_b); dest_rgb_new = (dest_r_new << 16) | (dest_g_new << 8) | dest_b_new; } dest[i] = dest_rgb_new; @@ -135,7 +135,7 @@ extern "C" { // Leds_frame.paste_pixels(neopixel:bytes(), led_buffer:bytes(), bri:int 0..100, gamma:bool) // - // Copy from ARGB buffer to GRB + // Copy from ARGB buffer to RGB int32_t be_leds_paste_pixels(bvm *vm); int32_t be_leds_paste_pixels(bvm *vm) { int32_t top = be_top(vm); // Get the number of arguments @@ -162,8 +162,8 @@ extern "C" { uint32_t src_r = (src_argb >> 16) & 0xFF; uint32_t src_g = (src_argb >> 8) & 0xFF; uint32_t src_b = (src_argb ) & 0xFF; - dest_buf[i * 3 + 0] = src_g; - dest_buf[i * 3 + 1] = src_r; + dest_buf[i * 3 + 0] = src_r; + dest_buf[i * 3 + 1] = src_g; dest_buf[i * 3 + 2] = src_b; } } diff --git a/lib/libesp32/berry_animate/src/embedded/animate_1_core.be b/lib/libesp32/berry_animate/src/embedded/animate_1_core.be index bacaf561dbaa..1906542444ad 100644 --- a/lib/libesp32/berry_animate/src/embedded/animate_1_core.be +++ b/lib/libesp32/berry_animate/src/embedded/animate_1_core.be @@ -110,6 +110,14 @@ class Animate_core self.strip.clear() end def start() + # check if the strip had a different animate object, stop it + var prev_animate = self.strip.get_animate() + import introspect + if (prev_animate != nil) && (type(prev_animate) == 'instance') && (prev_animate != self) + prev_animate.stop() + end + self.strip.set_animate(self) + self.running = true var animators = self.animators var idx = 0 diff --git a/lib/libesp32/berry_animate/src/solidify/solidified_animate_1_core.h b/lib/libesp32/berry_animate/src/solidify/solidified_animate_1_core.h index 440804ff2a1e..eee5e9752d6b 100644 --- a/lib/libesp32/berry_animate/src/solidify/solidified_animate_1_core.h +++ b/lib/libesp32/berry_animate/src/solidify/solidified_animate_1_core.h @@ -3,8 +3,8 @@ * Generated code, don't edit * \********************************************************************/ #include "be_constobj.h" -// compact class 'Animate_core' ktab size: 48, total: 98 (saved 400 bytes) -static const bvalue be_ktab_class_Animate_core[48] = { +// compact class 'Animate_core' ktab size: 52, total: 104 (saved 416 bytes) +static const bvalue be_ktab_class_Animate_core[52] = { /* K0 */ be_nested_str_weak(stop), /* K1 */ be_nested_str_weak(strip), /* K2 */ be_nested_str_weak(clear), @@ -51,8 +51,12 @@ static const bvalue be_ktab_class_Animate_core[48] = { /* K43 */ be_nested_str_weak(set_cb), /* K44 */ be_nested_str_weak(set_back_color), /* K45 */ be_nested_str_weak(add_animator), - /* K46 */ be_nested_str_weak(start), - /* K47 */ be_nested_str_weak(add_fast_loop), + /* K46 */ be_nested_str_weak(get_animate), + /* K47 */ be_nested_str_weak(introspect), + /* K48 */ be_nested_str_weak(instance), + /* K49 */ be_nested_str_weak(set_animate), + /* K50 */ be_nested_str_weak(start), + /* K51 */ be_nested_str_weak(add_fast_loop), }; @@ -716,7 +720,7 @@ be_local_closure(class_Animate_core_remove, /* name */ ********************************************************************/ be_local_closure(class_Animate_core_start, /* name */ be_nested_proto( - 6, /* nstack */ + 8, /* nstack */ 1, /* argc */ 10, /* varg */ 0, /* has upvals */ @@ -727,27 +731,47 @@ be_local_closure(class_Animate_core_start, /* name */ &be_ktab_class_Animate_core, /* shared constants */ be_str_weak(start), &be_const_str_solidified, - ( &(const binstruction[20]) { /* code */ - 0x50040200, // 0000 LDBOOL R1 1 0 - 0x90021601, // 0001 SETMBR R0 K11 R1 - 0x8804010C, // 0002 GETMBR R1 R0 K12 - 0x58080007, // 0003 LDCONST R2 K7 - 0x600C000C, // 0004 GETGBL R3 G12 - 0x5C100200, // 0005 MOVE R4 R1 - 0x7C0C0200, // 0006 CALL R3 1 - 0x140C0403, // 0007 LT R3 R2 R3 - 0x780E0004, // 0008 JMPF R3 #000E - 0x940C0202, // 0009 GETIDX R3 R1 R2 - 0x8C0C072E, // 000A GETMET R3 R3 K46 - 0x7C0C0200, // 000B CALL R3 1 - 0x0008050D, // 000C ADD R2 R2 K13 - 0x7001FFF5, // 000D JMP #0004 - 0x90022707, // 000E SETMBR R0 K19 K7 - 0xB80E0800, // 000F GETNGBL R3 K4 - 0x8C0C072F, // 0010 GETMET R3 R3 K47 - 0x8814010F, // 0011 GETMBR R5 R0 K15 - 0x7C0C0400, // 0012 CALL R3 2 - 0x80000000, // 0013 RET 0 + ( &(const binstruction[40]) { /* code */ + 0x88040101, // 0000 GETMBR R1 R0 K1 + 0x8C04032E, // 0001 GETMET R1 R1 K46 + 0x7C040200, // 0002 CALL R1 1 + 0xA40A5E00, // 0003 IMPORT R2 K47 + 0x4C0C0000, // 0004 LDNIL R3 + 0x200C0203, // 0005 NE R3 R1 R3 + 0x780E0008, // 0006 JMPF R3 #0010 + 0x600C0004, // 0007 GETGBL R3 G4 + 0x5C100200, // 0008 MOVE R4 R1 + 0x7C0C0200, // 0009 CALL R3 1 + 0x1C0C0730, // 000A EQ R3 R3 K48 + 0x780E0003, // 000B JMPF R3 #0010 + 0x200C0200, // 000C NE R3 R1 R0 + 0x780E0001, // 000D JMPF R3 #0010 + 0x8C0C0300, // 000E GETMET R3 R1 K0 + 0x7C0C0200, // 000F CALL R3 1 + 0x880C0101, // 0010 GETMBR R3 R0 K1 + 0x8C0C0731, // 0011 GETMET R3 R3 K49 + 0x5C140000, // 0012 MOVE R5 R0 + 0x7C0C0400, // 0013 CALL R3 2 + 0x500C0200, // 0014 LDBOOL R3 1 0 + 0x90021603, // 0015 SETMBR R0 K11 R3 + 0x880C010C, // 0016 GETMBR R3 R0 K12 + 0x58100007, // 0017 LDCONST R4 K7 + 0x6014000C, // 0018 GETGBL R5 G12 + 0x5C180600, // 0019 MOVE R6 R3 + 0x7C140200, // 001A CALL R5 1 + 0x14140805, // 001B LT R5 R4 R5 + 0x78160004, // 001C JMPF R5 #0022 + 0x94140604, // 001D GETIDX R5 R3 R4 + 0x8C140B32, // 001E GETMET R5 R5 K50 + 0x7C140200, // 001F CALL R5 1 + 0x0010090D, // 0020 ADD R4 R4 K13 + 0x7001FFF5, // 0021 JMP #0018 + 0x90022707, // 0022 SETMBR R0 K19 K7 + 0xB8160800, // 0023 GETNGBL R5 K4 + 0x8C140B33, // 0024 GETMET R5 R5 K51 + 0x881C010F, // 0025 GETMBR R7 R0 K15 + 0x7C140400, // 0026 CALL R5 2 + 0x80000000, // 0027 RET 0 }) ) ); diff --git a/lib/libesp32/berry_matter/src/embedded/Matter_UI.be b/lib/libesp32/berry_matter/src/embedded/Matter_UI.be index 7117500d2d73..551ddffebe23 100644 --- a/lib/libesp32/berry_matter/src/embedded/Matter_UI.be +++ b/lib/libesp32/berry_matter/src/embedded/Matter_UI.be @@ -82,7 +82,7 @@ class Matter_UI # webserver.content_send("

") webserver.content_send("

") + webserver.content_send(" Matter

") end #- ---------------------------------------------------------------------- -# diff --git a/lib/libesp32/berry_matter/src/solidify/solidified_Matter_UI.h b/lib/libesp32/berry_matter/src/solidify/solidified_Matter_UI.h index 6870526377e2..eb9a0bee3292 100644 --- a/lib/libesp32/berry_matter/src/solidify/solidified_Matter_UI.h +++ b/lib/libesp32/berry_matter/src/solidify/solidified_Matter_UI.h @@ -191,7 +191,7 @@ static const bvalue be_ktab_class_Matter_UI[232] = { /* K182 */ be_nested_str_weak(_X3C_X2Ftable_X3E_X3Chr_X3E), /* K183 */ be_nested_str_weak(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27matterc_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3E), /* K184 */ be_nested_str_weak(_LOGO), - /* K185 */ be_nested_str_weak(_X20Configure_X20Matter_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E), + /* K185 */ be_nested_str_weak(_X20Matter_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E), /* K186 */ be_nested_str_weak(get_plugin_class_displayname), /* K187 */ be_nested_str_weak(Matter_X20Advanced_X20Configuration), /* K188 */ be_nested_str_weak(show_passcode_form), diff --git a/lib/libesp32/berry_tasmota/src/be_ULP_lib.c b/lib/libesp32/berry_tasmota/src/be_ULP_lib.c index d133b2241338..6e56dffe58d5 100644 --- a/lib/libesp32/berry_tasmota/src/be_ULP_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_ULP_lib.c @@ -6,7 +6,7 @@ #include "be_constobj.h" #include "be_mapping.h" -#if defined(USE_BERRY_ULP) && (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)) +#if defined(USE_BERRY_ULP) && defined(CONFIG_ULP_COPROC_ENABLED) extern void be_ULP_run(int32_t entry); BE_FUNC_CTYPE_DECLARE(be_ULP_run, "", "[i]"); diff --git a/lib/libesp32/berry_tasmota/src/be_leds_ntv_lib.c b/lib/libesp32/berry_tasmota/src/be_leds_ntv_lib.c index 26ff0b69bd0f..bd6ad641b6ab 100644 --- a/lib/libesp32/berry_tasmota/src/be_leds_ntv_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_leds_ntv_lib.c @@ -7,19 +7,25 @@ #ifdef USE_WS2812 -extern int be_neopixelbus_call_native(bvm *vm); +#include "TasmotaLED.h" + +extern int be_tasmotaled_call_native(bvm *vm); extern int be_leds_blend_color(bvm *vm); extern int be_leds_apply_bri_gamma(bvm *vm); /* @const_object_info_begin class be_class_Leds_ntv (scope: global, name: Leds_ntv, strings: weak) { _p, var - _t, var - WS2812_GRB, int(1) - SK6812_GRBW, int(2) + WS2812_GRB, int(ws2812_grb) + SK6812_GRBW, int(sk6812_grbw) + SK6812_GRB, int(sk6812_grb) + + RMT, int(TasmotaLed_RMT) + SPI, int(TasmotaLed_SPI) + I2S, int(TasmotaLed_I2S) - call_native, func(be_neopixelbus_call_native) + call_native, func(be_tasmotaled_call_native) blend_color, static_func(be_leds_blend_color) apply_bri_gamma, static_func(be_leds_apply_bri_gamma) diff --git a/lib/libesp32/berry_tasmota/src/embedded/autoconf_module.be b/lib/libesp32/berry_tasmota/src/embedded/autoconf_module.be index 13688c46ae9b..cefd8b7b3bdf 100644 --- a/lib/libesp32/berry_tasmota/src/embedded/autoconf_module.be +++ b/lib/libesp32/berry_tasmota/src/embedded/autoconf_module.be @@ -124,7 +124,7 @@ autoconf_module.init = def (m) # Displays a "Autoconf" button on the configuration page def web_add_config_button() import webserver - webserver.content_send("

") + webserver.content_send("

") end diff --git a/lib/libesp32/berry_tasmota/src/embedded/leds.be b/lib/libesp32/berry_tasmota/src/embedded/leds.be index 019a64e3b309..98fbbfd296b5 100644 --- a/lib/libesp32/berry_tasmota/src/embedded/leds.be +++ b/lib/libesp32/berry_tasmota/src/embedded/leds.be @@ -28,70 +28,51 @@ class Leds : Leds_ntv var gamma # if true, apply gamma (true is default) var leds # number of leds var bri # implicit brightness for this led strip (0..255, default is 50% = 127) + var animate # attached animate object or nil - this allows to stop any existing animation for this strip if we add a new animate # leds:int = number of leds of the strip # gpio:int (optional) = GPIO for NeoPixel. If not specified, takes the WS2812 gpio # typ:int (optional) = Type of LED, defaults to WS2812 RGB - # rmt:int (optional) = RMT hardware channel to use, leave default unless you have a good reason - def init(leds, gpio_phy, typ, rmt) # rmt is optional + # hardware:int (optional) = hardware support (Leds.RMT, Leds.SPI) + def init(leds, gpio_phy, typ, hardware) import gpio self.gamma = true # gamma is enabled by default, it should be disabled explicitly if needed - if (gpio_phy == nil) || (gpio_phy == gpio.pin(gpio.WS2812, 0)) + if (leds == nil ) || (gpio_phy == nil) || (gpio_phy == gpio.pin(gpio.WS2812, 0)) # use native driver self.ctor() # no parameters + # in such case, `self._p` is equal to `0` self.leds = self.pixel_count() import light self.bri = light.get()['bri'] else # use pure Berry driver - self.leds = int(leds) + leds = int(leds) + self.leds = leds self.bri = 127 # 50% brightness by default # initialize the structure - self.ctor(self.leds, gpio_phy, typ, rmt) + # check if already in global `_lhw` + if !global.contains('_lhw') + global._lhw = {} + end + if global._lhw.find(leds) != nil + # an object already exists, attach it + var prov_led = global._lhw.find(leds) # already provisioned leds instance + if self.leds != prov_led.leds + raise "value_error", f"number of leds do not match with previous instanciation {self.leds} vs {prov_led.leds}" + end + self._p = prov_led._p + self.animate = prov_led.animate + global._lhw[leds] = self # put the most recent as current + else + self.ctor(leds, gpio_phy, typ, hardware) + global._lhw[leds] = self + # call begin + self.begin() + end end if self._p == nil raise "internal_error", "couldn't not initialize noepixelbus" end - # call begin - self.begin() - end - - # assign RMT - static def assign_rmt(gpio_phy) - gpio_phy = int(gpio_phy) - if gpio_phy < 0 raise "value_error", "invalid GPIO number" end - - import global - var rmt - # if "_rmt" is not initialized, set to an array of GPIO of size MAX_RMT - if !global.contains("_rmt") - rmt = [] - global._rmt = rmt - for i:0..gpio.MAX_RMT-1 - rmt.push(-1) - end - # if default WS2812 is set, assign RMT0 - if gpio.pin_used(gpio.WS2812, 0) - rmt[0] = gpio.pin(gpio.WS2812, 0) - end - end - - rmt = global._rmt - # find an already assigned slot or try to assign a new one - var i = 0 - var first_free = -1 - while i < gpio.MAX_RMT - var elt = rmt[i] - if elt == gpio_phy return i end # already assigned - if elt < 0 && first_free < 0 first_free = i end # found a free slot - i += 1 - end - if first_free >= 0 - rmt[first_free] = gpio_phy - return first_free - end - # no more slot - raise "internal_error", "no more RMT channel available" end def clear() @@ -109,17 +90,21 @@ class Leds : Leds_ntv return self.bri end - def ctor(leds, gpio_phy, typ, rmt) + def set_animate(animate) + self.animate = animate + end + def get_animate() + return self.animate + end + + def ctor(leds, gpio_phy, typ, hardware) if gpio_phy == nil self.call_native(0) # native driver else if typ == nil typ = self.WS2812_GRB end - if rmt == nil - rmt = self.assign_rmt(gpio_phy) - end - self.call_native(0, leds, gpio_phy, typ, rmt) + self.call_native(0, leds, gpio_phy, typ, hardware) end end def begin() @@ -155,9 +140,13 @@ class Leds : Leds_ntv def pixel_offset() return 0 end - def clear_to(col, bri) + def clear_to(col, bri, index, len) if (bri == nil) bri = self.bri end - self.call_native(9, self.to_gamma(col, bri)) + if index != nil && len != nil + self.call_native(9, self.to_gamma(col, bri), index, len) + else + self.call_native(9, self.to_gamma(col, bri)) + end end def set_pixel_color(idx, col, bri) if (bri == nil) bri = self.bri end @@ -403,15 +392,15 @@ anim() #- -var s = Leds_matrix(5, 5, gpio.pin(gpio.WS2812, 1)) +var s = Leds(25, gpio.pin(gpio.WS2812, 1)).create_matrix(5, 5) s.set_alternate(true) -s.clear_to(0x300000) +s.clear_to(0x400000) s.show() x = 0 y = 0 def anim() - s.clear_to(0x300000) + s.clear_to(0x400000) s.set_matrix_pixel_color(x, y, 0x004000) s.show() y = (y + 1) % 5 diff --git a/lib/libesp32/berry_tasmota/src/solidify/solidified_autoconf_module.h b/lib/libesp32/berry_tasmota/src/solidify/solidified_autoconf_module.h index c64fe3823d28..ef5e1921e94b 100644 --- a/lib/libesp32/berry_tasmota/src/solidify/solidified_autoconf_module.h +++ b/lib/libesp32/berry_tasmota/src/solidify/solidified_autoconf_module.h @@ -121,7 +121,7 @@ static const bvalue be_ktab_class_Autoconf[126] = { /* K113 */ be_nested_str(CFG_X3A_X20loaded_X20_X27_X25s_X27), /* K114 */ be_nested_str(files), /* K115 */ be_nested_str(CFG_X3A_X20exception_X20_X27_X25s_X27_X20_X2D_X20_X27_X25s_X27), - /* K116 */ be_nested_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E), + /* K116 */ be_nested_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2DConf_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E), /* K117 */ be_nested_str(add_driver), /* K118 */ be_nested_str(CFG_X3A_X20multiple_X20autoconf_X20files_X20found_X2C_X20aborting_X20_X28_X27_X25s_X27_X20_X2B_X20_X27_X25s_X27_X29), /* K119 */ be_nested_str(CFG_X3A_X20No_X20_X27_X2A_X2Eautoconf_X27_X20file_X20found), diff --git a/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h b/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h index 683e7e75619d..2e476a9760bc 100644 --- a/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h +++ b/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h @@ -1111,51 +1111,47 @@ be_local_class(Leds_matrix, })), (bstring*) &be_const_str_Leds_matrix ); -// compact class 'Leds' ktab size: 43, total: 83 (saved 320 bytes) -static const bvalue be_ktab_class_Leds[43] = { +// compact class 'Leds' ktab size: 39, total: 74 (saved 280 bytes) +static const bvalue be_ktab_class_Leds[39] = { /* K0 */ be_nested_str(leds), /* K1 */ be_const_int(0), /* K2 */ be_nested_str(value_error), /* K3 */ be_nested_str(out_X20of_X20range), /* K4 */ be_const_class(be_class_Leds_segment), - /* K5 */ be_nested_str(bri), - /* K6 */ be_nested_str(call_native), - /* K7 */ be_const_int(1), - /* K8 */ be_nested_str(clear_to), - /* K9 */ be_nested_str(show), - /* K10 */ be_nested_str(gpio), - /* K11 */ be_nested_str(gamma), - /* K12 */ be_nested_str(pin), - /* K13 */ be_nested_str(WS2812), - /* K14 */ be_nested_str(ctor), - /* K15 */ be_nested_str(pixel_count), - /* K16 */ be_nested_str(light), - /* K17 */ be_nested_str(get), - /* K18 */ be_nested_str(_p), - /* K19 */ be_nested_str(internal_error), - /* K20 */ be_nested_str(couldn_X27t_X20not_X20initialize_X20noepixelbus), - /* K21 */ be_nested_str(begin), - /* K22 */ be_nested_str(apply_bri_gamma), - /* K23 */ be_const_int(2), - /* K24 */ be_const_class(be_class_Leds), - /* K25 */ be_nested_str(Leds), - /* K26 */ be_nested_str(create_matrix), - /* K27 */ be_nested_str(to_gamma), - /* K28 */ be_const_class(be_class_Leds_matrix), - /* K29 */ be_const_int(3), - /* K30 */ be_nested_str(invalid_X20GPIO_X20number), - /* K31 */ be_nested_str(global), - /* K32 */ be_nested_str(contains), - /* K33 */ be_nested_str(_rmt), - /* K34 */ be_nested_str(MAX_RMT), - /* K35 */ be_nested_str(push), - /* K36 */ be_nested_str(stop_iteration), - /* K37 */ be_nested_str(pin_used), - /* K38 */ be_nested_str(no_X20more_X20RMT_X20channel_X20available), - /* K39 */ be_nested_str(WS2812_GRB), - /* K40 */ be_nested_str(assign_rmt), - /* K41 */ be_nested_str(pixel_size), - /* K42 */ be_nested_str(_change_buffer), + /* K5 */ be_nested_str(gamma), + /* K6 */ be_const_class(be_class_Leds), + /* K7 */ be_nested_str(Leds), + /* K8 */ be_nested_str(create_matrix), + /* K9 */ be_nested_str(call_native), + /* K10 */ be_nested_str(pixel_size), + /* K11 */ be_nested_str(pixel_count), + /* K12 */ be_nested_str(_change_buffer), + /* K13 */ be_nested_str(animate), + /* K14 */ be_const_int(2), + /* K15 */ be_nested_str(bri), + /* K16 */ be_nested_str(to_gamma), + /* K17 */ be_nested_str(clear_to), + /* K18 */ be_nested_str(show), + /* K19 */ be_nested_str(apply_bri_gamma), + /* K20 */ be_const_int(1), + /* K21 */ be_const_class(be_class_Leds_matrix), + /* K22 */ be_const_int(3), + /* K23 */ be_nested_str(gpio), + /* K24 */ be_nested_str(pin), + /* K25 */ be_nested_str(WS2812), + /* K26 */ be_nested_str(ctor), + /* K27 */ be_nested_str(light), + /* K28 */ be_nested_str(get), + /* K29 */ be_nested_str(global), + /* K30 */ be_nested_str(contains), + /* K31 */ be_nested_str(_lhw), + /* K32 */ be_nested_str(find), + /* K33 */ be_nested_str(number_X20of_X20leds_X20do_X20not_X20match_X20with_X20previous_X20instanciation_X20_X25s_X20vs_X20_X25s), + /* K34 */ be_nested_str(_p), + /* K35 */ be_nested_str(begin), + /* K36 */ be_nested_str(internal_error), + /* K37 */ be_nested_str(couldn_X27t_X20not_X20initialize_X20noepixelbus), + /* K38 */ be_nested_str(WS2812_GRB), }; @@ -1208,43 +1204,11 @@ be_local_closure(class_Leds_create_segment, /* name */ /******************************************************************** -** Solidified function: set_bri -********************************************************************/ -be_local_closure(class_Leds_set_bri, /* name */ - be_nested_proto( - 3, /* nstack */ - 2, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds, /* shared constants */ - &be_const_str_set_bri, - &be_const_str_solidified, - ( &(const binstruction[ 9]) { /* code */ - 0x14080301, // 0000 LT R2 R1 K1 - 0x780A0000, // 0001 JMPF R2 #0003 - 0x58040001, // 0002 LDCONST R1 K1 - 0x540A00FE, // 0003 LDINT R2 255 - 0x24080202, // 0004 GT R2 R1 R2 - 0x780A0000, // 0005 JMPF R2 #0007 - 0x540600FE, // 0006 LDINT R1 255 - 0x90020A01, // 0007 SETMBR R0 K5 R1 - 0x80000000, // 0008 RET 0 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: begin +** Solidified function: get_gamma ********************************************************************/ -be_local_closure(class_Leds_begin, /* name */ +be_local_closure(class_Leds_get_gamma, /* name */ be_nested_proto( - 4, /* nstack */ + 2, /* nstack */ 1, /* argc */ 10, /* varg */ 0, /* has upvals */ @@ -1253,13 +1217,11 @@ be_local_closure(class_Leds_begin, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_begin, + &be_const_str_get_gamma, &be_const_str_solidified, - ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 - 0x580C0007, // 0001 LDCONST R3 K7 - 0x7C040400, // 0002 CALL R1 2 - 0x80000000, // 0003 RET 0 + ( &(const binstruction[ 2]) { /* code */ + 0x88040105, // 0000 GETMBR R1 R0 K5 + 0x80040200, // 0001 RET 1 R1 }) ) ); @@ -1267,28 +1229,34 @@ be_local_closure(class_Leds_begin, /* name */ /******************************************************************** -** Solidified function: clear +** Solidified function: matrix ********************************************************************/ -be_local_closure(class_Leds_clear, /* name */ +be_local_closure(class_Leds_matrix, /* name */ be_nested_proto( - 4, /* nstack */ - 1, /* argc */ - 10, /* varg */ + 11, /* nstack */ + 4, /* argc */ + 12, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_clear, + &be_const_str_matrix, &be_const_str_solidified, - ( &(const binstruction[ 6]) { /* code */ - 0x8C040108, // 0000 GETMET R1 R0 K8 - 0x580C0001, // 0001 LDCONST R3 K1 - 0x7C040400, // 0002 CALL R1 2 - 0x8C040109, // 0003 GETMET R1 R0 K9 - 0x7C040200, // 0004 CALL R1 1 - 0x80000000, // 0005 RET 0 + ( &(const binstruction[12]) { /* code */ + 0x58100006, // 0000 LDCONST R4 K6 + 0xB8160E00, // 0001 GETNGBL R5 K7 + 0x08180001, // 0002 MUL R6 R0 R1 + 0x5C1C0400, // 0003 MOVE R7 R2 + 0x5C200600, // 0004 MOVE R8 R3 + 0x7C140600, // 0005 CALL R5 3 + 0x8C180B08, // 0006 GETMET R6 R5 K8 + 0x5C200000, // 0007 MOVE R8 R0 + 0x5C240200, // 0008 MOVE R9 R1 + 0x58280001, // 0009 LDCONST R10 K1 + 0x7C180800, // 000A CALL R6 4 + 0x80040C00, // 000B RET 1 R6 }) ) ); @@ -1296,12 +1264,12 @@ be_local_closure(class_Leds_clear, /* name */ /******************************************************************** -** Solidified function: init +** Solidified function: pixels_buffer ********************************************************************/ -be_local_closure(class_Leds_init, /* name */ +be_local_closure(class_Leds_pixels_buffer, /* name */ be_nested_proto( - 12, /* nstack */ - 5, /* argc */ + 8, /* nstack */ + 2, /* argc */ 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -1309,52 +1277,30 @@ be_local_closure(class_Leds_init, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_init, + &be_const_str_pixels_buffer, &be_const_str_solidified, - ( &(const binstruction[43]) { /* code */ - 0xA4161400, // 0000 IMPORT R5 K10 - 0x50180200, // 0001 LDBOOL R6 1 0 - 0x90021606, // 0002 SETMBR R0 K11 R6 - 0x4C180000, // 0003 LDNIL R6 - 0x1C180406, // 0004 EQ R6 R2 R6 - 0x741A0005, // 0005 JMPT R6 #000C - 0x8C180B0C, // 0006 GETMET R6 R5 K12 - 0x88200B0D, // 0007 GETMBR R8 R5 K13 - 0x58240001, // 0008 LDCONST R9 K1 - 0x7C180600, // 0009 CALL R6 3 - 0x1C180406, // 000A EQ R6 R2 R6 - 0x781A000A, // 000B JMPF R6 #0017 - 0x8C18010E, // 000C GETMET R6 R0 K14 - 0x7C180200, // 000D CALL R6 1 - 0x8C18010F, // 000E GETMET R6 R0 K15 - 0x7C180200, // 000F CALL R6 1 - 0x90020006, // 0010 SETMBR R0 K0 R6 - 0xA41A2000, // 0011 IMPORT R6 K16 - 0x8C1C0D11, // 0012 GETMET R7 R6 K17 - 0x7C1C0200, // 0013 CALL R7 1 - 0x941C0F05, // 0014 GETIDX R7 R7 K5 - 0x90020A07, // 0015 SETMBR R0 K5 R7 - 0x7002000B, // 0016 JMP #0023 - 0x60180009, // 0017 GETGBL R6 G9 - 0x5C1C0200, // 0018 MOVE R7 R1 - 0x7C180200, // 0019 CALL R6 1 - 0x90020006, // 001A SETMBR R0 K0 R6 - 0x541A007E, // 001B LDINT R6 127 - 0x90020A06, // 001C SETMBR R0 K5 R6 - 0x8C18010E, // 001D GETMET R6 R0 K14 - 0x88200100, // 001E GETMBR R8 R0 K0 - 0x5C240400, // 001F MOVE R9 R2 - 0x5C280600, // 0020 MOVE R10 R3 - 0x5C2C0800, // 0021 MOVE R11 R4 - 0x7C180A00, // 0022 CALL R6 5 - 0x88180112, // 0023 GETMBR R6 R0 K18 - 0x4C1C0000, // 0024 LDNIL R7 - 0x1C180C07, // 0025 EQ R6 R6 R7 - 0x781A0000, // 0026 JMPF R6 #0028 - 0xB0062714, // 0027 RAISE 1 K19 K20 - 0x8C180115, // 0028 GETMET R6 R0 K21 - 0x7C180200, // 0029 CALL R6 1 - 0x80000000, // 002A RET 0 + ( &(const binstruction[21]) { /* code */ + 0x8C080109, // 0000 GETMET R2 R0 K9 + 0x54120005, // 0001 LDINT R4 6 + 0x7C080400, // 0002 CALL R2 2 + 0x4C0C0000, // 0003 LDNIL R3 + 0x1C0C0203, // 0004 EQ R3 R1 R3 + 0x780E0009, // 0005 JMPF R3 #0010 + 0x600C0015, // 0006 GETGBL R3 G21 + 0x5C100400, // 0007 MOVE R4 R2 + 0x8C14010A, // 0008 GETMET R5 R0 K10 + 0x7C140200, // 0009 CALL R5 1 + 0x8C18010B, // 000A GETMET R6 R0 K11 + 0x7C180200, // 000B CALL R6 1 + 0x08140A06, // 000C MUL R5 R5 R6 + 0x7C0C0400, // 000D CALL R3 2 + 0x80040600, // 000E RET 1 R3 + 0x70020003, // 000F JMP #0014 + 0x8C0C030C, // 0010 GETMET R3 R1 K12 + 0x5C140400, // 0011 MOVE R5 R2 + 0x7C0C0400, // 0012 CALL R3 2 + 0x80040200, // 0013 RET 1 R1 + 0x80000000, // 0014 RET 0 }) ) ); @@ -1362,12 +1308,12 @@ be_local_closure(class_Leds_init, /* name */ /******************************************************************** -** Solidified function: to_gamma +** Solidified function: get_animate ********************************************************************/ -be_local_closure(class_Leds_to_gamma, /* name */ +be_local_closure(class_Leds_get_animate, /* name */ be_nested_proto( - 8, /* nstack */ - 3, /* argc */ + 2, /* nstack */ + 1, /* argc */ 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -1375,19 +1321,11 @@ be_local_closure(class_Leds_to_gamma, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_to_gamma, + &be_const_str_get_animate, &be_const_str_solidified, - ( &(const binstruction[10]) { /* code */ - 0x4C0C0000, // 0000 LDNIL R3 - 0x1C0C0403, // 0001 EQ R3 R2 R3 - 0x780E0000, // 0002 JMPF R3 #0004 - 0x88080105, // 0003 GETMBR R2 R0 K5 - 0x8C0C0116, // 0004 GETMET R3 R0 K22 - 0x5C140200, // 0005 MOVE R5 R1 - 0x5C180400, // 0006 MOVE R6 R2 - 0x881C010B, // 0007 GETMBR R7 R0 K11 - 0x7C0C0800, // 0008 CALL R3 4 - 0x80040600, // 0009 RET 1 R3 + ( &(const binstruction[ 2]) { /* code */ + 0x8804010D, // 0000 GETMBR R1 R0 K13 + 0x80040200, // 0001 RET 1 R1 }) ) ); @@ -1411,8 +1349,8 @@ be_local_closure(class_Leds_show, /* name */ &be_const_str_show, &be_const_str_solidified, ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 - 0x580C0017, // 0001 LDCONST R3 K23 + 0x8C040109, // 0000 GETMET R1 R0 K9 + 0x580C000E, // 0001 LDCONST R3 K14 0x7C040400, // 0002 CALL R1 2 0x80000000, // 0003 RET 0 }) @@ -1422,12 +1360,12 @@ be_local_closure(class_Leds_show, /* name */ /******************************************************************** -** Solidified function: pixel_count +** Solidified function: set_pixel_color ********************************************************************/ -be_local_closure(class_Leds_pixel_count, /* name */ +be_local_closure(class_Leds_set_pixel_color, /* name */ be_nested_proto( - 4, /* nstack */ - 1, /* argc */ + 12, /* nstack */ + 4, /* argc */ 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -1435,13 +1373,22 @@ be_local_closure(class_Leds_pixel_count, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_pixel_count, + &be_const_str_set_pixel_color, &be_const_str_solidified, - ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 - 0x540E0007, // 0001 LDINT R3 8 - 0x7C040400, // 0002 CALL R1 2 - 0x80040200, // 0003 RET 1 R1 + ( &(const binstruction[13]) { /* code */ + 0x4C100000, // 0000 LDNIL R4 + 0x1C100604, // 0001 EQ R4 R3 R4 + 0x78120000, // 0002 JMPF R4 #0004 + 0x880C010F, // 0003 GETMBR R3 R0 K15 + 0x8C100109, // 0004 GETMET R4 R0 K9 + 0x541A0009, // 0005 LDINT R6 10 + 0x5C1C0200, // 0006 MOVE R7 R1 + 0x8C200110, // 0007 GETMET R8 R0 K16 + 0x5C280400, // 0008 MOVE R10 R2 + 0x5C2C0600, // 0009 MOVE R11 R3 + 0x7C200600, // 000A CALL R8 3 + 0x7C100800, // 000B CALL R4 4 + 0x80000000, // 000C RET 0 }) ) ); @@ -1449,11 +1396,11 @@ be_local_closure(class_Leds_pixel_count, /* name */ /******************************************************************** -** Solidified function: get_bri +** Solidified function: is_dirty ********************************************************************/ -be_local_closure(class_Leds_get_bri, /* name */ +be_local_closure(class_Leds_is_dirty, /* name */ be_nested_proto( - 2, /* nstack */ + 4, /* nstack */ 1, /* argc */ 10, /* varg */ 0, /* has upvals */ @@ -1462,11 +1409,13 @@ be_local_closure(class_Leds_get_bri, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_get_bri, + &be_const_str_is_dirty, &be_const_str_solidified, - ( &(const binstruction[ 2]) { /* code */ - 0x88040105, // 0000 GETMBR R1 R0 K5 - 0x80040200, // 0001 RET 1 R1 + ( &(const binstruction[ 4]) { /* code */ + 0x8C040109, // 0000 GETMET R1 R0 K9 + 0x540E0003, // 0001 LDINT R3 4 + 0x7C040400, // 0002 CALL R1 2 + 0x80040200, // 0003 RET 1 R1 }) ) ); @@ -1474,12 +1423,12 @@ be_local_closure(class_Leds_get_bri, /* name */ /******************************************************************** -** Solidified function: set_gamma +** Solidified function: clear ********************************************************************/ -be_local_closure(class_Leds_set_gamma, /* name */ +be_local_closure(class_Leds_clear, /* name */ be_nested_proto( 4, /* nstack */ - 2, /* argc */ + 1, /* argc */ 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -1487,14 +1436,15 @@ be_local_closure(class_Leds_set_gamma, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_set_gamma, + &be_const_str_clear, &be_const_str_solidified, - ( &(const binstruction[ 5]) { /* code */ - 0x60080017, // 0000 GETGBL R2 G23 - 0x5C0C0200, // 0001 MOVE R3 R1 - 0x7C080200, // 0002 CALL R2 1 - 0x90021602, // 0003 SETMBR R0 K11 R2 - 0x80000000, // 0004 RET 0 + ( &(const binstruction[ 6]) { /* code */ + 0x8C040111, // 0000 GETMET R1 R0 K17 + 0x580C0001, // 0001 LDCONST R3 K1 + 0x7C040400, // 0002 CALL R1 2 + 0x8C040112, // 0003 GETMET R1 R0 K18 + 0x7C040200, // 0004 CALL R1 1 + 0x80000000, // 0005 RET 0 }) ) ); @@ -1502,12 +1452,12 @@ be_local_closure(class_Leds_set_gamma, /* name */ /******************************************************************** -** Solidified function: get_pixel_color +** Solidified function: pixel_size ********************************************************************/ -be_local_closure(class_Leds_get_pixel_color, /* name */ +be_local_closure(class_Leds_pixel_size, /* name */ be_nested_proto( - 6, /* nstack */ - 2, /* argc */ + 4, /* nstack */ + 1, /* argc */ 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -1515,14 +1465,13 @@ be_local_closure(class_Leds_get_pixel_color, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_get_pixel_color, + &be_const_str_pixel_size, &be_const_str_solidified, - ( &(const binstruction[ 5]) { /* code */ - 0x8C080106, // 0000 GETMET R2 R0 K6 - 0x5412000A, // 0001 LDINT R4 11 - 0x5C140200, // 0002 MOVE R5 R1 - 0x7C080600, // 0003 CALL R2 3 - 0x80040400, // 0004 RET 1 R2 + ( &(const binstruction[ 4]) { /* code */ + 0x8C040109, // 0000 GETMET R1 R0 K9 + 0x540E0006, // 0001 LDINT R3 7 + 0x7C040400, // 0002 CALL R1 2 + 0x80040200, // 0003 RET 1 R1 }) ) ); @@ -1530,9 +1479,9 @@ be_local_closure(class_Leds_get_pixel_color, /* name */ /******************************************************************** -** Solidified function: dirty +** Solidified function: pixel_count ********************************************************************/ -be_local_closure(class_Leds_dirty, /* name */ +be_local_closure(class_Leds_pixel_count, /* name */ be_nested_proto( 4, /* nstack */ 1, /* argc */ @@ -1543,13 +1492,13 @@ be_local_closure(class_Leds_dirty, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_dirty, + &be_const_str_pixel_count, &be_const_str_solidified, ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 - 0x540E0004, // 0001 LDINT R3 5 + 0x8C040109, // 0000 GETMET R1 R0 K9 + 0x540E0007, // 0001 LDINT R3 8 0x7C040400, // 0002 CALL R1 2 - 0x80000000, // 0003 RET 0 + 0x80040200, // 0003 RET 1 R1 }) ) ); @@ -1557,34 +1506,32 @@ be_local_closure(class_Leds_dirty, /* name */ /******************************************************************** -** Solidified function: matrix +** Solidified function: to_gamma ********************************************************************/ -be_local_closure(class_Leds_matrix, /* name */ +be_local_closure(class_Leds_to_gamma, /* name */ be_nested_proto( - 11, /* nstack */ - 4, /* argc */ - 12, /* varg */ + 8, /* nstack */ + 3, /* argc */ + 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_matrix, + &be_const_str_to_gamma, &be_const_str_solidified, - ( &(const binstruction[12]) { /* code */ - 0x58100018, // 0000 LDCONST R4 K24 - 0xB8163200, // 0001 GETNGBL R5 K25 - 0x08180001, // 0002 MUL R6 R0 R1 - 0x5C1C0400, // 0003 MOVE R7 R2 - 0x5C200600, // 0004 MOVE R8 R3 - 0x7C140600, // 0005 CALL R5 3 - 0x8C180B1A, // 0006 GETMET R6 R5 K26 - 0x5C200000, // 0007 MOVE R8 R0 - 0x5C240200, // 0008 MOVE R9 R1 - 0x58280001, // 0009 LDCONST R10 K1 - 0x7C180800, // 000A CALL R6 4 - 0x80040C00, // 000B RET 1 R6 + ( &(const binstruction[10]) { /* code */ + 0x4C0C0000, // 0000 LDNIL R3 + 0x1C0C0403, // 0001 EQ R3 R2 R3 + 0x780E0000, // 0002 JMPF R3 #0004 + 0x8808010F, // 0003 GETMBR R2 R0 K15 + 0x8C0C0113, // 0004 GETMET R3 R0 K19 + 0x5C140200, // 0005 MOVE R5 R1 + 0x5C180400, // 0006 MOVE R6 R2 + 0x881C0105, // 0007 GETMBR R7 R0 K5 + 0x7C0C0800, // 0008 CALL R3 4 + 0x80040600, // 0009 RET 1 R3 }) ) ); @@ -1592,11 +1539,11 @@ be_local_closure(class_Leds_matrix, /* name */ /******************************************************************** -** Solidified function: pixel_offset +** Solidified function: begin ********************************************************************/ -be_local_closure(class_Leds_pixel_offset, /* name */ +be_local_closure(class_Leds_begin, /* name */ be_nested_proto( - 1, /* nstack */ + 4, /* nstack */ 1, /* argc */ 10, /* varg */ 0, /* has upvals */ @@ -1605,10 +1552,13 @@ be_local_closure(class_Leds_pixel_offset, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_pixel_offset, + &be_const_str_begin, &be_const_str_solidified, - ( &(const binstruction[ 1]) { /* code */ - 0x80060200, // 0000 RET 1 K1 + ( &(const binstruction[ 4]) { /* code */ + 0x8C040109, // 0000 GETMET R1 R0 K9 + 0x580C0014, // 0001 LDCONST R3 K20 + 0x7C040400, // 0002 CALL R1 2 + 0x80000000, // 0003 RET 0 }) ) ); @@ -1616,12 +1566,12 @@ be_local_closure(class_Leds_pixel_offset, /* name */ /******************************************************************** -** Solidified function: clear_to +** Solidified function: create_matrix ********************************************************************/ -be_local_closure(class_Leds_clear_to, /* name */ +be_local_closure(class_Leds_create_matrix, /* name */ be_nested_proto( 10, /* nstack */ - 3, /* argc */ + 4, /* argc */ 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -1629,21 +1579,46 @@ be_local_closure(class_Leds_clear_to, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_clear_to, + &be_const_str_create_matrix, &be_const_str_solidified, - ( &(const binstruction[12]) { /* code */ - 0x4C0C0000, // 0000 LDNIL R3 - 0x1C0C0403, // 0001 EQ R3 R2 R3 - 0x780E0000, // 0002 JMPF R3 #0004 - 0x88080105, // 0003 GETMBR R2 R0 K5 - 0x8C0C0106, // 0004 GETMET R3 R0 K6 - 0x54160008, // 0005 LDINT R5 9 - 0x8C18011B, // 0006 GETMET R6 R0 K27 - 0x5C200200, // 0007 MOVE R8 R1 - 0x5C240400, // 0008 MOVE R9 R2 - 0x7C180600, // 0009 CALL R6 3 - 0x7C0C0600, // 000A CALL R3 3 - 0x80000000, // 000B RET 0 + ( &(const binstruction[37]) { /* code */ + 0x60100009, // 0000 GETGBL R4 G9 + 0x5C140600, // 0001 MOVE R5 R3 + 0x7C100200, // 0002 CALL R4 1 + 0x5C0C0800, // 0003 MOVE R3 R4 + 0x60100009, // 0004 GETGBL R4 G9 + 0x5C140200, // 0005 MOVE R5 R1 + 0x7C100200, // 0006 CALL R4 1 + 0x5C040800, // 0007 MOVE R1 R4 + 0x60100009, // 0008 GETGBL R4 G9 + 0x5C140400, // 0009 MOVE R5 R2 + 0x7C100200, // 000A CALL R4 1 + 0x5C080800, // 000B MOVE R2 R4 + 0x4C100000, // 000C LDNIL R4 + 0x1C100604, // 000D EQ R4 R3 R4 + 0x78120000, // 000E JMPF R4 #0010 + 0x580C0001, // 000F LDCONST R3 K1 + 0x08100202, // 0010 MUL R4 R1 R2 + 0x00100803, // 0011 ADD R4 R4 R3 + 0x88140100, // 0012 GETMBR R5 R0 K0 + 0x24100805, // 0013 GT R4 R4 R5 + 0x74120005, // 0014 JMPT R4 #001B + 0x14100501, // 0015 LT R4 R2 K1 + 0x74120003, // 0016 JMPT R4 #001B + 0x14100301, // 0017 LT R4 R1 K1 + 0x74120001, // 0018 JMPT R4 #001B + 0x14100701, // 0019 LT R4 R3 K1 + 0x78120000, // 001A JMPF R4 #001C + 0xB0060503, // 001B RAISE 1 K2 K3 + 0x58100015, // 001C LDCONST R4 K21 + 0xB4000015, // 001D CLASS K21 + 0x5C140800, // 001E MOVE R5 R4 + 0x5C180000, // 001F MOVE R6 R0 + 0x5C1C0200, // 0020 MOVE R7 R1 + 0x5C200400, // 0021 MOVE R8 R2 + 0x5C240600, // 0022 MOVE R9 R3 + 0x7C140800, // 0023 CALL R5 4 + 0x80040A00, // 0024 RET 1 R5 }) ) ); @@ -1651,12 +1626,12 @@ be_local_closure(class_Leds_clear_to, /* name */ /******************************************************************** -** Solidified function: set_pixel_color +** Solidified function: dirty ********************************************************************/ -be_local_closure(class_Leds_set_pixel_color, /* name */ +be_local_closure(class_Leds_dirty, /* name */ be_nested_proto( - 12, /* nstack */ - 4, /* argc */ + 4, /* nstack */ + 1, /* argc */ 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -1664,22 +1639,13 @@ be_local_closure(class_Leds_set_pixel_color, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_set_pixel_color, + &be_const_str_dirty, &be_const_str_solidified, - ( &(const binstruction[13]) { /* code */ - 0x4C100000, // 0000 LDNIL R4 - 0x1C100604, // 0001 EQ R4 R3 R4 - 0x78120000, // 0002 JMPF R4 #0004 - 0x880C0105, // 0003 GETMBR R3 R0 K5 - 0x8C100106, // 0004 GETMET R4 R0 K6 - 0x541A0009, // 0005 LDINT R6 10 - 0x5C1C0200, // 0006 MOVE R7 R1 - 0x8C20011B, // 0007 GETMET R8 R0 K27 - 0x5C280400, // 0008 MOVE R10 R2 - 0x5C2C0600, // 0009 MOVE R11 R3 - 0x7C200600, // 000A CALL R8 3 - 0x7C100800, // 000B CALL R4 4 - 0x80000000, // 000C RET 0 + ( &(const binstruction[ 4]) { /* code */ + 0x8C040109, // 0000 GETMET R1 R0 K9 + 0x540E0004, // 0001 LDINT R3 5 + 0x7C040400, // 0002 CALL R1 2 + 0x80000000, // 0003 RET 0 }) ) ); @@ -1687,9 +1653,41 @@ be_local_closure(class_Leds_set_pixel_color, /* name */ /******************************************************************** -** Solidified function: pixel_size +** Solidified function: set_bri ********************************************************************/ -be_local_closure(class_Leds_pixel_size, /* name */ +be_local_closure(class_Leds_set_bri, /* name */ + be_nested_proto( + 3, /* nstack */ + 2, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds, /* shared constants */ + &be_const_str_set_bri, + &be_const_str_solidified, + ( &(const binstruction[ 9]) { /* code */ + 0x14080301, // 0000 LT R2 R1 K1 + 0x780A0000, // 0001 JMPF R2 #0003 + 0x58040001, // 0002 LDCONST R1 K1 + 0x540A00FE, // 0003 LDINT R2 255 + 0x24080202, // 0004 GT R2 R1 R2 + 0x780A0000, // 0005 JMPF R2 #0007 + 0x540600FE, // 0006 LDINT R1 255 + 0x90021E01, // 0007 SETMBR R0 K15 R1 + 0x80000000, // 0008 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: can_show +********************************************************************/ +be_local_closure(class_Leds_can_show, /* name */ be_nested_proto( 4, /* nstack */ 1, /* argc */ @@ -1700,11 +1698,11 @@ be_local_closure(class_Leds_pixel_size, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_pixel_size, + &be_const_str_can_show, &be_const_str_solidified, ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 - 0x540E0006, // 0001 LDINT R3 7 + 0x8C040109, // 0000 GETMET R1 R0 K9 + 0x580C0016, // 0001 LDCONST R3 K22 0x7C040400, // 0002 CALL R1 2 0x80040200, // 0003 RET 1 R1 }) @@ -1714,12 +1712,12 @@ be_local_closure(class_Leds_pixel_size, /* name */ /******************************************************************** -** Solidified function: create_matrix +** Solidified function: set_animate ********************************************************************/ -be_local_closure(class_Leds_create_matrix, /* name */ +be_local_closure(class_Leds_set_animate, /* name */ be_nested_proto( - 10, /* nstack */ - 4, /* argc */ + 2, /* nstack */ + 2, /* argc */ 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -1727,46 +1725,11 @@ be_local_closure(class_Leds_create_matrix, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_create_matrix, + &be_const_str_set_animate, &be_const_str_solidified, - ( &(const binstruction[37]) { /* code */ - 0x60100009, // 0000 GETGBL R4 G9 - 0x5C140600, // 0001 MOVE R5 R3 - 0x7C100200, // 0002 CALL R4 1 - 0x5C0C0800, // 0003 MOVE R3 R4 - 0x60100009, // 0004 GETGBL R4 G9 - 0x5C140200, // 0005 MOVE R5 R1 - 0x7C100200, // 0006 CALL R4 1 - 0x5C040800, // 0007 MOVE R1 R4 - 0x60100009, // 0008 GETGBL R4 G9 - 0x5C140400, // 0009 MOVE R5 R2 - 0x7C100200, // 000A CALL R4 1 - 0x5C080800, // 000B MOVE R2 R4 - 0x4C100000, // 000C LDNIL R4 - 0x1C100604, // 000D EQ R4 R3 R4 - 0x78120000, // 000E JMPF R4 #0010 - 0x580C0001, // 000F LDCONST R3 K1 - 0x08100202, // 0010 MUL R4 R1 R2 - 0x00100803, // 0011 ADD R4 R4 R3 - 0x88140100, // 0012 GETMBR R5 R0 K0 - 0x24100805, // 0013 GT R4 R4 R5 - 0x74120005, // 0014 JMPT R4 #001B - 0x14100501, // 0015 LT R4 R2 K1 - 0x74120003, // 0016 JMPT R4 #001B - 0x14100301, // 0017 LT R4 R1 K1 - 0x74120001, // 0018 JMPT R4 #001B - 0x14100701, // 0019 LT R4 R3 K1 - 0x78120000, // 001A JMPF R4 #001C - 0xB0060503, // 001B RAISE 1 K2 K3 - 0x5810001C, // 001C LDCONST R4 K28 - 0xB400001C, // 001D CLASS K28 - 0x5C140800, // 001E MOVE R5 R4 - 0x5C180000, // 001F MOVE R6 R0 - 0x5C1C0200, // 0020 MOVE R7 R1 - 0x5C200400, // 0021 MOVE R8 R2 - 0x5C240600, // 0022 MOVE R9 R3 - 0x7C140800, // 0023 CALL R5 4 - 0x80040A00, // 0024 RET 1 R5 + ( &(const binstruction[ 2]) { /* code */ + 0x90021A01, // 0000 SETMBR R0 K13 R1 + 0x80000000, // 0001 RET 0 }) ) ); @@ -1774,9 +1737,9 @@ be_local_closure(class_Leds_create_matrix, /* name */ /******************************************************************** -** Solidified function: get_gamma +** Solidified function: get_bri ********************************************************************/ -be_local_closure(class_Leds_get_gamma, /* name */ +be_local_closure(class_Leds_get_bri, /* name */ be_nested_proto( 2, /* nstack */ 1, /* argc */ @@ -1787,10 +1750,10 @@ be_local_closure(class_Leds_get_gamma, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_get_gamma, + &be_const_str_get_bri, &be_const_str_solidified, ( &(const binstruction[ 2]) { /* code */ - 0x8804010B, // 0000 GETMBR R1 R0 K11 + 0x8804010F, // 0000 GETMBR R1 R0 K15 0x80040200, // 0001 RET 1 R1 }) ) @@ -1799,12 +1762,12 @@ be_local_closure(class_Leds_get_gamma, /* name */ /******************************************************************** -** Solidified function: is_dirty +** Solidified function: clear_to ********************************************************************/ -be_local_closure(class_Leds_is_dirty, /* name */ +be_local_closure(class_Leds_clear_to, /* name */ be_nested_proto( - 4, /* nstack */ - 1, /* argc */ + 12, /* nstack */ + 5, /* argc */ 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -1812,13 +1775,37 @@ be_local_closure(class_Leds_is_dirty, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_is_dirty, + &be_const_str_clear_to, &be_const_str_solidified, - ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 - 0x540E0003, // 0001 LDINT R3 4 - 0x7C040400, // 0002 CALL R1 2 - 0x80040200, // 0003 RET 1 R1 + ( &(const binstruction[28]) { /* code */ + 0x4C140000, // 0000 LDNIL R5 + 0x1C140405, // 0001 EQ R5 R2 R5 + 0x78160000, // 0002 JMPF R5 #0004 + 0x8808010F, // 0003 GETMBR R2 R0 K15 + 0x4C140000, // 0004 LDNIL R5 + 0x20140605, // 0005 NE R5 R3 R5 + 0x7816000C, // 0006 JMPF R5 #0014 + 0x4C140000, // 0007 LDNIL R5 + 0x20140805, // 0008 NE R5 R4 R5 + 0x78160009, // 0009 JMPF R5 #0014 + 0x8C140109, // 000A GETMET R5 R0 K9 + 0x541E0008, // 000B LDINT R7 9 + 0x8C200110, // 000C GETMET R8 R0 K16 + 0x5C280200, // 000D MOVE R10 R1 + 0x5C2C0400, // 000E MOVE R11 R2 + 0x7C200600, // 000F CALL R8 3 + 0x5C240600, // 0010 MOVE R9 R3 + 0x5C280800, // 0011 MOVE R10 R4 + 0x7C140A00, // 0012 CALL R5 5 + 0x70020006, // 0013 JMP #001B + 0x8C140109, // 0014 GETMET R5 R0 K9 + 0x541E0008, // 0015 LDINT R7 9 + 0x8C200110, // 0016 GETMET R8 R0 K16 + 0x5C280200, // 0017 MOVE R10 R1 + 0x5C2C0400, // 0018 MOVE R11 R2 + 0x7C200600, // 0019 CALL R8 3 + 0x7C140600, // 001A CALL R5 3 + 0x80000000, // 001B RET 0 }) ) ); @@ -1826,11 +1813,11 @@ be_local_closure(class_Leds_is_dirty, /* name */ /******************************************************************** -** Solidified function: can_show +** Solidified function: pixel_offset ********************************************************************/ -be_local_closure(class_Leds_can_show, /* name */ +be_local_closure(class_Leds_pixel_offset, /* name */ be_nested_proto( - 4, /* nstack */ + 1, /* nstack */ 1, /* argc */ 10, /* varg */ 0, /* has upvals */ @@ -1839,13 +1826,10 @@ be_local_closure(class_Leds_can_show, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_can_show, + &be_const_str_pixel_offset, &be_const_str_solidified, - ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 - 0x580C001D, // 0001 LDCONST R3 K29 - 0x7C040400, // 0002 CALL R1 2 - 0x80040200, // 0003 RET 1 R1 + ( &(const binstruction[ 1]) { /* code */ + 0x80060200, // 0000 RET 1 K1 }) ) ); @@ -1853,94 +1837,140 @@ be_local_closure(class_Leds_can_show, /* name */ /******************************************************************** -** Solidified function: assign_rmt +** Solidified function: get_pixel_color ********************************************************************/ -be_local_closure(class_Leds_assign_rmt, /* name */ +be_local_closure(class_Leds_get_pixel_color, /* name */ be_nested_proto( - 9, /* nstack */ - 1, /* argc */ - 12, /* varg */ + 6, /* nstack */ + 2, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds, /* shared constants */ + &be_const_str_get_pixel_color, + &be_const_str_solidified, + ( &(const binstruction[ 5]) { /* code */ + 0x8C080109, // 0000 GETMET R2 R0 K9 + 0x5412000A, // 0001 LDINT R4 11 + 0x5C140200, // 0002 MOVE R5 R1 + 0x7C080600, // 0003 CALL R2 3 + 0x80040400, // 0004 RET 1 R2 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: init +********************************************************************/ +be_local_closure(class_Leds_init, /* name */ + be_nested_proto( + 12, /* nstack */ + 5, /* argc */ + 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_assign_rmt, + &be_const_str_init, &be_const_str_solidified, - ( &(const binstruction[72]) { /* code */ - 0x58040018, // 0000 LDCONST R1 K24 - 0x60080009, // 0001 GETGBL R2 G9 - 0x5C0C0000, // 0002 MOVE R3 R0 - 0x7C080200, // 0003 CALL R2 1 - 0x5C000400, // 0004 MOVE R0 R2 - 0x14080101, // 0005 LT R2 R0 K1 - 0x780A0000, // 0006 JMPF R2 #0008 - 0xB006051E, // 0007 RAISE 1 K2 K30 - 0xA40A3E00, // 0008 IMPORT R2 K31 - 0x4C0C0000, // 0009 LDNIL R3 - 0x8C100520, // 000A GETMET R4 R2 K32 - 0x58180021, // 000B LDCONST R6 K33 - 0x7C100400, // 000C CALL R4 2 - 0x74120021, // 000D JMPT R4 #0030 - 0x60100012, // 000E GETGBL R4 G18 - 0x7C100000, // 000F CALL R4 0 - 0x5C0C0800, // 0010 MOVE R3 R4 - 0x900A4203, // 0011 SETMBR R2 K33 R3 - 0x60100010, // 0012 GETGBL R4 G16 - 0xB8161400, // 0013 GETNGBL R5 K10 - 0x88140B22, // 0014 GETMBR R5 R5 K34 - 0x04140B07, // 0015 SUB R5 R5 K7 - 0x40160205, // 0016 CONNECT R5 K1 R5 - 0x7C100200, // 0017 CALL R4 1 - 0xA8020005, // 0018 EXBLK 0 #001F - 0x5C140800, // 0019 MOVE R5 R4 - 0x7C140000, // 001A CALL R5 0 - 0x8C180723, // 001B GETMET R6 R3 K35 - 0x5421FFFE, // 001C LDINT R8 -1 - 0x7C180400, // 001D CALL R6 2 - 0x7001FFF9, // 001E JMP #0019 - 0x58100024, // 001F LDCONST R4 K36 - 0xAC100200, // 0020 CATCH R4 1 0 - 0xB0080000, // 0021 RAISE 2 R0 R0 - 0xB8121400, // 0022 GETNGBL R4 K10 - 0x8C100925, // 0023 GETMET R4 R4 K37 - 0xB81A1400, // 0024 GETNGBL R6 K10 - 0x88180D0D, // 0025 GETMBR R6 R6 K13 - 0x581C0001, // 0026 LDCONST R7 K1 - 0x7C100600, // 0027 CALL R4 3 - 0x78120006, // 0028 JMPF R4 #0030 - 0xB8121400, // 0029 GETNGBL R4 K10 - 0x8C10090C, // 002A GETMET R4 R4 K12 - 0xB81A1400, // 002B GETNGBL R6 K10 - 0x88180D0D, // 002C GETMBR R6 R6 K13 - 0x581C0001, // 002D LDCONST R7 K1 - 0x7C100600, // 002E CALL R4 3 - 0x980E0204, // 002F SETIDX R3 K1 R4 - 0x880C0521, // 0030 GETMBR R3 R2 K33 - 0x58100001, // 0031 LDCONST R4 K1 - 0x5415FFFE, // 0032 LDINT R5 -1 - 0xB81A1400, // 0033 GETNGBL R6 K10 - 0x88180D22, // 0034 GETMBR R6 R6 K34 - 0x14180806, // 0035 LT R6 R4 R6 - 0x781A000A, // 0036 JMPF R6 #0042 - 0x94180604, // 0037 GETIDX R6 R3 R4 - 0x1C1C0C00, // 0038 EQ R7 R6 R0 - 0x781E0000, // 0039 JMPF R7 #003B - 0x80040800, // 003A RET 1 R4 - 0x141C0D01, // 003B LT R7 R6 K1 - 0x781E0002, // 003C JMPF R7 #0040 - 0x141C0B01, // 003D LT R7 R5 K1 - 0x781E0000, // 003E JMPF R7 #0040 - 0x5C140800, // 003F MOVE R5 R4 - 0x00100907, // 0040 ADD R4 R4 K7 - 0x7001FFF0, // 0041 JMP #0033 - 0x28180B01, // 0042 GE R6 R5 K1 - 0x781A0001, // 0043 JMPF R6 #0046 - 0x980C0A00, // 0044 SETIDX R3 R5 R0 - 0x80040A00, // 0045 RET 1 R5 - 0xB0062726, // 0046 RAISE 1 K19 K38 - 0x80000000, // 0047 RET 0 + ( &(const binstruction[90]) { /* code */ + 0xA4162E00, // 0000 IMPORT R5 K23 + 0x50180200, // 0001 LDBOOL R6 1 0 + 0x90020A06, // 0002 SETMBR R0 K5 R6 + 0x4C180000, // 0003 LDNIL R6 + 0x1C180206, // 0004 EQ R6 R1 R6 + 0x741A0008, // 0005 JMPT R6 #000F + 0x4C180000, // 0006 LDNIL R6 + 0x1C180406, // 0007 EQ R6 R2 R6 + 0x741A0005, // 0008 JMPT R6 #000F + 0x8C180B18, // 0009 GETMET R6 R5 K24 + 0x88200B19, // 000A GETMBR R8 R5 K25 + 0x58240001, // 000B LDCONST R9 K1 + 0x7C180600, // 000C CALL R6 3 + 0x1C180406, // 000D EQ R6 R2 R6 + 0x781A000A, // 000E JMPF R6 #001A + 0x8C18011A, // 000F GETMET R6 R0 K26 + 0x7C180200, // 0010 CALL R6 1 + 0x8C18010B, // 0011 GETMET R6 R0 K11 + 0x7C180200, // 0012 CALL R6 1 + 0x90020006, // 0013 SETMBR R0 K0 R6 + 0xA41A3600, // 0014 IMPORT R6 K27 + 0x8C1C0D1C, // 0015 GETMET R7 R6 K28 + 0x7C1C0200, // 0016 CALL R7 1 + 0x941C0F0F, // 0017 GETIDX R7 R7 K15 + 0x90021E07, // 0018 SETMBR R0 K15 R7 + 0x70020039, // 0019 JMP #0054 + 0x60180009, // 001A GETGBL R6 G9 + 0x5C1C0200, // 001B MOVE R7 R1 + 0x7C180200, // 001C CALL R6 1 + 0x5C040C00, // 001D MOVE R1 R6 + 0x90020001, // 001E SETMBR R0 K0 R1 + 0x541A007E, // 001F LDINT R6 127 + 0x90021E06, // 0020 SETMBR R0 K15 R6 + 0xB81A3A00, // 0021 GETNGBL R6 K29 + 0x8C180D1E, // 0022 GETMET R6 R6 K30 + 0x5820001F, // 0023 LDCONST R8 K31 + 0x7C180400, // 0024 CALL R6 2 + 0x741A0003, // 0025 JMPT R6 #002A + 0xB81A3A00, // 0026 GETNGBL R6 K29 + 0x601C0013, // 0027 GETGBL R7 G19 + 0x7C1C0000, // 0028 CALL R7 0 + 0x901A3E07, // 0029 SETMBR R6 K31 R7 + 0xB81A3A00, // 002A GETNGBL R6 K29 + 0x88180D1F, // 002B GETMBR R6 R6 K31 + 0x8C180D20, // 002C GETMET R6 R6 K32 + 0x5C200200, // 002D MOVE R8 R1 + 0x7C180400, // 002E CALL R6 2 + 0x4C1C0000, // 002F LDNIL R7 + 0x20180C07, // 0030 NE R6 R6 R7 + 0x781A0016, // 0031 JMPF R6 #0049 + 0xB81A3A00, // 0032 GETNGBL R6 K29 + 0x88180D1F, // 0033 GETMBR R6 R6 K31 + 0x8C180D20, // 0034 GETMET R6 R6 K32 + 0x5C200200, // 0035 MOVE R8 R1 + 0x7C180400, // 0036 CALL R6 2 + 0x881C0100, // 0037 GETMBR R7 R0 K0 + 0x88200D00, // 0038 GETMBR R8 R6 K0 + 0x201C0E08, // 0039 NE R7 R7 R8 + 0x781E0005, // 003A JMPF R7 #0041 + 0x601C0018, // 003B GETGBL R7 G24 + 0x58200021, // 003C LDCONST R8 K33 + 0x88240100, // 003D GETMBR R9 R0 K0 + 0x88280D00, // 003E GETMBR R10 R6 K0 + 0x7C1C0600, // 003F CALL R7 3 + 0xB0060407, // 0040 RAISE 1 K2 R7 + 0x881C0D22, // 0041 GETMBR R7 R6 K34 + 0x90024407, // 0042 SETMBR R0 K34 R7 + 0x881C0D0D, // 0043 GETMBR R7 R6 K13 + 0x90021A07, // 0044 SETMBR R0 K13 R7 + 0xB81E3A00, // 0045 GETNGBL R7 K29 + 0x881C0F1F, // 0046 GETMBR R7 R7 K31 + 0x981C0200, // 0047 SETIDX R7 R1 R0 + 0x7002000A, // 0048 JMP #0054 + 0x8C18011A, // 0049 GETMET R6 R0 K26 + 0x5C200200, // 004A MOVE R8 R1 + 0x5C240400, // 004B MOVE R9 R2 + 0x5C280600, // 004C MOVE R10 R3 + 0x5C2C0800, // 004D MOVE R11 R4 + 0x7C180A00, // 004E CALL R6 5 + 0xB81A3A00, // 004F GETNGBL R6 K29 + 0x88180D1F, // 0050 GETMBR R6 R6 K31 + 0x98180200, // 0051 SETIDX R6 R1 R0 + 0x8C180123, // 0052 GETMET R6 R0 K35 + 0x7C180200, // 0053 CALL R6 1 + 0x88180122, // 0054 GETMBR R6 R0 K34 + 0x4C1C0000, // 0055 LDNIL R7 + 0x1C180C07, // 0056 EQ R6 R6 R7 + 0x781A0000, // 0057 JMPF R6 #0059 + 0xB0064925, // 0058 RAISE 1 K36 K37 + 0x80000000, // 0059 RET 0 }) ) ); @@ -1963,33 +1993,26 @@ be_local_closure(class_Leds_ctor, /* name */ &be_ktab_class_Leds, /* shared constants */ &be_const_str_ctor, &be_const_str_solidified, - ( &(const binstruction[26]) { /* code */ + ( &(const binstruction[19]) { /* code */ 0x4C140000, // 0000 LDNIL R5 0x1C140405, // 0001 EQ R5 R2 R5 0x78160003, // 0002 JMPF R5 #0007 - 0x8C140106, // 0003 GETMET R5 R0 K6 + 0x8C140109, // 0003 GETMET R5 R0 K9 0x581C0001, // 0004 LDCONST R7 K1 0x7C140400, // 0005 CALL R5 2 - 0x70020011, // 0006 JMP #0019 + 0x7002000A, // 0006 JMP #0012 0x4C140000, // 0007 LDNIL R5 0x1C140605, // 0008 EQ R5 R3 R5 0x78160000, // 0009 JMPF R5 #000B - 0x880C0127, // 000A GETMBR R3 R0 K39 - 0x4C140000, // 000B LDNIL R5 - 0x1C140805, // 000C EQ R5 R4 R5 - 0x78160003, // 000D JMPF R5 #0012 - 0x8C140128, // 000E GETMET R5 R0 K40 - 0x5C1C0400, // 000F MOVE R7 R2 - 0x7C140400, // 0010 CALL R5 2 - 0x5C100A00, // 0011 MOVE R4 R5 - 0x8C140106, // 0012 GETMET R5 R0 K6 - 0x581C0001, // 0013 LDCONST R7 K1 - 0x5C200200, // 0014 MOVE R8 R1 - 0x5C240400, // 0015 MOVE R9 R2 - 0x5C280600, // 0016 MOVE R10 R3 - 0x5C2C0800, // 0017 MOVE R11 R4 - 0x7C140C00, // 0018 CALL R5 6 - 0x80000000, // 0019 RET 0 + 0x880C0126, // 000A GETMBR R3 R0 K38 + 0x8C140109, // 000B GETMET R5 R0 K9 + 0x581C0001, // 000C LDCONST R7 K1 + 0x5C200200, // 000D MOVE R8 R1 + 0x5C240400, // 000E MOVE R9 R2 + 0x5C280600, // 000F MOVE R10 R3 + 0x5C2C0800, // 0010 MOVE R11 R4 + 0x7C140C00, // 0011 CALL R5 6 + 0x80000000, // 0012 RET 0 }) ) ); @@ -1997,11 +2020,11 @@ be_local_closure(class_Leds_ctor, /* name */ /******************************************************************** -** Solidified function: pixels_buffer +** Solidified function: set_gamma ********************************************************************/ -be_local_closure(class_Leds_pixels_buffer, /* name */ +be_local_closure(class_Leds_set_gamma, /* name */ be_nested_proto( - 8, /* nstack */ + 4, /* nstack */ 2, /* argc */ 10, /* varg */ 0, /* has upvals */ @@ -2010,30 +2033,14 @@ be_local_closure(class_Leds_pixels_buffer, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_pixels_buffer, + &be_const_str_set_gamma, &be_const_str_solidified, - ( &(const binstruction[21]) { /* code */ - 0x8C080106, // 0000 GETMET R2 R0 K6 - 0x54120005, // 0001 LDINT R4 6 - 0x7C080400, // 0002 CALL R2 2 - 0x4C0C0000, // 0003 LDNIL R3 - 0x1C0C0203, // 0004 EQ R3 R1 R3 - 0x780E0009, // 0005 JMPF R3 #0010 - 0x600C0015, // 0006 GETGBL R3 G21 - 0x5C100400, // 0007 MOVE R4 R2 - 0x8C140129, // 0008 GETMET R5 R0 K41 - 0x7C140200, // 0009 CALL R5 1 - 0x8C18010F, // 000A GETMET R6 R0 K15 - 0x7C180200, // 000B CALL R6 1 - 0x08140A06, // 000C MUL R5 R5 R6 - 0x7C0C0400, // 000D CALL R3 2 - 0x80040600, // 000E RET 1 R3 - 0x70020003, // 000F JMP #0014 - 0x8C0C032A, // 0010 GETMET R3 R1 K42 - 0x5C140400, // 0011 MOVE R5 R2 - 0x7C0C0400, // 0012 CALL R3 2 - 0x80040200, // 0013 RET 1 R1 - 0x80000000, // 0014 RET 0 + ( &(const binstruction[ 5]) { /* code */ + 0x60080017, // 0000 GETGBL R2 G23 + 0x5C0C0200, // 0001 MOVE R3 R1 + 0x7C080200, // 0002 CALL R2 1 + 0x90020A02, // 0003 SETMBR R0 K5 R2 + 0x80000000, // 0004 RET 0 }) ) ); @@ -2045,37 +2052,39 @@ be_local_closure(class_Leds_pixels_buffer, /* name */ ********************************************************************/ extern const bclass be_class_Leds_ntv; be_local_class(Leds, - 3, + 4, &be_class_Leds_ntv, - be_nested_map(27, + be_nested_map(29, ( (struct bmapnode*) &(const bmapnode[]) { - { be_const_key(leds, -1), be_const_var(1) }, - { be_const_key(create_segment, 25), be_const_closure(class_Leds_create_segment_closure) }, + { be_const_key(pixels_buffer, -1), be_const_closure(class_Leds_pixels_buffer_closure) }, + { be_const_key(get_gamma, 26), be_const_closure(class_Leds_get_gamma_closure) }, { be_const_key(clear, -1), be_const_closure(class_Leds_clear_closure) }, - { be_const_key(begin, -1), be_const_closure(class_Leds_begin_closure) }, - { be_const_key(ctor, 7), be_const_closure(class_Leds_ctor_closure) }, - { be_const_key(assign_rmt, 12), be_const_static_closure(class_Leds_assign_rmt_closure) }, - { be_const_key(to_gamma, 8), be_const_closure(class_Leds_to_gamma_closure) }, - { be_const_key(dirty, -1), be_const_closure(class_Leds_dirty_closure) }, - { be_const_key(matrix, -1), be_const_static_closure(class_Leds_matrix_closure) }, - { be_const_key(pixel_offset, 2), be_const_closure(class_Leds_pixel_offset_closure) }, - { be_const_key(set_gamma, 4), be_const_closure(class_Leds_set_gamma_closure) }, - { be_const_key(get_pixel_color, -1), be_const_closure(class_Leds_get_pixel_color_closure) }, - { be_const_key(pixel_size, -1), be_const_closure(class_Leds_pixel_size_closure) }, - { be_const_key(create_matrix, -1), be_const_closure(class_Leds_create_matrix_closure) }, - { be_const_key(set_bri, 9), be_const_closure(class_Leds_set_bri_closure) }, - { be_const_key(clear_to, -1), be_const_closure(class_Leds_clear_to_closure) }, + { be_const_key(matrix, 13), be_const_static_closure(class_Leds_matrix_closure) }, + { be_const_key(init, 23), be_const_closure(class_Leds_init_closure) }, + { be_const_key(get_animate, -1), be_const_closure(class_Leds_get_animate_closure) }, + { be_const_key(get_pixel_color, 12), be_const_closure(class_Leds_get_pixel_color_closure) }, { be_const_key(set_pixel_color, -1), be_const_closure(class_Leds_set_pixel_color_closure) }, - { be_const_key(gamma, -1), be_const_var(0) }, - { be_const_key(pixel_count, 17), be_const_closure(class_Leds_pixel_count_closure) }, - { be_const_key(get_bri, 13), be_const_closure(class_Leds_get_bri_closure) }, - { be_const_key(get_gamma, -1), be_const_closure(class_Leds_get_gamma_closure) }, - { be_const_key(bri, -1), be_const_var(2) }, + { be_const_key(animate, -1), be_const_var(3) }, { be_const_key(is_dirty, -1), be_const_closure(class_Leds_is_dirty_closure) }, - { be_const_key(can_show, -1), be_const_closure(class_Leds_can_show_closure) }, - { be_const_key(init, 5), be_const_closure(class_Leds_init_closure) }, - { be_const_key(show, -1), be_const_closure(class_Leds_show_closure) }, - { be_const_key(pixels_buffer, -1), be_const_closure(class_Leds_pixels_buffer_closure) }, + { be_const_key(create_segment, 4), be_const_closure(class_Leds_create_segment_closure) }, + { be_const_key(pixel_offset, -1), be_const_closure(class_Leds_pixel_offset_closure) }, + { be_const_key(clear_to, -1), be_const_closure(class_Leds_clear_to_closure) }, + { be_const_key(begin, 0), be_const_closure(class_Leds_begin_closure) }, + { be_const_key(set_animate, -1), be_const_closure(class_Leds_set_animate_closure) }, + { be_const_key(can_show, 24), be_const_closure(class_Leds_can_show_closure) }, + { be_const_key(create_matrix, -1), be_const_closure(class_Leds_create_matrix_closure) }, + { be_const_key(dirty, -1), be_const_closure(class_Leds_dirty_closure) }, + { be_const_key(set_bri, -1), be_const_closure(class_Leds_set_bri_closure) }, + { be_const_key(leds, 15), be_const_var(1) }, + { be_const_key(gamma, -1), be_const_var(0) }, + { be_const_key(bri, 14), be_const_var(2) }, + { be_const_key(get_bri, 11), be_const_closure(class_Leds_get_bri_closure) }, + { be_const_key(to_gamma, 2), be_const_closure(class_Leds_to_gamma_closure) }, + { be_const_key(pixel_count, -1), be_const_closure(class_Leds_pixel_count_closure) }, + { be_const_key(show, 6), be_const_closure(class_Leds_show_closure) }, + { be_const_key(pixel_size, -1), be_const_closure(class_Leds_pixel_size_closure) }, + { be_const_key(ctor, -1), be_const_closure(class_Leds_ctor_closure) }, + { be_const_key(set_gamma, -1), be_const_closure(class_Leds_set_gamma_closure) }, })), (bstring*) &be_const_str_Leds ); diff --git a/lib/libesp32_lvgl/lv_haspmota/src/embedded/lv_haspmota.be b/lib/libesp32_lvgl/lv_haspmota/src/embedded/lv_haspmota.be index 082110dcb481..9d65d6fb6307 100644 --- a/lib/libesp32_lvgl/lv_haspmota/src/embedded/lv_haspmota.be +++ b/lib/libesp32_lvgl/lv_haspmota/src/embedded/lv_haspmota.be @@ -2187,10 +2187,10 @@ class lvh_chart : lvh_obj end def set_series1_color(color) - self._lv_obj.set_series_color(self._ser1, color) + self._lv_obj.set_series_color(self._ser1, self.parse_color(color)) end def set_series2_color(color) - self._lv_obj.set_series_color(self._ser2, color) + self._lv_obj.set_series_color(self._ser2, self.parse_color(color)) end def set_h_div_line_count(_h_div) self._h_div = _h_div diff --git a/lib/libesp32_lvgl/lv_haspmota/src/solidify/solidified_lv_haspmota.h b/lib/libesp32_lvgl/lv_haspmota/src/solidify/solidified_lv_haspmota.h index 3936938da595..be6f8f13467c 100644 --- a/lib/libesp32_lvgl/lv_haspmota/src/solidify/solidified_lv_haspmota.h +++ b/lib/libesp32_lvgl/lv_haspmota/src/solidify/solidified_lv_haspmota.h @@ -9182,8 +9182,8 @@ be_local_class(lvh_span, })), be_str_weak(lvh_span) ); -// compact class 'lvh_chart' ktab size: 24, total: 54 (saved 240 bytes) -static const bvalue be_ktab_class_lvh_chart[24] = { +// compact class 'lvh_chart' ktab size: 25, total: 56 (saved 248 bytes) +static const bvalue be_ktab_class_lvh_chart[25] = { /* K0 */ be_nested_str_weak(_y_min), /* K1 */ be_nested_str_weak(_lv_obj), /* K2 */ be_nested_str_weak(set_next_value), @@ -9195,19 +9195,20 @@ static const bvalue be_ktab_class_lvh_chart[24] = { /* K8 */ be_nested_str_weak(_y_max), /* K9 */ be_nested_str_weak(_ser2), /* K10 */ be_nested_str_weak(set_series_color), - /* K11 */ be_const_int(0), - /* K12 */ be_nested_str_weak(_h_div), - /* K13 */ be_const_int(3), - /* K14 */ be_nested_str_weak(_v_div), - /* K15 */ be_nested_str_weak(set_update_mode), - /* K16 */ be_nested_str_weak(CHART_UPDATE_MODE_SHIFT), - /* K17 */ be_nested_str_weak(add_series), - /* K18 */ be_nested_str_weak(color), - /* K19 */ be_const_int(15615044), - /* K20 */ be_const_int(4517444), - /* K21 */ be_nested_str_weak(_val), - /* K22 */ be_nested_str_weak(add_point), - /* K23 */ be_nested_str_weak(set_div_line_count), + /* K11 */ be_nested_str_weak(parse_color), + /* K12 */ be_const_int(0), + /* K13 */ be_nested_str_weak(_h_div), + /* K14 */ be_const_int(3), + /* K15 */ be_nested_str_weak(_v_div), + /* K16 */ be_nested_str_weak(set_update_mode), + /* K17 */ be_nested_str_weak(CHART_UPDATE_MODE_SHIFT), + /* K18 */ be_nested_str_weak(add_series), + /* K19 */ be_nested_str_weak(color), + /* K20 */ be_const_int(15615044), + /* K21 */ be_const_int(4517444), + /* K22 */ be_nested_str_weak(_val), + /* K23 */ be_nested_str_weak(add_point), + /* K24 */ be_nested_str_weak(set_div_line_count), }; @@ -9360,7 +9361,7 @@ be_local_closure(class_lvh_chart_add_point2, /* name */ ********************************************************************/ be_local_closure(class_lvh_chart_set_series1_color, /* name */ be_nested_proto( - 6, /* nstack */ + 8, /* nstack */ 2, /* argc */ 10, /* varg */ 0, /* has upvals */ @@ -9371,13 +9372,15 @@ be_local_closure(class_lvh_chart_set_series1_color, /* name */ &be_ktab_class_lvh_chart, /* shared constants */ be_str_weak(set_series1_color), &be_const_str_solidified, - ( &(const binstruction[ 6]) { /* code */ + ( &(const binstruction[ 8]) { /* code */ 0x88080101, // 0000 GETMBR R2 R0 K1 0x8C08050A, // 0001 GETMET R2 R2 K10 0x88100103, // 0002 GETMBR R4 R0 K3 - 0x5C140200, // 0003 MOVE R5 R1 - 0x7C080600, // 0004 CALL R2 3 - 0x80000000, // 0005 RET 0 + 0x8C14010B, // 0003 GETMET R5 R0 K11 + 0x5C1C0200, // 0004 MOVE R7 R1 + 0x7C140400, // 0005 CALL R5 2 + 0x7C080600, // 0006 CALL R2 3 + 0x80000000, // 0007 RET 0 }) ) ); @@ -9389,7 +9392,7 @@ be_local_closure(class_lvh_chart_set_series1_color, /* name */ ********************************************************************/ be_local_closure(class_lvh_chart_set_series2_color, /* name */ be_nested_proto( - 6, /* nstack */ + 8, /* nstack */ 2, /* argc */ 10, /* varg */ 0, /* has upvals */ @@ -9400,13 +9403,15 @@ be_local_closure(class_lvh_chart_set_series2_color, /* name */ &be_ktab_class_lvh_chart, /* shared constants */ be_str_weak(set_series2_color), &be_const_str_solidified, - ( &(const binstruction[ 6]) { /* code */ + ( &(const binstruction[ 8]) { /* code */ 0x88080101, // 0000 GETMBR R2 R0 K1 0x8C08050A, // 0001 GETMET R2 R2 K10 0x88100109, // 0002 GETMBR R4 R0 K9 - 0x5C140200, // 0003 MOVE R5 R1 - 0x7C080600, // 0004 CALL R2 3 - 0x80000000, // 0005 RET 0 + 0x8C14010B, // 0003 GETMET R5 R0 K11 + 0x5C1C0200, // 0004 MOVE R7 R1 + 0x7C140400, // 0005 CALL R5 2 + 0x7C080600, // 0006 CALL R2 3 + 0x80000000, // 0007 RET 0 }) ) ); @@ -9462,32 +9467,32 @@ be_local_closure(class_lvh_chart_post_init, /* name */ be_str_weak(post_init), &be_const_str_solidified, ( &(const binstruction[32]) { /* code */ - 0x9002010B, // 0000 SETMBR R0 K0 K11 + 0x9002010C, // 0000 SETMBR R0 K0 K12 0x54060063, // 0001 LDINT R1 100 0x90021001, // 0002 SETMBR R0 K8 R1 - 0x9002190D, // 0003 SETMBR R0 K12 K13 + 0x90021B0E, // 0003 SETMBR R0 K13 K14 0x54060004, // 0004 LDINT R1 5 - 0x90021C01, // 0005 SETMBR R0 K14 R1 + 0x90021E01, // 0005 SETMBR R0 K15 R1 0x88040101, // 0006 GETMBR R1 R0 K1 - 0x8C04030F, // 0007 GETMET R1 R1 K15 + 0x8C040310, // 0007 GETMET R1 R1 K16 0xB80E0C00, // 0008 GETNGBL R3 K6 - 0x880C0710, // 0009 GETMBR R3 R3 K16 + 0x880C0711, // 0009 GETMBR R3 R3 K17 0x7C040400, // 000A CALL R1 2 0x88040101, // 000B GETMBR R1 R0 K1 - 0x8C040311, // 000C GETMET R1 R1 K17 + 0x8C040312, // 000C GETMET R1 R1 K18 0xB80E0C00, // 000D GETNGBL R3 K6 - 0x8C0C0712, // 000E GETMET R3 R3 K18 - 0x58140013, // 000F LDCONST R5 K19 + 0x8C0C0713, // 000E GETMET R3 R3 K19 + 0x58140014, // 000F LDCONST R5 K20 0x7C0C0400, // 0010 CALL R3 2 0xB8120C00, // 0011 GETNGBL R4 K6 0x88100907, // 0012 GETMBR R4 R4 K7 0x7C040600, // 0013 CALL R1 3 0x90020601, // 0014 SETMBR R0 K3 R1 0x88040101, // 0015 GETMBR R1 R0 K1 - 0x8C040311, // 0016 GETMET R1 R1 K17 + 0x8C040312, // 0016 GETMET R1 R1 K18 0xB80E0C00, // 0017 GETNGBL R3 K6 - 0x8C0C0712, // 0018 GETMET R3 R3 K18 - 0x58140014, // 0019 LDCONST R5 K20 + 0x8C0C0713, // 0018 GETMET R3 R3 K19 + 0x58140015, // 0019 LDCONST R5 K21 0x7C0C0400, // 001A CALL R3 2 0xB8120C00, // 001B GETNGBL R4 K6 0x88100907, // 001C GETMBR R4 R4 K7 @@ -9542,8 +9547,8 @@ be_local_closure(class_lvh_chart_set_val, /* name */ be_str_weak(set_val), &be_const_str_solidified, ( &(const binstruction[ 5]) { /* code */ - 0x90022A01, // 0000 SETMBR R0 K21 R1 - 0x8C080116, // 0001 GETMET R2 R0 K22 + 0x90022C01, // 0000 SETMBR R0 K22 R1 + 0x8C080117, // 0001 GETMET R2 R0 K23 0x5C100200, // 0002 MOVE R4 R1 0x7C080400, // 0003 CALL R2 2 0x80000000, // 0004 RET 0 @@ -9570,11 +9575,11 @@ be_local_closure(class_lvh_chart_set_v_div_line_count, /* name */ be_str_weak(set_v_div_line_count), &be_const_str_solidified, ( &(const binstruction[ 7]) { /* code */ - 0x90021C01, // 0000 SETMBR R0 K14 R1 + 0x90021E01, // 0000 SETMBR R0 K15 R1 0x88080101, // 0001 GETMBR R2 R0 K1 - 0x8C080517, // 0002 GETMET R2 R2 K23 - 0x8810010C, // 0003 GETMBR R4 R0 K12 - 0x8814010E, // 0004 GETMBR R5 R0 K14 + 0x8C080518, // 0002 GETMET R2 R2 K24 + 0x8810010D, // 0003 GETMBR R4 R0 K13 + 0x8814010F, // 0004 GETMBR R5 R0 K15 0x7C080600, // 0005 CALL R2 3 0x80000000, // 0006 RET 0 }) @@ -9600,11 +9605,11 @@ be_local_closure(class_lvh_chart_set_h_div_line_count, /* name */ be_str_weak(set_h_div_line_count), &be_const_str_solidified, ( &(const binstruction[ 7]) { /* code */ - 0x90021801, // 0000 SETMBR R0 K12 R1 + 0x90021A01, // 0000 SETMBR R0 K13 R1 0x88080101, // 0001 GETMBR R2 R0 K1 - 0x8C080517, // 0002 GETMET R2 R2 K23 - 0x8810010C, // 0003 GETMBR R4 R0 K12 - 0x8814010E, // 0004 GETMBR R5 R0 K14 + 0x8C080518, // 0002 GETMET R2 R2 K24 + 0x8810010D, // 0003 GETMBR R4 R0 K13 + 0x8814010F, // 0004 GETMBR R5 R0 K15 0x7C080600, // 0005 CALL R2 3 0x80000000, // 0006 RET 0 }) diff --git a/pio-tools/post_esp32.py b/pio-tools/post_esp32.py index 5fe75bf2b2b3..e986a5021b5b 100644 --- a/pio-tools/post_esp32.py +++ b/pio-tools/post_esp32.py @@ -45,19 +45,20 @@ chip = env.get("BOARD_MCU") mcu_build_variant = env.BoardConfig().get("build.variant", "").lower() flag_custom_sdkconfig = config.has_option("env:"+env["PIOENV"], "custom_sdkconfig") +flag_board_sdkconfig = env.BoardConfig().get("espidf.custom_sdkconfig", "") # Copy safeboots firmwares in place when running in Github github_actions = os.getenv('GITHUB_ACTIONS') extra_flags = ''.join([element.replace("-D", " ") for element in env.BoardConfig().get("build.extra_flags", "")]) build_flags = ''.join([element.replace("-D", " ") for element in env.GetProjectOption("build_flags")]) -if ("CORE32SOLO1" in extra_flags or "FRAMEWORK_ARDUINO_SOLO1" in build_flags) and flag_custom_sdkconfig == False: +if ("CORE32SOLO1" in extra_flags or "FRAMEWORK_ARDUINO_SOLO1" in build_flags) and flag_custom_sdkconfig == False and flag_board_sdkconfig == "": FRAMEWORK_DIR = platform.get_package_dir("framework-arduino-solo1") if github_actions and os.path.exists("./firmware/firmware"): shutil.copytree("./firmware/firmware", "/home/runner/.platformio/packages/framework-arduino-solo1/variants/tasmota", dirs_exist_ok=True) if variants_dir: shutil.copytree("./firmware/firmware", variants_dir, dirs_exist_ok=True) -elif ("CORE32ITEAD" in extra_flags or "FRAMEWORK_ARDUINO_ITEAD" in build_flags) and flag_custom_sdkconfig == False: +elif ("CORE32ITEAD" in extra_flags or "FRAMEWORK_ARDUINO_ITEAD" in build_flags) and flag_custom_sdkconfig == False and flag_board_sdkconfig == "": FRAMEWORK_DIR = platform.get_package_dir("framework-arduino-ITEAD") if github_actions and os.path.exists("./firmware/firmware"): shutil.copytree("./firmware/firmware", "/home/runner/.platformio/packages/framework-arduino-ITEAD/variants/tasmota", dirs_exist_ok=True) diff --git a/pio-tools/solidify-from-url.py b/pio-tools/solidify-from-url.py index af4bfdc040db..dae41dfc1f72 100644 --- a/pio-tools/solidify-from-url.py +++ b/pio-tools/solidify-from-url.py @@ -58,8 +58,7 @@ def addEntryToModtab(source): class_name = None is_module = False - - pattern = (r'''(?<=module\()[^"].*''') # module?? + pattern = (r'''(?<=module\([\"\']).*[\"\']''') # module?? result = re.findall(pattern,code) if len(result) > 0: class_name = result[0].replace("'","").replace('"','').replace(")","") diff --git a/tasmota/displaydesc/ST7701_480x480_WS_4inch.ini b/tasmota/displaydesc/ST7701_480x480_WS_4inch.ini new file mode 100644 index 000000000000..a60d9380f995 --- /dev/null +++ b/tasmota/displaydesc/ST7701_480x480_WS_4inch.ini @@ -0,0 +1,68 @@ +:H,ST7701,480,480,16,RGB,40,39,38,41,-1,5,45,48,47,21,14,13,12,11,10,9,46,3,8,18,17,6 +:V,1,10,8,50,1,10,8,20,0 +:S,2,1,1,0,40,20 +:IS,2,1,42,-1 +11,80 +FF,5,77,01,00,00,10 +C0,2,3B,00 +C1,2,0D,02 +C2,2,21,08 +CD,1,08 +B0,10,00,11,18,0E,11,06,07,08,07,22,04,12,0F,AA,31,18 +B1,10,00,11,19,0E,12,07,08,08,08,22,04,11,11,A9,32,18 +FF,5,77,01,00,00,11 +B0,1,60 +B1,1,30 +B2,1,87 +B3,1,80 +B5,1,49 +B7,1,85 +B8,1,21 +C1,1,78 +C2,1,78,20 +E0,3,00,1B,02 +E1,B,08,A0,00,00,07,A0,00,00,00,44,44 +E2,C,11,11,44,44,ED,A0,00,00,EC,A0,00,00 +E3,4,00,00,11,11 +E4,2,44,44 +E5,10,0A,E9,D8,A0,0C,EB,D8,A0,0E,ED,D8,A0,10,EF,D8,A0 +E6,4,00,00,11,11 +E7,2,44,44 +E8,10,09,E8,D8,A0,0B,EA,D8,A0,0D,EC,D8,A0,0F,EE,D8,A0 +EB,7,02,00,E4,E4,88,00,40 +EC,2,3C,00 +ED,10,AB,89,76,54,02,FF,FF,FF,FF,FF,FF,20,45,67,98,BA +FF,5,77,01,00,00,00 +36,1,04 +3A,1,66 +21,80 +29,0 +:B,100,02 +:UTI,GT911,I1,5d,-1,-1 +RDWM 8140 4 +MV 0 1 +CPR 39 +RTF +MV 1 1 +CPR 31 +RTF +MV 2 1 +CPR 31 +RTF +RT +:UTT +RDW 814E +MV 0 1 +AND 80 +CPR 80 +RTF +RDWM 8150 8 +WRW 814E 00 +RT +:UTX +MV 0 3 +RT +:UTY +MV 2 3 +RT +# diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index 48d6d20ff8bd..bfff6c971430 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -968,8 +968,8 @@ const char HTTP_SNS_F_VOLTAGE[] PROGMEM = "{s}%s " D_VOLTAGE "{ const char HTTP_SNS_F_CURRENT[] PROGMEM = "{s}%s " D_CURRENT "{m}%*_f " D_UNIT_AMPERE "{e}"; const char HTTP_SNS_F_CURRENT_MA[] PROGMEM = "{s}%s " D_CURRENT "{m}%*_f " D_UNIT_MILLIAMPERE "{e}"; const char HTTP_SNS_F_DISTANCE_CM[] PROGMEM = "{s}%s " D_DISTANCE "{m}%1_f " D_UNIT_CENTIMETER "{e}"; -const char HTTP_SNS_F_NOX[] PROGMEM = "{s}%s " D_NOX "{m}%*_f " "{e}"; -const char HTTP_SNS_F_VOC[] PROGMEM = "{s}%s " D_VOC "{m}%*_f " "{e}"; +const char HTTP_SNS_F_NOX[] PROGMEM = "{s}%s " D_NOX "{m}%*_f" "{e}"; +const char HTTP_SNS_F_VOC[] PROGMEM = "{s}%s " D_VOC "{m}%*_f" "{e}"; const char HTTP_SNS_F_ABS_HUM[] PROGMEM = "{s}%s " D_ABSOLUTE_HUMIDITY "{m}%*_f " D_UNIT_GRAM_PER_CUBIC_METER "{e}"; const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s " D_UNIT_PERCENT "{e}"; @@ -987,17 +987,17 @@ const char HTTP_SNS_MOISTURE[] PROGMEM = "{s}%s " D_MOISTURE "{ const char HTTP_SNS_RANGE_CHR[] PROGMEM = "{s}%s " D_RANGE "{m}%s" "{e}"; const char HTTP_SNS_RANGE[] PROGMEM = "{s}%s " D_RANGE "{m}%d" "{e}"; const char HTTP_SNS_HALL_EFFECT[] PROGMEM = "{s}%s " D_HALL_EFFECT "{m}%d" "{e}"; -const char HTTP_SNS_PH[] PROGMEM = "{s}%s " D_PH "{m}%s " "{e}"; -const char HTTP_SNS_MQ[] PROGMEM = "{s}" D_MQ"-%s" "{m}%s " D_UNIT_PARTS_PER_MILLION "{e}"; +const char HTTP_SNS_PH[] PROGMEM = "{s}%s " D_PH "{m}%s" "{e}"; +const char HTTP_SNS_MQ[] PROGMEM = "{s}" D_MQ "-%s" "{m}%s " D_UNIT_PARTS_PER_MILLION "{e}"; const char HTTP_SNS_ORP[] PROGMEM = "{s}%s " D_ORP "{m}%s " D_UNIT_MILLIVOLT "{e}"; const char HTTP_SNS_EC[] PROGMEM = "{s}%s " D_EC "{m}%s " D_UNIT_MICROSIEMENS_PER_CM "{e}"; const char HTTP_SNS_O2[] PROGMEM = "{s}%s " D_O2 "{m}%s " D_UNIT_PERCENT "{e}"; const char HTTP_SNS_LITERS[] PROGMEM = "{s}%s " D_VOLUME "{m}%s " D_UNIT_LITERS "{e}"; const char HTTP_SNS_LPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_LITERS_PER_MIN "{e}"; const char HTTP_SNS_DO[] PROGMEM = "{s}%s " D_DO "{m}%s " D_UNIT_MILIGRAMS_PER_LITER "{e}"; -const char HTTP_SNS_COLOR_RED[] PROGMEM = "{s}%s " D_COLOR_RED "{m}%u " "{e}"; -const char HTTP_SNS_COLOR_GREEN[] PROGMEM = "{s}%s " D_COLOR_GREEN "{m}%u " "{e}"; -const char HTTP_SNS_COLOR_BLUE[] PROGMEM = "{s}%s " D_COLOR_BLUE "{m}%u " "{e}"; +const char HTTP_SNS_COLOR_RED[] PROGMEM = "{s}%s " D_COLOR_RED "{m}%u" "{e}"; +const char HTTP_SNS_COLOR_GREEN[] PROGMEM = "{s}%s " D_COLOR_GREEN "{m}%u" "{e}"; +const char HTTP_SNS_COLOR_BLUE[] PROGMEM = "{s}%s " D_COLOR_BLUE "{m}%u" "{e}"; const char HTTP_SNS_MILLILITERS[] PROGMEM = "{s}%s " D_VOLUME "{m}%s " D_UNIT_MILLILITERS "{e}"; const char HTTP_SNS_GAS[] PROGMEM = "{s}%s " D_GAS "{m}%d " D_UNIT_PERCENT "LEL{e}"; const char HTTP_SNS_SOC[] PROGMEM = "{s}%s " D_SOC "{m}%d " D_UNIT_PERCENT "{e}"; @@ -1022,7 +1022,7 @@ const char HTTP_SNS_MAX_POWER[] PROGMEM = "{s}" D_MAX_POWER const char HTTP_SNS_POWER_TOTAL[] PROGMEM = "{s}" D_POWERUSAGE_ACTIVE_TOTAL "{m}%s " D_UNIT_WATT "{e}"; const char HTTP_SNS_POWERUSAGE_APPARENT[] PROGMEM = "{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}"; const char HTTP_SNS_POWERUSAGE_REACTIVE[] PROGMEM = "{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}"; -const char HTTP_SNS_POWER_FACTOR[] PROGMEM = "{s}" D_POWER_FACTOR "{m}%s {e}"; +const char HTTP_SNS_POWER_FACTOR[] PROGMEM = "{s}" D_POWER_FACTOR "{m}%s" "{e}"; const char HTTP_SNS_ENERGY_TODAY[] PROGMEM = "{s}" D_ENERGY_TODAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; const char HTTP_SNS_ENERGY_YESTERDAY[] PROGMEM = "{s}" D_ENERGY_YESTERDAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; const char HTTP_SNS_ENERGY_TOTAL[] PROGMEM = "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; diff --git a/tasmota/include/tasmota.h b/tasmota/include/tasmota.h index fccfc5b660df..79a3cc486ce1 100644 --- a/tasmota/include/tasmota.h +++ b/tasmota/include/tasmota.h @@ -40,7 +40,7 @@ const uint32_t POWER_SIZE = 32; // Power (relay) bit count #ifdef ESP8266 const uint8_t MAX_RELAYS = 8; // Max number of relays selectable on GPIO -const uint8_t MAX_INTERLOCKS = 4; // Max number of interlock groups (up to MAX_INTERLOCKS_SET) +const uint8_t MAX_INTERLOCKS = 16; // Max number of interlock groups (up to MAX_INTERLOCKS_SET) const uint8_t MAX_SWITCHES = 8; // Max number of switches selectable on GPIO const uint8_t MAX_KEYS = 8; // Max number of keys or buttons selectable on GPIO #endif // ESP8266 @@ -279,13 +279,13 @@ const uint32_t LOOP_SLEEP_DELAY = 50; // Lowest number of milliseconds to #define KNX_SLOT3 28 #define KNX_SLOT4 29 #define KNX_SLOT5 30 -#define KNX_SLOT6 31 -#define KNX_SLOT7 32 -#define KNX_SLOT8 33 -#define KNX_SLOT9 34 -#define KNX_SCENE 35 -#define KNX_DIMMER 36 // aka DPT_Scaling 5.001 -#define KNX_COLOUR 37 // aka DPT_Colour_RGB 232.600 or DPT_Colour_RGBW 251.600 +#define KNX_SCENE 31 +#define KNX_DIMMER 32 // aka DPT_Scaling 5.001 +#define KNX_COLOUR 33 // aka DPT_Colour_RGB 232.600 or DPT_Colour_RGBW 251.600 +#define KNX_SLOT6 34 +#define KNX_SLOT7 35 +#define KNX_SLOT8 36 +#define KNX_SLOT9 37 #define KNX_MAX_device_param 37 #define MAX_KNXTX_CMNDS 9 diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index dceb236ecad6..b42ddba1bc76 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -196,8 +196,8 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t counter_both_edges : 1; // bit 13 (v13.3.0.5) - SetOption159 - (Counter) Enable counting on both rising and falling edge (1) uint32_t ld2410_use_pin : 1; // bit 14 (v14.3.0.2) - SetOption160 - (LD2410) Disable generate moving event by sensor report - use LD2410 out pin for events (1) uint32_t gui_no_state_text : 1; // bit 15 (v14.3.0.7) - SetOption161 - (GUI) Disable display of state text (1) - uint32_t spare16 : 1; // bit 16 - uint32_t spare17 : 1; // bit 17 + uint32_t no_export_energy_today : 1; // bit 16 (v14.3.0.7) - SetOption162 - (Energy) Do not add export energy to energy today (1) + uint32_t gui_device_name : 1; // bit 17 (v14.4.1.1) - SetOption163 - (GUI) Disable display of GUI device name (1) uint32_t spare18 : 1; // bit 18 uint32_t spare19 : 1; // bit 19 uint32_t spare20 : 1; // bit 20 diff --git a/tasmota/include/tasmota_version.h b/tasmota/include/tasmota_version.h index 8e6cd813814a..07138cb2d4c3 100644 --- a/tasmota/include/tasmota_version.h +++ b/tasmota/include/tasmota_version.h @@ -22,6 +22,6 @@ #define TASMOTA_SHA_SHORT // Filled by Github sed -const uint32_t TASMOTA_VERSION = 0x0E030007; // 14.3.0.7 +const uint32_t TASMOTA_VERSION = 0x0E040101; // 14.4.1.1 #endif // _TASMOTA_VERSION_H_ diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index 0042913a8409..e0a26ee7a5a2 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Luggehalte" #define D_AP "AP" // Access Point #define D_AS "as" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -280,16 +281,16 @@ #define D_CONSOLE "Konsole" #define D_CONFIRM_RESTART "Bevestig weer te begin" -#define D_CONFIGURE_MODULE "Stel Module in" -#define D_CONFIGURE_WIFI "Stel WiFi in" -#define D_CONFIGURE_MQTT "Stel MQTT in" -#define D_CONFIGURE_DOMOTICZ "Stel Domoticz in" -#define D_CONFIGURE_LOGGING "Stel Logging in" -#define D_CONFIGURE_OTHER "Stel Ander in" +#define D_CONFIGURE_MODULE "Module" +#define D_CONFIGURE_WIFI "WiFi" +#define D_CONFIGURE_MQTT "MQTT" +#define D_CONFIGURE_DOMOTICZ "Domoticz" +#define D_CONFIGURE_LOGGING "Logging" +#define D_CONFIGURE_OTHER "Ander" #define D_CONFIRM_RESET_CONFIGURATION "Bevestig die herstel van die konfigurasie" -#define D_RESET_CONFIGURATION "Stel die konfigurasie terug" -#define D_BACKUP_CONFIGURATION "Rugsteun die konfigurasie" -#define D_RESTORE_CONFIGURATION "Herstel die konfigurasie" +#define D_RESET_CONFIGURATION "Stel terug" +#define D_BACKUP_CONFIGURATION "Rugsteun" +#define D_RESTORE_CONFIGURATION "Herstel" #define D_START_RESTORE "Start restore" #define D_MAIN_MENU "Hoofkieslys" @@ -356,7 +357,7 @@ #define D_SINGLE_DEVICE "enkele toestel" #define D_MULTI_DEVICE "multi toestel" -#define D_CONFIGURE_TEMPLATE "Konfigureer sjabloon" +#define D_CONFIGURE_TEMPLATE "sjabloon" #define D_TEMPLATE_PARAMETERS "Sjabloon parameters" #define D_TEMPLATE_NAME "Naam" #define D_BASE_TYPE "Gebaseer op" @@ -386,10 +387,10 @@ #define D_FLASH_CHIP_SIZE "Flash Size" #define D_FREE_PROGRAM_SPACE "Vrye program grootte" -#define D_UPGRADE_BY_WEBSERVER "Opgradeer per webbediener" +#define D_UPGRADE_BY_WEBSERVER "Per webbediener" #define D_OTA_URL "OTA Url" #define D_START_UPGRADE "Begin opgradering" -#define D_UPGRADE_BY_FILE_UPLOAD "Gradeer op volgens lêeroplaai" +#define D_UPGRADE_BY_FILE_UPLOAD "Volgens lêeroplaai" #define D_UPLOAD_FACTORY "Switching to safeboot partition" #define D_UPLOAD_STARTED "Oplaai begin" #define D_UPGRADE_STARTED "Opgradering is begin" @@ -463,7 +464,7 @@ #define D_DOMOTICZ_UPDATE_TIMER "Dateer tydopdatering op" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Stel Timer in" +#define D_CONFIGURE_TIMER "Timer" #define D_TIMER_PARAMETERS "Timer-parameters" #define D_TIMER_ENABLE "Aktiveer timers" #define D_TIMER_ARM "Aktiveer" @@ -474,7 +475,7 @@ #define D_TIMER_ACTION "Aksie" // xdrv_10_knx.ino -#define D_CONFIGURE_KNX "Stel KNX op" +#define D_CONFIGURE_KNX "KNX" #define D_KNX_PARAMETERS "KNX-parameters" #define D_KNX_GENERAL_CONFIG "Algemene" #define D_KNX_PHYSICAL_ADDRESS "Fisiese adres" @@ -546,7 +547,7 @@ #define D_DOMOTICZ_SHUTTER "Luik" // xdrv_28_pcf8574.ino -#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_CONFIGURE_PCF8574 "PCF8574" #define D_PCF8574_PARAMETERS "PCF8574 parameters" #define D_INVERT_PORTS "Keer poorte om" #define D_DEVICE "Toestel" @@ -571,10 +572,10 @@ #define D_THERMOSTAT_AUTOTUNE_HYBRID "Autotune (Hybrid)" // xdrv_79_esp32_ble.ino -#define D_CONFIGURE_BLE "Configure BLE" +#define D_CONFIGURE_BLE "BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" @@ -627,7 +628,7 @@ #define D_HX_CAL_DONE "Gekalibreer" #define D_HX_CAL_FAIL "Kon nie kalibreer nie" #define D_RESET_HX711 "Stel die skaal terug" -#define D_CONFIGURE_HX711 "Stel skaal op" +#define D_CONFIGURE_HX711 "skaal" #define D_HX711_PARAMETERS "Skaal parameters" #define D_ITEM_WEIGHT "Gewig van die item" #define D_REFERENCE_WEIGHT "Verwysingsgewig" diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index e4adb6dbb68b..f0b31779860c 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Качество на въздуха" #define D_AP "Точка за достъп" // Access Point #define D_AS "като" +#define D_AT "at" #define D_AUTO "АВТОМАТИЧНО" #define D_BATTERY "Battery" #define D_BATT "Бат." // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/ca_AD.h b/tasmota/language/ca_AD.h index 5c4638ddc340..abcb53f07814 100644 --- a/tasmota/language/ca_AD.h +++ b/tasmota/language/ca_AD.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Qualitat Aire" #define D_AP "PA" // Access Point #define D_AS "com" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Bat" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index f09bb97d323d..d776d111191e 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Kvalita vzduchu" #define D_AP "AP" // Access Point #define D_AS "jako" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index 04096ee3c58a..4c43ec3256f9 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v14.1.0.4 - Last update 28.07.2024 + * Updated until v14.3.0.7 - Last update 08.12.2024 \*********************************************************************/ //#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Luftqualität" #define D_AP "AP" // Access Point #define D_AS "als" +#define D_AT "an" #define D_AUTO "AUTO" #define D_BATTERY "Batterie" #define D_BATT "Batt" // Short for Battery @@ -259,18 +260,18 @@ #define D_NOSCRIPT "JavaScript aktivieren um Tasmota benutzen zu können" #define D_SAFEBOOT "SAFEBOOT" #define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "MINIMUM-Firmware
bitte upgraden" -#define D_WEBSERVER_ACTIVE_ON "Webserver aktiv bei" +#define D_WEBSERVER_ACTIVE_ON "Webserver aktiv" #define D_WITH_IP_ADDRESS "mit IP-Adresse" #define D_WEBSERVER_STOPPED "Webserver angehalten" #define D_FILE_NOT_FOUND "Datei nicht gefunden" #define D_REDIRECTED "umgeleitet zum Captive Portal" #define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "WLAN-Manager AccessPoint gesetzt und behält Station" #define D_WIFIMANAGER_SET_ACCESSPOINT "WLAN-Manager AccessPoint gesetzt" -#define D_TRYING_TO_CONNECT "Versuche mit Netzwerk zu verbinden" +#define D_TRYING_TO_CONNECT "Verbindungsversuch mit Netzwerk" #define D_RESTART_IN "Neustart in" #define D_SECONDS "Sekunden" -#define D_DEVICE_WILL_RESTART "Gerät wird jetzt neu gestartet" +#define D_DEVICE_WILL_RESTART "Neustart" #define D_BUTTON_TOGGLE "An/Aus" #define D_CONFIGURATION "Einstellungen" #define D_INFORMATION "Informationen" @@ -280,68 +281,68 @@ #define D_CONSOLE "Konsole" #define D_CONFIRM_RESTART "Wirklich neustarten?" -#define D_CONFIGURE_MODULE "Geräteeinstellungen" -#define D_CONFIGURE_WIFI "WLAN-Einstellungen" -#define D_CONFIGURE_MQTT "MQTT-Einstellungen" -#define D_CONFIGURE_DOMOTICZ "Domoticz-Einstellungen" -#define D_CONFIGURE_LOGGING "Logging-Einstellungen" -#define D_CONFIGURE_OTHER "Weitere Einstellungen" -#define D_CONFIRM_RESET_CONFIGURATION "Zurücksetzen der Konfiguration bestätigen" -#define D_RESET_CONFIGURATION "Konfiguration zurücksetzen" -#define D_BACKUP_CONFIGURATION "Konfiguration sichern" -#define D_RESTORE_CONFIGURATION "Konfiguration wiederherstellen" +#define D_CONFIGURE_MODULE "Gerät" +#define D_CONFIGURE_WIFI "WLAN" +#define D_CONFIGURE_MQTT "MQTT" +#define D_CONFIGURE_DOMOTICZ "Domoticz" +#define D_CONFIGURE_LOGGING "Logging" +#define D_CONFIGURE_OTHER "Erweitert" +#define D_CONFIRM_RESET_CONFIGURATION "Zurücksetzen bestätigen" +#define D_RESET_CONFIGURATION "Zurücksetzen" +#define D_BACKUP_CONFIGURATION "Sichern" +#define D_RESTORE_CONFIGURATION "Wiederherstellen" #define D_START_RESTORE "Wiederherstellung starten" #define D_MAIN_MENU "Hauptmenü" -#define D_MODULE_PARAMETERS "Geräteeinstellungen" -#define D_MODULE_TYPE "Gerätetyp" +#define D_MODULE_PARAMETERS "Parameter" +#define D_MODULE_TYPE "Typ" #define D_PULLUP_ENABLE "Pull-up aktiv" #define D_ADC "ADC" #define D_GPIO "GPIO" #define D_SERIAL_IN "serieller Eingang [serial in]" #define D_SERIAL_OUT "serieller Ausgang [serial out]" -#define D_WIFI_PARAMETERS "WLAN-Einstellungen" -#define D_SCAN_FOR_WIFI_NETWORKS "WLAN-Netzwerk suchen und auswählen" +#define D_WIFI_PARAMETERS "WLAN" +#define D_SCAN_FOR_WIFI_NETWORKS "WLAN suchen" #define D_SCAN_DONE "Suche abgeschlossen" #define D_NO_NETWORKS_FOUND "Kein Netzwerk gefunden" #define D_REFRESH_TO_SCAN_AGAIN "Aktualisieren, um erneut zu suchen" #define D_DUPLICATE_ACCESSPOINT "AccessPoint duplizieren" -#define D_SKIPPING_LOW_QUALITY "überspringe wegen niedriger Qualität" +#define D_SKIPPING_LOW_QUALITY "WLAN Signal zu schwach" #define D_MODE "Mode" #define D_RSSI "RSSI" #define D_WEP "WEP" #define D_WPA_PSK "WPA-PSK" #define D_WPA2_PSK "WPA2-PSK" #define D_AP1_SSID "WLAN 1 - SSID" -#define D_AP1_SSID_HELP "WiFi Netzwerk auswählen oder eingeben" +#define D_AP1_SSID_HELP "WLAN auswählen oder eingeben" #define D_AP2_SSID "WLAN 2 - SSID" -#define D_AP2_SSID_HELP "alternatives WiFi Netzwerk eingeben" -#define D_AP_PASSWORD "WLAN - Passwort" -#define D_AP_PASSWORD_HELP "WiFi Passwort eingeben" -#define D_SELECT_YOUR_WIFI_NETWORK "WiFi Netzwerk auswählen" -#define D_SHOW_MORE_WIFI_NETWORKS "Suche nach WiFi Netzwerken" +#define D_AP2_SSID_HELP "alternatives WLAN" +#define D_AP_PASSWORD "Passwort" +#define D_AP_PASSWORD_HELP "Passwort eingeben" +#define D_SELECT_YOUR_WIFI_NETWORK "WLAN auswählen" +#define D_SHOW_MORE_WIFI_NETWORKS "Suche nach WLAN" #define D_SHOW_MORE_OPTIONS "Mehr Optionen" -#define D_CHECK_CREDENTIALS "Bitte SSID/Passwort überprüfen" -#define D_SUCCESSFUL_WIFI_CONNECTION "mit Wifi verbunden" -#define D_NOW_YOU_CAN_CLOSE_THIS_WINDOW "Das Fenster kann geschlossen werden" -#define D_REDIRECTING_TO_NEW_IP "Umleitung zur neuen Geräte IP-Adresse" +#define D_CHECK_CREDENTIALS "Bitte SSID/Passwort prüfen" +#define D_SUCCESSFUL_WIFI_CONNECTION "mit WLAN verbunden" +#define D_NOW_YOU_CAN_CLOSE_THIS_WINDOW "Bitte Fenster schließen" +#define D_REDIRECTING_TO_NEW_IP "Umleitung zur IP-Adresse" -#define D_MQTT_PARAMETERS "MQTT-Einstellungen" +#define D_MQTT_PARAMETERS "MQTT" #define D_CLIENT "Client" #define D_FULL_TOPIC "Full Topic" -#define D_LOGGING_PARAMETERS "Logging-Einstellungen" -#define D_SERIAL_LOG_LEVEL "Seriell-Log Level" -#define D_MQTT_LOG_LEVEL "MQTT-Log Level" -#define D_WEB_LOG_LEVEL "Web-Log Level" -#define D_SYS_LOG_LEVEL "Syslog Level" +#define D_LOGGING_PARAMETERS "Logging" +#define D_SERIAL_LOG_LEVEL "Seriell" +#define D_MQTT_LOG_LEVEL "MQTT" +#define D_WEB_LOG_LEVEL "Web" +#define D_SYS_LOG_LEVEL "Syslog" #define D_MORE_DEBUG "Mehr Details" #define D_SYSLOG_HOST "Syslog Host" #define D_SYSLOG_PORT "Syslog Port" #define D_TELEMETRY_PERIOD "Telemetrieperiode" -#define D_OTHER_PARAMETERS "Weitere Einstellungen" +#define D_OTHER_PARAMETERS "Einstellungen" #define D_TEMPLATE "Vorlage" #define D_ACTIVATE "Aktivieren" #define D_DEVICE_NAME "Gerätename" @@ -356,29 +357,29 @@ #define D_SINGLE_DEVICE "Einzelnes Gerät" #define D_MULTI_DEVICE "Mehrfachgerät" -#define D_CONFIGURE_TEMPLATE "Vorlage konfigurieren" -#define D_TEMPLATE_PARAMETERS "Vorlage Parameter" +#define D_CONFIGURE_TEMPLATE "Vorlage" +#define D_TEMPLATE_PARAMETERS "Parameter" #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "basiert auf" #define D_TEMPLATE_FLAGS "Optionen" -#define D_SAVE_CONFIGURATION "Konfiguration speichern" -#define D_CONFIGURATION_SAVED "Konfiguration gespeichert" -#define D_CONFIGURATION_RESET "Konfiguration zurücksetzen" +#define D_SAVE_CONFIGURATION "speichern" +#define D_CONFIGURATION_SAVED "gespeichert" +#define D_CONFIGURATION_RESET "zurücksetzen" #define D_PROGRAM_VERSION "Tasmota Version" -#define D_BUILD_DATE_AND_TIME "Erstellungszeitpunkt" +#define D_BUILD_DATE_AND_TIME "Erstellt" #define D_CORE_AND_SDK_VERSION "Core-/SDK-Version" -#define D_FLASH_WRITE_COUNT "Flash-Schreibzyklen" +#define D_FLASH_WRITE_COUNT "Schreibzyklen" #define D_MAC_ADDRESS "MAC-Adresse" -#define D_MQTT_HOST "MQTT Host" -#define D_MQTT_PORT "MQTT Port" -#define D_MQTT_CLIENT "MQTT Client" -#define D_MQTT_USER "MQTT Benutzer" -#define D_MQTT_TOPIC "MQTT Topic" -#define D_MQTT_GROUP_TOPIC "MQTT Group Topic" -#define D_MQTT_FULL_TOPIC "MQTT Full Topic" -#define D_MQTT_NO_RETAIN "MQTT No Retain" +#define D_MQTT_HOST "Host" +#define D_MQTT_PORT "Port" +#define D_MQTT_CLIENT "Client" +#define D_MQTT_USER "Benutzer" +#define D_MQTT_TOPIC "Topic" +#define D_MQTT_GROUP_TOPIC "Group Topic" +#define D_MQTT_FULL_TOPIC "Full Topic" +#define D_MQTT_NO_RETAIN "No Retain" #define D_MDNS_DISCOVERY "mDNS-Erkennung" #define D_MDNS_ADVERTISE "mDNS-Freigaben" #define D_ESP_CHIP_ID "ESP Chip ID" @@ -388,7 +389,7 @@ #define D_UPGRADE_BY_WEBSERVER "Update über Webserver" #define D_OTA_URL "OTA-URL" -#define D_START_UPGRADE "Update starten" +#define D_START_UPGRADE "Starten" #define D_UPGRADE_BY_FILE_UPLOAD "Update Datei hochladen" #define D_UPLOAD_FACTORY "Wechsle zur Safeboot Partition" #define D_UPLOAD_STARTED "Upload gestartet" @@ -401,7 +402,7 @@ #define D_UPLOAD_ERR_3 "Falsche Dateisignatur" #define D_UPLOAD_ERR_4 "Datei überschreitet vorhdn․ Flashspeicher" #define D_UPLOAD_ERR_5 "Upload Buffer Vergleich weicht ab" -#define D_UPLOAD_ERR_6 "Upload fehlgeschlagen․ Aktiviere Logging 3" +#define D_UPLOAD_ERR_6 "Upload fehlgeschlagen․ Details mit Logging 3" #define D_UPLOAD_ERR_7 "Upload abgebrochen" #define D_UPLOAD_ERR_8 "Datei ungültig" #define D_UPLOAD_ERR_9 "Datei zu groß" @@ -417,8 +418,8 @@ #define D_NEED_USER_AND_PASSWORD "Benötige user=&password=" // xdrv_01_mqtt.ino -#define D_FINGERPRINT "TLS-Fingerabdruck wird verifiziert…" -#define D_TLS_CONNECT_FAILED_TO "TLS-Verbindung fehlgeschlagen an" +#define D_FINGERPRINT "TLS-Fingerabdruck wird verifiziert" +#define D_TLS_CONNECT_FAILED_TO "TLS-Verbindung fehlgeschlagen" #define D_RETRY_IN "Erneuter Versuch in" #define D_VERIFIED "verifiziert mit Fingerabdruck" #define D_INSECURE "unsichere Verbindung aufgrund ungültigen Fingerabdrucks" @@ -445,7 +446,7 @@ #define D_3_RESPONSE_PACKETS_SENT "3 Antwortpakete gesendet" // xdrv_07_domoticz.ino -#define D_DOMOTICZ_PARAMETERS "Domoticz-Einstellungen" +#define D_DOMOTICZ_PARAMETERS "Domoticz" #define D_DOMOTICZ_IDX "Idx" #define D_DOMOTICZ_KEY_IDX "Key Idx" #define D_DOMOTICZ_SWITCH_IDX "Switch Idx" @@ -463,8 +464,8 @@ #define D_DOMOTICZ_UPDATE_TIMER "Zeitplan-Update" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Zeitplaneinstellungen" -#define D_TIMER_PARAMETERS "Zeitplaneinstellungen" +#define D_CONFIGURE_TIMER "Zeitplan" +#define D_TIMER_PARAMETERS "Zeitplan" #define D_TIMER_ENABLE "Zeitpläne aktivieren" #define D_TIMER_ARM "Aktiv" #define D_TIMER_TIME "Uhrzeit" @@ -474,12 +475,12 @@ #define D_TIMER_ACTION "Aktion" // xdrv_10_knx.ino -#define D_CONFIGURE_KNX "KNX-Einstellungen" -#define D_KNX_PARAMETERS "KNX-Parameter" +#define D_CONFIGURE_KNX "KNX" +#define D_KNX_PARAMETERS "Parameter" #define D_KNX_GENERAL_CONFIG "Allgemein" #define D_KNX_PHYSICAL_ADDRESS "Physikalische Adresse" -#define D_KNX_PHYSICAL_ADDRESS_NOTE "(Muss einmalig im KNX-Netzwerk sein)" -#define D_KNX_ENABLE "KNX aktivieren" +#define D_KNX_PHYSICAL_ADDRESS_NOTE "Muss eindeutig im KNX-Netzwerk sein" +#define D_KNX_ENABLE "aktivieren" #define D_KNX_GROUP_ADDRESS_TO_WRITE "Daten zum Senden an Gruppenadresse" #define D_ADD "Hinzufügen" #define D_DELETE "Löschen" @@ -490,7 +491,7 @@ #define D_KNX_COMMAND_READ "Lesen" #define D_KNX_COMMAND_OTHER "Andere" #define D_SENT_TO "gesendet an" -#define D_KNX_WARNING "Die Gruppenadresse (0/0/0) ist reserviert und kann nicht verwendet werden" +#define D_KNX_WARNING "Die Gruppenadresse (0/0/0) ist reserviert" #define D_KNX_ENHANCEMENT "Erweiterte Kommunikation" #define D_KNX_TX_SLOT "KNX TX" #define D_KNX_RX_SLOT "KNX RX" @@ -498,15 +499,15 @@ #define D_KNX_RX_SCENE "KNX SCENE RX" // xdrv_23_zigbee -#define D_ZIGBEE_PERMITJOIN_ACTIVE "Gerätekopplung erlaubt" -#define D_ZIGBEE_MAPPING_TITLE "Tasmota Zigbee Karte" +#define D_ZIGBEE_PERMITJOIN_ACTIVE "Kopplung erlaubt" +#define D_ZIGBEE_MAPPING_TITLE "Zigbee Karte" #define D_ZIGBEE_NOT_STARTED "Zigbee nicht gestartet" #define D_ZIGBEE_MAPPING_IN_PROGRESS_SEC "Karte in Erstellung (%d s․ verbleibend)" #define D_ZIGBEE_MAPPING_NOT_PRESENT "Keine Karte" -#define D_ZIGBEE_MAP_REFRESH "Zigbee Karte erneuern" -#define D_ZIGBEE_MAP "Zigbee Karte" -#define D_ZIGBEE_PERMITJOIN "Zigbee Kopplung ein" -#define D_ZIGBEE_GENERATE_KEY "Erzeuge zufälligen Zigbee Netzwerkschlüssel" +#define D_ZIGBEE_MAP_REFRESH "Karte erneuern" +#define D_ZIGBEE_MAP "Karte" +#define D_ZIGBEE_PERMITJOIN "Kopplung ein" +#define D_ZIGBEE_GENERATE_KEY "Erzeuge zufälligen Netzwerkschlüssel" #define D_ZIGBEE_UNKNOWN_DEVICE "Unbekanntes Gerät" #define D_ZIGBEE_UNKNOWN_ATTRIBUTE "Unbekanntes Attribut" #define D_ZIGBEE_UNKNOWN_ENDPOINT "Unkbekannter Endpunkt" @@ -516,19 +517,19 @@ #define D_ZIGBEE_TOO_MANY_CLUSTERS "Nur eine Cluster-ID pro Kommando" #define D_ZIGBEE_CONFLICTING_ENDPOINTS "Kollidierende Endpunkte" #define D_ZIGBEE_WRONG_DELIMITER "Falscher Delimeter für Payload" -#define D_ZIGBEE_UNRECOGNIZED_COMMAND "Unerkanntes Zigbee Kommando: %s" +#define D_ZIGBEE_UNRECOGNIZED_COMMAND "Unerkanntes Kommando: %s" #define D_ZIGBEE_TOO_MANY_COMMANDS "Nur 1 Kommando zulässig (%d)" #define D_ZIGBEE_NO_ATTRIBUTE "Kein Attribut in der Liste" #define D_ZIGBEE_UNSUPPORTED_ATTRIBUTE_TYPE "Nicht unterstützter Attributtyp" #define D_ZIGBEE_JSON_REQUIRED "Konfiguration muss JSON-basiert sein" #define D_ZIGBEE_RESET_1_OR_2 "1 oder 2 für Reset" -#define D_ZIGBEE_EEPROM_FOUND_AT_ADDRESS "ZBBridge EEPROM gefunden an Adresse" -#define D_ZIGBEE_RANDOMIZING_ZBCONFIG "Zufällige Zigbee Parameter erstellt, Überprüfung mit 'ZbConfig'" +#define D_ZIGBEE_EEPROM_FOUND_AT_ADDRESS "EEPROM gefunden an Adresse" +#define D_ZIGBEE_RANDOMIZING_ZBCONFIG "Zufällige Parameter erstellt, Überprüfung mit 'ZbConfig'" // xdrv_89_dali.ino #define D_SENSOR_DALI_RX "Dali RX" #define D_SENSOR_DALI_TX "Dali TX" -#define D_CONFIGURE_DALI "DALI-Einstellungen" +#define D_CONFIGURE_DALI "Dali Einstellungen" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energie heute" @@ -571,10 +572,10 @@ #define D_THERMOSTAT_AUTOTUNE_HYBRID "Autotune (Hybrid)" // xdrv_79_esp32_ble.ino -#define D_CONFIGURE_BLE "BLE-Einstellungen" -#define D_BLE_PARAMETERS "Bluetooth-Einstellungen" -#define D_MQTT_BLE_ENABLE "Bluetooth aktivieren" -#define D_MQTT_BLE_ACTIVESCAN "Aktiv scannen (*)" +#define D_CONFIGURE_BLE "BLE" +#define D_BLE_PARAMETERS "BLE Parameter" +#define D_BLE_ENABLE "BLE aktivieren" +#define D_BLE_ACTIVESCAN "Aktiv scannen (*)" #define D_BLE_DEVICES "Erkannte Geräte" #define D_BLE_REMARK "Mit (*) markierte Geräte werden nicht gespeichert." diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index cfe3e1a39cd4..4a75cae60c70 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Ποιότητα αέρα" #define D_AP "AP" // Access Point #define D_AS "ως" +#define D_AT "at" #define D_AUTO "ΑΥΤΟΜΑΤΟ" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index f379bd8e410d..19312fd4e3a0 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Air quality" #define D_AP "AP" // Access Point #define D_AS "as" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -280,16 +281,16 @@ #define D_CONSOLE "Console" #define D_CONFIRM_RESTART "Confirm Restart" -#define D_CONFIGURE_MODULE "Configure Module" -#define D_CONFIGURE_WIFI "Configure WiFi" -#define D_CONFIGURE_MQTT "Configure MQTT" -#define D_CONFIGURE_DOMOTICZ "Configure Domoticz" -#define D_CONFIGURE_LOGGING "Configure Logging" -#define D_CONFIGURE_OTHER "Configure Other" +#define D_CONFIGURE_MODULE "Module" +#define D_CONFIGURE_WIFI "WiFi" +#define D_CONFIGURE_MQTT "MQTT" +#define D_CONFIGURE_DOMOTICZ "Domoticz" +#define D_CONFIGURE_LOGGING "Logging" +#define D_CONFIGURE_OTHER "Other" #define D_CONFIRM_RESET_CONFIGURATION "Confirm Reset Configuration" -#define D_RESET_CONFIGURATION "Reset Configuration" -#define D_BACKUP_CONFIGURATION "Backup Configuration" -#define D_RESTORE_CONFIGURATION "Restore Configuration" +#define D_RESET_CONFIGURATION "Reset" +#define D_BACKUP_CONFIGURATION "Backup" +#define D_RESTORE_CONFIGURATION "Restore" #define D_START_RESTORE "Start restore" #define D_MAIN_MENU "Main Menu" @@ -356,7 +357,7 @@ #define D_SINGLE_DEVICE "single device" #define D_MULTI_DEVICE "multi device" -#define D_CONFIGURE_TEMPLATE "Configure Template" +#define D_CONFIGURE_TEMPLATE "Template" #define D_TEMPLATE_PARAMETERS "Template parameters" #define D_TEMPLATE_NAME "Name" #define D_BASE_TYPE "Based on" @@ -386,10 +387,10 @@ #define D_FLASH_CHIP_SIZE "Flash Size" #define D_FREE_PROGRAM_SPACE "Free Program Space" -#define D_UPGRADE_BY_WEBSERVER "Upgrade by web server" +#define D_UPGRADE_BY_WEBSERVER "Use web server" #define D_OTA_URL "OTA Url" #define D_START_UPGRADE "Start upgrade" -#define D_UPGRADE_BY_FILE_UPLOAD "Upgrade by file upload" +#define D_UPGRADE_BY_FILE_UPLOAD "Use file upload" #define D_UPLOAD_FACTORY "Switching to safeboot partition" #define D_UPLOAD_STARTED "Upload started" #define D_UPGRADE_STARTED "Upgrade started" @@ -463,7 +464,7 @@ #define D_DOMOTICZ_UPDATE_TIMER "Update timer" // xdrv_09_timers.ino -#define D_CONFIGURE_TIMER "Configure Timer" +#define D_CONFIGURE_TIMER "Timer" #define D_TIMER_PARAMETERS "Timer parameters" #define D_TIMER_ENABLE "Enable Timers" #define D_TIMER_ARM "Enable" @@ -474,7 +475,7 @@ #define D_TIMER_ACTION "Action" // xdrv_10_knx.ino -#define D_CONFIGURE_KNX "Configure KNX" +#define D_CONFIGURE_KNX "KNX" #define D_KNX_PARAMETERS "KNX Parameters" #define D_KNX_GENERAL_CONFIG "General" #define D_KNX_PHYSICAL_ADDRESS "Physical Address" @@ -528,7 +529,7 @@ // xdrv_89_dali.ino #define D_SENSOR_DALI_RX "Dali RX" #define D_SENSOR_DALI_TX "Dali TX" -#define D_CONFIGURE_DALI "Config DALI" +#define D_CONFIGURE_DALI "DALI" // xdrv_03_energy.ino #define D_ENERGY_TODAY "Energy Today" @@ -546,7 +547,7 @@ #define D_DOMOTICZ_SHUTTER "Shutter" // xdrv_28_pcf8574.ino -#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_CONFIGURE_PCF8574 "PCF8574" #define D_PCF8574_PARAMETERS "PCF8574 parameters" #define D_INVERT_PORTS "Invert Ports" #define D_DEVICE "Device" @@ -571,10 +572,10 @@ #define D_THERMOSTAT_AUTOTUNE_HYBRID "Autotune (Hybrid)" // xdrv_79_esp32_ble.ino -#define D_CONFIGURE_BLE "Configure BLE" +#define D_CONFIGURE_BLE "BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" @@ -627,7 +628,7 @@ #define D_HX_CAL_DONE "Calibrated" #define D_HX_CAL_FAIL "Calibration failed" #define D_RESET_HX711 "Reset Scale" -#define D_CONFIGURE_HX711 "Configure Scale" +#define D_CONFIGURE_HX711 "Scale" #define D_HX711_PARAMETERS "Scale parameters" #define D_ITEM_WEIGHT "Item weight" #define D_REFERENCE_WEIGHT "Reference weight" diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index ff49b30587ec..3d4ad847ea45 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Calidad del Aire" #define D_AP "AP" // Access Point #define D_AS "como" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Bat" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index 1b07f5e8ba57..e87a1193cbb5 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Qualité de l'Air" #define D_AP "AP" // Access Point #define D_AS "comme" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index 189e2297a952..e00a178c2a5a 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Luchtkwaliteit" #define D_AP "AP" // Access Point #define D_AS "als" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index 6a04467ac2a1..1179797b6912 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "איכות אוויר" #define D_AP "AP" // Access Point #define D_AS "-כ" +#define D_AT "at" #define D_AUTO "אוטומטי" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index 9f07ff5cc27b..64b10fb22b96 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Levegőminőség" #define D_AP "AP" // Access Point #define D_AS "mint" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index 1c614468e4e0..00254ca7a2cd 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.4.0.1 - Last update 15.11.2024 + * Updated until v9.4.0.1 - Last update 07.12.2024 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Qualità dell'aria" #define D_AP "AP" // Access Point #define D_AS "come" +#define D_AT "in" #define D_AUTO "AUTO" #define D_BATTERY "Batteria" #define D_BATT "Batt" // Short for Battery @@ -386,10 +387,10 @@ #define D_FLASH_CHIP_SIZE "Dimensione flash" #define D_FREE_PROGRAM_SPACE "Memoria libera programma" -#define D_UPGRADE_BY_WEBSERVER "Aggiornamento via server web" +#define D_UPGRADE_BY_WEBSERVER "Aggiorna via server web" #define D_OTA_URL "URL OTA" #define D_START_UPGRADE "Esegui aggiornamento" -#define D_UPGRADE_BY_FILE_UPLOAD "Aggiornamento tramite file locale" +#define D_UPGRADE_BY_FILE_UPLOAD "Aggiorna tramite file locale" #define D_UPLOAD_FACTORY "Passaggio a partizione avvio sicuro" #define D_UPLOAD_STARTED "Caricamento..." #define D_UPGRADE_STARTED "Aggiornamento..." @@ -571,10 +572,10 @@ #define D_THERMOSTAT_AUTOTUNE_HYBRID "Regolazione automatica (ibrida)" // xdrv_79_esp32_ble.ino -#define D_CONFIGURE_BLE "Configura BLE" +#define D_CONFIGURE_BLE "BLE" #define D_BLE_PARAMETERS "Impostazioni Bluetooth" -#define D_MQTT_BLE_ENABLE "Abilita Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Abilita scansione attiva (*)" +#define D_BLE_ENABLE "Abilita Bluetooth" +#define D_BLE_ACTIVESCAN "Abilita scansione attiva (*)" #define D_BLE_DEVICES "Scansione dispositivi" #define D_BLE_REMARK "gli elementi segnati con (*) non sono memorizzati in config" diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index d47bee94792a..314f246bc4d9 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "공기질" #define D_AP "AP" // Access Point #define D_AS "as" +#define D_AT "at" #define D_AUTO "자동" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index dac4c6eee3e4..9a27938c9f93 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Lucht kwaliteit" #define D_AP "AP" // Access Point #define D_AS "als" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index e48f1e00dacf..c1c9650adda0 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Jakość powietrza" #define D_AP "AP" // Access Point #define D_AS "jak" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Bateria" #define D_BATT "Bat" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Konfiguracja BLE" #define D_BLE_PARAMETERS "Ustawienia Bluetooth" -#define D_MQTT_BLE_ENABLE "Załącz Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Załącz Aktywne Skanowanie(*)" +#define D_BLE_ENABLE "Załącz Bluetooth" +#define D_BLE_ACTIVESCAN "Załącz Aktywne Skanowanie(*)" #define D_BLE_DEVICES "Znalezione Urządzenia" #define D_BLE_REMARK "rzeczy oznaczone (*) nie są zapisywane w konfiguracji" diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index 4dfaf2058113..a940abfd6d8b 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Qualidade do ar" #define D_AP "Ponto de acesso" // Ponto de Acesso #define D_AS "como" +#define D_AT "at" #define D_AUTO "Auto" #define D_BATTERY "Battery" #define D_BATT "Bat" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index 4735ffe3f7cc..fe4a4714d194 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Qualidade do Ar" #define D_AP "AP" // Ponto de Acesso #define D_AS "como" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index 96a0f506dee7..3408016a9114 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Calitatea aerului" #define D_AP "AP" // Access Point #define D_AS "as" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index 0a70d129cff4..ef655916db74 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -57,6 +57,7 @@ #define D_AIR_QUALITY "Качество воздуха" #define D_AP "AP" // Access Point #define D_AS "как" +#define D_AT "at" #define D_AUTO "АВТО" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -574,8 +575,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 91ba8d635a58..a906456cb124 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Kvalita vzduchu" #define D_AP "AP" // Access Point #define D_AS "ako" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index 8bf23eb8da24..0d160b4f2777 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Luftkvalitet" #define D_AP "AP" // Access Point #define D_AS "som" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index d73fbd1abb90..320c058cfd04 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Hava Kalitesi" #define D_AP "AP" // Access Point #define D_AS "as" +#define D_AT "at" #define D_AUTO "OTOMATIK" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index 0cf2ac6909d6..7d64719579e3 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Якість повітря" #define D_AP "Точка доступу" // Access Point #define D_AS "як" +#define D_AT "at" #define D_AUTO "АВТО" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index 4b625a7c030c..42c68305dcce 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "Chất lượng không khí" #define D_AP "Mạng wifi" // Access Point #define D_AS "với tên gọi" +#define D_AT "at" #define D_AUTO "AUTO" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index 70d2c5d971ec..cd5692717060 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "空气质量" #define D_AP "AP" // Access Point #define D_AS "名称:" +#define D_AT "at" #define D_AUTO "自动" #define D_BATTERY "Battery" #define D_BATT "Batt" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index 2b721c2ab5fa..29125d6ea3d9 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -56,6 +56,7 @@ #define D_AIR_QUALITY "空氣品質" #define D_AP "存取點" // Access Point #define D_AS "名稱:" +#define D_AT "at" #define D_AUTO "自動" #define D_BATTERY "Battery" #define D_BATT "電池" // Short for Battery @@ -573,8 +574,8 @@ // xdrv_79_esp32_ble.ino #define D_CONFIGURE_BLE "Configure BLE" #define D_BLE_PARAMETERS "Bluetooth Settings" -#define D_MQTT_BLE_ENABLE "Enable Bluetooth" -#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)" +#define D_BLE_ENABLE "Enable Bluetooth" +#define D_BLE_ACTIVESCAN "Enable Active Scan(*)" #define D_BLE_DEVICES "Devices Seen" #define D_BLE_REMARK "items marked (*) are not stored in config" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index d6822db2fd2a..12a1ec8f9d31 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -211,7 +211,7 @@ #define COLOR_TIMER_TAB_TEXT "#fff" // [WebColor17] Config timer tab text color - White #define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark gray #define COLOR_TITLE_TEXT "#000" // [WebColor19] Title text color - Whiteish -#define COLOR_BUTTON_OFF "#08405e" // [WebColor20] Button color when off - Darkest blueish +#define COLOR_BUTTON_OFF "#a1d9f7" // [WebColor20] Button color when off - Light blue */ // Dark theme // WebColor {"WebColor":["#eaeaea","#252525","#4f4f4f","#000","#ddd","#65c115","#1f1f1f","#ff5661","#008000","#faffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#faffff","#999","#eaeaea","#08405e"]} @@ -234,7 +234,7 @@ #define COLOR_TIMER_TAB_TEXT "#faffff" // [WebColor17] Config timer tab text color - Very pale (mostly white) cyan. #define COLOR_TIMER_TAB_BACKGROUND "#999" // [WebColor18] Config timer tab background color - Dark gray #define COLOR_TITLE_TEXT "#eaeaea" // [WebColor19] Title text color - Very light gray -#define COLOR_BUTTON_OFF "#08405e" // [WebColor20] Button color when off - Darkest blueish +#define COLOR_BUTTON_OFF "#08405e" // [WebColor20] Button color when off - Darkest blueish // -- KNX ----------------------------------------- #define KNX_ENABLED false // [Knx_Enabled] Enable KNX protocol @@ -562,14 +562,7 @@ // #define MAGICSWITCH_MASKING_WINDOW_LEN 5 // Overridable masking window (in number of 50ms loops) // -- Optional light modules ---------------------- -#define USE_LIGHT // Add support for light control -#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // -// #define USE_WS2812_DMA // ESP8266 only, DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow - #define USE_WS2812_RMT 0 // ESP32 only, hardware RMT support (default). Specify the RMT channel 0..7. This should be preferred to software bit bang. -// #define USE_WS2812_I2S 0 // ESP32 only, hardware I2S support. Specify the I2S channel 0..2. This is exclusive from RMT. By default, prefer RMT support -// #define USE_WS2812_INVERTED // Use inverted data signal - #define USE_WS2812_HARDWARE NEO_HW_WS2812 // Hardware type (NEO_HW_WS2812, NEO_HW_WS2812X, NEO_HW_WS2813, NEO_HW_SK6812, NEO_HW_LC8812, NEO_HW_APA106, NEO_HW_P9813) - #define USE_WS2812_CTYPE NEO_GRB // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) +#define USE_LIGHT // Add support for light control #define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) @@ -583,6 +576,18 @@ #define USE_DGR_LIGHT_SEQUENCE // Add support for device group light sequencing (requires USE_DEVICE_GROUPS) (+0k2 code) //#define USE_LSC_MCSL // Add support for GPE Multi color smart light as sold by Action in the Netherlands (+1k1 code) +// -- Optional adressable leds ---------------------- +#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // +// -------- below is for ESP8266 only +// #define USE_WS2812_DMA // ESP8266 only, DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow + #define USE_WS2812_RMT 0 // ESP32 only, hardware RMT support (default). Specify the RMT channel 0..7. This should be preferred to software bit bang. +// #define USE_WS2812_I2S 0 // ESP32 only, hardware I2S support. Specify the I2S channel 0..2. This is exclusive from RMT. By default, prefer RMT support +// #define USE_WS2812_INVERTED // Use inverted data signal + #define USE_WS2812_HARDWARE NEO_HW_WS2812 // Hardware type (NEO_HW_WS2812, NEO_HW_WS2812X, NEO_HW_WS2813, NEO_HW_SK6812, NEO_HW_LC8812, NEO_HW_APA106, NEO_HW_P9813) + #define USE_WS2812_CTYPE NEO_GRB // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) +// -------- below if for ESP32x only -- ESP32 uses a lightweight library instead of NeoPixelBus + // #define USE_WS2812_FORCE_NEOPIXELBUS // this option forces to use NeoPixelBus (like ESP866), which disables Berry support and limits features -- DO NOT USE unless you have a good reason + // #define USE_LIGHT_ARTNET // Add support for DMX/ArtNet via UDP on port 6454 (+3.5k code) #define USE_LIGHT_ARTNET_MCAST 239,255,25,54 // Multicast address used to listen: 239.255.25.54 @@ -775,7 +780,7 @@ #define MTX_ADDRESS7 0x00 // [DisplayAddress7] I2C address of seventh 8x8 matrix module #define MTX_ADDRESS8 0x00 // [DisplayAddress8] I2C address of eigth 8x8 matrix module #define USE_DISPLAY_SEVENSEG // [DisplayModel 11] [I2cDriver47] Enable sevenseg display (I2C 0x70-0x77) (<+11k code) -// #define USE_DISPLAY_SEVENSEG_COMMON_ANODE // Enable support for common anode sevenseg displays +// #define USE_DISPLAY_SEVENSEG_COMMON_ANODE // Enable support for common anode sevenseg displays // Multiple sevenseg displays are logically arranged vertically with MTX_ADDRESS1 at y=0, // MTX_ADDRESS2 at y=1, up to MTX_ADDRESS8 at y=7 // Command: DisplayText [yn]8888 @@ -783,7 +788,7 @@ // Each segment may be address Command: DisplayText [xn]m // where n is 0..4 (4 digits and middle :) and m is decimal for bitmap of which segment to turn on. // Reference: https://cdn-learn.adafruit.com/downloads/pdf/adafruit-led-backpack.pdf - // #define SEVENSEG_ADDRESS1 0x70 // No longer used. Use MTX_ADDRESS1 - MTX_ADDRESS8 instead to specify I2C address of sevenseg displays +// #define SEVENSEG_ADDRESS1 0x70 // No longer used. Use MTX_ADDRESS1 - MTX_ADDRESS8 instead to specify I2C address of sevenseg displays // #define USE_DISPLAY_SH1106 // [DisplayModel 7] [I2cDriver6] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D) // #define USE_DISPLAY_TM1650 // [DisplayModel 20] [I2cDriver74] Enable TM1650 display (I2C addresses 0x24 - 0x27 and 0x34 - 0x37) // #define USE_DT_VARS // Display variables that are exposed in JSON MQTT strings e.g. in TelePeriod messages. @@ -793,10 +798,12 @@ #endif // USE_I2C -// #define USE_DISPLAY // Add I2C/TM1637/MAX7219 Display Support (+2k code) -// #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637 Seven Segment Display Module (4-6 digits) -// #define USE_DISPLAY_MAX7219 // [DisplayModel 15] Enable MAX7219 Seven Segment Display Module (8 digits) -// #define USE_DISPLAY_MAX7219_MATRIX // [DisplayModel 19] Enable MAX7219 8x8 Matrix Display +//#define USE_DISPLAY // Add I2C/TM1637/MAX7219 Display Support (+2k code) +// #define USE_DISPLAY_TM1637 // [DisplayModel 15] Enable TM1637 Seven Segment Display Module (4-6 digits) +// #define USE_DISPLAY_MAX7219 // [DisplayModel 15] Enable MAX7219 Seven Segment Display Module (8 digits) +// #define USE_DISPLAY_MAX7219_MATRIX // [DisplayModel 19] Enable MAX7219 8x8 Matrix Display +// #define USE_DISPLAY_TM1640 // [DisplayModel 13] Enable TM1640 module Seven Segment Display Module (stub) +// #define USE_IOTTIMER // Enable TM1640 based IotTimer // -- Universal Display Driver --------------------------------- // #define USE_UNIVERSAL_DISPLAY // New universal display driver for both I2C and SPI diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 94b611e1ae3e..923bd7556064 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -409,6 +409,7 @@ void setup(void) { #endif // DISABLE_ESP32_BROWNOUT #ifndef FIRMWARE_SAFEBOOT +#ifndef DISABLE_PSRAMCHECK #ifndef CORE32SOLO1 // restore GPIO5/18 or 16/17 if no PSRAM is found which may be used by Ethernet among others if (!FoundPSRAM()) { @@ -422,6 +423,7 @@ void setup(void) { } } #endif // CORE32SOLO1 +#endif // DISABLE_PSRAMCHECK #endif // FIRMWARE_SAFEBOOT #endif // CONFIG_IDF_TARGET_ESP32 #endif // ESP32 diff --git a/tasmota/tasmota_support/support.ino b/tasmota/tasmota_support/support.ino index 1c8609a5e9b0..4caa4bdf4d6e 100755 --- a/tasmota/tasmota_support/support.ino +++ b/tasmota/tasmota_support/support.ino @@ -832,12 +832,29 @@ int32_t UpdateDevicesPresent(int32_t change) { else if (devices_present >= POWER_SIZE) { // Support up to uint32_t as bitmask difference = devices_present - POWER_SIZE; devices_present = POWER_SIZE; - AddLog(LOG_LEVEL_DEBUG, PSTR("APP: Max number of devices reached")); +// AddLog(LOG_LEVEL_DEBUG, PSTR("APP: Max 32 devices supported")); } TasmotaGlobal.devices_present = devices_present; + +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DVC: DevicesPresent %d, Change %d"), TasmotaGlobal.devices_present, change); + return difference; } +void DevicesPresentNonDisplayOrLight(uint32_t &devices_claimed) { + uint32_t display_and_lights = 0; +#ifdef USE_LIGHT + display_and_lights += LightDevices(); // Skip light(s) +#endif // USE_LIGHT +#ifdef USE_DISPLAY + display_and_lights += DisplayDevices(); // Skip display +#endif // USE_DISPLAY + uint32_t devices_present = TasmotaGlobal.devices_present - display_and_lights; + if (devices_claimed > devices_present) { + devices_claimed = devices_present; // Reduce amount of claimed devices + } +} + char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option) { strncpy_P(dest, S_RSLT_POWER, size); // POWER @@ -1134,6 +1151,8 @@ int GetCommandCode(char* destination, size_t destination_size, const char* needl bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void), const uint8_t *synonyms = nullptr); bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void), const uint8_t *synonyms) { + SHOW_FREE_MEM(PSTR("DecodeCommand")); + GetTextIndexed(XdrvMailbox.command, CMDSZ, 0, haystack); // Get prefix if available int prefix_length = strlen(XdrvMailbox.command); if (prefix_length) { @@ -1953,7 +1972,7 @@ uint32_t JsonParsePath(JsonParserObject *jobj, const char *spath, char delim, fl uint32_t res = 0; const char *cp = spath; #ifdef DEBUG_JSON_PARSE_PATH - AddLog(LOG_LEVEL_INFO, PSTR("JSON: parsing json key: %s from json: %s"), cp, spath); + AddLog(LOG_LEVEL_INFO, PSTR("JSON: parsing json key: %s from json: %s"), cp, jpath); #endif JsonParserObject obj = *jobj; JsonParserObject lastobj = obj; @@ -2640,11 +2659,12 @@ void AddLogData(uint32_t loglevel, const char* log_data, const char* log_data_pa // Each entry has this format: [index][loglevel][log data]['\1'] // Truncate log messages longer than MAX_LOGSZ which is the log buffer size minus 64 spare + char *too_long = nullptr; uint32_t log_data_len = strlen(log_data) + strlen(log_data_payload) + strlen(log_data_retained); - char too_long[TOPSZ]; if (log_data_len > MAX_LOGSZ) { - snprintf_P(too_long, sizeof(too_long) - 20, PSTR("%s%s"), log_data, log_data_payload); // 20 = strlen("... 123456 truncated") - snprintf_P(too_long, sizeof(too_long), PSTR("%s... %d truncated"), too_long, log_data_len); + too_long = (char*)malloc(TOPSZ); // Use heap in favour of stack + snprintf_P(too_long, TOPSZ - 20, PSTR("%s%s"), log_data, log_data_payload); // 20 = strlen("... 123456 truncated") + snprintf_P(too_long, TOPSZ, PSTR("%s... %d truncated"), too_long, log_data_len); log_data = too_long; log_data_payload = empty; log_data_retained = empty; @@ -2665,6 +2685,7 @@ void AddLogData(uint32_t loglevel, const char* log_data, const char* log_data_pa } snprintf_P(TasmotaGlobal.log_buffer, LOG_BUFFER_SIZE, PSTR("%s%c%c%s%s%s%s\1"), TasmotaGlobal.log_buffer, TasmotaGlobal.log_buffer_pointer++, '0'+loglevel, mxtime, log_data, log_data_payload, log_data_retained); + if (too_long) { free(too_long); } TasmotaGlobal.log_buffer_pointer &= 0xFF; if (!TasmotaGlobal.log_buffer_pointer) { TasmotaGlobal.log_buffer_pointer++; // Index 0 is not allowed as it is the end of char string diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 2c25cdd2884c..727da327391e 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -348,6 +348,13 @@ void ExecuteCommand(const char *cmnd, uint32_t source) CommandHandler(stopic, svalue, strlen(svalue)); } +bool GetFallbackTopicFlag(char* topicBuf) { + // Use this function to free CommandHandler stack space from TOPSZ + char stemp1[TOPSZ]; + GetFallbackTopic_P(stemp1, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/ + return (!strncmp(topicBuf, stemp1, strlen(stemp1))); +} + /********************************************************************************************/ // topicBuf: /power1 dataBuf: toggle = Console command @@ -369,9 +376,7 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) { } } - char stemp1[TOPSZ]; - GetFallbackTopic_P(stemp1, ""); // Full Fallback topic = cmnd/DVES_xxxxxxxx_fb/ - TasmotaGlobal.fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); + TasmotaGlobal.fallback_topic_flag = GetFallbackTopicFlag(topicBuf); char *type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type) @@ -416,11 +421,13 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) { } Response_P(PSTR("_1")); // Signal error message for either Command Error or Command Unknown - char number[12]; - char command_line[64]; - snprintf_P(command_line, sizeof(command_line), PSTR("%s%s%s%s"), + char stemp1[16]; +// char command_line[64]; +// snprintf_P(command_line, sizeof(command_line), PSTR("%s%s%s%s"), + char *command_line = (char*)malloc(64); // Use heap in favour of stack + snprintf_P(command_line, 64, PSTR("%s%s%s%s"), type, - (index != 1) ? itoa(index, number, 10) : "", + (index != 1) ? itoa(index, stemp1, 10) : "", (data_len) ? " " : "", (data_len) ? (binary_data) ? HexToString((uint8_t*)dataBuf, data_len).c_str() : EscapeJSONString(dataBuf).c_str() : ""); @@ -483,6 +490,7 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) { } ResponseAppend_P(PSTR(",\"Input\":\"%s\"}"), command_line); } + free(command_line); if (ResponseLength()) { if (TasmotaGlobal.no_mqtt_response){ // If it is activated, Tasmota will not publish MQTT messages, but it will proccess event trigger rules diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino index 90a1717878eb..d8cceedc4b2c 100644 --- a/tasmota/tasmota_support/support_tasmota.ino +++ b/tasmota/tasmota_support/support_tasmota.ino @@ -466,7 +466,11 @@ void SetPowerOnState(void) for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { #ifdef ESP8266 if (!Settings->flag3.no_power_feedback && // SetOption63 - Don't scan relay power state at restart - #5594 and #5663 - !TasmotaGlobal.power_on_delay) { // SetOption47 - Delay switching relays to reduce power surge at power on + !TasmotaGlobal.power_on_delay // SetOption47 - Delay switching relays to reduce power surge at power on +#ifdef USE_SHUTTER + && !Settings->flag3.shutter_mode // SetOption80 - Enable shutter support +#endif // USE_SHUTTER + ) { if ((port < MAX_RELAYS) && PinUsed(GPIO_REL1, port)) { if (bitRead(TasmotaGlobal.rel_bistable, port)) { port++; // Skip both bistable relays as always 0 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino index 86a88d6d4c99..6e2383bd4d56 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino @@ -266,6 +266,9 @@ const char HTTP_HEAD_STYLE3[] PROGMEM = "

%s

" // Module name "

%s

"; // Device name +const char HTTP_MENU_HEAD[] PROGMEM = + "


%s

"; + const char HTTP_MSG_SLIDER_SHUTTER[] PROGMEM = "" "
%s" @@ -481,13 +484,11 @@ ESP8266WebServer *Webserver; struct WEB { String chunk_buffer = ""; // Could be max 2 * CHUNKED_BUFFER_SIZE uint32_t upload_size = 0; + uint32_t light_shutter_button_mask; + uint32_t buttons_non_light_non_shutter; uint32_t slider_update_time = 0; int slider[LST_MAX]; -#ifdef ESP8266 - int8_t shutter_slider[MAX_SHUTTERS]; -#else // ESP32 int8_t shutter_slider[16]; // MAX_SHUTTERS_ESP32 -#endif // ESP8266 uint16_t upload_error = 0; uint8_t state = HTTP_OFF; uint8_t upload_file_type; @@ -984,11 +985,12 @@ void WSContentSendStyle_P(const char* formatP, ...) { WebColor(COL_TEXT_WARNING), #endif WebColor(COL_TITLE), - (Web.initial_config) ? "" : (Settings->flag5.gui_module_name) ? "" : ModuleName().c_str(), SettingsTextEscaped(SET_DEVICENAME).c_str()); + (Web.initial_config) ? "" : (Settings->flag5.gui_module_name) ? "" : ModuleName().c_str(), // SetOption141 - (GUI) Disable display of GUI module name (1) + (Settings->flag6.gui_device_name) ? "" : SettingsTextEscaped(SET_DEVICENAME).c_str()); // SetOption163 - (GUI) Disable display of GUI device name (1) // SetOption53 - Show hostname and IP address in GUI main menu #if (RESTART_AFTER_INITIAL_WIFI_CONFIG) - if (Settings->flag3.gui_hostname_ip) { // SetOption53 - (GUI) Show hostname and IP address in GUI main menu + if (Settings->flag3.gui_hostname_ip) { // SetOption53 - (GUI) Show hostname and IP address in GUI main menu #else if ( Settings->flag3.gui_hostname_ip || ( (WiFi.getMode() == WIFI_AP_STA) && (!Web.initial_config) ) ) { #endif @@ -1258,31 +1260,37 @@ int32_t IsShutterWebButton(uint32_t idx) { /*-------------------------------------------------------------------------------------------*/ -void WebGetDeviceCounts(uint32_t &buttons_non_light, uint32_t &buttons_non_light_non_shutter, uint32_t &shutter_button) { - buttons_non_light = TasmotaGlobal.devices_present; +void WebGetDeviceCounts(void) { + Web.buttons_non_light_non_shutter = TasmotaGlobal.devices_present; + Web.light_shutter_button_mask = 0; // Bitmask for each light and/or shutter button #ifdef USE_LIGHT // Chk for reduced toggle buttons used by lights if (TasmotaGlobal.light_type) { - // Find and skip light buttons (Lights are controlled by the last TasmotaGlobal.devices_present (or 2)) - buttons_non_light = LightDevice() -1; + // Find and skip light buttons + uint32_t light_device = LightDevice(); + uint32_t light_devices = LightDevices(); + for (uint32_t button_idx = light_device; button_idx < (light_device + light_devices); button_idx++) { + Web.buttons_non_light_non_shutter--; + Web.light_shutter_button_mask |= (1 << (button_idx -1)); // Set button bit in bitmask + } } #endif // USE_LIGHT - buttons_non_light_non_shutter = buttons_non_light; - shutter_button = 0; // Bitmask for each button #ifdef USE_SHUTTER // Chk for reduced toggle buttons used by shutters - // Find and skip dedicated shutter buttons - if (buttons_non_light && Settings->flag3.shutter_mode) { // SetOption80 - Enable shutter support - for (uint32_t button_idx = 1; button_idx <= buttons_non_light; button_idx++) { + if (Settings->flag3.shutter_mode) { // SetOption80 - Enable shutter support + // Find and skip dedicated shutter buttons + for (uint32_t button_idx = 1; button_idx <= TasmotaGlobal.devices_present; button_idx++) { if (IsShutterWebButton(button_idx) != 0) { - buttons_non_light_non_shutter--; - shutter_button |= (1 << (button_idx -1)); // Set button bit in bitmask + Web.buttons_non_light_non_shutter--; + Web.light_shutter_button_mask |= (1 << (button_idx -1)); // Set button bit in bitmask } } } #endif // USE_SHUTTER + +// AddLog(LOG_LEVEL_DEBUG, PSTR("HTP: DP %d, BNLNS %d, SB %08X"), TasmotaGlobal.devices_present, Web.buttons_non_light_non_shutter, Web.light_shutter_button_mask); } #ifdef USE_LIGHT @@ -1365,14 +1373,9 @@ void HandleRoot(void) { #ifndef FIRMWARE_MINIMAL if (TasmotaGlobal.devices_present) { - uint32_t buttons_non_light; - uint32_t buttons_non_light_non_shutter; - uint32_t shutter_button; - WebGetDeviceCounts(buttons_non_light, buttons_non_light_non_shutter, shutter_button); - uint32_t button_idx = 1; - - if (buttons_non_light_non_shutter) { // Any non light AND non shutter button - // Display toggle buttons + WebGetDeviceCounts(); + + if (Web.buttons_non_light_non_shutter) { // Any non light AND non shutter button - Show toggle buttons WSContentSend_P(HTTP_TABLE100); // "" WSContentSend_P(PSTR("")); @@ -1391,18 +1394,14 @@ void HandleRoot(void) { #endif // USE_SONOFF_IFAN const uint32_t max_columns = 8; - uint32_t rows = buttons_non_light_non_shutter / max_columns; - if (buttons_non_light_non_shutter % max_columns) { rows++; } - uint32_t cols = buttons_non_light_non_shutter / rows; - if (buttons_non_light_non_shutter % rows) { cols++; } + uint32_t rows = Web.buttons_non_light_non_shutter / max_columns; + if (Web.buttons_non_light_non_shutter % max_columns) { rows++; } + uint32_t cols = Web.buttons_non_light_non_shutter / rows; + if (Web.buttons_non_light_non_shutter % rows) { cols++; } uint32_t button_ptr = 0; - for (button_idx = 1; button_idx <= buttons_non_light; button_idx++) { - -#ifdef USE_SHUTTER - if (bitRead(shutter_button, button_idx -1)) { continue; } // Skip non-sequential shutter button -#endif // USE_SHUTTER - + for (uint32_t button_idx = 1; button_idx <= TasmotaGlobal.devices_present; button_idx++) { + if (bitRead(Web.light_shutter_button_mask, button_idx -1)) { continue; } // Skip non-sequential light and/or shutter button bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1))); snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), button_idx); WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / cols, button_idx, button_idx, @@ -1419,30 +1418,29 @@ void HandleRoot(void) { } #ifdef USE_SHUTTER - if (shutter_button) { // Any button bit set - WSContentSend_P(HTTP_TABLE100); // "
" - - int32_t ShutterWebButton; - uint32_t shutter_button_idx = 1; - for (uint32_t shutter_idx = 0; shutter_idx < TasmotaGlobal.shutters_present ; shutter_idx++) { - while ((0 == shutter_button & (1 << (shutter_button_idx -1)))) { shutter_button_idx++; } - + if (TasmotaGlobal.shutters_present) { // Any shutter present - Show shutter buttons and slider + WSContentSend_P(HTTP_TABLE100); // "
" + uint32_t shutter_button_idx; + uint32_t shutter_button_idx_temp; + for (uint32_t shutter_idx = 0; shutter_idx < TasmotaGlobal.shutters_present; shutter_idx++) { WSContentSend_P(PSTR("")); - shutter_button_idx++; // Left button is next button first (down) + uint32_t shutter_options = ShutterGetOptions(shutter_idx); + shutter_button_idx = ShutterGetStartRelay(shutter_idx) +1; // Left button is next button first (down) for (uint32_t j = 0; j < 2; j++) { - ShutterWebButton = IsShutterWebButton(shutter_button_idx); - WSContentSend_P(HTTP_DEVICE_CONTROL, 15, shutter_button_idx, shutter_button_idx, - ((ShutterGetOptions(abs(ShutterWebButton)-1) & 2) /* is locked */ ? "-" : - ((ShutterGetOptions(abs(ShutterWebButton)-1) & 8) /* invert web buttons */ ? ((ShutterWebButton>0) ? "▼" : "▲") : ((ShutterWebButton>0) ? "▲" : "▼"))), + shutter_button_idx_temp = (shutter_options & 1) ? shutter_button_idx + (j * 2) - 1 : shutter_button_idx; // Invert index +// AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: j %d, shutter_idx %d, shutter_button_idx %d, shutter_idx %d, shutter_button_idx_temp %d"), j, shutter_idx, shutter_button_idx, shutter_idx, shutter_button_idx_temp); + WSContentSend_P(HTTP_DEVICE_CONTROL, 15, shutter_button_idx_temp, shutter_button_idx_temp, + ((shutter_options & 2) ? "-" : // Is locked + ((shutter_options & 1) ? (j ? "▼" : "▲") : (j ? "▲" : "▼"))), // Invert web buttons ""); - if (1 == j) { break; } + if (1 == j) { break; } // Both buttons shown - shutter_button_idx--; // Right button is previous button (up) + shutter_button_idx--; // Right button is previous button (up) bool set_button = ((shutter_button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(shutter_button_idx -1))); snprintf_P(stemp, sizeof(stemp), PSTR("Shutter %d"), shutter_idx +1); uint32_t shutter_real_to_percent_position = ShutterRealToPercentPosition(-9999, shutter_idx); - Web.shutter_slider[shutter_idx] = (ShutterGetOptions(shutter_idx) & 1) ? (100 - shutter_real_to_percent_position) : shutter_real_to_percent_position; + Web.shutter_slider[shutter_idx] = (shutter_options & 1) ? (100 - shutter_real_to_percent_position) : shutter_real_to_percent_position; WSContentSend_P(HTTP_MSG_SLIDER_SHUTTER, (set_button) ? HtmlEscape(GetWebButton(shutter_button_idx -1)).c_str() : stemp, shutter_idx +1, @@ -1450,30 +1448,28 @@ void HandleRoot(void) { shutter_idx +1); } WSContentSend_P(PSTR("")); - shutter_button_idx += 2; - } WSContentSend_P(PSTR("
")); - - if (1 == button_idx) { - button_idx = shutter_button_idx; - } } #endif // USE_SHUTTER #ifdef USE_LIGHT - if (TasmotaGlobal.light_type) { - WSContentSend_P(HTTP_TABLE100); // "" + if (TasmotaGlobal.light_type) { // Any light - Show light button and slider(s) + uint32_t light_device = LightDevice(); + uint32_t light_devices = LightDevices(); + uint32_t button_idx = light_device; + + WSContentSend_P(HTTP_TABLE100); // "
" uint8_t light_subtype = TasmotaGlobal.light_type &7; if (!Settings->flag3.pwm_multi_channels) { // SetOption68 0 - Enable multi-channels PWM instead of Color PWM - bool split_white = ((LST_RGBW <= light_subtype) && (TasmotaGlobal.devices_present > 1) && (Settings->param[P_RGB_REMAP] & 128)); // Only on RGBW or RGBCW and SetOption37 128 + bool split_white = ((LST_RGBW <= light_subtype) && (light_devices > 1) && (Settings->param[P_RGB_REMAP] & 128)); // Only on RGBW or RGBCW and SetOption37 128 if ((LST_COLDWARM == light_subtype) || ((LST_RGBCW == light_subtype) && !split_white)) { WebSliderColdWarm(); } - if (light_subtype > 2) { // No W or CW + if (light_subtype > 2) { // No W or CW uint16_t hue; uint8_t sat; LightGetHSB(&hue, &sat, nullptr); @@ -1541,7 +1537,7 @@ void HandleRoot(void) { uint32_t width = 100; WSContentSend_P(PSTR("")); - if (button_idx <= TasmotaGlobal.devices_present) { + if (button_idx < (light_device + light_devices)) { bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1))); char first[2]; snprintf_P(first, sizeof(first), PSTR("%s"), PSTR(D_BUTTON_TOGGLE)); @@ -1567,9 +1563,8 @@ void HandleRoot(void) { WSContentSend_P(PSTR("")); } } else { // Settings->flag3.pwm_multi_channels - SetOption68 1 - Enable multi-channels PWM instead of Color PWM - uint32_t pwm_channels = TasmotaGlobal.devices_present - buttons_non_light; - stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; // d0 - for (uint32_t i = 0; i < pwm_channels; i++) { + stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; // e0 + for (uint32_t i = 0; i < light_devices; i++) { bool set_button = ((button_idx <= MAX_BUTTON_TEXT) && strlen(GetWebButton(button_idx -1))); char first[2]; snprintf_P(first, sizeof(first), PSTR("%s"), PSTR(D_BUTTON_TOGGLE)); @@ -1689,7 +1684,7 @@ bool HandleRootStatusRefresh(void) { char svalue[32]; // Command and number parameter char webindex[5]; // WebGetArg name - WebGetArg(PSTR("o"), tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed + WebGetArg(PSTR("o"), tmp, sizeof(tmp)); // 1 - 32 Device number for button Toggle or Fanspeed if (strlen(tmp)) { ShowWebSource(SRC_WEBGUI); uint32_t device = atoi(tmp); @@ -1729,9 +1724,9 @@ bool HandleRootStatusRefresh(void) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp); ExecuteWebCommand(svalue); } - uint32_t light_device = LightDevice(); // Channel number offset - uint32_t pwm_channels = (TasmotaGlobal.light_type & 7) > LST_MAX ? LST_MAX : (TasmotaGlobal.light_type & 7); - for (uint32_t j = 0; j < pwm_channels; j++) { + uint32_t light_device = LightDevice(); // Channel number offset + uint32_t light_devices = LightDevices(); // Number of channels + for (uint32_t j = 0; j < light_devices; j++) { snprintf_P(webindex, sizeof(webindex), PSTR("e%d"), j +1); WebGetArg(webindex, tmp, sizeof(tmp)); // 0 - 100 percent if (strlen(tmp)) { @@ -1891,33 +1886,36 @@ bool HandleRootStatusRefresh(void) { XsnsXdrvCall(FUNC_WEB_SENSOR); WSContentSend_P(PSTR("
")); - if (!Settings->flag6.gui_no_state_text) { // SetOption161 - (GUI) Disable display of state text (1) - bool show_state = (TasmotaGlobal.devices_present); + if (!Settings->flag6.gui_no_state_text) { // SetOption161 - (GUI) Disable display of state text (1) + if (!Web.buttons_non_light_non_shutter) { // Might still be zero on restart so chk if we have at least one + WebGetDeviceCounts(); + } + if ((Web.buttons_non_light_non_shutter > 0) && + ( Web.buttons_non_light_non_shutter <= 8)) { // We need at least one non light AND non shutter button + WSContentSend_P(PSTR("{t}")); #ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { show_state = false; } + if (IsModuleIfan()) { + WSContentSend_P(HTTP_DEVICE_STATE, 36, (bitRead(TasmotaGlobal.power, 0)) ? PSTR("bold") : PSTR("normal"), 54, GetStateText(bitRead(TasmotaGlobal.power, 0))); + uint32_t fanspeed = GetFanspeed(); + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed); + WSContentSend_P(HTTP_DEVICE_STATE, 64, (fanspeed) ? PSTR("bold") : PSTR("normal"), 54, (fanspeed) ? svalue : GetStateText(0)); + } else { #endif // USE_SONOFF_IFAN - if (show_state) { - uint32_t buttons_non_light; - uint32_t buttons_non_light_non_shutter; - uint32_t shutter_button; - WebGetDeviceCounts(buttons_non_light, buttons_non_light_non_shutter, shutter_button); - - if (buttons_non_light_non_shutter <= 8) { // Any non light AND non shutter button - WSContentSend_P(PSTR("{t}")); - uint32_t cols = buttons_non_light_non_shutter; + uint32_t cols = Web.buttons_non_light_non_shutter; uint32_t fontsize = (cols < 5) ? 70 - (cols * 8) : 32; - for (uint32_t idx = 1; idx <= buttons_non_light; idx++) { - -#ifdef USE_SHUTTER - if (bitRead(shutter_button, idx -1)) { continue; } // Skip non-sequential shutter button -#endif // USE_SHUTTER - - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(TasmotaGlobal.power, idx -1)); - WSContentSend_P(HTTP_DEVICE_STATE, 100 / cols, (bitRead(TasmotaGlobal.power, idx -1)) ? PSTR("bold") : PSTR("normal"), fontsize, - (cols < 5) ? GetStateText(bitRead(TasmotaGlobal.power, idx -1)) : svalue); + uint32_t button_ptr = 0; + for (uint32_t button_idx = 1; button_idx <= TasmotaGlobal.devices_present; button_idx++) { + if (bitRead(Web.light_shutter_button_mask, button_idx -1)) { continue; } // Skip non-sequential shutter button + bool power_state = bitRead(TasmotaGlobal.power, button_idx -1); + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), power_state); + WSContentSend_P(HTTP_DEVICE_STATE, 100 / cols, (power_state) ? PSTR("bold") : PSTR("normal"), fontsize, (cols < 5) ? GetStateText(power_state) : svalue); + button_ptr++; + if (button_ptr >= Web.buttons_non_light_non_shutter) { break; } } - WSContentSend_P(PSTR("")); +#ifdef USE_SONOFF_IFAN } +#endif // USE_SONOFF_IFAN + WSContentSend_P(PSTR("")); } } @@ -1946,6 +1944,7 @@ void HandleConfiguration(void) { WSContentStart_P(PSTR(D_CONFIGURATION)); WSContentSendStyle(); + WSContentSend_P(HTTP_MENU_HEAD, D_CONFIGURATION); WSContentButton(BUTTON_MODULE); WSContentButton(BUTTON_WIFI); @@ -2828,13 +2827,14 @@ void HandleInformation(void) { // }1 = // }2 = WSContentSend_P(HTTP_SCRIPT_INFO_BEGIN); + WSContentSend_P(HTTP_MENU_HEAD, D_INFORMATION); WSContentSend_P(PSTR("
")); WSContentSend_P(PSTR(D_PROGRAM_VERSION "}2%s %s %s"), TasmotaGlobal.version, TasmotaGlobal.image_name, GetCodeCores().c_str()); WSContentSend_P(PSTR("}1" D_BUILD_DATE_AND_TIME "}2%s"), GetBuildDateAndTime().c_str()); WSContentSend_P(PSTR("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_CORE_RELEASE "/%s"), ESP.getSdkVersion()); WSContentSend_P(PSTR("}1" D_UPTIME "}2%s"), GetUptime().c_str()); #ifdef ESP8266 - WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d at 0x%X"), Settings->save_flag, GetSettingsAddress()); + WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d " D_AT " 0x%X"), Settings->save_flag, GetSettingsAddress()); #endif // ESP8266 #ifdef ESP32 WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d"), Settings->save_flag); @@ -3114,6 +3114,8 @@ void HandleUpgradeFirmware(void) { WSContentStart_P(PSTR(D_FIRMWARE_UPGRADE)); WSContentSendStyle(); + WSContentSend_P(HTTP_MENU_HEAD, D_FIRMWARE_UPGRADE); + WSContentSend_P(HTTP_FORM_UPG, SettingsTextEscaped(SET_OTAURL).c_str()); #ifdef ESP32 if (EspSingleOtaPartition() && !EspRunningFactoryPartition()) { @@ -3651,6 +3653,7 @@ void HandleManagement(void) { WSContentStart_P(PSTR(D_MANAGEMENT)); WSContentSendStyle(); + WSContentSend_P(HTTP_MENU_HEAD, D_MANAGEMENT); WSContentButton(BUTTON_CONSOLE); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino b/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino index 9fe1517a9da6..3d2d998e8450 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino @@ -566,7 +566,8 @@ bool MqttPublishLib(const char* topic, const uint8_t* payload, unsigned int plen MqttClient.endPublish(); - yield(); // #3313 +// yield(); // #3313 + delay(0); return true; } @@ -701,15 +702,14 @@ void MqttPublishLoggingAsync(bool refresh) { void MqttPublishPayload(const char* topic, const char* payload, uint32_t binary_length, bool retained) { // Publish payload string or binary when binary_length set with optional retained - - SHOW_FREE_MEM(PSTR("MqttPublish")); + SHOW_FREE_MEM(PSTR("MqttPublishPayload")); bool binary_data = (binary_length > 0); if (!binary_data) { binary_length = strlen(payload); } - if (Settings->flag4.mqtt_no_retain) { // SetOption104 - Disable all MQTT retained messages, some brokers don't support it: AWS IoT, Losant + if (Settings->flag4.mqtt_no_retain) { // SetOption104 - Disable all MQTT retained messages, some brokers don't support it: AWS IoT, Losant retained = false; // Some brokers don't support retained, they will disconnect if received } @@ -793,14 +793,30 @@ void MqttPublishPayloadPrefixTopic_P(uint32_t prefix, const char* subtopic, cons prefix 5 = stat using subtopic or RESULT prefix 6 = tele using subtopic or RESULT */ - char romram[64]; + SHOW_FREE_MEM(PSTR("MqttPublishPayloadPrefixTopic_P")); +/* + char romram[64]; // Claim 64 bytes from 4k stack snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings->flag.mqtt_response) ? S_RSLT_RESULT : subtopic); // SetOption4 - Switch between MQTT RESULT or COMMAND UpperCase(romram, romram); prefix &= 3; - char stopic[TOPSZ]; + char stopic[TOPSZ]; // Claim TOPSZ bytes from 4k stack GetTopic_P(stopic, prefix, TasmotaGlobal.mqtt_topic, romram); MqttPublishPayload(stopic, payload, binary_length, retained); +*/ + // Reduce important stack usage by 200 bytes but adding 52 bytes code + char *romram = (char*)malloc(64); // Claim 64 bytes from 20k heap + strcpy_P(romram, ((prefix > 3) && !Settings->flag.mqtt_response) ? S_RSLT_RESULT : subtopic); + UpperCase(romram, romram); + + prefix &= 3; + char *htopic = (char*)malloc(TOPSZ); // Claim TOPSZ bytes from 16k heap + GetTopic_P(htopic, prefix, TasmotaGlobal.mqtt_topic, romram); + char stopic[strlen_P(htopic) +1]; // Claim only strlen_P bytes from 4k stack + strcpy_P(stopic, htopic); + free(htopic); // Free 16k heap from TOPSZ bytes + free(romram); // Free 16k heap from 64 bytes + MqttPublishPayload(stopic, payload, binary_length, retained); #if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT) if ((prefix > 0) && (Settings->flag4.awsiot_shadow) && (Mqtt.connected)) { // placeholder for SetOptionXX @@ -856,6 +872,8 @@ void MqttPublishPayloadPrefixTopicRulesProcess_P(uint32_t prefix, const char* su void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained) { // Publish //> default ResponseData string with optional retained + SHOW_FREE_MEM(PSTR("MqttPublishPrefixTopic_P")); + MqttPublishPayloadPrefixTopic_P(prefix, subtopic, ResponseData(), 0, retained); } @@ -867,6 +885,8 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic) { void MqttPublishPrefixTopicRulesProcess_P(uint32_t prefix, const char* subtopic, bool retained) { // Publish //> default ResponseData string with optional retained // then process rules + SHOW_FREE_MEM(PSTR("MqttPublishPrefixTopicRulesProcess_P")); + MqttPublishPrefixTopic_P(prefix, subtopic, retained); XdrvRulesProcess(0); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino index a26644832b07..aae18311e74d 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino @@ -278,7 +278,9 @@ void EnergyUpdateToday(void) { int32_t delta = Energy->kWhtoday_delta[i] / 1000; delta_sum_balanced += delta; Energy->kWhtoday_delta[i] -= (delta * 1000); - Energy->kWhtoday[i] += delta; + if (!Settings->flag6.no_export_energy_today || (delta > 0)) { // SetOption162 - (Energy) Do not add export energy to energy today (1) + Energy->kWhtoday[i] += delta; + } if (delta < 0) { // Export energy Energy->kWhtoday_export[i] += (delta *-1); if (Energy->kWhtoday_export[i] > 100) { @@ -368,14 +370,14 @@ void EnergyUpdateTotal(void) { } } - if ((Energy->total[i] < (Energy->import_active[i] - 0.01f)) && // We subtract a little offset of 10Wh to avoid continuous updates - Settings->flag3.hardware_energy_total) { // SetOption72 - Enable hardware energy total counter as reference (#6561) + if (Settings->flag3.hardware_energy_total && // SetOption72 - Enable hardware energy total counter as reference (#6561) + fabs(Energy->total[i] - Energy->import_active[i]) > 0.01f) { // to avoid continuous updates, check for difference of min 10Wh // The following calculation allows total usage (Energy->import_active[i]) up to +/-2147483.647 kWh RtcSettings.energy_kWhtotal_ph[i] = (int32_t)((Energy->import_active[i] * 1000) - ((Energy->kWhtoday_offset[i] + Energy->kWhtoday[i]) / 100)); Settings->energy_kWhtotal_ph[i] = RtcSettings.energy_kWhtotal_ph[i]; Energy->total[i] = Energy->import_active[i]; Settings->energy_kWhtotal_time = (!Energy->kWhtoday_offset[i]) ? LocalTime() : Midnight(); - // AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value")); + // AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: EnergyTotal updated with hardware value")); } } @@ -407,7 +409,7 @@ void Energy200ms(void) { } bool midnight = (LocalTime() == Midnight()); - if (midnight || (RtcTime.day_of_year > Settings->energy_kWhdoy)) { + if ((midnight || RtcTime.day_of_year != Settings->energy_kWhdoy) && TasmotaGlobal.uptime > 10) { Energy->kWhtoday_offset_init = true; Settings->energy_kWhdoy = RtcTime.day_of_year; @@ -1472,6 +1474,10 @@ void EnergyShow(bool json) { WSContentSend_PD(HTTP_SNS_CURRENT, WebEnergyFmt(Energy->current, Settings->flag2.current_resolution)); } WSContentSend_PD(HTTP_SNS_POWER, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution)); +// if (abs(negative_phases) != Energy->phase_count) { // Provide total power if producing power (PV) and multi phase + if (Energy->phase_count > 1) { // Provide total power if multi phase + WSContentSend_PD(HTTP_SNS_POWER_TOTAL, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution, 3)); + } if (!Energy->type_dc) { if (Energy->current_available && Energy->voltage_available) { WSContentSend_PD(HTTP_SNS_POWERUSAGE_APPARENT, WebEnergyFmt(apparent_power, Settings->flag2.wattage_resolution)); @@ -1479,9 +1485,6 @@ void EnergyShow(bool json) { WSContentSend_PD(HTTP_SNS_POWER_FACTOR, WebEnergyFmt(power_factor, 2)); } } - if (abs(negative_phases) != Energy->phase_count) { // Provide total power if producing power (PV) and multi phase - WSContentSend_PD(HTTP_SNS_POWER_TOTAL, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution, 3)); - } WSContentSend_PD(HTTP_SNS_ENERGY_TODAY, WebEnergyFmt(Energy->daily, Settings->flag2.energy_resolution, 2)); WSContentSend_PD(HTTP_SNS_ENERGY_YESTERDAY, WebEnergyFmt(energy_yesterday_ph, Settings->flag2.energy_resolution, 2)); WSContentSend_PD(HTTP_SNS_ENERGY_TOTAL, WebEnergyFmt(Energy->total, Settings->flag2.energy_resolution, 2)); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino b/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino index 6b742332907f..44d6515e34f6 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_03_esp32_energy.ino @@ -543,9 +543,11 @@ void EnergyUpdateToday(void) { int32_t delta = Energy->kWhtoday_delta[i] / 1000; delta_sum_balanced += delta; Energy->kWhtoday_delta[i] -= (delta * 1000); - Energy->kWhtoday[i] += delta; + if (!Settings->flag6.no_export_energy_today || (delta > 0)) { // SetOption162 - (Energy) Do not add export energy to energy today (1) + Energy->kWhtoday[i] += delta; + } if (delta < 0) { // Export energy - RtcEnergySettings.energy_export_kWh[i] += ((float)(delta / 100) *-1) / 1000; + RtcEnergySettings.energy_export_kWh[i] += (((float)delta / 100) *-1) / 1000; } } @@ -624,14 +626,14 @@ void EnergyUpdateTotal(void) { } } - if ((Energy->total[i] < (Energy->import_active[i] - 0.01f)) && // We subtract a little offset of 10Wh to avoid continuous updates - Settings->flag3.hardware_energy_total) { // SetOption72 - Enable hardware energy total counter as reference (#6561) + if (Settings->flag3.hardware_energy_total && // SetOption72 - Enable hardware energy total counter as reference (#6561) + fabs(Energy->total[i] - Energy->import_active[i]) > 0.01f) { // to avoid continuous updates, check for difference of min 10Wh // The following calculation allows total usage (Energy->import_active[i]) up to +/-2147483.647 kWh RtcEnergySettings.energy_total_kWh[i] = Energy->import_active[i] - (Energy->energy_today_offset_kWh[i] + ((float)Energy->kWhtoday[i] / 100000)); Energy->Settings.energy_total_kWh[i] = RtcEnergySettings.energy_total_kWh[i]; Energy->total[i] = Energy->import_active[i]; Energy->Settings.energy_kWhtotal_time = (!Energy->energy_today_offset_kWh[i]) ? LocalTime() : Midnight(); - // AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value")); + // AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: EnergyTotal updated with hardware value")); } } @@ -663,7 +665,7 @@ void Energy200ms(void) { } bool midnight = (LocalTime() == Midnight()); - if (midnight || (RtcTime.day_of_year > Energy->Settings.energy_kWhdoy)) { + if ((midnight || RtcTime.day_of_year != Energy->Settings.energy_kWhdoy) && TasmotaGlobal.uptime > 10) { Energy->kWhtoday_offset_init = true; Energy->Settings.energy_kWhdoy = RtcTime.day_of_year; @@ -1862,6 +1864,10 @@ void EnergyShow(bool json) { WSContentSend_PD(HTTP_SNS_CURRENT, WebEnergyFmt(Energy->current, Settings->flag2.current_resolution)); } WSContentSend_PD(HTTP_SNS_POWER, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution)); +// if (abs(negative_phases) != Energy->phase_count) { // Provide total power if producing power (PV) and multi phase + if (Energy->phase_count > 1) { // Provide total power if multi phase + WSContentSend_PD(HTTP_SNS_POWER_TOTAL, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution, 3)); + } if (!Energy->type_dc) { if (Energy->current_available && Energy->voltage_available) { WSContentSend_PD(HTTP_SNS_POWERUSAGE_APPARENT, WebEnergyFmt(apparent_power, Settings->flag2.wattage_resolution)); @@ -1869,9 +1875,6 @@ void EnergyShow(bool json) { WSContentSend_PD(HTTP_SNS_POWER_FACTOR, WebEnergyFmt(power_factor, 2)); } } - if (abs(negative_phases) != Energy->phase_count) { // Provide total power if producing power (PV) and multi phase - WSContentSend_PD(HTTP_SNS_POWER_TOTAL, WebEnergyFmt(Energy->active_power, Settings->flag2.wattage_resolution, 3)); - } WSContentSend_PD(HTTP_SNS_ENERGY_TODAY, WebEnergyFmt(Energy->daily_kWh, Settings->flag2.energy_resolution, 2)); WSContentSend_PD(HTTP_SNS_ENERGY_YESTERDAY, WebEnergyFmt(energy_yesterday_kWh, Settings->flag2.energy_resolution, 2)); WSContentSend_PD(HTTP_SNS_ENERGY_TOTAL, WebEnergyFmt(Energy->total, Settings->flag2.energy_resolution, 2)); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino b/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino index 047da5b43b5d..638b5bb6d4ff 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino @@ -236,6 +236,7 @@ struct LIGHT { uint8_t random = 0; uint8_t subtype = 0; // LST_ subtype uint8_t device = 0; + uint8_t devices = 0; uint8_t old_power = 1; uint8_t wakeup_active = 0; // 0=inctive, 1=on-going, 2=about to start, 3=will be triggered next cycle uint8_t fixed_color_index = 1; @@ -293,6 +294,10 @@ uint8_t LightDevice(void) return Light.device; // Make external } +uint32_t LightDevices(void) { + return Light.devices; // Make external +} + static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) { return (a < b && a < c) ? a : (b < c) ? b : c; } @@ -1249,6 +1254,8 @@ void LightInit(void) Light.fade_initialized = true; // consider fade intialized starting from black } + Light.devices = TasmotaGlobal.devices_present - Light.device +1; // Last time that devices_present is not increments by display + LightUpdateColorMapping(); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino b/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino index d0d3b61ff05e..14204476665d 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino @@ -386,7 +386,7 @@ bool ArtNetStart(void) { Settings->light_rotation = 0; Ws2812InitStrip(); } else { - Ws2812Clear(); + Ws2812Clear(true); } } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino b/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino index d43b93aaf072..dae5cfd95dd2 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino @@ -1687,14 +1687,19 @@ float evaluateExpression(const char * expression, unsigned int len) { while (index < operators_size) { if (priority == pgm_read_byte(kExpressionOperatorsPriorities + operators[index])) { // Need to calculate the operator first // Get current object value and remove the next object with current operator + +// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: index %d, v1 '%4_f', v2 '%4_f', op %d"), index, &object_values[index], &object_values[index + 1], operators[index]); + va = calculateTwoValues(object_values[index], object_values[index + 1], operators[index]); uint32_t i = index; while (i <= operators_size) { - operators[i++] = operators[i]; // operators.remove(index) +// operators[i++] = operators[i]; // operators.remove(index) - Fails on ESP32 (#22636) + operators[i] = operators[i +1]; // operators.remove(index) + i++; object_values[i] = object_values[i +1]; // object_values.remove(index + 1) } operators_size--; - object_values[index] = va; // Replace the current value with the result + object_values[index] = va; // Replace the current value with the result // AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Intermediate '%4_f'"), &object_values[index]); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_11_knx.ino b/tasmota/tasmota_xdrv_driver/xdrv_11_knx.ino index d40c11f34a45..5b537620ff65 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_11_knx.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_11_knx.ino @@ -52,19 +52,14 @@ uint8_t Settings->knx_CB_param[MAX_KNX_CB] Type of Output (set relay, #include // KNX Library -bool knx_started = false; +#define TOGGLE_INHIBIT_TIME 15 // 15*50mseg = 750mseg (inhibit time for not toggling again relays by a KNX toggle command) -address_t KNX_physs_addr; // Physical KNX address of this device -address_t KNX_addr; // KNX Address converter variable +#ifndef KNX_ENHANCEMENT_REPEAT +#define KNX_ENHANCEMENT_REPEAT 3 +#endif #define KNX_Empty 255 -#define TOGGLE_INHIBIT_TIME 15 // 15*50mseg = 750mseg (inhibit time for not toggling again relays by a KNX toggle command) - -float last_temp; -float last_hum; -uint8_t toggle_inhibit; - typedef struct __device_parameters { uint8_t type; // PARAMETER_ID. Used as type of GA = relay, button, sensor, etc, (INPUTS) @@ -116,13 +111,13 @@ device_parameters_t device_param[] = { { KNX_SLOT3 , false, false, KNX_Empty }, { KNX_SLOT4 , false, false, KNX_Empty }, { KNX_SLOT5 , false, false, KNX_Empty }, + { KNX_SCENE , false, false, KNX_Empty }, + { KNX_DIMMER , false, false, KNX_Empty }, + { KNX_COLOUR , false, false, KNX_Empty }, { KNX_SLOT6 , false, false, KNX_Empty }, { KNX_SLOT7 , false, false, KNX_Empty }, { KNX_SLOT8 , false, false, KNX_Empty }, { KNX_SLOT9 , false, false, KNX_Empty }, - { KNX_SCENE , false, false, KNX_Empty }, - { KNX_DIMMER , false, false, KNX_Empty }, - { KNX_COLOUR , false, false, KNX_Empty }, { KNX_Empty, false, false, KNX_Empty} }; @@ -158,13 +153,13 @@ const char * device_param_ga[] = { D_KNX_TX_SLOT " 3", D_KNX_TX_SLOT " 4", D_KNX_TX_SLOT " 5", + D_KNX_TX_SCENE , + D_BRIGHTLIGHT , + D_COLOR , D_KNX_TX_SLOT " 6", D_KNX_TX_SLOT " 7", D_KNX_TX_SLOT " 8", D_KNX_TX_SLOT " 9", - D_KNX_TX_SCENE , - D_BRIGHTLIGHT , - D_COLOR , nullptr }; @@ -200,14 +195,51 @@ const char *device_param_cb[] = { D_KNX_RX_SLOT " 3", D_KNX_RX_SLOT " 4", D_KNX_RX_SLOT " 5", + D_KNX_RX_SCENE , + D_BRIGHTLIGHT , + D_COLOR , D_KNX_RX_SLOT " 6", D_KNX_RX_SLOT " 7", D_KNX_RX_SLOT " 8", D_KNX_RX_SLOT " 9", - D_KNX_RX_SCENE , - D_BRIGHTLIGHT , - D_COLOR , -nullptr + nullptr +}; + +uint8_t knx_slot_xref[] = { + KNX_SLOT1, + KNX_SLOT2, + KNX_SLOT3, + KNX_SLOT4, + KNX_SLOT5, + KNX_SLOT6, + KNX_SLOT7, + KNX_SLOT8, + KNX_SLOT9 +}; + +uint8_t knx_select_nice_list[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + KNX_TEMPERATURE -1, + KNX_HUMIDITY -1, + KNX_ENERGY_VOLTAGE -1, + KNX_ENERGY_CURRENT -1, + KNX_ENERGY_POWER -1, + KNX_ENERGY_POWERFACTOR -1, + KNX_ENERGY_DAILY -1, + KNX_ENERGY_YESTERDAY -1, + KNX_ENERGY_TOTAL -1, + KNX_SLOT1 -1, + KNX_SLOT2 -1, + KNX_SLOT3 -1, + KNX_SLOT4 -1, + KNX_SLOT5 -1, + KNX_SLOT6 -1, + KNX_SLOT7 -1, + KNX_SLOT8 -1, + KNX_SLOT9 -1, + KNX_SCENE -1, + KNX_DIMMER -1, + KNX_COLOUR -1 }; // Commands @@ -224,19 +256,27 @@ nullptr #define D_CMND_KNXTXDOUBLE "Tx_Double" // 4 bytes float (DPT14) #define D_CMND_KNXTXBYTE "Tx_Byte" // 1 byte unsigned (DPT5) - const char kKnxCommands[] PROGMEM = D_PRFX_KNX "|" // Prefix - D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB "|" D_CMND_KNXTXSCENE "|" + D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|" + D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB "|" D_CMND_KNXTXSCENE "|" D_CMND_KNXTXFLOAT "|" D_CMND_KNXTXDOUBLE "|" D_CMND_KNXTXBYTE; void (* const KnxCommand[])(void) PROGMEM = { - &CmndKnxTxCmnd, &CmndKnxTxVal, &CmndKnxEnabled, &CmndKnxEnhanced, &CmndKnxPa, &CmndKnxGa, &CmndKnxCb, &CmndKnxTxScene, + &CmndKnxTxCmnd, &CmndKnxTxVal, &CmndKnxEnabled, &CmndKnxEnhanced, + &CmndKnxPa, &CmndKnxGa, &CmndKnxCb, &CmndKnxTxScene, &CmndKnxTxFloat, &CmndKnxTxVal, &CmndKnxTxByte}; -#ifndef KNX_ENHANCEMENT_REPEAT -#define KNX_ENHANCEMENT_REPEAT 3 -#endif + address_t KNX_physs_addr; // Physical KNX address of this device + address_t KNX_addr; // KNX Address converter variable + +struct Knx_t { + float last_temp; + float last_hum; + uint8_t toggle_inhibit; + bool started = false; +} Knx; +/*********************************************************************************************/ void KNX_Send_1bit(address_t const &receiver, uint8_t value, knx_command_type_t ct) { @@ -312,6 +352,7 @@ void KNX_Send_6byte_color(address_t const &receiver, uint8_t* color, knx_command #define KNX_WRITE_6BYTE_COLOR(r,rgbw) KNX_Send_6byte_color((r),(rgbw),KNX_CT_WRITE) #define KNX_ANSWER_6BYTE_COLOR(r,rgbw) KNX_Send_6byte_color((r),(rgbw),KNX_CT_ANSWER) +/*********************************************************************************************/ uint8_t KNX_GA_Search( uint8_t param, uint8_t start = 0 ) { @@ -550,6 +591,7 @@ bool KNX_CONFIG_NOT_MATCH(void) return false; } +/*********************************************************************************************/ void KNXStart(void) { @@ -711,41 +753,46 @@ void KNX_CB_Action(message_t const &msg, void *arg) } else if (chan->type < 17) // Toggle Relays { - if (!toggle_inhibit) { + if (!Knx.toggle_inhibit) { ExecuteCommandPower((chan->type) -8, POWER_TOGGLE, SRC_KNX); if (Settings->flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; + Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME; } } } #if defined(USE_RULES) || defined(USE_SCRIPT) - else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT9)) // KNX RX SLOTs (write command) + else if (((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) || + ((chan->type >= KNX_SLOT6) && (chan->type <= KNX_SLOT9))) // KNX RX SLOTs (write command) { - if (!toggle_inhibit) { + if (!Knx.toggle_inhibit) { + uint32_t slot_offset = KNX_SLOT1; + if (chan->type >= KNX_SLOT6) { + slot_offset = KNX_SLOT6; + } char command[35]; //4294967295.00 13chars + 17 if (msg.data_len == 1) { // Command received - snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - KNX_SLOT1 + 1 ), msg.data[0]); + snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - slot_offset + 1 ), msg.data[0]); } else { // Value received - snprintf_P(command, sizeof(command), PSTR("event KNXRX_VAL%d=%s"), ((chan->type) - KNX_SLOT1 + 1 ), tempchar); + snprintf_P(command, sizeof(command), PSTR("event KNXRX_VAL%d=%s"), ((chan->type) - slot_offset + 1 ), tempchar); } ExecuteCommand(command, SRC_KNX); if (Settings->flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; + Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME; } } } else if (chan->type == KNX_SCENE) // KNX RX SCENE SLOT (write command) { - if (!toggle_inhibit) { + if (!Knx.toggle_inhibit) { char command[25]; // Value received snprintf_P(command, sizeof(command), PSTR("event KNX_SCENE=%s"), tempchar); ExecuteCommand(command, SRC_KNX); if (Settings->flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; + Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME; } } } @@ -753,25 +800,25 @@ void KNX_CB_Action(message_t const &msg, void *arg) #ifdef USE_LIGHT else if (chan->type == KNX_DIMMER) // KNX RX DIMMER SLOT (write command) { - if (!toggle_inhibit) { + if (!Knx.toggle_inhibit) { char command[25]; // Value received snprintf_P(command, sizeof(command), PSTR("Dimmer %s"), tempchar); ExecuteCommand(command, SRC_KNX); if (Settings->flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; + Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME; } } } else if (chan->type == KNX_COLOUR) // KNX RX COLOUR_RGB/RGBW SLOT (write command) { - if (!toggle_inhibit) { + if (!Knx.toggle_inhibit) { char command[25]; // Value received snprintf_P(command, sizeof(command), PSTR("Color #%s"), tempchar); ExecuteCommand(command, SRC_KNX); if (Settings->flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; + Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME; } } } @@ -784,17 +831,17 @@ void KNX_CB_Action(message_t const &msg, void *arg) else if (chan->type == KNX_TEMPERATURE) // Reply Temperature { #ifdef KNX_USE_DPT9 - KNX_ANSWER_2BYTE_FLOAT(msg.received_on, last_temp); + KNX_ANSWER_2BYTE_FLOAT(msg.received_on, Knx.last_temp); #else - KNX_ANSWER_4BYTE_FLOAT(msg.received_on, last_temp); + KNX_ANSWER_4BYTE_FLOAT(msg.received_on, Knx.last_temp); #endif // KNX_USE_DPT9 } else if (chan->type == KNX_HUMIDITY) // Reply Humidity { #ifdef KNX_USE_DPT9 - KNX_ANSWER_2BYTE_FLOAT(msg.received_on, last_hum); + KNX_ANSWER_2BYTE_FLOAT(msg.received_on, Knx.last_hum); #else - KNX_ANSWER_4BYTE_FLOAT(msg.received_on, last_hum); + KNX_ANSWER_4BYTE_FLOAT(msg.received_on, Knx.last_hum); #endif // KNX_USE_DPT9 } #if defined(USE_ENERGY_SENSOR) @@ -830,14 +877,19 @@ void KNX_CB_Action(message_t const &msg, void *arg) #if defined(USE_RULES) || defined(USE_SCRIPT) - else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT9)) // KNX RX SLOTs (read command) + else if (((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) || + ((chan->type >= KNX_SLOT6) && (chan->type <= KNX_SLOT9))) // KNX RX SLOTs (read command) { - if (!toggle_inhibit) { + if (!Knx.toggle_inhibit) { + uint32_t slot_offset = KNX_SLOT1; + if (chan->type >= KNX_SLOT6) { + slot_offset = KNX_SLOT6; + } char command[25]; - snprintf_P(command, sizeof(command), PSTR("event KNXRX_REQ%d"), ((chan->type) - KNX_SLOT1 + 1 ) ); + snprintf_P(command, sizeof(command), PSTR("event KNXRX_REQ%d"), ((chan->type) - slot_offset + 1 ) ); ExecuteCommand(command, SRC_KNX); if (Settings->flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; + Knx.toggle_inhibit = TOGGLE_INHIBIT_TIME; } } } @@ -955,10 +1007,10 @@ void KnxSensor(uint8_t sensor_type, float value) { if (sensor_type == KNX_TEMPERATURE) { - last_temp = value; + Knx.last_temp = value; } else if (sensor_type == KNX_HUMIDITY) { - last_hum = value; + Knx.last_hum = value; } if (!(Settings->flag.knx_enabled)) { return; } @@ -1149,9 +1201,9 @@ void HandleKNXConfiguration(void) WSContentSend_P(HTTP_FORM_KNX2); for (uint32_t i = 0; i < KNX_MAX_device_param ; i++) { - if ( device_param[i].show ) + if ( device_param[knx_select_nice_list[i]].show ) { - WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_ga[i]); + WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[knx_select_nice_list[i]].type, device_param_ga[knx_select_nice_list[i]]); } } WSContentSend_P(PSTR(" -> ")); @@ -1176,9 +1228,9 @@ void HandleKNXConfiguration(void) // Check How many Relays are available and add: RelayX and TogleRelayX if ( (i > 8) && (i < 16) ) { j=i-8; } else { j=i; } if ( i == 8 ) { j = 0; } - if ( device_param[j].show ) + if ( device_param[knx_select_nice_list[j]].show ) { - WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_cb[i]); + WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[knx_select_nice_list[i]].type, device_param_cb[knx_select_nice_list[i]]); } } WSContentSend_P(PSTR(" ")); @@ -1254,20 +1306,21 @@ void KNX_Save_Settings(void) void CmndKnxTxCmnd(void) { + // KNX_WRITE_1BIT if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings->flag.knx_enabled) { // XdrvMailbox.index <- KNX SLOT to use // XdrvMailbox.payload <- data to send // Search all the registered GA that has that output (variable: KNX SLOTx) as parameter - uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); + uint8_t i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1]); while ( i != KNX_Empty ) { KNX_addr.value = Settings->knx_GA_addr[i]; KNX_WRITE_1BIT(KNX_addr, !(XdrvMailbox.payload == 0)); AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d/%d/%d"), - device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0), + device_param_ga[knx_slot_xref[XdrvMailbox.index -1] -1], !(XdrvMailbox.payload == 0), KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); + i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1], i + 1); } ResponseCmndIdxChar (XdrvMailbox.data ); } @@ -1275,24 +1328,23 @@ void CmndKnxTxCmnd(void) void CmndKnxTxVal(void) { + // KNX_WRITE_4BYTE_FLOAT if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings->flag.knx_enabled) { // XdrvMailbox.index <- KNX SLOT to use // XdrvMailbox.payload <- data to send // Search all the registered GA that has that output (variable: KNX SLOTx) as parameter - uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); + uint8_t i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1]); while ( i != KNX_Empty ) { KNX_addr.value = Settings->knx_GA_addr[i]; float tempvar = CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar,2,XdrvMailbox.data); - KNX_WRITE_4BYTE_FLOAT(KNX_addr, tempvar); - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d/%d/%d"), - device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data, + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %2_f " D_SENT_TO " %d/%d/%d"), + device_param_ga[knx_slot_xref[XdrvMailbox.index -1] -1], &tempvar, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); + i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1], i + 1); } ResponseCmndIdxChar (XdrvMailbox.data ); } @@ -1301,24 +1353,23 @@ void CmndKnxTxVal(void) void CmndKnxTxFloat(void) { + // KNX_WRITE_2BYTE_FLOAT if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings->flag.knx_enabled) { // XdrvMailbox.index <- KNX SLOT to use // XdrvMailbox.payload <- data to send // Search all the registered GA that has that output (variable: KNX SLOTx) as parameter - uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); + uint8_t i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1]); while ( i != KNX_Empty ) { KNX_addr.value = Settings->knx_GA_addr[i]; float tempvar = CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar,2,XdrvMailbox.data); - KNX_WRITE_2BYTE_FLOAT(KNX_addr, tempvar); - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d/%d/%d (2 bytes float)"), - device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data, + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %2_f " D_SENT_TO " %d/%d/%d (2 bytes float)"), + device_param_ga[knx_slot_xref[XdrvMailbox.index -1] -1], &tempvar, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); + i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1], i + 1); } ResponseCmndIdxChar (XdrvMailbox.data ); } @@ -1326,24 +1377,23 @@ void CmndKnxTxFloat(void) void CmndKnxTxByte(void) { + // KNX_WRITE_1BYTE_UINT if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings->flag.knx_enabled) { // XdrvMailbox.index <- KNX SLOT to use // XdrvMailbox.payload <- data to send // Search all the registered GA that has that output (variable: KNX SLOTx) as parameter - uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); + uint8_t i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1]); while ( i != KNX_Empty ) { KNX_addr.value = Settings->knx_GA_addr[i]; uint8_t tempvar = TextToInt(XdrvMailbox.data); - dtostrfd(tempvar,0,XdrvMailbox.data); - KNX_WRITE_1BYTE_UINT(KNX_addr, tempvar); - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d/%d/%d (1 byte unsigned)"), - device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data, + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d/%d/%d (1 byte unsigned)"), + device_param_ga[knx_slot_xref[XdrvMailbox.index -1] -1], tempvar, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); + i = KNX_GA_Search(knx_slot_xref[XdrvMailbox.index -1], i + 1); } ResponseCmndIdxChar (XdrvMailbox.data ); } @@ -1358,12 +1408,10 @@ void CmndKnxTxScene(void) KNX_addr.value = Settings->knx_GA_addr[i]; uint8_t tempvar = TextToInt(XdrvMailbox.data); - dtostrfd(tempvar,0,XdrvMailbox.data); - KNX_WRITE_1BYTE_UINT(KNX_addr, tempvar); - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d/%d/%d"), - device_param_ga[KNX_SCENE-1], XdrvMailbox.data, + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d/%d/%d"), + device_param_ga[KNX_SCENE-1], tempvar, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); ResponseCmndIdxChar (XdrvMailbox.data); } @@ -1374,6 +1422,9 @@ void CmndKnxEnabled(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { Settings->flag.knx_enabled = XdrvMailbox.payload; + if (!Settings->flag.knx_enabled) { + Knx.started = false; + } } ResponseCmndChar (GetStateText(Settings->flag.knx_enabled) ); } @@ -1522,8 +1573,8 @@ bool Xdrv11(uint32_t function) if (!TasmotaGlobal.global_state.network_down) { knx.loop(); } // Process knx events break; case FUNC_EVERY_50_MSECOND: - if (toggle_inhibit) { - toggle_inhibit--; + if (Knx.toggle_inhibit) { + Knx.toggle_inhibit--; } break; case FUNC_ANY_KEY: @@ -1546,13 +1597,13 @@ bool Xdrv11(uint32_t function) KNX_INIT(); break; case FUNC_NETWORK_UP: - if (!knx_started && Settings->flag.knx_enabled) { // CMND_KNX_ENABLED + if (!Knx.started && Settings->flag.knx_enabled) { // CMND_KNX_ENABLED KNXStart(); - knx_started = true; + Knx.started = true; } break; case FUNC_NETWORK_DOWN: - knx_started = false; + Knx.started = false; break; // case FUNC_SET_POWER: // break; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_13_display.ino b/tasmota/tasmota_xdrv_driver/xdrv_13_display.ino index 4ef3786f6c3d..0153e0f3074c 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_13_display.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_13_display.ino @@ -269,6 +269,12 @@ bool disp_subscribed = false; /*********************************************************************************************/ +uint32_t DisplayDevices(void) { + return (disp_device) ? 1 : 0; +} + +/*********************************************************************************************/ + void DisplayClear(void) { if (renderer) { renderer->fillScreen(bg_color); @@ -1846,57 +1852,53 @@ void DisplayLocalSensor(void) \*********************************************************************************************/ void DisplayInitDriver(void) { + uint32_t display_model = Settings->display_model; + Settings->display_model = 0; // Test if any display_model is available XdspCall(FUNC_DISPLAY_INIT_DRIVER); + if (Settings->display_model && display_model) { // If any model found keep using user configured one for backward compatibility + Settings->display_model = display_model; + } + if (!Settings->display_model) { return; } -// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Display model %d"), Settings->display_model); +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DSP: Model %d"), Settings->display_model); - if (Settings->display_model) { -// ApplyDisplayDimmer(); // Not allowed here. Way too early in init sequence. Global power state has not been set at this point in time +// ApplyDisplayDimmer(); // Not allowed here. Way too early in init sequence. Global power state has not been set at this point in time #ifdef USE_MULTI_DISPLAY - Set_display(0); + Set_display(0); #endif // USE_MULTI_DISPLAY - if (renderer) { - renderer->setTextFont(Settings->display_font); - renderer->setTextSize(Settings->display_size); - // force opaque mode - renderer->setDrawMode(0); + if (renderer) { + renderer->setTextFont(Settings->display_font); + renderer->setTextSize(Settings->display_size); + // force opaque mode + renderer->setDrawMode(0); - for (uint32_t cnt = 0; cnt < (MAX_INDEXCOLORS - PREDEF_INDEXCOLORS); cnt++) { - index_colors[cnt] = 0; - } + for (uint32_t cnt = 0; cnt < (MAX_INDEXCOLORS - PREDEF_INDEXCOLORS); cnt++) { + index_colors[cnt] = 0; } + } #ifdef USE_DT_VARS - free_dt_vars(); + free_dt_vars(); #endif #ifdef USE_UFILESYS - Display_Text_From_File(DISP_BATCH_FILE); + Display_Text_From_File(DISP_BATCH_FILE); #endif #ifdef USE_GRAPH - for (uint8_t count = 0; count < NUM_GRAPHS; count++) { graph[count] = 0; } + for (uint8_t count = 0; count < NUM_GRAPHS; count++) { graph[count] = 0; } #endif - UpdateDevicesPresent(1); - if (!PinUsed(GPIO_BACKLIGHT)) { - if ((LT_PWM1 == TasmotaGlobal.light_type) && // Single PWM light channel - ((4 == Settings->display_model) || // ILI9341 legacy - (17 == Settings->display_model)) // Universal - ) { - UpdateDevicesPresent(-1); // Assume PWM channel is used for backlight - } - } - disp_device = TasmotaGlobal.devices_present; + UpdateDevicesPresent(1); + disp_device = TasmotaGlobal.devices_present; #ifndef USE_DISPLAY_MODES1TO5 - Settings->display_mode = 0; + Settings->display_mode = 0; #else - DisplayLogBufferInit(); + DisplayLogBufferInit(); #endif // USE_DISPLAY_MODES1TO5 - } } void DisplaySetPower(void) { @@ -2169,7 +2171,7 @@ void CmndDisplayText(void) { void CmndDisplayClear(void) { DisplayClear(); - ResponseCmndChar(XdrvMailbox.data); + ResponseCmndDone(); } void CmndDisplayNumber(void) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_27_esp32_shutter.ino b/tasmota/tasmota_xdrv_driver/xdrv_27_esp32_shutter.ino index 2be8c6f9d9ed..a26d1a6b1a05 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_27_esp32_shutter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_27_esp32_shutter.ino @@ -126,7 +126,10 @@ const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|" D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPTOGGLEDIR "|" D_CMND_SHUTTER_STOPPOSITION "|" D_CMND_SHUTTER_INCDEC "|" - D_CMND_SHUTTER_UNITTEST "|" D_CMND_SHUTTER_TILTCONFIG "|" D_CMND_SHUTTER_SETTILT "|" D_CMND_SHUTTER_TILTINCDEC "|" D_CMND_SHUTTER_MOTORSTOP "|" D_CMND_SHUTTER_SETUP "|" +#ifdef SHUTTER_UNITTEST + D_CMND_SHUTTER_UNITTEST "|" +#endif // SHUTTER_UNITTEST + D_CMND_SHUTTER_TILTCONFIG "|" D_CMND_SHUTTER_SETTILT "|" D_CMND_SHUTTER_TILTINCDEC "|" D_CMND_SHUTTER_MOTORSTOP "|" D_CMND_SHUTTER_SETUP "|" D_CMD_SHUTTER_EXTRASTOPRELAY; void (* const ShutterCommand[])(void) PROGMEM = { @@ -135,7 +138,10 @@ void (* const ShutterCommand[])(void) PROGMEM = { &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons, &CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopToggleDir, &CmndShutterStopPosition, &CmndShutterIncDec, - &CmndShutterUnitTest,&CmndShutterTiltConfig,&CmndShutterSetTilt,&CmndShutterTiltIncDec,&CmndShutterMotorStop,&CmndShutterSetup,&CmndShutterExtraStopPulseRelay +#ifdef SHUTTER_UNITTEST + &CmndShutterUnitTest, +#endif // SHUTTER_UNITTEST + &CmndShutterTiltConfig, &CmndShutterSetTilt, &CmndShutterTiltIncDec, &CmndShutterMotorStop, &CmndShutterSetup, &CmndShutterExtraStopPulseRelay }; const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d,\"Target\":%d,\"Tilt\":%d}"; @@ -2268,6 +2274,62 @@ void CmndShutterToggleDir(void) ShutterToggle(true); } +#ifdef SHUTTER_UNITTEST +void CmndShutterUnitTest(void) { + int16_t input_percent[10] = {-5,0,10,26,35,55,80,99,100,105}; + int16_t output_percent[10] = {0,0,10,26,35,55,80,99,100,100}; + uint32_t result_percent[2][2][10] = {{{0,0,24000,62400,84000,132000,192000,237600,240000,240000}, + {0,0,360000,936000,1260000,1980000,2880000,3564000,3600000,3600000}}, + {{0,0,76296,100000,113333,174299,205795,237983,240000,240000}, + {0,0,1144444,1500000,1700000,2614488,3086929,3569748,3600000,3600000}}}; + + uint32_t result = 0; + char svalue[50]; // Command and number parameter + ShutterSettings.shuttercoeff[0][0] = 0; + for (uint8_t i=0; i<2 ; i++){ + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12); + ExecuteCommand(svalue, SRC_SHUTTER); + ShutterInit(); + for (uint8_t j=0; j<2 ; j++){ + for (uint8_t k=0; k<10 ; k++){ + result += (result_percent[i][j][k] == ShutterPercentToRealPosition(input_percent[k] , 0) ? 0 : 1); + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition error %d: %d <-> %d"),result, ShutterPercentToRealPosition(input_percent[k] , 0), result_percent[i][j][k]); + } + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180); + ExecuteCommand(svalue, SRC_SHUTTER); + } + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210"); + ExecuteCommand(svalue, SRC_SHUTTER); + } + if (!result){ + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: PASS")); + } else { + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: FAIL")); + } + ShutterSettings.shuttercoeff[0][0] = 0; + for (uint8_t i=0; i<2 ; i++){ + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12); + ExecuteCommand(svalue, SRC_SHUTTER); + ShutterInit(); + for (uint8_t j=0; j<2 ; j++){ + for (uint8_t k=0; k<10 ; k++){ + result += (output_percent[k] == ShutterRealToPercentPosition(result_percent[i][j][k] , 0) ? 0 : 1); + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition error %d: %d <-> %d"),result, ShutterRealToPercentPosition(result_percent[i][j][k] , 0), output_percent[k]); + } + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180); + ExecuteCommand(svalue, SRC_SHUTTER); + } + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210"); + ExecuteCommand(svalue, SRC_SHUTTER); + } + if (!result){ + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: PASS")); + } else { + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: FAIL")); + } +} +#endif // SHUTTER_UNITTEST + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -2285,6 +2347,13 @@ bool Xdrv27(uint32_t function) char stemp1[10]; power_t save_powermatrix; switch (function) { + case FUNC_EVERY_50_MSECOND: + ShutterUpdatePosition(); + break; + case FUNC_EVERY_SECOND: + //case FUNC_EVERY_250_MSECOND: + ShutterReportPosition(false, MAX_SHUTTERS_ESP32); + break; case FUNC_RESTORE_SETTINGS: result = ShutterSettingsRestore(); break; @@ -2293,18 +2362,13 @@ bool Xdrv27(uint32_t function) break; case FUNC_PRE_INIT: ShutterSettingsLoad(0); + break; + case FUNC_INIT: ShutterInit(); break; case FUNC_RESET_SETTINGS: ShutterSettingsLoad(1); break; - case FUNC_EVERY_50_MSECOND: - ShutterUpdatePosition(); - break; - case FUNC_EVERY_SECOND: - //case FUNC_EVERY_250_MSECOND: - ShutterReportPosition(false, MAX_SHUTTERS_ESP32); - break; case FUNC_COMMAND: for (uint8_t i = counter; i <= counterend; i++) { XdrvMailbox.index = i; @@ -2319,7 +2383,7 @@ bool Xdrv27(uint32_t function) } break; case FUNC_JSON_APPEND: - if (!ShutterGlobal.sensor_data_reported) { + if (!ShutterGlobal.sensor_data_reported || TasmotaGlobal.tele_period == 0) { ShutterGlobal.sensor_data_reported = true; for (uint8_t i = 0; i < TasmotaGlobal.shutters_present; i++) { ResponseAppend_P(","); @@ -2391,64 +2455,5 @@ bool Xdrv27(uint32_t function) return result; } -#endif //USE_SHUTTER - -#ifdef SHUTTER_UNITTEST -void CmndShutterUnitTest(void) { - int16_t input_percent[10] = {-5,0,10,26,35,55,80,99,100,105}; - int16_t output_percent[10] = {0,0,10,26,35,55,80,99,100,100}; - uint32_t result_percent[2][2][10] = {{{0,0,24000,62400,84000,132000,192000,237600,240000,240000}, - {0,0,360000,936000,1260000,1980000,2880000,3564000,3600000,3600000}}, - {{0,0,76296,100000,113333,174299,205795,237983,240000,240000}, - {0,0,1144444,1500000,1700000,2614488,3086929,3569748,3600000,3600000}}}; - - uint32_t result = 0; - char svalue[50]; // Command and number parameter - ShutterSettings.shuttercoeff[0][0] = 0; - for (uint8_t i=0; i<2 ; i++){ - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12); - ExecuteCommand(svalue, SRC_SHUTTER); - ShutterInit(); - for (uint8_t j=0; j<2 ; j++){ - for (uint8_t k=0; k<10 ; k++){ - result += (result_percent[i][j][k] == ShutterPercentToRealPosition(input_percent[k] , 0) ? 0 : 1); - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition error %d: %d <-> %d"),result, ShutterPercentToRealPosition(input_percent[k] , 0), result_percent[i][j][k]); - } - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180); - ExecuteCommand(svalue, SRC_SHUTTER); - } - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210"); - ExecuteCommand(svalue, SRC_SHUTTER); - } - if (!result){ - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: PASS")); - } else { - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: FAIL")); - } - ShutterSettings.shuttercoeff[0][0] = 0; - for (uint8_t i=0; i<2 ; i++){ - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12); - ExecuteCommand(svalue, SRC_SHUTTER); - ShutterInit(); - for (uint8_t j=0; j<2 ; j++){ - for (uint8_t k=0; k<10 ; k++){ - result += (output_percent[k] == ShutterRealToPercentPosition(result_percent[i][j][k] , 0) ? 0 : 1); - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition error %d: %d <-> %d"),result, ShutterRealToPercentPosition(result_percent[i][j][k] , 0), output_percent[k]); - } - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180); - ExecuteCommand(svalue, SRC_SHUTTER); - } - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210"); - ExecuteCommand(svalue, SRC_SHUTTER); - } - if (!result){ - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: PASS")); - } else { - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: FAIL")); - } -} -#else -void CmndShutterUnitTest(void) {} -#endif // SHUTTER_UNITTEST - +#endif // USE_SHUTTER #endif // ESP32 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino index da7c7a138b0c..28292483951e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino @@ -19,6 +19,7 @@ #ifdef ESP8266 #ifdef USE_SHUTTER +//#if defined(USE_SHUTTER) && !defined(USE_UFILESYS) /*********************************************************************************************\ * Shutter or Blind support using two consecutive relays \*********************************************************************************************/ @@ -65,7 +66,10 @@ const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|" D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPTOGGLEDIR "|" D_CMND_SHUTTER_STOPPOSITION "|" D_CMND_SHUTTER_INCDEC "|" - D_CMND_SHUTTER_UNITTEST "|" D_CMND_SHUTTER_TILTCONFIG "|" D_CMND_SHUTTER_SETTILT "|" D_CMND_SHUTTER_TILTINCDEC "|" D_CMND_SHUTTER_MOTORSTOP; +#ifdef SHUTTER_UNITTEST + D_CMND_SHUTTER_UNITTEST "|" +#endif // SHUTTER_UNITTEST + D_CMND_SHUTTER_TILTCONFIG "|" D_CMND_SHUTTER_SETTILT "|" D_CMND_SHUTTER_TILTINCDEC "|" D_CMND_SHUTTER_MOTORSTOP; void (* const ShutterCommand[])(void) PROGMEM = { &CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterToggleDir, &CmndShutterStop, &CmndShutterPosition, @@ -73,7 +77,10 @@ void (* const ShutterCommand[])(void) PROGMEM = { &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons, &CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopToggleDir, &CmndShutterStopPosition, &CmndShutterIncDec, - &CmndShutterUnitTest,&CmndShutterTiltConfig,&CmndShutterSetTilt,&CmndShutterTiltIncDec,&CmndShutterMotorStop}; +#ifdef SHUTTER_UNITTEST + &CmndShutterUnitTest, +#endif // SHUTTER_UNITTEST + &CmndShutterTiltConfig, &CmndShutterSetTilt, &CmndShutterTiltIncDec, &CmndShutterMotorStop}; const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d,\"Target\":%d,\"Tilt\":%d}"; const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}"; @@ -1875,6 +1882,62 @@ void CmndShutterMotorStop(void) } } +#ifdef SHUTTER_UNITTEST +void CmndShutterUnitTest(void) { + int16_t input_percent[10] = {-5,0,10,26,35,55,80,99,100,105}; + int16_t output_percent[10] = {0,0,10,26,35,55,80,99,100,100}; + uint32_t result_percent[2][2][10] = {{{0,0,24000,62400,84000,132000,192000,237600,240000,240000}, + {0,0,360000,936000,1260000,1980000,2880000,3564000,3600000,3600000}}, + {{0,0,76296,100000,113333,174299,205795,237983,240000,240000}, + {0,0,1144444,1500000,1700000,2614488,3086929,3569748,3600000,3600000}}}; + + uint32_t result = 0; + char svalue[50]; // Command and number parameter + Settings->shuttercoeff[0][0] = 0; + for (uint8_t i=0; i<2 ; i++){ + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12); + ExecuteCommand(svalue, SRC_SHUTTER); + ShutterInit(); + for (uint8_t j=0; j<2 ; j++){ + for (uint8_t k=0; k<10 ; k++){ + result += (result_percent[i][j][k] == ShutterPercentToRealPosition(input_percent[k] , 0) ? 0 : 1); + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition error %d: %d <-> %d"),result, ShutterPercentToRealPosition(input_percent[k] , 0), result_percent[i][j][k]); + } + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180); + ExecuteCommand(svalue, SRC_SHUTTER); + } + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210"); + ExecuteCommand(svalue, SRC_SHUTTER); + } + if (!result){ + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: PASS")); + } else { + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: FAIL")); + } + Settings->shuttercoeff[0][0] = 0; + for (uint8_t i=0; i<2 ; i++){ + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12); + ExecuteCommand(svalue, SRC_SHUTTER); + ShutterInit(); + for (uint8_t j=0; j<2 ; j++){ + for (uint8_t k=0; k<10 ; k++){ + result += (output_percent[k] == ShutterRealToPercentPosition(result_percent[i][j][k] , 0) ? 0 : 1); + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition error %d: %d <-> %d"),result, ShutterRealToPercentPosition(result_percent[i][j][k] , 0), output_percent[k]); + } + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180); + ExecuteCommand(svalue, SRC_SHUTTER); + } + snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210"); + ExecuteCommand(svalue, SRC_SHUTTER); + } + if (!result){ + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: PASS")); + } else { + AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: FAIL")); + } +} +#endif // SHUTTER_UNITTEST + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -1892,9 +1955,6 @@ bool Xdrv27(uint32_t function) char stemp1[10]; power_t save_powermatrix; switch (function) { - case FUNC_PRE_INIT: - ShutterInit(); - break; case FUNC_EVERY_50_MSECOND: ShutterUpdatePosition(); break; @@ -1902,6 +1962,9 @@ bool Xdrv27(uint32_t function) //case FUNC_EVERY_250_MSECOND: ShutterReportPosition(false, MAX_SHUTTERS); break; + case FUNC_INIT: + ShutterInit(); + break; case FUNC_COMMAND: for (uint8_t i = counter; i <= counterend; i++) { XdrvMailbox.index = i; @@ -1911,7 +1974,7 @@ bool Xdrv27(uint32_t function) } break; case FUNC_JSON_APPEND: - if (!sensor_data_reported) { + if (!sensor_data_reported || TasmotaGlobal.tele_period == 0) { sensor_data_reported = true; for (uint8_t i = 0; i < TasmotaGlobal.shutters_present; i++) { uint8_t position = ShutterRealToPercentPosition(Shutter[i].real_position, i); @@ -1974,64 +2037,5 @@ bool Xdrv27(uint32_t function) return result; } -#endif //USE_SHUTTER - -#ifdef SHUTTER_UNITTEST -void CmndShutterUnitTest(void) { - int16_t input_percent[10] = {-5,0,10,26,35,55,80,99,100,105}; - int16_t output_percent[10] = {0,0,10,26,35,55,80,99,100,100}; - uint32_t result_percent[2][2][10] = {{{0,0,24000,62400,84000,132000,192000,237600,240000,240000}, - {0,0,360000,936000,1260000,1980000,2880000,3564000,3600000,3600000}}, - {{0,0,76296,100000,113333,174299,205795,237983,240000,240000}, - {0,0,1144444,1500000,1700000,2614488,3086929,3569748,3600000,3600000}}}; - - uint32_t result = 0; - char svalue[50]; // Command and number parameter - Settings->shuttercoeff[0][0] = 0; - for (uint8_t i=0; i<2 ; i++){ - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12); - ExecuteCommand(svalue, SRC_SHUTTER); - ShutterInit(); - for (uint8_t j=0; j<2 ; j++){ - for (uint8_t k=0; k<10 ; k++){ - result += (result_percent[i][j][k] == ShutterPercentToRealPosition(input_percent[k] , 0) ? 0 : 1); - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition error %d: %d <-> %d"),result, ShutterPercentToRealPosition(input_percent[k] , 0), result_percent[i][j][k]); - } - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180); - ExecuteCommand(svalue, SRC_SHUTTER); - } - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210"); - ExecuteCommand(svalue, SRC_SHUTTER); - } - if (!result){ - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: PASS")); - } else { - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterPercentToRealPosition: FAIL")); - } - Settings->shuttercoeff[0][0] = 0; - for (uint8_t i=0; i<2 ; i++){ - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 12); - ExecuteCommand(svalue, SRC_SHUTTER); - ShutterInit(); - for (uint8_t j=0; j<2 ; j++){ - for (uint8_t k=0; k<10 ; k++){ - result += (output_percent[k] == ShutterRealToPercentPosition(result_percent[i][j][k] , 0) ? 0 : 1); - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition error %d: %d <-> %d"),result, ShutterRealToPercentPosition(result_percent[i][j][k] , 0), output_percent[k]); - } - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_OPENTIME "%d %d"), 1, 180); - ExecuteCommand(svalue, SRC_SHUTTER); - } - snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_CLIBRATION "%d %s"), 1, "15 83 105 185 210"); - ExecuteCommand(svalue, SRC_SHUTTER); - } - if (!result){ - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: PASS")); - } else { - AddLog(LOG_LEVEL_ERROR, PSTR("SHT: ShutterRealToPercentPosition: FAIL")); - } -} -#else -void CmndShutterUnitTest(void) {} -#endif - +#endif // USE_SHUTTER #endif // ESP8266 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino b/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino index 874cbd772b24..b0f4420ce5e3 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574_v2.ino @@ -374,6 +374,7 @@ void Pcf8574Power(void) { rpower >>= Pcf8574.relay_offset; relay_max = Pcf8574.relay_max; } + DevicesPresentNonDisplayOrLight(relay_max); // Skip display and/or light(s) for (uint32_t index = 0; index < relay_max; index++) { power_t state = rpower &1; if (Pcf8574PinUsed(GPIO_REL1, index)) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_31_tasmota_client.ino b/tasmota/tasmota_xdrv_driver/xdrv_31_tasmota_client.ino index 345fc9dbac82..ef0a5b4d32fb 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_31_tasmota_client.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_31_tasmota_client.ino @@ -444,8 +444,10 @@ void TasmotaClient_Show(void) { char buffer[250]; // Keep size below 255 to stay within 8-bits index and len uint8_t len = TasmotaClient_receiveData(buffer, sizeof(buffer) -1); - buffer[len] = '\0'; - ResponseAppend_P(PSTR(",\"TasmotaClient\":%s"), buffer); + if (len) { + buffer[len] = '\0'; + ResponseAppend_P(PSTR(",\"TasmotaClient\":%s"), buffer); + } } } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino index 27c97ab857cf..84203d405c48 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino @@ -1603,7 +1603,7 @@ void UfsEditor(void) { AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("UFS: UfsEditor: read=%d"), l); if (l < 0) { break; } buf[l] = '\0'; - WSContentSend_P(PSTR("%s"), buf); + WSContentSend_P(PSTR("%s"), HtmlEscape((char*)buf).c_str()); filelen -= l; } fp.close(); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino index 4c1f0f711e39..97eb571e4b62 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino @@ -23,13 +23,6 @@ #include #include "esp8266toEsp32.h" -#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) -#if ESP_IDF_VERSION_MAJOR >= 5 -#include -#else -#include -#endif -#endif /*********************************************************************************************\ * Native functions mapped to Berry functions * @@ -40,9 +33,10 @@ extern "C" { #include "berry/include/be_gpio_defines.h" -#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) +#ifdef SOC_DAC_SUPPORTED #include "soc/dac_channel.h" -#endif // defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) + #include +#endif // SOC_DAC_SUPPORTED // virtual member int gp_member(bvm *vm); @@ -65,7 +59,7 @@ extern "C" { // synthetic mode if (-1 == mode) { // DAC -#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) +#ifdef SOC_DAC_SUPPORTED if (pin != DAC_CHAN0_GPIO_NUM && pin != DAC_CHAN1_GPIO_NUM) { be_raisef(vm, "value_error", "DAC only supported on GPIO%i-%i", DAC_CHAN0_GPIO_NUM, DAC_CHAN1_GPIO_NUM); } @@ -113,7 +107,7 @@ extern "C" { int gp_dac_voltage(bvm *vm); int gp_dac_voltage(bvm *vm) { -#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) +#ifdef SOC_DAC_SUPPORTED int32_t argc = be_top(vm); // Get the number of arguments if (argc == 2 && be_isint(vm, 1) && be_isnumber(vm, 2)) { int32_t pin = be_toint(vm, 1); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino index 0984cb14b3ce..555f4c44de62 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino @@ -24,22 +24,8 @@ #ifdef USE_WS2812 -#include - -enum { - ws2812_grb = 1, - sk6812_grbw = 2, - - neopixel_type_end -}; - -#ifdef CONFIG_IDF_TARGET_ESP32C2 -typedef NeoPixelBus neopixel_ws2812_grb_t; -typedef NeoPixelBus neopixel_sk6812_grbw_t; -#else -typedef NeoPixelBus neopixel_ws2812_grb_t; -typedef NeoPixelBus neopixel_sk6812_grbw_t; -#endif +#include "TasmotaLED.h" +#include "TasmotaLEDPusher.h" /*********************************************************************************************\ * Functions from Tasmota WS2812 driver @@ -86,93 +72,65 @@ extern "C" { // # 22 : ShiftLeft (rot:int [, first:int, last:int]) -> void // # 23 : ShiftRight (rot:int [, first:int, last:int]) -> void - void * be_get_neopixelbus(bvm *vm) { + void * be_get_tasmotaled(bvm *vm) { be_getmember(vm, 1, "_p"); void * strip = (void*) be_tocomptr(vm, -1); be_pop(vm, 1); - if (strip == nullptr) { - be_raise(vm, "internal_error", "neopixelbus object not initialized"); - } return strip; } - int32_t be_get_leds_type(bvm *vm) { - be_getmember(vm, 1, "_t"); - int32_t type = be_toint(vm, -1); - be_pop(vm, 1); - if (type < 0 || type >= neopixel_type_end) { - be_raise(vm, "internal_error", "invalid leds type"); - } - return type; - } - int be_neopixelbus_call_native(bvm *vm); - int be_neopixelbus_call_native(bvm *vm) { + int be_tasmotaled_call_native(bvm *vm); + int be_tasmotaled_call_native(bvm *vm) { int32_t argc = be_top(vm); // Get the number of arguments if (argc >= 2 && be_isint(vm, 2)) { int32_t cmd = be_toint(vm, 2); if (0 == cmd) { // 00 : ctor (leds:int, gpio:int) -> void - if ((argc != 2) && !(argc >= 6 && be_isint(vm, 3) && be_isint(vm, 4) && be_isint(vm, 5) && be_isint(vm, 6))) { - be_raise(vm, "value_error", "bad arguments for neopixelbus:ctor"); + if ((argc != 2) && !(argc >= 5 && be_isint(vm, 3) && be_isint(vm, 4) && be_isint(vm, 5))) { + be_raise(vm, "value_error", "bad arguments for tasmotaled:ctor"); } int32_t leds = -1; int32_t gpio = -1; - int32_t neopixel_type = 0; - int32_t rmt = 0; - void * strip = nullptr; + int32_t led_type = 0; + int32_t hardware = 0; + if (argc >= 6 && be_isint(vm, 6)) { + hardware = be_toint(vm, 6) & 0xFF0000; // remove the low 16 bits to avoid any interference with legacy parameter for RMT channels + } + TasmotaLED * strip = nullptr; if (argc > 2) { leds = be_toint(vm, 3); gpio = be_toint(vm, 4); - neopixel_type = be_toint(vm, 5); - rmt = be_toint(vm, 6); + led_type = be_toint(vm, 5); } if (-1 == gpio) { // if GPIO is '-1' - neopixel_type = 0; - Ws2812InitStrip(); // ensure the NeoPixelbus object is initialized, because Berry code runs before the driver is initialized - strip = Ws2812GetStrip(); + led_type = 0; + Ws2812InitStrip(); // ensure the tasmotaled object is initialized, because Berry code runs before the driver is initialized } else { - // allocate a new RMT - if (neopixel_type < 1) { neopixel_type = 1; } - if (neopixel_type >= neopixel_type_end) { neopixel_type = neopixel_type_end - 1; } - if (rmt < 0) { rmt = 0; } - if (rmt >= MAX_RMT) { rmt = MAX_RMT - 1; } - - switch (neopixel_type) { - case ws2812_grb: strip = new neopixel_ws2812_grb_t(leds, gpio, (NeoBusChannel) rmt); - break; - case sk6812_grbw: strip = new neopixel_sk6812_grbw_t(leds, gpio, (NeoBusChannel) rmt); - break; + if (led_type < 1) { led_type = 1; } + TasmotaLEDPusher * pusher = TasmotaLEDPusher::Create(hardware, gpio); + if (pusher == nullptr) { + be_raise(vm, "value_error", "LED interface not supported"); } + strip = new TasmotaLED(led_type, leds); + strip->SetPusher(pusher); } - // store type in attribute `_t` - be_pushint(vm, neopixel_type); - be_setmember(vm, 1, "_t"); - be_pop(vm, 1); - - be_pushcomptr(vm, (void*) strip); + be_pushcomptr(vm, (void*) strip); // if native driver, it is NULL be_setmember(vm, 1, "_p"); be_pop(vm, 1); be_pushnil(vm); } else { - // all other commands need a valid neopixelbus pointer - int32_t leds_type = be_get_leds_type(vm); - const void * s = be_get_neopixelbus(vm); // raises an exception if pointer is invalid - // initialize all possible variants - neopixel_ws2812_grb_t * s_ws2812_grb = (leds_type == ws2812_grb) ? (neopixel_ws2812_grb_t*) s : nullptr; - neopixel_sk6812_grbw_t * s_sk6812_grbw = (leds_type == sk6812_grbw) ? (neopixel_sk6812_grbw_t*) s : nullptr; - // detect native driver - bool native = (leds_type == 0); - + // all other commands need a valid tasmotaled pointer + TasmotaLED * strip = (TasmotaLED*) be_get_tasmotaled(vm); // raises an exception if pointer is invalid + // detect native driver means strip == nullptr be_pushnil(vm); // push a default `nil` return value switch (cmd) { case 1: // # 01 : begin void -> void - if (native) Ws2812Begin(); - if (s_ws2812_grb) s_ws2812_grb->Begin(); - if (s_sk6812_grbw) s_sk6812_grbw->Begin(); + if (strip) strip->Begin(); + else Ws2812Begin(); break; case 2: // # 02 : show void -> void { @@ -195,49 +153,41 @@ extern "C" { } } uint32_t pixels_size; // number of bytes to push - if (native) { Ws2812Show(); pixels_size = Ws2812PixelsSize(); } - if (s_ws2812_grb) { s_ws2812_grb->Show(); pixels_size = s_ws2812_grb->PixelsSize(); } - if (s_sk6812_grbw) { s_sk6812_grbw->Show(); pixels_size = s_sk6812_grbw->PixelsSize(); } - - // Wait for RMT/I2S to complete fixes distortion due to analogRead - // 1ms is needed for 96 bytes - SystemBusyDelay((pixels_size + 95) / 96); + bool update_completed = false; + if (strip) { + strip->Show(); + pixels_size = strip->PixelCount() * strip->PixelSize(); + update_completed = strip->CanShow(); + } else { + Ws2812Show(); + pixels_size = Ws2812PixelsSize(); + update_completed =Ws2812CanShow(); + } } break; case 3: // # 03 : CanShow void -> bool - if (native) be_pushbool(vm, Ws2812CanShow()); - if (s_ws2812_grb) be_pushbool(vm, s_ws2812_grb->CanShow()); - if (s_sk6812_grbw) be_pushbool(vm, s_sk6812_grbw->CanShow()); + if (strip) be_pushbool(vm, strip->CanShow()); + else be_pushbool(vm, Ws2812CanShow()); break; case 4: // # 04 : IsDirty void -> bool - if (native) be_pushbool(vm, Ws2812IsDirty()); - if (s_ws2812_grb) be_pushbool(vm, s_ws2812_grb->IsDirty()); - if (s_sk6812_grbw) be_pushbool(vm, s_sk6812_grbw->IsDirty()); + if (strip) be_pushbool(vm, strip->IsDirty()); + else be_pushbool(vm, Ws2812IsDirty()); break; case 5: // # 05 : Dirty void -> void - if (native) Ws2812Dirty(); - if (s_ws2812_grb) s_ws2812_grb->Dirty(); - if (s_sk6812_grbw) s_sk6812_grbw->Dirty(); + if (strip) strip->Dirty(); + else Ws2812Dirty(); break; case 6: // # 06 : Pixels void -> bytes() (mapped to the buffer) - { - uint8_t * pixels; - if (native) pixels = Ws2812Pixels(); - if (s_ws2812_grb) pixels = s_ws2812_grb->Pixels(); - if (s_sk6812_grbw) pixels = s_sk6812_grbw->Pixels(); - - be_pushcomptr(vm, pixels); - } + if (strip) be_pushcomptr(vm, strip->Pixels()); + else be_pushcomptr(vm, Ws2812Pixels()); break; case 7: // # 07 : PixelSize void -> int - if (native) be_pushint(vm, Ws2812PixelSize()); - if (s_ws2812_grb) be_pushint(vm, s_ws2812_grb->PixelSize()); - if (s_sk6812_grbw) be_pushint(vm, s_sk6812_grbw->PixelSize()); + if (strip) be_pushint(vm, strip->PixelSize()); + else be_pushint(vm, Ws2812PixelSize()); break; case 8: // # 08 : PixelCount void -> int - if (native) be_pushint(vm, Ws2812PixelCount()); - if (s_ws2812_grb) be_pushint(vm, s_ws2812_grb->PixelCount()); - if (s_sk6812_grbw) be_pushint(vm, s_sk6812_grbw->PixelCount()); + if (strip) be_pushint(vm, strip->PixelCount()); + else be_pushint(vm, Ws2812PixelCount()); break; case 9: // # 09 : ClearTo (color:??) -> void { @@ -252,44 +202,37 @@ extern "C" { if (from < 0) { from = 0; } if (len < 0) { len = 0; } - if (native) Ws2812ClearTo(r, g, b, w, from, from + len - 1); - if (s_ws2812_grb) s_ws2812_grb->ClearTo(RgbColor(r, g, b), from, from + len - 1); - if (s_sk6812_grbw) s_sk6812_grbw->ClearTo(RgbwColor(r, g, b, w), from, from + len - 1); + if (strip) strip->ClearTo(rgbw, from, from + len - 1); + else Ws2812ClearTo(r, g, b, w, from, from + len - 1); } else { - if (native) Ws2812ClearTo(r, g, b, w, -1, -1); - if (s_ws2812_grb) s_ws2812_grb->ClearTo(RgbColor(r, g, b)); - if (s_sk6812_grbw) s_sk6812_grbw->ClearTo(RgbwColor(r, g, b, w)); + if (strip) strip->ClearTo(rgbw); + else Ws2812ClearTo(r, g, b, w, -1, -1); } } break; - case 10: // # 10 : SetPixelColor (idx:int, color:??) -> void + case 10: // # 10 : SetPixelColor (idx:int, color:int wrgb) -> void { int32_t idx = be_toint(vm, 3); - uint32_t rgbw = be_toint(vm, 4); - uint8_t w = (rgbw >> 24) & 0xFF; - uint8_t r = (rgbw >> 16) & 0xFF; - uint8_t g = (rgbw >> 8) & 0xFF; - uint8_t b = (rgbw ) & 0xFF; - if (native) Ws2812SetPixelColor(idx, r, g, b, w); - if (s_ws2812_grb) s_ws2812_grb->SetPixelColor(idx, RgbColor(r, g, b)); - if (s_sk6812_grbw) s_sk6812_grbw->SetPixelColor(idx, RgbwColor(r, g, b, w)); + uint32_t wrgb = be_toint(vm, 4); + if (strip) { + strip->SetPixelColor(idx, wrgb); + } else { + uint8_t w = (wrgb >> 24) & 0xFF; + uint8_t r = (wrgb >> 16) & 0xFF; + uint8_t g = (wrgb >> 8) & 0xFF; + uint8_t b = (wrgb ) & 0xFF; + Ws2812SetPixelColor(idx, r, g, b, w); + } } break; - case 11: // # 11 : GetPixelColor (idx:int) -> color:?? + case 11: // # 11 : GetPixelColor (idx:int) -> color:int wrgb { int32_t idx = be_toint(vm, 3); - - if (native) { + if (strip) { + be_pushint(vm, strip->GetPixelColor(idx)); + } else { be_pushint(vm, Ws2812GetPixelColor(idx)); } - if (s_ws2812_grb) { - RgbColor rgb = s_ws2812_grb->GetPixelColor(idx); - be_pushint(vm, (rgb.R << 16) | (rgb.G << 8) | rgb.B); - } - if (s_sk6812_grbw) { - RgbwColor rgbw = s_sk6812_grbw->GetPixelColor(idx); - be_pushint(vm, (rgbw.W << 24) | (rgbw.R << 16) | (rgbw.G << 8) | rgbw.B); - } } break; default: diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino index 24835b64f4c7..5c17dc13c42e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino @@ -21,34 +21,39 @@ #ifdef USE_BERRY_ULP #include -#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) +// #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_ULP_COPROC_TYPE_LP_CORE) +#if defined(CONFIG_ULP_COPROC_ENABLED) #if defined(CONFIG_IDF_TARGET_ESP32) #include "esp32/ulp.h" #endif // esp32 -#if ESP_IDF_VERSION_MAJOR < 5 - #if defined(CONFIG_IDF_TARGET_ESP32S2) - #include "esp32s2/ulp.h" - #include "esp32s2/ulp_riscv.h" - #include "esp32s2/ulp_riscv_adc.h" - #endif // s2 - #if defined(CONFIG_IDF_TARGET_ESP32S3) - #include "esp32s3/ulp.h" - #include "esp32s3/ulp_riscv.h" - #include "esp32s3/ulp_riscv_adc.h" - #endif //s3 -#endif // ESP_IDF_VERSION_MAJOR < 5 +// #if ESP_IDF_VERSION_MAJOR < 5 +// #if defined(CONFIG_IDF_TARGET_ESP32S2) +// #include "esp32s2/ulp.h" +// #include "esp32s2/ulp_riscv.h" +// #include "esp32s2/ulp_riscv_adc.h" +// #endif // s2 +// #if defined(CONFIG_IDF_TARGET_ESP32S3) +// #include "esp32s3/ulp.h" +// #include "esp32s3/ulp_riscv.h" +// #include "esp32s3/ulp_riscv_adc.h" +// #endif //s3 +// #endif // ESP_IDF_VERSION_MAJOR < 5 #include "driver/rtc_io.h" #include "driver/gpio.h" -#if ESP_IDF_VERSION_MAJOR >= 5 +// #if ESP_IDF_VERSION_MAJOR >= 5 #include "esp_adc/adc_oneshot.h" #include "ulp_adc.h" - #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) + #if defined(CONFIG_ULP_COPROC_TYPE_RISCV) // S2 or S3 #include "ulp_riscv.h" #endif // defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) -#else - #include "driver/adc.h" -#endif // ESP_IDF_VERSION_MAJOR >= 5 +// #else +// #include "driver/adc.h" +// #endif // ESP_IDF_VERSION_MAJOR >= 5 +#ifdef CONFIG_ULP_COPROC_TYPE_LP_CORE + #include "ulp_lp_core.h" + ulp_lp_core_cfg_t be_ulp_lp_core_cfg; +#endif //CONFIG_ULP_COPROC_TYPE_LP_CORE #include "sdkconfig.h" @@ -60,14 +65,21 @@ extern "C" { void be_ULP_run(int32_t entry) { #if defined(CONFIG_IDF_TARGET_ESP32) ulp_run(entry); // entry point should be at the beginning of program -#else // S2 or S3 +#elif defined(CONFIG_ULP_COPROC_TYPE_RISCV) // S2 or S3 ulp_riscv_run(); +#else // lp_core + int err = ulp_lp_core_run(&be_ulp_lp_core_cfg); #endif } // `ULP.wake_period(period_index:int, period_us:int) -> nil` void be_ULP_wake_up_period(int32_t period_index, int32_t period_us) { +#ifdef CONFIG_ULP_COPROC_TYPE_LP_CORE + be_ulp_lp_core_cfg.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER; + be_ulp_lp_core_cfg.lp_timer_sleep_duration_us = period_us; +#else ulp_set_wakeup_period(period_index, period_us); +#endif //CONFIG_ULP_COPROC_TYPE_LP_CORE } // `ULP.set_mem(position:int, value:int) -> value:int` @@ -102,7 +114,6 @@ extern "C" { // enums: channel 0-7, attenuation 0-3, width 0-3 void be_ULP_adc_config(struct bvm *vm, int32_t channel, int32_t attenuation, int32_t width) { #if defined(CONFIG_IDF_TARGET_ESP32) -#if ESP_IDF_VERSION_MAJOR >= 5 ulp_adc_cfg_t cfg = { .adc_n = ADC_UNIT_1, .channel = (adc_channel_t)channel, @@ -111,20 +122,7 @@ extern "C" { .ulp_mode = ADC_ULP_MODE_FSM, }; esp_err_t err = ulp_adc_init(&cfg); -#else - esp_err_t err = adc1_config_channel_atten((adc1_channel_t)channel, (adc_atten_t)attenuation); - err += adc1_config_width((adc_bits_width_t)width); -#endif // ESP_IDF_VERSION_MAJOR >= 5 - if (err != ESP_OK) { - be_raisef(vm, "ulp_adc_config_error", "ULP: invalid code err=%i", err); - } -#if ESP_IDF_VERSION_MAJOR < 5 - else { - adc1_ulp_enable(); - } -#endif // ESP_IDF_VERSION_MAJOR < 5 -#else // S2 or S3 -#if ESP_IDF_VERSION_MAJOR >= 5 +#elif defined(CONFIG_ULP_COPROC_TYPE_RISCV) // S2 or S3 ulp_adc_cfg_t cfg = { .adc_n = ADC_UNIT_1, .channel = (adc_channel_t)channel, @@ -134,17 +132,18 @@ extern "C" { }; esp_err_t err = ulp_adc_init(&cfg); #else - ulp_riscv_adc_cfg_t cfg = { - .channel = (adc_channel_t)channel, - .atten = (adc_atten_t)attenuation, - .width = (adc_bits_width_t)width - }; - esp_err_t err = ulp_riscv_adc_init(&cfg); -#endif // ESP_IDF_VERSION_MAJOR >= 5 + // esp_err_t err = lp_core_lp_adc_init(ADC_UNIT_1); + // const lp_core_lp_adc_chan_cfg_t config = { + // .atten = (adc_atten_t)attenuation, + // .bitwidth = (adc_bitwidth_t)width, + // }; + // err += lp_core_lp_adc_config_channel(ADC_UNIT_1, (adc_channel_t)channel, &config); + be_raisef(vm, "ulp_adc_config_error", "ULP: not supported before ESP-IDF 5.4"); + esp_err_t err = ESP_FAIL; +#endif if (err != ESP_OK) { be_raisef(vm, "ulp_adc_config_error", "ULP: invalid code err=%i", err); } -#endif } /** @@ -156,8 +155,10 @@ extern "C" { void be_ULP_load(struct bvm *vm, const uint8_t *buf, size_t size) { #if defined(CONFIG_IDF_TARGET_ESP32) esp_err_t err = ulp_load_binary(0, buf, size / 4); // FSM type only, specific header, size in long words -#else // S2 or S3 +#elif defined(CONFIG_ULP_COPROC_TYPE_RISCV) // S2 or S3 esp_err_t err = ulp_riscv_load_binary(buf, size); // there are no header bytes, just load and hope for a valid binary - size in bytes +#else + esp_err_t err = ulp_lp_core_load_binary(buf,size); // check valid size, size in bytes #endif // defined(CONFIG_IDF_TARGET_ESP32) if (err != ESP_OK) { be_raisef(vm, "ulp_load_error", "ULP: invalid code err=%i", err); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino b/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino index 809ec86ac5f3..3e20116a2592 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino @@ -31,15 +31,28 @@ struct Shift595 { uint8_t pinOE; uint8_t outputs; uint8_t first; - bool connected = false; } *Shift595 = nullptr; +/*********************************************************************************************\ + * Low level Shift 74x595 +\*********************************************************************************************/ + void Shift595ConfigurePin(uint8_t pin, uint8_t value = 0) { pinMode(pin, OUTPUT); digitalWrite(pin, value); } -void Shift595Init(void) { +void Shift595LatchPin(uint8_t pin) { + digitalWrite(pin, 1); + digitalWrite(pin, 0); +} + +/*********************************************************************************************\ + * FUNC_SETUP_RING2 at T +1 + * Claim devices_present +\*********************************************************************************************/ + +void Shift595ModuleInit(void) { if (PinUsed(GPIO_SHIFT595_SRCLK) && PinUsed(GPIO_SHIFT595_RCLK) && PinUsed(GPIO_SHIFT595_SER)) { Shift595 = (struct Shift595*)calloc(1, sizeof(struct Shift595)); if (Shift595) { @@ -50,45 +63,70 @@ void Shift595Init(void) { Shift595ConfigurePin(Shift595->pinSRCLK); Shift595ConfigurePin(Shift595->pinRCLK); Shift595ConfigurePin(Shift595->pinSER); +#ifdef ESP32 + // Release hold on clocks (if set before restart) + gpio_hold_dis((gpio_num_t)Shift595->pinSRCLK); + gpio_hold_dis((gpio_num_t)Shift595->pinRCLK); +#endif if (PinUsed(GPIO_SHIFT595_OE)) { Shift595->pinOE = Pin(GPIO_SHIFT595_OE); - Shift595ConfigurePin(Shift595->pinOE, 1); + // Fix relay toggle at restart + // On power ON set all outputs to 3-state (3-state converted to OFF by ULN2803 relay drivers) + Shift595ConfigurePin(Shift595->pinOE, ResetReasonPowerOn()); } - Shift595->first = TasmotaGlobal.devices_present; - Shift595->outputs = Settings->shift595_device_count * 8; + Shift595->first = TasmotaGlobal.devices_present; // devices_present offset + Shift595->outputs = Settings->shift595_device_count * 8; // Max number of outputs present UpdateDevicesPresent(Shift595->outputs); - - Shift595->connected = true; - AddLog(LOG_LEVEL_DEBUG, PSTR("595: Controlling relays POWER%d to POWER%d"), Shift595->first + 1, Shift595->first + Shift595->outputs); } } } -void Shift595LatchPin(uint8_t pin) { - digitalWrite(pin, 1); - digitalWrite(pin, 0); -} +/*********************************************************************************************\ + * FUNC_SET_POWER at T +2 + * Reduce devices_present with display and/or lights not known before + * Add offset for previous defined relays +\*********************************************************************************************/ void Shift595SwitchRelay(void) { - if (Shift595 && Shift595->connected == true) { - for (uint32_t i = 0; i < Shift595->outputs; i++) { - uint8_t relay_state = bitRead(XdrvMailbox.index, Shift595->first + Shift595->outputs -1 -i); - digitalWrite(Shift595->pinSER, Settings->flag5.shift595_invert_outputs ? !relay_state : relay_state); - Shift595LatchPin(Shift595->pinSRCLK); + // XdrvMailbox.index = 32-bit rpower bit mask + // Use relative and sequential relay indexes + power_t rpower = XdrvMailbox.index; + uint32_t relay_max = Shift595->outputs; // Total number of outputs + DevicesPresentNonDisplayOrLight(relay_max); // Skip display and/or light(s) + uint32_t relay_offset = Shift595->outputs - relay_max + Shift595->first; + + static bool first = false; + if (!first) { + AddLog(LOG_LEVEL_DEBUG, PSTR("595: Output 1 to %d use POWER%d to POWER%d"), Shift595->outputs - relay_offset, Shift595->first + 1, relay_max); + first = true; + } + + uint32_t power_bit = relay_max -1; // Start at highest non display and/or light power bit + for (uint32_t i = 0; i < Shift595->outputs; i++) { // We need to set all shift outputs even if not used + uint32_t relay_state = 0; // Unused state + if (i >= relay_offset) { + relay_state = bitRead(rpower, power_bit); // Shift-in from high to low + power_bit--; } + digitalWrite(Shift595->pinSER, Settings->flag5.shift595_invert_outputs ? !relay_state : relay_state); // SetOption133 - (Shift595) Invert outputs of 74x595 shift registers + Shift595LatchPin(Shift595->pinSRCLK); + } - Shift595LatchPin(Shift595->pinRCLK); + Shift595LatchPin(Shift595->pinRCLK); - if (PinUsed(GPIO_SHIFT595_OE)) { - digitalWrite(Shift595->pinOE, 0); - } + if (PinUsed(GPIO_SHIFT595_OE)) { + digitalWrite(Shift595->pinOE, 0); } } +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + void CmndShift595Devices(void) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 3)) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload * 8 <= MAX_RELAYS_SET - Shift595->first)) { Settings->shift595_device_count = (1 == XdrvMailbox.payload) ? SHIFT595_DEVICE_COUNT : XdrvMailbox.payload; TasmotaGlobal.restart_flag = 2; } @@ -102,13 +140,20 @@ void CmndShift595Devices(void) { bool Xdrv60(uint32_t function) { bool result = false; - if (FUNC_PRE_INIT == function) { - Shift595Init(); + if (FUNC_SETUP_RING2 == function) { + Shift595ModuleInit(); } else if (Shift595) { switch (function) { case FUNC_SET_POWER: Shift595SwitchRelay(); break; +#ifdef ESP32 + case FUNC_SAVE_BEFORE_RESTART: + // Set hold on clocks to disable relay click on restart + gpio_hold_en((gpio_num_t)Shift595->pinSRCLK); + gpio_hold_en((gpio_num_t)Shift595->pinRCLK); + break; +#endif // ESP32 case FUNC_COMMAND: result = DecodeCommand(kShift595Commands, Shift595Command); break; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino b/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino index 8cb1196ac06f..6afa820e4c20 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_67_mcp23xxx.ino @@ -26,6 +26,7 @@ * Supported template fields: * NAME - Template name * BASE - Optional. 0 = use relative buttons and switches (default), 1 = use absolute buttons and switches + * IOCON - Optional. IOCON I/O Expander configuration register (bitmap: 0 MIRROR 0 DISSLW HAEN ODR INTPOL 0. Default 0b01011000 = 0x58) * GPIO - Sequential list of pin 1 and up with configured GPIO function * Function Code Description * ------------------- -------- ---------------------------------------- @@ -60,6 +61,9 @@ * * Buttons and relays B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8 * {"NAME":"MCP23017 A=B1-8, B=R1-8","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231]} + * + * Buttons and relays with open-drain INT B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8 + * {"NAME":"MCP23017 A=B1-8, B=R1-8","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231],"IOCON":0x5C} * * Buttons, relays, buttons and relays B1 B2 B3 B4 B5 B6 B7 B8 R1 R2 R3 R4 R5 R6 R7 R8 B9 B10B11B12B13B14B15B16R9 R10 R11 R12 R13 R14 R15 R16 * {"NAME":"MCP23017 A=B1-8, B=R1-8, C=B9-16, D=R9-16","GPIO":[32,33,34,35,36,37,38,39,224,225,226,227,228,229,230,231,40,41,42,43,44,45,46,47,232,233,234,235,236,237,238,239]} @@ -90,6 +94,8 @@ * MCP23017 support \*********************************************************************************************/ +#define D_JSON_IOCON "IOCON" + enum MCP23S08GPIORegisters { MCP23X08_IODIR = 0x00, MCP23X08_IPOL = 0x01, @@ -140,11 +146,25 @@ typedef struct { uint8_t olatb; uint8_t address; uint8_t interface; - uint8_t pins; // 8 (MCP23x08) or 16 (MCP23x17) + uint8_t pins; // 8 (MCP23x08) or 16 (MCP23x17) int8_t pin_cs; int8_t pin_int; } tMcp23xDevice; +typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... + uint8_t reg; // Allow bit manipulation using template IOCON + struct { + uint8_t spare0 : 1; // 0 Unimplemented + uint8_t INTPOL : 1; // (0) INT pin active-low. (1) active-high + uint8_t ODR : 1; // (0) INT pin active driver output. (1) Open-drain output (overrides INTPOL) + uint8_t HAEN : 1; // (1) Hardware Address enabled (MCS23S17 only) + uint8_t DISSLW : 1; // (0) SDA output slew rate disabled + uint8_t SEQOP : 1; // 0 Sequential operation enabled, address pointer increments + uint8_t MIRROR : 1; // (1) INT pins are internally connected + uint8_t BANK : 1; // 0 Registers are in the same bank (addresses are sequential) (MCS23x17 only) + }; +} tIOCON; + struct MCP230 { tMcp23xDevice device[MCP23XXX_MAX_DEVICES]; uint32_t relay_inverted; @@ -156,6 +176,7 @@ struct MCP230 { uint8_t relay_offset; uint8_t button_max; uint8_t switch_max; + tIOCON iocon; int8_t button_offset; int8_t switch_offset; bool base; @@ -339,15 +360,15 @@ void MCP23xPinMode(uint8_t pin, uint8_t flags) { } switch (flags) { case INPUT: - MCP23xUpdate(pin, true, iodir); - MCP23xUpdate(pin, false, gppu); + MCP23xUpdate(pin, true, iodir); // Pin is configured as an input + MCP23xUpdate(pin, false, gppu); // Pull-up disabled break; case INPUT_PULLUP: - MCP23xUpdate(pin, true, iodir); - MCP23xUpdate(pin, true, gppu); + MCP23xUpdate(pin, true, iodir); // Pin is configured as an input + MCP23xUpdate(pin, true, gppu); // Pull-up enabled break; case OUTPUT: - MCP23xUpdate(pin, false, iodir); + MCP23xUpdate(pin, false, iodir); // Pin is configured as an output break; } @@ -371,21 +392,21 @@ void MCP23xPinInterruptMode(uint8_t pin, uint8_t interrupt_mode) { } switch (interrupt_mode) { case MCP23XXX_CHANGE: - MCP23xUpdate(pin, true, gpinten); - MCP23xUpdate(pin, false, intcon); + MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event + MCP23xUpdate(pin, false, intcon); // Pin value is compared against the previous pin value break; case MCP23XXX_RISING: - MCP23xUpdate(pin, true, gpinten); - MCP23xUpdate(pin, true, intcon); - MCP23xUpdate(pin, true, defval); + MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event + MCP23xUpdate(pin, true, intcon); // Controls how the associated pin value is compared for interrupt-on-change + MCP23xUpdate(pin, false, defval); // If the associated pin level is the opposite from the register bit, an interrupt occurs. break; case MCP23XXX_FALLING: - MCP23xUpdate(pin, true, gpinten); - MCP23xUpdate(pin, true, intcon); - MCP23xUpdate(pin, false, defval); + MCP23xUpdate(pin, true, gpinten); // Enable GPIO input pin for interrupt-on-change event + MCP23xUpdate(pin, true, intcon); // Controls how the associated pin value is compared for interrupt-on-change + MCP23xUpdate(pin, true, defval); // If the associated pin level is the opposite from the register bit, an interrupt occurs. break; case MCP23XXX_NO_INTERRUPT: - MCP23xUpdate(pin, false, gpinten); + MCP23xUpdate(pin, false, gpinten); // Disable GPIO input pin for interrupt-on-change event break; } } @@ -394,7 +415,7 @@ void MCP23xSetPinModes(uint8_t pin, uint8_t flags) { // pin 0 - 63 MCP23xPinMode(pin, flags); if (Mcp23x.device[Mcp23x.chip].pin_int > -1) { // Mcp23x.chip is updated by call to MCP23xPinMode - MCP23xPinInterruptMode(pin, MCP23XXX_CHANGE); + MCP23xPinInterruptMode(pin, (Mcp23x.iocon.ODR) ? MCP23XXX_FALLING : MCP23XXX_CHANGE); } } @@ -457,6 +478,15 @@ uint32_t MCP23xGetPin(uint32_t lpin) { /*********************************************************************************************/ +bool MCP23xAddItem(uint8_t &item) { + if (item >= MAX_RELAYS_SET) { // MAX_RELAYS_SET = MAX_SWITCHES_SET = MAX_KEYS_SET = 32 + AddLog(LOG_LEVEL_INFO, PSTR("MCP: Max reached")); + return false; + } + item++; + return true; +} + String MCP23xTemplateLoadFile(void) { String mcptmplt = ""; #ifdef USE_UFILESYS @@ -497,7 +527,7 @@ bool MCP23xLoadTemplate(void) { } val = root[PSTR(D_JSON_NAME)]; if (val) { - AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Base %d, Template '%s'"), Mcp23x.base, val.getStr()); + AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: IOCON 0x%02X, Base %d, Template '%s'"), Mcp23x.iocon, Mcp23x.base, val.getStr()); } JsonParserArray arr = root[PSTR(D_JSON_GPIO)]; if (arr) { @@ -507,44 +537,36 @@ bool MCP23xLoadTemplate(void) { if (!val) { break; } uint16_t mpin = val.getUInt(); if (mpin) { // Above GPIO_NONE - if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES_SET))) { - Mcp23x.switch_max++; + if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES_SET)) && MCP23xAddItem(Mcp23x.switch_max)) { MCP23xSetPinModes(pin, INPUT_PULLUP); } - else if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES_SET))) { + else if ((mpin >= AGPIO(GPIO_SWT1_NP)) && (mpin < (AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES_SET)) && MCP23xAddItem(Mcp23x.switch_max)) { mpin -= (AGPIO(GPIO_SWT1_NP) - AGPIO(GPIO_SWT1)); - Mcp23x.switch_max++; MCP23xSetPinModes(pin, INPUT); } - else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS_SET))) { - Mcp23x.button_max++; + else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) { MCP23xSetPinModes(pin, INPUT_PULLUP); } - else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS_SET))) { + else if ((mpin >= AGPIO(GPIO_KEY1_NP)) && (mpin < (AGPIO(GPIO_KEY1_NP) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) { mpin -= (AGPIO(GPIO_KEY1_NP) - AGPIO(GPIO_KEY1)); - Mcp23x.button_max++; MCP23xSetPinModes(pin, INPUT); } - else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS_SET))) { + else if ((mpin >= AGPIO(GPIO_KEY1_INV)) && (mpin < (AGPIO(GPIO_KEY1_INV) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) { bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV)); mpin -= (AGPIO(GPIO_KEY1_INV) - AGPIO(GPIO_KEY1)); - Mcp23x.button_max++; MCP23xSetPinModes(pin, INPUT_PULLUP); } - else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS_SET))) { + else if ((mpin >= AGPIO(GPIO_KEY1_INV_NP)) && (mpin < (AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS_SET)) && MCP23xAddItem(Mcp23x.button_max)) { bitSet(Mcp23x.button_inverted, mpin - AGPIO(GPIO_KEY1_INV_NP)); mpin -= (AGPIO(GPIO_KEY1_INV_NP) - AGPIO(GPIO_KEY1)); - Mcp23x.button_max++; MCP23xSetPinModes(pin, INPUT); } - else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET))) { - Mcp23x.relay_max++; + else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS_SET)) && MCP23xAddItem(Mcp23x.relay_max)) { MCP23xPinMode(pin, OUTPUT); } - else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS_SET))) { + else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS_SET)) && MCP23xAddItem(Mcp23x.relay_max)) { bitSet(Mcp23x.relay_inverted, mpin - AGPIO(GPIO_REL1_INV)); mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); - Mcp23x.relay_max++; MCP23xPinMode(pin, OUTPUT); } else if (mpin == AGPIO(GPIO_OUTPUT_HI)) { @@ -558,14 +580,9 @@ bool MCP23xLoadTemplate(void) { else { mpin = 0; } Mcp23x_gpio_pin[pin] = mpin; } - if ((Mcp23x.switch_max >= MAX_SWITCHES_SET) || - (Mcp23x.button_max >= MAX_KEYS_SET) || - (Mcp23x.relay_max >= MAX_RELAYS_SET)) { - AddLog(LOG_LEVEL_INFO, PSTR("MCP: Max reached (S%d/B%d/R%d)"), Mcp23x.switch_max, Mcp23x.button_max, Mcp23x.relay_max); - break; - } } Mcp23x.max_pins = pin; // Max number of configured pins + AddLog(LOG_LEVEL_INFO, PSTR("MCP: Pins %d (S%d/B%d/R%d)"), Mcp23x.max_pins, Mcp23x.switch_max, Mcp23x.button_max, Mcp23x.relay_max); } // AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Pins %d, Mcp23x_gpio_pin %*_V"), Mcp23x.max_pins, Mcp23x.max_pins, (uint8_t*)Mcp23x_gpio_pin); @@ -582,6 +599,10 @@ uint32_t MCP23xTemplateGpio(void) { JsonParserObject root = parser.getRootObject(); if (!root) { return 0; } + JsonParserToken val = root[PSTR(D_JSON_IOCON)]; + if (val) { + Mcp23x.iocon.reg = val.getUInt() & 0x5E; // Only allow 0 MIRROR 0 DISSLW HAEN ODR INTPOL 0 + } JsonParserArray arr = root[PSTR(D_JSON_GPIO)]; if (arr.isArray()) { return arr.size(); // Number of requested pins @@ -590,9 +611,10 @@ uint32_t MCP23xTemplateGpio(void) { } void MCP23xModuleInit(void) { + Mcp23x.iocon.reg = 0b01011000; // Default 0x58 = Enable INT mirror, Disable Slew rate, HAEN pins for addressing int32_t pins_needed = MCP23xTemplateGpio(); if (!pins_needed) { - AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Invalid template")); + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: Invalid template")); return; } @@ -606,7 +628,8 @@ void MCP23xModuleInit(void) { #endif while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && PinUsed(GPIO_MCP23SXX_CS, Mcp23x.max_devices)) { Mcp23x.chip = Mcp23x.max_devices; - Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, Mcp23x.chip)) ? Pin(GPIO_MCP23XXX_INT, Mcp23x.chip) : -1; + uint32_t pin_int = (Mcp23x.iocon.ODR) ? 0 : Mcp23x.chip; // INT ODR pins are open-drain outputs and supposedly connected together to one GPIO + Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, pin_int)) ? Pin(GPIO_MCP23XXX_INT, pin_int) : -1; Mcp23x.device[Mcp23x.chip].pin_cs = Pin(GPIO_MCP23SXX_CS, Mcp23x.max_devices); digitalWrite(Mcp23x.device[Mcp23x.chip].pin_cs, 1); pinMode(Mcp23x.device[Mcp23x.chip].pin_cs, OUTPUT); @@ -619,12 +642,14 @@ void MCP23xModuleInit(void) { if (0x00 == buffer) { // MCP23S08 AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S08 found at CS%d"), Mcp23x.chip +1); Mcp23x.device[Mcp23x.chip].pins = 8; - MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing +// MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing + MCP23xWrite(MCP23X08_IOCON, Mcp23x.iocon.reg & 0x3E); Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT); } else if (0x80 == buffer) { // MCP23S17 AddLog(LOG_LEVEL_INFO, PSTR("SPI: MCP23S17 found at CS%d"), Mcp23x.chip +1); Mcp23x.device[Mcp23x.chip].pins = 16; - MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing +// MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing + MCP23xWrite(MCP23X17_IOCONA, Mcp23x.iocon.reg); Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA); Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB); } @@ -641,8 +666,9 @@ void MCP23xModuleInit(void) { uint8_t mcp23xxx_address = MCP23XXX_ADDR_START; while ((Mcp23x.max_devices < MCP23XXX_MAX_DEVICES) && (mcp23xxx_address < MCP23XXX_ADDR_END)) { Mcp23x.chip = Mcp23x.max_devices; + uint32_t pin_int = (Mcp23x.iocon.ODR) ? 0 : Mcp23x.chip; // INT pins are open-drain outputs and supposedly connected together to one GPIO if (I2cSetDevice(mcp23xxx_address)) { - Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, Mcp23x.chip)) ? Pin(GPIO_MCP23XXX_INT, Mcp23x.chip) : -1; + Mcp23x.device[Mcp23x.chip].pin_int = (PinUsed(GPIO_MCP23XXX_INT, pin_int)) ? Pin(GPIO_MCP23XXX_INT, pin_int) : -1; Mcp23x.device[Mcp23x.chip].interface = MCP23X_I2C; Mcp23x.device[Mcp23x.chip].address = mcp23xxx_address; @@ -652,7 +678,8 @@ void MCP23xModuleInit(void) { if (0x00 == buffer) { I2cSetActiveFound(mcp23xxx_address, "MCP23008"); Mcp23x.device[Mcp23x.chip].pins = 8; - MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing +// MCP23xWrite(MCP23X08_IOCON, 0b00011000); // Slew rate disabled, HAEN pins for addressing + MCP23xWrite(MCP23X08_IOCON, Mcp23x.iocon.reg & 0x3E); Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X08_OLAT); Mcp23x.max_devices++; } @@ -660,7 +687,8 @@ void MCP23xModuleInit(void) { I2cSetActiveFound(mcp23xxx_address, "MCP23017"); Mcp23x.device[Mcp23x.chip].pins = 16; MCP23xWrite(MCP23X08_IOCON, 0x00); // Reset bank mode to 0 (MCP23X17_GPINTENB) - MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing +// MCP23xWrite(MCP23X17_IOCONA, 0b01011000); // Enable INT mirror, Slew rate disabled, HAEN pins for addressing + MCP23xWrite(MCP23X17_IOCONA, Mcp23x.iocon.reg); Mcp23x.device[Mcp23x.chip].olata = MCP23xRead(MCP23X17_OLATA); Mcp23x.device[Mcp23x.chip].olatb = MCP23xRead(MCP23X17_OLATB); Mcp23x.max_devices++; @@ -691,6 +719,8 @@ void MCP23xModuleInit(void) { return; } + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MCP: INT open-drain %d"), Mcp23x.iocon.ODR); + Mcp23x.relay_offset = TasmotaGlobal.devices_present; Mcp23x.relay_max -= UpdateDevicesPresent(Mcp23x.relay_max); @@ -745,8 +775,10 @@ void MCP23xInit(void) { } else { gpio = MCP23xRead16(MCP23X17_GPIOA); // Clear MCP23x17 interrupt } + if (Mcp23x.iocon.ODR && Mcp23x.chip) { continue; } +// pinMode(Mcp23x.device[Mcp23x.chip].pin_int, (Mcp23x.iocon.ODR) ? INPUT_PULLUP : INPUT); pinMode(Mcp23x.device[Mcp23x.chip].pin_int, INPUT_PULLUP); - attachInterrupt(Mcp23x.device[Mcp23x.chip].pin_int, MCP23xInputIsr, CHANGE); + attachInterrupt(Mcp23x.device[Mcp23x.chip].pin_int, MCP23xInputIsr, (Mcp23x.iocon.ODR) ? FALLING : CHANGE); } } } @@ -762,6 +794,7 @@ void MCP23xPower(void) { rpower >>= Mcp23x.relay_offset; relay_max = Mcp23x.relay_max; } + DevicesPresentNonDisplayOrLight(relay_max); // Skip display and/or light(s) for (uint32_t index = 0; index < relay_max; index++) { power_t state = rpower &1; if (MCP23xPinUsed(GPIO_REL1, index)) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_69_pca9557.ino b/tasmota/tasmota_xdrv_driver/xdrv_69_pca9557.ino index 9ee9c1c8753f..5d11d31603b2 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_69_pca9557.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_69_pca9557.ino @@ -360,7 +360,7 @@ uint32_t PCA9557TemplateGpio(void) { void PCA9557ModuleInit(void) { int32_t pins_needed = PCA9557TemplateGpio(); if (!pins_needed) { - AddLog(LOG_LEVEL_DEBUG, PSTR("PCA: Invalid template")); + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PCA: Invalid template")); return; } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_71_magic_switch.ino b/tasmota/tasmota_xdrv_driver/xdrv_71_magic_switch.ino index a0c097b28070..46bc1e062634 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_71_magic_switch.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_71_magic_switch.ino @@ -105,6 +105,12 @@ void MagicSwitchLoop() } } +void MagicSwitchSetPower(void) { + // It can happen that on relay switch, disturbances on the mains is falsy see as a MagicSwitch pulse + // This restart the masking windows on every power change to avoid that effect + MagicSwitch->switch_state = MAGICSWITCH_MASKING_WINDOW_LEN; +} + /******************************************************************************************************** * Driver initialisation */ @@ -173,6 +179,9 @@ bool Xdrv71(uint32_t function) { //case FUNC_EVERY_250_MSECOND: MagicSwitchLoop(); break; + case FUNC_SET_POWER: + MagicSwitchSetPower(); + break; case FUNC_ADD_SWITCH: result = MagicSwitchAddSwitch(); break; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino b/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino index 2c5aa816dd12..81a6f779a8e8 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino @@ -108,6 +108,11 @@ BLEEnableUnsaved *0/1 - if BLE is disabled, this can be used to enable BLE without it being saved - useful as the last command in autoexec.bat + BLEFilterNames + BLEFilterNames0 - clear filter list + BLEFilterNames1 - , - set one or more device names + BLEMinRssiLevel + BLEMinRssiLevel - Sets the minimum allowable RSSI level for detected devices Other drivers can add callbacks to receive advertisements Other drivers can add 'operations' to be performed and receive callbacks from the operation's success or failure @@ -134,6 +139,8 @@ i.e. the Bluetooth of the ESP can be shared without conflict. */ #define BLE_ESP32_ALIASES +#define BLE_ESP32_FILTER_BY_NAME +#define BLE_ESP32_FILTER_BY_RSSI // uncomment for more diagnostic/information messages - + more flash use. //#define BLE_ESP32_DEBUG @@ -472,6 +479,12 @@ std::deque scancompleteCallbacks; std::deque aliases; #endif +#ifdef BLE_ESP32_FILTER_BY_NAME +std::vector bleFilterNames; +#endif +#ifdef BLE_ESP32_FILTER_BY_RSSI +int minRSSI = -100; +#endif /*********************************************************************************************\ * constants @@ -480,7 +493,7 @@ std::deque aliases; #define D_CMND_BLE "BLE" const char kBLE_Commands[] PROGMEM = D_CMND_BLE "|" - "Period|Adv|Op|Mode|Details|Scan|Alias|Name|Debug|Devices|MaxAge|AddrFilter|EnableUnsaved"; + "Period|Adv|Op|Mode|Details|Scan|Alias|Name|Debug|Devices|MaxAge|AddrFilter|EnableUnsaved|FilterNames|MinRssiLevel"; static void CmndBLEPeriod(void); static void CmndBLEAdv(void); @@ -495,6 +508,8 @@ static void CmndBLEDevices(void); static void CmndBLEMaxAge(void); static void CmndBLEAddrFilter(void); static void CmndBLEEnableUnsaved(void); +static void CmndBleFilterNames(void); +static void CmndSetMinRSSI(void); void (*const BLE_Commands[])(void) PROGMEM = { &BLE_ESP32::CmndBLEPeriod, @@ -509,7 +524,9 @@ void (*const BLE_Commands[])(void) PROGMEM = { &BLE_ESP32::CmndBLEDevices, &BLE_ESP32::CmndBLEMaxAge, &BLE_ESP32::CmndBLEAddrFilter, - &BLE_ESP32::CmndBLEEnableUnsaved + &BLE_ESP32::CmndBLEEnableUnsaved, + &BLE_ESP32::CmndBleFilterNames, + &BLE_ESP32::CmndSetMinRSSI }; const char *successStates[] PROGMEM = { @@ -1128,7 +1145,24 @@ void ReverseMAC(uint8_t _mac[]){ } - +/** + * @brief Search for device name in filer list + * + * @param deviceName device name string + */ +#ifdef BLE_ESP32_FILTER_BY_NAME +bool isDeviceInFilter(const String& deviceName) { +#ifdef BLE_ESP32_DEBUG + if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Device chcked in filter %s"), deviceName); +#endif + for (const auto& filterName : bleFilterNames) { + if (deviceName == filterName) { + return true; + } + } + return false; +} +#endif /*********************************************************************************************\ * Advertisment details @@ -1372,9 +1406,24 @@ class BLEAdvCallbacks: public NimBLEScanCallbacks { BLEAdvertisment.name[sizeof(BLEAdvertisment.name)-1] = 0; } + int filter = 0; +#ifdef BLE_ESP32_FILTER_BY_NAME + if (!bleFilterNames.empty()) { + if (!advertisedDevice->haveName() || !isDeviceInFilter(namestr)) + { + filter = 1; + } + } +#endif + +#ifdef BLE_ESP32_FILTER_BY_RSSI + if (advertisedDevice->getRSSI() < minRSSI) { + filter = 1; + } +#endif // log this device safely - if (BLEAdvertisment.addrtype <= BLEAddressFilter){ + if ((BLEAdvertisment.addrtype <= BLEAddressFilter) && (0 == filter) ){ addSeenDevice(BLEAdvertisment.addr, BLEAdvertisment.addrtype, BLEAdvertisment.name, BLEAdvertisment.RSSI); } @@ -2833,6 +2882,58 @@ void CmndBLEDetails(void){ } +void CmndBleFilterNames(void) { +#ifdef BLE_ESP32_FILTER_BY_NAME + int op = XdrvMailbox.index; +#ifdef BLE_ESP32_DEBUG + if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Name %d %s"), op, XdrvMailbox.data); +#endif + + switch(op){ + case 0:{ + bleFilterNames.clear(); + ResponseCmndDone(); + } break; + case 1:{ + if (XdrvMailbox.data_len) { + String filters = XdrvMailbox.data; + bleFilterNames.clear(); + + int start = 0; + int end = filters.indexOf(','); + while (end != -1) { + bleFilterNames.push_back(filters.substring(start, end)); + start = end + 1; + end = filters.indexOf(',', start); + } + bleFilterNames.push_back(filters.substring(start)); + + Response_P(PSTR("{\"BLEFilterNames\":\"%s\"}"), filters.c_str()); + } else { + String filterList; + for (const auto& name : bleFilterNames) { + if (!filterList.isEmpty()) { + filterList += ", "; + } + filterList += name; + } + + Response_P(PSTR("{\"BLEFilterNames\":\"%s\"}"), filterList.c_str()); + } + } break; + } +#endif +} + +void CmndSetMinRSSI(void) { +#ifdef BLE_ESP32_FILTER_BY_RSSI + if (XdrvMailbox.data_len) { + minRSSI = atoi(XdrvMailbox.data); + } + Response_P(PSTR("{\"MinRSSI\":\"%d\"}"), minRSSI); +#endif +} + void CmndBLEAlias(void){ #ifdef BLE_ESP32_ALIASES int op = XdrvMailbox.index; @@ -3472,8 +3573,8 @@ const char HTTP_BTN_MENU_BLE[] PROGMEM = const char HTTP_FORM_BLE[] PROGMEM = "
 " D_BLE_PARAMETERS " " "
" - "

" - "

" + "

" + "

" "

" D_BLE_REMARK "

"; diff --git a/tasmota/tasmota_xdsp_display/xdsp_02_ssd1306.ino b/tasmota/tasmota_xdsp_display/xdsp_02_ssd1306.ino deleted file mode 100644 index c22a20d02d8c..000000000000 --- a/tasmota/tasmota_xdsp_display/xdsp_02_ssd1306.ino +++ /dev/null @@ -1,21 +0,0 @@ -/* - xdsp_02_ssd1306.ino - Display Oled SSD1306 support for Tasmota - - Copyright (C) 2021 Theo Arends and Adafruit - - 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 . -*/ - -// REMOVED -// DEPRECATED - USE UNIVERSAL DISPLAY INSTEAD, https://tasmota.github.io/docs/Universal-Display-Driver/#migrating-to-udisplay diff --git a/tasmota/tasmota_xdsp_display/xdsp_04_ili9341.ino b/tasmota/tasmota_xdsp_display/xdsp_04_ili9341.ino deleted file mode 100644 index 4fbf365415ba..000000000000 --- a/tasmota/tasmota_xdsp_display/xdsp_04_ili9341.ino +++ /dev/null @@ -1,21 +0,0 @@ -/* - xdsp_04_ILI9341.ino - Display ILI9341/2 support for Tasmota - - Copyright (C) 2021 Gerhard Mutz and Theo Arends - - 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 . -*/ - -// REMOVED -// DEPRECATED - USE UNIVERSAL DISPLAY INSTEAD, https://tasmota.github.io/docs/Universal-Display-Driver/#migrating-to-udisplay diff --git a/tasmota/tasmota_xdsp_display/xdsp_07_sh1106.ino b/tasmota/tasmota_xdsp_display/xdsp_07_sh1106.ino deleted file mode 100644 index 68992a5abc89..000000000000 --- a/tasmota/tasmota_xdsp_display/xdsp_07_sh1106.ino +++ /dev/null @@ -1,21 +0,0 @@ -/* - xdsp_07_SH1106.ino - Display Oled SH1106 support for Tasmota - - Copyright (C) 2021 Theo Arends and Adafruit - - 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 . -*/ - -// REMOVED -// DEPRECATED - USE UNIVERSAL DISPLAY INSTEAD, https://tasmota.github.io/docs/Universal-Display-Driver/#migrating-to-udisplay diff --git a/tasmota/tasmota_xdsp_display/xdsp_09_SSD1351.ino b/tasmota/tasmota_xdsp_display/xdsp_09_SSD1351.ino deleted file mode 100644 index 24ea1d13da00..000000000000 --- a/tasmota/tasmota_xdsp_display/xdsp_09_SSD1351.ino +++ /dev/null @@ -1,21 +0,0 @@ -/* - xdsp_09_SSD1351.ino - Display SSD1351 support for Tasmota - - Copyright (C) 2021 Gerhard Mutz and Theo Arends - - 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 . -*/ - -// REMOVED -// DEPRECATED - USE UNIVERSAL DISPLAY INSTEAD, https://tasmota.github.io/docs/Universal-Display-Driver/#migrating-to-udisplay diff --git a/tasmota/tasmota_xdsp_display/xdsp_12_ST7789.ino b/tasmota/tasmota_xdsp_display/xdsp_12_ST7789.ino deleted file mode 100644 index c00c336c160f..000000000000 --- a/tasmota/tasmota_xdsp_display/xdsp_12_ST7789.ino +++ /dev/null @@ -1,21 +0,0 @@ -/* - xdsp_12_ST7789.ino - Display ST7789 support for Tasmota - - Copyright (C) 2021 Gerhard Mutz and Theo Arends - - 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 . -*/ - -// REMOVED -// DEPRECATED - USE UNIVERSAL DISPLAY INSTEAD, https://tasmota.github.io/docs/Universal-Display-Driver/#migrating-to-udisplay diff --git a/tasmota/tasmota_xdsp_display/xdsp_13_tm1640.ino b/tasmota/tasmota_xdsp_display/xdsp_13_tm1640.ino new file mode 100644 index 000000000000..a4f0318e27ce --- /dev/null +++ b/tasmota/tasmota_xdsp_display/xdsp_13_tm1640.ino @@ -0,0 +1,783 @@ +/* + xdsp_13_tm1640.ino - TM1640B LED display controller support for Tasmota + + Copyright (C) 2024 Stefan Oskamp + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_TM1640 +/*********************************************************************************************\ + This driver enables the display of the current time, numbers (both integers and floats) and + basic text on the IoTTimer clock. + + Template {"NAME":"IoTTimer Lo","GPIO":[32,33,0,34,3872,1312,0,0,10944,10912,640,480,608,4768],"FLAG":0,"BASE":18} + + In addition, it is possible to set brightness (seven levels plus off) and clear the display. + + To use, compile Tasmota with USE_DISPLAY, USE_DISPLAY_TM1640 and USE_IOTTIMER, or build the tasmota-display + firmware. + + Either use following template: + Template {"NAME":"IoTTimer Lo","GPIO":[32,33,0,34,3872,1312,0,0,10944,10912,640,480,608,4768],"FLAG":0,"BASE":18} + + or configure manually: + For the IoTTimer clock assign the pins as follows from Tasmota's GUI: + + GPIO12 --> "TM1640 DIN" + GPIO13 --> "TM1640 CLK" + + Once the GPIO configuration is saved and the ESP8266/ESP32 module restarts, set the Display + Model to 13 and Display Mode to 0 using the command "Backlog DisplayModel 13 ; DisplayMode 0;" + Before using it, set the Display Type to 1 (for IOTTIMER) using the "DisplayType 1" command. + + After the ESP8266 restarts again, turn ON the display with the command "Power 1" + + Now, the following "Display" commands can be used: + + + DisplayClear + + Clears the display, command: "DisplayClear" + + + DisplayFloat num + + Clears and then displays float (with decimal point) command e.g., "DisplayFloat 12.3" + See function description below for more details. + + + DisplayFloatNC num + + Same as DisplayFloatNC + + + DisplayClock 1|2|3|4 + + Displays a clock. + Commands "DisplayClock 1" // 12 hr format + "DisplayClock 2" // 24 hr format + "DisplayClock 3" // 12-hour without seconds + "DisplayClock 4" // 24-hour without seconds + + +In addition, if you compile using USE_DISPLAY_MODES1TO5, setting DisplayMode to 1 shows the time, +setting it to 2 shows the date and setting it to 3 alternates between time and date (using "DisplayRefresh [1..7]" +for the time and seconds you want to show the time before displaying the date) + +\*********************************************************************************************/ + +#define XDSP_13 13 + +#define TM1640_CMD_DATA_AUTO 0x40 +#define TM1640_CMD_DATA_FIXED 0x44 +#define TM1640_CMD_DISPLAY 0x80 +#define TM1640_CMD_ADDRESS 0xC0 + +#define TM1640_CLOCK_DELAY 1 // uSec + +#define LEVEL_MIN 0 +#define LEVEL_MAX 100 + +enum tm1640_display_options_types { + TM1640_DEFAULT, + TM1640_IOTTIMER // IOTTIMER WiFi clock +}; + +typedef struct Tm1640_t { + int8_t clock_pin; + int8_t data_pin; + bool show_clock; + bool clock_24; + bool clock_seconds; +} Tm1640_t; + +Tm1640_t* Tm1640 = nullptr; + +/*********************************************************************************************\ + * TM1640 low level functions +\*********************************************************************************************/ + +void TM1640Start (void) { + digitalWrite(Tm1640->data_pin, LOW); + digitalWrite(Tm1640->clock_pin, LOW); + delayMicroseconds(TM1640_CLOCK_DELAY); +} + +void TM1640Stop (void) { + digitalWrite(Tm1640->clock_pin, HIGH); + digitalWrite(Tm1640->data_pin, HIGH); + delayMicroseconds(TM1640_CLOCK_DELAY); +} + +void TM1640Send(uint8_t data) { + for (uint32_t i = 0; i < 8; i++) { // 8 bits + digitalWrite(Tm1640->data_pin, data & 1 ? HIGH : LOW); + delayMicroseconds(TM1640_CLOCK_DELAY); + data >>= 1; + digitalWrite(Tm1640->clock_pin, HIGH); + delayMicroseconds(TM1640_CLOCK_DELAY); + digitalWrite(Tm1640->clock_pin, LOW); + delayMicroseconds(TM1640_CLOCK_DELAY); + } + digitalWrite(Tm1640->data_pin, LOW); + delayMicroseconds(TM1640_CLOCK_DELAY); +} + +void TM1640SendData(uint8_t address, uint8_t data) { + // First, send data command using FIXED addressing: + TM1640Start(); + TM1640Send(TM1640_CMD_DATA_FIXED); + TM1640Stop(); + // Then, send address and one data byte: + TM1640Start(); + TM1640Send(TM1640_CMD_ADDRESS | address); + TM1640Send(data); + TM1640Stop(); +} + +void TM1640SendDataArray(uint8_t address, uint8_t *data, uint8_t count) { + // First, send data command using AUTO addressing: + TM1640Start(); + TM1640Send(TM1640_CMD_DATA_AUTO); + TM1640Stop(); + // Then, send address and all data bytes: + TM1640Start(); + TM1640Send(TM1640_CMD_ADDRESS | address); + while (count-- > 0) { + TM1640Send(*data++); + } + TM1640Stop(); +} + +void TM1640SetBrightness(uint8_t level) { + // level can be 0 to 8. + // 0 means off + // + // Other levels are mapped to TM1640 levels 0 ... 7 + // The mapping to the PWM level is non-linear, according to the data sheet + // level | TM1640 | PWM + // 1 | 0 | 1/16 + // 2 | 1 | 2/16 + // 3 | 2 | 4/16 + // 4 | 3 | 10/16 + // 5 | 4 | 11/16 + // 6 | 5 | 12/16 + // 7 | 6 | 13/16 + // 8 | 7 | 14/16 + uint8_t cmd = TM1640_CMD_DISPLAY | (level > 0 ? 0x8 : 0) | ((level - 1) % 8); + TM1640Start(); + TM1640Send (cmd); + TM1640Stop(); +} + +/*********************************************************************************************\ +* Init function +\*********************************************************************************************/ + +void TM1640Init(void) { + if (PinUsed(GPIO_TM1640CLK) && PinUsed(GPIO_TM1640DIN)) { + Tm1640 = (Tm1640_t*)calloc(sizeof(Tm1640_t), 1); // Need calloc to reset registers to 0/false + if (nullptr == Tm1640) { return; } + + Tm1640->clock_pin = Pin(GPIO_TM1640CLK); + Tm1640->data_pin = Pin(GPIO_TM1640DIN); + + pinMode(Tm1640->data_pin, OUTPUT); + pinMode(Tm1640->clock_pin, OUTPUT); + digitalWrite(Tm1640->clock_pin, HIGH); + digitalWrite(Tm1640->data_pin, HIGH); + + Settings->display_model = XDSP_13; + +#ifdef USE_IOTTIMER + Settings->display_options.type = TM1640_IOTTIMER; + + Settings->display_cols[0] = 9; // 4 (left) + 2 (lower right) + 3 (upper right). + Settings->display_rows = 1; + Settings->display_width = Settings->display_cols[0]; + Settings->display_height = Settings->display_rows; + + Tm1640->clock_24 = true; + Tm1640->clock_seconds = true; + + IoTTimerDim(); + IoTTimerClearDisplay(); +#endif // USE_IOTTIMER + + AddLog(LOG_LEVEL_INFO, PSTR("DSP: TM1640 with %d digits (type %d)"), Settings->display_width, Settings->display_options.type); + } +} + + +/*********************************************************************************************\ +* IotTimer +\*********************************************************************************************/ + +#ifdef USE_IOTTIMER + +/* + (specifically for its use in the IOTTIMER WiFi clock) + + The WiFi LED clock called IOTTIMER has the following characteristics: + - Controlled by an ESP-12F + - Display with four 35 mm (1 12/32 in), two 21 mm (26/32 in), and three 12 mm (~1/2 in), + seven-segment LED digits, plus special symbols (alarm, AM/PM) + - TM1640B LED controller + - R8010 RTC with CR1220 battery + - Temperature sensor M1601 + - Ambient light sensor (analog voltage) + - Buzzer + - Three buttons on the backside + - USB C port for power supply (only) + + The TM1640B chip is a controller for a sixteen-digit seven-segment (plus dot) LED display. + It is also sometimes used to control a 16 x 8 LED matrix. The controller is controlled + through a proprietary two-wire serial interface bearing some similarities with I2C. The + two wires are called CLK and DIN. We use two GPIO pins and one-microsecond sleeps to + implement the required timing. + + The wiring of the LEDs in the IOTTIMER clock has been optimized for a simple routing of the + traces on the display board. The enumeration of the digit segments is non-standard, but + consistent across all digits. The bigger digits have two LEDs per segment, controlled by + separate digit lines of the LED controller. From the software perspective, they appear as + two layers of four digits each. + + The brightness of the LEDs can be controlled in seven steps (plus off). In theory, the + brightness of the segments with two LEDs could be set in fifteen levels (plus off). + To keep things simple and to avoid brightness gradients within segments, both LEDs of a + segment will always be set to the same level. + + The intention of this display driver (together with the drivers for the other components) + is to be able to use the IOTTIMER as an alarm clock that can be fully integrated in your + home automation using Tasmota and rules. + + This driver is not a generic TM1640B driver as use cases of the TM1640B in different + devices will differ significantly. +*/ + + + + + + +#define IOTTIMER_DIGITS 16 +#define IOTTIMER_DOT_BIT 2 + +static unsigned char IoTTimerDisplay[IOTTIMER_DIGITS]; + +// Wiring of the LEDs (per digit): +// +// Seg# Bit Hex +// 07 06 40 +// 08 01 07 00 80 01 +// 02 01 02 +// 06 04 05 03 20 08 +// 05 03 04 02 10 04 +// +// Font as per wiring: +static const byte IoTTimerFont[128] { +//0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10 +// SP ! " # $ % & ' ( ) * + , - . / + 0x00, 0xA0, 0x81, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x59, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // 0x20 +//0 1 2 3 4 5 6 7 8 9 : ; < = > ? + 0xF9, 0x09, 0x73, 0x5B, 0x8B, 0xDA, 0xFA, 0x49, 0xFB, 0xDB, 0x00, 0x00, 0x00, 0x12, 0x00, 0x63, // 0x30 +// @ A B C D E F G H I J K L M N O + 0x00, 0xEB, 0xBA, 0xF0, 0x3B, 0xF2, 0xE2, 0xFA, 0xAB, 0x09, 0x19, 0x00, 0xB0, 0x00, 0xE9, 0xF9, // 0x40 +// P Q R S T U V W X Y Z [ \ ] ^(°) _ + 0xE3, 0xAB, 0x22, 0xDA, 0xB2, 0xB9, 0x00, 0x00, 0x00, 0x4B, 0x00, 0xF0, 0x00, 0x59, 0xC3, 0x10, // 0x50 +// `=° a b c d e f g h i j k l m n o + 0x01, 0x7B, 0xBA, 0x32, 0x3B, 0xF3, 0xE2, 0xDB, 0xAA, 0x08, 0x19, 0x00, 0x09, 0x00, 0x2A, 0x3A, // 0x60 +// p q r s t u v w x y z { | } ~ DEL + 0xE3, 0xAB, 0x22, 0xDA, 0xB2, 0x38, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x0B, 0x09, 0xA2, 0x00, 0x00 // 0x70 +}; + + +void IoTTimerDim(void) +{ + TM1640SetBrightness (changeUIntScale(GetDisplayDimmer(), 0, 100, 0, 8)); +} + + +void IoTTimerDisplayOn (void) +{ + IoTTimerDim(); +} + + +void IoTTimerDisplayOff (void) +{ + TM1640SetBrightness (0); +} + + +void IoTTimerDisplayOnOff(void) +{ + if (disp_power) { + IoTTimerDisplayOn(); + } + else { + IoTTimerDisplayOff(); + } +} + + +void IoTTimerClearDisplay (void) +{ + for (int i = 0; i < IOTTIMER_DIGITS; i++) { + IoTTimerDisplay[i] = 0; + } + TM1640SendDataArray(0, IoTTimerDisplay, IOTTIMER_DIGITS); +} + + +/*********************************************************************************************\ +* Init function +\*********************************************************************************************/ +void IoTTimerInit(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + IoTTimerDim(); + IoTTimerClearDisplay(); + break; + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + IoTTimerDim(); + IoTTimerClearDisplay(); + break; + } +} + +void IoTTimerDrawStringAt(uint32_t x, uint32_t y, const char *str, uint32_t color = 0, uint32_t flag = 0); +void IoTTimerDrawStringAt(uint32_t x, uint32_t y, const char *str, uint32_t color, uint32_t flag) { + // displaytext [x] = 0123456789 + // Considers display as 1111223334 where 1111 is large white display, + // 22 is small white display, + // 333 is green display, + // 4 is "1" = pm, "2" = alarm, "3" = both + // Following also works - notice scattered dot(.), colon(:), minus(-) or plus(+) + // displaytext 12:34:56.7.8.9ab - Show all lights including "pm" (=a) and "alarm" (=b) + // displaytext 12:34:56 - Show time + // displaytext 05-11-24 - Show date + // displaytext [x6]12.3 - Show value in green leds + // displaytext [ztS] - Clear display and show current time with seconds + + bool alarm = false; + bool pm = false; + bool dot_left_up = false; + bool dot_left_dn = false; + bool dot_right_dn = false; + bool dot_right_up_left = false; + bool dot_right_up_right = false; + bool dash_left = false; + bool dash_right = false; + + char chr; + uint32_t idx = x; + const char *pos = str; + while (*pos) { + chr = *pos & 0x7F; // We only support 0 to 127 + switch (idx) { + case 0: + IoTTimerDisplay[12] = IoTTimerDisplay[13] = IoTTimerFont[chr]; // col 0 + break; + case 1: + IoTTimerDisplay[14] = IoTTimerDisplay[15] = IoTTimerFont[chr]; // col 1 + break; + case 2: + if (('.' == chr) || (':' == chr) || ('-' == chr) || ('+' == chr)) { + if ('.' == chr) { + dot_left_dn = true; + } + else if (':' == chr) { + dot_left_up = true; + dot_left_dn = true; + } + else if ('-' == chr) { + dash_left = true; + dash_right = true; + } + else if ('+' == chr) { + dot_left_up = true; + dot_left_dn = true; + dash_left = true; + dash_right = true; + } + idx--; + } else { + IoTTimerDisplay[4] = IoTTimerDisplay[5] = IoTTimerFont[chr]; // col 2 + } + break; + case 3: + IoTTimerDisplay[11] = IoTTimerDisplay[1] = IoTTimerFont[chr]; // col 3 + break; + case 4: + if (('.' == chr) || (':' == chr) || ('-' == chr)) { + idx--; // Skip + } else { + IoTTimerDisplay[6] = IoTTimerFont[chr]; // col 4 + } + break; + case 5: + IoTTimerDisplay[7] = IoTTimerFont[chr]; // col 5 + break; + case 6: + if ('.' == chr) { + dot_right_dn = true; + idx--; + } else { + // Upper right (green) + IoTTimerDisplay[9] = IoTTimerFont[chr]; // col 6 + } + break; + case 7: + if ('.' == chr) { + dot_right_up_left = true; + idx--; + } else { + IoTTimerDisplay[10] = IoTTimerFont[chr]; // col 7 + } + break; + case 8: + if ('.' == chr) { + dot_right_up_right = true; + idx--; + } else { + IoTTimerDisplay[8] = IoTTimerFont[chr]; // col 8 + } + break; + case 9: // col 9 + if (chr & 0x01) { // 1, A, a + pm = true; + } + if (chr & 0x02) { // 2, B, b + alarm = true; + } + break; + } + idx++; + pos++; + } + + // Dots and dash + if (alarm) { + IoTTimerDisplay[12] |= 1 << IOTTIMER_DOT_BIT; // Alarm symbol + } + if (pm) { + IoTTimerDisplay[13] |= 1 << IOTTIMER_DOT_BIT; // PM + } + if (dot_left_up) { + IoTTimerDisplay[14] |= 1 << IOTTIMER_DOT_BIT; // Upper dot + } + if (dot_left_dn) { + IoTTimerDisplay[4] |= 1 << IOTTIMER_DOT_BIT; // Lower dot + } + if (dot_right_dn) { + IoTTimerDisplay[7] |= 1 << IOTTIMER_DOT_BIT; // Blue dot + } + if (dot_right_up_left) { + IoTTimerDisplay[10] |= 1 << IOTTIMER_DOT_BIT; // Green dot left + } + if (dot_right_up_right) { + IoTTimerDisplay[8] |= 1 << IOTTIMER_DOT_BIT; // Green dot right + } + if (dash_left) { + IoTTimerDisplay[5] |= 1 << IOTTIMER_DOT_BIT; + } + if (dash_right) { + IoTTimerDisplay[15] |= 1 << IOTTIMER_DOT_BIT; + } + + TM1640SendDataArray(0, IoTTimerDisplay, IOTTIMER_DIGITS); +} + + +/*********************************************************************************************\ +* Displays floating point number in the upper right sub-display of the IOTTIMER. +* Format is always "[n]n[.]n" (negative number is "-n[.]n") +\*********************************************************************************************/ +void IoTTimerShowFloat(float f) { +/* + char buffer[16]; + ext_snprintf_P(buffer, sizeof(buffer),PSTR("%1_f"), &f); + IoTTimerDrawStringAt(6, 0, buffer); +*/ + bool negative = false; + float threshold = 99.95; + if (f < 0.0) { + f = -f; + negative = true; + threshold = 9.95; + } + uint8_t precision = 0; + if (f < threshold) { + f *= 10.0; + precision++; + } + uint32_t n = (uint32_t) (f + 0.5); + char buffer[5] = { 0 }; + if (negative) { + if (n > 99) { + n = 99; + } + buffer[0] = '-'; + } else { + if (n > 999) { + n = 999; + } + if (n / 100 != 0) { + buffer[0] = '0' + n / 100; + } + } + buffer[1] = '0' + n % 100 / 10; + uint32_t idx = 2; + if (precision == 1) { + buffer[2] = '.'; + idx++; + } + buffer[idx] = '0' + n % 10; + IoTTimerDrawStringAt(6, 0, buffer); +} + + +/*********************************************************************************************\ +* Update the temperature in the upper right corner. +\*********************************************************************************************/ +void IoTTimerUpdateTemperature(void) { + IoTTimerShowFloat(ConvertTempToFahrenheit(TasmotaGlobal.temperature_celsius)); +} + + +/*********************************************************************************************\ +* Adjust the brightness based on the photo diode voltage. +\*********************************************************************************************/ +void IoTTimerAdjustBrightness(void) { + // Max ADC value is 3400 [lx], but that is only reached in direct sun light. + // 20 is already quite bright. + // Illuminance value of 0 should map to level 1 (level 0 is off) + static float filteredLevel = 1.0; + float level = (float) AdcGetLux(0) / (20.0 / 7.0) + 1.0; + if (level > 8.0) level = 8.0; + if (level < 1.0) level = 1.0; + filteredLevel = 0.9 * filteredLevel + 0.1 * (float) level; + + TM1640SetBrightness ((int) (filteredLevel + 0.5)); +} + + +#ifdef USE_DISPLAY_MODES1TO5 + +/*********************************************************************************************\ +* Show the current time +\*********************************************************************************************/ +void IoTTimerShowTime(void) { + uint8_t hour = RtcTime.hour; + uint8_t min = RtcTime.minute; + uint8_t sec = RtcTime.second; + uint8_t symbol = 0; + + if (!Tm1640->clock_24) { + if (hour > 12) { + hour -= 12; + } + if (hour == 0) { + hour = 12; + } + symbol |= 1; + } + + char buffer[16]; + snprintf_P(buffer, sizeof(buffer), PSTR("%2d:%02d"), hour, min); + if (Tm1640->clock_seconds) { + snprintf_P(buffer, sizeof(buffer), PSTR("%s:%02d"), buffer, sec); + } else { + snprintf_P(buffer, sizeof(buffer), PSTR("%s "), buffer); // Erase seconds in case toggling between date/time + } + IoTTimerDrawStringAt(0, 0, buffer); + + if (Settings->timer[0].arm) { + symbol |= 2; + } + + snprintf_P(buffer, sizeof(buffer), PSTR("%d"), symbol); + IoTTimerDrawStringAt(9, 0, buffer); +} + + +/*********************************************************************************************\ +* Show the current date +\*********************************************************************************************/ +void IoTTimerShowDate(void) +{ + uint8_t left = RtcTime.day_of_month; + uint8_t middle = RtcTime.month; + uint8_t right = RtcTime.year % 100; + + if (!Tm1640->clock_24) { + // Use U.S. date format. + left = RtcTime.month; + middle = RtcTime.day_of_month; + } + + char buffer[16]; + snprintf_P(buffer, sizeof(buffer), PSTR("%02d-%02d-%02d"), left, middle, right); + IoTTimerDrawStringAt(0, 0, buffer); +} + + +void IoTTimerRefresh(void) { // Every second + if (!disp_power) { return; } + + // Update temperature display content: + IoTTimerUpdateTemperature(); + + // Auto-adjust brightness: + IoTTimerAdjustBrightness(); + + if (Settings->display_mode) { // Mode 0 is User text + switch (Settings->display_mode) { + case 1: // Time + IoTTimerShowTime(); + break; + case 2: // Date + IoTTimerShowDate(); + break; + case 3: // Time/Date + if (TasmotaGlobal.uptime % Settings->display_refresh) + { + IoTTimerShowTime(); + } + else + { + IoTTimerShowDate(); + } + break; + case 4: + case 5: + // not in use + break; + } + } +} + +#endif // USE_DISPLAY_MODES1TO5 + + +/*********************************************************************************************\ +* Displays a clock. +* Command: DisplayClock 1 // 12-hour format +* DisplayClock 2 // 24-hour format +* DisplayClock 3 // 12-hour without seconds +* DisplayClock 4 // 24-hour without seconds +\*********************************************************************************************/ +void CmndIoTTimerClock(void) { + uint16_t val = XdrvMailbox.payload; + + if (ArgC() == 0) + val = 0; + + if ((val < 1) || (val > 4)) + return; + + if (val == 1) { + Tm1640->show_clock = true; + Tm1640->clock_24 = false; + Tm1640->clock_seconds = true; + } + else if (val == 2) { + Tm1640->show_clock = true; + Tm1640->clock_24 = true; + Tm1640->clock_seconds = true; + } + else if (val == 3) { + Tm1640->show_clock = true; + Tm1640->clock_24 = false; + Tm1640->clock_seconds = false; + } + else if (val == 4) { + Tm1640->show_clock = true; + Tm1640->clock_24 = true; + Tm1640->clock_seconds = false; + } else { + Tm1640->show_clock = false; + Tm1640->clock_24 = false; + } + + IoTTimerClearDisplay(); +} + + +#endif // USE_IOTTIMER + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdsp13(uint32_t function) { + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + TM1640Init(); + } + else if (Tm1640 && (XDSP_13 == Settings->display_model)) { + +#ifdef USE_IOTTIMER + + switch (function) { + case FUNC_DISPLAY_INIT: + IoTTimerInit(dsp_init); + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + IoTTimerRefresh(); + break; +#endif // USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_CLOCK: + CmndIoTTimerClock(); + break; + case FUNC_DISPLAY_CLEAR: + IoTTimerClearDisplay(); + break; + case FUNC_DISPLAY_NUMBER: + case FUNC_DISPLAY_NUMBERNC: + case FUNC_DISPLAY_FLOAT: + case FUNC_DISPLAY_FLOATNC: + IoTTimerShowFloat(CharToFloat(XdrvMailbox.data)); + break; + case FUNC_DISPLAY_DIM: + IoTTimerDim(); + break; + case FUNC_DISPLAY_POWER: + IoTTimerDisplayOnOff(); + break; + case FUNC_DISPLAY_DRAW_STRING: + IoTTimerDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); + break; + } + +#endif // USE_IOTTIMER + + } + return result; +} + +#endif // USE_DISPLAY_TM1640 +#endif // USE_DISPLAY diff --git a/tasmota/tasmota_xdsp_display/xdsp_14_SSD1331.ino b/tasmota/tasmota_xdsp_display/xdsp_14_SSD1331.ino deleted file mode 100644 index 2f6f88ceddf3..000000000000 --- a/tasmota/tasmota_xdsp_display/xdsp_14_SSD1331.ino +++ /dev/null @@ -1,21 +0,0 @@ -/* - xdsp_14_SSD1331.ino - Display SSD1331 support for Tasmota - - Copyright (C) 2021 Jeroen Vermeulen, Gerhard Mutz and Theo Arends - - 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 . -*/ - -// REMOVED -// DEPRECATED - USE UNIVERSAL DISPLAY INSTEAD, https://tasmota.github.io/docs/Universal-Display-Driver/#migrating-to-udisplay diff --git a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino index b05d15e8abcd..9be3d3117fb7 100644 --- a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino +++ b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino @@ -17,6 +17,7 @@ along with this program. If not, see . */ +#if defined(ESP8266) || defined(USE_WS2812_FORCE_NEOPIXELBUS) #ifdef USE_LIGHT #ifdef USE_WS2812 @@ -553,11 +554,14 @@ void Ws2812DDP(void) } #endif // USE_NETWORK_LIGHT_SCHEMES -void Ws2812Clear(void) +void Ws2812Clear(bool display = true); +void Ws2812Clear(bool display) { strip->ClearTo(0); - Ws2812LibStripShow(); - Ws2812.show_next = 1; + if (display) { + Ws2812LibStripShow(); + Ws2812.show_next = 1; + } } void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) @@ -965,3 +969,4 @@ bool Xlgt01(uint32_t function) #endif // USE_WS2812 #endif // USE_LIGHT +#endif // defined(ESP8266) || defined(USE_WS2812_FORCE_NEOPIXELBUS) diff --git a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino new file mode 100644 index 000000000000..e3ad5aa54011 --- /dev/null +++ b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino @@ -0,0 +1,881 @@ +/* + xlgt_01_ws2812.ino - led string support for Tasmota + + Copyright (C) 2021 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef ESP32 +#ifdef USE_LIGHT +#if defined(USE_WS2812) && !defined(USE_WS2812_FORCE_NEOPIXELBUS) + +/*********************************************************************************************\ + * WS2812 RGB / RGBW Leds using NeopixelBus library + * + * light_scheme WS2812 3+ Colors 1+2 Colors Effect + * ------------ ------ --------- ---------- ----------------- + * 0 (5) yes no no Clock + * 1 (6) yes no no Incandescent + * 2 (7) yes no no RGB + * 3 (8) yes no no Christmas + * 4 (9) yes no no Hanukkah + * 5 (10) yes no no Kwanzaa + * 6 (11) yes no no Rainbow + * 7 (12) yes no no Fire + * 8 (13) yes no no Stairs + * 9 (14) yes no no Clear (= Berry) + * 10 (15) yes no no Optional DDP +\*********************************************************************************************/ + +#define XLGT_01 1 + +const uint8_t WS2812_SCHEMES = 10; // Number of WS2812 schemes + +const char kWs2812Commands[] PROGMEM = "|" // No prefix + D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH "|" D_CMND_STEPPIXELS ; + +void (* const Ws2812Command[])(void) PROGMEM = { + &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth, &CmndStepPixels }; + +#include + +const uint16_t kLedType = 0; +// select the right pixel size +#if (USE_WS2812_CTYPE > NEO_3LED) + const uint16_t kTasLed_PixelSize = TasmotaLed_4_WRGB; +#else + const uint16_t kTasLed_PixelSize = TasmotaLed_3_RGB; +#endif + +// select the right pixel order +#if (USE_WS2812_CTYPE == NEO_GRB) + const uint16_t kTasLed_PixelOrder = TasmotaLed_GRB; +#elif (USE_WS2812_CTYPE == NEO_BRG) + const uint16_t kTasLed_PixelOrder = TasmotaLed_BRG; +#elif (USE_WS2812_CTYPE == NEO_RBG) + const uint16_t kTasLed_PixelOrder = TasmotaLed_RBG; + #elif (USE_WS2812_CTYPE == NEO_RGBW) + const uint16_t kTasLed_PixelOrder = TasmotaLed_RGB; +#elif (USE_WS2812_CTYPE == NEO_GRBW) + const uint16_t kTasLed_PixelOrder = TasmotaLed_GRB; +#else + const uint16_t kTasLed_PixelOrder = TasmotaLed_RGB; +#endif + +// all leds have always W at the end +const uint16_t kTasLed_PixelWhite = TasmotaLed_xxxW; + +// We drop support for NEO_HW_P9813, as it is too hard to support with hardwarre +#if (USE_WS2812_HARDWARE == NEO_HW_P9813) + #error "P9813 is not supported by this library" +#endif // USE_WS2812_CTYPE + +// select timing +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + const uint16_t kTasLed_Timing = TasmotaLed_WS2812; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + const uint16_t kTasLed_Timing = TasmotaLed_SK6812; +#elif (USE_WS2812_HARDWARE == NEO_HW_APA106) + #error "APA106 is not supported by this library" +#else // USE_WS2812_HARDWARE + const uint16_t kTasLed_Timing = TasmotaLed_WS2812; +#endif // USE_WS2812_HARDWARE + +const uint16_t kTasLed_Type = kTasLed_PixelSize | kTasLed_PixelOrder | kTasLed_PixelWhite | kTasLed_Timing; + +// select hardware acceleration - bitbanging is not supported on ESP32 due to interference of interrupts - use default +const uint32_t kTasLed_Hardware = TasmotaLed_HW_Default; // use whatever is available + +#if (USE_WS2812_HARDWARE == NEO_HW_P9813) + #error "P9813 is not supported by this library" +#endif + + +/*******************************************************************************************\ + * Support for TasmotaLED + * + * From here we have defined: + * kTasLed_Type: encodes pixel size, pixel order, pixel white, timing + * kTasLed_Hardware: encodes the harware support to push to Leds: RMT, SPI, I2S + *******************************************************************************************/ +TasmotaLED *strip = nullptr; + +typedef union LedColor { + uint32_t C; // encoded as 0xWWRRGGBB + struct { + uint8_t B, G, R, W; // WRGB in little endian + }; +} LedColor; + +struct ColorScheme { + const LedColor* colors; + uint8_t count; +}; + +const LedColor kIncandescent[2] = { 0xFF8C14, 0x000000 }; +const LedColor kRgb[3] = { 0xFF0000, 0x00FF00, 0x0000FF }; +const LedColor kChristmas[2] = { 0xFF0000, 0x00FF00 }; +const LedColor kHanukkah[2] = { 0x0000FF, 0xFFFFFF }; +const LedColor kwanzaa[3] = { 0xFF0000, 0x000000, 0x00FF00 }; +const LedColor kRainbow[7] = { 0xFF0000, 0xFF8000, 0xFFFF00, 0x00FF00, 0x0000FF, 0x8000FF, 0xFF00FF }; +const LedColor kFire[3] = { 0xFF0000, 0xFF6600, 0xFFC000 }; +const LedColor kStairs[2] = { 0x000000, 0xFFFFFF0 }; + +const ColorScheme kSchemes[WS2812_SCHEMES -2] = { // Skip clock and clear scheme + kIncandescent, 2, + kRgb, 3, + kChristmas, 2, + kHanukkah, 2, + kwanzaa, 3, + kRainbow, 7, + kFire, 3, + kStairs, 2 +}; + +const uint8_t kWidth[5] = { + 1, // Small + 2, // Medium + 4, // Large + 8, // Largest + 255 // All +}; +const uint8_t kWsRepeat[5] = { + 8, // Small + 6, // Medium + 4, // Large + 2, // Largest + 1 // All +}; + +struct WS2812 { + uint8_t show_next = 1; + uint8_t scheme_offset = 0; + bool suspend_update = false; +} Ws2812; + +/********************************************************************************************/ + +// For some reason map fails to compile so renamed to wsmap +long wsmap(long x, long in_min, long in_max, long out_min, long out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +void Ws2812LibStripShow(void) { + strip->Show(); +} + +void Ws2812StripShow(void) +{ + LedColor c; + + if (Settings->light_correction) { + for (uint32_t i = 0; i < Settings->light_pixels; i++) { + c.C = strip->GetPixelColor(i); + c.R = ledGamma(c.R); + c.G = ledGamma(c.G); + c.B = ledGamma(c.B); +#if (USE_WS2812_CTYPE > NEO_3LED) + c.W = ledGamma(c.W); +#endif + strip->SetPixelColor(i, c.C); + } + } + Ws2812LibStripShow(); +} + +int mod(int a, int b) +{ + int ret = a % b; + if (ret < 0) ret += b; + return ret; +} + +void Ws2812UpdatePixelColor(int position, LedColor hand_color, float offset); +void Ws2812UpdatePixelColor(int position, LedColor hand_color, float offset) +{ + LedColor color; + + uint32_t mod_position = mod(position, (int)Settings->light_pixels); + + color.C = strip->GetPixelColor(mod_position); + float dimmer = 100 / (float)Settings->light_dimmer; + color.R = tmin(color.R + ((hand_color.R / dimmer) * offset), 255); + color.G = tmin(color.G + ((hand_color.G / dimmer) * offset), 255); + color.B = tmin(color.B + ((hand_color.B / dimmer) * offset), 255); + strip->SetPixelColor(mod_position, color.C); +} + +void Ws2812UpdateHand(int position, uint32_t index) +{ + uint32_t width = Settings->light_width; + if (index < WS_MARKER) { width = Settings->ws_width[index]; } + if (!width) { return; } // Skip + + position = (position + Settings->light_rotation) % Settings->light_pixels; + + if (Settings->flag.ws_clock_reverse) { // SetOption16 - Switch between clockwise or counter-clockwise + position = Settings->light_pixels -position; + } + LedColor hand_color = {0}; + hand_color.R = Settings->ws_color[index][WS_RED]; + hand_color.G = Settings->ws_color[index][WS_GREEN]; + hand_color.B = Settings->ws_color[index][WS_BLUE]; + + Ws2812UpdatePixelColor(position, hand_color, 1); + + uint32_t range = ((width -1) / 2) +1; + for (uint32_t h = 1; h < range; h++) { + float offset = (float)(range - h) / (float)range; + Ws2812UpdatePixelColor(position -h, hand_color, offset); + Ws2812UpdatePixelColor(position +h, hand_color, offset); + } +} + +void Ws2812Clock(void) +{ + strip->ClearTo(0); // Reset strip + int clksize = 60000 / (int)Settings->light_pixels; + + Ws2812UpdateHand((RtcTime.second * 1000) / clksize, WS_SECOND); + Ws2812UpdateHand((RtcTime.minute * 1000) / clksize, WS_MINUTE); + Ws2812UpdateHand((((RtcTime.hour % 12) * 5000) + ((RtcTime.minute * 1000) / 12 )) / clksize, WS_HOUR); + if (Settings->ws_color[WS_MARKER][WS_RED] + Settings->ws_color[WS_MARKER][WS_GREEN] + Settings->ws_color[WS_MARKER][WS_BLUE]) { + for (uint32_t i = 0; i < 12; i++) { + Ws2812UpdateHand((i * 5000) / clksize, WS_MARKER); + } + } + + Ws2812StripShow(); +} + +void Ws2812GradientColor(uint32_t schemenr, LedColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i); +void Ws2812GradientColor(uint32_t schemenr, LedColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i) +{ +/* + * Compute the color of a pixel at position i using a gradient of the color scheme. + * This function is used internally by the gradient function. + */ + ColorScheme scheme = kSchemes[schemenr]; + uint32_t curRange = i / range; + uint32_t rangeIndex = i % range; + uint32_t colorIndex = rangeIndex / gradRange; + uint32_t start = colorIndex; + uint32_t end = colorIndex +1; + if (curRange % 2 != 0) { + start = (scheme.count -1) - start; + end = (scheme.count -1) - end; + } + float dimmer = 100 / (float)Settings->light_dimmer; + float fmyRed = (float)wsmap(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].R, scheme.colors[end].R) / dimmer; + float fmyGrn = (float)wsmap(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].G, scheme.colors[end].G) / dimmer; + float fmyBlu = (float)wsmap(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].B, scheme.colors[end].B) / dimmer; + mColor->R = (uint8_t)fmyRed; + mColor->G = (uint8_t)fmyGrn; + mColor->B = (uint8_t)fmyBlu; +} + +void Ws2812Gradient(uint32_t schemenr) +{ +/* + * This routine courtesy Tony DiCola (Adafruit) + * Display a gradient of colors for the current color scheme. + * Repeat is the number of repetitions of the gradient (pick a multiple of 2 for smooth looping of the gradient). + */ + LedColor c; + + ColorScheme scheme = kSchemes[schemenr]; + if (scheme.count < 2) { return; } + + uint32_t repeat = kWsRepeat[Settings->light_width]; // number of scheme.count per ledcount + uint32_t range = (uint32_t)ceil((float)Settings->light_pixels / (float)repeat); + uint32_t gradRange = (uint32_t)ceil((float)range / (float)(scheme.count - 1)); + uint32_t speed = ((Settings->light_speed * 2) -1) * (STATES / 10); + uint32_t offset = speed > 0 ? Light.strip_timer_counter / speed : 0; + + LedColor oldColor, currentColor; + Ws2812GradientColor(schemenr, &oldColor, range, gradRange, offset); + currentColor = oldColor; + speed = speed ? speed : 1; // should never happen, just avoid div0 + for (uint32_t i = 0; i < Settings->light_pixels; i++) { + if (kWsRepeat[Settings->light_width] > 1) { + Ws2812GradientColor(schemenr, ¤tColor, range, gradRange, i + offset + 1); + } + // Blend old and current color based on time for smooth movement. + c.R = wsmap(Light.strip_timer_counter % speed, 0, speed, oldColor.R, currentColor.R); + c.G = wsmap(Light.strip_timer_counter % speed, 0, speed, oldColor.G, currentColor.G); + c.B = wsmap(Light.strip_timer_counter % speed, 0, speed, oldColor.B, currentColor.B); + strip->SetPixelColor(i, c.C); + oldColor = currentColor; + } + Ws2812StripShow(); +} + +void Ws2812Bars(uint32_t schemenr) +{ +/* + * This routine courtesy Tony DiCola (Adafruit) + * Display solid bars of color for the current color scheme. + * Width is the width of each bar in pixels/lights. + */ + LedColor c; + + ColorScheme scheme = kSchemes[schemenr]; + + uint32_t maxSize = Settings->light_pixels / scheme.count; + if (kWidth[Settings->light_width] > maxSize) { maxSize = 0; } + + uint32_t speed = ((Settings->light_speed * 2) -1) * (STATES / 10); + uint32_t offset = (speed > 0) ? Light.strip_timer_counter / speed : 0; + + LedColor mcolor[scheme.count]; + memcpy(mcolor, scheme.colors, sizeof(mcolor)); + float dimmer = 100 / (float)Settings->light_dimmer; + for (uint32_t i = 0; i < scheme.count; i++) { + float fmyRed = (float)mcolor[i].R / dimmer; + float fmyGrn = (float)mcolor[i].G / dimmer; + float fmyBlu = (float)mcolor[i].B / dimmer; + mcolor[i].R = (uint8_t)fmyRed; + mcolor[i].G = (uint8_t)fmyGrn; + mcolor[i].B = (uint8_t)fmyBlu; + } + uint32_t colorIndex = offset % scheme.count; + for (uint32_t i = 0; i < Settings->light_pixels; i++) { + if (maxSize) { colorIndex = ((i + offset) % (scheme.count * kWidth[Settings->light_width])) / kWidth[Settings->light_width]; } + c.R = mcolor[colorIndex].R; + c.G = mcolor[colorIndex].G; + c.B = mcolor[colorIndex].B; + strip->SetPixelColor(i, c.C); + } + Ws2812StripShow(); +} + +void Ws2812Steps(uint32_t schemenr) { + LedColor c; + + ColorScheme scheme = kSchemes[schemenr]; + // apply main color if current sheme == kStairs + if (scheme.colors == kStairs) { + // we patch the colors + static LedColor colors_stairs[2] = { + { 0x000000 }, + { .B = Settings->light_color[2], .G = Settings->light_color[1], .R = Settings->light_color[0] } + }; + scheme.colors = colors_stairs; + } + + uint8_t scheme_count = scheme.count; + if (Settings->light_fade) { + scheme_count = Settings->ws_width[WS_HOUR]; // Width4 + } + if (scheme_count < 2) { + scheme_count = 2; + } + + LedColor mcolor[scheme_count]; + + uint8_t color_start = 0; + uint8_t color_end = 1; + if (Settings->light_rotation & 0x01) { + color_start = 1; + color_end = 0; + } + + if (Settings->light_fade) { + // generate gradient (width = Width4) + for (uint32_t i = 1; i < scheme_count - 1; i++) { + mcolor[i].R = (uint8_t) wsmap(i, 0, scheme_count, scheme.colors[color_start].R, scheme.colors[color_end].R); + mcolor[i].G = (uint8_t) wsmap(i, 0, scheme_count, scheme.colors[color_start].G, scheme.colors[color_end].G); + mcolor[i].B = (uint8_t) wsmap(i, 0, scheme_count, scheme.colors[color_start].B, scheme.colors[color_end].B); + } + } else { + memcpy(mcolor, scheme.colors, sizeof(mcolor)); + } + // Repair first & last color in gradient; apply scheme rotation if fade==0 + mcolor[0].R = scheme.colors[color_start].R; + mcolor[0].G = scheme.colors[color_start].G; + mcolor[0].B = scheme.colors[color_start].B; + mcolor[scheme_count-1].R = scheme.colors[color_end].R; + mcolor[scheme_count-1].G = scheme.colors[color_end].G; + mcolor[scheme_count-1].B = scheme.colors[color_end].B; + + // Adjust to dimmer value + float dimmer = 100 / (float)Settings->light_dimmer; + for (uint32_t i = 0; i < scheme_count; i++) { + float fmyRed = (float)mcolor[i].R / dimmer; + float fmyGrn = (float)mcolor[i].G / dimmer; + float fmyBlu = (float)mcolor[i].B / dimmer; + mcolor[i].R = (uint8_t)fmyRed; + mcolor[i].G = (uint8_t)fmyGrn; + mcolor[i].B = (uint8_t)fmyBlu; + } + + uint32_t speed = Settings->light_speed; + int32_t current_position = Light.strip_timer_counter / speed; + + //all pixels are shown already | rotation change will not change current state + if (current_position > Settings->light_pixels / Settings->light_step_pixels + scheme_count ) { + return; + } + + int32_t colorIndex; + int32_t step_nr; + + for (uint32_t i = 0; i < Settings->light_pixels; i++) { + step_nr = i / Settings->light_step_pixels; + colorIndex = current_position - step_nr; + if (colorIndex < 0) { colorIndex = 0; } + if (colorIndex > scheme_count - 1) { colorIndex = scheme_count - 1; } + c.R = mcolor[colorIndex].R; + c.G = mcolor[colorIndex].G; + c.B = mcolor[colorIndex].B; + // Adjust the scheme rotation + if (Settings->light_rotation & 0x02) { + strip->SetPixelColor(Settings->light_pixels - i - 1, c.C); + } else { + strip->SetPixelColor(i, c.C); + } + } + Ws2812StripShow(); +} + +#ifdef USE_NETWORK_LIGHT_SCHEMES +void Ws2812DDP(void) +{ + LedColor c = {0}; + + // Can't be trying to initialize UDP too early. + if (TasmotaGlobal.restart_flag || TasmotaGlobal.global_state.network_down) return; + + // Start DDP listener, if fail, just set last ddp_color + if (!ddp_udp_up) { + if (!ddp_udp.begin(4048)) return; + ddp_udp_up = 1; + AddLog(LOG_LEVEL_DEBUG_MORE, "DDP: UDP Listener Started: WS2812 Scheme"); + } + + // Get the DDP payload over UDP + std::vector payload; + while (uint16_t packet_size = ddp_udp.parsePacket()) { + payload.resize(packet_size); + if (!ddp_udp.read(&payload[0], payload.size())) { + continue; + } + } + + // No verification checks performed against packet besides length + if (payload.size() > (9+3*Settings->light_pixels)) { + for (uint32_t i = 0; i < Settings->light_pixels; i++) { + c.R = payload[10+3*i]; + c.G = payload[11+3*i]; + c.B = payload[12+3*i]; + strip->SetPixelColor(i, c.C); + } + Ws2812StripShow(); + } +} +#endif // USE_NETWORK_LIGHT_SCHEMES + +void Ws2812Clear(bool display = true); +void Ws2812Clear(bool display) +{ + strip->ClearTo(0); + if (display) { + Ws2812LibStripShow(); + Ws2812.show_next = 1; + } +} + +void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) +{ + LedColor lcolor = {0}; + + lcolor.R = red; + lcolor.G = green; + lcolor.B = blue; + if (led) { + strip->SetPixelColor(led -1, lcolor.C); // Led 1 is strip Led 0 -> substract offset 1 + } else { +// strip->ClearTo(lcolor); // Set WS2812_MAX_LEDS pixels + for (uint32_t i = 0; i < Settings->light_pixels; i++) { + strip->SetPixelColor(i, lcolor.C); + } + } + + if (!Ws2812.suspend_update) { + Ws2812LibStripShow(); + Ws2812.show_next = 1; + } +} + +char* Ws2812GetColor(uint32_t led, char* scolor) +{ + uint8_t sl_ledcolor[4]; + + LedColor lcolor; + lcolor.C = strip->GetPixelColor(led -1); + sl_ledcolor[0] = lcolor.R; + sl_ledcolor[1] = lcolor.G; + sl_ledcolor[2] = lcolor.B; + sl_ledcolor[3] = lcolor.W; + scolor[0] = '\0'; + for (uint32_t i = 0; i < Light.subtype; i++) { + if (Settings->flag.decimal_text) { // SetOption17 - Switch between decimal or hexadecimal output (0 = hexadecimal, 1 = decimal) + snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", sl_ledcolor[i]); + } else { + snprintf_P(scolor, 25, PSTR("%s%02X"), scolor, sl_ledcolor[i]); + } + } + return scolor; +} + +/*********************************************************************************************\ + * Public - used by scripter only +\*********************************************************************************************/ + +void Ws2812ForceSuspend (void) +{ + Ws2812.suspend_update = true; +} + +void Ws2812ForceUpdate (void) +{ + Ws2812.suspend_update = false; + Ws2812LibStripShow(); + Ws2812.show_next = 1; +} + +/********************************************************************************************/ + +bool Ws2812SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); + + return true; +} + +void Ws2812ShowScheme(void) +{ + uint32_t scheme = Settings->light_scheme - Ws2812.scheme_offset; + +#ifdef USE_NETWORK_LIGHT_SCHEMES + if ((scheme != 10) && (ddp_udp_up)) { + ddp_udp.stop(); + ddp_udp_up = 0; + AddLog(LOG_LEVEL_DEBUG_MORE, "DDP: UDP Stopped: WS2812 Scheme not DDP"); + } +#endif + switch (scheme) { + case 0: // Clock + if ((1 == TasmotaGlobal.state_250mS) || (Ws2812.show_next)) { + Ws2812Clock(); + Ws2812.show_next = 0; + } + break; + case 9: // Clear + if (Settings->light_scheme != Light.last_scheme) { + Ws2812Clear(); + } + break; +#ifdef USE_NETWORK_LIGHT_SCHEMES + case 10: + Ws2812DDP(); + break; +#endif // USE_NETWORK_LIGHT_SCHEMES + default: + if(Settings->light_step_pixels > 0){ + Ws2812Steps(scheme -1); + } else { + if (1 == Settings->light_fade) { + Ws2812Gradient(scheme -1); + } else { + Ws2812Bars(scheme -1); + } + } + Ws2812.show_next = 1; + break; + } +} + +bool Ws2812InitStrip(void) +{ + if (strip != nullptr) { + return true; + } + + if (PinUsed(GPIO_WS2812)) { // RGB led + int32_t gpio = Pin(GPIO_WS2812); + TasmotaLEDPusher * pusher = TasmotaLEDPusher::Create(kTasLed_Hardware, gpio); + if (pusher == nullptr) { + AddLog(LOG_LEVEL_ERROR, "LED: No hardware supported"); + return false; + } + strip = new TasmotaLED(kTasLed_Type, Settings->light_pixels); + strip->SetPusher(pusher); + strip->Begin(); + + Ws2812Clear(); + return true; + } + return false; +} + +void Ws2812ModuleSelected(void) +{ + if (Ws2812InitStrip()) { + Ws2812.scheme_offset = Light.max_scheme +1; + Light.max_scheme += WS2812_SCHEMES; + +#ifdef USE_NETWORK_LIGHT_SCHEMES + Light.max_scheme++; +#endif + +#if (USE_WS2812_CTYPE > NEO_3LED) + TasmotaGlobal.light_type = LT_RGBW; +#else + TasmotaGlobal.light_type = LT_RGB; +#endif + TasmotaGlobal.light_driver = XLGT_01; + } +} + +#ifdef ESP32 +#ifdef USE_BERRY +/********************************************************************************************/ +// Callbacks for Berry driver +// +// Since we dont' want to export all the template stuff, we need to encapsulate the calls +// in plain functions +// +void *Ws2812GetStrip(void) { + return strip; +} + +void Ws2812Begin(void) { + if (strip) { strip->Begin(); } +} + +void Ws2812Show(void) { + if (strip) { strip->Show(); } +} + +uint32_t Ws2812PixelsSize(void) { + if (strip) { return strip->PixelCount(); } + return 0; +} + +bool Ws2812CanShow(void) { + if (strip) { return strip->CanShow(); } + return false; +} + +bool Ws2812IsDirty(void) { + if (strip) { return strip->IsDirty(); } + return false; +} + +void Ws2812Dirty(void) { + if (strip) { strip->Dirty(); } +} + +uint8_t * Ws2812Pixels(void) { + if (strip) { return strip->Pixels(); } + return nullptr; +} + +size_t Ws2812PixelSize(void) { + if (strip) { return strip->PixelSize(); } + return 0; +} + +size_t Ws2812PixelCount(void) { + if (strip) { return strip->PixelCount(); } + return 0; +} + +void Ws2812ClearTo(uint8_t r, uint8_t g, uint8_t b, uint8_t w, int32_t from, int32_t to) { + LedColor lcolor; + lcolor.W = w; + lcolor.R = r; + lcolor.G = g; + lcolor.B = b; + if (strip) { + if (from < 0) { + strip->ClearTo(lcolor.C); + } else { + strip->ClearTo(lcolor.C, from, to); + } + } +} + +void Ws2812SetPixelColor(uint32_t idx, uint8_t r, uint8_t g, uint8_t b, uint8_t w) +{ + LedColor lcolor; + lcolor.W = w; + lcolor.R = r; + lcolor.G = g; + lcolor.B = b; + if (strip) { + strip->SetPixelColor(idx, lcolor.C); + } +} + +uint32_t Ws2812GetPixelColor(uint32_t idx) { + LedColor lcolor; + if (strip) { + lcolor.C = strip->GetPixelColor(idx); + return lcolor.C; // already encoded as WWRRGGBB + // return (lcolor.W << 24) | (lcolor.R << 16) | (lcolor.G << 8) | lcolor.B; + } + return 0; +} + +#endif // ESP32 +#endif // USE_BERRY + +/********************************************************************************************/ + +void CmndLed(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings->light_pixels)) { + if (XdrvMailbox.data_len > 0) { + char *p; + uint16_t idx = XdrvMailbox.index; + Ws2812ForceSuspend(); + for (char *color = strtok_r(XdrvMailbox.data, " ", &p); color; color = strtok_r(nullptr, " ", &p)) { + if (LightColorEntry(color, strlen(color))) { + Ws2812SetColor(idx, Light.entry_color[0], Light.entry_color[1], Light.entry_color[2], Light.entry_color[3]); + idx++; + if (idx > Settings->light_pixels) { break; } + } else { + break; + } + } + Ws2812ForceUpdate(); + } + char scolor[LIGHT_COLOR_SIZE]; + ResponseCmndIdxChar(Ws2812GetColor(XdrvMailbox.index, scolor)); + } +} + +void CmndPixels(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { +/* + Settings->light_pixels = XdrvMailbox.payload; + Settings->light_rotation = 0; + Ws2812ReinitStrip(); -- does not work with latest NeoPixelBus driver + Light.update = true; +*/ + Ws2812Clear(); // Clear all known pixels + Settings->light_pixels = XdrvMailbox.payload; + Settings->light_rotation = 0; + TasmotaGlobal.restart_flag = 2; // reboot instead + } + ResponseCmndNumber(Settings->light_pixels); +} + +void CmndStepPixels(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { + Settings->light_step_pixels = (XdrvMailbox.payload > WS2812_MAX_LEDS) ? WS2812_MAX_LEDS : XdrvMailbox.payload; + // Ws2812ReinitStrip(); -- not sure it's actually needed + Light.update = true; + } + ResponseCmndNumber(Settings->light_step_pixels); +} + + +void CmndRotation(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < Settings->light_pixels)) { + Settings->light_rotation = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings->light_rotation); +} + +void CmndWidth(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + if (1 == XdrvMailbox.index) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 4)) { + Settings->light_width = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings->light_width); + } else { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) { + Settings->ws_width[XdrvMailbox.index -2] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings->ws_width[XdrvMailbox.index -2]); + } + } +} + +/*********************************************************************************************\ + * Internal calls for ArtNet +\*********************************************************************************************/ +// check is the Neopixel strip is configured +bool Ws2812StripConfigured(void) { + return strip != nullptr; +} +size_t Ws2812StripGetPixelSize(void) { + return strip->PixelSize(); +} +// return true if strip was dirty and an actual refresh was triggered +bool Ws2812StripRefresh(void) { + if (strip->IsDirty()) { + Ws2812LibStripShow(); + return true; + } else { + return false; + } +} +void Ws2812CopyPixels(const uint8_t *buf, size_t len, size_t offset_in_matrix) { + uint8_t *pixels = strip->Pixels(); + memmove(&pixels[offset_in_matrix], buf, len); + strip->Dirty(); +} + + + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xlgt01(uint32_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Ws2812SetChannels(); + break; + case FUNC_SET_SCHEME: + Ws2812ShowScheme(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kWs2812Commands, Ws2812Command); + break; + case FUNC_MODULE_INIT: + Ws2812ModuleSelected(); + break; + } + return result; +} + +#endif // USE_WS2812 +#endif // USE_LIGHT +#endif // ESP32 \ No newline at end of file diff --git a/tasmota/tasmota_xnrg_energy/xnrg_07_ade7953.ino b/tasmota/tasmota_xnrg_energy/xnrg_07_ade7953.ino index 26f5f61ecea6..31a6fd3c6c61 100644 --- a/tasmota/tasmota_xnrg_energy/xnrg_07_ade7953.ino +++ b/tasmota/tasmota_xnrg_energy/xnrg_07_ade7953.ino @@ -24,7 +24,7 @@ #ifdef USE_ENERGY_SENSOR #ifdef USE_ADE7953 /*********************************************************************************************\ - * ADE7953 - Energy used in Shelly 2.5 (model 1), EM (model 2), Plus 2PM (model 3), Pro 1PM (model 4), Pro 2PM (model 5) and Pro 4PM (model 6) + * ADE7953 - Energy used in Shelly 2.5 (model 1), EM (model 2), Plus 2PM and 2PM Gen3 (model 3), Pro 1PM (model 4), Pro 2PM (model 5) and Pro 4PM (model 6) * * {"NAME":"Shelly 2.5","GPIO":[320,0,32,0,224,193,0,0,640,192,608,225,3456,4736],"FLAG":0,"BASE":18} * {"NAME":"Shelly EM","GPIO":[0,0,0,0,0,0,0,0,640,3457,608,224,8832,1],"FLAG":0,"BASE":18} @@ -33,14 +33,17 @@ * {"NAME":"Shelly Pro 1PM","GPIO":[9568,1,9472,1,768,0,0,0,672,704,736,0,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,3459,0,0,32,4736,0,160,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350"} * {"NAME":"Shelly Pro 2PM","GPIO":[9568,1,9472,1,768,0,0,0,672,704,736,9569,0,0,5600,6214,0,0,0,5568,0,0,0,0,0,0,0,0,3460,0,0,32,4736,4737,160,161],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350;AdcParam2 2,5600,4700,3350"} * {"NAME":"Shelly Pro 4PM","GPIO":[0,6210,0,6214,9568,0,0,0,0,0,9569,0,768,0,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,736,704,3461,0,4736,0,0,672],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350"} - * + * {"NAME":"Shelly 2PM Gen3","GPIO":[9472,3458,576,225,4736,224,640,608,1,1,193,0,0,0,0,0,0,0,192,32,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio4 10000,10000,4000"} + * * Based on datasheet from https://www.analog.com/en/products/ade7953.html * * Model differences: * Function Model1 Model2 Model3 Model4 Model5 Model6 Remark * ------------------------------ ------- ------- ------- ------ ------ ------ ------------------------------------------------- - * Shelly 2.5 EM Plus2PM Pro1PM Pro2PM Pro4PM + * Shelly 2.5 EM Plus2PM Pro1PM Pro2PM Pro4PM Shelly hardware + * 2PMGen3 * Processor ESP8266 ESP8266 ESP32 ESP32 ESP32 ESP32 + * ESP32C3 Shelly Gen3 * Interface I2C I2C I2C SPI SPI SPI Interface type used * Number of inputs 2 2 2 1 2 4 Count of ADE9753 inputs used * Number of ADE9753 chips 1 1 1 1 2 2 Count of ADE9753 chips diff --git a/tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino b/tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino index d0f8ad9e9ed4..4b26748ebfdc 100644 --- a/tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino +++ b/tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino @@ -32,6 +32,7 @@ // #define SOLAXX1_READCONFIG // enable to read inverters config; disable to save codespace (3k1) +#define SOLAXX1_BUFFERSIZE 256 #define INVERTER_ADDRESS 0x0A #define D_SOLAX_X1 "SolaxX1" @@ -100,6 +101,7 @@ union { struct SOLAXX1_LIVEDATA { int16_t temperature = 0; float energy_today = 0; + float energy_total = 0; float dc1_voltage = 0; float dc2_voltage = 0; float dc1_current = 0; @@ -329,7 +331,7 @@ void solaxX1_SwitchMeterMode(bool MeterMode) { /*********************************************************************************************/ void solaxX1_CyclicTask(void) { // Every 100/250 milliseconds - uint8_t DataRead[256] = {0}; + uint8_t DataRead[SOLAXX1_BUFFERSIZE] = {0}; uint8_t TempData[16] = {0}; char TempDataChar[32]; float TempFloat; @@ -399,7 +401,7 @@ void solaxX1_CyclicTask(void) { // Every 100/250 milliseconds Energy->frequency[0] = ((DataRead[25] << 8) | DataRead[26]) * 0.01f; // AC Frequency Energy->active_power[0] = ((DataRead[27] << 8) | DataRead[28]); // AC Power //temporal = (float)((DataRead[29] << 8) | DataRead[30]) * 0.1f; // Not Used - Energy->import_active[0] = ((DataRead[31] << 24) | (DataRead[32] << 16) | (DataRead[33] << 8) | DataRead[34]) * 0.1f; // Energy Total + solaxX1.energy_total = ((DataRead[31] << 24) | (DataRead[32] << 16) | (DataRead[33] << 8) | DataRead[34]) * 0.1f; // Energy Total uint32_t runtime_total = (DataRead[35] << 24) | (DataRead[36] << 16) | (DataRead[37] << 8) | DataRead[38]; // Work Time Total if (runtime_total) solaxX1.runtime_total = runtime_total; // Work Time valid solaxX1.runMode = (DataRead[39] << 8) | DataRead[40]; // Work mode @@ -413,7 +415,10 @@ void solaxX1_CyclicTask(void) { // Every 100/250 milliseconds solaxX1.errorCode = (DataRead[58] << 24) | (DataRead[57] << 16) | (DataRead[56] << 8) | DataRead[55]; // Error Code solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; - EnergyUpdateTotal(); // 484.708 kWh + if (Settings->flag3.hardware_energy_total) { // SetOption72 - Enable hardware energy total counter as reference (#6561) + Energy->import_active[0] = solaxX1.energy_total; + EnergyUpdateTotal(); // 484.708 kWh + } DEBUG_SENSOR_LOG(PSTR("SX1: received live data")); return; } // end received "Response for query (live data)" @@ -569,9 +574,16 @@ void solaxX1_CyclicTask(void) { // Every 100/250 milliseconds return; } // end solaxX1_CyclicTask +void solaxX1_EverySecond(void) { + if (Settings->flag3.hardware_energy_total) return; // SetOption72 - Enable hardware energy total counter as reference (#6561) + if (Energy->data_valid[0]) return; + Energy->kWhtoday_delta[0] += Energy->active_power[0] * 1000 / 36; + EnergyUpdateToday(); +} // end solaxX1_EverySecond + void solaxX1_SnsInit(void) { AddLog(LOG_LEVEL_INFO, PSTR("SX1: Init - RX-pin: %d, TX-pin: %d, RTS-pin: %d"), Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), Pin(GPIO_SOLAXX1_RTS)); - solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1, 0, 256); + solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1, 1, SOLAXX1_BUFFERSIZE); if (solaxX1Serial->begin(SOLAXX1_SPEED)) { if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } #ifdef ESP32 @@ -655,6 +667,10 @@ void solaxX1_Show(uint32_t function) { char pv2_power[33]; dtostrfd(solaxX1.dc2_power, Settings->flag2.wattage_resolution, pv2_power); #endif + char inverter_today[33]; + dtostrfd(solaxX1.energy_today, Settings->flag2.energy_resolution, inverter_today); + char inverter_total[33]; + dtostrfd(solaxX1.energy_total, Settings->flag2.energy_resolution, inverter_total); char status[33]; GetTextIndexed(status, sizeof(status), solaxX1.runMode + 2, kSolaxMode); @@ -702,6 +718,10 @@ void solaxX1_Show(uint32_t function) { WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV2_CURRENT, table_align.c_str(), pv2_current, D_UNIT_AMPERE); WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_PV2_POWER, table_align.c_str(), pv2_power, D_UNIT_WATT); #endif + if (!Settings->flag3.hardware_energy_total) { // SetOption72 - Enable hardware energy total counter as reference (#6561) + WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_ENERGY_TODAY, table_align.c_str(), inverter_today, D_UNIT_KILOWATTHOUR); + WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_ENERGY_TOTAL, table_align.c_str(), inverter_total, D_UNIT_KILOWATTHOUR); + } char SXTemperature[16]; dtostrfd(solaxX1.temperature, Settings->flag2.temperature_resolution, SXTemperature); WSContentSend_PD(HTTP_SNS_solaxX1_Num, D_TEMPERATURE, table_align.c_str(), SXTemperature, D_UNIT_DEGREE D_UNIT_CELSIUS); @@ -733,6 +753,9 @@ bool Xnrg12(uint32_t function) { case FUNC_EVERY_250_MSECOND: if (!solaxX1_global.MeterMode) solaxX1_CyclicTask(); break; + case FUNC_EVERY_SECOND: + solaxX1_EverySecond(); + break; #ifdef USE_WEBSERVER case FUNC_WEB_COL_SENSOR: case FUNC_WEB_SENSOR: diff --git a/tasmota/tasmota_xnrg_energy/xnrg_14_bl09xx.ino b/tasmota/tasmota_xnrg_energy/xnrg_14_bl09xx.ino index 4d9d1b15ff0f..8dcc7430eb20 100644 --- a/tasmota/tasmota_xnrg_energy/xnrg_14_bl09xx.ino +++ b/tasmota/tasmota_xnrg_energy/xnrg_14_bl09xx.ino @@ -25,13 +25,14 @@ /*********************************************************************************************\ * Support the following Shangai Belling energy sensors: * - * BL0942 - Energy (as in Shelly Plus1PMMini) - * Template {"NAME":"Shelly Plus1PMMini","GPIO":[576,32,0,4736,0,224,3200,8161,0,0,192,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350} - * Template {"NAME":"Shelly PlusPMMini","GPIO":[576,32,0,4736,0,0,3200,8161,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350} + * BL0942 - Energy (as in Shelly Gen3) + * {"NAME":"Shelly 1PM Gen3","GPIO":[0,32,0,4736,224,0,3200,8161,576,1,192,0,0,0,0,0,0,0,0,1,1,1],"FLAG":0,"BASE":1,"CMND":"AdcGpio3 10000,10000,4000"} + * {"NAME":"Shelly Plus1PMMini","GPIO":[576,32,0,4736,0,224,3200,8161,0,0,192,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350} + * {"NAME":"Shelly PlusPMMini","GPIO":[576,32,0,4736,0,0,3200,8161,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1,"CMND":"AdcParam1 2,5600,4700,3350} * Based on datasheet from https://datasheet.lcsc.com/lcsc/2110191830_BL-Shanghai-Belling-BL0942_C2909509.pdf * * BL0940 - Energy (as in Blitzwolf SHP10) - * Template {"NAME":"BW-SHP10","GPIO":[0,148,0,207,158,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} + * {"NAME":"BW-SHP10","GPIO":[0,148,0,207,158,21,0,0,0,17,0,0,0],"FLAG":0,"BASE":18} * Based on datasheet from http://www.belling.com.cn/media/file_object/bel_product/BL09XX/datasheet/BL09XX_V1.1_en.pdf * * BL0939 - Energy (as in Sonoff Dual R3 v2) diff --git a/tasmota/tasmota_xnrg_energy/xnrg_19_cse7761.ino b/tasmota/tasmota_xnrg_energy/xnrg_19_cse7761.ino index 1a4bd3002ab9..403824eba05f 100644 --- a/tasmota/tasmota_xnrg_energy/xnrg_19_cse7761.ino +++ b/tasmota/tasmota_xnrg_energy/xnrg_19_cse7761.ino @@ -33,6 +33,21 @@ * Based on datasheet from ChipSea and analysing serial data * See https://github.com/arendst/Tasmota/discussions/10793 * https://goldenrelay.en.alibaba.com/product/62119012875-811845870/GOLDEN_GI_1A_5LH_SPST_5V_5A_10A_250VAC_NO_18_5_10_5_15_3mm_sealed_type_all_certificate_compliances_class_F_SPDT_Form_available.html + * + * Model differences: + * Function Model1 Model2 Remark + * ------------------------------ ------- ------- ------------------------------------------------- + * Sonoff DualR3 PowCT + * Processor ESP32 ESP32 + * CSE7761 Rx 1 2 Index defines model number + * Number of inputs 2 1 Count of CSE7761 inputs used + * Current measurement device shunt CT CT = Current Transformer + * Common voltage Yes Yes Show common voltage in GUI/JSON + * Common frequency Yes Yes Show common frequency in GUI/JSON + * Inverted inputs Yes No Current direction defined by hardware design - Fixed by Tasmota + * Support Zero Cross detection Yes No Tasmota supports zero cross detection only on DualR3 due to timing + * Support Export Active No Yes Only CT supports correct negative value detection + * Show negative power No Yes Only CT supports correct negative value detection \*********************************************************************************************/ #define XNRG_19 19 @@ -96,8 +111,9 @@ struct { uint32_t frequency = 0; uint32_t voltage_rms = 0; uint32_t current_rms[2] = { 0 }; - uint32_t energy[2] = { 0 }; + int32_t energy[2] = { 0 }; uint32_t active_power[2] = { 0 }; + uint32_t power_factor[2] = { 0 }; uint16_t coefficient[8] = { 0 }; uint8_t energy_update[2] = { 0 }; uint8_t init = 4; @@ -445,32 +461,29 @@ void Cse7761GetData(void) { CSE7761Data.frequency = (value >= 0x8000) ? 0 : value; #endif // CSE7761_FREQUENCY - value = Cse7761ReadFallback(CSE7761_REG_RMSIA, CSE7761Data.current_rms[0], 3); -#ifdef CSE7761_SIMULATE - value = 455; -#endif - CSE7761Data.current_rms[0] = ((value >= 0x800000) || (value < 1600)) ? 0 : value; // No load threshold of 10mA - value = Cse7761ReadFallback(CSE7761_REG_POWERPA, CSE7761Data.active_power[0], 4); -#ifdef CSE7761_SIMULATE - value = 217; -#endif - CSE7761Data.active_power[0] = (0 == CSE7761Data.current_rms[0]) ? 0 : (value & 0x80000000) ? (~value) + 1 : value; + for (uint32_t channel = 0; channel < Energy->phase_count; channel++) { + if (CSE7761_MODEL_POWCT == CSE7761Data.model) { + if (Energy->phase_count > 1) { + Cse7761Write(CSE7761_SPECIAL_COMMAND, (channel) ? CSE7761_CMD_CHAN_B_SELECT : CSE7761_CMD_CHAN_A_SELECT); + } + CSE7761Data.power_factor[channel] = Cse7761ReadFallback(CSE7761_REG_POWERFACTOR, CSE7761Data.power_factor[channel], 3); + } - if (2 == Energy->phase_count) { - value = Cse7761ReadFallback(CSE7761_REG_RMSIB, CSE7761Data.current_rms[1], 3); + value = Cse7761ReadFallback((channel) ? CSE7761_REG_RMSIB : CSE7761_REG_RMSIA, CSE7761Data.current_rms[channel], 3); #ifdef CSE7761_SIMULATE - value = 29760; // 0.185A + value = 455; #endif - CSE7761Data.current_rms[1] = ((value >= 0x800000) || (value < 1600)) ? 0 : value; // No load threshold of 10mA - value = Cse7761ReadFallback(CSE7761_REG_POWERPB, CSE7761Data.active_power[1], 4); + CSE7761Data.current_rms[channel] = ((value >= 0x800000) || (value < 1600)) ? 0 : value; // No load threshold of 10mA + value = Cse7761ReadFallback((channel) ? CSE7761_REG_POWERPB : CSE7761_REG_POWERPA, CSE7761Data.active_power[channel], 4); #ifdef CSE7761_SIMULATE - value = 2126641; // 44.05W + value = 217; #endif - CSE7761Data.active_power[1] = (0 == CSE7761Data.current_rms[1]) ? 0 : (value & 0x80000000) ? (~value) + 1 : value; + CSE7761Data.active_power[channel] = (0 == CSE7761Data.current_rms[channel]) ? 0 : (value & 0x80000000) ? (~value) + 1 : value; } - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("C61: F%d, U%d, I%d/%d, P%d/%d"), + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("C61: F%d, U%d, PF%d/%d, I%d/%d, P%d/%d"), CSE7761Data.frequency, CSE7761Data.voltage_rms, + CSE7761Data.power_factor[0], CSE7761Data.power_factor[1], CSE7761Data.current_rms[0], CSE7761Data.current_rms[1], CSE7761Data.active_power[0], CSE7761Data.active_power[1]); @@ -497,6 +510,13 @@ void Cse7761GetData(void) { if (0 == Energy->active_power[channel]) { Energy->current[channel] = 0; } else { + if (CSE7761_MODEL_POWCT == CSE7761Data.model) { + int32_t power_factor = CSE7761Data.power_factor[channel] << 8; + if (power_factor < 0) { + // power factor is negative and active power is not zero -> handle negative active power + Energy->active_power[channel] = -Energy->active_power[channel]; + } + } uint32_t current_calibration = EnergyGetCalibration(ENERGY_CURRENT_CALIBRATION, channel); // Current = RmsIA * RmsIAC / 0x800000 // Energy->current[channel] = (float)(((uint64_t)CSE7761Data.current_rms[channel] * CSE7761Data.coefficient[RmsIAC + channel]) >> 23) / 1000; // A @@ -628,6 +648,9 @@ void Cse7761DrvInit(void) { if (CSE7761_MODEL_DUALR3 == CSE7761Data.model) { Energy->phase_count = 2; // Handle two channels as two phases } + if (CSE7761_MODEL_POWCT == CSE7761Data.model) { + Energy->local_energy_active_export = true; // Support energy export + } Energy->voltage_common = true; // Use common voltage #ifdef CSE7761_FREQUENCY Energy->frequency_common = true; // Use common frequency diff --git a/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_ble.ino b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_ble.ino index 3818717f83ed..752e2fdd1786 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_ble.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_ble.ino @@ -792,7 +792,7 @@ int genericSensorReadFn(int slot, int force){ break;*/ case MI_LYWSD03MMC: // don't read if key present and we've decoded at least one advert - if (MIBLEsensors[slot].needkey == KEY_REQUIRED_AND_FOUND && !force) return -2; + if ((MIBLEsensors[slot].needkey == KEY_NOT_REQUIRED || MIBLEsensors[slot].needkey == KEY_REQUIRED_AND_FOUND) && !force) return -2; res = MI32Operation(slot, OP_READ_HT_LY, LYWSD03_Svc, nullptr, LYWSD03_BattNotifyChar); break; case MI_LYWSD02MMC: @@ -800,7 +800,7 @@ int genericSensorReadFn(int slot, int force){ break; case MI_MHOC401: // don't read if key present and we've decoded at least one advert - if (MIBLEsensors[slot].needkey == KEY_REQUIRED_AND_FOUND && !force) return -2; + if ((MIBLEsensors[slot].needkey == KEY_NOT_REQUIRED || MIBLEsensors[slot].needkey == KEY_REQUIRED_AND_FOUND) && !force) return -2; res = MI32Operation(slot, OP_READ_HT_LY, MHOC401_Svc, nullptr, MHOC401_BattNotifyChar); break; diff --git a/tasmota/tasmota_xsns_sensor/xsns_90_hrg15.ino b/tasmota/tasmota_xsns_sensor/xsns_90_hrg15.ino index ff5dc777571e..1126b21bb4d9 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_90_hrg15.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_90_hrg15.ino @@ -31,7 +31,7 @@ #define XSNS_90 90 -#define RG15_NAME "RG-15" +#define RG15_NAME "RG15" #define RG15_BAUDRATE 9600 #define RG15_READ_TIMEOUT 500 #define RG15_EVENT_TIMEOUT 60 diff --git a/tasmota/tasmota_xx2c_global/xdrv_interface.ino b/tasmota/tasmota_xx2c_global/xdrv_interface.ino index b68c53c8e877..78810c253e90 100644 --- a/tasmota/tasmota_xx2c_global/xdrv_interface.ino +++ b/tasmota/tasmota_xx2c_global/xdrv_interface.ino @@ -1105,10 +1105,10 @@ bool XdrvRulesProcess(bool teleperiod) { #ifdef USE_DEBUG_DRIVER void ShowFreeMem(const char *where) { - char stemp[30]; - snprintf_P(stemp, sizeof(stemp), where); - XdrvMailbox.data = stemp; + char *XdrvMailboxData = XdrvMailbox.data; + XdrvMailbox.data = (char*)where; XdrvCall(FUNC_FREE_MEM); + XdrvMailbox.data = XdrvMailboxData; } #endif diff --git a/tools/decode-status.py b/tools/decode-status.py index dd487a206606..6ac433cca296 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -216,8 +216,10 @@ "(MQTT) Disable publish ModbusReceived MQTT messages (1), you must use event trigger rules instead", "(Counter) Enable counting on both rising and falling edge (1)", "(LD2410) Disable generate moving event by sensor report - use LD2410 out pin for events (1)", - "", - "","","","", + "(GUI) Disable display of state text (1)", + "(Energy) Do not add export energy to energy today (1)", + "(GUI) Disable display of GUI device name (1)", + "","", "","","","", "","","","", "","","","" @@ -340,7 +342,7 @@ obj = json.load(fp) def StartDecode(): - print ("\n*** decode-status.py v14.3.0.5 by Theo Arends and Jacek Ziolkowski ***") + print ("\n*** decode-status.py v14.4.1.1 by Theo Arends and Jacek Ziolkowski ***") # print("Decoding\n{}".format(obj))