Michael Haren’s Wassupy Blog

Arduino Day 5: Fun With Charlieplexing

in 30 days of arduino, arduino, code, and technology

Tip! This post is part of a series on my adventures with Arduino

Here’s the challenge of the day: wire up a bunch of LEDs to blink in sync to the music from yesterday’s fun. In all my LED fun before, though, I was wiring up LEDs directly to output pins on the Arduino—one-to-one. This doesn’t scale very well.

Fortunately there’s this thing called multiplexing (muxing) which lets you combine a bunch of signals together (board outputs), transmit them over fewer wires, and then demux them at the destination (LEDs).

A particularly popular method of this in the Arduino world seems to be Charlieplexing. Charlieplexing is pretty sweet because from n pins, you can individually address n(n-1) LEDs. For example, a mere 6 pins can address 30 LEDs. Nice!

Speaking of Charlie. Did you guys see that “Lost” finale a while back? I can’t talk about it but this sums up how I feel pretty well. I just watched that again and the pain/disappointment is still too real to discuss it.

So anyway, I Charlieplexed six LEDs for use with that silly Mario song from yesterday. The song has maybe 10 notes in total. I simply took the frequency range of those notes and broke it into six slices. As I mentioned yesterday, I have no musical skill whatsoever so these are completely arbitrary and not in any order.

Here we go:

Build

Circuit

This doesn’t well represent the circuit:

It was actually much simpler to build, requiring very little wire. Looking at the schematic below you can see that the four points on top of the LEDs connect to a single point as do the four points on bottom. This makes bread boarding the circuit pretty easy.

If you look closely in the build shot above you can see that I used four rows, and the middle two are common to provide less contorting of the LED leads.

Schematic

I don’t fully understand how thing thing works but it does. Let Google lead you to more information on the subject, including better schematics:

Code

I strongly suggest reading voraciously about how this Charlieplexing thing works. It will save you some grief.

Today’s changes are in bold:

int Pins[] = { 2, 3, 4 };
int Leds[6][2] = 
{
  { 0, 1 }, { 1, 0 },
  { 0, 2 }, { 2, 0 },
  { 1, 2 }, { 2, 1 } 
};

#define PinCount (sizeof(Pins)/sizeof(int))
#define LedCount (sizeof(Leds)/sizeof(int))

// notes adapted from http://www.phy.mtu.edu/~suits/notefreqs.html
// with some help from Excel and my amazing, super talented wife
// who is not completely tone deaf like me
#define c0 164
#define c0s 173
#define d0 184
#define d0s 195
#define e0 206
#define f0 218
#define f0s 231
#define g0 245
#define g0s 260
#define a0 275
#define a0s 291
#define b0 309
#define c1 327
#define c1s 347
#define d1 367
#define d1s 389
#define e1 412
#define f1 437
#define f1s 463
#define g1 490
#define g1s 519
#define a1 550
#define a1s 583
#define b1 617
#define c2 654
#define c2s 693
#define d2 734
#define d2s 778
#define e2 824
#define f2 873
#define f2s 925
#define g2 980
#define g2s 1038
#define a2 1100
#define a2s 1165
#define b2 1235
#define c3 1308
#define c3s 1386
#define d3 1468
#define d3s 1556
#define e3 1648
#define f3 1746
#define f3s 1850
#define g3 1960
#define g3s 2077
#define a3 2200
#define a3s 2331
#define b3 2469
#define c4 2616
#define c4s 2772
#define d4 2937
#define d4s 3111
#define e4 3296
#define f4 3492
#define f4s 3700
#define g4 3920
#define g4s 4153
#define a4 4400
#define a4s 4662
#define b4 4939
#define c5 5233
#define c5s 5544
#define d5 5873
#define d5s 6223
#define e5 6593
#define f5 6985
#define f5s 7400
#define g5 7840
#define g5s 8306
#define a5 8800
#define a5s 9323
#define b5 9878

// adapted from http://www.rose-hulman.edu/class/me
//             /HTML/ME430_0910_W_Olson/code/example buzzer.c

const int
    song[] =   { e2, e2, e2, c2, e2, g2, g1, 
                 c2, g1, e1, a1, b1, a1s,a1,
                 g1, e2, g2, a2, f2, g2, e2, c2, d2, b1,
                 c2, g1, e1, a1, b1, a1s,a1,
                 g1, e2, g2, a2, f2, g2, e2, c2, d2, b1,
                 g2, f2s,f2, d2s,e2, g1s,a1, c2, a1, c2, d2,
                 g2, f2s,f2, d2s,e2, c3, c3, c3,
                 g2, f2s,f2, d2s,e2, g1s,a1, c2, a1, c2, d2,
                 d2s,d2, c2 };
const float 
    length[] = { 1,  2,  1,  1,  2,  4,  4,  
                 2,  1,  2,  1,  1,  1,  2,
                 1.3,1.3,1.3,2,  1,  1,  1,  1,  1,  1,
                 2,  1,  2,  1,  1,  1,  2,
                 1.3,1.3,1.3,2,  1,  1,  1,  1,  1,  1,
                 1,  1,  1,  2,  1,  1,  1,  1,  1,  1,  1,
                 1,  1,  1,  2,  1,  1,  1,  4,
                 1,  1,  1,  2,  1,  1,  1,  1,  1,  1,  1,
                 2,  1,  4 };
const int 
    rests[] =  { 0,  0,  1,  0,  0,  0,  0,  
                 1,  2,  1,  1,  1,  0,  0,
                 0,  0,  0,  0,  0,  1,  1,  0,  0,  2,
                 1,  2,  1,  1,  1,  0,  0,
                 0,  0,  0,  0,  0,  1,  1,  0,  0,  4,
                 0,  0,  0,  0,  1,  0,  0,  1,  0,  0,  2,
                 0,  0,  0,  0,  1,  1,  0,  2,
                 0,  0,  0,  0,  1,  0,  0,  1,  0,  0,  2,
                 1,  2,  4 };

#define x (sizeof(song)/sizeof(int))
const int BuzzPin = 12;
const int NoteDuration = 100; 

void setup() {
  pinMode(BuzzPin, OUTPUT);  
}

void loop() {
  
  for(int i = 0; i<x; i++){
    // play each note for the corresponding duration
    Buzz(song[i], NoteDuration*length[i]);

    // tiny break between notes
    delay(50); 

    // rest the corresponding duration (often 0)
    delay(NoteDuration*rests[i]);
  }
  
  delay(3000); // pause a moment before starting over
}

void Buzz(int frequencyHz, int durationMillis){
  // e.g. 1 / 2048Hz = 488uS, or 244uS high and 244uS low
  // to create 50% duty cycle
  // http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1231194692
  int Osc = 1000000 / frequencyHz / 2; // in microseconds
  
  // compute the number of iterations needed to hold
  // the nfote the desired duration
  int Iterations = frequencyHz * ((float)durationMillis / 1000);
  
  // light on
  Beep(frequencyHz);
  for (long i = 0; i < Iterations; i++ )
  {
      // beep!
      digitalWrite(BuzzPin, HIGH);
      delayMicroseconds(Osc);
      digitalWrite(BuzzPin, LOW);
      delayMicroseconds(Osc);
  }  
  // led off
  Reset();
}

void Reset(){
  for (int i=0; i < PinCount; i++){
    pinMode(Pins[i] , INPUT);
  }
}

void Beep(int hz){
  if      (hz < 450)  { Burn(Leds[0]); }
  else if (hz < 600)  { Burn(Leds[1]); }
  else if (hz < 750)  { Burn(Leds[2]); }
  else if (hz < 900)  { Burn(Leds[3]); }
  else if (hz < 1050) { Burn(Leds[4]); }
  else                { Burn(Leds[5]); }
}

void Burn(int led[2]){
  Burn(led[0], led[1]); 
}

void Burn(int highPin, int lowPin){
  pinMode(Pins[highPin], OUTPUT);
  pinMode(Pins[lowPin], OUTPUT);

  digitalWrite(Pins[highPin], HIGH);
  digitalWrite(Pins[lowPin], LOW);  
}

Next Steps

I’m not totally sure what I’ve got next (I’m running out of new components). I might work a switch in there or maybe try to do a larger LED array for some animation. I’m thinking about projects I can get Thing1 interested in, too. She likes to help but loses interest after about four seconds so it’s tough. She loved pushing a button I wired into the speaker, though:

(The only way I can get her to look at the camera long enough for a snapshot is by asking her to stick out her tongue.)