2013/12/29

Notes About PCF8574 I2C LCD Using LiquidCrystal_I2C.h


IC Manufacturer
NXP - PCF8574P, PCF8574AP
TI - PCF8574N

Library
LiquidCrystal_I2C.h by Francisco Malpartida download from BitBucket Repository

Address
I2C address of PC8574 (all three address inputs grounded) = 0x20
I2C address of PC8574A (all three address inputs grounded) = 0x38

Pins Mapping (16 Pins 8574) (Schematic)
PCF8574.Pin1 -> GND
PCF8574.Pin2 -> GND
PCF8574.Pin3 -> GND
PCF8574.Pin4 (Port0) -> LCD.Pin11 (DB4)
PCF8574.Pin5 (Port1) -> LCD.Pin12 (DB5)
PCF8574.Pin6 (Port2) -> LCD.Pin13 (DB6)
PCF8574.Pin7 (Port3) -> LCD.Pin14 (DB7)
PCF8574.Pin9 (Port4) -> LCD.Pin4 (RS)
PCF8574.Pin10 (Port5) -> LCD.Pin5 (WR)
PCF8574.Pin11 (Port6) -> LCD.Pin6 (EN)
PCF8574.Pin14 (SCL) -> UNO.A5
PCF8574.Pin15 (SDA) -> UNO.A4
PCF8574.Pin16 (VDD) -> UNO.5V
PCF8574.Pin8 (VSS) -> UNO.GND

LCD Initialization
#include <Wire.h> // comes with Arduino IDE
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x20)
lcd.begin(16,2) // in Setup()

Variations (Yourduino)
   Marked "YwRobot Arduino LCM1602 IIC V1" -> lcd.begin(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE) // also SainSmart LCD2004, DFRobot (DZRMO)
   Marked "Arduino-IIC-LCD GY-LCD-V1" -> lcd.begin(0x20, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE)
   Marked "LCD1602 IIC A0 A1 A2" -> lcd.begin(0x20, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE)

Example Code (Yourduino)
/* YourDuino.com Example Software Sketch
 16 character 2 line I2C Display
 Backpack Interface labelled "YwRobot Arduino LCM1602 IIC V1"
 terry@yourduino.com */

/*-----( Import needed libraries )-----*/
#include <Wire.h>  // Comes with Arduino IDE
// Get the LCD I2C Library here: 
// https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads
// Move any other LCD libraries to another folder or delete them
// See Library "Docs" folder for possible commands etc.
#include <LiquidCrystal_I2C.h>

/*-----( Declare Constants )-----*/
/*-----( Declare objects )-----*/
// set the LCD address to 0x27 for a 20 chars 4 line display
// Set the pins on the I2C chip used for LCD connections:
//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x20);  // Set the LCD I2C address

/*-----( Declare Variables )-----*/
//NONE

void setup()   /*----( SETUP: RUNS ONCE )----*/
{
  Serial.begin(9600);  // Used to type in characters

  lcd.begin(16,2);   // initialize the lcd for 16 chars 2 lines, turn on backlight

// ------- Quick 3 blinks of backlight  -------------
  for(int i = 0; i< 3; i++)
  {
    lcd.backlight();
    delay(250);
    lcd.noBacklight();
    delay(250);
  }
  lcd.backlight(); // finish with backlight on  

//-------- Write characters on the display ------------------
// NOTE: Cursor Position: (CHAR, LINE) start at 0  
  lcd.setCursor(0,0); //Start at character 4 on line 0
  lcd.print("Hello, world!");
  delay(1000);
  lcd.setCursor(0,1);
  lcd.print("HI!YourDuino.com");
  delay(8000);  

// Wait and then tell user they can start the Serial Monitor and type in characters to
// Display. (Set Serial Monitor option to "No Line Ending")
  lcd.clear();
  lcd.setCursor(0,0); //Start at character 0 on line 0
  lcd.print("Use Serial Mon");
  lcd.setCursor(0,1);
  lcd.print("Type to display");  


}/*--(end setup )---*/


void loop()   /*----( LOOP: RUNS CONSTANTLY )----*/
{
  {
    // when characters arrive over the serial port...
    if (Serial.available()) {
      // wait a bit for the entire message to arrive
      delay(100);
      // clear the screen
      lcd.clear();
      // read all the available characters
      while (Serial.available() > 0) {
        // display each character to the LCD
        lcd.write(Serial.read());
      }
    }
  }

}/* --(end main loop )-- */


/* ( THE END ) */

2013/10/11

NTP Server Synchronized DS1307 RTC Analog Datalogger With File Download Server

The sketch is an extension based on NTP Synchronized Analog Datalogger post on Aug. 21, 2013 by adding web interface file download server function. All datalogger files are saved in the root directory of the microSD card. With the network infrastructure settled and router proper configured, the datalogger files on the microSD can be download by any device with web browser built in from anywhere.

Requirements
1. Arduino Mega2560 R3
2. W5100 Ethernet Shield
3. RTC Sensor Shield (designed to stack on Arduino Uno cannot stack on Mega2560)
4. LM35 Temperature Sensor Breakout connect to RTC Sensor Shield A3

Connection


File Download Server Screen




Sketch

/*
 * NTPSynchronizedRTCAnalogDataLoggerWithFileDownloadServer.ino
 *
 * This sketch calls alarm functions at 7:30 am and at 7:30 pm (19:30)
 * and sync DS1307, Arduino system time 
 *
 * At startup the system time read from DS1307, then both sync with NTP 
 *
 * Modified by Befun Hung on Oct. 11, 2013 based on NTPSyncronizedAnalogDataLogger.ino
 * by adding web interface file download server function, 
 * so that files can be downloaded from the remote site the data logger installed
 */

#include <Wire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <TimeAlarms.h>
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SD.h>
#define analogSensorStart 3 // sensor connect to A3
#define analogSensorEnd 3 // sensor connect to A3

// Enter a MAC address for your controller bellow.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
byte ip[] = {192, 168, 1, 177};
unsigned int localPort = 8888; // local port to listen for UDP packets
IPAddress timeServer(140, 112, 2, 188); // ntp2.ntu.edu.tw 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
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
// timeZoneOffset = (Time Zone) * 3600L eg. (+8) * 3600L = 28800L for Taipei, Taiwan
const long timeZoneOffset = 28800L; 
// sync to NTP server every "ntpSyncTime" seconds, set to 1 hour or more to be reasonable
unsigned long ntpSyncTime = 21600;
// adjust the sync latency with computer NTP client in seconds 
unsigned int syncLatency = 2;

// sd card variables
File file; // test file
const uint8_t SD_CS = 4; // SD chip select
String file_name = ""; // file name should not prefix with "prefix_word"
char fn[] = "MMDDHHMM.CSV";
int i=0;
int displayAtSecond = 0;
time_t t;

// Initialize the Ethernet server library
// with the IP address and port you want to use 
// (port 80 is default for HTTP):
EthernetServer server(80);

// for list files
File root;
File webFile;

//------------------------------------------------------------------------------
// call back for file timestamps 
void dateTime(uint16_t* date, uint16_t* time) {
  time_t timeStamp = now();

  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(year(timeStamp), month(timeStamp), day(timeStamp));

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(hour(timeStamp), minute(timeStamp), second(timeStamp));

//------------------------------------------------------------------------------

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  setSyncProvider(RTC.get); // the function to get the time from the RTC
  if (timeStatus() != timeSet)
    Serial.println("Unable to sync with the RTC");
  else
    Serial.println("RTC has set the system time");
  // set date time callback function
  SdFile::dateTimeCallback(dateTime);  
  // display RTC time
  Serial.print(year());
  Serial.print('-');
  Serial.print(month());
  Serial.print('-');
  Serial.print(day());
  Serial.print(' ');
  Serial.print(hour());
  Serial.print(':');
  Serial.print(minute());
  Serial.print(':');
  Serial.print(second());
  Serial.println(" --- RTC Time");
  // create the alarms 
  Alarm.alarmRepeat(7,30,0, ntpSyncDS1307);  // 7:30am every day
  Alarm.alarmRepeat(19,30,0, ntpSyncDS1307);  // 7:30pm every day 
  
  // start Ethernet and UDP
  Ethernet.begin(mac, ip);
  Udp.begin(localPort); 
  ntpSyncDS1307();
  
  // process file name string
  t = now();
  if (month(t) < 10) {
      file_name = String(file_name + '0' + String(month(t), DEC));
    }
    else {
    file_name = String(file_name +String(month(t), DEC));
    }
  if (day(t) < 10) {
      file_name = String(file_name + '0' + String(day(t), DEC));
    }
    else {
    file_name = String(file_name +String(day(t), DEC));
    }
  if (hour(t) < 10) {
      file_name = String(file_name + '0' + String(hour(t), DEC));
    }
    else {
    file_name = String(file_name +String(hour(t), DEC));
    }
  if (minute(t) < 10) {
      file_name = String(file_name + '0' + String(minute(t), DEC));
    }
    else {
    file_name = String(file_name +String(minute(t), DEC));
    }

  file_name = String(file_name + ".CSV");

  for (i=0;i<=file_name.length();i++) {
    fn[i] = file_name.charAt(i);
  }
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  if (!SD.begin(SD_CS)) {
    Serial.println("SD failed");
    // while(1);
  }

  // start the web server:
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
  
  root = SD.open("/");
}

void loop(){
  webServer();
  t = now();
  if (displayAtSecond != second(t)) {
    analogSensorDataLogger();
    displayAtSecond = second(t);
  }
  Alarm.delay(100); // wait 1/10 second between cycles
}

// functions to be called when an alarm triggers:
void ntpSyncDS1307() {
  sendNTPpacket(timeServer); // send an NTP packet to a time server
  // wait to see if a replay is available
  delay(1000);
  if (Udp.parsePacket()) {
    // We've received a packet, read the data from it
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
    // the timstamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, extract 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 = 2208988800L;
    // substract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears + timeZoneOffset + syncLatency;
    setTime(epoch);
    RTC.set(epoch);
    // output time and "Sync OK" message every sync 
    Serial.print(year());
    Serial.print('-');
    Serial.print(month());
    Serial.print('-');
    Serial.print(day());
    Serial.print(' ');
    Serial.print(hour());
    Serial.print(':');
    Serial.print(minute());
    Serial.print(':');
    Serial.print(second());
    Serial.print(' ');
    Serial.println("Sync OK");
  }
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& 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 clodk
  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.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

void analogSensorDataLogger() { 
  String data_string = "";
  data_string = String(year(t), DEC);
  data_string += "/";
  if (month(t) < 10) {
      data_string = String(data_string + '0' + String(month(t), DEC));
    }
    else {
    data_string = String(data_string +String(month(t), DEC));
    }
  data_string += "/";
  if (day(t) < 10) {
      data_string = String(data_string + '0' + String(day(t), DEC));
    }
    else {
    data_string = String(data_string +String(day(t), DEC));
    }
  data_string += " ";
  if (hour(t) < 10) {
      data_string = String(data_string + '0' + String(hour(t), DEC));
    }
    else {
    data_string = String(data_string +String(hour(t), DEC));
    }
  data_string += ":";
  if (minute(t) < 10) {
      data_string = String(data_string + '0' + String(minute(t), DEC));
    }
    else {
    data_string = String(data_string +String(minute(t), DEC));
    }
  data_string += ":";  
  if (second(t) < 10) {
      data_string = String(data_string + '0' + String(second(t), DEC));
    }
    else {
    data_string = String(data_string +String(second(t), DEC));
    }
  data_string += ",";

  //read sensor value from A0-A3 and append to the string
  for (int analogPin = analogSensorStart; analogPin <= analogSensorEnd; analogPin++)
    {
    int sensor = analogRead(analogPin);
    data_string += String(sensor);
    if (analogPin < analogSensorEnd) {
      data_string += ",";
      }
    }
  
  file = SD.open(fn, FILE_WRITE);
  if (file) {
    file.println(data_string);
    file.close();
    Serial.println(data_string);
    }
    else {
      Serial.print("error opening ");
      Serial.println(fn);
    }
}

#define BUFSIZ 100
void webServer()
{
  char clientline[BUFSIZ];
  int index = 0;
  
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;
    
    // reset the input buffer
    index = 0;
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        
        // If it isn't a new line, add the character to the buffer
        if (c != '\n' && c != '\r') {
          clientline[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZ) 
            index = BUFSIZ -1;
          
          // continue to read more data!
          continue;
        }
        
        // got a \n or \r new line, which means the string is done
        clientline[index] = 0;
        
        // Print it out for debugging
        Serial.print("clientline: ");
        Serial.println(clientline);
        
        // Look for substring such as a request to get the root file
        if (strstr(clientline, "GET / ") != 0) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          
          // print all the files, use a helper to keep it clean
          client.println("<h2>Files:</h2>");
          int numTabs;
          root.rewindDirectory();
          while (true) {
            File entry = root.openNextFile();
            if (! entry) {
              // no more files
              client.println("** no more files **");
              client.println("<br />");
              break;
            }
            client.print("<a href=\"");
            client.print(entry.name());
            client.print("\">");
            client.print(entry.name());
            client.print("</a>");
            client.print(" ");
            client.print(entry.size(), DEC);
            client.print("<br />");
            entry.close();
          }  
        } else if (strstr(clientline, "GET /") != 0) {
          // this time no space after the /, so a sub-file!
          char *filename;
          
          filename = clientline + 5; // look after the "GET /" (5 chars)
          // a little trick, look for the " HTTP/1.1" string and 
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;
          
          // print the file we want
          Serial.print("filename: ");
          Serial.print(filename);
          Serial.println(" Opened!");
                    
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/plain");
          client.println();
          
          webFile = SD.open(filename);
          if (webFile) {
            while (webFile.available()) {
              client.write(webFile.read());
            }
            webFile.close();
          }
        } else {
          // everything else is a 404
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>File Not Found!</h2>");
        }
        break;
      }
    }
    // give the web browser time to receive the data
    delay(1);
    client.stop();
  }
}

2013/08/21

NTP Server Synchronized DS1307 RTC Analog Data Logger


This sketch combine analog sensor data logger on Aug. 15, 2013 and NTP server synchronized DS1307 RTC using TimeAlarm library on Aug. 17, 2013.

The shetch use fixed IP, so DHCP server is not needed in the local area network.

Because the RTC is synchronized with NTP server, so the data logger keeps the time within 5 seconds. The NTP sync may fail sometimes depending on the network conditions.

Sketch

/*
 * NTPSynchronizedAnalogDataLogger.ino
 *
 * This sketch calls alarm functions at 7:30 am and at 7:30 pm (19:30)
 * and sync DS1307, Arduino system time 
 *
 * At startup the system time read from DS1307, then both sync with NTP 
 */

#include <Wire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <TimeAlarms.h>
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SD.h>
#define analogSensorStart 3 // sensor connect to A3
#define analogSensorEnd 3 // sensor connect to A3

// Enter a MAC address for your controller bellow.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
byte ip[] = {192, 168, 1, 177};
unsigned int localPort = 8888; // local port to listen for UDP packets
IPAddress timeServer(140, 112, 2, 188); // ntp2.ntu.edu.tw 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
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
// timeZoneOffset = (Time Zone) * 3600L eg. (+8) * 3600L = 28800L for Taipei, Taiwan
const long timeZoneOffset = 28800L; 
// sync to NTP server every "ntpSyncTime" seconds, set to 1 hour or more to be reasonable
unsigned long ntpSyncTime = 21600;
// adjust the sync latency with computer NTP client in seconds 
unsigned int syncLatency = 2;

// sd card variables
File file; // test file
const uint8_t SD_CS = 4; // SD chip select
String file_name = ""; // file name should not prefix with "prefix_word"
char fn[] = "MMDDHHMM.CSV";
int i=0;
int displayAtSecond = 0;
time_t t;

//------------------------------------------------------------------------------
// call back for file timestamps 
void dateTime(uint16_t* date, uint16_t* time) {
  time_t timeStamp = now();

  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(year(timeStamp), month(timeStamp), day(timeStamp));

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(hour(timeStamp), minute(timeStamp), second(timeStamp));

//------------------------------------------------------------------------------

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  setSyncProvider(RTC.get); // the function to get the time from the RTC
  if (timeStatus() != timeSet)
    Serial.println("Unable to sync with the RTC");
  else
    Serial.println("RTC has set the system time");
  // set date time callback function
  SdFile::dateTimeCallback(dateTime);  
  // display RTC time
  Serial.print(year());
  Serial.print('-');
  Serial.print(month());
  Serial.print('-');
  Serial.print(day());
  Serial.print(' ');
  Serial.print(hour());
  Serial.print(':');
  Serial.print(minute());
  Serial.print(':');
  Serial.print(second());
  Serial.println(" --- RTC Time");
  // create the alarms 
  Alarm.alarmRepeat(7,30,0, ntpSyncDS1307);  // 7:30am every day
  Alarm.alarmRepeat(19,30,0, ntpSyncDS1307);  // 7:30pm every day 
  
  // start Ethernet and UDP
  Ethernet.begin(mac, ip);
  Udp.begin(localPort); 
  // Serial.println("connect to ntp server");
  ntpSyncDS1307();
  
  // process file name string
  t = now();
  if (month(t) < 10) {
      file_name = String(file_name + '0' + String(month(t), DEC));
    }
    else {
    file_name = String(file_name +String(month(t), DEC));
    }
  if (day(t) < 10) {
      file_name = String(file_name + '0' + String(day(t), DEC));
    }
    else {
    file_name = String(file_name +String(day(t), DEC));
    }
  if (hour(t) < 10) {
      file_name = String(file_name + '0' + String(hour(t), DEC));
    }
    else {
    file_name = String(file_name +String(hour(t), DEC));
    }
  if (minute(t) < 10) {
      file_name = String(file_name + '0' + String(minute(t), DEC));
    }
    else {
    file_name = String(file_name +String(minute(t), DEC));
    }

  file_name = String(file_name + ".CSV");

  for (i=0;i<=file_name.length();i++) {
    fn[i] = file_name.charAt(i);
  }
  Serial.print("File Name: ");
  Serial.println(fn);
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  if (!SD.begin(SD_CS)) {
    Serial.println("SD failed");
    // while(1);
  }
}

void loop(){
  t = now();
  if (displayAtSecond != second(t)) {
    analogSensorDataLogger();
    displayAtSecond = second(t);
  }
  Alarm.delay(100); // wait 1/10 second between cycles
}

