ESP32-WROOM-32(D)とMAX7219 LEDマトリクスディスプレイ

Arduino互換マイコンでNTP時計を作るハードウエア編 0
市販品の電波時計がとにかくJJY標準電波信号を受信しない。室内では全く受信しなくて、ベランダのガラス戸近くに置いてもダメ。ベランダに普通に置いてもダメでベランダの手摺壁の上に置いてようやく。でも風が吹いて落下したらと思うと怖いのでそれもなかなか。
JJY標準電波は40kHz(福島・東日本)と60kHz(長崎・西日本)の長波で、超短波と違って回り込んで入るのは得意なんだろうど、波長が40kHz/7.5km, 60kHz/5kmとなると鉄筋/鉄骨の建物には電波が入れないのかな?ファラデーケージ的な意味で。知らんけど。
この手の時計は標準電波で頻繁に補正されることが前提の精度で作られているのか、補正されないと時刻の狂いっぷりが酷い。

で、正確な時刻を表示する時計が欲しいと思った。

  • 電波時計からアンテナを引き出して線をつないでアンテナを屋外に置けばもしかしたら受信できるかもしれない。(できないかもしれない)
  • JJY電波のレピーターを購入して中継してやれば屋内の電波時計で電波を掴めるようになるかもしれない。(ムリかもしれないし何しろ高額で手が出ない)
  • JJY電波のレピーターを自作して中継してやれば屋内の電波時計で電波を掴めるようになるかもしれない。(詳しい人なら簡単なのかもだが自分では作れない)
  • 特定の周波数でJJY電波をエミュレートした音声をイヤホンで出力し、イヤホンコードから出る電波で電波時計を補正する。(手で時刻合わせした方が早そう)
  • 電波時計を諦めてGPSクロックを購入すれば正しい時刻を表示できる。(まぁまぁ高額だし窓際に置くか屋外アンテナが要る)
  • 電波時計を諦めてGPSクロックを自作すれば正しい時刻を表示できる。(窓際に置くか屋外アンテナが要る)
  • 電波時計を諦めてNTPクロックを購入すれば正しい時刻を表示できる。(何故かすんごい高額)
  • 電波時計を諦めてNTPクロックを自作すれば正しい時刻を表示できる。(簡単で安そう)

最後のNTPクロックの自作が良さげ。NTPクライアントはナントカパイのようなSBCを使う手もあるが、最近は高額になっているのとたかが?NTPクライアントにそんな高性能は要らない。Arduinoはすごく安くて適度に低性能で情報が豊富なので良さそうだけどネットワーク接続に難あり。Arduino互換で安くてネットワーク接続対応なのを探したら、ESP32-WROOM-32xというマイコンボードがあった。Wi-Fiでネットワーク接続できてBluetoothも対応。お値段は200円程度。ただし、このマイコンは指先程度のチップ型(実際は極小基盤)なのでそのままでは扱いづらい。DevKitと呼ばれる基板付きのものを選ぶことに。これだとESP32-WROOM-32xのマイコン本体込みで300〜500円程度で販売されている。日本だとその3倍程度? 時計として使うなら時刻を表示するディスプレイが必要。これは液晶モニターでも良いのだろうけど、消費電力大きいし大きさの割にはそれなりに高額。そこでLEDのマトリクスパネルを使うことにした。これだと大きさの割にはかなり安価、そしてArduinoのようなマイコンからの給電で十分に機能するのでディスプレイ用の電源ケーブルが不要。ただし、解像度が低くて色表示が自由にはならない(若しくは単色のみ)。

Arduino互換マイコンでNTP時計を作るハードウエア編 1
例によってAliExpressで購入。後述の理由で商品のリンクは貼らない。今年は夏に中国で大洪水があったり停電があったりといろんな理由で注文から届くまで大遅延が発生している。そして、中国の物流は(他の国も物流ではその傾向があるけど)、先入れ先出し(FIFO)ではなく後入れ先出し(LIFO)なので早くから空港の倉庫などに入ってしまった荷物がいつまでも荷物の山の底に溜まったまま、後から来た荷物が先に目的の国に送られるという状態が続いている。つまり運良くサクッと届く荷物と待てど暮せど届かない荷物で二分化されている。

Arduino互換マイコンでNTP時計を作るハードウエア編 2
プチプチに包まれた中身。右がESP32-WROOM-32D DevKit(の筈な品)。お値段は購入時1枚US$3.59。
中央と左はLEDマトリクスディスプレイ(同じものの表と裏)。お値段は購入時1枚US$4.52。
ちなみに送料はUS$2.53だった。

Arduino互換マイコンでNTP時計を作るハードウエア編 3
ESP32-WROOM-32DのDevkit。画像では大きく見えるが、画像の基板の左右の長い辺が単3電池とちょうど同じ長さ。(右側に出ている黒い板Wi-Fiアンテナ部分を除く)
右半分の銀色の部分がESP32-WROOM-32D本体のマイコンボード。(チップのように見えるけど銀色の金属板を外すと中にいろいろ入った基板)
左端にmicroUSBポートがあって電力はここから。PCと接続してプログラムの書き込みもこのポートを使う。
プログラムを書き込むとき以外はPCは要らなくて、電源をつなぐと書き込み済みのプログラムがすぐに実行される。

Arduino互換マイコンでNTP時計を作るハードウエア編 4
DevKitの基板のウラ面には特に何もない。19x2のピンヘッダが付いている。

Arduino互換マイコンでNTP時計を作るハードウエア編 5
ESP32-WROOM-32D本体の銀色の金属板シールドの刻印に何か違和感を感じたので光を斜めから当てたらこんなの。
ESP32-WROOM-32D最後のDだけ取って付けたように薄かったり微妙にズレていたり。
つまりESP32-WROOM-32DではなくESP32-WROOM-32にリマークしたものかなと思った。少なくとも技適の番号はESP32-WROOM-32DのものではなくESP32-WROOM-32のもの。しかし、CMIIT IDはESP32-WROOM-32でもESP32-WROOM-32DでもなくESP32-WROOM-32Uのもの?「2017DP7123(M)」ならそうなのだが画像では(M)無し。
コイツ本当は何者なのかしら? と、いうことでモノが怪しいのでAliExpressの商品リンクは貼らないことに。

Arduino互換マイコンでNTP時計を作るハードウエア編 6
Max7219(シリーズ?)というシリアル制御の8 x 8のLEDマトリクスディスプレイが4つ並んだもの(4連タイプ)。それに5本並びのリボンケーブルと5本分のL字型ピンヘッダ。これで1セット。リボンケーブルのケーブルの色は選べないみたい。写真のリボンケーブルは個人的には特に苦手な色の組み合わせ。暗いところで間違いやすい色。
LEDの色は知っている範囲では赤と青と緑がある。白色があれば欲しかったのだが見つからなかったので青を購入した。
Max 7219ディスプレイモジュールには8 x 8のマトリクスタイプではなく7セグのデジタル数字表示タイプもある。(電卓や昔のデジタル時計の数字みたいなの)

