Skip to content
This repository has been archived by the owner on Sep 6, 2023. It is now read-only.

Neopixels not working correctly #159

Closed
mattytrentini opened this issue Aug 8, 2017 · 40 comments
Closed

Neopixels not working correctly #159

mattytrentini opened this issue Aug 8, 2017 · 40 comments

Comments

@mattytrentini
Copy link
Sponsor

Discovered on PyCon AU sprint day.

Using default values, a set of Neopixels don't seem to operate correctly and, instead, flicker at maximum brightness.

A work-around, changing an undocumented parameter, was discovered.

def fade_in(pixel, max_brightness=80, delay=20):
    for i in range(max_brightness):
        pixel.fill((i,i,i))
        pixel.write()
        time.sleep_ms(delay)

pixel = neopixel.NeoPixel(machine.Pin(12), 8)
fade_in(pixel)
# Flickers like crazy; note that the default for 'timing' is False

pixel = neopixel.NeoPixel(machine.Pin(12), 8, timing=True)
fade_in(pixel)
# Works as expected

What is the intent of the timing parameter? Should the default for ESP32 (like ESP8266!) be True? Or does a False value correctly work on one of the other Neopixel variants (APA10x) support?

@MrSurly
Copy link
Contributor

MrSurly commented Aug 8, 2017

@mattytrentini

Haven't used the neopixel code myself, but from esp32/espneopixel.c, inside the neopixel_write function, timing is treated as an integer (probably True == 1 and False == 0), and used to set pixel bus timing.

Look at the actual code, not the comments, as in the else block it seems the comments weren't updated after copy/paste.

Do you know the timing for your particular neopixels?

    if (timing == 1) {
        // 800 KHz
        time0 = (fcpu * 0.35) / 1000000; // 0.35us
        time1 = (fcpu * 0.8) / 1000000; // 0.8us
        period = (fcpu * 1.25) / 1000000; // 1.25us per bit
    } else {
        // 400 KHz
        time0 = (fcpu * 0.5) / 1000000; // 0.35us
        time1 = (fcpu * 1.2) / 1000000; // 0.8us
        period = (fcpu * 2.5) / 1000000; // 1.25us per bit
    }

@dpgeorge
Copy link
Member

dpgeorge commented Aug 9, 2017

See #17 for the original discussion around adding this code.

It seems that the intent was to also support APA106 which looks like it has slower timing than the WS2812. The "timing" parameter should be renamed to "is800khz" like esp8266 has it, and it should be set to True by default to work with WS2812. Then the APA106 driver would set this variable to False to get correct timing for it.

@nickzoic
Copy link
Collaborator

nickzoic commented Aug 9, 2017 via email

@mattytrentini
Copy link
Sponsor Author

Hi @MrSurly,

Thanks for your quick response. :)

I've come across that code and I believe the comments correctly indicate the operating frequency. I think my Neopixels are 800KHz devices. A few notes relevant to this issue:

  1. I believe the vast majority of Neopixels (more accurately, WS2812's) operate at this frequency. The WS2812 datasheet and these (1, 2) Adafruit articles seem to support that assertion.
  2. There is no Micropython documentation I've been able to find that mentions that timing parameter. The only places I've found reference to Neopixels at all are in the ESP8266 quickref, the ESP8266 Tutorial, the BBC micro:bit documentation and @deshipu's ESP8266 Workshop. We only found it by looking at the code.
  3. Given the timing can be configured, it should be an enumeration at the API, something like:
    pixel = neopixel.NeoPixel(machine.Pin(12), 8, operating_freq=NeoPixel.800KHz)
  4. The ESP8266 implementation is different. If I understand right it defaults to 800KHz (note the True in write). It also does use the term 'is800Khz' in the C API.

So, I guess my suggestion is to:

  • Make the default 800Khz
  • Allow the timing to be controlled with an enumeration
  • Reuse the same code where possible with the ESP8266 (I think it ought to be possible to use neopixel.py if not the C implementation)
  • Update the documentation across at least the ESP32 and ESP8266 ports

I'm happy to help.


I just saw @dpgeorge and @nickzoic's responses...

Looks like we roughly agree @nickzoic.

I've read #17 @dpgeorge, and yes, we should support APA106 (and APA102 and 104's). I thought the benefit of the APA series was that it could be driven by using SPI. But I've not used them nor do I have any to experiment with...yet - I do have one on order, so I'll be able to test soon. If it is just a slower rate and a reordering of RGB then I agree with you. Otherwise, if they require SPI, then it should be a different class. It's also not clear to me what the differences are between the 102/104/106 yet...

