今週末は天気がイマイチです。ゴールデンウィークに手入れした剪定枝の片づけを少しだけやりました。まだ切り切れていない枝と草むしりは来週末ですね。
10年以上前に作成して今も使い続けている青色LEDデジタル時計(
製作記事はこちら) を
NTP時計に改造しようかと思い、プロトタイプで実験を行いました。この時計はそれなりに精度は高いですが、それでも長期間使っていると次第にズレてきます。高い場所に設置してあるので、その都度時刻合わせするのが面倒です。
NTPとは、Network Time Protocolの略で、インターネットで時刻情報を提供しているサーバから日付や時刻情報を取ってくるプロトコルです。
普通の時計の時刻を合わせるには電波時計が一般的ですが、コンピュータの時刻を合わせるのはNTPが一般的です。WindowsパソコンなどもNTPで時刻合わせを行っています。他にGPSを使う方法もありますが、アンテナを屋外に出すのが面倒です。
WiFiがあるところなら3~400円程度のESP32マイコンでNTPサーバに繋がるので便利な世の中になりました。
ということで、以下はESP32単体で時計を作ってみた様子です。表示は8x2の小型液晶で、I2Cで繋がるので線は2本だけ。他には電源とGNDです。
一旦、NTPで時刻情報を取ってきてESP32の時計を合わせると、あとはNTPサーバにアクセスしなくても大丈夫です。一晩動かして精度を確認しましたが、目視では確認できないくらいズレていませんでした。1日に1回程度、NTPサーバに繋げば精度は十分維持できるでしょう。
次に時刻情報をPICマイコンに渡して、PICで小型液晶に表示してみました。PICは8ピンPICのPIC12F1501です。最終的にはLEDマトリクスをダイナミック点灯しているPICに繋ぎます。
時刻情報の受け渡しは、1線のシリアル転送にしました。スピードはとってもゆっくりで、1ビット当たり1msecです。スタート、ストップもあるので70bps程度になります。これは、最終的に受信側のPICがLEDのダイナミック点灯のための割り込みがずっと発生しているので、多少タイミングがズレても受信できるようにするため。
時刻合わせは時間、分、秒だけなので3バイト転送すればよいので遅くても問題ないです。時刻合わせだけなら、00:00:00 に送ることにして1ビットでもよいのですが、電源を入れた直後などに自動で時刻合わせするには3バイトあったほうがよいです。
あとは、ESP32単体(開発キットは高いので、ESP32のみ)をユニバーサル基板に載せますが、既存のデジタル時計は5Vで動いていて、ESP32は3.3Vなので3.3Vの3端子レギュレータと信号のレベル変換回路が必要です。最初から時計を作るならESP32だけで作った方が楽ですが、今回は既存の時計にアドオンする形にします。
以下、ESP32だけで小型液晶に日付と時刻を表示する(写真2枚目)スケッチです。無保証です。
//------------------------------------------------------------------------
// '22.05.14 naka
// ESP32 ntp時計(小型液晶に日付と時刻表示)
//
//------------------------------------------------------------------------
#include <WiFi.h>
#include <WiFiClientSecure.h>
#define SDAPIN 21
#define SCLPIN 22
#define SERIAL_OUT 25
#include <FaBoLCDmini_AQM0802A.h>
FaBoLCDmini_AQM0802A lcd;
char ssid[] = "your_WiFi_ssid";
char password[] = "your_WiFi_password";
void setup()
{
char msg[128];
pinMode(SERIAL_OUT, OUTPUT);
digitalWrite(SERIAL_OUT, HIGH);
Serial.begin(115200);
// モニタLCD設定
SetupLCD();
dispLCD_msg("NTP- "," CLOCK");
delay(1000);
if (wifi_connect()) {
// NTPサーバに接続し、時刻設定
configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp");
Serial.print("NTP done");
disp_time();
delay(1000);
}
// WiFi接続を切る
wifi_disconnect();
}
void loop()
{
disp_time();
delay(1000);
}
void disp_time() {
char yymmdd[10],hhmmss[10];
struct tm timeinfo;
getLocalTime(&timeinfo);
sprintf(yymmdd, "%02d.%02d.%02d",(timeinfo.tm_year + 1900)%100, timeinfo.tm_mon + 1, timeinfo.tm_mday);
sprintf(hhmmss, "%02d:%02d:%02d",timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
dispLCD_msg(yymmdd,hhmmss);
}
void dispLCD_msg(char msg1[],char msg2[]) {
lcd.clear();
lcd.print(msg1);
lcd.setCursor(0, 1); // Col,Raw
lcd.print(msg2);
}
void SetupLCD() {
lcd.begin();
lcd.command(0x38);
lcd.command(0x39);
lcd.command(0x14);
lcd.command(0x71);
// lcd.command(0x51); // 5V
lcd.command(0x56); // 3.3V
delay(2);
lcd.command(0x6c);
delay(300);
lcd.command(0x38);
delay(1);
lcd.command(0x01);
delay(2);
lcd.command(0x0c);
delay(2);
}
// Wifi接続
bool wifi_connect() {
char msg[128];
// 静的アドレスとDNS指定(普通は要らないはずだが、我が家のWiFiルータは動的割り当てとDNSサーバ指定がないとダメだった)
IPAddress myIP(192,168,0,78);
IPAddress gateway(192,168,0,1);
IPAddress subnet(255,255,255,0);
IPAddress dns1(8, 8, 8, 8); // Google DNS primery server
IPAddress dns2(8, 8, 4, 4); // Google DNS secondary server
WiFi.config(myIP,gateway,subnet,dns1,dns2);
for (int i=0;i<5;i++) { // WiFi接続、5回繰り返す
WiFi.begin(ssid, password);
delay(1000);
Serial.print(msg);
long int StartTime=millis();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
if ((StartTime+10000) < millis()) break; // 10秒待って繋がらないときは諦める
}
if (WiFi.status() == WL_CONNECTED) {
IPAddress ip = WiFi.localIP();
sprintf(msg,"WiFi connected IP:%d.%d.%d.%d",ip[0], ip[1], ip[2], ip[3]);
Serial.print(msg);
break;
}
}
if (WiFi.status() != WL_CONNECTED) return false;
return true;
}
void wifi_disconnect() {
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
//-------------------------------------------------------------------------------
// EOF
//-------------------------------------------------------------------------------