Xiao BLE Sense Buzzer Problems

Hi, I’m trying to play a little tune using a passive piezo. Usine the “tone()” function, the buzzer will sound, but it will not stop, and the code no longer seems to function when the sound starts. I suspect there is some interference between the buzzer pins and some other critical function of the Xiao BLE Sense. I recall that certain GPOs shouldn’t be used under certain circumstances but have tried a few pin combinations and still getting the issue. Any suggestions for which pins to use with a buzzer if I also have to use pins to signal a motor driver and pins to sense button pushes etc?

It is entirely possible that the code has a problem, so I’ll post it here. This code should play a little melody using a state machine. In the below, the serial monitor will only print a single millis row after the button is pushed and then freeze while the buzzer continuously sounds. In this instance, the piezo is playing the desired note. However, using different pins, I sometimes got the situation that the piezo would play a super high pitch and the xiao would lock up, only getting back to bootloader through a double reset click, some that’s why I suspect there’s something up with using the tone() function with certain pins:

#include <ezButton.h>

ezButton DrinkButton(D7);
const int Buzzer = A0;

enum Notes {GS4=415, FS4=369, E4=329, B3=246};
enum PlayState {Done, Playing, Pausing};

int playState = Done;
int playStep = 0;

unsigned long startTone = 0;
unsigned long startPause = 0;

int Tones[8]     
   = {E4, FS4, GS4, E4, GS4, E4, FS4, B3};
int Durations[8] 
   = {700, 700, 700, 700, 700, 700, 700, 2100};
int Pauses[8]
   = {100, 100, 100, 1400, 100, 100, 100, 10};
int lastStep = 7;

void setup() {
  pinMode( Buzzer, OUTPUT );
  Serial.begin(115200);
}

void loop() {

  DrinkButton.loop();
  if ( playState==Done and DrinkButton.isPressed() ) {
    playState = Playing;
    startTone = millis();
    playStep = 0;
    PlaySong();
  }

}

void PlaySong() {

  switch (playState) {
    case Done: break;
    case Playing:
      tone ( Buzzer, Tones[playStep] );
      Serial.print ("millis ");
      Serial.print (millis());
      Serial.print (" timer ");
      Serial.println (startTone + Durations[playStep]);
      if ( millis() > startTone + Durations[playStep] ){
        startPause = millis();
        playState = Pausing;
      }
    break;
    case Pausing:
      noTone(Buzzer);
      if( millis() > startPause + Pauses[playStep] ){
        playStep++;
        startTone = millis();
        playState = Playing;
      };
      if ( playStep > lastStep ) {
        playStep = 0;
        playState = Done;
      }
    break;
    default: break;
  }

}

Hmm, I think I have an active piezo, since it plays when directly connected to 3.7v

OK I’m talking to myself here but I hooked a tiny speaker instead, and get the same freeze behavior. So, while I do think maybe I do have an active piezo buzzer, it’s definitely not an active speaker… so I must either have a problem with my code or there’s still a conflict using the tone() library on certain pins on the Xiao…

