I want to drive RGB LEDs at a very low brightness, but an RGB LED driven as yellow always turns red at low brightness, whether neopixel or analog LED?

I am trying to have 3 indicator LEDs for a weather station. I want the LEDs to dim at night, so they're not blindingly bright.

My code achieves this, with one physical glitch - RGB LEDs don't like being driven at such slow PWMs apparently, and on the LED that is supposed to indicate yellow, the green LED shuts off, and it goes to full red.

I can even watch the threshold at which this happens by attaching a slider on my IOT app to the led brightness. I drag the slider down, and the yellow RGB LED gets dimmer and dimmer, until I drag the slider below a certain point and it instantly turns red.

I had tried playing around with various resistors - someone said this is because the red anode operates at a lower voltage than the green or blue ones, and requires a larger resistor as a result. Let me tell you this did not help one bit.

So I bought some Neopixel LEDs thinking this would solve the issue once and for all. They are fully balanced either internally, or in the libraries. I don't have to mess around with green being twice as bright or anything, they took care of all that.

It still happens. The exact same way. With a freakin Neopixel LED. With a completely different code library driving the LED, with a completely different LED.brightness function. It still turns red if I drag the brightness too low.

You're all welcome to look at my code if you want, it's large and messy. I have no idea if the issue is code or not. I figured it shouldn't have happened switching from analog LEDs to fully self contained RGB drivers, which tells me it's code. But then the fact that my code completely switched libraries tells me it isn't.

Here's the code that I think is relevant. Here is where I decide what colours to drive the LEDs based on information from my weather station, with poorly chosen variable names:

if (menuValue == 2) 
        {

                pmG = 55 - sliderValue; //"slidervalue" is indoor pm2.5
                if (pmG < 0) {pmG = 0;}
                pmG *= (255.0/55.0);
                if (pmG > 255) {pmG = 255;}
                
                pmR = sliderValue;
                if (pmR < 0) {pmR = 0;}
                pmR *= (255.0/55.0);
                if (pmR > 255) {pmR = 255;}
                
                pmB = sliderValue - 100;
                if (pmB < 0) {pmB = 0;}
                pmB *= (255.0/55.0);
                if (pmB > 255) {pmB = 255;}

                pmG2 = 55 - bridgedata; //bridgedata is outdoor pm2.5
                if (pmG2 < 0) {pmG2 = 0;}
                pmG2 *= (255.0/55.0);
                if (pmG2 > 255) {pmG2 = 255;}
                
                
                pmR2 = bridgedata;
                if (pmR2 < 0) {pmR2 = 0;}
                pmR2 *= (255.0/55.0);
                if (pmR2 > 255) {pmR2 = 255;}
                
                
                pmB2 = bridgedata - 100;
                if (pmB2 < 0) {pmB2 = 0;}
                pmB2 *= (255.0/55.0);
                if (pmB2 > 255) {pmB2 = 255;}

                

                pmG3 = 155 - bmeiaq;  //bmeiaq is IAQ from BME680
                if (pmG3 < 0) {pmG3 = 0;}
                pmG3 *= (255.0/155.0);
                if (pmG3 > 255) {pmG3 = 255;}
                
                
                pmR3 = bmeiaq;
                if (pmR3 < 0) {pmR3 = 0;}
                pmR3 *= (255.0/155.0);
                if (pmR3 > 255) {pmR3 = 255;}
                
                
                pmB3 = bmeiaq - 155;
                if (pmB3 < 0) {pmB3 = 0;}
                pmB3 *= (255.0/155.0);
                if (pmB3 > 255) {pmB3 = 255;}




                strip.setBrightness(LEDbrightness);
                strip.setPixelColor(2, strip.Color(pmR3, pmG3, pmB3)); 
                  strip.setPixelColor(0, strip.Color(pmR, pmG, pmB)); 
                strip.setPixelColor(1, strip.Color(pmR2, pmG2, pmB2)); 
        }

And here is where I decide how bright to drive the LEDs, by combining the values from a photocell on an analogread pin, and a slider on my IOT app to give more control over the photocell:

        {

            LEDbrightness = ((analogRead(A0) * 0.0005) * sliderLEDbrightness);
            if (LEDbrightness > 255) {LEDbrightness = 255;}
            if (LEDbrightness < 1) {LEDbrightness = 1;}

        }

If I change the "1"'s in that last line of code to a larger number over the threshold, like 6, so that LEDbrightness is forbidden from going below 6, then this prevents my issue, and the LEDs are always the colour they are told to be. It's only when that value goes below 6 (or 4, I can't remember which) that it suddenly snaps from the proper blended orangey yellow, to full red.

This happens whether I am driving a Neopixel 1wire RGB LED through a Neopixel library, or whether I'm driving a proper regular RGB LED through three PWM pins, even with proper resistor values (higher for the red pin) on all 3 pins.

have you measured the current draw on the three wires ? could you actually see the green going to 0 - showing the green led shuts down leaving only the red one active?

Adafruit wrote an article which describes the issue with RGB led dimming, causing a shift in color.

Thanks, I suspect that's what I'm seeing is something like this brightness response curve:

components_quantization

Where any value below 6 is a 0, but maybe 5 for red.

Apparently the WS2812 happily runs off the 3.3v regulator pin instead of 5v, so I'll just use that to lower the brightness some more. Which works. Still can't set brightness below 6 in code without the LEDs snapping to either green or red though. They never shut off completely, they just snap to one of those colours. I really have no idea what's going on.

have you tried pure red, pure green and pure blue at low brightness? can you go all the way to 1?
does the library you use have gamma correction?

Dumb RGB LEDs PWM-ed with 12-bit PWM instead of 8-bit PWM should be able to retain the colour at a much lower dim level.
Leo..

Thanks all. I'm not sure if the library I use has gamma correction, but I can't find a gamma correction table anywhere in it so I'm assuming not. Pretty sure the last one I used for the analog/dumb LEDs didn't either.

I'm not sure it's the 8-bits of brightness values that is the issue either. If I add a 5kohm resistor in series with the LED strip's Vin line, they are now much dimmer, but the "snap to one full color" issue happens at much higher LEDbrightness values.

IE without any resistor, this "snapping color" issue happens at an LEDbrightness value below ~4-6. Anything less than that, and I can only get solid red, solid green, or solid blue out of the LEDs, not mixtures of colours.

WITH the 5kohm resistor in series with Vin, this issue begins to happen all the way up in the 100's. If I set LEDbrightness to only as low as 99, the LEDs snap to full red or full green.

Furthermore, if I continue lowering LEDbrightness even lower than 99 with the resistor on, these fully red or fully green LEDs continue to get dimmer and dimmer.

I know with RGB LEDs, the red LED always operates at a lower voltage (2 volts) than the green and blue (3 volts each), but these things are supposed to be internally balanced, and I tried messing around with higher resistors for the red pin on a dumb LED and it didn't help.

I am completely stumped. I will try to get a video later.

Solution for now: I can't dim them very dark using code, and I can't dim them very dark using resistors, but I can do it with an ND filter.

Grey piece of glass in front of LEDs, then I just use the high end of the brightness scale instead of the low end. Problem bandaided.

Only three pixels, so you could turn them on/off rapidly.
That will also dim the LEDs.
Leo..

If you are talking about addressable led strips, they are not designed to work with a resistor like that. They contain digital circuits which will not run reliably without a stabilised voltage supply, and by putting a resistor in line with the Vin, you have made it unstable. Anything could happen.

I agree with others here, I think the problem is PWM resolution. Ws2812 LEDs have only 8-bit resolution, and that can't be changed. By default the Arduino's analogWrite() resolution is also 8-bit. On some Arduino it may be possible to increase the resolution on some pins to 10, 12 or even 16 bits. So there may be a solution there depending what model of arduino you are using.

Another possibility is to use an external led driver such as pca9685.

indeed - would be good to know which Arduino is being used

The fact that you can't figure out what the problem is means that there's an approximately 50% probability that the problem is somewhere else. So, please, always post your full code (not a link) here in the forum.

Looking at the code you did post, I can see potential problems, but can't be sure because, for example, I can't see what data type the variables are.

1 Like

One possibility is to use an extra PWM output as the "master" brightness channel. Use this, with a transistor to boost the current, to drive the common pins of the RGB LEDs. The frequency of the PWM signal on the master channel needs to be many times higher than the PWM frequency on the other channels, for example around 4KHz or 8KHz rather than the usual 500Hz. When you want to dim the LEDs, don't adjust their R, G, B duty cycle values, but instead reduce the brightness channel duty cycle value. This will keep the colour balance correct.

Problem with this idea is you are probably using an Uno or similar and don't have a spare PWM pin to use like this.

If you read more carefully you'll see I did post my full code, in a link, to a github page, because it is quite long. But as the other commenters have pointed out, the problem isn't in my code, it's to do with the 8-bit brightness values of this LED. You are welcome to click it, or not, but please refrain from the snarky comments if that's all you have to add.

I'm not using PWM pins, I'm using WS2182 neopixel LEDs that drive their own PWM internally, and can't be changed.

I've already come up with a solution, it's to expose to the right and lower the brightness externally using translucent filters.

I did read carefully. It seems it was you that did not read carefully!

Please report any snarky comments I have made to the forum moderators.

(Oops, that was a bit snarky...)

Ok, please report any snarky comments made before this snarky comment to the moderators.

Remember the color chart from school. Yellow is made of which TWO colors? Which color LED will require MORE current that the other?

Minimum Reproducible Example.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.