Makuna / NeoPixelBus

An Arduino NeoPixel support library supporting a large variety of individually addressable LEDs. Please refer to the Wiki for more details. Please use the GitHub Discussions to ask questions as the GitHub Issues feature is used for bug tracking.
GNU Lesser General Public License v3.0
1.17k stars 257 forks source link

Esp32 I2s (default method) is skipping sending data once in while. #292

Closed xj650t closed 3 years ago

xj650t commented 4 years ago

NOTE: If you are seeking help or have questions, this is NOT the place to do it. For questions and support, jump on Gitter and ask away.
Gitter

Describe the bug Animation judders or stops briefly about every second then continues smoothly again

To Reproduce Steps to reproduce the behavior: 1.Load NeoPixelRotateLoop example 2.Replace contents of DrawTailPixels with "for(uint16_t index = 0; index < strip.PixelCount(); index+=3) strip.SetPixelColor(index, RgbColor(128, 0, 0));"""

Expected behavior Smooth Animation loop of pixel lit every 3 pixels.

Development environment (please complete the following information):

Minimal Sketch that reproduced the problem: See To Reproduce above

Additional context I'm not sure this is a library bug or an ESP32 issue. I've also tried connecting to a WiFi access point but the juddering continued

Makuna commented 4 years ago

What version of Esp32 for Arduino board support are you using?

Try changing the starting "image" to just the first pixel lit. What do you see?

Try moving the show from the loop to the bottom of the LoopAnimUpdate.

xj650t commented 4 years ago

I tried updating the https://dl.espressif.com/dl/package_esp32_index.json but the certificate has expired and Arduino IDE refuses to download :-( I pulled out an esp8266 and the animation is smooth, no judder. I'll try the other steps above when I can download the json again.

Makuna commented 4 years ago

If you go to the board manager, what version of the esp32 board support does it state that you have? Is this where you hit checked if its updateable? I just updated last night; but I just checked and I see the same json issue now.

xj650t commented 4 years ago

I removed the json and restarted the ide then tried to add in the ESp32 which errors Error downloading https://dl.espressif.com/dl/package_esp32_index.json So can't actually add in the board support. Hopefully, they'll fix the certificate tomorrow.

xj650t commented 4 years ago

Espressif fixed the certificate issue so I've downloaded and used version 1.0.3. With the first pixel lit the juddering is still evident, I'm using a 60-pixel strip. Moving the Show() to the LoopAnimUpdate did not cure the juddering.

Makuna commented 4 years ago

I will take a deeper look. I know 1.0.4 is coming soon to fix some blocking issues.

spiffomatic64 commented 4 years ago

