q1 support: more leds, keyboard diff, etc
This commit is contained in:
parent
3eb98562ef
commit
bef465e2c7
@ -2,6 +2,7 @@
|
||||
#
|
||||
# Code for the simulator to run, to get it to the point where main.py is called
|
||||
# on real system. Equivilent to a few lines of code found in stm32/COLDCARD/initfs.c
|
||||
#
|
||||
|
||||
import machine, pyb, sys, os
|
||||
|
||||
@ -29,9 +30,10 @@ if '--sflash' not in sys.argv:
|
||||
|
||||
#glob.settings.current = dict(sim_defaults)
|
||||
|
||||
# Install Mk4 hacks and workarounds
|
||||
# Install various hacks and workarounds
|
||||
import mk4
|
||||
import sim_mk4
|
||||
import sim_q1
|
||||
import sim_psram
|
||||
import sim_vdisk
|
||||
|
||||
|
||||
@ -207,18 +207,32 @@ class LCDSimulator(SimulatedScreen):
|
||||
# - not planning to support, tedious
|
||||
return None
|
||||
|
||||
def draw_single_led(self, spriterenderer, x, y, red=False):
|
||||
sp = self.led_red if red else self.led_green
|
||||
sp.position = (x, y)
|
||||
spriterenderer.render(sp)
|
||||
|
||||
def draw_leds(self, spriterenderer, active_set=0):
|
||||
# always draw SE led, since one is always on
|
||||
GEN_LED = 0x1
|
||||
SD_LED = 0x2
|
||||
# redraw all LED's in their current state, indicated
|
||||
SE1_LED = 0x1
|
||||
SD1_LED = 0x2
|
||||
USB_LED = 0x4
|
||||
SD2_LED = 0x8
|
||||
NFC_LED = 0x10
|
||||
|
||||
spriterenderer.render(self.led_green if (active_set & GEN_LED) else self.led_red)
|
||||
if active_set & SE1_LED:
|
||||
self.draw_single_led(spriterenderer, 17, 0, red=False)
|
||||
else:
|
||||
self.draw_single_led(spriterenderer, 65, 0, red=True)
|
||||
|
||||
if active_set & SD_LED:
|
||||
spriterenderer.render(self.led_green) # XXX reposition
|
||||
if active_set & SD1_LED:
|
||||
self.draw_single_led(spriterenderer, -10, 125)
|
||||
if active_set & SD2_LED:
|
||||
self.draw_single_led(spriterenderer, -10, 215)
|
||||
if active_set & USB_LED:
|
||||
spriterenderer.render(self.led_green) # XXX reposition
|
||||
self.draw_single_led(spriterenderer, 195, 705)
|
||||
if active_set & NFC_LED:
|
||||
self.draw_single_led(spriterenderer, 400, 275)
|
||||
|
||||
class OLEDSimulator(SimulatedScreen):
|
||||
# top-left coord of OLED area; size is 1:1 with real pixels... 128x64 pixels
|
||||
@ -362,6 +376,67 @@ def alt_up(ch):
|
||||
|
||||
return None
|
||||
|
||||
q1_pressed = set()
|
||||
def handle_q1_key_events(event, numpad_tx):
|
||||
# Map SDL2 (unix, desktop) keyscan code into keynumber on Q1
|
||||
# - allow Q1 to do shift logic
|
||||
# - support up to 5 keys down at once
|
||||
global q1_pressed
|
||||
|
||||
assert event.type in { sdl2.SDL_KEYUP, sdl2.SDL_KEYDOWN}
|
||||
|
||||
is_press = (event.type == sdl2.SDL_KEYDOWN)
|
||||
|
||||
# first, see if we can convert to ascii char
|
||||
scancode = event.key.keysym.sym & 0xffff
|
||||
try:
|
||||
ch = chr(event.key.keysym.sym)
|
||||
except:
|
||||
ch = scancode_remap(scancode)
|
||||
|
||||
#print(f'scan 0x{scancode:04x} => char={ch}=0x{ord(ch) if ch else 0:02x}')
|
||||
|
||||
shift_down = bool(event.key.keysym.mod & 0x3) # left or right shift
|
||||
symbol_down = bool(event.key.keysym.mod & 0x200) # right ALT
|
||||
|
||||
#print(f"modifier = 0x{event.key.keysym.mod:04x} => shift={shift_down} symb={symbol_down}")
|
||||
|
||||
# reverse char to a keynum, and perhaps the meta key too
|
||||
kn = None
|
||||
|
||||
if ch:
|
||||
if ch in q1_charmap.DECODER:
|
||||
kn = q1_charmap.DECODER.find(ch)
|
||||
elif ch in q1_charmap.DECODER_SHIFT:
|
||||
kn = q1_charmap.DECODER_SHIFT.find(ch)
|
||||
shift_down = is_press
|
||||
elif ch in q1_charmap.DECODER_SYMBOL:
|
||||
kn = q1_charmap.DECODER_SYMBOL.find(ch)
|
||||
symbol_down = is_press
|
||||
|
||||
#print(f" .. => keynum={kn} => shift={shift_down} symb={symbol_down}")
|
||||
|
||||
if kn:
|
||||
if is_press:
|
||||
q1_pressed.add(kn)
|
||||
else:
|
||||
q1_pressed.discard(kn)
|
||||
|
||||
q1_pressed.discard(q1_charmap.KEYNUM_SHIFT)
|
||||
q1_pressed.discard(q1_charmap.KEYNUM_SYMBOL)
|
||||
|
||||
if shift_down:
|
||||
q1_pressed.add(q1_charmap.KEYNUM_SHIFT)
|
||||
if symbol_down:
|
||||
q1_pressed.add(q1_charmap.KEYNUM_SYMBOL)
|
||||
|
||||
#print(f" .. => pressed: {q1_pressed}")
|
||||
|
||||
# see variant/touch.py where this is decoded.
|
||||
assert len(q1_pressed) <= 5
|
||||
report = bytes(list(q1_pressed) + [ 255, 255, 255, 255, 255])[0:5]
|
||||
numpad_tx.write(report)
|
||||
|
||||
|
||||
def start():
|
||||
is_q1 = ('--q1' in sys.argv)
|
||||
@ -375,6 +450,7 @@ def start():
|
||||
if is_q1:
|
||||
print('''\
|
||||
Q1 specials:
|
||||
Right-Alt = AltGr - Symb (symbol key)
|
||||
Alt-L - lamp button (but ignored because implemented lower level)
|
||||
Alt-N - NFC button
|
||||
Alt-Q - QR button
|
||||
@ -488,6 +564,15 @@ Q1 specials:
|
||||
running = False
|
||||
break
|
||||
|
||||
if is_q1 and event.type in { sdl2.SDL_KEYUP, sdl2.SDL_KEYDOWN} :
|
||||
if event.key.keysym.mod == 0x40:
|
||||
# ctrl key down, not used on Q1, so process as simulator
|
||||
# command, see lower.
|
||||
pass
|
||||
else:
|
||||
handle_q1_key_events(event, numpad_tx)
|
||||
continue
|
||||
|
||||
if event.type == sdl2.SDL_KEYUP or event.type == sdl2.SDL_KEYDOWN:
|
||||
try:
|
||||
ch = chr(event.key.keysym.sym)
|
||||
@ -586,24 +671,18 @@ Q1 specials:
|
||||
spriterenderer.render(simdis.sprite)
|
||||
window.refresh()
|
||||
elif r is led_rx:
|
||||
# XXX 8+8 bits
|
||||
c = r.read(1)
|
||||
# XXX was 4+4 bits, now two bytes: [active, mask]
|
||||
c = r.read(2)
|
||||
if not c:
|
||||
break
|
||||
|
||||
c = c[0]
|
||||
if 1:
|
||||
#print("LED change: 0x%02x" % c[0])
|
||||
mask, lset = c
|
||||
active_set = (mask & lset)
|
||||
|
||||
mask = (c >> 4) & 0xf
|
||||
lset = c & 0xf
|
||||
|
||||
active_set = (mask & lset)
|
||||
|
||||
#print("Genuine LED: %r" % genuine_state)
|
||||
spriterenderer.render(bg)
|
||||
spriterenderer.render(simdis.sprite)
|
||||
simdis.draw_leds(spriterenderer, active_set)
|
||||
#print("Genuine LED: %r" % genuine_state)
|
||||
spriterenderer.render(bg)
|
||||
spriterenderer.render(simdis.sprite)
|
||||
simdis.draw_leds(spriterenderer, active_set)
|
||||
|
||||
window.refresh()
|
||||
else:
|
||||
|
||||
@ -18,7 +18,7 @@ rng_fd = open('/dev/urandom', 'rb')
|
||||
global genuine_led
|
||||
|
||||
led_pipe = open(int(sys.argv[3]), 'wb')
|
||||
led_pipe.write(b'\xf1') # all off, except green
|
||||
led_pipe.write(b'\xff\x01') # all off, except green
|
||||
genuine_led = True
|
||||
|
||||
# HACK: reduce size of heap in Unix simulator to be more similar to
|
||||
@ -84,11 +84,11 @@ def gate(method, buf_io, arg2):
|
||||
if arg2 == 1:
|
||||
# clear it
|
||||
genuine_led = False
|
||||
led_pipe.write(b'\x10')
|
||||
led_pipe.write(b'\x01\x00')
|
||||
if arg2 == 3:
|
||||
# real code would do checksum then go green
|
||||
genuine_led = True
|
||||
led_pipe.write(b'\x11')
|
||||
led_pipe.write(b'\x01\x01')
|
||||
return 1 if genuine_led else 0
|
||||
|
||||
if method == 5:
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
from mock import Mock
|
||||
|
||||
UNSPEC = object()
|
||||
|
||||
class Pin:
|
||||
def __init__(self, name, *a, **kw):
|
||||
self.name = name
|
||||
@ -15,14 +17,19 @@ class Pin:
|
||||
self.cur_value = int(n)
|
||||
|
||||
bm = 0
|
||||
# 0x01 => SE1 light
|
||||
if self.name == 'SD_ACTIVE':
|
||||
bm = 0x02
|
||||
elif self.name == 'USB_ACTIVE':
|
||||
bm = 0x04
|
||||
elif self.name == 'SD_ACTIVE2':
|
||||
bm = 0x08
|
||||
elif self.name == 'NFC_ACTIVE':
|
||||
bm = 0x10
|
||||
|
||||
if bm:
|
||||
from ckcc import led_pipe
|
||||
led_pipe.write(bytes([(bm << 4) | (bm if n else 0)]))
|
||||
led_pipe.write(bytes([bm, (bm if n else 0)]))
|
||||
|
||||
def pin(self):
|
||||
# ? pin number
|
||||
@ -35,6 +42,12 @@ class Pin:
|
||||
Touch()
|
||||
pass
|
||||
|
||||
def __call__(self, new_val=UNSPEC):
|
||||
if new_val==UNSPEC:
|
||||
return self.cur_value
|
||||
else:
|
||||
self.value(new_val)
|
||||
|
||||
ALT = None
|
||||
PULL_NONE = None
|
||||
PULL_UP = None
|
||||
|
||||
@ -8,6 +8,8 @@ freeze_as_mpy('', [
|
||||
'os.py',
|
||||
'pyb.py',
|
||||
'sim_mk4.py',
|
||||
'sim_q1.py',
|
||||
'sim_scanner.py',
|
||||
'sim_nfc.py',
|
||||
'sim_psram.py',
|
||||
'sim_quickstart.py',
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
import utime as time
|
||||
import uerrno as errno
|
||||
import sys
|
||||
|
||||
from machine import Pin
|
||||
|
||||
class USB_VCP:
|
||||
@staticmethod
|
||||
def isconnected():
|
||||
@ -117,7 +121,7 @@ class SDCard:
|
||||
@classmethod
|
||||
def power(cls, st=0):
|
||||
from ckcc import led_pipe
|
||||
led_pipe.write(bytes([0x20 | (0x2 if st else 0x0)]))
|
||||
led_pipe.write(bytes([0x2, (0x2 if st else 0x0)]))
|
||||
if st:
|
||||
time.sleep(0.100) # drama
|
||||
return False
|
||||
@ -129,12 +133,6 @@ class SDCard:
|
||||
b'2\x00^\x00\xd6\x81Y[\x8f\xff\xb7\xed\x94\x00@\x16',
|
||||
b'APA\tDU F\xd9\x92\x11\x10\x9a\x1a\x01\xdf')
|
||||
|
||||
class Pin:
|
||||
PULL_NONE =1
|
||||
PULL_UP =2
|
||||
|
||||
def __init__(self, *a, **kw):
|
||||
return
|
||||
|
||||
class ExtInt:
|
||||
def __init__(self, *a, **kw):
|
||||
@ -153,4 +151,4 @@ class Timer:
|
||||
def deinit(self): pass
|
||||
def init(self, **k): pass
|
||||
|
||||
|
||||
# EOF
|
||||
|
||||
@ -18,6 +18,10 @@ def _init0():
|
||||
import sim_nfc
|
||||
sys.modules['nfc'] = sim_nfc
|
||||
|
||||
# Q1: install (fake) QR scanner interface code
|
||||
import sim_scanner
|
||||
sys.modules['scanner'] = sim_scanner
|
||||
|
||||
mk4.rng_seeding()
|
||||
|
||||
mk4.init0 = _init0
|
||||
|
||||
16
unix/variant/sim_q1.py
Normal file
16
unix/variant/sim_q1.py
Normal file
@ -0,0 +1,16 @@
|
||||
# (c) Copyright 2023 by Coinkite Inc. This file is covered by license found in COPYING-CC.
|
||||
#
|
||||
# sim_q14.py - Simulate Q1 specific code, not needed on other devices.
|
||||
#
|
||||
# - shared/q1.py calls mk4.init0 so no need to replace that
|
||||
#
|
||||
import q1
|
||||
|
||||
q1.setup_adc = lambda: None
|
||||
|
||||
def mock_get_batt_level():
|
||||
return 3.69
|
||||
|
||||
q1.get_batt_level = mock_get_batt_level
|
||||
|
||||
# EOF
|
||||
72
unix/variant/sim_scanner.py
Normal file
72
unix/variant/sim_scanner.py
Normal file
@ -0,0 +1,72 @@
|
||||
# Replace QR Scanner module interface.
|
||||
# TODO: support (optional) local real scanner over USB serial.
|
||||
#
|
||||
import os
|
||||
import uasyncio as asyncio
|
||||
from scanner import QRScanner
|
||||
|
||||
# unix/working/...
|
||||
DATA_FILE = 'qrdata.txt'
|
||||
|
||||
class SimulatedQRScanner(QRScanner):
|
||||
def __init__(self):
|
||||
self.q = None
|
||||
|
||||
def hw_scan(self):
|
||||
# trigger a scan
|
||||
pass
|
||||
|
||||
async def _read_results(self):
|
||||
# be a task that reads incoming QR codes from scanner (already in operation)
|
||||
# - will be canceled when done/stopping
|
||||
try:
|
||||
_, orig_mtime, _ = os.stat(DATA_FILE)[-3:]
|
||||
except OSError:
|
||||
orig_mtime = None
|
||||
|
||||
while 1:
|
||||
await asyncio.sleep_ms(250)
|
||||
|
||||
try:
|
||||
_, mtime, _ = os.stat(DATA_FILE)[-3:]
|
||||
except OSError:
|
||||
mtime = None
|
||||
|
||||
if mtime == orig_mtime:
|
||||
continue
|
||||
|
||||
print("Got new QR scan data.")
|
||||
got = open(DATA_FILE, 'rb').read(8196)
|
||||
self.q.push(got)
|
||||
|
||||
orig_mtime = mtime
|
||||
|
||||
async def scan_start(self, test=15):
|
||||
# returns a Q we append to as results come in
|
||||
self.q = rv = Queue()
|
||||
|
||||
self._scan_task = asyncio.create_task(self._read_results())
|
||||
|
||||
return rv
|
||||
|
||||
async def scan_stop(self):
|
||||
self._scan_task.cancel()
|
||||
self._scan_task = None
|
||||
self.q = None
|
||||
|
||||
async def wakeup(self):
|
||||
return
|
||||
|
||||
async def sleep(self):
|
||||
return
|
||||
|
||||
async def tx(self, msg):
|
||||
return
|
||||
|
||||
async def torch(self, on):
|
||||
print("Torch is: " + 'ON' if on else 'off')
|
||||
|
||||
# close door behind ourselves
|
||||
QRScanner = SimulatedQRScanner
|
||||
|
||||
# EOF
|
||||
@ -256,7 +256,7 @@ def pin_stuff(submethod, buf_io):
|
||||
# greenlight firmware
|
||||
from ckcc import genuine_led, led_pipe
|
||||
genuine_led = True
|
||||
led_pipe.write(b'\x01')
|
||||
led_pipe.write(b'\x00\x01')
|
||||
|
||||
elif submethod == 6:
|
||||
if not version.has_608:
|
||||
|
||||
@ -6,31 +6,67 @@ import struct, sys
|
||||
import framebuf
|
||||
import uasyncio
|
||||
|
||||
class ST7788(framebuf.FrameBuffer):
|
||||
class ST7788:
|
||||
def __init__(self):
|
||||
# for framebuf.FrameBuffer
|
||||
self.width = 320
|
||||
self.height = 240
|
||||
self.buffer = bytearray(320*240)
|
||||
#self.buffer = bytearray(320*240)
|
||||
|
||||
self.pipe = open(int(sys.argv[1]), 'wb')
|
||||
|
||||
super().__init__(self.buffer, self.width, self.height, framebuf.GS8)
|
||||
#super().__init__(self.buffer, self.width, self.height, framebuf.GS8)
|
||||
|
||||
def show_partial(self, y, h):
|
||||
# update just a few rows of the display
|
||||
assert h >= 1
|
||||
assert h < 120 # sim limitation
|
||||
hdr = struct.pack('<4H', 0, y, 320, h)
|
||||
rows = memoryview(self.buffer)[320*y:320*(y+h)]
|
||||
hdr = struct.pack('<s5H', 'p', 0, y, 320, h, len(rows))
|
||||
self.pipe.write(hdr + rows)
|
||||
|
||||
def show(self):
|
||||
# send entire frame buffer, but two packets
|
||||
hdr = struct.pack('<4H', 0, 0, 320, 120)
|
||||
hdr = struct.pack('<s5H', 'p', 0, 0, 320, 120, 320*120)
|
||||
self.pipe.write(hdr + self.buffer[0:320*120])
|
||||
|
||||
hdr = struct.pack('<4H', 0, 120, 320, 120)
|
||||
hdr = struct.pack('<s5H', 'p', 0, 120, 320, 120, 320*120)
|
||||
self.pipe.write(hdr + self.buffer[320*120:])
|
||||
|
||||
def show_zpixels(self, x, y, w, h, zpixels):
|
||||
# display compressed pixel data
|
||||
hdr = struct.pack('<s5H', 'z', x, y, w, h, len(zpixels))
|
||||
self.pipe.write(hdr + zpixels)
|
||||
|
||||
def fill_screen(self, pixel=0x0000):
|
||||
# clear screen to indicated pixel value
|
||||
self.fill_rect(0,0, 320,240, pixel)
|
||||
|
||||
def fill_rect(self, x,y, w,h, pixel=0x0000):
|
||||
msg = struct.pack('<s5HH', 'f', x, y, w, h, 2, pixel)
|
||||
self.pipe.write(msg)
|
||||
|
||||
def show_pal_pixels(self, x, y, w, h, palette, pixels):
|
||||
# show 4-bit packed paletted lookup pixels; used for fonts, icons
|
||||
assert len(palette) == 2 * 16
|
||||
assert len(pixels) == w * h // 2
|
||||
|
||||
if 0:
|
||||
# do palette lookup now
|
||||
tmp = bytearray(2 * w * h)
|
||||
pos = 0
|
||||
for px in pixels:
|
||||
col = (px >> 4)
|
||||
tmp[pos:pos+2] = palette[col:col+2]
|
||||
col = px & 0xf
|
||||
tmp[pos+2:pos+4] = palette[col:col+2]
|
||||
pos += 4
|
||||
|
||||
hdr = struct.pack('<s5H', 'r', x, y, w, h, len(tmp))
|
||||
self.pipe.write(hdr + tmp)
|
||||
else:
|
||||
# assumes simulator has same palette
|
||||
hdr = struct.pack('<s5H', 't' if palette[0] == 0 else 'i', x, y, w, h, len(pixels))
|
||||
self.pipe.write(hdr + pixels)
|
||||
|
||||
# EOF
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import sys
|
||||
import sys, version
|
||||
from uasyncio.core import get_event_loop
|
||||
from uasyncio import StreamReader
|
||||
|
||||
NUM_ROWS = const(6)
|
||||
NUM_COLS = const(10)
|
||||
|
||||
class Touch:
|
||||
|
||||
# Misnomer: emulating a membrane now, not a touch interface
|
||||
@ -26,13 +29,26 @@ class Touch:
|
||||
from glob import numpad
|
||||
|
||||
while 1:
|
||||
# rx's any key that is pressed and now released
|
||||
key = await s.read(1)
|
||||
#print("Sim: %s" % key)
|
||||
if key == b'\0':
|
||||
await numpad._changes.put('') # all up
|
||||
if version.has_qwerty:
|
||||
# NOTE: numpad # will be FullKeyboard instance
|
||||
|
||||
# sends 2 keynums, might be few meta+specific keys
|
||||
# - pad with -1
|
||||
pressed = await s.read(5)
|
||||
|
||||
for kn in range(NUM_ROWS * NUM_COLS):
|
||||
numpad.is_pressed[kn] = (0 if kn not in pressed else 1)
|
||||
|
||||
# Q1 simulator sends keynumbers, from shared/charcodes.py
|
||||
numpad.process_chg_state()
|
||||
else:
|
||||
await numpad._changes.put(key.decode())
|
||||
# rx's any key that is pressed and now released
|
||||
key = await s.read(1)
|
||||
#print("Sim: %s" % key)
|
||||
if key == b'\0':
|
||||
await numpad._changes.put('') # all up
|
||||
else:
|
||||
await numpad._changes.put(key.decode())
|
||||
|
||||
def discharge(self):
|
||||
pass
|
||||
|
||||
Loading…
Reference in New Issue
Block a user