さてさて、今年もそろそろ暑い夏がやってきます。
夏というと熱中症が怖いもの。
今回は、「作る熱中症対策」ということで、M5StickCを使って「WBGT」の警報機を作ってみます。
家庭や職場などで熱中症対策の一助になれるかと思います。
【注意】
熱中症対策は水分やミネラルの補給・環境管理・体調管理が基本です。
今回作るWBGT警報機でWBGTや気温・湿度の数字は得られますが、本来はその数字をどう生かすかが大切です。
当たり前ですが、これさえ作れば万事OKというものではありませんのでご注意ください。
もくじ
WBGTとは
「暑さ指数」とも呼ばれ、熱中症予防の目的で特に近年日本国内でも使われ始めた数値です。
気温や湿度、輻射熱の影響を含めて算出される数値で、「28」を超えると熱中症患者が一気に増加するという統計が出ています。
上記の環境省のリンクで詳しい資料が公開されています。
熱中症について詳しく知る意味でも、一度読んでみてください。
専用の計測器
本来はWBGT専用の測定器を使って計測します。
温度と湿度で計算
目安として、温度と湿度から換算することもできます。
この場合、「室内で日射のない日常生活」における目安になります。
屋外や、屋内でも日射がある場合、労働環境での基準には当てはまりません。
(環境省資料「熱中症を防ごう!」より引用)
Arduinoなどのマイコンと外付けのセンサを使うと温度や湿度は簡単に測定することができます。
そこからWBGTを自動で計算させることができれば、熱中症対策として使えるのではないか?と思った次第です。
使う部品
・M5StickC
Arduino IDEで開発できる開発基盤で、ESP32というマイコンが載っています。
小型ながらフルカラーグラフィック液晶がついていたり、小型ながらバッテリーが載っていたり、Wi-FiやBluetoothにも対応してたりと、他にもいろいろてんこ盛りなモジュールです。
これで付属品付き¥1,980-、付属品なし¥1,650-はどうかしてるって(ほめ言葉)
・ENV HAT
温度・湿度・気圧・磁界を測定するセンサの載った、M5StickC専用のモジュールです。
こんな風にM5StickCにくっつけて使います。がっちゃんこ。
今回、ハード的にはなんとこれで完成だったりします。
サンプルプログラムのダウンロード
ENV HAT用のサンプルプログラムをダウンロードします。
スイッチサイエンスさんのENV HATの販売ページから飛べます。
ツリー表示の「M5StickC」をクリックして、
緑色の「Code」をクリックすると出てくる「Download ZIP」をクリックすると、ZIPファイルでダウンロードできます。
使うのはフォルダ内の「examples」>「Hat」>「ENV」>「ENV.ino」です。
Arduino IDEで開いて、M5StickCに書き込んでみると、
ずらずらっと測定値が表示されます。
2行目が温度と湿度、
3行目が方位角※1、
4行目が大気圧※2 が表示されています。
※1:北を0度として、反時計回りに何度回転した方向かを示す角度。ENV HATの方向が基準のようです。
※2:ヘクトパスカル単位の大気圧が100倍されて表示されます。また、標高による気圧のずれが考慮されていない(海面更生されていない)ので、天気予報の数値や気象庁の観測値とはたいていの場合ずれます。
そうです。すでに気温も気圧もそろっているので、あとはWBGT値を計算して表示すれば目的達成となるわけです。
使わない部分をそぎ落とす
先ほどの「ENV.ino」では、
- 気温を測定・表示する
- 湿度を測定・表示する
- 方位角を測定・表示する
- 大気圧を測定・表示する
という作業を行っていますが、今回は方位角、大気圧については不要です。
まず、不要な部分をそぎ落としてしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #include <M5StickC.h> #include "DHT12.h" #include <Wire.h> #include "Adafruit_Sensor.h" #include <Adafruit_BMP280.h> DHT12 dht12; Adafruit_BMP280 bme; void setup() { M5.begin(); Wire.begin(0,26); M5.Lcd.setRotation(3); M5.Lcd.fillScreen(BLACK); if (!bme.begin(0x76)){ Serial.println("Could not find a valid BMP280 sensor, check wiring!"); while (1); } Serial.print("\n\rCalibrate done.."); } void loop() { float tmp = dht12.readTemperature(); float hum = dht12.readHumidity(); M5.Lcd.setCursor(0, 5, 2); M5.Lcd.printf("Temp: %2.1fC Humi: %2.0f%%", tmp, hum); } |
185行ありましたが、30行になっちゃいました。
ついでに温度と湿度の表示位置を調整し、温度の数字の後に「C」を表示させました。
実行するとこんな感じ。
WBGTを計算する
といっても、「温度と湿度から」算出する数式はありません。
無理やり数式に落とし込むこともできるっちゃできますが、もっとシンプルな方法を考えます。
①ExcelでWBGT換算表を作る
先ほどの環境省のPDF資料をもとにExcelでデータを作ります。
この時、元のデータは縦軸の温度が表の「上に行くほど高く」なっていますが、ここでは逆に表の「下に行くほど高く」なるように作ります。
この後の処理を簡単にするためです。
②Excelからテキストファイルで書き出す
名前を付けて保存から、「テキスト(タブ区切り)(*.txt)」を選択して保存します。
テキストエディタで開くと、各セルの数字タブで区切られてずらっと並ぶので、テキストエディタの置換機能で「タブ(\t)」を「,」に置換すれば行列データの素が作れます。
あとは波カッコをうまくつけるだけ。
これ、Excelの機能で連番を含んだデータなどを手軽に作れる方法です。
③行列データを用意する
「WBGT」なる二次元配列を用意して、WBGTの数値を入れて初期化しておきます。
スケッチで書き換えられては困るので、「const」を使っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //WBGT計算テーブル const int WBGT[20][17] = {{15,15,16,16,17,17,18,19,19,20,20,21,21,22,23,23,24}, {15,16,17,17,18,18,19,19,20,21,21,22,22,23,24,24,25}, {16,17,17,18,19,19,20,20,21,22,22,23,23,24,25,25,26}, {17,18,18,19,19,20,21,21,22,22,23,24,24,25,26,26,27}, {18,18,19,20,20,21,22,22,23,23,24,25,25,26,27,27,28}, {18,19,20,20,21,22,22,23,24,24,25,26,26,27,28,28,29}, {19,20,21,21,22,23,23,24,25,25,26,27,27,28,29,29,30}, {20,21,21,22,23,23,24,25,25,26,27,28,28,29,30,30,31}, {21,21,22,23,24,24,25,26,26,27,28,29,29,30,31,31,32}, {21,22,23,24,24,25,26,27,27,28,29,29,30,31,32,32,33}, {22,23,24,24,25,26,27,27,28,29,30,30,31,32,33,33,34}, {23,24,25,25,26,27,28,28,29,30,31,31,32,33,34,34,35}, {24,25,25,26,27,28,28,29,30,31,32,32,33,34,35,35,36}, {25,25,26,27,28,29,29,30,31,32,33,33,34,35,36,37,37}, {25,26,27,28,29,29,30,31,32,33,33,34,35,36,37,38,38}, {26,27,28,29,29,30,31,32,33,34,34,35,36,37,38,39,39}, {27,28,29,29,30,31,32,33,34,35,35,36,37,38,39,40,41}, {28,28,29,30,31,32,33,34,35,35,36,37,38,39,40,41,42}, {28,29,30,31,32,33,34,35,35,36,37,38,39,40,41,42,43}, {29,30,31,32,33,34,35,35,36,37,38,39,40,41,42,43,44}}; |
④行列データを使ってWBGTを求める
WBGTのデータを2次元の行列にしました。1つ目のインデックスが温度、2つ目のインデックスが湿度に対応しているので、
1 | WBGT[温度][湿度] |
とするとWBGTが求められることになります。
もっとも、どちらも「0始まり1ステップ」のデータではないので、温度・湿度からそれぞれのインデックスを求める必要があります。
温度のインデックス
表の中で温度のデータは「21℃」からスタートしているので、単純に21を引けばいいことになります。
(21℃の時、インデックス=0になってほしいのでー21。)
ただ、温度は0.1℃単位で測定していますがデータは1℃刻みなので、小数点以下切り上げしておきます。
というのは
①WBGTは熱中症予防という観点から、高めか低めかでいうと高めに出てくれた方が良い
②温度が高くなれば、WBGTも上昇する傾向にある
からです。
なので、インデックスは、
1 2 3 4 5 6 | //温度データ取得 float tmp = dht12.readTemperature(); //温度データを小数点以下切り上げ int tmp_i = (tmp-0.1) + 1; //温度インデックス int index_temp = tmp_i - 21; |
で求めます。
ESP、もといArduinoには小数点以下の四捨五入、切り上げ、切り捨てに関する関数がないということが分かりました。
実装する方法を考えていましたがint型に変換するとその時点で小数点以下が切り捨てられる形になるので、「事前に0.1引いてから1を足して、int型に変換」すれば小数点以下を切り上げたことになります。(計算してみてください。要するに「0.1引いて1足して小数点以下切り捨て」です。)
「じゃあ0.9足せばいいじゃん」って?…3か月後に0.9ってなんだっけ?ってなりそうで。
(そのためのコメントなんですけどね)
湿度のインデックス
表の中で湿度のデータは「20%」からスタートして「100%」で終わり、途中は5%ステップです。
温度と同様に、「1の位を5%単位で端数切り上げ」して、表の最小目盛りより小さい数値は高めに出す方針で考えます。
(66%なら70%、76%なら80%、87%なら90%など)
こちらはArduinoでint型のあまりの出る割り算は「〇あまり△」の「〇」で出てくることを利用します。
(45÷10=4、25÷2=12など)
int型に置き換えて、5で割って、3を引いてあげればインデックスが求められそうです。
1 2 3 4 5 6 | //湿度データ取得 float hum = dht12.readHumidity(); //湿度データを整数型に int hum_i = hum; //湿度インデックス int index_hum = hum / 5 - 3; |
WBGTを求める
1 | WBGT[index_tmp][index_hum] |
⑤見やすく表示する
せっかく小さいながらもフルカラーの液晶が付いていて、自由に表示できるので見やすくします。
フォントを大きくし、液晶の背景色をWBGTに合わせて変えます。
(~20:水色、21~24:緑、25~27:黄、28~30:オレンジ、31~:赤)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | #include <M5StickC.h> #include "DHT12.h" #include <Wire.h> #include "Adafruit_Sensor.h" #include <Adafruit_BMP280.h> DHT12 dht12; Adafruit_BMP280 bme; //WBGT計算テーブル const int WBGT[20][17] = {{15,15,16,16,17,17,18,19,19,20,20,21,21,22,23,23,24}, {15,16,17,17,18,18,19,19,20,21,21,22,22,23,24,24,25}, {16,17,17,18,19,19,20,20,21,22,22,23,23,24,25,25,26}, {17,18,18,19,19,20,21,21,22,22,23,24,24,25,26,26,27}, {18,18,19,20,20,21,22,22,23,23,24,25,25,26,27,27,28}, {18,19,20,20,21,22,22,23,24,24,25,26,26,27,28,28,29}, {19,20,21,21,22,23,23,24,25,25,26,27,27,28,29,29,30}, {20,21,21,22,23,23,24,25,25,26,27,28,28,29,30,30,31}, {21,21,22,23,24,24,25,26,26,27,28,29,29,30,31,31,32}, {21,22,23,24,24,25,26,27,27,28,29,29,30,31,32,32,33}, {22,23,24,24,25,26,27,27,28,29,30,30,31,32,33,33,34}, {23,24,25,25,26,27,28,28,29,30,31,31,32,33,34,34,35}, {24,25,25,26,27,28,28,29,30,31,32,32,33,34,35,35,36}, {25,25,26,27,28,29,29,30,31,32,33,33,34,35,36,37,37}, {25,26,27,28,29,29,30,31,32,33,33,34,35,36,37,38,38}, {26,27,28,29,29,30,31,32,33,34,34,35,36,37,38,39,39}, {27,28,29,29,30,31,32,33,34,35,35,36,37,38,39,40,41}, {28,28,29,30,31,32,33,34,35,35,36,37,38,39,40,41,42}, {28,29,30,31,32,33,34,35,35,36,37,38,39,40,41,42,43}, {29,30,31,32,33,34,35,35,36,37,38,39,40,41,42,43,44}}; void setup() { M5.begin(); Wire.begin(0,26); M5.Lcd.setRotation(3); M5.Lcd.setTextColor(BLACK); if (!bme.begin(0x76)){ Serial.println("Could not find a valid BMP280 sensor, check wiring!"); while (1); } Serial.print("\n\rCalibrate done.."); } void loop() { //温度データ取得 float tmp = dht12.readTemperature(); //温度データを小数点以下切り上げ int tmp_i = (tmp-0.1) + 1; //温度インデックス int index_tmp = tmp_i - 21; //湿度データ取得 float hum = dht12.readHumidity(); //湿度データを整数型に int hum_i = hum; //湿度インデックス int index_hum = hum / 5 - 3; //WBGT表示値 int WBGT_V = WBGT[index_tmp][index_hum]; //画面描画 if(WBGT_V<=20){ M5.Lcd.fillScreen(CYAN); }else if(WBGT_V>20 && WBGT_V<=24){ M5.Lcd.fillScreen(GREEN); }else if(WBGT_V>24 && WBGT_V<=27){ M5.Lcd.fillScreen(YELLOW); }else if(WBGT_V>27 && WBGT_V<=30){ M5.Lcd.fillScreen(ORANGE); }else{ M5.Lcd.fillScreen(RED); } M5.Lcd.setCursor(5, 60 ,2); M5.Lcd.printf("WBGT-------------------"); M5.Lcd.setCursor(95, 20 ,6); M5.Lcd.printf("%d",WBGT_V); M5.Lcd.setCursor(5, 10 ,2); M5.Lcd.printf("Temp: %2.1fC", tmp); M5.Lcd.setCursor(5, 35 ,2); M5.Lcd.printf("Humid: %2.0f%%", hum); delay(5000); } |
そんなに暑いとは感じませんがWBGTは31あるそうです。室内で熱中症になるとか、多分こんな感じなんでしょうか。これくらいなら大丈夫、だいじょうぶ、だいj…みたいな。怖。
分かりやすさはバツグンですが、WBGT値が色の境目あたりをうろうろしたときに結構うっとおしいので、もっと考えた方が良いかもしれません。
(例えば、WBGTが30~31あたりでうろうろすると、背景が赤になったりオレンジになったりします。そんなに細かくリアルタイムに変化を見たいわけではないので、loop文にdelay(5000)を入れて画面の更新自体を5秒ごとに抑えてます。)
他にもフルカラーLEDを接続してより見やすく表示したり、何かアラームを鳴らしたりと、考えられる応用はたくさんあります。
【追記】その後:機能追加しました。
コメント