k9: Add support for Senbono K9
The K9 is similar to the PineTime and P8 devices but does not appear to use the CST[78]16 touch screen controllers. At present the protocol is not known (readfrom yields all zeros, readfrom_mem provokes an exception) so we have a hugely limited interface consisting of the side button and the touchscreen interrupts (in other words we can treat the touchscreen like a second button). Works suprisingly well considering... Signed-off-by: Daniel Thompson <daniel@redfelineninja.org.uk>
This commit is contained in:
parent
5c30b2e0f0
commit
2d1942f76a
6 changed files with 271 additions and 3 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit 89ba9a874fa052ae7b0df8a9b6a6f8916309cd29
|
Subproject commit 91fad65a8a0c5b3be0468e914a5788a519591c52
|
|
@ -1 +1 @@
|
||||||
Subproject commit c7cf47a3681ab1625b52b7b8663bfa95dd5d9096
|
Subproject commit 6df2db3f4cbe046eac17d7893d66a6c077228402
|
2
reloader
2
reloader
|
@ -1 +1 @@
|
||||||
Subproject commit b4512e5d92d062a65a216d68bffcdc03ff1625cf
|
Subproject commit 9f55c66be8f129fd00e3eda60988b4cbeffd8840
|
45
wasp/boards/k9/manifest.py
Normal file
45
wasp/boards/k9/manifest.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
# Copyright (C) 2020 Daniel Thompson
|
||||||
|
|
||||||
|
freeze('.', 'watch.py', opt=3)
|
||||||
|
freeze('../..',
|
||||||
|
(
|
||||||
|
'apps/clock.py',
|
||||||
|
'apps/flashlight.py',
|
||||||
|
'apps/heart.py',
|
||||||
|
'apps/launcher.py',
|
||||||
|
'apps/pager.py',
|
||||||
|
'apps/settings.py',
|
||||||
|
'apps/steps.py',
|
||||||
|
'apps/stopwatch.py',
|
||||||
|
'apps/testapp.py',
|
||||||
|
'boot.py',
|
||||||
|
'draw565.py',
|
||||||
|
'drivers/bma421.py',
|
||||||
|
'drivers/battery.py',
|
||||||
|
'drivers/hrs3300.py',
|
||||||
|
'drivers/nrf_rtc.py',
|
||||||
|
'drivers/signal.py',
|
||||||
|
'drivers/st7789.py',
|
||||||
|
'drivers/touch.py',
|
||||||
|
'drivers/vibrator.py',
|
||||||
|
'fonts/__init__.py',
|
||||||
|
'fonts/clock.py',
|
||||||
|
'fonts/sans24.py',
|
||||||
|
'fonts/sans28.py',
|
||||||
|
'fonts/sans36.py',
|
||||||
|
'gadgetbridge.py',
|
||||||
|
'icons.py',
|
||||||
|
'ppg.py',
|
||||||
|
'shell.py',
|
||||||
|
'wasp.py',
|
||||||
|
'widgets.py',
|
||||||
|
),
|
||||||
|
opt=3
|
||||||
|
)
|
||||||
|
freeze('../../drivers/flash',
|
||||||
|
(
|
||||||
|
'bdevice.py',
|
||||||
|
'flash/flash_spi.py'
|
||||||
|
), opt=3
|
||||||
|
)
|
139
wasp/boards/k9/watch.py.in
Normal file
139
wasp/boards/k9/watch.py.in
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
# Copyright (C) 2020 Daniel Thompson
|
||||||
|
|
||||||
|
def nop():
|
||||||
|
pass
|
||||||
|
schedule = nop
|
||||||
|
def _callback(obj):
|
||||||
|
schedule()
|
||||||
|
|
||||||
|
# Start measuring time (and feeding the watchdog) before *anything* else
|
||||||
|
from machine import RTCounter
|
||||||
|
from drivers.nrf_rtc import RTC
|
||||||
|
rtc = RTC(RTCounter(1, mode=RTCounter.PERIODIC, period=1, callback=_callback))
|
||||||
|
rtc.counter.start()
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
import draw565
|
||||||
|
|
||||||
|
from machine import I2C
|
||||||
|
from machine import Pin
|
||||||
|
#from machine import Signal
|
||||||
|
from machine import SPI
|
||||||
|
|
||||||
|
from drivers.battery import Battery
|
||||||
|
from drivers.bma421 import BMA421
|
||||||
|
from drivers.touch import TouchButton
|
||||||
|
from drivers.hrs3300 import HRS3300
|
||||||
|
from drivers.signal import Signal
|
||||||
|
from drivers.st7789 import ST7789_SPI
|
||||||
|
from drivers.vibrator import Vibrator
|
||||||
|
from flash.flash_spi import FLASH
|
||||||
|
|
||||||
|
from ubluepy import uart_connected as connected
|
||||||
|
|
||||||
|
class Backlight(object):
|
||||||
|
lo = Pin("BL_LO", Pin.OUT, value=0)
|
||||||
|
mid = Pin("BL_MID", Pin.OUT, value=1)
|
||||||
|
hi = Pin("BL_HI", Pin.OUT, value=1)
|
||||||
|
|
||||||
|
def __init__(self, level=1):
|
||||||
|
self.set(level)
|
||||||
|
|
||||||
|
def set(self, level):
|
||||||
|
hi = 1
|
||||||
|
mid = 1
|
||||||
|
lo = 0
|
||||||
|
|
||||||
|
if level >= 3:
|
||||||
|
hi = 0
|
||||||
|
mid = 0
|
||||||
|
elif level == 2:
|
||||||
|
hi = 0
|
||||||
|
elif level == 1:
|
||||||
|
mid = 0
|
||||||
|
|
||||||
|
self.hi(hi)
|
||||||
|
self.mid(mid)
|
||||||
|
self.lo(lo)
|
||||||
|
|
||||||
|
# Setup the display (and manage the backlight)
|
||||||
|
backlight = Backlight(0)
|
||||||
|
spi = SPI(0)
|
||||||
|
spi.init(polarity=1, phase=1, baudrate=8000000)
|
||||||
|
display = ST7789_SPI(240, 240, spi,
|
||||||
|
cs=Pin("DISP_CS", Pin.OUT),
|
||||||
|
dc=Pin("DISP_DC", Pin.OUT),
|
||||||
|
res=Pin("DISP_RST", Pin.OUT))
|
||||||
|
drawable = draw565.Draw565(display)
|
||||||
|
|
||||||
|
def boot_msg(s):
|
||||||
|
drawable.string(s, 0, 108, width=240)
|
||||||
|
if safe_mode:
|
||||||
|
time.sleep_ms(500)
|
||||||
|
|
||||||
|
safe_mode = True
|
||||||
|
boot_msg("Init button")
|
||||||
|
button = Pin('BUTTON', Pin.IN)
|
||||||
|
safe_mode = button.value()
|
||||||
|
if safe_mode:
|
||||||
|
backlight.set(2)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Setup the last few bits and pieces
|
||||||
|
boot_msg("Init battery")
|
||||||
|
battery = Battery(
|
||||||
|
Pin('BATTERY', Pin.IN),
|
||||||
|
Signal(Pin('CHARGING', Pin.IN), invert=True),
|
||||||
|
Signal(Pin('USB_PWR', Pin.IN), invert=True))
|
||||||
|
boot_msg("Init I2C")
|
||||||
|
i2c = I2C(1, scl='I2C_SCL', sda='I2C_SDA')
|
||||||
|
boot_msg("Init BMA421")
|
||||||
|
accel = BMA421(i2c)
|
||||||
|
boot_msg("Init HRS3300")
|
||||||
|
hrs = HRS3300(i2c)
|
||||||
|
boot_msg("Init touch")
|
||||||
|
touch = TouchButton(Pin('TP_INT', Pin.IN),
|
||||||
|
Pin('TP_RST', Pin.OUT, value=0), _callback)
|
||||||
|
boot_msg("Init motor")
|
||||||
|
vibrator = Vibrator(Pin('MOTOR', Pin.OUT, value=0), active_low=True)
|
||||||
|
|
||||||
|
# Release flash from deep power-down
|
||||||
|
boot_msg("Wake SPINOR")
|
||||||
|
nor_cs = Pin('NOR_CS', Pin.OUT, value=1)
|
||||||
|
nor_cs(0)
|
||||||
|
spi.write('\xAB')
|
||||||
|
nor_cs(1)
|
||||||
|
|
||||||
|
# Mount the filesystem
|
||||||
|
boot_msg("Init SPINOR")
|
||||||
|
flash = FLASH(spi, (nor_cs,))
|
||||||
|
try:
|
||||||
|
boot_msg("Mount FS")
|
||||||
|
os.mount(flash, '/flash')
|
||||||
|
except AttributeError:
|
||||||
|
# Format the filesystem (and provide a default version of main.py)
|
||||||
|
boot_msg("Format FS")
|
||||||
|
os.VfsLfs2.mkfs(flash)
|
||||||
|
boot_msg("Retry mount FS")
|
||||||
|
os.mount(flash,'/flash')
|
||||||
|
boot_msg("Write main.py")
|
||||||
|
with open('/flash/main.py', 'w') as f:
|
||||||
|
f.write('''\
|
||||||
|
#include('main.py')
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Only change directory if the button is not pressed (this will
|
||||||
|
# allow us access to fix any problems with main.py)!
|
||||||
|
if not safe_mode:
|
||||||
|
boot_msg("Enter /flash")
|
||||||
|
os.chdir('/flash')
|
||||||
|
boot_msg("Run main.py")
|
||||||
|
else:
|
||||||
|
boot_msg("Safe mode")
|
||||||
|
except:
|
||||||
|
drawable.string("FAILED", 0, 136, width=240)
|
||||||
|
backlight.set(2)
|
84
wasp/drivers/touch.py
Normal file
84
wasp/drivers/touch.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
# Copyright (C) 2020 Daniel Thompson
|
||||||
|
|
||||||
|
"""Basic touch sensor driver
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
"""
|
||||||
|
|
||||||
|
import array
|
||||||
|
import time
|
||||||
|
from machine import Pin
|
||||||
|
from watch import rtc
|
||||||
|
|
||||||
|
class TouchButton:
|
||||||
|
"""Simple touch controller driver.
|
||||||
|
|
||||||
|
.. automethod:: __init__
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, intr, rst, schedule=None):
|
||||||
|
"""Specify the bus used by the touch controller.
|
||||||
|
|
||||||
|
:param machine.I2C bus: I2C bus for the CST816S.
|
||||||
|
"""
|
||||||
|
self.tp_int = intr
|
||||||
|
self.tp_rst = rst
|
||||||
|
self.schedule = schedule
|
||||||
|
self.event = array.array('H', (0, 0, 0))
|
||||||
|
|
||||||
|
self._reset()
|
||||||
|
self.tp_int.irq(trigger=Pin.IRQ_FALLING, handler=self.get_touch_data)
|
||||||
|
|
||||||
|
def _reset(self):
|
||||||
|
self.tp_rst.off()
|
||||||
|
time.sleep_ms(5)
|
||||||
|
self.tp_rst.on()
|
||||||
|
time.sleep_ms(50)
|
||||||
|
self.event[0] = 0
|
||||||
|
self._wake_at = rtc.get_uptime_ms() + 300
|
||||||
|
|
||||||
|
def get_touch_data(self, pin_obj):
|
||||||
|
"""Synthesize a right swipe during interrupt.
|
||||||
|
"""
|
||||||
|
self.event[0] = 4
|
||||||
|
|
||||||
|
if self.schedule:
|
||||||
|
self.schedule(self)
|
||||||
|
|
||||||
|
def get_event(self):
|
||||||
|
"""Receive a touch event.
|
||||||
|
|
||||||
|
Check for a pending touch event and, if an event is pending,
|
||||||
|
prepare it ready to go in the event queue.
|
||||||
|
|
||||||
|
:return: An event record if an event is received, None otherwise.
|
||||||
|
"""
|
||||||
|
if rtc.get_uptime_ms() < self._wake_at:
|
||||||
|
self.event[0] = 0
|
||||||
|
|
||||||
|
if self.event[0] == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.event
|
||||||
|
|
||||||
|
def reset_touch_data(self):
|
||||||
|
"""Reset touch data.
|
||||||
|
|
||||||
|
Reset touch data, call this function after processing an event.
|
||||||
|
"""
|
||||||
|
self.event[0] = 0
|
||||||
|
|
||||||
|
def wake(self):
|
||||||
|
"""Wake up touch controller chip.
|
||||||
|
|
||||||
|
Just reset the chip in order to wake it up
|
||||||
|
"""
|
||||||
|
self._reset()
|
||||||
|
|
||||||
|
def sleep(self):
|
||||||
|
"""Put touch controller chip on sleep mode to save power.
|
||||||
|
"""
|
||||||
|
self.tp_rst.off()
|
||||||
|
|
||||||
|
# Ensure get_event() cannot return anything
|
||||||
|
self.event[0] = 0
|
Loading…
Reference in a new issue