From eeaaeca4a76c7f50e828208d3b3391c9bcc46ab4 Mon Sep 17 00:00:00 2001 From: PaulVua Date: Fri, 23 Jan 2026 17:20:04 +0100 Subject: [PATCH] fix(envea): correct serial read to prevent spurious spikes - Replace readline() with read(32) to avoid truncation at 0x0A bytes - Add reset_input_buffer() to clear stale data before each read - Add initial read to consume echo/acknowledgment from sensor - Add frame header validation (0xFF 0x02) to reject invalid data - Add delays to allow sensor response time Fixes issue where NO2/H2S sensors showed random spikes due to binary data containing newline characters being misinterpreted. Co-Authored-By: Claude Opus 4.5 --- envea/read_value_v2.py | 69 ++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/envea/read_value_v2.py b/envea/read_value_v2.py index 36defbe..9b5b801 100755 --- a/envea/read_value_v2.py +++ b/envea/read_value_v2.py @@ -105,39 +105,56 @@ try: serial_connection = serial_connections[name] try: debug_print(f"Reading from {name}...") - + + # Flush input buffer to clear any stale data + serial_connection.reset_input_buffer() + # Send command to sensor command = b"\xFF\x02\x13\x30\x01\x02\x03\x04\x05\x06\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x12\xAF\x88\x03" serial_connection.write(command) debug_print(f" → Sent command: {command.hex()}") + + # Wait for sensor response + time.sleep(0.5) + + # Read fixed number of bytes (not readline which stops at 0x0A) + # First read to consume any echo/acknowledgment + initial_data = serial_connection.read(serial_connection.in_waiting or 1) + debug_print(f" ← Initial read: {len(initial_data)} bytes") + + # Small delay then read the actual response + time.sleep(0.1) + data_envea = serial_connection.read(32) # Read fixed 32 bytes + debug_print(f" ← Received {len(data_envea)} bytes: {data_envea.hex() if data_envea else 'empty'}") - # Read response - data_envea = serial_connection.readline() - debug_print(f" ← Received {len(data_envea)} bytes: {data_envea.hex()}") - + # Validate frame: should start with 0xFF 0x02 and have at least 20 bytes if len(data_envea) >= 20: - byte_20 = data_envea[19] - raw_value = byte_20 - calculated_value = byte_20 * coefficient - debug_print(f" → Byte 20 value: {raw_value} (0x{raw_value:02X})") - debug_print(f" → Calculated value: {raw_value} × {coefficient} = {calculated_value}") - - if name == "h2s": - data_h2s = calculated_value - elif name == "no2": - data_no2 = calculated_value - elif name == "o3": - data_o3 = calculated_value - elif name == "co": - data_co = calculated_value - elif name == "nh3": - data_nh3 = calculated_value - elif name == "so2": - data_so2 = calculated_value - - debug_print(f" ✓ {name.upper()} = {calculated_value}") + # Check frame header (0xFF 0x02 expected for valid CAIRSENS response) + if data_envea[0] == 0xFF and data_envea[1] == 0x02: + byte_20 = data_envea[19] + raw_value = byte_20 + calculated_value = byte_20 * coefficient + debug_print(f" → Byte 20 value: {raw_value} (0x{raw_value:02X})") + debug_print(f" → Calculated value: {raw_value} × {coefficient} = {calculated_value}") + + if name == "h2s": + data_h2s = calculated_value + elif name == "no2": + data_no2 = calculated_value + elif name == "o3": + data_o3 = calculated_value + elif name == "co": + data_co = calculated_value + elif name == "nh3": + data_nh3 = calculated_value + elif name == "so2": + data_so2 = calculated_value + + debug_print(f" ✓ {name.upper()} = {calculated_value}") + else: + debug_print(f" ✗ Invalid frame header: expected FF 02, got {data_envea[0]:02X} {data_envea[1]:02X}") else: - debug_print(f" ✗ Response too short (expected ≥20 bytes)") + debug_print(f" ✗ Response too short ({len(data_envea)} bytes, expected ≥20)") except serial.SerialException as e: debug_print(f"✗ Error communicating with {name}: {e}")