Back to Fastled

ESP32 Hardware Drivers

src/platforms/esp/32/drivers/README.md

3.10.36.3 KB
Original Source

ESP32 Hardware Drivers

This directory contains centralized hardware driver implementations for ESP32 platforms.

Architecture

Drivers are bonafide peripheral abstractions that encapsulate ESP-IDF hardware APIs and provide:

  • Clean, object-oriented interfaces
  • Automatic initialization and fallback handling
  • Thread-safe operations (leveraging ESP-IDF internal mutexes)
  • Platform-independent usage patterns

Available Drivers

Console UART Driver (uart_esp32.h / uart_esp32.cpp.hpp)

Purpose: Buffered serial console I/O using ESP-IDF UART peripheral

⚠️ Note: This is separate from the LED UART driver (uart/ subdirectory) which uses UART for WS2812 LED control.

Features:

  • Automatic driver installation with Arduino compatibility detection
  • Buffered TX/RX (256 bytes each, Arduino default)
  • Fallback to ROM UART if driver installation fails
  • Non-blocking read operations (timeout=0)
  • Thread-safe (ESP-IDF provides internal mutex)

Usage:

cpp
#include "platforms/esp/32/drivers/uart_esp32.h"

fl::UartEsp32 uart(UART_NUM_0);  // Console UART

// Output
uart.write("Hello, world!");
uart.writeln("With newline");

// Input
int available = uart.available();  // Bytes in RX buffer
int byte = uart.read();            // Read single byte (-1 if no data)

// Status
bool buffered = uart.isBuffered(); // true if driver installed, false if ROM fallback

Architecture:

  • Buffered mode: Uses uart_driver_install() with RX/TX ring buffers
  • Fallback mode: Uses ROM UART (esp_rom_output_tx_one_char) for output only
  • Initialization: Lazy (on first use via Singleton pattern)
  • Detection: Tests if Arduino or other code already installed driver

Files:

  • uart_esp32.h - Driver interface
  • uart_esp32.cpp.hpp - Driver implementation

Used by:

  • io_esp_idf.hpp - EspIO class delegates all I/O to UartEsp32
  • External API: fl::print(), fl::println(), fl::available(), fl::read()

LED UART Driver (uart/ subdirectory)

Purpose: WS2812 LED control using UART waveform generation

Architecture:

  • Uses UART's automatic start/stop bit insertion for LED protocol encoding
  • 2-bit LUT encoding (4:1 expansion vs 8:1 in traditional wave8)
  • DMA-based transmission with minimal CPU usage
  • Supports multiple strips via multiple UART peripherals (UART1, UART2)

Key difference from console UART:

  • Console UART: Text I/O at 115200 baud (UART0)
  • LED UART: Waveform generation at 3.2 Mbps (UART1/UART2)
  • These can coexist - console uses UART0, LEDs use UART1/UART2

Files:

  • uart/iuart_peripheral.h - Virtual interface for testability
  • uart/uart_peripheral_esp.h - Real hardware implementation
  • uart/channel_engine_uart.h - LED control driver

Documentation: See uart/README.md for complete LED UART documentation

Design Principles

1. Separation of Concerns

  • Drivers: Hardware peripheral management (UART, SPI, I2C, etc.)
  • I/O Layer: Platform abstraction (io_esp_idf.hpp uses drivers)
  • Application: Uses platform-independent API (fl::print(), etc.)

2. Encapsulation

  • All ESP-IDF API calls live in driver implementations
  • Public interfaces expose only high-level operations
  • Internal state (buffers, config) is private

3. Reusability

  • Drivers can be used independently of EspIO
  • Multiple instances supported (e.g., UART1, UART2)
  • Clean interfaces enable testing and mocking

4. Safety

  • Automatic fallback handling (e.g., ROM UART if driver fails)
  • Null pointer checks and bounds validation
  • Thread-safe by leveraging ESP-IDF mutexes

Adding New Drivers

When adding a new driver (SPI, I2C, Timer, etc.):

  1. Create driver files:

    • <peripheral>_esp32.h - Driver interface
    • <peripheral>_esp32.cpp.hpp - Driver implementation
  2. Follow naming conventions:

    • Class: <Peripheral>Esp32 (e.g., SpiEsp32, I2cEsp32)
    • Namespace: fl::
    • Methods: Use standard names (e.g., read(), write(), transfer())
  3. Include proper headers:

    • Use FL_EXTERN_C_BEGIN/END for ESP-IDF C headers
    • Include fl/stl/compiler_control.h for macros
    • Include platforms/esp/esp_version.h for version detection
  4. Document:

    • Add architecture notes (buffered vs. unbuffered, fallback behavior)
    • Document thread safety guarantees
    • Provide usage examples
  5. Update this README:

    • Add driver to "Available Drivers" section
    • Document dependencies and usage patterns

Version Compatibility

All drivers handle ESP-IDF version differences:

  • IDF 3.x - Legacy API (no source_clk field)
  • IDF 4.x - UART_SCLK_APB clock source
  • IDF 5.x+ - UART_SCLK_DEFAULT clock source

Use ESP_IDF_VERSION macros for version-dependent code:

cpp
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
    uart_config.source_clk = UART_SCLK_DEFAULT;
#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
    uart_config.source_clk = UART_SCLK_APB;
#endif
// IDF 3.x: source_clk field doesn't exist - skip it

Thread Safety

All drivers leverage ESP-IDF's internal thread safety:

  • UART driver: Internal mutex protects all operations
  • Safe to call from multiple FreeRTOS tasks
  • No additional locking needed in driver code

Example (UART):

cpp
// Task 1
uart.write("Task 1\n");

// Task 2 (concurrent)
uart.write("Task 2\n");

// Result: Both writes succeed, ESP-IDF serializes them internally

Memory Management

Drivers use minimal heap allocation:

  • UART: 256 bytes RX buffer + 256 bytes TX buffer = 512 bytes total
  • Buffers allocated once during driver installation
  • No per-operation allocations (zero-copy when possible)

Stack usage:

  • Drivers use stack-local variables for temporary operations
  • Typical stack usage: < 256 bytes per operation

Testing

Drivers should be testable in isolation:

  • Create mock ESP-IDF APIs for unit testing
  • Use dependency injection where possible
  • Provide status queries (isInitialized(), isBuffered(), etc.)

Integration testing:

  • Use hardware-in-the-loop validation (bash validate)
  • Test both buffered and fallback modes
  • Verify thread safety with concurrent operations

References