4141DELAY_OF_SAME_BARCODES_ADDR = b"\x00 \x13 "
4242DELAY_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
4555class 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