#!/usr/bin/env python3 #Copyright 2021 @ github.com/catSIXe import time import serial import binascii class TPLBirdySlim: port = None def __init__(self, serialPortName): self.port = serial.Serial( port=serialPortName, baudrate=57600, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=30, xonxoff=False, rtscts=False, dsrdtr=False ) print(self.port.name) if self.port.is_open: self.port.close() self.port.open() self.port.reset_input_buffer() self.port.reset_output_buffer() def _changeBaudrate(self, newRate): if self.port.is_open: self.port.close() self.port.baudrate = newRate self.port.open() self.port.reset_input_buffer() self.port.reset_output_buffer() def checksum(self, data): cs = 0x00 for x in data: cs = (cs + x % 256) & 0xFF return cs & 0xFF def sendCMD(self, data): cmd = [] cmd.append(0x02) cmd.append(len(data)) cmd.extend(data) cmd.append(0x03) cmd.append(self.checksum(cmd.copy())) cmd.append(0x04) print(">", binascii.hexlify(bytes(cmd))) self.port.write(cmd) self.port.flush() def readSingleByte(self): return int.from_bytes(self.port.read(1), 'little') def wait4Response(self): #first Byte is always 0x02 if self.readSingleByte() != 0x02: return None dataLength = self.readSingleByte() reply = [] for x in range(dataLength): reply.append(self.readSingleByte()) if self.readSingleByte() != 0x03: return None pCRC = self.readSingleByte() pCRCData = [0x02, dataLength] pCRCData.extend(reply) pCRCData.extend([0x03]) rCRC = self.checksum(pCRCData) if self.readSingleByte() != 0x04: return None if pCRC != rCRC: return None # crc mismatch return reply def askHighspeed(self): self.sendCMD([0x25,0x01]) return self.wait4Response() def switchToHighspeed(self): resp = self.askHighspeed() if len(resp) == 1 and resp[0] == 0x06: self._changeBaudrate(115200) return True else: return False def readE2P(self, addr, size=0x80): _addr = addr.to_bytes(2, byteorder='big') _cmd = [0x29, 0x00, 0x00, _addr[0], _addr[1], size] self.sendCMD(_cmd) resp = self.wait4Response() print(">", binascii.hexlify(bytes(resp))) if resp[0:5] != _cmd[0:5]: print(resp[0:5], _cmd[0:5]) return None # mismatch return resp[5:] def writeE2P(self, addr, data): _addr = addr.to_bytes(2, byteorder='big') _cmd = [0x2A, 0x00, 0x00, _addr[0], _addr[1]] _cmd.extend(data) self.sendCMD(_cmd) resp = self.wait4Response() if resp[0:4] != _cmd[0:4]: print("resp mismatch", resp, _cmd) return False # mismatch if resp[5] != len(data): print("length mismatch") return False # mismatch if resp[6:] != _cmd[5:]: print("resp mismatch", resp, _cmd) return False # mismatch return True #def boskrypt_mode(self): # self.sendCMD([0xF3,0xF3]) def getDeviceInfo(self): self.sendCMD([0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff]) return self.wait4Response() def writeSerialNumber(self, newSerial): _cmd = [0x13] _cmd.extend(newSerial) self.sendCMD(_cmd) resp = self.wait4Response() if _cmd[2:] != resp[2:]: return False return True def checkE2PData(self, data): return len(data) / 0x80 == 0xCD test = TPLBirdySlim('/dev/ttyUSB0') #test.switchBootloaderSpeed() #test._changeBaudrate(38700) #print(bytes(test.getDeviceInfo()).decode('ascii')) #test.sendCMD([0x01,0x12]) #test.sendCMD([0x18,0x08]) #test.sendCMD([0x10,0x08]) #print(bytes(test.wait4Response())) #test.writeSerialNumber([0x42,0x36,0x31,0x33,0x33,0x37,0x31,0x33,0x33,0x37,0x31,0x33,0x33,0x37]) #print(bytes(test.getDeviceInfo()).decode('ascii')) #test.sendCMD([0x13,0x43,0x48,0x45,0x45,0x54,0x41,0x48,0x43,0x48,0x45,0x45,0x54,0x41,0x48]) #print( test.switchToHighspeed() #) """ e2pread = [] for i in range(0xCD): e2pread.extend(test.readE2P(i*0x80)) with open("dump_434_232500.bin", "wb") as e2pfile: e2pfile.write(bytes(e2pread)) """ e2pdata = [] #with open("BrainwvaebusinessDefualt.birdyIOT.rev2.bin", "rb") as e2pfile: with open("dump2.bin", "rb") as e2pfile: e2pdata = e2pfile.read() if test.checkE2PData(e2pdata): for i in range(0xCD): chunk = e2pdata[ 0x80*i : 0x80*(i+1) ] print(test.writeE2P(i*0x80, chunk)) #print(e2pdata) #print(test.port.read(62))