2017.07.24 13:30

【物聯網系統開發】準確掌握即時正確的時間

ADVERTISEMENT

本系列希望貢獻筆者一些經驗,讓 ARDUINO 創客神器()可以運用到時間功能,進而發展出強大的物聯網應用。

文\曹永忠、吳佳駿、許智誠、蔡英德

本篇承接上上篇『【物聯網系統開發】運用 ARDUINO 乙太網路擴充板建立簡單網頁伺服器』文章(曹永忠, 吳佳駿, 許智誠, & 蔡英德, 2017a)與上篇『【物聯網系統開發】運用 ARDUINO 乙太網路擴充板建立用戶端工具程式』文章(曹永忠, 吳佳駿, 許智誠, & 蔡英德, 2017b)後,我們發現準確、即時、可靠與全球化(可適用於多時區)的時間機制,則是物聯網開發中,不可或缺的一個核心功能。

本文要使用 ARDUINO 乙太網路擴充板,再具備 TCP/IP 之擷取通訊能力之後,我們將使用網路上最通用的 NTP 伺服器(NTP:Network Time Protocol),透過 NTP 通訊協定來取得最即時、正確的時間,讓最方便使用的開發版(曹永忠, 2016a, 2016b, 2016c)。 

ADVERTISEMENT

本系列希望貢獻筆者一些經驗,讓非資訊、電機、電子等 Makers 可以學到在物聯網開發中,一些程式開發的技巧、原理、法則與穩固的技術,因本系列文章主要讀者為初學者,內容程度為基礎入門程度,深入之處不足,但請高手們給筆者賜教,也請讀者關注本系列。

取得網路校時時間資料

首先,組立 W5100 以太網路模組是非常容易的一件事,如下圖所示,只要將 W5100 以太網路模組堆疊到任何 Arduino 開發板之上就可以了。

▲圖 1 將 Arduino 開發板與 W5100 以太網路模組堆疊組立

ADVERTISEMENT

之後,在將組立好的 W5100 以太網路模組,如下圖所示,只要將 USB 線差到 Arduino 開發板,再將 RJ 45的網路線一端插到 W5100 以太網路模組,另一端插到可以上網的集線器(Switch HUB)的任何一個區域網路接口(Lan Port)就可以了。

▲圖 2接上電源與網路線的 W5100 以太網路模組堆疊卡

我們遵照前幾章所述,將 Arduino 開發板的驅動程式安裝好之後,我們打開 Arduino 開發板的開發工具:Sketch IDE 整合開發軟體,攥寫一段程式,如下表所示之網路校時測試程式,我們就可以透過 W5100 以太網路模組堆疊卡取得網路校時時間。

ADVERTISEMENT

表 1網路校時測試程式

網路校時測試程式(UdpNtpClient)

/*

 

 Udp NTP Client

 

 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 9 Apr 2012

 by Tom Igoe

 

 This code is in the public domain.

 

 */

 

#include <SPI.h>

#include <Ethernet.h>

#include <EthernetUdp.h>

 

// Enter a MAC address for your controller below.

// Newer Ethernet shields have a MAC address printed on a sticker on the shield

byte mac[] = {

  0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF

};

IPAddress ip(192, 168, 30, 200);

IPAddress dnServer(168, 95, 1, 1);

// the router's gateway address:

IPAddress gateway(192, 168, 30, 254);

// the subnet:

IPAddress subnet(255, 255, 255, 0);

 

 

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

 

char timeServer[] = "time.nist.gov"; // 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

 

// A UDP instance to let us send and receive packets over UDP

EthernetUDP Udp;

 

void setup()

{

  // Open serial communications and wait for port to open:

  Serial.begin(9600);

  while (!Serial) {

    ; // wait for serial port to connect. Needed for Leonardo only

  }

 

 

  // start Ethernet and UDP

  if (Ethernet.begin(mac) == 0) {

    Serial.println("Failed to configure Ethernet using DHCP");

    // no point in carrying on, so do nothing forevermore:

  Ethernet.begin(mac, ip, dnServer, gateway, subnet);

 

  }

  Udp.begin(localPort);

}

 

void loop()

