Arduino + BME280 で温度、湿度、気圧(圧力)計を作ってみました。arduinoソース(スケッチ)の内容を中心に備忘録としてまとめておきます。いつものようにライブラリは使わずにやってみたいと思います。
概要
センサーはBME280(温度、湿度、気圧センサー)モジュールを使用。ArduinoからI2C通信でBME280から情報を取得、各計測値を物理量(°、%、hPa)へ計算してパソコンへ送信します。パソコン側では受け取った情報をもとにビジュアル化してます。
▼計測中▼
24時間ほどモニターしてみたところです。計測値をリアルタイムで表示するとともに変化をグラフ化してます。最終的にはLCDとか使用してスタンドアローンで稼働させたいと思っているのですが、今回はとりあえずの稼働テストということで・・。
以下からBME280の使い方中心に解説です。
回路(結線)図
ArduinoとBME280をI2C通信で接続します。5v⇔3.3v のレベル変換を一応入れてます。
絵はProMiniですがUNOでも同じです。モジュール品使ってるので繋ぐだけ。I2C通信の場合はCSBピンをVccへ接続。SPI通信の場合はプルダウンしておきます。SDO ⇒ GNDでデバイスアドレス「0x76」、Vddで「0x77」となります。
▼Arduino、I2C通信の方法はこちら▼
ソース(スケッチ)の内容
抜粋して、要点の説明です。
BME280の初期設定
まずはBME280の初期設定部分です。初期設定は一度実行するのみなのでsetup()内に記述します。
uint8_t osrs_t = 1; //Temperature oversampling x 1 uint8_t osrs_p = 1; //Pressure oversampling x 1 uint8_t osrs_h = 1; //Humidity oversampling x 1 uint8_t mode = 3; //Normal mode uint8_t t_sb = 0; //Tstandby 0.5ms uint8_t filter = 4; //IIR Filter x1 uint8_t spi3w_en = 0; //3-wire SPI Disable uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode; //温度、圧力、オーバーサンプリング uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en; //スタンバイ時間とIIRフィルター設定 uint8_t ctrl_hum_reg = osrs_h; //湿度オーバーサンプリング
設定する項目は、測定するオーバーサンプリング数やスタンバイ時間、フィルター、通信方法などがあります。割と項目が多いのでレジスタ毎に変数で纏めて設定してます。
レジスタ順にみていきます。
ctrl_measレジスタ
ここでは温度と気圧(圧力)のオーバーサンプリング数と測定モードの設定をします。
▼ctrl_measレジスタ▼
bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
ctrl_meas | osrs_t2 | osrs_t1 | osrs_t0 | osrs_p2 | osrs_p1 | osrs_p0 | mode1 | mode0 |
レジスタアドレスは「0xF4」です。上位から温度のオーバーサンプリング(3bit)、気圧のオーバーサンプリング(3bit)、と測定モード(2bit)となります。
▼温度、気圧のオーバーサンプリングの選択▼
osrs_t[2-0]、osrs_p[2-0] | オーバーサンプリング |
000 | スキップ |
001 | x1 |
010 | x2 |
011 | x4 |
100 | x8 |
101 | x16 |
温度と気圧のオーバーサンプリングの設定です。「000」は測定をスキップします。
▼測定モードの選択▼
mode[1-0] | センサーのモード |
00 | スリープモード |
10、01 | 強制モード |
11 | 通常モード |
となります。
で最初の例では、それぞれの項目を変数で設定して最後に結合してます。ほかの方法で記述するならば、例えば温度、圧力のオーバーサンプリングを「x1」、測定は通常モードで行う場合は、
uint8_t ctrl_meas_reg = B00100111; //or uint8_t ctrl_meas_reg = 0x39;
などと直接記述しても問題ありません。わかり難くなりますがすっきりはします。
configレジスタ
次にconfigレジスタです。ここではスタンバイ時間やフィルターの設定を行います。
▼configレジスタ▼
bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
config | t_sb[2] | t_sb[1] | t_sb[0] | filter[2] | filter[1] | filter[0] | 0 | spi_3w_en |
レジスタアドレスは「0xF5」。上位からスタンバイ時間(測定と測定の間の待機時間)(3bit)、IIRフィルタ(3bit)、最後はSPI接続時の設定(1bit)となります。
▼スタンバイ時間の選択▼
t_sb[2-0] | 時間[ms] |
000 | 0.5 |
001 | 62.5 |
010 | 125 |
011 | 250 |
100 | 500 |
101 | 1000 |
110 | 10 |
111 | 20 |
▼IIRフィルタ時定数の選択▼
filter[2-0] | フィルターの係数 |
000 | off |
001 | 2 |
010 | 4 |
011 | 8 |
100 | 16 |
spi3w_enはSPIインターフェースの選択で「0」が4線式、「1」で3線式です。
で最初のソースコード例のように個々で設定してから、纏めて変数「config_reg」に格納しておきます。
ctrl_humレジスタ
ここでは湿度のオーバーサンプリング数を設定するだけです。
▼ctrl_humレジスタ▼
bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
ctrl_hum | 0 | 0 | 0 | 0 | 0 | osrs_hum[2] | osrs_hum[1] | osrs_hum[0] |
レジスタアドレスは「0xF2」。下位3bitで湿度のオーバーサンプリング数を設定します。
▼湿度のオーバーサンプリング設定▼
osrs_h[2-0] | オーバーサンプリング |
000 | スキップ |
001 | x1 |
010 | x2 |
011 | x4 |
100 | x8 |
101 | x16 |
ctrl_humレジスタでの設定項目はこれだけです。
直接送信してもいいくらいですが、視認性よくするために設定値(値)を一度変数へ格納してます。
I2C通信で設定値を送信
最後に設定した値を各レジスタに送信して初期設定は完了です。
Wire.beginTransmission(0x76); Wire.write(0xF2); Wire.write(ctrl_hum_reg); Wire.endTransmission(); Wire.beginTransmission(0x76); Wire.write(0xF4); Wire.write(ctrl_meas_reg); Wire.endTransmission(); Wire.beginTransmission(0x76); Wire.write(0xF5); Wire.write(config_reg); Wire.endTransmission();
I2C通信でそれぞれの各レジスタアドレスへ設定した値を送信して初期設定は完了です。
調整パラメーターの読み出し
初期にもう一つだけ・・、調整パラメーターを読み取っておく必要があります。
パラメーターは計測した温度・湿度・気圧の生値をそれぞれ、「℃」、「%」、「hPa」に計算するのに必要な係数のようなものです。センサー固有のようなのでそれぞれで取得する必要があります。
パラメーターはアドレス「0x88~0xA1」と[0xE1~0xE7]に格納されていて、基本は16bitの符号付または符号無しの整数値となります。パラメーターが何故にこんなたくさんあるのかは全く持って不明。
▼パラメーター読み出し▼
int8_t dig_H6; uint8_t dig_H1, dig_H3; int16_t dig_T2, dig_T3, dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9; int16_t dig_H2, dig_H4, dig_H5; uint16_t dig_T1, dig_P1; Wire.beginTransmission(0x76); Wire.write(0x88); Wire.endTransmission(); Wire.requestFrom(0x76, 24); byte inBuf[32], i = 0; while (Wire.available()) { inBuf[i] = Wire.read(); i++; } Wire.beginTransmission(0x76); Wire.write(0xA1); Wire.endTransmission(); Wire.requestFrom(0x76, 1); while (Wire.available() < 1); inBuf[i] = Wire.read(); i++; Wire.beginTransmission(0x76); Wire.write(0xE1); Wire.endTransmission(); Wire.requestFrom(0x76, 7); while (Wire.available()) { inBuf[i] = Wire.read(); i++; } dig_T1 = (inBuf[1] << 8) | inBuf[0]; dig_T2 = (inBuf[3] << 8) | inBuf[2]; dig_T3 = (inBuf[5] << 8) | inBuf[4]; dig_P1 = (inBuf[7] << 8) | inBuf[6]; dig_P2 = (inBuf[9] << 8) | inBuf[8]; dig_P3 = (inBuf[11] << 8) | inBuf[10]; dig_P4 = (inBuf[13] << 8) | inBuf[12]; dig_P5 = (inBuf[15] << 8) | inBuf[14]; dig_P6 = (inBuf[17] << 8) | inBuf[16]; dig_P7 = (inBuf[19] << 8) | inBuf[18]; dig_P8 = (inBuf[21] << 8) | inBuf[20]; dig_P9 = (inBuf[23] << 8) | inBuf[22]; dig_H1 = inBuf[24]; dig_H2 = (inBuf[26] << 8) | inBuf[25]; dig_H3 = inBuf[27]; dig_H4 = (inBuf[28] << 4) | (0x0F & inBuf[29]); dig_H5 = (inBuf[30] << 4) | ((inBuf[29] >> 4) & 0x0F); dig_H6 = inBuf[31];
これでパラメーターを読み出して置きます。
温度・湿度・気圧データの読取り
初期設定が完了したので、温度、気圧、湿度の生データを読み込みます。
データは「0xF7」アドレスから気圧・温度・湿度の順に8byte分に格納されて、以下のような出力データとなります。
int i = 0; uint32_t data[8]; Wire.beginTransmission(0x76); Wire.write(0xF7); Wire.endTransmission(); Wire.requestFrom(0x76, 8); while (Wire.available()) { data[i] = Wire.read(); i++; } presRaw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); tempRaw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4); humRaw = (data[6] << 8) | data[7];
でそれぞれ生値を読み出したら温度(℃)・気圧(hPa)・湿度(%)へと変換します。
温度計算
温度計算は以下。
inline int32_t computeTemp32(int32_t adc_T) { int32_t var1, var2, T; var1 = ((((adc_T >> 3) - ((int32_t)dig_T1 << 1))) * ((int32_t)dig_T2)) >> 11; var2 = (((((adc_T >> 4) - ((int32_t)dig_T1)) * ((adc_T >> 4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14; t_fine = var1 + var2; T = (t_fine * 5 + 128) >> 8; return T; }
32bit固定少数点の補償式です。計算値に0.01を掛けると℃になります。℃をそのまま倍精度で計算する補償式もありますがここでは割愛。
気圧計算
気圧計算は以下。
inline uint32_t computePres32(int32_t adc_P) { int32_t var1, var2; uint32_t P; var1 = (((int32_t)t_fine) >> 1) - (int32_t)64000; var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((int32_t)dig_P6); var2 = var2 + ((var1 * ((int32_t)dig_P5)) << 1); var2 = (var2 >> 2) + (((int32_t)dig_P4) << 16); var1 = (((dig_P3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((int32_t)dig_P2) * var1) >> 1)) >> 18; var1 = ((((32768 + var1)) * ((int32_t)dig_P1)) >> 15); if (var1 == 0) { return 0; } P = (((uint32_t)(((int32_t)1048576) - adc_P) - (var2 >> 12))) * 3125; if (P < 0x80000000) { P = (P << 1) / ((uint32_t) var1); } else { P = (P / (uint32_t)var1) * 2; } var1 = (((int32_t)dig_P9) * ((int32_t)(((P >> 3) * (P >> 3)) >> 13))) >> 12; var2 = (((int32_t)(P >> 2)) * ((int32_t)dig_P8)) >> 13; P = (uint32_t)((int32_t)P + ((var1 + var2 + dig_P7) >> 4)); return P; }
符号無しの32bit整数として出力します。出力単位はそのまま「Pa」です。気圧も倍精度で出力する補償式がありますがここでは割愛。
湿度計算
湿度計算は以下。
uint32_t computeHum32(int32_t adc_H) { int32_t v_x1; v_x1 = (t_fine - ((int32_t)76800)); v_x1 = (((((adc_H << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1)) + ((int32_t)16384)) >> 15) * (((((((v_x1 * ((int32_t)dig_H6)) >> 10) * (((v_x1 * ((int32_t)dig_H3)) >> 11) + ((int32_t) 32768))) >> 10) + (( int32_t)2097152)) * ((int32_t) dig_H2) + 8192) >> 14)); v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4)); v_x1 = (v_x1 < 0 ? 0 : v_x1); v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1); return (uint32_t)(v_x1 >> 12); }
湿度計算の補償式です。出力値を1024で除すると%になります。
後半説明をだいぶ端折ってしまいましたが、これで温度・気圧・湿度の取得ができます。
最後まで読んでいただき有難う御座います。以下はおまけ。。BME280+6軸線センサーの組み合わせで高度計を作ったものです。
▼高度計▼
コメント