Tasmota/lib/lib_basic/TasmotaLED/TASMOTALED_DOCUMENTATION.md

41 KiB
Raw Blame History

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
  2. Class Hierarchy
  3. LED Type Encoding System
  4. Hardware Acceleration
  5. API Reference
  6. Configuration Guide
  7. Performance Characteristics
  8. Usage Examples
  9. Integration with Tasmota
  10. 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

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

// 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:

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:

// 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

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:

// 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

~TasmotaLED()

Cleans up resources, frees buffers, and deletes the pusher instance.

Initialization Methods

SetPusher()
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:

TasmotaLED strip(ws2812_grb, 60);
TasmotaLEDPusher *pusher = TasmotaLEDPusher::Create(TasmotaLed_RMT, 5);
strip.SetPusher(pusher);
Begin()
bool Begin(void)

Initializes the hardware pusher and prepares the strip for operation.

Returns: true if initialization successful, false otherwise

Example:

if (strip.Begin()) {
  // Strip is ready to use
} else {
  // Initialization failed
}

Pixel Manipulation Methods

SetPixelColor()
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:

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()
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:

uint32_t color = strip.GetPixelColor(5);
uint8_t red = (color >> 16) & 0xFF;
uint8_t green = (color >> 8) & 0xFF;
uint8_t blue = color & 0xFF;
ClearTo()
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:

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()
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:

strip.SetPixelColor(0, 0xFF0000);
strip.SetPixelColor(1, 0x00FF00);
strip.Show();  // Update the strip
CanShow()
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:

if (strip.CanShow()) {
  strip.Show();
}

Configuration Methods

SetPixelCount()
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:

strip.SetPixelCount(100);  // Resize to 100 LEDs
SetPixelSubType()
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:

// Change from GRB to RGB ordering
strip.SetPixelSubType(TasmotaLed_3_RGB | TasmotaLed_RGB);
SetRawFormat()
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:

strip.SetRawFormat(true);  // Disable format conversion

Query Methods

PixelCount()
uint16_t PixelCount(void) const

Returns the number of LEDs in the strip.

PixelSize()
uint8_t PixelSize(void) const

Returns the number of bytes per pixel (3 for RGB, 4 for RGBW).

GetType()
uint8_t GetType(void) const

Returns the LED type encoding.

Pixels()
uint8_t * Pixels(void) const

Returns a pointer to the work buffer for direct manipulation.

Example:

uint8_t *buf = strip.Pixels();
// Direct buffer access (advanced usage)
buf[0] = 0xFF;  // Set first byte
IsDirty()
bool IsDirty(void) const

Returns the dirty flag (for NeoPixelBus compatibility, but not used internally).

Dirty()
void Dirty(void)

Sets the dirty flag to true.

TasmotaLEDPusher Class (Abstract)

Static Factory Methods

Create()
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:

TasmotaLEDPusher *pusher = TasmotaLEDPusher::Create(TasmotaLed_RMT, 5);
if (pusher) {
  strip.SetPusher(pusher);
}
ResolveHardware()
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()
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()
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()
virtual bool CanShow(void) = 0

Checks if hardware is ready for next transmission (pure virtual).

Returns: true if ready

SetPixelCount()
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()
bool Initialized(void) const

Returns initialization status.

Error()
esp_err_t Error(void) const

Returns last error code.


Configuration Guide

Basic Setup

Step 1: Include Headers

#include "TasmotaLED.h"
#include "TasmotaLEDPusher.h"

Step 2: Create Strip Instance

// 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

// 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

if (!strip.Begin()) {
  // Handle initialization error
  Serial.println("Failed to initialize LED strip");
}

Step 5: Use the Strip

// 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

// 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

// 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

// 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

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:

build_flags = 
  -DTASMOTALED_HARDWARE_RMT=1
  -DTASMOTALED_HARDWARE_SPI=1
  -DTASMOTALED_HARDWARE_I2S=0

Or in your code before including TasmotaLED:

#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

    // 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

    // Faster than loop with SetPixelColor()
    strip.ClearTo(0xFF0000, 0, 29);  // First 30 pixels red
    
  3. Check CanShow() Before Show()

    if (strip.CanShow()) {
      strip.Show();
    }
    
  4. Direct Buffer Access for Complex Patterns

    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

#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

// 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

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

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)

    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)

    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

    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:

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
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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

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

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