Publish für MQTT integriert
This commit is contained in:
		
							parent
							
								
									e738438a94
								
							
						
					
					
						commit
						4ba75b5686
					
				
							
								
								
									
										2
									
								
								INSTALL
								
								
								
								
							
							
						
						
									
										2
									
								
								INSTALL
								
								
								
								
							|  | @ -5,7 +5,7 @@ Python muß mindestens Version 3.10 sein. | |||
| 
 | ||||
| apt-get install python3-cairo python3-gi python3-gi-cairo gir1.2-rsvg-2.0 \ | ||||
|     python3-serial python3-nmea2 python3-smbus2 python3-bme280 python3-astral \ | ||||
|     python3-can | ||||
|     python3-can python3-paho-mqtt | ||||
| 
 | ||||
| Zusätzlich zu diesem Programm muß auch die zugehörige NMEA2000-Bibliothek | ||||
| für Python vorhanden sein. Diese kann momentan am besten parallel zu dem  | ||||
|  |  | |||
							
								
								
									
										13
									
								
								nmea0183.py
								
								
								
								
							
							
						
						
									
										13
									
								
								nmea0183.py
								
								
								
								
							|  | @ -55,18 +55,17 @@ def GGA(boatdata, msg): | |||
| def GLL(boatdata, msg): | ||||
|     # Position data: position fix, time of position fix, and status | ||||
|     # UTC of position ignored | ||||
|     print(msg.data) | ||||
|     if not msg.status == 'A': | ||||
|         return | ||||
|     lat_fac = 1 if msg.lat_dir == 'N' else -1 | ||||
|     lat_deg = int(msg.lat[0:2]) | ||||
|     lat_min = float(msg.lat[2:]) | ||||
|     print(lat_deg, lat_min) | ||||
|     boatdata.setValue("LAT", lat_fac * lat_deg + lat_min / 60) | ||||
|     lon_fac = 1 if msg.lon_dir == 'E' else -1 | ||||
|     lon_deg = int(msg.lon[0:3]) | ||||
|     lon_min = float(msg.lon[3:]) | ||||
|     boatdata.setValue("LON", lon_fac * lon_deg + lon_min / 60) | ||||
|     boatdata.setValue("TSPOS", msg.timestamp) # datetime.time, UTC | ||||
| 
 | ||||
| def GSA(boatdata, msg): | ||||
|     # Satellites | ||||
|  | @ -297,7 +296,7 @@ def VBW(boatdata, msg): | |||
|     print("-> VBW") | ||||
| 
 | ||||
| def VHW(boatdata, msg): | ||||
|     print("-> VHW") | ||||
|     #print("-> VHW") | ||||
|     boatdata.setValue("STW", float(msg.water_speed_knots)) | ||||
| 
 | ||||
| def VPW(boatdata, msg): | ||||
|  | @ -319,15 +318,17 @@ def VTG(boatdata, msg): | |||
| ('FAA mode indicator', 'faa_mode')) | ||||
| ['', 'T', '', 'M', '0.117', 'N', '0.216', 'K', 'A'] | ||||
|     """ | ||||
|     print("-> VTG") | ||||
|     #print("-> VTG") | ||||
|     # msg.true_track true_track_sym | ||||
|     # msg.mag_track mag_track_sym | ||||
|     # msg.faa_mode | ||||
|     #TODO klären was für Typen hier ankommen können | ||||
|     # bytearray, str, decimal.Decimal? | ||||
|     sog = float(msg.spd_over_grnd_kts) | ||||
|     #sog = float(msg.spd_over_grnd_kts) | ||||
|     #str von OpenCPN: sog = float(msg.spd_over_grnd_kts[:-1]) | ||||
|     boatdata.setValue("SOG", sog) | ||||
|     #boatdata.setValue("SOG", sog) | ||||
|     #print("VTG", msg.spd_over_grnd_kts) | ||||
|     print("VTG", msg) | ||||
| 
 | ||||
| def VWR(boatdata, msg): | ||||
|     # Relative Wind Speed and Angle | ||||
|  |  | |||
|  | @ -46,6 +46,14 @@ mqtt_pass = 123456 | |||
| orgname = demo | ||||
| passcode = 123456 | ||||
| 
 | ||||
| [boat] | ||||
| name = MY boat | ||||
| sailno = GER 4711 | ||||
| class = One off | ||||
| handicap = 100.0 | ||||
| club = NONE | ||||
| team = OBP | ||||
| 
 | ||||
| [settings] | ||||
| timezone = 1 | ||||
| boat_draft = 1.3 | ||||
|  |  | |||
							
								
								
									
										107
									
								
								obp60v.py
								
								
								
								
							
							
						
						
									
										107
									
								
								obp60v.py
								
								
								
								
							|  | @ -126,41 +126,85 @@ cfg = { | |||
|     'industrygroup': 4, # Marine | ||||
|     'gps': False, | ||||
|     'bme280': False, | ||||
|     'tracker': { 'type': 'NONE' } | ||||
|     'tracker': { 'type': 'NONE' }, | ||||
|     'boat': { } | ||||
| } | ||||
| 
 | ||||
| def mqtt_on_connect(client, userdata, flags, rc): | ||||
|     print(f"MQTT connected with result code {rc}") | ||||
|     client.subscribe("regattahero/orgstatus/thomas") | ||||
|     #client.subscribe(topic_racestatus) | ||||
|     #userdata['connect_rc'] = rc | ||||
|     if rc != 0: | ||||
|         # Result codes: | ||||
|         # 1: Connection Refused, unacceptable protocol version | ||||
|         # 2: Connection Refused, identifier rejected | ||||
|         # 3: Connection Refused, Server unavailable | ||||
|         # 4: Connection Refused, bad user name or password | ||||
|         # 5: Connection Refused, not authorized | ||||
|         #userdata['connect_ok'] = True | ||||
|         pass | ||||
|     else: | ||||
|         client.subscribe("regattahero/orgstatus/thomas") | ||||
|         client.subscribe("regattahero/racestatus/thomas/#") | ||||
|         #userdata['connect_ok'] = False | ||||
| 
 | ||||
| def mqtt_on_message(client, userdata, msg): | ||||
|     """ | ||||
|     TODO raceid über userdata? dann topic prüfen? | ||||
|     """ | ||||
|     if msg.topic == "regattahero/orgstatus/thomas": | ||||
|         # kommt alle 10s | ||||
|         orgstatus = json.loads(msg.payload) | ||||
|         if orgstatus['allLogout']: | ||||
|             print("All logout received!") | ||||
|             client.disconnect() | ||||
|             sys.exit(0) | ||||
|             sys.exit(0) # TODO nur die MQTT-Task beenden | ||||
|         if orgstatus['message']: | ||||
|             # TODO Alarm-Funktion nutzen? | ||||
|             print("Nachricht der Wettfahrtkeitung:") | ||||
|             print(orgstatus['message']) | ||||
|         print(orgstatus['races']) | ||||
|         #for r in orgstatus['races']: | ||||
|         #    print(f"Race: {r}") | ||||
|     elif msg.topic.startswith("regattahero/racestatus/thomas"): | ||||
|         racestatus = json.loads(msg.payload) | ||||
|         print(racestatus) | ||||
|         # kommt alle 1s | ||||
|         # dem Topic angehängt ist noch die raceid | ||||
|         payload = json.loads(msg.payload) | ||||
|         racestatus = payload['racestatus'] | ||||
|         """ | ||||
|         time: negativ: Zeit vor dem Start, positiv: Zeit nach dem Start | ||||
|               in Sekunden | ||||
|          | ||||
|         Signale der Wettfahrtleitung hier anzeigen  | ||||
|            Regattaabbruch | ||||
|            Bahnverkürzung | ||||
|            Rückrufe | ||||
|          | ||||
|         """ | ||||
|     else: | ||||
|         print(f"UNKNOWN TOPIC: {msg.topic}") | ||||
|         print(msg.payload) | ||||
| 
 | ||||
| def mqtt_tracker(cfg): | ||||
| def mqtt_publish(client, topic, payload, bv_lat, bv_lon, bv_sog): | ||||
|     """ | ||||
|     Payload vorbelegt als Template, so daß nur noch die veränderlichen | ||||
|     GPS-Daten eingefügt werden müssen: LAT LON SOG TIMESTAMP | ||||
|     """ | ||||
|     lat = bv_lat.getValueRaw() | ||||
|     lon = bv_lon.getValueRaw() | ||||
|     sog = bv_sog.getValueRaw() | ||||
|     if lat and lon and sog: | ||||
|         payload['gps']['lat'] = round(lat, 5) | ||||
|         payload['gps']['lon'] = round(lon, 5) | ||||
|         payload['gps']['speed'] = sog | ||||
|         payload['gps']['timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()) | ||||
|          | ||||
|         client.publish(topic, json.dumps(payload)) | ||||
|     else: | ||||
|         print("No GPS data available. Nothing published!") | ||||
| 
 | ||||
| def mqtt_tracker(cfg, boat): | ||||
|     import paho.mqtt.client as mqtt | ||||
|     print("MQTT tracker enabled") | ||||
|     print(cfg) | ||||
|     client = mqtt.Client() | ||||
|     client.on_connect = mqtt_on_connect | ||||
|     client.on_message = mqtt_on_message | ||||
|  | @ -170,10 +214,42 @@ def mqtt_tracker(cfg): | |||
|     except ConnectionRefusedError: | ||||
|         print("MQTT connection refused. Check username and password.") | ||||
|         return | ||||
| 
 | ||||
|     topic = "regattahero/tracker/" + cfg['orgname'] | ||||
|     payload = { | ||||
|         "passcode": cfg['passcode'], | ||||
|         "orgid":  cfg['orgname'], | ||||
|         "raceid": "Demo Regatta", # TODO aus Selektion einstellen  | ||||
|         "gps": { | ||||
|             "lat": 0.0, | ||||
|             "lon": 0.0, | ||||
|             "speed": 0.0, | ||||
|             "age": 1000, | ||||
|             "odo": 1000, | ||||
|             "bat": 1.0, | ||||
|             "timestamp": ""  # ISO8601 Format mit Millisekunden in UTC | ||||
|             }, | ||||
|         "boat": { | ||||
|             "boatid": cfg['uuid'], | ||||
|             "sailno": boat['sailno'], | ||||
|             "team": boat['team'], | ||||
|             "boatclass": boat['class'], | ||||
|             "handicap": boat['handicap'], | ||||
|             "club": boat['club'], | ||||
|             "boatname": boat['name'] | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     # Zugriff auf Boatdata: Referenzen für leichten schnellen Zugriff | ||||
|     bv_lat = boatdata.getRef("LAT") | ||||
|     bv_lon = boatdata.getRef("LON") | ||||
|     bv_sog = boatdata.getRef("SOG") | ||||
| 
 | ||||
|     client.loop_start() | ||||
|     while not shutdown: | ||||
|         time.sleep(1) | ||||
|         #TODO publish here | ||||
|         if tracker_active: | ||||
|             mqtt_publish(client, topic, payload, bv_lat, bv_lon, bv_sog) | ||||
|     client.loop_stop() | ||||
|     client.disconnect() | ||||
| 
 | ||||
|  | @ -258,7 +334,7 @@ def rxd_0183(devname): | |||
|     setthreadtitle("0183listener") | ||||
|     while not shutdown: | ||||
|         raw = ser.readline().decode('ascii') | ||||
|         if len(raw) == 0: | ||||
|         if len(raw.strip()) == 0: | ||||
|             continue | ||||
|         try: | ||||
|             msg = pynmea2.parse(raw) | ||||
|  | @ -722,6 +798,7 @@ if __name__ == "__main__": | |||
|     setproctitle("obp60v") | ||||
| 
 | ||||
|     shutdown = False | ||||
|     tracker_active = False | ||||
| 
 | ||||
|     owndevice = Device(100) | ||||
|     # Hardcoding device, not intended to change | ||||
|  | @ -791,6 +868,14 @@ if __name__ == "__main__": | |||
|     cfg['tracker']['orgname'] = config.get('tracker', 'orgname') | ||||
|     cfg['tracker']['passcode'] = config.get('tracker', 'passcode') | ||||
| 
 | ||||
|     # Boat data | ||||
|     cfg['boat']['name'] = config.get('boat', 'name') | ||||
|     cfg['boat']['sailno'] = config.get('boat', 'sailno') | ||||
|     cfg['boat']['class'] = config.get('boat', 'class') | ||||
|     cfg['boat']['handicap'] = config.getfloat('boat', 'handicap') | ||||
|     cfg['boat']['club'] = config.get('boat', 'club') | ||||
|     cfg['boat']['team'] = config.get('boat', 'team') | ||||
| 
 | ||||
|     # Client UUID. Automatisch erzeugen wenn noch nicht vorhanden | ||||
|     create_uuid = False | ||||
|     try: | ||||
|  | @ -831,7 +916,7 @@ if __name__ == "__main__": | |||
|         t_rxd_net = threading.Thread(target=rxd_network, args=(cfg['net_port'],cfg['net_addr'])) | ||||
|         t_rxd_net.start() | ||||
|     if cfg['tracker']['type'] != 'NONE': | ||||
|         t_tracker = threading.Thread(target=mqtt_tracker, args=(cfg['tracker'],)) | ||||
|         t_tracker = threading.Thread(target=mqtt_tracker, args=(cfg['tracker'],cfg['boat'])) | ||||
|         t_tracker.start() | ||||
|     if not cfg['simulation']: | ||||
|         if cfg['bme280']: | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ from .mob import MOB | |||
| from .rollpitch import RollPitch | ||||
| from .skyview import SkyView | ||||
| from .solar import Solar | ||||
| from .tracker import Tracker | ||||
| from .rudder import Rudder | ||||
| from .voltage import Voltage | ||||
| from .windrose import WindRose | ||||
|  |  | |||
|  | @ -1,3 +1,8 @@ | |||
| """ | ||||
| Tracker with MQTT client | ||||
|  - currentry only Ragatta hero supported | ||||
| """ | ||||
| 
 | ||||
| import cairo | ||||
| from .page import Page | ||||
| 
 | ||||
|  | @ -9,7 +14,14 @@ class Tracker(Page): | |||
|     def draw(self, ctx): | ||||
|         # Name | ||||
|         ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD) | ||||
|         ctx.set_font_size(60)  | ||||
|         ctx.set_font_size(32)  | ||||
|         ctx.move_to(20, 100) | ||||
|         ctx.show_text("Tracker") | ||||
| 
 | ||||
|         ctx.set_font_size(16) | ||||
|         ctx.move_to(20, 140) | ||||
|         ctx.show_text("active: ") | ||||
|         if tracker_active: | ||||
|             ctx.show_text("yes") | ||||
|         else: | ||||
|             ctx.show_text("no") | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue