01.M5StickC+で簡易熱中症計


01.M5StickC+で簡易熱中症計

M5StickC Plusの仕様(一部)

M5StickC Plus (M5StickC Plus2)
・価格:4037円 (4147円)
・画面:240x135 (同じ)
・バッテリー:120mAh (200mAh)
・SoC:ESP32-PICO-D4 (ESP32-PICO-V3-02)
・Flash:4MB (8MB)
・SRAM:520kB (PSRAM:2MB)
・ボタン:x2 (x3)
・バッテリ管理IC:AXP192 (無し)

* ブザー,IR送信機,赤色LED,マイク,6軸IMU,RTCは両方にあります。2024/7/26に、在庫限りから通常販売になりましたが、これから購入なら2の方が良いでしょう。

WBGT(暑さ指数)レベル

ここでの区分は以下にしました。

35以上(黒) 熱中症特別警戒アラートレベル *1
33以上(紫) 極めて危険(非常に危険)熱中症警戒アラートレベル *2
31以上(赤) 危険
28以上(橙) 厳重警戒
25以上(黄) 警戒
21以上(水) 注意
21未満(青) ほぼ安全 *3

*1と*2は、環境省 熱中症予防情報サイト
https://www.wbgt.env.go.jp/about_alert.php
https://www.wbgt.env.go.jp/about_special_alert.php

*3は、環境省 熱中症予防情報サイト 運動に関する指針
https://www.wbgt.env.go.jp/wbgt.php

それ以外は、日本生気象学会 日常生活における熱中症予防指針
https://seikishou.jp/cms/wp-content/uploads/20220523-v4.pdf
気温(21~40℃で1℃刻み)と湿度(20~100%で5%刻み)で整数で表示。

によります。

不快指数

THI (Temperature-Humidity Index)
https://ja.wikipedia.org/wiki/%E4%B8%8D%E5%BF%AB%E6%8C%87%E6%95%B0

夏の蒸し暑さを数量的に表した指数で、温熱指標の一つです。

乾球温度と湿球温度を使用する場合
THI=0.72*(tmp1+tmp2)+40.6
 tmp1:乾球温度(℃)
 tmp2:湿球温度(℃)

気温と湿度を使用する場合
THI=0.81*tmp+0.01*hum*(0.99*tmp-14.3)+46.3
 tmp:乾球温度,気温(℃)
 hum:湿度(%)

例)気温27℃,湿度55%で不快指数75
気温29℃,湿度70%で不快指数80

THIと体感
 〜49 寒くてたまらない
50〜54 寒い
55〜59 肌寒い
60〜64 何も感じない
65〜69 快適
70〜74 不快感を持つ人が出始める
75〜79 半数以上が不快に感じる
80〜84 全員が不快に感じる
85〜  暑くてたまらない

気温(25~45℃で1℃刻み)と湿度(0~100%で5%刻み)の表では不快指数は整数で表示されています。

表示内容

バッテリー残量

(1行目右側)
Lang-ship M5StickCバッテリーライフ検証 その1
https://lang-ship.com/blog/work/m5stickc-battery-life-1/

にて、バッテリー駆動時間(分)とバッテリー電圧(V)のグラフ
"M5StickC Battery Test(液晶明るさ別)"より
0分目が約4.0V、止まる3分前ぐらいの電圧が約3.3Vとなっているので
・100%は4.0V
・0%は3.3V
・その間は直線変化
とし、バッテリ電圧から残量(%)を計算しました。

暑さ指数

(1行目左側,2行目)
1行目左側に計算値と、2行目に内容を表示します。1~4行目の地の色は暑さ指数の項目に書いた色にしました。

不快指数

(3行目左側,4行目)
3行目左側に計算値と、4行目に体感を表示します。

センサー測定値

