def sync(self): """\ Send the sync character and wait for an acknowledge. The sync procedure is retried if it fails once or twice. """ self.logger.debug('Sync...') if self.blindWrite: self.serial.write(bsl.BSL_SYNC) time.sleep(0.030) else: for tries in '210': self.serial.flushInput() self.serial.write(bsl.BSL_SYNC) ack = self.serial.read(1) if ack == bsl.DATA_ACK: self.logger.debug('Sync OK') return else: if tries != '0': self.logger.debug('Sync failed, retry...') # if something was received, ensure that a small delay is made if ack: time.sleep(0.2) self.logger.error('Sync failed, aborting...') raise bsl.BSLTimeout('could not sync')
def bsl(self, cmd, message='', expect=None): """\ Low level access to the serial communication. This function sends a command and waits until it receives an answer (including timeouts). It will return a string with the data part of the answer (an empty string for simple DATA_ACKs). In case of a failure read timeout or rejected commands by the slave, it will raise an exception. If the parameter "expect" is not None, "expect" bytes are expected in the answer, an exception is raised if the answer length does not match. If "expect" is None, DATA_ACK and DATA_FRAME are accepted and the answer is just returned. Frame format: +-----+-----+----+----+-----------+----+----+ | HDR | CMD | L1 | L2 | D1 ... DN | CL | CH | +-----+-----+----+----+-----------+----+----+ """ # first synchronize with slave self.sync() self.logger.debug('Command 0x{:02x} {}'.format(cmd, message.encode('hex'))) # prepare command with checksum txdata = struct.pack('<cBBB', bsl.DATA_FRAME, cmd, len(message), len(message)) + message txdata += struct.pack('<H', self.checksum(txdata) ^ 0xffff) # append checksum #~ self.logger.debug('Sending command: %r' % (txdata,)) # transmit command self.serial.write(txdata) # wait for command answer if self.blindWrite: time.sleep(0.100) return if self.ignore_answer: return self.logger.debug('Reading answer...') if self.extra_timeout is None: ans = self.serial.read(1) else: for timeout in range(self.extra_timeout): ans = self.serial.read(1) if ans: break # depending on answer type, read more, raise exceptions etc. if ans == '': raise bsl.BSLTimeout('timeout while reading answer (ack)') elif ans == bsl.DATA_NAK: self.logger.debug('Command failed (DATA_NAK)') raise bsl.BSLError('command failed (DATA_NAK)') elif ans == bsl.CMD_FAILED: self.logger.debug('Command failed (CMD_FAILED)') raise bsl.BSLError('command failed (CMD_FAILED)') elif ans == bsl.DATA_ACK: self.logger.debug('Simple ACK') if expect is not None and expect > 0: raise bsl.BSLError('expected data, but received a simple ACK') return '' elif ans == bsl.DATA_FRAME: self.logger.debug('Data frame...') head = self.serial.read(3) if len(head) != 3: raise bsl.BSLTimeout('timeout while reading answer (header)') (self.dummy, l1, l2) = struct.unpack('<BBB', head) if l1 != l2: raise bsl.BSLError('broken answer (L1 != L2)') if l1: data = self.serial.read(l1) if len(data) != l1: raise bsl.BSLTimeout('timeout while reading answer (data)') else: data = '' checksum = self.serial.read(2) if len(checksum) != 2: raise bsl.BSLTimeout('timeout while reading answer (checksum)') if self.checksum(ans + head + data) ^ 0xffff == struct.unpack( "<H", checksum)[0]: if expect is not None and len(data) != expect: raise bsl.BSLError( 'expected {} bytes, got {} bytes'.format( expect, len(data))) self.logger.debug('Data frame: {}'.format(data.encode('hex'))) return data else: raise bsl.BSLError('checksum error in answer') else: self.logger.debug('unexpected answer {!r}'.format(ans)) raise bsl.BSLError('unexpected answer: {!r}'.format(ans))