@dpgeorge
Copy link
Member

dpgeorge commented Aug 9, 2017

really, it should be something more like: ... (from @nickzoic)
Given the timing can be configured, it should be an enumeration at the API, something like: ... (from @mattytrentini)

An alternative to adding enums, or specifying the frequency as an integer, is to specify the 3 timings directly in nanoseconds, eg: neopixel_write(pin, buf, 350, 800, 1250). This makes the function more general purpose.

Otherwise, if they require SPI, then it should be a different class. It's also not clear to me what the differences are between the 102/104/106 yet...

It's also not clear to me... looks like 102 is SPI based while 106 is 1-wire timing based.

@mattytrentini
Copy link
Sponsor Author

Having just pored over a bunch of data sheets riding the train to work...the APA range is marginally clearer to me. ;)

As best I can tell the APA104 and APA106 are intended to be compatible with the WS2812 protocol. One wire for control, 800KHz (by the way, that seems to be a confusing unit when comparing addressable LED systems - it may be better to use data rate which would be 800kbps for these devices). It seems the different part numbers indicate different packages; the 104's seem to be 4-pad SMD, 106's are 4-pin thru-hole.

In any case I believe the same protocol can be used for the APA104, APA106 and WS2812.

The APA102's are six-pad SMD's using a two-wire protocol that is intended to be driven by an SPI signal. They allow much update higher frequencies (20+Mbps). Fast enough to be used for persistence of vision applications. These devices have significant improvements over all of the one-wire systems (faster updates, less timing critical, more powerful protocol, lower power) but cost more and, obviously, need two wires. Adafruit brands these as Dotstar's to differentiate them from Neopixel's which is their marketing name for the WS2812's.

But I'm still learning! I'm trying to get a wider understanding of all the devices out there (there are at least a dozen; mostly similar but subtly different) to try to make sure we have the right abstractions.

Worth noting; the only devices I've seen referenced that need the half-speed timing are the first-gen WS2811's. They were superseded by the second-gen WS2811's and then the WS2812's. It's not clear to me how many of them are in the wild.

The FastLED Arduino library is turning out to be a decent reference as they support many different addressable LED models. I'll get more acquainted with their code in the next couple of days...

If anyone knows more, please chime in!

@NyxCode
Copy link
Contributor

NyxCode commented Aug 17, 2017

For me, the snipped works without flickering. I am using PL9823, which are equivalent to WS1812B.

@mattytrentini
Copy link
Sponsor Author

Huh @NyxMC, that's interesting! They are not exactly equivalent but practically are. Maybe they're not quite so sensitive to timing? Or maybe the WS1812B's I have aren't actually WS1812B's (bought mine from AliExpress)?

It's tough supporting all these timings and protocols for all these different addressable LEDs. Just understanding the differences takes a lot of time...

I'm trying to get a hold of a digital oscilloscope over the weekend so I can take some measurements about what the Neopixel implementation is actually doing on the ESP32 - and ESP8266 if I get time.

@mattytrentini
Copy link
Sponsor Author

mattytrentini commented Aug 19, 2017

I made some quick measurements today with a (old and pretty rudimentary!) digital oscilloscope. The summary is that there are no real surprises; with timing=True the protocol implementation is very close to the WS2812B spec. But with timing=False it's not close enough to be reliable for WS2812B's.

So, the numbers. With timing=True:

T0H = 300ns
T0H+T0L = T1H+T1L = 1.275us
T1H = 750ns

These numbers are all within 150ns of spec.

And with timing=False:

T0H = 500ns
T0H+T0L = T1H+T1L = 2.5us
T1H = 1.1us

Obviously, a little beyond spec. I found that even with timing=False a couple of LED's in the chain could be driven - but larger numbers were unreliable. My ring of eight never worked correctly when all were driven.

All I've really done here is confirm that the code works as intended (and the comments need updating for the timing=False case). But I still feel it's still good to have these datapoints.

I'll try and measure the ESP8266 while I still have the CRO at home this weekend...

@NyxCode
Copy link
Contributor

NyxCode commented Aug 19, 2017

@mattytrentini I can confirm your measurements.
With timing=False

T0H = 0.5us
T0L = 2.0us
T1H = 1.2us
T1L = 1.3us

@mattytrentini
Copy link
Sponsor Author

mattytrentini commented Aug 19, 2017

