Compare commits

...

5 commits

Author SHA1 Message Date
c816d1db35 Re-introduce software-based gesture fallback
The algorithm still has to be adjusted for the new mapping.
2022-06-18 11:19:15 -04:00
a15cc6697d bma421: Flip all axes
This is what the hardware requires for gesture detection to work (all
clockwise directions should be positive, and counterclockwise directions
are negative).

TODO: Correct all software logic to match
2022-06-18 10:41:42 -04:00
6332f11502 Initial implementation of interrupt-based wrist tilt detection on BMA425 2022-06-18 10:41:33 -04:00
c08eb7c350 bma42x: Set hardware-based axes remapping 2022-06-17 16:44:57 -04:00
3cb60d7705 Initialize accelerometer during system init
It was originally only initialized in the step counter, but this won't
work when the step counter app is disabled.
2022-06-17 16:27:13 -04:00
6 changed files with 62 additions and 8 deletions

View file

@ -22,6 +22,7 @@ freeze('../..', manifest_240x240.manifest +
'gadgetbridge.py', 'gadgetbridge.py',
'ppg.py', 'ppg.py',
'shell.py', 'shell.py',
'motion.py',
'wasp.py', 'wasp.py',
), ),
opt=3 opt=3

View file

@ -90,7 +90,7 @@ try:
Signal(Pin('CHARGING', Pin.IN), invert=True), Signal(Pin('CHARGING', Pin.IN), invert=True),
Signal(Pin('USB_PWR', Pin.IN), invert=True)) Signal(Pin('USB_PWR', Pin.IN), invert=True))
i2c = I2C(1, scl='I2C_SCL', sda='I2C_SDA') i2c = I2C(1, scl='I2C_SCL', sda='I2C_SDA')
accel = BMA421(i2c) accel = BMA421(i2c, Pin('ACCEL_INT', Pin.IN))
hrs = HRS3300(i2c) hrs = HRS3300(i2c)
touch = CST816S(i2c, touch = CST816S(i2c,
Pin('TP_INT', Pin.IN), Pin('TP_RST', Pin.OUT, value=0), Pin('TP_INT', Pin.IN), Pin('TP_RST', Pin.OUT, value=0),

View file

@ -7,6 +7,8 @@
import bma42x import bma42x
import time import time
import motion
from machine import Pin
# Sensor orientation definition. # Sensor orientation definition.
# The 6 most significant bits define the indexes of the x, y, and z values # The 6 most significant bits define the indexes of the x, y, and z values
@ -17,7 +19,7 @@ import time
# Y index ───────────────┐ │ # Y index ───────────────┐ │
# X index ─────────────┐ │ │ # X index ─────────────┐ │ │
# ├┐├┐├┐ # ├┐├┐├┐
_DEFAULT_ORIENTATION = const(0b010010101) _DEFAULT_ORIENTATION = const(0b010010000)
# 1 = keep, 0 = negate │││ # 1 = keep, 0 = negate │││
# X sign ───────────────────┘││ # X sign ───────────────────┘││
# Y sign ────────────────────┘│ # Y sign ────────────────────┘│
@ -28,13 +30,19 @@ class BMA421:
.. automethod:: __init__ .. automethod:: __init__
""" """
def __init__(self, i2c, orientation=_DEFAULT_ORIENTATION): def __init__(self, i2c, intr=None, orientation=_DEFAULT_ORIENTATION):
"""Configure the driver. """Configure the driver.
:param machine.I2C i2c: I2C bus used to access the sensor. :param machine.I2C i2c: I2C bus used to access the sensor.
""" """
self._dev = bma42x.BMA42X(i2c) self._dev = bma42x.BMA42X(i2c)
self._orientation = orientation self._orientation = orientation
self._gesture_int = intr
self._gesture_event = motion.AccelGestureEvent.NONE
self.hardware_gesture_available = False
if self._gesture_int != None:
self._gesture_int.irq(trigger=Pin.IRQ_FALLING, handler=self.handle_interrupt)
def reset(self): def reset(self):
"""Reset and reinitialize the sensor.""" """Reset and reinitialize the sensor."""
@ -55,6 +63,36 @@ class BMA421:
perf_mode=bma42x.CIC_AVG_MODE) perf_mode=bma42x.CIC_AVG_MODE)
dev.feature_enable(bma42x.STEP_CNTR, True) dev.feature_enable(bma42x.STEP_CNTR, True)
# Set axes remapping
# This works only for hardware-based intelligence.
# Software readout is remapped manually in accel_xyz().
dev.set_remap_axes(self._orientation)
self.hardware_gesture_available = dev.get_chip_id() == bma42x.BMA425_CHIP_ID
# Enable gesture interrupts
if self.hardware_gesture_available:
dev.set_int_pin_config(int_line=bma42x.INTR1_MAP,
edge_ctrl=bma42x.LEVEL_TRIGGER,
lvl=bma42x.ACTIVE_LOW,
od=bma42x.PUSH_PULL,
output_en=True, input_en=False)
dev.feature_enable(bma42x.WRIST_WEAR, True)
dev.map_interrupt(bma42x.INTR1_MAP, bma42x.WRIST_WEAR_INT, True)
def handle_interrupt(self, pin_obj):
"""Interrupt handler for gesture events originating from the sensor"""
self._dev.read_int_status() # TODO: Actually read status from the register
self._gesture_event = motion.AccelGestureEvent.WRIST_TILT
def get_gesture_event(self):
"""Receive the latest gesture event if any"""
return self._gesture_event
def reset_gesture_event(self):
"""Call after processing the gesture event"""
self._gesture_event = motion.AccelGestureEvent.NONE
@property @property
def steps(self): def steps(self):
"""Report the number of steps counted.""" """Report the number of steps counted."""

@ -1 +1 @@
Subproject commit b968b2530440afae8b555613e89b1240afcf4da2 Subproject commit 30b19fec7c1d05abd533ce46dcbe2d1a38f56a0c

7
wasp/motion.py Normal file
View file

@ -0,0 +1,7 @@
from micropython import const
class AccelGestureEvent():
"""Enumerated ids for accelerometer-based gesture events
"""
NONE = const(0)
WRIST_TILT = const(1)

View file

@ -20,6 +20,7 @@ import micropython
import sys import sys
import watch import watch
import widgets import widgets
import motion
from apps.launcher import LauncherApp from apps.launcher import LauncherApp
from apps.pager import PagerApp, CrashApp, NotificationApp from apps.pager import PagerApp, CrashApp, NotificationApp
@ -157,6 +158,8 @@ class Manager():
def secondary_init(self): def secondary_init(self):
global free global free
watch.accel.reset()
if not self.app: if not self.app:
# Register default apps if main hasn't put anything on the quick ring # Register default apps if main hasn't put anything on the quick ring
if not self.quick_ring: if not self.quick_ring:
@ -496,11 +499,16 @@ class Manager():
self.wake() self.wake()
if self.raise_wake: if self.raise_wake:
now = rtc.get_uptime_ms() if watch.accel.hardware_gesture_available:
if now >= self.accel_poll_expiry: if watch.accel.get_gesture_event() == motion.AccelGestureEvent.WRIST_TILT:
self.accel_poll_expiry = (now + self.accel_poll_ms) watch.accel.reset_gesture_event()
if self._do_raise_wake():
self.wake() self.wake()
else:
now = rtc.get_uptime_ms()
if now >= self.accel_poll_expiry:
self.accel_poll_expiry = (now + self.accel_poll_ms)
if self._do_raise_wake():
self.wake()
def _do_raise_wake(self): def _do_raise_wake(self):