[project] GPS data on Miniscreen

Thanks for the quick reply, my OS (Buster) is up to date. I need to check the firmware. But to be on the safe side will do a fresh up to date install as suggested. One thing I wanted to ask, can I run both the pi-top menu system and this code? For example if the menu system is showing on the pi-top, when this code is executed will it display the gps data on the oled display or do I need to kill the process for oled menu system firsr? Thanks

When you run the code it will basically disable the menu and the script will only be on the screen. Nothing else is programmed. When we get an update to the system menu, I will look into converting it into a system widget.

No need to disable the the system menu service. When you run the script the screen will go blank then display the info on the first successful parse. When you stop the script, the system menu will return

1 Like

I went back on your last reply and I think I misunderstood something. You mentioned a fresh up to date install and provided the link to Pi-top OS Sirius. In my case I am not running Pi-top OS, I am running Raspbian Buster.
When running pi-top device hub I get the following output:
20210214_132923

I did tinker a bit with the gps code you provided and the issue is with the nmea decode variables (longitude, altitude, etc). If I comment these out:

displays the required GPS data

# to the pi-top[4] miniscreen
canvas.rectangle(ms.bounding_box, fill=0)
canvas.text((0, 0),f"Lat:  {data['latitude']}",font=ImageFont.load_default(),fill=1)
canvas.text((0, 12),f"Lon: {data['longitude']}",font=ImageFont.load_default(),fill=1)
canvas.text((0, 24),f"Spd: {data['speed']}",font=ImageFont.load_default(),fill=1)
canvas.text((0, 36),f"Fix: {'Yes' if data['fix'] else 'No'}, {data['fix_type']}, {data['satelites']} Sats",font=ImageFont.load_default(),fill=1)
canvas.text((0, 48),f"UTC:{data['date_time']}",font=ImageFont.load_default(),fill=1)

And display a simple text, the text is shown on the display and the code runs perfectly.

Could for example f"Lon: {data[‘longitude’]}" be replaced with something else to see if this would work? As this is the part I think is breaking the code in my case?

Also does the code require a gps fix in order to output on the screen? Or can it display and populate the gps details when a fix is established?

I do apologies I am not very versed in terms of programming. I am still learning.

Thanks again for your support!

I’m at work atm will look at it when at home

I double checked the code on my repo and it’s all good

nmea_display(nmeaDecode(l))

This line sends a list of NMEA statements to def nmeaDecode to Extract dada from in to a dictionary and returns the dictionary and sends that to def nmea_display

f"Lon: {data[‘longitude’]}” is a formatted string that displays the text Lon: and the information in the dictionary with the key ‘longitude’

KeyError your getting means it cannot find the entry in the dictionary with that key name.

When I get home I will give you the code with print statements in it to see what the code is actually doing, if it’s actually getting the information or not

@Luis try running this and screenshot the print statements

I also forgot to add the KeyboardInterupt (ctrl+c) exception which clears the screen and exits the script

import io
import serial
from pitop.miniscreen import Miniscreen
from PIL import Image, ImageDraw, ImageFont

# Serial setup
serialConf = serial.Serial('/dev/ttyAMA0', 9600,timeout=0.3)
serialio = io.TextIOWrapper(io.BufferedRWPair(serialConf, serialConf))

# Enable only NMEA sentences required
serialConf.write(b'$PMTK314,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1*34\r\n')

# pi-top[4] Miniscreen setup 
ms = Miniscreen()
image = Image.new(ms.mode, ms.size,)
canvas = ImageDraw.Draw(image)
ms.set_max_fps(1)

# NMEA sentences required
nmea_list = ['$GPGGA', '$GPGSA', '$GPRMC', '$GPZDA']

def speedCalc(data, u):
    #converts speed to user required units
    if u == 1:
          return f'{round((float(data) * 1.150779448),1)} MPH' 
    elif u == 2:
        return f'{round((float(data) * 1.852),1)} KM/H'
    elif u == 3:
        return f'{round((float(data) * 1.943844),1)} m/s'
    else:
        return f'{round(float(data),1)} kn'

def coordDecode(data, b):
    #decodes lat and lon co-ordinates from GPS NMEA
    sec = round((60*float(f"0.{data.split('.')[1]}")),4)
    return f"{data.split('.')[0][0:-2]}{b} {data.split('.')[0][-2:]}m {sec}s "

def nmeaDecode(data):
    #create dictionary to put data into
    nmea_dict = {}
    
    # for each NMEA sentence, extract the data required
    # and add it to the dictionary
    for x in data:
        if x[0] == '$GPGGA':
            nmea_dict['satelites'] = int(x[7])
            nmea_dict['altitude'] = f'{x[9]} {x[10]}'
            print(f'GPGGA {nmea_dict}')

        elif x[0] == '$GPGSA':
            nmea_dict['fix'] = True if int(x[2]) > 1 else False
            nmea_dict['fix_type'] = f'{x[2]}D' if int(x[2]) > 1 else ''
            print(f'GPGSA {nmea_dict}')
        
        elif x[0] == '$GPRMC':
            #decodes lat and lon to degrees and mins
            nmea_dict['latitude'] = coordDecode(x[3], x[4])
            nmea_dict['longitude'] = coordDecode(x[5], x[6])
        
            # for speed, it can be calculated in MPH, KM/H, m/s Knots
            # 1 = MPH | 2 = KM/H | 3 = m/s | any other no. for knots
            nmea_dict['speed'] = speedCalc(x[7], 1)
            print(f'GPRMC {nmea_dict}')
        elif x[0] == '$GPZDA':
            # gets the date and time from GPS
            nmea_dict['date_time'] = f'{x[2]}/{x[3]}/{x[4][-2:]} {x[1][0:2]}:{x[1][2:4]}:{x[1][4:6]}'
            print(f'GPZDA {nmea_dict}')
    print(nmea_dict)
    # return the dictionary    
    return nmea_dict

def nmea_display(data):
    # displays the required GPS data
    # to the pi-top[4] miniscreen
    canvas.rectangle(ms.bounding_box, fill=0)
    canvas.text((0, 0),f"Lat:  {data['latitude']}",font=ImageFont.load_default(),fill=1)
    canvas.text((0, 12),f"Lon: {data['longitude']}",font=ImageFont.load_default(),fill=1)
    canvas.text((0, 24),f"Spd: {data['speed']}",font=ImageFont.load_default(),fill=1)
    canvas.text((0, 36),f"Fix: {'Yes' if data['fix'] else 'No'}, {data['fix_type']}, {data['satelites']} Sats",font=ImageFont.load_default(),fill=1)
    canvas.text((0, 48),f"UTC:{data['date_time']}",font=ImageFont.load_default(),fill=1)
    ms.display_image(image)

while 1:
    try:
        # create a list for NMEA sentences
        l=[]
        # Get NMEA sentence
        s = serialio.readline().strip().split(',')
        # look for the start of the sentence queue
        if s[0] =='$GPGGA':
            # get all the sentences that matches the list
            for x in nmea_list:
                print(f'GPS DATA: {s}')
                # add sentence to the list
                l.append(s)
                # get next sentence
                s = serialio.readline().strip().split(',')
            # decode the NMEA sentences and display the information
            # on the pi-top[4] miniscreen
            print(f'NMEA List: {l}')
            nmea_display(nmeaDecode(l))
            
    # This exeption is to prevent the script from crashing if there
    # is some garbled GPS data that cannot be decoded to UTF-8
    # this normally happens at the start of running the script
    # and is away of ignoring it
    except UnicodeDecodeError as e:
        continue
    except KeyboardInterrupt:
        ms.clear()
        exit()

@CAProjects Thanks a lot for looking into this, much appreciated :blush:

This is the output I got:
20210214_235412

The GPS has no fix at the moment as I am indoors and I don’t have a fix with it indoors due to its poor antenna.

In the code I changed to /dev/ttyACM0.
Thanks

ah, that’s why that is happening, its because there is no fix. forgot to check for fix before getting data, my bad, will look into that this week

Looks like your also not getting GPRMC data which I use to get lat and lon coords, can you change the line

serialConf.write(b’$PMTK314,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1*34\r\n’)

And change all 0’s to 1’s and re run?

I’ve had a chance to run this just now, here is the output when changing all to 1 (this is without gps fix):
20210215_215546

I’ve done some research and it seems the pi 4 doesn’t go along to well with this usb gps. It’s taking an awful long to get a gps fix and connecting anything on the usb 3.0 ports makes the reception and fix almost impossible. I’ve ordered a new gps module (with ext antenna) that is going to be connected via gpio instead of usb. Hopefully this will fix the bad reception and gps fix issues. Thanks

Later edit: tested indoors with a 1m usb extension cable and the gps dongle gets a gps fix/lock. It seems the interference issues are true.

With the gps fix I still get the same error.

GPS can take up to 30 mins to get a fix with no RTC battery, those with an RTC (real time clock) battery can get a fix faster after the 1st fix. Mine will get a fix within 5 secs thanks to using an Battery keeping the RTC alive.

Cloud cover also effects signal and fix. Using an active antenna is the best. The adafruit module with the active antenna Is really good from my experience. I did have 4x NEO6M but it they all died the same day I first powered them on

The gps fix is now less than 1 min. It was only on this Pi4 that I am having issues. The one I’ve ordered has a battery holder for RTC.

I have some good news, the code is partially working. I think something has to do with the format for latitude, longitude and date. If I comment these out the code runs. Just have to figure out how the decode sruff works so I can adapt to what my gps receiver is outputing.
20210215_234647

date and timw is got from GPZDA, not all GPS get that NMEA. Lat and Lon is got from GPRMC, all the NMEA messages are the same in structure, it depends on the device, RMC should be obtainable on every device

@CAProjects we have an RTC inside the pi-top [4] but I’m not sure we’ve made it accessible yet - would it be useful to have that for your project?

not for GPS since GPS uses GPS sats to sync its RTC, it may come in handy for somehting else later down the line tho.

@duwudi does pi-topOS support PPS? if not, no worriesjust wanted to play around with it

@CAProjects remind me what PPS stands for? It’s our acronym for pi-top Production System internally so I’m blanking :sweat_smile:

PPS means pulse per second. It’s used to sync time mostly but can be used for a lot of things.

Can think of it like a sort of time code

Hi @CAProjects

Hope all is well.
I wanted to ask for your help if possible please with the following. Just bought a multiconstelation GPS unit and would love to make use of the code you share with us. Checking gpsmon outputs the following nmea list:
GNRMC
GNVTG
GNGGA
GNGSA
GPGSV
GLGSV
GNGLL
GNGST
GNZDA
GNGBS

Unfortunately for me, none of these are in the nmea decode portion of the code and don’t have any ideea how to write the code to decode these sentences. Would you be able to help me with a few from the above list so that I can display the same info as you?

Have posted a picture in case anyone else would want a similar gps unit on their pitop:
IMG-20210225-WA0006

Many thanks,
Luis

‘$GPGGA’, ‘$GPGSA’, ‘$GPRMC’, ‘$GPZDA’ all get decoded but please note its coded to get the information I want. in the function def nmeaDecode(data): is the code to extract the data from ‘$GPGGA’, ‘$GPGSA’, ‘$GPRMC’, ‘$GPZDA’. you also need to add the sentences you want to decode to this line nmea_list = ['$GPGGA', '$GPGSA', '$GPRMC', '$GPZDA'] otherwise it will ignore them

you need to add an elif there for what ever sentence you want to add to get the information along with the code puting the information in to nmea_dict so that it returns to display on the screen. you will also need to edit the code in def nmea_display(data): to display tin information you want. please note that this does not ise the miniscreen buttons as this code was just to get the GPS info and display it.

to understand NMEA i highly recommend looking at this
http://aprs.gids.nl/nmea/
This explains what all the NMEA data actually is.

I am not going to write the code for you as there is enough information in the example code i have provided along with the useful links in the GitHub repo to code what your needing

1 Like

Many thanks for your reply and for the pointers on how to get this sorted. Much appreciated!