From df04c9ad8e162a591cd9dcad03c496fd009c31d2 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Sat, 12 Jul 2025 07:26:32 +0200 Subject: [PATCH] NMEA0183 in eigenes Modul ausgelagert --- TODO | 3 ++ nmea0183.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++ obp60v.py | 53 +++++++++++++++++------------------ pages/barograph.py | 4 +++ 4 files changed, 102 insertions(+), 27 deletions(-) create mode 100644 nmea0183.py diff --git a/TODO b/TODO index a949def..06148e6 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,8 @@ Aufgaben- und Planungs- und Ideenliste +- datareader für mehrere I²C-Sensoren. History muß entsprechend + eine Liste sein. + - Barograph - Ankeralarm diff --git a/nmea0183.py b/nmea0183.py new file mode 100644 index 0000000..759d824 --- /dev/null +++ b/nmea0183.py @@ -0,0 +1,69 @@ +""" +NMEA0183 verarbeiten + +""" + +def DBS(boatdata, msg): + # Wassertiefe unter der Oberfläche + pass + +def DBT(boatdata, msg): + # Wassertiefe unter Geber + pass + +def GLL(boatdata, msg): + print("-> GLL") + boatdata.setValue("LAT", msg.latitude) + boatdata.setValue("LON", msg.longitude) + +def HDT(boatdata, msg): + # Heading True + print("-> HDT") + print(msg.fields) + +def MWV(boatdata, msg): + # Windgeschwindigkeit und -winkel + print(f"Wind: {msg.wind_angle}° {msg.wind_speed}kt") + boatdata.setValue("AWA", msg.wind_angle) + boatdata.setValue("AWS", msg.wind_speed) + +def RSA(boatdata, msg): + # Rudder Sensor Angle + # negative Werte bedeuten Backbord + # Boatdata: RPOS primär, PRPOS sekundär + print("-> RSA") + print(msg.fields) + +def RTE(boatdata, msg): + # Route + print("-> RTE") + print(msg.fields) + +def VHW(boatdata, msg): + boatdata.setValue("STW", float(msg.water_speed_knots)) + +def VTG(boatdata, msg): + boatdata.setValue("COG", int(msg.true_track)) + #TODO klären was für Typen hier ankommen können + # bytearray, str, decimal.Decimal? + sog = float(msg.spd_over_grnd_kts) + #str von OpenCPN: sog = float(msg.spd_over_grnd_kts[:-1]) + boatdata.setValue("SOG", sog) + +def WPL(boatdata, msg): + print("-> WPL") + print(msg.fields) + +# Aus Performancegründen eine direkte Sprungtabelle, ggf. können +# zukünftig außer der Funktion noch weitere Daten gespeichert werdeb +decoder = { + "DBS": DBS, + "DBT": DBT, + "GLL": GLL, + "MWV": MWV, + "RSA": RSA, + "RTE": RTE, + "VHW": VHW, + "VTG": VTG, + "WPL": WPL +} diff --git a/obp60v.py b/obp60v.py index a2be270..a452c6a 100755 --- a/obp60v.py +++ b/obp60v.py @@ -102,6 +102,7 @@ import time from datetime import datetime from nmea2000 import Device, BoatData, History, HistoryBuffer from nmea2000 import parser +import nmea0183 import pages import struct @@ -203,37 +204,29 @@ def rxd_0183(devname): return setthreadtitle("0183listener") while not shutdown: + raw = ser.readline().decode('ascii') + if len(raw) == 0: + continue try: - raw = ser.readline().decode('ascii') msg = pynmea2.parse(raw) except pynmea2.nmea.ParseError: print(f"NMEA0183: Parse-Error: {raw}") continue - # sentence_type kann fehlen + # sentence_type kann fehlen try: stype = msg.sentence_type except: print(f"NMEA0183: Sentence type missing: {raw}") continue - if stype == 'GLL': - boatdata.setValue("LAT", msg.latitude) - boatdata.setValue("LON", msg.longitude) - elif stype == 'VTG': - boatdata.setValue("COG", int(msg.true_track)) - #TODO klären was für Typen hier ankommen können - # bytearray, str, decimal.Decimal? - sog = float(msg.spd_over_grnd_kts) - #str von OpenCPN: sog = float(msg.spd_over_grnd_kts[:-1]) - boatdata.setValue("SOG", sog) - elif stype == 'VHW': - boatdata.setValue("STW", float(msg.water_speed_knots)) - elif stype == 'WPL': - # Wegepunkt - print(msg.fields) - elif stype == 'RTE': - # Route - print(msg.fields) + # WIP Neuer Code aus Modul + # TODO Filter mit gewünschen Satztypen + # if stype in stypefilter: + # continue + if stype in nmea0183.decoder: + nmea0183.decoder[stype(boatdata, msg)] else: + # Hier unbekannter Satztyp: protokollieren und ignorieren + print("Nicht implementiert") print(msg) ser.close() @@ -258,7 +251,7 @@ def rxd_gps(devname, devspeed): print(msg.fields) ser.close() -def datareader(histpath, history): +def datareader(cfg, history): """ Daten zu fest definierten Zeitpunkten lesen @@ -272,9 +265,9 @@ def datareader(histpath, history): setthreadtitle("datareader") # Speicherpfad für Meßwertdaten - if not os.path.exists(histpath): - os.makedirs(histpath) - history.basepath = histpath + if not os.path.exists(cfg['histpath']): + os.makedirs(cfg['histpath']) + history.basepath = cfg['histpath'] # Serien initialisieren history.addSeries("BMP280-75", 336 ,75) history.addSeries("BMP280-150", 336 , 150) @@ -687,17 +680,23 @@ if __name__ == "__main__": # Schnittstellen aktivieren if cfg['can']: + print("CAN enabled") t_rxd_n2k = threading.Thread(target=rxd_n2k, args=(cfg['can_intf'],)) t_rxd_n2k.start() if cfg['nmea0183']: + print("NMEA0183 enabled") t_rxd_0183 = threading.Thread(target=rxd_0183, args=(cfg['0183_port'],)) t_rxd_0183.start() if cfg['gps']: + print("GPS enabled (local)") t_rxd_gps = threading.Thread(target=rxd_gps, args=(cfg['gps_port'],)) t_rxd_gps.start() if not cfg['simulation']: - t_data = threading.Thread(target=datareader, args=(cfg['histpath'], history)) - t_data.start() + if cfg['bme280']: + t_data = threading.Thread(target=datareader, args=(cfg, history)) + t_data.start() + else: + print("Simulation mode enabled") app = Frontend(cfg, owndevice, boatdata, profile) app.run() @@ -708,6 +707,6 @@ if __name__ == "__main__": t_rxd_0183.join() if cfg['gps']: t_rxd_gps.join() - if not cfg['simulation']: + if not cfg['simulation'] and cfg['bme280']: t_data.join() print("Another fine product of the Sirius Cybernetics Corporation.") diff --git a/pages/barograph.py b/pages/barograph.py index e97d50f..e0e5f52 100644 --- a/pages/barograph.py +++ b/pages/barograph.py @@ -28,6 +28,10 @@ Damit eine saubere Skala auf der Y-Achse erreicht wird, gibt einige feste Skalierungen. Standard: 20hPa von unten nach oben, z.B. 1015, 1020, 1025, 1030, 1035 +TODO + - wenn keine Luftdruckdaten vorliegen, dann eine entsprechende + Meldung anzeigen + """ import time