Charlieplexing LEDs: Why is the last LED I am turning on brighter than the others?

Hi!

I am questioning my design choices, but I will try to push this circuit a little more until I get a microcontroller with more pins and don't use Charlieplexing.

Charlieplexing is a method of controlling n*(n-1), in this case LEDs, using n GPIO pins. This is achieved by alternating the state of pins: high, low, or input.

What I want to do:
I am trying to create a countdown timer circuit. The timer is going to light up between 1 and 12 LEDs, acting like a progress bar to show the time left. Also, the last LED will blink every second, so the progress bar indicates that the timer is counting down.

See the animation below for a visual explanation:

ezgif.com-animated-gif-maker(2).gif

So my requirements are:

  • Must be able to address/control all 12 LEDs individually (sometimes I might need to turn on one of them, sometimes two, sometimes all, and so on).
  • The rightmost LED of the lit-up LEDs must blink, like in the picture above.

I tried to use Charlieplexing (explained above) so I could get away with a microcontroller with few pins.
My problem:
I built a standard Charlieplexing circuit to control 12 LEDs (see for example: multiwingspan) on a breadboard.
To get LEDs lit up at the same time, I use a very small delay between when each pin is updated.
I get all the LEDs to appear lit up simultaneously, however, the LED that is supposed to blink (see the image above) somehow appears brighter than the other LEDs.
This is the code that I run on an Arduino UNO (the Charliplexing pins used are 13,12,11 and 10):

/* charlieplexing_12_pins.ino
Test Arduino sketch (to run on an Arduino UNO, not Attinys)
for testing 12-pin Charlieplexing.
*/
 const char allPinStates[12][4] = {
   {
     1,
     0,
     -1,
     -1
   }, // LED 1
   {
     0,
     1,
     -1,
     -1
   }, // LED 2
   {
     1,
     -1,
     0,
     -1
   }, // LED 3
   {
     0,
     -1,
     1,
     -1
   }, // LED 4
   {
     1,
     -1,
     -1,
     0
   }, // LED 5
   {
     0,
     -1,
     -1,
     1
   }, // LED 6
   {
     -1,
     1,
     0,
     -1
   }, // LED 7
   {
     -1,
     0,
     1,
     -1
   }, // LED 8
   {
     -1,
     1,
     -1,
     0
   }, // LED 9
   {
     -1,
     0,
     -1,
     1
   }, // LED 10
   {
     -1,
     -1,
     1,
     0
   }, // LED 11
   {
     -1,
     -1,
     0,
     1
   } // LED 12
 };
 const char gpioPins[4] = {
   13,
   12,
   11,
   10
 };
 // Used to set the 4 used pins to their wanted state
 // pinStates is a pointer to a const char[] with 4 entries,
 // each entry indicating the state of each GPIO pin:
 // 1: Pin should be OUTPUT+ON
 // 0: Pin should be OFF+OFF
 // -1: Pin should be input
 void setPinState(const char* pinStates){
   for (int j = 0; j < 4; j++) {
       const char pinState = * (pinStates + j);
       const char gpioPin = gpioPins[j];
       if (pinState != -1) {
         pinMode(gpioPin, OUTPUT);
         digitalWrite(gpioPin, pinState);
       } else {
         pinMode(gpioPin, INPUT);
       }
     }
 }
 // Later intended to be used as a progress bar for the timer,
 // to indicate time left, by only having turned on for example LED 1,2,3, and 4.
 char numberOfLEDsOn = 8;
 // Used to blink the rightmost LED on the progressbar every second,
 // keeping all other LEDs before it on, to indicate that the timer
 // is counting down.
 short timeElapsed = 0;
 char blinkState = 0;
 void setup() {

 }
 void loop() {
   // Turn on all LEDs up until the LED to blink (see comments around "timeElapsed") above
   for (int i = 0; i < numberOfLEDsOn-1; i++) {
     // For each pin state, set the LEDs accordingly
     setPinState(allPinStates[i]);
  }
  // Blink last LED in progressbar every second. See comments around "timeElapsed") above.
  if (blinkState){
    setPinState(allPinStates[numberOfLEDsOn-1]);
  }
  delay(1);
  timeElapsed++;
  // Update blinkState every second
  if (timeElapsed >= 1000){
    timeElapsed = 0;
    blinkState = !blinkState;
  }
 }

