最終更新日 2020年4月28日
【STM32miniShield】基板を使用したGPS TimeServerを紹介します。
表示を4-DigiLED Display(TM1637)、CASEをサナダ精工 トレーディングカードケースの例です。挙動:
- 電源を入れた直後は4-DigiLEDに "00:00" と表示します。
- 内部時計が起動し 9:00 から時刻を刻みます。: を秒毎に点滅します。※GMT+9の場合
- GPSを捕らえるとLED1が点滅します。時刻列を得たら正しい時刻を表示するようになります。
※GPSを捕らえるのに10分程度掛かる場合があります。- NTPの確認をする前にIPアドレスを確認してください。※添付スケッチでは初期値は192.168.0.206です。
- 予めIPアドレスが把握できていれば、コマンドプロンプトからpingを通すことで確認出来ます。ex ping 192.168.0.206
- arp -a で一覧を求め、該当するMACアドレスからIPアドレスを特定する事も可能ですが、一度でもpingを通さないと一覧に出てこない場合があります.
- ブラウザにて確認することも可能です。http://192.168.0.206/setup を実行して、セットアップ画面にて確認出来ます。
実機のMACアドレスやIPアドレスに食い違いがある場合は、直接数値を編集してください。そして、『SUBMIT』ボタンを押してファームウエアを更新してください。その場合、変更点を反映させるためには【STM32miniShield】をリセットによる再起動が必要です。- NTPサーバとの時刻合わせは、Windowsの場合、『インターネット時刻設定』を開いて、NTPサーバのIPアドレスを入力し、『今すぐ更新』を実行してください。
![]()
- 既にPCにNTPサーバが登録されている場合、コマンドラインから更新可能です。
w32tm /resync
アプリケーション起動時に、このコマンドを実行することで定期的な時刻同期が可能という事を意味します。
![]()
- w32tm /query /peers
で登録先NTPサーバを確認しています。ここに単一のLocalNTPサーバが登録されていれば、装置内のPC機器の時刻合わせが可能となります。
残り時間=603015秒というのは、タイムスケジューラに1週間に一度同期を掛けるという設定がされていることを意味します。
主参照-電波時計で同期 と有ります。設定上 GPSに直結の場合は stratum 1 を設定しろとあり、そうしています。- w32tm /tz
でタイムゾーンを確認しています。基本的にNTPサーバはUTCですので、日本で時刻表示するためにはタイムゾーンを考慮する必要があります。その設定値を確認しています。- w32tm /resync
で『今すぐ更新』相当となります。
アプリケーション起動時にこのコマンドを実行することで時刻合わせが可能となります。
※定期的にresyncを実行するために、OSのタイムスケジューラに登録することが可能です。
![]()
外観
手加工でサナダ精工 トレーディングカードケースを加工し、STM32miniShield用のエンクロージャを作ってみました。
材質がPPだとCNCでの追加工は難しそうです。
ボール盤とナイフでの加工になるかと思います。このケースの寸法は、99W×80H×40Dmmです。
内寸は64×94のカードが100枚(35mm)収納できるというのが目安のケースです。
透明度の高いケースのため内臓物がよく見えます。
![]()
![]()
![]()
@ | ケース | \160+16 | サナダ精工 トレーディングカードケースL-8997 |
A | STM32miniShield | \1500+150 | |
B | NIC | \2442 |
USR-ES1が入手出来れば安価に出来ます。 AliExpressから入手すれば600円程度でも入手可能です。 |
C | GPS | \998 |
AmazonJapanでCrosstour GPSモジュールを入手しました。 AliExpressから入手すれば500円程度でも入手可能です。 |
D | 4-DigiLED Display | \300 |
高さ0.56"のものです。 AmazonJapanだと5色で1600円というのがありました。 AliExpressから入手すれば100円程度でも入手可能です。 |
E | φ3.5ステレオジャック | \65 |
秋月で扱っている丸信のジャックの方がお勧めです。 https://akizukidenshi.com/catalog/g/gC-09630/ |
F | JST-XH5Pハウジング | \10×1 |
秋月から購入するのが一番リーズナブルだと思います。 コンタクトは100個入りの場合です。10個入りもあるのですが、結構失敗します。 工具はIZOKEE 圧着ペンチをお勧めします。 |
G | JST-XH4Pハウジング | \5×2 | |
H | JST-XHコンタクト | \200 | |
I | ACアダプタ | \1100 |
秋月 AD-B50P250 https://akizukidenshi.com/catalog/g/gM-10507/ |
J | USBケーブル | \500 | TOPK https://ja.aliexpress.com/item/32912994331.html |
合計 | 約\7500 |
スケッチ:
このコードを実行するに当たり追加が必要なライブラリは、
/* * 2020/4/27 T.Wanibe * NTP Time Serverの時刻表示をTM1637経由で4桁7セグLED版に変更しました。 * アンテナが外れた時にLED2が消灯するようにした。TimeServerとしては内部時計の値を返します。 * 起動時のLED&KEYの表示を変更しました。GPSの受信が曖昧な内は『HELLO』表示とし、受信後に時刻表示するように * 変更しました。 * 新たにGPSモジュールを購入しました。Crosstour CR900向けの3.5mmステレオジャックタイプです。 * こちらからは$GPのセンテンスが流れてきます。ところが、$GPZDAが流れてこないのです。多分設定で限定した * センテンスしか流さないように流さないようにしているものと考えました。 * と云う事は、$GxRMCを解析して日時情報を求めるのが正解なのだと痛感しました。修正します。 * LED2が点灯したら内部時計設定完了です。 * 設定画面がほしいので再度挑戦 * ver2のコードで問題を確認したため修正します。タイムゾンーンを単に+9してしまうとAM9に日付が変わります。 * これはダメです。正しいLocalタイム対応に修正します。※内部時計はLocalTimeに変更 * 内部時計はGMTのまま扱い、LED&KEYに表示するときに対策します。そうしないとタイムサーバとしては問題ありです。 * このコードはGPSタイムサーバを構築したくて検証中のコードです * NMEA0183のフォーマットを極力単純化して時刻取得するようにした。 * まずはNTPサーバを構築して内部時間を返すものを検討 * 当初ヘッダが$GPGGAに対応するようにしていたが、購入したGPSが$GNGGAのコードしか出さなかったので修正した。 * $GNxxxはロシア版GPSであるGLONASSのセンテンスとのようです。 * $ GNGGA、185824.00,2605.0247881、N、08014.3097082、W、4,14,0.7、-24.343、M ,,, 0.59,0402 * 36 * 185824.00 協定世界時(UTC)での時刻。 hhmmss.uu * 2605.0247881 緯度。dddmm.mmmm * N N = 北緯、South = 南緯 * 08014.3097082 経度。dddmm.mmmm * W E = 東経、W = 西経 * 4 位置特定品質 * 14 使用衛星数 * 0.7 水平精度低下率 * -24.343 アンテナの海抜高さ * M [m] * 0.59 * 0402 差動基準地点ID * * 36 チェックサム * GNGGAには日付情報が無いためGNRMCを使うべきとあった。 * $GNRMC,001903.000,A,3430.61495,N,13316.67709,E,0.00,100.54,120220,,,A,V*00 * RMC(時刻、位置、日付)$__RMC,hhmmss.ss,a, ddmm.mm ,a, ddmm.mm ,a,x.x,x.x,mmddyy,a,a,*hh<CR><LF> * $ センテンスの開始 * __RMC アドレスフィールド( 5 文字)、 __ は「 GN 」「 GP 」など * hhmmss.ss 時刻( UTC ) hh 時 mm 分 ss.ss 秒 * a ステータス * A=data valid * V=navigation receiver warming * ddmm.mm 経度 dd 度 mm.mm 分 * a 北緯( N )または南緯( S ) * ddmm.mm 経度 dd 度 mm.mm 分 * a 東経( E )または西経( W ) * x.x 対地速度( knot ) * x.x 対地コース(進路)(度) * mmddyy 日付 yy 年 mm 月 dd 日 * x.x 偏差 * a 偏差の方向 W または E * a モード * A :単独 * D :ディファレンシャルモード * E : Estimated (dead reckoning), * M :マニュアル入力 * S :シミュレーター入力 * N :無効 * *hh チェックサム( $ から * の間) * <CR><LF> センテンスの終了 * センテンス長が固定長ではないようなので一寸パスします。 * 結局GGA起動時のみ$GNZDAもチェックして日付情報を登録します。 * $GNZDA * ZDA(時刻、日付)$__ZDA,x.x,x.x,x.x,*hh<CR><LF> * $ センテンスの開始 * __ZDA アドレスフィールド( 5 文字)、 __ は「 GP 」など * hhmmss.ss 時刻( UTC ) hh 時 mm 分 ss.ss 秒 * dd 日 * mm 月 * yyyy 西暦 * hh 時(ローカルタイム) * mm 分(ローカルタイム) * *hh チェックサム( $ から * の間) * <CR><LF> センテンスの終了 * 最大131072バイトのフラッシュメモリのうち、スケッチが42576バイト(32%)を使っています。 * 最大20480バイトのRAMのうち、グローバル変数が4832バイト(23%)を使っていて、ローカル変数で15648バイト使うことができます。 */ #include <SPI.h> // needed for Arduino versions later than 0018 #include <Ethernet3.h> #include <EthernetUdp3.h> // UDP library from: bjoern@cs.stanford.edu 12/30/2008 //#include <UBLOX.h> //#include <TM1638.h> #include <TM1637.h> #include <RTClock.h> #include <EEPROM.h> #include <avr/pgmspace.h> #include <TextFinder.h> //WebSetting // Time Server Port #define ON 1 #define OFF 0 #define NTP_PORT 123 #define vers "NTP GPS STM32miniShield 3.5" #define debug true //PIN #define W550io_Rst PA8 //NIC_Reset #define SPI1_NSS_PIN PA4 //NIC_ChipSelec #define UpdateInterval 86400 //更新間隔 //#define dataPin PB4 //LED&KEY //#define clockPin PB3 //LED&KEY //#define strobePin PA15 //LED&KEY #define CLK PB3 //pins definitions for TM1637 and can be changed to other ports #define DIO PB4 #define BUILTIN_LED PC13 #define LED1 PB8 #define LED2 PB9 #define SERIAL_RX_BUFFER_SIZE 256 #define HttpPort 80 //HTTP #define DELIMITER "," #define debug 1 // Time Server MAC address byte mac[] = {0x00,0x08,0xDC,0x54,0x4D,0xD6}; //Wiznet byte ip[] = {192, 168, 0, 206}; byte dns_server[] = {192, 168, 0, 1}; byte gateway[] = {0, 0, 0, 0}; byte subnet[] = {255, 255, 255, 0}; static const int NTP_PACKET_SIZE = 48; byte packetBuffer[NTP_PACKET_SIZE]; //データを送受信するためのバッファ int8_t TimeDisp[] = {0x00,0x00,0x00,0x00}; unsigned char ClockPoint = 1; unsigned char Update; unsigned long halfsecond = 0; int year = 2020; byte month = 2; byte day = 11; byte hour = 13; byte minute = 10; byte second = 00; byte hundredths = 00; unsigned long gDate, gTime, age; uint32_t timestamp, tempval; // LM: GPS message parsing const char EOL = 10; // End-of-line const int MSGLEN = 67; // GNRMCメッセージの長さ、66文字の印刷可能文字+ 改行コード String tmpMsg = ""; String gnrmcMsg = ""; char chkHader[] = "RMC"; //このバージョンから3文字に変更 // LM: Date/time handling String sUTD = ""; // UT Date String sUTC = ""; // UT Time String HTTP_req= ""; // stores the HTTP request const int CENTURY = 2000; // LM: Alternative debug const boolean MYDEBUG = true; char STRBUF[50]; int timezone = 9; //TimeZone 日本は+9です。 const uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };//const or compiler complains const unsigned long seventyYears = 2208988800UL; // to convert unix time to epoch long gLoopCount = 3540; //1時間毎に更新予定 最初は1分後に更新したくて初期値を調整 //オブジェクトコンストラクタ EthernetServer webServer(HttpPort); EthernetClient client; //TM1638 LedAndKey(dataPin, clockPin, strobePin);//データピンPB4、クロックピンPB3、およびストローブピンPA15でモジュールを定義する TM1637 tm1637(CLK,DIO); RTClock rtclock (RTCSEL_LSE); //RTC初期化 tm_t tmm; EthernetUDP Udp; //イーサネットUDPインスタンス char buffer[100]; // これはすべて切り詰められたHTMLコードです。 エディターでHTMLコードを作成し、バッファに収まるように切り分けます。 // HTMLをすべての場所で切り取ることができますが、\ "部分ではできません。HTML内でsimple"の代わりに\ "を // 使用する必要があります。そうしないと機能しません。 const byte ID = 0x96; bool GpsEnable = false; //---------------------------------- void LANSetup(){ int idcheck = EEPROM.read(0); Serial.print(F("LocalID = 0x"));Serial.println(idcheck,HEX); if (idcheck == ID){ //idがIDと同じ値の場合、 //これは、このスケッチがシールドを設定するために使用されたことを意味します。 //EERPOMの値を読み取ってシールドを設定します。 for (int i = 0; i < 6; i++){ mac[i] = EEPROM.read(i+1); } for (int i = 0; i < 4; i++){ ip[i] = EEPROM.read(i+7); } for (int i = 0; i < 4; i++){ subnet[i] = EEPROM.read(i+11); } for (int i = 0; i < 4; i++){ gateway[i] = EEPROM.read(i+15); } }else{ //idが一致しない場合、初期値を書き込む事にします。 for (int i = 0 ; i < 6; i++){ EEPROM.write(i + 1,mac[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 7, ip[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 11, subnet[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 15, gateway[i]); } // IDを既知のビットに設定します。したがって、Arduinoをリセットすると、EEPROM値が使用されます。 EEPROM.write(0, ID); } Ethernet.begin(mac, ip); // Ethernet.begin(mac); //DHCPの場合 // Ethernet.begin(mac, ip, subnet); //SubnetMaskを意識した場合。 // Ethernet.begin(mac, ip, subnet, gateway); //gatewayを意識した場合。 } //-------------------------- void SetWebPage( EthernetClient client){ client.print(F("<!DOCTYPE HTML PUBLIC \"\"><html><HEAD><META http-equiv=\"Content-Type\" charset=UTF-8\">")); client.print(F("<META http-equiv=\"Content-Style-Type\"><TITLE>")); client.print(vers); client.print(F(" Setup Page</TITLE></HEAD>")); client.print(F("<BODY marginwidth=\"0\" marginheight=\"0\" leftmargin=\"0\" style=\"margin: 0; padding: 0;\"><BLOCKQUOTE><BLOCKQUOTE>")); client.print(F("<table bgcolor=\"#17A1A5\" border=\"0\" width=\"100%\" cellpadding=\"1\" style=\"font-family:Verdana;color:#ffffff;font-size:12px;\">")); client.print(F("<tr><td> ")); client.print(vers); client.print(F(" Setup Page</td></tr></table><br>")); // client.print(F("<script>function hex2num (s_hex) {eval(\"var n_num=0X\" + s_hex);return n_num;}</script>")); client.print(F("<FORM><input type=\"hidden\" name=\"SBM\" value=\"1\"><table><tr><td>MAC:</td><td>")); client.print(F("<input id=\"T1\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT1\" value=\"")); client.print(mac[0],HEX); client.print(F("\">.<input id=\"T3\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT2\" value=\"")); client.print(mac[1],HEX); client.print(F("\">.<input id=\"T5\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT3\" value=\"")); client.print(mac[2],HEX); client.print(F("\">.<input id=\"T7\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT4\" value=\"")); client.print(mac[3],HEX); client.print(F("\">.<input id=\"T9\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT5\" value=\"")); client.print(mac[4],HEX); client.print(F("\">.<input id=\"T11\" type=\"text\" size=\"3\" maxlength=\"2\" name=\"DT6\" value=\"")); client.print(mac[5],HEX); // client.print(F("\"><input id=\"T2\" type=\"hidden\" name=\"DT1\"><input id=\"T4\" type=\"hidden\" name=\"DT2")); client.print(F("\"><input id=\"T6\" type=\"hidden\" name=\"DT3\"><input id=\"T8\" type=\"hidden\" name=\"DT4")); client.print(F("\"><input id=\"T10\" type=\"hidden\" name=\"DT5\"><input id=\"T12\" type=\"hidden\" name=\"DT6")); client.print(F("\"></td></tr><tr><td>IP:</td><td><input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT7\" value=\"")); client.print(ip[0],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT8\" value=\"")); client.print(ip[1],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT9\" value=\"")); client.print(ip[2],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT10\" value=\"")); client.print(ip[3],DEC); // client.print(F("\"></td></tr><tr><td>MASK: </td><td><input type= \"text\" size=\"3\" maxlength=\"3\" name=\"DT11\" value=\"")); client.print(subnet[0],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT12\" value=\"")); client.print(subnet[1],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT13\" value=\"")); client.print(subnet[2],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT14\" value=\"")); client.print(subnet[3],DEC); // client.print(F("\"></td></tr><tr><td>GW: </td><td><input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT15\" value=\"")); client.print(gateway[0],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT16\" value=\"")); client.print(gateway[1],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT17\" value=\"")); client.print(gateway[2],DEC); client.print(F("\">.<input type=\"text\" size=\"3\" maxlength=\"3\" name=\"DT18\" value=\"")); client.print(gateway[3],DEC); client.print(F("\"></td></tr><tr><td><br></td></tr><tr><td><input id=\"button1\"type=\"submit\" value=\"SUBMIT\" ")); // client.print(F("Onclick=\"document.getElementById('T2').value ")); client.print(F("= hex2num(document.getElementById('T1').value);")); client.print(F("document.getElementById('T4').value = hex2num(document.getElementById('T3').value);")); client.print(F("document.getElementById('T6').value = hex2num(document.getElementById('T5').value);")); client.print(F("document.getElementById('T8').value = hex2num(document.getElementById('T7').value);")); client.print(F("document.getElementById('T10').value = hex2num(document.getElementById('T9').value);")); client.print(F("document.getElementById('T12').value = hex2num(document.getElementById('T11').value);\"")); // client.print(F("></td><td></td></tr></form></table></BLOCKQUOTE></BLOCKQUOTE></BODY></html>")); } //-------------------------- void checkWebPage( EthernetClient client) { Serial.println(F("new webClient")); if (client) { TextFinder finder(client ); while (client.connected()) { //digitalWrite(LED, HIGH); if (client.available()) { //この部分はすべてのテキスト検索を行います。 if( finder.find("GET /") ) { // 「setup」とい語が見つかった場合は、さらに探してください。 // その単語が見つからない場合は、検索を停止して先に進みます。 // これにより、後でスケッチに独自のWebページを配置できます。 if (finder.findUntil("setup", "\n\r")){ // 「SBM」という単語が見つかった場合は、さらに探し続けます。 // その言葉が見つからない場合は、探して停止します。 // SUBMITボタンが押されていない、何も押されていないことを意味します // セットアップページが構築されている場所に移動し、クライアントのブラウザに表示します。 if (finder.findUntil("SBM", "\n\r")){ byte SET = finder.getValue(); // これで、「DT」という文字を探している間に、「DT」の後ろにあるすべての数字を覚えて、 // 値を一致させて、mac、ip、subnet、およびgatewayに入れる必要があります。 while(finder.findUntil("DT", "\n\r")){ int val = finder.getValue(); // 「DT」のvalが1?6の場合、対応する値はMAC値でなければなりません。 if(val >= 1 && val <= 6) { mac[val - 1] = finder.getValue(); } // 「DT」のvalが7?10の場合、対応する値はIP値でなければなりません。 if(val >= 7 && val <= 10) { ip[val - 7] = finder.getValue(); } // 「DT」のvalが11?14の場合、対応する値はMASK値でなければなりません。 if(val >= 11 && val <= 14) { subnet[val - 11] = finder.getValue(); } // 「DT」のvalが15?18の場合、対応する値はGW値でなければなりません。 if(val >= 15 && val <= 18) { gateway[val - 15] = finder.getValue(); } } // すべてのデータを取得したので、EEPROMに保存できます for (int i = 0 ; i < 6; i++){ EEPROM.write(i + 1,mac[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 7, ip[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 11, subnet[i]); } for (int i = 0 ; i < 4; i++){ EEPROM.write(i + 15, gateway[i]); } // IDを既知のビットに設定します。したがって、Arduinoをリセットすると、EEPROM値が使用されます。 EEPROM.write(0, ID); // すべてのデータがEEPROMに書き込まれている場合、arduinoをリセットする必要があります。 //ハードウェアリセットボタンを使用する必要があります。 } // この時点から、セットアップページの構築を開始し、クライアントのブラウザーに表示できます。 client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); // SetWebPage(client); break; } } client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(); // put your own html from here on client.print("IT WORKS: go to "); client.print(ip[0],DEC); for (int i= 1; i < 4; i++){ client.print("."); client.print(ip[i],DEC); } client.print("/setup"); // put your own html until here break; } } //digitalWrite(LED, LOW); delay(1); client.stop(); } } //--------------------- void processNTP() { //利用可能なデータがある場合は、パケットを読み取ります //https://blog.goo.ne.jp/hiro239415/e/c426a545863921a13a9d6a70b7ae4484 //NTP_Packet (48Byte) int packetSize = Udp.parsePacket(); if(packetSize){ Serial.println(packetSize); Udp.read(packetBuffer,NTP_PACKET_SIZE); IPAddress Remote = Udp.remoteIP(); int PortNum = Udp.remotePort(); //if (!getGPSdata()) return; packetBuffer[0] = 0b00100100; // 閏秒警告なしLI, 4:SNTPサーバ(Version), 4:サーバ(Mode) packetBuffer[1] = 1 ; // 1:一次基準源(GPS等)(stratum packetBuffer[2] = 6 ; // ポーリング64秒デフォルト値 packetBuffer[3] = 0xFD; // 精度 -3 packetBuffer[7] = 0; // ルート遅延 packetBuffer[8] = 0; packetBuffer[9] = 8; packetBuffer[10] = 0; packetBuffer[11] = 0; // ルート分散 packetBuffer[12] = 0; packetBuffer[13] = 0xC; packetBuffer[14] = 0; //gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age); //crack(sUTD, sUTC); //timestamp = numberOfSecondsSince1900Epoch(year,month,day,hour,minute,second); rtclock.getTime(tmm); timestamp = numberOfSecondsSince1900Epoch(tmm.year+1970,tmm.month,tmm.day-1,tmm.hour,tmm.minute,tmm.second,timezone); #if debug Serial.print(timestamp);Serial.print(F(":Time synchronization Request received\n")); //print_date(gps); #endif tempval = timestamp; packetBuffer[12] = 71; //"G"; packetBuffer[13] = 80; //"P"; packetBuffer[14] = 83; //"S"; packetBuffer[15] = 0; //"0"; // reference timestamp packetBuffer[16] = (tempval >> 24) & 0XFF; tempval = timestamp; packetBuffer[17] = (tempval >> 16) & 0xFF; tempval = timestamp; packetBuffer[18] = (tempval >> 8) & 0xFF; tempval = timestamp; packetBuffer[19] = (tempval) & 0xFF; packetBuffer[20] = 0; packetBuffer[21] = 0; packetBuffer[22] = 0; packetBuffer[23] = 0; //Receive Timestamp 受信タイムスタンプ = サーバに要求が届いた時間 packetBuffer[24] = packetBuffer[40]; packetBuffer[25] = packetBuffer[41]; packetBuffer[26] = packetBuffer[42]; packetBuffer[27] = packetBuffer[43]; packetBuffer[28] = packetBuffer[44]; packetBuffer[29] = packetBuffer[45]; packetBuffer[30] = packetBuffer[46]; packetBuffer[31] = packetBuffer[47]; //Transmit Timestamp 送信タイムスタンプ = サーバからクライアントに送られた時間 packetBuffer[32] = (tempval >> 24) & 0XFF; tempval = timestamp; packetBuffer[33] = (tempval >> 16) & 0xFF; tempval = timestamp; packetBuffer[34] = (tempval >> 8) & 0xFF; tempval = timestamp; packetBuffer[35] = (tempval) & 0xFF; // packetBuffer[36] = 0; packetBuffer[37] = 0; packetBuffer[38] = 0; packetBuffer[39] = 0; //transmit_timestamp_seconds packetBuffer[40] = (tempval >> 24) & 0XFF; tempval = timestamp; packetBuffer[41] = (tempval >> 16) & 0xFF; tempval = timestamp; packetBuffer[42] = (tempval >> 8) & 0xFF; tempval = timestamp; packetBuffer[43] = (tempval) & 0xFF; //transmit_timestamp_fractions packetBuffer[44] = 0; packetBuffer[45] = 0; packetBuffer[46] = 0; packetBuffer[47] = 0; // Reply to the IP address and port that sent the NTP request Udp.beginPacket(Remote, PortNum); Udp.write(packetBuffer,NTP_PACKET_SIZE); Udp.endPacket(); } } //---------------NTP since 1900/01/01 static unsigned long int numberOfSecondsSince1900Epoch(uint16_t y, uint8_t m, uint8_t d, uint8_t h, uint8_t mm, uint8_t s,int timezone) { int leapAdjustment = 0; if (y >= 1970) { y -= 1970; leapAdjustment = 2; // LM: 1970 was NOT a leap year! } uint16_t days = d; for (uint8_t i = 1; i < m; ++i) days += pgm_read_byte(daysInMonth + i - 1); /* if (m > 2 && y % 4 == 0) ++days; */ if (m > 2 && (y + leapAdjustment) % 4 == 0) ++days; // LM: Weak but okay for the present days += 365 * y + (y + 3) / 4 - 1; return days*24L*3600L + (h-timezone)*3600L + mm*60L + s + seventyYears; } //------------------ bool readSensor(void){ if (Serial1.available()) { Serial1.readStringUntil('\n'); //読み捨てて先頭から読めるようにする String nmea = Serial1.readStringUntil('\n'); #if debug Serial.println(nmea); #endif String nmeames = nmea.substring(3,6); // $GP*** int count = 0; while(!nmeames.equals(chkHader)){ nmea = Serial1.readStringUntil('\n'); nmeames = nmea.substring(3,6); // $GP*** digitalWrite(LED1, count++%2); #if debug Serial.print(nmea);Serial.print(F("\t"));Serial.println(nmeames); #endif } int nextPos = 0; if ( nmeames.equals(chkHader)){ // get UTC nextPos = nmea.indexOf(DELIMITER);//int indexOf(char ch, unsigned int fromIndex) const; String shh = nmea.substring(nextPos+1,nextPos+3); String smm = nmea.substring(nextPos+3,nextPos+5); String sss = nmea.substring(nextPos+5,nextPos+7); nextPos = nmea.indexOf(DELIMITER,nextPos+1); //skip UTC nextPos = nmea.indexOf(DELIMITER,nextPos+1); //skip Status nextPos = nmea.indexOf(DELIMITER,nextPos+1); //skip 緯度 nextPos = nmea.indexOf(DELIMITER,nextPos+1); //skip 北緯か南緯か nextPos = nmea.indexOf(DELIMITER,nextPos+1); //skip 経度 nextPos = nmea.indexOf(DELIMITER,nextPos+1); //skip 東経か西経か nextPos = nmea.indexOf(DELIMITER,nextPos+1); //skip 地表における移動の速度[knot] nextPos = nmea.indexOf(DELIMITER,nextPos+1); //skip 地表における移動の真方位[deg] String sdd = nmea.substring(nextPos+1,nextPos+3); String smn = nmea.substring(nextPos+3,nextPos+5); String syy = nmea.substring(nextPos+5,nextPos+7); Serial.println("20" + syy + smn + sdd + shh + smm + sss); tmm.hour = shh.toInt(); hour = tmm.hour; tmm.minute = smm.toInt(); minute = tmm.minute; tmm.second = sss.toInt(); second = tmm.second; tmm.day = sdd.toInt(); day = tmm.day -1; tmm.month = smn.toInt(); month = tmm.month; tmm.year = syy.toInt() + 2000 - 1970; year = syy.toInt() + 2000; digitalWrite(LED1, LOW); digitalWrite(LED2, HIGH); if (tmm.year < 30) return false; return true; }else{ digitalWrite(LED1, HIGH); return false; } } } //------------------GPSにアクセスし、取得に成功したら内部時間を更新 bool ReadGPS(){ if(readSensor()) { if(gLoopCount>3600 | !GpsEnable){ rtclock.setTime(tmm); // 時刻の設定 一時間毎に更新 time_t Now = rtclock.getTime() + timezone * 3600; rtclock.setTime(Now); gLoopCount = 1; digitalWrite(LED2, HIGH); GpsEnable = true; } hundredths = 0; // LM: GPS time is always acquired on the second (not used) age = 0; // Not used in this adaptation }else{ delay(100); if(gLoopCount%100) Serial.print(F(".")); else{ Serial.println(F(".")); GpsEnable = false; } } } //----------------- 割込CallBack void TimeUpdate(void){ if(ClockPoint) tm1637.point(POINT_ON); else tm1637.point(POINT_OFF); TimeDisp[0] = tmm.hour / 10; TimeDisp[1] = tmm.hour % 10; TimeDisp[2] = tmm.minute / 10; TimeDisp[3] = tmm.minute % 10; ClockPoint = (~ClockPoint) & 0x01; tm1637.display(TimeDisp); } //------------------ void setup() { Serial.begin(115200); //シリアル通信を開ます。 delay(1000); Serial.println(F("Serial interface started")); pinMode(BUILTIN_LED,OUTPUT); pinMode(LED1,OUTPUT);digitalWrite(LED1, LOW); pinMode(LED2,OUTPUT);digitalWrite(LED2, LOW); //TM1637 init Timmer Setup tm1637.set(); tm1637.init(); Timer1.pause(); // Timer1.setPrescaleFactor(7200); // 72MHz100uSEC Timer1.setOverflow(10000); //1000mSEC Timer1.attachInterrupt(TIMER_UPDATE_INTERRUPT,TimeUpdate); //割り込みサーブルーチンの宣言:TimeUpdate Timer1.setCount(0); //0 Timer1.refresh(); Timer1.resume(); // start Ethernet and UDP:(Ethernet3API) Ethernet.setCsPin(SPI1_NSS_PIN); Ethernet.setRstPin(W550io_Rst); // pinMode(W550io_Rst, OUTPUT); digitalWrite(W550io_Rst, LOW); delay(10); digitalWrite(W550io_Rst, HIGH); Serial.print(F("NIC_Reset\n")); LANSetup(); //Ethernet.begin(mac,ip); Serial.println(F("Ethernet interface started")); Serial.print(F("server is at "));Serial.println(Ethernet.localIP()); webServer.begin(); Udp.begin(NTP_PORT); // #if debug Serial.print("Version:"); Serial.println(vers); #endif //GPS受信機との通信を開始する Serial1.begin(9600); //9600bps固定の場合 ReadGPS(); //GPS取得に成功したら内部時計を更新しています。 rtclock.getTime(tmm); //sprintf(STRBUF,"%02u%02u%02u",tmm.hour,tmm.minute,tmm.second); //LedAndKey.setDisplayToString(STRBUF,0,2); //LedAndKey.setDisplayToString(" HELLO ",0,0); Serial.println(F("EndSetUp")); } //-------------------- void loop() { //参考にしたコードはNTPdは常に動作させ、クライアントから時刻同期要求が有ったら //GPSにアクセスするというモノでした。 //この修正版は、NTPdは常に動作させ、要求があったらBluePillの内部クロックを //返します。 //また、NTPdの周回待ちにGPSにアクセスし、値を取得したら内部時間を更新します。 //delay(500); ReadGPS(); //delay(500); client = webServer.available(); if(client) checkWebPage(client); client.stop(); // コネクションを閉じる。 processNTP(); gLoopCount++; digitalWrite(BUILTIN_LED,gLoopCount%2); //毎秒LEDの点滅処理を実施 rtclock.getTime(tmm); }
免責事項
本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。
商標・登録商標
Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。