(Thanks @NyxMC!)

And the ESP8266:

import neopixel
import machine
pixel = neopixel.NeoPixel(machine.Pin(2), 1)
pixel.fill((0,0,0))
pixel.write() # Measure T0
pixel.fill((255, 255, 255))
pixel.write() # Measure T1

Very close to ideal:

T0H = 350ns
T0H+T0L = T1H+T1L = 1.28us
T1H = 800ns

An aside: Looking at esp8266/espneopixel.c I thought I ought to be able to try 400KHz by passing False when initializing NeoPixel...alas the device resets when write() is called:

>>> pixel = neopixel.NeoPixel(machine.Pin(2), 1, False)
>>> pixel.fill((0,0,0))
>>> pixel.write()

[Boom! Reset. Output snipped]

MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.

@mattytrentini
Copy link
Sponsor Author

Loboris has been beavering away on extending the ESP32 port to allow access to the large amount (4MB is common) of PSRAM available in some ESP32 configurations. As well as increasing the available RAM, other features have begun slipping in...

In a recent forum post Loboris mentioned that the Neopixel implementation has been improved.

It's a very flexible solution, allowing timing and colour order to be configured. Colour can be specified in HSB as well as RGB - and brightness can be set independently of colour. Finally, rather than bit-banging, the RMT module (see chapter 12 of the ESP32 Tech Reference Manual) was used to generate accurate output.

It looks like a very promising implementation.

Unfortunately Loboris' fork has drifted away somewhat and merging it back upstream may prove difficult. That said, it shouldn't be too challenging to just extract this Neopixel implementation if we wish...

@nickzoic
Copy link
Collaborator

nickzoic commented Aug 21, 2017 via email

@CrabbyPete
Copy link

I tried this with a 100 LEDs and it is really very bad. The colors are wrong, and there is loads of flicker

Here is my simple code

import time
import machine, neopixel

def demo(np):
    n = np.n

    np.fill((0,0,0))
    time.sleep_ms(1000)

    for i in range(n):
        np[i] = ( 0,100,0 )
        np.write()
        time.sleep_ms(100)

def run():
    np = neopixel.NeoPixel(machine.Pin(4), 100, timing = 1 )
    demo(np)


@mattytrentini
Copy link
Sponsor Author

Thanks for the report @CrabbyPete.

I have a longish WS2812B strip at home; can't remember how many LEDs but I'll try and reproduce the issue this weekend. It's especially helpful that you supplied your code, much appreciated. :)

If it is reproducible on larger numbers of LEDs I'll need to analyse the timing again but this time at the end of the strip.

@mattytrentini
Copy link
Sponsor Author

I tried reproducing the issue on my strip at home but I couldn't convince any of the LEDs to light. Tried with the ESP32 and ESP8266; I suspect I've done damage to the strip. I've ordered another but it might take awhile to arrive.

Anyone else have a WS2812B strip to test with?

@NyxCode
Copy link
Contributor

NyxCode commented Sep 11, 2017

@mattytrentini I got Pl9823 in a 5mm package. I'll chain 10 of them together and try it out.

@CrabbyPete
Copy link

CrabbyPete commented Sep 11, 2017 via email

@NyxCode
Copy link
Contributor

NyxCode commented Sep 11, 2017

Edit: MY LEDs ARE PROBABLY BUSTED

I am pretty suprised on how bad it works...
This is my code:

When I call run(0), this is what I get: Video
When I call run(1), this is what I get: Video
As you can see, it kinda works, with a lot of glitches. Especially the first LED acts pretty weird. For me, the timing parameter doesn't make a big difference.

My setup:

External power supply with 5V
10 PL9823 (should be compatible with WS2812B) LEDs chained together
The code:

import time
import machine, neopixel

def demo(np, r, b, g):
    n = np.n
    np.fill((0,0,0))
    np.write()
    time.sleep_ms(1000)

    for i in range(n):
        np[i] = ( r,b,g )
        np.write()
        time.sleep_ms(100)

def run(t):
    np = neopixel.NeoPixel(machine.Pin(4), 10, timing = t )
    demo(np, 255, 0, 0)
    demo(np, 0, 255, 0)
    demo(np, 0, 0, 255)
    demo(np, 255, 255, 255)
    np.fill((0, 0, 0))
    np.write()

@robert-hh
Copy link
Contributor

robert-hh commented Sep 11, 2017

