Using tone() for non-regular intervals

I've been trying to get this to work for a few days and tried various things that have just ended up with a garbled nasty constant sound coming from the piezo, and perhaps I can get a suggestion as to how to run this interval tone.

I've got a piezo hooked to pin 4 of the Arduino Micro and called is 'speaker', and I am able to make regular intervals of beeping happen, however I have a tone that requires different timing (as apposed to 250ms on, 250ms off, repeat.)

Below is the interval:

125ms @ 3000Hz
10ms Silence
125ms @ 3000Hz
145ms Silence
125ms @ 3000Hz
10ms Silence
125ms @ 3000Hz
145ms Silence
125ms @ 3000Hz
10ms Silence
125ms @ 3000Hz
645ms Silence
-Repeat-

This will just go into a function and will run when the function is called. I need to refrain from using delay() and I have tried quite a few things with millis(), but I haven't gotten it down correctly. I can also think of a couple ways to do this by making each on/off cycle its own function, but there has to be a more efficient way to accomplish this rather than bloating the code and taking up a lot more memory than is needed.

Any suggestions will be appreciated. Thank you.

Here is a sketch which will work but you have to add some code.
Figure out what has to be placed HERE.

I also put a bug in the code for you to find and correct.

Do you understand how everything works?

//Constants
const int silence[6] = {10,145,10,145,10,640};     //repeating sequence
const int playTime = 125;    //duration of the tone
//RAM
unsigned long millisNow; 
boolean playing;
int i;
int playingNow;

void setup()
{
  Serial.begin(9600);
  pinMode(4,OUTPUT);
  tone(4,3000);
  playing = true;
  playingNow = millis();
  i = 0;
}                        // END of setup()     

void loop()
{

  //Tone On timing
    if (playing == true && (millis() - playingNow >= playTime))
  {
    noTone(4);             
    playing = false;         //indicate tone not playing
    millisNow = millis();  //gets ready for off timing
  }

  //Tone Off timing
  if (playing == false)
  {
    if (    HERE      >= silence[i])
    {
      i++;                    //point to next delay off time
      if (i>5)
      {
        i = 0;
      }
      playing = true;        
      tone(4,3000);           
      playingNow = millis(); 
    }
  }
}                   // END of loop()

Well this is quite the exercise, I've come to understand arrays much better now, however I am unable to get this working. I'm pretty sure I have everything down correctly, but it doesn't operate correctly.

What I have right now is:

//Constants
const int speaker = 4; //piezo
const int silence[6] = {10,145,10,145,10,640};     //repeating sequence
const int playTime = 125;    //duration of the tone
//RAM
unsigned long millisNow; 
boolean playing;
int i;
int playingNow;