seeing the same issue (was not seeing it using the adafruit library, but it had "color accuracy" corruption issues

Makuna commented 4 years ago

Here is my test sketch, I am not seeing pausing in the code as the output is consistently showing a 66ms update. Switch to the alternate RMT (see code, comment out one def and uncomment the other).

Do you know what LEDs you have? Exact model (link to store where purchased)?

Try my sketch below and let me know the numbers you see.

output:

1024
 min delta : 65767
 max delta : 66001
 ave delta : 65999

sketch:

#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>

const uint16_t PixelCount = 60; // make sure to set this to the number of pixels in your strip
const uint16_t PixelPin = 2;  // make sure to set this to the correct pin, ignored for Esp8266
const uint16_t AnimCount = 1; // we only need one

NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> strip(PixelCount, PixelPin);
//NeoPixelBus<NeoGrbFeature, NeoEsp32Rmt0800KbpsMethod> strip(PixelCount, PixelPin);
NeoPixelAnimator animations(AnimCount); // NeoPixel animation management object

class TimeTrak
{
public:
  TimeTrak( size_t maxSamples ) :
     _prev(0),
     _current(0),
     _minDelta(0xffffffff),
     _maxDelta(0),
     _sampleCount(maxSamples),
     _sample(0)
     {
      _samples = new uint32_t[_sampleCount];
     }

  ~TimeTrak()
  {
    delete [] _samples;
  }

  void begin() {
    _prev = micros();
    _current = _prev;
     _minDelta = 0xffffffff;
     _maxDelta = 0;
     _sample = 0;
  }

  void start() {
    _prev = micros();
  }

  void restart() {
    _prev = _current;
  }

  void stop() {
    _current = micros();

    if (_sample != _sampleCount) {
      uint32_t delta = _current - _prev;
      _samples[_sample++] = delta;
      if (delta < _minDelta) {
        _minDelta = delta;
      }
      else if (delta > _maxDelta) {
        _maxDelta = delta;
      }
    }
  }

  uint32_t getLastDelta() {
    return _current - _prev;
  }

  uint32_t getMinDelta() {
    return _minDelta;
  }

  uint32_t getMaxDelta() {
    return _maxDelta;
  }

  uint32_t getAverageDelta() {
    if (_sample == 0) {
      return 0;
    }

    uint64_t sum = 0;

    for (size_t sample = 0; sample < _sample; sample++) {
      sum += _samples[sample];
    }

    return sum / _sample;
  }

  size_t currentSample() {
    return _sample;
  }

  bool maxSampleTaken() {
    return (_sample == _sampleCount);
  }

private:
  uint32_t _prev;
  uint32_t _current;

  uint32_t _minDelta;
  uint32_t _maxDelta;

  const size_t _sampleCount;
  uint32_t* _samples;
  size_t _sample;
};

TimeTrak animUpdate(1024);

void LoopAnimUpdate(const AnimationParam& param)
{
    // wait for this animation to complete,
    // we are using it as a timer of sorts
    if (param.state == AnimationState_Completed)
    {
        // done, time to restart this position tracking animation/timer
        animations.RestartAnimation(param.index);

        // rotate the complete strip one pixel to the right on every update
        strip.RotateRight(1);

        animUpdate.stop();
        animUpdate.restart();
    }
}

void DrawTailPixels() {
    for (uint16_t index = 0; index < strip.PixelCount(); index += 20)
    {
        strip.SetPixelColor(index, RgbColor(128,0,0));
    }
}

void setup() {
      Serial.begin(115200);
    while (!Serial); // wait for serial attach

    Serial.println();
    Serial.println("Initializing...");
    Serial.flush();

    strip.Begin();
    strip.Show();

    // Draw the tail that will be rotated through all the rest of the pixels
    DrawTailPixels();

    animUpdate.begin();

    // we use the index 0 animation to time how often we rotate all the pixels
    animations.StartAnimation(0, 66, LoopAnimUpdate); 

    Serial.println();
    Serial.println("Running...");
}

void loop() {
  if (animUpdate.maxSampleTaken()) {
    Serial.println(animUpdate.currentSample());
    Serial.print(" min delta : ");
    Serial.println(animUpdate.getMinDelta());
    Serial.print(" max delta : ");
    Serial.println(animUpdate.getMaxDelta());
    Serial.print(" ave delta : ");
    Serial.println(animUpdate.getAverageDelta());
    animUpdate.begin();
  }
    animations.UpdateAnimations();
    strip.Show();
}
Makuna commented 4 years ago

And note, I changed it to every 20 pixels, as every 3 pixels means that any single pixel will change state 5 times a second; which is hard to percieve.

xj650t commented 4 years ago

This is the led strip purchased https://www.amazon.co.uk/gp/aw/d/B01CDTED80 I’ll have a look at the code and update you later.

spiffomatic64 commented 4 years ago

Video of the code above running: https://www.youtube.com/watch?v=vg1YV1eMUmc

This is running on a DOIT esp32 clone https://www.amazon.com/gp/product/B079PVCF2G and the neopixels im not 100% sure, but the packages says WS2812B on it, 75% sure it was this: https://www.amazon.com/gp/product/B019HWACOA

And the numbers from the sketch serial: Initializing...

Running... 1024 min delta : 65855 max delta : 66001 ave delta : 65999

spiffomatic64 commented 4 years ago

Here is another recording in "slow motion" (recorded using slow motion on s10 Frame rate: 237.526528, transferred to computer, then changed the framerate to 30 via ffmpeg) https://youtu.be/QUZ1PYlv3eQ

xj650t commented 4 years ago

For the first method 1024 min delta : 65633 max delta : 66001 ave delta : 65999 1024 min delta : 65789 max delta : 66001 ave delta : 65999

For the second method Running... 1024 min delta : 65448 max delta : 66001 ave delta : 65999 1024 min delta : 65720 max delta : 66001 ave delta : 65999

Interestingly I'm not seeing the judder stuttering using the second method. If you change in DrawTailPixels for (uint16_t index = 0; index < strip.PixelCount(); index += 3)

and then change animations.StartAnimation(0, 100, LoopAnimUpdate);

It all runs fine now.

Makuna commented 4 years ago

The debug out confirms that it should not be having issues as it has plenty of time and consistent time to update the pixels.

So it seems I2s DMA isn't writing a frame about every 18th one sent. I am escalating to the Esp32 folks now.

xj650t commented 4 years ago

Captured using an iPhone SlowMo https://www.youtube.com/watch?v=-STZt7L61FY Judder is about 10s in, this is with the Neo800KbpsMethod

xj650t commented 4 years ago

Thanks so much for the great library and your help, I'll continue developing with the RMT method for just now.

Makuna commented 4 years ago

Until we find what's going on in the lower core, I switch the default over to RMT chan 6.

poelzi commented 4 years ago

Since this is related see https://github.com/esphome/issues/issues/858

I just discovered that my random flickers on i2s very much depends on the wifi state. When the wifi is not connected, there is no flickering. As soon as the wifi connects, flickering starts. Interestingly, RMT is currently broken on esphome for unknown reasons :(

drzony commented 4 years ago

I'm creating a rather complex project which uses a lot of networking (ESPAsyncWebServer - with WebSockets, AsyncMqttClient, AsyncUDP) and it seems that there are some problems with timings on ESP32 with all methods. Here are my observations for various LED libraries (using 72 LEDs, updates every 20ms): NeoPixelBus (RMT) - periodic flickering when sending AsyncMqtt messages, almost every time I send a message (every 3 sec) I get random LEDs blinking with a different color for example: all LEDs set to red, I get a couple of green LEDs. Separating Show() into a different task according to this suggestion helps a bit, but not much. NeoPixelBus (I2S) - This is almost OK, it seems to work when something is displayed, but when clearing the LEDs in every loop, once every couple of minutes I get a white flash on all LEDs. It seems that separating this method into a separate task helps this (I'm still confirming, but seems better). FastLED (RMT) - the same behavior as in NeoPixelBus (RMT) FastLED (I2S) - this one seems to be the solution that works for me, no random blinks, no white blinks, even without a separate task.

I'll post my additional findings about RMT in #321 not to mix issues (or maybe we could make some other issue that can combine ESP32 problems)

Makuna commented 4 years ago

@drzony Thanks for cross checking with FastLED. I will examine what he is doing in his i2s support and look for differences. Are you running my latest github version or the released? Github has a few fixes but I was still seeing the issue. Note that what I am using was written by esprisif for me (check out the file header). Things change in the core and while this used to work great, it stopped functioning at some point.

drzony commented 4 years ago

@Makuna I'm running the latest master

Makuna commented 3 years ago

https://github.com/Makuna/NeoPixelBus/releases/tag/2.6.0 Has some improvements.

Makuna commented 3 years ago

https://github.com/Makuna/NeoPixelBus/pull/427

Makuna commented 3 years ago

https://github.com/Makuna/NeoPixelBus/releases/tag/2.6.2