Trying to have the illusion of playing two tones at once

Hello! I've never posted on the forum before, so please let me know if I need to include additional information or posting to the incorrect category. I've been trying to learn how to use the millis() function properly and wanted to play two tones on two different piezoelectric passive buzzers. Was trying to make it sound like both buzzers were playing at the same time by alternating between the two pins (5 & 6) every 100 milliseconds every 500 milliseconds. Though the timing isn't that important currently, because I can't seem to get my tones to play correctly. Either the timing is slightly off or some tones aren't playing at all. Currently trying to run this program on an arduino uno. Any help/advice would be helpful! I also haven't used the millis() function before, so I'm not sure I'm using it correctly.

const double MAIN[] = {1046.50, 1567.98, 1975.53, 1479.98};

const char* CHORUS[] = {"N", "N", "N", "N", "N", "N", "N",
                      "N", "N", "N", "N", "N", "N",
                      "N", "N", "N", "E4", "N", "G4", "N",
                      "D4", "N", "F4#", "N", "E4", "F4#",
                      "E4", "B4", "E4", "C4", "C4#", "E4","E4",
                      "N", "G4", "N", "D4", "N", "F4#",
                      "N", "D4", "G4", "D4", "B4", "E4", "C4",
                      "N", "E4", "E4", "N", "G4", "N",
                      "D4", "N", "F4#", "N", "E4", "F4#", "E4",
                      "B4", "E4", "C4", "C4#", "E4", "E4",
                      "N", "G4", "N", "D4", "N", "F4#", "N",
                      "D4", "G4", "D4", "B4", "C4", "N",
                      "C4", "N", "E4", "N", "N", "N", "D4",
                      "N", "N", "N", "C4", "N", "N",
                      "N", "E4", "C4", "N", "E4", "E4", "N",
                      "G4", "N", "D4", "N", "F4#", "N",
                      "E4", "F4#", "E4", "B4", "E4", "C4", "C4#",
                      "E4", "E4", "N", "G4", "N", "D4",
                      "N", "F4", "N", "D4", "G4", "D4", "B4",
                      "E4", "C4", "N", "E4", "E4", "N",
                      "G4", "N", "D4", "N", "F4#", "N", "E4",
                      "F4#", "E4", "B4", "E4", "C4", "C4#",
                      "E4", "E4", "N", "G4", "N", "D4", "N",
                      "F4#", "N", "D4", "G4", "D4", "B4",
                      "C4", "N", "C4", "N", "E4", "N", "N",
                      "N", "D4", "N", "N", "N", "C4",
                      "N", "N", "N", "E4", "C4", "N", "E4"};



void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(6, OUTPUT);
  pinMode(5, OUTPUT);
}

double FrequencyCalc(const char*);

void loop() {

  double  mainFrequency,
          chorusFrequency;
  int noteInterval = 1000,
    switchInterval = 20,
    mainPin = 6,
    chorusPin = 5,
    mainFrequencyCount = 0;
  bool pinSwitch = false;

  unsigned long startMillis,
                currentMillis;

    for (int i = 0 ; i <= 176 ; i++){
      startMillis = millis();
      currentMillis = startMillis;

      if (mainFrequencyCount == 3){
        mainFrequency = MAIN[mainFrequencyCount];
        mainFrequencyCount = 0;
      }
      else{
        mainFrequency = MAIN[mainFrequencyCount];
        mainFrequencyCount++;
      }

      chorusFrequency = FrequencyCalc(CHORUS[i]);
      Serial.println(mainFrequency);
      Serial.println(chorusFrequency);


      while (currentMillis - startMillis <= noteInterval){
          while (currentMillis - startMillis <= switchInterval){

            if (chorusFrequency == 0.00){
              noTone(chorusPin);
              tone(mainPin, mainFrequency);
              currentMillis = millis();
            }
            
            if (chorusFrequency > 0.00){

              if (pinSwitch == true){
                noTone(chorusPin);
                tone(mainPin, mainFrequency);
                currentMillis = millis();
              }
              
              if (pinSwitch == false){
                noTone(mainPin);
                tone(chorusPin, chorusFrequency);
                currentMillis = millis();
              }

            } 
          }
        
           pinSwitch = !pinSwitch;
      
      currentMillis = millis();
      }
    }

}

double FrequencyCalc(const char* note) {

  double octave = note[1] - 48,
    key,
    frequency;
  char notes[] = {'C', 'D', 'E', 'F', 'G', 'A', 'B'};
  int keyPattern[] = {4, 6, 8, 9, 11, 13, 15};

  if (note[0] == 'N'){
    frequency = 0.00;
  }
  else {
      for (int a = 0 ; a <= 6 ; a++){
        if (note[0] == notes[a]){
          key = (octave - 1) * 12 + keyPattern[a];
          if (note[2] == '#'){
            key++;
          }
        }}

      key = (key - 49.00)/12.00;
      frequency = pow(2, key) * 440;
  }

    return frequency;}

Does it mean --
Buzzer-1 on DPin-6 will be delivering tone for 100 mS
when Buzzer-2 on DPin-5 is Off.

Buzzer-2 on DPin-5 will be delivering tone for 500 mS
when Buzzer-1 on DPin-6 is Off.

In effect, both Buzzers appear as delivering tones concurrently.

for 500 milliseconds it's supposed to play a frequency on buzzer-1 for 100 milliseconds. Then it's meant to switch to playing a frequency on buzzer-2, essentially switching 5 times.

Is it like this?
Buzzer-1 plays tone for 100 mS (Buzzer-2 is Off) and the Buzzer-2 plays tone for 500 mS (Buzzer-1 is Off) and then Buzzer-1 again....

What is the meanng of switching for 5 times?

The code could be refactored a bit but your main issue is going to be that 100ms or 500ms is a long time for the human ear and you will clearly hear the buzzers being on and off alternatively.

Also unless they have different physical properties there is no need to have two buzzers as you alternate between them anyway .

If you really want both to play at the same time best would be to merge the audio signals. There are simple to use components out there for signal shaping and I would recommend to watch YouTuber Julian Ilet’s series on his penny organ to get a grasp on this.

The Tone library can play multiple tones at the same time
https://docs.arduino.cc/libraries/tone/

Is it concurrent?

Ah this is new. Did not notice that before. I remember when tone was limited to one pin only.

It still is. This is the Tone library not the Arduino tone() function.

ah right - thx for pointing that out.

you may find this helpful.

don't understand why you wouldn't drive both buzzers at the same time. My hardware has only one buzzer

const byte MainPin   = 6;
const byte ChorusPin = 5;
const byte PinBut    = A1;

// -----------------------------------------------------------------------------
const double Freq [] = { 1046.50, 1567.98, 1975.53, 1479.98 };
const int    Nfreq   = sizeof(Freq)/sizeof(double);

const char* Chorus [] = {
    "N", "N", "N", "N", "N", "N", "N",
    "N", "N", "N", "N", "N", "N",
    "N", "N", "N", "E4", "N", "G4", "N",
    "D4", "N", "F4#", "N", "E4", "F4#",

    "E4", "B4", "E4", "C4", "C4#", "E4","E4",
    "N", "G4", "N", "D4", "N", "F4#",
    "N", "D4", "G4", "D4", "B4", "E4", "C4",
    "N", "E4", "E4", "N", "G4", "N",

    "D4", "N", "F4#", "N", "E4", "F4#", "E4",
    "B4", "E4", "C4", "C4#", "E4", "E4",
    "N", "G4", "N", "D4", "N", "F4#", "N",
    "D4", "G4", "D4", "B4", "C4", "N",

    "C4", "N", "E4", "N", "N", "N", "D4",
    "N", "N", "N", "C4", "N", "N",
    "N", "E4", "C4", "N", "E4", "E4", "N",
    "G4", "N", "D4", "N", "F4#", "N",

    "E4", "F4#", "E4", "B4", "E4", "C4", "C4#",
    "E4", "E4", "N", "G4", "N", "D4",
    "N", "F4", "N", "D4", "G4", "D4", "B4",
    "E4", "C4", "N", "E4", "E4", "N",

    "G4", "N", "D4", "N", "F4#", "N", "E4",
    "F4#", "E4", "B4", "E4", "C4", "C4#",
    "E4", "E4", "N", "G4", "N", "D4", "N",
    "F4#", "N", "D4", "G4", "D4", "B4",

    "C4", "N", "C4", "N", "E4", "N", "N",
    "N", "D4", "N", "N", "N", "C4",
    "N", "N", "N", "E4", "C4", "N", "E4"
};
const int Nchorus = sizeof(Chorus)/sizeof(char *);

