Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
温度・湿度・気圧センサモジュールの実験

概要

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の主なレジスタは以下の通りです。

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

実験

BME280とArduinoとの接続

BME280は、最大3.6Vまでしかかけられないので、Arduino Dueを利用しました。Arduino Unoを利用する際は、注意してください。

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

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

Arduinoプログラミング

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

初期化(begin())

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に接続しないよう、Arduino Due以外(正確には、__SAM3X8E__ が定義されているときだけ)ではコンパイルがエラーになるようにしています。気圧の補正をするときには、uint64_tを利用しています(データシート内のプログラムをそのまま利用しているだけですが)。

getdata.ino

#include <Wire.h>
#include "BME280.h"

BME280 bme280;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  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

#ifndef BME280_H
#define BME280_H

#ifndef __SAM3X8E__ 
#error "Not Arduino Due"
#error "Remove this with care"
#endif

#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 /* BME280_H */

BME280.cpp

#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 1.6.5/Arduino Due



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2015 garretlab all rights reserved.
inserted by FC2 system