Ostereier verstecken mit GPS (GPS/GSM SIM 808) - Teil 1

Dieses Jahr wollen wir den Osterhasen unterstützen, Ostereier zu verstecken, die auch wiedergefunden werden. Dafür statten wir ihn (und später die Suchenden) mit einem GPS-Empfänger aus. Das Verstecken und Ablesen der Position ist dabei der leichteste Teil, denn die Werte werden auf unserem Display angezeigt und müssen nur notiert werden. Aber auch dafür müssen wir ihm ein Grundverständnis über unsere Erde verschaffen, also Osterhase: aufgepasst.

Das Suchen stellt uns dann beim nächsten Mal vor größere Herausforderungen, denn zunächst müssen wir die aktuelle Position bestimmen und dann Kurs und Entfernung zum Ziel berechnen. Das erfordert schon ein gewisses Maß an Mathematik-Kenntnissen, bei der wir aber aufgrund kurzer Distanzen bei uns in Deutschland oder Mitteleuropa einige Vereinfachungen machen können.

Wenn wir den Empfänger nicht mehr für die Ostereiersuche oder GeoCaching benötigen, bauen wir uns im dritten Teil daraus ein Tracking-System, denn wir haben hier ein kombiniertes GPS/GSM Modul, mit dem wir die Position auch per SMS an ein Smartphone senden können.

Was brauchen wir?

Anzahl Bauteil
1 Micro Controller, z.B. den Uno R3 kompatiblen MC
1 LCD, z.B. das LCD-Keypad-Shield
1 GPS/GSM Modul SIM 808 (im dritten Teil mit Sim-Karte)
Akku oder Batterie


Um den Rahmen des Beitrags nicht zu sprengen, verweise ich wegen weiterer Details zum SIM 808 und Keypad-Shield auf die eBooks, die auf der jeweiligen Produktseite verlinkt sind.

 

Wer schon genau weiß, was 54°30’N 009°15’E bedeutet, kann die folgenden Absätze überfliegen. Wer nicht, lernt jetzt unser Koordinatensystem auf der Erde kennen.

Die Erde sieht aus wie eine Apfelsine (nur eben blau statt orange), fast kugelförmig, an den Polen minimal abgeflacht. Um einen Punkt auf der Oberfläche zu bestimmen, haben sich schlaue Leute schließlich auf ein einheitliches Koordinatensystem geeinigt. Die Erde dreht sich an einem Tag einmal um die eigene Achse. Wir definieren diese Drehachse als Erdachse durch die beiden Pole und halbieren die Kugel mit einer imaginären Linie mit Namen Äquator. Dann definieren wir die Geographische Breite als den Winkel am Erdmittelpunkt zwischen Äquator und unserem Ort. Deutschland liegt zwischen 47° 16′ 18″  und  55°03′ 31,1″  ° nördlicher Breite. Die Pole liegen dann bei 90° Nord bzw. Süd; Südliche Breite wird häufig mit Minuszeichen angezeigt. Schwieriger war die Festlegung der Geographischen Länge, weil jeder König die Nulllinie durch seinen Thron haben wollte (z.B. die Rosenlinie bei Dan Browns „Da Vinci Code“). Schließlich wurde die Sternwarte in Greenwich bei London als Bezugsmeridian festgelegt. Einmal um die Erde sind 360°, jeweils 180° nach Osten und nach Westen; die westlichen Längen ggf. wieder mit Minuszeichen. Deutschland liegt zwischen 005° 52′ 01″  und 015°02′ 37″  östlicher Länge. Die Datumslinie (meist bei 180° E/W) verläuft durch den Pazifischen Ozean.

Bild: Erdkugel mit Längen- und Breitenkreisen

