eucalyのアイコン画像

NTPな掛け時計を作ってみる

eucaly 2021年02月17日に作成

NTPな掛け時計を作ってみる

正確な時刻を、みんなで共有したいのよ

世はすっかりテレワーク時代、カレンダーだのTV会議だので、より一層「遅刻」が目立っちゃう、そんな時代になりました。
会社に出社しても、まあ人はまばらなのですが、だからこそ時間を忘れてゴニョゴニョしてしまう例もちらほら。
なのに会社の掛け時計、とまってたり盛大にずれていたりで、信用できません。
なので、NTPな掛け時計を作ってみることに。

LEDパネルとESP32で!

ま、表示器は、マトリックスLEDでいいでしょう。
一般的なHUB75な、64x32のP5をチョイス。
Adafruitのだと、このへんです。
https://www.marutsu.co.jp/pc/i/27108615/

信頼と実績のRaspberry Pi + rpi-rgb-led-matrixでもいいのですが。
まあ単機能なので、今回はESP32を使ってみることに。

ESP32には、「WROOM」という、BTやWIFIが入り且つモジュール化され技適もOKな素敵さんがあるので、それで。

早速入手!。
キャプションを入力できます

・・・失敗です。
WROOMシリーズには、「ESP-WROOM-02」と「ESP-WROOM-32」があるのですが。
HUB75のような高速I/Fを担保するには、末尾32が必要っぽいです。
秋月だと、このへんです。
https://akizukidenshi.com/catalog/g/gM-11819/
今回はAmazonでてきとーなの入手して使いました。

ま、詳細は、以下サイトなどを参考にしました、とさ。
https://qiita.com/Yukiya_Ishioka/items/c455f7bfbcb240bb2ba7

一応I/Fは5Vなので、バスバッファ使って5V昇圧しています。
「TC74LCXR163245」を使って、、、双方向バスバッファですが、まあ単方向で使ってますよと。
https://akizukidenshi.com/catalog/g/gM-08250/

電源は5V4AのACアダプタで、ちゃんとジャック側も高電流対応型を、ね。
https://akizukidenshi.com/catalog/g/gK-06998/

下準備

まあまず、「デカい文字フォント」を作ります。
これはむかぁし作った変換プログラムで、BMPからサクっと。
キャプションを入力できます

配線

まあ特に言うことはありません。
ワイヤリングは、上記qiitaを参照しつつ、パパっと作りましたとさ。
キャプションを入力できます
強いて言えば、バッファのEN配線忘れてて不安定だったことくらいです!。

ソフトウェア

Arduinoでサクっと。
だいたい半日に1回くらい、時計合わせにいきますって感じで。

ntpclocl.ino

