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..31aba0e3 --- /dev/null +++ b/drivers/bme280/bme280.cpp @@ -0,0 +1,83 @@ +#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; + } + + I2C* BME280::get_i2c() const { + return i2c; + } + + int BME280::get_int() const { + return interrupt; + } +} \ No newline at end of file diff --git a/drivers/bme280/bme280.hpp b/drivers/bme280/bme280.hpp new file mode 100644 index 00000000..9ab8f198 --- /dev/null +++ b/drivers/bme280/bme280.hpp @@ -0,0 +1,83 @@ +#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) {} + + // For print access in micropython + I2C* get_i2c() const; + int get_int() const; + + 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..019f3b2e --- /dev/null +++ b/drivers/bmp280/bmp280.cpp @@ -0,0 +1,68 @@ +#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; + } + + I2C* BMP280::get_i2c() const { + return i2c; + } + + int BMP280::get_int() const { + return interrupt; + } +} \ No newline at end of file diff --git a/drivers/bmp280/bmp280.hpp b/drivers/bmp280/bmp280.hpp new file mode 100644 index 00000000..02249b0c --- /dev/null +++ b/drivers/bmp280/bmp280.hpp @@ -0,0 +1,82 @@ +#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) {} + + // For print access in micropython + I2C* get_i2c() const; + int get_int() const; + + 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 diff --git a/micropython/examples/breakout_bme280/demo_bme280.py b/micropython/examples/breakout_bme280/demo_bme280.py new file mode 100644 index 00000000..382c7b44 --- /dev/null +++ b/micropython/examples/breakout_bme280/demo_bme280.py @@ -0,0 +1,14 @@ +import time +from breakout_bme280 import BreakoutBME280 +from pimoroni_i2c import PimoroniI2C + +PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5} +PINS_PICO_EXPLORER = {"sda": 20, "scl": 21} + +i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN) +bme = BreakoutBME280(i2c) + +while True: + reading = bme.read() + print(reading) + time.sleep(1.0) diff --git a/micropython/examples/breakout_bmp280/demo_bmp280.py b/micropython/examples/breakout_bmp280/demo_bmp280.py new file mode 100644 index 00000000..af2eb28c --- /dev/null +++ b/micropython/examples/breakout_bmp280/demo_bmp280.py @@ -0,0 +1,14 @@ +import time +from breakout_bmp280 import BreakoutBMP280 +from pimoroni_i2c import PimoroniI2C + +PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5} +PINS_PICO_EXPLORER = {"sda": 20, "scl": 21} + +i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN) +bmp = BreakoutBMP280(i2c) + +while True: + reading = bmp.read() + print(reading) + time.sleep(1.0) diff --git a/micropython/modules/breakout_bme280/README.md b/micropython/modules/breakout_bme280/README.md new file mode 100644 index 00000000..4459c914 --- /dev/null +++ b/micropython/modules/breakout_bme280/README.md @@ -0,0 +1,83 @@ +# BME280 + +- [Getting Started](#getting-started) +- [Reading Temperature, Pressure & Humidity](#reading-temperature-pressure--humidity) +- [Configuring The Sensor](#configuring-the-sensor) + - [Filter Settings](#filter-settings) + - [Oversampling Settings](#oversampling-settings) + - [Mode Settings](#mode-settings) + - [Standby/Output Data Rate Settings](#standbyoutput-data-rate-settings) + - [Defaults](#defaults) + +## Getting Started + +Construct new `PimoroniI2C` and `BreakoutBME280` instances: + +```python +from breakout_bme280 import BreakoutBME280 +from pimoroni_i2c import PimoroniI2C + +PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5} +PINS_PICO_EXPLORER = {"sda": 20, "scl": 21} + +i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN) +bme = BreakoutBME280(i2c) +``` + +## Reading Temperature, Pressure & Humidity + +The `read` method will return a tuple containing Temperature (degrees C), Pressure (Pa) and Humidity (RH %) values: + +```python +temperature, pressure, humidity = bme.read() +``` + +## Configuring The Sensor + +The `configure` method allows you to set up the oversampling, filtering and operation mode. + +```python +bmp.configure(filter, standby_time, os_pressure, os_humidity, os_temp, mode) +``` + +The `breakout_bme280` module includes constants for these: + +### Filter Settings + +* `FILTER_COEFF_OFF` +* `FILTER_COEFF_2` +* `FILTER_COEFF_4` +* `FILTER_COEFF_8` +* `FILTER_COEFF_16` + +### Oversampling Settings + +* `NO_OVERSAMPLING` +* `OVERSAMPLING_1X` +* `OVERSAMPLING_2X` +* `OVERSAMPLING_4X` +* `OVERSAMPLING_8X` +* `OVERSAMPLING_16X` + +### Mode Settings + +* `SLEEP_MODE` +* `FORCED_MODE` +* `NORMAL_MODE` + +### Standby/Output Data Rate Settings + +* `STANDBY_TIME_0_5_MS` +* `STANDBY_TIME_62_5_MS` +* `STANDBY_TIME_125_MS` +* `STANDBY_TIME_250_MS` +* `STANDBY_TIME_500_MS` +* `STANDBY_TIME_1000_MS` +* `STANDBY_TIME_2000_MS` +* `STANDBY_TIME_4000_MS` + +### Defaults + +```python +bme.configure(FILTER_COEFF_2, STANDBY_TIME_0_5_MS, OVERSAMPLING_16X, OVERSAMPLING_2X, OVERSAMPLING_1X) +``` \ No newline at end of file diff --git a/micropython/modules/breakout_bme280/breakout_bme280.c b/micropython/modules/breakout_bme280/breakout_bme280.c new file mode 100644 index 00000000..acf36fe6 --- /dev/null +++ b/micropython/modules/breakout_bme280/breakout_bme280.c @@ -0,0 +1,74 @@ +#include "breakout_bme280.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// BreakoutBME280 Class +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Methods *****/ +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutBME280_read_obj, BreakoutBME280_read); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBME280_configure_obj, 1, BreakoutBME280_configure); + +/***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t BreakoutBME280_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutBME280_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&BreakoutBME280_configure_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(BreakoutBME280_locals_dict, BreakoutBME280_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t breakout_bme280_BreakoutBME280_type = { + { &mp_type_type }, + .name = MP_QSTR_breakout_bme280, + .print = BreakoutBME280_print, + .make_new = BreakoutBME280_make_new, + .locals_dict = (mp_obj_dict_t*)&BreakoutBME280_locals_dict, +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// breakout_bme280 Module +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Globals Table *****/ +STATIC const mp_map_elem_t breakout_bme280_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_bme280) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutBME280), (mp_obj_t)&breakout_bme280_BreakoutBME280_type }, + + { MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_OFF), MP_ROM_INT(BME280_FILTER_COEFF_OFF) }, + { MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_2), MP_ROM_INT(BME280_FILTER_COEFF_2) }, + { MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_4), MP_ROM_INT(BME280_FILTER_COEFF_4) }, + { MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_8), MP_ROM_INT(BME280_FILTER_COEFF_8) }, + { MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_16), MP_ROM_INT(BME280_FILTER_COEFF_16) }, + + { MP_ROM_QSTR(MP_QSTR_NO_OVERSAMPLING), MP_ROM_INT(BME280_NO_OVERSAMPLING) }, + { MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_1X), MP_ROM_INT(BME280_OVERSAMPLING_1X) }, + { MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_2X), MP_ROM_INT(BME280_OVERSAMPLING_2X) }, + { MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_4X), MP_ROM_INT(BME280_OVERSAMPLING_4X) }, + { MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_8X), MP_ROM_INT(BME280_OVERSAMPLING_8X) }, + { MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_16X), MP_ROM_INT(BME280_OVERSAMPLING_16X) }, + + { MP_ROM_QSTR(MP_QSTR_SLEEP_MODE), MP_ROM_INT(BME280_SLEEP_MODE) }, + { MP_ROM_QSTR(MP_QSTR_FORCED_MODE), MP_ROM_INT(BME280_FORCED_MODE) }, + { MP_ROM_QSTR(MP_QSTR_NORMAL_MODE), MP_ROM_INT(BME280_NORMAL_MODE) }, + + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_0_5_MS), MP_ROM_INT(BME280_STANDBY_TIME_0_5_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_62_5_MS), MP_ROM_INT(BME280_STANDBY_TIME_62_5_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_125_MS), MP_ROM_INT(BME280_STANDBY_TIME_125_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_250_MS), MP_ROM_INT(BME280_STANDBY_TIME_250_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_500_MS), MP_ROM_INT(BME280_STANDBY_TIME_500_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_1000_MS), MP_ROM_INT(BME280_STANDBY_TIME_1000_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_10_MS), MP_ROM_INT(BME280_STANDBY_TIME_10_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_20_MS), MP_ROM_INT(BME280_STANDBY_TIME_20_MS) }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_bme280_globals, breakout_bme280_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t breakout_bme280_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_breakout_bme280_globals, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +MP_REGISTER_MODULE(MP_QSTR_breakout_bme280, breakout_bme280_user_cmodule, MODULE_BREAKOUT_BME280_ENABLED); +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_bme280/breakout_bme280.cpp b/micropython/modules/breakout_bme280/breakout_bme280.cpp new file mode 100644 index 00000000..8a00e0a8 --- /dev/null +++ b/micropython/modules/breakout_bme280/breakout_bme280.cpp @@ -0,0 +1,125 @@ +#include "drivers/bme280/bme280.hpp" + +#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) + +// SDA/SCL on even/odd pins, I2C0/I2C1 on even/odd pairs of pins. +#define IS_VALID_SCL(i2c, pin) (((pin) & 1) == 1 && (((pin) & 2) >> 1) == (i2c)) +#define IS_VALID_SDA(i2c, pin) (((pin) & 1) == 0 && (((pin) & 2) >> 1) == (i2c)) + + +using namespace pimoroni; + +extern "C" { +#include "breakout_bme280.h" +#include "pimoroni_i2c.h" + +/***** I2C Struct *****/ +typedef struct _PimoroniI2C_obj_t { + mp_obj_base_t base; + I2C *i2c; +} _PimoroniI2C_obj_t; + +/***** Variables Struct *****/ +typedef struct _breakout_bme280_BreakoutBME280_obj_t { + mp_obj_base_t base; + BME280 *breakout; +} breakout_bme280_BreakoutBME280_obj_t; + +/***** Print *****/ +void BreakoutBME280_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; //Unused input parameter + breakout_bme280_BreakoutBME280_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_bme280_BreakoutBME280_obj_t); + BME280* breakout = self->breakout; + mp_print_str(print, "BreakoutBME280("); + + mp_print_str(print, "i2c = "); + mp_obj_print_helper(print, mp_obj_new_int((breakout->get_i2c()->get_i2c() == i2c0) ? 0 : 1), PRINT_REPR); + + mp_print_str(print, ", sda = "); + mp_obj_print_helper(print, mp_obj_new_int(breakout->get_i2c()->get_sda()), PRINT_REPR); + + mp_print_str(print, ", scl = "); + mp_obj_print_helper(print, mp_obj_new_int(breakout->get_i2c()->get_scl()), PRINT_REPR); + + mp_print_str(print, ", int = "); + mp_obj_print_helper(print, mp_obj_new_int(breakout->get_int()), PRINT_REPR); + + mp_print_str(print, ")"); +} + +/***** Constructor *****/ +mp_obj_t BreakoutBME280_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + breakout_bme280_BreakoutBME280_obj_t *self = nullptr; + + enum { ARG_i2c, ARG_address, ARG_int }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_OBJ, {.u_obj = nullptr} }, + { MP_QSTR_address, MP_ARG_INT, {.u_int = BME280::DEFAULT_I2C_ADDRESS} }, + { MP_QSTR_interrupt, MP_ARG_INT, {.u_int = PIN_UNUSED} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + + if(!MP_OBJ_IS_TYPE(args[ARG_i2c].u_obj, &PimoroniI2C_type)) { + mp_raise_ValueError(MP_ERROR_TEXT("BreakoutBME280: Bad i2C object")); + return mp_const_none; + } + + _PimoroniI2C_obj_t *i2c = (_PimoroniI2C_obj_t *)MP_OBJ_TO_PTR(args[ARG_i2c].u_obj); + + self = m_new_obj(breakout_bme280_BreakoutBME280_obj_t); + self->base.type = &breakout_bme280_BreakoutBME280_type; + + self->breakout = new BME280(i2c->i2c, args[ARG_address].u_int, args[ARG_int].u_int); + + if(!self->breakout->init()) { + mp_raise_msg(&mp_type_RuntimeError, "BreakoutBME280: breakout not found when initialising"); + } + + return MP_OBJ_FROM_PTR(self); +} + +mp_obj_t BreakoutBME280_read(mp_obj_t self_in) { + breakout_bme280_BreakoutBME280_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_bme280_BreakoutBME280_obj_t); + BME280::bme280_reading result = self->breakout->read_forced(); + + mp_obj_t tuple[3]; + tuple[0] = mp_obj_new_float(result.temperature); + tuple[1] = mp_obj_new_float(result.pressure); + tuple[2] = mp_obj_new_float(result.humidity); + return mp_obj_new_tuple(3, tuple); +} + +mp_obj_t BreakoutBME280_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_filter, ARG_standby_time, ARG_os_pressure, ARG_os_temp, ARG_os_humidity, ARG_mode }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_filter, MP_ARG_INT, { .u_int=BME280_FILTER_COEFF_2 } }, + { MP_QSTR_standby_time, MP_ARG_INT, { .u_int=BME280_STANDBY_TIME_0_5_MS } }, + { MP_QSTR_os_pressure, MP_ARG_INT, { .u_int=BME280_OVERSAMPLING_16X } }, + { MP_QSTR_os_temp, MP_ARG_INT, { .u_int=BME280_OVERSAMPLING_2X } }, + { MP_QSTR_os_humidity, MP_ARG_INT, { .u_int=BME280_OVERSAMPLING_1X } }, + { MP_QSTR_mode, MP_ARG_INT, { .u_int=BME280_NORMAL_MODE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + breakout_bme280_BreakoutBME280_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_bme280_BreakoutBME280_obj_t); + self->breakout->configure( + args[ARG_filter].u_int, + args[ARG_standby_time].u_int, + args[ARG_os_pressure].u_int, + args[ARG_os_temp].u_int, + args[ARG_os_humidity].u_int, + args[ARG_mode].u_int + ); + + return mp_const_none; +} + + +} \ No newline at end of file diff --git a/micropython/modules/breakout_bme280/breakout_bme280.h b/micropython/modules/breakout_bme280/breakout_bme280.h new file mode 100644 index 00000000..2e669a4e --- /dev/null +++ b/micropython/modules/breakout_bme280/breakout_bme280.h @@ -0,0 +1,14 @@ +// Include MicroPython API. +#include "py/runtime.h" +#include "drivers/bme280/src/bme280_defs.h" + +/***** Constants *****/ + +/***** Extern of Class Definition *****/ +extern const mp_obj_type_t breakout_bme280_BreakoutBME280_type; + +/***** Extern of Class Methods *****/ +extern void BreakoutBME280_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +extern mp_obj_t BreakoutBME280_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); +extern mp_obj_t BreakoutBME280_read(mp_obj_t self_in); +extern mp_obj_t BreakoutBME280_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file diff --git a/micropython/modules/breakout_bme280/micropython.cmake b/micropython/modules/breakout_bme280/micropython.cmake new file mode 100644 index 00000000..584e854d --- /dev/null +++ b/micropython/modules/breakout_bme280/micropython.cmake @@ -0,0 +1,20 @@ +set(MOD_NAME breakout_bme280) +string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) +add_library(usermod_${MOD_NAME} INTERFACE) + +target_sources(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/bme280/src/bme280.c + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/bme280/bme280.cpp +) + +target_include_directories(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + +target_compile_definitions(usermod_${MOD_NAME} INTERFACE + MODULE_${MOD_NAME_UPPER}_ENABLED=1 +) + +target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) \ No newline at end of file diff --git a/micropython/modules/breakout_bme280/micropython.mk b/micropython/modules/breakout_bme280/micropython.mk new file mode 100755 index 00000000..ed13d3a3 --- /dev/null +++ b/micropython/modules/breakout_bme280/micropython.mk @@ -0,0 +1,13 @@ +set(MOD_NAME breakout_as7262) +BREAKOUT_MOD_DIR := $(USERMOD_DIR) + +# Add our source files to the respective variables. +SRC_USERMOD += $(BREAKOUT_MOD_DIR)/${MOD_NAME}.c +SRC_USERMOD_CXX += $(BREAKOUT_MOD_DIR)/${MOD_NAME}.cpp + +# Add our module directory to the include path. +CFLAGS_USERMOD += -I$(BREAKOUT_MOD_DIR) +CXXFLAGS_USERMOD += -I$(BREAKOUT_MOD_DIR) + +# We use C++ features so have to link against the standard library. +LDFLAGS_USERMOD += -lstdc++ \ No newline at end of file diff --git a/micropython/modules/breakout_bmp280/README.md b/micropython/modules/breakout_bmp280/README.md new file mode 100644 index 00000000..611eb983 --- /dev/null +++ b/micropython/modules/breakout_bmp280/README.md @@ -0,0 +1,83 @@ +# BMP280 + +- [Getting Started](#getting-started) +- [Reading Temperature & Pressure](#reading-temperature--pressure) +- [Configuring The Sensor](#configuring-the-sensor) + - [Filter Settings](#filter-settings) + - [Oversampling Settings](#oversampling-settings) + - [Mode Settings](#mode-settings) + - [Standby/Output Data Rate Settings](#standbyoutput-data-rate-settings) + - [Defaults](#defaults) + +## Getting Started + +Construct new `PimoroniI2C` and `BreakoutBMP280` instances: + +```python +from breakout_bmp280 import BreakoutBMP280 +from pimoroni_i2c import PimoroniI2C + +PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5} +PINS_PICO_EXPLORER = {"sda": 20, "scl": 21} + +i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN) +bmp = BreakoutBMP280(i2c) +``` + +## Reading Temperature & Pressure + +The `read` method will return a tuple containing Temperature (degrees C) and Pressure (Pa) values: + +```python +temperature, pressure = bmp.read() +``` + +## Configuring The Sensor + +The `configure` method allows you to set up the oversampling, filtering and operation mode. + +```python +bmp.configure(filter, standby_time, os_pressure, os_temp, mode) +``` + +The `breakout_bmp280` module includes constants for these: + +### Filter Settings + +* `FILTER_COEFF_OFF` +* `FILTER_COEFF_2` +* `FILTER_COEFF_4` +* `FILTER_COEFF_8` +* `FILTER_COEFF_16` + +### Oversampling Settings + +* `NO_OVERSAMPLING` +* `OVERSAMPLING_1X` +* `OVERSAMPLING_2X` +* `OVERSAMPLING_4X` +* `OVERSAMPLING_8X` +* `OVERSAMPLING_16X` + +### Mode Settings + +* `SLEEP_MODE` +* `FORCED_MODE` +* `NORMAL_MODE` + +### Standby/Output Data Rate Settings + +* `STANDBY_TIME_0_5_MS` +* `STANDBY_TIME_62_5_MS` +* `STANDBY_TIME_125_MS` +* `STANDBY_TIME_250_MS` +* `STANDBY_TIME_500_MS` +* `STANDBY_TIME_1000_MS` +* `STANDBY_TIME_2000_MS` +* `STANDBY_TIME_4000_MS` + +### Defaults + +```python +bmp.configure(FILTER_COEFF_2, STANDBY_TIME_1000_MS, OVERSAMPLING_4X, OVERSAMPLING_4X) +``` diff --git a/micropython/modules/breakout_bmp280/breakout_bmp280.c b/micropython/modules/breakout_bmp280/breakout_bmp280.c new file mode 100644 index 00000000..f6de8170 --- /dev/null +++ b/micropython/modules/breakout_bmp280/breakout_bmp280.c @@ -0,0 +1,74 @@ +#include "breakout_bmp280.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// BreakoutBMP280 Class +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Methods *****/ +MP_DEFINE_CONST_FUN_OBJ_1(BreakoutBMP280_read_obj, BreakoutBMP280_read); +MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBMP280_configure_obj, 1, BreakoutBMP280_configure); + +/***** Binding of Methods *****/ +STATIC const mp_rom_map_elem_t BreakoutBMP280_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutBMP280_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&BreakoutBMP280_configure_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(BreakoutBMP280_locals_dict, BreakoutBMP280_locals_dict_table); + +/***** Class Definition *****/ +const mp_obj_type_t breakout_bmp280_BreakoutBMP280_type = { + { &mp_type_type }, + .name = MP_QSTR_breakout_bmp280, + .print = BreakoutBMP280_print, + .make_new = BreakoutBMP280_make_new, + .locals_dict = (mp_obj_dict_t*)&BreakoutBMP280_locals_dict, +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// breakout_bmp280 Module +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/***** Globals Table *****/ +STATIC const mp_map_elem_t breakout_bmp280_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_bmp280) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutBMP280), (mp_obj_t)&breakout_bmp280_BreakoutBMP280_type }, + + { MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_OFF), MP_ROM_INT(BMP280_FILTER_OFF) }, + { MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_2), MP_ROM_INT(BMP280_FILTER_COEFF_2) }, + { MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_4), MP_ROM_INT(BMP280_FILTER_COEFF_4) }, + { MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_8), MP_ROM_INT(BMP280_FILTER_COEFF_8) }, + { MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_16), MP_ROM_INT(BMP280_FILTER_COEFF_16) }, + + { MP_ROM_QSTR(MP_QSTR_NO_OVERSAMPLING), MP_ROM_INT(BMP280_OS_NONE) }, + { MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_1X), MP_ROM_INT(BMP280_OS_1X) }, + { MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_2X), MP_ROM_INT(BMP280_OS_2X) }, + { MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_4X), MP_ROM_INT(BMP280_OS_4X) }, + { MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_8X), MP_ROM_INT(BMP280_OS_8X) }, + { MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_16X), MP_ROM_INT(BMP280_OS_16X) }, + + { MP_ROM_QSTR(MP_QSTR_SLEEP_MODE), MP_ROM_INT(BMP280_SLEEP_MODE) }, + { MP_ROM_QSTR(MP_QSTR_FORCED_MODE), MP_ROM_INT(BMP280_FORCED_MODE) }, + { MP_ROM_QSTR(MP_QSTR_NORMAL_MODE), MP_ROM_INT(BMP280_NORMAL_MODE) }, + + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_0_5_MS), MP_ROM_INT(BMP280_ODR_0_5_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_62_5_MS), MP_ROM_INT(BMP280_ODR_62_5_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_125_MS), MP_ROM_INT(BMP280_ODR_125_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_250_MS), MP_ROM_INT(BMP280_ODR_250_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_500_MS), MP_ROM_INT(BMP280_ODR_500_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_1000_MS), MP_ROM_INT(BMP280_ODR_1000_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_2000_MS), MP_ROM_INT(BMP280_ODR_2000_MS) }, + { MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_4000_MS), MP_ROM_INT(BMP280_ODR_4000_MS) }, +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_breakout_bmp280_globals, breakout_bmp280_globals_table); + +/***** Module Definition *****/ +const mp_obj_module_t breakout_bmp280_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_breakout_bmp280_globals, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +MP_REGISTER_MODULE(MP_QSTR_breakout_bmp280, breakout_bmp280_user_cmodule, MODULE_BREAKOUT_BMP280_ENABLED); +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/micropython/modules/breakout_bmp280/breakout_bmp280.cpp b/micropython/modules/breakout_bmp280/breakout_bmp280.cpp new file mode 100644 index 00000000..91532096 --- /dev/null +++ b/micropython/modules/breakout_bmp280/breakout_bmp280.cpp @@ -0,0 +1,121 @@ +#include "drivers/bmp280/bmp280.hpp" + +#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) + +// SDA/SCL on even/odd pins, I2C0/I2C1 on even/odd pairs of pins. +#define IS_VALID_SCL(i2c, pin) (((pin) & 1) == 1 && (((pin) & 2) >> 1) == (i2c)) +#define IS_VALID_SDA(i2c, pin) (((pin) & 1) == 0 && (((pin) & 2) >> 1) == (i2c)) + + +using namespace pimoroni; + +extern "C" { +#include "breakout_bmp280.h" +#include "pimoroni_i2c.h" + +/***** I2C Struct *****/ +typedef struct _PimoroniI2C_obj_t { + mp_obj_base_t base; + I2C *i2c; +} _PimoroniI2C_obj_t; + +/***** Variables Struct *****/ +typedef struct _breakout_bmp280_BreakoutBMP280_obj_t { + mp_obj_base_t base; + BMP280 *breakout; +} breakout_bmp280_BreakoutBMP280_obj_t; + +/***** Print *****/ +void BreakoutBMP280_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; //Unused input parameter + breakout_bmp280_BreakoutBMP280_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_bmp280_BreakoutBMP280_obj_t); + BMP280* breakout = self->breakout; + mp_print_str(print, "BreakoutBMP280("); + + mp_print_str(print, "i2c = "); + mp_obj_print_helper(print, mp_obj_new_int((breakout->get_i2c()->get_i2c() == i2c0) ? 0 : 1), PRINT_REPR); + + mp_print_str(print, ", sda = "); + mp_obj_print_helper(print, mp_obj_new_int(breakout->get_i2c()->get_sda()), PRINT_REPR); + + mp_print_str(print, ", scl = "); + mp_obj_print_helper(print, mp_obj_new_int(breakout->get_i2c()->get_scl()), PRINT_REPR); + + mp_print_str(print, ", int = "); + mp_obj_print_helper(print, mp_obj_new_int(breakout->get_int()), PRINT_REPR); + + mp_print_str(print, ")"); +} + +/***** Constructor *****/ +mp_obj_t BreakoutBMP280_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + breakout_bmp280_BreakoutBMP280_obj_t *self = nullptr; + + enum { ARG_i2c, ARG_address, ARG_int }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_OBJ, {.u_obj = nullptr} }, + { MP_QSTR_address, MP_ARG_INT, {.u_int = BMP280::DEFAULT_I2C_ADDRESS} }, + { MP_QSTR_interrupt, MP_ARG_INT, {.u_int = PIN_UNUSED} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + + if(!MP_OBJ_IS_TYPE(args[ARG_i2c].u_obj, &PimoroniI2C_type)) { + mp_raise_ValueError(MP_ERROR_TEXT("BreakoutBMP280: Bad i2C object")); + return mp_const_none; + } + + _PimoroniI2C_obj_t *i2c = (_PimoroniI2C_obj_t *)MP_OBJ_TO_PTR(args[ARG_i2c].u_obj); + + self = m_new_obj(breakout_bmp280_BreakoutBMP280_obj_t); + self->base.type = &breakout_bmp280_BreakoutBMP280_type; + + self->breakout = new BMP280(i2c->i2c, args[ARG_address].u_int, args[ARG_int].u_int); + + if(!self->breakout->init()) { + mp_raise_msg(&mp_type_RuntimeError, "BreakoutBMP280: breakout not found when initialising"); + } + + return MP_OBJ_FROM_PTR(self); +} + +mp_obj_t BreakoutBMP280_read(mp_obj_t self_in) { + breakout_bmp280_BreakoutBMP280_obj_t *self = MP_OBJ_TO_PTR2(self_in, breakout_bmp280_BreakoutBMP280_obj_t); + BMP280::bmp280_reading result = self->breakout->read(); + + mp_obj_t tuple[2]; + tuple[0] = mp_obj_new_float(result.temperature); + tuple[1] = mp_obj_new_float(result.pressure); + return mp_obj_new_tuple(2, tuple); +} + +mp_obj_t BreakoutBMP280_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_filter, ARG_standby_time, ARG_os_pressure, ARG_os_temp, ARG_mode }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_filter, MP_ARG_INT, { .u_int=BMP280_FILTER_COEFF_2 } }, + { MP_QSTR_standby_time, MP_ARG_INT, { .u_int=BMP280_ODR_0_5_MS } }, + { MP_QSTR_os_pressure, MP_ARG_INT, { .u_int=BMP280_OS_16X } }, + { MP_QSTR_os_temp, MP_ARG_INT, { .u_int=BMP280_OS_2X } }, + { MP_QSTR_mode, MP_ARG_INT, { .u_int=BMP280_NORMAL_MODE } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + breakout_bmp280_BreakoutBMP280_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_bmp280_BreakoutBMP280_obj_t); + self->breakout->configure( + args[ARG_filter].u_int, + args[ARG_standby_time].u_int, + args[ARG_os_pressure].u_int, + args[ARG_os_temp].u_int, + args[ARG_mode].u_int + ); + + return mp_const_none; +} + +} \ No newline at end of file diff --git a/micropython/modules/breakout_bmp280/breakout_bmp280.h b/micropython/modules/breakout_bmp280/breakout_bmp280.h new file mode 100644 index 00000000..a5d7839e --- /dev/null +++ b/micropython/modules/breakout_bmp280/breakout_bmp280.h @@ -0,0 +1,14 @@ +// Include MicroPython API. +#include "py/runtime.h" +#include "drivers/bmp280/src/bmp280_defs.h" + +/***** Constants *****/ + +/***** Extern of Class Definition *****/ +extern const mp_obj_type_t breakout_bmp280_BreakoutBMP280_type; + +/***** Extern of Class Methods *****/ +extern void BreakoutBMP280_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind); +extern mp_obj_t BreakoutBMP280_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); +extern mp_obj_t BreakoutBMP280_read(mp_obj_t self_in); +extern mp_obj_t BreakoutBMP280_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file diff --git a/micropython/modules/breakout_bmp280/micropython.cmake b/micropython/modules/breakout_bmp280/micropython.cmake new file mode 100644 index 00000000..663e6030 --- /dev/null +++ b/micropython/modules/breakout_bmp280/micropython.cmake @@ -0,0 +1,20 @@ +set(MOD_NAME breakout_bmp280) +string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) +add_library(usermod_${MOD_NAME} INTERFACE) + +target_sources(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c + ${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/bmp280/src/bmp280.c + ${CMAKE_CURRENT_LIST_DIR}/../../../drivers/bmp280/bmp280.cpp +) + +target_include_directories(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + +target_compile_definitions(usermod_${MOD_NAME} INTERFACE + MODULE_${MOD_NAME_UPPER}_ENABLED=1 +) + +target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) \ No newline at end of file diff --git a/micropython/modules/breakout_bmp280/micropython.mk b/micropython/modules/breakout_bmp280/micropython.mk new file mode 100755 index 00000000..ed13d3a3 --- /dev/null +++ b/micropython/modules/breakout_bmp280/micropython.mk @@ -0,0 +1,13 @@ +set(MOD_NAME breakout_as7262) +BREAKOUT_MOD_DIR := $(USERMOD_DIR) + +# Add our source files to the respective variables. +SRC_USERMOD += $(BREAKOUT_MOD_DIR)/${MOD_NAME}.c +SRC_USERMOD_CXX += $(BREAKOUT_MOD_DIR)/${MOD_NAME}.cpp + +# Add our module directory to the include path. +CFLAGS_USERMOD += -I$(BREAKOUT_MOD_DIR) +CXXFLAGS_USERMOD += -I$(BREAKOUT_MOD_DIR) + +# We use C++ features so have to link against the standard library. +LDFLAGS_USERMOD += -lstdc++ \ No newline at end of file diff --git a/micropython/modules/micropython.cmake b/micropython/modules/micropython.cmake index 616697f6..7c8dd9b3 100644 --- a/micropython/modules/micropython.cmake +++ b/micropython/modules/micropython.cmake @@ -23,6 +23,8 @@ include(breakout_sgp30/micropython) include(breakout_colourlcd240x240/micropython) include(breakout_bh1745/micropython) include(breakout_bme68x/micropython) +include(breakout_bme280/micropython) +include(breakout_bmp280/micropython) include(pico_scroll/micropython) include(pico_rgb_keypad/micropython)