RP2040-GEEK mit GPS-Modul


Adafruit Mini GPS PA1010D



Anleitung getestet mit CircuitPython 10.0.3





Bildbox 1 (klick hier)

Es folgt hier noch eine Anleitung mit einem GPS-Modul am Pico-Geek, das oben gezeigte Stemma Mini GPS PA1010D von Adafruit. Das Modul hat zwei Stemma-QT Buchsen. Die auf dem Bild (Bildbox 1)rechts zu sehende Buchse ist für den Anschluss am i2c-Bus. Die entsprechende Beschriftung befindet sich auf der Rückseite.
Wie im Beispiel mit dem Magnetsensor kann hier auch einfach das GPS-Modul am i2c-Port des Pico-Geek angeschlossen werden. Auf dem Bild 2 in Bildbox 2 von der Rückseite kann man sehen, dass sich hier eine Halterung befindet, für eine optionale CR1220-Knopfzelle, um die RTC am Laufen zu halten und Warmstarts zu ermöglichen. Darauf werde ich später noch eingehen.



Bildbox 2 (klick hier)



Bei der Auswahl der Firmware entscheide ich mich auch hier für die CircuitPython- Version von Waveshare .

Hardware

- Pico-Geek (z.B. von Botland )
- Qwiic-Kabel mit 4-poligem Stecker(z.B. von Botland )
- Mini GPS PA1010D von Adafruit (z.B. von DigiKey )

Software

- aktuelle Firmware Adafruit CircuitPython; Waveshare RP2040-GEEK with rp2040
- library adafruit_gps.mpy aus dem CircuitPython-Bundle zur Firmwareversion
- Ordner adafruit_display_text aus dem CircuitPython-Bundle zur Firmwareversion

Los gehts

Verbinden Sie also das GPS Modul über das Qwiic-Kabel mit der Stemma-QT Buchse am Pico-Geek.



Wenn das erledigt ist, kann das GPS-Modul in Betrieb genommen werden. Als erstes führen wir einen Echotest durch. Der liefert einen rohen GPS-'NMEA-Satz', der vom Modul ausgegeben wird. Es gibt verschiedene Arten von NMEA-Sätzen. Die am häufigsten verwendeten sind die $GPRMC-Sätze (Global Positioning RecommendedMinimum Coordinates oder so ähnlich) und die $GPGGA-Sätze. Diese beiden liefern Uhrzeit, Datum, Breitengrad, Längengrad, Höhe, geschätzte Landgeschwindigkeit und Fixtyp. Der Fixtyp gibt an, ob das GPS die Satellitendaten erfasst und genügend Daten empfangen hat, um den Standort (2D-Fix) oder Standort + Höhe (3D-Fix) zu bestimmen.

Setzen Sie den Code aus dem unteren Kasten in die Thonny-IDE ein und starten ihn.

  
  
1   # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
2   # SPDX-License-Identifier: MIT
3
4   # Simple GPS module demonstration.
5   # Will print NMEA sentences received from the GPS, great for testing connection
6   # Uses the GPS to send some commands, then reads directly from the GPS
7   import time
8   import board
9   import busio
10  import adafruit_gps
11
12  i2c = busio.I2C(board.GP29, board.GP28)  # SCL, SDA
13  # Create a GPS module instance.
14  gps = adafruit_gps.GPS_GtopI2C(i2c, debug=False) # Use I2C interface
15
16  # Turn on the basic GGA and RMC info (what you typically want)
17  gps.send_command(b"PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0")
18
19  # Set update rate to once a second (1hz) which is what you typically want.
20  gps.send_command(b"PMTK220,1000")
21
22
23  # Main loop runs forever printing data as it comes in
24  timestamp = time.monotonic()
25
26  while True:
27      data = gps.read(32)  # read up to 32 bytes
28      print(data)  # this is a bytearray type
29
30      if data is not None:
31          # convert bytearray to string
32          data_string = ''.join([chr(b) for b in data])
33          print(data_string, end="")
34
35      if time.monotonic() - timestamp > 5:
36          # every 5 seconds...
37          gps.send_command(b'PMTK605')  # request firmware version
38          timestamp = time.monotonic()
  

Wenn Sie das Programm starten, werden die Rohdaten in der Kommandozeile der Thonny-IDE ausgegeben:



Bildbox 3 (klick hier)


Im nächsten Schritt führen wir ein s.g. 'GPS Parsing' durch. Das bedeutet auf deutsch so viel wie 'GPS-Datenanalyse' oder 'Auslesen von GPS-Daten'. Es beschreibt den Prozess, bei dem rohe NMEA-Datensätze (Zeichenketten) von einem GPS-Empfänger zerlegt, strukturiert und in nutzbare Informationen wie Längen- und Breitengrade, Zeit oder Geschwindigkeit umgewandelt werden.
Im unteren Kasten ist der entsprechende Quellcode dafür:

  
  
1   import time
2   import board
3   import busio
4   import adafruit_gps
5
6   i2c = busio.I2C(board.GP29, board.GP28)  # SCL, SDA
7   # Create a GPS module instance.
8   gps = adafruit_gps.GPS_GtopI2C(i2c, debug=False) # Use I2C interface
9
10  # Turn on the basic GGA and RMC info (what you typically want)
11  gps.send_command(b"PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0")
12  # Set update rate to once a second (1hz) which is what you typically want.
13  gps.send_command(b"PMTK220,1000")
14
15  # Main loop runs forever printing data as it comes in
16  timestamp = time.monotonic()
17  last_print = time.monotonic()
18
19  while True:
20      # Make sure to call gps.update() every loop iteration and at least twice
21      # as fast as data comes from the GPS unit (usually every second).
22      # This returns a bool that's true if it parsed new data (you can ignore it
23      # though if you don't care and instead look at the has_fix property).
24      gps.update()
25      # Every second print out current location details if there's a fix.
26      current = time.monotonic()
27      if current - last_print >= 1:
28          last_print = current
29          if not gps.has_fix:
30              # Try again if we don't have a fix yet.
31              print("Waiting for fix...")
32              continue
33          # We have a fix! (gps.has_fix is true)
34          # Print out details about the fix like location, date, etc.
35          print("=" * 40)  # Print a separator line.
36          print(
37              "Fix timestamp: {}/{}/{} {:02}:{:02}:{:02}".format(  # noqa: UP032
38                  gps.timestamp_utc.tm_mon,  # Grab parts of the time from the
39                  gps.timestamp_utc.tm_mday,  # struct_time object that holds
40                  gps.timestamp_utc.tm_year,  # the fix time.  Note you might
41                  gps.timestamp_utc.tm_hour,  # not get all data like year, day,
42                  gps.timestamp_utc.tm_min,  # month!
43                  gps.timestamp_utc.tm_sec,
44              )
45          )
46          print(f"Latitude: {gps.latitude:.6f} degrees")
47          print(f"Longitude: {gps.longitude:.6f} degrees")
48          print(f"Precise Latitude: {gps.latitude_degrees} degs, {gps.latitude_minutes:2.4f} mins")
49          print(f"Precise Longitude: {gps.longitude_degrees} degs, {gps.longitude_minutes:2.4f} mins")
50          print(f"Fix quality: {gps.fix_quality}")
51          # Some attributes beyond latitude, longitude and timestamp are optional
52          # and might not be present.  Check if they're None before trying to use!
53          if gps.satellites is not None:
54              print(f"# satellites: {gps.satellites}")
55          if gps.altitude_m is not None:
56              print(f"Altitude: {gps.altitude_m} meters")
57          if gps.speed_knots is not None:
58              print(f"Speed: {gps.speed_knots} knots")
59          if gps.speed_kmh is not None:
60              print(f"Speed: {gps.speed_kmh} km/h")
61          if gps.track_angle_deg is not None:
62              print(f"Track angle: {gps.track_angle_deg} degrees")
63          if gps.horizontal_dilution is not None:
64              print(f"Horizontal dilution: {gps.horizontal_dilution}")
65          if gps.height_geoid is not None:
66              print(f"Height geoid: {gps.height_geoid} meters")
  

Auch hier erfolgt die Ausgabe zunächst in der Kommandozeile der Thonny-IDE. Das Bild in der nächsten Bildbox veranschaulicht dies:


Bildbox 4 (klick hier)


Nun soll eine kleine Anwendung mit der Ausgabe von Datum, Uhrzeit, Latitude, Longitude, Geschwindigkeit und Höhe auf dem Geek-Display entstehen. Das wird in den Zeilen 1 bis 40 vorbereitet. Es wird wie bereits bekannt, das Display initialisiert, der i2c-Bus definiert und der Displayhintergrund sowie Textausgabe vorbereitet. Die beiden Funktionen ab Zeile 42 und 49 dienen der formatierten Zuweisung von Datum und Uhrzeit aus den Satdaten. Bevor ich den Quellcode angebe, möchte ich noch auf die Bedeutung von 'gps.has_fix' hinweisen.

