• Hallo Zusammen, Aufgrund der aktuellen Situation setzten wir die Möglichkeit aus, sich mit Gmail zu registrieren. Wir bitten um Verständnis Das RCMP Team

Anleitung Arduino - GPS-Modul zur Zeiteinstellung

BAXL

Admin
Mitarbeiter
Zeitabgleich der internen Arduino-Uhr mit GPS-Daten. In diesen Thema beschreibe ich den Aufbau und die Programmierung.



Inspiriert wurde ich durch ein Thema unseres Mitgliedes @Quiddje , der bereits mit seinem Projekt Arduino GPS-Uhr mit TM1637 vorgelegt hat. Danke dafür! :thumbsup:
Ins Rollen ist die Sache durch unsere Uhren-Projekte mit DCF77 Empfängermodule gekommen: DCF77 Empfang mit Arduino und Arduino mit DCF77-Modul und RTC Echtzeituhr DS3231
Auch da hat @Quiddje den Initialimpuls gebracht. So kann man als Holzwurm und Strippenzieher gemeinsam sehr kreativ und effizient arbeiten. :)

Basis für dieses Projekt ist ein Arduino Nano-Clone, ein GPS-Modul GY-NEO6MV2 (Achtung, der Link ist keine bezahlte Werbung!), eine 16x2 LCD-Anzeige und eine LED nebst Vorwiderstand.
Das verlinkte GPS-Modul arbeitet mit 5V, sodass man das direkt an den Nano anschließen kann. Die LED dient nur der Anzeige, dass auf Sommerzeit eingestellt ist.

Der Anschluß / Hardware:
Das GPS-Modul wird an +5V, GND und der TX-Pin an Pin D8 des Nanos angesteckt.
Für die Erkennung der Sommerzeitumstellung wird Pin D7 des Nanos an +5V (Sommerzeit) oder GND (Normalzeit) gelegt.
Parallel zum Pin D7 schließe ich eine LED über einen Vorwiderstand an. Sobald die LED leuchtet, liegt ein High-Pegel an D7 und der Nano wertet das programmtechnisch als Sommerzeit.

Mein Display mit I2C-Interface wird an die Nano-Pins A4(SDA) und A5(SCL) sowie an +5V und GND angeschlossen.




Das Programm:
Für das Programm benötigt man diverse Libraries.

Für das GPS-Modul braucht man die TinyGPS++.h und die SoftwareSerial.h, sowie die SPI.h. Die beiden letztgenannten sind normalerweise Bestandteil der Arduino IDE.
Für ein angeschlossenes Display muß man ggf. eine Lib verwenden, die mit dem eigenen Display funktioniert, mein LCD-Display läuft mit der LiquidCrystal_I2C.h, weil ich das Display mit einem I2C-Adapter betreibe.

Zum Programmablauf:

Basis für mein Programm ist ein Programm von simple-circuits.com, dass ich auf meine Hardware und meine speziellen Wünsche angepasst habe.
Die wesentlichen Modifikationen sind die 16x2 LCD-Anzeige (im Urprogramm 20x4), die mit einem I2C-Interface läuft. Natürlich musste ich dafür noch die Ausgabe des Datums, der Zeit und des Wochentages anpassen.
Bezüglich der Programmfunktionen setze ich nicht einen festen Korrekturwert zur UTC (Zeit am nullten Breitengrad, ehemals GMT), sondern bestimme die Zeitzone über den Längengrad, den ich aus dem GPS-Modul beim ersten Schleifendurchlauf der Hauptfunktion abfrage.
Deutschland liegt ungefähr zwischen dem 6. und 15. Längengrad. Um auch in den östlichen und westlichen Randgebieten des Landes auf Nummer sicher zu gehen, erweitere ich den Bereich der Abfrage auf den 5. bis zum 16. Längengrad.
Die Umstellung zwischen Sommer- und Normalzeit erfolgt über die Abfrage des Digitalports D7, den ich in meinen Projekten üblicherweise immer für derartige Zwecke verwende. Ich hatte kurzzeitig über eine automatische Umstellung nachgedacht, mich aber aus folgenden Gründen dagegen entschieden:
  1. Die Zeitumstellung erfolgt nicht immer am selben Datum, sondern am letzten Sontag im März(SZ) und letzten Sonntag im Oktober(NZ). Die Berechnung des tatsächlichen Datums wäre dann etwas umfangreicher.
  2. Es könnte durchaus sein, dass die Umstellung der Zeit abgeschafft wird, dann wäre eine fest programmierte Zeitumstellung unbrauchbar und die geleistete Programmierarbeit für die Katz.
Bereits in der Setup-Routine lese ich den Sommerzeitstatus einmal ein. Für die Erkennung der Zeitzone frage ich beim ersten Hauptschleifendurchlauf den Längengrad ab, danach nicht mehr.
Die Umstellung zwischen Sommer- und Normalzeit könnte ich eigentlich auch nur einmal beim ersten Hauptschleifendurchlauf abfragen, nur müsste die "Uhr" dann resettet werden wenn man umschaltet, das gefiel mir aber nicht.
Also Frage ich bei jedem Schleifendurchlauf den Port D7 ab und vergleiche, ob vorher die jeweils andere Zeit aktiv war und berechne die Zeitkorrektur ggf. neu. Die parallel zum Pin D7 angeschlossene LED gibt Auskunft darüber, welche der beiden Zeiten aktiv ist. Ich könnte das auch auf dem Display anzeigen, habe das aber (noch) nicht gemacht.

Die Anzeige:
Auf dem Display erscheint in der ersten Zeile die Uhrzeit im Format HH:MM:SS, in der zweiten Zeile steht der Wochentag mit den zwei ersten Buchstaben und dahinter das komplette Datum inkl. Jahreszahl.
Die Uhrzeit wir bei jedem Schleifendurchlauf aktualisiert.

Das nun folgende Programmlisting enthält noch Programmzeilen zur Ausgabe über den seriellen Monitor am PC-Bildschirm, die ich für die Fehlersuche gebraucht habe, sowie auskommentierte Zeilen des Ur-Programms.
Aber es funktioniert auf jeden Fall.

C++:
/*
* Arduino GPS clock with local time using NEO-6M module and 20x4 LCD.
* This is a free software with NO WARRANTY.
* https://simple-circuit.com/
* angepasst von BAXL RC-Modellbau-Portal.de an eigene Bedürfnisse
*/

#include <SPI.h>
#include <TinyGPS++.h>        // include TinyGPS++ library
#include <TimeLib.h>          // include Arduino time library
#include <SoftwareSerial.h>   // include software serial library
//#include <LiquidCrystal.h>    // include LCD library

TinyGPSPlus gps;

#define S_RX    9   // define software serial RX pin
#define S_TX    8   // define software serial TX pin

SoftwareSerial SoftSerial(S_RX, S_TX);   // configure SoftSerial library

// LCD module connections (RS, E, D4, D5, D6, D7)
// LiquidCrystal lcd(2, 3, 4, 5, 6, 7);
#include <LiquidCrystal_I2C.h> // LiquidCrystal_I2C Bibliothek einbinden
LiquidCrystal_I2C lcd(0x27, 16, 2); //Hier wird festgelegt um was für einen Display es sich handelt.
// In diesem Fall eines mit 16 Zeichen in 2 Zeilen und der HEX-Adresse 0x27.
// Für ein vierzeiliges I2C-LCD verwendet man den Code "LiquidCrystal_I2C lcd(0x27, 20, 4)"

#define time_offset   7200  // define a clock offset of 7200 seconds (2 hour) ==> UTC + 2
int Zeitzuschlag;
int Sommerzeit = 0;
byte SommerzeitPin = 7;
byte ErstStart;
byte De;
byte Sommer = 0;
// variable definitions
char Time[]  = "00:00:00";
char Date[]  = "00.00.2000";
byte last_second, Second, Minute, Hour, Day, Month;
int Year;

void setup(void)
{
  Serial.begin(9600);
  SoftSerial.begin(9600);  // initialize software serial at 9600 baud

  lcd.init();       //Im Setup wird der LCD gestartet
  lcd.backlight();  //Hintergrundbeleuchtung einschalten (lcd.noBacklight(); schaltet die Beleuchtung aus).
  lcd.setCursor(0, 0); // move LCD cursor to column 1, row 0 [upper left position (0, 0)]
  lcd.print("GPS DE");
  Serial.println("gestartet");

  // Variablen zur Zeitumstellung zwischen Normal- und Sommerzeit
  pinMode(SommerzeitPin, INPUT); // Eingang zur Umstellung zwischen Sommer- und Normalzeit
  Sommerzeit = (digitalRead(SommerzeitPin)); // Berechnung der Zeitkorrektur bei Sommerzeit wen HIGH-Pegel
  Sommer = 0;
  if (Sommerzeit == 1){Sommer = 1;} // Merker für die Sommerzeitumstellung
  ErstStart = 1; // Merker für Längengradbestimmung bei Neustart des Programms
}

void loop()
{

  while (SoftSerial.available() > 0)
  {
    if (gps.encode(SoftSerial.read()))
    {
      // get time from GPS module
      if (gps.time.isValid())
      {
        Minute = gps.time.minute();
        Second = gps.time.second();
        Hour   = gps.time.hour();
        Serial.print(Hour);Serial.print(":");Serial.print(Minute);Serial.print(":");Serial.println(Second);
        Serial.println(ErstStart);
        if (ErstStart == 1)
        {UTC_De(); ErstStart = 0;Serial.println("ErstStart");}
      
         if (Sommer != digitalRead(SommerzeitPin)) {
              // Umstellung der Zeit wenn SommerZeitpin seinen pegel verändert hat
              // HIGH - Sommerzeit, LOW - Normalzeit
              Sommerzeit = !Sommerzeit;
              Sommer = !Sommer;
              Zeitzuschlag = (De + Sommerzeit) *3600; // Berechnung Korrektur aus GPS-Position und Sommerzeitstatus
       }
      }

      // get date drom GPS module
      if (gps.date.isValid())
      {
        Day   = gps.date.day();
        Month = gps.date.month();
        Year  = gps.date.year();

      }

      if(last_second != gps.time.second())  // if time has changed
      {
        last_second = gps.time.second();

        // set current UTC time
        setTime(Hour, Minute, Second, Day, Month, Year);
        // add the offset to get local time
        //adjustTime(time_offset);
        adjustTime(Zeitzuschlag);

        // update time array
        Time[6] = second() / 10 + '0';
        Time[7] = second() % 10 + '0';
        Time[3]  = minute() / 10 + '0';
        Time[4] = minute() % 10 + '0';
        Time[0]  = hour()   / 10 + '0';
        Time[1]  = hour()   % 10 + '0';

        // update date array
        Date[8] = (year()  / 10) % 10 + '0';
        Date[9] =  year()  % 10 + '0';
        Date[3]  =  month() / 10 + '0';
        Date[4] =  month() % 10 + '0';
        Date[0]  =  day()   / 10 + '0';
        Date[1]  =  day()   % 10 + '0';

        // print time & date
        print_wday(weekday());   // print day of the week
        lcd.setCursor(0, 0);     // move cursor to column 0 row 2
        lcd.print(Time);         // print time (HH:MM:SS)
        lcd.setCursor(3, 1);     // move cursor to column 0 row 3
        lcd.print(Date);         // print date (DD-MM-YYYY)

        //longi = gps.location.lng();
        //Serial.print("Längengrad");Serial.println(gps.location.lng());
//getlongi;
      }

    }

  }

}

// function for displaying day of the week
void print_wday(byte wday)
{
  lcd.setCursor(0, 1);  // move cursor to column 5, row 1
  switch(wday)
  {
    case 1:  lcd.print("So ");   break;
    case 2:  lcd.print("Mo ");   break;
    case 3:  lcd.print("Di ");   break;
    case 4:  lcd.print("Mi ");   break;
    case 5:  lcd.print("Do ");   break;
    case 6:  lcd.print("Fr ");   break;
    default: lcd.print("Sa ");
  }
}
void UTC_De()
{
  //double Longi;
  //Longi = gps.location.lng();
  if ((gps.location.lng()> 5) && (gps.location.lng() < 16)){De = 1;}
  Serial.print("Längengrad:");Serial.print(gps.location.lng());Serial.print(" Deutschland: ");Serial.println(De);
  Zeitzuschlag = (De + Sommerzeit) *3600;
}
void SetSommerWinter()
{
  //double Longi;
  //Longi = gps.location.lng();
  if ((gps.location.lng()> 5) && (gps.location.lng() < 16)){De = 1;}
  Serial.print("Längengrad:");Serial.print(gps.location.lng());Serial.print(" Deutschland: ");Serial.println(De);
  Zeitzuschlag = (3600 * De) + Sommerzeit;
}
// end of code.
Erkenntnisse:
Beim ersten Start der Schaltung (nach längerer Spannungslosigkeit) dauert es u.U. ca. 1 Minute, bis eine Zeit angezeigt wird, dabei ist es sogar möglich, dass kurzzeitig ein total unsinniges Datum (2099) und eine falsche Zeit angezeigt wird. Nach ein paar Sekunden hat man aber dann die korrekte Zeit. Nach einem Reset oder nur kurzer Trennung von der Betriebsspannung, erfolgt die richtige Datums- und Zeitanzeige bereits nach wenigen Augenblicken. Das GPS-Modul liegt bei mir nur in der Nähe des Fensters, also nicht direkt im Freien, da hat mich die relativ schnelle Reaktion sehr erstaunt. Mal sehen was sich aus dem Rohprojekt machen lässt. Im Moment läuft die "Uhr" erstmal nur auf meinem Schreibtisch zusammen mit der DCF77-Uhr aus dem Thema: Arduino mit DCF77-Modul und RTC Echtzeituhr DS3231
 
Zuletzt bearbeitet:

kuckuck

Mitglied
Hallo, ich bin zwar nicht unbedingt ein Anfänger aber auch nicht grade lange dabei ca 1 Jahr.
Folgende Fehlermedung taucht auf:

exit status 1
'int LiquidCrystal_I2C::init()' is private within this context

Ich vermute mal das meine Bibliothek die falsche ist und wenn ja welche ist die richtige.

Ich habe schon mit Displays gearbeitet sowohl serial als auch i2c jedoch nur kleine
Programme. Sobald der code umfangreicher wird verliere ich schnell den überblick.

Ich hoffe Ihr könnt helfen.

Danke für den Beitrag.

kuckuck
 

Anhänge

BAXL

Admin
Mitarbeiter
Hallo Kuckuck,

mir sieht das eher danach aus, dass Du im Code irgendwo etwas verändert hast, sofern der aus meinem Beispiel entstanden ist.

Edit:
Um welches Display handelt es sich bei Dir?
 

kuckuck

Mitglied
Hallo BAXL,
ich habe an den Code nichts geändert so wie er oben so steht er in einer leeren ino datei.
Der code soll nachher auf einem Arduino UNO mit dem Display 2004 mit i2c aufsatz laufen.
Der compiler zickt aber schon beim kompilieren rum.
Zur sicherheit habe ich den ganzen Code gerade noch einmal kopiert, eingefügt und der
fehler bleibt der gleiche. Der Unterschied sollte doch zwischen Arduino Uno und Ardoino
Nano nicht so gravierend sein oder?

Danke erst einmal für die Antwort

kuckuck
 

BAXL

Admin
Mitarbeiter
Ich sehe mal nach und poste meine Lib in den Ressourcen . Ich gebe Bescheid wenn ich den hochgeladen habe, im Moment habe ich keinen Zugriff auf den PC, passiert also erst im Laufe des Tages.
 

kuckuck

Mitglied
Hallo,
ich bin gerade rein ich habe einmal ein Screenshot von der verwendeten IDE und der Bibliothek gemacht falls das Wichtig währe.

Tut mir echt leid das nur ich das Problem habe.
 

Anhänge

kuckuck

Mitglied
Hallo BAXL,
es hat geklappt der Sketch wurde Kompiliert, jetzt muss ich nur noch schauen ob alles mit meinem Display läuft.
Zur info; die passende Display Bibliothek ist: LiquidCrystal_I2C.zip.
Ich habe mit absicht nicht den Link zu den libraries hier rein gelegt, weil ich nicht wusste ob es gewünscht ist.

Wenn alles geklappt hat dann melde ich mich, damit dieser thread geschlossen werden kann.
Ich Bitte um beschreibung wie ich den thread schliessen kann, damit ich das dann nachher erledigen kann.

Erst einmal Danke für die Bibliotheken.

kuckuck
 

BAXL

Admin
Mitarbeiter
Hi Kuckuck, Links sind hier in Ordnung :)
In meiner zip sind drei unterschiedliche Libs.
Das Thema lasse ich aber offen, falls noch weitere Mitglieder dazu Fragen oder Probleme haben. Das Thema ist ja von mir zu dem Zweck gestartet worden ;)
 

kuckuck

Mitglied
Hallo BAXL,
genau deshalb habe ich die passende Zip genannt. Hier nocheinmal die passende Datei aus dem Beitrag von 08:38 Uhr
heißt LiquidCrystal_I2C.zip ich habe sie hier noch einmal angehangen.
Ich bin aber leider immer noch nicht zum Testen gekommen keine Zeit gefunden. Ich melde mich.

kuckuck
 

Anhänge

dg0csg

Mitglied
Hallo,ich habe ei Problem die GPS gesteuerte Uhr läuft ausgezeichnet.
Nach Unterbrechung der Stromversorgung und wieder einschalten ist die Zeitanzeige um eine Stunde früher.
Nachdem ich den Arduino resette wird wieder die richtige Zeit angezeigt.
Wie kann dieses abgestellt werden?
 
Zuletzt bearbeitet:

BAXL

Admin
Mitarbeiter
Ok, die Sommerzeit wird nicht automatisch per GPS gesendet, das musst Du im Sketch selbst berücksichtigen. Guck mal in meine Erklärung
 
Zuletzt bearbeitet:
Top Bottom