#include "bme280.h" #include "hardware/i2c.h" #include "pico/binary_info.h" #include "pico/stdlib.h" #include #include /* Lib for Bosh BME280 Env Sensor. To be used in weather station context. Sources: - pico example for bmp280 a simpler sensor then bme280 that does not include humidity https://github.com/raspberrypi/pico-examples/blob/master/i2c/bmp280_i2c/bmp280_i2c.c - bme280 datasheet https://cdn.shopify.com/s/files/1/0174/1800/files/bst-bme280-ds002.pdf?v=1662743150 - bmp280 datasheet https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf - pimoroni eshop https://shop.pimoroni.com/products/bme280-breakout?variant=29420960677971 - pimoroni pico sdk https://github.com/boschsensortec/BME280_SensorAPI/tree/c47f06eb44fc96970f0abfcc941ec16425b2a9e6 */ // Define the custom pins for I2C #define I2C_SDA_PIN 14 // Replace with your custom SDA pin #define I2C_SCL_PIN 15 // Replace with your custom SCL pin // Define the I2C instance to use (i2c0 or i2c1) #define I2C_PORT i2c1 // device has default bus address of 0x76 #define ADDR _u(0x76) // hardware registers #define REG_CONFIG _u(0xF5) #define REG_CTRL_MEAS _u(0xF4) // #define REG_RESET _u(0xE0) // #define REG_TEMP_XLSB _u(0xFC) // #define REG_TEMP_LSB _u(0xFB) // #define REG_TEMP_MSB _u(0xFA) // #define REG_PRESSURE_XLSB _u(0xF9) // #define REG_PRESSURE_LSB _u(0xF8) #define REG_PRESSURE_MSB _u(0xF7) // calibration registers #define REG_DIG_T1_LSB _u(0x88) // #define REG_DIG_T1_MSB _u(0x89) // #define REG_DIG_T2_LSB _u(0x8A) // #define REG_DIG_T2_MSB _u(0x8B) // #define REG_DIG_T3_LSB _u(0x8C) // #define REG_DIG_T3_MSB _u(0x8D) // #define REG_DIG_P1_LSB _u(0x8E) // #define REG_DIG_P1_MSB _u(0x8F) // #define REG_DIG_P2_LSB _u(0x90) // #define REG_DIG_P2_MSB _u(0x91) // #define REG_DIG_P3_LSB _u(0x92) // #define REG_DIG_P3_MSB _u(0x93) // #define REG_DIG_P4_LSB _u(0x94) // #define REG_DIG_P4_MSB _u(0x95) // #define REG_DIG_P5_LSB _u(0x96) // #define REG_DIG_P5_MSB _u(0x97) // #define REG_DIG_P6_LSB _u(0x98) // #define REG_DIG_P6_MSB _u(0x99) // #define REG_DIG_P7_LSB _u(0x9A) // #define REG_DIG_P7_MSB _u(0x9B) // #define REG_DIG_P8_LSB _u(0x9C) // #define REG_DIG_P8_MSB _u(0x9D) // #define REG_DIG_P9_LSB _u(0x9E) // #define REG_DIG_P9_MSB _u(0x9F) // number of calibration registers to be read // 17 * 2 = 34 #define NUM_COMPENSATION_PARAMS 34 static void bmp280_get_compensation_params(i2c_inst_t *i2c, bme280_compensation_params *params) { // raw temp and pressure values need to be calibrated according to // parameters generated during the manufacturing of the sensor // there are 3 temperature params, and 9 pressure params, each with a LSB // and MSB register, so we read from 24 registers uint8_t buf[NUM_COMPENSATION_PARAMS] = {0}; uint8_t reg = REG_DIG_T1_LSB; i2c_write_blocking(i2c, ADDR, ®, 1, true); // true to keep master control of bus // read in one go as register addresses auto-increment i2c_read_blocking(i2c, ADDR, buf, NUM_COMPENSATION_PARAMS, false); // false, we're done reading // store these in a struct for later use params->dig_t1 = (uint16_t)(buf[1] << 8) | buf[0]; params->dig_t2 = (int16_t)(buf[3] << 8) | buf[2]; params->dig_t3 = (int16_t)(buf[5] << 8) | buf[4]; params->dig_p1 = (uint16_t)(buf[7] << 8) | buf[6]; params->dig_p2 = (int16_t)(buf[9] << 8) | buf[8]; params->dig_p3 = (int16_t)(buf[11] << 8) | buf[10]; params->dig_p4 = (int16_t)(buf[13] << 8) | buf[12]; params->dig_p5 = (int16_t)(buf[15] << 8) | buf[14]; params->dig_p6 = (int16_t)(buf[17] << 8) | buf[16]; params->dig_p7 = (int16_t)(buf[19] << 8) | buf[18]; params->dig_p8 = (int16_t)(buf[21] << 8) | buf[20]; params->dig_p9 = (int16_t)(buf[23] << 8) | buf[22]; params->dig_h1 = (int16_t)(buf[25] << 8) | buf[24]; params->dig_h2 = (int16_t)(buf[27] << 8) | buf[26]; params->dig_h3 = (int16_t)(buf[29] << 8) | buf[28]; params->dig_h4 = (int16_t)(buf[31] << 8) | buf[30]; params->dig_h5 = (int16_t)(buf[33] << 8) | buf[32]; } void bme280_init(bme280_config *config, i2c_inst_t *i2c, uint8_t sda_pin, uint8_t scl_pin) { i2c_init(i2c, 100 * 1000); gpio_set_function(sda_pin, GPIO_FUNC_I2C); gpio_set_function(scl_pin, GPIO_FUNC_I2C); gpio_pull_up(sda_pin); gpio_pull_up(scl_pin); // use the "handheld device dynamic" optimal setting (see datasheet) // bi_decl(bi_2pins_with_func(sda_pin, scl_pin, GPIO_FUNC_I2C)); // bi_decl(bi_program_description("BMP280 I2C LIB")); uint8_t buf[2]; // 500ms sampling time, x16 filter const uint8_t reg_config_val = ((0x04 << 5) | (0x05 << 2)) & 0xFC; // send register number followed by its corresponding value buf[0] = REG_CONFIG; buf[1] = reg_config_val; i2c_write_blocking(i2c, ADDR, buf, 2, false); // osrs_t x1, osrs_p x4, normal mode operation const uint8_t reg_ctrl_meas_val = (0x01 << 5) | (0x03 << 2) | (0x03); buf[0] = REG_CTRL_MEAS; buf[1] = reg_ctrl_meas_val; i2c_write_blocking(i2c, ADDR, buf, 2, false); bme280_compensation_params params; bmp280_get_compensation_params(i2c, ¶ms); config->params = params; config->i2c = i2c; return; } static void bmp280_read_raw(bme280_config *config, int32_t* temp, int32_t* pressure, int32_t* humidity) { // BMP280 data registers are auto-incrementing and we have 3 temperature and // pressure registers each, so we start at 0xF7 and read 6 bytes to 0xFC // note: normal mode does not require further ctrl_meas and config register writes uint8_t buf[6]; uint8_t reg = REG_PRESSURE_MSB; i2c_write_blocking(config->i2c, ADDR, ®, 1, true); // true to keep master control of bus i2c_read_blocking(config->i2c, ADDR, buf, 6, false); // false - finished with bus // store the 20 bit read in a 32 bit signed integer for conversion *pressure = (buf[0] << 12) | (buf[1] << 4) | (buf[2] >> 4); *temp = (buf[3] << 12) | (buf[4] << 4) | (buf[5] >> 4); } bme280_reading bme280_read(bme280_config *config) { int32_t raw_temperature = 0; int32_t raw_pressure = 0; int32_t raw_humidity = 0; bmp280_read_raw(config, &raw_temperature, &raw_pressure, &raw_humidity); bme280_reading reading; reading.temperature = raw_temperature; reading.pressure = raw_pressure; reading.humidity = raw_humidity; return reading; }