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
     |

スケッチ (メイン)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#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 件のコメント:

コメントを投稿