Go Down

Topic: Using tone() for non-regular intervals (Read 8625 times) previous topic - next topic

brevik

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.

larryd

#1
Aug 03, 2013, 09:53 am Last Edit: Aug 03, 2013, 10:34 am by LarryD Reason: 1
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?

Code: [Select]

//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()

No technical PMs.
If you are asked a question, please respond with an answer.
If you are asked for more information, please supply it.
If you need clarification, ask for help.

brevik

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:

Code: [Select]

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

jack wp

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.

brevik

The piezo buzzer is http://www.radioshack.com/product/index.jsp?productId=2062403 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.

jack wp


larryd

This is OK.
What do you think this does?

Quote
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.
No technical PMs.
If you are asked a question, please respond with an answer.
If you are asked for more information, please supply it.
If you need clarification, ask for help.

jack wp

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


Code: [Select]
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.

brevik

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

Code: [Select]
//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:
Code: [Select]

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.

larryd

@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

Code: [Select]
if (playing == true && (millis() - playingNow >= playTime))
HINT:  it is similar to the above.
No technical PMs.
If you are asked a question, please respond with an answer.
If you are asked for more information, please supply it.
If you need clarification, ask for help.

brevik

Now I did get the code to work with

Code: [Select]
    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. 


larryd

@brevik
Quote
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.
Code: [Select]
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"?

Quote
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.
No technical PMs.
If you are asked a question, please respond with an answer.
If you are asked for more information, please supply it.
If you need clarification, ask for help.

larryd

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

Code: [Select]
      if (i>5)
      {
        i = 0;
      }
      Serial.println(silence[i]);
No technical PMs.
If you are asked a question, please respond with an answer.
If you are asked for more information, please supply it.
If you need clarification, ask for help.

brevik

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:

Code: [Select]

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


and then change the comparison inside of the loop:

Code: [Select]

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?

jack wp

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

You say:
Quote

As for the loop(), it is:
Code:
Code: [Select]
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  
Quote
is called inside of displayDate();,
, and displayDate() is called from within loop(); then you could run out of stack memory pretty fast.

Go Up