diff --git a/lib/ism330dl/README.md b/lib/ism330dl/README.md new file mode 100644 index 00000000..d275e36b --- /dev/null +++ b/lib/ism330dl/README.md @@ -0,0 +1,254 @@ +# ISM330DL MicroPython Driver + +MicroPython driver for the **STMicroelectronics ISM330DL** 6-axis IMU. + +The ISM330DL integrates: + +* a **3-axis accelerometer** +* a **3-axis gyroscope** + +This driver provides a simple API to configure the sensor and read motion data using **I²C**. + +--- + +# Features + +* I²C communication +* device identification (`WHO_AM_I`) +* accelerometer configuration +* gyroscope configuration +* raw sensor readings +* converted physical units +* board orientation reading +* board rotation reading +* temperature reading +* data-ready status helpers +* power-down mode + +--- + +# Sensor overview + +| Sensor | Range | Unit | +| ------------- | ---------------------------------- | --------- | +| Accelerometer | ±2g / ±4g / ±8g / ±16g | g | +| Gyroscope | ±125 / ±250 / ±500 / ±1000 / ±2000 | degrees/s | +| Temperature | internal sensor | °C | + +--- + +# I²C Address + +The sensor can use two I²C addresses depending on the **SA0 pin**: + +| SA0 | Address | +| --- | ------- | +| 0 | `0x6A` | +| 1 | `0x6B` | + +The STeaMi board uses **0x6B** (default). + +--- + +# Basic Usage + +```python +from machine import I2C +from ism330dl import ISM330DL + +i2c = I2C(1) + +imu = ISM330DL(i2c) + +ax, ay, az = imu.acceleration_g() +gx, gy, gz = imu.gyroscope_dps() +temp = imu.temperature_c() + +print("Accel:", ax, ay, az) +print("Gyro :", gx, gy, gz) +print("Temp :", temp) +``` + +--- + +# API + +## Initialization + +```python +imu = ISM330DL(i2c) +``` + +--- + +## Accelerometer + +### Raw data + +```python +imu.acceleration_raw() +``` + +Returns: + +``` +(x, y, z) +``` + +16-bit raw values. + +--- + +### Acceleration in g + +```python +imu.acceleration_g() +``` + +Example: + +``` +(0.01, -0.02, 0.99) +``` + +--- + +### Acceleration in m/s² + +```python +imu.acceleration_ms2() +``` + +### Board orientation + +```python +imu.orientation() +``` + +--- + +## Gyroscope + +### Raw data + +```python +imu.gyroscope_raw() +``` + +--- + +### Angular velocity in degrees per second + +```python +imu.gyroscope_dps() +``` + +Example: + +``` +(0.5, -0.3, 1.2) +``` + +--- + +### Angular velocity in radians per second + +```python +imu.gyroscope_rads() +``` + +### Motion detection + +```python +imu.motion() +``` + +--- + +## Temperature + +```python +imu.temperature_c() +``` + +Example: + +``` +26.3 +``` + +--- + +## Configuration + +### Accelerometer + +```python +imu.configure_accel(odr, scale) +``` + +Example: + +```python +from ism330dl.const import ACCEL_ODR_104HZ, ACCEL_FS_4G + +imu.configure_accel(ACCEL_ODR_104HZ, ACCEL_FS_4G) +``` + +--- + +### Gyroscope + +```python +imu.configure_gyro(odr, scale) +``` + +Example: + +```python +from ism330dl.const import GYRO_ODR_104HZ, GYRO_FS_500DPS + +imu.configure_gyro(GYRO_ODR_104HZ, GYRO_FS_500DPS) +``` + +--- + +## Status + +```python +imu.status() +``` + +Returns: + +``` +{ + "temp_ready": True, + "gyro_ready": True, + "accel_ready": True +} +``` + +--- + +## Power Down + +```python +imu.power_down() +``` + +Stops accelerometer and gyroscope. + +--- + +# Examples + +The repository provides several example scripts: + +| Example | Description | +| ----------------------- | ------------------------------------------------- | +| `basic_read.py` | Simple sensor readout | +| `static_orientation.py` | Detect device orientation using the accelerometer | +| `motion_orientation.py` | Detect rotation using the gyroscope | + +--- \ No newline at end of file diff --git a/lib/ism330dl/examples/basic_read.py b/lib/ism330dl/examples/basic_read.py new file mode 100644 index 00000000..0e6a459a --- /dev/null +++ b/lib/ism330dl/examples/basic_read.py @@ -0,0 +1,28 @@ +from machine import I2C +from time import sleep_ms +from ism330dl import ISM330DL + +i2c = I2C(1) + +imu = ISM330DL(i2c) + +print("ISM330DL example: basic read") +print() + +while True: + + ax, ay, az = imu.acceleration_g() + gx, gy, gz = imu.gyroscope_dps() + temp = imu.temperature_c() + + print( + "A[g]=({:+.2f},{:+.2f},{:+.2f}) " + "G[dps]=({:+.1f},{:+.1f},{:+.1f}) " + "T={:.1f}°C".format( + ax, ay, az, + gx, gy, gz, + temp + ) + ) + + sleep_ms(500) diff --git a/lib/ism330dl/examples/motion_orientation.py b/lib/ism330dl/examples/motion_orientation.py new file mode 100644 index 00000000..d98a404c --- /dev/null +++ b/lib/ism330dl/examples/motion_orientation.py @@ -0,0 +1,17 @@ +from machine import I2C +from time import sleep_ms +from ism330dl import ISM330DL + +i2c = I2C(1) + +imu = ISM330DL(i2c) + +print("ISM330DL gyroscope direction demo") +print() + +while True: + motion, speed = imu.motion() + + print("Motion: {} Speed: {:.1f} dps".format(motion, speed)) + + sleep_ms(200) diff --git a/lib/ism330dl/examples/static_orientation.py b/lib/ism330dl/examples/static_orientation.py new file mode 100644 index 00000000..2af04420 --- /dev/null +++ b/lib/ism330dl/examples/static_orientation.py @@ -0,0 +1,22 @@ +from machine import I2C +from time import sleep_ms +from ism330dl import ISM330DL + +i2c = I2C(1) + +imu = ISM330DL(i2c) + +print("ISM330DL orientation demo") +print() + + +# ------------------------------------------------- +# Main loop +# ------------------------------------------------- + +while True: + dir = imu.orientation() + + print("Orientation: {}".format(dir)) + + sleep_ms(500) diff --git a/lib/ism330dl/ism330dl/__init__.py b/lib/ism330dl/ism330dl/__init__.py new file mode 100644 index 00000000..19358d98 --- /dev/null +++ b/lib/ism330dl/ism330dl/__init__.py @@ -0,0 +1,15 @@ +from ism330dl.device import ISM330DL +from ism330dl.exceptions import ( + ISM330DLError, + ISM330DLNotFound, + ISM330DLConfigError, + ISM330DLIOError, +) + +__all__ = [ + "ISM330DL", + "ISM330DLConfigError", + "ISM330DLError", + "ISM330DLIOError", + "ISM330DLNotFound", +] diff --git a/lib/ism330dl/ism330dl/const.py b/lib/ism330dl/ism330dl/const.py new file mode 100644 index 00000000..c13d8cc2 --- /dev/null +++ b/lib/ism330dl/ism330dl/const.py @@ -0,0 +1,138 @@ +from micropython import const + +# --------------------------------------------------------------------- +# I2C addresses / identity +# --------------------------------------------------------------------- +ISM330DL_I2C_ADDR_LOW = const(0x6A) +ISM330DL_I2C_ADDR_HIGH = const(0x6B) +ISM330DL_I2C_DEFAULT_ADDR = ISM330DL_I2C_ADDR_HIGH +ISM330DL_WHO_AM_I_VALUE = const(0x6A) + + +# --------------------------------------------------------------------- +# Registers +# --------------------------------------------------------------------- +REG_WHO_AM_I = const(0x0F) +REG_CTRL1_XL = const(0x10) +REG_CTRL2_G = const(0x11) +REG_CTRL3_C = const(0x12) +REG_STATUS_REG = const(0x1E) + +REG_OUT_TEMP_L = const(0x20) +REG_OUT_TEMP_H = const(0x21) + +REG_OUTX_L_G = const(0x22) +REG_OUTX_H_G = const(0x23) +REG_OUTY_L_G = const(0x24) +REG_OUTY_H_G = const(0x25) +REG_OUTZ_L_G = const(0x26) +REG_OUTZ_H_G = const(0x27) + +REG_OUTX_L_XL = const(0x28) +REG_OUTX_H_XL = const(0x29) +REG_OUTY_L_XL = const(0x2A) +REG_OUTY_H_XL = const(0x2B) +REG_OUTZ_L_XL = const(0x2C) +REG_OUTZ_H_XL = const(0x2D) + + +# --------------------------------------------------------------------- +# CTRL3_C bits +# --------------------------------------------------------------------- +CTRL3_C_BDU = const(1 << 6) +CTRL3_C_IF_INC = const(1 << 2) +CTRL3_C_SW_RESET = const(1 << 0) + + +# --------------------------------------------------------------------- +# STATUS bits +# --------------------------------------------------------------------- +STATUS_TDA = const(1 << 2) +STATUS_GDA = const(1 << 1) +STATUS_XLDA = const(1 << 0) + + +# --------------------------------------------------------------------- +# Accelerometer ODR +# --------------------------------------------------------------------- +ACCEL_ODR_POWER_DOWN = const(0x00) +ACCEL_ODR_12_5HZ = const(0x01) +ACCEL_ODR_26HZ = const(0x02) +ACCEL_ODR_52HZ = const(0x03) +ACCEL_ODR_104HZ = const(0x04) +ACCEL_ODR_208HZ = const(0x05) +ACCEL_ODR_416HZ = const(0x06) +ACCEL_ODR_833HZ = const(0x07) +ACCEL_ODR_1660HZ = const(0x08) + +ACCEL_ODR_VALUES = (0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08) + + +# --------------------------------------------------------------------- +# Accelerometer full scale +# --------------------------------------------------------------------- +ACCEL_FS_2G = const(2) +ACCEL_FS_4G = const(4) +ACCEL_FS_8G = const(8) +ACCEL_FS_16G = const(16) + +ACCEL_FS_BITS = { + ACCEL_FS_2G: 0x00, + ACCEL_FS_16G: 0x01, + ACCEL_FS_4G: 0x02, + ACCEL_FS_8G: 0x03, +} + +ACCEL_SENSITIVITY_MG = { + ACCEL_FS_2G: 0.061, + ACCEL_FS_4G: 0.122, + ACCEL_FS_8G: 0.244, + ACCEL_FS_16G: 0.488, +} + + +# --------------------------------------------------------------------- +# Gyroscope ODR +# --------------------------------------------------------------------- +GYRO_ODR_POWER_DOWN = const(0x00) +GYRO_ODR_12_5HZ = const(0x01) +GYRO_ODR_26HZ = const(0x02) +GYRO_ODR_52HZ = const(0x03) +GYRO_ODR_104HZ = const(0x04) +GYRO_ODR_208HZ = const(0x05) +GYRO_ODR_416HZ = const(0x06) +GYRO_ODR_833HZ = const(0x07) +GYRO_ODR_1660HZ = const(0x08) + +GYRO_ODR_VALUES = (0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08) + + +# --------------------------------------------------------------------- +# Gyroscope full scale +# --------------------------------------------------------------------- +GYRO_FS_125DPS = const(125) +GYRO_FS_250DPS = const(250) +GYRO_FS_500DPS = const(500) +GYRO_FS_1000DPS = const(1000) +GYRO_FS_2000DPS = const(2000) + +GYRO_FS_BITS = { + GYRO_FS_250DPS: 0x00, + GYRO_FS_500DPS: 0x01, + GYRO_FS_1000DPS: 0x02, + GYRO_FS_2000DPS: 0x03, +} + +GYRO_SENSITIVITY_MDPS = { + GYRO_FS_125DPS: 4.375, + GYRO_FS_250DPS: 8.75, + GYRO_FS_500DPS: 17.50, + GYRO_FS_1000DPS: 35.0, + GYRO_FS_2000DPS: 70.0, +} + + +TEMP_SENSITIVITY = 256.0 +TEMP_OFFSET = 25.0 + +STANDARD_GRAVITY = 9.80665 diff --git a/lib/ism330dl/ism330dl/device.py b/lib/ism330dl/ism330dl/device.py new file mode 100644 index 00000000..efc432c2 --- /dev/null +++ b/lib/ism330dl/ism330dl/device.py @@ -0,0 +1,214 @@ +from math import pi +from time import sleep_ms + +from ism330dl.const import * +from ism330dl.exceptions import * + + +class ISM330DL(object): + """MicroPython driver for the ISM330DL 6-axis IMU.""" + + def __init__(self, i2c, address=ISM330DL_I2C_DEFAULT_ADDR): + self.i2c = i2c + self.address = address + self._buffer_1 = bytearray(1) + + self._accel_scale = ACCEL_FS_2G + self._gyro_scale = GYRO_FS_250DPS + + self.check_device() + self.reset() + self.configure_accel(ACCEL_ODR_104HZ, ACCEL_FS_2G) + self.configure_gyro(GYRO_ODR_104HZ, GYRO_FS_250DPS) + + # -------------------------------------------------- + # Low level I2C + # -------------------------------------------------- + + def _read_u8(self, reg): + try: + self.i2c.readfrom_mem_into(self.address, reg, self._buffer_1) + except OSError as exc: + raise ISM330DLIOError("Read register 0x{:02X}".format(reg)) from exc + return self._buffer_1[0] + + def _write_u8(self, reg, value): + try: + self._buffer_1[0] = value & 0xFF + self.i2c.writeto_mem(self.address, reg, self._buffer_1) + except OSError as exc: + raise ISM330DLIOError("Write register 0x{:02X}".format(reg)) from exc + + def _read_bytes(self, reg, n): + try: + return self.i2c.readfrom_mem(self.address, reg, n) + except OSError as exc: + raise ISM330DLIOError("Read {} bytes from 0x{:02X}".format(n, reg)) from exc + + def _read_i16(self, reg): + data = self._read_bytes(reg, 2) + value = data[0] | (data[1] << 8) + if value & 0x8000: + value -= 0x10000 + return value + + def _read_vector(self, reg): + data = self._read_bytes(reg, 6) + + x = data[0] | (data[1] << 8) + y = data[2] | (data[3] << 8) + z = data[4] | (data[5] << 8) + + if x & 0x8000: + x -= 0x10000 + if y & 0x8000: + y -= 0x10000 + if z & 0x8000: + z -= 0x10000 + + return (x, y, z) + + # -------------------------------------------------- + # Device check + # -------------------------------------------------- + + def who_am_i(self): + return self._read_u8(REG_WHO_AM_I) + + def check_device(self): + if self.who_am_i() != ISM330DL_WHO_AM_I_VALUE: + raise ISM330DLNotFound("ISM330DL not detected") + + # -------------------------------------------------- + # Reset + # -------------------------------------------------- + + def reset(self): + self._write_u8(REG_CTRL3_C, CTRL3_C_SW_RESET) + sleep_ms(50) + self._write_u8(REG_CTRL3_C, CTRL3_C_BDU | CTRL3_C_IF_INC) + + # -------------------------------------------------- + # Configuration + # -------------------------------------------------- + + def configure_accel(self, odr, scale): + if odr not in ACCEL_ODR_VALUES: + raise ISM330DLConfigError("Invalid accel ODR") + if scale not in ACCEL_FS_BITS: + raise ISM330DLConfigError("Invalid accel scale") + self._accel_scale = scale + value = (odr << 4) | (ACCEL_FS_BITS[scale] << 2) + self._write_u8(REG_CTRL1_XL, value) + + def configure_gyro(self, odr, scale): + if odr not in GYRO_ODR_VALUES: + raise ISM330DLConfigError("Invalid gyro ODR") + if scale == GYRO_FS_125DPS: + value = (odr << 4) | 0x02 + else: + if scale not in GYRO_FS_BITS: + raise ISM330DLConfigError("Invalid gyro scale") + value = (odr << 4) | (GYRO_FS_BITS[scale] << 2) + self._gyro_scale = scale + self._write_u8(REG_CTRL2_G, value) + + # -------------------------------------------------- + # Raw readings + # -------------------------------------------------- + + def acceleration_raw(self): + return self._read_vector(REG_OUTX_L_XL) + + def gyroscope_raw(self): + return self._read_vector(REG_OUTX_L_G) + + def temperature_raw(self): + return self._read_i16(REG_OUT_TEMP_L) + + # -------------------------------------------------- + # Converted readings + # -------------------------------------------------- + + def acceleration_g(self): + sens = ACCEL_SENSITIVITY_MG[self._accel_scale] + raw = self.acceleration_raw() + return tuple((v * sens) / 1000.0 for v in raw) + + def acceleration_ms2(self): + g = self.acceleration_g() + return tuple(v * STANDARD_GRAVITY for v in g) + + def gyroscope_dps(self): + sens = GYRO_SENSITIVITY_MDPS[self._gyro_scale] + raw = self.gyroscope_raw() + return tuple((v * sens) / 1000.0 for v in raw) + + def gyroscope_rads(self): + dps = self.gyroscope_dps() + return tuple(v * pi / 180.0 for v in dps) + + def temperature_c(self): + raw = self.temperature_raw() + return TEMP_OFFSET + raw / TEMP_SENSITIVITY + + def orientation(self): + ax, ay, az = self.acceleration_g() + threshold = 0.75 + + if az > threshold: + return "SCREEN_DOWN" + if az < -threshold: + return "SCREEN_UP" + if ax > threshold: + return "TOP_EDGE_DOWN" + if ax < -threshold: + return "BOTTOM_EDGE_DOWN" + if ay > threshold: + return "RIGHT_EDGE_DOWN" + if ay < -threshold: + return "LEFT_EDGE_DOWN" + return "MOVING" + + def motion(self): + gx, gy, gz = self.gyroscope_dps() + threshold = 10 + + if abs(gz) > abs(gx) and abs(gz) > abs(gy): + if gz > threshold: + return "TURNING RIGHT", gz + if gz < -threshold: + return "TURNING LEFT", abs(gz) + + if abs(gx) > abs(gy): + if gx > threshold: + return "TILTING LEFT", gx + if gx < -threshold: + return "TILTING RIGHT", abs(gx) + else: + if gy > threshold: + return "TILTING DOWN", gy + if gy < -threshold: + return "TILTING UP", abs(gy) + + return "STABLE", 0 + + # -------------------------------------------------- + # Status + # -------------------------------------------------- + + def status(self): + s = self._read_u8(REG_STATUS_REG) + return { + "temp_ready": bool(s & STATUS_TDA), + "gyro_ready": bool(s & STATUS_GDA), + "accel_ready": bool(s & STATUS_XLDA), + } + + # -------------------------------------------------- + # Power + # -------------------------------------------------- + + def power_down(self): + self._write_u8(REG_CTRL1_XL, 0) + self._write_u8(REG_CTRL2_G, 0) diff --git a/lib/ism330dl/ism330dl/exceptions.py b/lib/ism330dl/ism330dl/exceptions.py new file mode 100644 index 00000000..5681b0f6 --- /dev/null +++ b/lib/ism330dl/ism330dl/exceptions.py @@ -0,0 +1,14 @@ +class ISM330DLError(Exception): + """Base exception for ISM330DL driver.""" + + +class ISM330DLNotFound(ISM330DLError): + """Raised when the sensor is not detected.""" + + +class ISM330DLConfigError(ISM330DLError): + """Raised when configuration is invalid.""" + + +class ISM330DLIOError(ISM330DLError): + """Raised when an I2C communication error occurs.""" diff --git a/lib/ism330dl/manifest.py b/lib/ism330dl/manifest.py new file mode 100644 index 00000000..c1eb34a9 --- /dev/null +++ b/lib/ism330dl/manifest.py @@ -0,0 +1,6 @@ +metadata( + description="Driver of ISM330DL gyroscope and accelerometer sensor.", + version="0.0.1", +) + +package("ism330dl") diff --git a/tests/scenarios/ism330dl.yaml b/tests/scenarios/ism330dl.yaml new file mode 100644 index 00000000..4a2a08c7 --- /dev/null +++ b/tests/scenarios/ism330dl.yaml @@ -0,0 +1,292 @@ +driver: ism330dl +driver_class: ISM330DL +i2c_address: 0x6B + +i2c: + id: 1 + +# Register values for mock tests +# Simulate an ISM330DL at rest, screen facing down +mock_registers: + # WHO_AM_I + 0x0F: 0x6A + # CTRL1_XL (written by configure_accel during init) + 0x10: 0x00 + # CTRL2_G (written by configure_gyro during init) + 0x11: 0x00 + # CTRL3_C (written by reset during init) + 0x12: 0x00 + # STATUS_REG (all data ready: TDA | GDA | XLDA) + 0x1E: 0x07 + # OUT_TEMP_L/H (raw=0 => 25.0°C) + 0x20: [0x00, 0x00] + # OUTX_L_G..OUTZ_H_G (gyro all zero => stable) + 0x22: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + # OUTX_L_XL..OUTZ_H_XL (z=0x4000=16384 => ~1g downward) + 0x28: [0x00, 0x00, 0x00, 0x00, 0x00, 0x40] + +tests: + # ----- Identity ----- + + - name: "Verify WHO_AM_I register" + action: read_register + register: 0x0F + expect: 0x6A + mode: [mock, hardware] + + - name: "Read WHO_AM_I via method" + action: call + method: who_am_i + expect: 0x6A + mode: [mock, hardware] + + # ----- Status ----- + + - name: "Status returns all ready flags" + action: script + script: | + s = dev.status() + result = s["temp_ready"] and s["gyro_ready"] and s["accel_ready"] + expect_true: true + mode: [mock] + + - name: "Status returns dictionary" + action: call + method: status + expect_not_none: true + mode: [hardware] + + # ----- Accelerometer ----- + + - name: "Acceleration raw returns tuple of 3 ints" + action: script + script: | + raw = dev.acceleration_raw() + result = len(raw) == 3 and all(isinstance(v, int) for v in raw) + expect_true: true + mode: [mock] + + - name: "Acceleration g returns expected values" + action: script + script: | + ax, ay, az = dev.acceleration_g() + result = abs(ax) < 0.01 and abs(ay) < 0.01 and abs(az - 1.0) < 0.01 + expect_true: true + mode: [mock] + + - name: "Acceleration ms2 matches g times 9.80665" + action: script + script: | + g = dev.acceleration_g() + ms2 = dev.acceleration_ms2() + ok = all(abs(ms2[i] - g[i] * 9.80665) < 0.01 for i in range(3)) + result = ok + expect_true: true + mode: [mock] + + - name: "Acceleration magnitude near 1g" + action: script + script: | + ax, ay, az = dev.acceleration_g() + mag = (ax**2 + ay**2 + az**2) ** 0.5 + result = 0.8 < mag < 1.2 + expect_true: true + mode: [hardware] + + # ----- Gyroscope ----- + + - name: "Gyroscope raw returns tuple of 3 ints" + action: script + script: | + raw = dev.gyroscope_raw() + result = len(raw) == 3 and all(isinstance(v, int) for v in raw) + expect_true: true + mode: [mock] + + - name: "Gyroscope dps returns zeros when static" + action: script + script: | + gx, gy, gz = dev.gyroscope_dps() + result = gx == 0.0 and gy == 0.0 and gz == 0.0 + expect_true: true + mode: [mock] + + - name: "Gyroscope rads consistent with dps" + action: script + script: | + from math import pi + dps = dev.gyroscope_dps() + rads = dev.gyroscope_rads() + ok = all(abs(rads[i] - dps[i] * pi / 180.0) < 0.001 for i in range(3)) + result = ok + expect_true: true + mode: [mock] + + - name: "Gyroscope values plausible at rest" + action: script + script: | + gx, gy, gz = dev.gyroscope_dps() + result = abs(gx) < 20 and abs(gy) < 20 and abs(gz) < 20 + expect_true: true + mode: [hardware] + + # ----- Temperature ----- + + - name: "Temperature returns 25.0 from mock data" + action: call + method: temperature_c + expect_range: [24.9, 25.1] + mode: [mock] + + - name: "Temperature raw returns int" + action: script + script: | + result = isinstance(dev.temperature_raw(), int) + expect_true: true + mode: [mock] + + - name: "Temperature in plausible range" + action: call + method: temperature_c + expect_range: [10.0, 50.0] + mode: [hardware] + + # ----- Orientation ----- + + - name: "Orientation detects SCREEN_DOWN" + action: call + method: orientation + expect: "SCREEN_DOWN" + mode: [mock] + + # ----- Motion ----- + + - name: "Motion returns STABLE when static" + action: script + script: | + direction, speed = dev.motion() + result = direction == "STABLE" and speed == 0 + expect_true: true + mode: [mock] + + # ----- Configuration ----- + + - name: "Configure accel writes correct register value" + action: script + script: | + from ism330dl.const import ACCEL_ODR_104HZ, ACCEL_FS_4G + i2c.clear_write_log() + dev.configure_accel(ACCEL_ODR_104HZ, ACCEL_FS_4G) + log = i2c.get_write_log() + reg, data = log[-1] + result = reg == 0x10 and data[0] == (0x04 << 4) | (0x02 << 2) + expect_true: true + mode: [mock] + + - name: "Configure gyro writes correct register value" + action: script + script: | + from ism330dl.const import GYRO_ODR_104HZ, GYRO_FS_500DPS + i2c.clear_write_log() + dev.configure_gyro(GYRO_ODR_104HZ, GYRO_FS_500DPS) + log = i2c.get_write_log() + reg, data = log[-1] + result = reg == 0x11 and data[0] == (0x04 << 4) | (0x01 << 2) + expect_true: true + mode: [mock] + + - name: "Configure accel rejects invalid scale" + action: script + script: | + from ism330dl.const import ACCEL_ODR_104HZ + try: + dev.configure_accel(ACCEL_ODR_104HZ, 99) + result = False + except Exception: + result = True + expect_true: true + mode: [mock] + + - name: "Configure accel rejects invalid ODR" + action: script + script: | + from ism330dl.const import ACCEL_FS_2G + try: + dev.configure_accel(99, ACCEL_FS_2G) + result = False + except Exception: + result = True + expect_true: true + mode: [mock] + + - name: "Configure gyro rejects invalid scale" + action: script + script: | + from ism330dl.const import GYRO_ODR_104HZ + try: + dev.configure_gyro(GYRO_ODR_104HZ, 99) + result = False + except Exception: + result = True + expect_true: true + mode: [mock] + + - name: "Configure gyro rejects invalid ODR" + action: script + script: | + from ism330dl.const import GYRO_FS_250DPS + try: + dev.configure_gyro(99, GYRO_FS_250DPS) + result = False + except Exception: + result = True + expect_true: true + mode: [mock] + + # ----- Power ----- + + - name: "Power down writes zero to CTRL registers" + action: script + script: | + i2c.clear_write_log() + dev.power_down() + log = i2c.get_write_log() + writes = {reg: data[0] for reg, data in log} + result = writes.get(0x10) == 0 and writes.get(0x11) == 0 + expect_true: true + mode: [mock] + + # ----- Reset ----- + + - name: "Reset sets BDU and IF_INC in CTRL3_C" + action: script + script: | + i2c.clear_write_log() + dev.reset() + log = i2c.get_write_log() + ctrl3_writes = [data[0] for reg, data in log if reg == 0x12] + bdu_if_inc = (1 << 6) | (1 << 2) + result = len(ctrl3_writes) == 2 and ctrl3_writes[1] == bdu_if_inc + expect_true: true + mode: [mock] + + # ----- Hardware manual ----- + + - name: "Sensor readings feel correct" + action: manual + display: + - method: acceleration_g + label: "Acceleration (g)" + unit: "" + - method: gyroscope_dps + label: "Gyroscope (dps)" + unit: "" + - method: temperature_c + label: "Temperature" + unit: "°C" + - method: orientation + label: "Orientation" + unit: "" + prompt: "Les valeurs d'accélération, rotation, température et orientation sont-elles cohérentes ?" + expect_true: true + mode: [hardware]