Arduino Forum

Using Arduino => Audio => Topic started by: brevik on Aug 03, 2013, 03:53 am

Title: Using tone() for non-regular intervals
Post by: brevik on Aug 03, 2013, 03:53 am
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.
Title: Re: Using tone() for non-regular intervals
Post by: larryd on Aug 03, 2013, 09:53 am
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()

Title: Re: Using tone() for non-regular intervals
Post by: brevik on Aug 03, 2013, 12:04 pm
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.
Title: Re: Using tone() for non-regular intervals
Post by: jack wp on Aug 03, 2013, 06:36 pm
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.
Title: Re: Using tone() for non-regular intervals
Post by: brevik on Aug 03, 2013, 07:58 pm
The piezo buzzer is http://www.radioshack.com/product/index.jsp?productId=2062403 (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.
Title: Re: Using tone() for non-regular intervals
Post by: jack wp on Aug 03, 2013, 08:06 pm
You need a loop() function
Title: Re: Using tone() for non-regular intervals
Post by: larryd on Aug 03, 2013, 08:10 pm
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.
Title: Re: Using tone() for non-regular intervals
Post by: jack wp on Aug 03, 2013, 08:21 pm
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.
Title: Re: Using tone() for non-regular intervals
Post by: brevik on Aug 03, 2013, 08:49 pm
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.
Title: Re: Using tone() for non-regular intervals
Post by: larryd on Aug 03, 2013, 08:54 pm
@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.
Title: Re: Using tone() for non-regular intervals
Post by: brevik on Aug 03, 2013, 09:20 pm
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. 

Title: Re: Using tone() for non-regular intervals
Post by: larryd on Aug 03, 2013, 09:37 pm
@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.
Title: Re: Using tone() for non-regular intervals
Post by: larryd on Aug 03, 2013, 09:42 pm
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]);
Title: Re: Using tone() for non-regular intervals
Post by: brevik on Aug 03, 2013, 09:55 pm
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?
Title: Re: Using tone() for non-regular intervals
Post by: jack wp on Aug 03, 2013, 09:57 pm
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.
Title: Re: Using tone() for non-regular intervals
Post by: brevik on Aug 03, 2013, 10:02 pm

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]);



That should return the position inside of the silence array when the pause is initialized.
Title: Re: Using tone() for non-regular intervals
Post by: larryd on Aug 03, 2013, 10:09 pm
Quote
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
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;
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()
Title: Re: Using tone() for non-regular intervals
Post by: larryd on Aug 03, 2013, 10:15 pm
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.
Title: Re: Using tone() for non-regular intervals
Post by: jack wp on Aug 03, 2013, 10:22 pm
@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
Code: [Select]
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?
Title: Re: Using tone() for non-regular intervals
Post by: larryd on Aug 03, 2013, 10:31 pm
Quote
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).

Title: Re: Using tone() for non-regular intervals
Post by: jack wp on Aug 03, 2013, 10:35 pm
Ok Larry, I see. I do appreciate all your input/suggestions.

I figure, you can lead the horse to water, but you can't make them drink.  LOL, so what you offered him, may or may not be what he is running.

Lets keep plugging away at this.
Thanks, Jack

Title: Re: Using tone() for non-regular intervals
Post by: brevik on Aug 03, 2013, 10:35 pm
Ah string, yes, I got all jumbled up. I tried unsuccessfully working with strings last week.

As for the entire code, the whole thing will be too long to post the entire thing in one post, but the displayDate() function is:

Code: [Select]

//setting up seconds time conversion
#define secondsinaday 86400  //((60*60)*24)
#define secondsinhour 3600  //(60*60)
#define secondsinminute 60

long countpreviousMillis = 0;   //stores count milliseconds    
long countinterval = 1000; //1 second

