0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ESP-WROOM-02 + BME280 + SSD1306 で 熱中症指数(WBGT)計を作ってみた

Last updated at Posted at 2020-06-09

はじめに

このところ急に暑くなってきました。
これからの時期は熱中症予防が大事になりますが、WBGT値に気を付けるようにとか言われますね。WBGTを正確に測定するには専用の器具が必要になりますが、簡易推計表があるようなので、WBGT計を作ってみました。

部品

  • SWITCH SCIENCE ESPr Developer (ESP8266搭載ボード)
  • AE-BME280 (I2C 温度・湿度・気圧系)
  • SSD1306 (I2C ミニLCD)
  • ブレッドボード
  • 赤LED 5mm
  • カーボン抵抗 330Ω

回路

BME280_SSD1306.png

LED書き忘れてしまいました(^^;
[IO2 - 330Ω - LED - GND] という感じでつないでます。なくても動くのでご容赦ください。
なお、本来はIO4とIO5の先にプルアップ抵抗を入れる必要がありますが、今回使用したESPr Developerにはプルアップ抵抗が搭載されているので、このようにつないでいます。

ブレッドボードでの配線

IMG_0493.jpg

開発環境

  • Windows 10 Pro 1909
  • Arduino IDE 1.8.12

スケッチ

ちょっと、だらけたソースになっちゃったかな。。。

/*
 * WBGT計
 */

# include "ESP8266WiFi.h"
/***********************************************
 * BME280関連の定義
 ***********************************************/
# include <Wire.h>
// I2Cアドレス
# define BME280_ADDRESS 0x76

// センサーから読みだした値
unsigned long int hum_raw, temp_raw, pres_raw;
signed long int t_fine;

// 補正後の確定値
double temp_act = 0.0, press_act = 0.0, hum_act = 0.0;

// ConfレジスタとCtrlレジスタの設定値をセットする
uint8_t osrs_t = 1;   // Temperature oversampling (= x1)
                      // 2⇒x2, 3⇒x4, 4⇒x8, 5~7⇒x16
uint8_t osrs_p = 1;   // Pressure oversampling
uint8_t osrs_h = 1;   // Humidity oversampling
uint8_t mode = 3;     // Normal=3, Sleep=0, Force=1,2
uint8_t t_sb = 5;     // 計測の間隔(Standbyにいる時間) 
                      // 0=>0.5ms, 1=62.5ms, 2=>125ms,3=>250ms,4=>500ms,5=>1000ms,6=>10ms,7=20ms
uint8_t filter = 0;   // IIR Filterの設定値(0=>off, 1=>2, 2=>4, 3=>8, 4~7=>16
uint8_t spi3w_en = 0; // SPIの3線モードをディセーブルにする(使わない)

// ビットマッピング
uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode;
uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en;
uint8_t ctrl_hum_reg = osrs_h;

// レジスタのアドレス
# define CTRL_HUM_REG  0xF2
# define CTRLREG   0xF4
# define CONFREG   0xF5

// 読み込まれるデータのマッピング
uint16_t  dig_T1;
int16_t   dig_T2;
int16_t   dig_T3;
uint16_t  dig_P1;
int16_t   dig_P2;
int16_t   dig_P3;
int16_t   dig_P4;
int16_t   dig_P5;
int16_t   dig_P6;
int16_t   dig_P7;
int16_t   dig_P8;
int16_t   dig_P9;
uint8_t   dig_H1;
int16_t   dig_H2;
uint8_t   dig_H3;
int16_t   dig_H4;
int16_t   dig_H5;
int8_t    dig_H6;


/***********************************************
 * SSD1306(Display)関連の定義
 ***********************************************/
# include "SSD1306Wire.h"
// I2Cアドレス
const int I2C_DISPLAY_ADDRESS = 0x3c;
// I2C用のPIN
const int SDA_PIN = 4;
const int SDC_PIN = 5;
// SSD1306との通信用オブジェクト
SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SDC_PIN);

/****
 * 初期化処理
 */
void setup() {
  Serial.begin(115200);

  /**
   * Display(SSD1306)の初期化
   */
  initDisplay();

  /**
   * BME280初期化
   */
  initBME280();

}

/*****
 * メインループ
 */
void loop() {

  // BME280センシング
  doBME280();

  // WBGTを算出する
  int wbgt = calcWbgt(temp_act, hum_act);

  // 測定結果を表示する
  displaydata(temp_act, hum_act, press_act, wbgt);

  // 10秒おきに実行
  delay(10000);
}

/**
 * WBGTを算出する
 */
