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

The pattern is repeated for the other pins.

I would hope that I have gotten the basic layout right, but I think that I do not really understand how to use Fritzing...

First, Fritzing still says that I have one unrouted connection, and it wants me to basically short-circuit the first resistor...

bild
and this is where it highlights the unrouted connection:

Additionally, I would assume that LED1 and LED2 should be connected to P2 somewhere, but I don't see such a connection, I would expect it to be here in this column:


The cross-connections I have made are, from what I think, guided by Fritzing...

After getting some good sleep I shall work out the connections between the pins... am I somewhere along the right track?

Not sure how common it is to have Fritzing installed, and know what software it is, but if you want to explore what I currently have yourselves then you could look here:

@albins123

I see much consternation in breadboarding this project, how about...

Note: Resistors not shown and LEDs would be on row 'd'. As is for clarity.

Then see it working in the Wokwi...

Thank you for the concrete circuit. I figured out the one I started on by working out the last details by hand.

However, I still have the same issue: the last LED is always brighter. Could it be the code now? The last thing I'll try is to swap out the LEDs for green ones and see if that makes a difference (in case the red LEDs I have are faulty).

@albins123

Yes it most definitely is.
In your efforts to code the last LED blink you have not taken into account that the blink must be part of the multiplex. You can't just switch the last LED on and off separately from the rest.

So the multiplex would go something like this...
Sequence 12 LEDs for a second
Sequence 11 LEDs for a second
Sequence 12 LEDs for a second and so on
After a period of time you stop calling LED 12 and move on to LEDs 11 and 10 and so on

I would start with the code and wiring scheme I posted in the Wokwi above.

Here is the code


#define A 10
#define B 11
#define C 12
#define D 13


int led_index = 0;
uint8_t led_Num = 12;

byte LEDS[][2] = {
  {B, A},
  {A, B},
  {C, B},
  {B, C},
  {D, C},
  {C, D},
  {C, A},
  {A, C},
  {D, B},
  {B, D},
  {D, A},
  {A, D},
};

void setup() {
  
}

void ledReset() {
  pinMode(A, INPUT);
  pinMode(B, INPUT);
  pinMode(C, INPUT);
  pinMode(D, INPUT);
}

void ledOn(int index) {
  ledReset();
  pinMode(LEDS[index][0], OUTPUT);
  pinMode(LEDS[index][1], OUTPUT);
  digitalWrite(LEDS[index][0], LOW);
  digitalWrite(LEDS[index][1], HIGH);
}

void loop() {
 
    ledOn(led_index);
    //delayMicroseconds(500);//All On
    delay(100);
    led_index = (led_index + 1) % led_Num;
    
}

You can edit the Wokwi directly in your browser to try out some ideas, saves compiling and uploading to your Uno all the time. Wokwi is a great development tool in that respect. Have fun

Ah yes... after a week it's finally resolved, and it was the code all the time! Thank you so much for providing something to compare against, and as I said before

I landed on this code, which gets rid of the blinking issue:

/* charlieplexing_12_pins.ino
Test Arduino sketch
for testing 12-pin Charlieplexing.
*/
// All GPIO pins in use.
const char TOTAL_PINS = 4;
 const char gpioPins[TOTAL_PINS] = {
   13,
   12,
   11,
   10
 };
// Contains the pins to set to output HIGH and output LOW to lit each of the 12 LEDs
// Led i is represented by index i in this map, and is in the format {pin to set to low, pin to set to high}
// Other pins are input pins.
// My schematic pins are called JP1, JP2, JP3, JP4, correspodning to GPIO pin number 1, 2, 3 and 4.
const char allPinStates[12][2] = {
  {gpioPins[1], gpioPins[0]}, // LED1  -> JP2 LOW, JP1 HIGH
  {gpioPins[0], gpioPins[1]}, // LED2  -> JP1 LOW, JP2 HIGH
  {gpioPins[2], gpioPins[0]}, // LED3  -> JP3 LOW, JP1 HIGH
  {gpioPins[0], gpioPins[2]}, // LED4  -> JP1 LOW, JP3 HIGH
  {gpioPins[3], gpioPins[0]}, // LED5  -> JP4 LOW, JP1 HIGH
  {gpioPins[0], gpioPins[3]}, // LED6  -> JP1 LOW, JP4 HIGH
  {gpioPins[2], gpioPins[1]}, // LED7  -> JP3 LOW, JP2 HIGH
  {gpioPins[1], gpioPins[2]}, // LED8  -> JP2 LOW, JP3 HIGH
  {gpioPins[3], gpioPins[1]}, // LED9  -> JP4 LOW, JP2 HIGH
  {gpioPins[1], gpioPins[3]}, // LED10 -> JP2 LOW, JP4 HIGH
  {gpioPins[3], gpioPins[2]}, // LED11 -> JP4 LOW, JP3 HIGH
  {gpioPins[2], gpioPins[3]}  // LED12 -> JP3 LOW, JP4 HIGH
};
unsigned long startMillis = 0;
 // 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.
 unsigned long microsecondsElapsed = 0;
 char blinkState = 0;
 // Set all pins to inputs. Used for clearing the LEDs.
 void setAllPinsToInputs(){
   for (int i=0;i<TOTAL_PINS;i++){
      pinMode(gpioPins[i], INPUT);
   }
 }
