micropython / micropython-esp32

Old port of MicroPython to the ESP32 -- new port is at https://github.com/micropython/micropython
MIT License
669 stars 216 forks source link

Neopixels not working correctly #159

Closed mattytrentini closed 5 years ago

mattytrentini commented 6 years ago

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 commented 6 years ago

@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 commented 6 years ago

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 commented 6 years ago

really, it should be something more like:

pixel = neopixel.NeoPixel(machine.Pin(12), 8,
timing=neopixel.TIMING_800_KHZ)

... if there's only a few discrete options, or if there's many different frequencies: pixel = neopixel.NeoPixel(machine.Pin(12), 8, frequency_khz=800)

... (then derive period, time0 and time1 as functions of frequency_khz). Obviously, this is going to raise the usual problems of cross port compatibility etc, but I'm just putting it out there anyway :-)

mattytrentini commented 6 years ago

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:

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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

mattytrentini commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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

T0H = 0.5us
T0L = 2.0us
T1H = 1.2us
T1L = 1.3us
mattytrentini commented 6 years ago

(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 commented 6 years ago

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 commented 6 years ago

Yeah, I've been keeping an eye on that too and thinking of doing some cherry-picking to bring the various features back in as individual PRs. The usual "copious free time" limitations exist of course :-(

CrabbyPete commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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

CrabbyPete commented 6 years ago

I can lend you a string.

On Mon, 11 Sep 2017 03:32:40 -0400, mattytrentini
notifications@github.com wrote:

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?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

-- Using Opera's mail client: http://www.opera.com/mail/

NyxCode commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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

CrabbyPete commented 6 years ago

I don’t know if makes a difference, but I am using

(sysname='esp32', nodename='esp32', release='1.9.2', version='v1.9.2-269-g5311cc76 on 2017-09-07', machine='ESP32 module with ESP32')

with the Espressif ESP32 Development Board - Developer Edition

From: robert-hh Sent: Monday, September 11, 2017 12:37 PM To: micropython/micropython-esp32 Cc: Pete Douma; Mention Subject: Re: [micropython/micropython-esp32] Neopixels not working correctly(#159)

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') — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

robert-hh commented 6 years ago

@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 commented 6 years ago

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 commented 6 years ago

I was playing with a 12 x neopixel ring on the weekend ... I don't know precisely which chips it uses (I'll check) but it worked perfectly on ESP8266 and on ESP32 with timing=True ... As discussed upthread, I think we should clean up the timing api a bit on the neopixel library because it's not exactly self documenting at this point ...

nickzoic commented 6 years ago

Sorry, this isn't the right place for support for the PyCom fork ... you'll need to ask at https://pycom.io/support/ I'd very much like the MicroPython/ESP32 forks to re-converge at some point, but at the moment they aren't entirely in sync.

ThomasWaldmann commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

Interesting ... in practice, it seems to work for most people, but you're correct that the datasheet specifies V_IH > 0.7 * V_DD so 3.3V isn't enough, especially if you want to run with V_DD = 7V. A level shifter might help with what seem to be software timing errors ...

Avi-TelnT commented 6 years ago

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 commented 6 years ago

...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- commented 6 years ago

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 commented 6 years ago

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

ThomasWaldmann commented 6 years ago

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- commented 6 years ago

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

EricPobot commented 6 years ago

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 :

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- commented 6 years ago

@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 commented 6 years ago

The signal will work at 3.3V, but you need to power the lights separately. I bought a 20 amp 5V supply to power over 400 lights. I hooked up the output of a raspberry pi, an ESP32, and even the Chip computer and all worked to drive the signal.

On Apr 28, 2018, at 10:31 AM, Ian Bobbitt notifications@github.com wrote:

@EricPobot https://github.com/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 https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf 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.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/micropython/micropython-esp32/issues/159#issuecomment-385180273, or mute the thread https://github.com/notifications/unsubscribe-auth/AAFSGDdZ-HZ4w_vONHY_GQTYsn2-rI2zks5ttH1KgaJpZM4Ow6ye.

EricPobot commented 6 years ago

@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 commented 5 years ago

Fixed upstream in cd52d2c691be0dd11e3a1104dc6eac3b3173d792