Die Längengrade sind Großkreise, die von Pol zu Pol verlaufen. Großkreise sind beliebige Kreise auf der Kugeloberfläche um den Erdmittelpunkt; die kürzeste Strecke zwischen zwei Punkten verläuft auf dem Großkreis durch diese Punkte. Der Äquator ist der einzige Breitenkreis, der ein Großkreis ist, Richtung Pol wird der Radius bzw. Umfang der Breitenkreise immer kleiner. Wer kennt die Winkelfunktion, die bei 0° maximal (=1) und bei 90° minimal (=0) ist? Kommt später, wenn wir nicht nur Winkel im Gradmaß, sondern auch im Bogenmaß kennenlernen müssen. Navigation hat viel mit Mathematik zu tun.

Zum besseren Verständnis des Planeten Erde ist ein Globus (am besten mit Innenbeleuchtung) unersetzbar, aber für die Navigation orientieren wir uns in einer Karte, ein flaches Stück Papier mit dem Gebiet, in dem wir uns bewegen. In der Seefahrt gilt: Immer die Karte mit dem größten verfügbaren Maßstab nehmen, aber was ist größer? 1:2.000.000 oder 1:50.000? Bei der ersten Karte entsprechen 10 cm 200 km in der Wirklichkeit, bei der zweiten sind die 10cm nur 5 km. Die zweite ist also detailgetreuer. Die kleinen Maßstäbe (Übersegler) nutzt man für die Routenplanung, den Satz Karten mit den größeren Maßstäben bei der Durchführung der Fahrt. Heute macht man das ja mit einem Touch-Screen, bei dem man mit zwei Fingern den Maßstab verändert.

Um so eine Karte, ein zweidimensionales Abbild der Erdoberfläche, zu erhalten, benutzen wir (aber auch Google Maps u.a.) die Methoden von Gheert Cremer, besser bekannt als Gerhard Mercator  (1512 - 1594) und  Johann Heinrich Lambert (1728 - 1777). Mercator hat die sogenannte Zylinder-Projektion, Lambert die (Schnitt-) Kegelprojektion beschrieben.

Bei der Merkator-Projektion wird ein Zylinder über die Kugel gestülpt, in unseren Breiten achsenparallel. Dabei werden die Pole nicht abgebildet und die Insel Grönland sieht größer aus als der Kontinent Afrika. Die Abbildung ist also nicht Längen- oder Flächen-treu und in Nähe der Pole unbrauchbar. Aber sie ist winkeltreu und führt uns in Mitteleuropa bei kurzen bis mittleren Distanzen genügend gut zum Ziel.

Projektionsarten: links Zylinder-Proj. (Mercator), rechts (Schnitt-) Kegel-Proj. (Lambert)

Bei der Lambert’schen Schnittkegel-Projektion wird ein Kegel am Pol angesetzt, der die Kugel z.B. bei 60° nördlicher Breite schneidet, also innerhalb der Kugel verläuft, und bei 30 Grad nördlicher Breite wieder austritt. Das ist (vor allem in der Nähe der Breitenkreise/Schnittlinien) der beste Kompromiss in Bezug auf Längen-, Flächen- und Winkeltreue, aber nichts stimmt 100%ig. Die ideale Karte für Piloten, die ja meist größere Strecken zurücklegen als die GeoCacher.

Auf diese Abbilder der Erdoberfläche malen wir dann unsere Hilfslinien ein und können so jeden Ort mit seiner Geografischen Breite (wieviel Grad nördlich des Äquators?) und Länge (wieviel Grad östlich der Sternwarte in Greenwich?) bzw. umgekehrt aus den Zahlenangaben den Ort bestimmen.

