diff --git a/CHANGELOG.md b/CHANGELOG.md index e66fc9fa0..35534baa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,14 @@ All notable changes to this project will be documented in this file. - ESP32 Hybrid compile take custom boards settings in account (#22542) ### Breaking Changed +- ArtNet on ESP32 switches from GRB to RGB encoding ### 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) +- Replace NeoPixelBus with TasmotaLED on ESP32x ### Fixed - ESP32 upgrade by file upload response based on file size (#22500) diff --git a/lib/lib_basic/NeoPixelBus/src/internal/NeoEsp32RmtMethod_idf5.h b/lib/lib_basic/NeoPixelBus/src/internal/NeoEsp32RmtMethod_idf5.h index a86b05330..d26aece56 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 000000000..8ee6079f5 --- /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 000000000..d323c20ab --- /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 000000000..ac1b78c1e --- /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 = 0x0 << 16, + TasmotaLed_RMT = 1 << 16, + TasmotaLed_SPI = 2 << 16, + TasmotaLed_I2S = 3 << 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 000000000..d765703e5 --- /dev/null +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.cpp @@ -0,0 +1,97 @@ +/* + 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) { +uint32_t hw_orig = hw; + // Step 1. discard any unsupported hardware, and replace with TasmotaLed_HW_Default + uint32_t hw_type = hw & 0xFF0000; // discard bits 0..15 +#if !TASMOTALED_HARDWARE_RMT + if (hw_type == TasmotaLed_RMT) { + hw = TasmotaLed_HW_None; + } +#endif // TASMOTALED_HARDWARE_RMT +#if !TASMOTALED_HARDWARE_SPI + if (hw_type == TasmotaLed_SPI) { + hw = TasmotaLed_HW_None; + } +#endif // TASMOTALED_HARDWARE_SPI +#if !TASMOTALED_HARDWARE_I2S + if (hw_type == TasmotaLed_I2S) { + hw = TasmotaLed_HW_None; + } +#endif // TASMOTALED_HARDWARE_I2S + + // Step 2. If TasmotaLed_HW_Default, find a suitable scheme, RMT preferred +#if TASMOTALED_HARDWARE_RMT + if ((hw & 0xFF0000) == TasmotaLed_HW_Default) { + hw = TasmotaLed_RMT; + } +#endif // TASMOTALED_HARDWARE_RMT +#if TASMOTALED_HARDWARE_I2S + if ((hw & 0xFF0000) == TasmotaLed_HW_Default) { + hw = TasmotaLed_I2S; + } +#endif // TASMOTALED_HARDWARE_I2S +#if TASMOTALED_HARDWARE_SPI + if ((hw & 0xFF0000) == 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); + + switch (hw & 0XFF0000) { +#if TASMOTALED_HARDWARE_RMT + case TasmotaLed_RMT: + pusher = new TasmotaLEDPusherRMT(gpio); + AddLog(LOG_LEVEL_DEBUG, "LED: RMT gpio %i", gpio); + break; +#endif // TASMOTALED_HARDWARE_RMT +#if TASMOTALED_HARDWARE_SPI + case TasmotaLed_SPI: + pusher = new TasmotaLEDPusherSPI(gpio); + AddLog(LOG_LEVEL_DEBUG, "LED: SPI gpio %i", gpio); + break; +#endif // TASMOTALED_HARDWARE_SPI + default: + break; + } + 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 000000000..2b8eb4896 --- /dev/null +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusher.h @@ -0,0 +1,161 @@ +/* + 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() : _pixel_count(0), _pixel_size(0), _led_timing(nullptr) {}; + virtual ~TasmotaLEDPusher() {}; + + 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: + 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) : _pin(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) : _pin(pin), _spi_strip({}) {}; + ~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; +}; +#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 000000000..747985008 --- /dev/null +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherRMT.cpp @@ -0,0 +1,240 @@ +/* + 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; + } +} + +bool TasmotaLEDPusherRMT::Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) { + TasmotaLEDPusher::Begin(pixel_count, pixel_size, led_timing); + + 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 + + ret = rmt_new_tx_channel(&config, &_channel); + if (ret != ESP_OK) { + AddLog(LOG_LEVEL_INFO, "RMT: cannot initialize Gpio %i err=%i", _pin, ret); + return false; + } + 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); + ret = rmt_new_led_strip_encoder(&encoder_config, &_led_encoder, RmtBit0, RmtBit1, RmtReset); + if (ret != ESP_OK) { + // AddLog(LOG_LEVEL_INFO, "RMT: cannot initialize led strip encoder err=%i", ret); + return false; + } + ret = rmt_enable(_channel); + if (ret != ESP_OK) { + // AddLog(LOG_LEVEL_INFO, "RMT: cannot enable channel err=%i", ret); + return false; + } + return true; +} + +bool TasmotaLEDPusherRMT::CanShow(void) { + if (_channel) { + return (ESP_OK == rmt_tx_wait_all_done(_channel, 0)); + } else { + return false; + } +} + +bool TasmotaLEDPusherRMT::Push(uint8_t *buf) { + + // 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 000000000..72ea342ef --- /dev/null +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLEDPusherSPI.cpp @@ -0,0 +1,191 @@ +/* + 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; + } +} + +bool TasmotaLEDPusherSPI::Begin(uint16_t pixel_count, uint16_t pixel_size, const TasmotaLED_Timing * led_timing) { + TasmotaLEDPusher::Begin(pixel_count, pixel_size, led_timing); + _spi_strip.bytes_per_pixel = _pixel_size; + _spi_strip.strip_len = _pixel_count; + + esp_err_t err = ESP_OK; + uint32_t mem_caps = MALLOC_CAP_DEFAULT; + // spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT; + spi_bus_config_t spi_bus_cfg; + spi_device_interface_config_t spi_dev_cfg; + spi_host_device_t spi_host = SPI2_HOST; + bool with_dma = true; /// TODO: pass value or compute based on pixelcount + 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_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) { + AddLog(LOG_LEVEL_INFO, PSTR("LED: Error create SPI bus failed")); + goto err; + } + _spi_strip.spi_host = spi_host; // confirmed working, so keep it's value to free it later + + 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; + } + + 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); + } + return false; +} + +bool TasmotaLEDPusherSPI::CanShow(void) { + return true; // TODO +} + +bool TasmotaLEDPusherSPI::Push(uint8_t *buf) { + + if (CanShow()) { + led_strip_transmit_buffer(&_spi_strip, buf); + } + return true; +} + +#endif // TASMOTALED_HARDWARE_SPI +#endif // ESP32 diff --git a/lib/libesp32/berry/default/be_modtab.c b/lib/libesp32/berry/default/be_modtab.c index e4f60edcc..f948b7d04 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 @@ -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 70917593f..65ab158cc 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_tasmota/src/be_leds_ntv_lib.c b/lib/libesp32/berry_tasmota/src/be_leds_ntv_lib.c index 26ff0b69b..11b2712f6 100644 --- a/lib/libesp32/berry_tasmota/src/be_leds_ntv_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_leds_ntv_lib.c @@ -7,7 +7,9 @@ #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); @@ -16,10 +18,15 @@ 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) - call_native, func(be_neopixelbus_call_native) + RMT, int(TasmotaLed_RMT) + SPI, int(TasmotaLed_SPI) + I2S, int(TasmotaLed_I2S) + + 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/leds.be b/lib/libesp32/berry_tasmota/src/embedded/leds.be index 019a64e3b..7a124fc50 100644 --- a/lib/libesp32/berry_tasmota/src/embedded/leds.be +++ b/lib/libesp32/berry_tasmota/src/embedded/leds.be @@ -31,8 +31,8 @@ class Leds : Leds_ntv # 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)) @@ -47,7 +47,7 @@ class Leds : Leds_ntv self.bri = 127 # 50% brightness by default # initialize the structure - self.ctor(self.leds, gpio_phy, typ, rmt) + self.ctor(self.leds, gpio_phy, typ, hardware) end if self._p == nil raise "internal_error", "couldn't not initialize noepixelbus" end @@ -56,44 +56,6 @@ class Leds : Leds_ntv 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() self.clear_to(0x000000) self.show() @@ -109,17 +71,14 @@ class Leds : Leds_ntv return self.bri end - def ctor(leds, gpio_phy, typ, rmt) + 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 +114,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 +366,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_leds.h b/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h index 683e7e756..5a493db0e 100644 --- a/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h +++ b/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h @@ -3,471 +3,9 @@ * Generated code, don't edit * \********************************************************************/ #include "be_constobj.h" -extern const bclass be_class_Leds_segment; extern const bclass be_class_Leds; extern const bclass be_class_Leds_matrix; -// compact class 'Leds_segment' ktab size: 16, total: 34 (saved 144 bytes) -static const bvalue be_ktab_class_Leds_segment[16] = { - /* K0 */ be_nested_str(offset), - /* K1 */ be_nested_str(bri), - /* K2 */ be_nested_str(strip), - /* K3 */ be_nested_str(call_native), - /* K4 */ be_nested_str(to_gamma), - /* K5 */ be_nested_str(leds), - /* K6 */ be_nested_str(dirty), - /* K7 */ be_nested_str(can_show), - /* K8 */ be_nested_str(set_pixel_color), - /* K9 */ be_nested_str(is_dirty), - /* K10 */ be_nested_str(clear_to), - /* K11 */ be_const_int(0), - /* K12 */ be_nested_str(show), - /* K13 */ be_nested_str(get_pixel_color), - /* K14 */ be_nested_str(offseta), - /* K15 */ be_nested_str(pixel_size), -}; - - extern const bclass be_class_Leds_segment; - -/******************************************************************** -** Solidified function: pixel_offset -********************************************************************/ -be_local_closure(class_Leds_segment_pixel_offset, /* name */ - be_nested_proto( - 2, /* nstack */ - 1, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_pixel_offset, - &be_const_str_solidified, - ( &(const binstruction[ 2]) { /* code */ - 0x88040100, // 0000 GETMBR R1 R0 K0 - 0x80040200, // 0001 RET 1 R1 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: clear_to -********************************************************************/ -be_local_closure(class_Leds_segment_clear_to, /* name */ - be_nested_proto( - 10, /* 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_segment, /* shared constants */ - &be_const_str_clear_to, - &be_const_str_solidified, - ( &(const binstruction[16]) { /* code */ - 0x4C0C0000, // 0000 LDNIL R3 - 0x1C0C0403, // 0001 EQ R3 R2 R3 - 0x780E0000, // 0002 JMPF R3 #0004 - 0x88080101, // 0003 GETMBR R2 R0 K1 - 0x880C0102, // 0004 GETMBR R3 R0 K2 - 0x8C0C0703, // 0005 GETMET R3 R3 K3 - 0x54160008, // 0006 LDINT R5 9 - 0x88180102, // 0007 GETMBR R6 R0 K2 - 0x8C180D04, // 0008 GETMET R6 R6 K4 - 0x5C200200, // 0009 MOVE R8 R1 - 0x5C240400, // 000A MOVE R9 R2 - 0x7C180600, // 000B CALL R6 3 - 0x881C0100, // 000C GETMBR R7 R0 K0 - 0x88200105, // 000D GETMBR R8 R0 K5 - 0x7C0C0A00, // 000E CALL R3 5 - 0x80000000, // 000F RET 0 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: pixel_count -********************************************************************/ -be_local_closure(class_Leds_segment_pixel_count, /* name */ - be_nested_proto( - 2, /* nstack */ - 1, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_pixel_count, - &be_const_str_solidified, - ( &(const binstruction[ 2]) { /* code */ - 0x88040105, // 0000 GETMBR R1 R0 K5 - 0x80040200, // 0001 RET 1 R1 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: pixels_buffer -********************************************************************/ -be_local_closure(class_Leds_segment_pixels_buffer, /* name */ - be_nested_proto( - 2, /* nstack */ - 1, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_pixels_buffer, - &be_const_str_solidified, - ( &(const binstruction[ 2]) { /* code */ - 0x4C040000, // 0000 LDNIL R1 - 0x80040200, // 0001 RET 1 R1 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: dirty -********************************************************************/ -be_local_closure(class_Leds_segment_dirty, /* name */ - be_nested_proto( - 3, /* nstack */ - 1, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_dirty, - &be_const_str_solidified, - ( &(const binstruction[ 4]) { /* code */ - 0x88040102, // 0000 GETMBR R1 R0 K2 - 0x8C040306, // 0001 GETMET R1 R1 K6 - 0x7C040200, // 0002 CALL R1 1 - 0x80000000, // 0003 RET 0 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: can_show -********************************************************************/ -be_local_closure(class_Leds_segment_can_show, /* name */ - be_nested_proto( - 3, /* nstack */ - 1, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_can_show, - &be_const_str_solidified, - ( &(const binstruction[ 4]) { /* code */ - 0x88040102, // 0000 GETMBR R1 R0 K2 - 0x8C040307, // 0001 GETMET R1 R1 K7 - 0x7C040200, // 0002 CALL R1 1 - 0x80040200, // 0003 RET 1 R1 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: set_pixel_color -********************************************************************/ -be_local_closure(class_Leds_segment_set_pixel_color, /* name */ - be_nested_proto( - 9, /* nstack */ - 4, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_set_pixel_color, - &be_const_str_solidified, - ( &(const binstruction[12]) { /* code */ - 0x4C100000, // 0000 LDNIL R4 - 0x1C100604, // 0001 EQ R4 R3 R4 - 0x78120000, // 0002 JMPF R4 #0004 - 0x880C0101, // 0003 GETMBR R3 R0 K1 - 0x88100102, // 0004 GETMBR R4 R0 K2 - 0x8C100908, // 0005 GETMET R4 R4 K8 - 0x88180100, // 0006 GETMBR R6 R0 K0 - 0x00180206, // 0007 ADD R6 R1 R6 - 0x5C1C0400, // 0008 MOVE R7 R2 - 0x5C200600, // 0009 MOVE R8 R3 - 0x7C100800, // 000A CALL R4 4 - 0x80000000, // 000B RET 0 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: is_dirty -********************************************************************/ -be_local_closure(class_Leds_segment_is_dirty, /* name */ - be_nested_proto( - 3, /* nstack */ - 1, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_is_dirty, - &be_const_str_solidified, - ( &(const binstruction[ 4]) { /* code */ - 0x88040102, // 0000 GETMBR R1 R0 K2 - 0x8C040309, // 0001 GETMET R1 R1 K9 - 0x7C040200, // 0002 CALL R1 1 - 0x80040200, // 0003 RET 1 R1 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: clear -********************************************************************/ -be_local_closure(class_Leds_segment_clear, /* name */ - be_nested_proto( - 4, /* nstack */ - 1, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_clear, - &be_const_str_solidified, - ( &(const binstruction[ 6]) { /* code */ - 0x8C04010A, // 0000 GETMET R1 R0 K10 - 0x580C000B, // 0001 LDCONST R3 K11 - 0x7C040400, // 0002 CALL R1 2 - 0x8C04010C, // 0003 GETMET R1 R0 K12 - 0x7C040200, // 0004 CALL R1 1 - 0x80000000, // 0005 RET 0 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: begin -********************************************************************/ -be_local_closure(class_Leds_segment_begin, /* name */ - be_nested_proto( - 1, /* nstack */ - 1, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_begin, - &be_const_str_solidified, - ( &(const binstruction[ 1]) { /* code */ - 0x80000000, // 0000 RET 0 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: get_pixel_color -********************************************************************/ -be_local_closure(class_Leds_segment_get_pixel_color, /* name */ - be_nested_proto( - 5, /* 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_segment, /* shared constants */ - &be_const_str_get_pixel_color, - &be_const_str_solidified, - ( &(const binstruction[ 6]) { /* code */ - 0x88080102, // 0000 GETMBR R2 R0 K2 - 0x8C08050D, // 0001 GETMET R2 R2 K13 - 0x8810010E, // 0002 GETMBR R4 R0 K14 - 0x00100204, // 0003 ADD R4 R1 R4 - 0x7C080400, // 0004 CALL R2 2 - 0x80040400, // 0005 RET 1 R2 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: pixel_size -********************************************************************/ -be_local_closure(class_Leds_segment_pixel_size, /* name */ - be_nested_proto( - 3, /* nstack */ - 1, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_pixel_size, - &be_const_str_solidified, - ( &(const binstruction[ 4]) { /* code */ - 0x88040102, // 0000 GETMBR R1 R0 K2 - 0x8C04030F, // 0001 GETMET R1 R1 K15 - 0x7C040200, // 0002 CALL R1 1 - 0x80040200, // 0003 RET 1 R1 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: init -********************************************************************/ -be_local_closure(class_Leds_segment_init, /* name */ - be_nested_proto( - 6, /* nstack */ - 4, /* argc */ - 10, /* varg */ - 0, /* has upvals */ - NULL, /* no upvals */ - 0, /* has sup protos */ - NULL, /* no sub protos */ - 1, /* has constants */ - &be_ktab_class_Leds_segment, /* shared constants */ - &be_const_str_init, - &be_const_str_solidified, - ( &(const binstruction[10]) { /* code */ - 0x90020401, // 0000 SETMBR R0 K2 R1 - 0x60100009, // 0001 GETGBL R4 G9 - 0x5C140400, // 0002 MOVE R5 R2 - 0x7C100200, // 0003 CALL R4 1 - 0x90020004, // 0004 SETMBR R0 K0 R4 - 0x60100009, // 0005 GETGBL R4 G9 - 0x5C140600, // 0006 MOVE R5 R3 - 0x7C100200, // 0007 CALL R4 1 - 0x90020A04, // 0008 SETMBR R0 K5 R4 - 0x80000000, // 0009 RET 0 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: show -********************************************************************/ -be_local_closure(class_Leds_segment_show, /* name */ - be_nested_proto( - 4, /* 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_segment, /* shared constants */ - &be_const_str_show, - &be_const_str_solidified, - ( &(const binstruction[16]) { /* code */ - 0x60080017, // 0000 GETGBL R2 G23 - 0x5C0C0200, // 0001 MOVE R3 R1 - 0x7C080200, // 0002 CALL R2 1 - 0x740A0007, // 0003 JMPT R2 #000C - 0x88080100, // 0004 GETMBR R2 R0 K0 - 0x1C08050B, // 0005 EQ R2 R2 K11 - 0x780A0007, // 0006 JMPF R2 #000F - 0x88080105, // 0007 GETMBR R2 R0 K5 - 0x880C0102, // 0008 GETMBR R3 R0 K2 - 0x880C0705, // 0009 GETMBR R3 R3 K5 - 0x1C080403, // 000A EQ R2 R2 R3 - 0x780A0002, // 000B JMPF R2 #000F - 0x88080102, // 000C GETMBR R2 R0 K2 - 0x8C08050C, // 000D GETMET R2 R2 K12 - 0x7C080200, // 000E CALL R2 1 - 0x80000000, // 000F RET 0 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified class: Leds_segment -********************************************************************/ -be_local_class(Leds_segment, - 3, - NULL, - be_nested_map(17, - ( (struct bmapnode*) &(const bmapnode[]) { - { be_const_key(pixel_offset, 9), be_const_closure(class_Leds_segment_pixel_offset_closure) }, - { be_const_key(clear_to, -1), be_const_closure(class_Leds_segment_clear_to_closure) }, - { be_const_key(show, -1), be_const_closure(class_Leds_segment_show_closure) }, - { be_const_key(pixels_buffer, 10), be_const_closure(class_Leds_segment_pixels_buffer_closure) }, - { be_const_key(offset, -1), be_const_var(1) }, - { be_const_key(dirty, -1), be_const_closure(class_Leds_segment_dirty_closure) }, - { be_const_key(can_show, -1), be_const_closure(class_Leds_segment_can_show_closure) }, - { be_const_key(set_pixel_color, 6), be_const_closure(class_Leds_segment_set_pixel_color_closure) }, - { be_const_key(get_pixel_color, -1), be_const_closure(class_Leds_segment_get_pixel_color_closure) }, - { be_const_key(pixel_count, -1), be_const_closure(class_Leds_segment_pixel_count_closure) }, - { be_const_key(strip, 7), be_const_var(0) }, - { be_const_key(leds, -1), be_const_var(2) }, - { be_const_key(begin, -1), be_const_closure(class_Leds_segment_begin_closure) }, - { be_const_key(is_dirty, 8), be_const_closure(class_Leds_segment_is_dirty_closure) }, - { be_const_key(pixel_size, -1), be_const_closure(class_Leds_segment_pixel_size_closure) }, - { be_const_key(init, -1), be_const_closure(class_Leds_segment_init_closure) }, - { be_const_key(clear, 2), be_const_closure(class_Leds_segment_clear_closure) }, - })), - (bstring*) &be_const_str_Leds_segment -); // compact class 'Leds_matrix' ktab size: 24, total: 62 (saved 304 bytes) static const bvalue be_ktab_class_Leds_matrix[24] = { /* K0 */ be_nested_str(strip), @@ -1111,140 +649,35 @@ 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] = { - /* 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), +// compact class 'Leds_segment' ktab size: 16, total: 34 (saved 144 bytes) +static const bvalue be_ktab_class_Leds_segment[16] = { + /* K0 */ be_nested_str(offset), + /* K1 */ be_nested_str(bri), + /* K2 */ be_nested_str(strip), + /* K3 */ be_nested_str(call_native), + /* K4 */ be_nested_str(to_gamma), + /* K5 */ be_nested_str(leds), + /* K6 */ be_nested_str(dirty), + /* K7 */ be_nested_str(can_show), + /* K8 */ be_nested_str(set_pixel_color), + /* K9 */ be_nested_str(is_dirty), + /* K10 */ be_nested_str(clear_to), + /* K11 */ be_const_int(0), + /* K12 */ be_nested_str(show), + /* K13 */ be_nested_str(get_pixel_color), + /* K14 */ be_nested_str(offseta), + /* K15 */ be_nested_str(pixel_size), }; -extern const bclass be_class_Leds; +extern const bclass be_class_Leds_segment; /******************************************************************** -** Solidified function: create_segment +** Solidified function: pixel_offset ********************************************************************/ -be_local_closure(class_Leds_create_segment, /* name */ +be_local_closure(class_Leds_segment_pixel_offset, /* name */ be_nested_proto( - 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_create_segment, - &be_const_str_solidified, - ( &(const binstruction[23]) { /* code */ - 0x600C0009, // 0000 GETGBL R3 G9 - 0x5C100200, // 0001 MOVE R4 R1 - 0x7C0C0200, // 0002 CALL R3 1 - 0x60100009, // 0003 GETGBL R4 G9 - 0x5C140400, // 0004 MOVE R5 R2 - 0x7C100200, // 0005 CALL R4 1 - 0x000C0604, // 0006 ADD R3 R3 R4 - 0x88100100, // 0007 GETMBR R4 R0 K0 - 0x240C0604, // 0008 GT R3 R3 R4 - 0x740E0003, // 0009 JMPT R3 #000E - 0x140C0301, // 000A LT R3 R1 K1 - 0x740E0001, // 000B JMPT R3 #000E - 0x140C0501, // 000C LT R3 R2 K1 - 0x780E0000, // 000D JMPF R3 #000F - 0xB0060503, // 000E RAISE 1 K2 K3 - 0x580C0004, // 000F LDCONST R3 K4 - 0xB4000004, // 0010 CLASS K4 - 0x5C100600, // 0011 MOVE R4 R3 - 0x5C140000, // 0012 MOVE R5 R0 - 0x5C180200, // 0013 MOVE R6 R1 - 0x5C1C0400, // 0014 MOVE R7 R2 - 0x7C100600, // 0015 CALL R4 3 - 0x80040800, // 0016 RET 1 R4 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** 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 -********************************************************************/ -be_local_closure(class_Leds_begin, /* name */ - be_nested_proto( - 4, /* nstack */ + 2, /* nstack */ 1, /* argc */ 10, /* varg */ 0, /* has upvals */ @@ -1252,14 +685,12 @@ be_local_closure(class_Leds_begin, /* name */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ - &be_ktab_class_Leds, /* shared constants */ - &be_const_str_begin, + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_pixel_offset, &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 */ + 0x88040100, // 0000 GETMBR R1 R0 K0 + 0x80040200, // 0001 RET 1 R1 }) ) ); @@ -1267,106 +698,11 @@ be_local_closure(class_Leds_begin, /* name */ /******************************************************************** -** Solidified function: clear +** Solidified function: clear_to ********************************************************************/ -be_local_closure(class_Leds_clear, /* name */ +be_local_closure(class_Leds_segment_clear_to, /* name */ be_nested_proto( - 4, /* nstack */ - 1, /* 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_clear, - &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 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** 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_init, - &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 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: to_gamma -********************************************************************/ -be_local_closure(class_Leds_to_gamma, /* name */ - be_nested_proto( - 8, /* nstack */ + 10, /* nstack */ 3, /* argc */ 10, /* varg */ 0, /* has upvals */ @@ -1374,47 +710,26 @@ be_local_closure(class_Leds_to_gamma, /* name */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ - &be_ktab_class_Leds, /* shared constants */ - &be_const_str_to_gamma, + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_clear_to, &be_const_str_solidified, - ( &(const binstruction[10]) { /* code */ + ( &(const binstruction[16]) { /* 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 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: show -********************************************************************/ -be_local_closure(class_Leds_show, /* name */ - be_nested_proto( - 4, /* nstack */ - 1, /* 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_show, - &be_const_str_solidified, - ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 - 0x580C0017, // 0001 LDCONST R3 K23 - 0x7C040400, // 0002 CALL R1 2 - 0x80000000, // 0003 RET 0 + 0x88080101, // 0003 GETMBR R2 R0 K1 + 0x880C0102, // 0004 GETMBR R3 R0 K2 + 0x8C0C0703, // 0005 GETMET R3 R3 K3 + 0x54160008, // 0006 LDINT R5 9 + 0x88180102, // 0007 GETMBR R6 R0 K2 + 0x8C180D04, // 0008 GETMET R6 R6 K4 + 0x5C200200, // 0009 MOVE R8 R1 + 0x5C240400, // 000A MOVE R9 R2 + 0x7C180600, // 000B CALL R6 3 + 0x881C0100, // 000C GETMBR R7 R0 K0 + 0x88200105, // 000D GETMBR R8 R0 K5 + 0x7C0C0A00, // 000E CALL R3 5 + 0x80000000, // 000F RET 0 }) ) ); @@ -1424,7 +739,422 @@ be_local_closure(class_Leds_show, /* name */ /******************************************************************** ** Solidified function: pixel_count ********************************************************************/ -be_local_closure(class_Leds_pixel_count, /* name */ +be_local_closure(class_Leds_segment_pixel_count, /* name */ + be_nested_proto( + 2, /* nstack */ + 1, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_pixel_count, + &be_const_str_solidified, + ( &(const binstruction[ 2]) { /* code */ + 0x88040105, // 0000 GETMBR R1 R0 K5 + 0x80040200, // 0001 RET 1 R1 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: pixels_buffer +********************************************************************/ +be_local_closure(class_Leds_segment_pixels_buffer, /* name */ + be_nested_proto( + 2, /* nstack */ + 1, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_pixels_buffer, + &be_const_str_solidified, + ( &(const binstruction[ 2]) { /* code */ + 0x4C040000, // 0000 LDNIL R1 + 0x80040200, // 0001 RET 1 R1 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: dirty +********************************************************************/ +be_local_closure(class_Leds_segment_dirty, /* name */ + be_nested_proto( + 3, /* nstack */ + 1, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_dirty, + &be_const_str_solidified, + ( &(const binstruction[ 4]) { /* code */ + 0x88040102, // 0000 GETMBR R1 R0 K2 + 0x8C040306, // 0001 GETMET R1 R1 K6 + 0x7C040200, // 0002 CALL R1 1 + 0x80000000, // 0003 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: can_show +********************************************************************/ +be_local_closure(class_Leds_segment_can_show, /* name */ + be_nested_proto( + 3, /* nstack */ + 1, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_can_show, + &be_const_str_solidified, + ( &(const binstruction[ 4]) { /* code */ + 0x88040102, // 0000 GETMBR R1 R0 K2 + 0x8C040307, // 0001 GETMET R1 R1 K7 + 0x7C040200, // 0002 CALL R1 1 + 0x80040200, // 0003 RET 1 R1 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: set_pixel_color +********************************************************************/ +be_local_closure(class_Leds_segment_set_pixel_color, /* name */ + be_nested_proto( + 9, /* nstack */ + 4, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_set_pixel_color, + &be_const_str_solidified, + ( &(const binstruction[12]) { /* code */ + 0x4C100000, // 0000 LDNIL R4 + 0x1C100604, // 0001 EQ R4 R3 R4 + 0x78120000, // 0002 JMPF R4 #0004 + 0x880C0101, // 0003 GETMBR R3 R0 K1 + 0x88100102, // 0004 GETMBR R4 R0 K2 + 0x8C100908, // 0005 GETMET R4 R4 K8 + 0x88180100, // 0006 GETMBR R6 R0 K0 + 0x00180206, // 0007 ADD R6 R1 R6 + 0x5C1C0400, // 0008 MOVE R7 R2 + 0x5C200600, // 0009 MOVE R8 R3 + 0x7C100800, // 000A CALL R4 4 + 0x80000000, // 000B RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: is_dirty +********************************************************************/ +be_local_closure(class_Leds_segment_is_dirty, /* name */ + be_nested_proto( + 3, /* nstack */ + 1, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_is_dirty, + &be_const_str_solidified, + ( &(const binstruction[ 4]) { /* code */ + 0x88040102, // 0000 GETMBR R1 R0 K2 + 0x8C040309, // 0001 GETMET R1 R1 K9 + 0x7C040200, // 0002 CALL R1 1 + 0x80040200, // 0003 RET 1 R1 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: clear +********************************************************************/ +be_local_closure(class_Leds_segment_clear, /* name */ + be_nested_proto( + 4, /* nstack */ + 1, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_clear, + &be_const_str_solidified, + ( &(const binstruction[ 6]) { /* code */ + 0x8C04010A, // 0000 GETMET R1 R0 K10 + 0x580C000B, // 0001 LDCONST R3 K11 + 0x7C040400, // 0002 CALL R1 2 + 0x8C04010C, // 0003 GETMET R1 R0 K12 + 0x7C040200, // 0004 CALL R1 1 + 0x80000000, // 0005 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: begin +********************************************************************/ +be_local_closure(class_Leds_segment_begin, /* name */ + be_nested_proto( + 1, /* nstack */ + 1, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_begin, + &be_const_str_solidified, + ( &(const binstruction[ 1]) { /* code */ + 0x80000000, // 0000 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: get_pixel_color +********************************************************************/ +be_local_closure(class_Leds_segment_get_pixel_color, /* name */ + be_nested_proto( + 5, /* 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_segment, /* shared constants */ + &be_const_str_get_pixel_color, + &be_const_str_solidified, + ( &(const binstruction[ 6]) { /* code */ + 0x88080102, // 0000 GETMBR R2 R0 K2 + 0x8C08050D, // 0001 GETMET R2 R2 K13 + 0x8810010E, // 0002 GETMBR R4 R0 K14 + 0x00100204, // 0003 ADD R4 R1 R4 + 0x7C080400, // 0004 CALL R2 2 + 0x80040400, // 0005 RET 1 R2 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: pixel_size +********************************************************************/ +be_local_closure(class_Leds_segment_pixel_size, /* name */ + be_nested_proto( + 3, /* nstack */ + 1, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_pixel_size, + &be_const_str_solidified, + ( &(const binstruction[ 4]) { /* code */ + 0x88040102, // 0000 GETMBR R1 R0 K2 + 0x8C04030F, // 0001 GETMET R1 R1 K15 + 0x7C040200, // 0002 CALL R1 1 + 0x80040200, // 0003 RET 1 R1 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: init +********************************************************************/ +be_local_closure(class_Leds_segment_init, /* name */ + be_nested_proto( + 6, /* nstack */ + 4, /* argc */ + 10, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + &be_ktab_class_Leds_segment, /* shared constants */ + &be_const_str_init, + &be_const_str_solidified, + ( &(const binstruction[10]) { /* code */ + 0x90020401, // 0000 SETMBR R0 K2 R1 + 0x60100009, // 0001 GETGBL R4 G9 + 0x5C140400, // 0002 MOVE R5 R2 + 0x7C100200, // 0003 CALL R4 1 + 0x90020004, // 0004 SETMBR R0 K0 R4 + 0x60100009, // 0005 GETGBL R4 G9 + 0x5C140600, // 0006 MOVE R5 R3 + 0x7C100200, // 0007 CALL R4 1 + 0x90020A04, // 0008 SETMBR R0 K5 R4 + 0x80000000, // 0009 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: show +********************************************************************/ +be_local_closure(class_Leds_segment_show, /* name */ + be_nested_proto( + 4, /* 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_segment, /* shared constants */ + &be_const_str_show, + &be_const_str_solidified, + ( &(const binstruction[16]) { /* code */ + 0x60080017, // 0000 GETGBL R2 G23 + 0x5C0C0200, // 0001 MOVE R3 R1 + 0x7C080200, // 0002 CALL R2 1 + 0x740A0007, // 0003 JMPT R2 #000C + 0x88080100, // 0004 GETMBR R2 R0 K0 + 0x1C08050B, // 0005 EQ R2 R2 K11 + 0x780A0007, // 0006 JMPF R2 #000F + 0x88080105, // 0007 GETMBR R2 R0 K5 + 0x880C0102, // 0008 GETMBR R3 R0 K2 + 0x880C0705, // 0009 GETMBR R3 R3 K5 + 0x1C080403, // 000A EQ R2 R2 R3 + 0x780A0002, // 000B JMPF R2 #000F + 0x88080102, // 000C GETMBR R2 R0 K2 + 0x8C08050C, // 000D GETMET R2 R2 K12 + 0x7C080200, // 000E CALL R2 1 + 0x80000000, // 000F RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified class: Leds_segment +********************************************************************/ +be_local_class(Leds_segment, + 3, + NULL, + be_nested_map(17, + ( (struct bmapnode*) &(const bmapnode[]) { + { be_const_key(pixel_offset, 9), be_const_closure(class_Leds_segment_pixel_offset_closure) }, + { be_const_key(clear_to, -1), be_const_closure(class_Leds_segment_clear_to_closure) }, + { be_const_key(show, -1), be_const_closure(class_Leds_segment_show_closure) }, + { be_const_key(pixels_buffer, 10), be_const_closure(class_Leds_segment_pixels_buffer_closure) }, + { be_const_key(offset, -1), be_const_var(1) }, + { be_const_key(dirty, -1), be_const_closure(class_Leds_segment_dirty_closure) }, + { be_const_key(can_show, -1), be_const_closure(class_Leds_segment_can_show_closure) }, + { be_const_key(set_pixel_color, 6), be_const_closure(class_Leds_segment_set_pixel_color_closure) }, + { be_const_key(get_pixel_color, -1), be_const_closure(class_Leds_segment_get_pixel_color_closure) }, + { be_const_key(pixel_count, -1), be_const_closure(class_Leds_segment_pixel_count_closure) }, + { be_const_key(strip, 7), be_const_var(0) }, + { be_const_key(leds, -1), be_const_var(2) }, + { be_const_key(begin, -1), be_const_closure(class_Leds_segment_begin_closure) }, + { be_const_key(is_dirty, 8), be_const_closure(class_Leds_segment_is_dirty_closure) }, + { be_const_key(pixel_size, -1), be_const_closure(class_Leds_segment_pixel_size_closure) }, + { be_const_key(init, -1), be_const_closure(class_Leds_segment_init_closure) }, + { be_const_key(clear, 2), be_const_closure(class_Leds_segment_clear_closure) }, + })), + (bstring*) &be_const_str_Leds_segment +); +// compact class 'Leds' ktab size: 33, total: 65 (saved 256 bytes) +static const bvalue be_ktab_class_Leds[33] = { + /* K0 */ be_nested_str(call_native), + /* K1 */ be_nested_str(bri), + /* K2 */ be_const_class(be_class_Leds), + /* K3 */ be_nested_str(Leds), + /* K4 */ be_nested_str(create_matrix), + /* K5 */ be_const_int(0), + /* K6 */ be_const_int(3), + /* K7 */ be_nested_str(gamma), + /* K8 */ be_const_int(2), + /* K9 */ be_nested_str(WS2812_GRB), + /* K10 */ be_nested_str(leds), + /* K11 */ be_nested_str(value_error), + /* K12 */ be_nested_str(out_X20of_X20range), + /* K13 */ be_const_class(be_class_Leds_matrix), + /* K14 */ be_nested_str(to_gamma), + /* K15 */ be_const_class(be_class_Leds_segment), + /* K16 */ be_nested_str(gpio), + /* K17 */ be_nested_str(pin), + /* K18 */ be_nested_str(WS2812), + /* K19 */ be_nested_str(ctor), + /* K20 */ be_nested_str(pixel_count), + /* K21 */ be_nested_str(light), + /* K22 */ be_nested_str(get), + /* K23 */ be_nested_str(_p), + /* K24 */ be_nested_str(internal_error), + /* K25 */ be_nested_str(couldn_X27t_X20not_X20initialize_X20noepixelbus), + /* K26 */ be_nested_str(begin), + /* K27 */ be_nested_str(clear_to), + /* K28 */ be_nested_str(show), + /* K29 */ be_const_int(1), + /* K30 */ be_nested_str(apply_bri_gamma), + /* K31 */ be_nested_str(pixel_size), + /* K32 */ be_nested_str(_change_buffer), +}; + + +extern const bclass be_class_Leds; + +/******************************************************************** +** Solidified function: is_dirty +********************************************************************/ +be_local_closure(class_Leds_is_dirty, /* name */ be_nested_proto( 4, /* nstack */ 1, /* argc */ @@ -1435,11 +1165,11 @@ 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_is_dirty, &be_const_str_solidified, ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 - 0x540E0007, // 0001 LDINT R3 8 + 0x8C040100, // 0000 GETMET R1 R0 K0 + 0x540E0003, // 0001 LDINT R3 4 0x7C040400, // 0002 CALL R1 2 0x80040200, // 0003 RET 1 R1 }) @@ -1465,7 +1195,7 @@ be_local_closure(class_Leds_get_bri, /* name */ &be_const_str_get_bri, &be_const_str_solidified, ( &(const binstruction[ 2]) { /* code */ - 0x88040105, // 0000 GETMBR R1 R0 K5 + 0x88040101, // 0000 GETMBR R1 R0 K1 0x80040200, // 0001 RET 1 R1 }) ) @@ -1473,6 +1203,100 @@ be_local_closure(class_Leds_get_bri, /* name */ /*******************************************************************/ +/******************************************************************** +** Solidified function: matrix +********************************************************************/ +be_local_closure(class_Leds_matrix, /* name */ + be_nested_proto( + 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_matrix, + &be_const_str_solidified, + ( &(const binstruction[12]) { /* code */ + 0x58100002, // 0000 LDCONST R4 K2 + 0xB8160600, // 0001 GETNGBL R5 K3 + 0x08180001, // 0002 MUL R6 R0 R1 + 0x5C1C0400, // 0003 MOVE R7 R2 + 0x5C200600, // 0004 MOVE R8 R3 + 0x7C140600, // 0005 CALL R5 3 + 0x8C180B04, // 0006 GETMET R6 R5 K4 + 0x5C200000, // 0007 MOVE R8 R0 + 0x5C240200, // 0008 MOVE R9 R1 + 0x58280005, // 0009 LDCONST R10 K5 + 0x7C180800, // 000A CALL R6 4 + 0x80040C00, // 000B RET 1 R6 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: can_show +********************************************************************/ +be_local_closure(class_Leds_can_show, /* name */ + be_nested_proto( + 4, /* nstack */ + 1, /* 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_can_show, + &be_const_str_solidified, + ( &(const binstruction[ 4]) { /* code */ + 0x8C040100, // 0000 GETMET R1 R0 K0 + 0x580C0006, // 0001 LDCONST R3 K6 + 0x7C040400, // 0002 CALL R1 2 + 0x80040200, // 0003 RET 1 R1 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** 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 */ + 0x14080305, // 0000 LT R2 R1 K5 + 0x780A0000, // 0001 JMPF R2 #0003 + 0x58040005, // 0002 LDCONST R1 K5 + 0x540A00FE, // 0003 LDINT R2 255 + 0x24080202, // 0004 GT R2 R1 R2 + 0x780A0000, // 0005 JMPF R2 #0007 + 0x540600FE, // 0006 LDINT R1 255 + 0x90020201, // 0007 SETMBR R0 K1 R1 + 0x80000000, // 0008 RET 0 + }) + ) +); +/*******************************************************************/ + + /******************************************************************** ** Solidified function: set_gamma ********************************************************************/ @@ -1493,7 +1317,7 @@ be_local_closure(class_Leds_set_gamma, /* name */ 0x60080017, // 0000 GETGBL R2 G23 0x5C0C0200, // 0001 MOVE R3 R1 0x7C080200, // 0002 CALL R2 1 - 0x90021602, // 0003 SETMBR R0 K11 R2 + 0x90020E02, // 0003 SETMBR R0 K7 R2 0x80000000, // 0004 RET 0 }) ) @@ -1502,37 +1326,9 @@ be_local_closure(class_Leds_set_gamma, /* name */ /******************************************************************** -** Solidified function: get_pixel_color +** Solidified function: show ********************************************************************/ -be_local_closure(class_Leds_get_pixel_color, /* name */ - be_nested_proto( - 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 */ - 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 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: dirty -********************************************************************/ -be_local_closure(class_Leds_dirty, /* name */ +be_local_closure(class_Leds_show, /* name */ be_nested_proto( 4, /* nstack */ 1, /* argc */ @@ -1543,11 +1339,11 @@ 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_show, &be_const_str_solidified, ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 - 0x540E0004, // 0001 LDINT R3 5 + 0x8C040100, // 0000 GETMET R1 R0 K0 + 0x580C0008, // 0001 LDCONST R3 K8 0x7C040400, // 0002 CALL R1 2 0x80000000, // 0003 RET 0 }) @@ -1557,106 +1353,12 @@ be_local_closure(class_Leds_dirty, /* name */ /******************************************************************** -** Solidified function: matrix +** Solidified function: ctor ********************************************************************/ -be_local_closure(class_Leds_matrix, /* name */ - be_nested_proto( - 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_matrix, - &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 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: pixel_offset -********************************************************************/ -be_local_closure(class_Leds_pixel_offset, /* name */ - be_nested_proto( - 1, /* nstack */ - 1, /* 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_pixel_offset, - &be_const_str_solidified, - ( &(const binstruction[ 1]) { /* code */ - 0x80060200, // 0000 RET 1 K1 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: clear_to -********************************************************************/ -be_local_closure(class_Leds_clear_to, /* name */ - be_nested_proto( - 10, /* 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_clear_to, - &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 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: set_pixel_color -********************************************************************/ -be_local_closure(class_Leds_set_pixel_color, /* name */ +be_local_closure(class_Leds_ctor, /* name */ be_nested_proto( 12, /* nstack */ - 4, /* argc */ + 5, /* argc */ 10, /* varg */ 0, /* has upvals */ NULL, /* no upvals */ @@ -1664,22 +1366,28 @@ 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_ctor, &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[19]) { /* code */ + 0x4C140000, // 0000 LDNIL R5 + 0x1C140405, // 0001 EQ R5 R2 R5 + 0x78160003, // 0002 JMPF R5 #0007 + 0x8C140100, // 0003 GETMET R5 R0 K0 + 0x581C0005, // 0004 LDCONST R7 K5 + 0x7C140400, // 0005 CALL R5 2 + 0x7002000A, // 0006 JMP #0012 + 0x4C140000, // 0007 LDNIL R5 + 0x1C140605, // 0008 EQ R5 R3 R5 + 0x78160000, // 0009 JMPF R5 #000B + 0x880C0109, // 000A GETMBR R3 R0 K9 + 0x8C140100, // 000B GETMET R5 R0 K0 + 0x581C0005, // 000C LDCONST R7 K5 + 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 }) ) ); @@ -1703,7 +1411,7 @@ be_local_closure(class_Leds_pixel_size, /* name */ &be_const_str_pixel_size, &be_const_str_solidified, ( &(const binstruction[ 4]) { /* code */ - 0x8C040106, // 0000 GETMET R1 R0 K6 + 0x8C040100, // 0000 GETMET R1 R0 K0 0x540E0006, // 0001 LDINT R3 7 0x7C040400, // 0002 CALL R1 2 0x80040200, // 0003 RET 1 R1 @@ -1713,6 +1421,110 @@ be_local_closure(class_Leds_pixel_size, /* name */ /*******************************************************************/ +/******************************************************************** +** Solidified function: dirty +********************************************************************/ +be_local_closure(class_Leds_dirty, /* name */ + be_nested_proto( + 4, /* nstack */ + 1, /* 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_dirty, + &be_const_str_solidified, + ( &(const binstruction[ 4]) { /* code */ + 0x8C040100, // 0000 GETMET R1 R0 K0 + 0x540E0004, // 0001 LDINT R3 5 + 0x7C040400, // 0002 CALL R1 2 + 0x80000000, // 0003 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: pixel_offset +********************************************************************/ +be_local_closure(class_Leds_pixel_offset, /* name */ + be_nested_proto( + 1, /* nstack */ + 1, /* 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_pixel_offset, + &be_const_str_solidified, + ( &(const binstruction[ 1]) { /* code */ + 0x80060A00, // 0000 RET 1 K5 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: get_pixel_color +********************************************************************/ +be_local_closure(class_Leds_get_pixel_color, /* name */ + be_nested_proto( + 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 */ + 0x8C080100, // 0000 GETMET R2 R0 K0 + 0x5412000A, // 0001 LDINT R4 11 + 0x5C140200, // 0002 MOVE R5 R1 + 0x7C080600, // 0003 CALL R2 3 + 0x80040400, // 0004 RET 1 R2 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: get_gamma +********************************************************************/ +be_local_closure(class_Leds_get_gamma, /* name */ + be_nested_proto( + 2, /* nstack */ + 1, /* 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_gamma, + &be_const_str_solidified, + ( &(const binstruction[ 2]) { /* code */ + 0x88040107, // 0000 GETMBR R1 R0 K7 + 0x80040200, // 0001 RET 1 R1 + }) + ) +); +/*******************************************************************/ + + /******************************************************************** ** Solidified function: create_matrix ********************************************************************/ @@ -1745,21 +1557,21 @@ be_local_closure(class_Leds_create_matrix, /* name */ 0x4C100000, // 000C LDNIL R4 0x1C100604, // 000D EQ R4 R3 R4 0x78120000, // 000E JMPF R4 #0010 - 0x580C0001, // 000F LDCONST R3 K1 + 0x580C0005, // 000F LDCONST R3 K5 0x08100202, // 0010 MUL R4 R1 R2 0x00100803, // 0011 ADD R4 R4 R3 - 0x88140100, // 0012 GETMBR R5 R0 K0 + 0x8814010A, // 0012 GETMBR R5 R0 K10 0x24100805, // 0013 GT R4 R4 R5 0x74120005, // 0014 JMPT R4 #001B - 0x14100501, // 0015 LT R4 R2 K1 + 0x14100505, // 0015 LT R4 R2 K5 0x74120003, // 0016 JMPT R4 #001B - 0x14100301, // 0017 LT R4 R1 K1 + 0x14100305, // 0017 LT R4 R1 K5 0x74120001, // 0018 JMPT R4 #001B - 0x14100701, // 0019 LT R4 R3 K1 + 0x14100705, // 0019 LT R4 R3 K5 0x78120000, // 001A JMPF R4 #001C - 0xB0060503, // 001B RAISE 1 K2 K3 - 0x5810001C, // 001C LDCONST R4 K28 - 0xB400001C, // 001D CLASS K28 + 0xB006170C, // 001B RAISE 1 K11 K12 + 0x5810000D, // 001C LDCONST R4 K13 + 0xB400000D, // 001D CLASS K13 0x5C140800, // 001E MOVE R5 R4 0x5C180000, // 001F MOVE R6 R0 0x5C1C0200, // 0020 MOVE R7 R1 @@ -1774,183 +1586,9 @@ be_local_closure(class_Leds_create_matrix, /* name */ /******************************************************************** -** Solidified function: get_gamma +** Solidified function: clear_to ********************************************************************/ -be_local_closure(class_Leds_get_gamma, /* name */ - be_nested_proto( - 2, /* nstack */ - 1, /* 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_gamma, - &be_const_str_solidified, - ( &(const binstruction[ 2]) { /* code */ - 0x8804010B, // 0000 GETMBR R1 R0 K11 - 0x80040200, // 0001 RET 1 R1 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: is_dirty -********************************************************************/ -be_local_closure(class_Leds_is_dirty, /* name */ - be_nested_proto( - 4, /* nstack */ - 1, /* 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_is_dirty, - &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 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: can_show -********************************************************************/ -be_local_closure(class_Leds_can_show, /* name */ - be_nested_proto( - 4, /* nstack */ - 1, /* 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_can_show, - &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 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: assign_rmt -********************************************************************/ -be_local_closure(class_Leds_assign_rmt, /* name */ - be_nested_proto( - 9, /* nstack */ - 1, /* 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_assign_rmt, - &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 - }) - ) -); -/*******************************************************************/ - - -/******************************************************************** -** Solidified function: ctor -********************************************************************/ -be_local_closure(class_Leds_ctor, /* name */ +be_local_closure(class_Leds_clear_to, /* name */ be_nested_proto( 12, /* nstack */ 5, /* argc */ @@ -1961,35 +1599,301 @@ be_local_closure(class_Leds_ctor, /* name */ NULL, /* no sub protos */ 1, /* has constants */ &be_ktab_class_Leds, /* shared constants */ - &be_const_str_ctor, + &be_const_str_clear_to, &be_const_str_solidified, - ( &(const binstruction[26]) { /* code */ + ( &(const binstruction[28]) { /* code */ 0x4C140000, // 0000 LDNIL R5 0x1C140405, // 0001 EQ R5 R2 R5 - 0x78160003, // 0002 JMPF R5 #0007 - 0x8C140106, // 0003 GETMET R5 R0 K6 - 0x581C0001, // 0004 LDCONST R7 K1 - 0x7C140400, // 0005 CALL R5 2 - 0x70020011, // 0006 JMP #0019 + 0x78160000, // 0002 JMPF R5 #0004 + 0x88080101, // 0003 GETMBR R2 R0 K1 + 0x4C140000, // 0004 LDNIL R5 + 0x20140605, // 0005 NE R5 R3 R5 + 0x7816000C, // 0006 JMPF R5 #0014 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 + 0x20140805, // 0008 NE R5 R4 R5 + 0x78160009, // 0009 JMPF R5 #0014 + 0x8C140100, // 000A GETMET R5 R0 K0 + 0x541E0008, // 000B LDINT R7 9 + 0x8C20010E, // 000C GETMET R8 R0 K14 + 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 + 0x8C140100, // 0014 GETMET R5 R0 K0 + 0x541E0008, // 0015 LDINT R7 9 + 0x8C20010E, // 0016 GETMET R8 R0 K14 + 0x5C280200, // 0017 MOVE R10 R1 + 0x5C2C0400, // 0018 MOVE R11 R2 + 0x7C200600, // 0019 CALL R8 3 + 0x7C140600, // 001A CALL R5 3 + 0x80000000, // 001B RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: create_segment +********************************************************************/ +be_local_closure(class_Leds_create_segment, /* name */ + be_nested_proto( + 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_create_segment, + &be_const_str_solidified, + ( &(const binstruction[23]) { /* code */ + 0x600C0009, // 0000 GETGBL R3 G9 + 0x5C100200, // 0001 MOVE R4 R1 + 0x7C0C0200, // 0002 CALL R3 1 + 0x60100009, // 0003 GETGBL R4 G9 + 0x5C140400, // 0004 MOVE R5 R2 + 0x7C100200, // 0005 CALL R4 1 + 0x000C0604, // 0006 ADD R3 R3 R4 + 0x8810010A, // 0007 GETMBR R4 R0 K10 + 0x240C0604, // 0008 GT R3 R3 R4 + 0x740E0003, // 0009 JMPT R3 #000E + 0x140C0305, // 000A LT R3 R1 K5 + 0x740E0001, // 000B JMPT R3 #000E + 0x140C0505, // 000C LT R3 R2 K5 + 0x780E0000, // 000D JMPF R3 #000F + 0xB006170C, // 000E RAISE 1 K11 K12 + 0x580C000F, // 000F LDCONST R3 K15 + 0xB400000F, // 0010 CLASS K15 + 0x5C100600, // 0011 MOVE R4 R3 + 0x5C140000, // 0012 MOVE R5 R0 + 0x5C180200, // 0013 MOVE R6 R1 + 0x5C1C0400, // 0014 MOVE R7 R2 + 0x7C100600, // 0015 CALL R4 3 + 0x80040800, // 0016 RET 1 R4 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: set_pixel_color +********************************************************************/ +be_local_closure(class_Leds_set_pixel_color, /* name */ + be_nested_proto( + 12, /* nstack */ + 4, /* 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_pixel_color, + &be_const_str_solidified, + ( &(const binstruction[13]) { /* code */ + 0x4C100000, // 0000 LDNIL R4 + 0x1C100604, // 0001 EQ R4 R3 R4 + 0x78120000, // 0002 JMPF R4 #0004 + 0x880C0101, // 0003 GETMBR R3 R0 K1 + 0x8C100100, // 0004 GETMET R4 R0 K0 + 0x541A0009, // 0005 LDINT R6 10 + 0x5C1C0200, // 0006 MOVE R7 R1 + 0x8C20010E, // 0007 GETMET R8 R0 K14 + 0x5C280400, // 0008 MOVE R10 R2 + 0x5C2C0600, // 0009 MOVE R11 R3 + 0x7C200600, // 000A CALL R8 3 + 0x7C100800, // 000B CALL R4 4 + 0x80000000, // 000C RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** 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_init, + &be_const_str_solidified, + ( &(const binstruction[43]) { /* code */ + 0xA4162000, // 0000 IMPORT R5 K16 + 0x50180200, // 0001 LDBOOL R6 1 0 + 0x90020E06, // 0002 SETMBR R0 K7 R6 + 0x4C180000, // 0003 LDNIL R6 + 0x1C180406, // 0004 EQ R6 R2 R6 + 0x741A0005, // 0005 JMPT R6 #000C + 0x8C180B11, // 0006 GETMET R6 R5 K17 + 0x88200B12, // 0007 GETMBR R8 R5 K18 + 0x58240005, // 0008 LDCONST R9 K5 + 0x7C180600, // 0009 CALL R6 3 + 0x1C180406, // 000A EQ R6 R2 R6 + 0x781A000A, // 000B JMPF R6 #0017 + 0x8C180113, // 000C GETMET R6 R0 K19 + 0x7C180200, // 000D CALL R6 1 + 0x8C180114, // 000E GETMET R6 R0 K20 + 0x7C180200, // 000F CALL R6 1 + 0x90021406, // 0010 SETMBR R0 K10 R6 + 0xA41A2A00, // 0011 IMPORT R6 K21 + 0x8C1C0D16, // 0012 GETMET R7 R6 K22 + 0x7C1C0200, // 0013 CALL R7 1 + 0x941C0F01, // 0014 GETIDX R7 R7 K1 + 0x90020207, // 0015 SETMBR R0 K1 R7 + 0x7002000B, // 0016 JMP #0023 + 0x60180009, // 0017 GETGBL R6 G9 + 0x5C1C0200, // 0018 MOVE R7 R1 + 0x7C180200, // 0019 CALL R6 1 + 0x90021406, // 001A SETMBR R0 K10 R6 + 0x541A007E, // 001B LDINT R6 127 + 0x90020206, // 001C SETMBR R0 K1 R6 + 0x8C180113, // 001D GETMET R6 R0 K19 + 0x8820010A, // 001E GETMBR R8 R0 K10 + 0x5C240400, // 001F MOVE R9 R2 + 0x5C280600, // 0020 MOVE R10 R3 + 0x5C2C0800, // 0021 MOVE R11 R4 + 0x7C180A00, // 0022 CALL R6 5 + 0x88180117, // 0023 GETMBR R6 R0 K23 + 0x4C1C0000, // 0024 LDNIL R7 + 0x1C180C07, // 0025 EQ R6 R6 R7 + 0x781A0000, // 0026 JMPF R6 #0028 + 0xB0063119, // 0027 RAISE 1 K24 K25 + 0x8C18011A, // 0028 GETMET R6 R0 K26 + 0x7C180200, // 0029 CALL R6 1 + 0x80000000, // 002A RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: clear +********************************************************************/ +be_local_closure(class_Leds_clear, /* name */ + be_nested_proto( + 4, /* nstack */ + 1, /* 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_clear, + &be_const_str_solidified, + ( &(const binstruction[ 6]) { /* code */ + 0x8C04011B, // 0000 GETMET R1 R0 K27 + 0x580C0005, // 0001 LDCONST R3 K5 + 0x7C040400, // 0002 CALL R1 2 + 0x8C04011C, // 0003 GETMET R1 R0 K28 + 0x7C040200, // 0004 CALL R1 1 + 0x80000000, // 0005 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: begin +********************************************************************/ +be_local_closure(class_Leds_begin, /* name */ + be_nested_proto( + 4, /* nstack */ + 1, /* 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_begin, + &be_const_str_solidified, + ( &(const binstruction[ 4]) { /* code */ + 0x8C040100, // 0000 GETMET R1 R0 K0 + 0x580C001D, // 0001 LDCONST R3 K29 + 0x7C040400, // 0002 CALL R1 2 + 0x80000000, // 0003 RET 0 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: pixel_count +********************************************************************/ +be_local_closure(class_Leds_pixel_count, /* name */ + be_nested_proto( + 4, /* nstack */ + 1, /* 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_pixel_count, + &be_const_str_solidified, + ( &(const binstruction[ 4]) { /* code */ + 0x8C040100, // 0000 GETMET R1 R0 K0 + 0x540E0007, // 0001 LDINT R3 8 + 0x7C040400, // 0002 CALL R1 2 + 0x80040200, // 0003 RET 1 R1 + }) + ) +); +/*******************************************************************/ + + +/******************************************************************** +** Solidified function: to_gamma +********************************************************************/ +be_local_closure(class_Leds_to_gamma, /* name */ + be_nested_proto( + 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_to_gamma, + &be_const_str_solidified, + ( &(const binstruction[10]) { /* code */ + 0x4C0C0000, // 0000 LDNIL R3 + 0x1C0C0403, // 0001 EQ R3 R2 R3 + 0x780E0000, // 0002 JMPF R3 #0004 + 0x88080101, // 0003 GETMBR R2 R0 K1 + 0x8C0C011E, // 0004 GETMET R3 R0 K30 + 0x5C140200, // 0005 MOVE R5 R1 + 0x5C180400, // 0006 MOVE R6 R2 + 0x881C0107, // 0007 GETMBR R7 R0 K7 + 0x7C0C0800, // 0008 CALL R3 4 + 0x80040600, // 0009 RET 1 R3 }) ) ); @@ -2013,7 +1917,7 @@ be_local_closure(class_Leds_pixels_buffer, /* name */ &be_const_str_pixels_buffer, &be_const_str_solidified, ( &(const binstruction[21]) { /* code */ - 0x8C080106, // 0000 GETMET R2 R0 K6 + 0x8C080100, // 0000 GETMET R2 R0 K0 0x54120005, // 0001 LDINT R4 6 0x7C080400, // 0002 CALL R2 2 0x4C0C0000, // 0003 LDNIL R3 @@ -2021,15 +1925,15 @@ be_local_closure(class_Leds_pixels_buffer, /* name */ 0x780E0009, // 0005 JMPF R3 #0010 0x600C0015, // 0006 GETGBL R3 G21 0x5C100400, // 0007 MOVE R4 R2 - 0x8C140129, // 0008 GETMET R5 R0 K41 + 0x8C14011F, // 0008 GETMET R5 R0 K31 0x7C140200, // 0009 CALL R5 1 - 0x8C18010F, // 000A GETMET R6 R0 K15 + 0x8C180114, // 000A GETMET R6 R0 K20 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 + 0x8C0C0320, // 0010 GETMET R3 R1 K32 0x5C140400, // 0011 MOVE R5 R2 0x7C0C0400, // 0012 CALL R3 2 0x80040200, // 0013 RET 1 R1 @@ -2047,35 +1951,34 @@ extern const bclass be_class_Leds_ntv; be_local_class(Leds, 3, &be_class_Leds_ntv, - be_nested_map(27, + be_nested_map(26, ( (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(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(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(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(to_gamma, -1), be_const_closure(class_Leds_to_gamma_closure) }, + { be_const_key(can_show, -1), be_const_closure(class_Leds_can_show_closure) }, + { be_const_key(pixel_offset, -1), be_const_closure(class_Leds_pixel_offset_closure) }, + { be_const_key(set_bri, 24), be_const_closure(class_Leds_set_bri_closure) }, + { be_const_key(show, 2), be_const_closure(class_Leds_show_closure) }, + { be_const_key(ctor, -1), be_const_closure(class_Leds_ctor_closure) }, + { be_const_key(begin, 22), be_const_closure(class_Leds_begin_closure) }, + { be_const_key(leds, -1), be_const_var(1) }, + { be_const_key(set_pixel_color, -1), be_const_closure(class_Leds_set_pixel_color_closure) }, + { be_const_key(dirty, 4), be_const_closure(class_Leds_dirty_closure) }, + { be_const_key(get_pixel_color, -1), be_const_closure(class_Leds_get_pixel_color_closure) }, + { be_const_key(get_gamma, -1), be_const_closure(class_Leds_get_gamma_closure) }, + { be_const_key(gamma, -1), be_const_var(0) }, + { be_const_key(create_matrix, -1), be_const_closure(class_Leds_create_matrix_closure) }, + { be_const_key(matrix, 8), be_const_static_closure(class_Leds_matrix_closure) }, + { be_const_key(create_segment, -1), be_const_closure(class_Leds_create_segment_closure) }, + { be_const_key(bri, 10), be_const_var(2) }, + { be_const_key(init, -1), be_const_closure(class_Leds_init_closure) }, + { be_const_key(clear, -1), be_const_closure(class_Leds_clear_closure) }, + { be_const_key(get_bri, 9), be_const_closure(class_Leds_get_bri_closure) }, + { be_const_key(clear_to, -1), be_const_closure(class_Leds_clear_to_closure) }, + { be_const_key(pixel_count, -1), be_const_closure(class_Leds_pixel_count_closure) }, + { be_const_key(set_gamma, -1), be_const_closure(class_Leds_set_gamma_closure) }, + { be_const_key(pixel_size, 1), be_const_closure(class_Leds_pixel_size_closure) }, })), (bstring*) &be_const_str_Leds ); diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 5af1f9aaa..b2873e8df 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -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 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino b/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino index d0d3b61ff..142044766 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_52_3_berry_leds.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino index 0984cb14b..a06890b09 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,12 +72,12 @@ 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"); + be_raise(vm, "internal_error", "tasmotaled object not initialized"); } return strip; } @@ -99,70 +85,65 @@ extern "C" { be_getmember(vm, 1, "_t"); int32_t type = be_toint(vm, -1); be_pop(vm, 1); - if (type < 0 || type >= neopixel_type_end) { + if (type < 0) { 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 + // strip = Ws2812GetStrip(); TODO } 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); } + // AddLog(LOG_LEVEL_DEBUG, "LED: leds %i gpio %i type %i", leds, gpio, led_type); // store type in attribute `_t` - be_pushint(vm, neopixel_type); + be_pushint(vm, led_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 + // all other commands need a valid tasmotaled 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; + TasmotaLED * strip = (TasmotaLED*) be_get_tasmotaled(vm); // raises an exception if pointer is invalid // detect native driver bool native = (leds_type == 0); @@ -171,8 +152,7 @@ extern "C" { 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(); + else if (strip) strip->Begin(); break; case 2: // # 02 : show void -> void { @@ -195,49 +175,50 @@ 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(); } + bool update_completed = false; + if (native) { + Ws2812Show(); + pixels_size = Ws2812PixelsSize(); + update_completed =Ws2812CanShow(); + } + else if (strip) { + strip->Show(); + pixels_size = strip->PixelCount() * strip->PixelSize(); + update_completed = strip->CanShow(); + } // Wait for RMT/I2S to complete fixes distortion due to analogRead // 1ms is needed for 96 bytes - SystemBusyDelay((pixels_size + 95) / 96); + if (!update_completed) { + SystemBusyDelay((pixels_size + 95) / 96); + } } 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()); + else if (strip) be_pushbool(vm, strip->CanShow()); 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()); + else if (strip) be_pushbool(vm, strip->IsDirty()); 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(); + else if (strip) strip->Dirty(); 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 (native) be_pushcomptr(vm, Ws2812Pixels()); + else if (strip) be_pushcomptr(vm, strip->Pixels()); } 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()); + else if (strip) be_pushint(vm, strip->PixelSize()); 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()); + else if (strip) be_pushint(vm, strip->PixelCount()); break; case 9: // # 09 : ClearTo (color:??) -> void { @@ -253,42 +234,35 @@ extern "C" { 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); + else if (strip) strip->ClearTo(rgbw, 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)); + else if (strip) strip->ClearTo(rgbw); } } 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 (native) { + 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); + } else if (strip) { + strip->SetPixelColor(idx, wrgb); + } } 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) { 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); + } else if (strip) { + be_pushint(vm, strip->GetPixelColor(idx)); } } break; diff --git a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino index b05d15e8a..9be3d3117 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 000000000..5ff5d94dd --- /dev/null +++ b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino @@ -0,0 +1,899 @@ +/* + 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 +#if CONFIG_IDF_TARGET_ESP32C2 + const uint32_t kTasLed_Hardware = TasmotaLed_I2S; // I2S +#else // all other ESP32 variants + #if defined(USE_WS2812_DMA) + const uint32_t kTasLed_Hardware = TasmotaLed_RMT; // default DMA to RMT + #elif defined(USE_WS2812_RMT) + const uint32_t kTasLed_Hardware = TasmotaLed_RMT; // default DMA to RMT + #elif defined(USE_WS2812_I2S) + const uint32_t kTasLed_Hardware = TasmotaLed_I2S; // I2S + #else + const uint32_t kTasLed_Hardware = TasmotaLed_RMT; // default DMA to RMT + #endif +#endif + +#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(); + +#if defined(USE_WS2812_DMA) || defined(USE_WS2812_RMT) || defined(USE_WS2812_I2S) + // Wait for DMA/RMT/I2S to complete fixes distortion due to analogRead +// delay((Settings->light_pixels >> 6) +1); // 256 / 64 = 4 +1 = 5 + SystemBusyDelay( (Settings->light_pixels + 31) >> 5); // (256 + 32) / 32 = 8 +#endif +} + +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