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 PowerShell
cd ~
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に接続して電源on
step 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で計算)