diff --git a/shared/lora_device.py b/shared/lora_device.py index 1ed0f29..0b3cb09 100644 --- a/shared/lora_device.py +++ b/shared/lora_device.py @@ -20,6 +20,8 @@ else: class BaseLoraDevice: """Base class providing automatic ACK generation, retries, and duplicate filtering.""" + MAX_PAYLOAD_SIZE = 222 # Maximum safe LoRa payload size in bytes + def __init__(self): self.processed_msg_ids = set() self.received_acks = set() @@ -225,6 +227,13 @@ if IS_MICROPYTHON: payload = payload.encode('utf-8') paquet_physique = bytes([group]) + payload + + if len(paquet_physique) > self.MAX_PAYLOAD_SIZE: + raise ValueError( + f"[LoRa SPI] Payload exceeds MTU limit: {len(paquet_physique)} bytes " + f"(max allowed: {self.MAX_PAYLOAD_SIZE} bytes)." + ) + try: self.lora.send(paquet_physique) except Exception as e: @@ -290,18 +299,19 @@ else: # Initial configuration self.configure(freq=868.1, sf=7, bw=125) - def _send_at_cmd(self, cmd, wait_time=0.3): - """Sends AT command, draining unread serial noise first.""" - # Drain any lingering lines (like 'Rssi= -4' or incoming data) - if self.ser.in_waiting > 0: - self.ser.read_all() - + def _send_at_cmd(self, cmd, timeout=1.5): + """Sends AT command and reads response until line received or timeout.""" self.ser.write(f"{cmd}\r\n".encode('utf-8')) - time.sleep(wait_time) + start = time.time() resp = "" - while self.ser.in_waiting > 0: - resp += self.ser.readline().decode('utf-8', errors='ignore') + while (time.time() - start) < timeout: + if self.ser.in_waiting > 0: + line = self.ser.readline().decode('utf-8', errors='ignore') + resp += line + if "OK" in line or "ERROR" in line or "AT_BUSY" in line: + break + time.sleep(0.02) return resp def configure(self, freq=868.1, sf=7, bw=125): @@ -312,14 +322,14 @@ else: # Parameters: Freq, SF, BW, CR(0=4/5), Preamble(8), Header(1=Explicit), CRC(1=ON), IQ(0=Standard), NetMode(0=P2P), Power(14), SyncWord(18=0x12), Format(0), Type(1) at_cfg_cmd = f"AT+CFG={freq_hz},{sf},{bw_code},0,8,1,1,0,0,14,18,0,1" - self._send_at_cmd(at_cfg_cmd, wait_time=0.2) + self._send_at_cmd(at_cfg_cmd, timeout=0.2) # Fallback standalone commands - self._send_at_cmd("AT+SYNCWORD=18", wait_time=0.1) - self._send_at_cmd("AT+PRECV=65535", wait_time=0.1) + self._send_at_cmd("AT+SYNCWORD=18", timeout=0.1) + self._send_at_cmd("AT+PRECV=65535", timeout=0.1) self.ser.reset_input_buffer() - def send(self, payload, group=None): + def send(self, payload, group=None, retries=0): """Encodes payload into a HEX string and transmits via 4-parameter AT+SEND.""" with self.lock: if group is None: @@ -331,14 +341,20 @@ else: if isinstance(payload, str): payload = payload.encode('utf-8') + if len(payload) > self.MAX_PAYLOAD_SIZE: + raise ValueError( + f"[LoRa Serial] Payload exceeds MTU limit: {len(payload)} bytes " + f"(max allowed: {self.MAX_PAYLOAD_SIZE} bytes)." + ) + hex_payload = payload.hex() log(f"[RPi LoRa Serial] Transmitting HEX string: {hex_payload}") # Format: AT+SEND=,,, - cmd = f"AT+SEND={group},{hex_payload},0,3" + cmd = f"AT+SEND={group},{hex_payload},0,{retries}" - resp = self._send_at_cmd(cmd, wait_time=0.3) + resp = self._send_at_cmd(cmd, timeout=0.3) log(f"[RPi LoRa Serial] AT+SEND response: {resp}") def receive_packet(self, timeout_ms=500): @@ -424,4 +440,5 @@ class LoraCommands: PING = "ping" COOKING_STATE_UPDATE = "cooking_state_update" TOGGLE_PAUSE = "toggle_pause" - TOGGLE_DEFROST = "toggle_defrost" \ No newline at end of file + TOGGLE_DEFROST = "toggle_defrost" + NEW_ALERT = "new_alert" \ No newline at end of file