passive buzzer/Using it/09. What tone() borrows
Using it · 09 of 10

What tone() borrows

tone() is cheap on your loop and not free on your board. On an Uno it takes Timer2, so PWM on D3 and D11 stops while a note plays; on an ESP32-S3 it takes one of eight LEDC channels. Both play one tone at a time, and a second call replaces the note on an Uno but queues behind it on an ESP32.

Something has to make the wave

A square wave at 2700 Hz is 5400 edges a second. A loop could make them, and it would do nothing else, so tone() hands the job to a piece of the chip that counts time on its own. Which piece depends on the board.

What tone() borrows
Board
Takes
Timer2
PWM lost
D3, D11
Tones at once
one
A second call
replaces
On an Uno, tone() runs on Timer2, so while a note plays, analogWrite on D3 and D11 stops working, the same clash an IR library causes. One note at a time, and a new call replaces the one playing at once: to play notes in order, delay() for each note's length after calling tone().

On an Uno: Timer2

The Uno's chip has three timers. tone() uses Timer2 and flips the pin on its tick, and Timer2 is also what makes analogWrite work on D3 and D11. So while a note plays, those two pins lose their PWM. It is the same clash the common IR libraries cause on an Uno, and for the same reason: they want Timer2 too.

A second tone() call on an Uno replaces the note that is playing, at once.

On an ESP32-S3: an LEDC channel

The ESP32-S3 makes PWM with its LEDC block, which has eight channels. tone() attaches the pin to one of them, and the other seven stay free for analogWrite and ledcWrite. Nothing else stops working.

It also works differently inside. Each call is posted to a small task, and a call with a length plays to its end before the next one starts. So a second tone() queues behind the first instead of cutting it off, and noTone() clears whatever is waiting.

One at a time, on both

Neither board plays two tones at once with tone(). On the ESP32, a call for a second pin while the first is still attached is refused, with an error in the log. For a chord, or two buzzers, drive each pin with ledcAttach and ledcWriteTone directly, one channel each.

When it does not work

An LED on D3 stopped dimming when the buzzer played.

On an Uno, tone() runs on Timer2, and Timer2 is also what makes the PWM on D3 and D11. While a note plays, analogWrite on those two pins does not work. Move the LED to D5, D6 or D10; D9 is the buzzer in this book.

My IR remote and the buzzer do not work together on an Uno.

The common IR libraries also use Timer2 on an Uno by default, so the two fight over it. Some IR libraries can be moved to another timer; otherwise use a board with more timers, such as an ESP32.

A second buzzer on another pin stays silent on the ESP32.

tone() drives one pin at a time. Calling it for a second pin while the first still owns it logs an error and does nothing. Call noTone() on the first pin before starting the second, or drive the second with ledcAttach and ledcWriteTone.

On the ESP32 my beeps keep coming after the loop has stopped calling them.

Calls with a length are queued and played in order, so a loop that calls tone() faster than its notes last builds up a backlog that plays on. Wait out each note, as the melody sketch does, or call noTone(), which clears the queue.

Where this goes next

Symptom first: what the LED and the sound say about where it went wrong.

When it is silent

Edit this page — content/books/passive-buzzer/what-tone-borrows.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

Passive Buzzer

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum