2020年5月6日水曜日

ESP32/arduino:NTPサーバーとの時刻同期と時刻表示

目的:

NTPサーバーと時刻同期を行い、現在時刻を取得する。

手順:

  1. 時刻同期
    wifi 設定後、configTime() により、NTPサーバーを設定する。
    書式:
      void configTime(long gmtOffset_sec, int daylightOffset_sec,
             const char* server1, const char* server2, const char* server3);

    パラメータ :
      gmtOffset_sec      GMTとローカル時刻との差(秒)。日本では 3600*9 (9時間)
     daylightOffset_sec 夏時間設定(秒)。日本では 0
     server1 ~ server3 NTPサーバ。最低一つ設定する。

    記述例 :
       configTime(3600 * 9, 0, "ntp.nict.jp", "time.google.com",
                 "ntp.jst.mfeed.ad.jp" );
     
    1. 時刻取得
      時刻の取得は、getLocalTime() で取得する。
      getLocalTime()
      書式 :
         bool getLocalTime(struct tm *info, uint32_t ms);
      パラメータ:
        info 
      取得する時刻情報を格納する領域。
        ms    タイムアウト時間。省略した場合は5000。

      記述例:
        struct tm timeInfo;
        getLocalTime(&timeInfo, 1);
        Serial.printf(" %04d/%02d/%02d %02d:%02d:%02d\n",
                       timeInfo.tm_year + 1900,
                       timeInfo.tm_mon + 1,
                       timeInfo.tm_mday,
                       timeInfo.tm_hour,
                       timeInfo.tm_min,
                       timeInfo.tm_sec) ;

      注:

       getLocalTime は、取得した年が 116(2016年) 以下の場合、タイムアウトになるまで取得し直す。NTPサーバーと同期がとれない場合、タイムアウト値を指定しないと、5秒間、リトライする(処理が待たされる) こととなる。

       NTPサーバーと同期がとれない要因の 1つとして、WIFI 固定IP設定時の DNSアドレス が正しく設定されていないことが考えられる。( やらかしてました。 )
       

例:

 「SPIFFSのテキストファイルをダウンロードする 」の シリアル出力にタイムスタンプを追加した例を以下に示す。
(メインスケッチ以外のファイルは 「SPIFFSのテキストファイルをダウンロードする 」を参照 )
<表示例>
     |
 2020/05/06 17:08:03
 +++++++++++++++++ new client! +++++++++++++++++
GET / HTTP/1.1
     |

#include <WiFi.h>                         // WiFi 使用の為
#include "FS.h"                           // SPIFFS 使用の為
#include "SPIFFS.h"                       // SPIFFS 使用の為
#include "time.h"                         // getLocalTime 使用の為
 
#define DEBUG_HTML                        // Debug用 Serial 表示制御用
#define DEBUG                             // Debug用 Serial 表示制御用
 
// AP モード用 WIFI 設定 ---------------------------------------------
const char ssid[] = "ESP32_AP";           // SSID for softAP
const char pass[] = "password";           // password for softAP
const IPAddress ip(192, 168, 4, 1);       // IPアドレス for softAP
const IPAddress subnet(255, 255, 255, 0); // サブネットマスク for softAP
 
// 動作モードフラグ ---------------------------------------------------
bool stamode = true ;                     // WiFi STA 接続モード : true
bool sta_exec = false ;                   // WiFi STA接続モード実行中フラグ
bool config_mode  = false ;               // 初期設定モード ; true
bool config_exec = false ;                // 初期設定実行中フラグ
 
// 設定ファイル用 -----------------------------------------------------
String s_config ;                         // 設定ファイル用 String
char wifi_ssid[128] = "" ;                // SSID        for WiFi STA
char wifi_pass[128] = "" ;                // password    for WiFi STA
IPAddress wifi_ip(192,168,1,66) ;         // IP Address  for WiFi STA
IPAddress wifi_gw(192,168,1,1)  ;         // gate way    for WiFi STA
IPAddress wifi_sm(255,255,255,0) ;        // subnet mask for WiFi STA
IPAddress wifi_dns(192,168,1,1) ;         // dns address for WiFi STA
char wifi_mode[16] = ""   ;               // wifi mode
String foot_msg ="" ;                     // リブートメッセージ用

#define JST 3600*9                        // 日本の GMT との時間差(秒)
#define CLIENT_TIMEOUT 3000               // クライアントタイムアウト (mS)
WiFiServer server(80);
 
String html_CONF ;                        // 設定用 HTML
String html_MAIN ;                        // メイン HTML
String html_resp ;
 
// LED 調光処理用 ----------------------------------------------------------------
// -- for ledc
byte brightness = 0 ;                     // LED 明るさ設定 (0-255)
 
// -----------------------------------------------------------------------------
//  arduino 初期化処理                                                        --
// -----------------------------------------------------------------------------
void setup() {
  int res = 0 ;                                // 結果格納用 (ワーク)
 
  Serial.begin(115200);                        // シリアル 開始
  SPIFFS.begin(true) ;                         // SPIFFS 開始
 
  // 初期設定用 HTML ファイルの読み込み ---------------------------------------
  res = rd_SPIFFS("/WiFi_Config.html",html_CONF) ;  // html_CONF に Config.html を格納
 
  // 本体 HTML ファイルの読み込み ----------------------------------------------
  res = rd_SPIFFS("/main.html",html_MAIN) ;         // html_MAIN に main.html を格納
   
  // レスポンス用HTML ファイルの読み込み ----------------------------------------
  res = rd_SPIFFS("/resp_cnfld.html",html_resp) ;   // html_resp に resp_confld.html を格納
   
  // 初期設定ファイルを読み込み、グローバル変数に設定し、wifi,画面モードを決定 ---
  res = rd_config() ;                          //
 
  // wifiモード(stamode) に応じて AP か STA かを選択してサーバー起動 -------------
  if (stamode == false) {                    // APモードの場合 softAP WiFi 開始
    start_AP_server() ;                      // APモードでサーバー起動
    sta_exec = false ;                       //
  } else {                                   // STAモードの場合 Wifi 開始
    start_STA_server() ;                     // STAモードでサーバー起動
    sta_exec = true ;                        //
    configTime(JST, 0, "ntp.nict.jp",        // NTPサーバー設定
               "time.google.com",            //
               "ntp.jst.mfeed.ad.jp");       //
  }                                          //

  // 実行中のモード (設定かメインか) を設定 -------------------------------------
  if (config_mode) config_exec = true ;   // config_mode なら config_exec を '1'
  else config_exec = false ;              //
 
  Serial.println("Server start!");
 
  // メイン処理用 初期化 ------------------------------------------------------
  ledcSetup(0, 5000, 8);            // channel 0 周波数 5000 Hz, 8 bit 
  ledcAttachPin(2,0);               // attach pin 2 to channel 0
  ledcWrite(0, 0);                  // initialize channel 0 to off     
 
 
}
// -----------------------------------------------------------------------------
 
 
// -----------------------------------------------------------------------------
//  arduino メインループ処理                                                  --
// -----------------------------------------------------------------------------
void loop() {
  WiFiClient client = server.available();
  String htmlwk = "" ;                            // html 用 ワーク変数
  String line   = "" ;                            // クライアントからの入力用
  int xhr = 0 ;                                   // xhr 要求フラグ
  unsigned long start_time = 0 ;                  // タイムアウト監視開始時間
  unsigned long wait_time  = 0 ;                  // 開始/前回受信からの経過時間
  struct tm timeInfo;                             // 時刻を格納するオブジェクト
  
  // HTML クライアント処理 ------------------------------------------------------
  if (client) {
    start_time = millis() ;                       // タイムアウト監視開始時間を更新
    getLocalTime(&timeInfo, 10);                  // 時刻を取得
    Serial.printf(" %04d/%02d/%02d %02d:%02d:%02d\n", // タイムスタンプ表示
                  timeInfo.tm_year + 1900,        //      年 (西暦)
                  timeInfo.tm_mon + 1,            //      月
                  timeInfo.tm_mday,               //      日
                  timeInfo.tm_hour,               //      時
                  timeInfo.tm_min,                //      分
                  timeInfo.tm_sec  )  ;           //      秒
    Serial.println(" +++++++++++++++++ new client! +++++++++++++++++ ");
    while (client.connected()) {                  // クライアントから接続されたとき
      if (client.available()) {                   //
        start_time = millis() ;                   // タイムアウト監視開始時間を更新
        line = client.readStringUntil('\n');      // 1行分取得
        # ifdef DEBUG_HTML                        //
          Serial.println(line) ;                  //
        # endif                                   //
        if (line.indexOf("GET /?") != -1) {       // GET 処理
          if (config_mode)                        // 初期設定用 フォームデータ処理
            set_form2param(line) ;                //   フォームデータを変数に格納
          else {                                  // メイン用 処理
            xhr = proc_main(line) ;               //   フォームデータのメイン処理
          }                                       //
        }                                         //
        // 最終行(空行)を受信した時 -------------------------------------------
        if (line.length() == 1 && line[0] == '\r'){  // 最終行判定
          if (stamode != sta_exec) {         // sta_mode が変わった場合
            if (strcmp(wifi_mode,"ap") == 0){    //  リブートメッセージを設定
              foot_msg = "アクセスポイントモードに移行します。<br> " ;
              foot_msg += "この画面を閉じてアクセスポイントに接続して下さい。" ;
            }else{
              foot_msg = "ステーションモードに移行します。<br>"; 
              foot_msg += "この画面を閉じて 設定したアドレスに接続して下さい。" ;
            }
            if (config_exec) {                    //   初期設定中なら
              send_CONF_html(client) ;            //     初期設定 HTML 送信
            } else {                              //   メイン画面なら
              send_MAIN_html(client) ;            //     メイン HTML 送信
            }                                     //    ( その後 リブート)
          } else {                                // sta_mode が変わらない場合 
            if (config_mode) {                    //   次に初期設定を表示するなら
              send_CONF_html(client) ;            //     初期設定 HTML 送信
              config_exec = true ;                //
            } else {                              //   次にメイン画面なら
              if (xhr) {                          //     XHR 応答の場合
                send_resp_html(client) ;          //       XHR 応答 送信
              } else {                            //     HTML 送信なら
                send_MAIN_html(client) ;          //     メイン HTML 送信
                config_exec = false ;             //
              }                                   //
            }                                     // 
          }                                       //
          # ifdef DEBUG_HTML                      //
            Serial.println("Send HTML") ;         //
          # endif                                 //
          break ;                                 // ループ終了 
        }                                         //
        //-------------------------------------------------------------------
      } else {                                    // 文字列受信していない時
        wait_time = millis() - start_time ;       //
        if ((wait_time) > CLIENT_TIMEOUT) {       // 文字列受信 タイムアウト
          Serial.print("タイムアウト :") ;        //
          Serial.print(wait_time) ;               //
          Serial.println(" mS") ;                 //
          break ;                                 // ループ終了
        }                                         //
      }
    }
    // 接続が切れた場合 ------------------------------------------------------
    client.stop();
    Serial.println("client disonnected");
    Serial.println("----------------------------------------------------");
  }
 
  if (stamode != sta_exec) {                      // sta_mode が変わった場合
    Serial.println("------------------- リブートします------------------- ");
    delay(500);                                   // 
    ESP.restart() ;                               // リブート
  } else {
  }
  // --------------------------------------------------------------------------
 
}

(行#4)
getLocalTime を使用する為に必要な include

(行#32)
日本時間(GMTとの差) の定義。

(行#72~74)
STAモードにする場合、NTPサーバー設定を行う。

(行#103)
時刻を格納するオブジェクトの定義。

(行#108)
時刻を取得。

(行#109~115)
時刻(タイムスタンプ)の表示。
Serial 表示に printf が使用できる模様。

0 件のコメント:

コメントを投稿