GPS TimeServerBox B

最終更新日 2020年4月28日

【STM32miniShield】基板を使用したGPS TimeServerを紹介します。
表示を4-DigiLED Display(TM1637)、CASEをサナダ精工 トレーディングカードケースの例です。

挙動:

外観

手加工でサナダ精工 トレーディングカードケースを加工し、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>&nbsp;"));
        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);
}


戯言(nonsense)に戻る


問い合わせ頁の表示


免責事項

本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。

本ソフトウエアの著作権は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社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。