Any GPS module in the queue for the Library?



We would like to know if there will be support for any GPS Modules within Zerynth any time soon. The Blog informs about a LoRa node with a Hornet GPS Nano Module interfaced within it.
Or is it simply possible to read the GPS information via the Serial Stream for e.g. when connected to a Particle Photon’s UART?

Thanks for your support.


Most GPS is UART based and if you just read from them you will get a standard text response. To parse it to something usable withing the microcontroller you should look for a pure Python parser or extract needed data yourself.


@riklaunim but that also implies that first one must write a Library to initialize the GPS and provide GPS Fix in order to obtain data from the module


Those I played with were working on their own. The same parts end up in USB based GPS dongles (which are UART GPS + USB UART in one package). By default they send over serial scan results every X seconds. More on for example


here you can find a minimal Zerynth library for OriginGPS Nano Hornet org1411 GPS module.
That library is at a very embrional stage, at the moment it only allows to retrive global position (latitude, longitude and altitude), note that get_lla() method returns a 0 until the module gets itself a fix.

Another article related to this blog post will be published soon,  as well as the complete version of the Nano Hornet library. Stay tuned!


We actually have Adafruit Ultimate GPS breakout board and have tried all possible ways to get the GPS coordinated on the Serial stream but we do not obtain anything as such.
As a part of this we wrote a a class for the MTK3339 class to obtain information but nothing worked there too.

a simple example on the Particle Photon does not yield any values

import streams
s1 = streams.serial(SERIAL1, baud=9600)
while True:
line = s1.readline()
MTK3339 class similar to link below<br><a rel="nofollow" target="_blank" href=""></a> <br><br>



we finally wrote a proper class for MTK3339 and we obtain all the raw values on the particle photon finally.

We will update the thread with the code soon


Great! :slight_smile: Thank you for the valuable contribution.
Since I’m here… the code you posted have a little bug: you forgot to set to False the set_default parameter on s1 definition (note that, as reported in the doc, set_default parameter is True by default). So the print’s output is sent to SERIAL1 instead of SERIAL0 and this is why no value is shown on the serial console.


Hi just to keep the thread going. How can be contribute to the repository the library for the Adafruit Ultimate GPS ?
And also we would like to use a Photon + GPS + RN2483 LoRa Module as an Asset Tracker as a usage example. However since both RN2483 and GPS use two different Serial Ports we need some guidance onto how to use threading with two different serial ports in Zerynth.

1. Thread - 1 could acquire data from SERIAL2 port and return longitude and latitude
2. Thread - 2 could use the coordinates to send it on the LoRa Module to the Gateway (connected to TTN)

Should Lock or Event be used in the usage?



Zerynth Studio allows publishing your projects as library packages that can be used by everyone else in the Zerynth community.
We are fixing a bug that might prevent you from completing the procedure; if this were to happen we can provide support: just post here on this thread or send a PM.

