Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.vscode/
build/
Build/
examples/.DS_Store
.DS_Store
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Each module in the library is designed to be lightweight, readable, and compatib
### Sensors
- [AD8495](Sensors/AD8495/)
- [APDS9960](Sensors/APDS9960/)
- [BHI385](Sensors/BHI385/)
- [BME280](Sensors/BME280/)
- [BME680](Sensors/BME680/)
- [BME688](Sensors/BME688/)
Expand Down
60 changes: 60 additions & 0 deletions Sensors/BHI385/BHI385/Examples/bhi385-doubleTap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# FILE: bhi385-doubleTap.py
# AUTHOR: Josip Šimun Kuči @ Soldered
# BRIEF: Detect double taps using the Bosch BHI385 Smart IMU multi-tap virtual
# sensor (sensor ID 153). Tap the board/sensor twice in quick succession
# to trigger a detection. The firmware's double-tap algorithm requires
# two sharp acceleration impulses within a short time window.
# WORKS WITH: Bosch BHI385 Smart IMU breakout: www.solde.red/333232
# LAST UPDATED: 2026-04-15

from machine import Pin, I2C
from bhi385 import BHI385, BHI385_I2C_ADDR_HIGH, BHI385_TAP_DOUBLE
import time

SCL_PIN = 22
SDA_PIN = 21

i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=400000)
imu = BHI385(i2c, BHI385_I2C_ADDR_HIGH)

print("BHI385 Double Tap Detection example")

if not imu.begin():
print("ERROR: BHI385 not found! Check wiring.")
raise SystemExit

imu.enableDebug()

print("Loading firmware...")
with open("bhi385_firmware.bin", "rb") as f:
firmware = f.read()

if not imu.loadFirmware(firmware):
print("Firmware load failed.")
raise SystemExit
print("Firmware loaded successfully.")

# Enable multi-tap sensor configured for double-tap-only detection at 100 Hz
if not imu.enableMultiTapDetect(BHI385_TAP_DOUBLE, 100.0):
print("ERROR: Failed to enable multi-tap sensor.")
raise SystemExit
print("Double tap detection enabled.")

imu.disableDebug()

print()
print("Tap the sensor twice quickly to detect a double tap.")
print("Count Type")

tap_count = 0

# Poll without delay — tap events are instantaneous and the FIFO should be
# drained as quickly as possible after the interrupt fires.
while True:
imu.update()

if imu.tapUpdated() and imu.getTapType() == BHI385_TAP_DOUBLE:
tap_count += 1
print("{} Double tap".format(tap_count))

imu.clearUpdatedFlags()
78 changes: 78 additions & 0 deletions Sensors/BHI385/BHI385/Examples/bhi385-doubleTapInterrupt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# FILE: bhi385-doubleTapInterrupt.py
# AUTHOR: Josip Šimun Kuči @ Soldered
# BRIEF: Detect double taps using the BHI385 multi-tap sensor driven by a
# hardware interrupt for immediate response. The tap sensor is a wake-up
# sensor — its events appear in the wake-up FIFO and assert the INT pin.
# Note: double-tap detection requires the firmware to observe two impulses
# within a short window, so the interrupt fires slightly later than for a
# single tap — this is expected behaviour.
# WORKS WITH: Bosch BHI385 Smart IMU breakout: www.solde.red/333232
# LAST UPDATED: 2026-04-15

from machine import Pin, I2C
from bhi385 import BHI385, BHI385_I2C_ADDR_HIGH, BHI385_TAP_DOUBLE
import time

# GPIO connected to the BHI385 INT pin — change as needed
INT_PIN = 4

SCL_PIN = 22
SDA_PIN = 21

i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=400000)
imu = BHI385(i2c, BHI385_I2C_ADDR_HIGH)

print("BHI385 Double Tap (interrupt mode)")

if not imu.begin():
print("ERROR: BHI385 not found! Check wiring.")
raise SystemExit

imu.enableDebug()

print("Loading firmware...")
with open("bhi385_firmware.bin", "rb") as f:
firmware = f.read()

if not imu.loadFirmware(firmware):
print("Firmware load failed.")
raise SystemExit
print("Firmware loaded successfully.")

if not imu.enableMultiTapDetect(BHI385_TAP_DOUBLE, 100.0):
print("ERROR: Failed to enable multi-tap sensor.")
raise SystemExit
print("Double tap detection enabled.")

imu.disableDebug()

# Attach interrupt AFTER firmware is loaded and sensors are configured
int_fired = False


def on_bhi385_int(pin):
global int_fired
int_fired = True


int_pin = Pin(INT_PIN, Pin.IN)
int_pin.irq(trigger=Pin.IRQ_RISING, handler=on_bhi385_int)

print()
print("Tap the sensor twice quickly to trigger a double tap.")
print("Count Type")

tap_count = 0

while True:
if not int_fired:
continue

int_fired = False
imu.update()

if imu.tapUpdated() and imu.getTapType() == BHI385_TAP_DOUBLE:
tap_count += 1
print("{} Double tap".format(tap_count))