(5行目)
温湿度・気圧センサーであるENV2の測定値を表示します。
本当は、M5StickC ENV Hatの方が線が無くかっこ良いです。
Ver3 (SHT30/QMP6988)は、1848円
Ver2(SHT30/BMP280/BMM150)は、売切れ 1430円 磁界も測定
Ver1 (DHT12/BMP280/BMM150)は、販売終了 814円 磁界も測定

表示残り秒

(3行目右側)
動作時間を持たせるため30秒で自動に電源offします。測定と表示は、5秒ごとなので飛び飛びの秒数になります。

スケッチ


// 温湿度・気圧・室内簡易暑さ指数・不快指数の表示 M5StickC+ 240x135 10字5行
// 暑さ指数:(温度:四捨五入で整数,湿度:切上げで5の倍数の整数)
// 不快指数:式は測定値そのもので計算
// 電源on:電源ボタンを2秒, 電源off:6秒
// Adafruit BMP280 Library と Adafruit SHT31 Library が必要
// 30秒で電源が切れるプログラムなので、更新中に電源onか注意必要
#include <M5StickCPlus.h>                      // M5StickCPlusを使用
#include <AXP192.h>                            // 電源管理ICを使用
#define LGFX_AUTODETECT                        // LovyanGFX自動認識
#define LGFX_USE_V1                            // LovyanGFX Ver1を使用
#include <LGFX_AUTODETECT.hpp>                 // クラス"LGFX"を用意
static LGFX lcd;                               // LGFXのインスタンスを作成
#include <Wire.h>                              // I2Cのセンサーを使用
#include <Adafruit_SHT31.h>                    // 温湿度センサを使用
#include <Adafruit_BMP280.h>                   // 気圧センサを使用
Adafruit_SHT31 sht = Adafruit_SHT31(&Wire);    // sht定義
Adafruit_BMP280 bme = Adafruit_BMP280(&Wire);  // bmp定義だがbmeとする
unsigned long startTime, currentTime;          // 開始時間,現在時間 mS
unsigned long period = 30 * 1000;              // 目標経過時間 mS
float batV;                                    // 電池電圧
int16_t tmp, pre;                              // 10倍の温度,気圧 -32768~32767
int8_t hum, thi, wbgt, batP;                   // 湿度,不快指数,暑さ指数,電池容量 -128~127
int8_t tmpIndex, humIndex, RemainingT;         // 温度配列,湿度配列.残り秒
//           ℃   %        20  25  30  35  40  45  50  55  60  65  70  75  80  85  90  95 100% 暑さ指数表ver4
int8_t wbgtT[20][17] = { { 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 19, 20, 20, 21, 21 },    //21℃
                         { 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 20, 21, 21, 22, 22 },    //22
                         { 15, 16, 16, 17, 18, 18, 19, 19, 20, 20, 20, 21, 21, 22, 22, 23, 23 },    //23
                         { 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24 },    //24
                         { 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25 },    //25
                         { 18, 18, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26 },    //26
                         { 18, 19, 20, 20, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27 },    //27
                         { 19, 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28 },    //28
                         { 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29 },    //29
                         { 21, 21, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 29, 30, 30 },    //30
                         { 21, 22, 23, 24, 24, 25, 26, 26, 27, 27, 28, 29, 29, 30, 30, 31, 31 },    //31
                         { 22, 23, 24, 24, 25, 26, 26, 27, 28, 28, 29, 29, 30, 31, 31, 32, 32 },    //32
                         { 23, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 30, 31, 31, 32, 33, 33 },    //33
                         { 24, 25, 25, 26, 27, 28, 28, 29, 30, 30, 31, 31, 32, 32, 33, 34, 34 },    //34
                         { 24, 25, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 33, 34, 34, 35 },    //35
                         { 25, 26, 27, 28, 29, 29, 30, 31, 31, 32, 33, 33, 34, 34, 35, 35, 36 },    //36
                         { 26, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 34, 35, 35, 36, 36, 37 },    //37
                         { 27, 28, 29, 29, 30, 31, 32, 33, 33, 34, 35, 35, 36, 36, 37, 37, 38 },    //38
                         { 27, 28, 29, 30, 31, 32, 33, 33, 34, 35, 35, 36, 37, 37, 38, 38, 39 },    //39
                         { 28, 29, 30, 31, 32, 33, 34, 34, 35, 36, 36, 37, 38, 38, 39, 39, 40 } };  //40

