Skip to content

fix(ESP32 RMT): make RMT memory block size user-overridable (fixes BLE/WiFi ghost pixels)#922

Open
darkgrue wants to merge 3 commits into
Makuna:masterfrom
darkgrue:issue-921-ESP32-+-BLE/WiFi-mem_block_symbols
Open

fix(ESP32 RMT): make RMT memory block size user-overridable (fixes BLE/WiFi ghost pixels)#922
darkgrue wants to merge 3 commits into
Makuna:masterfrom
darkgrue:issue-921-ESP32-+-BLE/WiFi-mem_block_symbols

Conversation

@darkgrue

@darkgrue darkgrue commented Jun 6, 2026

Copy link
Copy Markdown

Problem

When a BLE or WiFi connection is active on ESP32, sparse LED patterns produce ghost pixels at index N+8 for every addressed pixel N. The defect scales with strip position and is introduced during RMT transmission, not in the software color buffer.

Root cause: config.mem_block_num = 1 allocates a single 64-symbol RMT memory block. With ping-pong DMA, the RMT refill ISR must be serviced within ~40 µs at WS2812x 800 Kbps. NimBLE and WiFi event handlers run at higher interrupt priority than the RMT refill ISR and routinely preempt for longer, leaving the data line idle past the ~300 µs LED latch threshold.

Fixes #921.

Solution

Add a user-overridable preprocessor macro NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS that controls the number of RMT memory blocks allocated.

Default (backward compatible): 64 symbols → mem_block_num = 1 (unchanged behavior)

BLE/WiFi fix: add to build flags:

-D NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS=512

This allocates 8 memory blocks; the ping-pong half-buffer holds 256 symbols × 1.25 µs = 320 µs, which exceeds the 300 µs latch threshold.

Changes

src/internal/methods/NeoEsp32RmtMethod.h:

  • Added #ifndef NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS / #define NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS 64 / #endif after the NEOPIXELBUS_RMT_INT_FLAGS block
  • Changed config.mem_block_num = 1;config.mem_block_num = (NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS + 63) / 64;

Hardware Constraints

On the original ESP32 (8 RMT channels × 64 symbols each), setting NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS=512 consumes all 8 channels' memory for one strip. Users running multiple simultaneous RMT strips must choose a value that fits within available channels. Recommended values by use case:

Scenario Recommended value mem_block_num
No BLE/WiFi (default) 64 1
BLE or WiFi active (ESP32) 512 8
BLE or WiFi active (S2/S3/C3, 4 ch) 256 4

Testing

Verified by the issue reporter's reproduction sketch (ESP32 + NimBLE server, single pixel set to red):

  • Without fix: pixel 0 and pixel 8 both light red with a BLE client connected
  • With -D NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS=512: only pixel 0 lights

darkgrue and others added 3 commits June 6, 2026 11:51
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…MT_MEM_BLOCK_SYMBOLS

Hardcoded mem_block_num=1 (64 symbols, ~40us half-buffer at 800Kbps)
allows BLE/WiFi interrupt handlers to preempt the RMT refill ISR and
hold the data line idle past the 300us LED latch threshold, producing
ghost pixels at 8-pixel intervals.

Add NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS (default 64, backward-compatible).
Projects with active BLE or WiFi can set
  -D NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS=512
to allocate 8 memory blocks (320us half-buffer), eliminating false latches.

Fixes: Makuna#921

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ESP32 + BLE/WiFi: mem_block_symbols = 192 causes mid-frame LED latches; NEOPIXELBUS_RMT_INT_FLAGS defined but unused

1 participant