From 46b18ea41e0f033d02afbcc2a4562c140a766e62 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sat, 6 Mar 2021 18:43:32 +0100 Subject: [PATCH 1/6] Fix WS2812 ESP32 --- tasmota/xlgt_01_ws2812.ino | 107 +++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/tasmota/xlgt_01_ws2812.ino b/tasmota/xlgt_01_ws2812.ino index fcd685931..eaea82135 100644 --- a/tasmota/xlgt_01_ws2812.ino +++ b/tasmota/xlgt_01_ws2812.ino @@ -123,6 +123,113 @@ void (* const Ws2812Command[])(void) PROGMEM = { #endif // No USE_WS2812_DMA +#ifdef ESP32 +// +// Below is a quick work-around to ESP32 gcc that prevents templated methods to be in IRAM unless explicitly marked per specialization +// It will work only for the default WS2812 (800KHz) and inverted and non-inverted +// +template <> +void IRAM_ATTR NeoEspBitBangBase::send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin) +{ + const uint32_t pinRegister = _BV(pin); + uint8_t mask = 0x80; + uint8_t subpix = *pixels++; + uint32_t cyclesStart = 0; // trigger emediately + uint32_t cyclesNext = 0; + + for (;;) + { + // do the checks here while we are waiting on time to pass + uint32_t cyclesBit = NeoEspSpeed800Mhz::T0H; + if (subpix & mask) + { + cyclesBit = NeoEspSpeed800Mhz::T1H; + } + + // after we have done as much work as needed for this next bit + // now wait for the HIGH + while (((cyclesStart = getCycleCount()) - cyclesNext) < NeoEspSpeed800Mhz::Period); + + // set pin state + NeoEspPinset::setPin(pinRegister); + + // wait for the LOW + while ((getCycleCount() - cyclesStart) < cyclesBit); + + // reset pin start + NeoEspPinset::resetPin(pinRegister); + + cyclesNext = cyclesStart; + + // next bit + mask >>= 1; + if (mask == 0) + { + // no more bits to send in this byte + // check for another byte + if (pixels >= end) + { + // no more bytes to send so stop + break; + } + // reset mask to first bit and get the next byte + mask = 0x80; + subpix = *pixels++; + } + } +} +template <> +void IRAM_ATTR NeoEspBitBangBase::send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin) +{ + const uint32_t pinRegister = _BV(pin); + uint8_t mask = 0x80; + uint8_t subpix = *pixels++; + uint32_t cyclesStart = 0; // trigger emediately + uint32_t cyclesNext = 0; + + for (;;) + { + // do the checks here while we are waiting on time to pass + uint32_t cyclesBit = NeoEspSpeed800Mhz::T0H; + if (subpix & mask) + { + cyclesBit = NeoEspSpeed800Mhz::T1H; + } + + // after we have done as much work as needed for this next bit + // now wait for the HIGH + while (((cyclesStart = getCycleCount()) - cyclesNext) < NeoEspSpeed800Mhz::Period); + + // set pin state + NeoEspPinsetInverted::setPin(pinRegister); + + // wait for the LOW + while ((getCycleCount() - cyclesStart) < cyclesBit); + + // reset pin start + NeoEspPinsetInverted::resetPin(pinRegister); + + cyclesNext = cyclesStart; + + // next bit + mask >>= 1; + if (mask == 0) + { + // no more bits to send in this byte + // check for another byte + if (pixels >= end) + { + // no more bytes to send so stop + break; + } + // reset mask to first bit and get the next byte + mask = 0x80; + subpix = *pixels++; + } + } +} +#endif // ESP32 + NeoPixelBus *strip = nullptr; struct WsColor { From a10564b5c57af93df3587c14801d9c2b6213685e Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 7 Mar 2021 10:16:08 +0100 Subject: [PATCH 2/6] ESP32 better fix for IRAM Bit Bang --- .../src/internal/NeoEspBitBangMethod.cpp | 115 ++++++++++++++++ .../src/internal/NeoEspBitBangMethod.h | 129 +++++------------- tasmota/xlgt_01_ws2812.ino | 107 --------------- 3 files changed, 152 insertions(+), 199 deletions(-) create mode 100644 lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.cpp diff --git a/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.cpp b/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.cpp new file mode 100644 index 000000000..637fef0c5 --- /dev/null +++ b/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.cpp @@ -0,0 +1,115 @@ +/*------------------------------------------------------------------------- +NeoPixel library helper functions for Esp8266 and Esp32 + +Written by Michael C. Miller. + +I invest time and resources providing this open source code, +please support me by dontating (see https://github.com/Makuna/NeoPixelBus) + +------------------------------------------------------------------------- +This file is part of the Makuna/NeoPixelBus library. + +NeoPixelBus is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. + +NeoPixelBus 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with NeoPixel. If not, see +. +-------------------------------------------------------------------------*/ + +#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) + +#include +#include "NeoEspBitBangMethod.h" + +static inline uint32_t getCycleCount(void) +{ + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); + return ccount; +} + +void ICACHE_RAM_ATTR NeoEspBitBangBase_send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin, uint32_t t0h, uint32_t t1h, uint32_t period, uint8_t IdleLevel) +{ + const uint32_t pinRegister = _BV(pin); + uint8_t mask = 0x80; + uint8_t subpix = *pixels++; + uint32_t cyclesStart = 0; // trigger emediately + uint32_t cyclesNext = 0; + + for (;;) + { + // do the checks here while we are waiting on time to pass + uint32_t cyclesBit = t0h; + if (subpix & mask) + { + cyclesBit = t1h; + } + + // after we have done as much work as needed for this next bit + // now wait for the HIGH + while (((cyclesStart = getCycleCount()) - cyclesNext) < period); + + // set pin state + if (IdleLevel == LOW) { +#if defined(ARDUINO_ARCH_ESP32) + GPIO.out_w1ts = pinRegister; +#else + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); +#endif + } else { +#if defined(ARDUINO_ARCH_ESP32) + GPIO.out_w1tc = pinRegister; +#else + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); +#endif + } + // T_PINSET::setPin(pinRegister); + + // wait for the LOW + while ((getCycleCount() - cyclesStart) < cyclesBit); + + // reset pin start + if (IdleLevel == LOW) { +#if defined(ARDUINO_ARCH_ESP32) + GPIO.out_w1tc = pinRegister; +#else + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); +#endif + } else { +#if defined(ARDUINO_ARCH_ESP32) + GPIO.out_w1ts = pinRegister; +#else + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); +#endif + } + // T_PINSET::resetPin(pinRegister); + + cyclesNext = cyclesStart; + + // next bit + mask >>= 1; + if (mask == 0) + { + // no more bits to send in this byte + // check for another byte + if (pixels >= end) + { + // no more bytes to send so stop + break; + } + // reset mask to first bit and get the next byte + mask = 0x80; + subpix = *pixels++; + } + } +} + +#endif \ No newline at end of file diff --git a/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.h b/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.h index b0b9753b2..387751b8a 100644 --- a/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.h +++ b/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.h @@ -55,6 +55,14 @@ public: const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit }; +class NeoEspSpeedTm1829 +{ +public: + const static uint32_t T0H = (F_CPU / 3333333 - CYCLES_LOOPTEST); // 0.3us + const static uint32_t T1H = (F_CPU / 1250000 - CYCLES_LOOPTEST); // 0.8us + const static uint32_t Period = (F_CPU / 800000 - CYCLES_LOOPTEST); // 1.25us per bit +}; + class NeoEspSpeed800Mhz { public: @@ -83,109 +91,22 @@ class NeoEspPinset { public: const static uint8_t IdleLevel = LOW; - - inline static void setPin(const uint32_t pinRegister) - { -#if defined(ARDUINO_ARCH_ESP32) - GPIO.out_w1ts = pinRegister; -#else - GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); -#endif - } - - inline static void resetPin(const uint32_t pinRegister) - { -#if defined(ARDUINO_ARCH_ESP32) - GPIO.out_w1tc = pinRegister; -#else - GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); -#endif - } }; class NeoEspPinsetInverted { public: const static uint8_t IdleLevel = HIGH; - - inline static void setPin(const uint32_t pinRegister) - { -#if defined(ARDUINO_ARCH_ESP32) - GPIO.out_w1tc = pinRegister; -#else - GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegister); -#endif - } - - inline static void resetPin(const uint32_t pinRegister) - { -#if defined(ARDUINO_ARCH_ESP32) - GPIO.out_w1ts = pinRegister; -#else - GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegister); -#endif - } }; +extern void NeoEspBitBangBase_send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin, uint32_t t0h, uint32_t t1h, uint32_t period, uint8_t IdleLevel); + template class NeoEspBitBangBase { public: - __attribute__((noinline)) static void ICACHE_RAM_ATTR send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin) + static void send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin) { - const uint32_t pinRegister = _BV(pin); - uint8_t mask = 0x80; - uint8_t subpix = *pixels++; - uint32_t cyclesStart = 0; // trigger emediately - uint32_t cyclesNext = 0; - - for (;;) - { - // do the checks here while we are waiting on time to pass - uint32_t cyclesBit = T_SPEED::T0H; - if (subpix & mask) - { - cyclesBit = T_SPEED::T1H; - } - - // after we have done as much work as needed for this next bit - // now wait for the HIGH - while (((cyclesStart = getCycleCount()) - cyclesNext) < T_SPEED::Period); - - // set pin state - T_PINSET::setPin(pinRegister); - - // wait for the LOW - while ((getCycleCount() - cyclesStart) < cyclesBit); - - // reset pin start - T_PINSET::resetPin(pinRegister); - - cyclesNext = cyclesStart; - - // next bit - mask >>= 1; - if (mask == 0) - { - // no more bits to send in this byte - // check for another byte - if (pixels >= end) - { - // no more bytes to send so stop - break; - } - // reset mask to first bit and get the next byte - mask = 0x80; - subpix = *pixels++; - } - } - } - -protected: - static inline uint32_t getCycleCount(void) - { - uint32_t ccount; - __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); - return ccount; + NeoEspBitBangBase_send_pixels(pixels, end, pin, T_SPEED::T0H, T_SPEED::T1H, T_SPEED::Period, T_PINSET::IdleLevel); } }; @@ -214,6 +135,13 @@ public: static const uint32_t ResetTimeUs = 200; }; +// normal is inverted signal +class NeoEspBitBangSpeedTm1829 : public NeoEspBitBangBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + class NeoEspBitBangSpeed800Kbps : public NeoEspBitBangBase { public: @@ -257,6 +185,13 @@ public: static const uint32_t ResetTimeUs = 200; }; +// normal is inverted signal, so inverted is normal +class NeoEspBitBangInvertedSpeedTm1829 : public NeoEspBitBangBase +{ +public: + static const uint32_t ResetTimeUs = 200; +}; + class NeoEspBitBangInvertedSpeed800Kbps : public NeoEspBitBangBase { public: @@ -278,6 +213,8 @@ public: template class NeoEspBitBangMethodBase { public: + typedef NeoNoSettings SettingsObject; + NeoEspBitBangMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : _sizeData(pixelCount * elementSize + settingsSize), _pin(pin) @@ -354,6 +291,10 @@ public: return _sizeData; }; + void applySettings(const SettingsObject& settings) + { + } + private: const size_t _sizeData; // Size of '_data' buffer below const uint8_t _pin; // output pin number @@ -369,6 +310,7 @@ typedef NeoEspBitBangMethodBase NeoEsp32 typedef NeoEspBitBangMethodBase NeoEsp32BitBangWs2812xMethod; typedef NeoEspBitBangMethodBase NeoEsp32BitBangSk6812Method; typedef NeoEspBitBangMethodBase NeoEsp32BitBangTm1814Method; +typedef NeoEspBitBangMethodBase NeoEsp32BitBangTm1829Method; typedef NeoEspBitBangMethodBase NeoEsp32BitBang800KbpsMethod; typedef NeoEspBitBangMethodBase NeoEsp32BitBang400KbpsMethod; typedef NeoEspBitBangMethodBase NeoEsp32BitBangApa106Method; @@ -381,6 +323,7 @@ typedef NeoEspBitBangMethodBase NeoEsp32BitBangWs2812xInvertedMethod; typedef NeoEspBitBangMethodBase NeoEsp32BitBangSk6812InvertedMethod; typedef NeoEspBitBangMethodBase NeoEsp32BitBangTm1814InvertedMethod; +typedef NeoEspBitBangMethodBase NeoEsp32BitBangTm1829InvertedMethod; typedef NeoEspBitBangMethodBase NeoEsp32BitBang800KbpsInvertedMethod; typedef NeoEspBitBangMethodBase NeoEsp32BitBang400KbpsInvertedMethod; typedef NeoEspBitBangMethodBase NeoEsp32BitBangApa106InvertedMethod; @@ -395,6 +338,7 @@ typedef NeoEspBitBangMethodBase NeoEsp82 typedef NeoEspBitBangMethodBase NeoEsp8266BitBangWs2812xMethod; typedef NeoEspBitBangMethodBase NeoEsp8266BitBangSk6812Method; typedef NeoEspBitBangMethodBase NeoEsp8266BitBangTm1814Method; +typedef NeoEspBitBangMethodBase NeoEsp8266BitBangTm1829Method; typedef NeoEspBitBangMethodBase NeoEsp8266BitBang800KbpsMethod; typedef NeoEspBitBangMethodBase NeoEsp8266BitBang400KbpsMethod; typedef NeoEspBitBangMethodBase NeoEsp8266BitBangApa106Method; @@ -407,6 +351,7 @@ typedef NeoEspBitBangMethodBase NeoEsp8266BitBangWs2812xInvertedMethod; typedef NeoEspBitBangMethodBase NeoEsp8266BitBangSk6812InvertedMethod; typedef NeoEspBitBangMethodBase NeoEsp8266BitBangTm1814InvertedMethod; +typedef NeoEspBitBangMethodBase NeoEsp8266BitBangTm1829InvertedMethod; typedef NeoEspBitBangMethodBase NeoEsp8266BitBang800KbpsInvertedMethod; typedef NeoEspBitBangMethodBase NeoEsp8266BitBang400KbpsInvertedMethod; typedef NeoEspBitBangMethodBase NeoEsp8266BitBangApa106InvertedMethod; @@ -418,4 +363,4 @@ typedef NeoEsp8266BitBangSk6812InvertedMethod NeoEsp8266BitBangLc8812InvertedMet #endif // ESP bitbang doesn't have defaults and should avoided except for testing -#endif \ No newline at end of file +#endif diff --git a/tasmota/xlgt_01_ws2812.ino b/tasmota/xlgt_01_ws2812.ino index eaea82135..fcd685931 100644 --- a/tasmota/xlgt_01_ws2812.ino +++ b/tasmota/xlgt_01_ws2812.ino @@ -123,113 +123,6 @@ void (* const Ws2812Command[])(void) PROGMEM = { #endif // No USE_WS2812_DMA -#ifdef ESP32 -// -// Below is a quick work-around to ESP32 gcc that prevents templated methods to be in IRAM unless explicitly marked per specialization -// It will work only for the default WS2812 (800KHz) and inverted and non-inverted -// -template <> -void IRAM_ATTR NeoEspBitBangBase::send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin) -{ - const uint32_t pinRegister = _BV(pin); - uint8_t mask = 0x80; - uint8_t subpix = *pixels++; - uint32_t cyclesStart = 0; // trigger emediately - uint32_t cyclesNext = 0; - - for (;;) - { - // do the checks here while we are waiting on time to pass - uint32_t cyclesBit = NeoEspSpeed800Mhz::T0H; - if (subpix & mask) - { - cyclesBit = NeoEspSpeed800Mhz::T1H; - } - - // after we have done as much work as needed for this next bit - // now wait for the HIGH - while (((cyclesStart = getCycleCount()) - cyclesNext) < NeoEspSpeed800Mhz::Period); - - // set pin state - NeoEspPinset::setPin(pinRegister); - - // wait for the LOW - while ((getCycleCount() - cyclesStart) < cyclesBit); - - // reset pin start - NeoEspPinset::resetPin(pinRegister); - - cyclesNext = cyclesStart; - - // next bit - mask >>= 1; - if (mask == 0) - { - // no more bits to send in this byte - // check for another byte - if (pixels >= end) - { - // no more bytes to send so stop - break; - } - // reset mask to first bit and get the next byte - mask = 0x80; - subpix = *pixels++; - } - } -} -template <> -void IRAM_ATTR NeoEspBitBangBase::send_pixels(uint8_t* pixels, uint8_t* end, uint8_t pin) -{ - const uint32_t pinRegister = _BV(pin); - uint8_t mask = 0x80; - uint8_t subpix = *pixels++; - uint32_t cyclesStart = 0; // trigger emediately - uint32_t cyclesNext = 0; - - for (;;) - { - // do the checks here while we are waiting on time to pass - uint32_t cyclesBit = NeoEspSpeed800Mhz::T0H; - if (subpix & mask) - { - cyclesBit = NeoEspSpeed800Mhz::T1H; - } - - // after we have done as much work as needed for this next bit - // now wait for the HIGH - while (((cyclesStart = getCycleCount()) - cyclesNext) < NeoEspSpeed800Mhz::Period); - - // set pin state - NeoEspPinsetInverted::setPin(pinRegister); - - // wait for the LOW - while ((getCycleCount() - cyclesStart) < cyclesBit); - - // reset pin start - NeoEspPinsetInverted::resetPin(pinRegister); - - cyclesNext = cyclesStart; - - // next bit - mask >>= 1; - if (mask == 0) - { - // no more bits to send in this byte - // check for another byte - if (pixels >= end) - { - // no more bytes to send so stop - break; - } - // reset mask to first bit and get the next byte - mask = 0x80; - subpix = *pixels++; - } - } -} -#endif // ESP32 - NeoPixelBus *strip = nullptr; struct WsColor { From edf1c5bbb45b50dbdad18246b785d8d183d9a4e2 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 7 Mar 2021 10:18:30 +0100 Subject: [PATCH 3/6] Fix compilation --- .../NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.h b/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.h index 387751b8a..abbb4a685 100644 --- a/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.h +++ b/lib/lib_basic/NeoPixelBus-2.6.1.4/src/internal/NeoEspBitBangMethod.h @@ -213,8 +213,6 @@ public: template class NeoEspBitBangMethodBase { public: - typedef NeoNoSettings SettingsObject; - NeoEspBitBangMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) : _sizeData(pixelCount * elementSize + settingsSize), _pin(pin) @@ -291,10 +289,6 @@ public: return _sizeData; }; - void applySettings(const SettingsObject& settings) - { - } - private: const size_t _sizeData; // Size of '_data' buffer below const uint8_t _pin; // output pin number From 25dc8192797a9aff193e3c6fcacb10389a512b03 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 7 Mar 2021 14:41:04 +0100 Subject: [PATCH 4/6] Add version to OTA update Add version to OTA update (#11243) --- tasmota/support_tasmota.ino | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 9556f5aee..642b6afeb 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1099,9 +1099,11 @@ void Every250mSeconds(void) } } #endif // ESP8266 - AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s"), TasmotaGlobal.mqtt_data); + char version[50]; + snprintf_P(version, sizeof(version), PSTR("%s-%s"), TasmotaGlobal.image_name, TasmotaGlobal.version); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s %s"), TasmotaGlobal.mqtt_data, version); WiFiClient OTAclient; - ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(OTAclient, TasmotaGlobal.mqtt_data)); + ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(OTAclient, TasmotaGlobal.mqtt_data, version)); if (!ota_result) { #ifndef FIRMWARE_MINIMAL int ota_error = ESPhttpUpdate.getLastError(); From 8630debb7f9d73a7dcb42903f1474f44e71e1fe3 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 7 Mar 2021 15:14:20 +0100 Subject: [PATCH 5/6] Update changelog --- CHANGELOG.md | 22 ++++++++++++++++++---- RELEASENOTES.md | 21 +++++++++++++++++---- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02e2ce7b7..4465d003b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,22 +5,36 @@ All notable changes to this project will be documented in this file. ## [9.3.1.1] ### Added +- Support for CSE7761 energy monitor as used in ESP32 based Sonoff Dual R3 Pow (#10793) +- Command ``Sensor80 1 <0..7>`` to control MFRC522 RFID antenna gain from 18dB (0) to 48dB (7) (#11073) - Allow MCP230xx pinmode from output to input (#11104) - SML VBUS support (#11125) -- Command ``Sensor80 1 <0..7>`` to control MFRC522 RFID antenna gain from 18dB (0) to 48dB (7) (#11073) - Support for NEC and OPTOMA LCD/DLP Projector serial power control by Jan Bubík (#11145) +- Support for XPT2046 touch screen digitizer on ILI9341 display by nonix (#11159) +- Berry improvements (#11163) +- Support for zigbee lumi.sensor_wleak (#11200) +- Crash protection in ext_vnsprintf_P (#11202) +- Extent compile time SetOptions support (#11204) +- ESP32 Extent BLE (#11212) ### Changed -- TuyaMcu dimmer timeout (#11121) +- ESP32 core library from v1.0.5-rc6 to v1.0.5 - TasmotaSerial library from v3.2.0 to v3.3.0 +- TuyaMcu dimmer timeout (#11121) +- Rename epaper 42 commands (#11222) +- DeepSleep announcement topic (#11223) ### Fixed +- PN532 on ESP32 Serial flush both Tx and Rx buffers (#10910) +- Light scheme related color changes (#11041) - Refactor acceleration function for shutter stepper and servo (#11088) - LM75AD detection on different addresses (#11096) - Timer loop when console is scrolled up regression from v9.3.0 (#11108) - Display exception when no file system is present (#11125) -- PN532 on ESP32 Serial flush both Tx and Rx buffers (#10910) -- Light scheme related color changes (#11041) +- Scripter and SML fixes (#11150) +- Zigbee exception when bad frame is received (#11192) +- ESP32 flash script for Odroid and Core2 (#11227) +- ESP32 WS2812 bitbang support (#11248) ## [Released] diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ba3720df4..960d14b18 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -58,7 +58,7 @@ Above binaries are also available as gzipped version allowing faster uploads. The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota/release. The links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz`` ### ESP32 based -The following binary downloads have been compiled with ESP32/Arduino library core version **1.0.5-rc6**. +The following binary downloads have been compiled with ESP32/Arduino library core version **1.0.5**. - **tasmota32.bin** = The Tasmota version with most drivers. **RECOMMENDED RELEASE BINARY** - **tasmota32-BG.bin** to **tasmota32-TW.bin** = The Tasmota version in different languages. @@ -83,16 +83,29 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota - Command ``Sensor80 1 <0..7>`` to control MFRC522 RFID antenna gain from 18dB (0) to 48dB (7) [#11073](https://github.com/arendst/Tasmota/issues/11073) - Support for SML VBUS [#11125](https://github.com/arendst/Tasmota/issues/11125) - Support for NEC and OPTOMA LCD/DLP Projector serial power control by Jan Bubík [#11145](https://github.com/arendst/Tasmota/issues/11145) +- Support for XPT2046 touch screen digitizer on ILI9341 display by nonix [#11159](https://github.com/arendst/Tasmota/issues/11159) +- Support for zigbee lumi.sensor_wleak [#11200](https://github.com/arendst/Tasmota/issues/11200) +- Support for CSE7761 energy monitor as used in ESP32 based Sonoff Dual R3 Pow [#10793](https://github.com/arendst/Tasmota/issues/10793) - Allow MCP230xx pinmode from output to input [#11104](https://github.com/arendst/Tasmota/issues/11104) +- Berry improvements [#11163](https://github.com/arendst/Tasmota/issues/11163) +- Extent compile time SetOptions support [#11204](https://github.com/arendst/Tasmota/issues/11204) +- ESP32 Extent BLE [#11212](https://github.com/arendst/Tasmota/issues/11212) ### Changed -- TuyaMcu dimmer timeout [#11121](https://github.com/arendst/Tasmota/issues/11121) - TasmotaSerial library from v3.2.0 to v3.3.0 +- ESP32 core library from v1.0.5-rc6 to v1.0.5 +- TuyaMcu dimmer timeout [#11121](https://github.com/arendst/Tasmota/issues/11121) +- Rename epaper 42 commands [#11222](https://github.com/arendst/Tasmota/issues/11222) +- DeepSleep announcement topic [#11223](https://github.com/arendst/Tasmota/issues/11223) ### Fixed +- PN532 on ESP32 Serial flush both Tx and Rx buffers [#10910](https://github.com/arendst/Tasmota/issues/10910) +- Light scheme related color changes [#11041](https://github.com/arendst/Tasmota/issues/11041) - Refactor acceleration function for shutter stepper and servo [#11088](https://github.com/arendst/Tasmota/issues/11088) - LM75AD detection on different addresses [#11096](https://github.com/arendst/Tasmota/issues/11096) - Timer loop when console is scrolled up regression from v9.3.0 [#11108](https://github.com/arendst/Tasmota/issues/11108) - Display exception when no file system is present [#11125](https://github.com/arendst/Tasmota/issues/11125) -- PN532 on ESP32 Serial flush both Tx and Rx buffers [#10910](https://github.com/arendst/Tasmota/issues/10910) -- Light scheme related color changes [#11041](https://github.com/arendst/Tasmota/issues/11041) +- Scripter and SML fixes [#11150](https://github.com/arendst/Tasmota/issues/11150) +- Zigbee exception when bad frame is received [#11192](https://github.com/arendst/Tasmota/issues/11192) +- ESP32 flash script for Odroid and Core2 [#11227](https://github.com/arendst/Tasmota/issues/11227) +- ESP32 WS2812 bitbang support [#11248](https://github.com/arendst/Tasmota/issues/11248) From 4471d6b9d5034910492813343d0c774a128c71e1 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 7 Mar 2021 19:37:18 +0100 Subject: [PATCH 6/6] Berry improvements --- lib/libesp32/Berry-0.1.10/src/be_api.c | 4 + lib/libesp32/Berry-0.1.10/src/berry.h | 1 + .../Berry-0.1.10/src/port/be_tasmotalib.c | 6 + .../Berry-0.1.10/src/port/be_wirelib.c | 19 +- tasmota/xdrv_52_3_berry_native.ino | 216 ++++++++++++++++-- tasmota/xdrv_52_7_berry_embedded.ino | 3 + tasmota/xdrv_52_9_berry.ino | 10 +- 7 files changed, 232 insertions(+), 27 deletions(-) diff --git a/lib/libesp32/Berry-0.1.10/src/be_api.c b/lib/libesp32/Berry-0.1.10/src/be_api.c index ad1a0dfb0..dc34b950c 100644 --- a/lib/libesp32/Berry-0.1.10/src/be_api.c +++ b/lib/libesp32/Berry-0.1.10/src/be_api.c @@ -976,6 +976,7 @@ BERRY_API int be_pcall(bvm *vm, int argc) return be_protectedcall(vm, f, argc); } +__attribute__((noreturn)) BERRY_API void be_raise(bvm *vm, const char *except, const char *msg) { be_pushstring(vm, except); @@ -987,6 +988,9 @@ BERRY_API void be_raise(bvm *vm, const char *except, const char *msg) be_pop(vm, 2); be_save_stacktrace(vm); be_throw(vm, BE_EXCEPTION); +#ifdef __GNUC__ + __builtin_unreachable(); +#endif } BERRY_API void be_stop_iteration(bvm *vm) diff --git a/lib/libesp32/Berry-0.1.10/src/berry.h b/lib/libesp32/Berry-0.1.10/src/berry.h index 40ceecab4..91bc652a4 100644 --- a/lib/libesp32/Berry-0.1.10/src/berry.h +++ b/lib/libesp32/Berry-0.1.10/src/berry.h @@ -401,6 +401,7 @@ BERRY_API int be_pcall(bvm *vm, int argc); BERRY_API void be_exit(bvm *vm, int status); /* exception APIs */ +__attribute__((noreturn)) BERRY_API void be_raise(bvm *vm, const char *except, const char *msg); BERRY_API int be_getexcept(bvm *vm, int code); BERRY_API void be_dumpvalue(bvm *vm, int index); diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c b/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c index bb02f19ca..bd484a9da 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c +++ b/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c @@ -12,6 +12,7 @@ extern int l_getoption(bvm *vm); extern int l_millis(bvm *vm); extern int l_timereached(bvm *vm); extern int l_yield(bvm *vm); +extern int l_delay(bvm *vm); extern int l_respCmnd(bvm *vm); extern int l_respCmndStr(bvm *vm); @@ -20,6 +21,8 @@ extern int l_respCmndError(bvm *vm); extern int l_respCmndFailed(bvm *vm); extern int l_resolveCmnd(bvm *vm); +extern int l_getlight(bvm *vm); + // #if !BE_USE_PRECOMPILED_OBJECT #if 1 // TODO we will do pre-compiled later @@ -31,6 +34,7 @@ be_native_module_attr_table(tasmota_ntv) { be_native_module_function("millis", l_millis), be_native_module_function("timereached", l_timereached), be_native_module_function("yield", l_yield), + be_native_module_function("delay", l_delay), be_native_module_function("respcmnd", l_respCmnd), be_native_module_function("respcmndstr", l_respCmndStr), @@ -39,6 +43,8 @@ be_native_module_attr_table(tasmota_ntv) { be_native_module_function("respcmnd_failed", l_respCmndFailed), be_native_module_function("resolvecmnd", l_resolveCmnd), + be_native_module_function("getlight", l_getlight), + be_native_module_str("_operators", "=<>!|"), }; diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c b/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c index 0d3cd51f4..948ccddef 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c +++ b/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c @@ -14,18 +14,23 @@ extern int b_wire_available(bvm *vm); extern int b_wire_write(bvm *vm); extern int b_wire_read(bvm *vm); +extern int b_wire_scan(bvm *vm); + +extern int b_wire_validwrite(bvm *vm); extern int b_wire_validread(bvm *vm); // #if !BE_USE_PRECOMPILED_OBJECT #if 1 // TODO we will do pre-compiled later be_native_module_attr_table(wire) { - be_native_module_function("begintransmission", b_wire_begintransmission), - be_native_module_function("endtransmission", b_wire_endtransmission), - be_native_module_function("requestfrom", b_wire_requestfrom), - be_native_module_function("available", b_wire_available), - be_native_module_function("write", b_wire_write), - be_native_module_function("read", b_wire_read), - be_native_module_function("validread", b_wire_validread), + be_native_module_function("_begintransmission", b_wire_begintransmission), + be_native_module_function("_endtransmission", b_wire_endtransmission), + be_native_module_function("_requestfrom", b_wire_requestfrom), + be_native_module_function("_available", b_wire_available), + be_native_module_function("_write", b_wire_write), + be_native_module_function("_read", b_wire_read), + be_native_module_function("scan", b_wire_scan), + be_native_module_function("write", b_wire_validwrite), + be_native_module_function("read", b_wire_validread), }; be_define_native_module(wire, NULL); diff --git a/tasmota/xdrv_52_3_berry_native.ino b/tasmota/xdrv_52_3_berry_native.ino index 8897603a4..46b869544 100644 --- a/tasmota/xdrv_52_3_berry_native.ino +++ b/tasmota/xdrv_52_3_berry_native.ino @@ -23,6 +23,7 @@ #include #include +const char kTypeError[] PROGMEM = "type_error"; /*********************************************************************************************\ * Native functions mapped to Berry functions * @@ -58,7 +59,7 @@ extern "C" { be_return(vm); // Return } } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } // Berry: `tasmota.cmd(command:string) -> string` @@ -72,7 +73,7 @@ extern "C" { be_pushstring(vm, TasmotaGlobal.mqtt_data); be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } // Berry: tasmota.millis([delay:int]) -> int @@ -89,7 +90,7 @@ extern "C" { be_pushint(vm, ret_millis); be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } // Berry: tasmota.getoption(index:int) -> int @@ -102,7 +103,7 @@ extern "C" { be_pushint(vm, opt); be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } // Berry: tasmota.timereached(timer:int) -> bool @@ -116,7 +117,20 @@ extern "C" { be_pushbool(vm, reached); be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); + } + + // Berry: tasmota.delay(timer:int) -> nil + // + int32_t l_delay(struct bvm *vm); + int32_t l_delay(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 1 && be_isint(vm, 1)) { // only 1 argument of type string accepted + uint32_t timer = be_toint(vm, 1); + delay(timer); + be_return_nil(vm); // Return + } + be_raise(vm, kTypeError, nullptr); } // Berry: `yield() -> nil` @@ -137,7 +151,7 @@ extern "C" { be_pushint(vm, ret); be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } int32_t l_respCmnd(bvm *vm); @@ -146,8 +160,9 @@ extern "C" { if (top == 1) { const char *msg = be_tostring(vm, 1); Response_P("%s", msg); + be_return_nil(vm); // Return nil when something goes wrong } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } int32_t l_respCmndStr(bvm *vm); @@ -156,8 +171,9 @@ extern "C" { if (top == 1) { const char *msg = be_tostring(vm, 1); ResponseCmndChar(msg); + be_return_nil(vm); // Return nil when something goes wrong } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } int32_t l_respCmndDone(bvm *vm); @@ -185,9 +201,139 @@ extern "C" { if (top == 1 && be_isstring(vm, 1)) { const char *msg = be_tostring(vm, 1); strlcpy(XdrvMailbox.command, msg, CMDSZ); + be_return_nil(vm); // Return nil when something goes wrong } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } + + + static void map_insert_int(bvm *vm, const char *key, int value) + { + be_pushstring(vm, key); + be_pushint(vm, value); + be_data_insert(vm, -3); + be_pop(vm, 2); + } + static void map_insert_bool(bvm *vm, const char *key, bool value) + { + be_pushstring(vm, key); + be_pushbool(vm, value); + be_data_insert(vm, -3); + be_pop(vm, 2); + } + static void map_insert_str(bvm *vm, const char *key, const char *value) + { + be_pushstring(vm, key); + be_pushstring(vm, value); + be_data_insert(vm, -3); + be_pop(vm, 2); + } + + // get light + int32_t l_getlight(bvm *vm); + int32_t l_getlight(bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 0 || (top == 1 && be_isint(vm, 1))) { + int32_t light_num = 0; + if (top > 0) { + light_num = be_toint(vm, 1); + } + bool data_present = false; // do we have relevant data + be_newobject(vm, "map"); + // check if the light exist + // TasmotaGlobal.devices_present + // Light.device + // Light.subtype + // Light.pwm_multi_channels + // light_controller.isCTRGBLinked() + + if (Light.device > 0) { + // we have a light + + uint8_t channels[LST_MAX]; + uint8_t channelsb[LST_MAX]; + char rgbcw[12] = {0}; + char rgbcwb[12] = {0}; + light_state.getChannelsRaw(channels); + light_state.getChannels(channelsb); + + // map_insert_int(vm, "_devices_present", TasmotaGlobal.devices_present); + // map_insert_int(vm, "_light_device", Light.device); + // map_insert_int(vm, "_light_subtype", Light.subtype); + // map_insert_int(vm, "_light_multi", Light.pwm_multi_channels); + // map_insert_int(vm, "_light_linked", light_controller.isCTRGBLinked()); + + if (!Light.pwm_multi_channels) { + uint32_t subtype = Light.subtype; // virtual sub-type, for SO37 128 + uint32_t chanidx = 0; // channel offset, for SO37 128 + + + if (light_controller.isCTRGBLinked() && (light_num == 0)) { + data_present = true; // valid combination + } + if (!light_controller.isCTRGBLinked()) { + if (light_num == 0) { + data_present = true; // valid combination + if (subtype > LST_RGB) { subtype = LST_RGB; } // limit to RGB + } + if ((light_num == 1) && subtype > LST_RGB) { + data_present = true; // valid combination + subtype = subtype - LST_RGB; + chanidx = 3; // skip first 3 channels + } + } + + if (data_present) { + // see ResponseLightState() + map_insert_bool(vm, "power", (bool)(Light.power & 1)); + map_insert_int(vm, "bri", light_state.getBri()); + + + if (subtype >= LST_RGB) { + uint16_t hue; + uint8_t sat, bri; + light_state.getHSB(&hue, &sat, &bri); + map_insert_int(vm, "hue", hue); + map_insert_int(vm, "sat", sat); + } + if ((LST_COLDWARM == subtype) || (LST_RGBW <= subtype)) { + map_insert_int(vm, "ct", light_state.getCT()); + } + if (subtype > LST_NONE) { + for (uint32_t i=0; i < subtype; i++) { + snprintf_P(rgbcw, sizeof(rgbcw), PSTR("%s%02X"), rgbcw, channels[i+chanidx]); + snprintf_P(rgbcwb, sizeof(rgbcwb), PSTR("%s%02X"), rgbcwb, channelsb[i+chanidx]); + } + map_insert_str(vm, "channels", rgbcw); + map_insert_str(vm, "channelsb", rgbcwb); + // map_insert_bool(vm, "gamma", Settings.light_correction); + } + } + } else { // Light.pwm_multi_channels + if ((light_num >= 0) && (light_num < LST_MAX)) { + data_present = true; + map_insert_bool(vm, "power", Light.power & (1 << light_num)); + map_insert_int(vm, "bri", Light.current_color[light_num]); + snprintf_P(rgbcw, sizeof(rgbcw), PSTR("%02X"), channels[light_num]); + snprintf_P(rgbcwb, sizeof(rgbcwb), PSTR("%02X"), channelsb[light_num]); + map_insert_str(vm, "channels", rgbcw); + map_insert_str(vm, "channelsb", rgbcwb); + } + } + + be_pop(vm, 1); + if (data_present) { + be_return(vm); // Return + } else { + be_return_nil(vm); // no data, return nil instead of empty map + } + } else { + be_return_nil(vm); + } + } + be_raise(vm, kTypeError, nullptr); + } + } /*********************************************************************************************\ @@ -208,7 +354,7 @@ extern "C" { Wire.beginTransmission(address); be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } // Berry: `endtransmission([stop:bool]) -> nil` @@ -224,7 +370,7 @@ extern "C" { be_pushint(vm, ret); be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } // Berry: `requestfrom(address:int, quantity:int [stop:bool = true]) -> nil` @@ -242,7 +388,7 @@ extern "C" { Wire.requestFrom((uint16_t)address, (uint8_t)quantity, stop); be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } // Berry: `available() -> bool` @@ -254,14 +400,14 @@ extern "C" { be_pushint(vm, available); be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } // Berry: `write(value:int | s:string) -> nil` int32_t b_wire_write(struct bvm *vm); int32_t b_wire_write(struct bvm *vm) { int32_t top = be_top(vm); // Get the number of arguments - if (top == 1 && (be_isint(vm, 1) || be_isint(vm, 1))) { + if (top == 1 && (be_isint(vm, 1) || be_isstring(vm, 1))) { if (be_isint(vm, 1)) { int32_t value = be_toint(vm, 1); Wire.write(value); @@ -273,7 +419,7 @@ extern "C" { } be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } // Berry: `read() -> int` @@ -285,7 +431,43 @@ extern "C" { be_pushint(vm, value); be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); + } + + int32_t b_wire_scan(struct bvm *vm); + int32_t b_wire_scan(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 0) { + be_newobject(vm, "list"); + for (uint8_t address = 1; address <= 127; address++) { + Wire.beginTransmission(address); + int32_t error = Wire.endTransmission(); + if (0 == error) { + be_pushint(vm, address); + be_data_push(vm, -2); + be_pop(vm, 1); + } + } + be_pop(vm, 1); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); + } + + // Berry: `validwrite(address:int, reg:int, val:int, size:int) -> bool or nil` + int32_t b_wire_validwrite(struct bvm *vm); + int32_t b_wire_validwrite(struct bvm *vm) { + int32_t top = be_top(vm); // Get the number of arguments + if (top == 4 && be_isint(vm, 1) && be_isint(vm, 2) && be_isint(vm, 3) && be_isint(vm, 4)) { + uint8_t addr = be_toint(vm, 1); + uint8_t reg = be_toint(vm, 2); + uint8_t val = be_toint(vm, 3); + uint8_t size = be_toint(vm, 4); + bool ok = I2cWrite(addr, reg, val, size); + be_pushbool(vm, ok); + be_return(vm); // Return + } + be_raise(vm, kTypeError, nullptr); } // Berry: `validread(address:int, reg:int, size:int) -> int or nil` @@ -304,7 +486,7 @@ extern "C" { } be_return(vm); // Return } - be_return_nil(vm); // Return nil when something goes wrong + be_raise(vm, kTypeError, nullptr); } } diff --git a/tasmota/xdrv_52_7_berry_embedded.ino b/tasmota/xdrv_52_7_berry_embedded.ino index 43b33652c..b9575da81 100644 --- a/tasmota/xdrv_52_7_berry_embedded.ino +++ b/tasmota/xdrv_52_7_berry_embedded.ino @@ -45,6 +45,7 @@ const char berry_prog[] = // Again, this will be eventually pre-compiled "var getfreeheap, publish, cmd, getoption, millis, timereached, yield " "var respcmnd, respcmndstr, respcmnd_done, respcmnd_error, respcmnd_failed, resolvecmnd " + "var getlight " "def init_ntv() " "import tasmota_ntv " "self.getfreeheap = tasmota_ntv.getfreeheap " @@ -62,6 +63,8 @@ const char berry_prog[] = "self.respcmnd_error = tasmota_ntv.respcmnd_error " "self.respcmnd_failed = tasmota_ntv.respcmnd_failed " "self.resolvecmnd = tasmota_ntv.resolvecmnd " + + "self.getlight = tasmota_ntv.getlight " "end " "def init() " diff --git a/tasmota/xdrv_52_9_berry.ino b/tasmota/xdrv_52_9_berry.ino index 666b36818..fbbda679e 100644 --- a/tasmota/xdrv_52_9_berry.ino +++ b/tasmota/xdrv_52_9_berry.ino @@ -304,11 +304,15 @@ void CmndBrRun(void) { // AddLog(LOG_LEVEL_INFO, "run: type(2)=%s", be_typename(berry.vm, 2)); // code taken from REPL, look first at top, and if nil, look at return value - ret_val = be_tostring(berry.vm, 1); - Response_P("{\"" D_PRFX_BR "\":\"%s\"}", ret_val); // can't use XdrvMailbox.command as it may have been overwritten by subcommand + if (!be_isnil(berry.vm, 1)) { + ret_val = be_tostring(berry.vm, 1); + } else { + ret_val = be_tostring(berry.vm, 2); + } + Response_P("{\"" D_PRFX_BR "\":\"%s\"}", EscapeJSONString(ret_val).c_str()); // can't use XdrvMailbox.command as it may have been overwritten by subcommand be_pop(berry.vm, 1); } else { - Response_P(PSTR("{\"" D_PRFX_BR "\":\"[%s] %s\"}"), be_tostring(berry.vm, -2), be_tostring(berry.vm, -1)); + Response_P(PSTR("{\"" D_PRFX_BR "\":\"[%s] %s\"}"), EscapeJSONString(be_tostring(berry.vm, -2)).c_str(), EscapeJSONString(be_tostring(berry.vm, -1)).c_str()); be_pop(berry.vm, 2); }