WorldFamousElectronics / PulseSensorPlayground

A PulseSensor library (for Arduino) that collects our most popular projects in one place.
https://PulseSensor.com
MIT License
201 stars 97 forks source link

When using the duration parameter of tone, the sound will be distorted #193

Open qwqoffice opened 6 months ago

qwqoffice commented 6 months ago

I added the duration parameter to the tone function: tone(Buzzer, 932, 150); The sound sounds a bit strange. But when I don't use pulseSensor.begin(), the sound is normal What is the problem?

biomurph commented 6 months ago

@qwqoffice Please tell us what hardware you are using to read PulseSensor? Also, what computer OS you are using and what version of Arduino IDE are you using? Please share your code, so We can try to replicate your bug. If you can make a quick video, which doesn't have to look good, just show the PulseSensor and record sound with the PulseSensor running and not running?

qwqoffice commented 6 months ago

@qwqoffice Please tell us what hardware you are using to read PulseSensor? Also, what computer OS you are using and what version of Arduino IDE are you using? Please share your code, so We can try to replicate your bug. If you can make a quick video, which doesn't have to look good, just show the PulseSensor and record sound with the PulseSensor running and not running?

I am using Arduino UNO R3 board, with ATMEGA328P-PU, and a passive buzzer for tone. Windows11, Arduino IDE Version: 2.3.2 here is my code:

#include <PulseSensorPlayground.h>
#define THRESHOLD 700

PulseSensorPlayground pulseSensor;
const int PULSE_INPUT = A3;
const int Buzzer = 9;

void setup() {
  pulseSensor.analogInput(PULSE_INPUT);
  pulseSensor.setThreshold(THRESHOLD);
  pulseSensor.begin();
}

void loop() {
  // Tone
  if (pulseSensor.sawStartOfBeat()) {
    tone(Buzzer, 932, 150);
  }
}

It sounds like there's some noise.

https://github.com/WorldFamousElectronics/PulseSensorPlayground/assets/18651085/284a0283-3bff-4392-9fee-40879b20f2c9

And then, I deleted the initialization code for pulsesensor, and add tone code in loop, the sound is right, code here:

#include <PulseSensorPlayground.h>
#define THRESHOLD 700

PulseSensorPlayground pulseSensor;
const int PULSE_INPUT = A3;
const int Buzzer = 9;

void setup() {
}

void loop() {
  // Tone
  if (pulseSensor.sawStartOfBeat()) {
    tone(Buzzer, 932, 150);
  }

  tone(Buzzer, 932, 150);
  delay(500);
}

https://github.com/WorldFamousElectronics/PulseSensorPlayground/assets/18651085/c6ded39b-b169-47e6-8e5e-946a111f18d5

I noticed a note in the PulseSensor_Speaker example:

NOTE: Do not set the optional duration of tone! That is blocking!

Is it related to this? I want the sound to play for a longer time and sound like a monitor in a hospital, just like the second video

biomurph commented 6 months ago

@qwqoffice Thank you for the short videos and the code you are using. First, I would say that you should try the code that is in our example Speaker sketch. It turns on the tone when pulseSensor.sawStartOfBeat() returns true, and uses the tone command without the duration parameter. Then, it turns the tone off when pulseSensor.isInsideBeat() returns false using the noTone(pin) command.

Please try following the example that we have and see if that still gives you the noisy beep?

Second, it is clear that you are using a 'knock-off' PulseSensor. Our PulseSensor comes with a 24" cable with Red, Black, Purple wire colors. Also, our PulseSensor kit comes with a clear vinyl sticker to place on the front face to keep skin oils and sweat from interfering with the electronics. Please purchase your hardware from the original makers of PulseSensor. We provide code support for free, and free hardware support only to legit customers of PulseSensor.

qwqoffice commented 6 months ago

@biomurph I tried the PulseSensor_Speaker example, due to the sound stopping too quickly, I moved my fingers away and closer to the sensor to extend the sound, result:

https://github.com/WorldFamousElectronics/PulseSensorPlayground/assets/18651085/56197616-7403-4140-9271-6604c57686bc

As you said, my sensor is indeed a a 'knock-off' PulseSensor, but when I completely disconnected the sensor, the problem still exists, proving that the problem is not related to whether a original makers of PulseSensor is used or not. My following code calls a 10 second tone after the sensor initialization:

#include <PulseSensorPlayground.h>
#define THRESHOLD 700

PulseSensorPlayground pulseSensor;
const int PULSE_INPUT = A3;
const int Buzzer = 9;

void setup() {
  pulseSensor.analogInput(PULSE_INPUT);
  pulseSensor.setThreshold(THRESHOLD);
  pulseSensor.begin();
  tone(Buzzer, 932, 10000);
}

void loop() {
}

And It does not require the connection of sensor, the noise still exists:

https://github.com/WorldFamousElectronics/PulseSensorPlayground/assets/18651085/65c789f5-2695-4ae6-897c-85459e163936

We provide code support for free, and free hardware support only to legit customers of PulseSensor.

My English is weak and I don't understand the meaning. Does it mean that code support also requires the use of original makers of PulseSensor? If you can provide code support, then please continue reading.

I also tried other frequencies for the tone, such as 2000, 3000, 4000. There is a significant difference in sound when calling and not calling the sensor initialization code. I suspect that calling analogRead within a timer interrupt may be causing this issue. Because I also tried not using the PulseSensorPlayground library, directly creating a timer and using analogRead and tone in the callback, the sound becomes distorted. However, when analogRead and tone are called in the loop without using a timer, the sound does not distort.

