2015
HOME

Rheinturmuhr all over the world - Zeitsteuerung
GPS-Uhrzeit
Die aWatch: arduino-TFT-Watch mit Satellitensynchronisation
aWatch - model Sweden
Rechtzeitig zum Launch der Apple-Watch: Die aWatch, eine GPS-Uhr mit Arduino und NEO-6M-Modul von eblox. Hier die schwedische Variante. Weiter unten: weitere WatchFaces.
Im Gegensatz zu DCF77 ist GPS weltweit verfügbar. Somit wäre es nicht mehr schwierig weltweit funktionierende Funkwecker anzubieten. Das Angebot ist jedoch mager.

Neben Positionsdaten senden die GPS-Satelliten auch die Weltzeit sehr genau in Richtung Erde. Die Empfänger werden kleiner und preiswerter, wodurch der Einsatz für eigene Projekte möglich wird. Hier soll eine Uhr - auch die Rheinturmuhr - per Satellit synchronisiert werden.

Ein CRIUS NEO-6 GPS-Modul enthält Antenne, Empfänger und vier Anschlüsse: Versorgungsspannung und Masse, sowie die beiden seriellen Leitungen TX/RX. Das Modul arbeitet mit 9600 Baud und sendet einmal in der Sekunde unaufgefordert GPS-Daten in Textform. Eine blaue LED ist die Poweranzeige, eine grüne LED blinkt im Sekundentakt, wenn mehr als 3 Satelliten empfangen werden und ein so genannter FIX vorherrscht.

Im Auslieferungszustand sind das 8 oder 9 Zeilen mit bis zu 80 Zeichen pro Zeile. Jede Zeile beginnt mit dem Zeichen '$' und endet mit einem Zeilenvorschub (13). Je nach Phrase nach dem '$'-Zeichen folgen verschiedene CSV-Werte (durch Kommata getrennte Werte). Das Datenformat ist unter "NMEA 0183" bekannt und alle Datensätze findet man hier. Das CRIUS NEO-6 GPS-Modul liefert im Auslieferungszustand die Sätze: GPRMC, GPVTG, GPGGA, GPGSA, GPGLL sowie GPGSV mit 3 oder 4 Zeilen. Die für dieses Projekt verwendeten Zeilen lauten zum Beispiel (geändert, falsche Prüfsumme):

$GPRMC,072158.00,A,5100.12345,N,00645.12345,E,0.048,,210415,,,D*74
$GPGGA,072158.00,5100.12345,N,00645.12345,E,2,08,1.29,69.4,M,46.5,M,,0000*6D
...

GPS-Modul
Im ersten Datensatz findet man die Zeit (UTC) im ersten Parameter 7:21:58, sowie das Datum 21.04.2015 im 9. Parameter. Der zweite Datensatz enthält ebenfalls die Zeit und die Position, aber auch den FIX (6. Parameter) als 2 und die Anzahl der Satelliten (7. Parameter) mit 08, weiterhin die Höhe in Metern usw. Jeder Datensatz bzw. Zeile wird mit einer zweistelligen hexadezimalen Prüfsumme der Daten abgeschlossen, die nach dem Zeichen '*' steht. Mit dem 'u-center' von ublox können unter Windows viele Änderungen am GPS-Modul vorgenommen werden.


Ziele Randbedingungen

Die Realisierung der Arduino-GPS-Uhr soll einigen Bedingungen unterliegen:

  • Keine Veränderung am GPS-Modul (Datensätze, Baudrate)
  • Automatische Sommer-/Winterzeit für eine Zeitzone
  • Darstellung auf einem 1.8 TFT-Display
  • Anzeige von Uhrzeit, Datum mit Wochentag und Anzahl der Satelliten
  • Keine Softserial-Routinen - Datenzufuhr über Pin 0 (RX)
  • Verwendung des Arduino UNO mit 32K/16MHz

Im Laufe des Projekts stellte sich heraus, dass der Arduino mit TFT-Display und den entsprechenden Bibliotheken in Zeit- und Speichernot kam und die Datenmenge eigentlich reduziert werden sollte. Bei 9600 bps ergeben sich bei maximal 9x80 Zeichen 720 Bytes pro Sekunde. Mit Start- und Stoppbit sind das 7200 bps. Falls kein Denkfehler vorliegt, bliebe dann noch 250 ms zur Ansteuerung des TFT-Displays und für andere Dinge. Durch die asynchrone serielle Datenübertragung und dem 64 Byte großen Empfangspuffer im Arduino ist es nicht ganz einfach die Randbedingungen einzuhalten. Mit der Software 'u-center' von ublox können unter Windows viele Änderungen am GPS-Modul vorgenommen werden, was für noch zeitkritischere Anwendungen wohl unumgänglich erscheint. Nach verschiedenen gescheiterten Ansätzen konnten die gesetzten Bedingungen doch noch gerade eingehalten werden.


GPS-Routinen Zeile, Prüfsumme, Parameter

