LoginSignup
4
1

More than 1 year has passed since last update.

SPRESENSE Wi-Fi Add-onボード iS110BでNTP時刻取得する

Last updated at Posted at 2021-12-14

Qiita Advent calendar2021の記事です。

Spresenseの関係会社に勤務していますが、業務外の個人活動の紹介です。 当記事は会社と関係ありません。

誤った基板上のピン接続により故障する可能性があります。 自己責任でお願いします。

Wi-Fi Add-onボード iS110BでUDP通信するサンプルを自作できたので、NPT時刻取得を試してみました。

事前準備

いきなり外のNTPサーバにつなげるのは怖かったので、自分のPCでNTPサーバの機能を動くようにしました。
こちらを参考にしました。

スケッチの作成

12/15に公開したUDP通信用のサンプルをさらに改造しました。

ここでNTPの時刻取得のためのパケットはこちらを参考にしました。
http://arms22.blog91.fc2.com/blog-entry-445.html

AppFunc.cppの変更箇所だけ抜粋
/*-------------------------------------------------------------------------*
   Constants:
  -------------------------------------------------------------------------*/
// RTC.hを追加
#include <RTC.h>
...
/*---------------------------------------------------------------------------*
   App_UDPClient_Test
  ---------------------------------------------------------------------------*/
