import _thread from machine import UART import time import ujson class SafeUART: def __init__(self, uart_id, tx_pin, rx_pin, baudrate=115200): # Setting timeout allows readline() to be non-blocking self.uart = UART(uart_id, baudrate=baudrate, tx=tx_pin, rx=rx_pin, timeout=10, rxbuf=1024) self.lock = _thread.allocate_lock() self.rx_queue = [] _thread.stack_size(4096) _thread.start_new_thread(self._listener_worker, ()) _thread.stack_size(0) def _listener_worker(self): """Simple worker that relies on newline framing instead of manual JSON parsing.""" while True: if self.uart.any(): with self.lock: line = self.uart.readline() if line: try: decoded = line.decode('utf-8').strip() if decoded: # Ignore empty lines with self.lock: self.rx_queue.append(decoded) except UnicodeError: pass # Drop corrupted bytes cleanly time.sleep_ms(10) def send(self, message): if not message.endswith('\n'): message += '\n' with self.lock: self.uart.write(message.encode('utf-8')) def read(self): with self.lock: return self.rx_queue.pop(0) if self.rx_queue else None def send_as_command(self, command: 'UARTCommand'): """Safely sends a structured command over UART.""" json_message = command.to_json() self.send(json_message) def any(self): """Checks if any complete messages are waiting to be read.""" with self.lock: return len(self.rx_queue) > 0 def read_as_command(self) -> 'UARTCommand | None': """Attempts to read the oldest unread string and parse it as a UARTCommand. Returns None if empty or invalid.""" raw_message = self.read() if raw_message is not None: cmd = UARTCommand.from_json(raw_message) if cmd is None: print("[UART] Impossible de traiter le message brut :", raw_message) return cmd return None class UARTCommand: """A simple wrapper for commands sent over UART, allowing for structured data.""" def __init__(self, command_type: str, payload): self.command_type = command_type self.payload = payload def to_json(self): """Serializes the command to a JSON string.""" return ujson.dumps({ "command_type": self.command_type, "payload": self.payload }) @staticmethod def from_json(json_string: str) -> 'UARTCommand | None': """Deserializes a JSON string into a UARTCommand object.""" try: # Remplacement préventif si des guillemets simples sont reçus clean_str = json_string.replace("'", '"') if "'" in json_string else json_string data = ujson.loads(clean_str) if not isinstance(data, dict): return None return UARTCommand(data.get("command_type"), data.get("payload")) except Exception as err: # Affiche l'erreur exacte rencontrée par ujson (ex: syntax error) print(f"[UARTCommand Parsing Error]: {err} -> Contenu: {json_string}") return None class UARTCommandType: """Enumeration of known UART command types.""" COOKING_PARAMS = "COOKING_PARAMS" COOKING_STATE_UPDATE = "COOKING_STATE_UPDATE"