A tap switch
Each tap on the disc turns a TK01 XL LED on or off and prints how hard it was. A tap is a reading 150 mV above the lowest since the last one, and then 150 ms of ignoring everything, so one tap switches once, and a second tap on the tail of the first still counts.
Four wires
The TK59 as before: GND to GND, SIGNAL to A0 on an Uno, GPIO 34 on an ESP32, GPIO 4 on an ESP32-S3, GP26 on a Pico. Add a TK01 XL LED: its GND to GND and its SIGNAL to D9 on an Uno, GPIO 4 on an ESP32, GPIO 5 on an ESP32-S3, GP15 on a Pico. These are the TK27 Analog Microphone book's pins for the same two blocks.
Count a rise, not a level
The obvious rule is: over 150 mV is a tap, and wait for it to fall back under 150 mV before looking for the next. It fails on ordinary knocking. The hump from one tap takes a quarter of a second or more to fall under 150 mV, and a second tap in that time is never seen.
Play both rules. The sketch's rule keeps the lowest reading since the last tap. While a hump drains, that lowest reading follows it down. A new tap is a reading 150 mV above wherever the lowest has got to, so a tap on the tail of the last one still stands out. The taps in the figure are this book's model, not a measurement.
After each tap the sketch ignores everything for 150 ms. One tap is a press, a release and several milliseconds of ringing, and without the pause a finger that bounces could count twice. The pause is shorter than the gap between two ordinary knocks, and shorter than a hump takes to drain.
The sketch
floorMv is the lowest reading since the last tap. Every reading lower than
it becomes the new floor. When a reading is RISE_MV above the floor, the
sketch keeps reading for 5 ms to find the top, switches the light, prints the
top, sets the floor to it, and stops listening for LOCKOUT_MS.
The top it prints is how hard the tap was, roughly: harder taps print higher. Different surfaces and different fixings change that more than you might expect, so compare taps on one setup, not across two.
What you should see
At 115200 the serial monitor prints Tap the disc. Each tap switches the
light and prints a line such as tap, peak 1240 mV. The numbers depend on
the tap and on what the disc is lying on.
In MicroPython
The same rule for an ESP32, an ESP32-S3 or a Pico, with the ADC set to its widest range on an ESP32 as in the first reading.
"""
Piezo-Ceramic Sensor - a tap switch, MicroPython TK59 / /p/tk59
Each tap on the disc turns the TK01 XL LED on or off, and prints
how hard it was.
Wiring, the TK59. Count from the square pad, parts up, header on
the left. Two wires: the block needs no supply.
GND -> GND
NC -> nothing (connected to nothing on the board)
NC -> nothing (nor is this one: there is no VCC pin)
SIGNAL -> GPIO 34 on an ESP32, GPIO 4 on an ESP32-S3,
GP26 on a Raspberry Pi Pico
The TK01 XL LED, counted the same way from its square pad:
GND -> GND
NC -> nothing (both of its NC pins)
SIGNAL -> GPIO 4 on an ESP32, GPIO 5 on an ESP32-S3,
GP15 on a Raspberry Pi Pico
Thonny
Run > Configure interpreter MicroPython (ESP32) or
MicroPython (Raspberry Pi Pico)
Nothing to install: machine, sys and time are built in.
"""
import sys
import time
from machine import ADC, Pin
# The GPIO number SIGNAL is wired to. ESP32: 34. ESP32-S3: 4. Pico: 26.
PIEZO_PIN = 4
# The TK01's SIGNAL. ESP32: 4. ESP32-S3: 5. Pico: 15.
LED_PIN = 5
# A tap is a rise of this much above the lowest reading since the
# last one. Lower it for lighter taps, raise it if the LED switches
# on its own.
RISE_MV = 150
# Deaf for this long after each tap, so one tap's ringing cannot
# switch the light twice.
LOCKOUT_MS = 150
adc = ADC(Pin(PIEZO_PIN))
ESP32 = sys.platform == "esp32" # ESP32 and ESP32-S3
if ESP32:
adc.atten(ADC.ATTN_11DB) # the full range, to about 3.1 V
led = Pin(LED_PIN, Pin.OUT, value=0)
def read_mv():
if ESP32:
return adc.read_uv() // 1000 # calibrated in the chip
return adc.read_u16() * 3300 // 65535
floor_mv = 0 # lowest reading since the last tap
armed = True
tap_at = 0
print("Tap the disc.")
while True:
mv = read_mv()
# Follow the level down as the last hump drains away, so a second
# tap on its tail is measured from where it had got to.
if mv < floor_mv:
floor_mv = mv
if armed and mv - floor_mv >= RISE_MV:
# Follow the rise to its top, a few milliseconds at most.
peak = mv
start = time.ticks_ms()
while time.ticks_diff(time.ticks_ms(), start) < 5:
peak = max(peak, read_mv())
led.value(not led.value())
print("tap, peak", peak, "mV")
armed = False
tap_at = time.ticks_ms()
floor_mv = peak
since = time.ticks_diff(time.ticks_ms(), tap_at)
if not armed and since >= LOCKOUT_MS:
armed = TrueThe code
No library. loop() reads SIGNAL with no delay and keeps floorMv, the lowest reading since the last tap, which follows each hump down as it drains. A reading RISE_MV above floorMv is a tap: the sketch follows it for 5 ms to its top, switches the light, prints the top in millivolts, and ignores everything for LOCKOUT_MS.
/*
Piezo-Ceramic Sensor - a tap switch TK59 / /p/tk59
Each tap on the disc turns the TK01 XL LED on or off, and prints
how hard it was.
Wiring, the TK59. Count from the square pad, parts up, header on
the left. Two wires: the block needs no supply.
GND -> GND
NC -> nothing (connected to nothing on the board)
NC -> nothing (nor is this one: there is no VCC pin)
SIGNAL -> A0 on an Uno, GPIO 34 on an ESP32, GPIO 4 on an
ESP32-S3, GP26 on a Raspberry Pi Pico
The TK01 XL LED, counted the same way from its square pad:
GND -> GND
NC -> nothing (both of its NC pins)
SIGNAL -> D9 on an Uno, GPIO 4 on an ESP32, GPIO 5 on an
ESP32-S3, GP15 on a Raspberry Pi Pico
Arduino IDE
Tools > Board your board, e.g. Arduino Uno
Tools > Port the one that appears when you plug in
Tools > USB CDC On Boot Enabled (ESP32-S3 only)
No library needed.
*/
// The analog pin SIGNAL is wired to.
// Uno: A0. ESP32: 34. ESP32-S3: 4. Pico: 26.
const int PIEZO_PIN = A0;
// The TK01's SIGNAL. Uno: 9. ESP32: 4. ESP32-S3: 5. Pico: 15.
const int LED_PIN = 9;
// Uno and Pico only: the ADC's full scale, in mV.
// Uno: 5000. Pico: 3300.
const float FULL_SCALE_MV = 5000.0;
// A tap is a rise of this much above the lowest reading since
// the last one. Lower it for lighter taps, raise it if the LED
// switches on its own.
const int RISE_MV = 150;
// Deaf for this long after each tap, so one tap's ringing
// cannot switch the light twice.
const unsigned long LOCKOUT_MS = 150;
int floorMv = 0; // lowest reading since the last tap
bool armed = true;
unsigned long tapAt = 0;
bool lampOn = false;
int readMilliVolts() {
#if defined(ARDUINO_ARCH_ESP32)
return analogReadMilliVolts(PIEZO_PIN); // calibrated in the chip
#else
return analogRead(PIEZO_PIN) * FULL_SCALE_MV / 1023.0;
#endif
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
Serial.println("Tap the disc.");
}
void loop() {
int mv = readMilliVolts();
// Follow the level down as the last hump drains away, so a
// second tap on its tail is measured from where it had got to.
if (mv < floorMv) floorMv = mv;
if (armed && mv - floorMv >= RISE_MV) {
// Follow the rise to its top, a few milliseconds at most.
int peak = mv;
unsigned long start = millis();
while (millis() - start < 5) {
int v = readMilliVolts();
if (v > peak) peak = v;
}
lampOn = !lampOn;
digitalWrite(LED_PIN, lampOn ? HIGH : LOW);
Serial.print("tap, peak ");
Serial.print(peak);
Serial.println(" mV");
armed = false;
tapAt = millis();
floorMv = peak;
}
if (!armed && millis() - tapAt >= LOCKOUT_MS) armed = true;
}Change PIEZO_PIN and LED_PIN to the pins you wired, and FULL_SCALE_MV to 3300 on a Pico. RISE_MV and LOCKOUT_MS are the two numbers to tune. The sketch compiles for an ESP32-S3, an ESP32 and an Uno.
View on GitHub · blocks/tk59-piezo-sensor/arduino/piezo_tap_switch/piezo_tap_switch.ino @ v1.5When it does not work
Something is lifting SIGNAL by 150 mV without a tap: a knock through the table, a loose GND wire, or noise on a long lead. Check GND first, then raise RISE_MV, 50 mV at a time, until it stops.
Lower RISE_MV, and tap the disc itself rather than the table beside it: the disc answers to being bent. Keep RISE_MV above the resting noise, a few tens of millivolts on an ESP32, or the light switches on its own.
Taps closer together than LOCKOUT_MS, 150 ms, land in the time the sketch ignores everything. Lower it to 80 or 100 ms if you need them; below that, one tap's ringing can switch the light twice.
Then the piezo side is fine and the LED is not on LED_PIN. The TK01's SIGNAL goes to D9 on an Uno, GPIO 4 on an ESP32, GPIO 5 on an ESP32-S3, GP15 on a Pico, and its GND to GND.
Edit this page — content/books/piezo-sensor/a-tap-switch.mdx
Questions about this product
See what other owners have asked, and read their solutions.
Piezo-Ceramic Sensor
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.