<span style="text-indent: 24pt; font-family: 細明體;">本系列希望貢獻筆者一些經驗,讓&nbsp;<span>ARDUINO&nbsp;</span>創客神器</span><span style="text-indent: 24pt; font-family: 細明體;">(<a href="file:///F:/T%E5%AE%A2%E9%82%A6/%E4%B8%83%E6%9C%88%E7%AC%AC%E4%B8%80%E7%AF%87V2.docx#_ENREF_6" title="曹永忠, 2015 #2227"><span style="color: windowtext; text-decoration-line: none;"><span>曹永忠, </span></span><span style="color: windowtext; text-decoration-line: none;"><span>許智誠, &amp; </span></span><span style="color: windowtext; text-decoration-line: none;"><span>蔡英德, 2015</span></span></a>)</span><span style="text-indent: 24pt; font-family: 細明體;">可以運用到時間功能,進而發展出強大的物聯網應用。</span><p>文\曹永忠、吳佳駿、許智誠、蔡英德</p>
<p>本篇承接上上篇『【物聯網系統開發】運用 ARDUINO 乙太網路擴充板建立簡單網頁伺服器』文章(曹永忠, 吳佳駿, 許智誠, &amp; 蔡英德, 2017a)與上篇『【物聯網系統開發】運用 ARDUINO 乙太網路擴充板建立用戶端工具程式』文章(曹永忠, 吳佳駿, 許智誠, &amp; 蔡英德, 2017b)後,我們發現準確、即時、可靠與全球化(可適用於多時區)的時間機制,則是物聯網開發中,不可或缺的一個核心功能。</p>
<p>本文要使用 ARDUINO 乙太網路擴充板,再具備 TCP/IP 之擷取通訊能力之後,我們將使用網路上最通用的 NTP 伺服器(NTP:Network Time Protocol),透過 NTP 通訊協定來取得最即時、正確的時間,讓最方便使用的開發版(曹永忠, 2016a, 2016b, 2016c)。&nbsp;</p>
<p>本系列希望貢獻筆者一些經驗,讓非資訊、電機、電子等 Makers 可以學到在物聯網開發中,一些程式開發的技巧、原理、法則與穩固的技術,因本系列文章主要讀者為初學者,內容程度為基礎入門程度,深入之處不足,但請高手們給筆者賜教,也請讀者關注本系列。</p>
<h3 class="23421">取得網路校時時間資料</h3>
<p>首先,組立 W5100 以太網路模組是非常容易的一件事,如下圖所示,只要將 W5100 以太網路模組堆疊到任何 Arduino 開發板之上就可以了。</p>
<p style="text-align: center;"><img style="display: block; margin-left: auto; margin-right: auto;" src="https://cdn1-techbang.pixfs.net/system/images/403108/original/4a53b679b13c963e07b44f9a572a81b7.jpg?1499656438" alt="" />▲圖 1 將 Arduino 開發板與 W5100 以太網路模組堆疊組立</p>
<p style="text-align: left;">之後,在將組立好的 W5100 以太網路模組,如下圖所示,只要將 USB 線差到 Arduino 開發板,再將 RJ 45的網路線一端插到 W5100 以太網路模組,另一端插到可以上網的集線器(Switch HUB)的任何一個區域網路接口(Lan Port)就可以了。</p>
<p style="text-align: center;"><img style="display: block; margin-left: auto; margin-right: auto;" src="https://cdn2-techbang.pixfs.net/system/images/403109/original/acce6eee933509aed3d284f339bd9253.jpg?1499656438" alt="" />▲圖 2接上電源與網路線的 W5100 以太網路模組堆疊卡</p>
<p style="text-align: left;">我們遵照前幾章所述,將 Arduino 開發板的驅動程式安裝好之後,我們打開 Arduino 開發板的開發工具:Sketch IDE 整合開發軟體,攥寫一段程式,如下表所示之網路校時測試程式,我們就可以透過 W5100 以太網路模組堆疊卡取得網路校時時間。</p>
<h3>表 1網路校時測試程式</h3>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="697">
<p class="a">網路校時測試程式(UdpNtpClient)</p>
</td>
</tr>
<tr>
<td valign="top" width="697">
<p class="a">/*</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;Udp NTP Client</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;Get the time from a Network Time Protocol (NTP) time server</p>
<p class="a">&nbsp;Demonstrates use of UDP sendPacket and ReceivePacket</p>
<p class="a">&nbsp;For more on NTP time servers and the messages needed to communicate with them,</p>
<p class="a">&nbsp;see http://en.wikipedia.org/wiki/Network_Time_Protocol</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;created 4 Sep 2010</p>
<p class="a">&nbsp;by Michael Margolis</p>
<p class="a">&nbsp;modified 9 Apr 2012</p>
<p class="a">&nbsp;by Tom Igoe</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;This code is in the public domain.</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;*/</p>
<p class="a">&nbsp;</p>
<p class="a">#include &lt;SPI.h&gt;</p>
<p class="a">#include &lt;Ethernet.h&gt;</p>
<p class="a">#include &lt;EthernetUdp.h&gt;</p>
<p class="a">&nbsp;</p>
<p class="a">// Enter a MAC address for your controller below.</p>
<p class="a">// Newer Ethernet shields have a MAC address printed on a sticker on the shield</p>
<p class="a">byte mac[] = {</p>
<p class="a">&nbsp; 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF</p>
<p class="a">};</p>
<p class="a">IPAddress ip(192, 168, 30, 200);</p>
<p class="a">IPAddress dnServer(168, 95, 1, 1);</p>
<p class="a">// the router's gateway address:</p>
<p class="a">IPAddress gateway(192, 168, 30, 254);</p>
<p class="a">// the subnet:</p>
<p class="a">IPAddress subnet(255, 255, 255, 0);</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;</p>
<p class="a">unsigned int localPort = 8888;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // local port to listen for UDP packets</p>
<p class="a">&nbsp;</p>
<p class="a">char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server</p>
<p class="a">&nbsp;</p>
<p class="a">const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message</p>
<p class="a">&nbsp;</p>
<p class="a">byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets</p>
<p class="a">&nbsp;</p>
<p class="a">// A UDP instance to let us send and receive packets over UDP</p>
<p class="a">EthernetUDP Udp;</p>
<p class="a">&nbsp;</p>
<p class="a">void setup()</p>
<p class="a">{</p>
<p class="a">&nbsp; // Open serial communications and wait for port to open:</p>
<p class="a">&nbsp; Serial.begin(9600);</p>
<p class="a">&nbsp; while (!Serial) {</p>
<p class="a">&nbsp;&nbsp;&nbsp; ; // wait for serial port to connect. Needed for Leonardo only</p>
<p class="a">&nbsp; }</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp; // start Ethernet and UDP</p>
<p class="a">&nbsp; if (Ethernet.begin(mac) == 0) {</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.println("Failed to configure Ethernet using DHCP");</p>
<p class="a">&nbsp;&nbsp;&nbsp; // no point in carrying on, so do nothing forevermore:</p>
<p class="a">&nbsp; Ethernet.begin(mac, ip, dnServer, gateway, subnet);</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp; }</p>
<p class="a">&nbsp; Udp.begin(localPort);</p>
<p class="a">}</p>
<p class="a">&nbsp;</p>
<p class="a">void loop()</p>
<p class="a">{</p>
<p class="a">&nbsp; sendNTPpacket(timeServer); // send an NTP packet to a time server</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp; // wait to see if a reply is available</p>
<p class="a">&nbsp; delay(1000);</p>
<p class="a">&nbsp; if ( Udp.parsePacket() ) {</p>
<p class="a">&nbsp;&nbsp;&nbsp; // We've received a packet, read the data from it</p>
<p class="a">&nbsp;&nbsp;&nbsp; Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;&nbsp;&nbsp; //the timestamp starts at byte 40 of the received packet and is four bytes,</p>
<p class="a">&nbsp;&nbsp;&nbsp; // or two words, long. First, esxtract the two words:</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;&nbsp;&nbsp; unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);</p>
<p class="a">&nbsp;&nbsp;&nbsp; unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);</p>
<p class="a">&nbsp;&nbsp;&nbsp; // combine the four bytes (two words) into a long integer</p>
<p class="a">&nbsp;&nbsp;&nbsp; // this is NTP time (seconds since Jan 1 1900):</p>
<p class="a">&nbsp;&nbsp;&nbsp; unsigned long secsSince1900 = highWord &lt;&lt; 16 | lowWord;</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.print("Seconds since Jan 1 1900 = " );</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.println(secsSince1900);</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;&nbsp;&nbsp; // now convert NTP time into everyday time:</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.print("Unix time = ");</p>
<p class="a">&nbsp;&nbsp;&nbsp; // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:</p>
<p class="a">&nbsp;&nbsp;&nbsp; const unsigned long seventyYears = 2208988800UL;</p>
<p class="a">&nbsp;&nbsp;&nbsp; // subtract seventy years:</p>
<p class="a">&nbsp;&nbsp;&nbsp; unsigned long epoch = secsSince1900 - seventyYears;</p>
<p class="a">&nbsp;&nbsp;&nbsp; // print Unix time:</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.println(epoch);</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp;&nbsp;&nbsp; // print the hour, minute and second:</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.print("The UTC time is ");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // UTC is the time at Greenwich Meridian (GMT)</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.print((epoch&nbsp; % 86400L) / 3600); // print the hour (86400 equals secs per day)</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.print(':');</p>
<p class="a">&nbsp;&nbsp;&nbsp; if ( ((epoch % 3600) / 60) &lt; 10 ) {</p>
<p class="a">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // In the first 10 minutes of each hour, we'll want a leading '0'</p>
<p class="a">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Serial.print('0');</p>
<p class="a">&nbsp;&nbsp;&nbsp; }</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.print((epoch&nbsp; % 3600) / 60); // print the minute (3600 equals secs per minute)</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.print(':');</p>
<p class="a">&nbsp;&nbsp;&nbsp; if ( (epoch % 60) &lt; 10 ) {</p>
<p class="a">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // In the first 10 seconds of each minute, we'll want a leading '0'</p>
<p class="a">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Serial.print('0');</p>
<p class="a">&nbsp;&nbsp;&nbsp; }</p>
<p class="a">&nbsp;&nbsp;&nbsp; Serial.println(epoch % 60); // print the second</p>
<p class="a">&nbsp; }</p>
<p class="a">&nbsp; // wait ten seconds before asking for the time again</p>
<p class="a">&nbsp; delay(10000);</p>
<p class="a">}</p>
<p class="a">&nbsp;</p>
<p class="a">// send an NTP request to the time server at the given address</p>
<p class="a">unsigned long sendNTPpacket(char* address)</p>
<p class="a">{</p>
<p class="a">&nbsp; // set all bytes in the buffer to 0</p>
<p class="a">&nbsp; memset(packetBuffer, 0, NTP_PACKET_SIZE);</p>
<p class="a">&nbsp; // Initialize values needed to form NTP request</p>
<p class="a">&nbsp; // (see URL above for details on the packets)</p>
<p class="a">&nbsp; packetBuffer[0] = 0b11100011;&nbsp;&nbsp; // LI, Version, Mode</p>
<p class="a">&nbsp; packetBuffer[1] = 0;&nbsp;&nbsp;&nbsp;&nbsp; // Stratum, or type of clock</p>
<p class="a">&nbsp; packetBuffer[2] = 6;&nbsp;&nbsp;&nbsp;&nbsp; // Polling Interval</p>
<p class="a">&nbsp; packetBuffer[3] = 0xEC;&nbsp; // Peer Clock Precision</p>
<p class="a">&nbsp; // 8 bytes of zero for Root Delay &amp; Root Dispersion</p>
<p class="a">&nbsp; packetBuffer[12]&nbsp; = 49;</p>
<p class="a">&nbsp; packetBuffer[13]&nbsp; = 0x4E;</p>
<p class="a">&nbsp; packetBuffer[14]&nbsp; = 49;</p>
<p class="a">&nbsp; packetBuffer[15]&nbsp; = 52;</p>
<p class="a">&nbsp;</p>
<p class="a">&nbsp; // all NTP fields have been given values, now</p>
<p class="a">&nbsp; // you can send a packet requesting a timestamp:</p>
<p class="a">&nbsp; Udp.beginPacket(address, 123); //NTP requests are to port 123</p>
<p class="a">&nbsp; Udp.write(packetBuffer, NTP_PACKET_SIZE);</p>
<p class="a">&nbsp; Udp.endPacket();</p>
<p class="a">}</p>
<p class="a">&nbsp;</p>
</td>
</tr>
</tbody>
</table>
<p>如下圖所示,讀者可以看到本次實驗-網路校時測試程式結果畫面。</p>
<p style="text-align: center;"><img style="display: block; margin-left: auto; margin-right: auto;" src="https://cdn2-techbang.pixfs.net/system/images/403126/original/0455be9ace2e3d86bdd6d380fec01107.jpg?1499657132" alt="" />▲圖 3網路校時測試程式結果畫面</p>
<h3 style="text-align: left;">後續</h3>
<p>本篇為『物聯網系統開發系列』系列之準確掌握即時正確的時間,主要內容是要讓讀者使用創客神器 Arduino 開發板,透過乙太網路擴充板(Ethernet Shield)的網際網路連線之強大功能,可以讓 Arduino 開發板取得 NTP 伺服器之時間內容,進而將這個基礎理念與技術,進階運用到雲端平台中時間同步與校正的機制,進而在物聯網開發中,成為一個技術的核心能力,乃是筆者本篇內容想傳達的創作概念。</p>
<p>筆者本系列是針對非資訊、電機、電子等 Makers 攥寫的物聯網系統開發系列,這四、五年來在物聯網系統開發領域寫書、發表文章、辦展、授課,常遇到許多學子訓練不足,以交作業的心態來學習,並沒有把程式底子打好。</p>
<p>後續筆者還會繼續發表『物聯網系統開發系列』系列的文章,在未來我們可以創造出更優質,更具未來性的物聯網(Internet of Thing:IOT)產品開發相關技術。</p>
<p>敬請期待更多的文章。</p>
<h3>作者介紹</h3>
<table class="MsoNormalTable" style="border-collapse: collapse; mso-yfti-tbllook: 1184; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt;" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td style="width: 414.85pt; border: solid windowtext 1.0pt; mso-border-alt: solid windowtext .5pt; padding: 0cm 5.4pt 0cm 5.4pt;" valign="top" width="691">
<p class="MsoNormal"><strong><span style="font-family: 細明體;">曹永忠<span lang="EN-US"> (Yung-Chung Tsao) </span>,</span></strong><span style="font-family: 細明體;">目前為自由作家暨專業<span lang="EN-US">Maker</span>,專研於軟體工程、軟體開發與設計、物件導向程式設計,商品攝影及人像攝影。長期投入創客運動、資訊系統設計與開發、企業應用系統開發、軟體工程、新產品開發管理、商品及人像攝影等領域,並持續發表作品及相關專業著作。<span lang="EN-US">&nbsp;</span></span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: 細明體;">Email:</span><span lang="EN-US" style="font-family: 細明體;"><a href="mailto:[email protected]">[email protected]</a></span><span lang="EN-US" style="font-family: 細明體;">&nbsp;</span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: 細明體;">Line ID</span><span style="font-family: 細明體;">:<span lang="EN-US">dr.brucetsao</span></span></p>
<p class="MsoNormal"><span style="font-family: 細明體;"><span lang="EN-US"><img src="https://cdn1-techbang.pixfs.net/system/images/403134/original/4874249a9c7450486cc67d7f2e407995.jpg?1499657601" alt="" /><br /></span></span></p>
<p class="MsoNormal"><span style="font-family: 細明體;">作者網站:</span><span lang="EN-US" style="font-family: 細明體;"><a href="https://www.cs.pu.edu.tw/~yctsao/">https://www.cs.pu.edu.tw/~yctsao/</a></span><span lang="EN-US" style="font-family: 細明體;">&nbsp;</span></p>
<p class="MsoNormal" style="text-align: left;"><span style="font-family: 細明體;">臉書社群</span><span lang="EN-US" style="font-family: 細明體;">(Arduino.Taiwan)</span><span style="font-family: 細明體;">:</span><a style="font-family: 細明體; font-size: 10pt;" href="https://www.facebook.com/groups/Arduino.Taiwan/">https://www.facebook.com/groups/Arduino.Taiwan/<span style="font-size: 12.0pt; mso-font-kerning: 1.0pt;">&nbsp;</span></a></p>
<p class="MsoNormal"><span lang="EN-US" style="font-family: 細明體; mso-no-proof: yes;">Github</span><span style="font-family: 細明體; mso-no-proof: yes;">網站</span><span style="font-family: 細明體;">:</span><span lang="EN-US" style="font-family: 細明體;"><span style="font-size: 10.0pt; mso-font-kerning: 0pt;"><a href="https://github.com/brucetsao/">https://github.com/brucetsao/</a></span></span></p>
<p><strong>吳佳駿 (Chia-Chun Wu)</strong>,國立中興大學資訊科學與工程學系博士,現任教於國立金門大學工業工程與管理學系專任助理教授,目前兼任國立金門大學計算機與網路中心資訊網路組組長,主要研究為軟體工程與應用、行動裝置程式設計、物件導向程式設計、網路程式設計、動態網頁資料庫、資訊安全與管理。</p>
<p>Email:&nbsp;<a href="mailto:[email protected]">[email protected]</a></p>
<p><strong>許智誠 (Chih-Cheng Hsu)</strong>,美國加州大學洛杉磯分校(UCLA) 資訊工程系博士,曾任職於美國IBM等軟體公司多年,現任教於中央大學資訊管理學系專任副教授,主要研究為軟體工程、設計流程與自動化、數位教學、雲端裝置、多層式網頁系統、系統整合。</p>
<p>Email:&nbsp;<a href="mailto:[email protected]">[email protected]</a></p>
<p><strong>蔡英德 (Yin-Te Tsai)</strong>,國立清華大學資訊科學系博士,目前是靜宜大學資訊傳播工程學系教授、靜宜大學計算機及通訊中心主任,主要研究為演算法設計與分析、生物資訊、軟體開發、視障輔具設計與開發。</p>
Email:<a href="mailto:[email protected]">[email protected]<br /></a> </td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>參考文獻:</strong></p>
<p>&nbsp;曹永忠. (2016a). AMEBA透過網路校時RTC時鐘模組. <em>智慧家庭.</em>&nbsp; Retrieved from <a href="http://makerpro.cc/2016/03/using-ameba-to-develop-a-timing-controlling-device-via-internet/">http://makerpro.cc/2016/03/using-ameba-to-develop-a-timing-controlling-device-via-internet/</a></p>
<p class="EndNoteBibliography">曹永忠. (2016b). 智慧家庭:PM2.5空氣感測器(上網篇:啟動網路校時功能). <em>智慧家庭.</em>&nbsp; Retrieved from <a href="https://vmaker.tw/archives/7305">https://vmaker.tw/archives/7305</a></p>
<p class="EndNoteBibliography">曹永忠. (2016c). 智慧家庭實作:ARDUINO 永遠的時間靈魂-RTC時鐘模組. <em>智慧家庭.</em>&nbsp; Retrieved from <a href="http://www.techbang.com/posts/40838">http://www.techbang.com/posts/40838</a></p>
<p class="EndNoteBibliography">曹永忠, 吳佳駿, 許智誠, &amp; 蔡英德. (2017a). 【物聯網系統開發】運用 ARDUINO 乙太網路擴充板建立簡單網頁伺服器. <em>智慧家庭.</em>&nbsp; Retrieved from <a href="http://www.techbang.com/posts/51518-internet-system-using-arduino-ethernet-add-on-board-to-build-a-simple-web-server">http://www.techbang.com/posts/51518-internet-system-using-arduino-ethernet-add-on-board-to-build-a-simple-web-server</a></p>
<p class="EndNoteBibliography">曹永忠, 吳佳駿, 許智誠, &amp; 蔡英德. (2017b). 蔡英德,【物聯網系統開發】運用 ARDUINO 乙太網路擴充板建立用戶端工具程式. <em>智慧家庭.</em>&nbsp; Retrieved from <a href="http://www.techbang.com/posts/52039-internet-system-development-established-client-tools-using-arduino-ethernet-add-on-board-procedures">http://www.techbang.com/posts/52039-internet-system-development-established-client-tools-using-arduino-ethernet-add-on-board-procedures</a></p>
<p class="EndNoteBibliography">曹永忠, 許智誠, &amp; 蔡英德. (2015). 創客神器ARDUINO到底是什麼呢?.&nbsp;&nbsp; Retrieved from <a href="http://makerdiwo.com/archives/1893">http://makerdiwo.com/archives/1893</a></p>
</blockquote><a href="http://www.facebook.com/TKbang">加入T客邦Facebook粉絲團</a><img src="http://feeds.feedburner.com/~r/techbang/~4/Z8lVChsoM34" height="1" width="1" alt=""/>