Touch piano
Turn four bare wires into touch keys. Each touch prints a note, and an optional buzzer plays it.
Board
ESP32
3V3
GND
EN
23
VP
22
VN
TX
34
RX
35
21
32
GND
33
19
25
18
26
5
27
17
14
16
12
4
GND
0
13
2
D2
15
D3
D1
CMD
D0
5V
CLK
Wiring
| From | To board | Note |
|---|---|---|
| Key 1 | GPIO 4 | Any conductive pad. Bare wire is enough |
| Key 2 | GPIO 13 | |
| Key 3 | GPIO 14 | |
| Key 4 | GPIO 27 | |
| Buzzer + | GPIO 25 | Optional. A passive buzzer, not an active one |
| Buzzer - | GND |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32 Dev Module
// Four touch keys. Touch pins sense a finger through the wire, no parts needed.
const int KEYS[] = {4, 13, 14, 27};
const char* NOTES[] = {"C4", "E4", "G4", "C5"};
const int FREQ[] = {262, 330, 392, 523};
const int BUZZER = 25; // optional passive buzzer
uint32_t baseline[4];
bool down[4];
int playing = -1;
bool touched(int i, uint32_t v) {
#if CONFIG_IDF_TARGET_ESP32
return v < baseline[i] * 7 / 10; // classic ESP32: the reading drops when touched
#else
return v > baseline[i] * 13 / 10; // ESP32-S3: the reading rises when touched
#endif
}
void setup() {
Serial.begin(115200);
delay(500);
// Average a few readings with nobody touching the keys.
for (int i = 0; i < 4; i++) {
uint32_t sum = 0;
for (int k = 0; k < 16; k++) {
sum += touchRead(KEYS[i]);
delay(5);
}
baseline[i] = sum / 16;
Serial.printf("key %d on GPIO %d: baseline %lu\n", i + 1, KEYS[i], (unsigned long)baseline[i]);
}
Serial.println("Touch a key");
}
void loop() {
int now = -1;
for (int i = 0; i < 4; i++) {
uint32_t v = touchRead(KEYS[i]);
bool on = touched(i, v);
if (on && !down[i]) Serial.printf("key %d down %s (reading %lu)\n", i + 1, NOTES[i], (unsigned long)v);
if (!on && down[i]) Serial.printf("key %d up\n", i + 1);
down[i] = on;
if (on && now < 0) now = i;
}
if (now != playing) {
if (now >= 0) tone(BUZZER, FREQ[now]);
else noTone(BUZZER);
playing = now;
}
delay(20);
}S3
3V3
GND
3V3
TX
RST
RX
4
1
5
2
6
42
7
41
15
40
16
39
17
38
18
37
8
36
3
35
46
0
9
45
10
48
11
47
12
21
13
20
14
19
5V
GND
GND
GND
Wiring
| From | To board | Note |
|---|---|---|
| Key 1 | GPIO 4 | Any conductive pad. Bare wire is enough |
| Key 2 | GPIO 5 | |
| Key 3 | GPIO 6 | |
| Key 4 | GPIO 7 | |
| Buzzer + | GPIO 15 | Optional. A passive buzzer, not an active one |
| Buzzer - | GND |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32S3 Dev Module
// Four touch keys. Touch pins sense a finger through the wire, no parts needed.
const int KEYS[] = {4, 5, 6, 7};
const char* NOTES[] = {"C4", "E4", "G4", "C5"};
const int FREQ[] = {262, 330, 392, 523};
const int BUZZER = 15; // optional passive buzzer
uint32_t baseline[4];
bool down[4];
int playing = -1;
bool touched(int i, uint32_t v) {
#if CONFIG_IDF_TARGET_ESP32
return v < baseline[i] * 7 / 10; // classic ESP32: the reading drops when touched
#else
return v > baseline[i] * 13 / 10; // ESP32-S3: the reading rises when touched
#endif
}
void setup() {
Serial.begin(115200);
delay(500);
// Average a few readings with nobody touching the keys.
for (int i = 0; i < 4; i++) {
uint32_t sum = 0;
for (int k = 0; k < 16; k++) {
sum += touchRead(KEYS[i]);
delay(5);
}
baseline[i] = sum / 16;
Serial.printf("key %d on GPIO %d: baseline %lu\n", i + 1, KEYS[i], (unsigned long)baseline[i]);
}
Serial.println("Touch a key");
}
void loop() {
int now = -1;
for (int i = 0; i < 4; i++) {
uint32_t v = touchRead(KEYS[i]);
bool on = touched(i, v);
if (on && !down[i]) Serial.printf("key %d down %s (reading %lu)\n", i + 1, NOTES[i], (unsigned long)v);
if (!on && down[i]) Serial.printf("key %d up\n", i + 1);
down[i] = on;
if (on && now < 0) now = i;
}
if (now != playing) {
if (now >= 0) tone(BUZZER, FREQ[now]);
else noTone(BUZZER);
playing = now;
}
delay(20);
}Parts
| Quantity | Part | Where to buy |
|---|---|---|
| 1× | ESP32 or ESP32-S3 dev board | Buy at Amazon |
| 4× | Jumper wires, or foil, coins or fruit as keys | |
| 1× | Optional passive buzzer |
Amazon buy links may include an Associates tag, and this site may earn a commission from qualifying Amazon purchases at no extra cost to you. Adafruit links are direct.
Libraries
None beyond the ESP32 Arduino core.
Load it
- In the Arduino IDE, install the esp32 board package and pick the board named above the sketch.
- Paste the sketch, choose the board's port and upload. Hold BOOT if the upload stalls at Connecting.
- Close the IDE's serial monitor, then open the bench here to watch the output.
Field notes
- The ESP32-C3 has no touch sensor, so this project is for the classic ESP32 and the S3 only.
- Readings go the opposite way on the two chips: on the classic ESP32 they fall when touched, on the S3 they rise. The sketch handles both.
- Keep your hands off the keys while the board starts. The first half second sets the untouched baseline.
- Readings shift with humidity, wire length and whether the board is on USB or a battery. If keys trigger by themselves, press EN to take a new baseline.
Not tested yet
- Not tested on hardware yet. Compiles for every board listed with Arduino-ESP32 core 3.3.12.