Tasmota/lib/lib_basic/TasmotaLED/TASMOTALED_DOCUMENTATION.md

1496 lines
41 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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**