void displayDate(){

 unsigned long countcurrentMillis = millis();

 if(countcurrentMillis - countpreviousMillis > countinterval) {
   // save the last time counted
   countpreviousMillis = countcurrentMillis;  
   totalsectime--; //decrement of 1 second
   unsigned long sectime = totalsectime;
   /*takes the totalsectime variable and converts it inside of this function
    so that the time conversions do not interfere with the later functions */

   unsigned long days = sectime/secondsinaday; //calculates seconds in a day
   sectime = sectime % secondsinaday; //takes the remainder of the previous calculation
   unsigned long hours = sectime/secondsinhour; //calculates seconds in an hour
   sectime = sectime % secondsinhour; //takes the remainder of the previous calculation
   unsigned long minutes = sectime/secondsinminute; //calculates seconds in a minute
   unsigned long seconds = sectime % secondsinminute; //takes the remainder, and that is seconds

   /*below takes the values generated from above and makes variables for each digit to display
    based on on dividing the values by hundreds/tens/single numbers*/

   unsigned long days_hundreds = days/100;
   unsigned long days_tens = (days %100)/10;
   unsigned long days_units = (days %100)%10;

   unsigned long hours_tens = (hours %100)/10;
   unsigned long hours_units = (hours %100)%10;

   unsigned long minutes_tens = (minutes %100)/10;
   unsigned long minutes_units = (minutes %100)%10;

   unsigned long seconds_tens = (seconds %100)/10;
   unsigned long seconds_units = (seconds %100)%10;

   //sends digit to: 7221 device
   //digit order
   //variable from above calculations
   //true/false for the decimal point
   lc.setDigit(1,0,days_hundreds,false);
   lc.setDigit(1,1,days_tens,false);
   lc.setDigit(1,2,days_units,false);

   lc.setDigit(0,0,hours_tens,false);
   lc.setDigit(0,1,hours_units,false);
   lc.setDigit(0,2,minutes_tens,false);
   lc.setDigit(0,3,minutes_units,false);
   lc.setDigit(0,4,seconds_tens,false);
   lc.setDigit(0,5,seconds_units,false);  
 }

 if(totalsectime==15){   //wrap-around when time reaches 15 seconds
   displayWrap();
   return;
 }
 if(totalsectime<=15){   //wrap-around when time reaches 15 seconds
   lc.shutdown(0,false);
   lc.shutdown(1,false);
 }
 if(totalsectime <=5 ) {   //lights emitter LEDs when time reaches 5 seconds and keeps speaker solid
   digitalWrite(emitters, HIGH);
 }
 else{
   digitalWrite(emitters, LOW);
 }

 if(totalsectime > 20){
   updatespeaker();
 }
 
 if(totalsectime > 5){   //colon blink function for normal countdown
   colonBlink();
 }
 
 if(totalsectime < 21){
twentyspeaker();
 }
 
   if(totalsectime < 10){
tenspeaker();
 }

   if(totalsectime <= 0){
   zerospeaker();
   delay(2500);
   displayFade();
   genserOne();
   displayWrap();
   delay(5000);
   displayWrap();
   totalsectime = random(16756131);  
 }
 
}



I'm sure the code is bloated and inefficient, but it does operate currently, and I will work on making the code more efficient and memory conservative as I work on it. Still working on the learning curve.

I'm also using the LedControl Library for the 72xx display drivers to drive seven segment displays.

I didn't have any code previously to the suggestions, as I wasn't sure how to achieve what I wanted other than just regular intervals of on/off using millis().
Title: Re: Using tone() for non-regular intervals
Post by: jack wp on Aug 03, 2013, 10:46 pm
It looks like I am just adding more confusion to this thread than help, so I will step aside. You two carry on, and good luck. Jack
Title: Re: Using tone() for non-regular intervals
Post by: larryd on Aug 03, 2013, 10:52 pm
I see the potential of the bug  ;)  that I earlier discussed.
millis() is unsigned long

long countpreviousMillis = 0;   //stores count milliseconds    
long countinterval = 1000; //1 second
OR
unsigned long countpreviousMillis = 0;   //stores count milliseconds    
unsigned long countinterval = 1000; //1 second

@jackwp
Thanks for participating.
Title: Re: Using tone() for non-regular intervals
Post by: brevik on Aug 03, 2013, 11:02 pm
Jackwp: Any input is always appreciated, I'm always happy to answer any questions that may clear up any confusion.

LarryD: you got me there, I have those millis() assigned as a long number by mistake. Fixed.

Visuals are always good, so here is a video of the code in action:

Beware, it may come out as very high-pitched sounding, check your volume.

http://youtu.be/HFCiaQTHPhE (http://youtu.be/HFCiaQTHPhE)

I do need to work out the timing of the audio and getting the audio function to stop looping at appropriate times, but at the moment, your suggestions solved my initial issue and got me up and running. Thank you very much for your assistance.
Title: Re: Using tone() for non-regular intervals
Post by: larryd on Aug 03, 2013, 11:18 pm
Nice little project.
Looks like it would fit on your wrist quite nicely  ;)
Make sure you have some decoupling capacitors on the breadboard.

You may want to try these jumpers as they are multi colored and easily traced:
Title: Re: Using tone() for non-regular intervals
Post by: brevik on Aug 03, 2013, 11:27 pm
Haha yeah, I ran out of jumpers so I started to make some out of a spool of solid core wire, all the same color, which works for now, just as long as I don't take an extended break from the project. Then I'll forget where everything is.

The final project that I am aiming towards is going to look something like:

http://www.youtube.com/watch?v=rVJYx9rvMp4

Slowly but surely plugging away at getting it done. The Arduino forums have been a great help to me too. Everyone is very helpful here. Thanks again.