Die eigentlichen Schwierigkeiten beginnen dann im Detail, denn es gibt verschiedene Schreibweisen; und wir wollen die Ortsangaben ja möglichst genau haben. Die Breite „auf 1° genau“ bedeutet nämlich eine Genauigkeit von 111 km, also irgendwo zwischen Würzburg und Nürnberg. Eine feinere Unterteilung hat man dann mit den Bogenminuten gemacht. Das Wort Minute deutet schon darauf hin, dass die Zahl 60 ins Spiel kommt. 1° (sprich: Grad) entspricht 60‘ (sprich: (Bogen-) Minuten.

Hier merken wir uns: Eine Bogenminute am Meridian (Längenkreis) entspricht einer Seemeile oder 1,852km.

Immer noch zu ungenau für unsere Suche. Also weiter unterteilen in (Bogen-) Sekunden (wieder mit Faktor 60) oder nach dem Dezimalsystem. Beides ist möglich und üblich, man muss nur die Umrechnung beherrschen. Die GeoCacher bevorzugen das Format DDMM.mmmm, also die Gradzahl (engl. Degrees) zweistellig, dann die Minuten zweitstellig und die Bruchteile als Dezimale der Minuten hier mit kleinem „m“ dargestellt. Alternativ gibt es DDMMSSss, also Grad (DD), Minuten (MM), Sekunden (SS) und Dezimale der Sekunden (ss). Für die Umrechnung benötigen wir nur die schon genannte Zahl 60, also z.B. 30 Bogensekunden entsprechen 30/60 = ½ Bogenminute. Andersherum 0,8 Bogenminuten entsprechen 0,8 * 60 = 48 Bogensekunden.

 

Der folgende Sketch soll uns aus der Vielzahl der Daten, die unser GPS-Empfänger empfängt, die richtigen auszuwählen. Schließen wir unseren GPS-Empfänger an ein Terminalprogramm an, so erhält man im Sekundentakt folgende Angaben:


Schnell erkennt man, dass die interessanten Daten in den Zeilen stehen, die mit $GPGGA und „GPRMC beginnen:

$GPGGA,080635.000,5351.2345,N,00951.2345,E,1,9,1.02,32.1,M,45.6,M,,*67

$GPRMC,080635.000,A, 5351.2345,N, 00951.2345,E,0.20,159.92,140321,,,A*69

Die von uns im Sketch verwendete Programmbibliothek wertet die fettgedruckte Zeile aus, die mit GPRMC beginnt. Die wesentlichen Angaben stehen jedoch auch in der anderen Zeile. Dabei steht GP für Global Positioning System (GPS) und RMC für Recommended Minimum Navigation Information. (Quellen: https://de.wikipedia.org/wiki/NMEA_0183, http://www.nmea.de/nmea0183datensaetze.html#rmc)

Dann folgen:

  1) Universal Time Coordinated (UTC)   (früher bekannt als Greenwich Mean Time (GMT))
  2) Latitude  (Format DDMM.mmmm)
  3) N or S (North or South)
  4) Longitude   (Format DDDMM.mmmm)
  5) E or W (East or West)
  6) GPS Quality Indicator,    0 - fix not available,    1 - GPS fix,
2 - Differential GPS fix
  7) Number of satellites in view, 00 - 12
  8) Horizontal Dilution of precision
  9) Antenna Altitude above/below mean-sea-level (geoid) 
 10) Units of antenna altitude, meters

Bei der Hardware hatte ich mich für das LCD-Keypad-Shield entschieden, bei dem man mittels der Buttons des Keypads schnell zwischen verschiedenen Ansichten wechseln kann. Den LEFT Button nutzen wir für die Position im Format DDMMmmmm, DOWN für Datum und Uhrzeit in Universal Time Coordinated (UTC), UP für unsere Zeitzone und RIGHT für die Position im Format DDMMSSs; den SELECT Button nutzen wir beim nächsten Mal für Kurs und Distanz.

Vorbemerkungen zum Sketch:

  1. Für den GPS-Empfänger nutze ich SoftwareSerial an A3 = GPIO 17 und A4 = GPIO 18.
  2. Die verwendete Bibliothek enthält für die Umrechnung von Bogensekunden in Dezimale der Bogenminuten und umgekehrt einen Bug.
  3. Die Bibliothek ist auch sehr zeitkritisch. Die Abfrage if (sim808.getGPS()) kann ggf. zum Stolperstein werden.
  4. Variablennamen, die mit LAT beginnen, beziehen sich auf die geo. Breite (engl. Latitude);
    Variablennamen, die mit LON beginnen, beziehen sich auf die geo. Länge (engl. Longitude).
  1. Für MEZ/MESZ muss Offset eingegeben werden. Bei der Umrechnung der Zeit wird der Datumswechsel berücksichtigt, jedoch nicht Monat und Jahr (Aufwand).

