Can't figure out how to run two functions at once

Hello,

So, for a special occasion I made Jimmie P. Rodgers’ Charlieplexed Arduino heart:

It works great, no issues. Then I wanted to attach a piezo and play some music at the same time, so I found D. Cuartielles’ Play Melody code and got that working fine (in a separate sketch) as well. My problem is very simple: I would really like to play the light animation and the music simultaneously, and I can’t for the life of me figure out how to run these two functions at the same time. I’ve tried using a while loop and I’ve tried using millis() to just start the two functions at approximately the same time but I’m not quite getting it together somehow – every variation I can come up with seems to result in one function going first and then the second starting when it’s done. Could anyone give me a clue? I’d be most grateful. Here is the code as it stands (minus some notes and credits just for readability’s sake), which causes the two functions to play in sequence – thanks!:

#define lb    4170 // ?? Hz
#define  c     3830    // 261 Hz 
#define  d     3400    // 294 Hz 
#define  e     3038    // 329 Hz 
#define  f     2864    // 349 Hz 
#define  g     2550    // 392 Hz 
#define  a     2272    // 440 Hz 
#define  bf     2180    // 493 Hz 
#define  b     2028    // 493 Hz 
#define  C     1912    // 523 Hz 
#define  R     0

#include <avr/pgmspace.h>  //This is in the Arduino library

int speakerOut = 9; // for piezo
int opin = 2; // for charlieplexed heart
int owpin = 3;
int gpin = 7;
int gwpin = 5;
int bpin = 6;
int bwpin = 4;

const int pins[] = {
  opin,owpin,gpin,gwpin,bpin,bwpin};

const int heartpins[27][2] ={
  {gpin,opin}, // 1
  {opin,gpin}, // 2
  {bpin,opin}, //3
  {opin,bpin}, //4
  {gpin,gwpin}, //5
  {gwpin,opin}, //6
  {opin,gwpin}, //7
  {opin,owpin}, //8
  {bwpin,opin}, //9
  {opin,bwpin}, //10
  {bwpin,bpin}, //11
  {gwpin,gpin}, //12
  {gpin,owpin}, //13
  {owpin,gpin}, // 14
  {owpin,opin}, //15
  {bpin,owpin}, //16
  {owpin,bpin}, //17 
  {bpin,bwpin}, //18
  {gwpin,owpin}, //19
  {owpin,gwpin}, //20
  {gpin,bpin}, //21
  {bwpin,owpin}, //22
  {owpin,bwpin}, //23
  {bwpin,gwpin}, //24
  {bpin,gpin}, //25
  {gwpin,bwpin}, //26
  {gpin,bwpin} //27
};

int blinkdelay = 200;   //This basically controls brightness. Lower is dimmer
int runspeed = 50;      //smaller = faster