imu.clearUpdatedFlags()
61 changes: 61 additions & 0 deletions Sensors/BHI385/BHI385/Examples/bhi385-gameRotationVector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# FILE: bhi385-gameRotationVector.py
# AUTHOR: Josip Šimun Kuči @ Soldered
# BRIEF: Read a normalized quaternion from the Bosch BHI385 Smart IMU using the
# Game Rotation Vector virtual sensor (sensor ID 37).
# The Game Rotation Vector fuses accelerometer and gyroscope data to
# produce a quaternion (x, y, z, w). It does NOT use a magnetometer, so
# yaw is relative to the device orientation at power-on. Pitch and roll
# are absolute (gravity-referenced).
# WORKS WITH: Bosch BHI385 Smart IMU breakout: www.solde.red/333232
# LAST UPDATED: 2026-04-15

from machine import Pin, I2C
from bhi385 import BHI385, BHI385_I2C_ADDR_HIGH
import time

SCL_PIN = 22
SDA_PIN = 21

i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=400000)
imu = BHI385(i2c, BHI385_I2C_ADDR_HIGH)

print("BHI385 Game Rotation Vector example")

if not imu.begin():
print("ERROR: BHI385 not found! Check wiring.")
raise SystemExit

imu.enableDebug()

print("Loading firmware...")
with open("bhi385_firmware.bin", "rb") as f:
firmware = f.read()

if not imu.loadFirmware(firmware):
print("Firmware load failed.")
raise SystemExit
print("Firmware loaded successfully.")

# Enable Game Rotation Vector at 100 Hz
if not imu.enableGameRotationVector(100.0):
print("ERROR: Failed to enable Game Rotation Vector.")
raise SystemExit
print("Game Rotation Vector enabled: 100 Hz")

imu.disableDebug()

print()
print("Quat X Quat Y Quat Z Quat W")

while True:
imu.update()

if imu.quatUpdated():
print(
"{:.4f} {:.4f} {:.4f} {:.4f}".format(
imu.getQuatX(), imu.getQuatY(), imu.getQuatZ(), imu.getQuatW()
)
)

imu.clearUpdatedFlags()
time.sleep_ms(20)
86 changes: 86 additions & 0 deletions Sensors/BHI385/BHI385/Examples/bhi385-readAccelGyro.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# FILE: bhi385-readAccelGyro.py
# AUTHOR: Josip Šimun Kuči @ Soldered
# BRIEF: Read accelerometer and gyroscope data from the Bosch BHI385 Smart IMU
# WORKS WITH: Bosch BHI385 Smart IMU breakout: www.solde.red/333232
# LAST UPDATED: 2026-04-15

from machine import Pin, I2C
from bhi385 import BHI385, BHI385_I2C_ADDR_HIGH, BHI385_ACCEL_8G, BHI385_GYRO_2000DPS
import time

# Initialize I2C at 400 kHz for faster firmware upload (~1.3 s vs ~5 s)
SCL_PIN = 22
SDA_PIN = 21

i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=400000)

# Initialize sensor. Use BHI385_I2C_ADDR_HIGH (0x29) if HSDO is tied to VDDIO.
imu = BHI385(i2c, BHI385_I2C_ADDR_HIGH)

print("BHI385 Accelerometer and Gyroscope example")

if not imu.begin():
print("ERROR: BHI385 not found! Check wiring, I2C address,.")
print(" Chip ID read: 0x{:02X}".format(imu.getChipId()))
print(" Expected: 0x7C")
raise SystemExit

print("BHI385 found. Boot status: 0x{:02X}".format(imu.getBootStatus()))

# Enable verbose debug output so every step of firmware loading is printed
imu.enableDebug()

# Load firmware from a binary file. Obtain the firmware from Bosch Sensortec
# and place it on the filesystem as "bhi385_firmware.bin".
print("Loading firmware...")
with open("bhi385_firmware.bin", "rb") as f:
firmware = f.read()

if not imu.loadFirmware(firmware):
print("Firmware load failed.")
raise SystemExit

print("Firmware loaded successfully.")

# Enable accelerometer: 100 Hz, +-8 g
if not imu.enableAccelerometer(100.0, BHI385_ACCEL_8G):
print("ERROR: Failed to enable accelerometer.")
raise SystemExit
print("Accelerometer enabled: 100 Hz, +-8 g")

# Enable gyroscope: 100 Hz, +-2000 deg/s
if not imu.enableGyroscope(100.0, BHI385_GYRO_2000DPS):
print("ERROR: Failed to enable gyroscope.")
raise SystemExit
print("Gyroscope enabled: 100 Hz, +-2000 deg/s")

imu.disableDebug()

print()
print("AccelX(g) AccelY(g) AccelZ(g) GyroX(dps) GyroY(dps) GyroZ(dps)")

# Main loop — poll at ~50 Hz. At 100 Hz sensor rate this typically returns
# 2 samples per call.
while True:
imu.update()

accel_str = ""
if imu.accelUpdated():
accel_str = "{:.4f} {:.4f} {:.4f}".format(
imu.getAccelX(), imu.getAccelY(), imu.getAccelZ()
)
else:
accel_str = "N/A N/A N/A"

gyro_str = ""
if imu.gyroUpdated():
gyro_str = "{:.4f} {:.4f} {:.4f}".format(
imu.getGyroX(), imu.getGyroY(), imu.getGyroZ()
)
else:
gyro_str = "N/A N/A N/A"

print("{} {}".format(accel_str, gyro_str))

imu.clearUpdatedFlags()
time.sleep_ms(20)
Loading