Ethernet3_UdpNtpClient

最終更新日:2023/3/13

インクルードするライブラリですが、Ethernet3を使用します。このライブラリは安定していると思います。
TM1637用のポートは確保します。

オリジナルコード

/*
 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 <Ethernet3.h>
#include <EthernetUdp3.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[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
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:
    for (;;)
      ;
  }
  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();
}

説明

W5500-EVB-PICO単体で使用するEthernetスケッチです。今回はAdvancedChatServerです。
W5500-EVB-PICOはW5500とRP2040をSPI接続しています。GP16..21が占有されます。
汎用ライブラリの場合、自分でちゃんとPIN指定した方がいいです。
また、W5500のRESETピンがPULL_UPされていないようなので、自分でHIGHに固定する必要がありそうです。

MACアドレスはWIZNET系にしておきます。
IPは192.168.0.210にしておきます。
このスケッチは10秒毎にNTPd(time.nist.gov)にアクセスして時刻を取得してシリアルモニタに表示するモノです。
追加してTM1637に表示する様にレタッチしています。

server is at 192.168.0.210
Seconds since Jan 1 1900 = 3887687097
Unix time = 1678698297
The UTC time is 9:04:57
The JST time is 18:04:57
Seconds since Jan 1 1900 = 3887687108
Unix time = 1678698308
The UTC time is 9:05:08
The JST time is 18:05:08

\

レタッチコード

/*
 * 2023/03/13 T.Wanibe
 * Udp NTP Client
 * ネットワーク タイム プロトコル (NTP) タイム サーバーから時刻を取得する
 * UDP sendPacket および ReceivePacket の使用方法を示します
 * NTP タイム サーバーとそれらとの通信に必要なメッセージの詳細については、
 * http://en.wikipedia.org/wiki/Network_Time_Protocol を参照
 * 最大1044480バイトのフラッシュメモリのうち、スケッチが61332バイト(5%)を使っています。
 * 最大262144バイトのRAMのうち、グローバル変数が8096バイト(3%)を使っていて、ローカル変数で254048バイト使うことができます。
 */