Der Sketch:

#include <DFRobot_sim808.h>
#include <SoftwareSerial.h>
#define PIN_TX    17
#define PIN_RX    18

//  LCD has no I2C-Adapter, data transfer with Pins D4 to D7 
#include <LiquidCrystal.h>
//LCD pin to Arduino
//const int pin_BL = 15; 
const int pin_EN = 9; 
const int pin_RS = 8; 
const int pin_D4 = 4; 
const int pin_D5 = 5; 
const int pin_D6 = 6; 
const int pin_D7 = 7;  

LiquidCrystal lcd( pin_RS,  pin_EN,  pin_D4,  pin_D5,  pin_D6,  pin_D7);

// Offset for Time, here UTC zu MEZ / MESZ
// Summertime MESZ: 2, Wintertime MEZ: 1
#define Offset 1

SoftwareSerial mySerial(PIN_TX,PIN_RX);
DFRobot_SIM808 sim808(&mySerial); //Connect RX,TX,PWR

// unterbrechungsfreie Zeitsteuerung
unsigned long previousMillis = 0;
const long interval = 10;

// Buttons
int buttonInput = -1;
int buttonSelect = 0;

void setup() {
  mySerial.begin(9600);
  Serial.begin(9600);
  lcd.begin(16,2); // initialize the lcd
  lcd.clear();
  lcd.setCursor(0,0); //Zählung beginnt bei Null, erst Zeichen, dann Zeile
  lcd.print("AZ-Delivery.com");
  lcd.setCursor(0,1); // 0=Erstes Zeichen, 1=zweite Zeile

  //******** Initialize sim808 module *************
  while(!sim808.init()) {
    delay(1000);
    Serial.print("Sim808 init error\r\n");
  }

//************* Turn on the GPS power************
if( sim808.attachGPS())
  Serial.println("Open the GPS power success");
else
  Serial.println("Open the GPS power failure");
}

