wifi + NTP (ESP-IDF)

プログラムの書き方

wi-fi については, ESP-IDF Programming Guide の API Reference の Wi-Fi を参照してプログラムを作成する. 動作モードには Station mode と AP mode の 2 つがあるが, "ESP32 connects to an access point." である Station mode を使えば良い.

また, NTP での時刻合わせについては, ESP-IDF Programming Guide の API Reference の System Time の "SNTP Time Synchronization" を参照すれば良い.

サンプルプログラムの実行

wifi 接続

$ cd ~/esp

$ cp -r esp-idf/examples/wifi/getting_started/station wifi-station

$ cd wifi-station

コンパイル

$ make

最初に make をすると menuconfig の画面が表示される. "Example Configuration" を選択し, 接続先の AP (アクセスポイント) の SSID とパスワードを入力する.

実行する. wifi への接続と取得したネットワークパラメタが確認できる.

$ make flash monitor

 ...(中略)...
   I (3067) esp_netif_handlers: sta ip: 192.168.0.29, mask: 255.255.255.0, gw: 192.168.0.1
   I (3067) wifi station: got ip:192.168.0.29
 ...(後略)...

プロジェクトの作成とサンプルプログラムの実行

$ cd ~/esp

$ cp -r esp-idf/examples/protocols/sntp wifi-sntp

$ cd wifi-sntp

コンパイル

$ make

最初に make をすると menuconfig の画面が表示される. ここで SSID とパスワードを入力する.

実行する. ログに現在の New York や上海の現在時刻が表示されているのがわかる.

$ make flash monitor

 ...(中略)...
   I (3057) esp_netif_handlers: sta ip: 192.168.0.29, mask: 255.255.255.0, gw: 192.168.0.1
   I (3057) example_connect: Got IP event!
   I (3057) example_connect: Connected to <YOUR_SSID>
   I (3067) example_connect: IPv4 address: 192.168.0.29
   I (3067) example_connect: IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fe02:23f8
   I (3077) example: Initializing SNTP
   I (3077) example: Waiting for system time to be set... (1/10)
   I (5027) example: Notification of a time synchronization event
   I (5087) wifi:state: run -> init (0)
   I (5087) wifi:pm stop, total sleep time: 2802519 us / 4182121 us
 ...(中略)...
   I (5187) example: The current date/time in New York is: Tue May  5 22:40:08 2020
   I (5187) example: The current date/time in Shanghai is: Wed May  6 11:40:08 2020
   I (5197) example: Entering deep sleep for 10 seconds
 ...(後略)...   

サンプルプログラムの修正

前述の esp-idf/examples/wifi/getting_started/station に NTP 関連のソースコードを追加することを考える. esp-idf/examples/protocols/sntp でもネットワークに接続可能であるが, そのメインプログラム中に書いてある通り, 接続を担っている example_connect() は機能が非常に単純なようであり, そのまま使うのは推奨されていないからである.

esp-idf/examples/protocols/README.md より抜粋

  Protocols examples use a simple helper function, `example_connect()`, to establish Wi-Fi or Ethernet connection. This function is implemented in [examples/common_components/protocol_examples/common/connect.c](../common_components/protocol_examples_common/connect.c), and has a very simple behavior: block until connection is established and IP address is obtained, then return. This function is used to reduce the amount of boilerplate and to keep the example code focused on the protocol or library being demonstrated.

  The simple `example_connect()` function does not handle timeouts, does not gracefully handle various error conditions, and is only suited for use in examples. When developing real applications, this helper function needs to be replaced with full Wi-Fi / Ethernet connection handling code. Such code can be found in [examples/wifi/getting_started/](../wifi/getting_started) and [examples/ethernet/basic/](../ethernet/basic) examples.

以下では, メインプログラムの修正箇所のみ述べる.

ヘッダファイルの追加

18 
19 #include "lwip/err.h"
20 #include "lwip/sys.h"
21 
22 #include "esp_sntp.h"
23 static void sntp_obtain_time(void);
24 
  • 22 行目: include ファイルの追加. NTP のヘッダファイル.
  • 23 行目: 後述する関数の定義

メイン関数の修正

125 void app_main(void)
126 {
127     //Initialize NVS
128     esp_err_t ret = nvs_flash_init();
129     if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
130       ESP_ERROR_CHECK(nvs_flash_erase());
131       ret = nvs_flash_init();
132     }
133     ESP_ERROR_CHECK(ret);
134 
135     ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
136     wifi_init_sta();
137 
138     //NTPでの時間合わせ
139     sntp_obtain_time();
140 }
  • 138-139 行目: 追加

関数の追加

142 static void sntp_obtain_time(void)
143 {
144     char strftime_buf[64];
145   
146     // STNP 初期化
147     ESP_LOGI(TAG, "Initializing SNTP");
148     sntp_setoperatingmode(SNTP_OPMODE_POLL);
149     sntp_setservername(0, "ntp.nict.jp");
150     sntp_init();
151     
152     // wait for time to be set
153     time_t now = 0;
154     struct tm timeinfo = { 0 };
155     int retry = 0;
156     const int retry_count = 10;
157     while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) {
158         ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
159         vTaskDelay(2000 / portTICK_PERIOD_MS);
160     }
161 
162     time(&now);
163     setenv("TZ", "JST-9", 1);
164     tzset();
165     localtime_r(&now, &timeinfo);
166     strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
167     ESP_LOGI(TAG, "The current date/time in Tokyo is: %s", strftime_buf);
168
169     printf("%04d/", timeinfo.tm_year + 1900);
170     printf("%02d/", timeinfo.tm_mon + 1);
171     printf("%02d", timeinfo.tm_mday);
172     printf(" ");
173     printf("%d", timeinfo.tm_wday);
174     printf(" ");
175     printf("%02d:", timeinfo.tm_hour);
176     printf("%02d:", timeinfo.tm_min);
177     printf("%02d\n", timeinfo.tm_sec);
178 }

プログラムの実行

実行すると, 現在の日本時間がログに表示される.

$ make flash monitor

  I (3067) esp_netif_handlers: sta ip: 192.168.0.29, mask: 255.255.255.0, gw: 192.168.0.1
  I (3067) wifi station: got ip:192.168.0.29
  I (3067) wifi station: connected to ap SSID:XXXXXXXX  password:XXXXXXXX
  I (3077) wifi station: Initializing SNTP
  I (3077) wifi station: Waiting for system time to be set... (1/10)
  I (5087) wifi station: Waiting for system time to be set... (2/10)
  I (7087) wifi station: The current date/time in Tokyo is: Wed May  6 22:30:24 2020
  2020/05/06 3 22:30:24