05-12-2021 01:09 PM
I'm trying to use the BMP3-Sensor-API library from Github to read a BMP388 sensor via I2C, but I always get the same answer of raw values 00 00 80 00 00 80. The hardware is okay, measurements do work with the Adafruit Python library. I have implemented the i2c read and write functions similarly as for the BMP280_driver library, which does work. What am I missing? Here is my code that works with BMP280 but not with BMP388:
#ifdef BMP280
#include "bmp280.h"
#else
#include "bmp3.h"
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <stdbool.h>
#define DEVICE "/dev/i2c-1" // bus 1 on gpio 23 = sda, gpio 24 = scl
int fd_i2c; // file descriptor for i2c
bool i2c_interface_init(const char* device, const uint8_t address);
bool i2c_read(const uint8_t reg_addr, uint8_t *reg_data, const uint16_t length);
bool i2c_write(const uint8_t reg_addr, const uint8_t *reg_data, const uint16_t length);
/* Functions needed by the BMP library */
#ifdef BMP280
void delay_ms(uint32_t period_ms);
int8_t i2c_reg_write(uint8_t i2c_addr, uint8_t reg_addr, uint8_t *reg_data, uint16_t length);
int8_t i2c_reg_read(uint8_t i2c_addr, uint8_t reg_addr, uint8_t *reg_data, uint16_t length);
void print_rslt(const char api_name[], int8_t rslt);
#else
void bmp3_delay_us(uint32_t period, void *intf_ptr);
BMP3_INTF_RET_TYPE bmp3_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr);
BMP3_INTF_RET_TYPE bmp3_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr);
void bmp3_check_rslt(const char api_name[], int8_t rslt);
#endif
int main(int argc, char** argv)
{
/* Initialize bmp object. */
int8_t rslt;
#ifdef BMP280
struct bmp280_dev bmp;
bmp.dev_id = BMP280_I2C_ADDR_SEC; /* Assign device I2C address based on the status of SDO pin (GND for PRIMARY(0x76) & VDD for SECONDARY(0x77)) */
bmp.intf = BMP280_I2C_INTF; /* Select the interface mode as I2C */
/* Map the I2C read & write function pointer with the functions responsible for I2C bus transfer */
bmp.read = i2c_reg_read;
bmp.write = i2c_reg_write;
bmp.delay_ms = delay_ms; /* Map the delay function pointer with the function responsible for implementing the delay */
if (i2c_interface_init(DEVICE, bmp.dev_id) == false)
return EXIT_FAILURE;
rslt = bmp280_init(&bmp);
print_rslt(" bmp_init status", rslt);
#else
struct bmp3_dev dev;
dev.intf = BMP3_I2C_INTF;
uint8_t dev_addr = BMP3_ADDR_I2C_SEC;
dev.intf_ptr = &dev_addr;
dev.read = bmp3_i2c_read;
dev.write = bmp3_i2c_write;
dev.delay_us = bmp3_delay_us;
if (i2c_interface_init(DEVICE, dev_addr) == false)
return EXIT_FAILURE;
rslt = bmp3_init(&dev);
bmp3_check_rslt("bmp3_init", rslt);
#endif
/* Configure sensor settings. */
#ifdef BMP280
struct bmp280_config conf;
rslt = bmp280_get_config(&conf, &bmp); /* Always read the current settings before writing, especially when not all the configuration is modified */
print_rslt(" bmp280_get_config status", rslt);
conf.filter = BMP280_FILTER_COEFF_4; /* IIR filter coeff */
conf.os_pres = BMP280_OS_4X; /* Pressure oversampling set at 4x */
conf.os_temp = BMP280_OS_1X; /* Temperature oversampling set at 1x */
conf.odr = BMP280_ODR_125_MS; /* Setting the output data rate as 1Hz (1000ms) */
rslt = bmp280_set_config(&conf, &bmp);
print_rslt(" bmp280_set_config status", rslt);
rslt = bmp280_set_power_mode(BMP280_NORMAL_MODE, &bmp); /* Always set the power mode after setting the configuration. */
print_rslt(" bmp280_set_power_mode status", rslt);
#else
struct bmp3_settings settings = { 0 };
settings.int_settings.drdy_en = BMP3_ENABLE;
settings.press_en = BMP3_ENABLE;
settings.temp_en = BMP3_ENABLE;
settings.odr_filter.press_os = BMP3_OVERSAMPLING_2X;
settings.odr_filter.temp_os = BMP3_OVERSAMPLING_2X;
settings.odr_filter.odr = BMP3_ODR_12_5_HZ;
uint8_t settings_sel = BMP3_SEL_PRESS_EN | BMP3_SEL_TEMP_EN | BMP3_SEL_PRESS_OS | BMP3_SEL_TEMP_OS | BMP3_SEL_ODR |
BMP3_SEL_DRDY_EN;
rslt = bmp3_set_sensor_settings(settings_sel, &dev);
bmp3_check_rslt("bmp3_set_sensor_settings", rslt);
settings.op_mode = BMP3_MODE_NORMAL;
rslt = bmp3_set_op_mode(&dev);
bmp3_check_rslt("bmp3_set_op_mode", rslt);
#endif
/* Main loop. */
while (1)
{
/* Make a pressure reading. */
double temp = 0.;
double pres = 0.;
#ifdef BMP280
struct bmp280_uncomp_data ucomp_data;
rslt = bmp280_get_uncomp_data(&ucomp_data, &bmp); /* Read the raw data from sensor. */
print_rslt(" bmp280_get_uncomp_data status", rslt);
rslt = bmp280_get_comp_temp_double(&temp, ucomp_data.uncomp_temp, &bmp); /* Get the compensated temperature as floating point value */
print_rslt(" bmp280_get_comp_temp_double status", rslt);
rslt = bmp280_get_comp_pres_double(&pres, ucomp_data.uncomp_press, &bmp); /* Get the compensated pressure as floating point value */
print_rslt(" bmp280_get_comp_pres_double status", rslt);
#else
struct bmp3_data data = { 0 };
rslt = bmp3_get_sensor_data(BMP3_ALL, &data, &dev);
bmp3_check_rslt("bmp3_get_sensor_data", rslt);
rslt = bmp3_get_status(&dev); /* Read status register again to clear data ready interrupt status */
bmp3_check_rslt("bmp3_get_status", rslt);
#ifdef BMP3_DOUBLE_PRECISION_COMPENSATION
temp = data.temperature;
pres = data.pressure;
#else
temp = (double)data.temperature / 100.;
pres = (double)data.pressure / 100.;
#endif /* !BMP3_DOUBLE_PRECISION_COMPENSATION */
#endif
printf("%.1fPa %.1f°C\n", pres, temp);
#ifdef BMP280
bmp.delay_ms(125); /* Sleep time between measurements = BMP280_ODR_125_MS */
#else
usleep(125000); /* Sleep time between measurements */
#endif
}
return EXIT_SUCCESS;
}
bool i2c_interface_init(const char* device, const uint8_t address)
{
/* Open I2C device */
if ((fd_i2c = open(device, O_RDWR)) < 0)
{
fprintf(stderr, "Failed to open the i2c bus %s\n", device);
return false;
}
if (ioctl(fd_i2c, I2C_SLAVE, address) < 0)
{
fprintf(stderr, "Failed to acquire bus access and/or talk to slave.\n");
return false;
}
return true;
}
bool i2c_read(const uint8_t reg_addr, uint8_t *reg_data, const uint16_t length)
{
write(fd_i2c, ®_addr, 1);
read(fd_i2c, reg_data, length);
for (int i=0; i<length; i++)
printf("%02x ", reg_data[i]); // DEBUG
return true;
}
bool i2c_write(const uint8_t reg_addr, const uint8_t *reg_data, const uint16_t length)
{
int8_t *buf;
buf = malloc(length + 1);
buf[0] = reg_addr;
memcpy(buf + 1, reg_data, length);
if (write(fd_i2c, buf, length + 1) < length)
return false;
free(buf);
return true;
}
#ifdef BMP280
void delay_ms(uint32_t period_ms)
{
usleep(period_ms * 1000);
}
int8_t i2c_reg_write(uint8_t i2c_addr, uint8_t reg_addr, uint8_t *reg_data, uint16_t length)
{
if (i2c_write(reg_addr, reg_data, length))
return BMP280_OK;
else
return BMP280_E_COMM_FAIL;
}
int8_t i2c_reg_read(uint8_t i2c_addr, uint8_t reg_addr, uint8_t *reg_data, uint16_t length)
{
i2c_read(reg_addr, reg_data, length);
return BMP280_OK;
}
void print_rslt(const char api_name[], int8_t rslt)
{
if (rslt != BMP280_OK)
{
printf("%s\t", api_name);
if (rslt == BMP280_E_NULL_PTR)
{
printf("Error [%d] : Null pointer error\r\n", rslt);
}
else if (rslt == BMP280_E_COMM_FAIL)
{
printf("Error [%d] : Bus communication failed\r\n", rslt);
}
else if (rslt == BMP280_E_IMPLAUS_TEMP)
{
printf("Error [%d] : Invalid Temperature\r\n", rslt);
}
else if (rslt == BMP280_E_DEV_NOT_FOUND)
{
printf("Error [%d] : Device not found\r\n", rslt);
}
else
{
/* For more error codes refer "*_defs.h" */
printf("Error [%d] : Unknown error code\r\n", rslt);
}
}
}
#else
BMP3_INTF_RET_TYPE bmp3_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr)
{
i2c_read(reg_addr, reg_data, len);
return BMP3_INTF_RET_SUCCESS;
}
BMP3_INTF_RET_TYPE bmp3_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr)
{
if (i2c_write(reg_addr, reg_data, len))
return BMP3_INTF_RET_SUCCESS;
else
return BMP3_E_COMM_FAIL;
}
void bmp3_delay_us(uint32_t period, void *intf_ptr)
{
usleep(period);
}
void bmp3_check_rslt(const char api_name[], int8_t rslt)
{
switch (rslt)
{
case BMP3_OK:
/* Do nothing */
break;
case BMP3_E_NULL_PTR:
printf("API [%s] Error [%d] : Null pointer\r\n", api_name, rslt);
break;
case BMP3_E_COMM_FAIL:
printf("API [%s] Error [%d] : Communication failure\r\n", api_name, rslt);
break;
case BMP3_E_INVALID_LEN:
printf("API [%s] Error [%d] : Incorrect length parameter\r\n", api_name, rslt);
break;
case BMP3_E_DEV_NOT_FOUND:
printf("API [%s] Error [%d] : Device not found\r\n", api_name, rslt);
break;
case BMP3_E_CONFIGURATION_ERR:
printf("API [%s] Error [%d] : Configuration Error\r\n", api_name, rslt);
break;
case BMP3_W_SENSOR_NOT_ENABLED:
printf("API [%s] Error [%d] : Warning when Sensor not enabled\r\n", api_name, rslt);
break;
case BMP3_W_INVALID_FIFO_REQ_FRAME_CNT:
printf("API [%s] Error [%d] : Warning when Fifo watermark level is not in limit\r\n", api_name, rslt);
break;
default:
printf("API [%s] Error [%d] : Unknown error code\r\n", api_name, rslt);
break;
}
}
#endif
05-13-2021 04:29 AM
Hello ans,
In your bmp3_i2c_read(), bmp3_i2c_write() functions, they didn't use BMP388 I2C device address. I2C communication couldn't match correctly.
This is my reference code for your reference.
uint8_t GTXBuffer[256];
int8_t SensorAPI_I2Cx_Read(uint8_t subaddress, uint8_t *pBuffer, uint16_t ReadNumbr, void *intf_ptr)
{
uint8_t dev_addr = *(uint8_t*)intf_ptr;
uint16_t DevAddress = dev_addr << 1;
// send register address
HAL_I2C_Master_Transmit(&I2C_HANDLE, DevAddress, &subaddress, 1, BUS_TIMEOUT);
HAL_I2C_Master_Receive(&I2C_HANDLE, DevAddress, pBuffer, ReadNumbr, BUS_TIMEOUT);
return 0;
}
int8_t SensorAPI_I2Cx_Write(uint8_t subaddress, uint8_t *pBuffer, uint16_t WriteNumbr, void *intf_ptr)
{
uint8_t dev_addr = *(uint8_t*)intf_ptr;
uint16_t DevAddress = dev_addr << 1;
GTXBuffer[0] = subaddress;
memcpy(>XBuffer[1], pBuffer, WriteNumbr);
// send register address
HAL_I2C_Master_Transmit(&I2C_HANDLE, DevAddress, GTXBuffer, WriteNumbr+1, BUS_TIMEOUT);
return 0;
}
05-13-2021 08:04 AM
The i2c read and write functions use the I2C device address implicitly by the global file descriptor fd_i2c. The descriptor is opened in i2c_interface_init and bound to the I2C device address by a call to ioctl. This means all transfer with this file descriptor automatically goes to the device with this address. I have checked that your code
uint8_t dev_addr = *(uint8_t*)intf_ptr;
reproduces the device address 0x77 to which the file descriptor is already bound.
The same approach works flawlessly with the BMP280 library.
05-13-2021 08:52 AM
Hello ans,
If it's convenient for you to debug the program, you can see where the error is.
If it is not convenient to debug the program, you can use the logic analyzer to capture the I2C waveform to check.
05-14-2021 07:00 PM - edited 05-14-2021 07:03 PM
Ensuring data ready by
bool is_ready = false;
while (!is_ready) {
rslt = bmp3_get_status(&dev);
bmp3_check_rslt("bmp3_get_status", rslt);
is_ready = dev.status.sensor.drdy_press && dev.status.sensor.drdy_temp;
printf("ready: %d %d\n", dev.status.sensor.drdy_press, dev.status.sensor.drdy_temp);
usleep(2000);
}
before actually reading it I see that the sensor is never ready. It seems the sensor is not entering normal mode. I'm now trying to enter normal mode via
dev.settings.op_mode = BMP3_MODE_NORMAL;
rslt = bmp3_set_op_mode(&dev);
Is that not the correct approach? Unfortunately, a documentation is lacking. I notice that bmp3_set_op_mode in the library on Github is different from the version in COINES in the respect that in the former bmp3_set_op_mode does not accept an argument which mode to enter but reads dev->settings.op_mode, while in the latter bmp3_set_op_mode does have such an argument.