byte heart[][27] PROGMEM ={

  //Fills up from the center
  {0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0},
  {0,0,0,0,0,1,1,0,1,1,0,0,1,1,1,1,1,0,0,1,1,1,0,0,1,0,0},
  {1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,0,1,1,1,1,1,1},
  {1,1,1,1,1,0,0,1,0,0,1,1,0,0,0,0,0,1,1,0,0,0,1,1,0,1,1},
  {1,1,1,1,1,0,0,1,0,0,1,1,0,0,0,0,0,1,1,0,0,0,1,1,0,1,1},
 
  //Kill line
  {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};

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

int melody[] = { c,c,d,c,f,e,R,c,c,d,c,g,f,R,c,c,C,a,f,e,d,bf,bf,a,f,g,f,R, };
int beats[]  = { 3,1,4,4,4,4,4,3,1,4,4,4,4,4,2,2,4,4,4,4,8,2,2,4,4,4,4,16, }; 

int MAX_COUNT = sizeof(melody) / 2;
long tempo = 100000;
int pause = 1000;
int rest_count = 70;
int tone = 0;
int beat = 0;
long duration  = 0;


void loop()
{
playLights(); 
playSong(); 
}

void playTone() {
  long elapsed_time = 0;
  if (tone > 0) { 
    while (elapsed_time < duration) {
      digitalWrite(speakerOut,HIGH);
      delayMicroseconds(tone / 2);
      digitalWrite(speakerOut, LOW);
      delayMicroseconds(tone / 2);
      elapsed_time += (tone);
    } 
  }
  else { 
    for (int j = 0; j < rest_count; j++) { 
      delayMicroseconds(duration);  
    }                                
  }                                 
}

void playSong() {
 
  for (int i=0; i<MAX_COUNT; i++) {
   
    tone = melody[i];
    beat = beats[i];
    duration = beat * tempo; 
    playTone(); 
    delayMicroseconds(pause);
  }
}

void turnon(int led)
{
  int pospin = heartpins[led][0];
  int negpin = heartpins[led][1];
  pinMode (pospin, OUTPUT);
  pinMode (negpin, OUTPUT);
  digitalWrite (pospin, HIGH);
  digitalWrite (negpin, LOW);
}


void alloff()
{
  for(int i = 0; i < 6; i++)
  {
    pinMode (pins[i], INPUT);
  }
}

void playLights()
{
  boolean run = true; 
  byte k;
  int t = 0;
  while(run == true)
  
  { 
    
    for(int i = 0; i < runspeed; i++)
    { 
     for(int j = 0; j < 27; j++)
      { 
        k = pgm_read_byte(&(heart[t][j]));
        if (k == 2)
        {
          run = false;
        }
        else if(k == 1)
        {
          turnon(j); 
          delayMicroseconds(blinkdelay);
          alloff();
        }
        else if(k == 0)
        {
          delayMicroseconds(blinkdelay);
        }
      }
    }
    t++;  
  }
}

You can't run two functions at once. The mega168 will execute one function, then the other. The trick is to make them seem like they're running simultaneously by calling both functions repeatedly in your main loop and having the functions carry out the proper actions only when it's time for them. To do this, your functions cannot block processor execution (e.g. they cannot use delay() or delayMicroseconds()). Instead, if it's not time for them to do something different, they need to let program execution return back to the main loop.

You can find a piezo buzzer module in the Orangutan Arduino library that uses timer1 to play melodies in the background without blocking program execution. If you can adapt this module to work with your setup it would probably run just fine on top of your charliplexing code (since the buzzer code it's driven by interrupts). It's designed to make it easy to play melodies, and one of its strong points is that it can run entirely in the background without further input from you while the rest of your code executes.

  • Ben

Thanks bens! That sounds like a good approach, I will check it out tomorrow.

Edit: I had a chance to look at it -- would that mean purchasing the Orangutan LV-168 just to run the piezo? New chip purchases from overseas are probably out of scope for this application, but I appreciate the suggestion. I'm now thinking that I'll just combine the two functions into one so that one animation frame plays and then one tone, with the timing of the tones dictating the tempo of the animation frame changes. It's a little departure from the basic heart animation code, but it might have its own charms. We'll see!

Edit: I had a chance to look at it -- would that mean purchasing the Orangutan LV-168 just to run the piezo?

You wouldn't have to buy the LV-168 for this. Think of of the LV-168 as an arduino-compatible board with a lot of extra hardware packed in (such as a piezo in this case). The Orangutan libraries are something Bens wrote to make it easier to use all the extra hardware from the Arduino environment, but in this case you can just attach an piezo to your Arduino on the correct pin and use the library.

Ah, that's good to know for next time. For this time, I just integrated the two functions and replaced the delay functions for the lights with the ones which timed the melody, so that both the melody and the lights went at the same time. Worked very well, and it was cute having the lights flashing to the beat of the piezo song. I appreciate the followup!

Hi, this is Jimmie. A friend pointed out that someone in the forum had made my heart. Do you have any pictures or video of the one you built in action? I would love to see them.

Oh, and you can still use (URL REMOVED) the programmer for creating the animations. You just need to click on “Array” once you hit generate, and then paste it in.

Hi Jimmie,

Thanks so much for the excellent instructable, and the programmer, which was a huge timesaver. I will put up some photos when I get a sec and/or a little video if I can manage it, so you can get the full piezo experience ;) . The heart was a big hit with the recipient!