commit e82ad574abf50253d8f0f3146a7b646312483495 Author: Jono Targett Date: Sat Mar 14 23:19:44 2026 +1030 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..350c58d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +venv/ +*.bak diff --git a/assethandler.py b/assethandler.py new file mode 100755 index 0000000..a0341bc --- /dev/null +++ b/assethandler.py @@ -0,0 +1,64 @@ +#! /usr/bin/env python3 + +import asyncio +import uuid +import aioserial +import aiomqtt + +SERIAL_PORT = "/tmp/ttyV0" +BAUD = 115200 + +SERIAL_PUBLISH_TOPIC = "serial/data" +SERIAL_COMMAND_TOPIC = "serial/command" + + +async def serial_reader(ser, client): + while True: + data = await ser.read_async(64) + if data: + await client.publish(SERIAL_PUBLISH_TOPIC, data) + # Hand back to event loop + await asyncio.sleep(0) + + +async def mqtt_command_writer(ser, client): + async for message in client.messages: + topic = str(message.topic) + print(message.topic, message.payload) + + if str(topic) == SERIAL_COMMAND_TOPIC: + await ser.write_async(message.payload) + + +async def main(): + client_id = f"jono-test-{uuid.uuid4()}" + interval = 5 + + ser = aioserial.AioSerial( + port=SERIAL_PORT, + baudrate=BAUD, + timeout=0.05, # 50 ms + ) + + while True: + try: + async with aiomqtt.Client( + "127.0.0.1", + port=1883, + identifier=client_id, + ) as client: + await client.subscribe(SERIAL_COMMAND_TOPIC) + + await asyncio.gather( + serial_reader(ser, client), + mqtt_command_writer(ser, client), + ) + + except aiomqtt.MqttError as e: + print(e) + print(f"Connection lost; reconnecting in {interval} seconds...") + await asyncio.sleep(interval) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f9f43a4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +aiomqtt +aioserial +black +pyubx2 +pyubxutils diff --git a/tunnel.sh b/tunnel.sh new file mode 100755 index 0000000..fe98fb3 --- /dev/null +++ b/tunnel.sh @@ -0,0 +1,5 @@ +#! /bin/bash + +socat -d -d \ + pty,raw,echo=0,link=/tmp/ttyV0 \ + pty,raw,echo=0,link=/tmp/ttyV1 diff --git a/ubx.py b/ubx.py new file mode 100755 index 0000000..c1ef356 --- /dev/null +++ b/ubx.py @@ -0,0 +1,42 @@ +#! /usr/bin/env python3 + +import asyncio +import aioserial +import pyubxutils +import pyubx2 + +SERIAL_PORT = "/tmp/ttyV1" +BAUD = 115200 + + +async def simulator_to_serial(stream, ser): + ubr = pyubx2.UBXReader(stream) + + for raw, parsed in ubr: + await ser.write_async(raw) + + +async def serial_to_simulator(stream, ser): + while True: + data = await ser.read_async(1024) + if data: + stream.write(data) + + +async def main(): + ser = aioserial.AioSerial(port=SERIAL_PORT, baudrate=BAUD) + + with pyubxutils.UBXSimulator( + configfile="./ubxsimulator.json", + interval=1000, + timeout=3, + ) as stream: + + await asyncio.gather( + simulator_to_serial(stream, ser), + serial_to_simulator(stream, ser), + ) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/ubxsimulator.json b/ubxsimulator.json new file mode 100644 index 0000000..fe87f95 --- /dev/null +++ b/ubxsimulator.json @@ -0,0 +1,300 @@ +{ + "interval": 1000, + "timeout": 3, + "logfile": "./ubxsimulator.log", + "simVector": 1, + "global": { + "lat": 52.477311, + "lon": 13.391426, + "alt": 50.034, + "height": 47494, + "sep": 2.54, + "hMSL": 50034, + "NS": "N", + "EW": "E", + "gSpeed": 100000, + "headMot": 135, + "spd": 194.3844, + "cog": 126 + }, + "ubxmessages": [ + { + "msgCls": 1, + "msgId": 7, + "rate": 1, + "attrs": { + "validDate": 1, + "validTime": 1, + "validMag": 1, + "fixType": 3, + "numSV": 12, + "gnssFixOk": 1, + "pDOP": 0.843, + "hAcc": 585, + "vAcc": 543, + "diffSoln": 1, + "carrSoln": 1, + "lastCorrectionAge": 5 + } + }, + { + "msgCls": 1, + "msgId": 4, + "rate": 1, + "attrs": { + "gDOP": 0.45, + "pDOP": 0.356, + "tDOP": 0.3864, + "vDOP": 0.98733, + "hDOP": 0.96763, + "nDOP": 0.9873476, + "eDOP": 0.834 + } + }, + { + "msgCls": 1, + "msgId": 53, + "rate": 4, + "attrs": { + "version": 1, + "numSvs": 13, + "reserved0": 0, + "gnssId_01": 0, + "svId_01": 4, + "cno_01": 0, + "elev_01": 16, + "azim_01": 302, + "prRes_01": 0.0, + "qualityInd_01": 1, + "svUsed_01": 0, + "health_01": 1, + "diffCorr_01": 0, + "smoothed_01": 0, + "orbitSource_01": 2, + "ephAvail_01": 0, + "almAvail_01": 1, + "gnssId_02": 0, + "svId_02": 5, + "cno_02": 48, + "elev_02": 15, + "azim_02": 73, + "prRes_02": 0.0, + "qualityInd_02": 4, + "svUsed_02": 0, + "health_02": 1, + "diffCorr_02": 0, + "smoothed_02": 0, + "orbitSource_02": 2, + "ephAvail_02": 0, + "almAvail_02": 1, + "gnssId_03": 0, + "svId_03": 14, + "cno_03": 0, + "elev_03": -91, + "azim_03": 0, + "prRes_03": 0.0, + "qualityInd_03": 1, + "svUsed_03": 0, + "health_03": 1, + "diffCorr_03": 0, + "smoothed_03": 0, + "orbitSource_03": 0, + "gnssId_04": 0, + "svId_04": 18, + "cno_04": 50, + "elev_04": -91, + "azim_04": 0, + "prRes_04": 0.0, + "qualityInd_04": 4, + "svUsed_04": 0, + "health_04": 1, + "diffCorr_04": 0, + "smoothed_04": 0, + "orbitSource_04": 0, + "gnssId_05": 0, + "svId_05": 25, + "cno_05": 0, + "elev_05": 19, + "azim_05": 116, + "prRes_05": 0.0, + "qualityInd_05": 1, + "svUsed_05": 0, + "health_05": 1, + "diffCorr_05": 0, + "smoothed_05": 0, + "orbitSource_05": 1, + "ephAvail_05": 1, + "almAvail_05": 1, + "gnssId_06": 0, + "svId_06": 26, + "cno_06": 45, + "elev_06": 61, + "azim_06": 285, + "prRes_06": 3.5, + "qualityInd_06": 7, + "svUsed_06": 1, + "health_06": 1, + "diffCorr_06": 0, + "smoothed_06": 0, + "orbitSource_06": 1, + "ephAvail_06": 1, + "almAvail_06": 1, + "gnssId_07": 0, + "svId_07": 27, + "cno_07": 0, + "elev_07": 4, + "azim_07": 245, + "prRes_07": 0.0, + "qualityInd_07": 1, + "svUsed_07": 0, + "health_07": 1, + "diffCorr_07": 0, + "smoothed_07": 0, + "orbitSource_07": 2, + "ephAvail_07": 0, + "almAvail_07": 1, + "gnssId_08": 0, + "svId_08": 28, + "cno_08": 52, + "elev_08": 39, + "azim_08": 196, + "prRes_08": 2.1, + "qualityInd_08": 7, + "svUsed_08": 1, + "health_08": 1, + "diffCorr_08": 0, + "smoothed_08": 0, + "orbitSource_08": 1, + "ephAvail_08": 1, + "almAvail_08": 1, + "gnssId_09": 0, + "svId_09": 29, + "cno_09": 43, + "elev_09": 56, + "azim_09": 67, + "prRes_09": 1.8, + "qualityInd_09": 7, + "svUsed_09": 1, + "health_09": 1, + "diffCorr_09": 0, + "smoothed_09": 0, + "orbitSource_09": 1, + "ephAvail_09": 1, + "almAvail_09": 1, + "gnssId_10": 0, + "svId_10": 31, + "cno_10": 54, + "elev_10": 55, + "azim_10": 230, + "prRes_10": 0.3, + "qualityInd_10": 7, + "svUsed_10": 1, + "health_10": 1, + "diffCorr_10": 0, + "smoothed_10": 0, + "orbitSource_10": 1, + "ephAvail_10": 1, + "gnssId_11": 2, + "svId_11": 120, + "cno_11": 0, + "elev_11": 28, + "azim_11": 196, + "prRes_11": 0.0, + "qualityInd_11": 1, + "svUsed_11": 0, + "health_11": 0, + "diffCorr_11": 0, + "smoothed_11": 0, + "orbitSource_11": 7, + "gnssId_12": 2, + "svId_12": 123, + "cno_12": 0, + "elev_12": 22, + "azim_12": 140, + "prRes_12": 0.0, + "qualityInd_12": 1, + "svUsed_12": 0, + "health_12": 0, + "diffCorr_12": 0, + "smoothed_12": 0, + "orbitSource_12": 7, + "gnssId_13": 2, + "svId_13": 136, + "cno_13": 0, + "elev_13": 29, + "azim_13": 171, + "prRes_13": 0.0, + "qualityInd_13": 1, + "svUsed_13": 0, + "health_13": 0, + "diffCorr_13": 0, + "smoothed_13": 0, + "orbitSource_13": 7 + } + } + ], + "nmeamessages": [ + { + "talker": "GN", + "msgId": "GGA", + "rate": 1, + "attrs": { + "status": "A", + "posMode": "A", + "navStatus": "V", + "sepUnit": "M", + "altUnit": "M", + "quality": 5, + "numSV": 12, + "HDOP": 0.9873476, + "diffAge": 10, + "diffStation": 2746 + } + }, + { + "talker": "GN", + "msgId": "RMC", + "rate": 1, + "attrs": { + "status": "A", + "posMode": "A", + "navStatus": "V" + } + }, + { + "talker": "P", + "msgId": "QTMVERNO", + "rate": 1, + "attrs": { + "verstr": "LG290P03AANR01A03S", + "builddate": "2025/03/23", + "buildtime": "12:02:34" + } + }, + { + "talker": "P", + "msgId": "QTMPVT", + "rate": 1, + "attrs": { + "msgver": 1, + "tow": 1000, + "date": "20221225", + "time": 83737.000, + "fixtype": 3, + "numsv": 22, + "leaps": 3, + "lat": 52.477311, + "lon": 13.391426, + "alt": 45.287, + "sep": 31.075, + "veln": 0.034, + "vele": 0.016, + "veld": 0.065, + "spd": 23.48, + "hdg": 182.12, + "hdop": 1.23, + "pdop": 1.18 + } + } + ] +} \ No newline at end of file