Arduinoで遊ぶページ
Arduinoで遊んだ結果を残すページです
温度・湿度・気圧センサモジュール
DUEESP32

概要

BME280というBoschの温度・湿度・気圧センサを使った実験です。温度センサと湿度センサ、気圧センサの3つが入っています。

実際には、秋月電子通商で販売している、モジュールを使用しました。

データシート

BME280のデータシートを参照して、どのようなものかを調べました。

概要

BME280の仕様の概要は以下の通りです。I2CとSPIの両方をサポートしていますが、I2Cで利用しました。このため、J3をはんだでジャンパしました。

項目 内容
電源電圧(Vdd) 1.71V~3.6V
測定範囲(温度) -40℃~+85℃
精度(温度) ±0.5℃(25℃)
測定範囲(湿度) 0%~100%
精度(湿度) ±3%
測定範囲(気圧) 300hPa~1100hPa
精度(気圧) ±1hPa
I2Cアドレス 0x76 もしくは 0x77(5番ピンで選択)

私が利用したBME280モジュールは、6ピンのデバイスで各ピンの意味は以下の通りです。BME280を利用したモジュールはいろいろ販売されているようなので、必ず、自分の利用しているモジュールのピンの確認をしてください。

ピン番号 内容
1 VDD
2 GND
3 未使用
4 SDA
5 I2Cアドレス選択(GNDに接続すると0x76、VDDに接続すると0x77)
6 SCL

秋月電子通商で販売しているモジュールは、プルアップ抵抗が内蔵されています。必要に応じてはんだでジャンパします。

インタフェース

レジスタ

BME280の主なレジスタは以下の通りです。

アドレス 名前 説明
0xd0 id チップID
0xe1-0xf0 calib26..calib41 キャリブレーションデータ
0xf2 ctrl_hum 湿度センサの設定
0xf4 ctrl_meas 温度センサ・湿度センサの設定、動作モードの設定
0xf5 config 測定間隔、フィルタ等の設定
0xf7-0xf3 測定データ

実験

BME280とArduinoとの接続

BME280は、最大3.6Vまでしかかけられないので、ESP-WROOM32を利用しました。Arduino Unoを利用する際は、注意してください。。なお、Arduino 1.6.5とArduino Dueとの組み合わせは試しましたが、Arduino 1.8.10とArduino Dueとの組み合わせは、確認していないので、利用には注意してください(ここに限らずですが、自己責任でお願いします)。

BME280とESP-WROOM-32とを以下のように接続します。5番ピンはGNDに接続したので、I2Cアドレスは、0x76を利用しています。

BME280のピン番号 BME280のピン ESP-WROOM-32のピン番号 Arduino Due のピン番号
1 VDD 3.3V 3.3V
2 GND GND GND
3 未使用
4 SDA SDA(GPIO21) SDA(20番ピン)
5 I2Cアドレス選択 GND GND
6 SCL SCO(GPIO22) SCL(21番ピン)

Arduinoプログラミング

Wireライブラリを利用して、BME280を制御するクラスを作成しました。以下に、今回作成したパブリックメソッドを示します。

初期化(begin())

BME280を初期化します。実際には、Wire.begin()を呼び出すだけのメソッドです。

モード設定(setMode())

温度・湿度・気圧を測定する際のオーバーサンプリングの設定、IIRフィルタの設定を行なったうえで、BME280の動作モードを設定します。

動作モードは、以下の3つがあります。

  1. スリープ(sleep)

    電源投入後の初期状態です。測定を行いません。

  2. ノーマル(normarl)

    周期的に測定を行います。測定終期の設定も行うことができます。

  3. フォースト(forced)

    1回だけ測定を行います。測定後は、スリープモードに遷移します。

サンプルのプログラムでは、ノーマルモードに遷移して、1秒ごとに測定を行なうようにしています。

この関数では、以下のパラメータを設定することができます。

mode
動作モード
t_sb
ノーマルモード時の測定間隔
osrs_h, osrs_t, osrs_p
オーバーサンプリングの設定
filter
IIRフィルタの設定

データ取得(getData())

温度・湿度・気圧データを取得します。

読み込んだレジスタの値をもとに、補正を行い、温度・湿度・気圧を計算します。計算のためのプログラムは、データシートに記載されていた関数をそのまま利用しました。

状態取得(getStatus())

BME280の動作状態を取得します。測定状態とデータ更新中状態の2つの状態をまとめて取得します。

測定中確認(isMeasuring())

状態取得で取得できる状態のうち、測定状態だけを取得します。

更新中確認(isUpdating())

状態取得で取得できる状態のうち、データ更新中状態だけを取得します。

チップID読み出し(readId())

BME280のチップIDを読み出します。0x60が返ってきます。

スケッチ

温度・湿度・気圧を取得するスケッチです。最初にチップID(0x60)を取得して表示しています。その後、1秒ごとに、温度・湿度・気圧を測定して、シリアルコンソールに表示しています。

誤ってArduino Unoに接続しないよう、ESP-WROOM-32とArduino Due以外(正確には、ESP32か__SAM3X8E__ が定義されているときだけ)ではコンパイルがエラーになるようにしています。ただし、ESP-WROOM-32だけで動作確認をしています。

気圧の補正をするときには、uint64_tを利用しています(データシート内のプログラムをそのまま利用しているだけですが)。

getdata.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
27
#include "BME280.h"

BME280 bme280;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  bme280.begin();
  bme280.setMode(BME280_MODE_NORMAL, BME280_TSB_1000MS, BME280_OSRS_x1, BME280_OSRS_x1, BME280_OSRS_x1, BME280_FILTER_OFF);

  Serial.print("Chip ID = 0x");
  Serial.println(bme280.readId(), HEX);
}

void loop() {
  // put your main code here, to run repeatedly:
  double temperature, humidity, pressure;
  uint8_t measuring, im_update;
  char s[64];
  bme280.getData(&temperature, &humidity, &pressure);

  sprintf(s, "Temperature: %.1lf C, Humidity: %.1lf %%, Pressure: %.1lf hPa\n",
          temperature, humidity, pressure);
  Serial.print(s);

  delay(1000);
}

BME280.h

 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
84
85
86
#ifndef BME280_H
#define BME280_H

#if !defined (__SAM3X8E__) && !(ESP32)
#errof NOT DUE or ESP32
#else
#include <stdint.h>

#define BME280_ADDRESS 0x76

#define BME280_ID        0xd0
#define BME280_CTRL_HUM  0xf2
#define BME280_STATUS    0xf3
#define BME280_CTRL_MEAS 0xf4
#define BME280_CONFIG    0xf5
#define BME280_CALIB00   0x88
#define BME280_CALIB25   0xa1
#define BME280_CALIB26   0xe1
#define BME280_PRESS_MSB 0xf7

// Oversampling rate
#define BME280_OSRS_NONE 0x00
#define BME280_OSRS_x1   0x01
#define BME280_OSRS_x2   0x02
#define BME280_OSRS_x4   0x03
#define BME280_OSRS_x8   0x04
#define BME280_OSRS_x16  0x05

#define BME280_MODE_SLEEP   0x00
#define BME280_MODE_FORCED  0x01
#define BME280_MODE_NORMAL 0x03

#define BME280_TSB_0P5MS  0x00
#define BME280_TSB_62P5MS 0x01
#define BME280_TSB_125MS  0x02
#define BME280_TSB_250MS  0x03
#define BME280_TSB_500MS  0x04
#define BME280_TSB_1000MS 0x05
#define BME280_TSB_10MS   0x06
#define BME280_TSB_20MS   0x07

#define BME280_FILTER_OFF      0x00
#define BME280_FILTER_COEF_2   0x01
#define BME280_FILTER_COEF_4   0x02
#define BME280_FILTER_COEF_8   0x03
#define BME280_FILTER_COEF_16  0x04

