I wish to control the pitch of a buzzer with a potentiometer.
In order to accomplish that, is it sufficient to simply map the digital potentiometer values (0-4095) to the desired frequency range (for instance 4 -16 kHz). Using a formula such as:
return int(MIN_FREQUENCY + (pot_value / 4095) * (MAX_FREQUENCY - MIN_FREQUENCY)). I would then set the frequency of my buzzer: buzzer.setFrequency()
Apart from setting the calculated frequency dynamically, would I also have to change the duty cycle dynamically in some way? Is the duty cycle connected to the human hearing curve (frequencies) in some way?
The tone() function is a square wav (50% duty cycle). To change that, you'd have to use a variation of the blink example or the blink without delay example. And whatever processor cycles used to read the pot, or do other things, will interfere with the timing to some extent.
50% duty cycle gives the highest volume and the least harmonics (for a square wave or pulse). As the pulse gets wider or narrower (at the same frequency) it gets quieter and the harmonics become relatively stronger so the character of the tone changes.
Ensure the "buzzer" is passive. If 5vdc and GND are applied, and only a "click" is made, your buzzer is passive. If you have a tone, your buzzer is active, and will not work for this project.
The passive buzzer only needs a value, ranging from 31 to 1024. You could do "math" to give the buzzer a value, or read and "map()" a potentiometer value... or create a moving value... this simulation oscillates between 700 and 800 Hz.
sketch.ino for wokwi.com
unsigned long ledTimer, ledInterval = 250; // led interval - 250 = fast wigwag
unsigned long sirenTimer, sirenInterval = 50; // siren interval, 50 = fast warble
byte Led1Pin = 2, Led2Pin = 3, BuzzerPin = 4; // pins for LEDs and buzzer
int count = 1; // siren cycle counter
// int sirenCycles = 10; // larger numbers for testing siren
int sirenCycles = 20; // one full cycle (2 directions)
int sirenTone = 700, sirenDirection = 10; //
bool state = 0, led = 1, siren = 1; // enter sketch led/siren ON
void setup() {
Serial.begin(115200);
pinMode(Led1Pin, OUTPUT);
pinMode(Led2Pin, OUTPUT);
pinMode(BuzzerPin, OUTPUT);
}
void loop() {
if (led) // if the "led" flag is set
ledOn(); // call the LED function
if (siren) // if the "led" flag is set
sirenOn(); // call the siren function
}
void ledOn() {
if (millis() - ledTimer > ledInterval) { // time difference = ledInterval?
ledTimer = millis(); // reset ledTimer
state = !state; // on/off/on/off...
if (state) { // when state is HIGH
Serial.print("Siren cycle ");
Serial.print(count);
Serial.print(" RED ");
}
if (!state) { // when state is LOW
Serial.println("BLU ");
}
// alternate on/off state of LEDs
digitalWrite(Led1Pin, !digitalRead(Led1Pin)); // state
digitalWrite(Led2Pin, !digitalRead(Led1Pin)); // opposite state
}
}
void sirenOn() {
if (millis() - sirenTimer > sirenInterval) { // time difference > interval?
sirenTimer = millis(); // reset timer
sirenTone += sirenDirection; // change frequency up/down
tone(BuzzerPin, sirenTone); // make the tone
if (sirenTone < 700 || sirenTone > 800) { // frequency range
sirenDirection = -sirenDirection; // change frequency direction
if (count == sirenCycles) { // make two cycles (1) up (2) down
sirenOff(); // turn off siren
}
count++;
}
}
}
void sirenOff() {
Serial.println("\nSiren OFF");
digitalWrite(Led1Pin, LOW); // turn off red LED
digitalWrite(Led2Pin, LOW); // turn off blu LED
noTone(BuzzerPin); // turn off siren
siren = 0; // flag to stop going to siren function
led = 0; // flag to stop going to led function
}
I assume the buzzer I have is active (When connecting it to voltage and ground, it produces a sound).
Are active buzzers even capable of specific frequencies? When using freq() and duty() function of PWM() object (esp32), even frequencies of 20000 hz will produce an audible sound (which doesn't align with how human hearing should work).
So is there any workaround I can output specific frequencies by using an active buzzer?