// 部分的に追加・修正
void App_UDPClient_Test(void)
{
  ATCMD_RESP_E resp;
//  char server_cid = 0;
  uint8_t server_cid = 0;
  bool served = false;
//  ATCMD_NetworkStatus networkStatus;
  uint32_t timer = 0;

  AtCmd_Init();

  App_InitModule();
  App_ConnectAP();

  while (1) 
  {
    if (!served) 
    {
      ATCMD_NetworkStatus networkStatus;
      resp = ATCMD_RESP_UNMATCH;

      ConsoleLog( "Start UDP Client");
      resp = AtCmd_NCUDP( (char *)UDPSRVR_IP, (char *)UDPSRVR_PORT, (char *)LocalPort, &server_cid);   // Create UDP Client; AT+NCUDP=<Dest-Address>,<Port>[<,Src-Port>]
      ConsolePrintf( "server_cid: %d \r\n", server_cid);

      AtCmd_CID();

      if (resp != ATCMD_RESP_OK) 
      {
        ConsoleLog( "No Connect!" );
        delay(2000);
        continue;
      }
      if (server_cid == ATCMD_INVALID_CID) 
      {
        ConsoleLog( "No CID!" );
        delay(2000);
        continue;
      }

      do 
      {
        resp = AtCmd_NSTAT(&networkStatus);         // AT+NSTAT=?
      } while (ATCMD_RESP_OK != resp);

      ConsoleLog( "Connected" );
      ConsolePrintf("IP: %d.%d.%d.%d\r\n\r\n",
                    networkStatus.addr.ipv4[0], networkStatus.addr.ipv4[1], networkStatus.addr.ipv4[2], networkStatus.addr.ipv4[3]);

      AtCmd_Time();

      served = true;
    }
    else 
    {
      ConsoleLog( "Start to send UDP Data");
      // Prepare for the next chunck of incoming data
      WiFi_InitESCBuffer();

      // Start the infinite loop to send the data

      // http://arms22.blog91.fc2.com/blog-entry-445.html
      // set all bytes in the buffer to 0
      memset(UDP_Data, 0x00, NTP_PACKET_SIZE);
      // Initialize values needed to form NTP request
      // (see URL above for details on the packets)
      UDP_Data[0] = B11100011; //0x1b; // ; // 00(no announce) + 100(version4) + 011(client) // 0b11100011 LI, Version, Mode
      UDP_Data[1] = 0; //0x05; // not sync // Stratum, or type of clock  // 0
      UDP_Data[2] = 6; // 2^6 = 64 sec // 6 Polling Interval 2^6 = 64sec
      UDP_Data[3] = 0xEC; // 0xE9;       // 0xEC Peer Clock Precision 256 - EC = -20 -> 2^-20 ~ 0.9us
      // 8 bytes of zero for Root Delay & Root Dispersion CP_Data[4] ~ UDP_Data[11] = 0x00
      //
      UDP_Data[12]  = 49;       // Reference ID 49 4E 49 52
      UDP_Data[13]  = 0x4E;
      UDP_Data[14]  = 49;
      UDP_Data[15]  = 52;

//      for (int i = 0; i < NTP_PACKET_SIZE; i++)
//      {   
//        ConsolePrintf("%02x ", UDP_Data[i]);                   
//      }
      ConsolePrintf("\r\n");

      while ( 1 ) 
      {
//        ConsolePrintf( "send at cid: %d \r\n", server_cid);

        AtCmd_SendBulkData(server_cid, UDP_Data, NTP_PACKET_SIZE);

//        ConsolePrintf( "before GetGPIO37\r\n");

        resp = AtCmd_RecvResponse();  // Description: Wait for a response after sending a command. Keep parsing the data until a response is found.

//        ConsolePrintf( "response after sending a command: %d vs ref: %d\r\n", resp, ATCMD_RESP_BULK_DATA_RX);

//        ConsolePrintf( "after GetGPIO37\r\n");

        if ( ATCMD_RESP_BULK_DATA_RX == resp ) 
        {
//          ConsolePrintf( "Command sent successfully. \r\n\n");

          if ( Check_CID( server_cid ) ) 
          {
//            ConsolePrintf( "CID check succeeded\r\nReceive %d byte: \r\n", ESCBufferCnt - 1);

//            ConsolePrintf("Transmitted Data: ");         
//            for (int i = 0; i < NTP_PACKET_SIZE; i++)
//            {
//               ConsolePrintf("%02x ", UDP_Data[i]);        
//            }       
//            Serial.println("");
//            ConsolePrintf("Receive Data:     ");  
//            Receive Dataは1バイト目から開始 ESCBufferは0にサイズが入っている つまり1つずれている
//            for (int i = 1; i < ESCBufferCnt; i++)
//            {
//               ConsolePrintf("%02x ", ESCBuffer[i]);        
//            }       
//            Serial.println("");

            // 時刻情報は40, 41, 42, 43バイト目のデータ
            // ESCBufferは1つずれている 
            unsigned long highWord = word(ESCBuffer[40+1], ESCBuffer[41+1]);
            unsigned long lowWord = word(ESCBuffer[42+1], ESCBuffer[43+1]);

//            ConsolePrintf("time data: %02x %02x %02x %02x \r\n\n", ESCBuffer[40], ESCBuffer[41], ESCBuffer[42], ESCBuffer[43]);

            // NTPタイムスタンプ 小数部は切り捨て
            unsigned long secsSince1900 = highWord << 16 | lowWord;
//            Serial.print("Seconds since Jan 1 1900 = " );
//            Serial.println(secsSince1900);

            // NTPタイムスタンプをUNIXタイムに変換
            const unsigned long seventyYears = 2208988800UL;
            unsigned long epoch = secsSince1900 - seventyYears + 3600 * 9;
//            Serial.print("Unix time = ");
//            Serial.println(epoch);

            // 雰囲気で代入した         
            RtcTime now1 = epoch;

            Serial.print("JST is ");
            Serial.print(now1.year());
            Serial.print('/');
            Serial.print(now1.month());
            Serial.print('/');
            Serial.print(now1.day());
            Serial.print(' ');
            Serial.print(now1.hour());
            Serial.print(':');
            Serial.print(now1.minute());
            Serial.print(':');
            Serial.println(now1.second());
            Serial.println("parsed");
            Serial.println("");
          }
          WiFi_InitESCBuffer();

          delay(1000);
        }
...

送信バッファのUDP_Dataと異なり、受信バッファのESCBufferは先頭1バイトが受信データの長さを表すので、変換するときに注意が必要でした。

実行時の様子

Khabane lameさんのように言うとESP32だと1行で済むよねという内容ですが、NTPの勉強になってよかったです。
https://wak-tech.com/archives/833

受信した時刻を送信するパケットに反映させたり、iS110Bに時刻の情報をセットしたりすると面白そうです。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1