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

Neopixels not working correctly #159

Closed

Description

@mattytrentini

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?

Activity

  1. MrSurly commented on Aug 8, 2017

    @MrSurly
    Contributor

    @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
        }
  2. dpgeorge commented on Aug 9, 2017

    @dpgeorge
    Member

    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.

  3. nickzoic commented on Aug 9, 2017

    @nickzoic
    Collaborator
  4. mattytrentini commented on Aug 9, 2017

    @mattytrentini
    SponsorAuthor

    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...

  5. dpgeorge commented on Aug 9, 2017

    @dpgeorge
    Member

    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.

  6. mattytrentini commented on Aug 11, 2017

    @mattytrentini
    SponsorAuthor

    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!

  7. NyxCode commented on Aug 17, 2017

    @NyxCode
    Contributor

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

  8. mattytrentini commented on Aug 17, 2017

    @mattytrentini
    SponsorAuthor

    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.

  9. mattytrentini commented on Aug 19, 2017

    @mattytrentini
    SponsorAuthor

    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...

  10. NyxCode commented on Aug 19, 2017

    @NyxCode
    Contributor

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

    T0H = 0.5us
    T0L = 2.0us
    T1H = 1.2us
    T1L = 1.3us
    
  11. mattytrentini commented on Aug 19, 2017

    @mattytrentini
    SponsorAuthor

    (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.
    
  12. mattytrentini commented on Aug 20, 2017

    @mattytrentini
    SponsorAuthor

    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...

  13. nickzoic commented on Aug 21, 2017

    @nickzoic
    Collaborator
  14. CrabbyPete commented on Sep 7, 2017

    @CrabbyPete

    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)
    
    
    
  15. mattytrentini commented on Sep 8, 2017

    @mattytrentini
    SponsorAuthor

    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.

  16. 11 remaining items

  17. ThomasWaldmann commented on Nov 28, 2017

    @ThomasWaldmann

    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.

  18. red-mist commented on Feb 14, 2018

    @red-mist

    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.

  19. nickzoic commented on Feb 15, 2018

    @nickzoic
    Collaborator
  20. Avi-TelnT commented on Feb 15, 2018

    @Avi-TelnT

    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.

  21. mattytrentini commented on Feb 15, 2018

    @mattytrentini
    SponsorAuthor

    ...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.

  22. icb- commented on Feb 15, 2018

    @icb-

    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.

  23. Avi-TelnT commented on Feb 15, 2018

    @Avi-TelnT

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

  24. ThomasWaldmann commented on Feb 15, 2018

    @ThomasWaldmann

    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.

  25. icb- commented on Feb 15, 2018

    @icb-

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

  26. EricPobot commented on Apr 28, 2018

    @EricPobot

    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

  27. icb- commented on Apr 28, 2018

    @icb-

    @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.

  28. CrabbyPete commented on Apr 28, 2018

    @CrabbyPete
  29. EricPobot commented on Apr 29, 2018

    @EricPobot

    @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.

  30. dpgeorge commented on Jan 23, 2019

    @dpgeorge
    Member

    Fixed upstream in cd52d2c691be0dd11e3a1104dc6eac3b3173d792

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions