WS2812B - Color inadvertently changing with potentiometer addition

I developed a scoreboard based on something I found online using some Neopixel libraries and a strip of WS2812B. I fixed it up to fit my specific use, no issues, then converted it from button to IR, no issues. It's just two sets of two digits, different colors, that can go up/down to 21; pretty standard.

Then I decided to add a potentiometer to change the brightness. I added three lines on the bottom of the code below. It worked well, I just need to add/subtract a point to see the change in brightness. However, I noted that when I increment Player A (blue) that Player B (orange-ish) color changes to pure red. As soon as I change the score on Player B it goes back to orange.

I tried dropping the color setting commands in various places - in the lines following setting Player A score, at the end, etc. But it doesn't fix it. Primarily I'm curious as to why, but I'd also like to fix it. Thanks.

/* Cornhole Scoreboard
   Scoreboard for two players (A and B) with 21 pt limit
   Two modes: 21 and over wins, or must hit 21 even or score resets to 15
   Separate displays for A and B on the same strip pin using WS2812B
   IR Remote for input
   Libraries: Adafruit_NeoPixel.h; Noiasca_NeopixelDisplay.h; IRremote.h
   Inputs: 12-WS2812B; 8-Red LED; 9-Blue LED; 11-IR Receiver

*/

const byte ledPin = 12;                // Data pin from WS2812B
const byte numDigits = 2;              // Number of digits on each display
const byte pixelPerDigit = 21;         // all pixels, including decimal point pixels if available at each digit
const byte addPixels = 0;              // additional pixels to be added to the strip
int var;                               // input from Pot
int LED;                               // mapped input from Pot

const byte startPixelA = 0;            // First pixel of display A
const byte startPixelB = 42;           // First pixel of display B (pixelPerDigit x numDigits + addPixels (starts counting at 0 not 1))
const int redPin = 8;                  // LED for game mode "21 Even"
const int bluePin = 9;                 // LED for game mode "Over 21"

byte counterA;                         // Score for player A
byte counterB;                         // Score for player B

const uint16_t ledCount(pixelPerDigit * numDigits * 2 + addPixels);

// Wiring for 7-segment: In => G-B-A-F-E-D-C  => Out
//   --A--
//   F   B
//   --G--
//   E   C
//   --D--
//

typedef uint32_t segsize_t;            // fit variable size to your needed pixels. uint16_t --> max 16 Pixel per digit
const segsize_t segment[8] {
  0b00000000000000000000000111000000,                  // SEG_A
  0b00000000000000000000000000111000,                  // SEG_B
  0b00000000000111000000000000000000,                  // SEG_C
  0b00000000000000111000000000000000,                  // SEG_D
  0b00000000000000000111000000000000,                  // SEG_E
  0b00000000000000000000111000000000,                  // SEG_F
  0b00000000000000000000000000000111,                  // SEG_G
  0b00000000000000000000000000000000,                  // SEG_DP decimal point, just leave it zero
};

#include <Adafruit_NeoPixel.h>                                       // install Adafruit library from library manager
Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800);     // create neopixel object like you commonly used with Adafruit

#include <Noiasca_NeopixelDisplay.h>                                 // download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm
// in this sketch we handle displayA and displayB as two individual displays:
Noiasca_NeopixelDisplay displayA(strip, segment, numDigits, pixelPerDigit, startPixelA);  // create display object, handover the name of your strip as first parameter!
Noiasca_NeopixelDisplay displayB(strip, segment, numDigits, pixelPerDigit, startPixelB);  // create display object, handover the name of your strip as first parameter!

#include <IRremote.h>
const int RECV_PIN = 11; //IR Remote Setup
IRrecv irrecv(RECV_PIN);
decode_results results;


void setup() {
  Serial.begin(9600);
  Serial.println(F("\nNoiascaNeopixelDisplay\n40 scoreboard two displays"));

  strip.begin();                       // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();                        // Turn OFF all pixels ASAP
  strip.setBrightness(50);             // Set BRIGHTNESS to about 1/5 (max = 255)
  strip.clear();

  displayA.setColorFont(0x0000FF);     // Player A Color - Blue
  displayB.setColorFont(0xFF4500);     // Player B Color - Red Orange

  Serial.println(F("test display"));
  for (byte i = 99; i > 0; i = i - 11) {
    displayA.print(i);
    displayB.print(i);
    delay(200);
  }
  displayA.print(" 0");
  displayB.print(" 0");

  irrecv.enableIRIn();                // IR setup
  irrecv.blink13(true);

  pinMode(redPin, OUTPUT);            //  LED for game mode
  pinMode(bluePin, OUTPUT); 
  digitalWrite(bluePin, HIGH);  // sets default to Even 21
  digitalWrite(redPin, LOW);

  pinMode(A0, INPUT);
}

void loop() {
if (irrecv.decode(&results)){

    switch(results.value){
    case 0xFF629D: //Keypad button "Vol+"  - Used for Over 21
    digitalWrite(redPin, HIGH);
    digitalWrite(bluePin, LOW);
    }

    switch(results.value){
    case 0xFFA857: //Keypad button "Vol-"  - Used for Even 21
    digitalWrite(bluePin, HIGH);
    digitalWrite(redPin, LOW);
   }


    switch(results.value){
    case 0xFFC23D: //Keypad button "Skip Forward" for P1 up  
     if(counterA == 21 && digitalRead(bluePin) == HIGH)  
      {  
      counterA = 15;
        Serial.print(F("PlayerA ")); Serial.println(counterA);
        displayA.setCursor(0);
        if (counterA < 10) displayA.print(" ");
        displayA.print(counterA);   
      } 
     else if(counterA<21)  
      {  
        counterA++;
        Serial.print(F("PlayerA ")); Serial.println(counterA);
        displayA.setCursor(0);
        if (counterA < 10) displayA.print(" ");
        displayA.print(counterA);   
      } 
    }

    switch(results.value){
    case 0xFF22DD: //Keypad button "Skip Back" for P1 down
     if(counterA>0)  
      {  
        counterA--;
        Serial.print(F("PlayerA ")); Serial.println(counterA);
        displayA.setCursor(0);
        if (counterA < 10) displayA.print(" ");
        displayA.print(counterA);   
      } 
    }

    switch(results.value){
    case 0xFF906F: //Keypad button "Up" for P2 up

     if(counterB == 21 && digitalRead(bluePin) == HIGH)  
      {  
      counterB = 15;
        Serial.print(F("PlayerB ")); Serial.println(counterB);
        displayB.setCursor(0);
        if (counterB < 10) displayB.print(" ");
        displayB.print(counterB);    
      } 
     else if(counterB<21)  
      {  
        counterB++;
        Serial.print(F("PlayerB ")); Serial.println(counterB);
        displayB.setCursor(0);
        if (counterB < 10) displayB.print(" ");
        displayB.print(counterB);   
      } 
    }

    switch(results.value){
    case 0xFFE01F: //Keypad button "Down" for P2 down
     if(counterB>0)  
      {  
        counterB--;
        Serial.print(F("PlayerB ")); Serial.println(counterB);
        displayB.setCursor(0);
        if (counterB < 10) displayB.print(" ");
        displayB.print(counterB); 
      } 
    }

    switch(results.value){
    case 0xFFA25D: //Keypad button "Power" for reset
    counterA = 0;
    counterB = 0;
    displayA.clear();
    displayA.print(" 0");
    displayB.clear();
    displayB.print(" 0");
    digitalWrite(bluePin, HIGH);  // sets default to Even 21
    digitalWrite(redPin, LOW);
    }

    irrecv.resume(); 
    }

  var = analogRead(A0);
  LED = map(var,0,1023,20,255);
  strip.setBrightness(LED);
}

From

setBrightness() was intended to be called once, in setup(), to limit the current/brightness of the LEDs throughout the life of the sketch. It is not intended as an animation effect itself! The operation of this function is “lossy” — it modifies the current pixel data in RAM, not in the show() call — in order to meet NeoPixels’ strict timing requirements. Certain animation effects are better served by leaving the brightness setting at the default maximum, modulating pixel brightness in your own sketch logic and redrawing the full strip with setPixel().

You probably need to do a setPixelColor() with the result of the analog read to adjust the brightness.

1 Like

Got it, thanks for the link and the background. Guess I'll just re-write it to only set at startup/reset.

Did you try to only run strip.setBrightness() when the value changes.

brightness = map(analogRead(A0), 0, 1023, 20, 255);
if (brighness != oldBrightness) {
  strip.setBrightness(brightness);
  oldBrightness = brightness;
}

Leo..

Equally decreasing the value of each color also dims its brightness.
Rather than (pseudocode) (pix, red, green, blue); use (pix, red/pot, green/pot, blue/pot);

That's interesting. Haven't tried it, but I might give that a shot just out of curiosity sake. As linked above Adafruit says that it's only intended to run once and that you can get some issues if you adjust in loop, so it's not surprising.

I really don't need to adjust it often, I just wanted to have the option to reduce brightness if it's too glaring. It'd be fine to just adjust it and reset the board, doesn't have to be mid-game.

Hello, I am having the same issue:

I am also finding others with the same issue:

I was leaning towards this being a limitation of the LED - it can only use 8-bits or 256 values per pin, and at low brightnesses, a value of 2 is twice as bright as 1, and below that things go to 0 and only one gets left at 1, making colors snap to full red or full green or full blue.

However, I then added a current limiting resistor to the LEDs instead, and it still happened, and at much higher values than before, values as high as 100. Which means it cannot be an issue with 8-bit PWM because a value of 99 is very close to 100 yet my LED instantly snaps from orange to full red going from a brightness of 100 to 99.

I am on the case, but I am stumped. Let me know if you find a solution. So far my best hack has been to lower the supply voltage of the LEDs to 3.3v (where they surprisingly still work) to give me a lower brightness "floor" before this starts happening, and then limit my code around the LEDBrightness values to prevent it from reaching the value where it happens.

What adafruit means is that they apply the multiplying factor directly to the leds array and they work with bytes for the color, so if you dim a led too low so that the value goes to 0 and then want to increase brightness again then you are out of luck because 0 multiplied by anything will stay at 0.

Here if OP sets the brightness and then assign some values to the pixels (and hopefully call show) there is no accumulation effects.

What does that mean? Did you try? With which code and circuit? With which outcome?

I don't know, I was trying to link to the entire thread and for some reason this forum decides to pull one individual comment and quote it. I didn't mean to do anything but link to the entire thread.

The problem occurs when you drive the brightness too low. Some LED pins are getting set to 1, and some are getting set to 0. This is because these LEDs only have 8-bit (256 values) resolution to lower brightness, and the human eye doesn't even start to notice those changes until at a value of like 64 or lower because of gamma response curves. This problem can't be solved without switching to better LEDs.

But we can bandaid the problem by either limiting our code to never allow the brightness to go to such low levels, or we can combine doing that with some external physical filters like an opaque piece of white plastic that lets some light through, to bring the brightness back down to desired levels physically rather than in code.

you probably posted in the wrong thread. did you mean to post in your own thread?

or did you actively plan to answer here

No, I meant to post those here, to show OP some links to other people having the same problem, which is why I said "Hello, I am having the same issue:" and "I am also finding others with the same issue:"

OK -understood

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