void LCDset() {            // 初期画面設定関数
  lcd.init();              // 画面初期化
  lcd.setRotation(1);      // 回転方向(0-3)
  lcd.setBrightness(100);  // バックライト輝度(0-255)実際は数段階
  //lcd.setColorDepth(24);                  // RGB888 24bit (16=RGB565 16bit)
  lcd.setCursor(0, 0);                      // 表示位置(x,y)
  lcd.setFont(&fonts::lgfxJapanGothic_24);  // ゴシック(8,12,16,20,24,28,32,36,40)
  lcd.fillScreen(TFT_BLACK);                // 画面クリア(色)
  lcd.setTextColor(TFT_GREEN, TFT_BLACK);   // 色設定(文字色[,背景色])
}

void setup() {
  M5.begin();                              // M5初期化
  LCDset();                                // 初期画面設定関数へ
  while (!sht.begin(0x44)) {               // shtのアドレスで開始できない時
    M5.lcd.println("エラー SHT3X未接続");  // 表示
  }
  while (!bme.begin(0x76)) {                // BMP280のアドレスで開始できない時
    M5.lcd.println("エラー BMP280未接続");  // 表示
  }
  startTime = millis();  // 開始時間
}

void loop() {
  currentTime = millis();                               // 現在時間
  if (currentTime - startTime < period) {               // 指定表示時間以内なら
    tmp = int16_t(sht.readTemperature() * 10.0 + 0.5);  // センサーで10倍の温度を読取る
    lcd.setTextColor(TFT_GREEN, TFT_BLACK);             // (文字色{,背景色})
    lcd.setCursor(0, 108);                              // 表示位置(x,y) 5行目
    lcd.printf("%5.1f℃", tmp / 10.0);                   // 表示 温度

    hum = int8_t(sht.readHumidity() + 0.5);  // センサーで湿度を読取る
    lcd.setTextColor(TFT_CYAN, TFT_BLACK);   // (字,地)
    lcd.printf("%3d%%", hum);                // 表示 湿度

    pre = int16_t(bme.readPressure() / 100.0 + 0.5);  // センサーで気圧を読取る
    lcd.setTextColor(TFT_PINK, TFT_BLACK);            // (字,地)
    lcd.printf("%5dhPa\n", pre);                      // 表示 気圧

    tmpIndex = int8_t(tmp / 10.0 + 0.5) - 21;  // 四捨五入で整数 配列温度番号
    humIndex = (hum - 16) / 5;                 // 配列湿度番号
    wbgt = wbgtT[tmpIndex][humIndex];          // wbgtを表から読む

    lcd.setCursor(2, 28);  // 表示位置(x,y) 2行目
    if (wbgt < 21) {
      lcd.fillRect(0, 0, 240, 106, TFT_BLUE);  // 四角塗潰(x,y,w,h,色)
      lcd.setTextColor(TFT_WHITE, TFT_BLUE);   // (字,地)
      lcd.println("ほぼ安全");                 // 表示
    } else if (wbgt < 25) {
      lcd.fillRect(0, 0, 240, 106, TFT_CYAN);  // 四角塗潰(x,y,w,h,色)
      lcd.setTextColor(TFT_BLACK, TFT_CYAN);   // (字,地)
      lcd.println("注意");                     // 表示
    } else if (wbgt < 28) {
      lcd.fillRect(0, 0, 240, 106, TFT_YELLOW);  // 四角塗潰(x,y,w,h,色)
      lcd.setTextColor(TFT_BLACK, TFT_YELLOW);   // (字,地)
      lcd.println("警戒");                       // 表示
    } else if (wbgt < 31) {
      lcd.fillRect(0, 0, 240, 106, TFT_ORANGE);  // 四角塗潰(x,y,w,h,色)
      lcd.setTextColor(TFT_BLACK, TFT_ORANGE);   // (字,地)
      lcd.println("厳重警戒");                   // 表示
    } else if (wbgt < 33) {
      lcd.fillRect(0, 0, 240, 106, TFT_RED);  // 四角塗潰(x,y,w,h,色)
      lcd.setTextColor(TFT_WHITE, TFT_RED);   // (字,地)
      lcd.println("危険");                    // 表示
    } else if (wbgt < 35) {
      lcd.fillRect(0, 0, 240, 106, TFT_VIOLET);  // 四角塗潰(x,y,w,h,色)
      lcd.setTextColor(TFT_WHITE, TFT_VIOLET);   // (字,地)
      lcd.println("警戒アラートレベル");               // 表示
    } else {
      lcd.fillRect(0, 0, 240, 106, TFT_BLACK);  // 四角塗潰(x,y,w,h,色)
      lcd.setTextColor(TFT_WHITE, TFT_BLACK);   // (字,地)
      lcd.println("特別警戒アラートレベル");          // 表示
    }
    lcd.setCursor(0, 2);                 // 表示位置(x,y) 1行目
    lcd.printf("(暑さ指数%d)\n", wbgt);  // 表示 wbgt

    // 不快指数
    thi = int8_t(0.081 * tmp + 0.01 * hum * (0.099 * tmp - 14.3) + 46.3 + 0.5);
    lcd.setCursor(0, 55);                // 表示位置(x,y) 3行目
    lcd.printf("(不快指数%2d)\n", thi);  // 表示 不快指数
    lcd.setCursor(2, 81);                // 表示位置(x,y) 4行目
    if (thi < 50) {                      // 不快指数の感じ方
      lcd.println("寒くてたまらない");
    } else if (thi < 55) {
      lcd.println("寒い");
    } else if (thi < 60) {
      lcd.println("肌寒い");
    } else if (thi < 65) {
      lcd.println("何も感じない");
    } else if (thi < 70) {
      lcd.println("快適");
    } else if (thi < 75) {
      lcd.println("不快感の人がいる");
    } else if (thi < 80) {
      lcd.println("半数以上が不快感");
    } else if (thi < 85) {
      lcd.println("93%が不快感");
    } else {
      lcd.println("暑くてたまらない");
    }

    batV = M5.Axp.GetBatVoltage();                    // バッテリ電圧を読取る
    batP = int8_t((batV - 3.3) / (4.0 - 3.3) * 100);  // バッテリ残量計算
    // 100%は4.0V,0%は3.3Vで間は直線としました
    if (batP > 100) batP = 100;   // 100%以上は100%
    if (batP < 0) batP = 0;       // 0%以下は0%
    lcd.setCursor(165, 2);        // 表示位置(x,y) 1行目
    lcd.printf("BT%3d%%", batP);  // 表示 バッテリ残量

    RemainingT = (startTime + period - currentTime) / 1000;  // 残り秒数
    lcd.setCursor(200, 55);                                  // 表示位置(x,y) 3行目
    lcd.printf("%3d", RemainingT);                           // 電源offまでの秒数

    lcd.drawFastHLine(0, 54, 240, TFT_BLACK);  // 水平線(x,y,w,色)不快指数の上
    lcd.drawRect(165, 1, 73, 26, TFT_BLACK);   // 四角枠 (x,y,w,h,色) バッテリー枠
    lcd.drawRect(198, 56, 40, 21, TFT_BLACK);  // 四角枠 (x,y,w,h,色) 残秒枠

    delay(5000);  // 5秒待つ
  } else {
    M5.Axp.PowerOff();  // 電源off
  }
}
* flash memory(3.1Mbyte)のうち、スケッチが22%使用。RAM(328kbyte)のうち、global変数が7%使用、local変数で304kbyte使用可能。(1000byte=1kbyteで計算)