05.Basicのデータをweb表示


05.Basicのデータをweb表示

M5Stack Basicで測定した温湿度,気圧データを、同じwifiに接続しているPCのhttp://m5stack.local/env に測定No.,時刻付で表示します。
・webの画面再読込みで数秒たってからデータ更新されます。
・USBバッテリーからの電源供給では3H(データ数180)以上の動作を確認しました。しかし、長時間ではHTML表示用の配列がオーバーになるはずです。
・M5の電源がoffになるとwebで 「このサイトにアクセスできません」 と表示されます。
・Arduino IDE > ファイル > スケッチ例 > (M5Stack-Core-ESP32用のスケッチ例) WebServer > HelloServer.ino を参考にしています。
・webServerオブジェクトを作り、on関数で指定したURLにアクセスされた時の処理を設定します。
・loop関数の中でhandleClient関数を実行してon関数で指定したURLにブラウザからのアクセスがあると、登録した処理関数が呼ばれます。
・WEBサーバとは、クライアントの指示に応じて静的画面などをWebブラウザーに送り返してくれるサーバーの事です。

HTML の説明


<!DOCTYPE html>   <!-- HTML5を使用 -->
<html lang="ja">  <!-- 日本語      -->
  <head>          <!-- 文書情報    -->
    <meta charset=\"utf-8\">  <!-- 文字コード           -->
    <title>M5測定</title>  <!-- 頁タイトル(上のタブ) -->
  </head>
  <body style="line-height:0.1";>    <!-- 行間を狭くする -->
    <h3>M5Stack Basic測定データ</h3>   <!-- 見出し(1-6) -->
    <p>No.____年/月/日,__時:分:秒,__℃,\%RH,_hPa</p>  <!-- 段落 --> 
    <p>000.2021/08/29, 16:36:54, 27.9, 58, 1013</p>  <!-- 例 -->
    <p>001.2021/08/29, 16:37:00, 27.9, 58, 1013</p>  <!-- 例 -->
    <p>002.2021/08/29, 16:38:00, 27.9, 58, 1013</p>  <!-- 例 -->
    <!-- No.,時刻,温湿度,気圧を埋込んだ文字列。ここに追加していきます -->
  </body>   <!-- 表示内容 -->
</html>

スケッチ

2つの"*****"は、自分のssidとそのパスワードを記入してください。buf,buf2の配列要素数が23kだとエラーが発生します。

// M5Stack Basicのデータを、同じwifiに接続しているPCの
// http://m5stack.local/env にNo.,時刻,温湿度,気圧を表示します。
// web画面再読込みで数秒たってからデータ更新。USBバッテリーで3H以上の動作確認
// M5がoffになると"このサイトにアクセスできません"とweb表示
// buf,buf2が23kだとエラーが発生
#include <M5Stack.h>   // M5Stack Basicを使用
#include <WiFi.h>      // wifiを使用
#include <WebServer.h> // M5が送信
#include <ESPmDNS.h>   // 名前(m5stack.local)をIPアドレスに変換
#include <Adafruit_BMP280.h> //気圧センサを使用(300-1100)±1hPa
#include <Adafruit_SHT31.h> //温湿度センサ(0-60)±0.2℃,(10-90)±2%RH
const char* ssid = "*****";  // 自分のssid 時刻取得の為
const char* password = "*****";     // そのパスワード
WebServer server(80); //WebServerオブジェクトを作る,80はHTTP通信の一般的ポート
Adafruit_SHT31 sht3x = Adafruit_SHT31(&Wire);  // 定義
Adafruit_BMP280 bme = Adafruit_BMP280(&Wire);  // 定義
char buf[22000], buf2[22000];                  // html文字列(NG 23k)
int count, printI;   // データ数count,countの表示する行
const int n = 1000;  // データ数max 0-999
int sokutei[n][9];   // 0測定年,1月,2日,3時,4分,5秒,6温度,7湿度,8気圧
int maeT = 99;       // 前回のsokutei[][]時刻の値

