Neopixel PULSE_LONG_TICKS still not long enough

Basic Information:

Printer Model: None
MCU / Printerboard: Raspberry Pi Pico
Host / SBC: Linux PC
klippy.log

klippy.log (47.4 KB)

I flashed Klipper to a Raspberry Pi Pico and configured a printer with only neopixels. I use a SN74LVC1G17 to shift the signals to 5V. I tested with various RGB and RGBW LEDs. All worked correctly except for the ones marked “BIGTREETECH SB RGB KIT”, ie the only ones that I want to use on my actual printer.

I test by setting all LEDs to 0.5 brightness. This results in the bit string “10000000” for each LED.

This should cause all LEDs to light at 50% brightness, so they should appear white. What actually happens instead is that there is about a 50% chance that each 1 is recognized as a 0, and the LEDs change to a random colour. Usually all of them the same colour but not always.

I force Klipper to send the same bitstring repeatedly by using the INDEX option to only change the WHITE value of the last LED in the chain between 0 and 0.01. This means the first two always receive the same bits (and I have confirmed this with a scope). But they change to different colours each time this macro is run.

I can fix it by increasing PULSE_LONG_TICKS to 700. For maximum compatibility it should probably be much higher.

This happens because the value chosen for PULSE_LONG_TICKS is right at the lower limit of what is allowed. The different values given in various datasheets are all minimums, so the chosen value should be well above all of them.

I would say this discussion is related:

But your MCU should already have those fixes:

Args: ['/home/al/klipper/klippy/klippy.py', '/home/al/printer_data/config/printer.cfg', '-I', '/home/al/printer_data/comms/klippy.serial', '-l', '/home/al/printer_data/logs/klippy.log', '-a', '/home/al/printer_data/comms/klippy.sock']
Git version: 'v0.13.0-377-g877b63a2'
Branch: master
Remote: origin
Tracked URL: https://github.com/Klipper3d/klipper
CPU: 32 core AMD Ryzen 9 9950X 16-Core Processor
Device: MS-7E51
Linux: Linux version 6.14.0-29-generic (buildd@lcy02-amd64-033) (x86_64-linux-gnu-gcc-14 (Ubuntu 14.2.0-19ubuntu2) 14.2.0, GNU ld (GNU Binutils for Ubuntu) 2.44) #29-Ubuntu SMP PREEMPT_DYNAMIC Thu Aug  7 18:32:38 UTC 2025
Python: '3.13.3 (main, Aug 14 2025, 11:53:40) [GCC 14.2.0]'
...
Loaded MCU 'mcu' 132 commands (v0.13.0-375-gba79d72fb / gcc: (15:14.2.rel1-1) 14.2.1 20241119 binutils: (2.42-1ubuntu1+23) 2.42)

Taking into account the whole story about Neopixels in the past year, I was somewhat surprised that you had any issues in the way that you are describing.

2 Likes

The linked thread started as a request to increase PULSE_LONG_TICKS and ended up being resolved with some improvement to the timing code and no increase to the pulse duration. Because the pulse length is right on the very edge of what is acceptable, any slight change to the code that makes them slightly longer will fix it for some people, and that seems to be what has happened. The fundamental problem remains though: there is no upper limit to how long the 1 pulse can be, so it is unnecessary to make it the absolute minimum that the spec allows.

Every other neopixel library out there makes the 1 pulse at least 700ns and they don’t have any compatibility problems. I tested this Adafruit implementation with my problem LEDs and it worked fine.

This uses a 700ns 1 pulse. It use PIO so the speed of micropython does not influence the result. I ported it to C++ anyway. Note that Adafruit invented the term NeoPixel, so they are the ultimate authority on what a NeoPixel actually is.

This uses PIO with an integer divider meaning the timings are extremely accurate. With the BTT LEDs I could not reproduce the case where pulses are only sometimes recognized as a 1 with this set up. At 650 it is always 0. At 700 it is always 1. However switching to a fractional divider, which makes the pulses vary in length, does reproduce it.

With Klipper, the variance in pulse length seems to increase as you ask the firmware to do more things, so it is worse on a real toolhead than on my example which just has neopixels and nothing else. But it is still reproducible.

Another thing is that these LEDs are not gamma corrected. There is not much visual difference between brightness at the top end of the scale, while it is huge at the low end. If you send 100% ie “11111111” you will nearly always get at least some 1s and you will see approximately the colour you asked for. You won’t notice the problem if you aren’t looking for it. However when you send 50% ie “10000000” you can only ever get 50% or 0% and those are very visually distinct. But people probably aren’t sending 50% brightness in a loop to look for this bug.

Welcome ali1234,

CircuitPython is not the same as Klipper. You could start a pull request Pull requests · Klipper3d/klipper · GitHub. You might read in Klipper documentation before starting, what’s needed to start a pull request. I never started a request, but I think there are some regulations before starting. Maybe someone can help out here?

For what it is worth, if it can be shown that there is widespread deployment of alternative values for PULSE_LONG_TICKS (or EDGE_MIN_TICKS or BIT_MIN_TICKS) then I think we can change the Klipper code to match.

If that is the case, please open a PR with the proposed values.

Well, if you want my 2 cents, there is no inherent authority for these chips. The only authority would be widespread testing of actual chips in use. If Adafruit has widely deployed slightly different timing than I think that is useful, but only in that it indicates there are many real-world users successfully utilizing that timing.

Cheers,
-Kevin

I believe I can make a strong argument for that, backed up by references to Adafruit code and blog posts, datasheets etc, and I am happy to do so on a PR.

How would you feel about making all the timings configurable? The Neopixel code already sends some timing information from klippy to the firmware, so why not send the pulse times too and let users tweak them if they have problems? In addition to improving the defaults for better compatibility of course.

:wink:

nonsense, la la land (stupid 10 char. thing)

The timing of neopixels can be demanding especially on older micro-controllers. I did not make the low-level bit timing configurable because it was not clear to me if older micro-controllers could reach the desired timing if they also had to load the timing values from memory. So, I made the high-level timing configurable, but left the low-level timing as C code constants.

So, it’s fine to make additional values configurable, but doing so would also require testing on a wide range of older micro-controllers. I expect that would dramatically increase the work.

Cheers,
-Kevin

2 Likes

That’s a very good point. I actually didn’t realise Klipper supports the old 8 bit stuff.

PR is done: neopixel: Increase PULSE_LONG_TICKS to 800 for compatibility by ali1234 · Pull Request #7113 · Klipper3d/klipper · GitHub