Electrical – WS2812 ESP32 Flickering math issue

arduinows2812b

Alright, so I've searched just about everywhere to find an answer to this and I've personally experimented a lot to try and find a solution.

The error I'm experiencing is Neopixel (WS2812B 12ct LED ring) flicker using the NeoPixel library (this is a requirement for the project. Otherwise I would use FastLED and see if that has the same issues)

The following is my setup:
ESP32s (Node mcu brand esp32) using the Arduino IDE
3.7v LiPo OR USB power
2x WS2812B rings on pins 25/26 (i've also tried 16/17)

I have a LiPo battery hooked up in parallel with all the components (ESP32 on 5v pin, and both strips separately) with a proper shared ground. This hardware setup worked 100% perfectly when I use an Arduino CH340 Nano 3.3v + Adafruit BLE module separately.

What I've discovered is that the flickering ONLY occurs on patterns where I'm doing division on the pixel color. I.E. a fade or two color fade. Otherwise, in patterns like a Rainbow cycle or a simple Colorwipe, it works perfectly with no flickering on any pixel whatsoever. Occasionally, and much more rarely I get a similar issue when bitshifting to dim the color. The weirdest part is that after the pattern has run for 3 or 4 cycles, it "evens out" and stops flickering. The reason this is weird is that I'm not rebooting the board between patterns. I have an android bluetooth app I cooked up to send Serial BLE signals as a controller. So switching between my patterns, they all look great until I switch to a fading one.

void FadeUpdate()
{        
    uint16_t stepUp = (TotalSteps + 1);
    uint8_t red = ((Red(Color1) * (stepUp - Index))) / stepUp;
    uint8_t green = ((Green(Color1) * (stepUp - Index))) / stepUp;
    uint8_t blue = ((Blue(Color1) * (stepUp - Index))) / stepUp;

    ColorSet(Color(red, green, blue));
    show();
    Increment();
}

void TwoColorFadeUpdate()
{
    // Calculate linear interpolation between Color1 and Color2
    // Optimise order of operations to minimize truncation error
    uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
    uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
    uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;

    ColorSet(Color(red, green, blue));
    show();
    Increment();
}
uint32_t DimColor(uint32_t color)
{
    // Shift R, G and B components one bit to the right
    uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
    return dimColor;
}

Blue, Green, and Red all just do bit shifting on the uint32_t to grab the uint8_t representation of that color portion

TotalSteps and Index are both a uint16_t (I tried uint32_t as well)

This leads me to believe that my pixel flicking issue is due to some kind of odd rounding error

Things I've tried:

  • delay(1);
  • portDISABLE_INTERRUPTS();
  • casting to int instead of uint32_t for my colors

The first two actually did help slightly, and considering that the pixels flickering are the same ones, depending on the pattern (E.G. pixel 7 and 9 will flicker with X and Y pattern when Fade, but on TwoColorFade it will be pixel 1 and 8) this leads me to believe this could also be some kind of timing error as well.

Things I haven't tried yet:

  • Attaching the vin of the pixels to the 3v3 pin on the board.
  • Logic level shifter.
  • Setting the board clock frequency

I have NOT tried these things yet because the code executes perfectly on the nano, which is also a 3.3v board. I want to test changing the clock frequency but that's next on my list of stuff to test.

I can also post my code, but its split across multiple .ino files because im a novice at Arduino dev, but I still want some organization in my project.

Best Answer

After working with the NeoPixel library quite a bit I confirmed that it's a timing error related to the ESP32. To clarify, it is NOT any of the following if you are experiencing the same error (random pixel flashes that have a mathematical pattern to them) and have double checked all of these things.

  • Resistor on the data line
  • Capacitor on the power line
  • "Your voltage/logic levels are wrong"

What worked for me was using multi-threading to host an entire core (0) to running pixels.show();

Here is a code example using fastLED (I eventually just rewrote the whole program because the math effects are SO nice)

//Multi-threading synchronous show operations! Kinda slick honestly
static TaskHandle_t FastLEDshowTaskHandle = 0;
static TaskHandle_t userTaskHandle = 0;

void FastLEDshowESP32()
{
if (userTaskHandle == 0) {
    userTaskHandle = xTaskGetCurrentTaskHandle();
    xTaskNotifyGive(FastLEDshowTaskHandle);
    const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 200 );
    ulTaskNotifyTake(pdTRUE, xMaxBlockTime);
    userTaskHandle = 0;
}
}
void FastLEDshowTask(void *pvParameters)
{
// -- Run forever...
for(;;) {
    // -- Wait for the trigger
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    // -- Do the show (synchronously)        
    FastLED.show();
    // -- Notify the calling task
    xTaskNotifyGive(userTaskHandle);
}
}

void setup() {  
delay(3000); // 3 second delay for recovery
Serial.begin(115200);    
ser.begin(DEVICE_NAME);
Serial.println("Dare you enter my magical realm?");

FastLED.addLeds<LED_TYPE,RING_1_PIN,COLOR_ORDER>(Ring1, RING_1_LEDS).setCorrection(TypicalLEDStrip);
FastLED.addLeds<LED_TYPE,RING_2_PIN,COLOR_ORDER>(Ring2, RING_2_LEDS).setCorrection(TypicalLEDStrip);

FastLED.setBrightness(brightness);
//Set up multi-threaded execution of LED show on core 0
xTaskCreatePinnedToCore(FastLEDshowTask, "FastLEDshowTask", 2048, NULL, 2, &FastLEDshowTaskHandle, 0);
}
Related Topic