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 \
|
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-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
|
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
|
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):
|
def GLL(boatdata, msg):
|
||||||
# Position data: position fix, time of position fix, and status
|
# Position data: position fix, time of position fix, and status
|
||||||
# UTC of position ignored
|
# UTC of position ignored
|
||||||
print(msg.data)
|
|
||||||
if not msg.status == 'A':
|
if not msg.status == 'A':
|
||||||
return
|
return
|
||||||
lat_fac = 1 if msg.lat_dir == 'N' else -1
|
lat_fac = 1 if msg.lat_dir == 'N' else -1
|
||||||
lat_deg = int(msg.lat[0:2])
|
lat_deg = int(msg.lat[0:2])
|
||||||
lat_min = float(msg.lat[2:])
|
lat_min = float(msg.lat[2:])
|
||||||
print(lat_deg, lat_min)
|
|
||||||
boatdata.setValue("LAT", lat_fac * lat_deg + lat_min / 60)
|
boatdata.setValue("LAT", lat_fac * lat_deg + lat_min / 60)
|
||||||
lon_fac = 1 if msg.lon_dir == 'E' else -1
|
lon_fac = 1 if msg.lon_dir == 'E' else -1
|
||||||
lon_deg = int(msg.lon[0:3])
|
lon_deg = int(msg.lon[0:3])
|
||||||
lon_min = float(msg.lon[3:])
|
lon_min = float(msg.lon[3:])
|
||||||
boatdata.setValue("LON", lon_fac * lon_deg + lon_min / 60)
|
boatdata.setValue("LON", lon_fac * lon_deg + lon_min / 60)
|
||||||
|
boatdata.setValue("TSPOS", msg.timestamp) # datetime.time, UTC
|
||||||
|
|
||||||
def GSA(boatdata, msg):
|
def GSA(boatdata, msg):
|
||||||
# Satellites
|
# Satellites
|
||||||
|
@ -297,7 +296,7 @@ def VBW(boatdata, msg):
|
||||||
print("-> VBW")
|
print("-> VBW")
|
||||||
|
|
||||||
def VHW(boatdata, msg):
|
def VHW(boatdata, msg):
|
||||||
print("-> VHW")
|
#print("-> VHW")
|
||||||
boatdata.setValue("STW", float(msg.water_speed_knots))
|
boatdata.setValue("STW", float(msg.water_speed_knots))
|
||||||
|
|
||||||
def VPW(boatdata, msg):
|
def VPW(boatdata, msg):
|
||||||
|
@ -319,15 +318,17 @@ def VTG(boatdata, msg):
|
||||||
('FAA mode indicator', 'faa_mode'))
|
('FAA mode indicator', 'faa_mode'))
|
||||||
['', 'T', '', 'M', '0.117', 'N', '0.216', 'K', 'A']
|
['', 'T', '', 'M', '0.117', 'N', '0.216', 'K', 'A']
|
||||||
"""
|
"""
|
||||||
print("-> VTG")
|
#print("-> VTG")
|
||||||
# msg.true_track true_track_sym
|
# msg.true_track true_track_sym
|
||||||
# msg.mag_track mag_track_sym
|
# msg.mag_track mag_track_sym
|
||||||
# msg.faa_mode
|
# msg.faa_mode
|
||||||
#TODO klären was für Typen hier ankommen können
|
#TODO klären was für Typen hier ankommen können
|
||||||
# bytearray, str, decimal.Decimal?
|
# 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])
|
#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):
|
def VWR(boatdata, msg):
|
||||||
# Relative Wind Speed and Angle
|
# Relative Wind Speed and Angle
|
||||||
|
|
|
@ -46,6 +46,14 @@ mqtt_pass = 123456
|
||||||
orgname = demo
|
orgname = demo
|
||||||
passcode = 123456
|
passcode = 123456
|
||||||
|
|
||||||
|
[boat]
|
||||||
|
name = MY boat
|
||||||
|
sailno = GER 4711
|
||||||
|
class = One off
|
||||||
|
handicap = 100.0
|
||||||
|
club = NONE
|
||||||
|
team = OBP
|
||||||
|
|
||||||
[settings]
|
[settings]
|
||||||
timezone = 1
|
timezone = 1
|
||||||
boat_draft = 1.3
|
boat_draft = 1.3
|
||||||
|
|
105
obp60v.py
105
obp60v.py
|
@ -126,41 +126,85 @@ cfg = {
|
||||||
'industrygroup': 4, # Marine
|
'industrygroup': 4, # Marine
|
||||||
'gps': False,
|
'gps': False,
|
||||||
'bme280': False,
|
'bme280': False,
|
||||||
'tracker': { 'type': 'NONE' }
|
'tracker': { 'type': 'NONE' },
|
||||||
|
'boat': { }
|
||||||
}
|
}
|
||||||
|
|
||||||
def mqtt_on_connect(client, userdata, flags, rc):
|
def mqtt_on_connect(client, userdata, flags, rc):
|
||||||
print(f"MQTT connected with result code {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("regattahero/orgstatus/thomas")
|
||||||
#client.subscribe(topic_racestatus)
|
client.subscribe("regattahero/racestatus/thomas/#")
|
||||||
|
#userdata['connect_ok'] = False
|
||||||
|
|
||||||
def mqtt_on_message(client, userdata, msg):
|
def mqtt_on_message(client, userdata, msg):
|
||||||
"""
|
"""
|
||||||
TODO raceid über userdata? dann topic prüfen?
|
TODO raceid über userdata? dann topic prüfen?
|
||||||
"""
|
"""
|
||||||
if msg.topic == "regattahero/orgstatus/thomas":
|
if msg.topic == "regattahero/orgstatus/thomas":
|
||||||
|
# kommt alle 10s
|
||||||
orgstatus = json.loads(msg.payload)
|
orgstatus = json.loads(msg.payload)
|
||||||
if orgstatus['allLogout']:
|
if orgstatus['allLogout']:
|
||||||
print("All logout received!")
|
print("All logout received!")
|
||||||
client.disconnect()
|
client.disconnect()
|
||||||
sys.exit(0)
|
sys.exit(0) # TODO nur die MQTT-Task beenden
|
||||||
if orgstatus['message']:
|
if orgstatus['message']:
|
||||||
# TODO Alarm-Funktion nutzen?
|
# TODO Alarm-Funktion nutzen?
|
||||||
print("Nachricht der Wettfahrtkeitung:")
|
print("Nachricht der Wettfahrtkeitung:")
|
||||||
print(orgstatus['message'])
|
print(orgstatus['message'])
|
||||||
|
print(orgstatus['races'])
|
||||||
#for r in orgstatus['races']:
|
#for r in orgstatus['races']:
|
||||||
# print(f"Race: {r}")
|
# print(f"Race: {r}")
|
||||||
elif msg.topic.startswith("regattahero/racestatus/thomas"):
|
elif msg.topic.startswith("regattahero/racestatus/thomas"):
|
||||||
racestatus = json.loads(msg.payload)
|
# kommt alle 1s
|
||||||
print(racestatus)
|
# 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:
|
else:
|
||||||
print(f"UNKNOWN TOPIC: {msg.topic}")
|
print(f"UNKNOWN TOPIC: {msg.topic}")
|
||||||
print(msg.payload)
|
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
|
import paho.mqtt.client as mqtt
|
||||||
print("MQTT tracker enabled")
|
print("MQTT tracker enabled")
|
||||||
print(cfg)
|
|
||||||
client = mqtt.Client()
|
client = mqtt.Client()
|
||||||
client.on_connect = mqtt_on_connect
|
client.on_connect = mqtt_on_connect
|
||||||
client.on_message = mqtt_on_message
|
client.on_message = mqtt_on_message
|
||||||
|
@ -170,10 +214,42 @@ def mqtt_tracker(cfg):
|
||||||
except ConnectionRefusedError:
|
except ConnectionRefusedError:
|
||||||
print("MQTT connection refused. Check username and password.")
|
print("MQTT connection refused. Check username and password.")
|
||||||
return
|
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()
|
client.loop_start()
|
||||||
while not shutdown:
|
while not shutdown:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
#TODO publish here
|
if tracker_active:
|
||||||
|
mqtt_publish(client, topic, payload, bv_lat, bv_lon, bv_sog)
|
||||||
client.loop_stop()
|
client.loop_stop()
|
||||||
client.disconnect()
|
client.disconnect()
|
||||||
|
|
||||||
|
@ -258,7 +334,7 @@ def rxd_0183(devname):
|
||||||
setthreadtitle("0183listener")
|
setthreadtitle("0183listener")
|
||||||
while not shutdown:
|
while not shutdown:
|
||||||
raw = ser.readline().decode('ascii')
|
raw = ser.readline().decode('ascii')
|
||||||
if len(raw) == 0:
|
if len(raw.strip()) == 0:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
msg = pynmea2.parse(raw)
|
msg = pynmea2.parse(raw)
|
||||||
|
@ -722,6 +798,7 @@ if __name__ == "__main__":
|
||||||
setproctitle("obp60v")
|
setproctitle("obp60v")
|
||||||
|
|
||||||
shutdown = False
|
shutdown = False
|
||||||
|
tracker_active = False
|
||||||
|
|
||||||
owndevice = Device(100)
|
owndevice = Device(100)
|
||||||
# Hardcoding device, not intended to change
|
# Hardcoding device, not intended to change
|
||||||
|
@ -791,6 +868,14 @@ if __name__ == "__main__":
|
||||||
cfg['tracker']['orgname'] = config.get('tracker', 'orgname')
|
cfg['tracker']['orgname'] = config.get('tracker', 'orgname')
|
||||||
cfg['tracker']['passcode'] = config.get('tracker', 'passcode')
|
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
|
# Client UUID. Automatisch erzeugen wenn noch nicht vorhanden
|
||||||
create_uuid = False
|
create_uuid = False
|
||||||
try:
|
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 = threading.Thread(target=rxd_network, args=(cfg['net_port'],cfg['net_addr']))
|
||||||
t_rxd_net.start()
|
t_rxd_net.start()
|
||||||
if cfg['tracker']['type'] != 'NONE':
|
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()
|
t_tracker.start()
|
||||||
if not cfg['simulation']:
|
if not cfg['simulation']:
|
||||||
if cfg['bme280']:
|
if cfg['bme280']:
|
||||||
|
|
|
@ -37,6 +37,7 @@ from .mob import MOB
|
||||||
from .rollpitch import RollPitch
|
from .rollpitch import RollPitch
|
||||||
from .skyview import SkyView
|
from .skyview import SkyView
|
||||||
from .solar import Solar
|
from .solar import Solar
|
||||||
|
from .tracker import Tracker
|
||||||
from .rudder import Rudder
|
from .rudder import Rudder
|
||||||
from .voltage import Voltage
|
from .voltage import Voltage
|
||||||
from .windrose import WindRose
|
from .windrose import WindRose
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
"""
|
||||||
|
Tracker with MQTT client
|
||||||
|
- currentry only Ragatta hero supported
|
||||||
|
"""
|
||||||
|
|
||||||
import cairo
|
import cairo
|
||||||
from .page import Page
|
from .page import Page
|
||||||
|
|
||||||
|
@ -9,7 +14,14 @@ class Tracker(Page):
|
||||||
def draw(self, ctx):
|
def draw(self, ctx):
|
||||||
# Name
|
# Name
|
||||||
ctx.select_font_face("Ubuntu", cairo.FontSlant.NORMAL, cairo.FontWeight.BOLD)
|
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.move_to(20, 100)
|
||||||
ctx.show_text("Tracker")
|
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