14.Wio Terminalで温湿度・気圧ロガー
14.Wio Terminalで温湿度・気圧ロガー
M5Stack Basicで使っていたのを、必要部をWio Terminalに移植しました。
過去ブログの
・06.Wio TerminalでWi-Fiから時刻取得
・08.Wio Terminalで温湿度・気圧表示
と、Wi-Fi NTPサンプル
https://wiki.seeedstudio.com/Wio-Terminal-Wi-Fi/
を参照しました。
Wio Terminal:320x240 2.4inch
M5Stack Basic:320x240 2.0inch
USBコネクタ部がガタガタしていて接触が悪く、ポートが消えてしまいアップロード出来ないことが何度もありました。
(参考) Wio Terminalの使い方を始める
https://wiki.seeedstudio.com/ja/Wio-Terminal-Getting-Started/
Wio Terminal の Wi-Fi
https://wiki.seeedstudio.com/Wio-Terminal-Network-Overview/Wio端末のRealtek RTL8720 Wirelessコアのファームウェアを更新する必要があります。これは必須であり、サンプルに進む前に最新のファームウェアにアップデートする必要があります。
step 1-1
スタート > Windowsツール > Windows PowerShellcd ~
git clone https://github.com/Seeed-Studio/ambd_flash_tool
Gitをインストール
git cloneでエラーが出たので、Git for Windowsをインストールhttps://learn.microsoft.com/ja-jp/devops/develop/git/install-and-set-up-git
1.Git for Windows/x64用をダウンロード
https://github.com/git-for-windows/git/releases/download/v2.52.0.windows.1/Git-2.52.0-64-bit.exe
2.インストール。インストール時に既定値を選択。 インストールすると、コマンド プロンプトまたは PowerShellからGitを使用できます。本筋に戻ります。
step 1-2
(ambd_flash_toolディレクトリへ移動)cd ambd_flash_tool
step 1-3
WioをPCに接続して電源onstep 1-4
(初期ファームウェアを消去).\ambd_flash_tool.exe erase
(時間がかかるけど、ウィンドウは閉めない事)
step 2
最新ファームウェア(ambd_flash_toolディレクトリ内にいる間に、次のコマンドを実行)
.\ambd_flash_tool.exe flash
ファームウェア更新を終わります。
RTL8720ファームウェアのVer
Arduino-IDEで更新を確認します。スケッチは、
#include "rpcWiFi.h"
#include <erpc/erpc_port.h>
void setup() {
Serial.begin(115200);
while(!Serial); // シリアルモニタの起動待ち
char* version = rpc_system_version();
Serial.print("RTL8720 Firmware Ver: ");
Serial.println(version);
erpc_free(version);
}
void loop() {}
結果 2.1.2 → 2.1.3になりました。
ボードのVer
ArduinoCoreのSeeed SAMD ボードをVer1.8.1以上にアップSeeed SAMD Boards by Seeed Studio 1.8.5(最新)
にしました。
Wi-Fiに必要なライブラリ
WioでのWi-Fiを始めるには、以下のライブラリが必要です。更新する際は、rpcwifiを更新するだけで、その下4つのWiFi依存のライブラリもインストールします。Seeed Arduino rpcWiFi by... 1.1.0(最新)
1.Seeed Arduino rpcUnited by Hongtai... 2.1.4
2.0inch by Peter Yang 3.0.2
3.Seeed Arduino FS by Hongtai.liu 2.1.3
4.Seeed_Arduino_SFUD by Seeed Studio 2.0.2
上記5点にはスケッチ例があります。
温湿度の基準
厚生労働省の建築物環境衛生管理基準https://www.mhlw.go.jp/bunya/kenkou/seikatsu-eisei10/
には、建物内の温湿度等の基準が出ています。特定建築物維持管理権原者は、建築物衛生法に規定される「建築物環境衛生管理基準」に従って当該特定建築物の維持管理をしなければなりません。その内容の一部は、
2.空気環境の調整
エ.温度:18~28℃(居室温度を外気温度より低くする場合は、その差を著しくしない)
オ.湿度:40~70%
スケッチの設定内容
・温湿度・気圧測定に"ENV2"を使用します。・起動時と1週間ごとに時刻合わせをするためwifiに接続します。
1週間の設定は、"millisDelay"を使用します。
・画面右下の備考欄に、気温28℃を超えると"冷房"、18℃未満なら"暖房"、湿度40%未満なら"加湿"と表示します。
・1画面に6H前までのデータを表示します。1ドット約1.4分です。
・日本語表示は、LovyanGFXが使えます。
https://github.com/lovyan03/LovyanGFX
ボードの設定
rduino-IDE > ツール > ボード > Seeed SAMD Boards > Seeeduino Wio Terminalスケッチ
"**********"は自分のwifiのssidとパスワードを記入してください。
// ENV2で温湿度・気圧表示 logerWio.ino 320x240
// Seeed SAMD Boards by Seeed Studio 1.8.5(最新)
// Arduino-IDE 2.3.7(最新)
// Adafruit SHT31 Library by Adafruit 2.2.2(最新)
// Adafruit BMP280 Library by Adafruit 2.6.8(最新)
// LovyanGFX by lovyan03 1.2.7(最新)
// Seeed Arduino rpcWiFi by...1.1.0をインストール
#include <Adafruit_SHT31.h> // 温湿度センサ(0~60)±0.2℃,(10~90)±2%
Adafruit_SHT31 sht = Adafruit_SHT31(&Wire); // shtを定義
#include <Adafruit_BMP280.h> // 気圧センサ (300~1100)±1hPa
Adafruit_BMP280 bme = Adafruit_BMP280(&Wire); // bmeを定義
#include <rpcWiFi.h> // Wi-Fiを使用
WiFiClient client; // clientを定義
WiFiUDP udp; // udpを定義
unsigned int localPort = 2390; // udpローカルポート番号
#include <millisDelay.h> // 他の処理を止めずにdelay
millisDelay rtcDelay; // updateDelayを定義
#include <RTC_SAMD51.h> // RTC(real-time clock)を使用
RTC_SAMD51 rtc; // rtcを定義
DateTime now; // nowを定義
const char* timeURL = "ntp.nict.jp"; // 情報通信研究機構のNTPサーバー
unsigned long devicetime; // 受信時刻
const int NTP_PACKET_SIZE = 48; // NTPタイムスタンプはメッセージの最初の48バイト
byte packetBuffer[NTP_PACKET_SIZE]; // パケットを保持するためのバッファ
#define LGFX_AUTODETECT // LovyanGFX対応機種を自動認識
#include <LovyanGFX.hpp> // LovyanGFXを使用
static LGFX lcd; // LGFXのインスタンスを作成
float tmpS = 0, humS = 0, prsS = 0; // 温度(x10),湿度,気圧(/100)の測定値
int16_t tmp = 0, hum = 0, prs = 0; // 温度(x10),湿度,気圧(/100)のy軸値
int8_t xGraph0 = 60, yGraph0 = 25, dyGraph = 47; // グラフのx0,y0 横線刻み
int16_t tmpMin = 10, tmpMax = 30; // 温度表示10,15,20,25,30℃(0.11℃/dot)
int16_t humMin = 30, humMax = 70; // 湿度表示30,40,50,60,70%(0.53%/dot)
int16_t prsMin = 990, prsMax = 1030; // 気圧表示990,1000,1010,1020,1030hPa(1.1hPa/dot)
int16_t graphD[260][3]; // グラフデータ (横320-60)x(温度,湿度,気圧)
String bikou = " "; // 最下行に備考表示
static const char* wd[7] = { "日", "月", "火",
"水", "木", "金", "土" }; // 曜日
const char* ssid = "**********"; // 自分のwifiのSSID
const char* pw = "**********"; // そのパスワード
void printTime() { // 時刻画面表示関数 1分で更新
now = rtc.now(); // RTC時刻
lcd.setCursor(0, 0); // カーソル位置
lcd.printf(" %d/%2d/%2d (", now.year(), now.month(), now.day()); // 年/月/日表示
int youbiNo = now.dayOfTheWeek(); // 曜日番号 0→日
if (youbiNo == 6) lcd.setTextColor(TFT_BLUE, TFT_BLACK); // (字,地) 土曜日は青
if (youbiNo == 0) lcd.setTextColor(TFT_RED, TFT_BLACK); // (字,地) 日曜日は赤
lcd.print(wd[youbiNo]); // 曜日表示
lcd.setTextColor(TFT_GREEN, TFT_BLACK); // (字,地) 戻す
lcd.print(") "); // 表示
int hourP = now.hour(); // 時刻
if (hourP > 12) { // 13-23時の場合
hourP = hourP - 12; // 12引く
lcd.print("pm"); // 表示
} else { // 0-12時の場合
lcd.print("am"); // 表示
}
lcd.printf(" %2d:%02d", hourP, now.minute()); // 時分表示
lcd.setTextSize(1.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]:ルート遅延は8byteのゼロ
// packetBuffer[8]-[11]:ルート拡散は8byteのゼロ
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(timeURL); // NTP要求送信関数へ
delay(1000); // 1秒待つ
if (udp.parsePacket()) { // 次に利用可能な着信パケットの処理を開始し、
// パケットの存在を確認。戻り値:パケットのサイズ(バイト)。udp.read()の前に必要
Serial.println("UDPパケットを受信"); // 表示
udp.read(packetBuffer, NTP_PACKET_SIZE); // パケットをバッファに読込む
// (格納バッファ(char or 配列byte), 読取バイト数(int))
// NTPサーバのパケットの送信時刻(1900/1/1 0:0:0からの秒数)は、41-48バイト目で整数部は41-44byte目
// 1ワード=2byte=16bit
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); //値をwordデータ型に変換
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); //(上位(左)byte,下位(右)byte)
unsigned long secsSince1900 = highWord << 16 | lowWord; // 16bit左にシフトしbitごとにORする
const unsigned long seventyYears = 2208988800UL; // 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 setWifi() { // Wifi設定関数
Serial.print("接続:" + String(ssid)); // 接続するSSID表示しない?????
WiFi.disconnect(true); // Wi-Fiシールドをネットから切断 古い設定を削除
Serial.print("wifi接続待ち"); // 表示しない?????
WiFi.begin(ssid, pw); // WiFiライブラリのネット設定を初期化し、接続開始
while (WiFi.status() != WL_CONNECTED) { // WiFiネットに接続していなかったら
WiFi.begin(ssid, pw); // WiFiライブラリのネット設定を初期化し、接続開始
delay(500); // 0.5秒待つ
}
Serial.print("SSID: "); // 表示
Serial.println(WiFi.SSID()); // 接続しているネットワークのSSIDを表示
IPAddress ip = WiFi.localIP(); // 家庭内で使われるIPアドレスを取得
Serial.print("IP Address: "); // 表示
Serial.println(ip); // IPアドレスを表示
long rssi = WiFi.RSSI(); // 受信信号強度を取得
Serial.printf("受信強度:%4lddBm ", rssi); // 受信信号強度を表示
if (rssi <= -81) { // -81dBm以下の時
Serial.println("(使えない)"); // 表示
} else if (rssi <= -71) { // -71~-80dBmの時
Serial.println("(弱い)"); // 表示
} else if (rssi <= -68) { // -68~-70dBmの時
Serial.println("(強い)"); // 表示
} else if (rssi <= -31) { // -31~-67dBmの時
Serial.println("(とても強い)"); // 表示
} else { // 0~-30dBmの時
Serial.println("(非常に強い)"); // 表示
}
}
void setRTC() { // RTC設定関数
devicetime = getNTPtime(); // 時刻受信関数へ (夏時間調整なし) エポックUTC時刻
if (devicetime == 0) { // NTP時刻が0なら
Serial.println("NTP時刻を取得できません"); // 表示
}
if (!rtc.begin()) { // RTCが初期化できなければ
Serial.println("RTCが見つかりません"); // 表示
while (1) delay(10); // 永久ループ
}
rtc.adjust(DateTime(devicetime)); // NTPタイムを使用して時刻を調整
now = rtc.now(); // RTC時刻を取得
Serial.print("RTC時刻: "); // 表示
Serial.println(now.timestamp(DateTime::TIMESTAMP_FULL)); // RTC時刻を表示
}
void setGamen() { // 画面初期設定関数
lcd.init(); // LCD初期化
lcd.setBrightness(255); // バックライト(暗0-255) 64は暗い
lcd.setRotation(1); // 画面表示向き(0-3,4-7)
lcd.setFont(&lgfxJapanGothic_24); // ゴシック固定長(縦16,20,24,28,32)
lcd.setTextSize(1.0); // 文字サイズ(倍数)
lcd.setTextColor(TFT_GREEN, TFT_BLACK); // (文字色,背景色)
lcd.setCursor(0, 0); // 画面左上から開始
}
void setup() {
Serial.begin(115200); // シリアルモニタの通信速度
setGamen(); // 画面の初期設定関数へ
Wire.begin(); // Wireライブラリ初期化 ()はマスタとしてバスに接続
while (!sht.begin(0x44)) { // SHT31が見つからなかったら
lcd.println("ENV2 未接続"); // 表示
}
while (!bme.begin(0x76)) { // BMP280が見つからなかったら
lcd.println("ENV2 未接続"); // 表示
}
rtcDelay.start(7 * 24 * 60 * 60 * 1000); // mS NTP経由で更新 7日
setWifi(); // wifi設定関数へ
setRTC(); // RTC設定関数へ
}
void loop() {
bikou = " "; // 備考リセット
// グラフデータを1移動
for (int16_t i = 1; i < 260; i++) { // 全データについて
for (int16_t j = 0; j < 3; j++) { // 温度・湿度・気圧について
graphD[i - 1][j] = graphD[i][j]; // 右端を1つ空ける
}
}
// 測定し配列に保存 (tmp・hum・prsは、測定値ではなくy値)
tmpS = sht.readTemperature(); // 温度を取得し整数化
if (tmpS > 28.0) bikou = "冷房"; // 建築物環境衛生管理基準より
if (tmpS < 18.0) bikou = "暖房"; // 建築物環境衛生管理基準より
lcd.setCursor(0, 215); // 最下行のカーソル位置
lcd.printf("%5.1f℃", tmpS); // 温度を表示 四捨五入
tmp = (int)((tmpMax - tmpS) * (dyGraph * 4 + 1) / (tmpMax - tmpMin) + yGraph0 + 0.5); // y位置計算
if (tmp < yGraph0) tmp = yGraph0; // 上にはみ出したら
if (tmp > dyGraph * 4 + 1 + yGraph0) tmp = dyGraph * 4 + 1 + yGraph0; // 下にはみ出したら
humS = sht.readHumidity(); // 湿度を取得し整数化
if (humS < 40.0) bikou = "加湿"; // 建築物環境衛生管理基準より
lcd.setTextColor(TFT_CYAN, TFT_BLACK); // (字,地)
lcd.printf("%3.0f%%", humS); // 湿度を表示 四捨五入
hum = (int)((humMax - humS) * (dyGraph * 4 + 1) / (humMax - humMin) + yGraph0 + 0.5); // y位置計算
if (hum < yGraph0) hum = yGraph0; // 上にはみ出したら
if (hum > dyGraph * 4 + 1 + yGraph0) hum = dyGraph * 4 + 1 + yGraph0; // 下にはみ出したら
prsS = bme.readPressure() / 100.0; // 気圧を取得し単位変換して整数化
lcd.setTextColor(0xFB1C, TFT_BLACK); // (字,地) 薄いMAGENTA
lcd.printf("%5.0fhPa", prsS); // 気圧を表示 四捨五入
prs = (int)((prsMax - prsS) * (dyGraph * 4 + 1) / (prsMax - prsMin) + yGraph0 + 0.5); // y位置計算
if (prs < yGraph0) prs = yGraph0; // 上にはみ出したら
if (prs > dyGraph * 4 + 1 + yGraph0) prs = dyGraph * 4 + 1 + yGraph0; // 下にはみ出したら
graphD[259][0] = tmp; // 温度を右端に書込む
graphD[259][1] = hum; // 湿度を右端に書込む
graphD[259][2] = prs; // 気圧を右端に書込む
// グラフ部分クリア
lcd.fillRect(xGraph0, yGraph0, 320 - xGraph0, dyGraph * 4 + 1, TFT_BLACK); // 前の表示を消す
// 枠表示
lcd.drawFastVLine(xGraph0, yGraph0, dyGraph * 4, 0x8000); // 縦線(x0,y0,L,色)
for (int i = 0; i < 5; i++) { // 5回繰り返す
if (i == 1 || i == 3) { // 補助線
lcd.drawFastHLine(xGraph0, i * dyGraph + yGraph0, 320 - xGraph0, 0x4000); // 横線(x0,y0,L,色)
} else { // センター・上端・下端の線
lcd.drawFastHLine(xGraph0, i * dyGraph + yGraph0, 320 - xGraph0, 0x8000); // 横線(x0,y0,L,色)
}
}
// 測定値表示
for (int16_t i = 0; i < 259; i++) { // 全データについて
if (graphD[i][0] > 0) lcd.drawLine(i + 60, graphD[i][0], i + 61, graphD[i + 1][0], TFT_GREEN); // 温度の線表示
if (graphD[i][1] > 0) lcd.drawLine(i + 60, graphD[i][1], i + 61, graphD[i + 1][1], TFT_CYAN); // 湿度の線表示
if (graphD[i][2] > 0) lcd.drawLine(i + 60, graphD[i][2], i + 61, graphD[i + 1][2], 0xFB1C); // 気圧の線表示
}
lcd.setTextColor(TFT_GREEN, TFT_BLACK); // (字,地) 戻す
printTime(); // 時刻表示関数へ 1行目
// 軸の値
lcd.setTextSize(0.7); // 文字サイズ(倍数)
lcd.setCursor(0, 25); // 上線の値のカーソル位置
lcd.println(" 30℃"); // 表示
lcd.setTextColor(TFT_CYAN, TFT_BLACK); // (字,地)
lcd.println(" 70%"); // 表示
lcd.setTextColor(0xFB1C, TFT_BLACK); // (字,地) 薄いMAGENTA
lcd.println("1030hPa"); // 表示
lcd.setCursor(0, 95); // 中央線の値のカーソル位置
lcd.setTextColor(TFT_GREEN, TFT_BLACK); // (字,地)
lcd.println(" 20℃"); // 表示
lcd.setTextColor(TFT_CYAN, TFT_BLACK); // (字,地)
lcd.println(" 50%"); // 表示
lcd.setTextColor(0xFB1C, TFT_BLACK); // (字,地) 薄いMAGENTA
lcd.println("1010hPa"); // 表示
lcd.setCursor(0, 166); // 下線の値のカーソル位置
lcd.setTextColor(TFT_GREEN, TFT_BLACK); // (字,地)
lcd.println(" 10℃"); // 表示
lcd.setTextColor(TFT_CYAN, TFT_BLACK); // (字,地)
lcd.println(" 30%"); // 表示
lcd.setTextColor(0xFB1C, TFT_BLACK); // (字,地) 薄いMAGENTA
lcd.println(" 990hPa"); // 表示
lcd.setTextColor(TFT_GREEN, TFT_BLACK); // (字,地) 戻す
lcd.setTextSize(1.0); // 文字サイズ 戻す
lcd.setCursor(260, 215); // カーソル位置
lcd.print(bikou); // 備考内容を表示
//delay(6000); // 6秒待つ テスト用
delay(83077); // 1画面 6H=21,600,000ms≒ 83,077mSx260≒1.4分x260
//delay(332307);// 1画面24H=86,400,000ms≒332,308mSx260≒5.5分x260
if (rtcDelay.justFinished()) { // rtcDelayの設定遅延時間が終わったら
rtcDelay.repeat(); // タイマーを繰返す
setRTC(); // RTC設定関数へ
}
}
* flash memory(507kbyte)のうち、スケッチが76%使用。(1000byte=1kbyteで計算)