void setup()
{
  Serial.begin(9600);
  pinMode(speaker,OUTPUT);
  tone(speaker,3000);
  playing = true;
  playingNow = millis();
  i = 0;

void twentyspeaker()
{

  //Tone On timing
     if (playing == true && playingNow >= playTime)
  {
    noTone(speaker);             
    playing = false;         //indicate tone not playing
    millisNow = millis();  //gets ready for off timing
  }

  //Tone Off timing
  if (playing == false)
  {
    if (millisNow >= silence[i])
    {
      i++;                    //point to next delay off time
      if (i>5)
      {
        i = 0;
      }
      playing = true;        
      tone(speaker,3000);           
      playingNow = millis(); 
    }
  }
}

I'm assuming that the bug in the program is in: if (playing == true && (millis() - playingNow >= playTime))

As I can't figure out why playingNow would subtract millis(). However, if this is not the correct assumption, then why would millis() subtract playNow?

Then, I'm almost certain that millisNow is the "fill-in" piece.

As far as I can figure, it should be working, however I'm probably wrong. If I am correct though, then that would mean that there is something else that I need to fix somewhere else.

Is this piezo a "buzzer"?
A buzzer will produce audible noise, but will not be suitable to play a tune. If you want tunes (tones), try a speaker, or even an earphone. Maybe a small resistor would help if your speaker is 8 ohms.

The piezo buzzer is RadioShack.com Official Site - America's Technology Store and it works just fine when I apply a tone of 125ms at 3000Hz, 10ms silence, repeat. I can't imagine that it would work any differently when different duration of silence is applied, other than just the timing sounding different.

I have two other intervals working on it:

250ms @ 2500Hz
250ms Silence
Repeat

and

125ms @ 3000Hz
10ms Silence
Repeat

Both of which sound just as I had intended and can be easily achieved using the millis() timing (blink without delay type of function), I am just new to programming and wasn't able to figure out how to switch the silence durations to be different throughout the loop. Of which, I am still trying to work out right now, I still haven't worked out the issues in the above code.

You need a loop() function

This is OK.
What do you think this does?

I'm assuming that the bug in the program is in: if (playing == true && (millis() - playingNow >= playTime))

The bug I left in will only shows up after about 40-60 seconds of operation and won't stop things from working until then.

This is OK.

, I don't think so. Seems to be more than one "not OK"

int playingNow;

void setup()
{
  Serial.begin(9600);
  pinMode(speaker,OUTPUT);
  tone(speaker,3000);
  playing = true;
  playingNow = millis();

Don't think millis() will work to well being saved into an int.

I still don't see the loop(function). And I will stop at those two problems right now.

Well I've already changed some of the variables to be unsigned long numbers just in case:

//Constants
const unsigned long silence[6] = {10,145,10,145,10,640};     //repeating sequence
const int playTime = 125;    //duration of the tone
//RAM
unsigned long millisNow; 
boolean playing;
int i;
unsigned long playingNow;

However, with the above code, it still just produces an almost constant squeal. It sounds like the noTone() is not being used, but I'm still working it out...

As for the loop(), it is:

void loop() { 

  displayDate();
}

The loop which is being discussed here is called inside of displayDate();
Its an alarm of sorts that plays if a countdown time is below 15 seconds and will continue to play until the time reaches 10 seconds.

@jackwp
It will work as is for 40=60s then fail so that's the bug.
Let brevik fix it though.

the missing loop() is in @brevik code.

So brevik, what has to go HERE

if (playing == true && (millis() - playingNow >= playTime))
HINT: it is similar to the above.

Now I did get the code to work with

    if (millis() - millisNow >= silence[i])

However, I am still trying to envision the cycle of process and how the "millis()" part gets store and reset.
I'm guessing that this instance of millis() should actually be stored as a separate variable and that is why the loop will not function past 40 - 60 seconds.
For the application, I don't need the loop to go on for that long, but this is a really good opportunity for learning, so for that I thank you for the time and patience.

@brevik

I did get the code to work

Good for you!

Note: millis() returns the value in unsigned long format equal to the number of milliseconds since the Arduino has been on.

if (playing == true && (millis() - playingNow >= playTime))

Has the bug.
The problem is int playingNow; being of an integer.
It is not big enough to hold the value millis() since this is unsigned long.
Hence it should be unsigned long playinNow;
Would you know how to add to the sequence 900 128 etc?
How?

The question is, "is there anything in the code you do not understand"?

I don't need the loop to go on for that long

Well, you can adjust the code to have it run as long as you want.

PS
Make the following addition, what does this do and what use is it?

      if (i>5)
      {
        i = 0;
      }
      Serial.println(silence[i]);

Ohh ok, so I was correct in changing the playingNow to an unsigned long number, as have gotten into the habit of always making any millis() variables into unsigned long numbers.

Now if I wanted to add more pauses, with different values, I would just add to the array:

const unsigned long silence[8] = {10,145,10,145,10,640,900,128};

and then change the comparison inside of the loop:

if (i>7)
      {
        i = 0;
      }

To accommodate for the additional numbers so that it will go to the end of the array and make the pauses.
Now this array is accounting for the values from 0-7 and then the eighth value for null, correct?

I have not seen you full sketch, that shows the loop() function

You say:

As for the loop(), it is:
Code:

void loop() { 

displayDate();
}


The loop which is being discussed here is called inside of displayDate();
Its an alarm of sorts that plays if a countdown time is below 15 seconds and will continue to play until the time reaches 10 seconds.

The loop is a standard function, and runs without being called. If it

is called inside of displayDate();,

, and displayDate() is called from within loop(); then you could run out of stack memory pretty fast.

LarryD:
PS
Make the following addition, what does this do and what use is it?

      if (i>5)

{
        i = 0;
      }
      Serial.println(silence[i]);

That should return the position inside of the silence array when the pause is initialized.

the eighth value for null,

You are thinking about a string of characters.
This array has no null termination, hence you are using 0-7 in your code to control things.

The purpose of the Serial.println(..) is it can be used to help debug your program.
i.e. you place lines like this at strategic locations to see if want you think is happening is really happening.
You can then remove them when your code is functional.

@jacwp

//Constants
const int silence[6] = {10,145,10,145,10,640};     //repeating sequence
const int playTime = 125;    //duration of the tone
//RAM
unsigned long millisNow; 
unsigned long playingNow;
boolean playing;
int i;

void setup()
{
  Serial.begin(9600);
  pinMode(4,OUTPUT);
  tone(4,3000);
  playing = true;
  playingNow = millis();
  i = 0;
}                        // END of setup()     

void loop()
{

  //Tone On timing
    if (playing == true && (millis() - playingNow >= playTime))
  {
    noTone(4);             
    playing = false;         //indicate tone not playing
    millisNow = millis();  //gets ready for off timing
  }

  //Tone Off timing
  if (playing == false)
  {
    if (millis()- millisNow >= silence[i])
    {
      i++;                    //point to next delay off time
      if (i>5)
      {
        i = 0;
      }
      Serial.println(silence[i]);
      playing = true;        
      tone(4,3000);           
      playingNow = millis(); 
    }
  }
}                   // END of loop()

Also:
You obviously would not code things like 5 or 7 directly, it would be better to use #define count 7 in your definitions part of you code. Then if (i>count) later in the sketch.

@larry, I am a bit confused I guess. I was wanting to see the script that the OP is having trouble with. Are you two sitting next to each other? How do yu have his script?
The OP showed a snippet

void loop() { 

  displayDate();
}

Which is not shown in the script you uploaded.
What makes you think the script you uploaded, is the script the OP is currently using?

I am a bit confused I guess.

Sorry, I just uploaded the sketch that I originally offered with the fixes.

EDIT:
In post #2 brevik was working off my Sketch not his (we never saw his).