Over on the Arduino forum, a kind person mentioned that my … if ( playState==Done … snippet above was only going to run once and not play. I fixed that by putting the button checking in the state machine. However, it still does not work. I can see that it is stepping through the notes in the sequence, but it will stop after 2 or 3 and freeze, requiring a reset/change port/try again. So, there is definitely some incompatibility between tone() and either the NRF chip or this Xiao board, or at least I know the code works.

I found a few others mentioning this and pointing to an NRF PWM library, which I tried and it actually plays something, but it won’t play the notes, it just beeps the same tone for each step. Not finished troubleshooting that I guess, but, seems like the tone() library has probably been fixed somewhere …

Here’s the code that correctly goes through the state machine and iterates over the note sequence. However, it will not sound the notes, and it will freeze after 3 iterations. If someone could try this out and see if they have the same problem, I would be appreciative. It’s a super simple setup. Just one button and one speaker/piezo.

#include <ezButton.h>
//#include "nRF52_MBED_PWM.h"
// https://forum.arduino.cc/t/nano-33-ble-freezes-when-using-tone/1095569/13
//#include "nRF52_MBED_Slow_PWM.h"

ezButton DrinkButton(D4);
const int Buzzer = A0;

//enum Notes {B3=246, E4=329, FS4=369, GS4=415};
// float B3 = 246.0;
// float E4 = 329.0;
// float FS4 = 369.0;
// float GS4 = 415.0;
int B3 = 246;
int E4 = 329;
int FS4 = 369;
int GS4 = 415;

enum PlayState {Done, Playing, Pausing};

//// Stuff for nrf
//float dutyCycle = 80.0f;
//mbed::PwmOut* pwm   = NULL;

int playState = Done;
int playStep = 0;

unsigned long startTone = 0;
unsigned long startPause = 0;

 int Tones[8] 
 //= {GS4, E4, FS4, B3, B3, FS4, GS4, E4};
     = {415, 329, 369, 246, 246, 369, 415, 329};
// float Tones[8]     
//     = {415.0, 329.0, 369.0, 246.0, 246.0, 369.0, 415.0, 329.0};
int Durations[8] 
   = {700, 700, 700, 700, 700, 700, 700, 2100};
int Pauses[8]
   = {100, 100, 100, 1400, 100, 100, 100, 10};
int lastStep = 7;

void setup() {
  pinMode( Buzzer, OUTPUT );
  Serial.begin(115200);
}

void loop() {
  DrinkButton.loop();
  PlaySong();
}

void PlaySong() {

  switch (playState) {
    case Done: 
      if ( DrinkButton.isPressed() ){
        playState = Playing;
        startTone = millis();
        playStep = 0;
      }
    break;
    case Playing:
      tone ( Buzzer, Tones[playStep] );
      //setPWM(pwm, Buzzer, Tones[playStep], dutyCycle); //starts tone
      if ( millis() > startTone + Durations[playStep] ){
        startPause = millis();
        playState = Pausing;
        Serial.print ("Step ");
        Serial.println (playStep);
        Serial.println("Pausing");
      }
    break;
    case Pausing:
      noTone(Buzzer);
      //stopPWM(pwm, Buzzer);
      if( millis() > startPause + Pauses[playStep] ){
        playStep++;
        startTone = millis();
        playState = Playing;
        Serial.println("Playing");
      };
      if ( playStep > lastStep ) {
        playStep = 0;
        playState = Done;
        Serial.println("Done");
      }
    break;
    default: break;
  }

}

This unit has a buzzer on pin 3 i believe

Yes, believe it or not, I looked at that exact thing - you can see A3 in the images for the expansion board. I tried that pin, all pins I’ve tried have the same issue.

Hi there,
I use the Startup sound and RGB led test in these sketches to test the stuff.
The Zip has the code in it. this one

HTH
GL :slight_smile: PJ

Disregard the 2.9.2 stuff, it was fixed and figured out by Msfujino in another thread using the actual GPIO was the trick to getting the interrupt to work.

Hey @PJ_Glasso I’m not sure if your reply was meant for this thread? I looked at your linked one, (I was and am still very interested in batter voltage monitoring on EspC3 but this thread isn’t on that topic or using that board) but maybe there’s a define from MS Fujino I should use somewhere that would make the PWM work better?

maybe read the thread again,
It was directed at this statement. and the topic says NOTHING about an EXP32 XIAO.
Take it FWIW, the sound makes the buzzer buzz with different tones,(uses Analog A3) my third graders luv it.
HTH
GL :slight_smile: PJ

?
Neither is the link? :face_with_open_eyes_and_hand_over_mouth: what part of BSP 2.9.2 say’s ESP32??? :face_with_peeking_eye:
LOL
GL :slight_smile: PJ

Sorry, I was interested in battery charge while working with the esp. While I can see that thread refers to an NRF, it still seems to be about battery charge? I don’t know what BSP is. I didn’t look at your zip file. but I will.

1 Like

OK, so in your sketch, you use tone() with some delay()s and it works. That works for me also. I’ve made some simple tests with tone() and it can work. But once I combine with the state machine it no longer will sound the buzzer/speaker.

Hi there,
Yes , I see The state machine seems to block, you should init the milli’s I don’t see that ref?
and timers are better if they are the rtc based ones (more accurate)it’s a CASE switch state machine?
you try it with LED instead of tone ?
your close
GL :slight_smile: PJ :v:

yes, I tried with leds, and it’s the same. No lightee upee and the sketch freezes after three iterations. The millies for each of the three states Done, Playing and Pausing, are inited in the immediatly prior state. So in the Done state, when the button is pushed, it inits startTone=millis() similarly Playing inits startPause = millis() and Pausing inits startTone = millis() again. If you have a spare buzzer and button and an NRF xiao around, you could try it out :slight_smile:

Hi there,
Yea, The milli’s is what’s Nurffed up.
it’s probably overrunning or out of bounds, you use the sound to set it, no that can’t be correct, when you switch cases it’s over written or something.
You’ll get it
GL :slight_smile: PJ
always three times? Hmm.& LOL , got to love this math? :hole: :man_walking:

Get one, it is very useful for experimenting with the XIAO products

Get one what? An expansion board? I have been interested in the Arduino debugger, which I think that can help with?

Even the simple delay approach seems to have issues as well. I finally figured out, while writing the following sketch, that using delay() after a tone() call isn’t straightforward. The tone() isn’t blocking, so the tone will start and continue while the next step, a delay() begins. If your delays() are shorter than your tone() durations, the delay()s will be “eaten”. So you have to delay for the sum of the tone and pause duration if you want gaps between the notes. However, even the below still has some problems. The melody will play all the way through, but the first few notes are distorted, actually all sound distorted, but it lessens after the first few tones. It seems like the tone() timer is interfering with other timers somehow and instances of tone() on each timer cycle are overlapping each other and phasing, rather than playing in an orderly fashion. If you have a Xiao BLE and a buzzer, let me know what this sounds like to you (other than a corny melody):

const int Buzzer = D6;

unsigned int Tones[16]
  = {415, 329, 369, 246, 246, 369, 415, 329, 415, 369, 329, 246, 246, 369, 415, 311};
unsigned int Durations[16]
  = {700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, 800, 900, 1000, 2000};
unsigned int Pauses[16]
  = {20, 20, 20, 1400, 20, 20, 20, 1400, 20, 20, 20, 1500, 40, 50, 60, 2100};
int lastStep = 16;

void setup() {
  pinMode( Buzzer, OUTPUT );
  Serial.begin(115200);
}

void loop() { PlaySong(); }

void PlaySong() {
  for ( int i ; i < lastStep; i++ ){
    tone(Buzzer,Tones[i],Durations[i]);
    delay(Durations[i]+Pauses[i]);
    noTone(Buzzer);
    }
  delay(2100);
}

Actually, the overlapping/phasing is not unexpected given that tone() isn’t blocking - that’s partly why I wanted to do this with the state machine. I might try the classic prev/current millis() approach, but man it’s taken a long time to play a simple melody!