Aircoookie / WLED

Control WS2812B and many more types of digital RGB LEDs with an ESP8266 or ESP32 over WiFi!
https://kno.wled.ge
MIT License
14.31k stars 3.05k forks source link

Support inverted output for WS281x light types #2574

Open karwosts opened 2 years ago

karwosts commented 2 years ago

Is your feature request related to a problem? Please describe.

Not having the ability to invert output makes implementing a 3.3V->5V level shifter more difficult.

In transistor logic, an inverter is the simplest type of logic gate. TTL-family logic gates or single transistors can work well for level shifting the 3.3V output of an ESP device to the 5V required by led light strips.

If the output data pin from the ESP could be inverted, you could build a 3.3V to 5V level shifter with a single 74LS NAND gate, NOR gate, inverter, or a single discrete transistor.

To buffer the signal from 3.3V to 5V without inverting it, requires double the amount of logic. Either requires two NAND gates in series, two NOR gates in series, or multiple transistors. (In transistor logic a buffer or level shifter is built from two back to back inverters).

It's not necessary to have this, but it would make a lot more options available for creating a 5V level shifter, and requiring less electrical components to do so.

I saw a comment in a forum post a couple years back suggesting that this should be possible to do:

https://wled.discourse.group/t/invert-data-clock/238/4

Describe the solution you'd like A checkbox in the LED settings screen to electrically invert the output of the data pin when a WS281x LED output is selected. (Similar to how the PWM options have this).

blazoncek commented 2 years ago

Please check if NeoPixelBus supports inverting digital output. If yes, it may be incorporated into WLED. If no, you are out of luck.

karwosts commented 2 years ago

I think the support was added here:

https://github.com/Makuna/NeoPixelBus/pull/296

Looks like it was added for the same reason suggested here, to make an easy inverting level shifter.

Aircoookie commented 2 years ago

It indeed seems supported by NeoPixelBus 🎉 Will double the amount of definitions in bus_wrapper.h, but I think it would be nice to have

mcer12 commented 2 years ago

@karwosts you need 2 resistors, 2 transistors to level shift to 5V. You can say it's double the components but you can also say it's just one extra transistor and resistor, price of which is negligible. That said, I don't see any reason not to implement this change since neopixelbus supports it and it can save tiny bit of space.

It should be noted tho that if you design a board for inverted signal and later switch to different library, you'll find yourself in a bit of a pickle :)

blazoncek commented 2 years ago

You do not need transistors at all. Just use inverting level shifters/buffers (74AHCT logic).

bojanpotocnik commented 1 year ago

Can someone more familiar with these sources show roughly where such changes have to be made?

I'm using ESP-WROOM-32 and would like to level-shift the signal to 12 V. Using N-Ch+P-Ch FET or NPN+PNP does not work for some reason because resulting slew rate is too low for 800 kHz (checked with the oscilloscope), and I don't have any non-inverting buffers laying around.

Fiddling with BusOnOff::show() has no effect, but this is one of the rare digitalWrite() usages which I could find, that seemed relevant 🤔

In addition to inverted output, I would also like to try Open-Drain mode without Pull-Up.

Thank you

EDIT: After already giving up, I remembered common emitter/source amplifiers. So I took a N-Ch FET, Gate to 3.3 V, Source to GPIO, Drain to LED DI and via 620 Ohm resistor (20 mA limit) to 12 V. Works fine on 10 m strip of WS2814.

blazoncek commented 1 year ago

Why 12V? All digital LEDs have 5V input logic.

bojanpotocnik commented 1 year ago

You got me thinking... I thought of writing "For driving 30 m LED strip", but each next LED is driven with (I suppose push-pull) DO output of the previous one - so one must just satisfy DI requirements of the first chip.

However, while testing on 5 m WS2814 LED strip (60 LED/m, 3 LED per IC) it becomes unstable after ~3 m if driven with 3.3 V, but works fine with level-shifted to 5 V or 12 V (same power supply). If the first paragraph is correct, than I find this behaviour hard to explain 🤔

jeffeb3 commented 1 year ago

I have 2 H801 devices. They are meant to drive 5V-24V PWM Light strips (not addressable). They have LED drivers on 5 outputs and an esp8266 inside.

I am hoping to use this in a 5V install for 60 ws2812 leds.

The outputs (like 14) are running through an LED output which is low side drive. I believe this means a high signal on the esp results in a ground (sink) on the output and the low results in either floating or Vcc (which will be 5V).

If I tied the output to the data line and powered it only with 5V, then I have these potential hurdles to clear:

It seems like a decent solution? I have two of them and only 52 LEDs to light up. The 4A max output means I can use one of the other channels to act as a relay to power them off when not in use. The container is pretty resonable and the connectors will save me some soldering and crimping. Perfect for inside a kitchen cabinet.

So how do I invert the data line? Is it available on the template.json? Is it a hack and compile?

blazoncek commented 1 year ago

If your device is designed for PWM LEDs then it has (most likely) MOSFETs included. Output of those is not intended for digital LEDs. Also, as the name of this issue suggests, WLED does not support inverted output for digital LEDs.

And using any GPIO other than GPIO2 or GPIO1 for driving digital LEDs is a waste of time and only leads to frustration.

jeffeb3 commented 1 year ago

Thanks for taking time to respond.

Output of those is not intended for digital LEDs

Since when do we only do things based on what was "intended"? Is there a particular reason that MOSFETs wouldn't work for a digital output? Rise time? Jitter? I only know that it is inverted (and after some investigation, it is also floating when off, so I need to add a pull up).

