BME280 on 2 i2c ports

goossensbas
Posts: 2
Joined: Thu Apr 20, 2023 6:30 pm

BME280 on 2 i2c ports

Postby goossensbas » Sat Jun 10, 2023 4:43 am

Hello,

I am trying to learn esp-idf by using it for my first project.
I tested several components separately and prepared some example code to learn the esp-idf environment.

I am now starting on my first project, a greenhouse monitor, which will have 2 BME280 sensors and a light sensor.

The BME280 sensors i bought are a waterproof model which don't have an address pin, so i have to use both I2C ports to read the sensors.

I made a working example before with 1 sensor based on the code in this tutorial:
https://esp32tutorials.com/bme280-esp32-esp-idf-oled/

What i can't figure out is how to read the data from both sensors.

I adapted the code to have 2 init functions, each with a different i2c port (I2C_NUM_0 and I2C_NUM_1)
I also made 2 read and 2 write functions, each attached to the i2C ports.

In the task i created 2 bme280_t structs that contain the different functions pointed to the different i2C ports.

Now i am stuck on reading the values of the separate sensors, as there is no option in the bme280 read functions to address a specific sensor.

Can anyone help me with this issue?
I post my code here, but it's not finished:

Code: Select all

#include <stdio.h>
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/task.h"
#include "sdkconfig.h" // generated by "make menuconfig"
#include "bme280.h"

#define SDA1_PIN GPIO_NUM_16
#define SCL1_PIN GPIO_NUM_17
#define SDA2_PIN GPIO_NUM_21
#define SCL2_PIN GPIO_NUM_22

#define TAG_BME280_INDOOR "BME280 indoor"
#define TAG_BME280_OUTDOOR "BME280 outdoor"
#define TAG_LUXMETER "LUXmeter"
#define I2C_MASTER_ACK 0
#define I2C_MASTER_NACK 1


void i2c1_init()
{
    i2c_config_t i2c_config = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = SDA1_PIN,
        .scl_io_num = SCL1_PIN,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = 1000000};
    i2c_param_config(I2C_NUM_0, &i2c_config);
    i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);
}
void i2c2_init()
{
    i2c_config_t i2c_config = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = SDA2_PIN,
        .scl_io_num = SCL2_PIN,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = 1000000};
    i2c_param_config(I2C_NUM_1, &i2c_config);
    i2c_driver_install(I2C_NUM_1, I2C_MODE_MASTER, 0, 0, 0);
}

s8_t BME280_I2C_bus1_write(u8_t dev_addr, u8_t reg_addr, u8_t *reg_data, u8_t cnt)
{
    s32 iError = BME280_INIT_VALUE;

    esp_err_t espRc;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);

    i2c_master_write_byte(cmd, reg_addr, true);
    i2c_master_write(cmd, reg_data, cnt, true);
    i2c_master_stop(cmd);

    espRc = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS);
    if (espRc == ESP_OK)
    {
        iError = SUCCESS;
    }
    else
    {
        iError = FAIL;
    }
    i2c_cmd_link_delete(cmd);

    return (s8)iError;
}
s8_t BME280_I2C_bus2_write(u8_t dev_addr, u8_t reg_addr, u8_t *reg_data, u8_t cnt)
{
    s32 iError = BME280_INIT_VALUE;

    esp_err_t espRc;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);

    i2c_master_write_byte(cmd, reg_addr, true);
    i2c_master_write(cmd, reg_data, cnt, true);
    i2c_master_stop(cmd);

    espRc = i2c_master_cmd_begin(I2C_NUM_1, cmd, 10 / portTICK_PERIOD_MS);
    if (espRc == ESP_OK)
    {
        iError = SUCCESS;
    }
    else
    {
        iError = FAIL;
    }
    i2c_cmd_link_delete(cmd);

    return (s8)iError;
}

s8 BME280_I2C_bus1_read(u8_t dev_addr, u8_t reg_addr, u8_t *reg_data, u8_t cnt)
{
    s32 iError = BME280_INIT_VALUE;
    esp_err_t espRc;

    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg_addr, true);

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_READ, true);

    if (cnt > 1)
    {
        i2c_master_read(cmd, reg_data, cnt - 1, I2C_MASTER_ACK);
    }
    i2c_master_read_byte(cmd, reg_data + cnt - 1, I2C_MASTER_NACK);
    i2c_master_stop(cmd);

    espRc = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS);
    if (espRc == ESP_OK)
    {
        iError = SUCCESS;
    }
    else
    {
        iError = FAIL;
    }

    i2c_cmd_link_delete(cmd);

    return (s8)iError;
}

s8 BME280_I2C_bus2_read(u8_t dev_addr, u8_t reg_addr, u8_t *reg_data, u8_t cnt)
{
    s32 iError = BME280_INIT_VALUE;
    esp_err_t espRc;

    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg_addr, true);

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (dev_addr << 1) | I2C_MASTER_READ, true);

    if (cnt > 1)
    {
        i2c_master_read(cmd, reg_data, cnt - 1, I2C_MASTER_ACK);
    }
    i2c_master_read_byte(cmd, reg_data + cnt - 1, I2C_MASTER_NACK);
    i2c_master_stop(cmd);

    espRc = i2c_master_cmd_begin(I2C_NUM_1, cmd, 10 / portTICK_PERIOD_MS);
    if (espRc == ESP_OK)
    {
        iError = SUCCESS;
    }
    else
    {
        iError = FAIL;
    }

    i2c_cmd_link_delete(cmd);

    return (s8)iError;
}

