From f0eff195fea0279639a13c22194c8b4735b60d14 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 14 May 2021 12:39:46 +0100 Subject: [PATCH] C++ SDK Support for BME280 and BMP280 --- .gitmodules | 7 ++ drivers/CMakeLists.txt | 2 + drivers/bme280/CMakeLists.txt | 1 + drivers/bme280/bme280.cmake | 16 +++++ drivers/bme280/bme280.cpp | 75 +++++++++++++++++++ drivers/bme280/bme280.hpp | 79 +++++++++++++++++++++ drivers/bme280/src | 1 + drivers/bmp280/CMakeLists.txt | 1 + drivers/bmp280/bmp280.cmake | 12 ++++ drivers/bmp280/bmp280.cpp | 60 ++++++++++++++++ drivers/bmp280/bmp280.hpp | 78 ++++++++++++++++++++ drivers/bmp280/src | 1 + examples/CMakeLists.txt | 2 + examples/breakout_bme280/CMakeLists.txt | 1 + examples/breakout_bme280/bme280_basic.cmake | 12 ++++ examples/breakout_bme280/bme280_basic.cpp | 31 ++++++++ examples/breakout_bmp280/CMakeLists.txt | 1 + examples/breakout_bmp280/bmp280_basic.cmake | 12 ++++ examples/breakout_bmp280/bmp280_basic.cpp | 31 ++++++++ 19 files changed, 423 insertions(+) create mode 100644 drivers/bme280/CMakeLists.txt create mode 100644 drivers/bme280/bme280.cmake create mode 100644 drivers/bme280/bme280.cpp create mode 100644 drivers/bme280/bme280.hpp create mode 160000 drivers/bme280/src create mode 100644 drivers/bmp280/CMakeLists.txt create mode 100644 drivers/bmp280/bmp280.cmake create mode 100644 drivers/bmp280/bmp280.cpp create mode 100644 drivers/bmp280/bmp280.hpp create mode 160000 drivers/bmp280/src create mode 100644 examples/breakout_bme280/CMakeLists.txt create mode 100644 examples/breakout_bme280/bme280_basic.cmake create mode 100644 examples/breakout_bme280/bme280_basic.cpp create mode 100644 examples/breakout_bmp280/CMakeLists.txt create mode 100644 examples/breakout_bmp280/bmp280_basic.cmake create mode 100644 examples/breakout_bmp280/bmp280_basic.cpp diff --git a/.gitmodules b/.gitmodules index 9e8742e4..07595540 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,10 @@ [submodule "drivers/bme68x-sensor-api/src"] path = drivers/bme68x/src url = https://github.com/BoschSensortec/BME68x-Sensor-API +[submodule "drivers/bme280/src"] + path = drivers/bme280/src + url = https://github.com/BoschSensortec/BME280_driver +[submodule "drivers/bmp280/src"] + path = drivers/bmp280/src + url = https://github.com/pimoroni/BMP280_driver + branch = patch-intf-ptr diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 5fc5eb6b..58a2bbd3 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -15,5 +15,7 @@ add_subdirectory(sdcard) add_subdirectory(as7262) add_subdirectory(bh1745) add_subdirectory(bme68x) +add_subdirectory(bmp280) +add_subdirectory(bme280) add_subdirectory(button) add_subdirectory(rgbled) diff --git a/drivers/bme280/CMakeLists.txt b/drivers/bme280/CMakeLists.txt new file mode 100644 index 00000000..b8d8ac8d --- /dev/null +++ b/drivers/bme280/CMakeLists.txt @@ -0,0 +1 @@ +include(bme280.cmake) \ No newline at end of file diff --git a/drivers/bme280/bme280.cmake b/drivers/bme280/bme280.cmake new file mode 100644 index 00000000..2e09f51c --- /dev/null +++ b/drivers/bme280/bme280.cmake @@ -0,0 +1,16 @@ +set(DRIVER_NAME bme280) +add_library(${DRIVER_NAME} INTERFACE) + +target_sources(${DRIVER_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/bme280.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/bme280.c) + +target_include_directories(${DRIVER_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/src + ${CMAKE_CURRENT_LIST_DIR}) + +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_i2c pimoroni_i2c) + +target_compile_definitions(${DRIVER_NAME} INTERFACE + BME280_FLOAT_ENABLE +) diff --git a/drivers/bme280/bme280.cpp b/drivers/bme280/bme280.cpp new file mode 100644 index 00000000..c98d4a10 --- /dev/null +++ b/drivers/bme280/bme280.cpp @@ -0,0 +1,75 @@ +#include "bme280.hpp" +#include "pico/stdlib.h" + +namespace pimoroni { + bool BME280::init() { + int8_t result; + + if(interrupt != PIN_UNUSED) { + gpio_set_function(interrupt, GPIO_FUNC_SIO); + gpio_set_dir(interrupt, GPIO_IN); + gpio_pull_up(interrupt); + } + + device.intf_ptr = new i2c_intf_ptr{.i2c = i2c, .address = address}; + device.intf = bme280_intf::BME280_I2C_INTF; + device.read = (bme280_read_fptr_t)&read_bytes; + device.write = (bme280_write_fptr_t)&write_bytes; + device.delay_us = (bme280_delay_us_fptr_t)&delay_us; + + result = bme280_init(&device); + if(result != BME280_OK) return false; + + configure(BME280_FILTER_COEFF_2, BME280_STANDBY_TIME_0_5_MS, BME280_OVERSAMPLING_16X, BME280_OVERSAMPLING_2X, BME280_OVERSAMPLING_1X); + + return true; + } + + bool BME280::configure(uint8_t filter, uint8_t standby_time, uint8_t os_pressure, uint8_t os_temp, uint8_t os_humidity, uint8_t mode) { + int8_t result; + + settings.filter = filter; + settings.standby_time = standby_time; + settings.osr_p = os_pressure; + settings.osr_t = os_temp; + settings.osr_h = os_humidity; + + result = bme280_set_sensor_settings(BME280_ALL_SETTINGS_SEL, &device); + if(result != BME280_OK) return false; + + result = bme280_set_sensor_mode(mode, &device); + if(result != BME280_OK) return false; + + return true; + } + + BME280::bme280_reading BME280::read_forced() { + bme280_reading reading; + int8_t result; + + reading.status = false; + + result = bme280_set_sensor_mode(BME280_FORCED_MODE, &device); + if(result != BME280_OK) return reading; + + uint32_t req_delay = bme280_cal_meas_delay(&device.settings); + device.delay_us(req_delay, device.intf_ptr); + + bme280_data data; + reading.status = bme280_get_sensor_data(BME280_ALL, &data, &device) == BME280_OK; + reading.temperature = data.temperature; + reading.pressure = data.pressure; + reading.humidity = data.humidity; + return reading; + } + + BME280::bme280_reading BME280::read() { + bme280_reading reading; + bme280_data data; + reading.status = bme280_get_sensor_data(BME280_ALL, &data, &device) == BME280_OK; + reading.temperature = data.temperature; + reading.pressure = data.pressure; + reading.humidity = data.humidity; + return reading; + } +} \ No newline at end of file diff --git a/drivers/bme280/bme280.hpp b/drivers/bme280/bme280.hpp new file mode 100644 index 00000000..f06a26e8 --- /dev/null +++ b/drivers/bme280/bme280.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include "hardware/i2c.h" +#include "hardware/gpio.h" +#include "src/bme280.h" +#include "src/bme280_defs.h" +#include "stdio.h" +#include "common/pimoroni_i2c.hpp" + +namespace pimoroni { + class BME280 { + public: + static const uint8_t DEFAULT_I2C_ADDRESS = 0x76; + static const uint8_t ALTERNATE_I2C_ADDRESS = 0x77; + static const uint8_t DEFAULT_INT_PIN = I2C_DEFAULT_INT; + + struct i2c_intf_ptr { + I2C *i2c; + int8_t address; + }; + + struct bme280_reading { + float temperature; + float pressure; + float humidity; + bool status; + }; + + bool debug = false; + + bool init(); + bool configure(uint8_t filter, uint8_t standby_time, uint8_t os_pressure, uint8_t os_temp, uint8_t os_humidity, uint8_t mode=BME280_NORMAL_MODE); + + BME280() : BME280(new I2C()) {} + BME280(uint8_t address) : BME280(new I2C(), address) {} + BME280(I2C *i2c, uint8_t address = DEFAULT_I2C_ADDRESS, uint interrupt = PIN_UNUSED) : i2c(i2c), address(address), interrupt(interrupt) {} + + bme280_reading read(); + BME280::bme280_reading read_forced(); + + // Bindings for bme280_dev + static int8_t write_bytes(uint8_t reg_addr, uint8_t *reg_data, uint16_t length, void *intf_ptr) { + BME280::i2c_intf_ptr* i2c = (BME280::i2c_intf_ptr *)intf_ptr; + + uint8_t buffer[length + 1]; + buffer[0] = reg_addr; + for(int x = 0; x < length; x++) { + buffer[x + 1] = reg_data[x]; + } + + int result = i2c->i2c->write_blocking(i2c->address, buffer, length + 1, false); + + return result == PICO_ERROR_GENERIC ? BME280_E_COMM_FAIL : BME280_OK; + }; + + static int8_t read_bytes(uint8_t reg_addr, uint8_t *reg_data, uint16_t length, void *intf_ptr) { + BME280::i2c_intf_ptr* i2c = (BME280::i2c_intf_ptr *)intf_ptr; + + int result = i2c->i2c->write_blocking(i2c->address, ®_addr, 1, true); + result = i2c->i2c->read_blocking(i2c->address, reg_data, length, false); + + return result == PICO_ERROR_GENERIC ? BME280_E_COMM_FAIL : BME280_OK; + }; + + static void delay_us(uint32_t period, void *intf_ptr) { + sleep_us(period); + } + + private: + bme280_dev device; + bme280_settings settings; + + I2C *i2c; + + // interface pins with our standard defaults where appropriate + int8_t address = DEFAULT_I2C_ADDRESS; + uint interrupt = DEFAULT_INT_PIN; + }; +} \ No newline at end of file diff --git a/drivers/bme280/src b/drivers/bme280/src new file mode 160000 index 00000000..c47f06eb --- /dev/null +++ b/drivers/bme280/src @@ -0,0 +1 @@ +Subproject commit c47f06eb44fc96970f0abfcc941ec16425b2a9e6 diff --git a/drivers/bmp280/CMakeLists.txt b/drivers/bmp280/CMakeLists.txt new file mode 100644 index 00000000..6a5d45e5 --- /dev/null +++ b/drivers/bmp280/CMakeLists.txt @@ -0,0 +1 @@ +include(bmp280.cmake) \ No newline at end of file diff --git a/drivers/bmp280/bmp280.cmake b/drivers/bmp280/bmp280.cmake new file mode 100644 index 00000000..c50fdde0 --- /dev/null +++ b/drivers/bmp280/bmp280.cmake @@ -0,0 +1,12 @@ +set(DRIVER_NAME bmp280) +add_library(${DRIVER_NAME} INTERFACE) + +target_sources(${DRIVER_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/bmp280.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/bmp280.c) + +target_include_directories(${DRIVER_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/src + ${CMAKE_CURRENT_LIST_DIR}) + +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_i2c pimoroni_i2c) diff --git a/drivers/bmp280/bmp280.cpp b/drivers/bmp280/bmp280.cpp new file mode 100644 index 00000000..744b7b1a --- /dev/null +++ b/drivers/bmp280/bmp280.cpp @@ -0,0 +1,60 @@ +#include "bmp280.hpp" +#include "pico/stdlib.h" + +namespace pimoroni { + bool BMP280::init() { + int8_t result; + + if(interrupt != PIN_UNUSED) { + gpio_set_function(interrupt, GPIO_FUNC_SIO); + gpio_set_dir(interrupt, GPIO_IN); + gpio_pull_up(interrupt); + } + + device.intf_ptr = new i2c_intf_ptr{.i2c = i2c, .address = address}; + device.intf = BMP280_I2C_INTF; + device.read = (bmp280_com_fptr_t)&read_bytes; + device.write = (bmp280_com_fptr_t)&write_bytes; + device.delay_ms = (bmp280_delay_fptr_t)&delay_ms; + + result = bmp280_init(&device); + if(result != BMP280_OK) return false; + + result = bmp280_set_config(&conf, &device); + if(result != BMP280_OK) return false; + + configure(BMP280_FILTER_COEFF_2, BMP280_ODR_1000_MS, BMP280_OS_4X, BMP280_OS_4X); + + return true; + } + + bool BMP280::configure(uint8_t filter, uint8_t odr, uint8_t os_pressure, uint8_t os_temp, uint8_t mode) { + int8_t result; + + conf.filter = filter; + conf.odr = odr; + conf.os_pres = os_pressure; + conf.os_temp = os_temp; + + result = bmp280_set_config(&conf, &device); + if(result != BMP280_OK) return false; + + result = bmp280_set_power_mode(mode, &device); + if(result != BMP280_OK) return false; + + return true; + } + + BMP280::bmp280_reading BMP280::read() { + bmp280_reading result; + bmp280_get_uncomp_data(&ucomp_data, &device); + + int32_t temperature; + result.status = bmp280_get_comp_temp_32bit(&temperature, ucomp_data.uncomp_temp, &device); + result.temperature = 0.01f * temperature; + + result.status &= bmp280_get_comp_pres_32bit(&result.pressure, ucomp_data.uncomp_press, &device); + + return result; + } +} \ No newline at end of file diff --git a/drivers/bmp280/bmp280.hpp b/drivers/bmp280/bmp280.hpp new file mode 100644 index 00000000..6ae33529 --- /dev/null +++ b/drivers/bmp280/bmp280.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include "hardware/i2c.h" +#include "hardware/gpio.h" +#include "src/bmp280.h" +#include "src/bmp280_defs.h" +#include "stdio.h" +#include "common/pimoroni_i2c.hpp" + +namespace pimoroni { + class BMP280 { + public: + static const int8_t DEFAULT_I2C_ADDRESS = 0x76; + static const uint8_t ALTERNATE_I2C_ADDRESS = 0x77; + static const uint DEFAULT_INT_PIN = I2C_DEFAULT_INT; + + struct i2c_intf_ptr { + I2C *i2c; + int8_t address; + }; + + struct bmp280_reading { + double temperature; + uint32_t pressure; + bool status; + }; + + bool debug = false; + + bool init(); + bool configure(uint8_t filter, uint8_t odr, uint8_t os_pressure, uint8_t os_temp, uint8_t mode = BMP280_NORMAL_MODE); + + BMP280() : BMP280(new I2C()) {} + BMP280(uint8_t address) : BMP280(new I2C(), address) {} + BMP280(I2C *i2c, uint8_t address = DEFAULT_I2C_ADDRESS, uint interrupt = PIN_UNUSED) : i2c(i2c), address(address), interrupt(interrupt) {} + + bmp280_reading read(); + + // Bindings for bmp280_dev + static int8_t write_bytes(void *intf_ptr, uint8_t reg_addr, uint8_t *reg_data, uint16_t length) { + BMP280::i2c_intf_ptr* i2c = (BMP280::i2c_intf_ptr *)intf_ptr; + + uint8_t buffer[length + 1]; + buffer[0] = reg_addr; + for(int x = 0; x < length; x++) { + buffer[x + 1] = reg_data[x]; + } + + int result = i2c->i2c->write_blocking(i2c->address, buffer, length + 1, false); + + return result == PICO_ERROR_GENERIC ? 1 : 0; + }; + + static int8_t read_bytes(void *intf_ptr, uint8_t reg_addr, uint8_t *reg_data, uint16_t length) { + BMP280::i2c_intf_ptr* i2c = (BMP280::i2c_intf_ptr *)intf_ptr; + + int result = i2c->i2c->write_blocking(i2c->address, ®_addr, 1, true); + result = i2c->i2c->read_blocking(i2c->address, reg_data, length, false); + + return result == PICO_ERROR_GENERIC ? 1 : 0; + }; + + static void delay_ms(uint32_t period, void *intf_ptr) { + sleep_ms(period); + } + + private: + bmp280_dev device; + bmp280_config conf; + bmp280_uncomp_data ucomp_data; + + I2C *i2c; + + // interface pins with our standard defaults where appropriate + int8_t address = DEFAULT_I2C_ADDRESS; + uint interrupt = DEFAULT_INT_PIN; + }; +} \ No newline at end of file diff --git a/drivers/bmp280/src b/drivers/bmp280/src new file mode 160000 index 00000000..2b5273fe --- /dev/null +++ b/drivers/bmp280/src @@ -0,0 +1 @@ +Subproject commit 2b5273fe8f8ba3682c7651082f0503079472fe72 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 59226faf..f37fbe9d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -14,6 +14,8 @@ add_subdirectory(breakout_sgp30) add_subdirectory(breakout_colourlcd240x240) add_subdirectory(breakout_msa301) add_subdirectory(breakout_bme688) +add_subdirectory(breakout_bmp280) +add_subdirectory(breakout_bme280) add_subdirectory(pico_display) add_subdirectory(pico_unicorn) diff --git a/examples/breakout_bme280/CMakeLists.txt b/examples/breakout_bme280/CMakeLists.txt new file mode 100644 index 00000000..71ec4b5b --- /dev/null +++ b/examples/breakout_bme280/CMakeLists.txt @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/bme280_basic.cmake") \ No newline at end of file diff --git a/examples/breakout_bme280/bme280_basic.cmake b/examples/breakout_bme280/bme280_basic.cmake new file mode 100644 index 00000000..b3079784 --- /dev/null +++ b/examples/breakout_bme280/bme280_basic.cmake @@ -0,0 +1,12 @@ +set(OUTPUT_NAME bme280_basic_demo) + +add_executable( + ${OUTPUT_NAME} + bme280_basic.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib bme280) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_bme280/bme280_basic.cpp b/examples/breakout_bme280/bme280_basic.cpp new file mode 100644 index 00000000..8ba35a5e --- /dev/null +++ b/examples/breakout_bme280/bme280_basic.cpp @@ -0,0 +1,31 @@ +#include +#include +#include "pico/stdlib.h" + +#include "bme280.hpp" +#include "common/pimoroni_i2c.hpp" + +using namespace pimoroni; + +I2C i2c(BOARD::BREAKOUT_GARDEN); +BME280 bme280(&i2c); + + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + stdio_init_all(); + + if(!bme280.init()) { + printf("Failed to init bme280!\n"); + } + + while (1) { + BME280::bme280_reading result = bme280.read_forced(); + printf("%s %0.2lf deg C, %0.2lf hPa, %0.2lf%%\n", result.status == BME280_OK ? "OK" : "ER", result.temperature, result.pressure, result.humidity); + sleep_ms(1000); + } + + return 0; +} \ No newline at end of file diff --git a/examples/breakout_bmp280/CMakeLists.txt b/examples/breakout_bmp280/CMakeLists.txt new file mode 100644 index 00000000..6ca4bec7 --- /dev/null +++ b/examples/breakout_bmp280/CMakeLists.txt @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/bmp280_basic.cmake") \ No newline at end of file diff --git a/examples/breakout_bmp280/bmp280_basic.cmake b/examples/breakout_bmp280/bmp280_basic.cmake new file mode 100644 index 00000000..0538b776 --- /dev/null +++ b/examples/breakout_bmp280/bmp280_basic.cmake @@ -0,0 +1,12 @@ +set(OUTPUT_NAME bmp280_basic_demo) + +add_executable( + ${OUTPUT_NAME} + bmp280_basic.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib bmp280) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/breakout_bmp280/bmp280_basic.cpp b/examples/breakout_bmp280/bmp280_basic.cpp new file mode 100644 index 00000000..76511c3b --- /dev/null +++ b/examples/breakout_bmp280/bmp280_basic.cpp @@ -0,0 +1,31 @@ +#include +#include +#include "pico/stdlib.h" + +#include "bmp280.hpp" +#include "common/pimoroni_i2c.hpp" + +using namespace pimoroni; + +I2C i2c(BOARD::BREAKOUT_GARDEN); +BMP280 bmp280(&i2c); + + +int main() { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + + stdio_init_all(); + + if(!bmp280.init()) { + printf("Failed to init bmp280!\n"); + } + + while (1) { + BMP280::bmp280_reading result = bmp280.read(); + printf("%s %0.2lf deg C, %ld hPa\n", result.status == BMP280_OK ? "OK" : "ER", result.temperature, result.pressure); + sleep_ms(1000); + } + + return 0; +} \ No newline at end of file