Counting shakes
A still board reads 1 g in total, whichever way up it is. So anything that pushes the total away from 1 g is motion: a shake, a knock, a drop. The counter needs one number and one threshold, and one setting that decides whether it sees a short knock at all: how often the chip looks.
Anything but 1 g
At rest, the three readings together, the square root of X² + Y² + Z², come to 1 g, however the board is turned. The counter watches that total. When it goes more than 0.5 g from 1 — past 1.5 on a knock, or under 0.5 in a drop — that is a jolt, and the counter adds one.
After a jolt it ignores everything for 300 ms. A shake swings back and forth and every swing crosses the line, so without the pause one shake would count as several.
How often the chip looks
A knock on a table is short. The chip only sees what happens at the moments it takes a reading, so if it looks too rarely, a short spike can pass between two readings completely:
The spike in the figure is an illustration of a tap's shape, a few milliseconds long. At 25 readings a second the chip looks every 40 ms and the spike falls between two looks: nothing is counted. At 100 a second it may or may not land a reading on it. At 400 a second, a reading every 2.5 ms, it cannot miss.
That is why this sketch writes 0x77 to CTRL_REG1, 400 readings a second, where the others use 0x57 for 100. The loop reads as fast as the bus allows, with a 2 ms pause, so it collects every reading the chip makes.
The chip also has a tap detector of its own, which checks every reading the chip takes, whether or not your loop collected it. Its result is in a register the sketch could poll; the pin that would signal it, INT1, is not connected.
What you should see
Tap the table beside the board, then shake it once:
jolt 1 1.62 g
jolt 2 2.18 gIllustrative lines. The size printed is the reading that crossed the line, not the peak of the knock: the peak may have come between two readings, or past the ±2 g the sketch leaves the range at.
The code
The first reading's helpers again, with the chip set to 400 readings a second, and a loop that compares the total against 1 g.
/*
3-Axis Accelerometer - shake counter TK115 / /p/tk115
Wiring. Count from the square pad, which is GND. Chip side up,
header at the bottom, left to right:
GND -> GND
3V3 -> 3V3 (3.3 V only. The chip's limit is 3.6 V, and the
board's pull-ups put this pin on SDA and SCL.)
SCL -> GPIO 9 on an ESP32-S3, GPIO 22 on an ESP32, GP5 on a Pico
SDA -> GPIO 8 on an ESP32-S3, GPIO 21 on an ESP32, GP4 on a Pico
Each board's default I2C pins, so nothing in the sketch names them.
A 5 V Arduino Uno needs a level converter (TK97) in between.
Arduino IDE
Tools > Board your board, e.g. ESP32S3 Dev Module
Tools > Port the one that appears when you plug in
Tools > USB CDC On Boot Enabled (ESP32-S3 only)
Library Manager nothing to install, only Wire
Serial Monitor 115200
*/
#include <Wire.h>
// 0x19, because the board leaves the chip's SDO pin open and the chip
// pulls it high itself. Tied to GND it would answer at 0x18.
const uint8_t ACCEL_ADDR = 0x19;
bool writeReg(uint8_t reg, uint8_t value) {
Wire.beginTransmission(ACCEL_ADDR);
Wire.write(reg);
Wire.write(value);
return Wire.endTransmission() == 0;
}
// Read n registers from reg on. Bit 7 set on the register number makes
// the chip step to the next register after every byte.
bool readRegs(uint8_t reg, uint8_t *buf, uint8_t n) {
Wire.beginTransmission(ACCEL_ADDR);
Wire.write(reg | 0x80);
if (Wire.endTransmission(false) != 0) return false;
if (Wire.requestFrom(ACCEL_ADDR, n) != n) return false;
for (uint8_t i = 0; i < n; i++) buf[i] = Wire.read();
return true;
}
// X, Y and Z in g. Low byte first; the 12 bits sit at the top of the
// 16, so shift them down, and at +-2 g every count is then 1 mg.
bool readG(float &x, float &y, float &z) {
uint8_t b[6];
if (!readRegs(0x28, b, 6)) return false; // OUT_X_L .. OUT_Z_H
x = ((int16_t)(b[1] << 8 | b[0]) >> 4) / 1000.0;
y = ((int16_t)(b[3] << 8 | b[2]) >> 4) / 1000.0;
z = ((int16_t)(b[5] << 8 | b[4]) >> 4) / 1000.0;
return true;
}
const float JOLT_G = 0.5; // this far from 1 g is a jolt
const unsigned long QUIET_MS = 300; // and one jolt is counted once
void setup() {
Serial.begin(115200);
while (!Serial) delay(10); // native USB: wait for the monitor
Wire.begin();
uint8_t id = 0; // WHO_AM_I: 0x11 on this chip
if (!readRegs(0x0F, &id, 1) || id != 0x11) {
Serial.println("no SC7A20 at 0x19: check GND, then SDA and SCL");
while (true) delay(100);
}
// It powers up asleep. CTRL_REG1: 400 readings a second, X Y Z on.
writeReg(0x20, 0x77);
// CTRL_REG4: +-2 g, and never half of one reading and half the next.
writeReg(0x23, 0x80);
}
unsigned long lastJolt = 0;
unsigned int jolts = 0;
void loop() {
float x, y, z;
if (!readG(x, y, z)) return;
// Still, the total is 1 g whichever way up the board is. A knock or
// a shake is anything that pushes it away from 1 g.
float total = sqrt(x * x + y * y + z * z);
bool jolt = fabs(total - 1.0) > JOLT_G;
if (jolt && millis() - lastJolt > QUIET_MS) {
lastJolt = millis();
jolts++;
Serial.print("jolt "); Serial.print(jolts);
Serial.print(" "); Serial.print(total, 2);
Serial.println(" g");
}
delay(2); // a new reading every 2.5 ms
}The total is the square root of X squared plus Y squared plus Z squared, the same number the tilt sketch uses to decide the board is moving. It is 1 g at rest whichever way up the board sits, which is why this sketch does not care how the board is mounted.
View on GitHub · blocks/tk115-3-axis-accelerometer/arduino/accelerometer_shake/accelerometer_shake.ino @ v1.12The same counter in MicroPython. time.ticks_ms and ticks_diff do the dead time, because they handle the counter wrapping round.
"""
3-Axis Accelerometer - shake counter, MicroPython TK115 / /p/tk115
Wiring. Count from the square pad, which is GND. Chip side up,
header at the bottom, left to right:
GND -> GND
3V3 -> 3V3. 3.3 V only: the chip's limit is 3.6 V.
SCL -> GPIO 9 on an ESP32-S3, GPIO 22 on an ESP32, GP5 on a Pico
SDA -> GPIO 8 on an ESP32-S3, GPIO 21 on an ESP32, GP4 on a Pico
Change SDA_PIN and SCL_PIN below for an ESP32 or a Pico.
Thonny
Run > Configure interpreter MicroPython (ESP32) or
MicroPython (Raspberry Pi Pico)
No library needed: the registers are all in this file.
"""
from machine import I2C, Pin
import time
import math
# 0x19: the board leaves the chip's SDO pin open and the chip pulls it
# high itself.
ADDR = 0x19
# GPIO numbers. ESP32-S3: 8 and 9. ESP32: 21 and 22. Pico: 4 and 5.
SDA_PIN = 8
SCL_PIN = 9
i2c = I2C(0, sda=Pin(SDA_PIN), scl=Pin(SCL_PIN), freq=100_000)
try:
found = i2c.readfrom_mem(ADDR, 0x0F, 1)[0] == 0x11 # WHO_AM_I
except OSError:
found = False
if not found:
print("no SC7A20 at 0x19: check GND, then SDA and SCL")
raise SystemExit
# It powers up asleep. CTRL_REG1: 400 readings a second, X Y Z on.
i2c.writeto_mem(ADDR, 0x20, bytes([0x77]))
# CTRL_REG4: +-2 g, and never half of one reading and half the next.
i2c.writeto_mem(ADDR, 0x23, bytes([0x80]))
def axis(lo, hi):
v = (hi << 8) | lo
if v & 0x8000:
v -= 0x10000 # two's complement
return (v >> 4) / 1000 # 12 bits at the top; 1 mg a count
def read_g():
# 0x28 is OUT_X_L; bit 7 set steps through all six registers.
b = i2c.readfrom_mem(ADDR, 0x28 | 0x80, 6)
return axis(b[0], b[1]), axis(b[2], b[3]), axis(b[4], b[5])
JOLT_G = 0.5 # this far from 1 g counts as a jolt
QUIET_MS = 300 # and one jolt is counted once
last = time.ticks_ms()
jolts = 0
# Still, the total is 1 g whichever way up the board is. A knock or a
# shake is anything that pushes it away from 1 g.
while True:
x, y, z = read_g()
total = math.sqrt(x * x + y * y + z * z)
now = time.ticks_ms()
jolt = abs(total - 1) > JOLT_G
if jolt and time.ticks_diff(now, last) > QUIET_MS:
last = now
jolts += 1
print("jolt %d %.2f g" % (jolts, total))
time.sleep_ms(2)SDA_PIN and SCL_PIN are GPIO numbers: 8 and 9 on an ESP32-S3, 21 and 22 on an ESP32, 4 and 5 on a Pico. Change JOLT_G and QUIET_MS at the top to tune it. Stop it with Ctrl-C.
View on GitHub · blocks/tk115-3-axis-accelerometer/micropython/accelerometer_shake.py @ v1.12When it does not work
A shake goes back and forth, and every swing crosses the threshold. The sketch ignores anything for 300 ms after a jolt for that reason; raise QUIET_MS if your shakes are slower, or lower it to count fast taps separately.
Picking it up quickly is a push, and a firm one crosses 0.5 g. Raise JOLT_G to 1.0 or so to count only knocks and hard shakes. The number is the one to tune for your project, and 0.5 is only a starting point.
A tap is over in a few milliseconds, and if the chip does not take a reading during it, the sketch never sees it. The shake sketch runs the chip at 400 readings a second for this reason; if you lowered it to save power, put it back to 0x77.
Yes, and it is counted the same way, because 0 is more than 0.5 away from 1. Falling is the one state in which nothing pushes on the board. A sketch that only wants drops can look for a total under about 0.3 g for several readings in a row.
Five things a serial monitor can show, and what each one means.
When it reads wrong →Edit this page — content/books/3-axis-accelerometer/counting-shakes.mdx
Questions about this product
See what other owners have asked, and read their solutions.
3-Axis Accelerometer
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.