Review: Fortebit Polaris 3G Kit+
Een kwestie van software
Deze GPS-tracker is anders, en wel op een heel goede manier vanuit ontwikkelaar- en klantperspectief. Het is een open platform, iets wat bij GPS-trackers zelden het geval is. Firmware met basisfuncties voor het volgen van voertuigen is af fabriek geïnstalleerd (wat met andere systemen uiteraard ook mogelijk is). Het grote verschil is dat u voor deze GPS-tracker zelf software kunt schrijven – en dat hoeft voor de verandering niet per se in C/C++. Elk apparaat wordt geleverd met de ZERYNTH-middleware en een geïntegreerde licentie voor de daarin ondergebrachte Python-omgeving. ZERYNTH Studio, dat bij de fabrikant kan worden gedownload, is vereist voor ontwikkeling. Zelfs als u niet met Python aan de slag wilt, moet u dat toch doen, omdat de geïntegreerde licentie voor de Python-omgeving op de MCU anders verloren zou gaan wanneer een Python-programma niet minstens één keer met succes is overgedragen en de licentie op de lokale computer daarbij in veiligheid is gebracht. Bij ZERYNTH Studio is het verplicht om een account bij de fabrikant te hebben om het te kunnen gebruiken.
ZERYNTH Studio zelf is wat de editor betreft heel eenvoudig gehouden; wanneer u alleen een editor voor de broncode wilt, bent u beter af met andere gratis software. Onder andere Visual Code wordt officieel ondersteund. De licentie voor de Python-omgeving kan en moet in veiligheid worden gebracht voordat verdere acties worden ondernomen. Daarna kan de eerste test van de GPS tracker worden gestart. De firmware op het apparaat kan alleen gegevens uitwisselen met het Fortebit-portal, waarvoor op zijn beurt een account nodig is. Dit account is niet gratis; bij aankoop van de GPS-tracker kunt u het echter 30 dagen gratis uitproberen. Iedereen die een wagenpark of andere zaken wil monitoren, stelt zijn eigen eisen aan de gegevensverwerking en -voorbereiding. Daarom gaan we hier niet nader op dit portal in, omdat het ons vooral om de hardware gaat en niet om het telematica-portal.
Vanaf hier moet u zelf aan de slag om de demo-applicatie gegevens te laten uitwisselen met het systeem waarvoor u gekozen hebt. Ook wanneer u al een portal voor het volgen van voertuiggegevens hebt, kunt u deze stap niet vermijden. Dit is iets dat nog voor verbetering vatbaar is ten opzichte van de overigens goede eerste indruk. Wilt u niet met Python werken, dan kunt u via het Arduino-framework de GPS-tracker voorzien van nieuwe software. De benodigde software en Board Packages zijn te vinden op het ForteBit GitHub-account en kunnen met behulp van de Board Manager in de Arduino IDE worden geïntegreerd.
We hebben het Python-voorbeeld herschreven om de tracker uit te proberen (het betreft demosoftware die niet geschikt voor ‘echt’ gebruik). De demo laat echter zien hoe de hardware kan worden aangesloten op een MQTT-broker naar keuze. Met een paar regels Python worden de aanpassingen doorgevoerd en snel in de hardware geladen. De GPS-gegevens worden naar een MQTT-makelaar naar keuze gestuurd.
Overzicht
De open hardware-benadering van de GPS-tracker en de flexibiliteit in de keuze van de ontwikkelomgeving en programmeertaal maken de Fortebit Polaris 3G Kit+ tot een zeer interessant platform, zeker wanneer u uw eigen speciale functies wilt implementeren. De documentatie en ondersteuning met voorbeelden voor Python en de Arduino-omgeving zijn echter nog voor uitbreiding vatbaar – hopelijk maakt de fabrikant daar nog werk van.
Om de Fortebit Polaris 3G Kit+ met een MQTT-broker naar keuze uit te proberen, geven we hieronder het gewijzigde main.py-bestand:
# POLARIS_FORTEBIT
# Created at 2019-07-26 11:34:26.282569
# modified by someone(tm) 06-08-2020 for generic MQTT ( unsecured ) transport
# to use test.mosquitto.org as mqtt broker
# not for production use at all !
from mqtt import mqtt
from fortebit.polaris import polaris
from fortebit.polaris import modem
from fortebit.polaris import gnss
import vm
import sfw
vm.set_option(vm.VM_OPT_RESET_ON_EXCEPTION, 1)
vm.set_option(vm.VM_OPT_TRACE_ON_EXCEPTION, 1)
vm.set_option(vm.VM_OPT_RESET_ON_HARDFAULT, 1)
vm.set_option(vm.VM_OPT_TRACE_ON_HARDFAULT, 1)
import streams
s = streams.serial()
import mcu
import timestamp
import timers
import ssl
import requests
import threading
from wireless import gsm
import utils
sleep(1000)
# CONFIG
poll_time = 100 # poll inputs at twice the specified period in ms
gps_period = 10000 # gps lat,lon and speed telemetry period in ms
update_period = 6 * gps_period # other telemetry data period in ms
no_ignition_period = 300000 # no ignition telemetry data period in ms
fw_version = "1.11"
# POLARIS INIT
try:
print("Polaris default app")
polaris.init()
print("MCU UID:", [hex(b) for b in mcu.uid()])
print("VM info:", vm.info())
print("FW version:", fw_version)
print("Watchdog was triggered:", sfw.watchdog_triggered())
polaris.ledRedOn()
polaris.ledGreenOff()
except Exception as e:
print("Failed polaris init with", e)
sleep(500)
mcu.reset()
# INIT HW
try:
print("Initializing Modem...")
modem = modem.init()
print("Initializing GNSS...")
gnss = gnss.init()
# verify preconditions and start utility thread
utils.start()
print("Starting Accelerometer...")
import accel
accel.start()
print("Starting GNSS...")
gnss.start()
gnss.set_rate(2000)
print("Starting Modem...")
modem.startup()
# enable modem/gnss utilities
utils.modem = modem
utils.gnss = gnss
sfw.watchdog(0, 30000)
sfw.kick()
if utils.check_terminal(s):
utils.do_terminal(s)
minfo = gsm.mobile_info()
print("Modem:", minfo)
# enable SMS checking
utils.check_sms = True
except Exception as e:
print("Failed init hw with", e)
sleep(500)
mcu.reset()
# GATHERING SETTINGS
name = None
apn = None
email = None
try:
print("Read settings...")
settings = utils.readSettings()
sfw.kick()
if "apn" in settings:
apn = settings["apn"]
print("APN:", apn)
if "email" in settings:
email = settings["email"]
print("Email:", email)
if "name" in settings:
name = settings["name"]
print("Name:", name)
if apn is not None and not utils.validate_apn(apn):
print("Invalid APN!")
apn = None
if email is not None and not utils.validate_email(email):
print("Invalid email!")
email = None
if name is None:
name = "Polaris"
print("Saving name:", name)
settings["name"] = name
utils.saveSettings(settings)
if apn is None:
print("APN is not defined, can't connect to Internet!")
apn = utils.request_apn(s)
print("Saving email:", email)
print("Saving APN:", apn)
settings["apn"] = apn
utils.saveSettings(settings)
if email is None:
print("email is not defined, can't register to Cloud!")
email = utils.request_email(s)
settings["email"] = email
utils.saveSettings(settings)
except Exception as e:
print("Failed gathering settings with", e)
sleep(500)
mcu.reset()
# GSM ATTACH
try:
print("Waiting for network...",end='')
for _ in range(150):
sfw.kick()
ninfo = gsm.network_info()
if ninfo[6]:
break
sleep(1000)
if (_ % 10) == 9:
print('.',end='')
else:
raise TimeoutError
print("ok!")
print("Attached to network:", ninfo)
print("Activating data connection...")
for _ in range(3):
try:
gsm.attach(apn)
break
except Exception as e:
print("Retrying...", e)
try:
gsm.detach()
sleep(5000 * (_ + 1))
except:
pass
else:
raise TimeoutError
linfo = gsm.link_info()
print("Connection parameters:", linfo)
except Exception as e:
print("Network failure", e)
sleep(500)
mcu.reset()
# TELEMETRY LOOP
try:
accel.get_sigma() # reset accumulated value
sleep(500)
last_time = -(no_ignition_period + gps_period) # force sending data immediately
counter = 0
sos = -1
connected = True
ignition = None
sos = None
telemetry = {}
disconn_time = None
while True:
# allow other threads to run while waiting
sleep(poll_time*2)
sfw.kick()
now_time = timers.now()
if utils.check_terminal(s):
utils.do_terminal(s)
# read inputs
old_ign = ignition
ignition = polaris.getIgnitionStatus()
old_sos = sos
sos = polaris.getEmergencyStatus()
extra_send = False
# led waiting status
utils.status_led(False, ignition, connected)
if sos != old_sos:
telemetry['sos'] = sos
extra_send = True
if ignition != old_ign:
telemetry['ignition'] = ignition
extra_send = True
if not extra_send:
if ignition == 0:
# sleep as indicated by rate for no ignition
if now_time - last_time < no_ignition_period - poll_time:
continue
extra_send = True
else:
# sleep as indicated by rate
if now_time - last_time < gps_period - poll_time:
continue
ts = modem.rtc()
#print("MODEM RTC =", ts)
if counter % (update_period / gps_period) == 0 or extra_send:
telemetry['ignition'] = ignition
telemetry['sos'] = sos
if polaris.isBatteryBackup():
telemetry['charger'] = -1
else:
telemetry['battery'] = utils.decimal(3, polaris.readMainVoltage())
telemetry['charger'] = polaris.getChargerStatus()
telemetry['backup'] = utils.decimal(3, polaris.readBattVoltage())
telemetry['temperature'] = utils.decimal(2, accel.get_temperature())
telemetry['sigma'] = utils.decimal(3, accel.get_sigma())
pr = accel.get_pitchroll()
telemetry['pitch'] = utils.decimal(1, pr[0])
telemetry['roll'] = utils.decimal(1, pr[1])
if gnss.has_fix():
fix = gnss.fix()
# only transmit position when it's accurate
if fix[6] < 2.5:
telemetry['latitude'] = utils.decimal(6, fix[0])
telemetry['longitude'] = utils.decimal(6, fix[1])
telemetry['speed'] = utils.decimal(1, fix[3])
if counter % (update_period / gps_period) == 0 or extra_send:
telemetry['altitude'] = utils.decimal(1, fix[2])
telemetry['COG'] = utils.decimal(1, fix[4])
if counter % (update_period / gps_period) == 0 or extra_send:
telemetry['nsat'] = fix[5]
telemetry['HDOP'] = utils.decimal(2, fix[6])
# replace timestamp with GPS
ts = fix[9]
elif ts is not None and ts[0] < 2019:
ts = None
if ts is not None:
ts = str(timestamp.to_unix(ts)) + "000"
counter += 1
last_time = now_time
# led sending status
utils.status_led(True, ignition, connected)
telemetry['counter']=counter
telemetry['dev_ts']=ts
sfw.kick()
# set the mqtt id to "zerynth-mqtt"
client = mqtt.Client("zerynth-mqtt",True)
# and try to connect to "test.mosquitto.org"
for retry in range(10):
try:
client.connect("test.mosquitto.org", 60)
break
except Exception as e:
print("connecting...")
print("connected.")
print("Publishing:", telemetry)
client.publish("test", str(telemetry))
client.loop()
ok = False;
except Exception as e:
print("Failed telemetry loop", e)
sleep(500)
mcu.reset()
Discussie (0 opmerking(en))