Skip to content

Commit 9ba9aed

Browse files
committed
Initial Scanner Update
*Add version check and set RAW mode for readers that need this *Report detected scanner SW version on the QR scanner screen
1 parent 24d1137 commit 9ba9aed

File tree

1 file changed

+55
-21
lines changed

1 file changed

+55
-21
lines changed

src/hosts/qr.py

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@
4141
DELAY_OF_SAME_BARCODES_ADDR = b"\x00\x13"
4242
DELAY_OF_SAME_BARCODES = 0x85 # 5 seconds
4343

44+
""" Address for software version check.
45+
(The ability to check firmware version via this memory was removed from newer GM65 type devices, but still lets us identify versions that need a fix)"""
46+
VERSION_ADDR = b"\x00\xE2"
47+
VERSION_NEEDS_RAW = 0x69 # A version of GM65 that needs RAW mode to be turned on...
48+
49+
""" Some newer GM65 Scanners need to be set to "RAW" mode to correctly scan binary QR codes.
50+
(The address for this is actually undocumented and doesn't seem to work the same on newer varients like the GM805)"""
51+
RAW_MODE_ADDR = b"\x00\xBC"
52+
RAW_MODE_VALUE = 0x08
53+
4454

4555
class QRHost(Host):
4656
"""
@@ -56,6 +66,9 @@ class QRHost(Host):
5666
button = "Scan QR code"
5767
settings_button = "QR scanner"
5868

69+
# Some Information to report device to user
70+
version_str = "No Scanner Detected"
71+
5972
def __init__(self, path, trigger=None, uart="YA", baudrate=9600):
6073
super().__init__(path)
6174

@@ -90,13 +103,13 @@ def __init__(self, path, trigger=None, uart="YA", baudrate=9600):
90103

91104
@property
92105
def MASK(self):
93-
b = (1<<7)
106+
b = (1 << 7)
94107
if self.settings.get("sound", True):
95-
b |= (1<<6)
108+
b |= (1 << 6)
96109
if self.settings.get("aim", True):
97-
b |= (1<<4)
110+
b |= (1 << 4)
98111
if self.settings.get("light", False):
99-
b |= (1<<2)
112+
b |= (1 << 2)
100113
return b
101114

102115
@property
@@ -142,41 +155,61 @@ def save_settings_on_scanner(self):
142155
def configure(self):
143156
"""Tries to configure scanner, returns True on success"""
144157
save_required = False
158+
159+
# Set Serial Output Mode
145160
val = self.get_setting(SERIAL_ADDR)
146161
if val is None:
147162
return False
148163
if val & 0x3 != 0:
149164
self.set_setting(SERIAL_ADDR, val & 0xFC)
150165
save_required = True
151166

167+
# Set Command Mode
152168
val = self.get_setting(SETTINGS_ADDR)
153169
if val is None:
154170
return False
155171
if val != self.CMD_MODE:
156172
self.set_setting(SETTINGS_ADDR, self.CMD_MODE)
157173
save_required = True
158174

175+
# Set scanning timeout
159176
val = self.get_setting(TIMOUT_ADDR)
160177
if val is None:
161178
return False
162179
if val != 0:
163180
self.set_setting(TIMOUT_ADDR, 0)
164181
save_required = True
165182

183+
# Set interval between scans
166184
val = self.get_setting(INTERVAL_OF_SCANNING_ADDR)
167185
if val is None:
168186
return False
169187
if val != INTERVAL_OF_SCANNING:
170188
self.set_setting(INTERVAL_OF_SCANNING_ADDR, INTERVAL_OF_SCANNING)
171189
save_required = True
172190

191+
# Set delay beteen re-reading the same barcode
173192
val = self.get_setting(DELAY_OF_SAME_BARCODES_ADDR)
174193
if val is None:
175194
return False
176195
if val != DELAY_OF_SAME_BARCODES:
177196
self.set_setting(DELAY_OF_SAME_BARCODES_ADDR, DELAY_OF_SAME_BARCODES)
178197
save_required = True
179198

199+
# Check the module software and enable "RAW" mode if required
200+
val = self.get_setting(VERSION_ADDR)
201+
if val is None:
202+
return False
203+
self.version_str = "Detected GM65 Scanner, SW:" + str(val)
204+
if val == VERSION_NEEDS_RAW:
205+
val = self.get_setting(RAW_MODE_ADDR)
206+
if val is None:
207+
return False
208+
if val != RAW_MODE_VALUE:
209+
self.set_setting(RAW_MODE_ADDR, RAW_MODE_VALUE)
210+
save_required = True
211+
212+
# Save settings to EEPROM if anything has changed.
180213
if save_required:
181214
val = self.save_settings_on_scanner()
182215
if not val:
@@ -218,6 +251,7 @@ def init(self):
218251

219252
async def settings_menu(self, show_screen, keystore):
220253
title = "QR scanner"
254+
note = self.version_str
221255
controls = [{
222256
"label": "Enable QR scanner",
223257
"hint": "Enable or disable QR scanner and remove corresponding button from the main menu",
@@ -235,7 +269,7 @@ async def settings_menu(self, show_screen, keystore):
235269
"hint": "Can create blicks on the screen",
236270
"value": self.settings.get("light", False)
237271
}]
238-
scr = HostSettings(controls, title=title)
272+
scr = HostSettings(controls, title=title, note=note)
239273
res = await show_screen(scr)
240274
if res:
241275
enabled, sound, aim, light = res
@@ -254,7 +288,7 @@ def clean_uart(self):
254288

255289
def _stop_scanner(self):
256290
if self.trigger is not None:
257-
self.trigger.on() # trigger is reversed, so on means disable
291+
self.trigger.on() # trigger is reversed, so on means disable
258292
else:
259293
self.set_setting(SCAN_ADDR, 0)
260294

@@ -278,21 +312,21 @@ def stop_scanning(self):
278312
self._stop_scanner()
279313

280314
def abort(self):
281-
with open(self.tmpfile,"wb"):
315+
with open(self.tmpfile, "wb"):
282316
pass
283317
self.cancelled = True
284318
self.stop_scanning()
285319

286320
@property
287321
def tmpfile(self):
288-
return self.path+"/tmp"
322+
return self.path + "/tmp"
289323

290324
async def scan(self, raw=True, chunk_timeout=0.5):
291325
self.raw = raw
292326
self.chunk_timeout = chunk_timeout
293327
self._start_scanner()
294328
# clear the data
295-
with open(self.tmpfile,"wb") as f:
329+
with open(self.tmpfile, "wb") as f:
296330
pass
297331
if self.f is not None:
298332
self.f.close()
@@ -319,14 +353,14 @@ async def scan(self, raw=True, chunk_timeout=0.5):
319353
gc.collect()
320354
if self.cancelled:
321355
return None
322-
self.f = open(self.path+"/data.txt", "rb")
356+
self.f = open(self.path + "/data.txt", "rb")
323357
return self.f
324358

325359
def check_animated(self, data: bytes):
326360
try:
327361
# should be only ascii characters
328362
d = data.decode().strip().lower()
329-
if d.startswith("ur:"): # ur:bytes or ur:crypto-psbt
363+
if d.startswith("ur:"): # ur:bytes or ur:crypto-psbt
330364
return True
331365
# this will raise if it's not a valid prefix
332366
self.parse_prefix(data.split(b" ")[0])
@@ -342,7 +376,7 @@ async def update(self):
342376
return
343377
# read all available data
344378
if self.uart.any() > 0:
345-
if not self.animated: # read only one QR code
379+
if not self.animated: # read only one QR code
346380
# let all data to come on the first QR code
347381
await asyncio.sleep(self.chunk_timeout)
348382
d = self.uart.read()
@@ -361,14 +395,14 @@ async def update(self):
361395
d = self.uart.read()
362396
# no new lines - just write and continue
363397
if d[-len(self.EOL):] != self.EOL:
364-
with open(self.tmpfile,"ab") as f:
398+
with open(self.tmpfile, "ab") as f:
365399
f.write(d)
366400
return
367401
# restart scan while processing data
368402
await self._restart_scanner()
369403
# slice to write
370404
d = d[:-len(self.EOL)]
371-
with open(self.tmpfile,"ab") as f:
405+
with open(self.tmpfile, "ab") as f:
372406
f.write(d)
373407
try:
374408
if self.process_chunk():
@@ -451,7 +485,7 @@ def process_bcur(self, f):
451485
# allocate stuff
452486
self.animated = True
453487
self.parts = [None] * n
454-
fname = "%s/p%d.txt" % (self.path, m-1)
488+
fname = "%s/p%d.txt" % (self.path, m - 1)
455489
with open(fname, "wb") as fout:
456490
read_write(f, fout)
457491
self.parts[m - 1] = fname
@@ -467,7 +501,7 @@ def process_bcur(self, f):
467501
if hsh != self.bcur_hash:
468502
print(hsh, self.bcur_hash)
469503
raise HostError("Checksum mismatch")
470-
fname = "%s/p%d.txt" % (self.path, m-1)
504+
fname = "%s/p%d.txt" % (self.path, m - 1)
471505
with open(fname, "wb") as fout:
472506
fout.write(f.read())
473507
self.parts[m - 1] = fname
@@ -509,7 +543,7 @@ def process_normal(self, f):
509543
# allocate stuff
510544
self.animated = True
511545
self.parts = [None] * n
512-
fname = "%s/p%d.txt" % (self.path, m-1)
546+
fname = "%s/p%d.txt" % (self.path, m - 1)
513547
with open(fname, "wb") as fout:
514548
read_write(f, fout)
515549
self.parts[m - 1] = fname
@@ -533,7 +567,7 @@ def process_normal(self, f):
533567
m, n = self.parse_prefix(chunk)
534568
if n != len(self.parts):
535569
raise HostError("Invalid prefix")
536-
fname = "%s/p%d.txt" % (self.path, m-1)
570+
fname = "%s/p%d.txt" % (self.path, m - 1)
537571
with open(fname, "wb") as fout:
538572
read_write(f, fout)
539573
self.parts[m - 1] = fname
@@ -580,7 +614,7 @@ async def send_data(self, stream, meta, *args, **kwargs):
580614
note = meta.get("note")
581615
start = stream.read(4)
582616
stream.seek(-len(start), 1)
583-
if start in [b"cHNi", b"cHNl"]: # convert from base64 for QR encoder
617+
if start in [b"cHNi", b"cHNl"]: # convert from base64 for QR encoder
584618
with open(self.tmpfile, "wb") as f:
585619
a2b_base64_stream(stream, f)
586620
with open(self.tmpfile, "rb") as f:
@@ -592,13 +626,13 @@ async def send_data(self, stream, meta, *args, **kwargs):
592626
return await self.manager.gui.qr_alert(title, msg, response, note=note, qr_width=480)
593627

594628
EncoderCls = None
595-
if self.bcur2: # we need binary
629+
if self.bcur2: # we need binary
596630
from qrencoder import CryptoPSBTEncoder as EncoderCls
597631
elif self.bcur:
598632
from qrencoder import LegacyBCUREncoder as EncoderCls
599633
else:
600634
from qrencoder import Base64QREncoder as EncoderCls
601-
with EncoderCls(stream, tempfile=self.path+"/qrtmp") as enc:
635+
with EncoderCls(stream, tempfile=self.path + "/qrtmp") as enc:
602636
await self.manager.gui.qr_alert(title, "", enc, note=note, qr_width=480)
603637

604638
@property

0 commit comments

Comments
 (0)