0
\$\begingroup\$

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.

\$\endgroup\$
9
  • \$\begingroup\$ There could be many explanations. You identified a few of them. Try the things you propose to try. It would also be good to probe the signal with an oscilloscope. I think there is some chance that you have some type of signal integrity problem. It may go away when things get warm. \$\endgroup\$
    – user57037
    May 22, 2018 at 5:59
  • \$\begingroup\$ And technically, you are running outside the spec. VCC is supposed to be 5V, and Vinhi is 0.7*VCC. If you go by 0.7*VCC, connecting VCC to 3.3V seems like a good idea. But since VCC is supposed to be 5V, it may be a bad idea. So, try it and see what happens. But the best thing would be to power it with 5V and drive it with a 5V data signal. \$\endgroup\$
    – user57037
    May 22, 2018 at 6:04
  • \$\begingroup\$ debug by changing ColorSet(Color(red, green, blue)); to something like ColorSet(Color(red, 0, 0)); to see if it is just one color that flickers \$\endgroup\$
    – jsotola
    May 22, 2018 at 6:42
  • \$\begingroup\$ I've had experiences where when signal voltage is too low, then the neopixels sometimes act sporadically. Although only happened with complicated patterns. If you try running at 5v, maybe this will fix your issue, try using a boost converter to go to 5v \$\endgroup\$
    – Makoto
    May 22, 2018 at 10:55
  • 1
    \$\begingroup\$ Absolutely. I'm a software engineer by trade, so finding old stack exchanges with no good answer is anathema to me. \$\endgroup\$
    – Mdigibou
    May 22, 2018 at 18:22

1 Answer 1

0
\$\begingroup\$

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);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.