frame

Any GPS module in the queue for the Library?

Hello,

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.

fab lab for the development of internet of things projects from research prototypes through industrial installations.
iotfablab.eu

Comments

  • 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

    fab lab for the development of internet of things projects from research prototypes through industrial installations.
    iotfablab.eu
  • 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 https://bigdanzblog.wordpress.com/2015/01/18/connecting-u-blox-neo-6m-gps-to-raspberry-pi/

  • anbaanba Member
    Hi,
    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!



    Andrea Baù
    Zerynth Support Team
  • iotfablab.eu Member
    edited October 6
    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
    streams.serial()
    s1 = streams.serial(SERIAL1, baud=9600)
    print("Setup..")
    while True:
    line = s1.readline()
    print(line)
    sleep(2000)
    MTK3339 class similar to link below
    https://github.com/PrzemoF/mtk3339/blob/master/mtk3339.py


    fab lab for the development of internet of things projects from research prototypes through industrial installations.
    iotfablab.eu
  • update


    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

    fab lab for the development of internet of things projects from research prototypes through industrial installations.
    iotfablab.eu
  • anbaanba Member
    Great! :) 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.
    Andrea Baù
    Zerynth Support Team
  • 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?

    Thanks

    fab lab for the development of internet of things projects from research prototypes through industrial installations.
    iotfablab.eu
  • anbaanba Member
    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/<current release>/libs/official/microchip/rn2483/rn2483.py)
    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.

    Or you can modify the _read() function to recreate the streams object every time.

    All of this (lock, reset and so on) it is not needed when the microcontroller have more UART peripherals..
     



    main.rar 769B
    Andrea Baù
    Zerynth Support Team
  • iotfablab.eu Member
    edited October 10
    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
    rn2483.tx_uncnf(str(longitude))
    sleep(100)
    rn2483.tx_uncnf(str(latitude))
    sleep(2000)

    streams.serial()
    print(rn2483.get_hweui(SERIAL1, D6))

    if not rn2483.init(SERIAL1, appeui, appkey, D6):
    print("Denied")
    raise Exception
    sleep(1000)

    # This message is sent and can be seen to at the TTN Console
    rn2483.tx_uncnf("TTN")

    sleep(2000)
    try:

    gps.init(SERIAL2)
    gps.cold_start()
    gps.set_nmea_updaterate(1000)
    gps.set_nmea_baudrate(9600)
    gps.set_nmea_output("rmc_only")
    gps.set_antenna_update("off")
    sleep(2000)
    except Exception as e:
    print(e)

    try:
    parse()

    except Exception as e:
    print(e)

    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.
    (Haven't yet made changes to the rn2483 module yet as you mentioned)


    fab lab for the development of internet of things projects from research prototypes through industrial installations.
    iotfablab.eu
  • also the mtk3339.py file for reference
    """
    MTK Command Sheet: https://cdn-shop.adafruit.com/datasheets/PMTK_A11.pdf
    NMEA CheckSum Calc: http://www.hhhh.org/wiml/proj/nmeaxor.html
    Based on https://github.com/PrzemoF/mtk3339/blob/master/mtk3339.py
    https://github.com/andreabau/org1411
    """
    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,3*32":
    # if t.get() > 40000:
    # raise mtk3339Exception

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

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

    def hot_fix():
    command = "$PMTK101*32"
    _send(command)

    def warm_start():
    command = "$PMTK102*31"
    _send(command)

    def cold_start():
    command = "$PMTK103*30"
    _send(command)

    def full_cold_start():
    command = "$PMTK104*37"
    _send(command)

    def set_nmea_updaterate(rate):
    rates = {
    10000: "$PMTK220,10000*2F",
    5000: "$PMTK220,5000*1B",
    1000: "$PMTK220,1000*1F",
    500: "$PMTK220,500*2B",
    200: "$PMTK220, 200*2C",
    100: "$PMTK220,100*2F"
    }

    if rate in rates:
    command = rates[rate]
    _send(command)
    else:
    print("wrong rate")

    def set_nmea_baudrate(baud):
    baudrates = {
    4800: "$PMTK251,4800*14",
    9600: "$PMTK251,9600*17",
    14400: "$PMTK251,14400*29",
    19200: "$PMTK251,19200*22",
    38400: "$PMTK251,38400*27",
    57600: "$PMTK251,57600*2C",
    115200: "$PMTK251,115200*1F"
    }

    if baud in baudrates:
    command = baudrates[baud]
    _send(command)
    else:
    print("wrong baudrate, setting default")
    command = baudrates[9600]
    _send(command)

    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,0*29",
    "rmc_gga": "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28",
    "all": "$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28",
    "off": "$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"
    }

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

    def set_antenna_update(ant_input):
    ant_cmds = {
    "on": "$PGCMD,33,1*6C",
    "off": "$PGCMD,33,0*6D"
    }

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




    fab lab for the development of internet of things projects from research prototypes through industrial installations.
    iotfablab.eu
  • anbaanba Member
    edited October 11
    Hi,
    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)
        digitalWrite(D0,HIGH)
        digitalWrite(D1,HIGH)
        digitalWrite(D10,HIGH)
        digitalWrite(D11,HIGH)
    • add this function in rn2483 library
    def update_ser(drv):
    global _ser
    _ser = streams.serial(drv, set_default = False, baud = 57600)
    _ser.write('\r\n')
    _read()


    • add this function in both rn2483 and your gps libraries
    def close_ser():
    sleep(200)
    _ser.close()
    sleep(200)
    • edit main.py in order to close and reset the serials when needed, eg:
    # after line 38

    rn2483.tx_uncnf("TTN")

    sleep(2000)

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

    try:

    gps.init(SERIAL2)
    gps.cold_start()
    # after line 47

    gps.set_antenna_update("off")
    sleep(2000)

    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
    rn2483.tx_uncnf(str(longitude))
    sleep(100)
    rn2483.tx_uncnf(str(latitude))
    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..)






    Andrea Baù
    Zerynth Support Team
  • 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 == '':
    pass
    else:
    lat = str(lat)
    long = str(long)
    rn2483.update_ser(SERIAL1)
    r = rn2483.tx_uncnf(lat)
    sleep(1000)
    r = rn2483.tx_uncnf(long)
    rn2483.close_ser()
    ser_rst()

    def parse():
    while True:
    gps.init(SERIAL2, 9600)
    sleep(10000)
    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))
    gps.close_ser()
    ser_rst()
    lora_send(latitude, longitude)

    sleep(2000)
    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 bytearray to send it as one payload.
    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
    5306.6694 which means 53°06' and will be dynamically changing. How do we put such values in bytearray ?

    lora_send(lat, long):
    lat = float(lat)
    long = float(long)
    data = bytearray(9)
    data[0:2] = bytearray([ int(lat) + 127, int((lat - int(lat))* 1000) ])
    data[2:4] = bytearray([ int(long) + 127, int((long - int(long))* 1000) ])
    actually throws an TypeError.

    Thanks for all the great support.


    fab lab for the development of internet of things projects from research prototypes through industrial installations.
    iotfablab.eu
  • 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

    fab lab for the development of internet of things projects from research prototypes through industrial installations.
    iotfablab.eu
  • anbaanba Member
    Good thing that it throws an exception! :)
    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

    it will be always equal to 0!



    Andrea Baù
    Zerynth Support Team
Sign In or Register to comment.

ZERYNTH Community

@ 2016 Zerynth.com, all rights reserved.