Skip to content

Commit d6fdead

Browse files
committed
fix(ism330dl): Add auto-zero calibration and IMU power-off to spirit level.
Without calibration, the 0.05g level-detection threshold is too tight for most boards: accelerometer bias keeps the bubble off-center at rest and the "level" state may never trigger. Add a startup auto-zero phase that averages 20 samples on a flat surface and subtracts the measured bias from every subsequent reading. Also power off the IMU in the finally block so a Ctrl+C does not leave the sensor running at 104 Hz, draining the battery until the next reboot.
1 parent 3ea0a77 commit d6fdead

1 file changed

Lines changed: 32 additions & 6 deletions

File tree

lib/ism330dl/examples/spirit_level.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
33
Displays a digital bubble level. The bubble moves according to the board's tilt.
44
When the board is perfectly flat, the bubble centers and the background lights up.
5+
6+
On startup, the board must be placed on a flat surface for a brief auto-zero
7+
calibration (averages a few samples to compensate accelerometer bias).
58
"""
69

710
from time import sleep_ms
@@ -22,6 +25,10 @@
2225
# Tilt thresholds (in g) to consider the board "level/flat"
2326
LEVEL_THRESHOLD = 0.05
2427

28+
# Auto-zero calibration: number of samples averaged at startup
29+
CAL_SAMPLES = 20
30+
CAL_DELAY_MS = 50
31+
2532
# Display Colors (0 to 15 greyscale)
2633
COLOR_BG_TILTED = 0
2734
COLOR_BG_LEVEL = 4
@@ -38,28 +45,46 @@ def fill_circle(fbuf, x0, y0, r, c):
3845
if x * x + y * y <= r * r:
3946
fbuf.pixel(x0 + x, y0 + y, c)
4047

41-
# Hardware Initialization
42-
# I2C and ISM330DL
48+
49+
# --- Hardware Initialization ---
50+
4351
i2c = I2C(1)
4452
imu = ISM330DL(i2c)
4553

46-
# SPI and SSD1327 OLED
4754
spi = SPI(1)
4855
dc = Pin("DATA_COMMAND_DISPLAY")
4956
res = Pin("RST_DISPLAY")
5057
cs = Pin("CS_DISPLAY")
5158
display = ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs)
5259

60+
# --- Auto-zero calibration ---
61+
# Average N acceleration samples to compensate the board's bias at rest.
62+
# The board must be on a flat surface during this phase.
63+
5364
print("=======================")
5465
print(" Spirit Level ")
5566
print("=======================")
67+
print("Calibrating... keep the board flat and still.")
68+
69+
cal_ax, cal_ay = 0.0, 0.0
70+
for _ in range(CAL_SAMPLES):
71+
ax, ay, _az = imu.acceleration_g()
72+
cal_ax += ax
73+
cal_ay += ay
74+
sleep_ms(CAL_DELAY_MS)
75+
cal_ax /= CAL_SAMPLES
76+
cal_ay /= CAL_SAMPLES
77+
print("Offset: ax={:.3f}g, ay={:.3f}g".format(cal_ax, cal_ay))
78+
5679
print("Tilt the board to move the bubble.")
5780
print("Press Ctrl+C to exit.")
5881

5982
try:
6083
while True:
61-
# Read acceleration in g-forces
62-
ax, ay, _az = imu.acceleration_g()
84+
# Read acceleration in g-forces, subtract startup bias
85+
raw_ax, raw_ay, _az = imu.acceleration_g()
86+
ax = raw_ax - cal_ax
87+
ay = raw_ay - cal_ay
6388

6489
# Level Detection
6590
# If both X and Y axes are close to 0g, the board is flat
@@ -108,8 +133,9 @@ def fill_circle(fbuf, x0, y0, r, c):
108133
except KeyboardInterrupt:
109134
print("\nSpirit level stopped.")
110135
finally:
111-
# Clean up and power off display on exit
136+
# Clean up: power off display and IMU to avoid battery drain
112137
display.fill(0)
113138
display.show()
114139
sleep_ms(100)
115140
display.power_off()
141+
imu.power_off()

0 commit comments

Comments
 (0)