WLED does not support inverted output for digital LEDs.

This issue is about adding the ability to do inverted to digital outputs. In the original case, for level shifting. But in my case, it is for a different reason. NeoPixelBus supports it, so I am asking if there is a place where I can edit the code or settings to invert the output. Seems like a reasonable question to me. I'm hacking at the code, ATM. A pointer would be nice. I'm poking at bus_wrapper.h, but I'm not really sure which Method should be inverted.

And using any GPIO other than GPIO2 or GPIO1 for driving digital LEDs is a waste of time and only leads to frustration.

That may be the killer to this idea right there. GPIO2 is available internally, so I will probably just solder to that. This board also only seems to have 1M flash, AFAICT, so I should probably abandon ship and just use a spare raw board I have.

blazoncek commented 1 year ago

Well... You seem confident so go ahead. 😁 I merely stated the obvious.

jeffeb3 commented 1 year ago

In the interest of science :microscope:, I got it working, and I will document it here.

I compiled the code in platformio, using the esp01_1m_full target. I made these changes to the bus_wrapper.h file:

 /*** ESP8266 Neopixel methods ***/
 #ifdef ESP8266
 //RGB
-#define B_8266_U0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart0Ws2813Method> //3 chan, esp8266, gpio1
-#define B_8266_U1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart1Ws2813Method> //3 chan, esp8266, gpio2
-#define B_8266_DM_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Dma800KbpsMethod>  //3 chan, esp8266, gpio3
-#define B_8266_BB_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> //3 chan, esp8266, bb (any pin but 16)
+#define B_8266_U0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart0Ws2813InvertedMethod> //3 chan, esp8266, gpio1
+#define B_8266_U1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart1Ws2813InvertedMethod> //3 chan, esp8266, gpio2
+#define B_8266_DM_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266DmaInverted800KbpsMethod>  //3 chan, esp8266, gpio3
+#define B_8266_BB_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsInvertedMethod> //3 chan, esp8266, bb (any pin but 16)
 //RGBW
-#define B_8266_U0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Uart0Ws2813Method>   //4 chan, esp8266, gpio1
-#define B_8266_U1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Uart1Ws2813Method>   //4 chan, esp8266, gpio2
-#define B_8266_DM_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Dma800KbpsMethod>    //4 chan, esp8266, gpio3
-#define B_8266_BB_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod> //4 chan, esp8266, bb (any pin)
+#define B_8266_U0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Uart0Ws2813InvertedMethod>   //4 chan, esp8266, gpio1
+#define B_8266_U1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266Uart1Ws2813InvertedMethod>   //4 chan, esp8266, gpio2
+#define B_8266_DM_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266DmaInverted800KbpsMethod>    //4 chan, esp8266, gpio3
+#define B_8266_BB_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsInvertedMethod> //4 chan, esp8266, bb (any pin)
 //400Kbps
-#define B_8266_U0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart0400KbpsMethod>   //3 chan, esp8266, gpio1
-#define B_8266_U1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart1400KbpsMethod>   //3 chan, esp8266, gpio2
-#define B_8266_DM_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Dma400KbpsMethod>     //3 chan, esp8266, gpio3
-#define B_8266_BB_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266BitBang400KbpsMethod> //3 chan, esp8266, bb (any pin)
+#define B_8266_U0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart0400KbpsInvertedMethod>   //3 chan, esp8266, gpio1
+#define B_8266_U1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266Uart1400KbpsInvertedMethod>   //3 chan, esp8266, gpio2
+#define B_8266_DM_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266DmaInverted400KbpsMethod>     //3 chan, esp8266, gpio3
+#define B_8266_BB_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp8266BitBang400KbpsInvertedMethod> //3 chan, esp8266, bb (any pin)
 //TM1814 (RGBW)
-#define B_8266_U0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814Method>
-#define B_8266_U1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method>
-#define B_8266_DM_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method>
-#define B_8266_BB_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method>
+#define B_8266_U0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart0Tm1814InvertedMethod>
+#define B_8266_U1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814InvertedMethod>
+#define B_8266_DM_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266DmaInvertedTm1814Method>
+#define B_8266_BB_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814InvertedMethod>
 //TM1829 (RGB)
-#define B_8266_U0_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method>
-#define B_8266_U1_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method>
-#define B_8266_DM_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266DmaTm1829Method>
-#define B_8266_BB_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266BitBangTm1829Method>
+#define B_8266_U0_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266Uart0Tm1829InvertedMethod>
+#define B_8266_U1_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266Uart1Tm1829InvertedMethod>
+#define B_8266_DM_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266DmaInvertedTm1829Method>
+#define B_8266_BB_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266BitBangTm1829InvertedMethod>
 #endif

I am not sure which method is being used, but it is an 8266, so I just changed all of them. Annoyingly, one of them (dma) has the word inverted in a different place.

Screenshot_20230219-131257

The W2 output is wired to GPIO4 through a MOSFET. I wired the data pin of the LEDs to W2. I also wired a 330Ohm resistor between W2 and 5V so it would be 5V instead of floating.

I wired the ground of the LEDs to W1, and I set the relay pin to 14, inverted in the WLED UI. That seems to work fine with the 52 WB2812B LEDs I connected to it.

PXL_20230219_201346710

It is not terribly practical. And Tasmota says that the Rx pin is GPIO2, so I will probably revert to stock firmware (1M, blargh) and use that instead.