class BME280 {
  public:
    BME280();
    void begin();
    void setMode(
      uint8_t mode, // sensor mode
      uint8_t t_sb, // inactive duration
      uint8_t osrs_h, // oversampling of humidity data
      uint8_t osrs_t, // oversampling of temperature data
      uint8_t osrs_p, // oversampling of pressure data
      uint8_t filter // time constant of the IIR filter
    );
    void getData(double *temperature, double *humidity, double *pressure);
    void getStatus(uint8_t *measuring, uint8_t *im_update);
    uint8_t isMeasuring();
    uint8_t isUpdating();
    uint8_t readId();
  private:
    /* Trimming parameters dig_ */
    uint16_t dig_T1;
    int16_t dig_T2, dig_T3;
    uint16_t dig_P1;
    int16_t dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;
    uint8_t dig_H1, dig_H3;
    int16_t dig_H2, dig_H4, dig_H5;
    int8_t dig_H6;
    int32_t t_fine;

    void readTrimmingParameter();
    uint8_t writeRegister(uint8_t address, uint8_t data);
    uint8_t readRegister(uint8_t address, uint8_t data[], uint8_t numberOfData);

    int32_t compensate_T(int32_t adc_T);
    uint32_t compensate_P(int32_t adc_P);
    uint32_t compensate_H(int32_t adc_H);
};

#endif
#endif /* BME280_H */

BME280.cpp

  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
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include "BME280.h"
#include "Arduino.h"
#include "Wire.h"

BME280::BME280() {

}

void BME280::begin() {
  Wire.begin();
}

void BME280::setMode(uint8_t mode, uint8_t t_sb, uint8_t osrs_h, uint8_t osrs_t, uint8_t osrs_p, uint8_t filter) {
  uint8_t config, ctrl_meas, ctrl_hum;

  config = (t_sb << 5) | (osrs_p << 2); // spi3w_en = 0;
  ctrl_meas = (osrs_t << 5) | (osrs_p << 2) | mode;
  ctrl_hum = osrs_h;

  writeRegister(BME280_CTRL_HUM, ctrl_hum);
  writeRegister(BME280_CTRL_MEAS, ctrl_meas);
  writeRegister(BME280_CONFIG, config);

  readTrimmingParameter();
}

void BME280::getData(double *temperature, double *humidity, double *pressure) {
  uint8_t data[8];
  uint32_t adc_T, adc_H, adc_P;

  readRegister(BME280_PRESS_MSB, data, 8);

  adc_P = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
  adc_T = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
  adc_H = (data[6] << 8) | data[7];

  *temperature = compensate_T(adc_T) / 100.0;
  *pressure = compensate_P(adc_P) / 256.0 / 100.0;
  *humidity = compensate_H(adc_H) / 1024.0;
}

void BME280::getStatus(uint8_t *measuring, uint8_t *im_update) {
  uint8_t status;

  readRegister(BME280_STATUS, &status, 1);
  *measuring = (status & 0x08) ? 1 : 0;
  *im_update = (status & 0x01) ? 1 : 0;
}

uint8_t BME280::isMeasuring() {
  uint8_t status;

  readRegister(BME280_STATUS, &status, 1);
  return (status & 0x08) ? 1 : 0;
}

uint8_t BME280::isUpdating() {
  uint8_t status;

  readRegister(BME280_STATUS, &status, 1);
  return (status & 0x01) ? 1 : 0;
}

uint8_t BME280::readId() {
  uint8_t data;

  readRegister(BME280_ID, &data, 1);
  return data;
}