Die Verwendung einer GPS-Bibliothek (TinyGPS) wurde verworfen, da die Ziele hier so nicht erreicht werden konnten. Um die seriellen GPS-Daten zu verarbeiten wird eine Hand voll Routinen benötigt:

  • getline - Empfang einer kompletten Zeile über die serielle Schnittstelle
  • checksum - Überprüfung der Integrität der Datenzeile
  • getparam - Auslesen des gewünschten Parameters
//----------------------------------------------
//--------------GPS-ROUTINEN--------------------
//----------------------------------------------
//String Line="";

boolean getline(char *phrase) //HARD POLLING
{char s[100];byte b,n;unsigned long t=millis();
 for(int i=0; i<sizeof(s);i++)s[i]=0;
 Line="";
 do 
 {b=Serial.read();
  if(millis()>(t+100))return false;
 }while(b!='$');
 s[0]=b;
 n=Serial.readBytesUntil('\n', &s[1], 90);
 s[n]=0;
 if(strstr(s,phrase)==s)
 {for(int i=0;i<n;i++)Line+=s[i];
  return true; 
 }
 return false;
}

#define hex(i)  ((i<=9) ? ('0'+i): ('A'- 10 + i))

boolean checksum()
{byte b=0;int e;
 e=Line.indexOf('*');
 if(e>10)
 {for(int i=1;i<e;i++)b^=Line[i]; 
  if((hex((b&15))==Line[e+2]) && 
     (hex(b/16)==Line[e+1]))return true;
 }
 return false;
}

String getparam(int ix)
{int c,cc=0;
 if(checksum())
 {do
  {c=Line.indexOf(',',cc);
   if(c>=0)cc=c+1; else break;
  }while (--ix);
  return(Line.substring(c+1,Line.indexOf(',',c+1))); 
 }
 return "xx"; //debug
}

Die Routine getline löscht zunächst alle Zeichen der lokalen Zeichenkette und setzt den globalen String Line auf "". Nun wird maximal 0,1 s auf ein Zeilenanfang ($) gewartet und bei Erfolg die ganze Zeile bis zum Zeilenende '\n' eingelesen (readBytesUntil) und das Resultat in die globale Variable Line kopiert.

Checksum überprüft, ob die Zeile fehlerfrei vorliegt. Dazu werden alle Zeichen zwischen dem Anfang '$' und dem Ende '*' exklusiv-oder addiert. Das Ergebnis sollte dann mit den beiden Zeichen nach dem '*'-Zeichen übereinstimmen.

Mit getparam wird schließlich der Parameter ix als Zeichenkette geliefert. Das Datum erhält man demnach im 9. Parameter der Zeile, wenn sie mit $GPRMC beginnt.


Testlauf 'Ohne Alles'

Der folgende Sketch dient dazu die obigen Routinen zu überprüfen und ohne jedes Beiwerk die gewünschten Daten anzuzeigen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
String Line = ""; // a string to hold incoming data

void setup()  
{Line.reserve(100);
 Serial.begin(9600);
 Serial.println("u-blox NEO-6M GPS at PIN 0 ...");
}

void loop() 
{if(getline("$GPGGA"))GGA();
 if(getline("$GPRMC"))RMC();
 while(Serial.available())Serial.read();
}//loop

void GGA()//FIX SAT ect.
{Serial.print  (getparam(7));
 Serial.print(" Sat:");
 Serial.println(getparam(6));
}//GPGGA

void RMC()//TIME DATE 
{Serial.print(getparam(1));
 Serial.print("  ");
 Serial.print(getparam(9));
 Serial.print("  ");
}//GPRMC

//----------------------------------------------
//--------------GPS-ROUTINEN--------------------
//----------------------------------------------

Nach der Zeile 27 müssen nun die drei weiter oben angegebenen GPS-Routinen eingefügt werden. Bei der Übertragung des Sketches muss der PIN 0 vom Arduino unbelegt sein, sonst erscheint ein Fehler: avrdude: stk500_getsync(): not in sync: resp=0x00. Danach wird die TX-Leitung vom GPS-Modul an PIN 0 angeschlossen. Im SerialMonitor könnten folgende Zeilen erscheinen:

u-blox NEO-6M GPS at PIN 0 ...
173507.00 140415 07 Sat:1
173508.00 140415 07 Sat:1
173509.00 140415 07 Sat:1
173510.00 140415 07 Sat:1
....

Solche Ergebnisse sind auch mit Bibliotheken oder SerialEvent-Routinen möglich. Hier sollen aber genau die obigen Routinen getestet werden.


Testlauf mit Zeitzone und Sommer-/Winterzeit

Um die entsprechende Lokalzeit und die Sommer-/und Winterzeit zu erhalten, kommen nun die Bibliotheken "Time" und "Timezone" dazu.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//------- DATE TIME --------------------------
#include <Time.h> 
#include <Timezone.h>    
TimeChangeRule CEST = {"", Last, Sun, Mar, 2, 120};     
TimeChangeRule CET = {"", Last, Sun, Oct, 3, 60}; 
Timezone CE(CEST, CET);
TimeChangeRule *tcr; 
char *Tag[7] = {"SONNTAG","MONTAG","DIENSTAG","MITTWOCH",
                "DONNERSTAG","FREITAG","SONNABEND"};

String Line = "";    // a string to hold incoming data

void setup()  
{Line.reserve(100);
 Serial.begin(9600);
}

void loop() 
{static int os=-1;

 if(!(os%3))if(getline("$GPGGA"))GGA();
 if(!(os%4))if(getline("$GPRMC"))RMC();
 SerialClear(); 

 if(second()!=os)//every second
 {char s[80];
  sprintf(s,"%s der %02d.%02d.%04d um %02d:%02d:%02d",
  Tag[weekday()-1],day(),month(),year(),
  hour(),minute(),second());
  Serial.println(s);
  os=second();
 }//second
}//loop

void GGA()//FIX SAT ect.
{Serial.print (getparam(7));Serial.println(" Sat");
}//GPGGA

void RMC()//TIME DATE 
{setTime(getparam(1).substring(0,0+2).toInt(),
         getparam(1).substring(2,2+2).toInt(),
         getparam(1).substring(4,4+2).toInt(),
         getparam(9).substring(0,0+2).toInt(),
         getparam(9).substring(2,2+2).toInt(),
         getparam(9).substring(4,4+2).toInt());
 time_t cet=CE.toLocal(now(),&tcr);
 setTime(cet);
 Serial.println("sync");
}//GPRMC

void SerialClear()
{while(Serial.available())Serial.read();
}

//----------------------------------------------
//--------------GPS-ROUTINEN--------------------
//----------------------------------------------

Nach Zeile 54 müssen wieder die GPS-Routinen eingefügt werden. In der Hauptschleife wird nun jede 3. bzw. 4. Sekunde versucht GPS-Zeilen auszuwerten. Im SerialMonitor erscheinen zunächst die Daten der ungestellten Uhr, die laut Unix im Jahr 1970 beginnt. Nach einer Weile wird die korrekte Sommerzeit für Mitteleuropa dargestellt. Ein "sync" sagt, wann die GPS-Uhr gestellt wurde. Beispiel:

DONNERSTAG der 01.01.1970 um 00:00:13
DONNERSTAG der 01.01.1970 um 00:00:14
DONNERSTAG der 01.01.1970 um 00:00:15
06 Sat
DONNERSTAG der 01.01.1970 um 00:00:16
sync
DIENSTAG der 14.04.2015 um 20:04:44
sync
DIENSTAG der 14.04.2015 um 20:04:45
05 Sat


Uhrenkabinett Watchfaces
Studio
Sweden
Canvas
TowerArt
Wenn alles funktioniert, kann nun eine Uhr mit Zifferblatt oder Ähnlichem auf einem TFT-Display dargestellt werden. Im Laufe dieses Projekts entstanden vier Watchfaces, die alle in vier beliebigen Orientierungen (Porträt/Landschaft) funktionieren. Sie wurden für ein 1.8" TFT-Display ST7735 kodiert mit einer Auflösung von 128x160 Pixel. Die Pegel an PIN A0 und PIN A1 legen beim Start die Orientierung fest. Quellenangaben zur Analoguhr unter Zeitserver-Referenz.

Die Anschlussbelegung des Displays ist im Projekt Rheinturmuhr und TFT erläutert. Alle dortigen Hinweise gelten auch hier. Die vier Sketche funktionieren nur unverändert unter folgenden Bedingungen (IDE 1.0):

  • SainSmart 1.8-Zoll-TFT-Display - schnelle Ansteuerung
  • Arduino Uno
  • CRIUS NEO-6 GPS-Modul

Studio Sweden Canvas TowerArt

Ticks und Pips Zeitzeichen - Tonsignal
Alle Uhrenvarianten verfügen über einen Sekunden-Tick. Das Tonsignal der Frequenz 1000 Hz während einer Millisekunde ist an PIN 3 (TICKPIN) für einen Piezo-Beeper verfügbar. Zur halben und zur vollen Stunde erfolgt ein Tonsignal, wie man es aus dem Radio kannte oder kennt. In einem 'aufwendigen Prozess' wurden die Pip-Zeiten des Deutschlandfunks ausgemessen und in die Uhren integriert. Einziger Unterschied ist, dass diese Uhren 5x kurz und 1x lang zur vollen Stunde geben, während zurzeit beim DLF nur 3x kurz ertönt. Zu diesem Thema findet man mit den Suchbegriffen "radioforen Uhrzeit Tonsignal" überaus unterhaltsame 'Diskussionen'.

HTML und der Canvas
Rheinturmuhr und TFT
Rheinturmuhr und DCF
Netzzeit/Timeserver

Weitere Software
.
Startseite Bücher Software Digital RTV Musik Kontakt

Für Inhalt und weitere Verzweigung externer Links sind die Betreiber der dortigen Seiten verantwortlich - H.-J. Berndt