Back to Tasmota

TasmotaLED Library Documentation

lib/lib_basic/TasmotaLED/TASMOTALED_DOCUMENTATION.md

15.4.041.3 KB
Original Source

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)

ValueEnumDescriptionBytes per Pixel
0x0TasmotaLed_1_DefDefault (same as RGB)3
0x1TasmotaLed_3_RGBRGB format3
0x2TasmotaLed_4_WRGBRGBW format4

Pixel Order Encoding (Bits 4-6)

ValueEnumDescriptionChannel Order
0b000TasmotaLed_DefDefault (GRB)G, R, B
0b001TasmotaLed_GRBGreen-Red-BlueG, R, B
0b010TasmotaLed_RGBRed-Green-BlueR, G, B
0b011TasmotaLed_RBGRed-Blue-GreenR, B, G
0b100TasmotaLed_BRGBlue-Red-GreenB, R, G
0b101TasmotaLed_BGRBlue-Green-RedB, G, R
0b110TasmotaLed_GBRGreen-Blue-RedG, B, R

W Channel Position (Bit 7)

ValueEnumDescriptionFormat
0TasmotaLed_xxxWW after colorRGB + W
1TasmotaLed_WxxxW before colorW + RGB

Timing Code (Bits 8-15)

ValueEnumDescriptionT0HT0LT1HT1LReset
0TasmotaLed_WS2812WS2812/WS2812B400ns850ns800ns450ns80µs
1TasmotaLed_SK6812SK6812300ns900ns600ns600ns80µ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

HardwareESP32ESP32-S2ESP32-S3ESP32-C3ESP32-C2ESP32-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

ComponentSizeDescription
TasmotaLED object~60 bytesClass instance
Work bufferpixels × sizeEditable buffer (RGB or RGBW)
Show bufferpixels × sizeInternal transmission buffer
Pusher object~40-80 bytesHardware implementation
Total~100 + (2 × pixels × size)

Examples

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

ConfigurationAdditional 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)

OperationTimeNotes
ClearTo(0x000000)~15 µsOptimized memset
ClearTo(0xFF0000)~124 µsPer-pixel write
SetPixelColor()~0.2 µsSingle pixel
Format conversion~124 µsRGB→GRB conversion
Show() (RMT)~16.2 msActual transmission
Show() (SPI)~16.6 msTransmission + 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:

CommandDescriptionExample
PixelsSet number of LEDsPixels 60
SchemeSet color schemeScheme 2
SpeedSet animation speedSpeed 10
WidthSet effect widthWidth 1
ColorSet RGB colorColor #FF0000
WhiteSet white channelWhite 50
DimmerSet brightnessDimmer 75
FadeEnable fadeFade 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

FeatureTasmotaLEDNeoPixelBus
MemoryLower (2 buffers)Higher (3+ buffers)
Code Size~15 KB~40 KB
HardwareRMT, SPI, I2SRMT, I2S, UART, BitBang
ESP8266❌ No✅ Yes
ESP32✅ Yes✅ Yes
Buffer SwapNo (copy on Show)Yes (double buffering)
Dirty FlagIgnoredUsed
APISimilarFull-featured
PerformanceOptimizedFeature-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

VersionDateChanges
1.02024Initial release with RMT and SPI support

End of Documentation