아두이노 NodeMCU서버가 비록 쥐새끼만한 서버이긴 하지만 그래도 서버이기 때문에 반드시 시간 출력이 가능해야 할 것이다. 크든 적든 서버에 별도의 건전지로 작동하는 실시간 시계를 부착하기 보다는 표준 타임서버로부터 시간 정보를 당겨다 쓰는 것이 바람직하다.
시간 정보를 당겨다 쓰는 방법으로서 UDP(User Datagram Protocol) 방법을 제시하기로 한다. UDP 프로토콜에서는 Thingspeak 사물인터넷 예제의 상당히 작은 데이터 사이즈 통신과비교 해 보아서 상대적으로 큰 편이다. 한편으로는 UDP 프로토콜은 IPTV, 카메라 또는 캠코더와 같이 대규모 이미지 용량 전송에 사용되는 방법이기도 하다.
앞으로도 캠코더 이미지 전송 또는 Pixy 비전 센서 데이터 처리 시 Udp 프로토콜 사용 여부를 블로그에서 검토할 예정이다.
UDP 프로토콜 사용을 위해서 ESP8266WiFi.h 라이브러리 외에 추가로 UDP.h 라이브러리를 불러들일 필요가 있다. 이 라이브러리는 NodeMCU 말고도 아두이노 와이파이쉴드를 사용하거나 또는 아예 유선으로 이더넷 쉴드를 사용할 경우에도 반드시 필요한 라이브러리이다.
시간 정보를 얻어 올 타임서버는 미국 푲눈 연구소인 NIST로 설정했다. 국내의 Postech 포항공대를 설정해도 NIST와 동기화 되어 있어 동일한 시간 정보를 제공한다.
프로그램 상 상이점은 앞서의 예제에서 WiFiServer server(80) 대신에 WiFiUdp udp가 선언된다. 와이파이 연결 후 가상 ip 출력까지는 앞서의 예제들과 거의 대동소이하다.
Loop()에서 타임서버를 지정 호출하여 48바이트 시간정보를 전송 받은 후 시산 분 초로 가공 후 시리얼 모니터에 출력한다.
이와같이 타임서버를 호출하여 동기화 할 경우 생각보다 NodeMCU 의 메모리를 39% 수준으로 많이 사용한다는 점에 유의하자. 마이크로콘트롤러의 메모리 용량이 상당히 작은 편이므로 자신이 필요로 하는 웹서버 응용 프로그램을 함께 넣을 경우 메모리 부족 현상이 올 수도 있다. 그럴 때 앞의 예제에서 사용했던 자바스크립트나 HTML 방식의 프로그램과 함께 응용 프로그램을 작성하여 메모리 경제성을 높이는 것도 바람직하다.
Udp 프로토콜에 의해서 전송 받은 시간 정보는 영국 그리니치 천문대 기준으로 국내 서울 현지 시간과 경도차 135도에 따라 9시간의 시간 우리나라가 빠르다. 시리얼 모니터 출력에서 괄호 친 부분이 국내 로컬 시간이다.
Webserver_nodemcu_udp_networkclock_01
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
char ssid[] = "AndroidHotspot1234"; // 본인 스마트 폰 핫스팟 ID
char pass[] = "00000000"; // 본인 스마트 폰 핫스팟 비밀번호
unsigned int localPort = 2390; // local port to listen for UDP packets
//IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";
// const char* ntpServerName = "ntp.postech.ac.kr";
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
WiFiUDP udp;
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println();
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Starting UDP");
udp.begin(localPort);
Serial.print("Local port: ");
Serial.println(udp.localPort());
}
void loop()
{
//get a random server from the pool
WiFi.hostByName(ntpServerName, timeServerIP);
sendNTPpacket(timeServerIP); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
int cb = udp.parsePacket();
if (!cb) {
Serial.println("no packet yet");
}
else {
Serial.print("packet received, length=");
Serial.println(cb);
// We've received a packet, read the data from it
udp.read(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;
Serial.print("Seconds since Jan 1 1900 = " );
Serial.println(secsSince1900);
// now convert NTP time into everyday time:
Serial.print("Unix 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;
// print Unix time:
Serial.println(epoch);
// print the hour, minute and second:
Serial.print("The UTC(Seoul) time is "); // UTC is the time at Greenwich Meridian (GMT)
Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day)
Serial.print('(');
Serial.print((epoch % 86400L) / 3600+9); // 경도 135도 기준 서울 9시간 영국그리니치 보다 빠름
Serial.print(')'); Serial.print(':');
if ( ((epoch % 3600) / 60) < 10 ) {
// In the first 10 minutes of each hour, we'll want a leading '0'
Serial.print('0');
}
Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute)
Serial.print(':');
if ( (epoch % 60) < 10 ) {
// In the first 10 seconds of each minute, we'll want a leading '0'
Serial.print('0');
}
Serial.println(epoch % 60); // print the second
}
// wait ten seconds before asking for the time again
delay(10000);
}
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
Serial.println("sending NTP packet...");
// 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.beginPacket(address, 123); //NTP requests are to port 123
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
}
'아두이노프로세싱 프로그래밍' 카테고리의 다른 글
단일 리튬이온 폴리머 배터리에 의한 포터블 아두이노 NodeMCU 보드 전원공급 (0) | 2017.01.31 |
---|---|
아두이노 NodeMCU 웹서버 아날로그 시계 출력예제 (0) | 2017.01.30 |
아두이노 NodeMCU로부터 디지털 시계형 스마트폰 시간 출력 HTML 예제: II (0) | 2017.01.30 |
아두이노 NodeMCU 웹서버로부터 스마트 폰에 시간 출력예제: I Text type (0) | 2017.01.27 |
NodeMCU 웹서버가 Random 하게 실시간 생성한 각도 데이터 로 스마트 폰에서 움직이는 게이지 그래프 작성 III: (0) | 2017.01.26 |