diff --git a/adafruit_featherwing/gps_featherwing.py b/adafruit_featherwing/gps_featherwing.py new file mode 100644 index 0000000..2948bef --- /dev/null +++ b/adafruit_featherwing/gps_featherwing.py @@ -0,0 +1,183 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Melissa LeBlanc-Williams for Adafruit Industries LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_featherwing.gps_featherwing` +==================================================== + +Helper for using the `Ultimate GPS FeatherWing `_. + +* Author(s): Melissa LeBlanc-Williams +""" + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_FeatherWing.git" + +import busio +import adafruit_gps +from adafruit_featherwing import shared + +class GPSFeatherWing: + """Class representing an `Ultimate GPS FeatherWing + `_. + + Automatically uses the feather's I2C bus.""" + def __init__(self, update_period=1000, baudrate=9600): + """ + :param int update_period: (Optional) The amount of time in milliseconds between + updates (default=1000) + :param int baudrate: (Optional) The Serial Connection speed to the GPS (default=9600) + """ + if not isinstance(update_period, int): + raise ValueError("Update Frequency should be an integer in milliseconds") + if update_period < 250: + raise ValueError("Update Frequency be at least 250 milliseconds") + timeout = update_period // 1000 + 2 + if timeout < 3: + timeout = 3 + + self._uart = busio.UART(shared.TX, shared.RX, baudrate=baudrate, timeout=timeout) + self._gps = adafruit_gps.GPS(self._uart, debug=False) + # Turn on the basic GGA and RMC info + self._gps.send_command(b'PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0') + self._gps.send_command(b'PMTK220,{}'.format(update_period)) + + def update(self): + """ + Make sure to call ``gps.update()`` every loop iteration and at least twice + as fast as data comes from the GPS unit (usually every second). + + :return: Whether it has parsed new data + :rtype: bool + """ + return self._gps.update() + + def read(self, size): + """ + Read the UART for any information that may be on it + + :param int size: The size in bytes of the data to retrieve + :return: Any data that is on the UART + :rtype: bytearray + """ + if isinstance(size, int) and size > 0: + return self._uart.read(size) + return None + + def send_command(self, command): + """ + Send a bytearray command to the GPS module + + :param bytearray command: The command to send + """ + if isinstance(command, bytearray): + self._gps.send_command(command) + + @property + def latitude(self): + """ + Return the Current Latitude from the GPS + """ + return self._gps.latitude + + @property + def longitude(self): + """ + Return the Current Longitude from the GPS + """ + return self._gps.longitude + + @property + def fix_quality(self): + """ + Return the Fix Quality from the GPS + """ + return self._gps.fix_quality + + @property + def has_fix(self): + """ + Return whether the GPS has a Fix on some satellites + """ + return self._gps.has_fix + + @property + def timestamp(self): + """ + Return the Fix Timestamp as a struct_time + """ + return self._gps.timestamp_utc + + @property + def satellites(self): + """ + Return the Number of Satellites we have a fix on + """ + return self._gps.satellites + + @property + def altitude(self): + """ + Return the Altitude in meters + """ + return self._gps.altitude_m + + @property + def speed_knots(self): + """ + Return the GPS calculated speed in knots + """ + return self._gps.speed_knots + + @property + def speed_mph(self): + """ + Return the GPS calculated speed in Miles per Hour + """ + return self._gps.speed_knots * 6076 / 5280 + + @property + def speed_kph(self): + """ + Return the GPS calculated speed in Kilometers per Hour + """ + return self._gps.speed_knots * 1.852 + + @property + def track_angle(self): + """ + Return the Tracking angle in degrees + """ + return self._gps.track_angle_deg + + @property + def horizontal_dilution(self): + """ + Return the Horizontal Dilution + """ + return self._gps.horizontal_dilution + + @property + def height_geoid(self): + """ + Return the Height GeoID in meters + """ + return self._gps.height_geoid diff --git a/adafruit_featherwing/shared.py b/adafruit_featherwing/shared.py index f0a3f68..379592f 100644 --- a/adafruit_featherwing/shared.py +++ b/adafruit_featherwing/shared.py @@ -33,3 +33,6 @@ import busio I2C_BUS = busio.I2C(board.SCL, board.SDA) + +RX = board.RX +TX = board.TX diff --git a/docs/api.rst b/docs/api.rst index 6e46a33..469668b 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -17,4 +17,7 @@ :members: .. automodule:: adafruit_featherwing.rtc_featherwing - :members: + :members: + +.. automodule:: adafruit_featherwing.gps_featherwing + :members: diff --git a/docs/examples.rst b/docs/examples.rst index 3237c13..ff1b1fe 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -31,6 +31,10 @@ Ensure your device works with this simple test. :caption: examples/featherwing_rtc_simpletest.py :linenos: +.. literalinclude:: ../examples/featherwing_gps_simpletest.py + :caption: examples/featherwing_gps_simpletest.py + :linenos: + Other Tests ------------ diff --git a/examples/featherwing_gps_simpletest.py b/examples/featherwing_gps_simpletest.py new file mode 100644 index 0000000..f08be69 --- /dev/null +++ b/examples/featherwing_gps_simpletest.py @@ -0,0 +1,57 @@ +""" +This example will connect to the GPS at the default 9600 baudrate and +update once per second. Initialization is automatically handled and there +are some additional features such as MPH and KPH calculations. +""" +import time +from adafruit_featherwing import gps_featherwing + +# Create a GPS featherwing instance. +gps = gps_featherwing.GPSFeatherWing() + +# Main loop runs forever printing the location, etc. every second. +last_print = time.monotonic() +while True: + # Make sure to call gps.update() every loop iteration and at least twice + # as fast as data comes from the GPS unit (usually every second). + # This returns a bool that's true if it parsed new data (you can ignore it + # though if you don't care and instead look at the has_fix property). + gps.update() + # Every second print out current location details if there's a fix. + current = time.monotonic() + if current - last_print >= 1.0: + last_print = current + if not gps.has_fix: + # Try again if we don't have a fix yet. + print('Waiting for fix...') + continue + # Print out details about the fix like location, date, etc. + print('=' * 40) # Print a separator line. + print('Fix timestamp: {}/{}/{} {:02}:{:02}:{:02}'.format( + gps.timestamp.tm_mon, # Grab parts of the time from the + gps.timestamp.tm_mday, # struct_time object that holds + gps.timestamp.tm_year, # the fix time. Note you might + gps.timestamp.tm_hour, # not get all data like year, day, + gps.timestamp.tm_min, # month! + gps.timestamp.tm_sec)) + 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! + if gps.satellites is not None: + print('# satellites: {}'.format(gps.satellites)) + if gps.altitude is not None: + print('Altitude: {} meters'.format(gps.altitude)) + if gps.speed_knots is not None: + print('Speed (Knots): {} knots'.format(gps.speed_knots)) + if gps.speed_mph is not None: + print('Speed (Miles Per Hour): {} MPH'.format(gps.speed_mph)) + if gps.speed_kph is not None: + print('Speed (KM Per Hour): {} KPH'.format(gps.speed_kph)) + if gps.track_angle is not None: + print('Track angle: {} degrees'.format(gps.track_angle)) + if gps.horizontal_dilution is not None: + print('Horizontal dilution: {}'.format(gps.horizontal_dilution)) + if gps.height_geoid is not None: + print('Height geo ID: {} meters'.format(gps.height_geoid)) diff --git a/requirements.txt b/requirements.txt index 2ec7b8b..a0f7a50 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ adafruit-circuitpython-ht16k33 adafruit-circuitpython-dotstar adafruit-circuitpython-neopixel adafruit-circuitpython-ds3231 +adafruit-circuitpython-gps diff --git a/setup.py b/setup.py index 7a8fd3c..643597f 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ 'adafruit-circuitpython-register', 'adafruit-circuitpython-ina219', 'adafruit-circuitpython-seesaw', 'adafruit-circuitpython-ht16k33', 'adafruit-circuitpython-dotstar', 'adafruit-circuitpython-neopixel', - 'adafruit-circuitpython-ds3231'], + 'adafruit-circuitpython-ds3231', 'adafruit-circuitpython-gps'], # Choose your license license='MIT',