Der Meilenstein dieser Episode: Meine ersten echten Telemetrie-Daten. Der Weg dahin bedeutete das Auslöten von Stiftleisten, endloses Debugging der Verdrahtung und das Entwerfen eines ersten Gehäuses — aber die Belohnung war es, meine aufgezeichnete GPS-Route nach einer Testfahrt auf der Karte zu sehen.

🎧 Hör dir Build, Create & Learn — A Maker’s Journey Episode 6 an, um die ganze Geschichte hinter diesem Blogpost zu erfahren.


Hinweis: Der Podcast ist ausschließlich auf Englisch verfügbar.

Raus aus dem Breadboard

Nach Episode 5 dachte ich, dass ich fast bereit für den ersten Drohnenflug sei. Mein Feather RP2040 loggte sowohl BME280- als auch GPS-Daten auf eine SD-Karte, und alles funktionierte wunderbar auf dem Breadboard. Der nächste Schritt schien simpel: alles in ein Gehäuse packen, einen Akku anschließen und ab in die Luft.

Oh Mann, wie weit ich von der Realität entfernt war.

Schon bei der Planung des Gehäuses wurde klar: Stiftleisten und Jumper-Kabel würden nicht ausreichen. Für eine flugtaugliche Version mussten die Boards ordentlich mit Kabeln verbunden werden — und das bedeutete, die Stiftleisten auszulöten.

Stiftleisten entfernen

Dieser Schritt war schmerzhafter als erwartet. Zuerst versuchte ich, ganze Reihen von Stiftleisten mit dem Lötkolben zu entfernen. Keine Chance. Selbst mit viel Flussmittel wollten die Boards nicht mitspielen.

Der Trick, der schließlich funktionierte, kam aus einem YouTube-Video: Die Stiftleisten in einzelne Pins schneiden, den Plastikträger entfernen und die Pins dann Stück für Stück auslöten. Langsam und mühsam, aber erfolgreich.

Leider war ich etwas zu grob. Einige Pads auf meinem Feather RP2040 und den Breakout-Boards wurden beschädigt oder geschwächt — Probleme, die mir später beim Debuggen wieder begegneten.

Feather RP2040 beim Auslöten der Stiftleisten

Kampf mit der „dritten Hand“

Ein weiteres Ärgernis war das Fixieren der Boards beim Auslöten. Meine billige „dritte Hand“ wackelte und brach unter dem geringsten Druck zusammen.

Für feine Lötarbeiten mag das reichen — aber wenn man Kraft aufbringen muss, ist sie unbrauchbar. Das hat mir gezeigt, wie sehr ich mir ein stabileres, professionelles Haltewerkzeug für die Elektronikarbeit wünsche. (Vielleicht ein zukünftiges eigenes Designprojekt.)

Gehäuse entwerfen

Da meine Breakout-Boards Befestigungslöcher haben, verwarf ich die erste Idee mit Abstandshaltern. Stattdessen entschied ich mich für eine einfachere rechteckige Box: 80×50×30 mm mit einem Deckel, der per Reibschluss sitzt, und Schrauben in zwei Ecken.

Kein Scharnier, kein komplizierter Verschluss — einfach ein fester Sitz. Ich modellierte das Gehäuse in CAD, druckte ein paar Testversionen und war überrascht, wie schnell das funktionierte.

3D-gedrucktes Drohnen-Telemetrie-Gehäuse

Verdrahtung debuggen

Nachdem die Stiftleisten entfernt und alles mit 22 AWG Solid-Core-Kabeln neu verdrahtet war, verband ich die Komponenten wieder wie auf dem Breadboard: Feather RP2040, BME280, microSD und GPS. Zumindest war das der Plan.

Die SD-Karte meldete sofort einen Fehler:

OSError: no SD card

Ich überprüfte alle Lötstellen, lötete sie mit Flussmittel nach und testete die Durchgängigkeit mit dem Multimeter. Alles schien in Ordnung. Dann fiel mir auf: Das Pad für GPIO18 (geplant als SD-Clock) am Feather war beschädigt. Die Verbindung war nicht zuverlässig.

Die Lösung war ein Umrouten des SPI-Busses auf alternative Pins (D10/D11/D12). Plötzlich initialisierte die SD-Karte und funktionierte wieder.

Als nächstes kam der BME280 mit folgender Meldung:

ValueError: No I2C device at address

Der I²C-Scan lieferte keine Geräte. Wieder prüfte ich Spannungen, lötete nach und verdächtigte sogar das Sensor-Board selbst. Nach Stunden der Frustration legte ich es beiseite und widmete mich dem GPS.

Das GPS zeigte zunächst nur Nullen im CSV-Log. Der Übeltäter? Wieder ein beschädigtes Pad an den UART-Pins des Feather. Nach einem Umverdrahten auf UART1 erhielt ich endlich saubere NMEA-Strings — und einen funktionierenden GPS-Fix.

Diese Debugging-Phase brachte mir eine wichtige, wenn auch schmerzhafte Lektion: Beim nächsten Mal verwende ich lieber Lochrasterplatinen mit Pin-Sockeln statt Breakout-Stiftleisten auszulöten. Viel sicherer.

Erste Testfahrt

Das Wetter war zu regnerisch für einen Drohnenflug, also baute ich das Gerät ins Auto und fuhr eine kurze 5-km-Strecke.

Mit angeschlossener externer Antenne und blauem LED-Blinken (Satelliten-Fix) loggte ich die ganze Fahrt — und schaute gleichzeitig gespannt auf den Tacho, um die Geschwindigkeit später mit den Daten zu vergleichen.

Zurück zu Hause zog ich die SD-Karte, öffnete die CSV-Datei — und da waren sie: Höhe, Geschwindigkeit und Koordinaten, sauber aufgezeichnet. Der erste echte Datensatz meines selbstgebauten Telemetrie-Loggers.