int calcWbgt(float temp, float hum){
  const int mat[20][17] = {
    { 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44},
    { 28, 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 41, 42, 43},
    { 28, 28, 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 41, 42},
    { 27, 28, 29, 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 41},
    { 26, 27, 28, 29, 29, 30, 31, 32, 33, 34, 34, 35, 36, 37, 38, 39, 39},
    { 25, 26, 27, 28, 29, 29, 30, 31, 32, 33, 33, 34, 35, 36, 37, 38, 38},
    { 25, 25, 26, 27, 28, 29, 29, 30, 31, 32, 33, 33, 34, 35, 36, 37, 37},
    { 24, 25, 25, 26, 27, 28, 28, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36},
    { 23, 24, 25, 25, 26, 27, 28, 28, 29, 30, 31, 31, 32, 33, 34, 34, 35},
    { 22, 23, 24, 24, 25, 26, 27, 27, 28, 29, 30, 30, 31, 32, 33, 33, 34},
    { 21, 22, 23, 24, 24, 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33},
    { 21, 21, 22, 23, 24, 24, 25, 26, 26, 27, 28, 29, 29, 30, 31, 31, 32},
    { 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 27, 28, 28, 29, 30, 30, 31},
    { 19, 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30},
    { 18, 19, 20, 20, 21, 22, 22, 23, 24, 24, 25, 26, 26, 27, 28, 28, 29},
    { 18, 18, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28},
    { 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 24, 24, 25, 26, 26, 27},
    { 16, 17, 17, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26},
    { 15, 16, 17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 24, 24, 25},
    { 15, 15, 16, 16, 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24}
  };
  int ti, hi;
  // 温度は40度-20度の降順の表になっている
  ti = 40 - (int)temp;
  ti = (ti < 0) ? 0 : ti;
  ti = (ti >= 20) ? 19 : ti;
  // 湿度は20%-100%の5%刻みになっている
  hi = (((int)hum) / 5) - 4;
  hi = (hi < 0) ? 0 : hi;
  hi = (hi >= 17) ? 16 : hi;

  return mat[ti][hi];
}

/**
 * 測定結果の表示
 */
void displaydata(
  float bme_temp,
  float bme_hum,
  float bme_pres,
  int wbgt
  ){

  // Displayに表示する文字列を生成する
  char bmestr[64];
  char bmestr2[64];
  char bmestr3[64];
  char bmestr4[64];
  sprintf(bmestr, "temp: %2.2f[°C]", bme_temp);
  sprintf(bmestr2, "humd: %2.2f[%%]", bme_hum);
  sprintf(bmestr3, "pres: %2.2f[hpa]", bme_pres);
  sprintf(bmestr4, "WGBT: %d[°C]", wbgt);

  // Displayに表示する
  display.clear();
  display.setFont(ArialMT_Plain_10);
  display.drawString(10, 0, bmestr);
  display.drawString(10, 10, bmestr2);
  display.drawString(10, 20, bmestr3);
  display.setFont(ArialMT_Plain_16);
  display.drawString(10, 36, bmestr4);
  display.display();
}

/*****************************************************
 * BME280関連
 *****************************************************/
// 初期化
void initBME280(){
  // I2Cインタフェースを開始する
  Wire.begin();
  // LEDのPINをセット
  pinMode(LED_BUILTIN, OUTPUT);
  // Ctrlレジスタを書き込む
  writeI2C(CTRL_HUM_REG, ctrl_hum_reg);
  writeI2C(CTRLREG, ctrl_meas_reg);
  writeI2C(CONFREG, config_reg);

  // trimming parameterを読み込む
  readTrim();
}
// 計測
void doBME280(){
  // 補正後の値
  signed long int temp_cal;
  unsigned long int press_cal, hum_cal;
  char s[20], t[20];

  // データを読み込む
  readData();

  // 補正を適用する
  temp_cal = calibration_T(temp_raw);
  press_cal = calibration_P(pres_raw);
  hum_cal = calibration_H(hum_raw);
  temp_act = (double)temp_cal / 100.0;
  press_act = (double)press_cal / 100.0;
  hum_act = (double)hum_cal / 1024.0;

  // センサーをsleepさせる
  bme280_sleep();
  // 測定結果をデバッグ用にコンソール出力  Serial.print("BME280#TEMP : ");
  Serial.print(temp_act);
  Serial.print(" DegC  PRESS : ");
  Serial.print(press_act);
  Serial.print(" hPa  HUM : ");
  Serial.print(hum_act);
  Serial.println(" %");    
  // タイミングをとってセンサーをNormalに戻す
  delay(900);
  bme280_awake();
  delay(100);
}

// センサーをsleep状態にする
void bme280_sleep(){
  // LEDを消灯する
  digitalWrite(LED_BUILTIN,0);
  // sleepモードにする
  mode=0;
  ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode;
  // CONFIGレジスタに書き込む
  writeI2C(CONFREG,config_reg);
  // trimming parameterを読み込む
  readTrim(); 
}
void bme280_awake(){
  // LEDを点ける
  digitalWrite(LED_BUILTIN,1);  
  // normalモードにする
  mode=3;
  ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode;
  // CONFIGレジスタに書き込む
  writeI2C(CONFREG,config_reg);
  // trimming parameterを読み込む
  readTrim(); 
}

/*
 *  trimming parameterを読み込む
 *  ※ConfigRegisterをセットした後にこれを実行する
 */
void readTrim(){
  // 読み込み用のバッファ
  uint8_t data[32];
  uint8_t* pdata = &data[0];
  int i = 0;

  // データ転送(読み込み)
  pdata = readI2C(0x88, 24, pdata, 0);
  pdata = readI2C(0xA1, 1, pdata, 0);
  pdata = readI2C(0xE1, 7, pdata, 1000);

  // 取り出したデータをマッピングに従って取り出す
  dig_T1 = (data[1] << 8) | data[0];
  dig_T2 = (data[3] << 8) | data[2];
  dig_T3 = (data[5] << 8) | data[4];
  dig_P1 = (data[7] << 8) | data[6];
  dig_P2 = (data[9] << 8) | data[8];
  dig_P3 = (data[11] << 8) | data[10];
  dig_P4 = (data[13] << 8) | data[12];
  dig_P5 = (data[15] << 8) | data[14];
  dig_P6 = (data[17] << 8) | data[16];
  dig_P7 = (data[19] << 8) | data[18];
  dig_P8 = (data[21] << 8) | data[20];
  dig_P9 = (data[23] << 8) | data[22];

  dig_H1 = data[24];
  dig_H2 = (data[26] << 8) | data[25];
  dig_H3 = data[27];
  dig_H4 = (data[28] << 4) | (0x0F & data[29]);
  dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F);
  dig_H6 = data[31];
}

// センサーデータを読み込む
void readData(){
  uint8_t data[32];
  readI2C(0xF7, 8, &data[0], 0);

  pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
  temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
  hum_raw = (data[6] << 8) | data[7];

}

// I2Cライブラリによってデータを転送(書き込み)する
void writeI2C(uint8_t reg_address, uint8_t data){
  Wire.beginTransmission(BME280_ADDRESS);
  Wire.write(reg_address);
  Wire.write(data);
  Wire.endTransmission();
}

// I2Cライブラリによってデータを転送(読み込み)する
uint8_t* readI2C(uint8_t reg, int size, uint8_t* pdata, int dly){
  Wire.beginTransmission(BME280_ADDRESS);
  Wire.write(reg);
  Wire.endTransmission();
  // 必要な場合はwait
  if(dly > 0){
    delay(dly);
  }
  Wire.requestFrom(BME280_ADDRESS, size);
  while(Wire.available()){
    *pdata = Wire.read();
    ++pdata;
  }
  return pdata; 
}
/**
 * Calibration処理(この関数はBME280の仕様書に記載されている)
 */
signed long int calibration_T(signed long int adc_T)
{
    signed long int var1, var2, T;
    var1 = ((((adc_T >> 3) - ((signed long int)dig_T1<<1))) * ((signed long int)dig_T2)) >> 11;
    var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T>>4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14;

    t_fine = var1 + var2;
    T = (t_fine * 5 + 128) >> 8;
    return T; 
}
/**
 * Calibration処理(この関数はBME280の仕様書に記載されている)
 */
unsigned long int calibration_P(signed long int adc_P)
{
    signed long int var1, var2;
    unsigned long int P;
    var1 = (((signed long int)t_fine)>>1) - (signed long int)64000;
    var2 = (((var1>>2) * (var1>>2)) >> 11) * ((signed long int)dig_P6);
    var2 = var2 + ((var1*((signed long int)dig_P5))<<1);
    var2 = (var2>>2)+(((signed long int)dig_P4)<<16);
    var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((signed long int)dig_P2) * var1)>>1))>>18;
    var1 = ((((32768+var1))*((signed long int)dig_P1))>>15);
    if (var1 == 0)
    {
        return 0;
    }    
    P = (((unsigned long int)(((signed long int)1048576)-adc_P)-(var2>>12)))*3125;
    if(P<0x80000000)
    {
       P = (P << 1) / ((unsigned long int) var1);   
    }
    else
    {
        P = (P / (unsigned long int)var1) * 2;    
    }
    var1 = (((signed long int)dig_P9) * ((signed long int)(((P>>3) * (P>>3))>>13)))>>12;
    var2 = (((signed long int)(P>>2)) * ((signed long int)dig_P8))>>13;
    P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4));
    return P;
}
/**
 * Calibration処理(この関数はBME280の仕様書に記載されている)
 */
unsigned long int calibration_H(signed long int adc_H)
{
    signed long int v_x1;

    v_x1 = (t_fine - ((signed long int)76800));
    v_x1 = (((((adc_H << 14) -(((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) + 
              ((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) * 
              (((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int) 32768))) >> 10) + (( signed long int)2097152)) * 
              ((signed long int) dig_H2) + 8192) >> 14));
   v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4));
   v_x1 = (v_x1 < 0 ? 0 : v_x1);
   v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1);
   return (unsigned long int)(v_x1 >> 12);   
}
/*********
 * Display初期化
 */
void initDisplay(){
  // initialize dispaly
  display.init();
  display.clear();
  display.display();
  //display.flipScreenVertically();
  // フォントサイズを設定する
  display.setFont(ArialMT_Plain_10);
  display.setTextAlignment(TEXT_ALIGN_LEFT);  // TEXT_ALIGN_CENTER: Xpos=128/2=64
  display.setContrast(255);
  display.display();  
}

参考

日本気象学会 日常生活における熱中症予防指針

環境省 熱中症予防情報サイト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?