diff --git a/Documentation/pycardium/bme680.rst b/Documentation/pycardium/bme680.rst new file mode 100644 index 0000000000000000000000000000000000000000..aa937fdfbf699cf59a9b7acfe17470ffde0c6748 --- /dev/null +++ b/Documentation/pycardium/bme680.rst @@ -0,0 +1,17 @@ +``bme680`` - 4-in-1 Sensor +========================== + +.. py:function:: bme680.init() + + Before being able to read data from the sensor, you have to call init(). + +.. py:function:: bme680.get_data() + + Does a single measurement to get environmental data. + + :return: Tuple containing ``temperature`` (°C), ``humidity`` (% r.h.), + ``pressure`` (hPa) and ``gas resistance`` (Ohm). + +.. py:function:: bme680.deinit() + + De-Initializes the sensor. \ No newline at end of file diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 69899aaeb9b7e52a848b61b3e9451dd75f14708e..b576662965726dcf83caefcb3287e87c791da319 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -107,6 +107,10 @@ typedef _Bool bool; #define API_PERSONAL_STATE_GET 0xc1 #define API_PERSONAL_STATE_IS_PERSISTENT 0xc2 +#define API_BME680_INIT 0xD0 +#define API_BME680_DEINIT 0xD1 +#define API_BME680_GET_DATA 0xD2 + /* clang-format on */ typedef uint32_t api_int_id_t; @@ -673,6 +677,66 @@ API(API_LEDS_SET_GAMMA_TABLE, void epic_leds_set_gamma_table( */ API(API_LEDS_CLEAR_ALL, void epic_leds_clear_all(uint8_t r, uint8_t g, uint8_t b)); +/** + * BME680 + * ====== + */ + +/** + * BME680 Sensor Data + */ +struct bme680_sensor_data { + /** Temperature in degree celsius */ + float temperature; + /** Humidity in % relative humidity */ + float humidity; + /** Pressure in hPa */ + float pressure; + /** Gas resistance in Ohms */ + float gas_resistance; +}; + +/** + * Initialize the BM680 sensor. + * + * :return: 0 on success or ``-Exxx`` on error. The following + * errors might occur: + * + * - ``-EFAULT``: On NULL-pointer. + * - ``-EINVAL``: Invalid configuration. + * - ``-EIO``: Communication with the device failed. + * - ``-ENODEV``: Device was not found. + */ +API(API_BME680_INIT, int epic_bme680_init()); + +/** + * De-Initialize the BM680 sensor. + * + * :return: 0 on success or ``-Exxx`` on error. The following + * errors might occur: + * + * - ``-EFAULT``: On NULL-pointer. + * - ``-EINVAL``: Invalid configuration. + * - ``-EIO``: Communication with the device failed. + * - ``-ENODEV``: Device was not found. + */ +API(API_BME680_DEINIT, int epic_bme680_deinit()); + +/** + * Get the current BME680 data. + * + * :param data: Where to store the environmental data. + * :return: 0 on success or ``-Exxx`` on error. The following + * errors might occur: + * + * - ``-EFAULT``: On NULL-pointer. + * - ``-EINVAL``: Sensor not initialized. + * - ``-EIO``: Communication with the device failed. + * - ``-ENODEV``: Device was not found. + */ +API(API_BME680_GET_DATA, + int epic_bme680_read_sensors(struct bme680_sensor_data *data)); + /** * Personal State * ============== diff --git a/epicardium/modules/bme680.c b/epicardium/modules/bme680.c new file mode 100644 index 0000000000000000000000000000000000000000..bdc603d699fe5c4a63e8d29c0dfa79d6e810b66b --- /dev/null +++ b/epicardium/modules/bme680.c @@ -0,0 +1,138 @@ +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> + +#include "bme680.h" +#include "bosch.h" +#include "card10.h" + +#include "epicardium.h" +#include "modules.h" +#include "modules/log.h" + +#define HEATR_TEMP 320 +#define HEATR_DUR 150 + +static bool initialized; +static struct bme680_dev bme; + +static int convert_error(int8_t error) +{ + switch (error) { + case BME680_E_NULL_PTR: + return EFAULT; + case BME680_E_COM_FAIL: + return EIO; + case BME680_E_DEV_NOT_FOUND: + return ENODEV; + case BME680_E_INVALID_LENGTH: + return EINVAL; + default: + return 1; + } +} + +int epic_bme680_init() +{ + int8_t result = BME680_OK; + + if (__builtin_expect(!initialized, 0)) { + bme.dev_id = BME680_I2C_ADDR_PRIMARY; + bme.intf = BME680_I2C_INTF; + bme.read = card10_bosch_i2c_read; + bme.write = card10_bosch_i2c_write; + bme.delay_ms = card10_bosch_delay; + /* amb_temp can be set to 25 prior to configuring the gas sensor + * or by performing a few temperature readings without operating the gas sensor. + */ + bme.amb_temp = 25; + + result = bme680_init(&bme); + if (result != BME680_OK) { + LOG_ERR("bme680", "bme680_init error: %d\n", result); + return -convert_error(result); + } + + /* Select the power mode */ + /* Must be set before writing the sensor configuration */ + bme.power_mode = BME680_FORCED_MODE; + + /* Set the temperature, pressure and humidity settings */ + bme.tph_sett.os_hum = BME680_OS_2X; + bme.tph_sett.os_pres = BME680_OS_4X; + bme.tph_sett.os_temp = BME680_OS_8X; + bme.tph_sett.filter = BME680_FILTER_SIZE_3; + + /* Set the remaining gas sensor settings and link the heating profile */ + bme.gas_sett.run_gas = BME680_ENABLE_GAS_MEAS; + /* Create a ramp heat waveform in 3 steps */ + bme.gas_sett.heatr_temp = HEATR_TEMP; /* degree Celsius */ + bme.gas_sett.heatr_dur = HEATR_DUR; /* milliseconds */ + + /* Set the required sensor settings needed */ + uint16_t settings_sel = BME680_OST_SEL | BME680_OSP_SEL | + BME680_OSH_SEL | BME680_FILTER_SEL | + BME680_GAS_SENSOR_SEL; + + result = bme680_set_sensor_settings(settings_sel, &bme); + if (result != BME680_OK) { + LOG_ERR("bme680", + "bme680_set_sensor_settings error: %d\n", + result); + return -convert_error(result); + } + + initialized = true; + } + return 0; +} + +int epic_bme680_deinit() +{ + int8_t result = BME680_OK; + + result = bme680_soft_reset(&bme); + if (result != BME680_OK) { + LOG_ERR("bme680", "bme680_soft_reset error: %d\n", result); + return -convert_error(result); + } + + initialized = false; + return 0; +} + +int epic_bme680_read_sensors(struct bme680_sensor_data *data) +{ + int8_t result = BME680_OK; + + if (!initialized) { + LOG_ERR("bme680", "bme680 sensor not initialized"); + return -EINVAL; + } + + uint16_t profile_dur = 0; + bme680_get_profile_dur(&profile_dur, &bme); + + result = bme680_set_sensor_mode(&bme); /* Trigger a measurement */ + if (result != BME680_OK) { + LOG_ERR("bme680", "bme680_set_sensor_mode error: %d\n", result); + return -convert_error(result); + } + + vTaskDelay(pdMS_TO_TICKS( + profile_dur)); /* Wait for the measurement to complete */ + + struct bme680_field_data raw_data; + result = bme680_get_sensor_data(&raw_data, &bme); + if (result != BME680_OK) { + LOG_ERR("bme680", "bme680_get_sensor_data error: %d\n", result); + return -convert_error(result); + } + + data->temperature = raw_data.temperature / 100.0l; + data->humidity = raw_data.humidity / 1000.0l; + data->pressure = raw_data.pressure / 100.0l; + data->gas_resistance = raw_data.gas_resistance; + + return 0; +} diff --git a/epicardium/modules/hardware.c b/epicardium/modules/hardware.c index 5cb95dc286d3262504886d210580bdc9f9b12263..44ae9ab75dd0f9dd7223f40837db38e6b2d47e08 100644 --- a/epicardium/modules/hardware.c +++ b/epicardium/modules/hardware.c @@ -231,5 +231,10 @@ int hardware_reset(void) */ display_init_slim(); + /* + * BME680 Sensor + */ + epic_bme680_deinit(); + return 0; } diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index 5b4199b1739de26ef4a633397e0aa80dabd42c5d..dc2318359ecafaae8b78566b4ec87ecf891af44b 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -1,4 +1,5 @@ module_sources = files( + 'bme680.c', 'buttons.c', 'dispatcher.c', 'display.c', diff --git a/pycardium/meson.build b/pycardium/meson.build index 40fabde700ce03eff4d117b7e5e87c024b5f35f5..f0a3798a6ab5ef5538673a98f6de9748e705f3b7 100644 --- a/pycardium/meson.build +++ b/pycardium/meson.build @@ -13,6 +13,7 @@ modsrc = files( 'modules/sys_display.c', 'modules/utime.c', 'modules/vibra.c', + 'modules/bme680.c' ) ################################# diff --git a/pycardium/modules/bme680.c b/pycardium/modules/bme680.c new file mode 100644 index 0000000000000000000000000000000000000000..c9921cd7086b44d6c70168633755aa21a02014f6 --- /dev/null +++ b/pycardium/modules/bme680.c @@ -0,0 +1,62 @@ +#include "py/obj.h" +#include "py/objlist.h" +#include "py/runtime.h" + +#include "epicardium.h" + +static mp_obj_t mp_bme680_init() +{ + int ret = epic_bme680_init(); + + if (ret < 0) { + mp_raise_OSError(-ret); + } + + return 0; +} +static MP_DEFINE_CONST_FUN_OBJ_0(bme680_init_obj, mp_bme680_init); + +static mp_obj_t mp_bme680_deinit() +{ + int ret = epic_bme680_deinit(); + + if (ret < 0) { + mp_raise_OSError(-ret); + } + + return 0; +} +static MP_DEFINE_CONST_FUN_OBJ_0(bme680_deinit_obj, mp_bme680_deinit); + +static mp_obj_t mp_bme680_get_data() +{ + struct bme680_sensor_data data; + int ret = epic_bme680_read_sensors(&data); + + if (ret < 0) { + mp_raise_OSError(-ret); + } + + mp_obj_t values_list[] = { mp_obj_new_float(data.temperature), + mp_obj_new_float(data.humidity), + mp_obj_new_float(data.pressure), + mp_obj_new_float(data.gas_resistance) }; + return mp_obj_new_tuple(4, values_list); +} +static MP_DEFINE_CONST_FUN_OBJ_0(bme680_get_data_obj, mp_bme680_get_data); + +static const mp_rom_map_elem_t bme680_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bme680) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&bme680_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&bme680_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_data), MP_ROM_PTR(&bme680_get_data_obj) }, +}; +static MP_DEFINE_CONST_DICT(bme680_module_globals, bme680_module_globals_table); + +const mp_obj_module_t bme680_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&bme680_module_globals, +}; + +/* Register the module to make it available in Python */ +MP_REGISTER_MODULE(MP_QSTR_bme680, bme680_module, MODULE_BME680_ENABLED); diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index 6574d4369ff0ae21be5c1217fa41daa347073feb..60c5c56c9cbc385bb87fb8fe33f2f4881fd41947 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -76,6 +76,12 @@ Q(start) Q(get_reading) Q(stop) +/* bme680 */ +Q(bme680) +Q(init) +Q(deinit) +Q(get_data) + /* file */ Q(__del__) Q(__enter__) diff --git a/pycardium/mpconfigport.h b/pycardium/mpconfigport.h index a1c88a7476f0b63111a4e6049aaac33f32b7fa0c..af27e146f99e46735e5f33bd013383dd47233fa7 100644 --- a/pycardium/mpconfigport.h +++ b/pycardium/mpconfigport.h @@ -45,6 +45,7 @@ int mp_hal_trng_read_int(void); #define MICROPY_PY_UERRNO (1) /* Modules */ +#define MODULE_BME680_ENABLED (1) #define MODULE_BUTTONS_ENABLED (1) #define MODULE_DISPLAY_ENABLED (1) #define MODULE_GPIO_ENABLED (1)