아두이노에 이더넷 쉴드를 사용하면 다양한 응용이 가능한데 제일 먼저 필요한것중 하나가 시간에 대한 것이다.
센서 측정이든,다른 기능이든 기준이 되는 시간 값을 가지고 오는 것은 데이터 신뢰 측면에서 중요하다.
28J60 칩을 사용한 아두이노 나노용 이더넷쉴드를 이용하여 NTP 서버와 1일 1번 동기화 하는 기능을 구현하였다.
준비물 :
1. 아두이노 나노(CH340을 사용하는 중국산 제품을 사용하였다.)
2. 아두이노 나노용 이더넷쉴드(가치정보기술에서 파는 제품을 구매했는데 문자가 있어서 자가수리를 했다) 알리에서 구하는게 더 좋을듯 하다.
#include <EtherCard.h> //https://github.com/jcw/ethercard
#include <TimeLib.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// NTP Server
const char timeServer[] PROGMEM = "pool.ntp.org";
const int utcOffset = 9; // Asia Korea Seoul Time
// Packet buffer, must be big enough to packet and payload
#define BUFFER_SIZE 550
byte Ethernet::buffer[BUFFER_SIZE];
const unsigned int remotePort = 123;
void setup()
{
Serial.begin(9600);
Serial.println("TimeNTP_ENC28J60 Example");
if (ether.begin(BUFFER_SIZE, mac) == 0) {
// no point in carrying on, so do nothing forevermore:
while (1) {
Serial.println("Failed to access Ethernet controller");
delay(10000);
}
}
if (!ether.dhcpSetup()) {
// no point in carrying on, so do nothing forevermore:
while (1) {
Serial.println("Failed to configure Ethernet using DHCP");
delay(10000);
}
}
ether.printIp("IP number assigned by DHCP is ", ether.myip);
Serial.println("waiting for sync");
//setSyncProvider(getNtpTime); // Use this for GMT time
setSyncProvider(getDstCorrectedTime); // Use this for local, DST-corrected time
setSyncInterval(86400); //every day 1day = 86400second
}
time_t prevDisplay = 0; // when the digital clock was displayed
char buffer[20];
void loop()
{
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { //update the display only if time has changed
prevDisplay = now();
digitalClockDisplay();
}
}
}
void digitalClockDisplay(){
// digital clock display of the time
sprintf(buffer , "%4d.%02d.%02d %02d:%02d:%02d", year(),month(),day(),hour(),minute(),second());
Serial.println(buffer);
}
/*-------- NTP code ----------*/
// SyncProvider that returns UTC time
time_t getNtpTime() {
// Send request
Serial.println("Transmit NTP Request");
if (!ether.dnsLookup(timeServer)) {
Serial.println("DNS failed");
return 0; // return 0 if unable to get the time
} else {
//ether.printIp("SRV: ", ether.hisip);
ether.ntpRequest(ether.hisip, remotePort);
// Wait for reply
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
word len = ether.packetReceive();
ether.packetLoop(len);
unsigned long secsSince1900 = 0L;
if (len > 0 && ether.ntpProcessAnswer(&secsSince1900, remotePort)) {
Serial.println("Receive NTP Response");
return secsSince1900 - 2208988800UL;
}
}
Serial.println("No NTP Response :-(");
return 0;
}
}
/* Alternative SyncProvider that automatically handles Daylight Saving Time (DST) periods,
* at least in Europe, see below.
*/
time_t getDstCorrectedTime (void) {
time_t t = getNtpTime ();
if (t > 0) {
TimeElements tm;
breakTime (t, tm);
t += (utcOffset + dstOffset (tm.Day, tm.Month, tm.Year + 1970, tm.Hour)) * SECS_PER_HOUR;
}
return t;
}
/* This function returns the DST offset for the current UTC time.
* This is valid for the EU, for other places see
* http://www.webexhibits.org/daylightsaving/i.html
*
* Results have been checked for 2012-2030 (but should work since
* 1996 to 2099) against the following references:
* - http://www.uniquevisitor.it/magazine/ora-legale-italia.php
* - http://www.calendario-365.it/ora-legale-orario-invernale.html
*/
byte dstOffset (byte d, byte m, unsigned int y, byte h) {
// Day in March that DST starts on, at 1 am
byte dstOn = (31 - (5 * y / 4 + 4) % 7);
// Day in October that DST ends on, at 2 am
byte dstOff = (31 - (5 * y / 4 + 1) % 7);
if ((m > 3 && m < 10) ||
(m == 3 && (d > dstOn || (d == dstOn && h >= 1))) ||
(m == 10 && (d < dstOff || (d == dstOff && h <= 1))))
return 1;
else
return 0;
}
이렇게 나온다.
아두이노 나도에서 차지하는 공간은 아래와 같다.
스케치는 프로그램 저장 공간 11566 바이트(37%)를 사용. 최대 30720 바이트.
전역 변수는 동적 메모리 1193바이트(58%)를 사용, 855바이트의 지역변수가 남음. 최대는 2048 바이트.
#include <EtherCard.h> //https://github.com/jcw/ethercard
#include <TimeLib.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// NTP Server
const char timeServer[] PROGMEM = "pool.ntp.org";
const int utcOffset = 9; // Asia Korea Seoul Time
// Packet buffer, must be big enough to packet and payload
#define BUFFER_SIZE 550
byte Ethernet::buffer[BUFFER_SIZE];
const unsigned int remotePort = 123;
void setup()
{
Serial.begin(9600);
Serial.println(F("TimeNTP_ENC28J60 Example"));
if (ether.begin(BUFFER_SIZE, mac) == 0) {
// no point in carrying on, so do nothing forevermore:
while (1) {
Serial.println(F("Failed to access Ethernet controller"));
delay(10000);
}
}
if (!ether.dhcpSetup()) {
// no point in carrying on, so do nothing forevermore:
while (1) {
Serial.println(F("Failed to configure Ethernet using DHCP"));
delay(10000);
}
}
ether.printIp("IP number assigned by DHCP is ", ether.myip);
Serial.println(F("waiting for sync"));
//setSyncProvider(getNtpTime); // Use this for GMT time
setSyncProvider(getDstCorrectedTime); // Use this for local, DST-corrected time
setSyncInterval(86400); //every day 1day = 86400second
}
time_t prevDisplay = 0; // when the digital clock was displayed
char buffer[20];
void loop()
{
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { //update the display only if time has changed
prevDisplay = now();
digitalClockDisplay();
}
}
}
void digitalClockDisplay(){
// digital clock display of the time
sprintf(buffer , "%4d.%02d.%02d %02d:%02d:%02d", year(),month(),day(),hour(),minute(),second());
Serial.println(buffer);
}
/*-------- NTP code ----------*/
// SyncProvider that returns UTC time
time_t getNtpTime() {
// Send request
Serial.println(F("Transmit NTP Request"));
if (!ether.dnsLookup(timeServer)) {
Serial.println(F("DNS failed"));
return 0; // return 0 if unable to get the time
} else {
//ether.printIp("SRV: ", ether.hisip);
ether.ntpRequest(ether.hisip, remotePort);
// Wait for reply
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
word len = ether.packetReceive();
ether.packetLoop(len);
unsigned long secsSince1900 = 0L;
if (len > 0 && ether.ntpProcessAnswer(&secsSince1900, remotePort)) {
Serial.println(F("Receive NTP Response"));
return secsSince1900 - 2208988800UL;
}
}
Serial.println(F("No NTP Response :-("));
return 0;
}
}
/* Alternative SyncProvider that automatically handles Daylight Saving Time (DST) periods,
* at least in Europe, see below.
*/
time_t getDstCorrectedTime (void) {
time_t t = getNtpTime ();
if (t > 0) {
TimeElements tm;
breakTime (t, tm);
t += (utcOffset + dstOffset (tm.Day, tm.Month, tm.Year + 1970, tm.Hour)) * SECS_PER_HOUR;
}
return t;
}
/* This function returns the DST offset for the current UTC time.
* This is valid for the EU, for other places see
* http://www.webexhibits.org/daylightsaving/i.html
*
* Results have been checked for 2012-2030 (but should work since
* 1996 to 2099) against the following references:
* - http://www.uniquevisitor.it/magazine/ora-legale-italia.php
* - http://www.calendario-365.it/ora-legale-orario-invernale.html
*/
byte dstOffset (byte d, byte m, unsigned int y, byte h) {
// Day in March that DST starts on, at 1 am
byte dstOn = (31 - (5 * y / 4 + 4) % 7);
// Day in October that DST ends on, at 2 am
byte dstOff = (31 - (5 * y / 4 + 1) % 7);
if ((m > 3 && m < 10) ||
(m == 3 && (d > dstOn || (d == dstOn && h >= 1))) ||
(m == 10 && (d < dstOff || (d == dstOff && h <= 1))))
return 1;
else
return 0;
}
소스와 같이 Serial.println 부분을 F()로 변경하면 차지하는 메모리는 전역변수 메모리가 10% 감소한다.
스케치는 프로그램 저장 공간 11610 바이트(37%)를 사용. 최대 30720 바이트.
전역 변수는 동적 메모리 1001바이트(48%)를 사용, 1047바이트의 지역변수가 남음. 최대는 2048 바이트.