14.picoでNTP時計 Arduino
14.picoでNTP時計 Arduino
モニタへ時刻表示します。起動時にwifi接続し、1度だけNTPサーバーから時刻を取得します。その後RTCで時刻更新します。
ハード
・pico・wifi : Pico-ESP8266
・モニタ : Pico-ResTouch-LCD-3.5
参照
・RTCの内蔵時計(RP2040_RTCライブラリ)で、"13.picoでRTCサンプル動作 Arduino"参照・NTPの時刻取得(ESP8266_AT_WebServe)で、"12.picoでNTPサンプル動作 Arduino"参照
・モニタ表示(TFT_eSPIライブラリ)で、"09.picoでモニタ表示 Arduino編"参照
カラー
TFT_eSPI Ver2.4.72https://www.arduino.cc/reference/en/libraries/tft_espi/
Bodmer/TFT_eSPI
https://github.com/Bodmer/TFT_eSPI/blob/master/TFT_eSPI.h
より、25色定義されています。
0x0000 0, 0, 0 TFT_BLACK 黒
0x000F 0, 0, 128 TFT_NAVY 濃紺
0x001F 0, 0, 255 TFT_BLUE 青
0x03E0 0, 128, 0 TFT_DARKGREEN 深緑
0x03EF 0, 128, 128 TFT_DARKCYAN 暗いシアン
0x07E0 0, 255, 0 TFT_GREEN 緑
0x07FF 0, 255, 255 TFT_CYAN 水色に近い青緑
0x7800 128, 0, 0 TFT_MAROON 栗色
0x780F 128, 0, 128 TFT_PURPLE 赤紫
0x7BE0 128, 128, 0 TFT_OLIVE 暗い緑みの黄
0x7BEF 128, 128, 128 TFT_DARKGREY 濃い灰色
0x867D 135, 206, 235 TFT_SKYBLUE 空色
0x9A60 150, 75, 0 TFT_BROWN 茶
0x915C 180, 46, 226 TFT_VIOLET 青紫
0xB7E0 180, 255, 0 TFT_GREENYELLOW 黄緑
0xC618 192, 192, 192 TFT_SILVER 銀
0xD69A 211, 211, 211 TFT_LIGHTGREY 淡い灰色
0xF800 255, 0, 0 TFT_RED 赤
0xF81F 255, 0, 255 TFT_MAGENTA 赤紫
0xFC9F 255, 146, 255 TFT_Lighter pink より淡いピンク
0xFDA0 255, 180, 0 TFT_ORANGE 橙
0xFE19 255, 192, 203 TFT_PINK ピンク
0xFEA0 255, 215, 0 TFT_GOLD 金
0xFFE0 255, 255, 0 TFT_YELLOW 黄
0xFFFF 255, 255, 255 TFT_WHITE 白
0000-0xFFFFの16bit 65536色でRGB565は、15-11の5bitは赤, 10-5の6bitは緑, 4-0の5bitは青となります。通常の24bitカラー RGB888 のRとBの下位3bit、Gの下位2bitを切り捨てています。RGの場合、0~111(2)→0(2), 1000(2)→1(2), FF(16)→1F(16)となります。
しかし、ここではなぜか10が反転しています。
時分表示の色の変化
スケッチで時計の時分表示を256色変化させています。256を3等分し、刻みは3にします。R(赤)G(緑)B(青)の1周85ループで、1色を0で固定し、他の1色を255から3まで下げ、残りの色を0から252まで上げていきます。周の切替わりは色が大きく変わらないようにします。___1周目_, 2周目_, 3週目
(R)255→3, 0_____, 0→252
(G)0_____, 0→252, 255→3
(B)0→252, 255→3, 0
ただし、RGB888で計算していますが、実際はRGB565なので、RとBの3,6の時は0で、Gの3の時も0のはずです。
使用している通信
Serial(=SerialUSB) : PCSerial1(=UART 0) : ESP8266(GP0-1)
Serial2(=UART 1) : -
SPI0 : -
SPI1 : モニタ(GP8-13,15)
I2C0 : -
I2C1 : -
SD : (GP5,18-22)
Touch : (GP16-17)
空き : GP2-4,6-7,14,23-28
書込みが出来ない
現象
書込みが出来なくなり、シリアルポートも開けなくなってしまいました。表示
・Resetting COM15 (COM15のリセット)・スケッチの書き込み中にエラーが発生しました
・Converting to uf2, output size: 102912, start address: 0x2000 (uf2に変換、出力サイズ:102912、開始アドレス:0x2000)(uf2ファイルはできている模様)
・No drive to deploy.(展開するドライブがありません。)
対策
ドライブが無いというので、1.シリアルモニタを閉じる。
2.picoのBOOTSELスイッチを押しながらUSBを接続し、スイッチを離す。
3.スケッチをマイコンボードに書込む。
4....NEW.UF2 が表示されたら早くシリアルモニタを開く。
5.「シリアルポートが開けません。」と表示されたら、再度シリアルモニタを開く。
これで、今のところ対処できています。
スイッチ一覧
pico
●BOOTSEL (プッシュSW) CN近く押しながらUSBケーブルを接続するとpicoがドライブになる。 (FLASHのChip Select端子をopen→1kΩでGNDに接続)
モニタ
Pico-ResTouch-LCD-3.5●RUN (プッシュSW) CNを上にして左面中央に
USBの抜き差しの代わり (picoのRUNをGNDに接続)
1.電池基板のRESET SWを押し続けたまま
2.picoのBOOTSEL SWを押し続け
3.1.を離し、2.を離す。
wifi
Pico ESP8266●IO0 (プッシュSW)
ESP8266に書込時に使用 (ESP88266のGP0をGNDに接続 プルアップ)
●RST (プッシュSW)
ESP8266に書込時に使用 (ESP8266のRSTをGNDに接続 プルアップ)
* ESP8266へ書込みは、IO0ボタンを押したまま、ESP8266のRESETボタンを押す。
電池基板
●RESET(プッシュSW) 基板裏面でCNと反対側モニタのRUNと同じにUSBの抜き差しの代わり (picoのRUNをGNDに接続)
●POWER(スライドSW)
(電池の+側と昇圧ICのEN端子間に接続)
スケッチ
"**********"の2ヶ所は自分のssidとそのパスワードを記入してください。
// tokei.ino (Udpは1日480回(=3分に1回)以上受信しない事)
#ifndef defines_h // 定義がされてなければ
#define defines_h // 定義する
#include <ESP8266_AT_WebServer.h> // ESP8266のAT_WebServerを使用
#include <RP2040_RTC.h> // RP2040_RTCを使用
char ssid[] = "**********"; // 自分のSSID
char pass[] = "**********"; // そのパスワード
#define DEBUG_ESP8266_AT_WEBSERVER_PORT Serial // 定義
#define _ESP_AT_LOGLEVEL_ 0 // デバックレベル(0-4)
#define SHIELD_TYPE "ESP8266-AT & ESP8266_AT_WebServer Library" // 定義
#define EspSerial Serial1 // picoは1と2がある
#endif // defines_h
#include "ESP8266_AT_Udp.h" // ESP8266ATコマンド用UDP
#include <SPI.h> // SPI通信LCD使用
#include <TFT_eSPI.h> // ハードウェア固有のライブラリ
int status = WL_IDLE_STATUS;//WiFi.begin()が呼び出された時に割当てられる一時的ステータス
char timeServer[] = "ntp.nict.jp"; // 情報通信研究機構のNTPサーバー
unsigned int localPort = 2390; // UDPパケットを受信するローカルポート(ESP8266側)
const int NTP_PACKET_SIZE = 48; // NTPタイムスタンプは 最初の48バイトに含まれる
const int UDP_TIMEOUT = 2000; // UDPパケットの到着タイムアウト(mS)
byte packetBuffer[48]; // 入出力パケットを保持するバッファ
char buf[32]; // 時刻表示文字用
ESP8266_AT_UDP Udp; // UDPでパケットを送受信するためのUDPインスタンス
TFT_eSPI tft = TFT_eSPI(); // カスタムライブラリを呼出す
int i = 0, c = 0, R, G, B, hour12; // i&c:色計算,RGB=0-255,hour12:12時間表示
time_t timep; // timepをtime_t型変数に定義
struct tm *time_inf; //
void sendNTPpacket(char *ntpSrv) { // 指定タイムサーバーにNTPリクエストを送信する関数
memset(packetBuffer, 0, 48);//バッファの全byteを0 (メモリのポインタ,セット値,セットサイズ)
//NTPリクエストの形成に必要な値を初期化(パケットの詳細は上記URL参照)
packetBuffer[0] = 0b11100011; // LI=0b11(時刻同期無し),Ver=0b100(現在),Mode=0b011(Client)
packetBuffer[1] = 0; // 階層=0(取扱わず)
packetBuffer[2] = 6; // パケット送出最大間隔 2^6=64秒
packetBuffer[3] = 0xEC; // 精度 符号付EC(16)=-20(10) 2^-20=0.95uS
// ルート遅延[4]-[ 7]の4byte(32bit)は0
// ルート拡散[8]-[11]の4byte(32bit)は0
packetBuffer[12] = 49; // 参照識別子32bit [49,78,40,52]?
packetBuffer[13] = 0x4E; //
packetBuffer[14] = 49; //
packetBuffer[15] = 52; // これで、全NTPフィールドを設定完了
Udp.beginPacket(ntpSrv, 123); // 接続開始し UDPデータをリモート接続に書込む。NTPのリクエストは123番ポート
Udp.write(packetBuffer, 48); // UDP送信データをリモート接続に書込む
Udp.endPacket(); // パケットを終了して送信
}
void setGamen() {
tft.init(); // TFT_eSPIライブラリを初期化
tft.setRotation(1); // 回転 0:縦,1:左が下,2:逆さま,3:右が下 (CNを上にして)
tft.invertDisplay(1); // なぜか色反転しないとだめ
tft.fillScreen(TFT_BLACK); // 黒地
}
void setWifi() {
Serial.println(BOARD_NAME); // "RASPBERRY_PI_PICO"と表示
Serial.println(SHIELD_TYPE); // "ESP8266-AT & ESP8266_AT_WebServer Library"と表示
Serial.println(ESP8266_AT_WEBSERVER_VERSION);//"ESP8266_AT_WebServer v1.5.4"と表示
EspSerial.begin(115200); // ESPモジュールのシリアル初期化
WiFi.init(&EspSerial); // ESPモジュールの初期化 "[ESP_AT] Use ES8266-AT Command"と表示
if (WiFi.status() == WL_NO_SHIELD) { // wifi状態でシールドが無ければ
Serial.println("WiFiシールドがありません"); // 表示
while (true); // 永久ループ
}
WiFi.begin(ssid, pass); // ネットに接続
while (WiFi.status() != WL_CONNECTED) { // WiFiに接続して無ければ
delay(500); // 0.5秒待つ
Serial.print("."); // 表示
}
//Serial.println(WiFi.localIP()); // ESP8266のIP 表示させると止まる時がある???
Udp.begin(localPort); // WiFiUDPライブラリとネット設定を初期化
}
void getNTP() { // 時刻を取得しセットする
sendNTPpacket(timeServer); //タイムサーバーにNTPパケットを送信する関数へ ここのみ
unsigned long startMs = millis(); // 現在時間をセット
while (!Udp.available() && (millis() - startMs) < UDP_TIMEOUT) {}//データが未到着で 時間内なら待つ
int packetSize = Udp.parsePacket(); // 次に利用可能な処理を開始しその着信パケットサイズ
if (packetSize) { // パケットがあれば
Serial.print("受信のパケットサイズ= "); // 表示
Serial.println(packetSize); // "48"と表示
Serial.print("接続先IP= "); // 表示
//IPAddress remoteIp = Udp.remoteIP(); // リモートIP読込み
//Serial.print(remoteIp); // リモートIP表示 表示させると止まる時がある???
Serial.print(", port= "); // 表示
Serial.println(Udp.remotePort()); // リモートポート表示
Udp.read(packetBuffer, NTP_PACKET_SIZE); // パケットを受信したのでバッファに読込む
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);//送信時刻上位16bit
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);//送信時刻下位16bit
// [40]-[47]の64bitが送信時刻で、そのうち[40]-[43]が整数秒 [44]-[47]が小数点以下
unsigned long secsSince1900 = highWord << 16 | lowWord;//2ワードをつなげて長整数。NTP時間
Serial.print("NTP時間(1900/1/1からの秒数)= "); // 表示
Serial.println(secsSince1900); // NTP時間(秒)
const unsigned long seventyYears = 2208988800UL; // 70年=(70x365+閏日17)x24x60x60秒
unsigned long JSTtime = secsSince1900 - seventyYears + 32400; // -70年+日本時間9H
Serial.print("JST時間(1970/1/1からの秒数)= "); // 表示
Serial.println(JSTtime); // JST(秒)を表示
setTime(JSTtime); // システム時刻を設定
}
}
void setup() {
Serial.begin(115200); // システムモニタ初期設定
setGamen(); // 画面初期設定
while (!Serial && millis() < 5000); // モニタを開いてなく起動5秒以内なら待つ
setWifi(); // wifi初期設定
delay(200); // 0.2秒待つ
rtc_init(); // RTCシステムを初期化
getNTP(); // 関数へ 時刻を取得しセット
}
void loop() {
tft.setCursor(40, 30, 4); // (x,y,font)フォント4(26px 96文字)
tft.setTextColor(TFT_GREEN, TFT_BLACK); // 緑字 黒地
tft.setTextSize(2); // 文字サイズ倍数
time_t t = now(); // time_t型式に変換
sprintf(buf, "%d/%.2d/%.2d %s ", year(t), month(t), day(t),
dayShortStr(weekday(t))); // 時刻文字列作成
tft.println(buf); // 時刻表示
tft.setCursor(200, 90, 4); // (x,y,font)フォント4(26px 96文字)
if (hour(t) > 12) { // 午後なら
hour12 = hour(t) - 12; // 12時間引く
tft.println("PM"); // 表示
} else { // 午前なら
hour12 = hour(t); // 12時間時刻変数
tft.println("AM"); // 表示
}
tft.setCursor(30, 150, 7); // (x,y,font)フォント7(48px 7セグメント)
i = c % 255; // i=0-254 255=85x3
if (i < 85) { // 1周目
B = i * 3; R = 255 - B; G = 0; // G=0で色変化
} else if (i < 170) { // 2周目
i -= 85; G = i * 3; B = 255 - G; R = 0; // R=0で色変化
} else { // 3周目
i -= 170; R = i * 3; G = 255 - R; B = 0; // B=0で色変化
}
if ((++c) == 255) c = 0; // 次の色
tft.setTextColor(tft.color565(R, G, B), TFT_BLACK); // 緑字 黒地
tft.setTextSize(3); // 文字サイズ倍数
sprintf(buf, "%.2d:%.2d", hour12, minute(t)); // 時刻文字列作成
tft.println(buf); // 時刻表示
tft.setTextSize(1); // 文字サイズ倍数を戻す
}
* フラッシュメモリ(2Mbyte)を、スケッチが5%使用。RAM(262kbyte)を、グローバル変数が3%使用、ローカル変数で253kbyte使用可能。(1000byte=1kで計算)