#include <WiFi.h> #include <Adafruit_GFX.h> // Core graphics library #include <P3RGB64x32MatrixPanel.h> #define WIFI_SSID "wifi_no_SSID" #define WIFI_PASSWORD "wifi_no_PASSWORD" #define JST 3600*9 // constructor with default pin wiring P3RGB64x32MatrixPanel matrix; long ntpcounter = 0; time_t tprev = 0; char mprev; const uint8_t moji_9[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xe0, 0x0f,0xf8,0x1f,0xfc,0x1f,0xfc,0x3e,0x3e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x3e,0x3e,0x3f,0xfe,0x1f,0xfe,0x1f,0xde, 0x07,0x1e,0x00,0x1e,0x00,0x1e,0x3c,0x1e, 0x3c,0x1e,0x3e,0x3e,0x1f,0xfc,0x1f,0xfc, 0x0f,0xf8,0x03,0xe0,0x00,0x00,0x00,0x00, 0x00,0x00, }; const uint8_t moji_8[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xe0, 0x0f,0xf8,0x1f,0xfc,0x1f,0xfc,0x3e,0x3e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x3c,0x1e,0x1e,0x3c,0x1f,0xfc,0x0f,0xf8, 0x0f,0xf8,0x1f,0xfc,0x1e,0x3c,0x3c,0x1e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x3c,0x1e,0x3e,0x3e,0x1f,0xfc,0x1f,0xfc, 0x0f,0xf8,0x03,0xe0,0x00,0x00,0x00,0x00, 0x00,0x00, }; const uint8_t moji_7[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0xfe, 0x3f,0xfe,0x3f,0xfe,0x3f,0xfe,0x00,0x3e, 0x00,0x3c,0x00,0x3c,0x00,0x3c,0x00,0x7c, 0x00,0x78,0x00,0x78,0x00,0x78,0x00,0xf0, 0x00,0xf0,0x00,0xf0,0x01,0xf0,0x01,0xe0, 0x01,0xe0,0x01,0xe0,0x03,0xe0,0x03,0xc0, 0x03,0xc0,0x03,0xc0,0x07,0xc0,0x07,0x80, 0x07,0x80,0x0f,0x80,0x00,0x00,0x00,0x00, 0x00,0x00, }; const uint8_t moji_6[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xe0, 0x0f,0xf8,0x1f,0xfc,0x1f,0xfc,0x3e,0x3e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x00,0x3c,0x00, 0x3c,0x00,0x3c,0x70,0x3d,0xfc,0x3f,0xfc, 0x3f,0xfe,0x3e,0x3e,0x3c,0x1e,0x3c,0x1e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x3c,0x1e,0x3e,0x3e,0x1f,0xfc,0x1f,0xfc, 0x0f,0xf8,0x03,0xe0,0x00,0x00,0x00,0x00, 0x00,0x00, }; const uint8_t moji_5[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0xf8, 0x3f,0xf8,0x3f,0xf8,0x3f,0xf8,0x3c,0x00, 0x3c,0x00,0x3c,0x00,0x3c,0x00,0x3c,0x00, 0x3c,0xe0,0x3f,0xf8,0x3f,0xf8,0x3f,0xfc, 0x3c,0x7c,0x3c,0x3c,0x3c,0x3c,0x00,0x3c, 0x00,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c,0x3c, 0x3c,0x3c,0x3c,0x7c,0x3f,0xf8,0x1f,0xf8, 0x0f,0xf0,0x07,0xc0,0x00,0x00,0x00,0x00, 0x00,0x00, }; const uint8_t moji_4[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8, 0x00,0xf8,0x00,0xf8,0x01,0xf8,0x01,0xf8, 0x03,0xf8,0x03,0xf8,0x03,0xf8,0x07,0xf8, 0x07,0xf8,0x0f,0x78,0x0f,0x78,0x1e,0x78, 0x1e,0x78,0x1c,0x78,0x3c,0x78,0x3c,0x78, 0x78,0x78,0x7f,0xfe,0x7f,0xfe,0x7f,0xfe, 0x7f,0xfe,0x00,0x78,0x00,0x78,0x00,0x78, 0x00,0x78,0x00,0x78,0x00,0x00,0x00,0x00, 0x00,0x00, }; const uint8_t moji_3[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xe0, 0x0f,0xf8,0x1f,0xfc,0x1f,0xfc,0x3e,0x3e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x00,0x1e, 0x00,0x1e,0x00,0x3c,0x03,0xfc,0x03,0xf8, 0x03,0xf8,0x03,0xfc,0x00,0x3c,0x00,0x1e, 0x00,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x3c,0x1e,0x3e,0x3e,0x1f,0xfc,0x1f,0xfc, 0x0f,0xf8,0x03,0xe0,0x00,0x00,0x00,0x00, 0x00,0x00, }; const uint8_t moji_2[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xe0, 0x0f,0xf8,0x1f,0xfc,0x1f,0xfc,0x3e,0x3e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x00,0x1e,0x00,0x3c,0x00,0x3c,0x00,0x78, 0x00,0xf0,0x01,0xf0,0x03,0xe0,0x07,0xc0, 0x0f,0x80,0x0f,0x00,0x1e,0x00,0x1e,0x00, 0x3c,0x00,0x3c,0x00,0x3f,0xfe,0x3f,0xfe, 0x3f,0xfe,0x3f,0xfe,0x00,0x00,0x00,0x00, 0x00,0x00, }; const uint8_t moji_1[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0, 0x00,0xe0,0x01,0xe0,0x03,0xe0,0x0f,0xe0, 0x0f,0xe0,0x0f,0xe0,0x01,0xe0,0x01,0xe0, 0x01,0xe0,0x01,0xe0,0x01,0xe0,0x01,0xe0, 0x01,0xe0,0x01,0xe0,0x01,0xe0,0x01,0xe0, 0x01,0xe0,0x01,0xe0,0x01,0xe0,0x01,0xe0, 0x01,0xe0,0x01,0xe0,0x01,0xe0,0x01,0xe0, 0x01,0xe0,0x01,0xe0,0x00,0x00,0x00,0x00, 0x00,0x00, }; const uint8_t moji_0[] PROGMEM = { 0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xe0, 0x0f,0xf8,0x1f,0xfc,0x1f,0xfc,0x3e,0x3e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x3c,0x1e,0x3c,0x1e,0x3c,0x1e,0x3c,0x1e, 0x3c,0x1e,0x3e,0x3e,0x1f,0xfc,0x1f,0xfc, 0x0f,0xf8,0x03,0xe0,0x00,0x00,0x00,0x00, 0x00,0x00, }; void setup() { Serial.begin(9600); tuneNtp(); } void setLEDChibimoji(char datachar,char datachar2){ matrix.setTextSize(1); matrix.setCursor(50, 24); matrix.setTextColor(matrix.color444(0, 15 ,0)); matrix.print(datachar); matrix.print(datachar2); } void setLEDChibimoji2(char datachar,char datachar2,char datachar3,char datachar4,char datachar5,char datachar6,char datachar7,char datachar8){ matrix.setTextSize(1); matrix.setCursor(0, 0); matrix.setTextColor(matrix.color444(15, 0, 0)); // matrix.print(datachar); // matrix.print(datachar2); // matrix.print(datachar3); // matrix.print(datachar4); matrix.print(datachar5); matrix.print(datachar6); matrix.print(datachar7); matrix.print(datachar8); } void setLEDDekamoji(int keta,char datachar){ int data; data = (int) datachar; data = data - 48; keta = keta * 16; if (data == 0){ matrix.drawBitmap(keta, 0, (uint8_t*)moji_0, 16, 32, 0xFFF0,0x0000); } if (data == 1){ matrix.drawBitmap(keta, 0, (uint8_t*)moji_1, 16, 32, 0xFFF0,0x0000); } if (data == 2){ matrix.drawBitmap(keta, 0, (uint8_t*)moji_2, 16, 32, 0xFFF0,0x0000); } if (data == 3){ matrix.drawBitmap(keta, 0, (uint8_t*)moji_3, 16, 32, 0xFFF0,0x0000); } if (data == 4){ matrix.drawBitmap(keta, 0, (uint8_t*)moji_4, 16, 32, 0xFFF0,0x0000); } if (data == 5){ matrix.drawBitmap(keta, 0, (uint8_t*)moji_5, 16, 32, 0xFFF0,0x0000); } if (data == 6){ matrix.drawBitmap(keta, 0, (uint8_t*)moji_6, 16, 32, 0xFFF0,0x0000); } if (data == 7){ matrix.drawBitmap(keta, 0, (uint8_t*)moji_7, 16, 32, 0xFFF0,0x0000); } if (data == 8){ matrix.drawBitmap(keta, 0, (uint8_t*)moji_8, 16, 32, 0xFFF0,0x0000); } if (data == 9){ matrix.drawBitmap(keta, 0, (uint8_t*)moji_9, 16, 32, 0xFFF0,0x0000); } } void tuneNtp() { // wifi_set_sleep_type(NONE_SLEEP_T); matrix.stop(); delay(1000); Serial.printf("set wifi"); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); for(int i=0; i<30; i=i+1) { if (WiFi.status() == WL_CONNECTED) { break; } delay(500); } configTzTime("JST-9", "ntp.nict.jp", "ntp.jst.mfeed.ad.jp"); delay(500); WiFi.disconnect(); delay(1000); matrix.begin(); // wifi_set_sleep_type(LIGHT_SLEEP_T); } void loop() { time_t t; struct tm *tm; static const char *wd[7] = {"Sun","Mon","Tue","Wed","Thr","Fri","Sat"}; char datebuffer[16]; t = time(NULL); if (t == tprev){ delay(20); return; } tm = localtime(&t); Serial.printf("%04d/%02d/%02d(%s) %02d:%02d:%02d\n", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, wd[tm->tm_wday], tm->tm_hour, tm->tm_min, tm->tm_sec); sprintf(datebuffer, "%04d%02d%02d%02d%02d%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); if (datebuffer[11] != mprev) { // matrix.fillScreen(matrix.color444(0, 0, 0)); int mdata; mdata = (int) datebuffer[8]; if (mdata != 0){ setLEDDekamoji(0,datebuffer[8]); } setLEDDekamoji(1,datebuffer[9]); setLEDDekamoji(2,datebuffer[10]); setLEDDekamoji(3,datebuffer[11]); setLEDChibimoji2(datebuffer[0],datebuffer[1],datebuffer[2],datebuffer[3],datebuffer[4],datebuffer[5],datebuffer[6],datebuffer[7]); setLEDChibimoji(datebuffer[12],datebuffer[13]); } else { setLEDDekamoji(3,datebuffer[11]); setLEDChibimoji(datebuffer[12],datebuffer[13]); } ntpcounter++; if (ntpcounter > 40000){ tuneNtp(); ntpcounter = 0; } tprev = t; mprev = datebuffer[11]; }

できました!

特に事故もなく、無事点灯。
キャプションを入力できます

遠くからでもクッキリで。
いやあ便利!。

それにしてもまあ、RTCとか何もつけてないのに、勝手に時刻が合うのがちょっとだいぶヘン!。

まあ、NTP時計は、市販もされていますが、結構お高いので。
自作したら、1万以内に納まっちゃうので、結構かなりオススメです!。

以上です。

eucalyのアイコン画像
いつも、てきとうです
  • eucaly さんが 2021/02/17 に 編集 をしました。 (メッセージ: 初版)
ログインしてコメントを投稿する