rendered paste body#!/usr/bin/pythonimport serial,threading,Queue,struct,sys,arrayclass BotLoadIOException(Exception): passclass TimeoutException(BotLoadIOException): passclass ProtocolException(BotLoadIOException): passclass StateException(BotLoadIOException): passclass CommandQueue: def __init__(self, serial): self.serial = serial self.cmdqueue = Queue.Queue() self.error = None self.errorFetched = False self.finished = False self.finishedFetched = False self.abortRequested = False self.discarded = 0 self.parentThread = threading.currentThread() self.thread = threading.Thread(target=self._service_thread) self.queueEmpty = threading.Event() self.queueEmpty.set() self.queueItems = 0 self.queueItemsLock = threading.Lock() def _service_thread(self): while self.error == None and not self.finished and not self.abortRequested and self.parentThread.isAlive(): try: cmd, final = self.cmdqueue.get(True,0.4) try: try: self._send_command(final, cmd) if final: self.finished = True except BotLoaderException,e: self.error = e finally: self._updateQueueLength(-1) except Queue.Empty: try: self._send_command(False, "N") except BotLoaderException,e: self.error = e if self.abortRequested or not self.parentThread.isAlive(): return while (self.error != None and self.errorFetched == False) or (self.finished and self.finishedFetched == False): try: self.cmdqueue.get(True,0.1) self._updateQueueLength(-1) self.discarded += 1 except Queue.Empty: pass def _send_command(self, final, cmd): self.serial.write(cmd) if not final: res = self.serial.read(1) if len(res) != 1: raise TimeoutException("Timeout in command '%s' (0x%02X)"%(cmd[0],ord(cmd[0]))) if res != "K": if res == "!": raise ProtocolException("CRC Error in command '%s' (0x%02X)"%(cmd[0],ord(cmd[0]))) elif res == "?": raise ProtocolException("Unrecognized command '%s' (0x%02X)"%(cmd[0],ord(cmd[0]))) else: raise ProtocolException("Unknown error: %s (0x%02X) in command '%s' (0x%02X)"%(res,ord(res),cmd[0],ord(cmd[0]))) def _updateQueueLength(self, delta): self.queueItemsLock.acquire() self.queueItems += delta if self.queueItems > 0: self.queueEmpty.clear() else: self.queueEmpty.set() self.queueItemsLock.release() def start(self): self.thread.start() def sendCommand(self, cmd, final, blocking=True): if blocking: self.join() if self.finishedFetched: raise StateException("Dialog finished, no further commands allowed") if self.errorFetched: raise StateException("Errors occured, no further commands allowed") self._updateQueueLength(1) self.cmdqueue.put((cmd,final),True) if blocking: self.join() def join(self): self.queueEmpty.wait() if self.error != None: self.errorFetched = True raise self.error if self.finished == True: self.finishedFetched = True def finished(self): return self.finishedFetched def errorOccured(self): return self.errorFetched def abort(self): self.abortRequested = True self.thread.join()class BotLoaderInterface: def __init__(self, port, retries=10): self.retries = retries if isinstance(port,serial.Serial): self.serial = port else: self.serial = serial.Serial(port,115200,timeout=0.5) def start(self,retries=None): if retries == None: retries = self.retries rnum = 0 valid = False picid = 0 try: while rnum < retries: sys.stdout.write("\x0dConnecting to bootloader (attempt %d of %d)..."%(rnum+1,retries)) sys.stdout.flush() self.serial.write("BL") res = self.serial.read(1000000) if len(res) >= 3 and res[-1]=="K": valid = True picid = struct.unpack("<H",res[-3:-1]) sys.stdout.write(" Connected, device is 0x%04X"%picid) break rnum += 1 finally: print if not valid: raise TimeoutException("Maximum retries reached while connecting to bootloader") if picid != 0x1105: raise ValueError("Unsupported PIC: %04X",picid) self.serial.timeout = 0.5 self.queue = CommandQueue(self.serial) self.queue.start() self.picid = picid return picid def abort(self): self.queue.abort() self.serial.close() def runUser(self): self.queue.sendCommand("R", True) def writeEEPROM(self, address, data): if not 0x00 <= address <= 0xFF: raise ValueError("EEPROM address out of range: 0x%X"%address) if not 0x00 <= data <= 0xFF: raise ValueError("EEPROM data out of range: %d"%data) crc = (-(address+data))&0xFF cmd = struct.pack("cBBB","E",address,data,crc) self.queue.sendCommand(cmd, False) def writeConfig(self, address, data): if not 0x300000 <= address <= 0x30000F: raise ValueError("CONFIG address out of range: 0x%X"%address) if not 0x00 <= data <= 0xFF: raise ValueError("CONFIG data out of range: %d"%data) addu = (address>>16) & 0xFF addh = (address>>8) & 0xFF addl = address & 0xFF crc = (-(addu+addh+addl+data))&0xFF cmd = struct.pack("cBBBBB","C",addu,addh,addl,data,crc) self.queue.sendCommand(cmd, False) def writeFlash(self, address, data): if not 0 <= address <= 0x7FC0: raise ValueError("Flash address out of range: 0x%X"%address) if address&0x40 != 0: raise ValueError("Flash address not aligned: 0x%X"%address) if len(data) != 0x40: raise ValueError("Flash data length != 64: %d"%len(data)) for a,i in enumerate(data): if not 0x00 <= i <= 0xFF: raise ValueError("Flash data out of range: %d (address 0x%04X)"%(i,address+a)) addu = (address>>16) & 0xFF addh = (address>>8) & 0xFF addl = address & 0xFF crc = (-(addu+addh+addl+sum(data)))&0xFF cmd = struct.pack("cBBB","F",addu,addh,addl) cmd += array.array("B",data+[crc]).tostring() self.queue.sendCommand(cmd, False)