# Buzzers with Transistors

![Breadboard diagram](breadboard.png)

These two programs allow you to control a buzzer with a button to make a noise on command. They both use the exact same breadboard, the only difference is that one uses a passive buzzer, while the other uses an active buzzer (pn TMB12A05). 

## Lessons Learned 

I learned several things from these projects:

- The differences in active vs passive buzzers
- How transistors work, and how you can use them to control the flow of power in a circuit
- Why you might place a transistor upstream or downstream of the component whose power you are controlling
- How to turn a transistor on or off using the GPIO pin on an ESP32
- How to use my oscilloscope to diagnose PWM output issues

## Buzzers

Not a lot to say here. Buzzers take some current, pass it through a moving diaphragm and thereby produce output. The primary difference is that passive buzzers emit only one frequency that depends on how they're constructed and driven.

- Passive buzzers cannot be driven without an external oscillator - they require rapid voltage change to work. With the external oscillator, a passive buzzer can produce different frequencies. However, passive buzzers do have a resonant frequency specific to the buzzer, and will be loudest when being driven near that frequency.
- Active buzzers include oscillators and other circuity on the package, and thereby can be driven without an external oscillator; all you need to do is provide DC voltage. However, they generally only produce sound at one single frequency, because their oscillators are on the package.

## Transistors

I really didn't understand transistors before this project. And frankly the way the the tutorial prsented them did not help. The tutorial presented these transistors as serving as an "amplifier". 

> Because the buzzer requires such large current that GPIO of ESP32-S3 output capability cannot meet the requirement, a transistor of NPN type is needed here to amplify the current.

That's not an accurate statement for what the transistor is doing in this circuit, and it sent me on a bit of a rabbit hole (which is good because I learned something). 

Transistors are semiconductors that are indeed used to amplify or (in this case) switch electrical signals. Transistors come in many shapes and sizes, but the two used in this project are NPN and PNP BJT transistors (specifically S8050 and S8550). These BJT transistors are used as electronically controlled switches to either hold current back, or allow it to flow. Specifically, we can use these to turn our buzzer on or off with a GPIO pin from our ESP32. They are not amplifying anything - they're controlling flow, like a Y valve on a garden hose. I think the reason they called it an amplifier is because it allows us to use our 3.3v GPIO control signal to deliver 5v, even though there's no actual amplification involved.

