Compare commits
11 commits
c63c29fe76
...
f4ad9fb4cd
Author | SHA1 | Date | |
---|---|---|---|
Peter Cai | f4ad9fb4cd | ||
Peter Cai | bb8f651481 | ||
244230bad9 | |||
Peter Cai | 5a6dd6124b | ||
af59556a65 | |||
a527624cfa | |||
4d4c83e851 | |||
0cca03a94b | |||
a72285eea3 | |||
fd5d6cbbe8 | |||
9a1964ce41 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,3 +11,4 @@ attic/
|
||||||
wasp/boards/*/watch.py
|
wasp/boards/*/watch.py
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
env/
|
||||||
|
|
|
@ -261,3 +261,7 @@ application (and the "blank" white screen is a torch application):
|
||||||
.. image:: res/LevelApp.png
|
.. image:: res/LevelApp.png
|
||||||
:alt: Shows a time as words in the wasp-os simulator
|
:alt: Shows a time as words in the wasp-os simulator
|
||||||
:width: 179
|
:width: 179
|
||||||
|
|
||||||
|
.. image:: res/BeaconApp.png
|
||||||
|
:alt: Flash the relatively powerful HRS LED repeatedly
|
||||||
|
:width: 179
|
104
apps/Beacon.py
Normal file
104
apps/Beacon.py
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
# Copyright (C) 2021 Francesco Gazzetta
|
||||||
|
"""Beacon application
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Flash the relatively powerful HRS LED repeatedly, mostly for signaling purposes.
|
||||||
|
|
||||||
|
Frequency and intensity can be changed.
|
||||||
|
|
||||||
|
The blinking is handled by the HRS, so this app consumes very little power.
|
||||||
|
With BLE and/or step counter disabled and blinking frequency set to the minimum,
|
||||||
|
the watch's battery will last for many days.
|
||||||
|
|
||||||
|
.. figure:: res/BeaconApp.png
|
||||||
|
:width: 179
|
||||||
|
"""
|
||||||
|
|
||||||
|
import wasp
|
||||||
|
import machine
|
||||||
|
from micropython import const
|
||||||
|
|
||||||
|
class BeaconApp():
|
||||||
|
NAME = "Beacon"
|
||||||
|
# 2-bit RLE, 96x64, generated from res/beacon_icon.png, 336 bytes
|
||||||
|
ICON = (
|
||||||
|
b'\x02'
|
||||||
|
b'`@'
|
||||||
|
b'?\xff\x11@\xfcB?\x1dB\x80z\x82B?\x1aA'
|
||||||
|
b'\x86A?\x18A\x88A?\t\xc0\x18\xc3\nA\x8aA'
|
||||||
|
b'\n\xc3=\xc5\x04N\x04\xc5?\x06\xc3\x02A\xcaA\x02'
|
||||||
|
b'\xc3?\x10A\xcaA?\x08\xcb\x02A\xc3@\x1eD\xc3'
|
||||||
|
b'\x80\xfc\x81\x02\xcc9\xcb\x01\x81\xc4D\xc4\x81\x01\xcc?'
|
||||||
|
b'\x06\x81\xcc\x81?\x0f\xc3\x01\x81\xcc\x81\x01\xc3?\x06\xc5'
|
||||||
|
b'\x04\x8e\x04\xc5=\xc3\t\x81\xc0z\xcc\x81\t@\x18C'
|
||||||
|
b'?\x07\x81\xcc\x81?\x12\x81\xce\x81?\x11\x81\xce\x81?'
|
||||||
|
b'\x11\x81\xce\x81?\x11\x81\xce\x81?\x11\x81\xce\x81?\x11'
|
||||||
|
b'\x81\xce\x81?\x11\x81\xce\x81?\x10\x81\xd0\x81?\x0f\x81'
|
||||||
|
b'\xd0\x81?\x0f\x92?\x0f\x81\x80\x81\x90\xc0\xfc\xc1?\x0f'
|
||||||
|
b'\xc1\x90\xc1?\x0f\xc1\x90\xc1?\x0e\xc1\x92\xc1?\r\xc1'
|
||||||
|
b'\x92\xc1?\r\xc1\x92\xc1?\r\xc1\x92\xc1?\r\xc1\x92'
|
||||||
|
b'\xc1?\r\xc1\x92\xc1?\r\xc1\x92\xc1?\x0c\xc1\x94\xc1'
|
||||||
|
b'?\x0b\xd6?\x0b\xc1@zT\xc1?\x0b\xc1T\xc1?'
|
||||||
|
b'\x0b\xc1T\xc1?\x0b\xc1T\xc1?\n\xc1V\xc1?\t'
|
||||||
|
b'\xc1V\xc1?\t\xc1V\xc1?\t\xc1V\xc1?\t\xc1'
|
||||||
|
b'V\xc1?\t\xc1V\xc1?\t\xc1V\xc1?\x08\xda?'
|
||||||
|
b'\x07\xc1\x98\xc1?\x07\xc1\x98\xc1?\x07\xc1\x98\xc1?\x07'
|
||||||
|
b'\xc1\x98\xc1?\x07\xc1\x98\xc1?\x06\xc1\x9a\xc1?\x05\xc1'
|
||||||
|
b'\x9a\xc1?\x05\xc1\x9a\xc1?\x05\xdc?\xff\x04'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._checkbox = wasp.widgets.Checkbox(10, 45, "Enable beacon")
|
||||||
|
self._slider_current = wasp.widgets.Slider(4, 10, 110, 0x27e4)
|
||||||
|
self._slider_wait_time = wasp.widgets.Slider(8, 10, 180)
|
||||||
|
|
||||||
|
def foreground(self):
|
||||||
|
wasp.system.bar.clock = True
|
||||||
|
self._draw()
|
||||||
|
wasp.system.request_event(wasp.EventMask.TOUCH)
|
||||||
|
|
||||||
|
def _draw(self):
|
||||||
|
draw = wasp.watch.drawable
|
||||||
|
draw.fill()
|
||||||
|
wasp.system.bar.draw()
|
||||||
|
self._checkbox.draw()
|
||||||
|
draw.string("Intensity:", 10, 85)
|
||||||
|
self._slider_current.draw()
|
||||||
|
draw.string("Frequency:", 10, 155)
|
||||||
|
self._slider_wait_time.draw()
|
||||||
|
self._draw_preview()
|
||||||
|
|
||||||
|
def touch(self, event):
|
||||||
|
if self._checkbox.touch(event):
|
||||||
|
if self._checkbox.state:
|
||||||
|
wasp.watch.hrs.enable()
|
||||||
|
wasp.watch.hrs.set_hwt(self._slider_wait_time.value)
|
||||||
|
wasp.watch.hrs.set_drive(self._slider_current.value)
|
||||||
|
else:
|
||||||
|
wasp.watch.hrs.disable()
|
||||||
|
self._checkbox.update()
|
||||||
|
elif event[2] >= 180:
|
||||||
|
if self._slider_wait_time.touch(event):
|
||||||
|
wasp.watch.hrs.set_hwt(self._slider_wait_time.value)
|
||||||
|
self._slider_wait_time.update()
|
||||||
|
self._draw_preview()
|
||||||
|
elif event[2] >= 110:
|
||||||
|
if self._slider_current.touch(event):
|
||||||
|
wasp.watch.hrs.set_drive(self._slider_current.value)
|
||||||
|
self._slider_current.update()
|
||||||
|
self._draw_preview()
|
||||||
|
wasp.system.bar.update()
|
||||||
|
|
||||||
|
def _draw_preview(self):
|
||||||
|
"""
|
||||||
|
Draw a dashed line representing intensity and frequency
|
||||||
|
with thickness and separation of dashes
|
||||||
|
"""
|
||||||
|
draw = wasp.watch.drawable
|
||||||
|
draw.fill(None, 10, 220, 227, 20)
|
||||||
|
x = 10
|
||||||
|
while x < 220:
|
||||||
|
wasp.watch.drawable.fill(0x27e4, x, 227, 8, (self._slider_current.value + 1) * 3)
|
||||||
|
x += (8 - self._slider_wait_time.value) * 8
|
|
@ -45,6 +45,8 @@ Applications
|
||||||
|
|
||||||
.. automodule:: apps.alarm
|
.. automodule:: apps.alarm
|
||||||
|
|
||||||
|
.. automodule:: Beacon
|
||||||
|
|
||||||
.. automodule:: apps.calc
|
.. automodule:: apps.calc
|
||||||
|
|
||||||
.. automodule:: apps.demo
|
.. automodule:: apps.demo
|
||||||
|
|
BIN
res/BeaconApp.png
Normal file
BIN
res/BeaconApp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
BIN
res/beacon_icon.png
Normal file
BIN
res/beacon_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 568 B |
|
@ -39,7 +39,8 @@ class SettingsApp():
|
||||||
self._yy = wasp.widgets.Spinner(160, 60, 20, 60, 2)
|
self._yy = wasp.widgets.Spinner(160, 60, 20, 60, 2)
|
||||||
self._units = ['Metric', 'Imperial']
|
self._units = ['Metric', 'Imperial']
|
||||||
self._units_toggle = wasp.widgets.Button(32, 90, 176, 48, "Change")
|
self._units_toggle = wasp.widgets.Button(32, 90, 176, 48, "Change")
|
||||||
self._settings = ['Brightness', 'Notification Level', 'Time', 'Date', 'Units']
|
self._raise_to_wake_toggle = wasp.widgets.Button(32, 90, 176, 48, "On/Off")
|
||||||
|
self._settings = ['Brightness', 'Notification Level', 'Time', 'Date', 'Units', 'Raise To Wake']
|
||||||
self._sett_index = 0
|
self._sett_index = 0
|
||||||
self._current_setting = self._settings[0]
|
self._current_setting = self._settings[0]
|
||||||
|
|
||||||
|
@ -73,6 +74,9 @@ class SettingsApp():
|
||||||
elif self._current_setting == 'Units':
|
elif self._current_setting == 'Units':
|
||||||
if self._units_toggle.touch(event):
|
if self._units_toggle.touch(event):
|
||||||
wasp.system.units = self._units[(self._units.index(wasp.system.units) + 1) % len(self._units)]
|
wasp.system.units = self._units[(self._units.index(wasp.system.units) + 1) % len(self._units)]
|
||||||
|
elif self._current_setting == "Raise To Wake":
|
||||||
|
if self._raise_to_wake_toggle.touch(event):
|
||||||
|
wasp.system.raise_wake = not wasp.system.raise_wake
|
||||||
self._update()
|
self._update()
|
||||||
|
|
||||||
def swipe(self, event):
|
def swipe(self, event):
|
||||||
|
@ -122,6 +126,8 @@ class SettingsApp():
|
||||||
draw.string('DD MM YY',0,180, width=240)
|
draw.string('DD MM YY',0,180, width=240)
|
||||||
elif self._current_setting == 'Units':
|
elif self._current_setting == 'Units':
|
||||||
self._units_toggle.draw()
|
self._units_toggle.draw()
|
||||||
|
elif self._current_setting == 'Raise To Wake':
|
||||||
|
self._raise_to_wake_toggle.draw()
|
||||||
self._scroll_indicator.draw()
|
self._scroll_indicator.draw()
|
||||||
self._update()
|
self._update()
|
||||||
mute(False)
|
mute(False)
|
||||||
|
@ -149,3 +155,9 @@ class SettingsApp():
|
||||||
draw.string(say, 0, 150, width=240)
|
draw.string(say, 0, 150, width=240)
|
||||||
elif self._current_setting == 'Units':
|
elif self._current_setting == 'Units':
|
||||||
draw.string(wasp.system.units, 0, 150, width=240)
|
draw.string(wasp.system.units, 0, 150, width=240)
|
||||||
|
elif self._current_setting == 'Raise To Wake':
|
||||||
|
if wasp.system.raise_wake:
|
||||||
|
say = "On"
|
||||||
|
else:
|
||||||
|
say = "Off"
|
||||||
|
draw.string(say, 0, 150, width=240)
|
||||||
|
|
|
@ -159,6 +159,12 @@ class HRS():
|
||||||
self._i = 0
|
self._i = 0
|
||||||
self._step = 1
|
self._step = 1
|
||||||
|
|
||||||
|
def read_reg(self, addr):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def write_reg(self, addr, val):
|
||||||
|
pass
|
||||||
|
|
||||||
def enable(self):
|
def enable(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -178,6 +184,12 @@ class HRS():
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
def set_drive(self, drive):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_hwt(self, t):
|
||||||
|
pass
|
||||||
|
|
||||||
backlight = Backlight()
|
backlight = Backlight()
|
||||||
spi = SPI(0)
|
spi = SPI(0)
|
||||||
spi.init(polarity=1, phase=1, baudrate=8000000)
|
spi.init(polarity=1, phase=1, baudrate=8000000)
|
||||||
|
|
|
@ -13,10 +13,13 @@ _I2CADDR = const(0x44)
|
||||||
_ID = const(0x00)
|
_ID = const(0x00)
|
||||||
_ENABLE = const(0x01)
|
_ENABLE = const(0x01)
|
||||||
_ENABLE_HEN = const(0x80)
|
_ENABLE_HEN = const(0x80)
|
||||||
|
_ENABLE_HWT = const(0x70)
|
||||||
|
_ENABLE_PDRIVE1 = const(0x08)
|
||||||
_C1DATAM = const(0x08)
|
_C1DATAM = const(0x08)
|
||||||
_C0DATAM = const(0x09)
|
_C0DATAM = const(0x09)
|
||||||
_C0DATAH = const(0x0a)
|
_C0DATAH = const(0x0a)
|
||||||
_PDRIVER = const(0x0c)
|
_PDRIVER = const(0x0c)
|
||||||
|
_PDRIVER_PDRIVE0 = const(0x40)
|
||||||
_C1DATAH = const(0x0d)
|
_C1DATAH = const(0x0d)
|
||||||
_C1DATAL = const(0x0e)
|
_C1DATAL = const(0x0e)
|
||||||
_C0DATAL = const(0x0f)
|
_C0DATAL = const(0x0f)
|
||||||
|
@ -88,11 +91,40 @@ class HRS3300:
|
||||||
self.write_reg(_HGAIN, hgain << 2)
|
self.write_reg(_HGAIN, hgain << 2)
|
||||||
|
|
||||||
def set_drive(self, drive):
|
def set_drive(self, drive):
|
||||||
|
"""
|
||||||
|
Set LED drive current
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
drive (int) LED drive current
|
||||||
|
0 = 12.5 mA
|
||||||
|
1 = 20 mA
|
||||||
|
2 = 30 mA
|
||||||
|
3 = 40 mA
|
||||||
|
"""
|
||||||
en = self.read_reg(_ENABLE)
|
en = self.read_reg(_ENABLE)
|
||||||
pd = self.read_reg(_PDRIVER)
|
pd = self.read_reg(_PDRIVER)
|
||||||
|
|
||||||
en = (en & 0xf7) | ((drive & 2) << 2)
|
en = (en & ~_ENABLE_PDRIVE1 ) | ((drive & 2) << 2)
|
||||||
pd = (pd & 0xbf) | ((drive & 1) << 6)
|
pd = (pd & ~_PDRIVER_PDRIVE0) | ((drive & 1) << 6)
|
||||||
|
|
||||||
self.write_reg(_ENABLE, en)
|
self.write_reg(_ENABLE, en)
|
||||||
self.write_reg(_PDRIVER, pd)
|
self.write_reg(_PDRIVER, pd)
|
||||||
|
|
||||||
|
def set_hwt(self, t):
|
||||||
|
"""
|
||||||
|
Set wait time between each conversion cycle
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
t (int) Wait time between each conversion cycle
|
||||||
|
0 = 800 ms
|
||||||
|
1 = 400 ms
|
||||||
|
2 = 200 ms
|
||||||
|
3 = 100 ms
|
||||||
|
4 = 75 ms
|
||||||
|
5 = 50 ms
|
||||||
|
6 = 12.5 ms
|
||||||
|
7 = 0 ms
|
||||||
|
"""
|
||||||
|
en = self.read_reg(_ENABLE)
|
||||||
|
en = (en & ~_ENABLE_HWT) | (t << 4)
|
||||||
|
self.write_reg(_ENABLE, en)
|
||||||
|
|
44
wasp/wasp.py
44
wasp/wasp.py
|
@ -26,6 +26,12 @@ from apps.launcher import LauncherApp
|
||||||
from apps.pager import PagerApp, CrashApp, NotificationApp
|
from apps.pager import PagerApp, CrashApp, NotificationApp
|
||||||
from apps.steps import StepCounterApp
|
from apps.steps import StepCounterApp
|
||||||
|
|
||||||
|
RAISE_WAKE_Y_SWITCH_THRESHOLD = -1536
|
||||||
|
RAISE_WAKE_SPEED_MODIFIER = 8
|
||||||
|
RAISE_WAKE_X_THRESHOLD = 512
|
||||||
|
RAISE_WAKE_Y_THRESHOLD = 0
|
||||||
|
RAISE_WAKE_REQUIRED_SPEED = 512
|
||||||
|
|
||||||
class EventType():
|
class EventType():
|
||||||
"""Enumerated interface actions.
|
"""Enumerated interface actions.
|
||||||
|
|
||||||
|
@ -114,6 +120,11 @@ class Manager():
|
||||||
self.musicinfo = {}
|
self.musicinfo = {}
|
||||||
self.weatherinfo = {}
|
self.weatherinfo = {}
|
||||||
self.units = "Metric"
|
self.units = "Metric"
|
||||||
|
self.raise_wake = False
|
||||||
|
self.raise_wake_last_y = sys.maxsize
|
||||||
|
self.raise_wake_last_z = sys.maxsize
|
||||||
|
self.accel_poll_ms = 100
|
||||||
|
self.accel_poll_expiry = 0
|
||||||
|
|
||||||
self._theme = (
|
self._theme = (
|
||||||
b'\x7b\xef' # ble
|
b'\x7b\xef' # ble
|
||||||
|
@ -166,7 +177,6 @@ class Manager():
|
||||||
def register_defaults(self):
|
def register_defaults(self):
|
||||||
"""Register the default applications."""
|
"""Register the default applications."""
|
||||||
self.register('apps.clock.ClockApp', True, no_except=True)
|
self.register('apps.clock.ClockApp', True, no_except=True)
|
||||||
self.register('apps.steps.StepCounterApp', True, no_except=True)
|
|
||||||
self.register('apps.stopwatch.StopwatchApp', True, no_except=True)
|
self.register('apps.stopwatch.StopwatchApp', True, no_except=True)
|
||||||
self.register('apps.heart.HeartApp', True, no_except=True)
|
self.register('apps.heart.HeartApp', True, no_except=True)
|
||||||
|
|
||||||
|
@ -493,6 +503,38 @@ class Manager():
|
||||||
self._charging != watch.battery.charging():
|
self._charging != watch.battery.charging():
|
||||||
self.wake()
|
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()
|
||||||
|
|
||||||
|
def _do_raise_wake(self):
|
||||||
|
|
||||||
|
(x, y, z) = watch.accel.accel_xyz()
|
||||||
|
|
||||||
|
y = -y
|
||||||
|
|
||||||
|
if self.raise_wake_last_y == sys.maxsize:
|
||||||
|
self.raise_wake_last_y = y
|
||||||
|
self.raise_wake_last_z = z
|
||||||
|
return False
|
||||||
|
|
||||||
|
delta_y = y - self.raise_wake_last_y
|
||||||
|
delta_z = z - self.raise_wake_last_z
|
||||||
|
|
||||||
|
self.raise_wake_last_y = y
|
||||||
|
self.raise_wake_last_z = z
|
||||||
|
|
||||||
|
if y < RAISE_WAKE_Y_SWITCH_THRESHOLD:
|
||||||
|
return delta_z > RAISE_WAKE_REQUIRED_SPEED
|
||||||
|
|
||||||
|
if z > 0:
|
||||||
|
return delta_y > (RAISE_WAKE_REQUIRED_SPEED + (y - delta_y / 2) / RAISE_WAKE_SPEED_MODIFIER)
|
||||||
|
|
||||||
|
return delta_y < (-RAISE_WAKE_REQUIRED_SPEED - (y - delta_y / 2) / RAISE_WAKE_SPEED_MODIFIER)
|
||||||
|
|
||||||
def run(self, no_except=True):
|
def run(self, no_except=True):
|
||||||
"""Run the system manager synchronously.
|
"""Run the system manager synchronously.
|
||||||
|
|
||||||
|
|
|
@ -430,7 +430,9 @@ class Slider():
|
||||||
v = 0
|
v = 0
|
||||||
elif v >= self._steps:
|
elif v >= self._steps:
|
||||||
v = self._steps - 1
|
v = self._steps - 1
|
||||||
|
changed = self.value != v
|
||||||
self.value = v
|
self.value = v
|
||||||
|
return changed
|
||||||
|
|
||||||
class Spinner():
|
class Spinner():
|
||||||
"""A simple Spinner widget.
|
"""A simple Spinner widget.
|
||||||
|
|
Loading…
Reference in a new issue