Add IMU telemetry pipeline and live sensor UI.

Wire LSM6DS3 readings into the runtime telemetry stream, expose them over web endpoints, and render live voltage/IMU data in the dashboard with websocket updates.

Made-with: Cursor
This commit is contained in:
2026-04-01 23:04:41 +13:00
parent d0d73b422d
commit 397d48a43a
10 changed files with 948 additions and 479 deletions

64
lib/lsm6ds3.py Normal file
View File

@@ -0,0 +1,64 @@
"""LSM6DS3 accelerometer / gyroscope over I2C (MicroPython)."""
import struct
WHO_AM_I_REG = 0x0F
WHO_AM_I_LSM6DS3 = 0x69
CTRL1_XL = 0x10
CTRL2_G = 0x11
OUT_TEMP_L = 0x20
# Default CTRL1_XL = 0x60: 104 Hz ODR, ±2 g, anti-aliasing filter BW ~100 Hz
# Default CTRL2_G = 0x40: 104 Hz ODR, 245 dps full scale
_DEFAULT_XL = b"\x60"
_DEFAULT_G = b"\x40"
# LSB scaling for default full-scale settings above
_G_PER_LSB = 0.000061
_DPS_PER_LSB = 0.00875
def is_lsm6ds3(who_am_i: int) -> bool:
return who_am_i == WHO_AM_I_LSM6DS3
class LSM6DS3:
def __init__(self, i2c, addr=0x6A):
self.i2c = i2c
self.addr = addr
def who_am_i(self) -> int:
return self.i2c.readfrom_mem(self.addr, WHO_AM_I_REG, 1)[0]
def configure(self, ctrl1_xl=_DEFAULT_XL, ctrl2_g=_DEFAULT_G) -> None:
self.i2c.writeto_mem(self.addr, CTRL1_XL, ctrl1_xl)
self.i2c.writeto_mem(self.addr, CTRL2_G, ctrl2_g)
def read_raw(self):
"""Returns (temp_raw, gx, gy, gz, ax, ay, az) as signed 16-bit values."""
data = self.i2c.readfrom_mem(self.addr, OUT_TEMP_L, 14)
return struct.unpack("<hhhhhhh", data)
def read(self):
"""
Scaled readings for default ±2 g / 245 dps configuration.
Returns:
temp_c: float, degrees C
accel_g: (ax, ay, az) in g
gyro_dps: (gx, gy, gz) in degrees per second
"""
temp, gx, gy, gz, ax, ay, az = self.read_raw()
temp_c = 25.0 + temp / 16.0
accel_g = (
ax * _G_PER_LSB,
ay * _G_PER_LSB,
az * _G_PER_LSB,
)
gyro_dps = (
gx * _DPS_PER_LSB,
gy * _DPS_PER_LSB,
gz * _DPS_PER_LSB,
)
return temp_c, accel_g, gyro_dps