From 97f850365e6d66bfb04b672c42fdd24d2f5583cd Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Thu, 28 May 2026 08:13:57 -0400 Subject: [PATCH] Add NPN buzzer projects --- .../activebuzzer_npn_lowside_switching.ino | 31 +++++++++++++++++++ .../buzzer-npn-lowside-switching.ino | 15 +++++++++ flowing_light_with_button/README.md | 23 +++++++++----- 3 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 activebuzzer_npn_lowside_switching/activebuzzer_npn_lowside_switching.ino create mode 100644 buzzer-npn-lowside-switching/buzzer-npn-lowside-switching/buzzer-npn-lowside-switching.ino diff --git a/activebuzzer_npn_lowside_switching/activebuzzer_npn_lowside_switching.ino b/activebuzzer_npn_lowside_switching/activebuzzer_npn_lowside_switching.ino new file mode 100644 index 0000000..bdb1e78 --- /dev/null +++ b/activebuzzer_npn_lowside_switching/activebuzzer_npn_lowside_switching.ino @@ -0,0 +1,31 @@ +#define PIN_BUZZER_SWITCH 14 +#define PIN_BUTTON 21 +#define PWM_CHANNEL 0 + +void setup() { + pinMode(PIN_BUZZER_SWITCH, OUTPUT); + pinMode(PIN_BUTTON, INPUT); + // Tutorial had a bug here. Needs to be freq=1000, not freq=1. + ledcAttachChannel(PIN_BUZZER_SWITCH, 1000, 10, PWM_CHANNEL); + ledcWriteTone(PIN_BUZZER_SWITCH, 2000); + delay(300); +} + +void alarm() { + float sine; + int tone; + for ( int i = 0; i < 360 ; i += 10 ) { + sine = sin(i * (PI / 180)); + tone = 2000 + sine * 500; + ledcWriteTone(PIN_BUZZER_SWITCH, tone); + delay(50); + } +} + +void loop() { + if ( digitalRead(PIN_BUTTON) == LOW ) { + alarm(); + } else { + ledcWriteTone(PIN_BUZZER_SWITCH, 0); + } +} diff --git a/buzzer-npn-lowside-switching/buzzer-npn-lowside-switching/buzzer-npn-lowside-switching.ino b/buzzer-npn-lowside-switching/buzzer-npn-lowside-switching/buzzer-npn-lowside-switching.ino new file mode 100644 index 0000000..3381e75 --- /dev/null +++ b/buzzer-npn-lowside-switching/buzzer-npn-lowside-switching/buzzer-npn-lowside-switching.ino @@ -0,0 +1,15 @@ +#define PIN_BUZZER_SWITCH 14 +#define PIN_BUTTON 21 + +void setup() { + pinMode(PIN_BUZZER_SWITCH, OUTPUT); + pinMode(PIN_BUTTON, INPUT); +} + +void loop() { + if ( digitalRead(PIN_BUTTON) == LOW ) { + digitalWrite(PIN_BUZZER_SWITCH, HIGH); + } else { + digitalWrite(PIN_BUZZER_SWITCH, LOW); + } +} diff --git a/flowing_light_with_button/README.md b/flowing_light_with_button/README.md index 96a15a2..af7dab1 100644 --- a/flowing_light_with_button/README.md +++ b/flowing_light_with_button/README.md @@ -20,6 +20,7 @@ I learned several things from this project: ## Debouncing buttons ![Illustration of bounce](bounce.png) + *image from Freenove tutorials* @@ -31,7 +32,7 @@ if (digitalRead(PIN_BUTTON) == LOW) { // do something with the pin ``` -... This is really wasteful. I paid good money for those CPU cycles, and on embedded hardware we don't have a lot of them to go around. (The ESP32-S3 has an Xtensa dual-core 32-bit LX7 running up to 240Mhz - stunning when you think about the price and size of the package, but still, not enough to be wasteful.) I improved that by using a timer in a function that yields time back to the CPU while it's waiting to verify that the button is debounced. +... This is not great. On simpler microcontrollers, `delay()` burns CPU cycles (The ESP32-S3 has an Xtensa dual-core 32-bit LX7 running up to 240Mhz - stunning when you think about the price and size of the package, but still, not enough to be wasteful). But on the ESP32-S3 in particular, the solution package as a whole isn't losing those cycles - it runs Arduino on top of an RTOS, so `delay()` is actually yielding back to the RTOS scheduler so that things like bluetooth and wifi stacks can continue. But *our* program is fully blocked here. If I want to scan additional buttons, or perform some other task, we're locked in here. So I improved that by using a timer in a function that yields time back to the CPU while it's waiting to verify that the button is debounced. ```arduino void checkButtonPressed(void) { @@ -54,23 +55,29 @@ void checkButtonPressed(void) { ## Pulse Width Modulation -Pulse Width Modulation (PWM) is essentially a way for digital circuits to pretend to be analog. Essentially, analog circuits can produce signals that have gentle sweeping curves, elegant waves, like God creating the feminine. Digital circuits can only produce on or off values - hard square shapes with sharp edges, like man chiseling facts into stone. +Pulse Width Modulation (PWM) is essentially a way for digital circuits to pretend to be analog. Essentially, analog circuits can produce signals that have gentle sweeping curves, elegant waves, like the waves on the sea. Digital circuits can only produce on or off values - hard square shapes with sharp edges, like man chiseling facts into stone. ![analog vs digital signals](analog-digital.png) + *image source [Klipsch Blog](https://www.klipsch.com/blog/digital-vs-analog-audio)* -Basically pulse width modulation allows us to say that, over a given period of time T ("duty cycle"), we can use a digital value V with a precision of P bits, to express that for a given percentage of the duty cycle, the circuit should be ON (usually HIGH), while for the remainder of the duty cycle, it shouuld be OFF (normally LOW). For example: +Basically pulse width modulation allows us to say that, over a given period of time T, we can use a digital value V with a precision of P bits, to express that for a given percentage of the period (known as the "duty cycle"), the circuit should be ON (usually HIGH), while for the remainder of the duty cycle, it should be OFF (normally LOW). For example: * Circuit produces 5v when HIGH -* Over a duty cycle T of 1000ms -* Using a 10 bit precision +* Over a period T of 1000ms +* Using a 10 bit precision (maximum value `1023`) -... We can send a value of `(1 << 5)` to turn the signal HIGH for 50% of the duty cycle (approximately 500ms). Sending `(1 << 2)` would be approximately 25%, `(1 << 7)` approximately 75%, and `(1 << 9)` approximately 100%. This effectively varies the output voltage on the circuit according to a time series pattern that can make it seem like a wave, when in reality, we are just providing the same voltage for more or less of the time that the circuit is on. +... We can send a value of `512` to turn the signal HIGH for 50% duty cycle (50% of the period, approximately 500ms). Sending `256` would be approximately 25%, `768` approximately 75%, and `1023` 100%. While the output pin is only ever sending two values (ON at 5v or OFF at 0v), most circuits and devices actually average the voltage on the circuit. This makes our PWM output seem like a wave, when in reality, we are just providing the same voltage for more or less of the time that the circuit is on. + +Time period T is also related to frequency, F. The time period in milliseconds has an inverse relationship to the frequency in hertz. + +$$ f = \frac{1}{T} $$ + +As T increases positive from zero, F decreases. So longer time results in lower frequencies. This will become more important in future tutorials where we start using PWM for audio devices like buzzers. ![pulse width modulation](pwm.png) -*image source Freenove tutorials* -It's not *really* analog, because the voltage stays the same the entire time - an analog signal will oscilate from 0v to the height, then back down, and may even go negative to complete all 4 phases of the wave. But in many controls, it has a similar effect, and *feels* like analog. +*image source Freenove tutorials* In the case of an LED, by controlling the voltage with PWM, we can create a glowing or fading effect. As the duty cycle increases, the LED gets brighter; as it decreases, the LED gets dimmer.