In the video below, I run the code above, which is expected to turn on 8 of the 12 LEDs and the uneven brightness issue can be illustrated. Watch the video and you can see that these LEDs (LED 7 and 8) are always brighter than the rest:


Video: (I hope it is allowed to include it as a Google Drive link): https://drive.google.com/file/d/1eFfYHAGzBa38fJHtqHBOYSPHYbvdRqES/view?t=2
(the breadboard connection won't win awards, I know...)

I did not connect the LEDs on my breadboard circuit in a logical order - hope you can bear with that. The 8th LED is supposed to alternate between on and off like in the image I provided above.

My setup gives me uneven brightness on just the last LED I want to illuminate. Why? Is it a timing problem with my code? Circuit issue? Charlieplexing limitation?

Many thanks for your help!

Some complementary information:
The LEDs are all identical. They are rated: Red at 20 mA: 1,92-2,06 V. I run them on an Arduino UNO with 150ohm resistors between each pin and the rest of the connections that the pin has.

Are all leds from the same batch?
Are all 150 ohm resistors really 150 ohm?
There is variance in everything...
Maybe you have a led with low forward voltage combined with a 143 ohm resistor...
Breadboards are also not the best conductors, but that would be more likely cause one led to be dim...

With some leds you set the pin to output first and then the other pin to input. With some leds you fo that in reverse order.... could that influence what is happening?
Would it be an idea to set everything to input and then set the outputs? Now you set the outputs while some pins are still configured as outputs from the round before...
Why do you update the leds every loop? It is only needed after each blink... or 30 times per second, not 1000 times per second.... (which makes the difference between first setting the ouputs compared to first setting the input relatively much larger)

By the way: thumbs up for clear description, code in code block, animation...
Video is not accessible...

The bright LED is probably being turned on more times than the others. Maybe the first or last element is due to array control (writing to 0-1 or n+1.

@albins123

You could easily achieve what you want to build, by using a couple of 74HC595 shift registers.
They are very cheap and only require 3 digital outputs from your arduino to drive.

With credit to the author Anderson Costa of the original Wokwi from which I borrowed the component build. I came up with this after adding my own code, hope it helps...

Same batch, I can't swear, but they are from the same store, bought at the same time, taken out of the same ESD bag...

Yes, they are, just double checked! Same story here - they all come fresh from the same previously untouched bag, labelled 150Ohm.

I tried changing it to ensure inputs are set first, then outputs. There is no notable difference.

It may be a code bug or something strange then. I agree with you that the delay shouldn't have to be that short. However, if I set a delay larger than 2, the LEDs appear to blink... something is fishy here, but I don't know what it is!

I would think so too, but I would claim that the loop handles index 0 to numberOfLEDsOn-2
and the if statement handles numberOfLEDsOn-1 if applicable.

This is the relevant section of the code to illustrate:

 for (int i = 0; i < numberOfLEDsOn-1; i++) {
     // For each pin state, set the LEDs accordingly
     setPinState(allPinStates[i]);
  }
  // Blink last LED in progressbar every second. See comments around "timeElapsed") above.
  if (blinkState){
    setPinState(allPinStates[numberOfLEDsOn-1]);
  }

Wow, thank you so much for just setting that demo up for a complete stranger on the internet :blush: This helps a lot, and I have shift registers as my backup plan. I will order some to try both setups, if I get Charlieplexing working!

Sorry! Try again: LED brightness issue.mov - Google Drive


I think the main takeaway is that my code needs closer analysis, also because delay(1) (2 is also fine) is the smallest delay I can use without seeing the LEDs blink. It can also be the breadboard, but if I change the constant numberOfLEDsOn to turn on for example only the first 4 LEDs, or to turn on all 12 LEDs, the behavior is the same. I thank you all for your input!

I did also validate that all possible LED states are covered under allPinStates, at least from what I could tell.

Without wading through your code, since it's the last LED you're turning on that's brighter than the rest, my first suspicion would be that you're doing something at the end of whatever loop is multiplexing the LEDs that's resulting in a slightly longer ON time for that last LED compared to all the rest.

Easy test? Put a scope on one of the other LED pins. Measure its ON time. Repeat with the last LED. Is its ON time longer? If yes, you know you need to add an extra "final" state with all LEDs off.

1 Like

Oh that's true! Thanks a lot for pointing in that direction. I don't own a proper scope but I do have a ripoff Salae Logic 8. I'll measure and report back.

The logic analyzer will do just fine as long as your sample rate is high enough to catch any possible difference.

This is probably not causing your problem, but it is ugly... it would be fine if blinkState was declared as boolean...

I think I have got it!
Depending on your board: your pin 13 is connected to the built in led!
Therefore, setting it to input does not fully cut current through this pin...

1 Like

The OP originally said they were using an Uno. If it's an R3, the onboard LED is isolated by an opamp.

If it's an R4 WiFi, the LED is behind a MOSFET. There is a 100K resistor to ground but my initial feeling is that wouldn't pass enough current to matter.

But, if it's an R4 Minima...

...you might be on to something!

Thanks for pointing out the potential presence of other circuitry than a resistor and a led...
I think it is at least worth a try to use another pin...
There are also many clones of R3... some providers might save money by not having the opamp...

Absolutely. Even if it doesn't pan out it's a dead easy test.

Edit: I'd be very surprised if the opamp was omitted from a clone board, as it's the unused half of the "doesn't usually quite work anymore USB power shutoff when power's applied through the barrel connector" circuitry. But could clones simply not use that half? Oh yeah.

That is not possible because this op amp is a duel OP Amp. That is two op amps in one package. It is left over from the op amp that switches the power source between the USB and the external input.

Unless of course both features are missing, but is should be easy to spot the op amp on your clone board. It will be an 8 pin package close to the regulator.

I should actually have mentioned it is a clone... I'm sorry for omitting that.
It is a "Kjell Academy Uno" from Kjell & Co which, on the other hand is a quite credible Swedish electronics company selling everything from phone cases to Arduino stuff.
It was bought years ago, and sadly, the revision isn't marked. It has a small 8-pin package just below the regulator and next to the crystal.

Anyways, I tried with different pins. The issue still persists.


I think I should move on and rebuild my circuit. However, this poses a different question, because the circuit I have now is really ugly. I did not figure out how to get a cleaner conversion from "schematic to breadboard" than this:

(you can access this circuit in the simulator I am using here, I believe)

So I think the next step is to ask strangers online how to get the connections cleaner. I did move from TinkerCad to Fritzing in hope of making that conversion easier. The schematic is this:

....3 images limit, see the post below for a follow up

At least Fritizing gives me some guidelines, but it still looks like spiderweb to me...


Alright, well, I did also do the logic level analyzer stuff.

In my messy circuit, this is the LED that blinks each second:

I plugged in my logic level analyzer as indicated below:

....3 images limit, see the post below for a follow up

but I am not really sure what I see...

I've plugged it in wrong right? Because Channel 0 will go high when addressing other LEDs that are related to pin 10, not just the particular LED that blinks.

What a mess...
It would not surprise me if there is a wiring error...
I think it is a good idea to rebuild the whole lot indeed...
Make use of the regular patterns in the schematic...
Use the space on your breadboard. Be smart... yse as little as possible wires. Put the leds back to back together.

Normally the top and bottom lines are connected horizontally... so, all your outputs seem to be wired together...

1 Like

Oh, they're not, it's very hard to see but the connections are not touching the females on the breadboard. It's another bad design choice by me.
bild

I have worked my way through trying to fix the messy circuit, redrawing stuff a million of times. I think I might be approaching a cleaner setup, but I am not sure...

What I did was to try to group the circuit by pin, you'll see what I mean:

For instance, the parts of LED1 through LED6 facing toward JP1 would be able to connect to the same row vertically on a breadboard, like that:

After resolving pretty much all of the unrouted connections according to Fritzing, I get


But I don't think Fritzing cooperated with me. I haven't used that software in years...

The idea is that LED2, LED4, and LED6, are these LEDs

LED6 is the "bottom-most" LED. Taking LED6 as an example, it has its anode connected to the cathode of LED5: