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
|
||||
|
|
105
obp60v.py
105
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}")
|
||||
#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(topic_racestatus)
|
||||
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