Arduino -- Elektronik-Experimentierkasten -- Lectron Lehrsytem -- Jugendarbeit -- Elektronik -- Amateurfunk -- Notfunk -- Shop

Arduino-UTC-Uhr, mit Anbindung an NTP-Zeitserver

Als "Fingerübung" habe ich eine einfache UTC-Uhr programmiert. Die Uhr holt sich die aktuelle Zeit von einem NTP-Server und zeigt sie auf einem LCD-Display an.

Variante I  [ LCD-Anzeige 2 x 16 Zeichen ]




Variante II [ LCD-Anzeige LCD4884 ] 



Auf den Duemilanove wird das Ethernet-Shield gesteckt. Über die Ethernet-Verbindung wird die aktuelle Zeit von einem NTP-Server geholt. Die Zeit wird dann auf dem LCD-Shield  angezeigt.

Als Ausgangsbasis habe ich das Sketch "UdpNtpClient" verwendet und es um die Funktionen zur Ansteuerung der LCD-Anzeige ergänzt. Die darin enthaltende Ausgabe der Uhrzeit auf der seriellen Schnittstelle ist dabei entfallen.

Diese Beispiel wurden "schnell runterprogrammiert". Sie erheben keinesfalls den Anspruch als Beispiel für gute Programmierung zu dienen ;-) Ich möchte damit einfach zeigen das man durch "zusammenstecken" von Arduino-Hardware und Software mit wenig Aufwand was Nützliches zusammenbauen kann. Vielleicht sind sie auch die Basis für die eine oder andere Weiterentwicklung.

Hardware:


Anmerkungen:


Das hier verwendete LCD-Shield 16x2 kann nicht richtig auf das Ethernet-Shield gesteckt werden. Die Netzwerkbuchse des Ethernet-Shields ist zu hoch. Entweder man steckt die Anzeigeplatine "schräg" drauf oder verwendet Stift-/Buchsenleisten als Abstandverlängerung.

Als alternative Anzeige habe ich in der Variante II das LCD-Shield LCD4884 von DFROBOT verwendet. Diese Shield kann direkt auf das Ethernet-Shield ohne mechanische Probleme aufgesteckt werden.

Verbesserungsmäglichkeiten:


Die aktuelle Zeit wird hier mehrfach pro Sekunde beim Zeitserver abgerufen. Besser wäre es eine "freilaufende" Uhr auf dem Arduino als Softwarelösung einzubauen und diese dann in deutlich längeren Zeitabständen mit dem NTP-Zeitserver abzugleichen.

Erweiterung mit Datumsanzeige, Anzeige der lokalen Zeit mit Umschaltung Sommer / Winterzeit

Durch die Verwendung einer anderen LCD-Anzeige die besser auf das Ethernet-Shield gesteckt wird kann man das mechanische Problem mit dem 16x2-LCD-Shield umgehen.


Code: (Variante I - LCD 16x2)
/*
 Udp NTP Client für LCD 16x2

 Get the time from a Network Time Protocol (NTP) time server
 Demonstrates use of UDP sendPacket and ReceivePacket
 For more on NTP time servers and the messages needed to communicate with them,
 see http://en.wikipedia.org/wiki/Network_Time_Protocol

 created 4 Sep 2010
 by Michael Margolis
 modified 17 Sep 2010
 by Tom Igoe
 modified 23 Oct 2010
 by Juergen Mayer, DL8MA, Grossheppach

 This code is in the public domain.

 */

#include <LCD4Bit_mod.h>
LCD4Bit_mod lcd = LCD4Bit_mod(2);

#include <SPI.h>        
#include <Ethernet.h>
#include <Udp.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,178,15 };

char string[ 17 ] = { "" };

int stunde, minute, sekunde;

unsigned int localPort = 8888;      // local port to listen for UDP packets

byte timeServer[] = { 192, 43, 244, 18}; // time.nist.gov NTP server

const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

void setup()
{
  // start Ethernet and UDP
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);

  lcd.init();
  lcd.clear();
  lcd.printIn("NTP-Uhr");
}

void loop()
{
  sendNTPpacket(timeServer); // send an NTP packet to a time server

    // wait to see if a reply is available
  delay(1000); 
  if ( Udp.available() ) { 
    Udp.readPacket(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord; 

    // now convert NTP time into everyday time:
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;    
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears; 

    stunde = (epoch % 86400L) / 3600;         
    minute = (epoch % 3600) / 60;
    sekunde = (epoch % 60);

    sprintf( string, "%02d:%02d:%02d UTC", stunde, minute, sekunde );

    lcd.cursorTo(2, 0);  //line=2, x=0
    lcd.printIn( string );

  }
  // wait ten seconds before asking for the time again
  delay( 333 );
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(byte *address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:           
  Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE,  address, 123); //NTP requests are to port 123
}
 

Code: (Variante I - LCD4884)

/*
 Udp NTP Client für LCD4884 (DFROBOT)

 Get the time from a Network Time Protocol (NTP) time server
 Demonstrates use of UDP sendPacket and ReceivePacket
 For more on NTP time servers and the messages needed to communicate with them,
 see http://en.wikipedia.org/wiki/Network_Time_Protocol

 created 4 Sep 2010
 by Michael Margolis
 modified 17 Sep 2010
 by Tom Igoe
 modified 23 Oct 2010
 by Juergen Mayer, Grossheppach

 This code is in the public domain.

 */

#include <LCD4884.h>
#include <SPI.h>        
#include <Ethernet.h>
#include <Udp.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,178,15 };

char string[ 17 ] = { "" };

int stunde, minute, sekunde;

unsigned int localPort = 8888;      // local port to listen for UDP packets

byte timeServer[] = { 192, 43, 244, 18}; // time.nist.gov NTP server

const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

void setup()
{
  // start Ethernet and UDP
  Ethernet.begin(mac,ip);
  Udp.begin(localPort);

  lcd.LCD_init();
  lcd.LCD_clear();
  lcd.LCD_write_string( 0, 0, "NTP-UTC-Uhr", 0 );
}

void loop()
{
  sendNTPpacket(timeServer); // send an NTP packet to a time server

    // wait to see if a reply is available
  delay(1000); 
  if ( Udp.available() ) { 
    Udp.readPacket(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord; 

    // now convert NTP time into everyday time:
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;    
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears; 

    stunde = (epoch % 86400L) / 3600;         
    minute = (epoch % 3600) / 60;
    sekunde = (epoch % 60);

    sprintf( string, "%02d:%02d:%02d UTC", stunde, minute, sekunde );

    lcd.LCD_write_string( 0, 2, string, 0 );

  }
  // wait ten seconds before asking for the time again
  delay( 333 );
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(byte *address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:           
  Udp.sendPacket( packetBuffer,NTP_PACKET_SIZE,  address, 123); //NTP requests are to port 123
}


23.10.2010

Dipl.-Ing. (FH) Jürgen Mayer, DL8MA - Weinstadt-Grossheppach
Impressum

zurück zu www.DL8MA.de