Wenn die Spannungsversorgung am GPS-Modul hergestellt wird, erfolgt ein s.g. Kaltstart, der ohne aktuelle Satellitendaten (Ephemeriden), auftritt. Dabei benötigt das Gerät 2 bis über 15 Minuten für den sogenannten TTFF (Time to First Fix), da es Satellitensignale neu suchen muss. Für eine erfolgreiche Initialisierung ist eine freie Sicht zum Himmel zwingend erforderlich. Bei vorhandener Batterie (siehe oben) wird ein Warmstart ausgeführt. Dieser ist effektiv, denn er verkürzt die Wartezeit bei der Positionsbestimmung, da das Gerät nicht komplett neu initialisiert werden muss. Zu erkennen ist der 'Fix' am Blinken einer roten LED neben der grünen Betriebsanzeige-LED. Auf dem Geek-Display erfolgt bis zum 'Fix' die Anzeige entsprechend Zeile 70 im Code.

Im nächsten Kasten sehen Sie jetzt den Code, den Sie in die Thonny-IDE kopieren können und unter 'code.py' abspeichern. Die Zeilen 72 bis 79 bringen die genannten Anzeigen auf das Display. Natürlich können Sie die beliebig verändern. Experimentieren Sie ruhig damit.

  
  
1   # SPDX-FileCopyrightText : 2025 Detlef Gebhardt, written for 10.0.3 on 2025-10-17; Waveshare RP2040-GEEK with rp2040
2   # SPDX-FileCopyrightText : Copyright (c) 2025 Detlef Gebhardt
3   # SPDX-Filename          : RP2040 GEEK - GPS
4   # SPDX-License-Identifier: https://dgebhardt.de
5   import time
6   import board
7   import busio
8   import displayio
9   import terminalio
10  from adafruit_display_text import label
11  import adafruit_gps
12
13  # Display initialisieren
14  display = board.DISPLAY
15  display.rotation = 90
16  display.brightness = 1
17  main_group = displayio.Group()
18  display.root_group = main_group
19
20  i2c = busio.I2C(board.GP29, board.GP28)  # SCL, SDA
21  # Create a GPS module instance.
22  gps = adafruit_gps.GPS_GtopI2C(i2c, debug=False) # Use I2C interface
23  # Turn on the basic GGA and RMC info (what you typically want)
24  gps.send_command(b"PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0")
25  # Set update rate to once a second (1hz) which is what you typically want.
26  gps.send_command(b"PMTK220,1000")
27
28  # Make a background color fill
29  color_bitmap = displayio.Bitmap(display.width, display.height, 3)
30  color_palette = displayio.Palette(3)
31  color_palette[0] = 0x660088
32  color_palette[1] = 0x000000
33  color_palette[2] = 0xffffff
34  bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
35  main_group.append(bg_sprite)
36
37  label = label.Label(font = terminalio.FONT, text="Geek-GPS", color=0xffffff, scale=2, line_spacing=0.9)
38  text = displayio.Group(x=10, y= 10)
39  text.append(label)
40  main_group.append(text)
41
42  def _format_date(datum):
43      return "{:02}.{:02}.{}".format(
44          gps.timestamp_utc.tm_mday,
45          gps.timestamp_utc.tm_mon,
46          gps.timestamp_utc.tm_year,
47      )
48
49  def _format_time(zeit):
50      return "{:02}:{:02}:{:02}".format(
51          gps.timestamp_utc.tm_hour,
52          gps.timestamp_utc.tm_min,
53          gps.timestamp_utc.tm_sec,
54      )
55
56  # Main loop runs forever printing data as it comes in
57  timestamp = time.monotonic()
58  last_print = time.monotonic()
59
60  while True:
61      # Make sure to call gps.update() every loop iteration and at least twice
62      # as fast as data comes from the GPS unit (usually every second).
63      gps.update()
64      # Every second print out current location details if there's a fix.
65      current = time.monotonic()
66      if current - last_print >= 1:
67          last_print = current
68          if not gps.has_fix:
69              # Try again if we don't have a fix yet.
70              label.text = "\n\n\nWaiting for fix..."
71              continue
72          datum = _format_date(gps.timestamp_utc)
73          zeit = _format_time(gps.timestamp_utc)
74          if gps.satellites is not None:
75               label.text = (datum + " " + zeit +
76                            "\nLatitude/Longitude\n  {0:.6f} Grad".format(gps.latitude) +
77                            "\n  {0:.6f} Grad".format(gps.longitude) +
78                            "\nspeed : {} km/h".format(gps.speed_knots*1,852) +
79                            "\nheight: {} m".format(gps.altitude_m) )
  

Das Display sieht nach dem 'gps.has_fix' dann z.B. so aus:



Bildbox 5 (klick hier)



Viel Spass und Erfolg beim Ausprobieren.