About the Photon.
The 2 exposed serial (SERIAL1 and SERIAL2) are linked to the same hardware peripheral and, as reported in the pins map, should not be used together. However, using a simple trick, it is possible to use both serials even if not simultaneously. Have a look at the code attached.
To exploit this code with lora and gps modules, their libraries have to be edited. For example you can add this function to the rn2483 lib (zerynth2/dist//libs/official/microchip/rn2483/

def update_ser(ser):
   global _ser
    _ser = ser
and call it from the thread whenever you need to use the lora module, passing the proper streams object. <br><br>Or you can modify the _read() function to recreate the streams object every time.<br><br>All of this (lock, reset and so on) it is not needed when the microcontroller have more UART peripherals..<br>&nbsp;<br><br><br><br>


Hi @anba
Sorry couldn’t wrap around the concept so had here is an example

# -- coding: utf-8 --
import mtk3339 as gps
import streams
from microchip.rn2483 import rn2483

def parse():
“”“Parsing RMC data for latitude longitude”""
while True:
if gps._read():
sentence = gps._read().split(’,’)
if sentence[0] == “$GPRMC”:
latitude = sentence[3]
latitude_d = sentence[4]
longitude = sentence[5]
longitude_d = sentence[6]
if latitude_d == “S”:
latitude = -latitude
if longitude_d == “W”:
longitude = -longitude
# trying to send the values to the LoRa Gateway
# Not sure if this is even correct for float values to bytearray

print(rn2483.get_hweui(SERIAL1, D6))

if not rn2483.init(SERIAL1, appeui, appkey, D6):
raise Exception

# This message is sent and can be seen to at the TTN Console


except Exception as e:


except Exception as e:
<br>SERIAL2 is where the GPS is connected and SERIAL1 is the LoRa module. Any help would be appreciated where all we want to do is obtain the latitiude longitude and send them the Gateway for observing how far is the range of our Gateway by plotting it on the TTN mapper.<br>(Haven't yet made changes to the <b>rn2483 </b>module yet as you mentioned)<br><br>


also the file for reference

MTK Command Sheet:
NMEA CheckSum Calc:
Based on
import streams
import timers

new_exception(mtk3339Exception, Exception)

_ser = None

def init(port):
global _ser
_ser = streams.serial(port, set_default=False, baud=9600)
# t = timers.timer()
# t.start()
# while _read() != “$PMTK001,604,332":
# if t.get() > 40000:
# raise mtk3339Exception

def _send(cmd):
_ser.write(cmd + '\r

def _read(timeout=2000):
t = timers.timer()
while not _ser.available():
if t.get() > timeout:
return “timeout”
return _ser.readline()

def hot_fix():
command = "$PMTK101

def warm_start():
command = “$PMTK10231"

def cold_start():
command = "$PMTK103

def full_cold_start():
command = “$PMTK10437"

def set_nmea_updaterate(rate):
rates = {
10000: "$PMTK220,10000
5000: “$PMTK220,50001B",
1000: "$PMTK220,1000
500: “$PMTK220,5002B",
200: "$PMTK220, 200
100: “$PMTK220,1002F"

if rate in rates:
command = rates[rate]
print(“wrong rate”)

def set_nmea_baudrate(baud):
baudrates = {
4800: "$PMTK251,4800
9600: “$PMTK251,960017",
14400: "$PMTK251,14400
19200: “$PMTK251,1920022",
38400: "$PMTK251,38400
57600: “$PMTK251,576002C",
115200: "$PMTK251,115200

if baud in baudrates:
command = baudrates[baud]
print(“wrong baudrate, setting default”)
command = baudrates[9600]

def set_nmea_output(op_type):
types_op = {
“rmc_only”: “$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,029",
“rmc_gga”: "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
“all”: “$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,028",
“off”: "$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

if op_type in types_op:
command = types_op[op_type]
print(“No such output supported, setting to rmc_gga”)
command = types_op[“rmc_gga”]

def set_antenna_update(ant_input):
ant_cmds = {
“on”: “$PGCMD,33,16C",
“off”: "$PGCMD,33,0

if ant_input in ant_cmds:
command = ant_cmds[ant_input]
elif ant_input is None:
command = ant_cmds[“off”]
print(“No Input. Antenna Updates Off by default”)


in theory resetting the serials every time you need to switch the peripheral (from GPS to LORA and vice versa) should work. In practice, using a slightly different setup, that is not enough. Resetting the serial makes tx and rx pins go down and up again and it is interpreted as a byte by the serial device. This thing is neglected by our gps module (and should be so for all nmea0183 devices) but instead, bother the lora module.
Coming to the point, to make your Photon work with that two serial devices you have to:

  •  define a function that “clears” the status of the serial pins, in the main file
def rst_ser():
    pinMode(D10, OUTPUT)
    pinMode(D11, OUTPUT)
    pinMode(D0, OUTPUT)
    pinMode(D1, OUTPUT)
<ul><li>add this function in rn2483 library</li></ul><pre class="CodeBlock">def update_ser(drv):<br>    global _ser<br>    _ser = streams.serial(drv, set_default = False, baud = 57600)<br>    _ser.write('\\r\
')<br>    _read()<br><br><br>
  • add this function in both rn2483 and your gps libraries
def close_ser():
sleep(200) ```
  • edit in order to close and reset the serials when needed, eg:
# after line 38



rn2483.close_ser() #<------- add this line
rst_ser() #<------- add this line


# after line 47


gps.close_ser() #<------- add this line
rst_ser() #<------- add this line
except Exception as e:
print(e) ``` and also inside parse() after reading the coordinates and after sending messages via lora.
  • edit the main in order to "update" the serial again when you need to use the peripheral
# in parse()

while True:
gps.init(SERIAL2) #<------- add this line
if gps._read():
sentence = gps._read().split(',')
# in parse()
rn2483.update_ser(SERIAL1) #<------- add this line
``` Let us know if this solution meets you needs.

Last note:
In your coude latitude and longitude are strings! "latitude = -latitude" is not allowed. Also, an NMEA reference manual can help to interpret messages (how many characters for degree part , how many for minutes part ecc..)


Thanks @anba for the explanation. It works with some modification in the suggestion thanks to your explanation
Here is the snippet (All about closing the serial port once the task is done)

def lora_send(lat, long):
if lat == ‘’ or long == ‘’:
lat = str(lat)
long = str(long)
r = rn2483.tx_uncnf(lat)
r = rn2483.tx_uncnf(long)

def parse():
while True:
gps.init(SERIAL2, 9600)
sentence = gps._read().split(’,’)
if sentence[0] == “$GPGLL”:
# latitude = float(sentence[1])/100.0
latitude = sentence[1]
latitude_d = sentence[2]
#longitude = float(sentence[3])/100.0
longitude = sentence[3]
longitude_d = sentence[4]
if latitude_d == “S”:
latitude = -(float(latitude))
if longitude_d == “W”:
longitude = -(float(longitude))
lora_send(latitude, longitude)

this successfully shows the raw longitude and latitude on the TTN console as seperate payloads. The final step is how to pack the coordinates using <b>bytearray </b>to send it as one payload. <br>We referred to the blog post about LoRa nodes and real time plotting. Where the coordinates are hard coded upto two decimal places. In our case, the raw values are <br><span>5306.6694 which means 53°06' and will be dynamically changing. How do we put such values in <b>bytearray </b>?<br><br></span><pre class="CodeBlock">lora_send(lat, long):<br>    lat = float(lat)<br>    long = float(long)<br>    data = bytearray(9)<br>    data[0:2] = bytearray([ int(lat) + 127, int((lat - int(lat))* 1000) ]) <br>    data[2:4] = bytearray([ int(long) + 127, int((long - int(long))* 1000) ])

actually throws an TypeError.

Thanks for all the great support.


Found a more convenient way by using json.dumps rather than bytearray for the coordinate. You can safely ignore the bytearray query.

thanks for all the support


Good thing that it throws an exception! :slight_smile:
In your example, int(lat) returns 5306 that is way greater than 255. As reported in the doc “A bytearray is a mutable sequence of integers in the range 0 <= x < 256” (link). Actually, such error should throw a ValueError exception instead of a TypeError, we will fix this…

The way you build the payload is up to you: you can use a single byte for each digit (ascii code?) or one byte for the degree integer part, one byte for the minutes integer part and another byte for the minutes decimal part or anything else, just remind that the elements of a bytearray must be integers in the range 0-255.

Also be careful about this:

int((lat - int(lat))* 1000
<p>it will be always equal to 0!<br></p><br><br>