void BME280::readTrimmingParameter() {
  uint8_t data[32];

  readRegister(BME280_CALIB00, &(data[0]), 24); /* 0x88-0x9f */
  readRegister(BME280_CALIB25, &(data[24]), 1); /* 0xa1 */
  readRegister(BME280_CALIB26, &(data[25]), 7); /* 0xe1-0xe7 */

  dig_T1 = data[0] | (data[1] << 8);
  dig_T2 = data[2] | (data[3] << 8);
  dig_T3 = data[4] | (data[5] << 8);

  dig_P1 = data[6] | (data[7] << 8);
  dig_P2 = data[8] | (data[9] << 8);
  dig_P3 = data[10] | (data[11] << 8);
  dig_P4 = data[12] | (data[13] << 8);
  dig_P5 = data[14] | (data[15] << 8);
  dig_P6 = data[16] | (data[17] << 8);
  dig_P7 = data[18] | (data[19] << 8);
  dig_P8 = data[20] | (data[21] << 8);
  dig_P9 = data[22] | (data[23] << 8);

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

// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
int32_t BME280::compensate_T(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;
}

// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
uint32_t BME280::compensate_P(int32_t adc_P) {
  int64_t var1, var2, p;
  var1 = ((int64_t)t_fine) - 128000;
  var2 = var1 * var1 * (int64_t)dig_P6;
  var2 = var2 + ((var1 * (int64_t)dig_P5) << 17);
  var2 = var2 + (((int64_t)dig_P4) << 35);
  var1 = ((var1 * var1 * (int64_t)dig_P3) >> 8) + ((var1 * (int64_t)dig_P2) << 12);
  var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)dig_P1) >> 33;
  if (var1 == 0) {
    return 0; // avoid exception caused by division by zero
  }
  p = 1048576 - adc_P;
  p = (((p << 31) - var2) * 3125) / var1;
  var1 = (((int64_t)dig_P9) * (p >> 13) * (p >> 13)) >> 25;
  var2 = (((int64_t)dig_P8) * p) >> 19;
  p = ((p + var1 + var2) >> 8) + (((int64_t)dig_P7) << 4);
  return (uint32_t)p;
}

// Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits).
// Output value of “47445” represents 47445/1024 = 46.333 %RH
uint32_t BME280::compensate_H(int32_t adc_H) {
  int32_t v_x1_u32r;

  v_x1_u32r = (t_fine - ((int32_t)76800));
  v_x1_u32r = (((((adc_H << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1_u32r)) +
                 ((int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)dig_H6)) >> 10) * (((v_x1_u32r *
                     ((int32_t)dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) *
                     ((int32_t)dig_H2) + 8192) >> 14));
  v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
  v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
  v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
  return (uint32_t)(v_x1_u32r >> 12);
}

uint8_t BME280::writeRegister(uint8_t address, uint8_t data) {
  Wire.beginTransmission(BME280_ADDRESS);
  Wire.write(address);
  Wire.write(data);
  return Wire.endTransmission();
}

uint8_t BME280::readRegister(uint8_t address, uint8_t data[], uint8_t numberOfData) {
  uint8_t numberOfDataRead;

  Wire.beginTransmission(BME280_ADDRESS);
  Wire.write(address);
  Wire.endTransmission();

  Wire.requestFrom((uint8_t)BME280_ADDRESS, numberOfData);
  while (Wire.available() < numberOfData) {
    ;
  }

  for (numberOfDataRead = 0; numberOfDataRead < numberOfData; numberOfDataRead++) {
    data[numberOfDataRead] = Wire.read();
  }

  return (numberOfDataRead);
}

実行の様子

上記のスケッチを実行している様子です。

注意

Arduino Dueについては、Arduino-1.6.5では確認しましたが、Arduino-1.8.10では確認していません。

ESP-WROOM-32については、Arduino-1.8.10 + Arduino core for the ESP32 1.0.4 で確認しました。

バージョン

Hardware:Arduino Due/ESP-WROOM-32
Software:Arduino 1.6.5/Arduino 1.5.10/Arduino core for the ESP32 1.0.4

最終更新日

June 20, 2020

inserted by FC2 system