void handleRoot() {               // "/"(ルート)にアクセスされた時の処理関数
  String message = "/envへ移動してください。\n";  //表示文字
  server.send(200, "text/plain", message);//(ok,ファイルの分類/種類,web表示文字)
  Serial.println("ルートにアクセスされました");  // シリアルモニタに表示
}

void handleEnv() {     // "/env"にアクセスされた時の処理関数
  memset(buf2, '\0', sizeof(buf2));
  // 配列全体をヌル文字でクリア (メモリのポインタ,セット値,セットサイズ)
  printI = 0;                        // 最初のデータから
  while (printI <= count) {          // データの最後まで
    sprintf(buf,
            "<p>%03d, %4d/%02d/%02d, %02d:%02d:%02d, %4.1f, %3d, %4d</p>",
            printI, sokutei[printI][0], sokutei[printI][1], sokutei[printI][2],
            sokutei[printI][3], sokutei[printI][4], sokutei[printI][5],
            (float)sokutei[printI][6] / 10.0, sokutei[printI][7],
            sokutei[printI][8]);  // HTML用にNo.,温湿度,気圧を埋込んだ文字列
    strcat(buf2, buf);            // buf2にbufを結合
    printI++;                     // 次のデータNo.
  }
  sprintf(buf, "%s%s%s",
          "<!DOCTYPE html><html lang=\"ja\">"
          "<head><meta charset=\"utf-8\"><title>M5測定</title></head>"
          "<body style=\"line-height:0.1\";>"
          "<h3>M5Stack Basic測定データ</h3>"
          "<p>No.____年/月/日,__時:分:秒,__℃,\%RH,_hPa</p>",
          buf2,
          "</body>"
          "</html>");
  //Serial.println(buf);
  server.send(200, "text/html", buf); // ブラウザに送る
}

void handleNotFound() {  // ファイルが見つからない時の関数
  String message = "File Not Found\n\n";  // 表示文字
  server.send(404, "text/plain", message);
  //            (404=見つからない,ファイルの分類/種類,web表示文字)
  Serial.println("ファイルが見つかりません");//シリアルモニタに表示
}

void printGyou() {                           // 1行表示関数
  M5.Lcd.setTextColor(WHITE, BLACK);         // 黒地(前の字を消す)に白文字
  M5.Lcd.printf("%02d/%02d", sokutei[printI][1], sokutei[printI][2]); // 月日
  Serial.printf("%03d %02d/%02d", printI, sokutei[printI][1], sokutei[printI][2]);
  M5.Lcd.printf(" %02d:%02d:%02d", sokutei[printI][3],
                sokutei[printI][4], sokutei[printI][5]);  // 時分秒
  Serial.printf(" %02d:%02d:%02d", sokutei[printI][3],
                sokutei[printI][4], sokutei[printI][5]);  // シリアルモニタに表示
  M5.Lcd.setTextColor(GREEN, BLACK);               // 黒地(前の字を消す)に緑文字
  M5.Lcd.printf(" %4.1f", (float)sokutei[printI][6] / 10.0);    // 温度を表示
  Serial.printf(" %4.1f℃", (float)sokutei[printI][6] / 10.0);  // シリアルモニタに表示
  M5.Lcd.setTextColor(CYAN, BLACK);                // 黒地(前の字を消す)にほぼ黄文字
  M5.Lcd.printf("%3d", sokutei[printI][7]);        // 湿度を表示
  Serial.printf(" %3d%%", sokutei[printI][7]);     // シリアルモニタに表示
  M5.Lcd.setTextColor(PINK, BLACK);                // 黒地(前の字を消す)にピンク文字
  M5.Lcd.printf("%4d", sokutei[printI][8]);        // 気圧を表示
  Serial.printf(" %4dhPa\n", sokutei[printI][8]);  // シリアルモニタに表示
}

