Piezo buzzer, play tones non blocking - how to?

Rezo

Well-known member
I have an application that visually displays alerts on an LCD when a threshold is passed, and I would like to add an audiable notifiation/alarm to this.
I have a basic buzzer/piezo hooked up to a PWM pin on a teensy 4.x with a transistor (NPN).

What I would like to do is be able to call a function that will "play" a series of beeps in intervals. So for example, play beep-beep-beep (200ms delays between them) and then stop and not play again for another 0.4 or 1.4 seconds.
I would like to use this library as it can play tones but can also control the volume via code (this is crucial to me, I can't use any additional hardware to control the volume)

Does anyone have a suggestion on how to acheive this? The code needs to be none blocking as well.
 
The Arduino tone() function is by far the simplest way.

Here is Arduino's documentation. All Teensy models support this.

https://www.arduino.cc/reference/tr/language/functions/advanced-io/tone/

Using tone() does take interrupts and use one IntervalTimer. If you wish to use PWM, using analogWriteFrequency() and analogWrite() can give you an output without any CPU overhead. Documentation here:

https://www.pjrc.com/teensy/td_pulse.html

Unlike tone(), you can't just specify the duration and have the waveform automatically turn off. You'll probably need to use something like elapsedMillis to make a non-blocking check for how long the waveform has been on and then turn it off.

https://www.pjrc.com/teensy/td_timing_elaspedMillis.html

These will give you simple on/off waveforms.

To also control the volume, you'll probably want to use the audio library, probably with the MQS output so you can get the waveform on a pin without adding extra hardware. The audio library may be overkill, but if you want to control the volume or other aspects of the waveform (maybe use the envelope effect for a nice attach-decay-sustain-release) it is definitely the best non-blocking way to synthesize audio waveforms.

This tutorial is the best place to get started with the audio library.

https://www.pjrc.com/store/audio_tutorial_kit.html

All the tutorial material is written for use with the audio shield. Just know that you have many options in the design tool for output, and MQS is probably the best choice. Detailed documentation is in the design tool (right side panel).
 
For what it is worth, for some programs in the past, before tone I had a quick and dirty version of sound output, that did what I needed, like to let me know the robot got the last command from the remote...
Also I found tone did not work very well for multiple notes.

Again this is nothing special...
Code:
void SoundNoTimer(unsigned long duration,  unsigned int frequency)
{
// The tone command does sort of work, but does not play multiple sounds smoothly
//  tone(SOUND_PIN, frequency, duration);  // Try the arduino library
//  delay(duration);
  pinMode(SOUND_PIN, OUTPUT);
  digitalWrite(SOUND_PIN, LOW);
  toggle_count = 2 * frequency * duration / 1000;
  lusDelayPerHalfCycle = 1000000L/(frequency * 2);

  // if we are using an 8 bit timer, scan through prescalars to find the best fit
  while (toggle_count--) {
    // toggle the pin
    fHigh  = !fHigh;
    digitalWrite(SOUND_PIN, fHigh? LOW : HIGH);
    // delay a half cycle
    delayMicroseconds(lusDelayPerHalfCycle);
  }    
  digitalWrite(SOUND_PIN, LOW);
}

void MSound(byte cNotes, ...)
{
  va_list ap;
  unsigned int uDur;
  unsigned int uFreq;
  va_start(ap, cNotes);

  while (cNotes > 0) {
    uDur = va_arg(ap, unsigned int);
    uFreq = va_arg(ap, unsigned int);
    SoundNoTimer(uDur, uFreq);
    cNotes--;
  }
  va_end(ap);
}
But for example with with the hexapod, I would have sounds in like:
Startup: MSound(3, 60, 2000, 80, 2250, 100, 2500);
Shutdown: MSound(3, 100, 2500, 80, 2250, 60, 2000);
Command received: MSound( 1, 50, 2000);
Command options cycled back to first: MSound( 2, 50, 2000, 50, 3000);

But again maybe for simple timed beep with pause, just use tone as you mentioned not blocking...
 
Thanks both for your comments.

I would have liked to try the Audio library with the MQS output but the hardware is already assembled and the buzzer is on pin #3 (PWM)
What's very important is the use of the PWM duty cycle to control the volume, which seems to work very well when doing simple tests.

I'll try hack something togetger with the code Kurt shared and see how it goes
 
Back
Top