char s [90];
char p [20];
char q [20];
char r [20];

// -----------------------------------------------------------------------------
//                 A   B  C  D  E  F   G
int KeyVal [] = { 13, 15, 4, 6, 8, 9, 11 };

double chorusFreq (
    const char* note )
{

    if (note[0] == 'N')
        return 0.00;

    int    octave = note[1] - '0';
    double key    = (octave - 1) * 12 + KeyVal [note[0] - 'A'];
           key    = (key - 49.00) / 12.00;
    double freq   = pow (2, key) * 440;


    dtostrf (key,  8, 2, q);
    dtostrf (freq, 8, 2, r);
    sprintf (s, "chorusFreq: %-3s %s Hz, octave %2d, key %s",
                                    note, r, octave, q);
    Serial.println (s);

    return freq;
}

// -----------------------------------------------------------------------------
unsigned long   MsecInterval  = 1000;
unsigned long   msec0;

int             freqIdx;
int             idx      = 30;
bool            disable;

void loop ()
{
    if (LOW == digitalRead (PinBut))
        disable = true;

    if (disable)
        return;

    // --------------------------------------
    // generate tones
    unsigned long msec = millis ();

    if (msec - msec0 < MsecInterval)
        return;

    msec0 = msec;

#if (0)
    // update main tone
    tone (MainPin, Freq [freqIdx]);
    if (Nfreq <= ++freqIdx)
        freqIdx = 0;

#else
    // update chorus freq
    tone (MainPin, chorusFreq (Chorus [idx]));
    if (Nchorus <= ++idx)
        idx = 0;
#endif
}

// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);

    pinMode (PinBut,    INPUT_PULLUP);

    pinMode (MainPin,   OUTPUT);
    pinMode (ChorusPin, OUTPUT);

    tone (MainPin, 440);
    delay (1000);
}

If you google "arduino tone library", you get the usual Arduino doc page for a library of which the page says

You can make multiple instances of the Tone object, to create tones on different pins.

Which is either new, or something I never noticed.

a7

Refactored a bit? And thank you! I'll have to take a look at the video later today :blush:

Thank you! I didn't know there was a tone library, I might have to just switch to using that and avoid some pain

Cf previous discussion, I was confused too.

Tone() function versus tone library where they state

Arduino Core Version

A simplified version of the Tone library has been incorporated into the Arduino core since 0018. It only provides a single tone (since only one timer is used).

Yes you make it more verbose than it could (like you could have currentMillis = millis(); only once, or just an else instead of if (pinSwitch == false){ (if a bool is not true then it’s false) or repeating noTone/Tone or having a for loop when just letting the loop loops and increment both index separately in their interval et. …)

Resting state here!

I finally got two frequencies one each from two pins.

By then I had taken some missteps. But I'm sure I had to install ToneLibrary (I use the 1.18x IDE) and go about things differently to just using tone().

This is were I left it

Tone freq1;
Tone freq2;

void setup()
{
 Serial.begin(9600);

 freq1.begin(10);
 freq2.begin(9);
}

void playDTMF()
{
 freq1.play(700, 15000);
 freq2.play(900, 15000);
}

void loop() {
 playDTMF();
}

I'll find out sooner later what the implications are of having, at one point, uninstalled the Tone library.

It's odd the doc page can be read as talking about "good old" tone(); with the time I have I did not see that the library has tone(), which it could provide and hide the details needed in the code above. I hacked it out of one of the examples to see if it even works: 700 and 900 are totally arbitrary choices.:wink:

a7

is a frequency of zero the same as noTone()? is the above test necessary?

That was meant to preventing it from switching between the two buzzer when chorusFrequency is 0. Main is never 0, so when chorusFrequency is 0 it was meant to just play mainFrequency for the duration of noteInterval.

I don't need it in every conditional statement? I thought if I checked (currentMillis - start Millis), it would make a never ending loop unless I updated the value at the end of each loop