Skip to content

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

@darkgrue

Description

@darkgrue

Describe the bug

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: pixels 0–7 produce one ghost each; pixels 8–15 produce two; pixels 16–23 produce three; pixels 24+ produce nothing. Dense patterns mask the problem; all-off works correctly.

GetPixelColor immediately before Show() confirms the software buffer is always correct. The defect is introduced during RMT transmission.

The root cause is the hardcoded mem_block_symbols = 192 in NeoEsp32RmtXMethod.h. With 96 symbols per ping-pong half, WS2811/WS2812x transmits one half in approximately 120 µs. NimBLE and WiFi event handlers run at higher interrupt priority than the RMT refill ISR and routinely preempt for longer than 120 µs, leaving the data line idle past the LED latch threshold (~300 µs). The LEDs latch the partial frame, producing a false frame boundary at every 8-pixel interval.

Increasing mem_block_symbols to 512 raises the half-buffer transmission time to ~320 µs - exceeding the latch threshold - which eliminates the false latches.

To Reproduce

Connect a WS2811 or WS2812x strip to an ESP32.
Initialize a NimBLE (or Arduino BLE) server and establish a BLE connection from a client device.
In the main loop, clear the strip to black and set a single pixel to red, then call Show(). Repeat with a visible delay.
Observe that the addressed pixel lights correctly but an additional pixel 8 positions downstream also lights red. Pixels addressed at index 8+ produce proportionally more ghost pixels downstream.
Expected behavior

Only the explicitly addressed pixel should light. All other pixels should remain off.

Development environment

OS: Windows 11
Build Environment: PlatformIO with pioarduino platform (ESP-IDF v5.x / Arduino framework)
Board target: ESP32-WROOM-32 (esp32dev)
Library version: NeoPixelBus CORE3 branch (GitHub clone, Makuna/NeoPixelBus#CORE3)

Minimal Sketch that reproduced the problem:

#include <NeoPixelBus.h>
#include <NimBLEDevice.h>

// Adjust pin and count for your hardware
const uint8_t PIN_DATA = 33;
const uint16_t NUM_LEDS = 40;

NeoPixelBus<NeoGrbFeature, NeoEsp32RmtXWs2811Method> strip(NUM_LEDS, PIN_DATA);

void setup() {
  Serial.begin(115200);

  // Start BLE — connection is required to trigger the bug
  NimBLEDevice::init("test");
  NimBLEServer *server = NimBLEDevice::createServer();
  NimBLEAdvertising *adv = NimBLEDevice::getAdvertising();
  adv->start();
  Serial.println("Advertising — connect a BLE client, then observe LEDs");

  strip.Begin();
  strip.ClearTo(RgbColor(0, 0, 0));
  strip.Show();
}

void loop() {
  // Light only pixel 0 red; all others black
  strip.ClearTo(RgbColor(0, 0, 0));
  strip.SetPixelColor(0, RgbColor(255, 0, 0));
  strip.Show();
  delay(500);
}

Without a BLE client connected: only pixel 0 lights.

With a BLE client connected: pixel 0 and pixel 8 both light red.

Additional context

Two related issues in NeoEsp32RmtXMethod.h:

NEOPIXELBUS_RMT_INT_FLAGS is defined but never used. Line 60 defines #define NEOPIXELBUS_RMT_INT_FLAGS (ESP_INTR_FLAG_LOWMED) but this macro is not referenced anywhere in Initialize() or elsewhere in the CORE3 driver. If this flag could be applied to elevate the RMT ISR above BLE/WiFi priority, the buffer-size workaround would be unnecessary.

Stale comment. config.mem_block_symbols = 192; // memory block size, 64 * 4 = 256 Bytes - the comment describes a single 64-symbol block (256 bytes). 192 symbols × 4 bytes = 768 bytes; the comment was not updated when the value changed from 64.

Suggested fix: make mem_block_symbols user-overridable so BLE/WiFi projects can increase the ISR deadline without patching the library:

#ifndef NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS
#define NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS 192
#endif
config.mem_block_symbols = NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS;

This preserves backward compatibility. Projects with BLE or WiFi active can add -D NEOPIXELBUS_RMT_MEM_BLOCK_SYMBOLS=512 to their build flags. 512 is the maximum on ESP32 original (8 channels × 64 symbols); 256 or 384 are reasonable defaults that fit all ESP32 variants and provide meaningful improvement over 192.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions