Fortgeschrittener Wander-Tracker mit Arduino: GPS, Sensoren & Batteriepack
Komponenten und Materialien
| Bauteil | Menge | Hinweise |
|---|---|---|
| Arduino 101 | 1 | |
| AA-Batterien | 4 | Im 4er-Halter |
| 4×AA-Batteriehalter | 1 | |
| Accessory Shield (WaveShare) | 1 | Mit diversen Onboard-Sensoren |
| BMP180 Drucksensor (Adafruit) | 1 | |
| Ultimate GPS Breakout (Adafruit) | 1 | MTK3339 |
| 3-Achsen-Magnetometer (Adafruit) | 1 | HMC5883 |
| AM2320 Luftfeuchtesensor (Adafruit) | 1 | |
| CR2012 Knopfzelle | 2 | Für RTC |
| Widerstand 10 kΩ | 1 | |
| Widerstand 3,9 kΩ | 1 | Für Batteriespannungs-Teiler |
Benötigte Werkzeuge
- Heißklebepistole
- Lötkolben
Software und Online-Dienste
- Arduino IDE (Build 1.8.5)
Projektbeschreibung
Was ist der Hiking Tracker?
Ein Gerät zur Erfassung der Umgebungsveränderungen während des Wanderns: Temperatur, Druck, Luftfeuchte, Höhe, Position, Richtung. Es gibt Einblicke in die Bedingungen – nützlich, um persönliche Grenzen (Atmung, trockene Haut) in großer Höhe zu erkennen, aber auch einfach spannend, die Daten live zu sehen.
Schritt 1: Hardware – Welcher Sensor wofür?
Accessory Shield (Onboard I2C-Sensoren):
- LM75B – Umgebungstemperatur
- ADXL345 – 3-Achsen-Beschleunigung (Neigung, Gravity)
- DS3231 – Hochpräzise RTC (Zeit)
- HMC5883 – 3-Achsen-Magnetometer (Kompass)
- AM2320 – Luftfeuchte (% RH)
- BMP180 – Luftdruck & Höhe
- MTK3339 GPS – Position & Wegstrecke
Zusätzlich auf Arduino-Seite:
- Spannungsteiler 3,9k + 22k → 4×AA-Batteriespannung messen (ADC A1)
- SSD1306 OLED – Datenanzeige
- Summer – Alarm
- RGB-LED (nicht genutzt)
- XBee-Schnittstelle (D2, D3, D9, D10 ausführbar, D2 = Reset)
- Joystick & Potentiometer (nicht genutzt, Joystick entfernt für A1-Freiheit)
Schritt 2: Hardware-Modifikation & Aufbau
Arduino auf 4×AA-Halter montiert (Schraubabstandshalter + Heißkleber).
Batterien (4×AA + CR1220 für RTC) eingesetzt.
Zwischen Halter und Arduino: Platz für BMP180, AM2320, Magnetometer, GPS.
I2C-Sensoren auf Lochrasterplatine gelötet, in den Zwischenraum platziert.
Anschluss von der Unterseite.
Spannungsteiler für Akku-Messung an ADC A1.
Joystick entfernt (belegte A1).
Lötbrücken auf Accessory Shield für D2 (Soft-Reset), D3, D9, D10 an XBee-Schnittstelle.
Stapel-Aufbau:
- Oben: Accessory Shield auf Arduino
- Mitte: Externe Sensoren & GPS
- Unten: 4×AA-Batteriehalter
Schritt 3: Programmierung
Arduino IDE 1.8.5. Bibliotheken über Library Manager oder ZIP-Import (Accessory Shield Libs).
Beispiel-Codes der Sensoren analysiert, APIs extrahiert, alle Header in einem Sketch vereint.
Header-Dateien
math.h, inttypes.h, Wire.h, lm75.h, ADXL345.h, ChainableLED.h,
U8glib.h, ds3231.h, Adafruit_Sensor.h, Adafruit_AM2320.h,
Adafruit_BMP085_U.h, Adafruit_HMC5883_U.h, Adafruit_GPS.h
Nach mehreren Iterationen (Kompilieren, Debuggen, lockere Verbindungen – BMP180 funktionierte sogar ohne VCC durch Leckströme an I2C-Pins) war der Code fertig.
Einschränkungen & Caveats
- Höhenberechnung basiert auf Druckabfall – nur bei normalem Wetter genau.
- Kompass nicht tilt-kompensiert – Gerät muss waagerecht gehalten werden. Innerer Kreis auf OLED zeigt Neigung: Punkt = waagerecht = genau.
- Deklinationswinkel ortsabhängig, variiert durch Erdmagnetfeld/Sonnenstürme. Ohne Korrektur Abweichung um einige Grad. Ermittlung: magnetic-declination.com
- Magnetische Störungen durch Erze etc. möglich.
- Mindestspannung ~4,5 V – dann AA-Batterien tauschen.
- Zeit/Datum per Code gesetzt – Änderung erfordert CR1220 entfernen + neuen Upload mit neuem Zeitstempel.
- GPS während Entwicklung nicht verfügbar – Demokoordinaten (23.57, 90.36) im Code. Für Nachbau: GPS-Library & Code einbinden.
- Sensor-Orientierung auf PCB beeinflusst X/Y-Achsen – Code anpassen.
- Beschleunigungsmesser empfindlich gegen Vibrationen – nur stehend nutzen.
Verbesserungspotenzial
Firmware: Periodisches Logging auf EEPROM, Alarme bei Höhe/Position-Meilensteinen, Trink-/Pausen-Erinnerung, Batterie-/Temperatur-/Feuchte-Warnung, Software-Tilt-Kompensation für Kompass, Auto-Deklination per GPS & IoT.
Hardware: Bedientasten für Zeit/Deklination, 3D-gedrucktes Gehäuse, LiPo-Akku, kompakte Einzelplatine.
Fazit
Wandern macht mehr Spaß, wenn man die Umweltveränderungen live sieht: Höhe, Feuchte, Druck, Temperatur. Man lernt, ab welcher Höhe man Symptome bekommt, welche Feuchte die Haut austrocknet. Trotz Verbesserungspotenzial ein cooles Gadget für die Wildnis!
Externe Ressourcen
WaveShare Accessory Shield Wiki
Empfohlene Hardware für bessere Version
Rapid Prototyping Kit
Code
Hiking_Tracker.ino (Hauptprogramm)
/*/////////// NOTE ///////////
D2 allows soft reset from code
D3,D9,D10 available via XBee interface pins 12,13,16
D4, D13 not accessible
///////////////////////////*/
//// HEADER FILES /////
#include <math.h>
#include <inttypes.h>
#include <Wire.h>
#include <lm75.h>
#include <ADXL345.h>
#include <ChainableLED.h>
#include "U8glib.h"
#include "ds3231.h"
#include "Adafruit_Sensor.h"
#include "Adafruit_AM2320.h"
#include <Adafruit_BMP085_U.h>
#include <Adafruit_HMC5883_U.h>
#include <Adafruit_GPS.h>
///////////////////////// Variablen /////////////////////
uint8_t time[8]; struct ts t; int s,m,h,dy,mo,yr;
float X=0,Y=0,Z=0,Gravity=0;
float Xm=0,Ym=0,Zm=0;
const int rgb_pwr=12, clk_pin=6, data_pin=5;
float hue=0; boolean up=true;
int pot=0;
float Vbatt=0, temp=0, humid=0, prsr=0, alt=0;
float lat=0, lon=0, heading=0;
int mstime=0, sensor_selector=1;
//////////// I2C Geräte-Objekte ////////////
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0); // OLED
TempI2C_LM75 termo(0x48, TempI2C_LM75::nine_bits); // Temp
ADXL345 accelerometer; // Accel
ChainableLED leds(clk_pin, data_pin, rgb_pwr, 1); // RGB LED
Adafruit_AM2320 am2320; // Humidity
Adafruit_BMP085_Unified bmp180(10085); // Pressure
Adafruit_HMC5883_Unified mag(12345); // Compass
// GPS (auskommentiert, da Serial für Debug genutzt):
// HardwareSerial mySerial = Serial;
// Adafruit_GPS GPS(&mySerial);
void setup() {
// OLED Signale auf Accessory Shield (Lötbrücken nötig)
pinMode(2,1); pinMode(3,1); pinMode(9,1); pinMode(10,1);
digitalWrite(2,HIGH); digitalWrite(3,HIGH); digitalWrite(9,LOW); digitalWrite(10,HIGH);
pinMode(7,1); pinMode(8,1); digitalWrite(7,HIGH); digitalWrite(8,LOW);
// 10k POT auf A0, Vin auf A1 (3.91k/21.76k)
// Serial.begin(9600); // Debug optional
if (!accelerometer.begin()) delay(50);
DS3231_init(DS3231_INTCN); // RTC Interrupt
pinMode(11,1); digitalWrite(11,LOW); // Buzzer
}
void loop() {
// OLED Bild-Aufbau (Page-Modus)
u8g.firstPage();
do { draw(); } while(u8g.nextPage());
// Sensor-Round-Robin (alle 3 Schleifen ein anderer Sensor)
sensor_selector++; if (sensor_selector >= 3) sensor_selector=0;
// Temperatur (LM75)
temp = termo.getTemp(); delay(5);
// Druck & Höhe (BMP180) bei sensor_selector 0
if (sensor_selector == 0) {
if(!bmp180.begin()) delay(11);
sensors_event_t event;
bmp180.getEvent(&event);
if (event.pressure) { prsr = event.pressure/10; delay(50); } // hPa -> kPa
bmp180.getEvent(&event);
alt = bmp180.pressureToAltitude(1013.25, event.pressure); delay(50);
}
// Luftfeuchte (AM2320) bei sensor_selector 1
if (sensor_selector == 1) {
am2320.begin(); delay(50);
humid = am2320.readHumidity(); delay(50);
}
// Beschleunigung
Vector norm = accelerometer.readNormalize();
X = norm.XAxis; Y = norm.YAxis; Z = norm.ZAxis;
Gravity = sqrt(X*X + Y*Y + Z*Z) - 0.3; // Offset
// Zeit (DS3231)
DS3231_get(&t);
s=t.sec; m=t.min; h=t.hour; dy=t.mday; mo=t.mon; yr=t.year;
// Potentiometer
pot = analogRead(A0)*5/(10*3); delay(2);
// Batteriespannung (interner 1.1V Ref ~1.08V auf diesem Chip)
Vbatt = (1.08*analogRead(A1)/1023)/3.91*((3.91+21.76)); delay(2);
// Kompass (HMC5883) bei sensor_selector 2
if (sensor_selector == 2) {
if(!mag.begin()) while(1);
delay(70);
sensor_t sensor2; mag.getSensor(&sensor2); delay(70);
sensors_event_t event2; mag.getEvent(&event2); delay(70);
float declination = 0.0; // Ortsabhängig!
heading = atan2(event2.magnetic.y, event2.magnetic.x);
heading += declination;
if(heading < 0) heading += 2*PI;
if(heading > 2*PI) heading -= 2*PI;
heading = heading * 180/M_PI; // Grad
}
// Summer (Pot > 80%)
if (pot > 80) digitalWrite(11,1); else digitalWrite(11,0);
// RGB LED (Pot > 40%)
if(pot > 40) {
leds.pwr_set(PWR_ENABLE);
for(byte i=0;i<1;i++) leds.setColorHSB(i, hue, 1.0, 0.5);
if(up) hue+=0.025; else hue-=0.025;
if(hue>=1.0 && up) up=false;
else if(hue<=0.0 && !up) up=true;
}
// Soft Reset: digitalWrite(2,LOW);
}
// OLED Zeichnen
void draw() {
u8g.setFont(u8g_font_7x13);
u8g.drawHLine(37,0,62);
u8g.setPrintPos(41,12); if(h<10) u8g.print(0); u8g.print(h); u8g.drawStr(54,12,":");
u8g.setPrintPos(62,12); if(m<10) u8g.print(0); u8g.print(m); u8g.drawStr(75,12,":");
u8g.setPrintPos(83,12); if(s<10) u8g.print(0); u8g.print(s);
u8g.drawHLine(37,14,62);
u8g.setFont(u8g_font_5x8);
u8g.drawVLine(37,0,14);
u8g.setPrintPos(0,7); if(dy<10) u8g.print(0); u8g.print(dy); u8g.drawStr(11,7,"/");
u8g.setPrintPos(17,7); if(mo<10) u8g.print(0); u8g.print(mo); u8g.drawStr(0,16,"/");
u8g.setPrintPos(6,16); u8g.print(yr);
u8g.drawVLine(99,0,14);
u8g.drawStr(104,7,"BATT"); u8g.setPrintPos(102,15); u8g.print(Vbatt); u8g.drawStr(123,15,"V");
u8g.print(round(temp)); u8g.drawStr(55,25,"'C");
u8g.setFont(u8g_font_5x8);
u8g.setPrintPos(15,40); u8g.print(round(X));
u8g.setPrintPos(60,40); u8g.print(round(Y));
u8g.setPrintPos(100,40); u8g.print(round(Z));
u8g.drawVLine(37,18,36);
u8g.drawStr(39,24,"AIR TEMP: "); u8g.setPrintPos(85,24); u8g.print(temp); u8g.drawStr(118,24,"C");
u8g.drawStr(39,34,"HUMIDITY: "); u8g.setPrintPos(85,34); u8g.print(humid); u8g.drawStr(118,34,"%");
u8g.drawStr(39,44,"PRESSURE: "); u8g.setPrintPos(85,44); u8g.print(prsr); u8g.drawStr(118,44,"hP");
u8g.drawStr(39,54,"ALTITUDE: "); u8g.setPrintPos(85,54); u8g.print(round(alt)); u8g.drawStr(118,54,"m");
u8g.drawHLine(0,56,128);
u8g.drawStr(19,64,"LAT:"); u8g.setPrintPos(39,64); u8g.print(23.57); // Demo
u8g.drawStr(69,64,"LONG:"); u8g.setPrintPos(94,64); u8g.print(90.36); // Demo
// Kompass-Anzeige
int r = round(sqrt(X*X + Y*Y));
u8g.drawStr(0,22,"heading");
u8g.drawCircle(18,34,r, U8G_DRAW_ALL); // Innerer Kreis (Neigung)
u8g.drawCircle(18,34,10, U8G_DRAW_ALL); // Äußerer fixer Kreis
u8g.setPrintPos(0,53); u8g.print(heading); // 0/360=N, 90=O, 180=S, 270=W
// Kompass-Zeiger
if ((heading>315)||(heading<=45)) u8g.drawLine(18,34,8,34); // N
if ((heading>45)&&(heading<=135)) u8g.drawLine(18,34,18,24); // O
if ((heading>135)&&(heading<=225)) u8g.drawLine(18,34,28,34); // S
if ((heading>225)&&(heading<315)) u8g.drawLine(18,34,18,44); // W
}
Bibliotheken
Als ZIP-Datei verfügbar. Entpacken und einzelne Bibliotheken über Arduino Library Manager hinzufügen.
Schaltplan
Herstellungsprozess
- Lambda-Sonde (O2-Sensor) verstehen: Rolle, Position und Ausfallanzeichen
- LED mit PIR-Bewegungsmelder am Raspberry Pi steuern
- Günstiger Raspberry Pi Bodenfeuchtesensor für smarte Bewässerung
- Hamster-Fitness-Tracker mit Raspberry Pi: Tägliche Laufrad-Aktivität messen
- DS18B20 Temperatursensor am Raspberry Pi testen
- Raspberry Pi Heimtemperaturmonitor mit MCP9808, InfluxDB & Grafana bauen
- Walabot-gestützter Toiletten-Hygienemonitor
- Emotionssensor & EEG: Günstige Hardware für Echtzeit-Emotionserkennung
- Adafruit 10DOF IMU Daten von Raspberry Pi 2 an Azure Event Hubs senden (Windows 10 IoT Core)
- Präzises WiFi-RSSI-Tracking für ISS-Sensoren