void loop() {

  buttonInput = Button();
  switch (buttonInput) {
    case 0: Serial.println("0");buttonSelect=0; break;
    case 1: Serial.println("1");buttonSelect=1; break;
    case 2: Serial.println("2");buttonSelect=2; break;
    case 3: Serial.println("3");buttonSelect=3; break;
    case 4: Serial.println("4");buttonSelect=4; break;
    default: break;
  }

  if (millis() - previousMillis >= interval) {
  //************** Get GPS data *******************
    if (sim808.getGPS()) {
    int MONTH = sim808.GPSdata.month;
    int DAY = sim808.GPSdata.day;
    int DAYLCL = DAY;
    int HOUR = sim808.GPSdata.hour;
    int HOURLCL = HOUR + Offset;
    int MINUTE = sim808.GPSdata.minute;
    int SECOND = sim808.GPSdata.second;
    if (HOURLCL>24) {
      HOURLCL = HOURLCL-24;
      DAYLCL = DAYLCL + 1; }
    Serial.print(sim808.GPSdata.year);
    Serial.print("/");
    Serial.print(MONTH);
    Serial.print("/");
    Serial.print(DAY);
    Serial.print(" ");
    Serial.print(HOUR);
    Serial.print(":");
    Serial.print(MINUTE);
    Serial.print(":");
    Serial.println(SECOND);
    float LAT = sim808.GPSdata.lat;
    int LATDD = int(LAT);
    float LATMMmmmm = (LAT - LATDD)*100;
    float LON = sim808.GPSdata.lon;
    int LONDDD = int(LON);
    float LONMMmmmm = (LON - LONDDD)*100;
    Serial.print("latitude: ");
    Serial.print(LATDD);
    Serial.print("°");
    Serial.print(LATMMmmmm,4);
    Serial.println("'N");
    Serial.print("longitude: ");
    if (LON<100.0) Serial.print("0");
    if (LON<10.0) Serial.print("0");
    Serial.print(LONDDD);
    Serial.print("°");
    Serial.print(LONMMmmmm,4);
    Serial.println("'E");
    float SPEED = sim808.GPSdata.speed_kph;
    if (SPEED >= 3.0) {
      Serial.print("speed_kph: ");
      Serial.println(SPEED);
      Serial.print("heading: ");
      Serial.println(sim808.GPSdata.heading); }
    else {
      Serial.print("speed_kph :");
      Serial.println("below 3");
      Serial.print("heading :");
      Serial.println("not determined"); }

    if (buttonSelect==0) {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print(LATDD);
      lcd.print("\xDF");
      int LATMM = int(LATMMmmmm);
      float LATSSs = (LATMMmmmm-LATMM)*60;
      lcd.print(LATMM);
      lcd.print("'");
      lcd.print(LATSSs,1);
      lcd.print("\x22\ N");
      lcd.setCursor(0,1);
      if (LON<100.0) lcd.print("0");
      if (LON<10.0) lcd.print("0");
      lcd.print(LONDDD);
      lcd.print("\xDF");
      int LONMM = int(LONMMmmmm);
      float LONSSs = (LONMMmmmm-LONMM)*60;
      if (LONMM<10) lcd.print("0");
      lcd.print(LONMM);
      lcd.print("'");
      if (LONSSs<10.0) lcd.print("0");
      lcd.print(LONSSs,1);
      lcd.print("\x22\ E"); }

    else if (buttonSelect==1) {
       lcd.clear();
       lcd.setCursor(0,0);
       lcd.print("EUR: ");
       if (DAYLCL<10) lcd.print("0");
       lcd.print(DAYLCL);
       lcd.print(".");
       if (MONTH<10) lcd.print("0");
       lcd.print(MONTH);
       lcd.print(".");
       lcd.print(sim808.GPSdata.year);
       lcd.setCursor(5,1);
       if (HOURLCL<10) lcd.print("0");
       lcd.print(HOURLCL);
       lcd.print(":");
       if (MINUTE<10) lcd.print("0");
       lcd.print(MINUTE);
       lcd.print(":");
       if (SECOND<10) lcd.print("0");
       lcd.print(SECOND);
     }
    else if (buttonSelect==2) {
       lcd.clear();
       lcd.setCursor(0,0);
       lcd.print("UTC: ");
       lcd.print(sim808.GPSdata.year);
       lcd.print("/");
       if (MONTH<10) lcd.print("0");
       lcd.print(MONTH);
       lcd.print("/");
       if (DAY<10) lcd.print("0");
       lcd.print(DAY);
       lcd.setCursor(5,1);
       if (HOUR<10) lcd.print("0");
       lcd.print(HOUR);
       lcd.print(":");
       if (MINUTE<10) lcd.print("0");
       lcd.print(MINUTE);
       lcd.print(":");
       if (SECOND<10) lcd.print("0");
       lcd.print(SECOND);
     }
    else if (buttonSelect==3) {
       lcd.clear();
       lcd.setCursor(0,0);
       lcd.print(LATDD);
       lcd.print("\xDF");
       lcd.print(LATMMmmmm,4);
       lcd.print("' N");
       lcd.setCursor(0,1);
       if (LON<100.0) lcd.print("0");
       if (LON<10.0) lcd.print("0");
       lcd.print(LONDDD);
       lcd.print("\xDF");
       if (LONMMmmmm<10.0) lcd.print("0");
       lcd.print(LONMMmmmm,4);
       lcd.print("' E"); }
     else if (buttonSelect==4) {
       lcd.clear();
       lcd.setCursor(0,0);
       lcd.print("AZ-Delivery.com"); }
       //
    // //************* Turn off the GPS power ************
    // sim808.detachGPS();
    previousMillis = millis();
    }
  }
}