using timer code:

#include <TimerOne.h>

void setup() {
  Timer1.initialize(20000);
  Timer1.attachInterrupt(timerTone);
  tone(9, 932, 10000);
}

void timerTone(void) {
  int Signal = analogRead(A3);
}

void loop() {
}

https://github.com/WorldFamousElectronics/PulseSensorPlayground/assets/18651085/f6def444-f6b8-4b60-a86c-fc37362e2fa4

not using timer, loop instead:

void setup() {
  tone(9, 932, 10000);
}

void loop() {
  int Signal = analogRead(A3);
}

https://github.com/WorldFamousElectronics/PulseSensorPlayground/assets/18651085/9c8b3301-3f0c-428a-be66-c0d8dab58ea5

qwqoffice commented 6 months ago

@biomurph Through the oscilloscope, I can see that the distorted sound waveform frequency is fluctuating,

https://github.com/WorldFamousElectronics/PulseSensorPlayground/assets/18651085/e112d9b5-097a-4b85-9543-c4135060e2d3

while the normal sound waveform frequency remains stable.

https://github.com/WorldFamousElectronics/PulseSensorPlayground/assets/18651085/d934a3bd-9835-4ce5-8d39-47d2a8224e93

biomurph commented 6 months ago

@qwqoffice We will help you with your software issues, but if a problem arises from the 'clone' hardware we can't support because we don't know what that hardware is made of.

The tone() command in Arduino makes extensive use of hardware timer interrupts in order to generate the tone, as well as turning off the tone when the duration parameter us used. Producing the tone uses one timer, and setting the duration uses another. PulseSensor Playground library also uses interrupts to time the accurate sampling of the PulseSensor. The duration parameter timer use crashes with the PulseSensor timer use, so that is why there is distortion in the audible tone.

The easiest way to get around this issue is to turn on the tone when a heartbeat happens, and then turn off the tone some time later. In our example, we are using the sawStartOfBeat function to turn on the tone and the isInsideBeat function to turn off the tone. The isInsideBeat function will return true as long as the pulse signal wave is above the THRESHOLD value.

As you have noticed, the length of the tone is limited. Another way to set the time length of the tone without using the duration parameter would be to use a software timer. Here is an example of a software timer that you can use in your code.

Above the setup() declare some global variables:

unsigned long toneStartTime;
unsigned long toneDuration = 150;  // time in milliseconds taken from original post

In the loop:

if(pulseSensor.sawStartOfBeat()){
  tone(Buzzer, 932);  // start playing the tone
  toneStartTime = millis();  // set the time in milliseconds that the tone started
  // other stuff if you want
}

if(millis() - toneStartTime >= toneDuration){  // when the time is up,
  noTone(Buzzer);  // stop the tone
}

Please let us know if this software timer works to solve the problem?

qwqoffice commented 6 months ago

@biomurph Thank you for your software timer code, but I just tried it and the problem still exists. I posted a video above, which uses your PulseSensor_Speaker example without the duration parameter but I manually extended the tone to hear the noise clearly. I'm reposting the video using the PulseSensor_Speaker example here.

https://github.com/WorldFamousElectronics/PulseSensorPlayground/assets/18651085/56197616-7403-4140-9271-6604c57686bc

biomurph commented 6 months ago

@qwqoffice I am out of my lab until Thursday this week. I will try to replicate your bug then.

Please provide a link to the components you are using? The clone PulseSensor, the speaker, the arduino board?

qwqoffice commented 6 months ago

@biomurph Thank you. Pulse Sensor: https://item.taobao.com/item.htm?id=532146320168 Speaker: https://item.taobao.com/item.htm?id=522573889254 And the Arduino board has been taken down.

biomurph commented 6 months ago

@qwqoffice Thanks for those links.

I have setup an Arduino UNO in my lab, with a speaker just like the tutrorial found here https://pulsesensor.com/pages/pulse-sensor-speaker-tutorial

I can confirm that the beep sound does have a small amount of noise in it. I have not found the source of the noise, but I am going to do more tests to see where the bug is coming from.

qwqoffice commented 6 months ago

@biomurph My guess is that the noise is due to performance limitations of the ATmega328P on the Arduino UNO. If you have another MCU with higher performance, please test with it. Thanks for your work. And I will use the STM32 series MCU for testing later and report the results to you.

biomurph commented 3 months ago

@qwqoffice I have been able to dig into the noise problem in our speaker example. After going down a deep deep hole, I discovered that the the Arduino tone() function starts up a hardware timer at a specific freqeuency, and initializes an interrupt. The problem is that the interrupt needs to be called in order to toggle the pin at frequency! So that interrupt is interfered with by the PulseSensor Playground interrupt! I imagine that our interrupts are messed with too. I have created a better performing 'beep' by using analogWrite instead to create a square wave on the speaker pin. It seems like on AVR, the frequency of analogWrite is 500Hz (about Bb). It's not a bad note, and it sounds clean. Also, our beat finder is not messed with! The PulseSensor Speaker.ino example is updated in version 2.3.0

In order to fix this problem and use tone() the Arduino core needs to be updated so that the tone library operates 'hands free' like the PWM library. Or, the PWM library needs to be updated to accept a frequency parameter to ensure a clean(er) note. There are some architectures upon which the Tone library might work: nRF52? ESP32?

Please try the new example, and let us know if it works for you?