def start_reading(self): readBuffer = UartReadBuffer() self.started = True _LOGGER.debug(F"Read starting on serial port.{self.port} {self.running}") try: while self.running: bytesFromSerial = self.serialController.read() if bytesFromSerial: # clear out the entire read buffer if self.serialController.in_waiting > 0: additionalBytes = self.serialController.read(self.serialController.in_waiting) bytesFromSerial = bytesFromSerial + additionalBytes readBuffer.addByteArray(bytesFromSerial) # print("Cleaning up UartBridge") except OSError or serial.SerialException: _LOGGER.info("Connection to USB Failed. Retrying...") except KeyboardInterrupt: self.running = False _LOGGER.debug("Closing serial connection.") # close the serial controller self.serialController.close() self.serialController = None # remove the event listener pointing to the old connection UartEventBus.unsubscribe(self.eventId) self.started = False UartEventBus.emit(SystemTopics.connectionClosed, True)
def initialize_usb_sync(self, port=None, baudrate=230400, writeChunkMaxSize=0): ''' writing in chunks solves issues writing to certain JLink chips. A max chunkSize of 64 was found to work well for our case. For normal usage with Crownstones this is not required. writeChunkMaxSize of 0 will not send the payload in chunks :param port: :param baudrate: :param writeChunkMaxSize: :return: ''' self.uartManager.config(port, baudrate, writeChunkMaxSize) result = [False] def handleMessage(result, data): result[0] = True event = UartEventBus.subscribe( SystemTopics.connectionEstablished, lambda data: handleMessage(result, data)) self.uartManager.start() try: while not result[0] and self.running: time.sleep(0.1) except KeyboardInterrupt: print("\nClosing Crownstone Uart.... Thanks for your time!") self.stop() UartEventBus.unsubscribe(event)
def parse(self, wrapperPacket: UartWrapperPacket): """ Callback for SystemTopics.uartNewPackage, emits a message of type SystemTopic.uartNewMessage if the received message was of the correct protocol version and unencrypted. :param wrapperPacket: :return: """ if type(wrapperPacket) is not UartWrapperPacket: raise TypeError if wrapperPacket.protocolMajor != PROTOCOL_MAJOR: _LOGGER.warning( F"Unknown protocol: {wrapperPacket.protocolMajor}.{wrapperPacket.protocolMinor}" ) return msgType = wrapperPacket.messageType if msgType == UartMessageType.UART_MESSAGE: uartMsg = UartMessagePacket() if uartMsg.parse(wrapperPacket.payload): UartEventBus.emit(SystemTopics.uartNewMessage, uartMsg) elif msgType == UartMessageType.ENCRYPTED_UART_MESSAGE: _LOGGER.info( f"Received encrypted msg: decryption is not implemented.") return else: _LOGGER.warning(F"Unknown message type: {msgType}") return
def _switch_crownstone(self, crownstone_id: int, switch_val: int): """ :param crownstone_id: :param switch_val: 0% .. 100% or special value (SwitchValSpecial). :return: """ # create a stone switch state packet to go into the multi switch stoneSwitchPacket = StoneMultiSwitchPacket(crownstone_id, switch_val) # wrap it in a mesh multi switch packet meshMultiSwitchPacket = MeshMultiSwitchPacket([stoneSwitchPacket ]).getPacket() # wrap that in a control packet controlPacket = ControlPacket(ControlType.MULTISWITCH).loadByteArray( meshMultiSwitchPacket).getPacket() # wrap that in a uart message uartMessage = UartMessagePacket(UartTxType.CONTROL, controlPacket).getPacket() # finally wrap it in a uart wrapper packet uartPacket = UartWrapperPacket(UartMessageType.UART_MESSAGE, uartMessage).getPacket() # send over uart UartEventBus.emit(SystemTopics.uartWriteData, uartPacket)
def process(self): """ Process a buffer. Check CRC, and emit a uart packet. Buffer starts after size header, and includes wrapper header and tail (CRC). Return: processing success """ # Check size bufferSize = len(self.buffer) wrapperSize = WRAPPER_HEADER_SIZE + CRC_SIZE if bufferSize < wrapperSize: _LOGGER.warning("Buffer too small") return False # Get the buffer between size field and CRC: baseBuffer = self.buffer[0 : bufferSize - CRC_SIZE] # Check CRC calculatedCrc = crc16ccitt(baseBuffer) sourceCrc = Conversion.uint8_array_to_uint16(self.buffer[bufferSize - CRC_SIZE : ]) if calculatedCrc != sourceCrc: _LOGGER.warning("Failed CRC: {0} != {1} (data: {2})".format(calculatedCrc, sourceCrc, baseBuffer)) UartEventBus.emit(DevTopics.uartNoise, "crc mismatch") return False wrapperPacket = UartWrapperPacket() if wrapperPacket.parse(baseBuffer): UartEventBus.emit(SystemTopics.uartNewPackage, wrapperPacket) return True
async def _command_via_mesh_broadcast(self, packet: bytearray): # this is only for time and noop # broadcast to all: # value: 1 corePacket = MeshBroadcastPacket(packet).getPacket() controlPacket = ControlPacket( ControlType.MESH_COMMAND).loadByteArray(corePacket).getPacket() uartMessage = UartMessagePacket(UartTxType.CONTROL, controlPacket).getPacket() uartPacket = UartWrapperPacket(UartMessageType.UART_MESSAGE, uartMessage).getPacket() resultCollector = Collector(timeout=2, topic=SystemTopics.resultPacket) # send the message to the Crownstone UartEventBus.emit(SystemTopics.uartWriteData, uartPacket) # wait for the collectors to fill commandResultData = await resultCollector.receive() if commandResultData is not None: if commandResultData.resultCode is ResultValue.BUSY: await asyncio.sleep(0.2) return await self._command_via_mesh_broadcast(packet) elif commandResultData.resultCode is not ResultValue.SUCCESS: raise CrownstoneException(commandResultData.resultCode, "Command has failed.") await asyncio.sleep(0.1)
def write_with_result_sync(self, success_codes : List[ResultValue]=None, result_timeout=0.25) -> ResultPacket: """ write_with_result_sync will take the data packet you have provided to the constructor and send it over UART. It will wait for an incoming result packet and return it once it has been received. You can optionally provide a list of ResultValue result codes. If the list is empty [] all resultPackets will be returned. Otherwise it will use your provided list to filter on. If you leave it None, we will default to ResultValue.SUCCESS. If a resultcode is not in the list, an CrownstoneException will be raised. This method is blocking. If the write fails, an CrownstoneException will be raised. :return: :param success_codes: List[ResultValue] :param result_timeout: time to wait on a result in seconds before raising a timeout error. This should be higher than the UART_WRITE_TIMEOUT :return: ResultPacket """ if success_codes is None: success_codes = [ResultValue.SUCCESS] UartEventBus.emit(SystemTopics.uartWriteData, self.dataToSend) counter = 0 while counter < result_timeout: if self.result: return self._checkResult(success_codes) time.sleep(self.interval) counter += self.interval self._wrapUpFailedResult(result_timeout)
def write_to_uart(self, data): _LOGGER.debug(f"write_to_uart: {data}") if self.serialController is not None and self.started: try: if self.writeChunkMaxSize == 0: self.serialController.write(data) else: # writing in chunks solves issues writing to certain JLink chips. A max chunkSize of 64 was found to work well for our case. chunkSize = self.writeChunkMaxSize index = 0 while (index*chunkSize) < len(data): chunkedData = data[index*chunkSize:chunkSize*(index+1)] index += 1 self.serialController.write(chunkedData) UartEventBus.emit(SystemTopics.uartWriteSuccess, data) except serial.SerialTimeoutException as e: UartEventBus.emit(SystemTopics.uartWriteError, {"message":"Timeout on uart write.", "error": e}) except serial.SerialException as e: UartEventBus.emit(SystemTopics.uartWriteError, {"message":"SerialException occurred during uart write", "error": e}) except OSError as e: UartEventBus.emit(SystemTopics.uartWriteError, {"message":"OSError occurred during uart write.", "error": e}) except: e = sys.exc_info()[0] UartEventBus.emit(SystemTopics.uartWriteError, {"message":"Unknown error during uart write.", "error": e}) else: self.stop()
def writeHello(self): helloPacket = UartCommandHelloPacket().getPacket() uartMessage = UartMessagePacket(UartTxType.HELLO, helloPacket).getPacket() uartPacket = UartWrapperPacket(UartMessageType.UART_MESSAGE, uartMessage).getPacket() UartEventBus.emit(SystemTopics.uartWriteData, uartPacket)
def echo(self, string): controlPacket = ControlPacket( ControlType.UART_MESSAGE).loadString(string).getPacket() uartMessage = UartMessagePacket(UartTxType.CONTROL, controlPacket).getPacket() uartPacket = UartWrapperPacket(UartMessageType.UART_MESSAGE, uartMessage).getPacket() UartEventBus.emit(SystemTopics.uartWriteData, uartPacket)
def add(self, byte): # An escape shouldn't be followed by a special byte. if self.escapingNextByte and (byte is START_TOKEN or byte is ESCAPE_TOKEN): _LOGGER.warning("Special byte after escape token") UartEventBus.emit(DevTopics.uartNoise, "special byte after escape token") self.reset() return # Activate on start token. if byte is START_TOKEN: if self.active: _LOGGER.warning("MULTIPLE START TOKENS") _LOGGER.debug( f"Multiple start tokens: sizeToRead={self.sizeToRead} bufLen={len(self.buffer)} buffer={self.buffer}" ) UartEventBus.emit(DevTopics.uartNoise, "multiple start token") self.reset() self.active = True return if not self.active: return # Escape next byte on escape token. if byte is ESCAPE_TOKEN: self.escapingNextByte = True return if self.escapingNextByte: byte ^= BIT_FLIP_MASK self.escapingNextByte = False self.buffer.append(byte) bufferSize = len(self.buffer) if self.sizeToRead == 0: # We didn't parse the size yet. if bufferSize == SIZE_HEADER_SIZE: # Now we know the remaining size to read self.sizeToRead = Conversion.uint8_array_to_uint16(self.buffer) # Size to read shouldn't be 0. if self.sizeToRead == 0: self.reset() return self.buffer = [] return elif bufferSize >= self.sizeToRead: self.process() self.reset() return
def handleStateUpdate(self, data): stoneId = data[0] advPayload = data[1] if stoneId in self.stones: if hasattr(advPayload, 'timestamp'): if self.stones[stoneId].timestamp < advPayload.timestamp: self.stones[stoneId] = advPayload self.emitNewData(advPayload) else: UartEventBus.emit(SystemTopics.newCrownstoneFound, stoneId) self.stones[stoneId] = advPayload self.emitNewData(advPayload)
def receive_sync(self): counter = 0 while counter < self.timeout: if self.response is not None: # cleanup the listener(s) UartEventBus.unsubscribe(self.cleanupId) return self.response time.sleep(self.interval) counter += self.interval UartEventBus.unsubscribe(self.cleanupId) return None
def setupConnection(self, port, performHandshake=True): _LOGGER.debug(F"Setting up connection... port={port} baudRate={self.baudRate} performHandshake={performHandshake}") bridge_exception_queue = queue.Queue() self._uartBridge = UartBridge(bridge_exception_queue, port, self.baudRate, self.writeChunkMaxSize) self._uartBridge.start() def initialize_bridge(): # handle exceptions that happen in thread while initializing while not self._uartBridge.started and self.running: try: exception, exception_value, trace = bridge_exception_queue.get(block=False) self._uartBridge.join() raise exception(exception_value) except queue.Empty: pass time.sleep(0.1) # attempt serial connection with the dongle initialize_bridge() # this is true by default if no handshake is required handshake_succesfull = True if performHandshake: collector = Collector(timeout=0.25, topic=UartTopics.hello) self.writeHello() reply = collector.receive_sync() handshake_succesfull = False if isinstance(reply, UartCrownstoneHelloPacket): handshake_succesfull = True _LOGGER.debug("Handshake successful") else: if self.port == port: _LOGGER.warning("Handshake failed: no reply from the crownstone.") else: _LOGGER.debug("Handshake failed: no reply from the crownstone.") if not handshake_succesfull: _LOGGER.debug("Reinitialization required") self._attemptingIndex += 1 self._uartBridge.stop() initialize_bridge() else: _LOGGER.info("Connection established to {}".format(port)) self.port = port self.ready = True UartEventBus.emit(SystemTopics.connectionEstablished)
def reset(self, discarded=False): """ Resets to initial state, emitting uartDiscardedData if necessary. :param discarded: True when reset due to discarded data. """ if discarded and len(self.rawBuffer): UartEventBus.emit(SystemTopics.uartDiscardedData, self.rawBuffer) self.rawBuffer = [] self.buffer = [] self.escapingNextByte = False self.active = False self.sizeToRead = 0
def __init__(self, dataToSend: List[int], interval = 0.001): """ This class will handle the event flow around writing to uart and receiving errors or result codes. :param dataToSend: This is your data packet :param interval: Polling interval. Don't touch. This is cheap and local only. It does not do uart things. """ self.dataToSend : List[int] = dataToSend self.interval = interval self.error = None self.success = False self.result = None self.cleanupIds = [] self.cleanupIds.append(UartEventBus.subscribe(SystemTopics.resultPacket, self._handleResult)) self.cleanupIds.append(UartEventBus.subscribe(SystemTopics.uartWriteSuccess, self._handleSuccess)) self.cleanupIds.append(UartEventBus.subscribe(SystemTopics.uartWriteError, self._handleError))
def __init__(self, topic=None, timeout=10, interval=0.05): self.response = None self.timeout = timeout self.interval = interval self.cleanupId = None if topic is not None: self.cleanupId = UartEventBus.subscribe(topic, self.collect)
def write_sync(self) -> True: """ write_sync will take the data packet you have provided to the constructor and send it over UART. This method is blocking. If the write fails, an CrownstoneException will be raised. :return: """ UartEventBus.emit(SystemTopics.uartWriteData, self.dataToSend) counter = 0 while counter < 2 * UART_WRITE_TIMEOUT: if self.success: # cleanup the listener(s) self.__del__() return True time.sleep(self.interval) counter += self.interval self._wrapUpFailedWrite()
def setupConnection(self, port, performHandshake=True): _LOGGER.debug( F"Setting up connection... port={port} baudRate={self.baudRate} performHandshake={performHandshake}" ) self._uartBridge = UartBridge(port, self.baudRate, self.writeChunkMaxSize) self._uartBridge.start() # wait for the bridge to initialize while not self._uartBridge.started and self.running: time.sleep(0.1) success = True if performHandshake: collector = Collector(timeout=0.25, topic=UartTopics.hello) self.writeHello() reply = collector.receive_sync() success = False if isinstance(reply, UartCrownstoneHelloPacket): success = True _LOGGER.debug("Handshake successful") else: if self.port == port: _LOGGER.warning( "Handshake failed: no reply from the crownstone.") else: _LOGGER.debug( "Handshake failed: no reply from the crownstone.") if not success: _LOGGER.debug("Reinitialization required") self._attemptingIndex += 1 self._uartBridge.stop() while self._uartBridge.started and self.running: time.sleep(0.1) else: _LOGGER.info("Connection established to {}".format(port)) self.port = port self.ready = True UartEventBus.emit(SystemTopics.connectionEstablished)
def initialize_usb_sync(self, port=None, baudrate=230400, writeChunkMaxSize=0): """ Initialize a Crownstone serial device. :param port: serial port of the USB. e.g. '/dev/ttyUSB0' or 'COM3'. :param baudrate: baudrate that should be used for this connection. default is 230400. :param writeChunkMaxSize: writing in chunks solves issues writing to certain JLink chips. A max chunkSize of 64 was found to work well for our case. For normal usage with Crownstones this is not required. a writeChunkMaxSize of 0 will not send the payload in chunks. """ self.uartManager.config(port, baudrate, writeChunkMaxSize) result = [False] def handleMessage(result, data): result[0] = True event = UartEventBus.subscribe( SystemTopics.connectionEstablished, lambda data: handleMessage(result, data)) self.uartManager.start() try: while not result[0] and self.running: try: exc = self.manager_exception_queue.get(block=False) self.uartManager.join() self.stop() raise exc[0](exc[1]) except queue.Empty: pass time.sleep(0.1) except KeyboardInterrupt: print("\nClosing Crownstone Uart.... Thanks for your time!") self.stop() UartEventBus.unsubscribe(event)
def __init__(self, port, baudrate, writeChunkMaxSize=0): self.baudrate = baudrate self.port = port self.writeChunkMaxSize = writeChunkMaxSize self.serialController = None self.started = False self.running = True self.parser = UartParser() self.eventId = UartEventBus.subscribe(SystemTopics.uartWriteData, self.write_to_uart) threading.Thread.__init__(self)
def start_reading(self): readBuffer = UartReadBuffer() self.started = True _LOGGER.debug(F"Read starting on serial port {self.port} {self.running}") try: while self.running: bytesFromSerial = self.serialController.read() # reads single byte (blocks until it is received) if bytesFromSerial: # read all extra bytes in read buffer if self.serialController.in_waiting > 0: additionalBytes = self.serialController.read(self.serialController.in_waiting) bytesFromSerial = bytesFromSerial + additionalBytes UartEventBus.emit(SystemTopics.uartRawData, bytesFromSerial) readBuffer.addByteArray(bytesFromSerial) except OSError or serial.SerialException: _LOGGER.info("Connection to USB Failed. Retrying...") except KeyboardInterrupt: self.running = False _LOGGER.debug("Closing serial connection.") except Exception as error: self.running = False _LOGGER.error(f"Error in handling UART data: {error}") traceback.print_exc() # close the serial controller self.serialController.close() self.serialController = None # remove the event listener pointing to the old connection UartEventBus.unsubscribe(self.eventId) self.started = False UartEventBus.emit(SystemTopics.connectionClosed, True)
async def setPowerZero(self, mW: int): controlPacket = ControlStateSetPacket( StateType.POWER_ZERO).loadInt32(mW).serialize() uartMessage = UartMessagePacket(UartTxType.CONTROL, controlPacket).serialize() uartPacket = UartWrapperPacket(UartMessageType.UART_MESSAGE, uartMessage).serialize() resultCollector = Collector(timeout=1, topic=SystemTopics.resultPacket) # send the message to the Crownstone UartEventBus.emit(SystemTopics.uartWriteData, uartPacket) # wait for the collectors to fill commandResultData = await resultCollector.receive() if commandResultData is not None: if commandResultData.resultCode is ResultValue.BUSY: await asyncio.sleep(0.2) return await self.setPowerZero(mW) elif commandResultData.resultCode is not ResultValue.SUCCESS: raise CrownstoneException(commandResultData.resultCode, "Command has failed.")
async def _write(self, controlPacket: [int], successCodes = [ResultValue.SUCCESS, ResultValue.SUCCESS_NO_CHANGE, ResultValue.WAIT_FOR_SUCCESS]) -> [int] or None: """ Returns the result payload. TODO: return result packet. TODO: use a ControlPacket as param, instead of int array. """ _LOGGER.debug(f"Write control packet {controlPacket}") uartMessage = UartMessagePacket(UartTxType.CONTROL, controlPacket).serialize() uartPacket = UartWrapperPacket(UartMessageType.UART_MESSAGE, uartMessage).serialize() resultCollector = Collector(timeout=1, topic=SystemTopics.resultPacket) # send the message to the Crownstone UartEventBus.emit(SystemTopics.uartWriteData, uartPacket) # wait for the collectors to fill commandResultData = await resultCollector.receive() if commandResultData is not None: if commandResultData.resultCode not in successCodes: raise CrownstoneException(commandResultData.resultCode, f"Command has failed: result code is {commandResultData.resultCode}") return commandResultData.payload return None
async def _command_via_mesh_broadcast_acked( self, crownstone_uid_array: List[int], packet: bytearray) -> MeshResult: # this is only for the set_iBeacon_config_id # broadcast to all, but retry until ID's in list have acked or timeout # value: 3 corePacket = MeshBroadcastAckedPacket(crownstone_uid_array, packet).getPacket() controlPacket = ControlPacket( ControlType.MESH_COMMAND).loadByteArray(corePacket).getPacket() uartMessage = UartMessagePacket(UartTxType.CONTROL, controlPacket).getPacket() uartPacket = UartWrapperPacket(UartMessageType.UART_MESSAGE, uartMessage).getPacket() resultCollector = Collector(timeout=2, topic=SystemTopics.resultPacket) individualCollector = BatchCollector( timeout=15, topic=SystemTopics.meshResultPacket) finalCollector = Collector(timeout=15, topic=SystemTopics.meshResultFinalPacket) # send the message to the Crownstone UartEventBus.emit(SystemTopics.uartWriteData, uartPacket) # wait for the collectors to fill commandResultData = await resultCollector.receive() if commandResultData is not None: if commandResultData.resultCode is ResultValue.BUSY: await asyncio.sleep(0.2) return await self._command_via_mesh_broadcast_acked( crownstone_uid_array, packet) elif commandResultData.resultCode is not ResultValue.SUCCESS: raise CrownstoneException(commandResultData.resultCode, "Command has failed.") return await self._handleCollectors(crownstone_uid_array, individualCollector, finalCollector)
def __init__(self): self.port = None # Port configured by user. self.baudRate = 230400 self.writeChunkMaxSize = 0 self.running = True self.loop = None self._availablePorts = list(list_ports.comports()) self._attemptingIndex = 0 self._uartBridge = None self.ready = False self.eventId = UartEventBus.subscribe(SystemTopics.connectionClosed, self.resetEvent) threading.Thread.__init__(self)
async def _set_state_via_mesh_acked(self, crownstone_id: int, packet: bytearray) -> MeshResult: # 1:1 message to N crownstones with acks (only N = 1 supported for now) # flag value: 2 corePacket = MeshSetStatePacket(crownstone_id, packet).getPacket() controlPacket = ControlPacket( ControlType.MESH_COMMAND).loadByteArray(corePacket).getPacket() uartMessage = UartMessagePacket(UartTxType.CONTROL, controlPacket).getPacket() uartPacket = UartWrapperPacket(UartMessageType.UART_MESSAGE, uartMessage).getPacket() resultCollector = Collector(timeout=2, topic=SystemTopics.resultPacket) individualCollector = BatchCollector( timeout=15, topic=SystemTopics.meshResultPacket) finalCollector = Collector(timeout=15, topic=SystemTopics.meshResultFinalPacket) # send the message to the Crownstone UartEventBus.emit(SystemTopics.uartWriteData, uartPacket) # wait for the collectors to fill commandResultData = await resultCollector.receive() if commandResultData is not None: if commandResultData.resultCode is ResultValue.BUSY: await asyncio.sleep(0.2) return await self._set_state_via_mesh_acked( crownstone_id, packet) elif commandResultData.resultCode is not ResultValue.SUCCESS: raise CrownstoneException(commandResultData.resultCode, "Command has failed.") return await self._handleCollectors([crownstone_id], individualCollector, finalCollector)
def process(self): """ Process a buffer. Check CRC, and emit a uart packet. Buffer starts after size header, and includes wrapper header and tail (CRC). """ # Check size bufferSize = len(self.buffer) wrapperSize = WRAPPER_HEADER_SIZE + CRC_SIZE if bufferSize < wrapperSize: _LOGGER.warning("Buffer too small") UartEventBus.emit(DevTopics.uartNoise, "buffer too small") return # Get the buffer between size field and CRC: baseBuffer = self.buffer[0:bufferSize - CRC_SIZE] # Check CRC calculatedCrc = crc16ccitt(baseBuffer) sourceCrc = Conversion.uint8_array_to_uint16(self.buffer[bufferSize - CRC_SIZE:]) if calculatedCrc != sourceCrc: _LOGGER.warning("Failed CRC") _LOGGER.debug( f"Failed CRC: sourceCrc={sourceCrc} calculatedCrc={calculatedCrc} bufSize={len(self.buffer)} buffer={self.buffer}" ) UartEventBus.emit(DevTopics.uartNoise, "crc mismatch") return wrapperPacket = UartWrapperPacket() if wrapperPacket.parse(baseBuffer): UartEventBus.emit(SystemTopics.uartNewPackage, wrapperPacket)
def __init__(self): self.stones = {} UartEventBus.subscribe(SystemTopics.stateUpdate, self.handleStateUpdate)
def emitNewData(self, advPayload): UartEventBus.emit(UartTopics.newDataAvailable, advPayload)