Arduino互換マイコンでNTP時計を作るハードウエア編 7
ウラ面は同じ基板が4つ連結したものになっている。ただし、これで1枚の基板。糸ノコで切り分けなければ分離はできない。
これは4モジュールの製品だが、1モジュールの基板も販売されているので必要な数を買って連結して使う。なお、基板が8 x 8のLED部分とほぼ同じ大きさなのが新しいタイプでICチップが載った部分が飛び出ているのが古いタイプ。古いタイプも販売されているので購入時は間違わないように注意。新しいタイプは直線に連結する(横に並べるだけでなく縦に並べて大きな1つのLEDディスプレイのようにすることもできる。古いタイプは縦並べ(実際は横並べ)は構造的に最大で2列に制限される。しかも上下反転の処理が必要。

Arduino互換マイコンでNTP時計を作るハードウエア編 8
今回はブレッドボードは使わずにピンヘッダに直接リボンケーブルをつなぐことにした。
ESP32-WROOM-32 DevKit側は5VとGNDだけは間違えないようにし、他は数字だけが書かれたピンに適当に(テキトーではない)につなぐ。Max7219 LEDマトリクスディスプレイに付属のリボンケーブルを使うことにするならリボンケーブルの並びどおりにつないでやると良いかと。つまり、5VとGNDを先に接続して他はその隣に並べるとラク。数字のピンは今回は12, 14, 27を使うことにした。(好みでOK)

Arduino互換マイコンでNTP時計を作るハードウエア編 9
リボンケーブルの並ぶ順のまま接続してやるとリボンケーブルの負担を少なくできる。(接続間違いも防ぎやすい)

Arduino互換マイコンでNTP時計を作るハードウエア編 10
対向するMax7219 LEDディスプレイ側は「IN←」が書かれている側のピン(ピンヘッダが付いている側)にマイコンボードからのケーブルをつなぐ。
VCCにマイコン側5Vからの線を接続。GNDは両方GND。他はリボンケーブルの並び順で接続する。
つまり、DIN(Data Input)が12ピン、CS(Chip-Select Input)が14ピン、CLK(シリアルクロック)が27ピンにつながることになる。これをメモしておく。プログラムを作るときに使う情報になる。

Arduino互換マイコンでNTP時計を作るハードウエア編 11
白い8 x 8のLEDマトリクス部分は基板から分離可能。LED側のピンが軟弱なので抜き取りと差し込みには注意。
で、マイコンボードとつないだ反対側(アウト側)は基板にピンホールがあるだけなので別のディスプレイモジュールと接続するためにピンヘッダを取り付ける。基板と基板を接着してジャンパ線を使うかハンダを使うかして接続する方法もあるが、それだと直線に並べて使うことしかできない。リボンケーブル等で接続するならディスプレイモジュールの並べ方は後で変えることができる。Max7219ディスプレイモジュールに付属するL型ピンヘッダを使うのが良さそう。
なお、付属のピンヘッダを使うなら必ず基板とのハンダ付けが必要。テストでハンダ無しの状態で動かしてみたら全く使い物にならない状態だった(壊れるわけではないが)。

Arduino互換マイコンでNTP時計を作るハードウエア編 12
接続自体が簡単なのは画像の向きに取り付けること。これはデータイン側に最初から付いているピンヘッダと同じ。
しかし、このように外向けにピンヘッダを付けるとその方向に別のLEDディスプレイモジュールをぴったり並べるのにじゃまになる。

Arduino互換マイコンでNTP時計を作るハードウエア編 13
画像のようにL型ピンヘッダの先端が基板の内側方向を向くように取り付けた。リボンケーブルの取り回しを変更するときには画像のようにLEDパネル部分を取り外す必要はあるが、別のモジュールをぴったり並べるにはこの方が都合が良い。縦方向に重ねて使うにもこの方がケーブルの接続がスマートになる。

Arduino互換マイコンでNTP時計を作るハードウエア編 14
リボンケーブルをつないでみた。
画像に黄文字で書いたが、白いLEDパネルは正方形で接続の向きが判りにくくなりやすい。側面の1方向に黒い文字列が書いてあるのでその文字列が他のLEDパネルと同じになるよう嵌めてやるのが簡単。(4連で全部外してしまうと嵌めるときに悩むかも。)一応、Data-inが右側、Data-outが左側になるように基板を置いて手前側に文字列があるように嵌めるのが正解っぽい。

Arduino互換マイコンでNTP時計を作るハードウエア編 15
マイコンボードと接続するMax7219モジュール以外はData-in側のピンヘッダが元々の外向きだと邪魔になる。ピンヘッダを外して内向きに差し替えるのが良い。ただし、ハンダごてでハンダを溶かして5ピンを抜くのは難しいというかムリだと思われるのでヒートガンを使うのが良さそう。なお、ヒートガンの当て方が悪いと裏側のICチップが剥がれるかもしれないので使い方注意。
これでモジュール間に邪魔者が無くなるのでモジュール同士をピタっと並べることができる。

Arduino互換マイコンでNTP時計を作るハードウエア編 16
このLEDディスプレイは屋外でも通用しそうなほど強力な光を放つ。1m以上離れていても直視すると眼の奥が痛ダルくなるので要注意。特に青タイプの「毒の光」はキツネリスが発狂して死ぬほど体に有害そう。
LED点灯中に撮影すると、周りは普通に明るいのに(カメラが勝手に露出を調整するので)暗いところで撮影しているみたいになる。

LEDディスプレイはガラスかアクリルの入れ物に入れて間に紙か黒アクリル板などを挟んで光量を調節した方が良さそう。プログラムでも光量を制御できるが、最低輝度でも眩しい。(眼が痛くなるほどではない)

関連記事:

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
「コンパイルが完了しました。」が表示されてその下のメッセージ欄に特にエラーが表示されなければコンパイル成功。

次は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接続が簡単にできてしまう。なんて恐ろしい子なのかしら。

関連記事: