From 3cb60d7705358022e2b6941da4d74be91964d1c5 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Fri, 17 Jun 2022 16:27:13 -0400 Subject: [PATCH 1/5] 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. --- wasp/wasp.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wasp/wasp.py b/wasp/wasp.py index fbb2f4f..b81ad22 100644 --- a/wasp/wasp.py +++ b/wasp/wasp.py @@ -157,6 +157,8 @@ class Manager(): def secondary_init(self): global free + watch.accel.reset() + if not self.app: # Register default apps if main hasn't put anything on the quick ring if not self.quick_ring: From c08eb7c350e3a5c762109758f79512a06d996abe Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Fri, 17 Jun 2022 16:44:57 -0400 Subject: [PATCH 2/5] bma42x: Set hardware-based axes remapping --- wasp/drivers/bma421.py | 5 +++++ wasp/modules/bma42x-upy | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/wasp/drivers/bma421.py b/wasp/drivers/bma421.py index 14562d4..4657bc8 100644 --- a/wasp/drivers/bma421.py +++ b/wasp/drivers/bma421.py @@ -55,6 +55,11 @@ class BMA421: perf_mode=bma42x.CIC_AVG_MODE) 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) + @property def steps(self): """Report the number of steps counted.""" diff --git a/wasp/modules/bma42x-upy b/wasp/modules/bma42x-upy index b968b25..85622fc 160000 --- a/wasp/modules/bma42x-upy +++ b/wasp/modules/bma42x-upy @@ -1 +1 @@ -Subproject commit b968b2530440afae8b555613e89b1240afcf4da2 +Subproject commit 85622fc37610ebb3821f0b315c37c69a4bffa72d From 6332f11502af6f43a81474a1aacf18eb30161e2c Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 18 Jun 2022 10:40:42 -0400 Subject: [PATCH 3/5] Initial implementation of interrupt-based wrist tilt detection on BMA425 --- wasp/boards/pinetime/manifest.py | 1 + wasp/boards/pinetime/watch.py.in | 2 +- wasp/drivers/bma421.py | 32 +++++++++++++++++++++++++++++++- wasp/modules/bma42x-upy | 2 +- wasp/motion.py | 7 +++++++ wasp/wasp.py | 14 +++++++++----- 6 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 wasp/motion.py diff --git a/wasp/boards/pinetime/manifest.py b/wasp/boards/pinetime/manifest.py index 9243d77..cb2d788 100644 --- a/wasp/boards/pinetime/manifest.py +++ b/wasp/boards/pinetime/manifest.py @@ -22,6 +22,7 @@ freeze('../..', manifest_240x240.manifest + 'gadgetbridge.py', 'ppg.py', 'shell.py', + 'motion.py', 'wasp.py', ), opt=3 diff --git a/wasp/boards/pinetime/watch.py.in b/wasp/boards/pinetime/watch.py.in index 91c59c3..8bd4a85 100644 --- a/wasp/boards/pinetime/watch.py.in +++ b/wasp/boards/pinetime/watch.py.in @@ -90,7 +90,7 @@ try: Signal(Pin('CHARGING', Pin.IN), invert=True), Signal(Pin('USB_PWR', Pin.IN), invert=True)) i2c = I2C(1, scl='I2C_SCL', sda='I2C_SDA') - accel = BMA421(i2c) + accel = BMA421(i2c, Pin('ACCEL_INT', Pin.IN)) hrs = HRS3300(i2c) touch = CST816S(i2c, Pin('TP_INT', Pin.IN), Pin('TP_RST', Pin.OUT, value=0), diff --git a/wasp/drivers/bma421.py b/wasp/drivers/bma421.py index 4657bc8..fa40fcc 100644 --- a/wasp/drivers/bma421.py +++ b/wasp/drivers/bma421.py @@ -7,6 +7,8 @@ import bma42x import time +import motion +from machine import Pin # Sensor orientation definition. # The 6 most significant bits define the indexes of the x, y, and z values @@ -28,13 +30,18 @@ class BMA421: .. automethod:: __init__ """ - def __init__(self, i2c, orientation=_DEFAULT_ORIENTATION): + def __init__(self, i2c, intr=None, orientation=_DEFAULT_ORIENTATION): """Configure the driver. :param machine.I2C i2c: I2C bus used to access the sensor. """ self._dev = bma42x.BMA42X(i2c) self._orientation = orientation + self._gesture_int = intr + self._gesture_event = motion.AccelGestureEvent.NONE + + if self._gesture_int != None: + self._gesture_int.irq(trigger=Pin.IRQ_FALLING, handler=self.handle_interrupt) def reset(self): """Reset and reinitialize the sensor.""" @@ -60,6 +67,29 @@ class BMA421: # Software readout is remapped manually in accel_xyz(). dev.set_remap_axes(self._orientation) + # Enable gesture interrupts + 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.feature_enable(bma42x.DOUBLE_TAP, 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 def steps(self): """Report the number of steps counted.""" diff --git a/wasp/modules/bma42x-upy b/wasp/modules/bma42x-upy index 85622fc..905652f 160000 --- a/wasp/modules/bma42x-upy +++ b/wasp/modules/bma42x-upy @@ -1 +1 @@ -Subproject commit 85622fc37610ebb3821f0b315c37c69a4bffa72d +Subproject commit 905652f9fd6541283591cf6e085c31f9aa63bf40 diff --git a/wasp/motion.py b/wasp/motion.py new file mode 100644 index 0000000..f8cb0ee --- /dev/null +++ b/wasp/motion.py @@ -0,0 +1,7 @@ +from micropython import const + +class AccelGestureEvent(): + """Enumerated ids for accelerometer-based gesture events + """ + NONE = const(0) + WRIST_TILT = const(1) diff --git a/wasp/wasp.py b/wasp/wasp.py index b81ad22..2ebf1e6 100644 --- a/wasp/wasp.py +++ b/wasp/wasp.py @@ -20,6 +20,7 @@ import micropython import sys import watch import widgets +import motion from apps.launcher import LauncherApp from apps.pager import PagerApp, CrashApp, NotificationApp @@ -498,11 +499,14 @@ class Manager(): self.wake() if self.raise_wake: - 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() + if watch.accel.get_gesture_event() == motion.AccelGestureEvent.WRIST_TILT: + watch.accel.reset_gesture_event() + self.wake() + #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): From a15cc6697d1e71a005ec30a150b1109bf54a434e Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 18 Jun 2022 10:41:42 -0400 Subject: [PATCH 4/5] 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 --- wasp/drivers/bma421.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasp/drivers/bma421.py b/wasp/drivers/bma421.py index fa40fcc..d69cc56 100644 --- a/wasp/drivers/bma421.py +++ b/wasp/drivers/bma421.py @@ -19,7 +19,7 @@ from machine import Pin # Y index ───────────────┐ │ # X index ─────────────┐ │ │ # ├┐├┐├┐ -_DEFAULT_ORIENTATION = const(0b010010101) +_DEFAULT_ORIENTATION = const(0b010010000) # 1 = keep, 0 = negate │││ # X sign ───────────────────┘││ # Y sign ────────────────────┘│ From c816d1db354085c2cd3d8a1f1bfaebb61f78f8f8 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 18 Jun 2022 11:19:15 -0400 Subject: [PATCH 5/5] Re-introduce software-based gesture fallback The algorithm still has to be adjusted for the new mapping. --- wasp/drivers/bma421.py | 19 +++++++++++-------- wasp/modules/bma42x-upy | 2 +- wasp/wasp.py | 18 ++++++++++-------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/wasp/drivers/bma421.py b/wasp/drivers/bma421.py index d69cc56..6dbfe82 100644 --- a/wasp/drivers/bma421.py +++ b/wasp/drivers/bma421.py @@ -39,6 +39,7 @@ class BMA421: 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) @@ -67,15 +68,17 @@ class BMA421: # 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 - 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.feature_enable(bma42x.DOUBLE_TAP, True) - dev.map_interrupt(bma42x.INTR1_MAP, bma42x.WRIST_WEAR_INT, True) + 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""" diff --git a/wasp/modules/bma42x-upy b/wasp/modules/bma42x-upy index 905652f..30b19fe 160000 --- a/wasp/modules/bma42x-upy +++ b/wasp/modules/bma42x-upy @@ -1 +1 @@ -Subproject commit 905652f9fd6541283591cf6e085c31f9aa63bf40 +Subproject commit 30b19fec7c1d05abd533ce46dcbe2d1a38f56a0c diff --git a/wasp/wasp.py b/wasp/wasp.py index 2ebf1e6..fcebdc7 100644 --- a/wasp/wasp.py +++ b/wasp/wasp.py @@ -499,14 +499,16 @@ class Manager(): self.wake() if self.raise_wake: - if watch.accel.get_gesture_event() == motion.AccelGestureEvent.WRIST_TILT: - watch.accel.reset_gesture_event() - self.wake() - #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() + if watch.accel.hardware_gesture_available: + if watch.accel.get_gesture_event() == motion.AccelGestureEvent.WRIST_TILT: + watch.accel.reset_gesture_event() + 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):