From bc13b3450a692447796cd572b6c6e702f2571680 Mon Sep 17 00:00:00 2001 From: Daniel Kampert Date: Fri, 20 Oct 2023 22:47:58 +0200 Subject: [PATCH] =?UTF-8?q?-=20Add=20Bosch=20BME6XX=20driver=20as=20submod?= =?UTF-8?q?ule=20-=20Disable=20BME680=20support=20in=20project=20configura?= =?UTF-8?q?tion=20-=20Rename=20submodule=20path=20for=20BMI270=20sensor=20?= =?UTF-8?q?to=20align=20it=20with=20the=20other=20external=20drivers=20-?= =?UTF-8?q?=20Remove=20not=20needed=20"CONFIG=5FINPUT"=20from=20debug=20co?= =?UTF-8?q?nfig=20-=20Move=20"SPI=20RTT=20Flash=20Loader"=20into=20"ZSWatc?= =?UTF-8?q?h"=20menu=20-=20Move=20CST816S=20driver=20into=20device=20speci?= =?UTF-8?q?fic=20directory=20-=20Move=20GC9A01=20driver=20into=20device=20?= =?UTF-8?q?specific=20directory=20-=20Add=20default=20configuration=20menu?= =?UTF-8?q?=20in=20Kconfig=20-=20Add=20default=20idle=20timeout=20option?= =?UTF-8?q?=20to=20Kconfig=20-=20Remove=20"CONFIG=5FDISPLAY=5FFAST=5FWAKEU?= =?UTF-8?q?P"=20because=20it=C2=B4s=20not=20needed=20anymore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Kampert --- .gitmodules | 7 +- app/Kconfig | 76 +-- app/boards/debug.conf | 4 +- app/drivers/display/CMakeLists.txt | 10 +- app/drivers/display/Kconfig | 4 +- app/drivers/display/Kconfig.gc9a01 | 8 - app/drivers/display/gc9a01/CMakeLists.txt | 1 + app/drivers/display/gc9a01/Kconfig | 6 + app/drivers/display/{ => gc9a01}/gc9a01.c | 0 app/drivers/input/CMakeLists.txt | 10 +- app/drivers/input/Kconfig | 32 +- app/drivers/input/cst816s/CMakeLists.txt | 1 + app/drivers/input/cst816s/Kconfig | 29 + .../input/{ => cst816s}/input_cst816s.c | 0 app/drivers/sensor/CMakeLists.txt | 3 +- app/drivers/sensor/Kconfig | 3 +- app/drivers/sensor/bme68x_iaq/CMakeLists.txt | 47 ++ app/drivers/sensor/bme68x_iaq/Kconfig | 74 +++ app/drivers/sensor/bme68x_iaq/bme68x_iaq.c | 517 ++++++++++++++++++ app/drivers/sensor/bme68x_iaq/bme68x_iaq.h | 49 ++ app/prj.conf | 24 +- app/src/ext_drivers/bmi270/BMI270-Sensor_API | 1 - app/src/ext_drivers/bmi270/CMakeLists.txt | 6 +- app/src/ext_drivers/bmi270/bmi270_port.h | 5 +- app/src/managers/zsw_power_manager.c | 4 +- app/src/sensors/zsw_env_sensor.c | 3 +- 26 files changed, 827 insertions(+), 97 deletions(-) delete mode 100644 app/drivers/display/Kconfig.gc9a01 create mode 100644 app/drivers/display/gc9a01/CMakeLists.txt create mode 100644 app/drivers/display/gc9a01/Kconfig rename app/drivers/display/{ => gc9a01}/gc9a01.c (100%) create mode 100644 app/drivers/input/cst816s/CMakeLists.txt create mode 100644 app/drivers/input/cst816s/Kconfig rename app/drivers/input/{ => cst816s}/input_cst816s.c (100%) create mode 100644 app/drivers/sensor/bme68x_iaq/CMakeLists.txt create mode 100644 app/drivers/sensor/bme68x_iaq/Kconfig create mode 100644 app/drivers/sensor/bme68x_iaq/bme68x_iaq.c create mode 100644 app/drivers/sensor/bme68x_iaq/bme68x_iaq.h delete mode 160000 app/src/ext_drivers/bmi270/BMI270-Sensor_API diff --git a/.gitmodules b/.gitmodules index ed395399..ef9aaff1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,12 @@ -[submodule "app/src/ext_drivers/bmi270/BMI270-Sensor_API"] - path = app/src/ext_drivers/bmi270/BMI270-Sensor_API +[submodule "app/src/ext_drivers/bmi270/BMI270-Sensor-API"] + path = app/src/ext_drivers/bmi270/BMI270-Sensor-API url = https://github.com/boschsensortec/BMI270-Sensor-API.git [submodule "app/src/ext_drivers/bmp581/BMP5-Sensor-API"] path = app/src/ext_drivers/bmp581/BMP5-Sensor-API url = https://github.com/boschsensortec/BMP5-Sensor-API.git +[submodule "app/src/ext_drivers/BME68x-Sensor-API"] + path = app/src/ext_drivers/BME68x-Sensor-API + url = https://github.com/boschsensortec/BME68x-Sensor-API.git [submodule "app/src/applications/2048/2048_lib"] path = app/src/applications/2048/2048_lib url = https://github.com/100askTeam/lv_lib_100ask.git diff --git a/app/Kconfig b/app/Kconfig index 51c1f1fb..85983a62 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -78,6 +78,20 @@ menu "ZSWatch" endchoice endmenu + menu "External" + config EXTERNAL_USE_BOSCH_BSEC + bool + prompt "Enable the usage of the external Bosch BSEC 2 library." + default y + endmenu + + menu "Configuration" + config CONFIGURATION_IDLE_TIMEOUT_SECONDS + int + prompt "Idle timeout in seconds" + default 20 + endmenu + config DISABLE_PAIRING_REQUIRED bool prompt "Disable encryption required for BLE connection (pairing/bonding)" @@ -85,41 +99,35 @@ menu "ZSWatch" help "Disable encryption for BLE connection (pairing/bonding). Used only for debugging purposes." - config DISPLAY_FAST_WAKEUP - bool - prompt "Don't turn off the display power, just put it into sleep mode when going idle." - default n - help - "When screen goes idle, don't turn off the display power, just put it into sleep mode. This will consume more power, but will make the screen wake up faster." - -endmenu + menu "SPI RTT Flash Loader" + config SPI_FLASH_LOADER + bool + prompt "Enable SPI flash loader" + default y + select USE_SEGGER_RTT + #select SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL + help + "Enable SPI flash loader" + + if SPI_FLASH_LOADER + config ERASE_PROGRESSIVELY + bool + depends on SPI_FLASH_LOADER + prompt "Erase sectors one by one when writing instead of full partition before starting." + default y + help + "If not writing to the full partition this will be faster" + + config RTT_TRANSFER_CHANNEL + int + depends on SPI_FLASH_LOADER + prompt "The RTT channel to use for transfer of data to and form flash" + default 2 + help + "If RTT logging is enabled, channel 0 is used for this, hence avoid 1 in this scenario." + endif + endmenu -menu "SPI RTT Flash Loader" - config SPI_FLASH_LOADER - bool - prompt "Enable SPI flash loader" - default y - select USE_SEGGER_RTT - #select SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL - help - "Enable SPI flash loader" -if SPI_FLASH_LOADER - config ERASE_PROGRESSIVELY - bool - depends on SPI_FLASH_LOADER - prompt "Erase sectors one by one when writing instead of full partition before starting." - default y - help - "If not writing to the full partition this will be faster" - - config RTT_TRANSFER_CHANNEL - int - depends on SPI_FLASH_LOADER - prompt "The RTT channel to use for transfer of data to and form flash" - default 2 - help - "If RTT logging is enabled, channel 0 is used for this, hence avoid 1 in this scenario." -endif endmenu rsource "drivers/Kconfig" diff --git a/app/boards/debug.conf b/app/boards/debug.conf index cc676e5a..0a073a24 100644 --- a/app/boards/debug.conf +++ b/app/boards/debug.conf @@ -13,6 +13,4 @@ CONFIG_LOG_BACKEND_RTT_MODE_BLOCK=y CONFIG_LOG_BUFFER_SIZE=4096 CONFIG_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE=128 CONFIG_LOG_BLOCK_IN_THREAD=y -# End of SEGGER RTT - -CONFIG_INPUT=y +# End of SEGGER RTT \ No newline at end of file diff --git a/app/drivers/display/CMakeLists.txt b/app/drivers/display/CMakeLists.txt index ce022446..51a2f8f5 100644 --- a/app/drivers/display/CMakeLists.txt +++ b/app/drivers/display/CMakeLists.txt @@ -1 +1,9 @@ -zephyr_sources_ifdef(CONFIG_GC9A01 gc9a01.c) +# TODO: +# Weird compilation issue when not using +# zephyr_library() +# zephyr_library_sources(apds9306.c) +# +# -> ..add_library cannot create target "..__app__drivers__sensor" because another +# -> target with the same name already exists. The existing target is a static... +# +add_subdirectory_ifdef(CONFIG_GC9A01 gc9a01) \ No newline at end of file diff --git a/app/drivers/display/Kconfig b/app/drivers/display/Kconfig index c0b9d718..f822a234 100644 --- a/app/drivers/display/Kconfig +++ b/app/drivers/display/Kconfig @@ -1 +1,3 @@ -rsource "Kconfig.gc9a01" \ No newline at end of file +if DISPLAY + rsource "gc9a01/Kconfig" +endif \ No newline at end of file diff --git a/app/drivers/display/Kconfig.gc9a01 b/app/drivers/display/Kconfig.gc9a01 deleted file mode 100644 index 6430515d..00000000 --- a/app/drivers/display/Kconfig.gc9a01 +++ /dev/null @@ -1,8 +0,0 @@ -# GC9A01 display controller configuration options - -config GC9A01 - bool "GC9A01 compatible display controller driver" - default n - select SPI - help - Enable driver for GC9A01 compatible controller. diff --git a/app/drivers/display/gc9a01/CMakeLists.txt b/app/drivers/display/gc9a01/CMakeLists.txt new file mode 100644 index 00000000..a58a904e --- /dev/null +++ b/app/drivers/display/gc9a01/CMakeLists.txt @@ -0,0 +1 @@ +zephyr_sources(gc9a01.c) \ No newline at end of file diff --git a/app/drivers/display/gc9a01/Kconfig b/app/drivers/display/gc9a01/Kconfig new file mode 100644 index 00000000..8bc74830 --- /dev/null +++ b/app/drivers/display/gc9a01/Kconfig @@ -0,0 +1,6 @@ +menuconfig GC9A01 + bool "GC9A01 compatible display controller driver" + default n + select SPI + help + Enable driver for GC9A01 compatible controller. \ No newline at end of file diff --git a/app/drivers/display/gc9a01.c b/app/drivers/display/gc9a01/gc9a01.c similarity index 100% rename from app/drivers/display/gc9a01.c rename to app/drivers/display/gc9a01/gc9a01.c diff --git a/app/drivers/input/CMakeLists.txt b/app/drivers/input/CMakeLists.txt index b88871ab..e7e79d9c 100644 --- a/app/drivers/input/CMakeLists.txt +++ b/app/drivers/input/CMakeLists.txt @@ -1 +1,9 @@ -zephyr_sources_ifdef(CONFIG_INPUT_MODIFIED_CST816S input_cst816s.c) +# TODO: +# Weird compilation issue when not using +# zephyr_library() +# zephyr_library_sources(apds9306.c) +# +# -> ..add_library cannot create target "..__app__drivers__sensor" because another +# -> target with the same name already exists. The existing target is a static... +# +add_subdirectory_ifdef(CONFIG_INPUT_MODIFIED_CST816S cst816s) \ No newline at end of file diff --git a/app/drivers/input/Kconfig b/app/drivers/input/Kconfig index 43adc895..694dd980 100644 --- a/app/drivers/input/Kconfig +++ b/app/drivers/input/Kconfig @@ -1,29 +1,3 @@ -# Copyright (c) 2020 Qingsong Gou -# Copyright (c) 2022 Jakob Krantz -# SPDX-License-Identifier: Apache-2.0 - -menuconfig INPUT_MODIFIED_CST816S - bool "Use modified out of tree CST816S capacitive touch panel driver" - default y - depends on DT_HAS_HYNITRON_CST816S_ENABLED - select I2C - help - Enable modified out of tree driver for hynitron cst816s touch panel. - -if INPUT_MODIFIED_CST816S - -config INPUT_CST816S_PERIOD - int "Sample period" - depends on !INPUT_CST816S_INTERRUPT - default 20 - help - Sample period in milliseconds when in polling mode. - -config INPUT_CST816S_INTERRUPT - bool "Interrupt support" - default y - depends on GPIO - help - Enable interrupt support (requires GPIO). - -endif # INPUT_CST816S +if INPUT + rsource "cst816s/Kconfig" +endif \ No newline at end of file diff --git a/app/drivers/input/cst816s/CMakeLists.txt b/app/drivers/input/cst816s/CMakeLists.txt new file mode 100644 index 00000000..276410b3 --- /dev/null +++ b/app/drivers/input/cst816s/CMakeLists.txt @@ -0,0 +1 @@ +zephyr_sources(input_cst816s.c) \ No newline at end of file diff --git a/app/drivers/input/cst816s/Kconfig b/app/drivers/input/cst816s/Kconfig new file mode 100644 index 00000000..43adc895 --- /dev/null +++ b/app/drivers/input/cst816s/Kconfig @@ -0,0 +1,29 @@ +# Copyright (c) 2020 Qingsong Gou +# Copyright (c) 2022 Jakob Krantz +# SPDX-License-Identifier: Apache-2.0 + +menuconfig INPUT_MODIFIED_CST816S + bool "Use modified out of tree CST816S capacitive touch panel driver" + default y + depends on DT_HAS_HYNITRON_CST816S_ENABLED + select I2C + help + Enable modified out of tree driver for hynitron cst816s touch panel. + +if INPUT_MODIFIED_CST816S + +config INPUT_CST816S_PERIOD + int "Sample period" + depends on !INPUT_CST816S_INTERRUPT + default 20 + help + Sample period in milliseconds when in polling mode. + +config INPUT_CST816S_INTERRUPT + bool "Interrupt support" + default y + depends on GPIO + help + Enable interrupt support (requires GPIO). + +endif # INPUT_CST816S diff --git a/app/drivers/input/input_cst816s.c b/app/drivers/input/cst816s/input_cst816s.c similarity index 100% rename from app/drivers/input/input_cst816s.c rename to app/drivers/input/cst816s/input_cst816s.c diff --git a/app/drivers/sensor/CMakeLists.txt b/app/drivers/sensor/CMakeLists.txt index 84fbd383..8338ec89 100644 --- a/app/drivers/sensor/CMakeLists.txt +++ b/app/drivers/sensor/CMakeLists.txt @@ -11,4 +11,5 @@ # -> ..add_library cannot create target "..__app__drivers__sensor" because another # -> target with the same name already exists. The existing target is a static... # -add_subdirectory_ifdef(CONFIG_APDS9306 apds9306) \ No newline at end of file +add_subdirectory_ifdef(CONFIG_APDS9306 apds9306) +add_subdirectory_ifdef(CONFIG_BME68X_IAQ bme68x_iaq) \ No newline at end of file diff --git a/app/drivers/sensor/Kconfig b/app/drivers/sensor/Kconfig index 287b7ee6..9c8f8547 100644 --- a/app/drivers/sensor/Kconfig +++ b/app/drivers/sensor/Kconfig @@ -1,3 +1,4 @@ if SENSOR rsource "apds9306/Kconfig" -endif # SENSOR \ No newline at end of file + rsource "bme68x_iaq/Kconfig" +endif \ No newline at end of file diff --git a/app/drivers/sensor/bme68x_iaq/CMakeLists.txt b/app/drivers/sensor/bme68x_iaq/CMakeLists.txt new file mode 100644 index 00000000..ae3182ed --- /dev/null +++ b/app/drivers/sensor/bme68x_iaq/CMakeLists.txt @@ -0,0 +1,47 @@ +if(CONFIG_EXTERNAL_USE_BOSCH_BSEC) + zephyr_library_compile_definitions_ifdef(CONFIG_BME68X_IAQ_SAMPLE_RATE_ULTRA_LOW_POWER + BSEC_SAMPLE_RATE=BSEC_SAMPLE_RATE_ULP + BSEC_SAMPLE_PERIOD_S=300 + ) + + zephyr_library_compile_definitions_ifdef(CONFIG_BME68X_IAQ_SAMPLE_RATE_LOW_POWER + BSEC_SAMPLE_RATE=BSEC_SAMPLE_RATE_LP + BSEC_SAMPLE_PERIOD_S=3 + ) + + zephyr_library_compile_definitions_ifdef(CONFIG_BME68X_IAQ_SAMPLE_RATE_CONTINUOUS + BSEC_SAMPLE_RATE=BSEC_SAMPLE_RATE_CONT + BSEC_SAMPLE_PERIOD_S=1 + ) + + #if (CONFIG_FP_HARDABI) + # if (CONFIG_CPU_CORTEX_M33) + # zephyr_library_import(bsec_lib ${ZEPHYR_BASE}/../modules/lib/bsec/src/cortex-m33/fpv5-sp-d16-hard/libalgobsec.a) + # elseif(CONFIG_CPU_CORTEX_M4) + # zephyr_library_import(bsec_lib ${ZEPHYR_BASE}/../modules/lib/bsec/src/cortex-m4/fpv4-sp-d16-hard/libalgobsec.a) + # else() + # assert(0 "Unsupported configuration.") + # endif() + #else() + # zephyr_library_compile_definitions(BME68X_DO_NOT_USE_FPU) + # if (CONFIG_CPU_CORTEX_M33 OR CONFIG_CPU_CORTEX_M4) + # zephyr_library_import(bsec_lib ${ZEPHYR_BASE}/../modules/lib/bsec/src/cortex-m4/libalgobsec.a) + # else() + # assert(0 "Unsupported configuration.") + # endif() + #endif() + #zephyr_library_sources(bme68x_iaq.c) + + zephyr_sources(bme68x_iaq.c) + + # Add the Bosch BSEC2 library to the project. + zephyr_library_include_directories(${PROJECT_SOURCE_DIR}/src/ext_drivers/BSEC2/algo/normal_version/inc) + set(LIB_DIR ${PROJECT_SOURCE_DIR}/src/ext_drivers/BSEC2/algo/normal_version/bin/gcc/Cortex_M4) + add_library(libalgobsec STATIC IMPORTED PRIVATE) + set_target_properties(libalgobsec PROPERTIES IMPORTED_LOCATION ${LIB_DIR}/libalgobsec.a) + target_link_libraries(app PUBLIC libalgobsec) + + # Add the Bosch BME68X-Sensor-API to the project. + zephyr_library_include_directories(${PROJECT_SOURCE_DIR}/src/ext_drivers/BME68x-Sensor-API) + zephyr_sources(${PROJECT_SOURCE_DIR}/src/ext_drivers/BME68x-Sensor-API/bme68x.c) +endif() \ No newline at end of file diff --git a/app/drivers/sensor/bme68x_iaq/Kconfig b/app/drivers/sensor/bme68x_iaq/Kconfig new file mode 100644 index 00000000..98e06944 --- /dev/null +++ b/app/drivers/sensor/bme68x_iaq/Kconfig @@ -0,0 +1,74 @@ +# +# Copyright (c) 2023 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +config BME68X_IAQ + bool "Use Bosch BSEC library" + depends on !BME680 + depends on SETTINGS && !SETTINGS_NONE + depends on I2C + help + Enable the use of Bosch BSEC library. + This configuration depends on the BME680 Zephyr driver being disabled. + +if BME68X_IAQ + +config BME68X_IAQ_SAVE_INTERVAL_MINUTES + int "Period in minutes after which BSEC state is saved to flash" + default 60 + +config BME68X_IAQ_THREAD_STACK_SIZE + int "BSEC thread stack size" + default 4096 + +config BME68X_IAQ_EXPECTED_AMBIENT_TEMP + int "Expected ambient temperature in C" + default 25 + +config BME68X_IAQ_TEMPERATURE_OFFSET + int "BSEC temperature offset in centidegrees" + default 200 if BOARD_THINGY91_NRF9160_NS + default 200 if BOARD_THINGY53_NRF5340_CPUAPP + default 200 if BOARD_THINGY53_NRF5340_CPUAPP_NS + default 200 if BOARD_THINGY53_NRF5340_CPUNET + default 100 + help + BSEC temperature offset. To account for external heat sources on the board. + The actual value is divided by 100. This is due to the Kconfig parser + not supporting floating point types. + The default value 200 is translated to 2.0 degrees celsius offset. + +choice BME68X_IAQ_SAMPLE_RATE + prompt "Bosch BSEC sample mode" + default BME68X_IAQ_SAMPLE_RATE_LOW_POWER + help + Configuration that sets how often sensor data is sampled from the BSEC library. + Each mode corresponds an internal preset that decides how often data is sampled from the + BME680. + +config BME68X_IAQ_SAMPLE_RATE_ULTRA_LOW_POWER + bool "BSEC low ultra power mode" + help + Sample data from BSEC severy 300 seconds. + +config BME68X_IAQ_SAMPLE_RATE_LOW_POWER + bool "BSEC low power mode" + help + Sample data from BSEC every 3 seconds. + +config BME68X_IAQ_SAMPLE_RATE_CONTINUOUS + bool "BSEC continuous mode" + help + Sample data from BSEC every second. + This is a particularly power-hungry sample mode that should only be considered for + testing purposes. + +endchoice # BME68X_IAQ_SAMPLE_RATE + +module = BME68X_IAQ +module-str = BME68X_IAQ +source "subsys/logging/Kconfig.template.log_config" + +endif # BME68X_IAQ diff --git a/app/drivers/sensor/bme68x_iaq/bme68x_iaq.c b/app/drivers/sensor/bme68x_iaq/bme68x_iaq.c new file mode 100644 index 00000000..b1f4c0d6 --- /dev/null +++ b/app/drivers/sensor/bme68x_iaq/bme68x_iaq.c @@ -0,0 +1,517 @@ +#include +#include +#include +#include +#include + +#include + +#include "bme68x_iaq.h" + +//LOG_MODULE_REGISTER(bsec, CONFIG_BME68X_IAQ_LOG_LEVEL); +LOG_MODULE_REGISTER(bsec, LOG_LEVEL_INF); + +#define DT_DRV_COMPAT bosch_bme680 + +#define BSEC_TOTAL_HEAT_DUR UINT16_C(140) +#define BSEC_INPUT_PRESENT(x, shift) (x.process_data & (1 << (shift - 1))) + +/* Temperature offset due to external heat sources. */ +static const float temp_offset = (CONFIG_BME68X_IAQ_TEMPERATURE_OFFSET / (float)100); + +/* Define which sensor values to request. + * The order is not important, but output_ready needs to be updated if different types + * of sensor values are requested. + */ +static const bsec_sensor_configuration_t bsec_requested_virtual_sensors[4] = { + { + .sensor_id = BSEC_OUTPUT_IAQ, + .sample_rate = BSEC_SAMPLE_RATE, + }, + { + .sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, + .sample_rate = BSEC_SAMPLE_RATE, + }, + { + .sensor_id = BSEC_OUTPUT_RAW_PRESSURE, + .sample_rate = BSEC_SAMPLE_RATE, + }, + { + .sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, + .sample_rate = BSEC_SAMPLE_RATE, + }, +}; + + +/* Definitions used to store and retrieve BSEC state from the settings API */ +#define SETTINGS_NAME_BSEC "bsec" +#define SETTINGS_KEY_STATE "state" +#define SETTINGS_BSEC_STATE SETTINGS_NAME_BSEC "/" SETTINGS_KEY_STATE + +/* Stack size of internal BSEC thread. */ +static K_THREAD_STACK_DEFINE(thread_stack, CONFIG_BME68X_IAQ_THREAD_STACK_SIZE); + +/* Used for a timeout for when BSEC's state should be saved. */ +static K_TIMER_DEFINE(bsec_save_state_timer, NULL, NULL); + +/* I2C spec for BME68x sensor */ +static struct i2c_dt_spec bme68x_i2c_spec; + +/* Semaphore to make sure output data isn't read while being updated */ +static K_SEM_DEFINE(output_sem, 1, 1); + +static int settings_load_handler(const char *key, size_t len, + settings_read_cb read_cb, void *cb_arg, void *param) +{ + ARG_UNUSED(key); + struct bme68x_iaq_data *data = param; + + if (len > ARRAY_SIZE(data->state_buffer)) { + return -EINVAL; + } + + data->state_len = read_cb(cb_arg, data->state_buffer, len); + + if (data->state_len > 0) { + return 0; + } + + LOG_WRN("No settings data read"); + return -ENODATA; +} + + +/* Export current state of BSEC and save it to flash. */ +static void state_save(const struct device *dev) +{ + int ret; + struct bme68x_iaq_data *data = dev->data; + + LOG_DBG("saving state to flash"); + + ret = bsec_get_state(0, data->state_buffer, ARRAY_SIZE(data->state_buffer), + data->work_buffer, ARRAY_SIZE(data->work_buffer), + &data->state_len); + + __ASSERT(ret == BSEC_OK, "bsec_get_state failed."); + __ASSERT(data->state_len <= sizeof(data->state_buffer), "state buffer too big to save."); + + ret = settings_save_one(SETTINGS_BSEC_STATE, data->state_buffer, data->state_len); + + __ASSERT(ret == 0, "storing state to flash failed."); +} + +/* I2C bus write forwarder for bme68x driver */ +static int8_t bus_write(uint8_t reg_addr, const uint8_t *reg_data_ptr, uint32_t len, void *intf_ptr) +{ + uint8_t buf[len + 1]; + + buf[0] = reg_addr; + memcpy(&buf[1], reg_data_ptr, len); + + return i2c_write_dt(&bme68x_i2c_spec, buf, ARRAY_SIZE(buf)); +} + +/* I2C bus read forwarder for bme68x driver */ +static int8_t bus_read(uint8_t reg_addr, uint8_t *reg_data_ptr, uint32_t len, void *intf_ptr) +{ + return i2c_write_read_dt(&bme68x_i2c_spec, ®_addr, 1, reg_data_ptr, len); +} + +/* delay function for bme68x driver */ +static void delay_us(uint32_t period, void *intf_ptr) +{ + k_usleep((int32_t) period); +} + +/* function to handle output of BSEC */ +static void output_ready(const struct device *dev, const bsec_output_t *outputs, uint8_t n_outputs) +{ + struct bme68x_iaq_data *data = dev->data; + + k_sem_take(&output_sem, K_FOREVER); + for (size_t i = 0; i < n_outputs; ++i) { + switch (outputs[i].sensor_id) { + case BSEC_OUTPUT_IAQ: + data->latest.air_quality = (uint16_t) outputs[i].signal; + LOG_DBG("IAQ: %d", data->latest.air_quality); + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE: + data->latest.temperature = (double) outputs[i].signal; + LOG_DBG("Temp: %.2f C", data->latest.temperature); + break; + case BSEC_OUTPUT_RAW_PRESSURE: + data->latest.pressure = (double) outputs[i].signal; + LOG_DBG("Press: %.2f Pa", data->latest.pressure); + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY: + data->latest.humidity = (double) outputs[i].signal; + LOG_DBG("Hum: %.2f %%", data->latest.humidity); + break; + default: + LOG_WRN("unknown bsec output id: %d", outputs[i].sensor_id); + break; + } + } + k_sem_give(&output_sem); + if (data->trg_handler != NULL) { + data->trg_handler(dev, data->trigger); + } +} + +/* convert raw bme68x output to valid input for BSEC */ +static size_t sensor_data_to_bsec_inputs(bsec_bme_settings_t sensor_settings, + const struct bme68x_data *data, + bsec_input_t *inputs, uint64_t timestamp_ns) +{ + size_t i = 0; + + if (BSEC_INPUT_PRESENT(sensor_settings, BSEC_INPUT_TEMPERATURE)) { + /* append heatsource input */ + inputs[i].sensor_id = BSEC_INPUT_HEATSOURCE; + inputs[i].signal = temp_offset; + inputs[i].time_stamp = timestamp_ns; + LOG_DBG("Temp offset: %.2f", inputs[i].signal); + i++; + + /* append temperature input */ + inputs[i].sensor_id = BSEC_INPUT_TEMPERATURE; + inputs[i].signal = data->temperature; + + if (IS_ENABLED(BME68X_DO_NOT_USE_FPU)) { + /* in this config, temperature is output in centidegrees */ + inputs[i].signal /= 100.0f; + } + + inputs[i].time_stamp = timestamp_ns; + LOG_DBG("Temp: %.2f", inputs[i].signal); + i++; + } + if (BSEC_INPUT_PRESENT(sensor_settings, BSEC_INPUT_HUMIDITY)) { + inputs[i].sensor_id = BSEC_INPUT_HUMIDITY; + inputs[i].signal = data->humidity; + + if (IS_ENABLED(BME68X_DO_NOT_USE_FPU)) { + /* in this config, humidity is output in millipercent */ + inputs[i].signal /= 1000.0f; + } + + inputs[i].time_stamp = timestamp_ns; + LOG_DBG("Hum: %.2f", inputs[i].signal); + i++; + } + if (BSEC_INPUT_PRESENT(sensor_settings, BSEC_INPUT_PRESSURE)) { + inputs[i].sensor_id = BSEC_INPUT_PRESSURE; + inputs[i].signal = data->pressure; + inputs[i].time_stamp = timestamp_ns; + LOG_DBG("Press: %.2f", inputs[i].signal); + i++; + } + if (BSEC_INPUT_PRESENT(sensor_settings, BSEC_INPUT_GASRESISTOR)) { + inputs[i].sensor_id = BSEC_INPUT_GASRESISTOR; + inputs[i].signal = data->gas_resistance; + inputs[i].time_stamp = timestamp_ns; + LOG_DBG("Gas: %.2f", inputs[i].signal); + i++; + } + if (BSEC_INPUT_PRESENT(sensor_settings, BSEC_INPUT_PROFILE_PART)) { + inputs[i].sensor_id = BSEC_INPUT_PROFILE_PART; + if (sensor_settings.op_mode == BME68X_FORCED_MODE) { + inputs[i].signal = 0; + } else { + inputs[i].signal = data->gas_index; + } + inputs[i].time_stamp = timestamp_ns; + LOG_DBG("Profile: %.2f", inputs[i].signal); + i++; + } + return i; +} + +/* convert and apply bme68x settings chosen by BSEC */ +static int apply_sensor_settings(const struct device *dev, bsec_bme_settings_t sensor_settings) +{ + int ret; + struct bme68x_conf config = {0}; + struct bme68x_heatr_conf heater_config = {0}; + struct bme68x_iaq_data *data = dev->data; + + heater_config.enable = BME68X_ENABLE; + heater_config.heatr_temp = sensor_settings.heater_temperature; + heater_config.heatr_dur = sensor_settings.heater_duration; + heater_config.heatr_temp_prof = sensor_settings.heater_temperature_profile; + heater_config.heatr_dur_prof = sensor_settings.heater_duration_profile; + heater_config.profile_len = sensor_settings.heater_profile_len; + heater_config.shared_heatr_dur = 0; + + switch (sensor_settings.op_mode) { + case BME68X_PARALLEL_MODE: + /* this block is only executed for BME68X_PARALLEL_MODE */ + /* shared heating duration in milliseconds (converted from microseconds) */ + heater_config.shared_heatr_dur = + BSEC_TOTAL_HEAT_DUR - + (bme68x_get_meas_dur(sensor_settings.op_mode, &config, &data->dev) + / INT64_C(1000)); + + __fallthrough; + case BME68X_FORCED_MODE: + /* this block is executed for any measurement mode */ + ret = bme68x_get_conf(&config, &data->dev); + if (ret) { + LOG_ERR("bme68x_get_conf err: %d", ret); + return ret; + } + + config.os_hum = sensor_settings.humidity_oversampling; + config.os_temp = sensor_settings.temperature_oversampling; + config.os_pres = sensor_settings.pressure_oversampling; + + ret = bme68x_set_conf(&config, &data->dev); + if (ret) { + LOG_ERR("bme68x_set_conf err: %d", ret); + return ret; + } + + bme68x_set_heatr_conf(sensor_settings.op_mode, &heater_config, &data->dev); + + __fallthrough; + case BME68X_SLEEP_MODE: + /* this block is executed for all modes */ + ret = bme68x_set_op_mode(sensor_settings.op_mode, &data->dev); + if (ret) { + LOG_ERR("bme68x_set_op_mode err: %d", ret); + return ret; + } + break; + default: + LOG_ERR("unknown op mode: %d", sensor_settings.op_mode); + } + return 0; +} + +static void fetch_and_process_output(const struct device *dev, + bsec_bme_settings_t *sensor_settings, + uint64_t timestamp_ns) +{ + uint8_t n_fields = 0; + uint8_t n_outputs = 0; + uint8_t n_inputs = 0; + bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR] = {0}; + bsec_output_t outputs[ARRAY_SIZE(bsec_requested_virtual_sensors)] = {0}; + struct bme68x_data sensor_data[3] = {0}; + struct bme68x_iaq_data *data = dev->data; + int ret = bme68x_get_data(sensor_settings->op_mode, sensor_data, &n_fields, &data->dev); + + if (ret) { + LOG_DBG("bme68x_get_data err: %d", ret); + return; + } + + for (size_t i = 0; i < n_fields; ++i) { + n_outputs = ARRAY_SIZE(bsec_requested_virtual_sensors); + n_inputs = sensor_data_to_bsec_inputs(*sensor_settings, + sensor_data + i, + inputs, timestamp_ns); + + if (n_inputs == 0) { + continue; + } + ret = bsec_do_steps(inputs, n_inputs, outputs, &n_outputs); + if (ret != BSEC_OK) { + LOG_ERR("bsec_do_steps err: %d", ret); + continue; + } + output_ready(dev, outputs, n_outputs); + } +} + +/* Manage all recurrings tasks for the sensor: + * - update device settings according to BSEC + * - fetch measurement values + * - update BSEC state + * - periodically save BSEC state to flash + */ +static void bsec_thread_fn(const struct device *dev) +{ + int ret; + bsec_bme_settings_t sensor_settings = {0}; + + while (true) { + uint64_t timestamp_ns = k_ticks_to_ns_floor64(k_uptime_ticks()); + + if (timestamp_ns < sensor_settings.next_call) { + LOG_DBG("bsec_sensor_control not ready yet"); + k_sleep(K_NSEC(sensor_settings.next_call - timestamp_ns)); + continue; + } + + memset(&sensor_settings, 0, sizeof(sensor_settings)); + ret = bsec_sensor_control((int64_t)timestamp_ns, &sensor_settings); + if (ret != BSEC_OK) { + LOG_ERR("bsec_sensor_control err: %d", ret); + continue; + } + + if (apply_sensor_settings(dev, sensor_settings)) { + continue; + } + + if (sensor_settings.trigger_measurement && + sensor_settings.op_mode != BME68X_SLEEP_MODE) { + fetch_and_process_output(dev, &sensor_settings, timestamp_ns); + } + + /* if save timer is expired, save and restart timer */ + if (k_timer_remaining_get(&bsec_save_state_timer) == 0) { + state_save(dev); + k_timer_start(&bsec_save_state_timer, + K_MINUTES(CONFIG_BME68X_IAQ_SAVE_INTERVAL_MINUTES), + K_NO_WAIT); + } + + k_sleep(K_SECONDS(BSEC_SAMPLE_PERIOD_S)); + } + +} + +static int bme68x_bsec_init(const struct device *dev) +{ + int err; + struct bme68x_iaq_data *data = dev->data; + const struct bme68x_iaq_config *config = dev->config; + + bme68x_i2c_spec = config->i2c; + + err = settings_subsys_init(); + if (err) { + LOG_ERR("settings_subsys_init, error: %d", err); + return err; + } + + err = settings_load_subtree_direct(SETTINGS_BSEC_STATE, settings_load_handler, data); + if (err) { + LOG_ERR("settings_load_subtree, error: %d", err); + return err; + } + + if (!device_is_ready(bme68x_i2c_spec.bus)) { + LOG_ERR("I2C device not ready"); + return -ENODEV; + } + + data->dev.intf = BME68X_I2C_INTF; + data->dev.intf_ptr = NULL; + data->dev.read = bus_read; + data->dev.write = bus_write; + data->dev.delay_us = delay_us; + data->dev.amb_temp = CONFIG_BME68X_IAQ_EXPECTED_AMBIENT_TEMP; + + err = bme68x_init(&data->dev); + if (err) { + LOG_ERR("Failed to init bme68x: %d", err); + return err; + } + + err = bsec_init(); + if (err != BSEC_OK) { + LOG_ERR("Failed to init BSEC: %d", err); + return err; + } + + err = bsec_set_state(data->state_buffer, data->state_len, + data->work_buffer, ARRAY_SIZE(data->work_buffer)); + if (err != BSEC_OK && err != BSEC_E_CONFIG_EMPTY) { + LOG_ERR("Failed to set BSEC state: %d", err); + } else if (err == BSEC_OK) { + LOG_DBG("Setting BSEC state successful."); + } + + bsec_update_subscription(bsec_requested_virtual_sensors, + ARRAY_SIZE(bsec_requested_virtual_sensors), + data->required_sensor_settings, &data->n_required_sensor_settings); + + k_thread_create(&data->thread, + thread_stack, + CONFIG_BME68X_IAQ_THREAD_STACK_SIZE, + (k_thread_entry_t)bsec_thread_fn, + (void *)dev, NULL, NULL, K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT); + + k_timer_start(&bsec_save_state_timer, + K_MINUTES(CONFIG_BME68X_IAQ_SAVE_INTERVAL_MINUTES), + K_NO_WAIT); + return 0; +} + +static int bme68x_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct bme68x_iaq_data *data = dev->data; + + if (trig->type != SENSOR_TRIG_TIMER) { + LOG_ERR("Unsupported sensor channel"); + return -ENOTSUP; + } + + if ((trig->chan == SENSOR_CHAN_ALL) + || (trig->chan == SENSOR_CHAN_HUMIDITY) + || (trig->chan == SENSOR_CHAN_AMBIENT_TEMP) + || (trig->chan == SENSOR_CHAN_PRESS) + || (trig->chan == SENSOR_CHAN_IAQ)) { + data->trigger = trig; + data->trg_handler = handler; + } else { + LOG_ERR("Unsupported sensor channel"); + return -ENOTSUP; + } + return 0; +} + +static int bme68x_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + /* fetching is a requirement for the API */ + return 0; +} + +static int bme68x_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct bme68x_iaq_data *data = dev->data; + int result = 0; + + k_sem_take(&output_sem, K_FOREVER); + if (chan == SENSOR_CHAN_HUMIDITY) { + sensor_value_from_double(val, data->latest.humidity); + } else if (chan == SENSOR_CHAN_AMBIENT_TEMP) { + sensor_value_from_double(val, data->latest.temperature); + } else if (chan == SENSOR_CHAN_PRESS) { + sensor_value_from_double(val, data->latest.pressure); + } else if (chan == SENSOR_CHAN_IAQ) { + val->val1 = data->latest.air_quality; + val->val2 = 0; + } else { + LOG_ERR("Unsupported sensor channel"); + result = -ENOTSUP; + } + k_sem_give(&output_sem); + return result; +} + +static const struct sensor_driver_api bme68x_driver_api = { + .sample_fetch = &bme68x_sample_fetch, + .channel_get = &bme68x_channel_get, + .trigger_set = bme68x_trigger_set, +}; + +/* there can be only one device supported here because of BSECs internal state */ +static struct bme68x_iaq_config config_0 = { + .i2c = I2C_DT_SPEC_INST_GET(0), +}; +static struct bme68x_iaq_data data_0; + +SENSOR_DEVICE_DT_INST_DEFINE(0, bme68x_bsec_init, NULL, + &data_0, + &config_0, + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, + &bme68x_driver_api); diff --git a/app/drivers/sensor/bme68x_iaq/bme68x_iaq.h b/app/drivers/sensor/bme68x_iaq/bme68x_iaq.h new file mode 100644 index 00000000..f2035b3b --- /dev/null +++ b/app/drivers/sensor/bme68x_iaq/bme68x_iaq.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include "bme68x.h" +#include "bme68x_iaq.h" +#include "bsec_interface.h" + +#define SENSOR_CHAN_IAQ (SENSOR_CHAN_PRIV_START + 1) + +struct bme_sample_result { + double temperature; + double humidity; + double pressure; + uint16_t air_quality; +}; + +struct bme68x_iaq_config { + const struct i2c_dt_spec i2c; +}; + +struct bme68x_iaq_data { + /* Variable to store intermediate sample result */ + struct bme_sample_result latest; + + /* Trigger and corresponding handler */ + sensor_trigger_handler_t trg_handler; + const struct sensor_trigger *trigger; + + /* Internal BSEC thread metadata value. */ + struct k_thread thread; + + /* Buffer used to maintain the BSEC library state. */ + uint8_t state_buffer[BSEC_MAX_STATE_BLOB_SIZE]; + + /* Size of the saved state */ + int32_t state_len; + + bsec_sensor_configuration_t required_sensor_settings[BSEC_MAX_PHYSICAL_SENSOR]; + uint8_t n_required_sensor_settings; + + /* some RAM space needed by bsec_get_state and bsec_set_state for (de-)serialization. */ + uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE]; + + bool initialized; + + struct bme68x_dev dev; +}; \ No newline at end of file diff --git a/app/prj.conf b/app/prj.conf index 76fdf798..51520c78 100644 --- a/app/prj.conf +++ b/app/prj.conf @@ -20,7 +20,6 @@ CONFIG_ADC=y CONFIG_BMI270=n CONFIG_LIS2MDL=y CONFIG_LIS2MDL_MAG_ODR_RUNTIME=y -CONFIG_BME680=y CONFIG_PINCTRL=y CONFIG_PM_DEVICE=y @@ -31,10 +30,7 @@ CONFIG_PWM=y CONFIG_SPI=y CONFIG_GC9A01=y CONFIG_INPUT=y -CONFIG_INPUT_CST816S=n -CONFIG_INPUT_MODIFIED_CST816S=y CONFIG_LV_Z_POINTER_INPUT_MSGQ_COUNT=200 -CONFIG_INPUT_CST816S_INTERRUPT=y CONFIG_LV_Z_POINTER_INPUT=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED=y @@ -182,7 +178,6 @@ CONFIG_ZBUS=y CONFIG_ZBUS_RUNTIME_OBSERVERS=y CONFIG_ZBUS_CHANNEL_NAME=y CONFIG_ZBUS_OBSERVER_NAME=y -CONFIG_DISPLAY_FAST_WAKEUP=y # Choose one or many of below CONFIG_WATCHFACE_ANALOG=n @@ -195,5 +190,22 @@ CONFIG_WATCHFACE_BACKGROUND_FLOWER=n CONFIG_WATCHFACE_BACKGROUND_PLANET=n CONFIG_WATCHFACE_BACKGROUND_NONE=n +# Default watch configuration +CONFIG_CONFIGURATION_IDLE_TIMEOUT_SECONDS=20 + +# CST816S configuration +CONFIG_INPUT_CST816S_INTERRUPT=y +CONFIG_INPUT_CST816S=n +CONFIG_INPUT_MODIFIED_CST816S=y + # APDS9306 configuration -CONFIG_APDS9306_IS_APDS9306_065=y \ No newline at end of file +CONFIG_APDS9306_IS_APDS9306_065=y + +# Bosch BSEC configuration +CONFIG_EXTERNAL_USE_BOSCH_BSEC=y +CONFIG_BME680=n +CONFIG_BME68X_IAQ=y +CONFIG_BME68X_IAQ_SAMPLE_RATE_ULTRA_LOW_POWER=y + +CONFIG_APPLICATIONS_USE_2048=n +CONFIG_APPLICATIONS_USE_COMPASS=n \ No newline at end of file diff --git a/app/src/ext_drivers/bmi270/BMI270-Sensor_API b/app/src/ext_drivers/bmi270/BMI270-Sensor_API deleted file mode 160000 index 126b685a..00000000 --- a/app/src/ext_drivers/bmi270/BMI270-Sensor_API +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 126b685af34a3f35fad497ae653c5d4741eedf6b diff --git a/app/src/ext_drivers/bmi270/CMakeLists.txt b/app/src/ext_drivers/bmi270/CMakeLists.txt index 84884100..cb07b179 100644 --- a/app/src/ext_drivers/bmi270/CMakeLists.txt +++ b/app/src/ext_drivers/bmi270/CMakeLists.txt @@ -1,8 +1,8 @@ target_include_directories(app PRIVATE .) -target_include_directories(app PRIVATE BMI270-Sensor_API) +target_include_directories(app PRIVATE BMI270-Sensor-API) target_sources(app PRIVATE - BMI270-Sensor_API/bmi270.c - BMI270-Sensor_API/bmi2.c + BMI270-Sensor-API/bmi270.c + BMI270-Sensor-API/bmi2.c bmi270_port.c ) diff --git a/app/src/ext_drivers/bmi270/bmi270_port.h b/app/src/ext_drivers/bmi270/bmi270_port.h index 2ce52a2a..1f768458 100644 --- a/app/src/ext_drivers/bmi270/bmi270_port.h +++ b/app/src/ext_drivers/bmi270/bmi270_port.h @@ -18,8 +18,9 @@ extern "C" { #include #include #include -#include "BMI270-Sensor_API/bmi2.h" -#include + +#include "BMI270-Sensor-API/bmi2.h" +#include "bmi270.h" #define CONFIG_BMI270_TRIGGER #define CONFIG_BMI270_TRIGGER_GLOBAL_THREAD diff --git a/app/src/managers/zsw_power_manager.c b/app/src/managers/zsw_power_manager.c index eda10be5..b6fdbdc2 100644 --- a/app/src/managers/zsw_power_manager.c +++ b/app/src/managers/zsw_power_manager.c @@ -33,7 +33,7 @@ LOG_MODULE_REGISTER(zsw_power_manager, LOG_LEVEL_INF); #ifdef CONFIG_BOARD_NATIVE_POSIX #define IDLE_TIMEOUT_SECONDS UINT32_MAX #else -#define IDLE_TIMEOUT_SECONDS 20 +#define IDLE_TIMEOUT_SECONDS CONFIG_CONFIGURATION_IDLE_TIMEOUT_SECONDS #endif static void update_and_publish_state(zsw_power_manager_state_t new_state); @@ -208,4 +208,4 @@ static int zsw_power_manager_init(void) return 0; } -SYS_INIT(zsw_power_manager_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); \ No newline at end of file +//SYS_INIT(zsw_power_manager_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); \ No newline at end of file diff --git a/app/src/sensors/zsw_env_sensor.c b/app/src/sensors/zsw_env_sensor.c index cd6db9da..2ac34c85 100644 --- a/app/src/sensors/zsw_env_sensor.c +++ b/app/src/sensors/zsw_env_sensor.c @@ -18,7 +18,6 @@ #include #include #include -#include #include "sensors/zsw_env_sensor.h" @@ -50,4 +49,4 @@ int zsw_env_sensor_fetch_all(float *temperature, float *pressure, float *humidit *pressure = sensor_value_to_float(&temp_sensor_val); return 0; -} +} \ No newline at end of file