Arduino互換ESP32マイコンボードをNTPクライアント化

ESP32マイコンボードではNTPサーバから時刻を所得してマイコンボードの内部時計に適用するconfigTime()が用意されていて、使うのもとても簡単。Unix系OSのntpdateで時刻合わせをする感じ?ただし、バツンと時刻を合わせるようなので時間が飛んだり戻ったりというのはイヤかも。そして継続して時刻合わせするにはどうするのか、適度な間隔で勝手に時刻を取り続けてくれるのかな?
configTime()は時刻取得が簡単とはいえ、できればNTPクライアントとしてずれを調整しながら時刻を合わせて欲しいところ。なのでもう少しNTPクライアントっぽい処理をして欲しい。そこで使ってみようと思ったのがNTPクライアント用ライブラリ。これが期待しているNTPクライアントとしての動きをしてくれるのかは不明だけど。

Arduino互換マイコンでNTP時計を作る 1
Arduino IDEのメイン画面のメニューから「ツール」を開く。
「ライブラリを管理」をクリックする。(ここからハングアップしたかと思うほど時間がかかるかも)

Arduino互換マイコンでNTP時計を作る 2
ライブラリマネージャが開いたら右上の「検索をフィルタ」のテキストボックスに「ntp」と入力。(入力後の反応がないかもなので暫く待ってやる)
しばらく待つとNTPClientがリストに出る(リストの一番上に表示される筈だが、多少上下にスクロールが必要かも)。
NTPClientの行のどこかにポインタを合わせるとバージョンのドロップダウンメニューと「インストール」ボタンが表示される。バージョンは基本最新が選択されている筈なので触らずに「インストール」ボタンを押す。

Arduino互換マイコンでNTP時計を作る 3
NTPClientの行に「INSTALLED」が表示されたら (画像の赤色下線部分)、右下の「閉じる」をクリック。
ここでインストールされたNTPClientライブラリはhttps://github.com/arduino-libraries/NTPClientのものだと思われるのでそこで使い方等の情報を得る。

やりたいこと。
configTime()でNTPサーバから時刻を取得してローカルタイムを設定する(ESP32マイコンの内部時計の時刻を設定する)のではなく、NTPClientのtimeClient()でNTPサーバから時刻を取得する。(ESP32マイコンの内部時計の時刻は設定しない)
取得したNTP時刻をlocaltime()に入れてstrftime()で日付と曜日を得る。

 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
#include <WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include "time.h"

#define SSID "MyAP-SSID"             //Wi-Fiルーターなど無線LANのAPのSSID
#define WIFIKEY "MyAP-Key"           //上のAPのKey(パスワード)
#define ntpServer "192.168.128.1"    //NTPサーバのIPアドレスやホスト名
#define tzOffset 32400               //UTCからの時差 日本だと32400秒(マイナスになる)

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, ntpServer, tzOffset, 60000);

void setup() {
  setenv("TZ", "JST-9", 1);
  tzset();

  Serial.begin(115200);             //シリアル通信速度を115200bpsに指定して通信開始
  WiFi.begin(SSID, WIFIKEY);        //Wi-Fi接続開始
  //configTime(tzOffset, 0, ntpServer);  //NTPサーバへの接続

  
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  Serial.println(WiFi.localIP());

  timeClient.begin();
}

void loop() {
  timeClient.update();     //NTP時刻の更新
  Serial.print("NTP Time: ");
  Serial.println(timeClient.getFormattedTime());   //NTPで取得した時刻のみ表示
  Serial.println(timeClient.getEpochTime());       //NTP時刻(≒UnixTime)を表示

  time_t epTime = timeClient.getEpochTime() - tzOffset; //NTP時刻(≒UnixTime)から時差分(オフセット)を差し引く
  struct tm  ts;
  char buf[32];
  ts = *localtime(&epTime);
  strftime(buf, sizeof(buf), "%Y.%m.%d (%a) %H:%M:%S %Z", &ts);
  Serial.printf("%s\n", buf);              //整形したNTP時間(JST)を表示

  time_t loTime = time(nullptr);   //内部時計の日時を取得して
  Serial.print("LocalTime: ");
  Serial.print(String(ctime(&loTime)));    //内部時計を日時表示
  Serial.println(loTime);                  //内部時計のUnixTimeを表示

 
  delay(1000);
}

20行目をコメントにしたり有効にすることでESP32マイコンが持つ内部時間とNTP時間が同じになったり異なることがわかります。configTime()ではNTPサーバから時間を取ってその時間をESP32マイコンの内部時計に適用するようです。今回はESP32マイコンの時計にはNTP時間を適用せずにNTP時刻を表示する方法を採用します。(それが良いか悪いかは不明)

Arduino互換マイコンでNTP時計を作る 4
20行目の行頭に//を付けてコメントにてマイコンボードに書き込んだ。これにより内部時計には時刻を設定することがないためプログラムの起動開始によりエポック秒が0から始まる。つまり1970年1月1日の零時から時を刻む。NTP時刻は(おそらく)非同期でNTPサーバの取りに行った時刻が入る。以後はマイコンボードの時計の制度が酷いものでも徐々に補正される?

Arduino互換マイコンでNTP時計を作る 5
20行目の行頭の//を取って非コメント化してマイコンボードに書き込んだ。これによりプログラム開始時にNTPサーバから取得した時刻を内部時計に書き込まれる。それと共に内部時計とは非同期と思われるNTP時刻も持つ。当然だけど同じ時刻を表示する。ただし、内部的には日本時間のオフセット分の32400秒ズレている。(内部時計はUTCじゃなくてJSTで設定されてるから)

内部時計がいきなり補正されると嫌。そのせいでNTP時刻に影響するかもしれないので内部時計は設定しないでNTP時計だけを利用するつもり。
次回は表示側。USBシリアルで表示するんじゃ意味ないし。

関連記事:

Arduino互換ESP32マイコンボード用のプログラム環境の作成

前回、Arduino IDEのインストールと日本語表示化を行った。今回はその続きのようなもの。環境を整えて実際にESP32マイコンボードにプログラムを書き込んで実行するところまで簡単に一通り行う。(プログラムの内容は省略)

Arduino IDEの起動前〜プログラムをマイコンボードに書き込む間のどこかでArduino IDEの動いているPCとESP32マイコンをUSBケーブルで接続する。ESP32マイコンがDevKitならのPower LED(赤)が点灯する筈。

ESP32の開発準備 1
Espressif Systemsが持ってるGitHubのESP32用リポジトリArduino core for the ESP32 を見ると If you want to test ESP32-S2 and/or ESP32-C3 through the board manager, please use the development release link と書いてある。
つまり https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json をArduino IDEの設定ページのボードに貼る。

ESP32の開発準備 2
Arduino IDEを起動。メイン画面のメニューから「ファイル」をクリック。

ESP32の開発準備 3
「ファイル」から「環境設定」をクリック。

ESP32の開発準備 4
「環境設定」の「設定タブ」(左上の隅、最初から選択されている筈)で、「追加のボードマネージャのURL」(画像の赤枠部分)にGitHubからコピーしてきたURLを貼り付ける。
右下のOKをクリック。

ESP32の開発準備 5
Arduino IDEのメイン画面に戻り、メニューの「ツール」をクリック。

ESP32の開発準備 6
「ツール」から「ボード:hogehoge」(hogehogeは何かのマイコンボード名)にポインタを合わせ、開いたリストの一番上の「ボードマネージャ」をクリック。

ESP32の開発準備 7
ボードマネージャの画面は殆どがスクロールするリスト。下にスクロールする。

ESP32の開発準備 8
スクロールして「esp32」を見つけ、その行(画像の破線の四角部分)にポインタを合わせると背景の色が変わり、バージョン選択のドロップダウンメニューと「インストール」ボタンが表示される。バージョンは最新が選択されている筈なので特に必要がなければそのままで「インストール」ボタンをクリック。

ESP32の開発準備 9
インストールは意外と時間がかかる。ウインドウの最下段にプログレスバーが表示されるので一番右に進むまで待つ。

ESP32の開発準備 10
esp32がINSTALLED (画像の下線部分)が表示されていることを確認して「閉じる」

ESP32の開発準備 11
再度メイン画面の「ツール」から「ボード: hogehoge」にポインタを合わせ、表示されたリストに「ESP32 Arduino」があることを確認し、それにポインタを合わせる。 さらにリストが表示されるので「ESP32 Dev Module」を探しクリックして選択する。

ESP32の開発準備 12
さらにもう一度メイン画面の「ツール」を開く。「ボード: "hogehoge"」のhogehoge部分が「ESP32 Dev Module」になっていればO.K.

$ cd ~/arduino-1.8.16
$ ./arduino-linux-setup.sh $USER
USBポートに対するユーザーのアクセス権を(関連グループに)割り当ててくれる。

ESP32の開発準備 13
メイン画面のメニューから「ツール」を開き、「シリアルポート」でUSBのポート(何かが表示される筈)を選択。

ESP32の開発準備 14
Sketch(スケッチ)とかいうソースをテキトーに書いてコンパイル(画像左上の赤枠のアイコン)

ところが、コンパイルで変なメッセージが出る。
例:
ImportError: No module named serial
「WiFi.h」に対して複数のライブラリが見つかりました

これ、エラーメッセージを見た感じだと書いたスケッチ側とかライブラリに異常があるのではないかと思うが、Pythonで問題が発生しているっぽい。
Arduino IDEが動いているPC側、Linuxに必要なパッケージ

  • python3-pip
  • python3-serial
  • python-is-python3 ←特に、これを入れたら上手くいった
  • esptool
インストール
$ sudo apt install python3-pip python3-serial python-is-python3 esptool

不要かもしれないが、念の為Arduino IDEを再起動した。

ESP32の開発準備 15
「コンパイルが完了しました。」が表示されてその下のメッセージ欄に特にエラーが表示されなければコンパイル成功。

暫く(数日以上数ヶ月未満)して再び「WiFi.hに対して複数のライブラリが見つかりました」の異常が発生。
Linuxの更新で何か(esptoolに関連する?)パッケージを更新すると異常が発生することがあるみたい。

$ sudo apt reinstall python3-serial esptool

これが正解はか不明だが、「がとらぼ」の中の人の環境ではこれで復旧した。

次はESP32マイコンボードにコンパイルしたプログラムを書き込むのだが、その前にArduino IDEでシリアルモニターを起動しておく。
Arduino IDEのメイン画面のメニューから「ツール」を開き「シリアルモニタ」を選択。白いウィンドウが開く。それはとりあえず触らず次へ。

ESP32の開発準備 16
メニューの下の(マイコンボードに書き込む)を実行する。

ESP32の開発準備 17
「Writing at ・・・ ○%」が幾つか出て100%になり、「ボードへの書き込みが完了しました。」が表示されれば成功。
書き込みが完了するとESP32マイコンボード側では書き込まれたプログラムが実行される。

ESP32の開発準備 18
作成したプログラムにはシリアルへのメッセージの出力処理がある。そしてWi-FiでAPに接続し、接続先NWにあるDHCPサーバからIPアドレスが割り振られるとそれが表示されるようになっている。その表示を確認するために先にシリアルモニタを開いておいた。
表示されたIPアドレスで本当に通信できているのかpingを送信して疎通を確認。

僅か20行程度でシリアル通信とWI-Fi接続が簡単にできてしまう。なんて恐ろしい子なのかしら。

関連記事:
Up