06.Wio TerminalでWi-Fiから時刻取得
06.Wio TerminalでWi-Fiから時刻取得
Wi-Fi経由でNTP時刻を所得し表示するスケッチです。NTP (Network Time Protocol)はネットにつながれた機器が正確な時刻を共有するために使われるプロトコルです。時刻同期に協定世界時(UTC)を基準にしています。日本時刻は、協定世界時を9H進めたもの(UTC+9)で、日本標準時(JST)と呼ばれます。スケッチは
Seeed Wiki > Platform > Wio Terminal > Network > Wi-Fi の
https://wiki.seeedstudio.com/Wio-Terminal-Wi-Fi/
に出ているWi-Fi NTPサンプルコードを少し変更して日本語で画面表示しました。元の例では、UDPを使用してNTP時刻を12時間ごとに取得し、メインチップに組込まれているRTCを使用して時刻を最新の状態に保ちますが、時計のように表示時刻は更新していません。また、表示先はシリアルモニタです。
UDP(User Datagram Protocol)は、ネットで一般的に使用されているプロトコルの1つです。TCP等と同様に、IPの上位プロトコルとして使用されます。一方的にメッセージを送るプロトコルで、TCPに比べて通信エラーへの耐性は低く、信頼性は高くありませんが、スピードやリアルタイム性が求められる通信に向いています。
RTC(real-time clock)は、内蔵時計です。Wio Terminalのメインチップ(SAMD51)内に組込まれている時計機能を使用していて、バッテリーバックアップはしていません。
サンプルからの主な変更点は、
・NTPサーバーはいくつもありその1つの情報通信研究機構の"ntp.nict.jp"を使用しました。
・起動時のみ1度受信し、以後内部RTCを1分ごとに表示するので誤差は最大1分程度あります,
・オフセット時間を+8H(中国)から+9H(JST)に修正。
注意) Wio Terminalでは、電源offで時刻は保持せず、2000/1/1 00:00:00になってしまいます。
RSSI
RSSI(Received Signal Strength Indicator)は、ルータからの信号をデバイスがどれくらい受信できるかを測定したものです。SSIRとmetageek.comの信号品質基準
-30dBm 非常に強い
-67dBm とても強い
-70dBm 強い
-80dBm 弱い
-90dBm 使えない
https://milestone-of-se.nesuke.com/nw-basic/wireless/rssi-snr-definition/
Seeed_Arduino_rpcWiFi
前回インストールしたSeeed ArduinorpcWiFi 1.0.5ライブラリについてhttps://github.com/Seeed-Studio/Seeed_Arduino_rpcWiFi
特徴
・ESP32wifiライブラリとの最大互換性
SeeedArduino rpcUnifiedを呼出して、Arduino-ESP32とのWiFi機能の互換性があるので、小さな変更でESP32 Wi-Fiアプリを使用できます。
・RealtekRTL8720DNを搭載
・デュアルバンド 2.4/5GHzWi-Fi (802.11a/b/g/n)
・低消費電力
Seeed_Arduino_RTCライブラリをインストール
Arduino-IDE > スケッチ > ライブラリをインクルード > ライブラリを管理... > "Seeed_Arduino_RTC"で検索Seeed_Arduino_RTC by Seeed Studio 2.0.0をインストール > 閉じる
millisDelayライブラリをインストール
遅延機能とタイマーを提供しています。https://github.com/ansonhe97/millisDelay
にて、Code > Download ZIP > そのままディスクトップに置く
Arduino-IDE > スケッチ > ライブラリをインクルード > .ZIP形式のライブラリをインストール... > 先程の"millisDelay-master.zip"を選択して開く
C:\Users\〇〇〇\Documents\Arduino\libraries\millisDelay-master\src
の下に"millisDelay.cpp" と "millisDelay.h" が出来ました。
NTPのパケットフォーマット
最初の32bitで送信元ポート
Source Port=123 上位(左)16bit宛先ポート
Destination Port=123 下位(右)16bit次の32bitで
セグメント長
Segment Length 上位(左)16bitチェックサム
Checksum 下位(右)16bitその次はUDPデータで、最初の32bitは上位(左)から
閏秒指示子 (Leap Indicator)
1バイト目の一部1-2の2bit 11(2)=3(10)
その日の最後の1分が1秒追加(うるう秒=Leap Second)されるか、もしくは1秒削除されるかを事前に予告するフィールド。
0:予告なし(通常)
1:その日の最後の1分が61秒
2:その日の最後の1分が59秒
3:時刻同期無し
バージョン番号 (Version Number)
1バイト目の一部3-5の3bit 100(2)=4(10)
現在のNTP及びSNTPバージョンは4です。
Mode
1バイト目の一部6-8の3bit 011(2)=3(10)
アソシエーションのモード。NTP時刻情報の提供形態
0:予約
1:Symmetric Active
2:Symmetric Passive
3:Client
4:Server
5:Broadcast
6:NTP control message(制御クエリ)
7:プライベート利用に予約
階層 (Stratum)
2バイト目 9-16の8bit0:原子時計やGPSの時刻、NTPパケット上は取扱いません。
1:原子時計やGPSに物理的に直結したNTPサーバ
2以降は、それより1つ上のStratumのNTPサーバから時刻同期したことを意味します。
16:時刻同期していない。
17-255:使われていません。
ポーリング間隔 (Poll)
3バイト目 17-24の8bit次のNTPパケット送出までの最大間隔 秒に対してlog2の値。例)デフォルト値10の場合は2^10=1024秒。
精度 (Precision)
4バイト目 25-32の8bitNTPを扱う機器のシステムクロックが扱える精度を相手に示します。Pollと同じく、log2の値。例)-18は約1uS。
ルート遅延 (Root Delay)
5-8バイト目32bit符号付固定小数点数 小数点がビット15と16の間
Strarum 1までの往復遅延(秒)。相対時間と周波数変位によって正負両方の値をとります。通常、-数mS~数百mS。
ルート拡散 (Root Dispersion)
9-12バイト目32bit符号無し固定小数点数で小数点がビット15と16の間
Strarum 1までの誤差(秒)。通常、0~数百mS。
参照識別子 (Reference ID)
13-16バイト目 32bitどのNTPサーバを参照しているかを表しています。
Stratum 2~14のNTPサーバは、上位の NTPサーバのIPアドレスをセット。
Stratum 1のNTPサーバは、参照先を暗示する任意の1~4 文字のASCII文字列(GPS、WWVB等)
参照時刻 (Reference Timestamp)
17-24バイト目64bitのタイムスタンプフォーマット
最後に同期した時刻
開始時刻 (Origin Timestamp)
25-32バイト目64bitのタイムスタンプフォーマット
NTPサーバにリクエストを送信した時刻
受信時刻 (Receive Timestamp)
33-40バイト目64bitのタイムスタンプフォーマット
NTPサーバがリクエストを受信した時刻
送信時刻 (Transmit Timestamp)
41-48バイト目64bitのタイムスタンプフォーマット
NTPサーバのパケットの送信時刻
以下略
タイムスタンプフォーマット
64bit符号なし固定小数点。上位32bitが整数秒、下位32bitが小数点以下。1900/1/1 0:0:0からの経過秒数。協定世界時(UTC:Universal Time, Coordinated)。日本時間はUTCから9H遅れ(+9H)
時刻
・NTP(Network Time Protocol)サーバのパケットの送信時刻(UTC)
・UTC(協定世界時 Coordinated Universal Time)
1900/1/1 00:00:00からの秒数
・JST(日本標準時)
JST=UTC+9H (UTCから9H進める)
・UNIX時間(コンピューターシステム上での時刻)
1970/1/1 00:00:00からの秒数
うるう年
2/29がある年です。西暦/4が割切れる年。しかし、100で割切れて、400で割切れない年は平年とします。よって、1900年は平年です。1900-1970年にうるう日は17日あります。スケッチ
自分のSSIDとそのパスワードの**********の所は自分のを入れて下さい。
// ネットから時刻受信表示 (起動時のみ受信,以後内部RTCを1分ごとに表示するので誤差1分あります,
// 電源offで時刻リセット)
#include <rpcWiFi.h> // Wi-Fiを使用
#include <millisDelay.h> // ノンブロック遅延を使用。システムが停止しないタイマー
#include <RTC_SAMD51.h> // RTC(real-time clock)を使用
#define LGFX_AUTODETECT // LovyanGFX対応機種を自動認識
#include <LovyanGFX.hpp> // LovyanGFXを使用
millisDelay updateDelay; // updateDelayを定義
DateTime now; // nowを定義
WiFiClient client; // clientを定義
WiFiUDP udp; // udpを定義
RTC_SAMD51 rtc; // rtcを定義
static LGFX lcd; // LGFXのインスタンスを作成
unsigned long devicetime; // 受信時刻
const char ssid[] = "**********"; // 自分のSSID
const char password[] = "**********"; // そのパスワード
const int NTP_PACKET_SIZE = 48; // NTPタイムスタンプはメッセージの最初の48バイト
unsigned int localPort = 2390; // udpローカルポート番号
char timeServer[] = "ntp.nict.jp"; // 情報通信研究機構のNTPサーバー
byte packetBuffer[NTP_PACKET_SIZE]; // パケットを保持するためのバッファ
char daysOfTheWeek[7][4] = {"日", "月", "火", "水", "木", "金", "土"}; // 曜日
void setGamen() { // 画面初期設定関数
lcd.init(); // LCD初期化
lcd.setBrightness(64); // バックライト輝度(暗0-255明)
lcd.setFont(&lgfxJapanGothic_20); // ゴシック固定長 縦ドット(16,20,24,28,32)
//lcd.setTextScroll(true); // 画面下端に到達後スクロール
//lcd.setScrollRect(0, 0, lcd.width(), lcd.height());//スクロール範囲(X,Y,W,H)
lcd.setTextSize(1.0); // 文字サイズ(横縦幅倍数)
lcd.setTextColor(TFT_GREEN, TFT_BLACK); // (文字色,背景色)
//lcd.setCursor(0, 0); // 画面左上から開始
}
unsigned long sendNTPpacket(const char* address) { // 指定アドレスにNTP要求送信関数
for (int i = 0; i < NTP_PACKET_SIZE; ++i) { // 0-47の48バイトについて
packetBuffer[i] = 0; // バッファ内の全バイトを0
} // 以下にNTP要求を形成するための値を設定
packetBuffer[0] = 0b11100011; // うるう年=同期無し,Ver4(通常),Mode=Client
packetBuffer[1] = 0; // 地層=原子時計???
packetBuffer[2] = 6; // 送出間隔 2^6=64秒
packetBuffer[3] = 0xEC; // 精度
// packetBuffer[4]-[7]:ルート遅延は8バイトのゼロ
// packetBuffer[8]-[11]:ルート拡散は8バイトのゼロ
packetBuffer[12] = 49; // [12]-[15]はIPアドレス[49,78,49,52]
packetBuffer[13] = 0x4E; // IPアドレス
packetBuffer[14] = 49; // IPアドレス
packetBuffer[15] = 52; // IPアドレス
udp.beginPacket(address, 123); // addressのポート123へ書込開始
udp.write(packetBuffer, NTP_PACKET_SIZE);// udpデータを書出す(送信バッファ,メッセージ長)
udp.endPacket(); // udpデータを書込んだ後に送信
}
unsigned long getNTPtime() { // 時刻受信関数 UNIXの日本時間を返す
if (WiFi.status() == WL_CONNECTED) { // WiFiネットに接続したら
udp.begin(WiFi.localIP(), localPort);// udp(転送バッファ)を初期化 (IPアドレス,ポート番号)
sendNTPpacket(timeServer); // NTP要求送信関数へ
delay(1000); // 1秒待つ
if (udp.parsePacket()) { // 次に利用可能な着信パケットの処理を開始し、
// パケットの存在を確認。戻り値:パケットのサイズ(バイト)。udp.read()の前に必要
//lcd.setCursor(10, 210); lcd.print("時刻受信 "); // 表示
udp.read(packetBuffer, NTP_PACKET_SIZE); // パケットをバッファに読込む
// (格納バッファ(char or 配列byte), 読取バイト数(int))
// NTPサーバのパケットの送信時刻(1900/1/1 0:0:0からの秒数)は、41-48バイト目で整数部は41-44バイト目
// 1ワード=2バイト=16ビット
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);// 値をwordデータ型に変換
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // (上位(左)バイト,下位(右)バイト)
unsigned long secsSince1900 = highWord << 16 | lowWord; // 16ビット左にシフトしビットごとにORする
const unsigned long seventyYears = 2208988800; // 70年=(70x365+閏日17)x24x60x60秒
unsigned long epoch = secsSince1900 - seventyYears; // UNIX=UTC-70年
long tzOffset = 32400; // 9H=9x60x60=32400S
unsigned long adjustedTime; // JST(日本時間)
return adjustedTime = epoch + tzOffset; // JST=UTC+9H
} else { // 利用可能なパケットがない場合
udp.stop(); // サーバーから切断し、udpセッション中に使用されていたリソースを解放
return 0; // 0は失敗、利用可能なパケットがない
}
udp.stop(); //ntp timeを頻繁に呼び出さないで、リソースのリリースを停止
} else {
return 0; // 0は失敗、ネットに接続できない
}
}
void timeprint() { // 時刻画面表示関数
lcd.setCursor(60, 10); lcd.setTextSize(2.0); // 表示位置 文字サイズ
lcd.printf("%04d/%02d/%02d", now.year(), now.month(), now.day());// 年月日表示
lcd.setCursor(100, 60); lcd.setTextSize(1.5); // 表示位置 文字サイズ
lcd.print("("); // 表示
lcd.print(daysOfTheWeek[now.dayOfTheWeek()]); // 曜日表示
lcd.print("曜日)"); // 表示
lcd.setCursor(60, 135); lcd.setTextSize(1.5); // 表示位置 文字サイズ
int hourP = now.hour(); // 時刻
if (hourP > 12) { // 0-12時の場合
hourP = hourP - 12; // 12引く
lcd.print("PM"); // 表示
} else { // 13-23時の場合
lcd.print("AM"); // 表示
}
lcd.setCursor(100, 110); lcd.setTextSize(3.0); // 表示位置 文字サイズ
lcd.printf("%02d:%02d", hourP, now.minute()); // 時分表示
lcd.setTextSize(1.0); // 文字サイズ
}
void setup() {
setGamen(); // 画面初期設定関数へ
lcd.setCursor(10, 180); lcd.print("接続:" + String(ssid)); // 接続するSSID表示
WiFi.disconnect(); // Wi-Fiシールドをネットから切断
lcd.setCursor(10, 210); lcd.print("Wi-Fi接続待ち。 "); // 表示
WiFi.begin(ssid, password); // WiFiライブラリのネット設定を初期化し、接続開始
while (WiFi.status() != WL_CONNECTED) { // WiFiネットに接続していなかったら
WiFi.begin(ssid, password); // WiFiライブラリのネット設定を初期化し、接続開始
delay(500); // 0.5秒待つ
}
long rssi = WiFi.RSSI(); // 受信信号強度を取得
lcd.setCursor(10, 210); lcd.printf("受信強度:%4lddBm", rssi); // 受信信号強度を表示
if (rssi <= -81) { // -81dBm以下の時
lcd.print("(使えない)"); // 表示
} else if (rssi <= -71) { // -71~-80dBmの時
lcd.print("(弱い)"); // 表示
} else if (rssi <= -68) { // -68~-70dBmの時
lcd.print("(強い)"); // 表示
} else if (rssi <= -31) { // -31~-67dBmの時
lcd.print("(とても強い)"); // 表示
} else { // 0~-30dBmの時
lcd.print("(非常に強い)"); // 表示
}
devicetime = getNTPtime(); // 時刻受信関数へ
if (devicetime == 0) { // 時刻取得出来ない場合
lcd.setCursor(10, 210); lcd.print("時刻を受信できません。 "); // 表示
}
rtc.begin(); // 内部RTCを初期化
rtc.adjust(DateTime(devicetime)); // 受信時刻に更新
now = rtc.now(); // 更新後のWioのRTC時刻
timeprint(); // 時刻画面表示関数へ
updateDelay.start(60 * 1000); // ノンブロック遅延開始 遅延1分
}
void loop() {
if (updateDelay.justFinished()) { // ノンブロック遅延が終了したら
updateDelay.repeat(); // ノンブロック遅延を再開 定期更新
//devicetime = getNTPtime(); // 時刻受信関数へ
//rtc.adjust(DateTime(devicetime)); // 受信時刻に更新
now = rtc.now(); // RTC時刻
timeprint(); // 時刻画面表示関数へ
}
}
* フラッシュメモリの60%を使用。