Nordic nRF52840 で温湿圧力センサ BME280

# ボタン電池で温湿度を計測する

以前、DHT11を使って温湿度測定をしてみました。
「Nordic nRF52840 で温度湿度センサ DHT11 を使う」
https://qiita.com/nanbuwks/items/6440657c055b1c8b457e

しかしながら DHT11 は電源電圧：DC3.3V~5.5Vであるため、ボタン電池などで動かすのは難しいです。

ボタン電池での使用を想定し、電源電圧の幅の広いセンサを探して、Bosch の温湿度圧力センサ BME280 を選定しました。

- 電源電圧 1.71 V to 3.6 V
- 消費電流 (毎秒温湿度圧力計測) 3.6 µA
- スリープ下での消費電流 0.1 µA

と、消費電流も優秀です。(DHT11の消費電流は測定時0.3mA、スタンバイ時 60μA)

今回は BME280 を Nordic nRF52840 に TWI(I2C) で接続し、データを取得してみます。

# 環境

- 開発ボード アオノドン2019(Raytac MDBT50Q / Nordic nRF52840 搭載) - IDE Segger Enbedid Studio
- 開発PC Ubuntu 18.04LTS
- SDK nRF5_SDK 15.2 および 16.0 、および nRF5_SDK_for_Thread_and_Zigbee_v4.0.0_dc7186b で確認

# 配線、通信確認

開発ボード アオノドン2019 はウラ面にGROVE端子があり、それをI2Cとして使うことができます。

以下のように結線します。

| 信号名 | AE-BME280 | GROVE端子/ケーブル | アオノドン2019 (nRF52840) |
|:---:|:---:|:---:|:---:|
| SCL |SCK|Pin1/黃| P0.05 |
| SDA |SDI|Pin2/白| P0.07 |
|(電源/VDD)|VDD|Pin3/赤|(VDD)|
|(電源/GND)|GND|Pin4/黒|(GND)|

GROVEケーブルはSeeedStudio製のものを使いました。ケーブルの色は、M5Stack用などだと異なるので注意！


「Nordic SDK でTWI(I2C)」
https://qiita.com/nanbuwks/items/776d4ad8abf5eb0501e2
を参照し、「examples/peripheral/twi_scanner」を使います。

配線に合わせ、以下の記述を main.c 内に追加します。

```

#define ARDUINO_SCL_PIN 5
#define ARDUINO_SDA_PIN 7
```

実行するとアオノドン2019に接続したUSBシリアルモジュールから以下のように出力されます。

```

<info> app: TWI scanner started.
<info> app: TWI device detected at address 0x76.
```

無事接続、通信が確認できました。


# データを取得する

twi_sensorを元に、直してみます。

## main.c

```

#include "nrf_delay.h"
#include <stdint.h>
#include <string.h>
#include "app_uart.h"
// #include "bsp_btn_ble.h"
#include "nrf_uarte.h"

#if defined (UART_PRESENT)
#include "nrf_uart.h"
#endif
#if defined (UARTE_PRESENT)
#include "nrf_uarte.h"
#endif

#define UART_TX_BUF_SIZE                256   
#define UART_RX_BUF_SIZE                256     


#include <stdio.h>
#include "boards.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "nrf_drv_twi.h"
#include "BME280.h"


#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

/* TWI instance ID. */
#define TWI_INSTANCE_ID     0



/* Indicates if operation on TWI has ended. */
static volatile bool m_xfer_done = false;

/* TWI instance. */
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);

/* Buffer for samples read from temperature sensor. */
static uint8_t m_sample;
// #define ARDUINO_SCL_PIN 10
// #define ARDUINO_SDA_PIN 9

// aonodon20181113 
//#define ARDUINO_SCL_PIN 30
//#define ARDUINO_SDA_PIN 31

// aonodon20190303
//#define ARDUINO_SCL_PIN 27
//#define ARDUINO_SDA_PIN 26

// aonodon2019-grove
#define ARDUINO_SCL_PIN 5
#define ARDUINO_SDA_PIN 7

/**
 * @brief Function for setting active mode on MMA7660 accelerometer.
 */

void uart_event_handle(app_uart_evt_t * p_event)
{
}
static void uart_init(void)
{
    uint32_t                     err_code;
    app_uart_comm_params_t const comm_params =
    {
        .rx_pin_no    = RX_PIN_NUMBER,
        .tx_pin_no    = TX_PIN_NUMBER,
        .rts_pin_no   = RTS_PIN_NUMBER,
        .cts_pin_no   = CTS_PIN_NUMBER,
        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
        .use_parity   = false,
#if defined (UART_PRESENT)
        .baud_rate    = NRF_UART_BAUDRATE_115200
#else
        .baud_rate    = NRF_UARTE_BAUDRATE_115200
#endif
    };
    APP_UART_FIFO_INIT(&comm_params,
                       UART_RX_BUF_SIZE,
                       UART_TX_BUF_SIZE,
                       uart_event_handle,
                       APP_IRQ_PRIORITY_LOWEST,
                       err_code);
    APP_ERROR_CHECK(err_code);
}

void twi_init (void)
{
    ret_code_t err_code;

    const nrf_drv_twi_config_t twi_lm75b_config = {
       .scl                = ARDUINO_SCL_PIN,
       .sda                = ARDUINO_SDA_PIN,
       .frequency          = NRF_DRV_TWI_FREQ_100K,
       .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
       .clear_bus_init     = false
    };
// non-blocking mode
//  err_code = nrf_drv_twi_init(&m_twi, &twi_lm75b_config, twi_handler, NULL);
// blocking mode
    err_code = nrf_drv_twi_init(&m_twi, &twi_lm75b_config, NULL, NULL);
    APP_ERROR_CHECK(err_code);
    nrf_drv_twi_enable(&m_twi);
}


ret_code_t read_register(nrf_drv_twi_t twi_instance, uint8_t device_addr, uint8_t register_addr, uint8_t *p_data, uint8_t bytes, bool no_stop)
{
  ret_code_t err_code;

  err_code = nrf_drv_twi_tx(&twi_instance, device_addr, &register_addr, 1, no_stop);
  APP_ERROR_CHECK(err_code);

  if(err_code != NRF_SUCCESS) {
    return err_code;
  }

  err_code = nrf_drv_twi_rx(&twi_instance, device_addr, p_data, bytes);
  return err_code;
} 

int main(void)
{

  double temp_act = 0.0, press_act = 0.0,hum_act=0.0;
  signed long int temp_cal;
  unsigned long int press_cal,humi_cal;
  struct BME280DATASTRUCT BME280Data;  

  //uart_init();
  APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
  NRF_LOG_DEFAULT_BACKENDS_INIT();

  NRF_LOG_INFO("\r\nTWI sensor example started.");
  NRF_LOG_FLUSH();
  twi_init();

  BME280init();

  while (true)
  {

    BME280Data = readBME280Data();
    temp_cal = calibration_T(BME280Data.temp_raw);
    press_cal = calibration_P(BME280Data.pres_raw);
    humi_cal = calibration_H(BME280Data.humi_raw);

    NRF_LOG_INFO("TWI TEMP and HUMI: " NRF_LOG_FLOAT_MARKER " "  NRF_LOG_FLOAT_MARKER,  NRF_LOG_FLOAT(temp_cal/100),NRF_LOG_FLOAT(humi_cal/1024));
    NRF_LOG_FLUSH();
    NRF_LOG_INFO("TWI PRESSER: " NRF_LOG_FLOAT_MARKER ,  NRF_LOG_FLOAT(press_cal/100));
    NRF_LOG_FLUSH();

    nrf_delay_ms(500);


    }
}

/** @} */

```

## BME280.h

```

/**
 * BME280.h
 *
 * nRF52 TWI interface for BME280.
 *
 * ported from http://trac.switch-science.com/wiki/BME280
 * nanbuwks / openforce
 */

#include <stdint.h>
#include <stdbool.h>
#include "nrf_delay.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "boards.h"
#include <nrf_drv_twi.h>


/*Device Address*/

#define DEVICE_ADDR          0x76U


struct BME280DATASTRUCT{
    unsigned long int temp_raw;
    unsigned long int humi_raw;
    unsigned long int pres_raw;
};



ret_code_t readTrim();
struct BME280DATASTRUCT readBME280Data();

  signed long int calibration_T();
unsigned long int calibration_H();
unsigned long int calibration_P();




```

## BME280.c 

スイッチサイエンスのおまけページ
「BME280搭載　温湿度・気圧センサモジュールの使い方」
http://trac.switch-science.com/wiki/BME280
のArduinoスケッチから移植しました。

```


/**
 * LSM6DS3.h
 *
 * nRF52 TWI interface for BME280.
 *
 * from http://trac.switch-science.com/wiki/BME280
 * ported by nanbuwks / openforce
 */
#include "app_error.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

#include "stdlib.h"
#include "time.h"
#include "BME280.h"
#include <nrf_drv_twi.h>
const nrf_drv_twi_t p_twi_sensors = NRF_DRV_TWI_INSTANCE(0);

signed long int t_fine;

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;
 int8_t  dig_H1;
 int16_t dig_H2;
 int8_t  dig_H3;
 int16_t dig_H4;
 int16_t dig_H5;
 int8_t  dig_H6;

void BME280init(void){
  uint8_t m_sample;
  ret_code_t err_code;
  err_code= read_register(  p_twi_sensors, DEVICE_ADDR, 0xD0,  &m_sample, sizeof(m_sample), false);
  NRF_LOG_INFO("WHO AM I %d.", m_sample);
  NRF_LOG_FLUSH();
  APP_ERROR_CHECK(err_code);
  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 = 5;               //Tstandby 1000ms
  uint8_t filter = 0;             //Filter off 
  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;
  uint8_t ctrl_hum_reg  = osrs_h;


  // set SLEEP_G bit in CTRL4_C register to enable sleep mode
  uint8_t tx_data[2];
  tx_data[0] = 0xF2;
  tx_data[1] = ctrl_hum_reg;
  err_code = nrf_drv_twi_tx(&p_twi_sensors, DEVICE_ADDR, tx_data, sizeof(tx_data), false);
  APP_ERROR_CHECK(err_code);
  tx_data[0] = 0xF4;
  tx_data[1] = ctrl_meas_reg;
  err_code = nrf_drv_twi_tx(&p_twi_sensors, DEVICE_ADDR, tx_data, sizeof(tx_data), false);
  APP_ERROR_CHECK(err_code);
  tx_data[0] = 0xF5;
  tx_data[1] = config_reg;
  err_code = nrf_drv_twi_tx(&p_twi_sensors, DEVICE_ADDR, tx_data, sizeof(tx_data), false);
  APP_ERROR_CHECK(err_code);
  err_code =   readTrim();
  APP_ERROR_CHECK(err_code);

}    

ret_code_t readTrim()
{
// Calib 00 - 23 
// 24 bytes data read
// address 0x88-0x9E
  ret_code_t err_code;
  uint8_t tx_data[1];
  uint8_t p_data[32];
  tx_data[0] = 0x88;
  err_code = nrf_drv_twi_tx(&p_twi_sensors, DEVICE_ADDR, tx_data, 1, false);
  if(err_code != NRF_SUCCESS) {
    return err_code;
  }

  err_code = nrf_drv_twi_rx(&p_twi_sensors, DEVICE_ADDR, p_data, 24);
  if(err_code != NRF_SUCCESS) {
    return err_code;
  }
// Calib 25 
// 1 byte data read
// address 0xA1
  tx_data[0] = 0xA1;
  err_code = nrf_drv_twi_tx(&p_twi_sensors, DEVICE_ADDR, tx_data, 1, false);
  if(err_code != NRF_SUCCESS) {
    return err_code;
  }

  err_code = nrf_drv_twi_rx(&p_twi_sensors, DEVICE_ADDR, p_data+24, 1);
  if(err_code != NRF_SUCCESS) {
    return err_code;
  }
// Carib 26 - 32
  tx_data[0] = 0xE1;
  err_code = nrf_drv_twi_tx(&p_twi_sensors, DEVICE_ADDR, tx_data, 1, false);
  if(err_code != NRF_SUCCESS) {
    return err_code;
  }

  err_code = nrf_drv_twi_rx(&p_twi_sensors, DEVICE_ADDR, p_data+25, 7);
  if(err_code != NRF_SUCCESS) {
    return err_code;
  }

    
  dig_T1 = (p_data[1] << 8) | p_data[0];
  dig_T2 = (p_data[3] << 8) | p_data[2];
  dig_T3 = (p_data[5] << 8) | p_data[4];
  dig_P1 = (p_data[7] << 8) | p_data[6];
  dig_P2 = (p_data[9] << 8) | p_data[8];
  dig_P3 = (p_data[11]<< 8) | p_data[10];
  dig_P4 = (p_data[13]<< 8) | p_data[12];
  dig_P5 = (p_data[15]<< 8) | p_data[14];
  dig_P6 = (p_data[17]<< 8) | p_data[16];
  dig_P7 = (p_data[19]<< 8) | p_data[18];
  dig_P8 = (p_data[21]<< 8) | p_data[20];
  dig_P9 = (p_data[23]<< 8) | p_data[22];
  dig_H1 = p_data[24];
  dig_H2 = (p_data[26]<< 8) | p_data[25];
  dig_H3 = p_data[27];
  dig_H4 = (p_data[28]<< 4) | (0x0F & p_data[29]);
  dig_H5 = (p_data[30] << 4) | ((p_data[29] >> 4) & 0x0F); 
  dig_H6 = p_data[31];                                  
  return err_code;

}
struct BME280DATASTRUCT readBME280Data()
{
  ret_code_t err_code;
  int i = 0;

  uint8_t tx_data[1];
  tx_data[0] = 0xF7;
  uint8_t p_data[8];
  struct BME280DATASTRUCT Data;

  err_code = nrf_drv_twi_tx(&p_twi_sensors, DEVICE_ADDR, tx_data, 1, false);
  APP_ERROR_CHECK(err_code);
   
  err_code = nrf_drv_twi_rx(&p_twi_sensors, DEVICE_ADDR, p_data, 8);
  APP_ERROR_CHECK(err_code);
   
  Data.pres_raw = (p_data[0] << 12) | (p_data[1] << 4) | (p_data[2] >> 4);
  Data.temp_raw = (p_data[3] << 12) | (p_data[4] << 4) | (p_data[5] >> 4);
  Data.humi_raw = (p_data[6] << 8) | p_data[7];
  return Data;
}
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; 
}

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;
}

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


```

# 実行結果

```

<info> app: TWI TEMP and HUMI: 24.00 53.00
<info> app: TWI PRESSER: 1011.00
<info> app: TWI TEMP and HUMI: 24.00 53.00
<info> app: TWI PRESSER: 1011.00
<info> app: TWI TEMP and HUMI: 24.00 53.00
<info> app: TWI PRESSER: 1011.00
```