void setup() {
  M5.begin();                 // M5stack初期化
  M5.Lcd.setTextColor(GREEN); // 色変更(文字色[,背景色])
  M5.Lcd.setTextSize(2);      // 文字サイズ1~7
  M5.Lcd.setCursor(0, 0);     // 表示位置(x,y)
  Serial.println();           // シリアルモニタ 改行
  while (!bme.begin(0x76)) {  // I2Cアドレスが違っていたら
    M5.Lcd.println("エラー BMP280");  // 気圧センサー
  }
  while (!sht3x.begin(0x44)) {  // I2Cアドレスが違っていたら
    M5.Lcd.println("エラー SHT3X");  // 温湿度センサー
  }
  WiFi.begin(ssid, password);  // wifi接続開始(Webサーバを起動)
  Serial.print("wifi接続開始");  // シリアルモニタに表示
  while (WiFi.status() != WL_CONNECTED) { // wifi接続完待ち
    delay(500);                // 0.5秒待つ
    Serial.print(".");         // シリアルモニタに表示
  }
  Serial.print("\n接続中 ");       // シリアルモニタに表示
  Serial.println(ssid);           // ssid表示
  Serial.print("IPアドレス ");      // シリアルモニタに表示
  Serial.println(WiFi.localIP()); // IPアドレス表示
  configTime(3600L * 9, 0, "ntp.nict.jp",
              "time.google.com", "ntp.jst.mfeed.ad.jp");
  // 時刻の同期(GMTとローカル時刻との差(秒),夏時間で進める秒,NTPサーバ)
  if (MDNS.begin("m5stack")) {    // マルチキャストDNSにホスト名m5stackを
    // 登録し成功したら http://m5stack.local でこのWebサーバにアクセスできる
    Serial.println("MDNSレスポンダー開始");  // シリアルモニタに表示
  }
  server.on("/", handleRoot);    // ルートアクセス時に実行する関数を設定
  server.on("/env", handleEnv);  // envフォルダアクセス時に実行する関数を設定
  server.onNotFound(handleNotFound); // 見つからない時に実行する関数を設定
  server.begin();                    // Webサーバ開始
  Serial.println("HTTPサーバー起動");  // シリアルモニタに表示
  for (int i = 0; i <= 999; i++) {   // i=0-999
    for (int j = 0; j <= 8; j++) {   // j=0-8
      sokutei[i][j] = 0;             // 測定データクリア
    }
  }
  count = 0;                         // データNo.
}
void loop() {
  struct tm tm;                             // 時計用
  if (getLocalTime(&tm)) {                  // 現在時刻を取得
    sokutei[count][0] = tm.tm_year + 1900;  // 年
    sokutei[count][1] = tm.tm_mon + 1;      // 月
    sokutei[count][2] = tm.tm_mday;         // 日
    sokutei[count][3] = tm.tm_hour;         // 時
    sokutei[count][4] = tm.tm_min;          // 分
    sokutei[count][5] = tm.tm_sec;          // 秒
    if (maeT != sokutei[count][4]) {        // 前と分が違ったら
      maeT = sokutei[count][4];             // 今の値を前の値とする
      sokutei[count][6] = (int)(sht3x.readTemperature() * 10.0 + 0.5); // センサーの温度x10を読取る
      sokutei[count][7] = (int)(sht3x.readHumidity() + 0.5);  // センサーの湿度を読取る
      sokutei[count][8] = (int)(bme.readPressure() / 100.0 + 0.5); // センサーの圧力を読取る
      if (count < 15) {               // 1画面(15行)埋まっていなければ
        printI = count; printGyou();  // 下の行に表示
      } else {                        // 1画面埋まったら
        M5.Lcd.setCursor(0, 0);       // 表示位置(x,y)
        for (printI = count - 14; printI <= count; printI++) { // スクロール
          printGyou();                                         // 1行表示
        }
      }
      count++;            // 次のデータ
    }
  }
  server.handleClient();  // クライアントからのアクセスの処理(ブラウザからのリクエストを処理)
  delay(1000);            // 1秒待つ
}