1496 lines
41 KiB
Markdown
1496 lines
41 KiB
Markdown
# TasmotaLED Library Documentation
|
||
|
||
## Executive Summary
|
||
|
||
TasmotaLED is a lightweight, high-performance library for controlling addressable LED strips on ESP32 microcontrollers. It serves as a streamlined replacement for the NeoPixelBus library, focusing on efficient pixel pushing with minimal memory overhead while supporting multiple hardware acceleration methods (RMT, SPI, I2S).
|
||
|
||
**Key Features:**
|
||
- Hardware-accelerated LED control via RMT (preferred), SPI, or I2S
|
||
- Support for WS2812 and SK6812 LED strips
|
||
- 3-byte (RGB) and 4-byte (RGBW) pixel formats
|
||
- Flexible pixel ordering (GRB, RGB, RBG, BRG, BGR, GBR)
|
||
- Zero-copy buffer management for optimal performance
|
||
- ESP32 platform exclusive (ESP8266 not supported)
|
||
|
||
**Version:** 1.0
|
||
**Author:** Stephan Hadinger
|
||
**License:** GNU General Public License v3.0
|
||
**Copyright:** 2024
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
1. [Architecture Overview](#architecture-overview)
|
||
2. [Class Hierarchy](#class-hierarchy)
|
||
3. [LED Type Encoding System](#led-type-encoding-system)
|
||
4. [Hardware Acceleration](#hardware-acceleration)
|
||
5. [API Reference](#api-reference)
|
||
6. [Configuration Guide](#configuration-guide)
|
||
7. [Performance Characteristics](#performance-characteristics)
|
||
8. [Usage Examples](#usage-examples)
|
||
9. [Integration with Tasmota](#integration-with-tasmota)
|
||
10. [Troubleshooting](#troubleshooting)
|
||
|
||
---
|
||
|
||
## Architecture Overview
|
||
|
||
### Design Philosophy
|
||
|
||
TasmotaLED is designed with the following principles:
|
||
|
||
1. **Minimal Memory Footprint**: Uses only two buffers (work and show) with no buffer swapping overhead
|
||
2. **Hardware Acceleration First**: Leverages ESP32's RMT, SPI, or I2S peripherals for precise timing
|
||
3. **Zero-Copy Operations**: Direct buffer manipulation without intermediate copies
|
||
4. **NeoPixelBus Compatibility**: Similar API for easy migration from NeoPixelBus
|
||
5. **Compile-Time Optimization**: Hardware support can be selectively enabled/disabled
|
||
|
||
### System Architecture
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Application Layer │
|
||
│ (Tasmota Light Driver) │
|
||
└────────────────────┬────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ TasmotaLED Class │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ _buf_work │ │ _buf_show │ │ Pixel Format │ │
|
||
│ │ (editable) │─▶│ (internal) │ │ Conversion │ │
|
||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||
└────────────────────┬────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ TasmotaLEDPusher (Abstract) │
|
||
└────────────────────┬────────────────────────────────────────┘
|
||
│
|
||
┌────────────┼────────────┐
|
||
▼ ▼ ▼
|
||
┌──────────────┐ ┌──────────┐ ┌──────────┐
|
||
│ RMT Pusher │ │ SPI │ │ I2S │
|
||
│ (Primary) │ │ Pusher │ │ Pusher │
|
||
└──────┬───────┘ └────┬─────┘ └────┬─────┘
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌─────────────────────────────────────────┐
|
||
│ ESP32 Hardware Peripherals │
|
||
│ (RMT / SPI / I2S Controllers) │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
|
||
### Memory Management
|
||
|
||
TasmotaLED uses a dual-buffer system:
|
||
|
||
1. **Work Buffer (`_buf_work`)**:
|
||
- Directly accessible by application
|
||
- Stores pixels in WRGB or RGB format (0xWWRRGGBB or 0xRRGGBB)
|
||
- Can be modified at any time
|
||
- Size: `pixel_count × pixel_size` bytes
|
||
|
||
2. **Show Buffer (`_buf_show`)**:
|
||
- Internal buffer for hardware transmission
|
||
- Stores pixels in LED strip format (e.g., GRB, GRBW)
|
||
- Populated during `Show()` call with format conversion
|
||
- Size: `pixel_count × pixel_size` bytes
|
||
|
||
**Memory Allocation Example:**
|
||
- 512 RGB pixels: 2 × 512 × 3 = 3,072 bytes (~3 KB)
|
||
- 512 RGBW pixels: 2 × 512 × 4 = 4,096 bytes (~4 KB)
|
||
|
||
---
|
||
|
||
## Class Hierarchy
|
||
|
||
### Class Diagram
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ TasmotaLED │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ - _type: uint16_t │
|
||
│ - _pixel_order: uint8_t │
|
||
│ - _w_before: bool │
|
||
│ - _pixel_reverse: bool │
|
||
│ - _timing: uint8_t │
|
||
│ - _started: bool │
|
||
│ - _dirty: bool │
|
||
│ - _raw_format: bool │
|
||
│ - _pixel_count: uint16_t │
|
||
│ - _pixel_size: uint8_t │
|
||
│ - _buf_work: uint8_t* │
|
||
│ - _buf_show: uint8_t* │
|
||
│ - _pixel_matrix: const uint8_t(*)[3] │
|
||
│ - _pusher: TasmotaLEDPusher* │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ + TasmotaLED(type, num_leds) │
|
||
│ + ~TasmotaLED() │
|
||
│ + Begin(): bool │
|
||
│ + Show(): void │
|
||
│ + SetPixelColor(index, wrgb): void │
|
||
│ + GetPixelColor(index): uint32_t │
|
||
│ + ClearTo(rgbw, first, last): void │
|
||
│ + SetPixelCount(num_leds): void │
|
||
│ + SetPixelSubType(type): void │
|
||
│ + SetPusher(pusher): void │
|
||
│ + CanShow(): bool │
|
||
│ + Pixels(): uint8_t* │
|
||
│ + PixelCount(): uint16_t │
|
||
│ + PixelSize(): uint8_t │
|
||
│ + GetType(): uint8_t │
|
||
│ + IsDirty(): bool │
|
||
│ + Dirty(): void │
|
||
│ + SetPixelReverse(bool): void │
|
||
│ + SetRawFormat(raw): void │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
│ uses
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ TasmotaLEDPusher (Abstract) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ # _initialized: bool │
|
||
│ # _err: esp_err_t │
|
||
│ # _pixel_count: uint16_t │
|
||
│ # _pixel_size: uint16_t │
|
||
│ # _led_timing: const TasmotaLED_Timing* │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ + Begin(pixel_count, pixel_size, timing): bool │
|
||
│ + Push(buf): bool = 0 │
|
||
│ + CanShow(): bool = 0 │
|
||
│ + SetPixelCount(pixel_count): bool = 0 │
|
||
│ + Initialized(): bool │
|
||
│ + Error(): esp_err_t │
|
||
│ + static ResolveHardware(hw): uint32_t │
|
||
│ + static Create(hw, gpio): TasmotaLEDPusher* │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
△
|
||
│
|
||
┌─────────────────┼─────────────────┐
|
||
│ │ │
|
||
┌─────────┴──────────┐ ┌────┴─────────┐ ┌─────┴──────────┐
|
||
│ TasmotaLEDPusherRMT│ │TasmotaLED │ │ TasmotaLED │
|
||
│ │ │PusherSPI │ │PusherI2S │
|
||
├────────────────────┤ ├──────────────┤ ├────────────────┤
|
||
│ - _pin: int8_t │ │- _pin: int8_t│ │(Future) │
|
||
│ - _channel: handle │ │- _spi_strip │ │ │
|
||
│ - _led_encoder │ │- _with_dma │ │ │
|
||
│ - _tx_config │ │ │ │ │
|
||
├────────────────────┤ ├──────────────┤ ├────────────────┤
|
||
│ + Push(): bool │ │+ Push(): bool│ │ │
|
||
│ + CanShow(): bool │ │+ CanShow() │ │ │
|
||
└────────────────────┘ └──────────────┘ └────────────────┘
|
||
```
|
||
|
||
|
||
---
|
||
|
||
## LED Type Encoding System
|
||
|
||
### Type Encoding Structure
|
||
|
||
The LED type is encoded in a 16-bit value with the following bit layout:
|
||
|
||
```
|
||
Bits 15-8: Timing Code (WS2812, SK6812, etc.)
|
||
Bit 7: W Channel Position (0=after RGB, 1=before RGB)
|
||
Bits 6-4: Pixel Order (GRB, RGB, RBG, BRG, BGR, GBR)
|
||
Bits 3-0: Bytes per Pixel (3=RGB, 4=RGBW)
|
||
|
||
┌────────┬───┬───────┬──────────┐
|
||
│15 8 │ 7 │ 6 4 │ 3 0 │
|
||
├────────┼───┼───────┼──────────┤
|
||
│ Timing │ W │ Order │ Size │
|
||
└────────┴───┴───────┴──────────┘
|
||
```
|
||
|
||
### Pixel Size Encoding (Bits 0-3)
|
||
|
||
| Value | Enum | Description | Bytes per Pixel |
|
||
|-------|------|-------------|-----------------|
|
||
| `0x0` | `TasmotaLed_1_Def` | Default (same as RGB) | 3 |
|
||
| `0x1` | `TasmotaLed_3_RGB` | RGB format | 3 |
|
||
| `0x2` | `TasmotaLed_4_WRGB` | RGBW format | 4 |
|
||
|
||
### Pixel Order Encoding (Bits 4-6)
|
||
|
||
| Value | Enum | Description | Channel Order |
|
||
|-------|------|-------------|---------------|
|
||
| `0b000` | `TasmotaLed_Def` | Default (GRB) | G, R, B |
|
||
| `0b001` | `TasmotaLed_GRB` | Green-Red-Blue | G, R, B |
|
||
| `0b010` | `TasmotaLed_RGB` | Red-Green-Blue | R, G, B |
|
||
| `0b011` | `TasmotaLed_RBG` | Red-Blue-Green | R, B, G |
|
||
| `0b100` | `TasmotaLed_BRG` | Blue-Red-Green | B, R, G |
|
||
| `0b101` | `TasmotaLed_BGR` | Blue-Green-Red | B, G, R |
|
||
| `0b110` | `TasmotaLed_GBR` | Green-Blue-Red | G, B, R |
|
||
|
||
### W Channel Position (Bit 7)
|
||
|
||
| Value | Enum | Description | Format |
|
||
|-------|------|-------------|--------|
|
||
| `0` | `TasmotaLed_xxxW` | W after color | RGB + W |
|
||
| `1` | `TasmotaLed_Wxxx` | W before color | W + RGB |
|
||
|
||
### Timing Code (Bits 8-15)
|
||
|
||
| Value | Enum | Description | T0H | T0L | T1H | T1L | Reset |
|
||
|-------|------|-------------|-----|-----|-----|-----|-------|
|
||
| `0` | `TasmotaLed_WS2812` | WS2812/WS2812B | 400ns | 850ns | 800ns | 450ns | 80µs |
|
||
| `1` | `TasmotaLed_SK6812` | SK6812 | 300ns | 900ns | 600ns | 600ns | 80µs |
|
||
|
||
### Predefined LED Types
|
||
|
||
```cpp
|
||
enum TasmotaLEDTypes : uint16_t {
|
||
// WS2812 with GRB ordering (most common)
|
||
ws2812_grb = TasmotaLed_3_RGB | TasmotaLed_GRB | TasmotaLed_WS2812,
|
||
// Value: 0x0011 (decimal 17)
|
||
|
||
// SK6812 with GRBW ordering and W after RGB
|
||
sk6812_grbw = TasmotaLed_4_WRGB | TasmotaLed_GRB | TasmotaLed_xxxW | TasmotaLed_SK6812,
|
||
// Value: 0x0112 (decimal 274)
|
||
|
||
// SK6812 with GRB ordering (no white channel)
|
||
sk6812_grb = TasmotaLed_3_RGB | TasmotaLed_GRB | TasmotaLed_SK6812,
|
||
// Value: 0x0111 (decimal 273)
|
||
};
|
||
```
|
||
|
||
### Type Encoding Examples
|
||
|
||
```cpp
|
||
// WS2812 RGB strip with GRB ordering
|
||
uint16_t type1 = TasmotaLed_3_RGB | TasmotaLed_GRB | TasmotaLed_WS2812;
|
||
// Binary: 0000 0000 0001 0001 = 0x0011
|
||
|
||
// SK6812 RGBW strip with RGB ordering and W first
|
||
uint16_t type2 = TasmotaLed_4_WRGB | TasmotaLed_RGB | TasmotaLed_Wxxx | TasmotaLed_SK6812;
|
||
// Binary: 0000 0001 1010 0010 = 0x01A2
|
||
|
||
// WS2812 RGB strip with BGR ordering
|
||
uint16_t type3 = TasmotaLed_3_RGB | TasmotaLed_BGR | TasmotaLed_WS2812;
|
||
// Binary: 0000 0000 0101 0001 = 0x0051
|
||
```
|
||
|
||
|
||
---
|
||
|
||
## Hardware Acceleration
|
||
|
||
### Hardware Support Matrix
|
||
|
||
| Hardware | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 | ESP32-C2 | ESP32-C6 |
|
||
|----------|-------|----------|----------|----------|----------|----------|
|
||
| **RMT** | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
||
| **SPI** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||
| **I2S** | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
||
|
||
### Hardware Selection
|
||
|
||
Hardware acceleration is selected using the `TasmotaLEDHardware` enum:
|
||
|
||
```cpp
|
||
enum TasmotaLEDHardware : uint32_t {
|
||
TasmotaLed_HW_Default = 0x000000, // Auto-select best available
|
||
TasmotaLed_RMT = (1 << 0) << 16, // 0x010000 - RMT peripheral
|
||
TasmotaLed_SPI = (1 << 1) << 16, // 0x020000 - SPI peripheral
|
||
TasmotaLed_I2S = (1 << 2) << 16, // 0x040000 - I2S peripheral
|
||
TasmotaLed_HW_None = 0xFF << 16, // 0xFF0000 - No hardware support
|
||
};
|
||
```
|
||
|
||
### Hardware Resolution Priority
|
||
|
||
When `TasmotaLed_HW_Default` is specified, the library selects hardware in this order:
|
||
|
||
1. **RMT** (if available) - Preferred for precision and low CPU overhead
|
||
2. **I2S** (if available) - Good for large strips
|
||
3. **SPI** (if available) - Fallback option, works on all ESP32 variants
|
||
|
||
### Compile-Time Configuration
|
||
|
||
Hardware support can be enabled/disabled at compile time:
|
||
|
||
```cpp
|
||
// In your build configuration or before including TasmotaLED
|
||
#define TASMOTALED_HARDWARE_RMT 1 // Enable RMT (default: 1)
|
||
#define TASMOTALED_HARDWARE_I2S 0 // Enable I2S (default: 0)
|
||
#define TASMOTALED_HARDWARE_SPI 0 // Enable SPI (default: 0)
|
||
```
|
||
|
||
**Note:** If no hardware is explicitly enabled, SPI is automatically enabled as fallback.
|
||
|
||
### RMT Implementation
|
||
|
||
**Advantages:**
|
||
- Precise timing control (25ns resolution at 40MHz)
|
||
- Low CPU overhead
|
||
- Non-blocking operation with DMA
|
||
- Supports up to 8 channels (ESP32) or 4 channels (ESP32-S2/S3/C3)
|
||
|
||
**Technical Details:**
|
||
- Clock resolution: 40 MHz (25ns per tick)
|
||
- Memory block: 192 symbols (768 bytes)
|
||
- Transaction queue depth: 4
|
||
- Encoder: Custom LED strip encoder with bit-level control
|
||
|
||
**Timing Precision:**
|
||
```
|
||
WS2812: T0H=400ns (16 ticks), T0L=850ns (34 ticks)
|
||
T1H=800ns (32 ticks), T1L=450ns (18 ticks)
|
||
Reset=80µs (3200 ticks)
|
||
|
||
SK6812: T0H=300ns (12 ticks), T0L=900ns (36 ticks)
|
||
T1H=600ns (24 ticks), T1L=600ns (24 ticks)
|
||
Reset=80µs (3200 ticks)
|
||
```
|
||
|
||
### SPI Implementation
|
||
|
||
**Advantages:**
|
||
- Available on all ESP32 variants
|
||
- Works on ESP32-C2 (no RMT support)
|
||
- Reliable fallback option
|
||
|
||
**Technical Details:**
|
||
- Clock frequency: 2.5 MHz
|
||
- Each LED bit encoded as 3 SPI bits
|
||
- Bit encoding: `0` = `100`, `1` = `110`
|
||
- DMA support: Optional (auto-selected)
|
||
- Memory overhead: 3× pixel buffer size
|
||
|
||
**Bit Encoding:**
|
||
```
|
||
LED bit 0: SPI bits 100 (high for 400ns, low for 800ns at 2.5MHz)
|
||
LED bit 1: SPI bits 110 (high for 800ns, low for 400ns at 2.5MHz)
|
||
```
|
||
|
||
### I2S Implementation
|
||
|
||
**Status:** Planned but not yet implemented
|
||
|
||
**Advantages:**
|
||
- Very high throughput
|
||
- Excellent for large LED installations
|
||
- Parallel data transmission
|
||
|
||
---
|
||
|
||
## API Reference
|
||
|
||
### TasmotaLED Class
|
||
|
||
#### Constructor
|
||
|
||
```cpp
|
||
TasmotaLED(uint16_t type, uint16_t num_leds)
|
||
```
|
||
|
||
Creates a new TasmotaLED instance.
|
||
|
||
**Parameters:**
|
||
- `type`: LED type encoding (see LED Type Encoding section)
|
||
- `num_leds`: Number of LEDs in the strip
|
||
|
||
**Example:**
|
||
```cpp
|
||
// Create a 60-LED WS2812 strip with GRB ordering
|
||
TasmotaLED strip(ws2812_grb, 60);
|
||
|
||
// Create a 100-LED SK6812 RGBW strip
|
||
TasmotaLED strip(sk6812_grbw, 100);
|
||
```
|
||
|
||
#### Destructor
|
||
|
||
```cpp
|
||
~TasmotaLED()
|
||
```
|
||
|
||
Cleans up resources, frees buffers, and deletes the pusher instance.
|
||
|
||
#### Initialization Methods
|
||
|
||
##### SetPusher()
|
||
|
||
```cpp
|
||
void SetPusher(TasmotaLEDPusher *pusher)
|
||
```
|
||
|
||
Sets the hardware pusher implementation. Must be called before `Begin()`.
|
||
|
||
**Parameters:**
|
||
- `pusher`: Pointer to a TasmotaLEDPusher instance (RMT, SPI, or I2S)
|
||
|
||
**Example:**
|
||
```cpp
|
||
TasmotaLED strip(ws2812_grb, 60);
|
||
TasmotaLEDPusher *pusher = TasmotaLEDPusher::Create(TasmotaLed_RMT, 5);
|
||
strip.SetPusher(pusher);
|
||
```
|
||
|
||
##### Begin()
|
||
|
||
```cpp
|
||
bool Begin(void)
|
||
```
|
||
|
||
Initializes the hardware pusher and prepares the strip for operation.
|
||
|
||
**Returns:** `true` if initialization successful, `false` otherwise
|
||
|
||
**Example:**
|
||
```cpp
|
||
if (strip.Begin()) {
|
||
// Strip is ready to use
|
||
} else {
|
||
// Initialization failed
|
||
}
|
||
```
|
||
|
||
#### Pixel Manipulation Methods
|
||
|
||
##### SetPixelColor()
|
||
|
||
```cpp
|
||
void SetPixelColor(int32_t index, uint32_t wrgb)
|
||
```
|
||
|
||
Sets the color of a single pixel.
|
||
|
||
**Parameters:**
|
||
- `index`: Pixel index (0-based, negative values count from end)
|
||
- `wrgb`: Color value in 0xWWRRGGBB format (or 0xRRGGBB for RGB strips)
|
||
|
||
**Example:**
|
||
```cpp
|
||
strip.SetPixelColor(0, 0xFF0000); // Red
|
||
strip.SetPixelColor(1, 0x00FF00); // Green
|
||
strip.SetPixelColor(2, 0x0000FF); // Blue
|
||
strip.SetPixelColor(-1, 0xFFFFFF); // Last pixel white
|
||
strip.SetPixelColor(10, 0x80FF8000); // RGBW: W=0x80, R=0xFF, G=0x80, B=0x00
|
||
```
|
||
|
||
##### GetPixelColor()
|
||
|
||
```cpp
|
||
uint32_t GetPixelColor(int32_t index)
|
||
```
|
||
|
||
Retrieves the color of a single pixel.
|
||
|
||
**Parameters:**
|
||
- `index`: Pixel index (0-based, negative values count from end)
|
||
|
||
**Returns:** Color value in 0xWWRRGGBB or 0xRRGGBB format
|
||
|
||
**Example:**
|
||
```cpp
|
||
uint32_t color = strip.GetPixelColor(5);
|
||
uint8_t red = (color >> 16) & 0xFF;
|
||
uint8_t green = (color >> 8) & 0xFF;
|
||
uint8_t blue = color & 0xFF;
|
||
```
|
||
|
||
##### ClearTo()
|
||
|
||
```cpp
|
||
void ClearTo(uint32_t rgbw, int32_t first = 0, int32_t last = -1)
|
||
```
|
||
|
||
Sets a range of pixels to the same color.
|
||
|
||
**Parameters:**
|
||
- `rgbw`: Color value in 0xWWRRGGBB or 0xRRGGBB format
|
||
- `first`: First pixel index (default: 0)
|
||
- `last`: Last pixel index (default: -1 = last pixel)
|
||
|
||
**Example:**
|
||
```cpp
|
||
strip.ClearTo(0x000000); // Clear entire strip to black
|
||
strip.ClearTo(0xFF0000, 0, 9); // Set first 10 pixels to red
|
||
strip.ClearTo(0x00FF00, 10, 19); // Set pixels 10-19 to green
|
||
strip.ClearTo(0x0000FF, -10, -1); // Set last 10 pixels to blue
|
||
```
|
||
|
||
##### Show()
|
||
|
||
```cpp
|
||
void Show(void)
|
||
```
|
||
|
||
Pushes the pixel buffer to the LED strip. Converts from WRGB/RGB format to the strip's native format and transmits via hardware.
|
||
|
||
**Example:**
|
||
```cpp
|
||
strip.SetPixelColor(0, 0xFF0000);
|
||
strip.SetPixelColor(1, 0x00FF00);
|
||
strip.Show(); // Update the strip
|
||
```
|
||
|
||
##### CanShow()
|
||
|
||
```cpp
|
||
bool CanShow(void) const
|
||
```
|
||
|
||
Checks if the strip is ready to accept a new `Show()` command.
|
||
|
||
**Returns:** `true` if ready, `false` if previous transmission still in progress
|
||
|
||
**Example:**
|
||
```cpp
|
||
if (strip.CanShow()) {
|
||
strip.Show();
|
||
}
|
||
```
|
||
|
||
#### Configuration Methods
|
||
|
||
##### SetPixelCount()
|
||
|
||
```cpp
|
||
void SetPixelCount(uint16_t num_leds)
|
||
```
|
||
|
||
Changes the number of LEDs in the strip. Reallocates buffers if necessary.
|
||
|
||
**Parameters:**
|
||
- `num_leds`: New number of LEDs
|
||
|
||
**Example:**
|
||
```cpp
|
||
strip.SetPixelCount(100); // Resize to 100 LEDs
|
||
```
|
||
|
||
##### SetPixelSubType()
|
||
|
||
```cpp
|
||
void SetPixelSubType(uint8_t type)
|
||
```
|
||
|
||
Changes only the pixel ordering and size (lower 8 bits of type), keeping timing unchanged.
|
||
|
||
**Parameters:**
|
||
- `type`: New subtype (bits 0-7 of type encoding)
|
||
|
||
**Example:**
|
||
```cpp
|
||
// Change from GRB to RGB ordering
|
||
strip.SetPixelSubType(TasmotaLed_3_RGB | TasmotaLed_RGB);
|
||
```
|
||
|
||
##### SetRawFormat()
|
||
|
||
```cpp
|
||
void SetRawFormat(bool raw_format)
|
||
```
|
||
|
||
Enables/disables raw format mode. When enabled, buffer is copied directly without format conversion.
|
||
|
||
**Parameters:**
|
||
- `raw_format`: `true` for raw mode, `false` for automatic conversion
|
||
|
||
**Example:**
|
||
```cpp
|
||
strip.SetRawFormat(true); // Disable format conversion
|
||
```
|
||
|
||
#### Query Methods
|
||
|
||
##### PixelCount()
|
||
|
||
```cpp
|
||
uint16_t PixelCount(void) const
|
||
```
|
||
|
||
Returns the number of LEDs in the strip.
|
||
|
||
##### PixelSize()
|
||
|
||
```cpp
|
||
uint8_t PixelSize(void) const
|
||
```
|
||
|
||
Returns the number of bytes per pixel (3 for RGB, 4 for RGBW).
|
||
|
||
##### GetType()
|
||
|
||
```cpp
|
||
uint8_t GetType(void) const
|
||
```
|
||
|
||
Returns the LED type encoding.
|
||
|
||
##### Pixels()
|
||
|
||
```cpp
|
||
uint8_t * Pixels(void) const
|
||
```
|
||
|
||
Returns a pointer to the work buffer for direct manipulation.
|
||
|
||
**Example:**
|
||
```cpp
|
||
uint8_t *buf = strip.Pixels();
|
||
// Direct buffer access (advanced usage)
|
||
buf[0] = 0xFF; // Set first byte
|
||
```
|
||
|
||
##### IsDirty()
|
||
|
||
```cpp
|
||
bool IsDirty(void) const
|
||
```
|
||
|
||
Returns the dirty flag (for NeoPixelBus compatibility, but not used internally).
|
||
|
||
##### Dirty()
|
||
|
||
```cpp
|
||
void Dirty(void)
|
||
```
|
||
|
||
Sets the dirty flag to `true`.
|
||
|
||
|
||
### TasmotaLEDPusher Class (Abstract)
|
||
|
||
#### Static Factory Methods
|
||
|
||
##### Create()
|
||
|
||
```cpp
|
||
static TasmotaLEDPusher * Create(uint32_t hw, int8_t gpio)
|
||
```
|
||
|
||
Creates a pusher instance for the specified hardware type.
|
||
|
||
**Parameters:**
|
||
- `hw`: Hardware type (TasmotaLed_RMT, TasmotaLed_SPI, TasmotaLed_I2S, or TasmotaLed_HW_Default)
|
||
- `gpio`: GPIO pin number for LED data output
|
||
|
||
**Returns:** Pointer to pusher instance, or `nullptr` if creation failed
|
||
|
||
**Example:**
|
||
```cpp
|
||
TasmotaLEDPusher *pusher = TasmotaLEDPusher::Create(TasmotaLed_RMT, 5);
|
||
if (pusher) {
|
||
strip.SetPusher(pusher);
|
||
}
|
||
```
|
||
|
||
##### ResolveHardware()
|
||
|
||
```cpp
|
||
static uint32_t ResolveHardware(uint32_t hw)
|
||
```
|
||
|
||
Resolves hardware type based on SOC capabilities.
|
||
|
||
**Parameters:**
|
||
- `hw`: Requested hardware type
|
||
|
||
**Returns:** Resolved hardware type (removes unsupported flags)
|
||
|
||
#### Instance Methods
|
||
|
||
##### Begin()
|
||
|
||
```cpp
|
||
virtual bool Begin(uint16_t pixel_count, uint16_t pixel_size,
|
||
const TasmotaLED_Timing * led_timing)
|
||
```
|
||
|
||
Initializes the hardware pusher.
|
||
|
||
**Parameters:**
|
||
- `pixel_count`: Number of LEDs
|
||
- `pixel_size`: Bytes per pixel (3 or 4)
|
||
- `led_timing`: Pointer to timing structure
|
||
|
||
**Returns:** `true` if successful
|
||
|
||
##### Push()
|
||
|
||
```cpp
|
||
virtual bool Push(uint8_t *buf) = 0
|
||
```
|
||
|
||
Pushes pixel data to the LED strip (pure virtual).
|
||
|
||
**Parameters:**
|
||
- `buf`: Pointer to pixel buffer in strip format
|
||
|
||
**Returns:** `true` if successful
|
||
|
||
##### CanShow()
|
||
|
||
```cpp
|
||
virtual bool CanShow(void) = 0
|
||
```
|
||
|
||
Checks if hardware is ready for next transmission (pure virtual).
|
||
|
||
**Returns:** `true` if ready
|
||
|
||
##### SetPixelCount()
|
||
|
||
```cpp
|
||
virtual bool SetPixelCount(uint16_t pixel_count) = 0
|
||
```
|
||
|
||
Updates pixel count (pure virtual).
|
||
|
||
**Parameters:**
|
||
- `pixel_count`: New number of pixels
|
||
|
||
**Returns:** `true` if successful
|
||
|
||
##### Initialized()
|
||
|
||
```cpp
|
||
bool Initialized(void) const
|
||
```
|
||
|
||
Returns initialization status.
|
||
|
||
##### Error()
|
||
|
||
```cpp
|
||
esp_err_t Error(void) const
|
||
```
|
||
|
||
Returns last error code.
|
||
|
||
---
|
||
|
||
## Configuration Guide
|
||
|
||
### Basic Setup
|
||
|
||
#### Step 1: Include Headers
|
||
|
||
```cpp
|
||
#include "TasmotaLED.h"
|
||
#include "TasmotaLEDPusher.h"
|
||
```
|
||
|
||
#### Step 2: Create Strip Instance
|
||
|
||
```cpp
|
||
// For WS2812 RGB strip with 60 LEDs
|
||
TasmotaLED strip(ws2812_grb, 60);
|
||
|
||
// For SK6812 RGBW strip with 100 LEDs
|
||
TasmotaLED strip(sk6812_grbw, 100);
|
||
|
||
// Custom configuration
|
||
uint16_t custom_type = TasmotaLed_3_RGB | TasmotaLed_RGB | TasmotaLed_WS2812;
|
||
TasmotaLED strip(custom_type, 144);
|
||
```
|
||
|
||
#### Step 3: Create and Assign Pusher
|
||
|
||
```cpp
|
||
// Auto-select best hardware for GPIO 5
|
||
TasmotaLEDPusher *pusher = TasmotaLEDPusher::Create(TasmotaLed_HW_Default, 5);
|
||
if (pusher) {
|
||
strip.SetPusher(pusher);
|
||
} else {
|
||
// Handle error
|
||
}
|
||
|
||
// Or explicitly request RMT
|
||
TasmotaLEDPusher *pusher = TasmotaLEDPusher::Create(TasmotaLed_RMT, 5);
|
||
```
|
||
|
||
#### Step 4: Initialize
|
||
|
||
```cpp
|
||
if (!strip.Begin()) {
|
||
// Handle initialization error
|
||
Serial.println("Failed to initialize LED strip");
|
||
}
|
||
```
|
||
|
||
#### Step 5: Use the Strip
|
||
|
||
```cpp
|
||
// Set colors
|
||
strip.ClearTo(0x000000); // Clear to black
|
||
strip.SetPixelColor(0, 0xFF0000); // First pixel red
|
||
strip.SetPixelColor(1, 0x00FF00); // Second pixel green
|
||
strip.Show(); // Update strip
|
||
```
|
||
|
||
### Advanced Configuration
|
||
|
||
#### Custom LED Type
|
||
|
||
```cpp
|
||
// Create custom type: RGB, BGR ordering, WS2812 timing
|
||
uint16_t custom_type = TasmotaLed_3_RGB | // 3 bytes per pixel
|
||
TasmotaLed_BGR | // BGR ordering
|
||
TasmotaLed_WS2812; // WS2812 timing
|
||
|
||
TasmotaLED strip(custom_type, 60);
|
||
```
|
||
|
||
#### RGBW with W Channel First
|
||
|
||
```cpp
|
||
// SK6812 RGBW with W channel before RGB
|
||
uint16_t rgbw_type = TasmotaLed_4_WRGB | // 4 bytes per pixel
|
||
TasmotaLed_RGB | // RGB ordering
|
||
TasmotaLed_Wxxx | // W first
|
||
TasmotaLed_SK6812; // SK6812 timing
|
||
|
||
TasmotaLED strip(rgbw_type, 100);
|
||
```
|
||
|
||
#### Multiple Strips
|
||
|
||
```cpp
|
||
// Strip 1 on GPIO 5
|
||
TasmotaLED strip1(ws2812_grb, 60);
|
||
TasmotaLEDPusher *pusher1 = TasmotaLEDPusher::Create(TasmotaLed_RMT, 5);
|
||
strip1.SetPusher(pusher1);
|
||
strip1.Begin();
|
||
|
||
// Strip 2 on GPIO 18
|
||
TasmotaLED strip2(sk6812_grbw, 100);
|
||
TasmotaLEDPusher *pusher2 = TasmotaLEDPusher::Create(TasmotaLed_RMT, 18);
|
||
strip2.SetPusher(pusher2);
|
||
strip2.Begin();
|
||
```
|
||
|
||
#### Raw Format Mode
|
||
|
||
```cpp
|
||
strip.SetRawFormat(true); // Disable automatic format conversion
|
||
|
||
// Now buffer must be in strip's native format (e.g., GRB for WS2812)
|
||
uint8_t *buf = strip.Pixels();
|
||
buf[0] = 0xFF; // Green
|
||
buf[1] = 0x00; // Red
|
||
buf[2] = 0x00; // Blue
|
||
strip.Show();
|
||
```
|
||
|
||
### Compile-Time Configuration
|
||
|
||
#### Enable/Disable Hardware Support
|
||
|
||
In your `platformio.ini` or build flags:
|
||
|
||
```ini
|
||
build_flags =
|
||
-DTASMOTALED_HARDWARE_RMT=1
|
||
-DTASMOTALED_HARDWARE_SPI=1
|
||
-DTASMOTALED_HARDWARE_I2S=0
|
||
```
|
||
|
||
Or in your code before including TasmotaLED:
|
||
|
||
```cpp
|
||
#define TASMOTALED_HARDWARE_RMT 1
|
||
#define TASMOTALED_HARDWARE_SPI 0
|
||
#define TASMOTALED_HARDWARE_I2S 0
|
||
|
||
#include "TasmotaLED.h"
|
||
```
|
||
|
||
|
||
---
|
||
|
||
## Performance Characteristics
|
||
|
||
### Memory Usage
|
||
|
||
#### Per-Strip Overhead
|
||
|
||
| Component | Size | Description |
|
||
|-----------|------|-------------|
|
||
| TasmotaLED object | ~60 bytes | Class instance |
|
||
| Work buffer | `pixels × size` | Editable buffer (RGB or RGBW) |
|
||
| Show buffer | `pixels × size` | Internal transmission buffer |
|
||
| Pusher object | ~40-80 bytes | Hardware implementation |
|
||
| **Total** | **~100 + (2 × pixels × size)** | |
|
||
|
||
#### Examples
|
||
|
||
| Configuration | Memory Usage |
|
||
|---------------|--------------|
|
||
| 60 RGB pixels | ~460 bytes |
|
||
| 144 RGB pixels | ~964 bytes |
|
||
| 300 RGB pixels | ~1,900 bytes |
|
||
| 512 RGB pixels | ~3,172 bytes |
|
||
| 60 RGBW pixels | ~580 bytes |
|
||
| 144 RGBW pixels | ~1,252 bytes |
|
||
|
||
#### SPI Additional Overhead
|
||
|
||
SPI requires 3× buffer for encoding:
|
||
|
||
| Configuration | Additional Memory |
|
||
|---------------|-------------------|
|
||
| 60 RGB pixels | +540 bytes |
|
||
| 144 RGB pixels | +1,296 bytes |
|
||
| 300 RGB pixels | +2,700 bytes |
|
||
|
||
### Timing Performance
|
||
|
||
#### Buffer Operations (512 RGB pixels)
|
||
|
||
| Operation | Time | Notes |
|
||
|-----------|------|-------|
|
||
| `ClearTo(0x000000)` | ~15 µs | Optimized memset |
|
||
| `ClearTo(0xFF0000)` | ~124 µs | Per-pixel write |
|
||
| `SetPixelColor()` | ~0.2 µs | Single pixel |
|
||
| Format conversion | ~124 µs | RGB→GRB conversion |
|
||
| `Show()` (RMT) | ~16.2 ms | Actual transmission |
|
||
| `Show()` (SPI) | ~16.6 ms | Transmission + encoding |
|
||
|
||
#### Transmission Times
|
||
|
||
Transmission time depends on pixel count and timing:
|
||
|
||
**WS2812 (30 µs per pixel):**
|
||
- 60 pixels: ~1.8 ms
|
||
- 144 pixels: ~4.3 ms
|
||
- 300 pixels: ~9.0 ms
|
||
- 512 pixels: ~15.4 ms
|
||
|
||
**SK6812 (30 µs per pixel):**
|
||
- Similar to WS2812
|
||
|
||
**Maximum Refresh Rate:**
|
||
- 60 pixels: ~555 Hz
|
||
- 144 pixels: ~232 Hz
|
||
- 300 pixels: ~111 Hz
|
||
- 512 pixels: ~65 Hz
|
||
|
||
### CPU Overhead
|
||
|
||
#### RMT Implementation
|
||
- **Setup:** ~100 µs (one-time)
|
||
- **Per Show():** ~150 µs (format conversion + DMA setup)
|
||
- **During transmission:** Near zero (DMA handles transfer)
|
||
- **CPU usage:** <1% during transmission
|
||
|
||
#### SPI Implementation
|
||
- **Setup:** ~200 µs (one-time)
|
||
- **Per Show():** ~550 µs (encoding + format conversion)
|
||
- **During transmission:** Near zero (DMA handles transfer)
|
||
- **CPU usage:** <2% during transmission
|
||
|
||
### Optimization Tips
|
||
|
||
1. **Minimize Show() Calls**
|
||
```cpp
|
||
// Bad: Multiple Show() calls
|
||
for (int i = 0; i < 60; i++) {
|
||
strip.SetPixelColor(i, color);
|
||
strip.Show(); // 60 transmissions!
|
||
}
|
||
|
||
// Good: Single Show() call
|
||
for (int i = 0; i < 60; i++) {
|
||
strip.SetPixelColor(i, color);
|
||
}
|
||
strip.Show(); // 1 transmission
|
||
```
|
||
|
||
2. **Use ClearTo() for Bulk Operations**
|
||
```cpp
|
||
// Faster than loop with SetPixelColor()
|
||
strip.ClearTo(0xFF0000, 0, 29); // First 30 pixels red
|
||
```
|
||
|
||
3. **Check CanShow() Before Show()**
|
||
```cpp
|
||
if (strip.CanShow()) {
|
||
strip.Show();
|
||
}
|
||
```
|
||
|
||
4. **Direct Buffer Access for Complex Patterns**
|
||
```cpp
|
||
uint8_t *buf = strip.Pixels();
|
||
// Direct manipulation (advanced)
|
||
for (int i = 0; i < strip.PixelCount() * strip.PixelSize(); i++) {
|
||
buf[i] = pattern[i];
|
||
}
|
||
strip.Show();
|
||
```
|
||
|
||
---
|
||
|
||
## Usage Examples
|
||
|
||
### Basic Initialization
|
||
|
||
```cpp
|
||
#include "TasmotaLED.h"
|
||
#include "TasmotaLEDPusher.h"
|
||
|
||
// Create strip instance
|
||
TasmotaLED strip(ws2812_grb, 60);
|
||
|
||
void setup() {
|
||
// Create pusher with auto hardware selection
|
||
TasmotaLEDPusher *pusher = TasmotaLEDPusher::Create(TasmotaLed_HW_Default, 5);
|
||
if (pusher) {
|
||
strip.SetPusher(pusher);
|
||
if (strip.Begin()) {
|
||
// Strip ready
|
||
strip.ClearTo(0x000000); // Clear to black
|
||
strip.Show();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Setting Pixels
|
||
|
||
```cpp
|
||
// Set individual pixels
|
||
strip.SetPixelColor(0, 0xFF0000); // Red
|
||
strip.SetPixelColor(1, 0x00FF00); // Green
|
||
strip.SetPixelColor(2, 0x0000FF); // Blue
|
||
|
||
// Set range of pixels
|
||
strip.ClearTo(0xFF0000, 0, 9); // First 10 pixels red
|
||
|
||
// Update strip
|
||
strip.Show();
|
||
```
|
||
|
||
### RGBW Control
|
||
|
||
```cpp
|
||
TasmotaLED strip(sk6812_grbw, 60);
|
||
|
||
// Pure white using W channel
|
||
strip.ClearTo(0xFF000000); // W=0xFF, RGB=0
|
||
|
||
// Warm white (W + Red)
|
||
strip.ClearTo(0x80FF0000); // W=0x80, R=0xFF, G=0, B=0
|
||
|
||
// RGB color (no W)
|
||
strip.ClearTo(0x00FF00FF); // W=0, R=0xFF, G=0, B=0xFF (Magenta)
|
||
|
||
strip.Show();
|
||
```
|
||
|
||
### Non-Blocking Updates
|
||
|
||
```cpp
|
||
void loop() {
|
||
// Check if strip is ready before updating
|
||
if (strip.CanShow()) {
|
||
// Update pixels
|
||
for (int i = 0; i < strip.PixelCount(); i++) {
|
||
strip.SetPixelColor(i, colors[i]);
|
||
}
|
||
strip.Show();
|
||
}
|
||
|
||
// Other code continues without blocking
|
||
}
|
||
```
|
||
|
||
|
||
---
|
||
|
||
## Integration with Tasmota
|
||
|
||
### Tasmota Light Driver Integration
|
||
|
||
TasmotaLED is designed to integrate seamlessly with Tasmota's light driver system. Here's how it fits into the Tasmota architecture:
|
||
|
||
#### Integration Points
|
||
|
||
1. **Driver Initialization (FUNC_INIT)**
|
||
```cpp
|
||
void TasmotaLEDInit(void) {
|
||
// Create strip based on Settings
|
||
uint16_t led_type = Settings->light_type;
|
||
uint16_t led_count = Settings->light_pixels;
|
||
int8_t gpio = Pin(GPIO_LED);
|
||
|
||
strip = new TasmotaLED(led_type, led_count);
|
||
|
||
// Create pusher with hardware auto-selection
|
||
TasmotaLEDPusher *pusher = TasmotaLEDPusher::Create(
|
||
TasmotaLed_HW_Default, gpio);
|
||
|
||
if (pusher) {
|
||
strip->SetPusher(pusher);
|
||
if (strip->Begin()) {
|
||
AddLog(LOG_LEVEL_INFO, "LED: Initialized %d pixels on GPIO %d",
|
||
led_count, gpio);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
2. **Pixel Updates (FUNC_SET_CHANNELS)**
|
||
```cpp
|
||
void TasmotaLEDUpdate(void) {
|
||
// Get color data from Tasmota light engine
|
||
for (int i = 0; i < strip->PixelCount(); i++) {
|
||
uint32_t color = GetPixelColorFromLightEngine(i);
|
||
strip->SetPixelColor(i, color);
|
||
}
|
||
|
||
// Push to hardware
|
||
if (strip->CanShow()) {
|
||
strip->Show();
|
||
}
|
||
}
|
||
```
|
||
|
||
3. **Configuration Changes**
|
||
```cpp
|
||
void TasmotaLEDReconfigure(void) {
|
||
// Handle pixel count changes
|
||
if (new_pixel_count != strip->PixelCount()) {
|
||
strip->SetPixelCount(new_pixel_count);
|
||
}
|
||
|
||
// Handle type changes
|
||
if (new_led_type != strip->GetType()) {
|
||
strip->SetPixelSubType(new_led_type & 0xFF);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Tasmota Commands
|
||
|
||
When integrated with Tasmota, these commands control the LED strip:
|
||
|
||
| Command | Description | Example |
|
||
|---------|-------------|---------|
|
||
| `Pixels` | Set number of LEDs | `Pixels 60` |
|
||
| `Scheme` | Set color scheme | `Scheme 2` |
|
||
| `Speed` | Set animation speed | `Speed 10` |
|
||
| `Width` | Set effect width | `Width 1` |
|
||
| `Color` | Set RGB color | `Color #FF0000` |
|
||
| `White` | Set white channel | `White 50` |
|
||
| `Dimmer` | Set brightness | `Dimmer 75` |
|
||
| `Fade` | Enable fade | `Fade 1` |
|
||
|
||
### Settings Storage
|
||
|
||
Tasmota stores LED configuration in Settings structure:
|
||
|
||
```cpp
|
||
struct SETTINGS {
|
||
// ...
|
||
uint16_t light_pixels; // Number of LEDs
|
||
uint8_t light_type; // LED type encoding
|
||
uint8_t light_scheme; // Animation scheme
|
||
uint8_t light_speed; // Animation speed
|
||
uint8_t light_width; // Effect width
|
||
// ...
|
||
};
|
||
```
|
||
|
||
### Hardware Selection in Tasmota
|
||
|
||
Tasmota automatically selects the best hardware based on:
|
||
|
||
1. **SOC Capabilities**: Checks what hardware is available
|
||
2. **GPIO Availability**: Ensures GPIO is not in use
|
||
3. **Performance Requirements**: Selects RMT for best performance
|
||
4. **Fallback**: Uses SPI if RMT unavailable
|
||
|
||
```cpp
|
||
uint32_t TasmotaSelectHardware(void) {
|
||
uint32_t hw = TasmotaLed_HW_Default;
|
||
|
||
// Force SPI on ESP32-C2 (no RMT)
|
||
#ifdef CONFIG_IDF_TARGET_ESP32C2
|
||
hw = TasmotaLed_SPI;
|
||
#endif
|
||
|
||
// User override via SetOption
|
||
if (Settings->flag5.led_use_spi) {
|
||
hw = TasmotaLed_SPI;
|
||
}
|
||
|
||
return hw;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### Common Issues
|
||
|
||
#### 1. Strip Not Lighting Up
|
||
|
||
**Symptoms:** No LEDs light up after calling `Show()`
|
||
|
||
**Possible Causes:**
|
||
- Pusher not initialized
|
||
- Wrong GPIO pin
|
||
- Incorrect LED type
|
||
- Power supply issues
|
||
|
||
**Solutions:**
|
||
```cpp
|
||
// Check initialization
|
||
if (!strip.Begin()) {
|
||
Serial.println("Failed to initialize");
|
||
// Check pusher creation
|
||
if (pusher == nullptr) {
|
||
Serial.println("Pusher creation failed");
|
||
}
|
||
}
|
||
|
||
// Verify GPIO
|
||
Serial.printf("Using GPIO %d\n", gpio_pin);
|
||
|
||
// Test with simple pattern
|
||
strip.ClearTo(0xFF0000); // All red
|
||
strip.Show();
|
||
```
|
||
|
||
#### 2. Wrong Colors
|
||
|
||
**Symptoms:** Colors appear incorrect (e.g., red shows as green)
|
||
|
||
**Possible Causes:**
|
||
- Incorrect pixel ordering
|
||
- Wrong LED type
|
||
|
||
**Solutions:**
|
||
```cpp
|
||
// Try different orderings
|
||
strip.SetPixelSubType(TasmotaLed_3_RGB | TasmotaLed_RGB); // RGB
|
||
strip.SetPixelSubType(TasmotaLed_3_RGB | TasmotaLed_GRB); // GRB
|
||
strip.SetPixelSubType(TasmotaLed_3_RGB | TasmotaLed_BGR); // BGR
|
||
|
||
// Test each ordering
|
||
strip.ClearTo(0xFF0000); // Should be red
|
||
strip.Show();
|
||
```
|
||
|
||
#### 3. Flickering or Glitches
|
||
|
||
**Symptoms:** Random flickering, incorrect colors, or partial updates
|
||
|
||
**Possible Causes:**
|
||
- Timing issues
|
||
- Insufficient power
|
||
- Long cable runs
|
||
- Electromagnetic interference
|
||
|
||
**Solutions:**
|
||
```cpp
|
||
// Try different timing
|
||
uint16_t type_ws2812 = TasmotaLed_3_RGB | TasmotaLed_GRB | TasmotaLed_WS2812;
|
||
uint16_t type_sk6812 = TasmotaLed_3_RGB | TasmotaLed_GRB | TasmotaLed_SK6812;
|
||
|
||
// Check power supply (5V, sufficient amperage)
|
||
// Add capacitor (1000µF) across power supply
|
||
// Keep data cable short (<1m) or use level shifter
|
||
```
|
||
|
||
#### 4. Memory Allocation Failures
|
||
|
||
**Symptoms:** `Begin()` returns false, crashes, or resets
|
||
|
||
**Possible Causes:**
|
||
- Too many LEDs for available memory
|
||
- Memory fragmentation
|
||
|
||
**Solutions:**
|
||
```cpp
|
||
// Check available heap
|
||
Serial.printf("Free heap: %d bytes\n", ESP.getFreeHeap());
|
||
|
||
// Calculate required memory
|
||
uint32_t required = strip.PixelCount() * strip.PixelSize() * 2;
|
||
Serial.printf("Required: %d bytes\n", required);
|
||
|
||
// Reduce pixel count if necessary
|
||
if (required > ESP.getFreeHeap() / 2) {
|
||
strip.SetPixelCount(smaller_count);
|
||
}
|
||
```
|
||
|
||
#### 5. Performance Issues
|
||
|
||
**Symptoms:** Slow updates, low frame rate
|
||
|
||
**Possible Causes:**
|
||
- Too many `Show()` calls
|
||
- Inefficient pixel updates
|
||
- Wrong hardware selection
|
||
|
||
**Solutions:**
|
||
```cpp
|
||
// Batch updates
|
||
for (int i = 0; i < strip.PixelCount(); i++) {
|
||
strip.SetPixelColor(i, colors[i]);
|
||
}
|
||
strip.Show(); // Single show call
|
||
|
||
// Use ClearTo() for bulk operations
|
||
strip.ClearTo(0xFF0000, 0, 29); // Faster than loop
|
||
|
||
// Check hardware
|
||
Serial.printf("Using hardware: 0x%08X\n", hw_type);
|
||
|
||
// Verify CanShow() before Show()
|
||
if (strip.CanShow()) {
|
||
strip.Show();
|
||
}
|
||
```
|
||
|
||
### Debug Logging
|
||
|
||
Enable debug logging to diagnose issues:
|
||
|
||
```cpp
|
||
// In Tasmota, set log level
|
||
SerialLog 4 // Debug level
|
||
|
||
// Look for these messages:
|
||
// LED: RMT gpio 5
|
||
// LED: SPI gpio 5
|
||
// LED: Error create RMT bus failed 5 err=XXX
|
||
```
|
||
|
||
### Hardware-Specific Issues
|
||
|
||
#### ESP32-C2
|
||
- Only SPI supported (no RMT)
|
||
- Ensure `TASMOTALED_HARDWARE_SPI` is enabled
|
||
|
||
#### ESP32-S2/S3
|
||
- RMT channels limited to 4
|
||
- Check channel availability if using multiple strips
|
||
|
||
#### ESP32 Classic
|
||
- RMT channels limited to 8
|
||
- Most flexible hardware support
|
||
|
||
### Timing Verification
|
||
|
||
Verify timing with oscilloscope or logic analyzer:
|
||
|
||
**WS2812 Expected:**
|
||
- T0H: 400ns ±150ns
|
||
- T0L: 850ns ±150ns
|
||
- T1H: 800ns ±150ns
|
||
- T1L: 450ns ±150ns
|
||
- Reset: >50µs
|
||
|
||
**SK6812 Expected:**
|
||
- T0H: 300ns ±150ns
|
||
- T0L: 900ns ±150ns
|
||
- T1H: 600ns ±150ns
|
||
- T1L: 600ns ±150ns
|
||
- Reset: >80µs
|
||
|
||
---
|
||
|
||
## Appendix
|
||
|
||
### Pixel Order Matrix
|
||
|
||
The library uses a lookup table for pixel ordering:
|
||
|
||
```cpp
|
||
static const uint8_t TASMOTALED_CHANNEL_ORDERS[8][3] = {
|
||
{1, 0, 2}, // Def=GRB (0)
|
||
{1, 0, 2}, // GRB (1)
|
||
{0, 1, 2}, // RGB (2)
|
||
{0, 2, 1}, // RBG (3)
|
||
{2, 1, 0}, // BRG (4)
|
||
{1, 2, 0}, // BGR (5)
|
||
{2, 0, 1}, // GBR (6)
|
||
{1, 0, 2} // GRB (7) - fallback
|
||
};
|
||
```
|
||
|
||
**Interpretation:**
|
||
- Index 0: Position of Red channel
|
||
- Index 1: Position of Green channel
|
||
- Index 2: Position of Blue channel
|
||
|
||
**Example:** GRB ordering `{1, 0, 2}` means:
|
||
- Red goes to position 1
|
||
- Green goes to position 0
|
||
- Blue goes to position 2
|
||
- Result: G(0) R(1) B(2)
|
||
|
||
### Timing Structure
|
||
|
||
```cpp
|
||
typedef struct TasmotaLED_Timing {
|
||
uint16_t T0H; // Bit 0 high time (nanoseconds)
|
||
uint16_t T0L; // Bit 0 low time (nanoseconds)
|
||
uint16_t T1H; // Bit 1 high time (nanoseconds)
|
||
uint16_t T1L; // Bit 1 low time (nanoseconds)
|
||
uint32_t Reset; // Reset time (nanoseconds)
|
||
} TasmotaLED_Timing;
|
||
```
|
||
|
||
### Comparison with NeoPixelBus
|
||
|
||
| Feature | TasmotaLED | NeoPixelBus |
|
||
|---------|------------|-------------|
|
||
| **Memory** | Lower (2 buffers) | Higher (3+ buffers) |
|
||
| **Code Size** | ~15 KB | ~40 KB |
|
||
| **Hardware** | RMT, SPI, I2S | RMT, I2S, UART, BitBang |
|
||
| **ESP8266** | ❌ No | ✅ Yes |
|
||
| **ESP32** | ✅ Yes | ✅ Yes |
|
||
| **Buffer Swap** | No (copy on Show) | Yes (double buffering) |
|
||
| **Dirty Flag** | Ignored | Used |
|
||
| **API** | Similar | Full-featured |
|
||
| **Performance** | Optimized | Feature-rich |
|
||
|
||
### License
|
||
|
||
```
|
||
TasmotaLED - Lightweight implementation for addressable 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 <http://www.gnu.org/licenses/>.
|
||
```
|
||
|
||
---
|
||
|
||
## Revision History
|
||
|
||
| Version | Date | Changes |
|
||
|---------|------|---------|
|
||
| 1.0 | 2024 | Initial release with RMT and SPI support |
|
||
|
||
---
|
||
|
||
**End of Documentation**
|