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 
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.