I ran the test code above with a neopixel ring of WS2812B's. It runs pretty well. All LEDs behave fine.
Board: Sparkfun ESP32 Thing
Firmware: (sysname='esp32', nodename='esp32', release='1.9.2', version='v1.9.2-270-g14fb53e on 2017-09-10', machine='ESP32 module with ESP32')

Edit: I hooked up an oscilloscope. The timing is pretty stable w/o jitter. I used infinite persistence for displaying, and there us no deviation in timing. Values:
t=0: period 2.54µs, T1H = 1.2µs, T0H = 470ns
t=1: period 1.28µs, T1H = 780ns, T0H = 360ns

@NyxCode
Copy link
Contributor

NyxCode commented Sep 11, 2017

@robert-hh damm, that means my leds are busted... I'll buy some new ones. ( :

@CrabbyPete
Copy link

CrabbyPete commented Sep 11, 2017 via email

@robert-hh
Copy link
Contributor

robert-hh commented Sep 11, 2017

@NyxCode According to the video it looks like at least the first one may be broken. That can have an impact on the others, but maybe the cabling too. I'm using an adafruit ring (which I just had in the drawer), with a lot of filtering capacitors on the board, and very short connections between the devices, wheres you cabling may cause a lot of crosstalk and noise.
Edit: This is a adafruit recommendation: "Place a 300 to 500 Ohm resistor between the Arduino data output pin and the input to the first NeoPixel. The resistor should be at the end of the wire closest to the NeoPixel(s), not the microcontroller." The device I'm using has that built in.

@Djsharma07
Copy link

Hello @robert-hh, @NyxCode.

I'm using 3 WS2812B LEDs in a chain and I'm using Pycom's LoPy(Micropython enabled) which has Espressif ESP32 Chipset.
Firmware: (sysname='LoPy', nodename='LoPy', release='1.9.2.b2', version='v1.8.6-796-g489fafa0 on 2017-10-15', machine='LoPy with ESP32', lorawan='1.0.0')

However, I'm not able to find the neopixel library for it. I've tried to use this library, but it didn't work.

@nickzoic
Copy link
Collaborator

nickzoic commented Nov 9, 2017 via email

@nickzoic
Copy link
Collaborator

nickzoic commented Nov 9, 2017 via email

@ThomasWaldmann
Copy link

ThomasWaldmann commented Nov 28, 2017

UPDATE: one of my LED rings is defective, other works flawlessly!

Also having troubles getting a correctly functioning 24 LED strip (round 360 degree LED arrangement).

About the first 75% of the strip works correctly, then 1 LED is often completely off, then 1 with with often wrong and way-to-dark color, then the rest with higher brightness or completely wrong color. The last two are also sometimes completely off.

It looks like a timing error builds up and hits the threshold after ~18 LEDs.

Tried with timing = 0 or 1, same thing.

WeMOS ESP32 board with OLED display. Micropython recent build from yesterday.

@red-mist
Copy link

Just wanted to point out that esp32 output is 3.3v and ws1812 leds commonly take 5v. At that voltage, the controller chip doesn’t recognise the transition anymore and you can expect strange behaviour. The correct solution would be a level shifter on the output; or to feed the leds on 3.3v. There’s a hack with a diode and an extra ws2812 as well.
Very curious about the rmt peripheral implementation, sounds totally sweet.

@nickzoic
Copy link
Collaborator

nickzoic commented Feb 15, 2018 via email

@Avi-TelnT
Copy link

Avi-TelnT commented Feb 15, 2018

The working solution is to place diode in serial with 5V feeding ws1812 VDD.
Diode drops on itself 0.7V. So ws1812 gets 4.3V
So ws1812 VDD become 4.3V
0.7*4.3=3.01 that is working perfectly with ESP32 GPIO output sending 3.3V
Since ESP32 GPIO is only output, it does not distorted by that high voltage.

@mattytrentini
Copy link
Sponsor Author

...at the cost of a slightly dim first Neopixel.

Level shifters are a good option too but take some care that the one you choose can switch fast enough.

@icb-
Copy link

icb- commented Feb 15, 2018

One of the easiest, most commonly accepted level converters for driving a WS2812 (and clones) from a 3.3v signal is a 74AHCT125 buffer. ESP32 outputs at least 0.8*vcc (2.64v when powered at 3.3v) The 74AHCT125 sees anything over 2v as high, and is perfectly capable of switching fast enough (worst case 6.5ns rise/fall time). They're available in DIP14 with 4 buffers (e.g. SN74HACT125N, 40¢ in quantity 1 from Digikey) down to SOT23 for a single buffer (e.g. SN74AHCT1G125DBV, 43¢ in quantity 1 from Digikey) and smaller.