{

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

 

  // wait to see if a reply 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 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 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(':');

    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(char* 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.beginPacket(address, 123); //NTP requests are to port 123

  Udp.write(packetBuffer, NTP_PACKET_SIZE);

  Udp.endPacket();

}

 

如下圖所示,讀者可以看到本次實驗-網路校時測試程式結果畫面。

▲圖 3網路校時測試程式結果畫面

後續

本篇為『物聯網系統開發系列』系列之準確掌握即時正確的時間,主要內容是要讓讀者使用創客神器 Arduino 開發板,透過乙太網路擴充板(Ethernet Shield)的網際網路連線之強大功能,可以讓 Arduino 開發板取得 NTP 伺服器之時間內容,進而將這個基礎理念與技術,進階運用到雲端平台中時間同步與校正的機制,進而在物聯網開發中,成為一個技術的核心能力,乃是筆者本篇內容想傳達的創作概念。

ADVERTISEMENT

筆者本系列是針對非資訊、電機、電子等 Makers 攥寫的物聯網系統開發系列,這四、五年來在物聯網系統開發領域寫書、發表文章、辦展、授課,常遇到許多學子訓練不足,以交作業的心態來學習,並沒有把程式底子打好。

後續筆者還會繼續發表『物聯網系統開發系列』系列的文章,在未來我們可以創造出更優質,更具未來性的物聯網(Internet of Thing:IOT)產品開發相關技術。

敬請期待更多的文章。

作者介紹

曹永忠 (Yung-Chung Tsao) 目前為自由作家暨專業Maker,專研於軟體工程、軟體開發與設計、物件導向程式設計,商品攝影及人像攝影。長期投入創客運動、資訊系統設計與開發、企業應用系統開發、軟體工程、新產品開發管理、商品及人像攝影等領域,並持續發表作品及相關專業著作。 

Email: 

Line IDdr.brucetsao


作者網站:https://www.cs.pu.edu.tw/~yctsao/ 

臉書社群(Arduino.Taiwan)https://www.facebook.com/groups/Arduino.Taiwan/ 

Github網站https://github.com/brucetsao/

吳佳駿 (Chia-Chun Wu),國立中興大學資訊科學與工程學系博士,現任教於國立金門大學工業工程與管理學系專任助理教授,目前兼任國立金門大學計算機與網路中心資訊網路組組長,主要研究為軟體工程與應用、行動裝置程式設計、物件導向程式設計、網路程式設計、動態網頁資料庫、資訊安全與管理。

Email: 

許智誠 (Chih-Cheng Hsu),美國加州大學洛杉磯分校(UCLA) 資訊工程系博士,曾任職於美國IBM等軟體公司多年,現任教於中央大學資訊管理學系專任副教授,主要研究為軟體工程、設計流程與自動化、數位教學、雲端裝置、多層式網頁系統、系統整合。

Email: 

蔡英德 (Yin-Te Tsai),國立清華大學資訊科學系博士,目前是靜宜大學資訊傳播工程學系教授、靜宜大學計算機及通訊中心主任,主要研究為演算法設計與分析、生物資訊、軟體開發、視障輔具設計與開發。

Email:

參考文獻:

 曹永忠. (2016a). AMEBA透過網路校時RTC時鐘模組. 智慧家庭.  Retrieved from

曹永忠. (2016b). 智慧家庭:PM2.5空氣感測器(上網篇:啟動網路校時功能). 智慧家庭.  Retrieved from https://vmaker.tw/archives/7305

曹永忠. (2016c). 智慧家庭實作:ARDUINO 永遠的時間靈魂-RTC時鐘模組. 智慧家庭.  Retrieved from

曹永忠, 吳佳駿, 許智誠, & 蔡英德. (2017a). 【物聯網系統開發】運用 ARDUINO 乙太網路擴充板建立簡單網頁伺服器. 智慧家庭.  Retrieved from

曹永忠, 吳佳駿, 許智誠, & 蔡英德. (2017b). 蔡英德,【物聯網系統開發】運用 ARDUINO 乙太網路擴充板建立用戶端工具程式. 智慧家庭.  Retrieved from

曹永忠, 許智誠, & 蔡英德. (2015). 創客神器ARDUINO到底是什麼呢?.   Retrieved from

ADVERTISEMENT