int Button() {
  int A0;
  // all buttons are connected to A0 via voltage divider
  // Values of ADC are between 0 and 1023
  // if necessary, values must be changed slightly
  A0 = analogRead(0); //
  if (A0 < 60) {
    return 0;
  }
  else if (A0 >= 60 && A0 < 250) {
    return 1;
  }
  else if (A0 >= 250 && A0 < 450){
    return 2;
  }
  else if (A0 >= 450 && A0 < 700){
    return 3;
  }
  else if (A0 >= 700 && A0 < 900){
    return 4;
  }
  else {
    return -1;
  }
}   //end Button()

 

So, lieber Osterhase, mit diesem Sketch kannst Du die Ostereier verstecken und den Kindern Hinweise geben, wo sie suchen müssen. Die Suchenden müssen sich bis zum nächsten Sketch gedulden, wenn wir Richtung (Kurs) und Entfernung zu den Wegpunkten berechnen lassen.

 Download als pdf

7 Kommentare

Dirk Harms

Dirk Harms

Sehr schönes Projekt, meine Kinder freuen sich schon ;-)
Falls auf dem Display nichts erscheint – einfach mal den Kontrast am Poti verändern.

Bernd Albrecht

Bernd Albrecht

Danke an Ingo für den Hinweis und die Lösung des Problems. Ich hatte schon die Sim-Karte eingesteckt für den dritten Teil der Blog-Reihe. Deshalb ist mir diese unüberwindliche Hürde nicht aufgefallen. Tatsächlich funktioniert der Sketch mit den folgenden Zeilen nur, wenn eine Sim-Karte eingesteckt ist.
//******** Initialize sim808 module *************
while(!sim808.init()) {
delay(1000);
Serial.print(“Sim808 init error\r\n”);
}
Deshalb: Wer den Sketch ohne Sim-Karte benutzen möchte, diese Zeilen bitte auskommentieren.

Reinhard Völler

Reinhard Völler

Ich bekomme leider das LCD Display Shield nicht zum laufen. Habe im Sketch mal alles auskommentiert, was nichts mit dem LCD zu tun hat, aber “AZ-Delivery” erscheint bei mir nicht. Auch die angepassten Beispiele aus der Liquidcrystal Library funktionieren bei mir nicht. Ich habe D4-D7 auf 4-7 gesetzt, EN=9, RS=8. Hintergrundbeleuchtung ist an. Was mache ich da falsch?

Ingo

Ingo

Ich habe das ganze mal bei mir aufgebaut. Bekomme aber immer Sim808 init error. Kann es sein das die Funktion while(!sim808.init()) { nur funktioniert wenn eine SIM Karte eingelegt ist? Wenn ich diesen Teil auskommentiere läuft alles einwandfrei.
Viele Grüße Ingo

Juergen

Juergen

Hallo,
schöner Bericht, danke dafür.
Es wäre noch ganz hilfreich wenn man einen Anschlussplan bekommen könnte, um zu wissen wie die Komponenten verbunden werden sollen.
mfg

Jürgen

Jürgen

So etwas in der Art stand schon lange auf meiner ToDo-Liste.
Ich denke, ich werde, das mit dieser Superunterstützung jetzt
auch mal in Micropython versuchen.
Schönen Gruß
Jürgen

Reinhard Völler

Reinhard Völler

Witziges Projekt, habe ich gleich mal geordert.
Ein Hinweis, dass man die Library auf github findet, wäre vielleicht hilfreich:
https://github.com/DFRobot/DFRobot_SIM808
Gruß an den Osterhasen :-)

Einen Kommentar hinterlassen

Alle Kommentare werden vor der Veröffentlichung moderiert