#define          TIMER_INTERRUPT_DEBUG          1
#define         _TIMERINTERRUPT_LOGLEVEL_       4
#include        "RPi_Pico_TimerInterrupt.h"
#include        "TM1637.h"
#include        <SPI.h>
#include        <Ethernet3.h>
#include        <EthernetUdp3.h>
#define         SEG_DP  0x80
#define         ON 1
#define         OFF 0
#define         TIMER0_INTERVAL_MS      1000
#define         SPI_SCK         18
#define         SPI_RX          16
#define         SPI_TX          19
#define         SPI_CS          17
#define         NICReset        20
#define         CLK             28                                              //pins definitions for TM1637 and can be changed to other ports
#define         DIO             27
#define         HTTPport        80
#define         TELNETport      23
#define         NTPport         123
#define         SerialRate      115200
// コントローラの MAC アドレスと IP アドレスを以下に入力します。
// IP アドレスは、ローカル ネットワークによって異なります。
// ゲートウェイとサブネットはオプションです。
byte            mac[]           = {0x00,0x08,0xDC,0x54,0x4D,0xE0};              //WIZNET
byte            ip[]            = {192, 168, 0, 210};
byte            dns_server[]    = {192, 168, 0, 1};
byte            gateway[]       = {0, 0, 0, 0};
byte            subnet[]        = {255, 255, 255, 0};
int8_t          TimeDisp[]      = {0x00, 0x00, 0x00, 0x00};
unsigned char   ClockPoint      = 1;
unsigned char   Update;
unsigned char   halfsecond      = 0;
unsigned char   second;
unsigned char   minute          = 0;
unsigned char   hour            = 12;
static int8_t   tube_tab[] = {0x3f, 0x06, 0x5b, 0x4f,
                            0x66, 0x6d, 0x7d, 0x07,
                            0x7f, 0x6f, 0x77, 0x7c,
                            0x39, 0x5e, 0x79, 0x71
                           }; //0~9,A,b,C,d,E,F
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
int             timeZone        = int(9.0 * 3600);
// UDP 経由でパケットを送受信できるようにする UDP インスタンス
EthernetUDP     Udp;
TM1637          tm1637(CLK, DIO);
RPI_PICO_Timer  ITimer0(0);
//--------------
void custom_display(uint8_t bit_addr, uint8_t seg_data) {
        tm1637.start();
        tm1637.writeByte( 0x44 );
        tm1637.stop();
        tm1637.start();
        tm1637.writeByte(bit_addr | 0xc0);
        tm1637.writeByte(seg_data);
        tm1637.stop();
        tm1637.start();
        tm1637.writeByte(tm1637.cmd_disp_ctrl);
        tm1637.stop();
}
//--------------
void TimeUpdate(void) {
        tm1637.point(POINT_OFF);
        TimeDisp[0] = hour / 10;
        TimeDisp[1] = hour % 10;
        TimeDisp[2] = minute / 10;
        TimeDisp[3] = minute % 10;
        if (ClockPoint) {
                custom_display( 0, tube_tab[TimeDisp[0]]);
                custom_display( 1, tube_tab[TimeDisp[1]] | SEG_DP);
                custom_display( 2, tube_tab[TimeDisp[2]]);
                custom_display( 3, tube_tab[TimeDisp[3]]);
        } else {
                custom_display( 0, tube_tab[TimeDisp[0]]);
                custom_display( 1, tube_tab[TimeDisp[1]]);
                custom_display( 2, tube_tab[TimeDisp[2]]);
                custom_display( 3, tube_tab[TimeDisp[3]]);
        }
        Update = OFF;
}
//---------指定されたアドレスのタイム サーバーに NTP 要求を送信します
void    sendNTPpacket(char* address){
        // バッファ内のすべてのバイトを 0 に設定します
        memset(packetBuffer, 0, NTP_PACKET_SIZE);
        // NTP リクエストを形成するために必要な値を初期化します (パケットの詳細については、上記の URL を参照してください)
        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;
        // すべての NTP フィールドに値が与えられたので、タイムスタンプを要求するパケットを送信できます:
        Udp.beginPacket(address, NTPport);                      //NTP requests are to port 123
        Udp.write(packetBuffer, NTP_PACKET_SIZE);
        Udp.endPacket();
}
//----------------
void setup(){
        tm1637.set(7);                           //0..7
        tm1637.clearDisplay();
        pinMode(LED_BUILTIN, OUTPUT);
        digitalWrite(LED_BUILTIN, HIGH);
       // シリアル通信を開き、ポートが開くのを待ちます。
        Serial.begin(SerialRate);
        while (!Serial) {
                ; // シリアルポートが接続されるのを待ちます。
        }
        pinMode(SPI_CS,OUTPUT);
        pinMode(NICReset,OUTPUT);
        // イーサネット接続とサーバーを開始します。
        SPI.setSCK(SPI_SCK);
        SPI.setRX(SPI_RX);
        SPI.setTX(SPI_TX);
        SPI.setCS(SPI_CS);
        SPI.begin();
        // Ethernet.init(pin)を使用してCSピンを設定できます
        Ethernet.setCsPin(SPI_CS);
        Ethernet.setRstPin(NICReset);
        digitalWrite(NICReset,LOW);
        delay(10);
        digitalWrite(NICReset,HIGH);
        Ethernet.init(SPI_CS);
        // イーサネットと UDP を開始する
        Ethernet.begin(mac,ip);
        Serial.print("server is at ");
        Serial.println(Ethernet.localIP());     
        Udp.begin(localPort);
}
//----------------
void loop(){
        sendNTPpacket(timeServer);                      // NTP パケットをタイム サーバーに送信する
        delay(1000);                                    // 返信があるかどうかを確認するのを待ちます
        if ( Udp.parsePacket() ) {
                // パケットを受信しました。そこからデータを読み取ります
                Udp.read(packetBuffer, NTP_PACKET_SIZE);        // read the packet into the buffer
                //タイムスタンプは受信パケットのバイト 40 から始まり、4 バイトまたは 2 ワードの長さです。 まず、次の 2 つの単語を抽出します。
                unsigned long   highWord        = word(packetBuffer[40], packetBuffer[41]);
                unsigned long   lowWord         = word(packetBuffer[42], packetBuffer[43]);
                // 4 バイト (2 ワード) を結合して長整数にします。これは NTP 時間 (1900 年 1 月 1 日からの秒数) です。
                unsigned long   secsSince1900   = highWord << 16 | lowWord;
                Serial.print("Seconds since Jan 1 1900 = " );
                Serial.println(secsSince1900);
                // NTP 時間を毎日の時間に変換します。
                Serial.print("Unix time = ");
                // Unix 時間は 1970 年 1 月 1 日から始まります。秒単位では 2208988800 です。
                const unsigned long     seventyYears    = 2208988800UL;
                // 70年を引く:
                unsigned long           epoch           = secsSince1900 - seventyYears;
                // JST
                unsigned long           JST             = epoch + timeZone;
                // Unix 時間を表示:
                Serial.println(epoch);
                // 時、分、秒を出力します。効率の悪い記述です
                Serial.print("The UTC time is ");       // UTC はグリニッジ子午線 (GMT) の時間です。
                Serial.print((epoch  % 86400L) / 3600); // 時間を出力します (86400 は 1 日あたりの秒に相当します)
                Serial.print(':');
                if ( ((epoch % 3600) / 60) < 10 ) {
                        // 各時間の最初の 10 分間では、先頭に '0' が必要です
                        Serial.print('0');
                }
                Serial.print((epoch  % 3600) / 60);     // 分を表示します (3600 は 1 時間あたりの秒数)
                Serial.print(':');
                if ( (epoch % 60) < 10 ) {
                        // 毎分の最初の 10 秒には、先頭の '0' が必要です
                        Serial.print('0');
                }
                Serial.println(epoch % 60);             // 秒を表示します
                Serial.print("The JST time is ");       // UTC はグリニッジ子午線 (GMT) の時間です。
                hour    =       (JST  % 86400L) / 3600;
                Serial.print(hour);                     // 時間を出力します (86400 は 1 日あたりの秒に相当します)
                Serial.print(':');
                if ( ((JST % 3600) / 60) < 10 ) {
                        // 各時間の最初の 10 分間では、先頭に '0' が必要です
                        Serial.print('0');
                }
                minute  =     (JST  % 3600) / 60;  
                Serial.print(minute);                   // 分を表示します (3600 は 1 時間あたりの秒数)
                Serial.print(':');
                if ( (JST % 60) < 10 ) {
                        // 毎分の最初の 10 秒には、先頭の '0' が必要です
                        Serial.print('0');
                }
                Serial.println(JST % 60);             // 秒を表示します
        }
        TimeUpdate();
        // もう一度時間を尋ねる前に 10 秒待ってください
        delay(10000);
}


戯言(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社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。