Putting it to work · 10 of 11

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:

Counting shakes
Data rate
Between samples
40.0 ms
Tap above the line
5.1 ms
Chance of catching it
13%
At 25 Hz the chip reads once every 40.0 ms. The tap is above the line for about 5.1 ms. Run it and watch which samples land on it.

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 g

Illustrative 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.

accelerometer_shake.ino
/*
  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.12

When it does not work

One shake counts as three or four jolts.

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.

It counts jolts when I just pick it up.

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 on the table is not counted.

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.

Dropping it reads about 0 g. Is that a jolt?

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.

Where this goes next

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

Community

Questions about this product

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

Ask a question ↗

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.

Browse Modules and blocks on the forum →