Arduino/Raspberry Pi で温度・湿度・気圧(BME280)を測定する方法

 Raspberry Pi Pico(ラズパイPico)でBME280を使って温度、湿度、気圧を測定してみます。ラズパイPicoの開発環境はArduinoIDEを使用するので、内容(使い方)はほとんど Arduino同様 となり、ほぼそのままArduinoでも使える内容となります。今回もライブラリは使わずにやってみたいと思います。

▼PicoをArduinoIDEから使う方法▼

▼Arduino+BME280の使い方▼

▲以前ArduinoでBME280を使っていて、ここの内容とほぼ同じです。なので今回は必要最低限、簡素に使う方法だけを紹介します。(BME280の詳細は上のリンクから確認下さい)

スポンサーリンク

結線

 使用するBME280センサモジュールは秋月から入手した「AE-BME280」です。使用する通信方式は、I2C通信。SPIでも通信できますがここでは触れません。

▼結線▼

BME280 VDD ⇔ Pico 3V3(OUT)
BME280 GND ⇔ Pico GND
BME280 CSB ⇔ Pico 3V3(OUT)
BME280 SDI ⇔ Pico I2C0 SDA(GP16)
BME280 SDO ⇔ Pico GND
BME280 SCK ⇔ Pico I2C0 SCL(GP17)

 I2C通信を使用する場合、CSBピンはVDDへ接続しておきます。秋月のモジュールを使用であればジャンパー(j3)が準備されているのでそこを接続してもOKです。SDOをGNDに接続するとデバイスアドレスが「0x76」、VDDに接続で「0x77」になります。今回はGNDに接続してます。Picoの場合、I2C通信に使えるピンが複数あるのでここではこのように設定してますが、Arduinoの場合は、A4/A5ピンを使用します。

▼ブレッドボード▼

 ブレッドボードで準備してます。写真はPico。

スケッチ(ソース)

 スケッチ全体は記事後半に掲載してます。ここでは要所を切り取って説明。

▼セットアップ▼

  uint8_t osrs_t   = 2;     //Temperature oversampling x 2
  uint8_t osrs_p   = 5;     //Pressure oversampling    x 16
  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 x16
  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;                                  //湿度オーバーサンプリング

 BME280の初期設定の抜粋です。温湿度気圧測定でそれぞれ細かい設定ができますが、ここではデータシートにある推奨の動作モード(室内監視)の設定そのままにしてます。設定した内容をI2C通信で決められたレジスタへ書き込みます。あとここで計算に必要なtrimデータも読みだしておきます。

▼読み取り▼

    read_BME280();

 この関数の先で、温度、湿度、気圧のbit値を読み取ってます。

▼計算▼

  temp32  = computeTemp32(tempRaw) * 0.01;  //℃
  pres32  = computePres32(presRaw) * 0.01;  //hPa
  pres64  = computePres64(presRaw) >> 8;    //pa
  pres64 *= 0.01;                           //hpa
  hum32   = computeHum32(humRaw)   >> 10;   //%

 ビット値からそれぞれ物理量へ変換します。気圧は32/64bitの精度で計算できます。関数の中身は結構ややこしい計算式ですがデータシートのままです。

 だいぶ説明は端折りましたが、ただ計測値を読み取るだけでいいなら記事下段にあるソースをそのまま使えばOKです。

計測結果

 読み取った値をシリアルモニターで表示してます。

 精度はよくわかりません。。。気圧が32、64bitで若干差が出ます。少し様子見てましたが~4Pa程度の差はでてました。

ソース

 今回使用したソース全体です。Unoで使う場合はI2C通信のピンアサインのところ(2行)をコメントアウトして使用します。

// BME280モジュール、RaspberriPiPico用およびArduinoUno用スケッチ
// 2021/12/22 imo lab.

#include <Wire.h>

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;

int32_t  t_fine;
uint32_t humRaw, tempRaw, presRaw, hum32;
float    temp32, pres32, pres64;


uint32_t currentTime, loopTimer, baroTimer, dt, debugDt, debugTimer;

void setup() {
  pinMode(25, OUTPUT);   //ON BORD LED/GP25
  Serial.begin(115200);
  Wire.setSDA(16);       //I2C0 SDA GP16 \Unoで使う場合、この行をコメントアウト
  Wire.setSCL(17);       //I2C0 SCL GP17 \Unoで使う場合、この行をコメントアウト
  Wire.setClock(400000);
  Wire.begin();          //I2C0 begin
  setupBME280();

  digitalWrite(25, HIGH);
}

void loop() {
  while (currentTime - loopTimer <= 100000)currentTime = micros(); //100ms
  loopTimer = currentTime;

  if (loopTimer - baroTimer > 1000000) {//1Hz
    baroTimer = micros();

    read_BME280();
    //debugTimer = micros();
    //debugDt = micros() - debugTimer;

    Serial.printf("\n%11s%8.2f%5s\n", "THERMO:", temp32, "degC");
    Serial.printf("%s%8.2f%4s\n", "BARO 32bit:", pres32, "hPa");
    Serial.printf("%s%8.2f%4s\n", "BARO 64bit:", pres64, "hPa");
    Serial.printf("%11s%8d%2s\n", "HYGRO:", hum32, "%");
    Serial.printf("\n\n\n\n\n\n\n\n");
  }

  Serial.printf("%s","///");
}

// *****************
// * read BME280
// *****************
inline void read_BME280() {
  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];

  temp32 = computeTemp32(tempRaw) * 0.01;  //℃
  pres32 = computePres32(presRaw) * 0.01;  //hPa
  pres64 = computePres64(presRaw) >> 8;    //pa
  pres64 *= 0.01;                          //hpa
  hum32  = computeHum32(humRaw)   >> 10;   //%
}

// *****************
// * 温度計算
// *****************
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;
}

// *****************
// * 気圧計算
// *****************
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;
}

inline uint32_t computePres64(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; // ゼロ除算による例外を避ける。
  }
  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;
}

// *****************
// * 湿度計算
// *****************
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);
}

// ****************************************************************
// ***BME280 初期設定 ***
// ****************************************************************
void setupBME280() {
  uint8_t osrs_t   = 2;     //Temperature oversampling x 2
  uint8_t osrs_p   = 5;     //Pressure oversampling    x 16
  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 x16
  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;                                  //湿度オーバーサンプリング

  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();

  readTrimBME280();
}

// ****************************************************************
// * read trim data BME280 ***
// ****************************************************************
void readTrimBME280() {
  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];
}

コメント