// functions to be called when an alarm triggers:
void ntpSyncDS1307() {
  sendNTPpacket(timeServer); // send an NTP packet to a time server
  // wait to see if a replay is available
  delay(1000);
  if (Udp.parsePacket()) {
    // We've received a packet, read the data from it
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
    // the timstamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, extract 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 = 2208988800L;
    // substract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears + timeZoneOffset + syncLatency;
    setTime(epoch);
    RTC.set(epoch);
    // output time and "Sync OK" message every sync 
    Serial.print(year());
    Serial.print('-');
    Serial.print(month());
    Serial.print('-');
    Serial.print(day());
    Serial.print(' ');
    Serial.print(hour());
    Serial.print(':');
    Serial.print(minute());
    Serial.print(':');
    Serial.print(second());
    Serial.print(' ');
    Serial.println("Sync OK");
  }
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& 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 clodk
  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.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

void analogSensorDataLogger() { 
  String data_string = "";
  data_string = String(year(t), DEC);
  data_string += "/";
  if (month(t) < 10) {
      data_string = String(data_string + '0' + String(month(t), DEC));
    }
    else {
    data_string = String(data_string +String(month(t), DEC));
    }
  data_string += "/";
  if (day(t) < 10) {
      data_string = String(data_string + '0' + String(day(t), DEC));
    }
    else {
    data_string = String(data_string +String(day(t), DEC));
    }
  data_string += " ";
  if (hour(t) < 10) {
      data_string = String(data_string + '0' + String(hour(t), DEC));
    }
    else {
    data_string = String(data_string +String(hour(t), DEC));
    }
  data_string += ":";
  if (minute(t) < 10) {
      data_string = String(data_string + '0' + String(minute(t), DEC));
    }
    else {
    data_string = String(data_string +String(minute(t), DEC));
    }
  data_string += ":";  
  if (second(t) < 10) {
      data_string = String(data_string + '0' + String(second(t), DEC));
    }
    else {
    data_string = String(data_string +String(second(t), DEC));
    }
  data_string += ",";

  //read sensor value from A0-A3 and append to the string
  for (int analogPin = analogSensorStart; analogPin <= analogSensorEnd; analogPin++)
    {
    int sensor = analogRead(analogPin);
    data_string += String(sensor);
    if (analogPin < analogSensorEnd) {
      data_string += ",";
      }
    }
  
  file = SD.open(fn, FILE_WRITE);
  if (file) {
    file.println(data_string);
    file.close();
    Serial.println(data_string);
    }
    else {
      Serial.print("error opening ");
      Serial.println(fn);
    }
}

2013/08/17

NTP Server Synchronized DS1307 RTC Using TimeAlarms Library

This sketch calls alarm functions at 7:30 am and at 7:30 pm (19:30) and sync DS1307, Arduino system time. At startup the Arduino system time is synchronized with DS1307. The sketch output time and "Sync OK" message to Arduino serial monitor at every successful synchronization.

A DHCP server is needed.

Requirements
1. Arduino Uno R3
2. W5100 Ethernet Shield
3. RTC Sensor Shield

Schetch

/*
 * TimeAlarmNTPSyncRTC.ino
 *
 * This sketch calls alarm functions at 7:30 am and at 7:30 pm (19:30)
 * and sync DS1307, Arduino system time 
 *
 * At startup the time is sync with DS1307
 */

#include <Wire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <TimeAlarms.h>
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>

// Enter a MAC address for your controller bellow.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
unsigned int localPort = 8888; // local port to listen for UDP packets
IPAddress timeServer(140, 112, 2, 188); // ntp2.ntu.edu.tw 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
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
// timeZoneOffset = (Time Zone) * 3600L eg. (+8) * 3600L = 28800L for Taipei, Taiwan
const long timeZoneOffset = 28800L; 
// sync to NTP server every "ntpSyncTime" seconds, set to 1 hour or more to be reasonable
unsigned long ntpSyncTime = 21600;
// keep track of how long ago we updated the NTP server
// unsigned long ntpLastUpdate = 0;
// adjust the sync latency with computer NTP client in seconds 
unsigned int syncLatency = 2;

void setup()
{
  Serial.begin(9600);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  // create the alarms 
  Alarm.alarmRepeat(7,30,0, ntpSyncDS1307);  // 7:30am every day
  Alarm.alarmRepeat(19,30,0, ntpSyncDS1307);  // 7:30pm every day 
  
  // start Ethernet and UDP
  if (Ethernet.begin(mac) == 0) {
    for (;;);
  }
  Udp.begin(localPort); 
ntpSyncDS1307();
}

void  loop(){  
  Alarm.delay(100); // wait 1/10 second between cycles
}

// functions to be called when an alarm triggers:
void ntpSyncDS1307() {
  sendNTPpacket(timeServer); // send an NTP packet to a time server
  // wait to see if a replay is available
  delay(1000);
  if (Udp.parsePacket()) {
    // We've received a packet, read the data from it
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
    // the timstamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, extract 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 = 2208988800L;
    // substract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears + timeZoneOffset + syncLatency;
    setTime(epoch);
    RTC.set(epoch);
    // ntpLastUpdate = now();
    // output time and "Sync OK" message every sync 
    Serial.print(year());
    Serial.print('-');
    Serial.print(month());
    Serial.print('-');
    Serial.print(day());
    Serial.print(' ');
    Serial.print(hour());
    Serial.print(':');
    Serial.print(minute());
    Serial.print(':');
    Serial.print(second());
    Serial.print(' ');
    Serial.println("Sync OK");
  }
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& 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 clodk
  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.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

2013/08/15

Analog Sensor Data Logger


Create data file name:MMDDhhmm.CSV where M means month, D means day, h means hour and m means minute respectively. Once the reset on Arduino board is pressed, a new logger file is created.

An altenative is to use a DS1307 RTC shield (such as RTC Sensor Shield) and a SD shield.

This is the new version of the post on May 01, 2012 with the difference of using Time.h library.

Pin Used

Analog pin 3 is used for LM35 temperature sensor.
Analog pins 4 and 5 are used for I2C protocol implemented by Maxim DS1307.
Digital pins 10, 11, 12, 13 are used for SPI protocol to save data file to micro SD card.

Time.h Download

http://playground.arduino.cc//Code/Time

Sketch

// File Name: AnalogDataLoggerTimeLibrary.ino
// The sketch works on Arduino IDE 1.05
#include <SD.h>
#include <Wire.h>
#include <Time.h>
#include <DS1307RTC.h>
#define analogSensorStart 3
#define analogSensorEnd 3

File file; // test file
const uint8_t SD_CS = 10; // SD chip select
String file_name = ""; // file name should not prefix with "prefix_word"
char fn[] = "MMDDHHMM.CSV";
int i=0;
int displayAtSecond = 0;
time_t t;

//------------------------------------------------------------------------------
// call back for file timestamps 
void dateTime(uint16_t* date, uint16_t* time) {
  time_t timeStamp = now();

  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(year(timeStamp), month(timeStamp), day(timeStamp));

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(hour(timeStamp), minute(timeStamp), second(timeStamp));

//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  Wire.begin();
  setSyncProvider(RTC.get); // the function to get the time from the RTC
  if (timeStatus() != timeSet)
    Serial.println("Unable to sync with the RTC");
    else
    Serial.println("RTC has set the system time");
    
  // set date time callback function
  SdFile::dateTimeCallback(dateTime);

  t = now();
  if (month(t) < 10) {
      file_name = String(file_name + '0' + String(month(t), DEC));
    }
    else {
    file_name = String(file_name +String(month(t), DEC));
    }
  if (day(t) < 10) {
      file_name = String(file_name + '0' + String(day(t), DEC));
    }
    else {
    file_name = String(file_name +String(day(t), DEC));
    }
  if (hour(t) < 10) {
      file_name = String(file_name + '0' + String(hour(t), DEC));
    }
    else {
    file_name = String(file_name +String(hour(t), DEC));
    }
  if (minute(t) < 10) {
      file_name = String(file_name + '0' + String(minute(t), DEC));
    }
    else {
    file_name = String(file_name +String(minute(t), DEC));
    }

  file_name = String(file_name + ".CSV");

  for (i=0;i<=file_name.length();i++) {
    fn[i] = file_name.charAt(i);
  }

  if (!SD.begin(SD_CS)) {
    Serial.println("SD failed");
    while(1);
  }
  Serial.print("File Name: ");
  Serial.println(fn);
}
//------------------------------------------------------------------------------
void loop()
{
  t = now();
  if (displayAtSecond != second(t)) {
    analogSensorDataLogger();
    displayAtSecond = second(t);
  }
}

void analogSensorDataLogger() { 
  String data_string = "";
  data_string = String(year(t), DEC);
  data_string += "/";
  if (month(t) < 10) {
      data_string = String(data_string + '0' + String(month(t), DEC));
    }
    else {
    data_string = String(data_string +String(month(t), DEC));
    }
  data_string += "/";
  if (day(t) < 10) {
      data_string = String(data_string + '0' + String(day(t), DEC));
    }
    else {
    data_string = String(data_string +String(day(t), DEC));
    }
  data_string += " ";
  if (hour(t) < 10) {
      data_string = String(data_string + '0' + String(hour(t), DEC));
    }
    else {
    data_string = String(data_string +String(hour(t), DEC));
    }
  data_string += ":";
  if (minute(t) < 10) {
      data_string = String(data_string + '0' + String(minute(t), DEC));
    }
    else {
    data_string = String(data_string +String(minute(t), DEC));
    }
  data_string += ":";  
  if (second(t) < 10) {
      data_string = String(data_string + '0' + String(second(t), DEC));
    }
    else {
    data_string = String(data_string +String(second(t), DEC));
    }
  data_string += ",";

  //read sensor value from A0-A3 and append to the string
  for (int analogPin = analogSensorStart; analogPin <= analogSensorEnd; analogPin++)
    {
    int sensor = analogRead(analogPin);
    data_string += String(sensor);
    if (analogPin < analogSensorEnd) {
      data_string += ",";
      }
    }
  
  file = SD.open(fn, FILE_WRITE);
  if (file) {
    file.println(data_string);
    file.close();
    Serial.println(data_string);
    }
    else {
      Serial.print("error opening ");
      Serial.println(fn);
    }
}

2013/08/04

NTP Server Synchronized LCD Keypad Adjustable Clock With Temperature Using Time.h Library

This sketch extend from post on Aug. 01, 2013 by adding synchronizing with NTP server.The Uno will synchronize with NTP server every 6 hours and override the time adjusted by using LCD shield keypad. An alternative to synchronize at certain time is to use Alarm.alarmRepeat() function in <TimAlarms.h>. The latency is about 2 seconds in my case, adjust the latency according your Internet connection condition. The binary sketch size is 19,614 bytes.

Requirements:

1. Arduino Uno R3
2. RTC (DS1307) Sensor Shield
3. W5100 Ethernet Shield
4. LCD Keypad Shield
5. LM35 Temperature Sensor Breakout

Usage

1. Use <RIGHT> keypad to enter set mode.
2. Use <RIGHT> to navigate on parameter to be modified.
3. Use <UP> to set value of the aimed parameter.

4. Use <DOWN> to set value of the aimed parameter.
5. Use <SELECT> to save date and time to RTC.
6. Use <LEFT> to leave set mode.


Schetch

/*
 * TimeRTC.pde
 * example code illustrating Time library with Real Time Clock.
 * the sketch works on Arduino IDE 1.05
 * 1) modified by Befun Hung on Jul. 28, 2013 
 *    changing the sequence to year, month, day, hour, minute, second
 *    adding the day of week
 *    almost same function as sketch on May 1, 2012
 * 2) modified by Befun Hung on Jul. 29, 2013
 *    change display device to LCD Shield
 *    adding temperature readout from LM35
 *    the sketch does not contain delay() in the loop section 
 * 3) modified by Befun Hung on Jul. 31, 2013
 *    divide digitalClockDisplay() into dateDisplay(), weekdayDisplay(), timeDisplay() and temperatureDisplay()
 *    use time_t t to store the value of now()
 * 4) modified by Befun Hung on Aug. 04, 2013
 *    adding ntpSyncDS1307() to synchronize DS1307 real time clock with NTP server
 */

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>
#include <Wire.h>  
#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t
#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9,4,5,6,7);

#define btnRIGHT 0
#define btnUP 1
#define btnDOWN 2
#define btnLEFT 3
#define btnSELECT 4
#define btnNONE 5

char *dayOfWeek[] = {"", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
int lcdKey = 0;
int adcKeyIn = 0;
time_t t;
int potPin = 3; // change potPin value to 0, 1, 2 for A0, A1, A2 respectly
float temperature = 0;
int displayAtSecond;

// Enter a MAC address for your controller bellow.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
unsigned int localPort = 8888; // local port to listen for UDP packets
IPAddress timeServer(140, 112, 2, 188); // ntp2.ntu.edu.tw 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
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
// timeZoneOffset = (Time Zone) * 3600L eg. (+8) * 3600L = 28800L for Taipei, Taiwan
const long timeZoneOffset = 28800L; 
// sync to NTP server every "ntpSyncTime" seconds, set to 1 hour or more to be reasonable
unsigned long ntpSyncTime = 21600;
// keep track of how long ago we updated the NTP server
unsigned long ntpLastUpdate = 0;
// adjust the sync latency with computer NTP client in seconds 
unsigned int syncLatency = 2;

void setup()  {
  lcd.begin(16,2);
  lcd.print("*cheaphousetek*");
  lcd.setCursor(0,1);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  if(timeStatus()!= timeSet) 
     lcd.print("Unable to sync");
  else
     lcd.print("Sync system time ");
  displayAtSecond = second();
  delay(2000);
  lcd.clear();
  // start Ethernet and UDP
  if (Ethernet.begin(mac) == 0) {
    lcd.setCursor(0,1);
    lcd.print("DHCP failed");
    for (;;);
  }
  Udp.begin(localPort);
}

void loop()
{
  if ((now() - ntpLastUpdate) >= ntpSyncTime) {
    ntpSyncDS1307();
  }
  // for reading keypad stroke to set the date and time once the RIGHT is pressed
  t = now();
  lcdKey = readLCDButton();
  if (lcdKey == btnRIGHT) {
    keypadSetDateTime();
  }
  // for LCD shield to disp date, day of the week, time and temperature once a second
  if (displayAtSecond != second(t)) { 
    digitalClockDisplay(); 
    displayAtSecond = second(t); 
  }
}

int readLCDButton() {
  adcKeyIn = analogRead(0);
  delay(200);
  // read the value from the sensor
  // my buttons when read are centered at these values: 0, 144, 329, 504, 741
  // we add approx 50 to those values and check to see if we are close
  if (adcKeyIn > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
  if (adcKeyIn < 73) return btnRIGHT;
  if (adcKeyIn < 237) return btnUP;
  if (adcKeyIn < 415) return btnDOWN;
  if (adcKeyIn < 623) return btnLEFT;
  if (adcKeyIn < 882) return btnSELECT;
  return btnNONE; // when all others fail, return this...
}

void digitalClockDisplay(){
  // digital clock display of the time
  dateDisplay();
  weekdayDisplay();
  timeDisplay();
  temperatureDisplay();  
}

void dateDisplay() {
  lcd.setCursor(0,0);
  lcd.print(year(t));
  lcd.print('-');
  if (month(t) < 10) {
    lcd.print('0');
  }
  lcd.print(month(t));
  lcd.print('-');
  if (day(t) < 10) {
    lcd.print('0');
  }
  lcd.print(day(t));
  // lcd.print(' ');
}

void weekdayDisplay() {
  lcd.setCursor(11,0);
  lcd.print(dayOfWeek[weekday()]);
}

void timeDisplay() {
  lcd.setCursor(0,1);
  if (hour(t) < 10) {
    lcd.print('0');
  }
  lcd.print(hour(t));
  lcd.print(':');
  if (minute(t) < 10) {
    lcd.print('0');
  }
  lcd.print(minute(t));
  lcd.print(':');
  if (second(t) < 10) {
    lcd.print('0');
  }
  lcd.print(second(t));
  // lcd.print(' ');
}

void temperatureDisplay() {
  int span = 10;
  long aRead = 0;
  unsigned long temp;
  
  for (int i=0;i<span;i++) {
    aRead = aRead + analogRead(potPin);
  }
  temperature = (aRead / span * 500.0 / 1024.0); // for other analog sensor change the constants
  printTenths(long (temperature * 10));
  lcd.setCursor(14,1);
  lcd.print(char(223));
  // lcd.setCursor(15,1);
  lcd.print('C'); 
}

void printTenths(long value) {
  // prints a value of 123 as 12.3
  lcd.setCursor(10,1);
  lcd.print(value / 10);
  lcd.setCursor(12,1);
  lcd.print('.');
  lcd.setCursor(13,1);
  lcd.print(value % 10);
}

void keypadSetDateTime() {
  int setYear=year(t), setMonth=month(t), setDay=day(t), setHour=hour(t), setMinute=minute(t), setSecond=second(t);
  int setVariable=0, checkStatus=0;
  
  dateDisplay();
  timeDisplay();
  while(true) {
    lcdKey = readLCDButton();
    switch(lcdKey) {
      case btnNONE:
      {
        lcd.blink();
        if (setVariable == 0) lcd.setCursor(3,0);
        if (setVariable == 1) lcd.setCursor(6,0);
        if (setVariable == 2) lcd.setCursor(9,0);
        if (setVariable == 3) lcd.setCursor(1,1);
        if (setVariable == 4) lcd.setCursor(4,1);
        if (setVariable == 5) lcd.setCursor(7,1);
        break;
      }
      case btnRIGHT:
      {
        setVariable = (setVariable + 1) % 6;
        break;
      }
      case btnLEFT:
      {
        lcd.noBlink();
        lcd.clear();
        return;
      }
      case btnUP:
      {
        if (setVariable == 0) {
          setYear = ((setYear + 1) % 100) + 2000;
          lcd.setCursor(0,0);
          lcd.print(setYear);
        }
        if (setVariable == 1) {
          setMonth = (setMonth % 12) + 1;
          lcd.setCursor(5,0);
          if (setMonth < 10) {
            lcd.print('0');
            lcd.print(setMonth);
          }
          else {
            lcd.print(setMonth);
          }
        }
        if (setVariable == 2) {
          setDay = (setDay % 31) + 1;
          lcd.setCursor(8,0);
          if (setDay < 10) {
            lcd.print('0');
            lcd.print(setDay);
          }
          else {
            lcd.print(setDay);
          }
        }
        if (setVariable == 3) {
          setHour = (setHour + 1) % 24;
          lcd.setCursor(0,1);
          if (setHour < 10) {
            lcd.print('0');
            lcd.print(setHour);
          }
          else {
            lcd.print(setHour);
          }
        }
        if (setVariable == 4) {
          setMinute = (setMinute + 1) % 60;
          lcd.setCursor(3,1);
          if (setMinute < 10) {
            lcd.print('0');
            lcd.print(setMinute);
          }
          else {
            lcd.print(setMinute);
          }
        }
        if (setVariable == 5) {
          setSecond = (setSecond + 1) % 60;
          lcd.setCursor(6,1);
          if (setSecond < 10) {
            lcd.print('0');
            lcd.print(setSecond);
          }
          else {
            lcd.print(setSecond);
          }
        }
        break;
      }
      case btnDOWN:
      {
        if (setVariable == 0) {
          setYear = ((setYear - 1) % 100) + 2000;
          lcd.setCursor(0,0);
          lcd.print(setYear);
        }
        if (setVariable == 1) {
          setMonth = ((setMonth - 1) % 12);
          if (setMonth == 0) {
            setMonth = setMonth + 12;
          }
          lcd.setCursor(5,0);
          if (setMonth < 10) {
            lcd.print('0');
            lcd.print(setMonth);
          }
          else {
            lcd.print(setMonth);
          }
        }
        if (setVariable == 2) {
          setDay = ((setDay - 1) % 31);
          if (setDay == 0) {
            setDay = setDay + 31;
          }
          lcd.setCursor(8,0);
          if (setDay < 10) {
            lcd.print('0');
            lcd.print(setDay);
          }
          else {
            lcd.print(setDay);
          }
        }
        if (setVariable == 3) {
          setHour = (setHour - 1 + 24) % 24;
          lcd.setCursor(0,1);
          if (setHour < 10) {
            lcd.print('0');
            lcd.print(setHour);
          }
          else {
            lcd.print(setHour);
          }
        }
        if (setVariable == 4) {
          setMinute = (setMinute - 1 + 60) % 60;
          lcd.setCursor(3,1);
          if (setMinute < 10) {
            lcd.print('0');
            lcd.print(setMinute);
          }
          else {
            lcd.print(setMinute);
          }
        }
        if (setVariable == 5) {
          setSecond = (setSecond - 1 + 60) % 60;
          lcd.setCursor(6,1);
          if (setSecond < 10) {
            lcd.print('0');
            lcd.print(setSecond);
          }
          else {
            lcd.print(setSecond);
          }
        }
        break;
      }
      case btnSELECT:
      {
        if ((setMonth == 1 || setMonth == 3 || setMonth == 5 || setMonth == 7 || setMonth == 8 || setMonth == 10 || setMonth == 12) && setDay <= 31) checkStatus = 1;
        if ((setMonth == 4 || setMonth == 6 || setMonth == 9 || setMonth == 11) && setDay <=30) checkStatus = 1;
        if ((setMonth == 2 && (setYear % 4) == 0) && setDay <= 29) checkStatus = 1;
        if ((setMonth == 2 && (setYear % 4) != 0) && setDay <= 28) checkStatus = 1;
        if (checkStatus) {
          setTime(setHour, setMinute, setSecond, setDay, setMonth, setYear);
          RTC.set(now());
        }
        break;
      }
    }
  }
}

void ntpSyncDS1307() {
  sendNTPpacket(timeServer); // send an NTP packet to a time server
  // wait to see if a replay is available
  delay(1000);
  if (Udp.parsePacket()) {
    // We've received a packet, read the data from it
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
    // the timstamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, extract 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 = 2208988800L;
    // substract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears + timeZoneOffset + syncLatency;
    setTime(epoch);
    RTC.set(epoch);
    ntpLastUpdate = now();
  }
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& 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 clodk
  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.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}