@Avi-TelnT
Copy link

@icb,
Did you tested SN74AHCT1G125DBV yourself and it is working ?

@ThomasWaldmann
Copy link

Note: after encountering troubles and reading the data sheet, I also noticed the too low high level when the neopixels are at 5V. In practice though (with the not defective neopix ring), it works for me without any level shifting or diode or whatever.

Guess it depends on whether you build something just to play around or for "production".
In the first case, you can just try your luck and it might just work.
In the latter case, I guess one would go the no-risk way and use the diode or level shifter to be within the spec'ed range.

@icb-
Copy link

icb- commented Feb 15, 2018

@Avi-TelnT I haven't personally tested the SN74AHCT1G125DBV, only the SN74AHCT125N.

@EricPobot
Copy link

WRT NeoPixel supply voltage, it must be noted that the WS2812 uses internally two voltages : 5V for the LEDs and 3V3 for the logic. The WS2812 comes in (at least) two versions :

  • the older one has 6 pins and separated ones for VDD (LEDs) and VCC (logic). The maker's datasheet shows how to generate VCC from VDD, using a simple resistor.
  • the newest version (WS2812B) has only 4 pins, with a single supply pin which has to be powered with 5V, the logic voltage being generated internally.

True Neopixels (from Adafruit) uses the WS2812B (4 pins) and have a "5V" indication on the silk screen. Beware that cheap clones sold from Banggood and alike are not true copies of the Neopixels. They use the 6 pins WS2812 version, and connect directly VCC and VDD on the PCB to spare the cost of the drop down resistor. This is bad since, apart if you supply the board with 3V3, it powers the logic above the specs.

I've noticed that this leads to misbehaviours when you power clones from the 5V output pin of ESP boards (flashes, or wrong colours sometimes), while it works fine if you use the 3V3 output from the embedded regulator (at the price of a lower brightness of course). This does not occur with the genuine Neopixels from Adafruit powered from the 5V pin of the ESP board.

WRT control signal, although it is a 3V3 logic one in theory, it seems to be 5V tolerant (I haven't blown any of my beasts until now even when connecting them to a 5V output on an Arduino board). Anyway, adding the resistor on the control line as suggested by Adafruit is a good practice. It adds an attenuation on the signal, which brings it closer to the 3V3 range when using 5V output logic signals.

In addition, even if what has been said in other messages here about 3V3 logic signal being correctly recognized by a 5V logic input is 100% correct, I don't think it is relevant here, since the WS2312 logic is 3V3. Anyway, the bottom line is that you don't need any level shifter here.

Disclaimer: the above sayings are the result of my own experiments and documentation understanding. I can't guaranty a 100% correctness and accuracy. So take it at your own risks as usual and don't hesitate to correct me if relevant

@icb-
Copy link

icb- commented Apr 28, 2018

@EricPobot You're going to need to back that up with some data sheets. What I see on the Adafruit hosted data sheet for the WS2812B is that minimum voltage for a logic high is 0.7*Vdd (3.5v for a 5v supply). A high from a 3.3v output might work, but is by no means guaranteed to.

@CrabbyPete
Copy link

CrabbyPete commented Apr 28, 2018 via email

@EricPobot
Copy link

@icb You're correct about the datasheet published on the Adafruit site, but it happens that 5V chips generally recognize a 3V3 signal as high. BTW, tech docs often use the 2/3 VDD formula, which makes 0.66 VDD, i.e. 3.3V (maybe 0.7 comes from the rounding to 1 dec. place ;)). The fact that 3V3 works in the vast majority of cases comes from various sources and also from my own experience tinkering with electronics... started in the late 70's ;). It is possible that the reliability of the level detection will not be 100% accurate for very high frequency signals, but chances are that we are not in this context here.

The facts are that until now I've had strictly no problem to drive WS2812B based genuine Neopixels sticks and strips with the 3V3 signals produced by ESPs, although powering them (the NPs) with 5V. A lot of people use RPis too for driving NPs. Maybe this 5V supply with 3V3 signal configuration is at the edge of the nominal conditions, but is seems to work.

@dpgeorge
Copy link
Member

dpgeorge commented Jan 23, 2019

Fixed upstream in cd52d2c691be0dd11e3a1104dc6eac3b3173d792

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests