nRF5340 Audio DK with BMP280 sensor

Hello,

I am fairly new to the development with nRF and am having problems getting sensor data from a connected BMP280 to the nRF5340 audio dk.

The first thing I did was look at the sample for the BME280 (unfortunately there is no pre-made sample for the BMP280 as far as I know). I thought I could rewrite it to work with the BMP280, but it didn't find the device. Then I tried to repeate exercise 2 from "Lesson 6 - Serial Communication (I2C)" and adapt it to the BMP280 datasheet. Is this the best way to get sensor data from the BMP280? I've also found this BMP2 SensorAPI https://github.com/BoschSensortec/BMP2-Sensor-API/#sensor-overview, but I haven't figured out how to use it in nRF yet. What is the most elegant and easiest way to get sensor data from the BMP280?

My development setup:

OS: macOS Sonoma 14.0

SW versions: VS Code 1.84.1, newest nRF Connect for VS Code extension pack

Best regards,

Lukas

Parents
  • Hi,

    Could I see what you have so far, so that I can see if it's just something small that is missing?

    Have you made sure to use the correct address?

    A logic trace would also be nice if possible, to see if the correct data is being sent from the board.

  • Thanks for your reply! 

    This is the error message I am getting in Serial Terminal with my current code:

    *** Booting nRF Connect SDK v2.5.0-rc2 ***
    Found device "I2C_1", getting sensor data
    [00:00:02.254,089] <err> os: ***** USAGE FAULT *****
    [00:00:02.254,089] <err> os: Attempt to execute undefined instruction
    [00:00:02.254,119] <err> os: r0/a1: 0x200022e8 r1/a2: 0x00000000 r2/a3: 0x00000004
    [00:00:02.254,119] <err> os: r3/a4: 0x00008cfb r12/ip: 0x0000000c r14/lr: 0x000082bb
    [00:00:02.254,119] <err> os: xpsr: 0x49000000
    [00:00:02.254,150] <err> os: Faulting instruction address (r15/pc): 0x000082ac
    [00:00:02.254,180] <err> os: >>> ZEPHYR FATAL ERROR 36: Unknown error on CPU 0
    [00:00:02.254,211] <err> os: Current thread: 0x200007f8 (unknown)
    [00:00:00.753,509] <err> i2c_nrfx_twim: Error on I2C line occurred for message 0
    [00:00:00.753,570] <err> INA230: Failed to write configuration register!
    [00:00:01.253,662] <err> i2c_nrfx_twim: Error on I2C line occurred for message 0
    [00:00:01.253,692] <err> INA230: Failed to write configuration register!
    [00:00:01.753,814] <err> i2c_nrfx_twim: Error on I2C line occurred for message 0
    [00:00:01.753,845] <err> INA230: Failed to write configuration register!
    [00:00:02.253,936] <err> i2c_nrfx_twim: Error on I2C line occurred for message 0
    [00:00:02.253,997] <err> INA230: Failed to write configuration register!

    and this is my code in main.c:

    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/sensor.h>
    #include <zephyr/drivers/i2c.h>
    
    #define I2C_NODE DT_NODELABEL(bmp280)
    
    // TODO: This information typically goes into a separate header file
    #define BMP280_I2C_ADDR 								0x76 // BMP280 I2C address, 0x77 if ADDR pin is high
    
    #define BMP280_REG_CTRL_MEAS 							0xF4
    #define BMP280_REG_CONFIG 								0xF5
    #define BMP280_REG_PRESS_MSB 							0xF7
    #define BMP280_REG_TEMP_MSB 							0xFA
    
    static const struct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(I2C_NODE);
    
    void bmp280_init() {
        if (dev_i2c.bus == NULL) {
    		/* No such node, or the node does not have status "okay". */
    		printk("\nError: no device found.\n");
    		return;
        }
    
    	if (!device_is_ready(dev_i2c.bus)) {
    		printk("I2C bus %s is not ready!\n\r", dev_i2c.bus->name);
    		return;
    	}
    
    	printk("Found device \"%s\", getting sensor data\n", dev_i2c.bus->name);
    }
    
    void bmp280_read_calibration_data(uint16_t *calib_data) {
        uint8_t calib[24];
    
        if (i2c_burst_read(&dev_i2c, BMP280_I2C_ADDR, 0x88, calib, sizeof(calib)) < 0) {
            printk("Failed to read calibration data\n");
            return;
        }
    
        // Parse calibration data (16-bit values)
        for (int i = 0; i < 12; i++) {
            calib_data[i] = (calib[2 * i] << 8) | calib[2 * i + 1];
        }
    }
    
    void bmp280_configure() {
        uint8_t ctrl_meas = 0x57; // Temperature oversampling x1, Pressure oversampling x16, Normal mode
        uint8_t config = 0x00;    // Filter off, 3-wire SPI disabled
    
        if (i2c_reg_write_byte(&dev_i2c, BMP280_I2C_ADDR, BMP280_REG_CTRL_MEAS, ctrl_meas) < 0) {
            printk("Failed to configure BMP280 control measurement\n");
            return;
        }
    
        if (i2c_reg_write_byte(&dev_i2c, BMP280_I2C_ADDR, BMP280_REG_CONFIG, config) < 0) {
            printk("Failed to configure BMP280 config\n");
            return;
        }
    }
    
    void bmp280_read_data(int16_t *temperature, uint32_t *pressure) {
        uint8_t data[6];
    
        if (i2c_burst_read(&dev_i2c, BMP280_I2C_ADDR, BMP280_REG_TEMP_MSB, data, sizeof(data)) < 0) {
            printk("Failed to read BMP280 data\n");
            return;
        }
    
        int32_t adc_temp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
        int32_t t_fine = 0; // I need to calculate t_fine using calibration data, right?
    
        *temperature = ((t_fine * 5 + 128) >> 8) / 100;
        *pressure = 0; // I need to calculate pressure using calibration data, right?
    }
    
    int main(void) {
        int16_t temperature;
        uint32_t pressure;
        uint16_t calib_data[12];
    
        bmp280_init();
        bmp280_read_calibration_data(calib_data);
        bmp280_configure();
    
        while (1) {
            bmp280_read_data(&temperature, &pressure);
            printk("Temperature: %d°C, Pressure: %u Pa\n", temperature, pressure);
            k_sleep(K_MSEC(1000)); // Sleep for 1 second
        }
    }

    overlay file:

    &i2c1 {
    	status = "okay";
    	compatible = "nordic,nrf-twim";
    	label = "I2C_1";
    	pinctrl-0 = <&i2c1_default>;
    	pinctrl-1 = <&i2c1_sleep>;
    	pinctrl-names = "default", "sleep";
    	clock-frequency = <100000>; 
    	bmp280: bmp280@76 {
    		compatible = "i2c-device";
    		reg = < 0x76 >;
    		label = "BMP280";
    	};
    };
    
    &pinctrl {
    	i2c1_default: i2c1_default{
    		group1 {
    			psels = <NRF_PSEL(TWIM_SDA, 0, 5)>,
    				<NRF_PSEL(TWIM_SCL, 0, 4)>;
    		};
    	};
    
    	i2c1_sleep: i2c1_sleep{
    		group1 {
    			psels = <NRF_PSEL(TWIM_SDA, 0, 5)>,
    				<NRF_PSEL(TWIM_SCL, 0, 4)>;
    			low-power-enable;
    		};
    	};
    
    };
    

    Here is the datasheet for the BMP280: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf

    It seems like the SensorAPI might be useful to get it working, but I haven't figured out how to embed and use it in nRF correctly. 

    BMP280 seems to be on the market for quite some time, but I couldn't find any useful resources to get sensor data from it in nRF. Maybe I missed something?

    I'd appreciate any kind of help!

    Best regards,

    Lukas

Reply
  • Thanks for your reply! 

    This is the error message I am getting in Serial Terminal with my current code:

    *** Booting nRF Connect SDK v2.5.0-rc2 ***
    Found device "I2C_1", getting sensor data
    [00:00:02.254,089] <err> os: ***** USAGE FAULT *****
    [00:00:02.254,089] <err> os: Attempt to execute undefined instruction
    [00:00:02.254,119] <err> os: r0/a1: 0x200022e8 r1/a2: 0x00000000 r2/a3: 0x00000004
    [00:00:02.254,119] <err> os: r3/a4: 0x00008cfb r12/ip: 0x0000000c r14/lr: 0x000082bb
    [00:00:02.254,119] <err> os: xpsr: 0x49000000
    [00:00:02.254,150] <err> os: Faulting instruction address (r15/pc): 0x000082ac
    [00:00:02.254,180] <err> os: >>> ZEPHYR FATAL ERROR 36: Unknown error on CPU 0
    [00:00:02.254,211] <err> os: Current thread: 0x200007f8 (unknown)
    [00:00:00.753,509] <err> i2c_nrfx_twim: Error on I2C line occurred for message 0
    [00:00:00.753,570] <err> INA230: Failed to write configuration register!
    [00:00:01.253,662] <err> i2c_nrfx_twim: Error on I2C line occurred for message 0
    [00:00:01.253,692] <err> INA230: Failed to write configuration register!
    [00:00:01.753,814] <err> i2c_nrfx_twim: Error on I2C line occurred for message 0
    [00:00:01.753,845] <err> INA230: Failed to write configuration register!
    [00:00:02.253,936] <err> i2c_nrfx_twim: Error on I2C line occurred for message 0
    [00:00:02.253,997] <err> INA230: Failed to write configuration register!

    and this is my code in main.c:

    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/drivers/sensor.h>
    #include <zephyr/drivers/i2c.h>
    
    #define I2C_NODE DT_NODELABEL(bmp280)
    
    // TODO: This information typically goes into a separate header file
    #define BMP280_I2C_ADDR 								0x76 // BMP280 I2C address, 0x77 if ADDR pin is high
    
    #define BMP280_REG_CTRL_MEAS 							0xF4
    #define BMP280_REG_CONFIG 								0xF5
    #define BMP280_REG_PRESS_MSB 							0xF7
    #define BMP280_REG_TEMP_MSB 							0xFA
    
    static const struct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(I2C_NODE);
    
    void bmp280_init() {
        if (dev_i2c.bus == NULL) {
    		/* No such node, or the node does not have status "okay". */
    		printk("\nError: no device found.\n");
    		return;
        }
    
    	if (!device_is_ready(dev_i2c.bus)) {
    		printk("I2C bus %s is not ready!\n\r", dev_i2c.bus->name);
    		return;
    	}
    
    	printk("Found device \"%s\", getting sensor data\n", dev_i2c.bus->name);
    }
    
    void bmp280_read_calibration_data(uint16_t *calib_data) {
        uint8_t calib[24];
    
        if (i2c_burst_read(&dev_i2c, BMP280_I2C_ADDR, 0x88, calib, sizeof(calib)) < 0) {
            printk("Failed to read calibration data\n");
            return;
        }
    
        // Parse calibration data (16-bit values)
        for (int i = 0; i < 12; i++) {
            calib_data[i] = (calib[2 * i] << 8) | calib[2 * i + 1];
        }
    }
    
    void bmp280_configure() {
        uint8_t ctrl_meas = 0x57; // Temperature oversampling x1, Pressure oversampling x16, Normal mode
        uint8_t config = 0x00;    // Filter off, 3-wire SPI disabled
    
        if (i2c_reg_write_byte(&dev_i2c, BMP280_I2C_ADDR, BMP280_REG_CTRL_MEAS, ctrl_meas) < 0) {
            printk("Failed to configure BMP280 control measurement\n");
            return;
        }
    
        if (i2c_reg_write_byte(&dev_i2c, BMP280_I2C_ADDR, BMP280_REG_CONFIG, config) < 0) {
            printk("Failed to configure BMP280 config\n");
            return;
        }
    }
    
    void bmp280_read_data(int16_t *temperature, uint32_t *pressure) {
        uint8_t data[6];
    
        if (i2c_burst_read(&dev_i2c, BMP280_I2C_ADDR, BMP280_REG_TEMP_MSB, data, sizeof(data)) < 0) {
            printk("Failed to read BMP280 data\n");
            return;
        }
    
        int32_t adc_temp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
        int32_t t_fine = 0; // I need to calculate t_fine using calibration data, right?
    
        *temperature = ((t_fine * 5 + 128) >> 8) / 100;
        *pressure = 0; // I need to calculate pressure using calibration data, right?
    }
    
    int main(void) {
        int16_t temperature;
        uint32_t pressure;
        uint16_t calib_data[12];
    
        bmp280_init();
        bmp280_read_calibration_data(calib_data);
        bmp280_configure();
    
        while (1) {
            bmp280_read_data(&temperature, &pressure);
            printk("Temperature: %d°C, Pressure: %u Pa\n", temperature, pressure);
            k_sleep(K_MSEC(1000)); // Sleep for 1 second
        }
    }

    overlay file:

    &i2c1 {
    	status = "okay";
    	compatible = "nordic,nrf-twim";
    	label = "I2C_1";
    	pinctrl-0 = <&i2c1_default>;
    	pinctrl-1 = <&i2c1_sleep>;
    	pinctrl-names = "default", "sleep";
    	clock-frequency = <100000>; 
    	bmp280: bmp280@76 {
    		compatible = "i2c-device";
    		reg = < 0x76 >;
    		label = "BMP280";
    	};
    };
    
    &pinctrl {
    	i2c1_default: i2c1_default{
    		group1 {
    			psels = <NRF_PSEL(TWIM_SDA, 0, 5)>,
    				<NRF_PSEL(TWIM_SCL, 0, 4)>;
    		};
    	};
    
    	i2c1_sleep: i2c1_sleep{
    		group1 {
    			psels = <NRF_PSEL(TWIM_SDA, 0, 5)>,
    				<NRF_PSEL(TWIM_SCL, 0, 4)>;
    			low-power-enable;
    		};
    	};
    
    };
    

    Here is the datasheet for the BMP280: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf

    It seems like the SensorAPI might be useful to get it working, but I haven't figured out how to embed and use it in nRF correctly. 

    BMP280 seems to be on the market for quite some time, but I couldn't find any useful resources to get sensor data from it in nRF. Maybe I missed something?

    I'd appreciate any kind of help!

    Best regards,

    Lukas

Children
Related