The first reading
Four wires and a sketch with no library in it. No library in the Library Manager knows this chip, so the sketch talks to its registers directly, and that turns out to be about forty lines: check it is there, wake it up, read six bytes.
The four wires
Four jumpers from the header to your board. The pull-ups are already on the TK115, so nothing else goes on the breadboard:
GND first, from the square pad. Then 3V3 to your board's 3.3 V pin, never 5V. Then SCL and SDA to your board's I²C pins: GPIO 9 and GPIO 8 on an ESP32-S3, GPIO 22 and 21 on an ESP32, GP5 and GP4 on a Pico. Those are each board's defaults, so the sketch never names them.
An Arduino Uno is the exception, and the figure shows why. Its Wire library turns on the Uno's own pull-ups to 5 V, which fight the board's 4.7 kΩ to 3.3 V and settle the idle lines somewhere between about 3.45 and 3.62 V, at the chip's absolute limit. Put a TK97 logic level converter between them, fed from the Uno's 5V, and take this board's 3V3 from the TK97's own 3V3 pin. Or use a 3.3 V board.
What the sketch does
Three things, in this order, and each is a few lines.
It asks the chip its name. Register 0x0F, called WHO_AM_I, holds 0x11 on this chip and nothing else. If the answer is missing or wrong, the sketch says so and stops, which separates a wiring fault from a code fault before either can waste your time.
It wakes the chip up. Two register writes: one sets 100 readings a second with all three axes on, the other keeps the range at ±2 g. Without the first of them the chip stays asleep, which the next article is about.
It reads six bytes, two per axis, and turns each pair into a number in g. How two bytes become a number is from counts to g.
What you should see
Flat on the desk, chip side up, five lines a second:
SC7A20 found, awake at 100 Hz
X 0.031 Y -0.012 Z 1.004 g
X 0.028 Y -0.016 Z 1.008 g
X 0.032 Y -0.008 Z 0.996 gZ near 1 and the other two near 0, each wandering by a few thousandths from line to line. Your X and Y will not be exactly zero either: a mounted chip can be up to 0.12 g off on any axis, and the example lines above are an illustration of that, not a recording.
Now pick the board up and stand it on its header. X should jump to about −1 and Z drop to about 0. That is which way is X checked on your own board, and it takes ten seconds.
For a live picture instead of numbers, the
accelerometer sketch on the block's page prints the same three readings
in the form the Serial Plotter draws as three lines.
The code
No library to install: the sketch uses Wire, which comes with every Arduino core. Three small helpers write one register, read several, and turn six bytes into three numbers in g.
/*
3-Axis Accelerometer - first reading 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;
}
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: 100 readings a second, X Y Z on.
writeReg(0x20, 0x57);
// CTRL_REG4: +-2 g, and never half of one reading and half the next.
writeReg(0x23, 0x80);
Serial.println("SC7A20 found, awake at 100 Hz");
}
void loop() {
float x, y, z;
if (readG(x, y, z)) {
Serial.print("X "); Serial.print(x, 3);
Serial.print(" Y "); Serial.print(y, 3);
Serial.print(" Z "); Serial.print(z, 3);
Serial.println(" g");
} else {
Serial.println("read failed");
}
delay(200);
}The two register writes in setup are the part that is easy to leave out. The chip powers up asleep and reports nothing new until CTRL_REG1 is written, which is the next article. The 0x80 in readRegs is what lets six registers come back from one request.
View on GitHub · blocks/tk115-3-axis-accelerometer/arduino/accelerometer_first_reading/accelerometer_first_reading.ino @ v1.12The same reading in MicroPython, again with no library: readfrom_mem and writeto_mem do what the three Arduino helpers do.
"""
3-Axis Accelerometer - first reading, 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
# 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: 100 readings a second, X Y Z on.
i2c.writeto_mem(ADDR, 0x20, bytes([0x57]))
# 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])
print("SC7A20 found, awake at 100 Hz")
while True:
x, y, z = read_g()
print("X %6.3f Y %6.3f Z %6.3f g" % (x, y, z))
time.sleep_ms(200)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. If nothing answers, the script says so and stops rather than raising an OSError. Stop the loop with Ctrl-C.
View on GitHub · blocks/tk115-3-axis-accelerometer/micropython/accelerometer_first_reading.py @ v1.12When it does not work
Nothing answered, or something answered with the wrong name. Check GND first: without it the power light can still glow, borrowing a return through the signal lines. Then check SDA and SCL are not swapped, which looks perfectly normal because both lines still idle high. Then check 3V3 is on a pin that is actually powered.
On an ESP32-S3 or any board with native USB, set Tools > USB CDC On Boot to Enabled and upload again. Without it the USB serial port is not started at boot, and the sketch runs with nowhere to print. Check the monitor is at 115200 too.
The chip answered once, at the start, and has stopped answering. That is almost always a wire that moved: a loose jumper on a breadboard makes and breaks as the board is handled, which is exactly what an accelerometer is for. Plug the header straight into the breadboard or use wires with a firm grip.
An axis reads +1 g when it points up, which feels backwards at first: flat and chip side up, Z reads +1. If one axis still disagrees with Which way is X after that, trust the board and flip that axis's sign in readG.
On an ESP32 or ESP32-S3, yes: call Wire.begin(sdaPin, sclPin) instead of Wire.begin() and any two free GPIOs will do. In MicroPython change SDA_PIN and SCL_PIN. On a Pico the I²C hardware reaches particular pins, and GP4 and GP5 are the defaults.
The one register write without which every reading is stale, and the register that proves you found the right chip.
It wakes up asleep →Edit this page — content/books/3-axis-accelerometer/the-first-reading.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.