Page 1 of 1

BMP280 I2C temperature & pressure reading issue ***Solved***

Posted: Tue Apr 30, 2019 3:38 am
by erik2727
The BMP280 temperature and pressure reading seemed to be static using the below ESP32 esp-idf I2C code together with official Bosch bmp280 c library
at https://github.com/BoschSensortec/BMP280_driver. I have also tested the result using Arduino Adafruit BMP280 , the output from the Arduino seemed to be much reliable with variation noticed in both temperature and pressure values readout.

I think this issue may be due to either the I2C config or the bmp config missing some vital parameter or wrong setup given the fact the self test always return unkown error code -9 , any advice or solution?
  1. #include <stdio.h>
  2. #include <math.h>
  3. #include "freertos/FreeRTOS.h"
  4. #include "freertos/task.h"
  5. #include "esp_system.h"
  6. #include "esp_spi_flash.h"
  7. #include "driver/gpio.h"
  8. #include "driver/i2c.h"
  9. #include "esp_err.h"
  10. #include "nvs_flash.h"
  11. #include "bmp280.h"
  12. #include "bmp280_defs.h"
  13. #include "bmp280.c"
  14.  
  15.  
  16.  
  17. #define SDA_GPIO 21
  18. #define SCL_GPIO 22
  19. #define I2C_MASTER_FREQ_HZ CONFIG_I2C_MASTER_FREQUENCY
  20. struct bmp280_uncomp_data ucomp_data;
  21. int32_t temp32;
  22. double temp;
  23. uint32_t pres32, pres64;
  24. double pres;
  25.  
  26. void i2c_master_init() {
  27.     i2c_config_t i2c_config = {
  28.             .mode = I2C_MODE_MASTER,
  29.             .sda_io_num = SDA_GPIO,
  30.             .scl_io_num = SCL_GPIO,
  31.             .sda_pullup_en = GPIO_PULLUP_ENABLE,
  32.             .scl_pullup_en = GPIO_PULLUP_ENABLE,
  33.             .master.clk_speed = 100000
  34.  
  35.     };
  36.     i2c_param_config(I2C_NUM_0, &i2c_config);
  37.     i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);
  38.  
  39. //*************// Verify that the I2C slave is working properly
  40.    esp_err_t esp_retval;  
  41.  
  42.    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  43.     i2c_master_start(cmd);
  44.     i2c_master_write_byte(cmd, (BMP280_I2C_ADDR_PRIM << 1) | I2C_MASTER_WRITE, true);
  45.     i2c_master_stop(cmd);
  46.     f_retval = i2c_master_cmd_begin(I2C_NUM_0, cmd, RTOS_DELAY_1SEC);
  47.     if (f_retval != ESP_OK) {
  48.         printf("I2C slave NOT working or wrong I2C slave address - error (%i)", f_retval);
  49.         // LABEL
  50.  
  51.     }
  52. i2c_cmd_link_delete(cmd);
  53.  
  54.  
  55. }
  56.  
  57.  
  58. int8_t i2c_reg_read(uint8_t i2c_addr, uint8_t reg_addr, uint8_t *reg_data, uint16_t length) {
  59.  
  60.     int8_t iError;
  61.     esp_err_t esp_err;
  62.     i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();
  63.     i2c_master_start(cmd_handle);
  64.     i2c_master_write_byte(cmd_handle, (i2c_addr << 1) | I2C_MASTER_WRITE, true);
  65.     i2c_master_write_byte(cmd_handle, reg_addr, true);
  66.     i2c_master_start(cmd_handle);
  67.     i2c_master_write_byte(cmd_handle, (i2c_addr << 1) | I2C_MASTER_READ, true);
  68.     if (length > 1) {
  69.         i2c_master_read(cmd_handle, reg_data, length - 1, I2C_MASTER_ACK);
  70.     }
  71.     i2c_master_read_byte(cmd_handle, reg_data + length - 1, I2C_MASTER_NACK);
  72.     i2c_master_stop(cmd_handle);
  73.     esp_err = i2c_master_cmd_begin(I2C_NUM_0, cmd_handle, 1000 / portTICK_PERIOD_MS);
  74.  
  75.     if (esp_err == ESP_OK) {
  76.         iError = 0;
  77.     } else {
  78.         iError = -1;
  79.     }
  80.  
  81.     i2c_cmd_link_delete(cmd_handle);
  82.  
  83.    
  84.     return iError;
  85. }
  86.  
  87. int8_t i2c_reg_write(uint8_t i2c_addr, uint8_t reg_addr, uint8_t *reg_data, uint16_t length) {
  88.     /* Implement the I2C write routine according to the target machine. */
  89.     int8_t iError;
  90.     esp_err_t esp_err;
  91.     i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();
  92.     i2c_master_start(cmd_handle);
  93.     i2c_master_write_byte(cmd_handle, (i2c_addr << 1) | I2C_MASTER_WRITE, true);
  94.     i2c_master_write_byte(cmd_handle, reg_addr, true);
  95.     i2c_master_write(cmd_handle, reg_data, length,true);
  96.     i2c_master_stop(cmd_handle);
  97.     esp_err = i2c_master_cmd_begin(I2C_NUM_0, cmd_handle, 1000 / portTICK_PERIOD_MS);
  98.     if (esp_err == ESP_OK) {
  99.         iError = 0;
  100.     } else {
  101.         iError = -1;
  102.     }
  103.     i2c_cmd_link_delete(cmd_handle);
  104.     return iError;
  105. }
  106.  
  107.  
  108. void delay_ms(uint32_t period_ms) {
  109.     /* Implement the delay routine according to the target machine */
  110.     //vTaskDelay(period_ms / portTICK_PERIOD_MS);
  111. //********************//
  112.          ets_delay_us(period_ms * 1000);
  113. }
  114.  
  115. void print_rslt(const char api_name[], int8_t rslt) {
  116.  
  117.  
  118.     if (rslt != BMP280_OK) {
  119.         printf("%s\t", api_name);
  120.         if (rslt == BMP280_E_NULL_PTR) {
  121.             printf("Error [%d] : Null pointer error\r\n", rslt);
  122.         } else if (rslt == BMP280_E_COMM_FAIL) {
  123.             printf("Error [%d] : Bus communication failed\r\n", rslt);
  124.         } else if (rslt == BMP280_E_IMPLAUS_TEMP) {
  125.             printf("Error [%d] : Invalid Temperature\r\n", rslt);
  126.         } else if (rslt == BMP280_E_DEV_NOT_FOUND) {
  127.             printf("Error [%d] : Device not found\r\n", rslt);
  128.         } else {
  129.             /* For more error codes refer "*_defs.h" */
  130.             printf("Error [%d] : Unknown error code\r\n", rslt);
  131.         }
  132.     }else{
  133.         printf("%s\t", api_name);
  134.         printf(" BMP280 status [%d]\n ",rslt);
  135.     }
  136. }
  137.  
  138. void bmp280_test(void *pvParamters) {
  139.     struct bmp280_dev bmp;
  140.     struct bmp280_config config;
  141.     int8_t bmpstatus;
  142.     bmp.delay_ms = delay_ms;
  143.     /* Assign device I2C address based on the status of SDO pin (GND for PRIMARY(0x76) & VDD for SECONDARY(0x77)) */
  144.     bmp.dev_id = BMP280_I2C_ADDR_PRIM;
  145.     bmp.intf = BMP280_I2C_INTF;
  146.     bmp.read = i2c_reg_read;
  147.     bmp.write = i2c_reg_write;
  148.  
  149.     bmpstatus = bmp280_init(&bmp);
  150.     //printf("bmp280 init status %d",bmpstatus);
  151.     print_rslt("bmp280_init status", bmpstatus);
  152.     bmpstatus = bmp280_get_config(&config, &bmp);
  153.     print_rslt("bmp280_get_config status", bmpstatus);
  154.     /* configuring the temperature oversampling, filter coefficient and output data rate */
  155.     /* Overwrite the desired settings */
  156.    
  157.  
  158.  
  159.     config.filter = BMP280_FILTER_COEFF_2;
  160.  
  161. //*******// configure the sampling according to data spec sheet recommendation
  162.  
  163.     /* Temperature oversampling set at 1x */
  164.     config.os_temp = BMP280_OS_1X;
  165.  
  166.     /* Pressure over sampling  ( pressure measurement) */
  167.     config.os_pres = BMP280_OS_8X;
  168.  
  169.  
  170.  
  171.     /* Setting the output data rate as 1HZ(1000ms) */
  172.     config.odr = BMP280_ODR_1000_MS;
  173.  
  174.     bmpstatus = bmp280_set_config(&config, &bmp);
  175.     print_rslt(" bmp280_set_config status", bmpstatus);
  176.     bmpstatus = bmp280_set_power_mode(BMP280_NORMAL_MODE, &bmp);
  177.  
  178.     print_rslt(" bmp280_set_power_mode status", bmpstatus);
  179.     while (1) {
  180.              //*************// Add a delay of minimum 50millisec or more to get  sensor  reading!
  181.  
  182.  
  183.                 vTaskDelay(50 / portTICK_PERIOD_MS);
  184.  
  185.         /* Reading the raw data from sensor */
  186.         bmpstatus = bmp280_get_uncomp_data(&ucomp_data, &bmp);
  187.         //print_rslt(" bmp280_raw status", bmpstatus);
  188.         /* Getting the 32 bit compensated temperature */
  189.         bmpstatus = bmp280_get_comp_temp_32bit(&temp32, ucomp_data.uncomp_temp, &bmp);
  190.         //print_rslt(" bmp280_comp_temp32", bmpstatus);
  191.         /* Getting the compensated temperature as floating point value */
  192.         bmpstatus = bmp280_get_comp_temp_double(&temp, ucomp_data.uncomp_temp, &bmp);
  193.         //print_rslt(" bmp280_comp_temp", bmpstatus);
  194.         printf("UT: %d, T32: %d, T: %f \r\n", ucomp_data.uncomp_temp, temp32, temp);
  195.  
  196.         /* Getting the compensated pressure using 32 bit precision */
  197.         bmpstatus = bmp280_get_comp_pres_32bit(&pres32, ucomp_data.uncomp_press, &bmp);
  198.     //  print_rslt(" bmp280_comp_pressure32", bmpstatus);
  199.         /* Getting the compensated pressure using 64 bit precision */
  200.         bmpstatus = bmp280_get_comp_pres_64bit(&pres64, ucomp_data.uncomp_press, &bmp);
  201.     //  print_rslt(" bmp280_comp_pressure64", bmpstatus);
  202.         /* Getting the compensated pressure as floating point value */
  203.         bmpstatus = bmp280_get_comp_pres_double(&pres, ucomp_data.uncomp_press, &bmp);
  204.     //  print_rslt(" bmp280_comp_pressure", bmpstatus);
  205.         printf("UP: %d, P32: %d, P64: %d, P64N: %d, P: %f\r\n",
  206.                ucomp_data.uncomp_press,
  207.                pres32,
  208.                pres64,
  209.                pres64 / 256,
  210.                pres);
  211.  
  212.         printf("\n");
  213.         float altitude;
  214.  
  215.         float pressure = pres64 / 256; // in Si units for Pascal
  216.         pressure /= 100;
  217.  
  218.         altitude = 44330 * (1.0 - pow(pressure / 1013.25, 0.1903));
  219.  
  220.         printf("Altitude %f\n", altitude);
  221.         printf("\n");
  222.        
  223.  
  224.         /* Sleep time between measurements = BMP280_ODR_1000_MS */
  225.         bmp.delay_ms(1);
  226.        
  227. //******************// add a delay of reasonable  ms within the while loop
  228.        vTaskDelay(3000 / portTICK_PERIOD_MS);
  229.  
  230.  
  231.  
  232.     }
  233.     vTaskDelete(NULL);
  234. }
  235.  
  236.  
  237. void app_main() {
  238.    
  239.     ESP_ERROR_CHECK(nvs_flash_init());
  240.    
  241.    
  242.     i2c_master_init();
  243.     xTaskCreate(&bmp280_test, "bmp280_test", 2048, NULL, 6, NULL);
  244.  
  245.  
  246. }
  247.  

Re: BMP280 I2C temperature & pressure reading issue

Posted: Tue Apr 30, 2019 5:45 pm
by fly135
We tried using the BME280. I was able to get it to work with the IDF using the Bosch libs. But when we manufactured some boards the humidity reading was totally off on a bunch. And yes, I did use the library's calibration routines with the BME internal calibration data. We were using the HTU21D with good results. So we axed the BME design and went back to the HTU. No pressure, but not really finding that useful anyway for our needs.

John A

Re: BMP280 I2C temperature & pressure reading issue

Posted: Wed May 01, 2019 4:32 am
by pa_denizen
Quick observation:

Your line 49: i2c_master_start(cmd_handle);

Why do you restart the i2c transaction in the middle of writing the register address and then writing the read request?

Re: BMP280 I2C temperature & pressure reading issue

Posted: Thu May 02, 2019 1:02 am
by erik2727
Without the i2c start ,bus communication error will occur,it is kind of strange in esp32 .

Re: BMP280 I2C temperature & pressure reading issue

Posted: Thu May 02, 2019 1:25 am
by WiFive
In forced mode, a single measurement is performed according to selected measurement and
filter options. When the measurement is finished, the sensor returns to sleep mode and the
measurement results can be obtained from the data registers. For a next measurement, forced
mode needs to be selected again. This is similar to BMP180 operation. Forced mode is
recommended for applications which require low sampling rate or host-based synchronization.
You set force mode once, you get one measurement. Then you are just reading that same measurement over and over.

https://github.com/BoschSensortec/BMP28 ... efs.h#L139

Re: BMP280 I2C temperature & pressure reading issue

Posted: Thu May 02, 2019 3:57 am
by erik2727
I have tested both force and normal mode ,result still remain static, result remained static even if I set the power config as force mode inside the while continuous loop which suppose to yield new measurement when triggered .

Bosch api st_set_config called by self test always revert back to forced mode,I have thus removed all self test routine, reading still remain as static. It may got something to do with default data register shadowing specify in the data sheet

Re: BMP280 I2C temperature & pressure reading issue

Posted: Fri May 03, 2019 3:35 am
by WiFive
Hmm, well comparing logic analyzer captures may help you.

Re: BMP280 I2C temperature & pressure reading issue

Posted: Fri May 03, 2019 11:09 am
by papaluna
1. The vTaskDelay() is not suited for low values of millisec_delays. It is essential to use ets_delay_us() instead. Maybe that fixes it.

Code: Select all

/*  \Brief : The delay routine
 *  \param : delay in ms
 */
void BMP280_delay_millisec(u32 millisec) {
    ets_delay_us(millisec * 1000);
}
2. A working ESP-IDF project for the BMP280 sensor can be found here.
https://github.com/pantaluna/esp32_bmp2 ... _using_lib

Re: BMP280 I2C temperature & pressure reading issue

Posted: Fri May 03, 2019 8:58 pm
by erik2727
Apparently a delay of 50ms or more need to be placed before the bmp280_get_uncomp_data command inside the while loop together with a reasonable vtaskdelay solved the static reading problem. Thank you so much to those sharing the valuable feedback and suggestion. I will update the revised workable code identify with tag //*************//after validating the result as this is a very simple esp i2c idf proven to work with latest 2019 Bosch BMP280 library.

//*************// are those additional revised code added to resolve static reading issue encounter--done