// Given a pointer to a 2-element char array in the format {pin to set to low, pin to set to high},
// sets one of the pins to low and the other one to high.
 void setPinState(const char* pinState){
   for (int i=0;i<2;i++){
     const char pin = *pinState;
     pinMode(pin, OUTPUT);
     digitalWrite(pin, i == 1);
     pinState++;
   }
 }
 void setup() {
   // Set up pins
   setAllPinsToInputs();
 }
 void loop() {
   // Set all pins to input
   // Turn on all LEDs up until the LED to blink (see comments around "timeElapsed") above
   char upperLimit = numberOfLEDsOn-1;
   if (blinkState){
     upperLimit++;
   }
   for (int i = 0; i < upperLimit; i++) {
     // For each pin state, set the LEDs accordingly
     setPinState(allPinStates[i]);
     delayMicroseconds(1000);
     microsecondsElapsed += 1000;
    setAllPinsToInputs();
  }
  // Update blinkState every second
  if (microsecondsElapsed >= 1e6){
    microsecondsElapsed = 0;
    blinkState = !blinkState;
  }
 }

The main changes are that:

  • After setting the pin state to turn on one LED, all pins are reset to inputs. (without it, I still see some flickering with a 1000us delay, but much less than before)
  • Before, I iterated through all LEDs like this and had a delay after the loop:
 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);

Now I delay for fewer amount of time and after setting each LED to ON:

char upperLimit = numberOfLEDsOn-1;
   if (blinkState){
     upperLimit++;
   }
   for (int i = 0; i < upperLimit; i++) {
     // For each pin state, set the LEDs accordingly
     setPinState(allPinStates[i]);
     delayMicroseconds(1000);
     microsecondsElapsed += 1000;
    setAllPinsToInputs();
  }

Now I want to understand the errors with my initial code, what I think is what I did wrong was:

  • The fact that I now inserted the delay after changing the state of each LED instead of after the whole sequence ensured more precise timing so that the LEDs are on for the same amount of time.
  • Decreasing the delay time from 1ms between each full n LED sequence (where n is the number of LEDs to turn on at a particular timestamp) to 1000us between each separate LED might have helped (another delay time)
  • After toggling an LED on, setting the other pins to inputs might have helped. As descibed above, if I remove it from my new code, there is a slight flickering effect, so it must have some impact.

But I am not sure, and I want to be sure, so that I can learn from this :slight_smile:

I read through your comment about my code:

But I politely disagree with this, or I have misunderstood you. I interpret this as a suggestion to change my (initial) code from

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]);
  }

to something like:

for (int i = 0; i < numberOfLEDsOn; i++) {
     if (i == numberOfledSon - 1 && !blinkState){
          continue;
     }
     // For each pin state, set the LEDs accordingly
     setPinState(allPinStates[i]);
  }

I don't see why that would make a change, the code would still do the same thing.

My initial code (and with this, I mean from the OP, which I assume is what you commented on in the post I quoted) tries to do exactly what you explained here:

The idea was that this:

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]);
  }

would be called all the time. blinkState is toggled every second, so the last LED is only included in the sequence every other second.

Ok, so...

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;
  }
 }

loop() triggers the for loop, which unhindered by any delay, executes as fast as it can. Lighting the first 7 leds (in this case) in mere microseconds. Each one barely see's any current, hence they are rather dim.
Out of the for loop and check blinkState
if true turn on led 8.
delay(1)
Hahaa, just that 1 millisecond allows led 8 to suck a lot more current than leds 1 thru 7.
That is why led 8 is much brighter

Ah, you're totally right. Thank you so much for the help!