Daten visualisieren

Rohdaten sind nett, aber Karten und Plots machen sie lebendig. In JupyterLab lud ich die CSV-Datei, säuberte sie und nutzte Folium, um die GPS-Route auf OpenStreetMap-Kacheln darzustellen. Meine kurze Fahrt auf einer echten Karte zu sehen, war unglaublich befriedigend.

Zusätzlich erstellte ich zwei Plots:

  • Geschwindigkeit über Zeit — direkt mit dem Tacho verglichen.
  • Höhe über Zeit — etwas verrauscht, aber spannend.
import math
from pathlib import Path
import pandas as pd
import folium
from folium.plugins import MousePosition
import matplotlib.pyplot as plt
import gpxpy, gpxpy.gpx

# Make pandas show more
pd.set_option("display.max_columns", None)
csv_path = Path(r"data.csv") # Point this to your copied log file from the SD card
df = pd.read_csv(csv_path)

# Parse types
df["datetime_utc"] = pd.to_datetime(df["datetime_utc"], errors="coerce")
for col in ["lat","lon","spd_kmh","alt_gps_m","hdop","sats","fix_q"]:
    if col in df.columns:
        df[col] = pd.to_numeric(df[col], errors="coerce")

# Keep valid rows and sort
df = df.dropna(subset=["lat","lon"])
df = df[(df["lat"].between(-90, 90)) & (df["lon"].between(-180, 180))]
df = df.sort_values("datetime_utc")
df.head()
assert not df.empty, "No valid lat/lon rows."

lat0, lon0 = df.iloc[0][["lat","lon"]]
m = folium.Map(location=[lat0, lon0], zoom_start=14, control_scale=True, tiles="OpenStreetMap")

# Speed color function (0 km/h = blue, 120 km/h = red)
def color_for(v, vmin=0, vmax=120):
    if pd.isna(v): v = 0
    t = max(0, min(1, (v - vmin) / (vmax - vmin + 1e-9)))
    r = int(255 * t); g = 0; b = int(255 * (1 - t))
    return f"#{r:02x}{g:02x}{b:02x}"

coords = df[["lat","lon","spd_kmh"]].to_numpy()
for i in range(len(coords)-1):
    (la1,lo1,s1),(la2,lo2,s2) = coords[i], coords[i+1]
    c = color_for((0 if pd.isna(s1) else s1 + 0 if pd.isna(s2) else s2)/2)
    folium.PolyLine([(la1,lo1),(la2,lo2)], color=c, weight=4, opacity=0.9).add_to(m)

# Start/Finish markers
folium.Marker([df.iloc[0].lat, df.iloc[0].lon], tooltip="Start", icon=folium.Icon(color="green")).add_to(m)
folium.Marker([df.iloc[-1].lat, df.iloc[-1].lon], tooltip="Finish", icon=folium.Icon(color="red")).add_to(m)

MousePosition(position="topright", separator=" | ", prefix="Lat/Lon").add_to(m)

m  # Show in notebook

out_html = csv_path.with_suffix("").with_name(csv_path.stem + "_map.html")
m.save(str(out_html))
out_html
plt.figure(figsize=(10,3))
plt.plot(df["datetime_utc"], df["spd_kmh"].fillna(0))
plt.xlabel("Time (UTC)")
plt.ylabel("Speed (km/h)")
plt.title("Speed over time")
plt.tight_layout()

# Altitude over time plot
plt.figure(figsize=(10,3))
plt.plot(df["datetime_utc"], df["alt_gps_m"], color="purple")
plt.xlabel("Time (UTC)")
plt.ylabel("Altitude (m)")
plt.title("GPS Altitude over Time")
plt.tight_layout()

gpx = gpxpy.gpx.GPX()
trk = gpxpy.gpx.GPXTrack(); gpx.tracks.append(trk)
seg = gpxpy.gpx.GPXTrackSegment(); trk.segments.append(seg)

for _, row in df.iterrows():
    seg.points.append(gpxpy.gpx.GPXTrackPoint(
        float(row.lat), float(row.lon),
        elevation=None if pd.isna(row.get("alt_gps_m", float("nan"))) else float(row["alt_gps_m"]),
        time=None if pd.isna(row["datetime_utc"]) else pd.Timestamp(row["datetime_utc"]).to_pydatetime()
    ))

out_gpx = csv_path.with_suffix(".gpx")
with open(out_gpx, "w", encoding="utf-8") as f:
    f.write(gpx.to_xml())
out_gpx

Zum Schluss exportierte ich die Route als GPX-Datei und visualisierte sie in GPX Studio und Google Earth. Für eine 5-km-Autofahrt war die Genauigkeit überraschend gut — auch wenn Geschwindigkeit und Höhe leichte Schwankungen zeigten.

Visualized GPS drive test with Folium map

Nächste Schritte

Diese Phase fühlte sich wie ein echter Meilenstein an: vom Breadboard zum funktionierenden Prototypen mit echten Log-Daten.

Als Nächstes stehen auf meiner Liste:

  • BME280-Verkabelung reparieren und Umweltdaten wieder einbinden
  • Gehäuse für Drohnen-Montage optimieren
  • Akku-Überwachung hinzufügen
  • Mit LoRa für Langstrecken-Telemetrie experimentieren

Und natürlich das große Ziel: der erste echte Drohnenflugtest.

📬 Willst du wöchentliche Einblicke hinter die Kulissen meiner Werkbank (Projekte, die es nicht immer in den Podcast schaffen)? Dann abonniere mein Maker’s Logbook.



Lasst uns weiter bauen, erschaffen und lernen — gemeinsam.