From 716c32b0cef6edfc5fb7bfd2e3511d251d4c5c51 Mon Sep 17 00:00:00 2001 From: Hector van der Aa Date: Mon, 6 Apr 2026 00:45:03 +0200 Subject: [PATCH] Telemetry receive over serial, parsing and status flash --- src/dataflux.zip | Bin 0 -> 26882 bytes src/dataflux/services/serial/__init__.py | 117 +++++++++++++++++++++++ src/dataflux/state.py | 8 ++ src/dataflux/tags.py | 1 + src/dataflux/telemetry_common | 1 + src/dataflux/ui/colors.py | 12 +-- src/dataflux/ui/routines/status.py | 8 +- src/dataflux/ui/windows.py | 8 +- telemetry_common | 1 - 9 files changed, 146 insertions(+), 10 deletions(-) create mode 100644 src/dataflux.zip create mode 120000 src/dataflux/telemetry_common delete mode 120000 telemetry_common diff --git a/src/dataflux.zip b/src/dataflux.zip new file mode 100644 index 0000000000000000000000000000000000000000..b4e749f7694e4ba2858102d84224eb8a71bf6f39 GIT binary patch literal 26882 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 WmoX^=2n4?5e;j$6*+W1Cf`0+yG;ss~ literal 0 HcmV?d00001 diff --git a/src/dataflux/services/serial/__init__.py b/src/dataflux/services/serial/__init__.py index 7e2f0b7..57f44f4 100644 --- a/src/dataflux/services/serial/__init__.py +++ b/src/dataflux/services/serial/__init__.py @@ -2,8 +2,14 @@ # Copyright (C) 2026 Association Exergie # SPDX-License-Identifier: GPL-3.0-or-later +from queue import Empty +from sys import base_exec_prefix +from threading import Thread from serial import Serial import serial.tools.list_ports +from dataflux import telemetry_common +import dataflux.telemetry_common.telemetry_common +import dataflux.ui.routines.status from dataflux.state import AppState @@ -23,11 +29,122 @@ def connect_serial(state: AppState, device: str) -> None: state.serial_port = None state.serial_port = Serial(port=device, baudrate=115200) + state.serial_thread = Thread(target=serial_reader_worker, args=(state,), daemon=True) + state.serial_status_thread = Thread(target=serial_status_worker, args=(state,), daemon=True) + + state.serial_thread_running = True + state.serial_status_thread.start() + state.serial_thread.start() def disconnect_serial(state: AppState) -> None: if state.serial_port is not None: + state.serial_thread_running = False state.serial_port.close() state.serial_port = None +def serial_status_worker(state: AppState) -> None: + while state.serial_thread_running: + try: + duration = state.serial_status_queue.get(timeout=0.1) + except Empty: + continue + dataflux.ui.routines.status.flash_status_connection_status(duration) + + + + +def serial_reader_worker(state: AppState) -> None: + while state.serial_thread_running: + port = state.serial_port + if port is None: + break + + try: + packet = read_one_uart_packet(port) + if packet is None: + continue + + parsed = parse_uart_packet(packet) + if parsed is not None: + state.packet_queue.put(parsed) + state.serial_status_queue.put(0.1) + print(parsed) + + except Exception as e: + print(f"Serial parser error: {e}") + +def read_one_uart_packet(port: Serial) -> bytes | None: + first = port.read(1) + if not first: + return None + + if first != dataflux.telemetry_common.telemetry_common.UART_MAGIC[:1]: + return None + + rest_magic = port.read(3) + if len(rest_magic) != 3: + return None + + if first + rest_magic != dataflux.telemetry_common.telemetry_common.UART_MAGIC: + return None + + size_bytes = port.read(1) + if len(size_bytes) != 1: + return None + + body_size = size_bytes[0] + + body = port.read(body_size) + if len(body) != body_size: + return None + + return body + +def parse_uart_packet(body: bytes) -> dict | None: + if len(body) < dataflux.telemetry_common.telemetry_common.LORA_HEADER_SIZE: + return None + + lora = dataflux.telemetry_common.telemetry_common.unpack_lora_header(body[:dataflux.telemetry_common.telemetry_common.LORA_HEADER_SIZE]) + payload = body[dataflux.telemetry_common.telemetry_common.LORA_HEADER_SIZE:] + + if lora.size != len(payload): + print(f"Serial size mismatch header says {lora.size} actual payload is {len(payload)}") + return None + + calc_crc = dataflux.telemetry_common.telemetry_common.crc16_ccitt(payload) + + if calc_crc != lora.crc16: + print("crc mismatch") + return None + + base = { + "source": lora.source, + "dest": lora.dest, + "version": lora.version, + } + + if lora.version == 1: + pkt = dataflux.telemetry_common.telemetry_common.unpack_packet1(payload) + return { + **base, + "type": "packet1", + "ping": pkt.ping.decode("ascii", errors="replace") + } + + if lora.version == 2: + pkt = dataflux.telemetry_common.telemetry_common.unpack_packet2(payload) + return { + **base, + "type": "packet2", + "time_stamp": pkt.time_stamp, + "vbat": pkt.vbat, + "teng": pkt.teng, + "lat": pkt.lat, + "lng": pkt.lng, + "speed": pkt.speed, + } + + print("Unknown payload") + return None diff --git a/src/dataflux/state.py b/src/dataflux/state.py index eddd313..89cec5b 100644 --- a/src/dataflux/state.py +++ b/src/dataflux/state.py @@ -5,6 +5,7 @@ from dataclasses import dataclass, field from threading import Lock, Thread from serial import Serial +from queue import Queue @dataclass class AppState: @@ -12,7 +13,14 @@ class AppState: serial_port: Serial | None = None serial_thread: Thread | None = None + serial_thread_running: bool = False telemetry_thread: Thread | None = None + serial_status_thread: Thread | None = None + serial_status_queue: Queue = field(default_factory=Queue) + + packet_queue: Queue = field(default_factory=Queue) + latest_telemetry: dict = field(default_factory=dict) + lock: Lock = field(default_factory=Lock) diff --git a/src/dataflux/tags.py b/src/dataflux/tags.py index 7097382..57cfb1c 100644 --- a/src/dataflux/tags.py +++ b/src/dataflux/tags.py @@ -12,3 +12,4 @@ STATUS_SERIAL_STATUS_TEXT: str = "status_serial_status_text" THEME_STATUS_DISCONNECTED: str = "theme_status_disconnected" THEME_STATUS_CONNECTED: str = "theme_status_connected" +THEME_STATUS_CONNECTED_BRIGHT: str = "theme_status_connected_bigght" diff --git a/src/dataflux/telemetry_common b/src/dataflux/telemetry_common new file mode 120000 index 0000000..edc567c --- /dev/null +++ b/src/dataflux/telemetry_common @@ -0,0 +1 @@ +/home/hector/projects/Exergie/TelemetryCommon/python/ \ No newline at end of file diff --git a/src/dataflux/ui/colors.py b/src/dataflux/ui/colors.py index bebb68b..6fe3e23 100644 --- a/src/dataflux/ui/colors.py +++ b/src/dataflux/ui/colors.py @@ -2,11 +2,11 @@ # Copyright (C) 2026 Association Exergie # SPDX-License-Identifier: GPL-3.0-or-later -STATUS_RED_DARK = (140, 35, 35, 255) -STATUS_RED_BRIGHT = (255, 70, 70, 255) +STATUS_RED_DARK = (140, 35, 35, 255) +STATUS_RED_BRIGHT = (205, 85, 85, 255) -STATUS_ORANGE_DARK = (160, 90, 20, 255) -STATUS_ORANGE_BRIGHT= (255, 165, 40, 255) +STATUS_ORANGE_DARK = (160, 90, 20, 255) +STATUS_ORANGE_BRIGHT = (210, 140, 60, 255) -STATUS_GREEN_DARK = (40, 130, 55, 255) -STATUS_GREEN_BRIGHT = (70, 255, 110, 255) +STATUS_GREEN_DARK = (40, 130, 55, 255) +STATUS_GREEN_BRIGHT = (95, 185, 115, 255) diff --git a/src/dataflux/ui/routines/status.py b/src/dataflux/ui/routines/status.py index b4b6e66..1eb0d0d 100644 --- a/src/dataflux/ui/routines/status.py +++ b/src/dataflux/ui/routines/status.py @@ -4,7 +4,8 @@ import dearpygui.dearpygui as dpg from dataflux.state import AppState -from dataflux.tags import STATUS_SERIAL_STATUS_BOX, STATUS_SERIAL_STATUS_TEXT, THEME_STATUS_CONNECTED, THEME_STATUS_DISCONNECTED +from dataflux.tags import STATUS_SERIAL_STATUS_BOX, STATUS_SERIAL_STATUS_TEXT, THEME_STATUS_CONNECTED, THEME_STATUS_CONNECTED_BRIGHT, THEME_STATUS_DISCONNECTED +from time import sleep def update_status_connection_status(state: AppState): if state.serial_port is None: @@ -13,3 +14,8 @@ def update_status_connection_status(state: AppState): else: dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_CONNECTED) dpg.set_value(STATUS_SERIAL_STATUS_TEXT, "Serial: Connected") + +def flash_status_connection_status(duration: float) -> None: + dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_CONNECTED_BRIGHT) + sleep(duration) + dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_CONNECTED) diff --git a/src/dataflux/ui/windows.py b/src/dataflux/ui/windows.py index bad3f7a..0d59605 100644 --- a/src/dataflux/ui/windows.py +++ b/src/dataflux/ui/windows.py @@ -7,8 +7,8 @@ import dataflux.callbacks.menu import dataflux.callbacks.serial from dataflux.state import AppState -from dataflux.tags import MENU_FILE_CONNECT, MENU_FILE_DISCONNECT, STATUS_SERIAL_STATUS_BOX, STATUS_SERIAL_STATUS_TEXT, THEME_STATUS_CONNECTED, THEME_STATUS_DISCONNECTED, WINDOW_CONNECTION_MENU, WINDOW_CONNECTION_MENU_COMBO -from dataflux.ui.colors import STATUS_GREEN_DARK, STATUS_RED_DARK +from dataflux.tags import MENU_FILE_CONNECT, MENU_FILE_DISCONNECT, STATUS_SERIAL_STATUS_BOX, STATUS_SERIAL_STATUS_TEXT, THEME_STATUS_CONNECTED, THEME_STATUS_CONNECTED_BRIGHT, THEME_STATUS_DISCONNECTED, WINDOW_CONNECTION_MENU, WINDOW_CONNECTION_MENU_COMBO +from dataflux.ui.colors import STATUS_GREEN_BRIGHT, STATUS_GREEN_DARK, STATUS_RED_DARK def build_windows(state: AppState) -> None: @@ -44,6 +44,10 @@ def build_windows(state: AppState) -> None: with dpg.theme_component(dpg.mvChildWindow): dpg.add_theme_color(dpg.mvThemeCol_ChildBg, STATUS_GREEN_DARK) + with dpg.theme(tag=THEME_STATUS_CONNECTED_BRIGHT): + with dpg.theme_component(dpg.mvChildWindow): + dpg.add_theme_color(dpg.mvThemeCol_ChildBg, STATUS_GREEN_BRIGHT) + with dpg.theme(tag=THEME_STATUS_DISCONNECTED): with dpg.theme_component(dpg.mvChildWindow): dpg.add_theme_color(dpg.mvThemeCol_ChildBg, STATUS_RED_DARK) diff --git a/telemetry_common b/telemetry_common deleted file mode 120000 index 66ae1b3..0000000 --- a/telemetry_common +++ /dev/null @@ -1 +0,0 @@ -/home/hector/projects/Exergie/TelemetryCommon/ \ No newline at end of file