async def do_usec(minutes): global gps tick = Message() print('Setting up GPS.') await us_setup(tick) print('Waiting for time data.') await gps.ready() max_us = 0 min_us = 0 sd = 0 nsamples = 0 count = 0 terminate = Event() asyncio.create_task(killer(terminate, minutes)) while not terminate.is_set(): await tick usecs = tick.value() tick.clear() err = 1000000 - usecs count += 1 print('Timing discrepancy is {:4d}μs {}'.format( err, '(skipped)' if count < 3 else '')) if count < 3: # Discard 1st two samples from statistics continue # as these can be unrepresentative max_us = max(max_us, err) min_us = min(min_us, err) sd += err * err nsamples += 1 # SD: apply Bessel's correction for infinite population sd = int(math.sqrt(sd / (nsamples - 1))) print( 'Timing discrepancy is: {:5d}μs max {:5d}μs min. Standard deviation {:4d}μs' .format(max_us, min_us, sd))
def __init__(self, pin, callback, extended, *args): # Optional args for callback self._ev_start = Message() self._callback = callback self._extended = extended self._addr = 0 self.block_time = 80 if extended else 73 # Allow for some tx tolerance (?) self._args = args self._times = array('i', (0 for _ in range(_EDGECOUNT + 1))) # +1 for overrun pin.irq(handler = self._cb_pin, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING)) self._edge = 0 self._ev_start.clear() asyncio.create_task(self._run())
async def run_message_test(): print('Test Lock class') lock = asyncio.Lock() asyncio.create_task(run_lock(1, lock)) asyncio.create_task(run_lock(2, lock)) asyncio.create_task(run_lock(3, lock)) print('Test Message class') message = Message() asyncio.create_task(messageset(message)) await messagewait(message) # run_message_test runs fast until this point print('Message status {}'.format('Incorrect' if message.is_set() else 'OK')) print('Tasks complete')
def __init__(self, conn_id, config, hardware, verbose): self.verbose = verbose self.initial = True self._status = False # Server status self.wlock = asyncio.Lock() self.rxmsg = Message() # rx data ready self.tim_boot = Delay_ms(func=self.reboot) config.insert(0, conn_id) config.append('cfg') # Marker defines a config list self.cfg = ''.join((ujson.dumps(config), '\n')) i2c, syn, ack, rst = hardware self.chan = asi2c_i.Initiator(i2c, syn, ack, rst, verbose, self._go, (), self.reboot) self.sreader = asyncio.StreamReader(self.chan) self.swriter = asyncio.StreamWriter(self.chan, {}) self.lqueue = [] # Outstanding lines
def __init__(self, pin, callback, extended, *args): # Optional args for callback self._ev_start = Message() self._callback = callback self._extended = extended self._addr = 0 self.block_time = 80 if extended else 73 # Allow for some tx tolerance (?) self._args = args self._times = array('i', (0 for _ in range(_EDGECOUNT + 1))) # +1 for overrun if platform == 'pyboard': ExtInt(pin, ExtInt.IRQ_RISING_FALLING, Pin.PULL_NONE, self._cb_pin) else: # PR5962 ESP8266 hard IRQ's not supported pin.irq(handler=self._cb_pin, trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING)) self._edge = 0 self._ev_start.clear() asyncio.create_task(self._run())
async def listner(trigger): global devs m = Message() trigger.set(True) async def poller(stream): sreader = asyncio.StreamReader(stream) i = 0 while True: await scheduling.wait() #and await disconnect.wait() b = await sreader.read(1) if b: try: b.decode('utf-8') except UnicodeError: await asyncio.sleep(0) continue if (b == dfl.ESC_CHAR.encode() and stream.__class__.__name__ == 'UART' and not session.logging): i += 1 if i > 2: asyncio.create_task(session.login(m, stream)) session.logging = True elif (b == b'\x1b' and (stream.__class__.__name__ == 'UART' and session.loggedin or stream.__class__.__name__ == 'USB_VCP') and not menu.interactive): asyncio.create_task(menu.main(m, stream, devs)) m.set(b) # Passes ESC to menu. menu.interactive = True else: m.set(b) i = 0 await asyncio.sleep_ms(100) asyncio.create_task(poller(pyb.USB_VCP())) # Polls the usb vcp. while True: await trigger.wait() if trigger.value(): polluart = asyncio.create_task(poller(pyb.UART( 3, 9600))) # Polls the modem uart. else: polluart.cancel() trigger.clear() await asyncio.sleep_ms(100)
async def run_ack(): message = Message() ack1 = asyncio.Event() ack2 = asyncio.Event() count = 0 while True: asyncio.create_task(event_wait(message, ack1, 1)) asyncio.create_task(event_wait(message, ack2, 2)) message.set(count) count += 1 print('message was set') await ack1.wait() ack1.clear() print('Cleared ack1') await ack2.wait() ack2.clear() print('Cleared ack2') message.clear() print('Cleared message') await asyncio.sleep(1)
from primitives import launch from primitives.message import Message def get_unique_id(): if LINUX is False: from machine import unique_id return hexlify(unique_id()).decode() else: raise NotImplementedError( "Linux doesn't have a unique id. Provide the DEVICE_ID option in your settings.py." ) # Decorator to block async tasks until the device is in "ready" state _MESSAGE = Message() def await_ready_state(func): def new_gen(*args, **kwargs): # fmt: off await _MESSAGE await func(*args, **kwargs) # fmt: on return new_gen class HomieDevice: """MicroPython implementation of the Homie MQTT convention for IoT.""" def __init__(self, settings):
import uasyncio as asyncio from primitives.message import Message from primitives.semaphore import Semaphore from primitives.queue import Queue import time import os import json import _thread import pyb from configs import dfl, cfg logger = True # Prints out messages. f_lock = asyncio.Lock() # Data file lock. alert = Message() # Sms message. trigger = Message() timesync = asyncio.Event() # Gps fix event. scheduling = asyncio.Event() # Scheduler event. disconnect = asyncio.Event() # Modem event. u2_lock = asyncio.Lock() # Uart 2 lock. u4_lock = asyncio.Lock() # Uart 4 lock. def welcome_msg(): print( '{:#^80}\n\r#{: ^78}#\n\r#{: ^78}#\n\r# {: <20}{: <57}#\n\r# {: <20}{: <57}#\n\r# {: <20}{: <57}#\n\r# {: <20}{: <57}#\n\r{:#^80}'.format( '', 'WELCOME TO ' + cfg.HOSTNAME + ' ' + dfl.SW_NAME + ' ' + dfl.SW_VERSION, '', ' current time:', iso8601(time.time()),
class NEC_IR(): def __init__(self, pin, callback, extended, *args): # Optional args for callback self._ev_start = Message() self._callback = callback self._extended = extended self._addr = 0 self.block_time = 80 if extended else 73 # Allow for some tx tolerance (?) self._args = args self._times = array('i', (0 for _ in range(_EDGECOUNT + 1))) # +1 for overrun if platform == 'pyboard': ExtInt(pin, ExtInt.IRQ_RISING_FALLING, Pin.PULL_NONE, self._cb_pin) else: # PR5962 ESP8266 hard IRQ's not supported pin.irq(handler=self._cb_pin, trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING)) self._edge = 0 self._ev_start.clear() asyncio.create_task(self._run()) async def _run(self): while True: await self._ev_start # Wait until data collection has started # Compensate for asyncio latency latency = ticks_diff(ticks_ms(), self._ev_start.value()) await asyncio.sleep_ms(self.block_time - latency ) # Data block should have ended self._decode() # decode, clear event, prepare for new rx, call cb # Pin interrupt. Save time of each edge for later decode. def _cb_pin(self, line): t = ticks_us() # On overrun ignore pulses until software timer times out if self._edge <= _EDGECOUNT: # Allow 1 extra pulse to record overrun if not self._ev_start.is_set(): # First edge received self._ev_start.set(ticks_ms()) # asyncio latency compensation self._times[self._edge] = t self._edge += 1 def _decode(self): overrun = self._edge > _EDGECOUNT val = OVERRUN if overrun else BADSTART if not overrun: width = ticks_diff(self._times[1], self._times[0]) if width > 4000: # 9ms leading mark for all valid data width = ticks_diff(self._times[2], self._times[1]) if width > 3000: # 4.5ms space for normal data if self._edge < _EDGECOUNT: # Haven't received the correct number of edges val = BADBLOCK else: # Time spaces only (marks are always 562.5µs) # Space is 1.6875ms (1) or 562.5µs (0) # Skip last bit which is always 1 val = 0 for edge in range(3, _EDGECOUNT - 2, 2): val >>= 1 if ticks_diff(self._times[edge + 1], self._times[edge]) > 1120: val |= 0x80000000 elif width > 1700: # 2.5ms space for a repeat code. Should have exactly 4 edges. val = REPEAT if self._edge == 4 else BADREP addr = 0 if val >= 0: # validate. Byte layout of val ~cmd cmd ~addr addr addr = val & 0xff cmd = (val >> 16) & 0xff if addr == ((val >> 8) ^ 0xff) & 0xff: # 8 bit address OK val = cmd if cmd == (val >> 24) ^ 0xff else BADDATA self._addr = addr else: addr |= val & 0xff00 # pass assumed 16 bit address to callback if self._extended: val = cmd if cmd == (val >> 24) ^ 0xff else BADDATA self._addr = addr else: val = BADADDR if val == REPEAT: addr = self._addr # Last valid addresss self._edge = 0 # Set up for new data burst and run user callback self._ev_start.clear() self._callback(val, addr, *self._args)
async def run_message_test(): message = Message() asyncio.create_task(wait_message(message)) await asyncio.sleep(1) message.set('Hello world') await asyncio.sleep(1)
class AppBase: def __init__(self, conn_id, config, hardware, verbose): self.verbose = verbose self.initial = True self._status = False # Server status self.wlock = asyncio.Lock() self.rxmsg = Message() # rx data ready self.tim_boot = Delay_ms(func=self.reboot) config.insert(0, conn_id) config.append('cfg') # Marker defines a config list self.cfg = ''.join((ujson.dumps(config), '\n')) i2c, syn, ack, rst = hardware self.chan = asi2c_i.Initiator(i2c, syn, ack, rst, verbose, self._go, (), self.reboot) self.sreader = asyncio.StreamReader(self.chan) self.swriter = asyncio.StreamWriter(self.chan, {}) self.lqueue = [] # Outstanding lines # Runs after sync acquired on 1st or subsequent ESP8266 boots. async def _go(self): self.verbose and print('Sync acquired, sending config') if not self.wlock.locked(): # May have been acquired in .reboot await self.wlock.acquire() self.verbose and print('Got lock, sending config', self.cfg) self.swriter.write(self.cfg) await self.swriter.drain() # 1st message is config while self.lqueue: self.swriter.write(self.lqueue.pop(0)) await self.swriter.drain() self.wlock.release() # At this point ESP8266 can handle the Pyboard interface but may not # yet be connected to the server if self.initial: self.initial = False self.start() # User starts read and write tasks # **** API **** async def await_msg(self): while True: line = await self.sreader.readline() h, p = chr(line[0]), line[1:] # Header char, payload if h == 'n': # Normal message self.rxmsg.set(p) elif h == 'b': asyncio.create_task(self.bad_wifi()) elif h == 's': asyncio.create_task(self.bad_server()) elif h == 'r': asyncio.create_task(self.report(ujson.loads(p))) elif h == 'k': self.tim_boot.trigger(4000) # hold off reboot (4s) elif h in ('u', 'd'): up = h == 'u' self._status = up asyncio.create_task(self.server_ok(up)) else: raise ValueError('Unknown header:', h) async def write(self, line, qos=True, wait=True): ch = chr(0x30 + ((qos << 1) | wait)) # Encode args fstr = '{}{}' if line.endswith('\n') else '{}{}\n' line = fstr.format(ch, line) try: await asyncio.wait_for(self.wlock.acquire(), 1) self.swriter.write(line) await self.swriter.drain() except asyncio.TimeoutError: # Lock is set because ESP has crashed self.verbose and print('Timeout getting lock: queueing line', line) # Send line later. Can avoid message loss, but this self.lqueue.append(line) # isn't a bomb-proof guarantee finally: if self.wlock.locked(): self.wlock.release() async def readline(self): await self.rxmsg line = self.rxmsg.value() self.rxmsg.clear() return line # Stopped getting keepalives. ESP8266 crash: prevent user code from writing # until reboot sequence complete async def reboot(self): self.verbose and print('AppBase reboot') if self.chan.reset is None: # No config for reset raise OSError('Cannot reset ESP8266.') asyncio.create_task(self.chan.reboot()) # Hardware reset board self.tim_boot.stop() # No more reboots if not self.wlock.locked(): # Prevent user writes await self.wlock.acquire() def close(self): self.verbose and print('Closing channel.') self.chan.close() def status(self): # Server status return self._status # **** For subclassing **** async def bad_wifi(self): await asyncio.sleep(0) raise OSError('No initial WiFi connection.') async def bad_server(self): await asyncio.sleep(0) raise OSError('No initial server connection.') async def report(self, data): await asyncio.sleep(0) print('Connects {} Count {} Mem free {}'.format(data[0], data[1], data[2])) async def server_ok(self, up): await asyncio.sleep(0) print('Server is {}'.format('up' if up else 'down'))
async def asend(self, files): msg = Message() # Message to wait for threads completion. # Reads out n-bytes from the current file. def r_data(file, ptr, sz, msg): try: with open(file) as s: s.seek(ptr) data = s.read(sz) tptr = s.tell() except: pass msg.set((data, tptr)) # Saves last read byte. def set_lb(tmpf, ptr, msg): with open(tmpf, 'w') as t: t.write(str(ptr)) msg.set() # Gets last read byte. def get_lb(tmpf, msg): try: with open(tmpf) as t: ptr = int(t.read()) except: ptr = 0 # File not exists. msg.set(ptr) # Backups the current daily file for asyncronous access. async def bkp_f(file): bkp = file.replace(file.split('/')[-1], BPFX + file.split('/')[-1]) async with f_lock: shutil.copyfile(file, bkp) return bkp # Gets file info. def stat_f(file, msg): fstat = os.stat(file) msg.set(fstat) def mk_file_hdr(sz): b = [] if sz == 128: b.append(ord(SOH)) elif sz == 1024: b.append(ord(STX)) b.extend([0x00, 0xff]) return bytearray(b) def mk_data_hdr(seq, sz): assert sz in (128, 1024), sz b = [] if sz == 128: b.append(ord(SOH)) elif sz == 1024: b.append(ord(STX)) b.extend([seq, 0xff - seq]) return bytearray(b) # Makes the checksum for the current packet. def mk_cksum(data, crc_mode, msg): def calc_cksum(data, cksum=0): return (sum(map(ord, data)) + cksum) % 256 #Calculates the 16 bit Cyclic Redundancy Check for a given block of data. def calc_crc(data, crc=0): for c in bytearray(data): crctbl_idx = ((crc >> 8) ^ c) & 0xff crc = ((crc << 8) ^ CRC_TAB[crctbl_idx]) & 0xffff return crc & 0xffff b = [] if crc_mode: crc = calc_crc(data) b.extend([crc >> 8, crc & 0xff]) else: crc = calc_cksum(data) b.append(crc) msg.set(bytearray(b)) # Archives totally sent files. def totally_sent(file, sntf, tmpf): def is_new_day(file): today = time.time() - time.time() % 86400 try: last_file_write = os.stat( file)[8] - os.stat(file)[8] % 86400 if today - last_file_write >= 86400: return True return False except: return False if is_new_day(file): try: os.rename(file, sntf) try: os.remove(tmpf) except: verbose('UNABLE TO REMOVE FILE {}'.format(tmpf)) except: verbose('UNABLE TO RENAME FILE {}'.format(file)) # Clear to send. async def cts(): ec = 0 while True: if ec > self.retry: verbose('TOO MANY ERRORS, ABORTING...') return False c = await self.agetc(1, self.tout) if not c: verbose('TIMEOUT OCCURRED, RETRY...') ec += 1 elif c == C: verbose('<-- C') return True else: verbose('UNATTENDED CHAR {}, RETRY...'.format(c)) ec += 1 await asyncio.sleep(0) ######################################################################## # Transaction starts here ######################################################################## try: sz = dict(Ymodem=128, Ymodem1k=1024)[self.mode] # Packet size. except KeyError: raise ValueError('INVALID MODE {}'.format(self.mode)) # # Waits for receiver. # ec = 0 # Error counter. verbose('BEGIN TRANSACTION, PACKET SIZE {}'.format(sz)) while True: if ec > self.retry: verbose('TOO MANY ERRORS, ABORTING...') return False c = await self.agetc(1, self.tout) if not c: verbose( 'TIMEOUT OCCURRED WHILE WAITING FOR STARTING TRANSMISSION, RETRY...' ) ec += 1 elif c == C: verbose('<-- C') verbose('16 BIT CRC REQUESTED') crc_mode = 1 break elif c == NAK: verbose('<-- NAK') verbose('STANDARD CECKSUM REQUESTED') crc_mode = 0 break else: verbose('UNATTENDED CHAR {}, RETRY...'.format(c)) ec += 1 await asyncio.sleep(0) # # Iterates over file list. # fc = 0 # File counter. for f in files: # Temporary files store only the count of sent bytes. tmpf = f.replace(f.split('/')[-1], TPFX + f.split('/')[-1]) # Sent files get renamed in order to be archived. sntf = f.replace(f.split('/')[-1], SPFX + f.split('/')[-1]) fname = f.split('/')[-1] if f != '\x00': if f.split('/')[-1] == self.daily: # Daily file gets copied before being sent. f = await bkp_f(f) _thread.start_new_thread(get_lb, (tmpf, msg)) await asyncio.sleep_ms(10) await msg ptr = msg.value() msg.clear() if ptr == int(os.stat(f)[6]): # Check if eof. verbose('FILE {} ALREADY TRANSMITTED, SEND NEXT FILE...'. format(fname)) totally_sent(f, sntf, tmpf) continue fc += 1 # # If multiple files waits for clear to send. # if fc > 1: if not await cts(): return False # # Create file name packet # hdr = mk_file_hdr(sz) data = bytearray(fname + '\x00', 'utf8') # self.fname + space if f != '\x00': _thread.start_new_thread(stat_f, (f, msg)) await asyncio.sleep_ms(10) await msg fstat = msg.value() msg.clear() data.extend((str(fstat[6] - ptr) + ' ' + str( fstat[8])).encode('utf8')) # Sends data size and mod date. pad = bytearray(sz - len(data)) # Fills packet size with nulls. data.extend(pad) _thread.start_new_thread(mk_cksum, (data, crc_mode, msg)) await asyncio.sleep_ms(10) await msg cksum = msg.value() msg.clear() await asyncio.sleep(0.1) ec = 0 while True: # # Sends filename packet. # while True: if ec > self.retry: verbose('TOO MANY ERRORS, ABORTING...') return False if not await self.aputc(hdr + data + cksum, self.tout): ec += 1 await asyncio.sleep(0) continue verbose('SENDING FILE {}'.format(fname)) break # # Waits for reply to filename paket. # cc = 0 # Cancel counter. ackd = 0 # Acked. while True: if ec > self.retry: verbose('TOO MANY ERRORS, ABORTING...') return False c = await self.agetc(1, self.tout) if not c: # handle rx erros verbose('TIMEOUT OCCURRED, RETRY...') ec += 1 await asyncio.sleep(0) continue elif c == ACK: verbose('<-- ACK TO FILE {}'.format(fname)) if data == bytearray(sz): verbose('TRANSMISSION COMPLETE, EXITING...') return True else: ackd = 1 break elif c == CAN: verbose('<-- CAN') if cc: verbose('TRANSMISSION CANCELED BY RECEIVER') return False else: cc = 1 await asyncio.sleep(0) continue # Waits for a second CAN else: verbose('UNATTENDED CHAR {}, RETRY...'.format(c)) ec += 1 break # Resends packet. if ackd: break # Waits for data. if f == '\x00': return True # # Waits for clear to send. # if not await cts(): return False # # Sends file. # sc = 0 # Succeded counter. pc = 0 # Packets counter. seq = 1 while True: _thread.start_new_thread(r_data, (f, ptr, sz, msg)) await asyncio.sleep_ms(10) await msg data, tptr = msg.value() msg.clear() if not data: verbose('EOF') break pc += 1 hdr = mk_data_hdr(seq, sz) fst = '{:' + PAD.decode('utf-8') + '<' + str( sz) + '}' # Right fills data with pad byte. data = fst.format(data) data = data.encode('utf8') _thread.start_new_thread(mk_cksum, (data, crc_mode, msg)) await asyncio.sleep_ms(10) await msg cksum = msg.value() msg.clear() ec = 0 while True: # # Send data packet. # while True: if ec > self.retry: verbose('TOO MANY ERRORS, ABORTING...') return False if not await self.aputc(hdr + data + cksum, self.tout): ec += 1 await asyncio.sleep(0) continue # Resend packet. else: verbose('PACKET {} -->'.format(seq)) break # # Waits for reply. # cc = 0 ackd = 0 while True: if ec > self.retry: verbose('TOO MANY ERRORS, ABORTING...') return False c = await self.agetc(1, self.tout) if not c: # handle rx errors verbose('TIMEOUT OCCURRED, RETRY...') ec += 1 break elif c == ACK: verbose('<-- ACK TO PACKET {}'.format(seq)) ptr = tptr # Updates pointer. _thread.start_new_thread(set_lb, (tmpf, ptr, msg)) await asyncio.sleep_ms(10) await msg msg.clear() ackd = 1 sc += 1 seq = (seq + 1) % 0x100 break elif c == NAK: verbose('<-- NAK') ec += 1 break # Resends packet. elif c == CAN: verbose('<-- CAN') if cc: verbose('TRANSMISSION CANCELED BY RECEIVER') return False else: cc = 1 await asyncio.sleep(0) continue # Waits for a second CAN. else: verbose('UNATTENDED CHAR {}, RETRY...'.format(c)) ec += 1 break # Resends last packet. await asyncio.sleep(0) if ackd: break # Sends next packet # # End of transmission. # ec = 0 while True: if ec > self.retry: verbose('TOO MANY ERRORS, ABORTING...') return False if not await self.aputc(EOT, self.tout): ec += 1 await asyncio.sleep(0) continue # resend EOT verbose('EOT -->') c = await self.agetc(1, self.tout) # waiting for reply if not c: # handle rx errors verbose( 'TIMEOUT OCCURRED WHILE WAITING FOR REPLY TO EOT, RETRY...' ) ec += 1 elif c == ACK: verbose('<-- ACK TO EOT') verbose('FILE {} SUCCESSFULLY TRANSMITTED'.format(fname)) totally_sent(f, sntf, tmpf) break # Sends next file. else: verbose('UNATTENDED CHAR {}, RETRY...'.format(c)) ec += 1 await asyncio.sleep(0)
async def arecv(self, crc_mode=1): msg = Message() # Message to wait for threads completion. def finalize(file, length): tmp = file.replace(file.split('/')[-1], TPFX + file.split('/')[-1]) bkp = file.replace(file.split('/')[-1], BPFX + file.split('/')[-1]) sz = int(os.stat(tmp)[6]) + self.nulls if sz == length: try: os.rename(file, bkp) # Backups existing file. except: verbose('FILE {} NOT EXISTS'.format(file)) try: os.rename(tmp, file) except: verbose('UNABLE TO COMMIT FILE {}'.format(tmp)) os.remove(tmp) os.rename(bkp, file) # Restore original file. else: try: os.remove(tmp) except: verbose('UNABLE TO REMOVE FILE {}'.format(tmp)) # Writes out data to the passed file. # Runs in a separate thread to not block scheduler. def w_data(file, data, msg): tmp = file.replace(file.split('/')[-1], TPFX + file.split('/')[-1]) try: with open(tmp, 'ab') as s: self.nulls = data.count(PAD) s.write(data.replace(PAD, NULL)) msg.set(True) except: verbose('ERROR OPENING {}'.format(tmp)) msg.set(False) async def cancel(): verbose('CANCEL TRANSMISSION...') for _ in range(2): await self.aputc(CAN, 60) verbose('CAN -->') await asyncio.sleep(1) async def ack(): ec = 0 while True: if ec > self.retry: verbose('TOO MANY ERRORS, ABORTING...') return False if not await self.aputc(ACK, self.tout): verbose('ERROR SENDING ACK, RETRY...') ec += 1 else: verbose('ACK -->') return True await asyncio.sleep(0) async def nak(): ec = 0 while True: if ec > self.retry: verbose('TOO MANY ERRORS, ABORTING...') return False if not await self.aputc(NAK, self.tout): verbose('ERROR SENDING NAK, RETRY...') ec += 1 else: verbose('NAK -->') return True await asyncio.sleep(0) # Clear to receive. async def ctr(): ec = 0 while True: if ec > self.retry: verbose('TOO MANY ERRORS, ABORTING...') return False if not await self.aputc(C, self.tout): verbose('ERROR SENDING C, RETRY...') ec += 1 else: verbose('C -->') return True await asyncio.sleep(0) # Validate checksum. async def v_cksum(data, crc_mode): async def calc_cksum(data, cksum=0): return (sum(map(ord, data)) + cksum) % 256 # Calculates the 16 bit Cyclic Redundancy Check for a given block of data. async def calc_crc(data, crc=0): for c in bytearray(data): crctbl_idx = ((crc >> 8) ^ c) & 0xff crc = ((crc << 8) ^ CRC_TAB[crctbl_idx]) & 0xffff await asyncio.sleep(0) return crc & 0xffff if crc_mode: cksum = bytearray(data[-2:]) recv = (cksum[0] << 8) + cksum[1] data = data[:-2] calc = await calc_crc(data) valid = bool(recv == calc) if not valid: verbose('CRC FAIL EXPECTED({:04x}) GOT({:4x})'.format( recv, calc)) else: cksum = bytearray([data[-1]]) recv = cksum[0] data = data[:-1] calc = await calc_cksum(data) valid = recv == calc if not valid: verbose('CHECKSUM FAIL EXPECTED({:02x}) GOT({:2x})'.format( recv, calc)) return valid, data ######################################################################## # Transaction starts here ######################################################################## ec = 0 # Error counter. verbose('REQUEST 16 BIT CRC') while True: if crc_mode: while True: if ec == (self.retry // 2): verbose('REQUEST STANDARD CHECKSUM') crc_mode = 0 break if not await self.aputc( C ): # Sends C to request 16 bit CRC as first choice. verbose('ERROR SENDING C, RETRY...') ec += 1 await asyncio.sleep(0) else: verbose('C -->') break if not crc_mode and ec < self.retry: if not await nak( ): # Sends NAK to request standard checksumum as fall back. return False # # Receives packets. # sz = 128 # Packet size. cc = 0 # Cancel counter. seq = 0 # Sequence counter. isz = 0 # Income size. while True: c = await self.agetc(1, self.tout) if ec == self.retry: verbose('TOO MANY ERRORS, ABORTING') await cancel() # Cancels transmission. return False elif not c: verbose('TIMEOUT OCCURRED WHILE RECEIVING') ec += 1 break # Resends start byte. elif c == CAN: verbose('<-- CAN') if cc: verbose('TRANSMISSION CANCELED BY SENDER') return False else: cc = 1 ec = 0 # Ensures to receive a second CAN. elif c == SOH: verbose('SOH <--') if sz != 128: sz = 128 verbose('USING 128 BYTES PACKET SIZE') elif c == STX: verbose('STX <--') if sz != 1024: sz = 1024 verbose('USING 1 KB PACKET SIZE') elif c == EOT: verbose('EOT <--') if not await ack(): # Acknowledges EOT. return False finalize(fname, length) seq = 0 isz = 0 if not await ctr(): # Clears to receive. return False ec = 0 await asyncio.sleep(0) continue else: verbose('UNATTENDED CHAR {}'.format(c)) ec += 1 await asyncio.sleep(0) continue # # Reads packet sequence. # ec = 0 while True: seq1 = await self.agetc(1, self.tout) if not seq1: verbose('FAILED TO GET FIRST SEQUENCE BYTE') seq2 = None else: seq1 = ord(seq1) seq2 = await self.agetc(1, self.tout) if not seq2: verbose('FAILED TO GET SECOND SEQUENCE BYTE') else: seq2 = 0xff - ord(seq2) verbose('PACKET {} <--'.format(seq)) if not (seq1 == seq2 == seq): verbose( 'SEQUENCE ERROR, EXPECTED {} GOT {}, DISCARD DATA'. format(seq, seq1)) await self.agetc(sz + 1 + crc_mode ) # Discards data packet. if seq1 == 0: # If receiving file name packet, clears for transmission. if not await ctr(): return False ec = 0 else: data = await self.agetc(sz + 1 + crc_mode, self.tout) valid, data = await v_cksum(data, crc_mode) if not valid: if not await nak(): # Requests retransmission. return False ec = 0 else: if seq == 0: # Sequence 0 contains file name. if data == bytearray( sz ): # Sequence 0 with null data state end of trasmission. if not await ack(): # Acknowledges EOT. return False await asyncio.sleep(1) verbose('END OF TRANSMISSION') return True ds = [] # Data string. df = '' # Data field. for b in data: if b != 0: df += chr(b) elif len(df) > 0: ds.append(df) df = '' fname = ds[0] length = int(ds[1].split(' ')[0]) verbose('RECEIVING FILE {}'.format(fname)) if not await ack(): # Acknowledges packet. return False if not await ctr(): # Clears for transmission. return False ec = 0 else: tn = isz - length # Counts trailing null chars. _thread.start_new_thread( w_data, (fname, data[:-tn], msg)) await asyncio.sleep_ms(10) await msg if not msg.value(): # Error opening file. if not await nak( ): # Requests retransmission. return False ec += 1 else: if not await ack(): return False isz += len(data) ec = 0 msg.clear() seq = (seq + 1) % 0x100 # Calcs next expected seq. break
async def main(msg, uart, objs): global interactive board = False devices = False device = False dev = None logger = False # Stops log stream. fw = Message() # Forwards main uart to device. while True: await msg fw.set(msg.value()) if not board: if not devices: if not device: if msg.value() == ESC: board = True await board_menu() else: # device if msg.value() == b'0': dev.toggle() await device_menu(dev) elif msg.value() == b'1': fw.clear() # Clears last byte. asyncio.create_task(pass_through(dev, uart, fw)) #await device_menu(dev) elif msg.value() == b'2': pass elif msg.value() == b'3': await get_config(dev) await device_menu(dev) elif msg.value() in BACKSPACE: device = False devices = True await devices_menu(objs) elif msg.value() == ESC: await device_menu(dev) else: # devices if msg.value() == ESC: await devices_menu(objs) elif msg.value() in BACKSPACE: devices = False board = True await board_menu() else: devices = False device = True try: dev = objs[int(msg.value())] await device_menu(dev) except: await devices_menu(objs) else: # board if msg.value() == ESC: board = True await board_menu() elif msg.value() == b'0': board = False devices = True await devices_menu(objs) elif msg.value() == b'1': await data_files() await board_menu() elif msg.value() == b'2': await last_log() await board_menu() elif msg.value() in BACKSPACE: interactive = False logger = True msg.clear() return msg.clear() await asyncio.sleep(0)