Skip to content

Make compatible with pyserial, add some examples as well for computer usage #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 19, 2018
21 changes: 12 additions & 9 deletions adafruit_gps.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class GPS:
"""GPS parsing module. Can parse simple NMEA data sentences from serial GPS
modules to read latitude, longitude, and more.
"""
def __init__(self, uart):
def __init__(self, uart, debug=False):
self._uart = uart
# Initialize null starting values for GPS attributes.
self.timestamp_utc = None
Expand All @@ -90,6 +90,7 @@ def __init__(self, uart):
self.velocity_knots = None
self.speed_knots = None
self.track_angle_deg = None
self.debug = debug

def update(self):
"""Check for updated data from the GPS module and process it
Expand All @@ -101,11 +102,13 @@ def update(self):
sentence = self._parse_sentence()
if sentence is None:
return False
if self.debug:
print(sentence)
data_type, args = sentence
data_type = data_type.upper()
if data_type == 'GPGGA': # GGA, 3d location fix
data_type = bytes(data_type.upper(), "utf-8")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using "ascii" here and below as the encoding? In CircuitPython this arg is ignored (you can say "foobar"), but in regular Python it has to be an encoding name. If pyserial is reading junk with chars >128, then it will throw an error:

>>> bytes('ab\xbb', 'ascii')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\xbb' in position 2: ordinal not in range(128)
>>> bytes('ab\xbb', 'utf-8')
b'ab\xc2\xbb'
>>> 

if data_type == b'GPGGA': # GGA, 3d location fix
self._parse_gpgga(args)
elif data_type == 'GPRMC': # RMC, minimum location info
elif data_type == b'GPRMC': # RMC, minimum location info
self._parse_gprmc(args)
return True

Expand All @@ -115,15 +118,15 @@ def send_command(self, command, add_checksum=True):
Note you should NOT add the leading $ and trailing * to the command
as they will automatically be added!
"""
self._uart.write('$')
self._uart.write(b'$')
self._uart.write(command)
if add_checksum:
checksum = 0
for char in command:
checksum ^= ord(char)
self._uart.write('*')
self._uart.write('{:02x}'.format(checksum).upper())
self._uart.write('\r\n')
checksum ^= char
self._uart.write(b'*')
self._uart.write(bytes('{:02x}'.format(checksum).upper(), "utf-8"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"utf-8" -> "ascii", per previous comment

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yah im into it. lemme do that

self._uart.write(b'\r\n')

@property
def has_fix(self):
Expand Down
29 changes: 29 additions & 0 deletions examples/computer_datalogging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Simple GPS datalogging demonstration for use with a computer like Linux/desktop.
# This actually doesn't even use the GPS library and instead just reads raw
# NMEA sentences from the GPS unit and dumps them to a file.

import serial # pyserial is required

# Path to the file to log GPS data. By default this will be appended to
# which means new lines are added at the end and all old data is kept.
# Change this path to point at the filename desired
LOG_FILE = 'gps.txt' # Example for writing to local file gps.txt

# File more for opening the log file. Mode 'ab' means append or add new lines
# to the end of the file rather than erasing it and starting over. If you'd
# like to erase the file and start clean each time use the value 'wb' instead.
LOG_MODE = 'ab'

# Create a serial connection for the GPS connection using default speed and
# a slightly higher timeout (GPS modules typically update once a second).
# Update the serial port name to match the serial connection for the GPS!
uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=3000)

# Main loop just reads data from the GPS module and writes it back out to
# the output file while also printing to serial output.
with open(LOG_FILE, LOG_MODE) as outfile:
while True:
sentence = uart.readline()
print(str(sentence, 'ascii').strip())
outfile.write(sentence)
outfile.flush()
66 changes: 66 additions & 0 deletions examples/gps_echotest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Simple GPS module demonstration.
# Will print NMEA sentences received from the GPS, great for testing connection
# Uses the GPS only to send some commands, then reads directly from UART
import time
import board
import busio

import adafruit_gps


# Define RX and TX pins for the board's serial port connected to the GPS.
# These are the defaults you should use for the GPS FeatherWing.
# For other boards set RX = GPS module TX, and TX = GPS module RX pins.
RX = board.RX
TX = board.TX

# Create a serial connection for the GPS connection using default speed and
# a slightly higher timeout (GPS modules typically update once a second).
uart = busio.UART(TX, RX, baudrate=9600, timeout=3000)

# for a computer, use the pyserial library for uart access
#import serial
#uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=3000)

# Create a GPS module instance.
gps = adafruit_gps.GPS(uart)

# Initialize the GPS module by changing what data it sends and at what rate.
# These are NMEA extensions for PMTK_314_SET_NMEA_OUTPUT and
# PMTK_220_SET_NMEA_UPDATERATE but you can send anything from here to adjust
# the GPS module behavior:
# https://cdn-shop.adafruit.com/datasheets/PMTK_A11.pdf

# Turn on the basic GGA and RMC info (what you typically want)
gps.send_command(b'PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
# Turn on just minimum info (RMC only, location):
#gps.send_command(b'PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
# Turn off everything:
#gps.send_command(b'PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
# Tuen on everything (not all of it is parsed!)
#gps.send_command(b'PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0')

# Set update rate to once a second (1hz) which is what you typically want.
gps.send_command(b'PMTK220,1000')
# Or decrease to once every two seconds by doubling the millisecond value.
# Be sure to also increase your UART timeout above!
#gps.send_command(b'PMTK220,2000')
# You can also speed up the rate, but don't go too fast or else you can lose
# data during parsing. This would be twice a second (2hz, 500ms delay):
#gps.send_command(b'PMTK220,500')

# Main loop runs forever printing data as it comes in
timestamp = time.monotonic()
while True:
data = uart.read(32) # read up to 32 bytes
# print(data) # this is a bytearray type

if data is not None:
# convert bytearray to string
data_string = ''.join([chr(b) for b in data])
print(data_string, end="")

if time.monotonic() - timestamp > 5:
# every 5 seconds...
gps.send_command(b'PMTK605') # request firmware version
timestamp = time.monotonic()
24 changes: 14 additions & 10 deletions examples/gps_simpletest.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@
# a slightly higher timeout (GPS modules typically update once a second).
uart = busio.UART(TX, RX, baudrate=9600, timeout=3000)

# for a computer, use the pyserial library for uart access
#import serial
#uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=3000)

# Create a GPS module instance.
gps = adafruit_gps.GPS(uart)
gps = adafruit_gps.GPS(uart, debug=False)

# Initialize the GPS module by changing what data it sends and at what rate.
# These are NMEA extensions for PMTK_314_SET_NMEA_OUTPUT and
Expand All @@ -28,22 +32,22 @@
# https://cdn-shop.adafruit.com/datasheets/PMTK_A11.pdf

# Turn on the basic GGA and RMC info (what you typically want)
gps.send_command('PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
gps.send_command(b'PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
# Turn on just minimum info (RMC only, location):
#gps.send_command('PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
#gps.send_command(b'PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
# Turn off everything:
#gps.send_command('PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
#gps.send_command(b'PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
# Tuen on everything (not all of it is parsed!)
#gps.send_command('PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0')
#gps.send_command(b'PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0')

# Set update rate to once a second (1hz) which is what you typically want.
gps.send_command('PMTK220,1000')
gps.send_command(b'PMTK220,1000')
# Or decrease to once every two seconds by doubling the millisecond value.
# Be sure to also increase your UART timeout above!
#gps.send_command('PMTK220,2000')
#gps.send_command(b'PMTK220,2000')
# You can also speed up the rate, but don't go too fast or else you can lose
# data during parsing. This would be twice a second (2hz, 500ms delay):
#gps.send_command('PMTK220,500')
#gps.send_command(b'PMTK220,500')

# Main loop runs forever printing the location, etc. every second.
last_print = time.monotonic()
Expand Down Expand Up @@ -71,8 +75,8 @@
gps.timestamp_utc.tm_hour, # not get all data like year, day,
gps.timestamp_utc.tm_min, # month!
gps.timestamp_utc.tm_sec))
print('Latitude: {} degrees'.format(gps.latitude))
print('Longitude: {} degrees'.format(gps.longitude))
print('Latitude: {0:.6f} degrees'.format(gps.latitude))
print('Longitude: {0:.6f} degrees'.format(gps.longitude))
print('Fix quality: {}'.format(gps.fix_quality))
# Some attributes beyond latitude, longitude and timestamp are optional
# and might not be present. Check if they're None before trying to use!
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@

pyserial