nodemcu / nodemcu-firmware

Lua based interactive firmware for ESP8266, ESP8285 and ESP32
https://nodemcu.readthedocs.io
MIT License
7.62k stars 3.12k forks source link

ESP32 WS2812 module not working as documented with M5Stack Hex neopixel board #3405

Open jmdasnoy opened 3 years ago

jmdasnoy commented 3 years ago

The ws2812 module is meant for driving addressable leds a.k.a. "Neo Pixels". The documented examples don't work as described on an ESP-32 driving a recent M5Stack hex neopixel board (37 SK6812 GRB leds) Symptoms include flickering during writes, bad pixel addressing or colours, incomplete fills.

One workaround is to repeat each write instruction ws2812.write( {pin = hexpin, data = buffer} ) ; ws2812.write( {pin = hexpin, data = buffer} )

or to issue an empty write before each effective write ws2812.write( {pin = hexpin, data = ""} ) ; ws2812.write( {pin = hexpin, data = buffer} )

The ws2812 module issues a reset signal to the leds before writing out the effective data. This signal currently has a length of 50 µS. It may have been long enough for older neopixels, but the SK6812 pixels on the M5Stack board seem to expect a reset pulse longer than 80 µS. Available datasheets are quite laconic or cryptic...

The workaround of an empty write before a full data write seems to work by issuing two back to back reset signals before sending out data.

I have modified the timing definitions in components/platform/ws2812.c to provide a 90 µS reset pulse. I have also adapted the bit on/off timings to add up to 1.2 µS.

With these changes, the documented examples (and many others) work reliably and as expected. A longer reset pulse should not cause any problems to older leds, so this proposed change should not break any existing code or applications.

Existing code also assumes an 80 MHz APB clock. This could also be rewritten to use the effective APB clock frequency.

If this is of interest or use to the community, I will provide a pull request with these changes and updated documentation. Let me know your thoughts.

f4grx commented 2 years ago

hello yes, I am interested, I had to do the same kind of code changes for my own m5stack, but it would be cool to have it upstream.

the timing should be configurable by indicating the neopixel model to use.

jmdasnoy commented 2 years ago

@f4grx thanks for your interest and comments. It's nice to know I wasn't the only one to struggle with this ! What code changes did you make to get this working ? Are you planning to make a pull request ? Your suggestion of configurable timing is interesting but it should not break existing code, so this needs some more thought... Regards.

f4grx commented 2 years ago

I just changed the timings in the cod to match the specs of these particular leds, because I have a specific source tree for the m5 stack.

Unfortunately I did not make this code configurable in any way :(

marcelstoer commented 2 years ago

I am no expert on the subject but after watching https://youtu.be/rHoFqKGOPRI?t=560 I at least understand the basic principle of how these pixels work.

So, yes, the timing for the SK6812 does appear to be incompatible with the ws2812.

jmdasnoy commented 2 years ago

To summarise: The SK6812 is meant and designed to be compatible with the WS2812. A very few, minor, changes to the existing ESP32 module will make it work reliably with both. I have mentioned those changes in my initial post and have been using them reliably for several months.

Bit timing: The protocol is simple and quite forgiving. Bits are sent as positive pulses, every 1.25 µs, for a data rate of 800 Kbps. A short pulse for a 0, a longer pulse for 1. The led controller will sample the data line, halfway through the expected bit time, i.e. 0.625 µs after the pulse rising edge and classify the bit accordingly. With the stated timing tolerance of +/-0.15 µs, a pulse shorter than 0.475 µs will be classified as a 0, a pulse longer than 0.775 µs as a 1. Typical values in datasheets, libraries or the Youtube video mentioned above are in the range 0.35-0.45 µs for a 0, 0.8-0.95 µs for a 1. I have adapted my code tree to 0.4 µs and 0.8 µs values, as it works with a limited granularity of 0.1 µs increments.

The low i.e idle part of the pulse should last whatever is needed to make the timing add up to 1.25 µs. (1.2 µs for my code)

Some datasheets are wrong, as stated in the Adafruit Uberguide for neopixels, as the total timing for a 1 and a 0 are not equal. This is also the case for the current ESP32 module... but it still works.

Reset timing: This is the main issue. WS2812 expect the reset to last more than 50µs, the SK6812 requires a longer reset, more than 80µs. There are no upper bounds on the reset timing, so using the longer timing should work reliably for both.

As stated previously, I will prepare a PR if it is considered useful by the community.

marcelstoer commented 2 years ago

@jmdasnoy thanks for putting into words what I learned from the video 😄

I will prepare a PR if it is considered useful by the community.

We'd certainly appreciated that.

f4grx commented 2 years ago

It is great to learn that the changes are so simple. Also appreciated, thanks.

jmdasnoy commented 2 years ago

Regards and thanks for your positive comments. I will submit a PR accordingly.