Specifically, Bipolar Junction Transistors are a collection of 2 diode-like PN junctions. PN junctions are arrangements of two blocks of P type and N type silicon. Naturally, silicon is an insulator, but in the manufacturing process, silicon is "doped" with impurities to make it conductive. N type silicon is doped with extra electrons, while P type silicon lacks electrons, creating a positive charge via "holes". (Yes they're actually called holes.) PN junctions only allow current to flow in one direction; their configuration determines their "bias". [When connecting P to - and N to +, it is reverse biased, and very little current flows; when connecting P to + and N to -, it is forward biased, and current flows readily](http://hyperphysics.phy-astr.gsu.edu/hbase/Solids/pnjun2.html).  Under normal conditions, the PN junction has a depletion zone that resists current. In other words, the connection from C to E is normally impassible.

If you're into electrical engineering, you can go really really deep down this rabbit hole about silicon, PN junctions, the depletion zone, and all kinds of stuff. Way deeper than you need to perform the kind of work you're doing. If you want to know all about PN junctions, biasing, and what's going on with the individual electrons, [this page from GSU](http://hyperphysics.phy-astr.gsu.edu/hbase/Solids/pnjun.html) is a great starting point with nice pictures and good english.

Anyway, these 2 PN junctions are connected to the 3 legs of the transistor: the **B**ase, **E**mmitter, and the **C**ollector. So you have two PN junctions at the junctions of the 3 connectors: **BE**, and **BC**, with the **B**ase (being the single point that connects to both junctions) being the control. A supply voltage is attached to the collector. A load (or ground) is attached to the emitter. A third voltage (such as 3.3v from a GPIO pin) is attached to the base. The potential difference between the base and the emitter act as a gate for the collector, turning it on or off.

### NPN

```
N | P | N
E   B   C
```

* The base emitter connection is forward biased (the emitter side is N type silicon)
* The base collector connection is forward biased (the collector side is N type silicon)
* The base pin controls the voltage at the junction between BE and BC, supplying normally low voltage while closed, providing positive voltage to allow current to flow

By applying current to the base that is slightly higher than the emitter, the barrier in the depletion zone is lowered, charge carriers can cross the junction (they are actually pushed towards it), and current can flow.

A key thing to understand is that, in order for an NPN transistor to function, the base must be able to supply a voltage slightly higher than the emitter for as long as the transistor needs to stay open.

### PNP

PNP BJTs are, as you might imagine, the reverse of an NPN..

```
P | N | P
E   B   C
```

* The base emitter connection is reverse biased (the emitter side is P type silicon)
* The base collector connection is reverse biased (the collector side is P type silicon)
* The base pin controlls the voltage at the junction between BE and BC, supplying normally high voltage, providing low voltage to allow current to flow

Under normal circumstances, the two junctions (`PN -> NP`) will act as an efficient bridge and allow current to pass. When the base voltage rises above the emitter, charge carriers are pulled away from the junction, effectively closing off the circuit. 

## Transistor Placement (High vs Low)

![driving the buzzer in both high side and low side configurations](highsidelowside.png)

One thing I was curious about was why the tutorial placed the NPN transistor between the buzzer and ground in the circuit. I hadn't yet really figured out transistors - and I'll be honest I still struggle a *little bit* with how all the depletion zone logic works with the PNP - so it didn't make sense to me. If the goal is to only activate the buzzer when we press the button, why not put the transistor between the buzzer and the power supply?

Hopefully, after reading the above description of transistors, it makes sense. If not, let me break it down for you:

* With the NPN transistor placed between the buzzer and the ground, when base gets +3.3v, the circuit opens, the buzzer dumps the remainder of its +5v through the collector, out to the emitter, and out to ground. The ground soaks the +5v, which means that the transistor can stay open even though the base is only giving out +3.3v to control that +5v circuit. This is called `low-side switching`.
* With the NPN transistor placed between the buzzer and the power supply, when the base gets +3.3v, the collector +5v would flow to the emitter but as soon as it rose above +3.3v it would close again, because the potential difference between the base and emitter is the only thing the transistor cares about. In this configuration, the buzzer might work, but if it did it would work very poorly, and probably not at all. This is called `high-side switching`, and there are use cases for it, but this isn't one of them.

For this reason you'll commonly find NPN transistors between a component and ground, especially when driven by low voltage base currents like a 3.3v IO. High side switching is more complicated and often requires level shifting, charge pumps, or dedicated driver ICs, so low side switching is preferable in these kinds of circuits. There is a tradeoff though - with low side switching, the load is always connected to +V and the load just floats when it's turned off. This can be a pretty big problem for sensitive circuits like audio, precision analog measurement, multiplexed displays, and safety critical components like aircraft, medical or automotive equipment.

## Using the oscilloscope to diagnose errors

The active buzzer example worked fine. But when I went to drive the passive buzzer, everything fell off the rails. There was only one physical component different between the two, and that was the active buzzer. The code was significantly more different. So I figured the problem was in the code. But one of the great things about working with microcontroller hardware is that, in addition to stepping through the code in the debugger, I can get measurements on all the hardware itself and figure out WTF that software is really doing. So:

* I verified all the connections and voltages with my voltmeter. Maybe i moved a wire and didn't realize it. 
  * 3.3v to the button and GPIO 21. Dropped to 0 when the button was pressed, matching the expectation in the code (`LOW`).
  * 5v to the + on the buzzer
  * 5v out the buzzer to the NPN collector
  * 0v from GPIO 14, even when the button is pressed. This was as expected - Pin 14 is producing PWM, and when testing PWM with a voltmeter, you will usually see only one voltage (being the average), often zero.
  * 0v but good continuity from the NPN emitter to ground
  
Still wasn't working. Unlike the previous tutorial with the flowing LED, I didn't really change the code much, but I thought maybe I jacked it up, so I copypasted their code directly. No dice - still didn't work.

WTF. Okay. So at this point:

* I'm using their code directly. No change.
* Everything on the breadboard is testing good. The button and both GPIO pins are doing what they should.
* No audio out of the buzzer

Okay. So if I'm not getting audio out of the buzzer, then I need to inspect the signal between the GPIO and the NPN transistor. The voltmeter can't tell me if it's working or not, for the previously mentioned reason. I needed something that could show me the waveform coming out of that GPIO pin when I pushed the button. Enter ....

![oscilloscope](testbench.png)

The oscilloscope. If you don't know, an oscilloscope allows you to do lots of stuff, including visualizing waveforms going through a circuit. There are lots and lots of different types, sizes, varieties and qualities of oscilloscopes on the market. My particular oscilloscope used here is a [Hanmatek DOS1102](https://hanmatek.com/collections/oscilloscope/products/hanmatek-dos1102-110mhz-2ch-oscilloscope), a budget friendly digital oscilloscope with lots of features.

Oscilloscopes are measurement instruments and need to be calibrated. Basically any time they get moved or the climate around them changes, they should be calibrated. The digital oscilloscope makes it pretty easy - disconnect the probes, run through a few menu selections, and wait a while. Boom, done.

Once that was done, I put a tap between GPIO pin 14 and the NPN, hooked my probe, and pressed the button. The oscilloscope didn't really show anything useful. Okay .... so either the circuit was busted, the code wasn't driving the GPIO, or I was using the scope wrong. This particular oscilloscope has a couple of lugs on the front of the scope that can be used for testing and calibration, and one of them produces a 5v square wave. Handy! I hooked to that lug, told it to auto-detect, and voila - now i saw a square wave. I locked the scope settings in, moved the probe back to the tap, and hit the button again. Nada.

The oscilloscope was proving, beyond the shadow of a doubt, that there was NO PWM SIGNAL coming out of GPIO 14 when I pushed that button. So .... the PWM code must be jacked up in the source code.

So I compared the PWM code for this project with the last flowing LED project I did - I knew that one worked, while this one did not. Finally the difference stood out:

```arduino
// Previous project 

ledcAttachChannel(ledPins[i], 1000, PWM_PRECISION, chns[i]);

--------------------

// Current project

ledcAttachChannel(PIN_BUZZER, 0, 10, CHN);
```

Wait... what? Why are they passing zero for the frequency? The documentation for [`ledcAttachChannel`](https://docs.espressif.com/projects/arduino-esp32/en/latest/api/ledc.html#ledcattachchannel) shows the second argument is the frequency. What do you think happens if you set a frequency of zero on a PWM function?

Set that to 1000, or 500, or ... literally any value other than zero, and ..

[It works!](https://x.com/AKLabsDotNet/status/2059735218622538228)