void BME280_delay_msek(u32_t msek)
{
    vTaskDelay(msek / portTICK_PERIOD_MS);
}

void bme_read(void *arg){
    struct bme280_t bme280_indoor = {
        .bus_write = BME280_I2C_bus1_write,
        .bus_read = BME280_I2C_bus1_read,
        .dev_addr = BME280_I2C_ADDRESS1,
        .delay_msec = BME280_delay_msek};
    struct bme280_t bme280_outdoor = {
        .bus_write = BME280_I2C_bus2_write,
        .bus_read = BME280_I2C_bus2_read,
        .dev_addr = BME280_I2C_ADDRESS1,
        .delay_msec = BME280_delay_msek};

     MDF_LOGI("BME task is running");

    s32 bme1_rslt;
    s32 bme2_rslt;
    s32 v_uncomp_pressure_s32;
    s32 v_uncomp_temperature_s32;
    s32 v_uncomp_humidity_s32;

    bme1_rslt = bme280_init(&bme280_indoor);
    bme2_rslt = bme280_init(&bme280_outdoor);

    bme1_rslt += bme280_set_oversamp_pressure(BME280_OVERSAMP_16X);
    bme2_rslt += bme280_set_oversamp_pressure(BME280_OVERSAMP_16X);
    bme1_rslt += bme280_set_oversamp_temperature(BME280_OVERSAMP_2X);
    bme2_rslt += bme280_set_oversamp_temperature(BME280_OVERSAMP_2X);
    bme1_rslt += bme280_set_oversamp_humidity(BME280_OVERSAMP_1X);
    bme2_rslt += bme280_set_oversamp_humidity(BME280_OVERSAMP_1X);

    bme1_rslt += bme280_set_standby_durn(BME280_STANDBY_TIME_1_MS);
    bme2_rslt += bme280_set_standby_durn(BME280_STANDBY_TIME_1_MS);
    bme1_rslt += bme280_set_filter(BME280_FILTER_COEFF_16);
    bme2_rslt += bme280_set_filter(BME280_FILTER_COEFF_16);

    bme1_rslt += bme280_set_power_mode(BME280_NORMAL_MODE); 
    bme2_rslt += bme280_set_power_mode(BME280_NORMAL_MODE); 
    if (com_rslt == SUCCESS)
    {
        bme1_rslt = bme280_read_uncomp_pressure_temperature_humidity(
                &v_uncomp_pressure_s32, &v_uncomp_temperature_s32, &v_uncomp_humidity_s32);
        bme2_rslt = bme280_read_uncomp_pressure_temperature_humidity(
                &v_uncomp_pressure_s32, &v_uncomp_temperature_s32, &v_uncomp_humidity_s32);
    }
}

MicroController
Posts: 1709
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: BME280 on 2 i2c ports

Postby MicroController » Sun Jun 11, 2023 1:38 pm

Apparently, the original BME280 "driver" only supports a single sensor; evidenced by the fact that it internally holds exactly one bme280_t pointer.
bme280_init() sets this internal pointer but also initializes the sensor, which you may or may not want to do every time you switch from one sensor to the other.

So, one option is of course to modify the driver code to your needs, if you want go there.
The modification may be as simple as removing the "static" from "static struct bme280_t *p_bme280; /**< pointer to BME280 */" in bme280.c. Then you can access the pointer from your code

Code: Select all

extern struct bme280_t* p_bme280; // Make the BME280 driver's internal pointer visible to our code.

struct bme280_t sensor1 = {...};
struct bme280_t sensor2 = {...};

void init() {
  bme280_init(&sensor1);
  bme280_init(&sensor2);
}

void switch_to_sensor1() {
  p_bme280 = &sensor1;
}

void switch_to_sensor2() {
  p_bme280 = &sensor2;
}
and call the corresponding switch function before accessing a sensor.

Another option:
The driver holds on to the bme280_t pointer you provide to bme280_init and uses this exact pointer for all other operations.
So you could swap the content of the initial bme280_t with another one to switch the sensor.

Code: Select all

// Holds the calibration/configuration of the current sensor to access:
struct bme280_t current_sensor;

void init_bme_sensors() {

  // Copy sensor1 contents to current_sensor
  current_sensor = sensor1;

  // Init sensor1
  bme280_init(&current_sensor);

  // Copy initialized data back from current_sensor to sensor1
  sensor1 = current_sensor;

  // Repeat for sensor #2
  current_sensor = sensor2;

  bme280_init(&current_sensor);

  sensor2 = current_sensor;

}

void do_something_with_sensor(struct bme280_t* sensor) {
  current_sensor = *sensor;
  bme280_do_something(...);
  // If "something" may have altered current_sensor's contents, copy back:
  // *sensor = current_sensor;
}

void measure_both() {
  do_something_with_sensor(&sensor1);
  do_something_with_sensor(&sensor2);
}
[x] quick
[x] dirty
[x] might work

Who is online

Users browsing this forum: Bing [Bot], Majestic-12 [Bot] and 87 guests