diff --git a/.github/workflows/squareline.yml b/.github/workflows/squareline.yml index 907ad8d7f..8461850ef 100644 --- a/.github/workflows/squareline.yml +++ b/.github/workflows/squareline.yml @@ -8,6 +8,10 @@ on: - 'SquareLine/**' - '.github/workflows/squareline.yml' +# Required for uploading the release +permissions: + contents: write + jobs: create-squareline-packages: runs-on: ubuntu-24.04 diff --git a/README.md b/README.md index 574e51fd3..7ca3b8f88 100644 --- a/README.md +++ b/README.md @@ -79,11 +79,10 @@ Here is a summary of the available examples: | [LVGL Demos Example](examples/display_lvgl_demos) | Run the LVGL demo player - all LVGL examples are included (LVGL) |
12 boards[esp-box](bsp/esp-box)
[esp-box-3](bsp/esp-box-3)
[esp-box-lite](bsp/esp-box-lite)
[esp32_p4_function_ev_board](bsp/esp32_p4_function_ev_board)
[esp32_s2_kaluga_kit](bsp/esp32_s2_kaluga_kit)
[esp32_s3_eye](bsp/esp32_s3_eye)
[esp32_s3_korvo_2](bsp/esp32_s3_korvo_2)
[esp32_s3_lcd_ev_board](bsp/esp32_s3_lcd_ev_board)
[esp_vocat](bsp/esp_vocat)
[m5dial](bsp/m5dial)
[m5stack_core_s3](bsp/m5stack_core_s3)
[m5stack_tab5](bsp/m5stack_tab5)
| [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_lvgl_demos-) | | [Display Rotation Example](examples/display_rotation) | Rotate screen using buttons or an accelerometer (`BSP_CAPS_IMU`, if available) |
12 boards[esp-box](bsp/esp-box)
[esp-box-3](bsp/esp-box-3)
[esp-box-lite](bsp/esp-box-lite)
[esp32_p4_eye](bsp/esp32_p4_eye)
[esp32_p4_function_ev_board](bsp/esp32_p4_function_ev_board)
[esp32_s3_korvo_2](bsp/esp32_s3_korvo_2)
[esp32_s3_lcd_ev_board](bsp/esp32_s3_lcd_ev_board)
[esp_vocat](bsp/esp_vocat)
[m5dial](bsp/m5dial)
[m5stack_core](bsp/m5stack_core)
[m5stack_core_s3](bsp/m5stack_core_s3)
[m5stack_tab5](bsp/m5stack_tab5)
| [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_rotation-) | | [Display SD card Example](examples/display_sdcard) | Example of mounting an SD card using SD-MMC/SPI with display interaction. This example is also supported on boards without a display. |
5 boards[esp-box-3](bsp/esp-box-3)
[esp32_p4_function_ev_board](bsp/esp32_p4_function_ev_board)
[esp32_s3_korvo_2](bsp/esp32_s3_korvo_2)
[esp_vocat](bsp/esp_vocat)
[m5stack_tab5](bsp/m5stack_tab5)
| [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_sdcard) | -| [Sensors Example](examples/display_sensors) | Display sensor data on a monochrome screen (LVGL) |
1 board[esp32_azure_iot_kit](bsp/esp32_azure_iot_kit)
| [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_sensors-) | +| [Sensors Example](examples/display_sensors) | Acquire sensor data using the sensor hub component |
1 board[esp-box-3](bsp/esp-box-3)
| [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_sensors) | | [USB HID Example](examples/display_usb_hid) | USB HID demo (keyboard, mouse, or gamepad visualization using LVGL) |
3 boards[esp32_p4_function_ev_board](bsp/esp32_p4_function_ev_board)
[esp32_s3_usb_otg](bsp/esp32_s3_usb_otg)
[m5stack_tab5](bsp/m5stack_tab5)
| - | | [Generic Button and LED Example](examples/generic_button_led) | Minimal example using the Generic BSP: button and LED control |
2 boards[esp_bsp_devkit](bsp/esp_bsp_devkit)
[esp_bsp_generic](bsp/esp_bsp_generic)
| - | | [MQTT Example](examples/mqtt_example) | Collect sensor data and publish to an MQTT server |
1 board[esp32_azure_iot_kit](bsp/esp32_azure_iot_kit)
| - | -| [Sensors Example](examples/sensors) | Acquire sensor data using the sensor hub component |
1 board[esp-box-3](bsp/esp-box-3)
| [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=sensors) | diff --git a/bsp/esp-box-3/README.md b/bsp/esp-box-3/README.md index dcb4cf1b5..5f32084a6 100644 --- a/bsp/esp-box-3/README.md +++ b/bsp/esp-box-3/README.md @@ -55,7 +55,7 @@ ESP32-S3-BOX-3 also uses a Type-C USB connector that provides 5 V of power input | [LVGL Demos Example](https://github.com/espressif/esp-bsp/tree/master/examples/display_lvgl_demos) | Run the LVGL demo player - all LVGL examples are included (LVGL) | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_lvgl_demos-) | | [Display Rotation Example](https://github.com/espressif/esp-bsp/tree/master/examples/display_rotation) | Rotate screen using buttons or an accelerometer (`BSP_CAPS_IMU`, if available) | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_rotation-) | | [Display SD card Example](https://github.com/espressif/esp-bsp/tree/master/examples/display_sdcard) | Example of mounting an SD card using SD-MMC/SPI with display interaction. This example is also supported on boards without a display. | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_sdcard) | -| [Sensors Example](https://github.com/espressif/esp-bsp/tree/master/examples/sensors) | Acquire sensor data using the sensor hub component | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=sensors) | +| [Sensors Example](https://github.com/espressif/esp-bsp/tree/master/examples/display_sensors) | Acquire sensor data using the sensor hub component | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_sensors) | diff --git a/bsp/esp-box-3/idf_component.yml b/bsp/esp-box-3/idf_component.yml index 32e31bb95..72f909264 100644 --- a/bsp/esp-box-3/idf_component.yml +++ b/bsp/esp-box-3/idf_component.yml @@ -49,4 +49,4 @@ examples: - path: ../../examples/display_lvgl_demos - path: ../../examples/display_lvgl_benchmark - path: ../../examples/display_sdcard -- path: ../../examples/sensors +- path: ../../examples/display_sensors diff --git a/bsp/esp32_azure_iot_kit/README.md b/bsp/esp32_azure_iot_kit/README.md index b2ea76455..2acd9e44c 100644 --- a/bsp/esp32_azure_iot_kit/README.md +++ b/bsp/esp32_azure_iot_kit/README.md @@ -59,8 +59,7 @@ ESP32-Azure IoT Kit has integrated an ESP32-WROVER-B module, serial port-to-USB | Example | Description | Try with ESP Launchpad | | ------- | ----------- | ---------------------- | -| [Sensors Example](https://github.com/espressif/esp-bsp/tree/master/examples/display_sensors) | Display sensor data on a monochrome screen (LVGL) | [Flash Example](https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_sensors-) | | [MQTT Example](https://github.com/espressif/esp-bsp/tree/master/examples/mqtt_example) | Collect sensor data and publish to an MQTT server | - | - + \ No newline at end of file diff --git a/bsp/esp32_azure_iot_kit/idf_component.yml b/bsp/esp32_azure_iot_kit/idf_component.yml index 1aac2d00e..7859d5532 100644 --- a/bsp/esp32_azure_iot_kit/idf_component.yml +++ b/bsp/esp32_azure_iot_kit/idf_component.yml @@ -47,4 +47,3 @@ dependencies: examples: - path: ../../examples/mqtt_example - - path: ../../examples/display_sensors diff --git a/components/mpu6050/README.md b/components/mpu6050/README.md index 459620ea9..600db4dc6 100644 --- a/components/mpu6050/README.md +++ b/components/mpu6050/README.md @@ -38,6 +38,5 @@ This driver, along with many other components from this repository, can be used Another option is to manually create a `idf_component.yml` file. You can find more about using .yml files for components from [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). ## See Also -* [Sensors example, including the MPU6050 driver](https://github.com/espressif/esp-bsp/tree/master/examples/display_sensors) * [MPU6050 datasheet](https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf) * [MPU6000 and MPU6050 register map](https://invensense.tdk.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf) diff --git a/components/sensors/bmi270/CMakeLists.txt b/components/sensors/bmi270/CMakeLists.txt new file mode 100644 index 000000000..66884adc8 --- /dev/null +++ b/components/sensors/bmi270/CMakeLists.txt @@ -0,0 +1,9 @@ +if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3") + set(REQ esp_driver_i2c esp_driver_spi) +else() + set(REQ driver) +endif() + +idf_component_register(SRCS src/bmi270.c src/bmi270_sensor_hub.c INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS "include_priv" REQUIRES ${REQ}) + +target_link_libraries(${COMPONENT_LIB} INTERFACE "-u bmi270_impl_init") \ No newline at end of file diff --git a/components/sensors/bmi270/README.md b/components/sensors/bmi270/README.md new file mode 100644 index 000000000..c518cc6e4 --- /dev/null +++ b/components/sensors/bmi270/README.md @@ -0,0 +1,93 @@ +# Bosch BMI270 inertial measurement unit driver + +[![Component Registry](https://components.espressif.com/components/espressif/bmi270/badge.svg)](https://components.espressif.com/components/espressif/bmi270) +![maintenance-status](https://img.shields.io/badge/maintenance-actively--maintained-green.svg) + +## Features + +- Reading of the chip ID. +- Configuring output data rate and range of the onboard accelerometer and gyroscope. +- Reading normalized float values of acceleration and rotation angle. + +### Limitations + +- Missing implementation of the SPI interface. +- Advanced features such as gesture control, FIFO buffering, interrupt on data ready and auxilary interface are not supported. + +## Integration guide + +This driver, along with many other components from this repository, can be used as a package from [Espressif's IDF Component Registry](https://components.espressif.com). To include this driver in your project, run the following idf.py from the project's root directory: + +``` sh +idf.py add-dependency "espressif/bmi270==*" +``` + +## Usage guide + +### Low level driver +When using the low level driver directly then firstly initialize it by providing a valid low level driver configuration: +``` c +const bmi270_driver_config_t driver_config = { + .addr = addr, + .interface = BMI270_USE_I2C, + .i2c_bus = i2c_bus_handle +}; +bmi270_create(&driver_config, &bmi270_handle_pointer); +``` +Start acquisition with a valid measurement configuration: +``` c +const bmi270_config_t measurement_config = { + .acce_odr = BMI270_ACC_ODR_X, + .acce_range = BMI270_ACC_RANGE_X, + .gyro_odr = BMI270_GYR_ODR_X, + .gyro_range = BMI270_GYR_RANGE_X +}; +bmi270_start(bmi270_handle_pointer, &measurement_config); +``` +After a completed measurement data can be read using: +``` c +bmi270_get_acce_data(bmi270_handle_pointer, &x, &y, &z); +``` +Disable measurements and clean up the BMI270 driver using: +``` c +bmi270_stop(bmi270_handle_pointer); +bmi270_delete(bmi270_handle_pointer); +bmi270_handle_pointer = NULL; +``` + +### Espressif sensor hub + +This driver can be used with [sensor-hub](https://docs.espressif.com/projects/esp-iot-solution/en/latest/sensors/sensor_hub.html) abstraction. +Create a BMI270 sensor instance using a valid configuration: +``` c +iot_sensor_create("sensor_hub_bmi270", &bmi270_sensor_hub_configuration, sensor_handle_pointer); +``` + +## Driver structure + +``` +BMI270 +├── CMakeLists.txt # Driver component CMake file +├── README.md +├── idf_component.yml # Driver component configuration file +├── include # Public include folder +│ └── bmi270.h +├── include_priv # Private include folder +│ └── bmi270_priv.h +├── license.txt +├── src +│ ├── bmi270.c # Low level driver source file +│ └── bmi270_sensor_hub.c # Sensor-hub wrapper +└── test_app # Test app for the low level driver + ├── CMakeLists.txt + ├── main + │ ├── CMakeLists.txt + │ ├── idf_component.yml + │ └── test_app_bmi270.c + └── sdkconfig.defaults +``` + +## See also +[BMI270 datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi270-ds000.pdf) +[ESP-IDF component registry documentation](https://docs.espressif.com/projects/idf-component-manager/en/latest/) +[Sensor hub documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/sensors/sensor_hub.html) \ No newline at end of file diff --git a/components/sensors/bmi270/idf_component.yml b/components/sensors/bmi270/idf_component.yml new file mode 100644 index 000000000..02e2834b9 --- /dev/null +++ b/components/sensors/bmi270/idf_component.yml @@ -0,0 +1,11 @@ +version: 1.0.0 +description: I2C driver for BMI270 inertial measurement unit +url: https://github.com/espressif/esp-bsp/tree/master/components/bmi270 +tags: + - i2c + - sensor_hub +dependencies: + idf: ">=5.2" + sensor_hub: + version: ^0.1.4 + public: true diff --git a/components/sensors/bmi270/include/bmi270.h b/components/sensors/bmi270/include/bmi270.h new file mode 100644 index 000000000..9fc47af07 --- /dev/null +++ b/components/sensors/bmi270/include/bmi270.h @@ -0,0 +1,256 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "driver/i2c_types.h" +#include "driver/spi_master.h" + +#define BMI270_CHIP_ID 0x24 +#define BMI270_I2C_ADDRESS_L 0x68 +#define BMI270_I2C_ADDRESS_H 0x69 + +/** + * @brief Selection for the BMI270 communication interface + * @note The SPI interface is currently not supported + */ +typedef enum { + BMI270_USE_I2C, + BMI270_USE_SPI, +} bmi270_interface_e; + +/** + * @brief BMI270 accelerometer output data rates + */ +typedef enum { + BMI270_ACC_ODR_0_78_HZ = 1, + BMI270_ACC_ODR_1_5_HZ, + BMI270_ACC_ODR_3_1_HZ, + BMI270_ACC_ODR_6_25_HZ, + BMI270_ACC_ODR_12_5_HZ, + BMI270_ACC_ODR_25_HZ, + BMI270_ACC_ODR_50_HZ, + BMI270_ACC_ODR_100_HZ, + BMI270_ACC_ODR_200_HZ, + BMI270_ACC_ODR_400_HZ, + BMI270_ACC_ODR_800_HZ, + BMI270_ACC_ODR_1600_HZ +} bmi270_acce_odr_e; + +/** + * @brief BMI270 gyroscope output data rates + */ +typedef enum { + BMI270_GYR_ODR_25_HZ = 6, + BMI270_GYR_ODR_50_HZ, + BMI270_GYR_ODR_100_HZ, + BMI270_GYR_ODR_200_HZ, + BMI270_GYR_ODR_400_HZ, + BMI270_GYR_ODR_800_HZ, + BMI270_GYR_ODR_1600_HZ, + BMI270_GYR_ODR_3200_HZ +} bmi270_gyro_odr_e; + +/** + * @brief BMI270 accelerometer data ranges + */ +typedef enum { + BMI270_ACC_RANGE_2_G = 0, + BMI270_ACC_RANGE_4_G, + BMI270_ACC_RANGE_8_G, + BMI270_ACC_RANGE_16_G +} bmi270_acce_range_e; + +/** + * @brief BMI270 gyroscope data ranges + */ +typedef enum { + BMI270_GYR_RANGE_2000_DPS = 0, + BMI270_GYR_RANGE_1000_DPS, + BMI270_GYR_RANGE_500_DPS, + BMI270_GYR_RANGE_250_DPS, + BMI270_GYR_RANGE_125_DPS +} bmi270_gyro_range_e; + +/** + * @brief Driver interface configuration structure + * @note The SPI interface is currently not supported + */ +typedef struct { + uint8_t addr; + bmi270_interface_e interface; + union { + i2c_master_bus_handle_t i2c_bus; + }; +} bmi270_driver_config_t; + +/** + * @brief BMI270 data acquisition configuration + */ +typedef struct { + bmi270_acce_odr_e acce_odr; + bmi270_acce_range_e acce_range; + bmi270_gyro_odr_e gyro_odr; + bmi270_gyro_range_e gyro_range; +} bmi270_config_t; + +/** + * @brief BMI270 handle + */ +typedef struct { + bool initialized; + bmi270_interface_e interface; + union { + i2c_master_dev_handle_t i2c_handle; + spi_device_handle_t spi_handle; + }; + bmi270_acce_range_e acce_range; + bmi270_gyro_range_e gyro_range; +} bmi270_handle_t; + +/** + * @brief Create and initialize the BMI270 sensor object + * + * @param[in] config BMI270 sensor driver configuration + * @param[out] dev_handle Pointer to the allocated memory for the BMI270 handle + * + * @return + * - ESP_OK Success + * - ESP_ERR_NO_MEM Memory allocation failure + * - ESP_ERR_NOT_FOUND Device was not found on the data bus + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_create(const bmi270_driver_config_t *config, bmi270_handle_t **dev_handle); + +/** + * @brief Free and delete the BMI270 sensor object + * + * @param[in] dev_handle Pointer to an initialized BMI270 handle + * + * @return + * - ESP_OK Success + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_delete(bmi270_handle_t *dev_handle); + +/** + * @brief Read the chip ID register + * + * @param[in] dev_handle Initialized BMI270 handle + * @param[out] chip_id Value read from the chip ID register + * + * @return + * - ESP_OK Success + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_get_chip_id(const bmi270_handle_t *dev_handle, uint8_t *chip_id); + +/** + * @brief Start accelerometer, gyroscope and temperature sensor measurement + * + * @param[in] dev_handle Initialized BMI270 handle + * @param[out] config Configuration of data acquisition + * + * @return + * - ESP_OK Success + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_start(bmi270_handle_t *dev_handle, const bmi270_config_t *config); + +/** + * @brief Stop accelerometer, gyroscope and temperature sensor measurement + * + * @param[in] dev_handle Initialized BMI270 handle + * + * @return + * - ESP_OK Success + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_stop(const bmi270_handle_t *dev_handle); + +/** + * @brief Read out scaled accelerometer data + * + * @param[in] dev_handle Initialized BMI270 handle + * @param[out] x X axis accelerometer value in g + * @param[out] y Y axis accelerometer value in g + * @param[out] z Z axis accelerometer value in g + * + * @return + * - ESP_OK Success + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_get_acce_data(const bmi270_handle_t *dev_handle, float *x, float *y, float *z); + +/** + * @brief Read out scaled gyroscope data + * + * @param[in] dev_handle Initialized BMI270 handle + * @param[out] x X axis gyroscope value in dps + * @param[out] y Y axis gyroscope value in dps + * @param[out] z Z axis gyroscope value in dps + * + * @return + * - ESP_OK Success + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_get_gyro_data(const bmi270_handle_t *dev_handle, float *x, float *y, float *z); + +/** + * @brief Set accelerometer output data rate + * + * @param[in] dev_handle Initialized BMI270 handle + * @param[in] odr Accelerometer output data rate + * + * @return + * - ESP_OK Success + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_set_acce_odr(const bmi270_handle_t *dev_handle, bmi270_acce_odr_e odr); + +/** + * @brief Set gyroscope output data rate + * + * @param[in] dev_handle Initialized BMI270 handle + * @param[in] odr Gyroscope output data rate + * + * @return + * - ESP_OK Success + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_set_gyro_odr(const bmi270_handle_t *dev_handle, bmi270_gyro_odr_e odr); + +/** + * @brief Set accelerometer measurement range + * + * @param[in] dev_handle Initialized BMI270 handle + * @param[in] odr Accelerometer measurement range + * + * @return + * - ESP_OK Success + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_set_acce_range(bmi270_handle_t *dev_handle, bmi270_acce_range_e range); + +/** + * @brief Set gyroscope measurement range + * + * @param[in] dev_handle Initialized BMI270 handle + * @param[in] odr Gyroscope measurement range + * + * @return + * - ESP_OK Success + * - Or other errors from the underlying I2C driver + */ +esp_err_t bmi270_set_gyro_range(bmi270_handle_t *dev_handle, bmi270_gyro_range_e range); + +#ifdef __cplusplus +} +#endif diff --git a/components/sensors/bmi270/include_priv/bmi270_priv.h b/components/sensors/bmi270/include_priv/bmi270_priv.h new file mode 100644 index 000000000..de5b6387f --- /dev/null +++ b/components/sensors/bmi270/include_priv/bmi270_priv.h @@ -0,0 +1,534 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define BMI270_RAW_DATA_READ_LEN 6 +#define BMI270_STATUS_CHECK_CNT 10 +#define BMI270_FILE_WRITE_LEN 32 + +#define BMI270_POWER_ON_TIME_MS 3 +#define BMI270_SOFT_RESET_TIME_MS 3 +#define BMI270_STATUS_CHECK_PERIOD_MS 50 + +#define BMI270_CMD_SOFT_RESET 0xB6 +#define BMI270_STATUS_INIT_OK 0x01 + +#define BMI270_ACC_EN_MSK 0x02 +#define BMI270_GYR_EN_MSK 0x04 +#define BMI270_TEMP_EN_MSK 0x08 +#define BMI270_ODR_MSK 0xF0 +#define BMI270_GYR_RANGE_MSK 0xF8 + +#define BMI270_CHIP_ID_REG 0x00 +#define BMI270_STATUS_REG 0x03 +#define BMI270_AUX_X_LSB_REG 0x04 +#define BMI270_ACC_X_LSB_REG 0x0C +#define BMI270_GYR_X_LSB_REG 0x12 +#define BMI270_SENSORTIME_REG 0x18 +#define BMI270_EVENT_REG 0x1B +#define BMI270_INT_STATUS_0_REG 0x1C +#define BMI270_INT_STATUS_1_REG 0x1D +#define BMI270_SC_OUT_0_REG 0x1E +#define BMI270_SYNC_COMMAND_REG 0x1E +#define BMI270_GYR_CAS_GPIO0_REG 0x1E +#define BMI270_DSD_OUT_REG 0x1E +#define BMI270_INTERNAL_STATUS_REG 0x21 +#define BMI270_TEMPERATURE_0_REG 0x22 +#define BMI270_TEMPERATURE_1_REG 0x23 +#define BMI270_FIFO_LENGTH_0_REG 0x24 +#define BMI270_FIFO_DATA_REG 0x26 +#define BMI270_FEAT_PAGE_REG 0x2F +#define BMI270_FEATURES_REG_REG 0x30 +#define BMI270_ACC_CONF_REG 0x40 +#define BMI270_ACC_RANGE_REG 0x41 +#define BMI270_GYR_CONF_REG 0x42 +#define BMI270_GYR_RANGE_REG 0x43 +#define BMI270_AUX_CONF_REG 0x44 +#define BMI270_FIFO_DOWNS_REG 0x45 +#define BMI270_FIFO_WTM_0_REG 0x46 +#define BMI270_FIFO_WTM_1_REG 0x47 +#define BMI270_FIFO_CONFIG_0_REG 0x48 +#define BMI270_FIFO_CONFIG_1_REG 0x49 +#define BMI270_SATURATION_REG 0x4A +#define BMI270_AUX_DEV_ID_REG 0x4B +#define BMI270_AUX_IF_CONF_REG 0x4C +#define BMI270_AUX_RD_REG 0x4D +#define BMI270_AUX_WR_REG 0x4E +#define BMI270_AUX_WR_DATA_REG 0x4F +#define BMI270_ERR_REG_MSK_REG 0x52 +#define BMI270_INT1_IO_CTRL_REG 0x53 +#define BMI270_INT2_IO_CTRL_REG 0x54 +#define BMI270_INT_LATCH_REG 0x55 +#define BMI270_INT1_MAP_FEAT_REG 0x56 +#define BMI270_INT2_MAP_FEAT_REG 0x57 +#define BMI270_INT_MAP_DATA_REG 0x58 +#define BMI270_INIT_CTRL_REG 0x59 +#define BMI270_INIT_ADDR_0_REG 0x5B +#define BMI270_INIT_ADDR_1_REG 0x5C +#define BMI270_INIT_DATA_REG 0x5E +#define BMI270_INTERNAL_ERR_REG 0x5F +#define BMI270_AUX_IF_TRIM_REG 0x68 +#define BMI270_GYR_CRT_CONF_REG 0x69 +#define BMI270_NVM_CONF_REG 0x6A +#define BMI270_IF_CONF_REG 0x6B +#define BMI270_DRV_STR_REG 0x6C +#define BMI270_ACC_SELF_TEST_REG 0x6D +#define BMI270_GYR_SELF_TEST_AXES_REG 0x6E +#define BMI270_SELF_TEST_MEMS_REG 0x6F +#define BMI270_NV_CONF_REG 0x70 +#define BMI270_ACC_OFF_COMP_0_REG 0x71 +#define BMI270_GYR_OFF_COMP_3_REG 0x74 +#define BMI270_GYR_OFF_COMP_6_REG 0x77 +#define BMI270_GYR_USR_GAIN_0_REG 0x78 +#define BMI270_PWR_CONF_REG 0x7C +#define BMI270_PWR_CTRL_REG 0x7D +#define BMI270_CMD_REG 0x7E + +const uint8_t bmi270_config_file[] = { + 0xc8, 0x2e, 0x00, 0x2e, 0x80, 0x2e, 0x3d, 0xb1, 0xc8, 0x2e, 0x00, 0x2e, 0x80, 0x2e, 0x91, 0x03, 0x80, 0x2e, 0xbc, + 0xb0, 0x80, 0x2e, 0xa3, 0x03, 0xc8, 0x2e, 0x00, 0x2e, 0x80, 0x2e, 0x00, 0xb0, 0x50, 0x30, 0x21, 0x2e, 0x59, 0xf5, + 0x10, 0x30, 0x21, 0x2e, 0x6a, 0xf5, 0x80, 0x2e, 0x3b, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x19, 0x01, 0x00, 0x22, + 0x00, 0x75, 0x00, 0x00, 0x10, 0x00, 0x10, 0xd1, 0x00, 0xb3, 0x43, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0xe0, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x19, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0xe0, 0xaa, 0x38, 0x05, 0xe0, 0x90, 0x30, 0xfa, 0x00, 0x96, 0x00, 0x4b, 0x09, 0x11, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x2d, 0x01, 0xd4, 0x7b, 0x3b, 0x01, 0xdb, 0x7a, 0x04, 0x00, 0x3f, 0x7b, 0xcd, 0x6c, 0xc3, 0x04, 0x85, 0x09, 0xc3, + 0x04, 0xec, 0xe6, 0x0c, 0x46, 0x01, 0x00, 0x27, 0x00, 0x19, 0x00, 0x96, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x0c, 0x00, + 0xf0, 0x3c, 0x00, 0x01, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x05, 0x00, 0xee, + 0x06, 0x04, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x04, 0x00, 0xa8, 0x05, 0xee, 0x06, 0x00, 0x04, 0xbc, 0x02, 0xb3, 0x00, + 0x85, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xb4, 0x00, 0x01, 0x00, 0xb9, 0x00, 0x01, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2e, 0x00, 0xc1, 0xfd, 0x2d, 0xde, + 0x00, 0xeb, 0x00, 0xda, 0x00, 0x00, 0x0c, 0xff, 0x0f, 0x00, 0x04, 0xc0, 0x00, 0x5b, 0xf5, 0xc9, 0x01, 0x1e, 0xf2, + 0x80, 0x00, 0x3f, 0xff, 0x19, 0xf4, 0x58, 0xf5, 0x66, 0xf5, 0x64, 0xf5, 0xc0, 0xf1, 0xf0, 0x00, 0xe0, 0x00, 0xcd, + 0x01, 0xd3, 0x01, 0xdb, 0x01, 0xff, 0x7f, 0xff, 0x01, 0xe4, 0x00, 0x74, 0xf7, 0xf3, 0x00, 0xfa, 0x00, 0xff, 0x3f, + 0xca, 0x03, 0x6c, 0x38, 0x56, 0xfe, 0x44, 0xfd, 0xbc, 0x02, 0xf9, 0x06, 0x00, 0xfc, 0x12, 0x02, 0xae, 0x01, 0x58, + 0xfa, 0x9a, 0xfd, 0x77, 0x05, 0xbb, 0x02, 0x96, 0x01, 0x95, 0x01, 0x7f, 0x01, 0x82, 0x01, 0x89, 0x01, 0x87, 0x01, + 0x88, 0x01, 0x8a, 0x01, 0x8c, 0x01, 0x8f, 0x01, 0x8d, 0x01, 0x92, 0x01, 0x91, 0x01, 0xdd, 0x00, 0x9f, 0x01, 0x7e, + 0x01, 0xdb, 0x00, 0xb6, 0x01, 0x70, 0x69, 0x26, 0xd3, 0x9c, 0x07, 0x1f, 0x05, 0x9d, 0x00, 0x00, 0x08, 0xbc, 0x05, + 0x37, 0xfa, 0xa2, 0x01, 0xaa, 0x01, 0xa1, 0x01, 0xa8, 0x01, 0xa0, 0x01, 0xa8, 0x05, 0xb4, 0x01, 0xb4, 0x01, 0xce, + 0x00, 0xd0, 0x00, 0xfc, 0x00, 0xc5, 0x01, 0xff, 0xfb, 0xb1, 0x00, 0x00, 0x38, 0x00, 0x30, 0xfd, 0xf5, 0xfc, 0xf5, + 0xcd, 0x01, 0xa0, 0x00, 0x5f, 0xff, 0x00, 0x40, 0xff, 0x00, 0x00, 0x80, 0x6d, 0x0f, 0xeb, 0x00, 0x7f, 0xff, 0xc2, + 0xf5, 0x68, 0xf7, 0xb3, 0xf1, 0x67, 0x0f, 0x5b, 0x0f, 0x61, 0x0f, 0x80, 0x0f, 0x58, 0xf7, 0x5b, 0xf7, 0x83, 0x0f, + 0x86, 0x00, 0x72, 0x0f, 0x85, 0x0f, 0xc6, 0xf1, 0x7f, 0x0f, 0x6c, 0xf7, 0x00, 0xe0, 0x00, 0xff, 0xd1, 0xf5, 0x87, + 0x0f, 0x8a, 0x0f, 0xff, 0x03, 0xf0, 0x3f, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0xb9, 0x00, 0x2d, 0xf5, 0xca, 0xf5, + 0xcb, 0x01, 0x20, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x50, 0x98, 0x2e, + 0xd7, 0x0e, 0x50, 0x32, 0x98, 0x2e, 0xfa, 0x03, 0x00, 0x30, 0xf0, 0x7f, 0x00, 0x2e, 0x00, 0x2e, 0xd0, 0x2e, 0x00, + 0x2e, 0x01, 0x80, 0x08, 0xa2, 0xfb, 0x2f, 0x98, 0x2e, 0xba, 0x03, 0x21, 0x2e, 0x19, 0x00, 0x01, 0x2e, 0xee, 0x00, + 0x00, 0xb2, 0x07, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x03, 0x2f, 0x01, 0x50, 0x03, 0x52, 0x98, 0x2e, 0x07, + 0xcc, 0x01, 0x2e, 0xdd, 0x00, 0x00, 0xb2, 0x27, 0x2f, 0x05, 0x2e, 0x8a, 0x00, 0x05, 0x52, 0x98, 0x2e, 0xc7, 0xc1, + 0x03, 0x2e, 0xe9, 0x00, 0x40, 0xb2, 0xf0, 0x7f, 0x08, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x04, 0x2f, 0x00, + 0x30, 0x21, 0x2e, 0xe9, 0x00, 0x98, 0x2e, 0xb4, 0xb1, 0x01, 0x2e, 0x18, 0x00, 0x00, 0xb2, 0x10, 0x2f, 0x05, 0x50, + 0x98, 0x2e, 0x4d, 0xc3, 0x05, 0x50, 0x98, 0x2e, 0x5a, 0xc7, 0x98, 0x2e, 0xf9, 0xb4, 0x98, 0x2e, 0x54, 0xb2, 0x98, + 0x2e, 0x67, 0xb6, 0x98, 0x2e, 0x17, 0xb2, 0x10, 0x30, 0x21, 0x2e, 0x77, 0x00, 0x01, 0x2e, 0xef, 0x00, 0x00, 0xb2, + 0x04, 0x2f, 0x98, 0x2e, 0x7a, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0xef, 0x00, 0x01, 0x2e, 0xd4, 0x00, 0x04, 0xae, 0x0b, + 0x2f, 0x01, 0x2e, 0xdd, 0x00, 0x00, 0xb2, 0x07, 0x2f, 0x05, 0x52, 0x98, 0x2e, 0x8e, 0x0e, 0x00, 0xb2, 0x02, 0x2f, + 0x10, 0x30, 0x21, 0x2e, 0x7d, 0x00, 0x01, 0x2e, 0x7d, 0x00, 0x00, 0x90, 0x90, 0x2e, 0xf1, 0x02, 0x01, 0x2e, 0xd7, + 0x00, 0x00, 0xb2, 0x04, 0x2f, 0x98, 0x2e, 0x2f, 0x0e, 0x00, 0x30, 0x21, 0x2e, 0x7b, 0x00, 0x01, 0x2e, 0x7b, 0x00, + 0x00, 0xb2, 0x12, 0x2f, 0x01, 0x2e, 0xd4, 0x00, 0x00, 0x90, 0x02, 0x2f, 0x98, 0x2e, 0x1f, 0x0e, 0x09, 0x2d, 0x98, + 0x2e, 0x81, 0x0d, 0x01, 0x2e, 0xd4, 0x00, 0x04, 0x90, 0x02, 0x2f, 0x50, 0x32, 0x98, 0x2e, 0xfa, 0x03, 0x00, 0x30, + 0x21, 0x2e, 0x7b, 0x00, 0x01, 0x2e, 0x7c, 0x00, 0x00, 0xb2, 0x90, 0x2e, 0x09, 0x03, 0x01, 0x2e, 0x7c, 0x00, 0x01, + 0x31, 0x01, 0x08, 0x00, 0xb2, 0x04, 0x2f, 0x98, 0x2e, 0x47, 0xcb, 0x10, 0x30, 0x21, 0x2e, 0x77, 0x00, 0x81, 0x30, + 0x01, 0x2e, 0x7c, 0x00, 0x01, 0x08, 0x00, 0xb2, 0x61, 0x2f, 0x03, 0x2e, 0x89, 0x00, 0x01, 0x2e, 0xd4, 0x00, 0x98, + 0xbc, 0x98, 0xb8, 0x05, 0xb2, 0x0f, 0x58, 0x23, 0x2f, 0x07, 0x90, 0x09, 0x54, 0x00, 0x30, 0x37, 0x2f, 0x15, 0x41, + 0x04, 0x41, 0xdc, 0xbe, 0x44, 0xbe, 0xdc, 0xba, 0x2c, 0x01, 0x61, 0x00, 0x0f, 0x56, 0x4a, 0x0f, 0x0c, 0x2f, 0xd1, + 0x42, 0x94, 0xb8, 0xc1, 0x42, 0x11, 0x30, 0x05, 0x2e, 0x6a, 0xf7, 0x2c, 0xbd, 0x2f, 0xb9, 0x80, 0xb2, 0x08, 0x22, + 0x98, 0x2e, 0xc3, 0xb7, 0x21, 0x2d, 0x61, 0x30, 0x23, 0x2e, 0xd4, 0x00, 0x98, 0x2e, 0xc3, 0xb7, 0x00, 0x30, 0x21, + 0x2e, 0x5a, 0xf5, 0x18, 0x2d, 0xe1, 0x7f, 0x50, 0x30, 0x98, 0x2e, 0xfa, 0x03, 0x0f, 0x52, 0x07, 0x50, 0x50, 0x42, + 0x70, 0x30, 0x0d, 0x54, 0x42, 0x42, 0x7e, 0x82, 0xe2, 0x6f, 0x80, 0xb2, 0x42, 0x42, 0x05, 0x2f, 0x21, 0x2e, 0xd4, + 0x00, 0x10, 0x30, 0x98, 0x2e, 0xc3, 0xb7, 0x03, 0x2d, 0x60, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0x01, 0x2e, 0xd4, 0x00, + 0x06, 0x90, 0x18, 0x2f, 0x01, 0x2e, 0x76, 0x00, 0x0b, 0x54, 0x07, 0x52, 0xe0, 0x7f, 0x98, 0x2e, 0x7a, 0xc1, 0xe1, + 0x6f, 0x08, 0x1a, 0x40, 0x30, 0x08, 0x2f, 0x21, 0x2e, 0xd4, 0x00, 0x20, 0x30, 0x98, 0x2e, 0xaf, 0xb7, 0x50, 0x32, + 0x98, 0x2e, 0xfa, 0x03, 0x05, 0x2d, 0x98, 0x2e, 0x38, 0x0e, 0x00, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0x00, 0x30, 0x21, + 0x2e, 0x7c, 0x00, 0x18, 0x2d, 0x01, 0x2e, 0xd4, 0x00, 0x03, 0xaa, 0x01, 0x2f, 0x98, 0x2e, 0x45, 0x0e, 0x01, 0x2e, + 0xd4, 0x00, 0x3f, 0x80, 0x03, 0xa2, 0x01, 0x2f, 0x00, 0x2e, 0x02, 0x2d, 0x98, 0x2e, 0x5b, 0x0e, 0x30, 0x30, 0x98, + 0x2e, 0xce, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0x7d, 0x00, 0x50, 0x32, 0x98, 0x2e, 0xfa, 0x03, 0x01, 0x2e, 0x77, 0x00, + 0x00, 0xb2, 0x24, 0x2f, 0x98, 0x2e, 0xf5, 0xcb, 0x03, 0x2e, 0xd5, 0x00, 0x11, 0x54, 0x01, 0x0a, 0xbc, 0x84, 0x83, + 0x86, 0x21, 0x2e, 0xc9, 0x01, 0xe0, 0x40, 0x13, 0x52, 0xc4, 0x40, 0x82, 0x40, 0xa8, 0xb9, 0x52, 0x42, 0x43, 0xbe, + 0x53, 0x42, 0x04, 0x0a, 0x50, 0x42, 0xe1, 0x7f, 0xf0, 0x31, 0x41, 0x40, 0xf2, 0x6f, 0x25, 0xbd, 0x08, 0x08, 0x02, + 0x0a, 0xd0, 0x7f, 0x98, 0x2e, 0xa8, 0xcf, 0x06, 0xbc, 0xd1, 0x6f, 0xe2, 0x6f, 0x08, 0x0a, 0x80, 0x42, 0x98, 0x2e, + 0x58, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0xee, 0x00, 0x21, 0x2e, 0x77, 0x00, 0x21, 0x2e, 0xdd, 0x00, 0x80, 0x2e, 0xf4, + 0x01, 0x1a, 0x24, 0x22, 0x00, 0x80, 0x2e, 0xec, 0x01, 0x10, 0x50, 0xfb, 0x7f, 0x98, 0x2e, 0xf3, 0x03, 0x57, 0x50, + 0xfb, 0x6f, 0x01, 0x30, 0x71, 0x54, 0x11, 0x42, 0x42, 0x0e, 0xfc, 0x2f, 0xc0, 0x2e, 0x01, 0x42, 0xf0, 0x5f, 0x80, + 0x2e, 0x00, 0xc1, 0xfd, 0x2d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x01, + 0x34, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x50, 0xe7, 0x7f, 0xf6, 0x7f, 0x06, 0x32, 0x0f, 0x2e, 0x61, 0xf5, 0xfe, 0x09, 0xc0, 0xb3, 0x04, + 0x2f, 0x17, 0x30, 0x2f, 0x2e, 0xef, 0x00, 0x2d, 0x2e, 0x61, 0xf5, 0xf6, 0x6f, 0xe7, 0x6f, 0xe0, 0x5f, 0xc8, 0x2e, + 0x20, 0x50, 0xe7, 0x7f, 0xf6, 0x7f, 0x46, 0x30, 0x0f, 0x2e, 0xa4, 0xf1, 0xbe, 0x09, 0x80, 0xb3, 0x06, 0x2f, 0x0d, + 0x2e, 0xd4, 0x00, 0x84, 0xaf, 0x02, 0x2f, 0x16, 0x30, 0x2d, 0x2e, 0x7b, 0x00, 0x86, 0x30, 0x2d, 0x2e, 0x60, 0xf5, + 0xf6, 0x6f, 0xe7, 0x6f, 0xe0, 0x5f, 0xc8, 0x2e, 0x01, 0x2e, 0x77, 0xf7, 0x09, 0xbc, 0x0f, 0xb8, 0x00, 0xb2, 0x10, + 0x50, 0xfb, 0x7f, 0x10, 0x30, 0x0b, 0x2f, 0x03, 0x2e, 0x8a, 0x00, 0x96, 0xbc, 0x9f, 0xb8, 0x40, 0xb2, 0x05, 0x2f, + 0x03, 0x2e, 0x68, 0xf7, 0x9e, 0xbc, 0x9f, 0xb8, 0x40, 0xb2, 0x07, 0x2f, 0x03, 0x2e, 0x7e, 0x00, 0x41, 0x90, 0x01, + 0x2f, 0x98, 0x2e, 0xdc, 0x03, 0x03, 0x2c, 0x00, 0x30, 0x21, 0x2e, 0x7e, 0x00, 0xfb, 0x6f, 0xf0, 0x5f, 0xb8, 0x2e, + 0x20, 0x50, 0xe0, 0x7f, 0xfb, 0x7f, 0x00, 0x2e, 0x27, 0x50, 0x98, 0x2e, 0x3b, 0xc8, 0x29, 0x50, 0x98, 0x2e, 0xa7, + 0xc8, 0x01, 0x50, 0x98, 0x2e, 0x55, 0xcc, 0xe1, 0x6f, 0x2b, 0x50, 0x98, 0x2e, 0xe0, 0xc9, 0xfb, 0x6f, 0x00, 0x30, + 0xe0, 0x5f, 0x21, 0x2e, 0x7e, 0x00, 0xb8, 0x2e, 0x73, 0x50, 0x01, 0x30, 0x57, 0x54, 0x11, 0x42, 0x42, 0x0e, 0xfc, + 0x2f, 0xb8, 0x2e, 0x21, 0x2e, 0x59, 0xf5, 0x10, 0x30, 0xc0, 0x2e, 0x21, 0x2e, 0x4a, 0xf1, 0x90, 0x50, 0xf7, 0x7f, + 0xe6, 0x7f, 0xd5, 0x7f, 0xc4, 0x7f, 0xb3, 0x7f, 0xa1, 0x7f, 0x90, 0x7f, 0x82, 0x7f, 0x7b, 0x7f, 0x98, 0x2e, 0x35, + 0xb7, 0x00, 0xb2, 0x90, 0x2e, 0x97, 0xb0, 0x03, 0x2e, 0x8f, 0x00, 0x07, 0x2e, 0x91, 0x00, 0x05, 0x2e, 0xb1, 0x00, + 0x3f, 0xba, 0x9f, 0xb8, 0x01, 0x2e, 0xb1, 0x00, 0xa3, 0xbd, 0x4c, 0x0a, 0x05, 0x2e, 0xb1, 0x00, 0x04, 0xbe, 0xbf, + 0xb9, 0xcb, 0x0a, 0x4f, 0xba, 0x22, 0xbd, 0x01, 0x2e, 0xb3, 0x00, 0xdc, 0x0a, 0x2f, 0xb9, 0x03, 0x2e, 0xb8, 0x00, + 0x0a, 0xbe, 0x9a, 0x0a, 0xcf, 0xb9, 0x9b, 0xbc, 0x01, 0x2e, 0x97, 0x00, 0x9f, 0xb8, 0x93, 0x0a, 0x0f, 0xbc, 0x91, + 0x0a, 0x0f, 0xb8, 0x90, 0x0a, 0x25, 0x2e, 0x18, 0x00, 0x05, 0x2e, 0xc1, 0xf5, 0x2e, 0xbd, 0x2e, 0xb9, 0x01, 0x2e, + 0x19, 0x00, 0x31, 0x30, 0x8a, 0x04, 0x00, 0x90, 0x07, 0x2f, 0x01, 0x2e, 0xd4, 0x00, 0x04, 0xa2, 0x03, 0x2f, 0x01, + 0x2e, 0x18, 0x00, 0x00, 0xb2, 0x0c, 0x2f, 0x19, 0x50, 0x05, 0x52, 0x98, 0x2e, 0x4d, 0xb7, 0x05, 0x2e, 0x78, 0x00, + 0x80, 0x90, 0x10, 0x30, 0x01, 0x2f, 0x21, 0x2e, 0x78, 0x00, 0x25, 0x2e, 0xdd, 0x00, 0x98, 0x2e, 0x3e, 0xb7, 0x00, + 0xb2, 0x02, 0x30, 0x01, 0x30, 0x04, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x00, 0x2f, 0x21, 0x30, 0x01, 0x2e, + 0xea, 0x00, 0x08, 0x1a, 0x0e, 0x2f, 0x23, 0x2e, 0xea, 0x00, 0x33, 0x30, 0x1b, 0x50, 0x0b, 0x09, 0x01, 0x40, 0x17, + 0x56, 0x46, 0xbe, 0x4b, 0x08, 0x4c, 0x0a, 0x01, 0x42, 0x0a, 0x80, 0x15, 0x52, 0x01, 0x42, 0x00, 0x2e, 0x01, 0x2e, + 0x18, 0x00, 0x00, 0xb2, 0x1f, 0x2f, 0x03, 0x2e, 0xc0, 0xf5, 0xf0, 0x30, 0x48, 0x08, 0x47, 0xaa, 0x74, 0x30, 0x07, + 0x2e, 0x7a, 0x00, 0x61, 0x22, 0x4b, 0x1a, 0x05, 0x2f, 0x07, 0x2e, 0x66, 0xf5, 0xbf, 0xbd, 0xbf, 0xb9, 0xc0, 0x90, + 0x0b, 0x2f, 0x1d, 0x56, 0x2b, 0x30, 0xd2, 0x42, 0xdb, 0x42, 0x01, 0x04, 0xc2, 0x42, 0x04, 0xbd, 0xfe, 0x80, 0x81, + 0x84, 0x23, 0x2e, 0x7a, 0x00, 0x02, 0x42, 0x02, 0x32, 0x25, 0x2e, 0x62, 0xf5, 0x05, 0x2e, 0xd6, 0x00, 0x81, 0x84, + 0x25, 0x2e, 0xd6, 0x00, 0x02, 0x31, 0x25, 0x2e, 0x60, 0xf5, 0x05, 0x2e, 0x8a, 0x00, 0x0b, 0x50, 0x90, 0x08, 0x80, + 0xb2, 0x0b, 0x2f, 0x05, 0x2e, 0xca, 0xf5, 0xf0, 0x3e, 0x90, 0x08, 0x25, 0x2e, 0xca, 0xf5, 0x05, 0x2e, 0x59, 0xf5, + 0xe0, 0x3f, 0x90, 0x08, 0x25, 0x2e, 0x59, 0xf5, 0x90, 0x6f, 0xa1, 0x6f, 0xb3, 0x6f, 0xc4, 0x6f, 0xd5, 0x6f, 0xe6, + 0x6f, 0xf7, 0x6f, 0x7b, 0x6f, 0x82, 0x6f, 0x70, 0x5f, 0xc8, 0x2e, 0xc0, 0x50, 0x90, 0x7f, 0xe5, 0x7f, 0xd4, 0x7f, + 0xc3, 0x7f, 0xb1, 0x7f, 0xa2, 0x7f, 0x87, 0x7f, 0xf6, 0x7f, 0x7b, 0x7f, 0x00, 0x2e, 0x01, 0x2e, 0x60, 0xf5, 0x60, + 0x7f, 0x98, 0x2e, 0x35, 0xb7, 0x02, 0x30, 0x63, 0x6f, 0x15, 0x52, 0x50, 0x7f, 0x62, 0x7f, 0x5a, 0x2c, 0x02, 0x32, + 0x1a, 0x09, 0x00, 0xb3, 0x14, 0x2f, 0x00, 0xb2, 0x03, 0x2f, 0x09, 0x2e, 0x18, 0x00, 0x00, 0x91, 0x0c, 0x2f, 0x43, + 0x7f, 0x98, 0x2e, 0x97, 0xb7, 0x1f, 0x50, 0x02, 0x8a, 0x02, 0x32, 0x04, 0x30, 0x25, 0x2e, 0x64, 0xf5, 0x15, 0x52, + 0x50, 0x6f, 0x43, 0x6f, 0x44, 0x43, 0x25, 0x2e, 0x60, 0xf5, 0xd9, 0x08, 0xc0, 0xb2, 0x36, 0x2f, 0x98, 0x2e, 0x3e, + 0xb7, 0x00, 0xb2, 0x06, 0x2f, 0x01, 0x2e, 0x19, 0x00, 0x00, 0xb2, 0x02, 0x2f, 0x50, 0x6f, 0x00, 0x90, 0x0a, 0x2f, + 0x01, 0x2e, 0x79, 0x00, 0x00, 0x90, 0x19, 0x2f, 0x10, 0x30, 0x21, 0x2e, 0x79, 0x00, 0x00, 0x30, 0x98, 0x2e, 0xdc, + 0x03, 0x13, 0x2d, 0x01, 0x2e, 0xc3, 0xf5, 0x0c, 0xbc, 0x0f, 0xb8, 0x12, 0x30, 0x10, 0x04, 0x03, 0xb0, 0x26, 0x25, + 0x21, 0x50, 0x03, 0x52, 0x98, 0x2e, 0x4d, 0xb7, 0x10, 0x30, 0x21, 0x2e, 0xee, 0x00, 0x02, 0x30, 0x60, 0x7f, 0x25, + 0x2e, 0x79, 0x00, 0x60, 0x6f, 0x00, 0x90, 0x05, 0x2f, 0x00, 0x30, 0x21, 0x2e, 0xea, 0x00, 0x15, 0x50, 0x21, 0x2e, + 0x64, 0xf5, 0x15, 0x52, 0x23, 0x2e, 0x60, 0xf5, 0x02, 0x32, 0x50, 0x6f, 0x00, 0x90, 0x02, 0x2f, 0x03, 0x30, 0x27, + 0x2e, 0x78, 0x00, 0x07, 0x2e, 0x60, 0xf5, 0x1a, 0x09, 0x00, 0x91, 0xa3, 0x2f, 0x19, 0x09, 0x00, 0x91, 0xa0, 0x2f, + 0x90, 0x6f, 0xa2, 0x6f, 0xb1, 0x6f, 0xc3, 0x6f, 0xd4, 0x6f, 0xe5, 0x6f, 0x7b, 0x6f, 0xf6, 0x6f, 0x87, 0x6f, 0x40, + 0x5f, 0xc8, 0x2e, 0xc0, 0x50, 0xe7, 0x7f, 0xf6, 0x7f, 0x26, 0x30, 0x0f, 0x2e, 0x61, 0xf5, 0x2f, 0x2e, 0x7c, 0x00, + 0x0f, 0x2e, 0x7c, 0x00, 0xbe, 0x09, 0xa2, 0x7f, 0x80, 0x7f, 0x80, 0xb3, 0xd5, 0x7f, 0xc4, 0x7f, 0xb3, 0x7f, 0x91, + 0x7f, 0x7b, 0x7f, 0x0b, 0x2f, 0x23, 0x50, 0x1a, 0x25, 0x12, 0x40, 0x42, 0x7f, 0x74, 0x82, 0x12, 0x40, 0x52, 0x7f, + 0x00, 0x2e, 0x00, 0x40, 0x60, 0x7f, 0x98, 0x2e, 0x6a, 0xd6, 0x81, 0x30, 0x01, 0x2e, 0x7c, 0x00, 0x01, 0x08, 0x00, + 0xb2, 0x42, 0x2f, 0x03, 0x2e, 0x89, 0x00, 0x01, 0x2e, 0x89, 0x00, 0x97, 0xbc, 0x06, 0xbc, 0x9f, 0xb8, 0x0f, 0xb8, + 0x00, 0x90, 0x23, 0x2e, 0xd8, 0x00, 0x10, 0x30, 0x01, 0x30, 0x2a, 0x2f, 0x03, 0x2e, 0xd4, 0x00, 0x44, 0xb2, 0x05, + 0x2f, 0x47, 0xb2, 0x00, 0x30, 0x2d, 0x2f, 0x21, 0x2e, 0x7c, 0x00, 0x2b, 0x2d, 0x03, 0x2e, 0xfd, 0xf5, 0x9e, 0xbc, + 0x9f, 0xb8, 0x40, 0x90, 0x14, 0x2f, 0x03, 0x2e, 0xfc, 0xf5, 0x99, 0xbc, 0x9f, 0xb8, 0x40, 0x90, 0x0e, 0x2f, 0x03, + 0x2e, 0x49, 0xf1, 0x25, 0x54, 0x4a, 0x08, 0x40, 0x90, 0x08, 0x2f, 0x98, 0x2e, 0x35, 0xb7, 0x00, 0xb2, 0x10, 0x30, + 0x03, 0x2f, 0x50, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0x10, 0x2d, 0x98, 0x2e, 0xaf, 0xb7, 0x00, 0x30, 0x21, 0x2e, 0x7c, + 0x00, 0x0a, 0x2d, 0x05, 0x2e, 0x69, 0xf7, 0x2d, 0xbd, 0x2f, 0xb9, 0x80, 0xb2, 0x01, 0x2f, 0x21, 0x2e, 0x7d, 0x00, + 0x23, 0x2e, 0x7c, 0x00, 0xe0, 0x31, 0x21, 0x2e, 0x61, 0xf5, 0xf6, 0x6f, 0xe7, 0x6f, 0x80, 0x6f, 0xa2, 0x6f, 0xb3, + 0x6f, 0xc4, 0x6f, 0xd5, 0x6f, 0x7b, 0x6f, 0x91, 0x6f, 0x40, 0x5f, 0xc8, 0x2e, 0x60, 0x51, 0x0a, 0x25, 0x36, 0x88, + 0xf4, 0x7f, 0xeb, 0x7f, 0x00, 0x32, 0x31, 0x52, 0x32, 0x30, 0x13, 0x30, 0x98, 0x2e, 0x15, 0xcb, 0x0a, 0x25, 0x33, + 0x84, 0xd2, 0x7f, 0x43, 0x30, 0x05, 0x50, 0x2d, 0x52, 0x98, 0x2e, 0x95, 0xc1, 0xd2, 0x6f, 0x27, 0x52, 0x98, 0x2e, + 0xd7, 0xc7, 0x2a, 0x25, 0xb0, 0x86, 0xc0, 0x7f, 0xd3, 0x7f, 0xaf, 0x84, 0x29, 0x50, 0xf1, 0x6f, 0x98, 0x2e, 0x4d, + 0xc8, 0x2a, 0x25, 0xae, 0x8a, 0xaa, 0x88, 0xf2, 0x6e, 0x2b, 0x50, 0xc1, 0x6f, 0xd3, 0x6f, 0xf4, 0x7f, 0x98, 0x2e, + 0xb6, 0xc8, 0xe0, 0x6e, 0x00, 0xb2, 0x32, 0x2f, 0x33, 0x54, 0x83, 0x86, 0xf1, 0x6f, 0xc3, 0x7f, 0x04, 0x30, 0x30, + 0x30, 0xf4, 0x7f, 0xd0, 0x7f, 0xb2, 0x7f, 0xe3, 0x30, 0xc5, 0x6f, 0x56, 0x40, 0x45, 0x41, 0x28, 0x08, 0x03, 0x14, + 0x0e, 0xb4, 0x08, 0xbc, 0x82, 0x40, 0x10, 0x0a, 0x2f, 0x54, 0x26, 0x05, 0x91, 0x7f, 0x44, 0x28, 0xa3, 0x7f, 0x98, + 0x2e, 0xd9, 0xc0, 0x08, 0xb9, 0x33, 0x30, 0x53, 0x09, 0xc1, 0x6f, 0xd3, 0x6f, 0xf4, 0x6f, 0x83, 0x17, 0x47, 0x40, + 0x6c, 0x15, 0xb2, 0x6f, 0xbe, 0x09, 0x75, 0x0b, 0x90, 0x42, 0x45, 0x42, 0x51, 0x0e, 0x32, 0xbc, 0x02, 0x89, 0xa1, + 0x6f, 0x7e, 0x86, 0xf4, 0x7f, 0xd0, 0x7f, 0xb2, 0x7f, 0x04, 0x30, 0x91, 0x6f, 0xd6, 0x2f, 0xeb, 0x6f, 0xa0, 0x5e, + 0xb8, 0x2e, 0x03, 0x2e, 0x97, 0x00, 0x1b, 0xbc, 0x60, 0x50, 0x9f, 0xbc, 0x0c, 0xb8, 0xf0, 0x7f, 0x40, 0xb2, 0xeb, + 0x7f, 0x2b, 0x2f, 0x03, 0x2e, 0x7f, 0x00, 0x41, 0x40, 0x01, 0x2e, 0xc8, 0x00, 0x01, 0x1a, 0x11, 0x2f, 0x37, 0x58, + 0x23, 0x2e, 0xc8, 0x00, 0x10, 0x41, 0xa0, 0x7f, 0x38, 0x81, 0x01, 0x41, 0xd0, 0x7f, 0xb1, 0x7f, 0x98, 0x2e, 0x64, + 0xcf, 0xd0, 0x6f, 0x07, 0x80, 0xa1, 0x6f, 0x11, 0x42, 0x00, 0x2e, 0xb1, 0x6f, 0x01, 0x42, 0x11, 0x30, 0x01, 0x2e, + 0xfc, 0x00, 0x00, 0xa8, 0x03, 0x30, 0xcb, 0x22, 0x4a, 0x25, 0x01, 0x2e, 0x7f, 0x00, 0x3c, 0x89, 0x35, 0x52, 0x05, + 0x54, 0x98, 0x2e, 0xc4, 0xce, 0xc1, 0x6f, 0xf0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, 0x04, 0x2d, 0x01, 0x30, 0xf0, 0x6f, + 0x98, 0x2e, 0x95, 0xcf, 0xeb, 0x6f, 0xa0, 0x5f, 0xb8, 0x2e, 0x03, 0x2e, 0xb3, 0x00, 0x02, 0x32, 0xf0, 0x30, 0x03, + 0x31, 0x30, 0x50, 0x8a, 0x08, 0x08, 0x08, 0xcb, 0x08, 0xe0, 0x7f, 0x80, 0xb2, 0xf3, 0x7f, 0xdb, 0x7f, 0x25, 0x2f, + 0x03, 0x2e, 0xca, 0x00, 0x41, 0x90, 0x04, 0x2f, 0x01, 0x30, 0x23, 0x2e, 0xca, 0x00, 0x98, 0x2e, 0x3f, 0x03, 0xc0, + 0xb2, 0x05, 0x2f, 0x03, 0x2e, 0xda, 0x00, 0x00, 0x30, 0x41, 0x04, 0x23, 0x2e, 0xda, 0x00, 0x98, 0x2e, 0x92, 0xb2, + 0x10, 0x25, 0xf0, 0x6f, 0x00, 0xb2, 0x05, 0x2f, 0x01, 0x2e, 0xda, 0x00, 0x02, 0x30, 0x10, 0x04, 0x21, 0x2e, 0xda, + 0x00, 0x40, 0xb2, 0x01, 0x2f, 0x23, 0x2e, 0xc8, 0x01, 0xdb, 0x6f, 0xe0, 0x6f, 0xd0, 0x5f, 0x80, 0x2e, 0x95, 0xcf, + 0x01, 0x30, 0xe0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, 0x11, 0x30, 0x23, 0x2e, 0xca, 0x00, 0xdb, 0x6f, 0xd0, 0x5f, 0xb8, + 0x2e, 0xd0, 0x50, 0x0a, 0x25, 0x33, 0x84, 0x55, 0x50, 0xd2, 0x7f, 0xe2, 0x7f, 0x03, 0x8c, 0xc0, 0x7f, 0xbb, 0x7f, + 0x00, 0x30, 0x05, 0x5a, 0x39, 0x54, 0x51, 0x41, 0xa5, 0x7f, 0x96, 0x7f, 0x80, 0x7f, 0x98, 0x2e, 0xd9, 0xc0, 0x05, + 0x30, 0xf5, 0x7f, 0x20, 0x25, 0x91, 0x6f, 0x3b, 0x58, 0x3d, 0x5c, 0x3b, 0x56, 0x98, 0x2e, 0x67, 0xcc, 0xc1, 0x6f, + 0xd5, 0x6f, 0x52, 0x40, 0x50, 0x43, 0xc1, 0x7f, 0xd5, 0x7f, 0x10, 0x25, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, + 0x2e, 0x74, 0xc0, 0x86, 0x6f, 0x30, 0x28, 0x92, 0x6f, 0x82, 0x8c, 0xa5, 0x6f, 0x6f, 0x52, 0x69, 0x0e, 0x39, 0x54, + 0xdb, 0x2f, 0x19, 0xa0, 0x15, 0x30, 0x03, 0x2f, 0x00, 0x30, 0x21, 0x2e, 0x81, 0x01, 0x0a, 0x2d, 0x01, 0x2e, 0x81, + 0x01, 0x05, 0x28, 0x42, 0x36, 0x21, 0x2e, 0x81, 0x01, 0x02, 0x0e, 0x01, 0x2f, 0x98, 0x2e, 0xf3, 0x03, 0x57, 0x50, + 0x12, 0x30, 0x01, 0x40, 0x98, 0x2e, 0xfe, 0xc9, 0x51, 0x6f, 0x0b, 0x5c, 0x8e, 0x0e, 0x3b, 0x6f, 0x57, 0x58, 0x02, + 0x30, 0x21, 0x2e, 0x95, 0x01, 0x45, 0x6f, 0x2a, 0x8d, 0xd2, 0x7f, 0xcb, 0x7f, 0x13, 0x2f, 0x02, 0x30, 0x3f, 0x50, + 0xd2, 0x7f, 0xa8, 0x0e, 0x0e, 0x2f, 0xc0, 0x6f, 0x53, 0x54, 0x02, 0x00, 0x51, 0x54, 0x42, 0x0e, 0x10, 0x30, 0x59, + 0x52, 0x02, 0x30, 0x01, 0x2f, 0x00, 0x2e, 0x03, 0x2d, 0x50, 0x42, 0x42, 0x42, 0x12, 0x30, 0xd2, 0x7f, 0x80, 0xb2, + 0x03, 0x2f, 0x00, 0x30, 0x21, 0x2e, 0x80, 0x01, 0x12, 0x2d, 0x01, 0x2e, 0xc9, 0x00, 0x02, 0x80, 0x05, 0x2e, 0x80, + 0x01, 0x11, 0x30, 0x91, 0x28, 0x00, 0x40, 0x25, 0x2e, 0x80, 0x01, 0x10, 0x0e, 0x05, 0x2f, 0x01, 0x2e, 0x7f, 0x01, + 0x01, 0x90, 0x01, 0x2f, 0x98, 0x2e, 0xf3, 0x03, 0x00, 0x2e, 0xa0, 0x41, 0x01, 0x90, 0xa6, 0x7f, 0x90, 0x2e, 0xe3, + 0xb4, 0x01, 0x2e, 0x95, 0x01, 0x00, 0xa8, 0x90, 0x2e, 0xe3, 0xb4, 0x5b, 0x54, 0x95, 0x80, 0x82, 0x40, 0x80, 0xb2, + 0x02, 0x40, 0x2d, 0x8c, 0x3f, 0x52, 0x96, 0x7f, 0x90, 0x2e, 0xc2, 0xb3, 0x29, 0x0e, 0x76, 0x2f, 0x01, 0x2e, 0xc9, + 0x00, 0x00, 0x40, 0x81, 0x28, 0x45, 0x52, 0xb3, 0x30, 0x98, 0x2e, 0x0f, 0xca, 0x5d, 0x54, 0x80, 0x7f, 0x00, 0x2e, + 0xa1, 0x40, 0x72, 0x7f, 0x82, 0x80, 0x82, 0x40, 0x60, 0x7f, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, 0x2e, 0x74, + 0xc0, 0x62, 0x6f, 0x05, 0x30, 0x87, 0x40, 0xc0, 0x91, 0x04, 0x30, 0x05, 0x2f, 0x05, 0x2e, 0x83, 0x01, 0x80, 0xb2, + 0x14, 0x30, 0x00, 0x2f, 0x04, 0x30, 0x05, 0x2e, 0xc9, 0x00, 0x73, 0x6f, 0x81, 0x40, 0xe2, 0x40, 0x69, 0x04, 0x11, + 0x0f, 0xe1, 0x40, 0x16, 0x30, 0xfe, 0x29, 0xcb, 0x40, 0x02, 0x2f, 0x83, 0x6f, 0x83, 0x0f, 0x22, 0x2f, 0x47, 0x56, + 0x13, 0x0f, 0x12, 0x30, 0x77, 0x2f, 0x49, 0x54, 0x42, 0x0e, 0x12, 0x30, 0x73, 0x2f, 0x00, 0x91, 0x0a, 0x2f, 0x01, + 0x2e, 0x8b, 0x01, 0x19, 0xa8, 0x02, 0x30, 0x6c, 0x2f, 0x63, 0x50, 0x00, 0x2e, 0x17, 0x42, 0x05, 0x42, 0x68, 0x2c, + 0x12, 0x30, 0x0b, 0x25, 0x08, 0x0f, 0x50, 0x30, 0x02, 0x2f, 0x21, 0x2e, 0x83, 0x01, 0x03, 0x2d, 0x40, 0x30, 0x21, + 0x2e, 0x83, 0x01, 0x2b, 0x2e, 0x85, 0x01, 0x5a, 0x2c, 0x12, 0x30, 0x00, 0x91, 0x2b, 0x25, 0x04, 0x2f, 0x63, 0x50, + 0x02, 0x30, 0x17, 0x42, 0x17, 0x2c, 0x02, 0x42, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, 0x2e, 0x74, 0xc0, 0x05, + 0x2e, 0xc9, 0x00, 0x81, 0x84, 0x5b, 0x30, 0x82, 0x40, 0x37, 0x2e, 0x83, 0x01, 0x02, 0x0e, 0x07, 0x2f, 0x5f, 0x52, + 0x40, 0x30, 0x62, 0x40, 0x41, 0x40, 0x91, 0x0e, 0x01, 0x2f, 0x21, 0x2e, 0x83, 0x01, 0x05, 0x30, 0x2b, 0x2e, 0x85, + 0x01, 0x12, 0x30, 0x36, 0x2c, 0x16, 0x30, 0x15, 0x25, 0x81, 0x7f, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0x98, 0x2e, + 0x74, 0xc0, 0x19, 0xa2, 0x16, 0x30, 0x15, 0x2f, 0x05, 0x2e, 0x97, 0x01, 0x80, 0x6f, 0x82, 0x0e, 0x05, 0x2f, 0x01, + 0x2e, 0x86, 0x01, 0x06, 0x28, 0x21, 0x2e, 0x86, 0x01, 0x0b, 0x2d, 0x03, 0x2e, 0x87, 0x01, 0x5f, 0x54, 0x4e, 0x28, + 0x91, 0x42, 0x00, 0x2e, 0x82, 0x40, 0x90, 0x0e, 0x01, 0x2f, 0x21, 0x2e, 0x88, 0x01, 0x02, 0x30, 0x13, 0x2c, 0x05, + 0x30, 0xc0, 0x6f, 0x08, 0x1c, 0xa8, 0x0f, 0x16, 0x30, 0x05, 0x30, 0x5b, 0x50, 0x09, 0x2f, 0x02, 0x80, 0x2d, 0x2e, + 0x82, 0x01, 0x05, 0x42, 0x05, 0x80, 0x00, 0x2e, 0x02, 0x42, 0x3e, 0x80, 0x00, 0x2e, 0x06, 0x42, 0x02, 0x30, 0x90, + 0x6f, 0x3e, 0x88, 0x01, 0x40, 0x04, 0x41, 0x4c, 0x28, 0x01, 0x42, 0x07, 0x80, 0x10, 0x25, 0x24, 0x40, 0x00, 0x40, + 0x00, 0xa8, 0xf5, 0x22, 0x23, 0x29, 0x44, 0x42, 0x7a, 0x82, 0x7e, 0x88, 0x43, 0x40, 0x04, 0x41, 0x00, 0xab, 0xf5, + 0x23, 0xdf, 0x28, 0x43, 0x42, 0xd9, 0xa0, 0x14, 0x2f, 0x00, 0x90, 0x02, 0x2f, 0xd2, 0x6f, 0x81, 0xb2, 0x05, 0x2f, + 0x63, 0x54, 0x06, 0x28, 0x90, 0x42, 0x85, 0x42, 0x09, 0x2c, 0x02, 0x30, 0x5b, 0x50, 0x03, 0x80, 0x29, 0x2e, 0x7e, + 0x01, 0x2b, 0x2e, 0x82, 0x01, 0x05, 0x42, 0x12, 0x30, 0x2b, 0x2e, 0x83, 0x01, 0x45, 0x82, 0x00, 0x2e, 0x40, 0x40, + 0x7a, 0x82, 0x02, 0xa0, 0x08, 0x2f, 0x63, 0x50, 0x3b, 0x30, 0x15, 0x42, 0x05, 0x42, 0x37, 0x80, 0x37, 0x2e, 0x7e, + 0x01, 0x05, 0x42, 0x12, 0x30, 0x01, 0x2e, 0xc9, 0x00, 0x02, 0x8c, 0x40, 0x40, 0x84, 0x41, 0x7a, 0x8c, 0x04, 0x0f, + 0x03, 0x2f, 0x01, 0x2e, 0x8b, 0x01, 0x19, 0xa4, 0x04, 0x2f, 0x2b, 0x2e, 0x82, 0x01, 0x98, 0x2e, 0xf3, 0x03, 0x12, + 0x30, 0x81, 0x90, 0x61, 0x52, 0x08, 0x2f, 0x65, 0x42, 0x65, 0x42, 0x43, 0x80, 0x39, 0x84, 0x82, 0x88, 0x05, 0x42, + 0x45, 0x42, 0x85, 0x42, 0x05, 0x43, 0x00, 0x2e, 0x80, 0x41, 0x00, 0x90, 0x90, 0x2e, 0xe1, 0xb4, 0x65, 0x54, 0xc1, + 0x6f, 0x80, 0x40, 0x00, 0xb2, 0x43, 0x58, 0x69, 0x50, 0x44, 0x2f, 0x55, 0x5c, 0xb7, 0x87, 0x8c, 0x0f, 0x0d, 0x2e, + 0x96, 0x01, 0xc4, 0x40, 0x36, 0x2f, 0x41, 0x56, 0x8b, 0x0e, 0x2a, 0x2f, 0x0b, 0x52, 0xa1, 0x0e, 0x0a, 0x2f, 0x05, + 0x2e, 0x8f, 0x01, 0x14, 0x25, 0x98, 0x2e, 0xfe, 0xc9, 0x4b, 0x54, 0x02, 0x0f, 0x69, 0x50, 0x05, 0x30, 0x65, 0x54, + 0x15, 0x2f, 0x03, 0x2e, 0x8e, 0x01, 0x4d, 0x5c, 0x8e, 0x0f, 0x3a, 0x2f, 0x05, 0x2e, 0x8f, 0x01, 0x98, 0x2e, 0xfe, + 0xc9, 0x4f, 0x54, 0x82, 0x0f, 0x05, 0x30, 0x69, 0x50, 0x65, 0x54, 0x30, 0x2f, 0x6d, 0x52, 0x15, 0x30, 0x42, 0x8c, + 0x45, 0x42, 0x04, 0x30, 0x2b, 0x2c, 0x84, 0x43, 0x6b, 0x52, 0x42, 0x8c, 0x00, 0x2e, 0x85, 0x43, 0x15, 0x30, 0x24, + 0x2c, 0x45, 0x42, 0x8e, 0x0f, 0x20, 0x2f, 0x0d, 0x2e, 0x8e, 0x01, 0xb1, 0x0e, 0x1c, 0x2f, 0x23, 0x2e, 0x8e, 0x01, + 0x1a, 0x2d, 0x0e, 0x0e, 0x17, 0x2f, 0xa1, 0x0f, 0x15, 0x2f, 0x23, 0x2e, 0x8d, 0x01, 0x13, 0x2d, 0x98, 0x2e, 0x74, + 0xc0, 0x43, 0x54, 0xc2, 0x0e, 0x0a, 0x2f, 0x65, 0x50, 0x04, 0x80, 0x0b, 0x30, 0x06, 0x82, 0x0b, 0x42, 0x79, 0x80, + 0x41, 0x40, 0x12, 0x30, 0x25, 0x2e, 0x8c, 0x01, 0x01, 0x42, 0x05, 0x30, 0x69, 0x50, 0x65, 0x54, 0x84, 0x82, 0x43, + 0x84, 0xbe, 0x8c, 0x84, 0x40, 0x86, 0x41, 0x26, 0x29, 0x94, 0x42, 0xbe, 0x8e, 0xd5, 0x7f, 0x19, 0xa1, 0x43, 0x40, + 0x0b, 0x2e, 0x8c, 0x01, 0x84, 0x40, 0xc7, 0x41, 0x5d, 0x29, 0x27, 0x29, 0x45, 0x42, 0x84, 0x42, 0xc2, 0x7f, 0x01, + 0x2f, 0xc0, 0xb3, 0x1d, 0x2f, 0x05, 0x2e, 0x94, 0x01, 0x99, 0xa0, 0x01, 0x2f, 0x80, 0xb3, 0x13, 0x2f, 0x80, 0xb3, + 0x18, 0x2f, 0xc0, 0xb3, 0x16, 0x2f, 0x12, 0x40, 0x01, 0x40, 0x92, 0x7f, 0x98, 0x2e, 0x74, 0xc0, 0x92, 0x6f, 0x10, + 0x0f, 0x20, 0x30, 0x03, 0x2f, 0x10, 0x30, 0x21, 0x2e, 0x7e, 0x01, 0x0a, 0x2d, 0x21, 0x2e, 0x7e, 0x01, 0x07, 0x2d, + 0x20, 0x30, 0x21, 0x2e, 0x7e, 0x01, 0x03, 0x2d, 0x10, 0x30, 0x21, 0x2e, 0x7e, 0x01, 0xc2, 0x6f, 0x01, 0x2e, 0xc9, + 0x00, 0xbc, 0x84, 0x02, 0x80, 0x82, 0x40, 0x00, 0x40, 0x90, 0x0e, 0xd5, 0x6f, 0x02, 0x2f, 0x15, 0x30, 0x98, 0x2e, + 0xf3, 0x03, 0x41, 0x91, 0x05, 0x30, 0x07, 0x2f, 0x67, 0x50, 0x3d, 0x80, 0x2b, 0x2e, 0x8f, 0x01, 0x05, 0x42, 0x04, + 0x80, 0x00, 0x2e, 0x05, 0x42, 0x02, 0x2c, 0x00, 0x30, 0x00, 0x30, 0xa2, 0x6f, 0x98, 0x8a, 0x86, 0x40, 0x80, 0xa7, + 0x05, 0x2f, 0x98, 0x2e, 0xf3, 0x03, 0xc0, 0x30, 0x21, 0x2e, 0x95, 0x01, 0x06, 0x25, 0x1a, 0x25, 0xe2, 0x6f, 0x76, + 0x82, 0x96, 0x40, 0x56, 0x43, 0x51, 0x0e, 0xfb, 0x2f, 0xbb, 0x6f, 0x30, 0x5f, 0xb8, 0x2e, 0x01, 0x2e, 0xb8, 0x00, + 0x01, 0x31, 0x41, 0x08, 0x40, 0xb2, 0x20, 0x50, 0xf2, 0x30, 0x02, 0x08, 0xfb, 0x7f, 0x01, 0x30, 0x10, 0x2f, 0x05, + 0x2e, 0xcc, 0x00, 0x81, 0x90, 0xe0, 0x7f, 0x03, 0x2f, 0x23, 0x2e, 0xcc, 0x00, 0x98, 0x2e, 0x55, 0xb6, 0x98, 0x2e, + 0x1d, 0xb5, 0x10, 0x25, 0xfb, 0x6f, 0xe0, 0x6f, 0xe0, 0x5f, 0x80, 0x2e, 0x95, 0xcf, 0x98, 0x2e, 0x95, 0xcf, 0x10, + 0x30, 0x21, 0x2e, 0xcc, 0x00, 0xfb, 0x6f, 0xe0, 0x5f, 0xb8, 0x2e, 0x00, 0x51, 0x05, 0x58, 0xeb, 0x7f, 0x2a, 0x25, + 0x89, 0x52, 0x6f, 0x5a, 0x89, 0x50, 0x13, 0x41, 0x06, 0x40, 0xb3, 0x01, 0x16, 0x42, 0xcb, 0x16, 0x06, 0x40, 0xf3, + 0x02, 0x13, 0x42, 0x65, 0x0e, 0xf5, 0x2f, 0x05, 0x40, 0x14, 0x30, 0x2c, 0x29, 0x04, 0x42, 0x08, 0xa1, 0x00, 0x30, + 0x90, 0x2e, 0x52, 0xb6, 0xb3, 0x88, 0xb0, 0x8a, 0xb6, 0x84, 0xa4, 0x7f, 0xc4, 0x7f, 0xb5, 0x7f, 0xd5, 0x7f, 0x92, + 0x7f, 0x73, 0x30, 0x04, 0x30, 0x55, 0x40, 0x42, 0x40, 0x8a, 0x17, 0xf3, 0x08, 0x6b, 0x01, 0x90, 0x02, 0x53, 0xb8, + 0x4b, 0x82, 0xad, 0xbe, 0x71, 0x7f, 0x45, 0x0a, 0x09, 0x54, 0x84, 0x7f, 0x98, 0x2e, 0xd9, 0xc0, 0xa3, 0x6f, 0x7b, + 0x54, 0xd0, 0x42, 0xa3, 0x7f, 0xf2, 0x7f, 0x60, 0x7f, 0x20, 0x25, 0x71, 0x6f, 0x75, 0x5a, 0x77, 0x58, 0x79, 0x5c, + 0x75, 0x56, 0x98, 0x2e, 0x67, 0xcc, 0xb1, 0x6f, 0x62, 0x6f, 0x50, 0x42, 0xb1, 0x7f, 0xb3, 0x30, 0x10, 0x25, 0x98, + 0x2e, 0x0f, 0xca, 0x84, 0x6f, 0x20, 0x29, 0x71, 0x6f, 0x92, 0x6f, 0xa5, 0x6f, 0x76, 0x82, 0x6a, 0x0e, 0x73, 0x30, + 0x00, 0x30, 0xd0, 0x2f, 0xd2, 0x6f, 0xd1, 0x7f, 0xb4, 0x7f, 0x98, 0x2e, 0x2b, 0xb7, 0x15, 0xbd, 0x0b, 0xb8, 0x02, + 0x0a, 0xc2, 0x6f, 0xc0, 0x7f, 0x98, 0x2e, 0x2b, 0xb7, 0x15, 0xbd, 0x0b, 0xb8, 0x42, 0x0a, 0xc0, 0x6f, 0x08, 0x17, + 0x41, 0x18, 0x89, 0x16, 0xe1, 0x18, 0xd0, 0x18, 0xa1, 0x7f, 0x27, 0x25, 0x16, 0x25, 0x98, 0x2e, 0x79, 0xc0, 0x8b, + 0x54, 0x90, 0x7f, 0xb3, 0x30, 0x82, 0x40, 0x80, 0x90, 0x0d, 0x2f, 0x7d, 0x52, 0x92, 0x6f, 0x98, 0x2e, 0x0f, 0xca, + 0xb2, 0x6f, 0x90, 0x0e, 0x06, 0x2f, 0x8b, 0x50, 0x14, 0x30, 0x42, 0x6f, 0x51, 0x6f, 0x14, 0x42, 0x12, 0x42, 0x01, + 0x42, 0x00, 0x2e, 0x31, 0x6f, 0x98, 0x2e, 0x74, 0xc0, 0x41, 0x6f, 0x80, 0x7f, 0x98, 0x2e, 0x74, 0xc0, 0x82, 0x6f, + 0x10, 0x04, 0x43, 0x52, 0x01, 0x0f, 0x05, 0x2e, 0xcb, 0x00, 0x00, 0x30, 0x04, 0x30, 0x21, 0x2f, 0x51, 0x6f, 0x43, + 0x58, 0x8c, 0x0e, 0x04, 0x30, 0x1c, 0x2f, 0x85, 0x88, 0x41, 0x6f, 0x04, 0x41, 0x8c, 0x0f, 0x04, 0x30, 0x16, 0x2f, + 0x84, 0x88, 0x00, 0x2e, 0x04, 0x41, 0x04, 0x05, 0x8c, 0x0e, 0x04, 0x30, 0x0f, 0x2f, 0x82, 0x88, 0x31, 0x6f, 0x04, + 0x41, 0x04, 0x05, 0x8c, 0x0e, 0x04, 0x30, 0x08, 0x2f, 0x83, 0x88, 0x00, 0x2e, 0x04, 0x41, 0x8c, 0x0f, 0x04, 0x30, + 0x02, 0x2f, 0x21, 0x2e, 0xad, 0x01, 0x14, 0x30, 0x00, 0x91, 0x14, 0x2f, 0x03, 0x2e, 0xa1, 0x01, 0x41, 0x90, 0x0e, + 0x2f, 0x03, 0x2e, 0xad, 0x01, 0x14, 0x30, 0x4c, 0x28, 0x23, 0x2e, 0xad, 0x01, 0x46, 0xa0, 0x06, 0x2f, 0x81, 0x84, + 0x8d, 0x52, 0x48, 0x82, 0x82, 0x40, 0x21, 0x2e, 0xa1, 0x01, 0x42, 0x42, 0x5c, 0x2c, 0x02, 0x30, 0x05, 0x2e, 0xaa, + 0x01, 0x80, 0xb2, 0x02, 0x30, 0x55, 0x2f, 0x03, 0x2e, 0xa9, 0x01, 0x92, 0x6f, 0xb3, 0x30, 0x98, 0x2e, 0x0f, 0xca, + 0xb2, 0x6f, 0x90, 0x0f, 0x00, 0x30, 0x02, 0x30, 0x4a, 0x2f, 0xa2, 0x6f, 0x87, 0x52, 0x91, 0x00, 0x85, 0x52, 0x51, + 0x0e, 0x02, 0x2f, 0x00, 0x2e, 0x43, 0x2c, 0x02, 0x30, 0xc2, 0x6f, 0x7f, 0x52, 0x91, 0x0e, 0x02, 0x30, 0x3c, 0x2f, + 0x51, 0x6f, 0x81, 0x54, 0x98, 0x2e, 0xfe, 0xc9, 0x10, 0x25, 0xb3, 0x30, 0x21, 0x25, 0x98, 0x2e, 0x0f, 0xca, 0x32, + 0x6f, 0xc0, 0x7f, 0xb3, 0x30, 0x12, 0x25, 0x98, 0x2e, 0x0f, 0xca, 0x42, 0x6f, 0xb0, 0x7f, 0xb3, 0x30, 0x12, 0x25, + 0x98, 0x2e, 0x0f, 0xca, 0xb2, 0x6f, 0x90, 0x28, 0x83, 0x52, 0x98, 0x2e, 0xfe, 0xc9, 0xc2, 0x6f, 0x90, 0x0f, 0x00, + 0x30, 0x02, 0x30, 0x1d, 0x2f, 0x05, 0x2e, 0xa1, 0x01, 0x80, 0xb2, 0x12, 0x30, 0x0f, 0x2f, 0x42, 0x6f, 0x03, 0x2e, + 0xab, 0x01, 0x91, 0x0e, 0x02, 0x30, 0x12, 0x2f, 0x52, 0x6f, 0x03, 0x2e, 0xac, 0x01, 0x91, 0x0f, 0x02, 0x30, 0x0c, + 0x2f, 0x21, 0x2e, 0xaa, 0x01, 0x0a, 0x2c, 0x12, 0x30, 0x03, 0x2e, 0xcb, 0x00, 0x8d, 0x58, 0x08, 0x89, 0x41, 0x40, + 0x11, 0x43, 0x00, 0x43, 0x25, 0x2e, 0xa1, 0x01, 0xd4, 0x6f, 0x8f, 0x52, 0x00, 0x43, 0x3a, 0x89, 0x00, 0x2e, 0x10, + 0x43, 0x10, 0x43, 0x61, 0x0e, 0xfb, 0x2f, 0x03, 0x2e, 0xa0, 0x01, 0x11, 0x1a, 0x02, 0x2f, 0x02, 0x25, 0x21, 0x2e, + 0xa0, 0x01, 0xeb, 0x6f, 0x00, 0x5f, 0xb8, 0x2e, 0x91, 0x52, 0x10, 0x30, 0x02, 0x30, 0x95, 0x56, 0x52, 0x42, 0x4b, + 0x0e, 0xfc, 0x2f, 0x8d, 0x54, 0x88, 0x82, 0x93, 0x56, 0x80, 0x42, 0x53, 0x42, 0x40, 0x42, 0x42, 0x86, 0x83, 0x54, + 0xc0, 0x2e, 0xc2, 0x42, 0x00, 0x2e, 0xa3, 0x52, 0x00, 0x51, 0x52, 0x40, 0x47, 0x40, 0x1a, 0x25, 0x01, 0x2e, 0x97, + 0x00, 0x8f, 0xbe, 0x72, 0x86, 0xfb, 0x7f, 0x0b, 0x30, 0x7c, 0xbf, 0xa5, 0x50, 0x10, 0x08, 0xdf, 0xba, 0x70, 0x88, + 0xf8, 0xbf, 0xcb, 0x42, 0xd3, 0x7f, 0x6c, 0xbb, 0xfc, 0xbb, 0xc5, 0x0a, 0x90, 0x7f, 0x1b, 0x7f, 0x0b, 0x43, 0xc0, + 0xb2, 0xe5, 0x7f, 0xb7, 0x7f, 0xa6, 0x7f, 0xc4, 0x7f, 0x90, 0x2e, 0x1c, 0xb7, 0x07, 0x2e, 0xd2, 0x00, 0xc0, 0xb2, + 0x0b, 0x2f, 0x97, 0x52, 0x01, 0x2e, 0xcd, 0x00, 0x82, 0x7f, 0x98, 0x2e, 0xbb, 0xcc, 0x0b, 0x30, 0x37, 0x2e, 0xd2, + 0x00, 0x82, 0x6f, 0x90, 0x6f, 0x1a, 0x25, 0x00, 0xb2, 0x8b, 0x7f, 0x14, 0x2f, 0xa6, 0xbd, 0x25, 0xbd, 0xb6, 0xb9, + 0x2f, 0xb9, 0x80, 0xb2, 0xd4, 0xb0, 0x0c, 0x2f, 0x99, 0x54, 0x9b, 0x56, 0x0b, 0x30, 0x0b, 0x2e, 0xb1, 0x00, 0xa1, + 0x58, 0x9b, 0x42, 0xdb, 0x42, 0x6c, 0x09, 0x2b, 0x2e, 0xb1, 0x00, 0x8b, 0x42, 0xcb, 0x42, 0x86, 0x7f, 0x73, 0x84, + 0xa7, 0x56, 0xc3, 0x08, 0x39, 0x52, 0x05, 0x50, 0x72, 0x7f, 0x63, 0x7f, 0x98, 0x2e, 0xc2, 0xc0, 0xe1, 0x6f, 0x62, + 0x6f, 0xd1, 0x0a, 0x01, 0x2e, 0xcd, 0x00, 0xd5, 0x6f, 0xc4, 0x6f, 0x72, 0x6f, 0x97, 0x52, 0x9d, 0x5c, 0x98, 0x2e, + 0x06, 0xcd, 0x23, 0x6f, 0x90, 0x6f, 0x99, 0x52, 0xc0, 0xb2, 0x04, 0xbd, 0x54, 0x40, 0xaf, 0xb9, 0x45, 0x40, 0xe1, + 0x7f, 0x02, 0x30, 0x06, 0x2f, 0xc0, 0xb2, 0x02, 0x30, 0x03, 0x2f, 0x9b, 0x5c, 0x12, 0x30, 0x94, 0x43, 0x85, 0x43, + 0x03, 0xbf, 0x6f, 0xbb, 0x80, 0xb3, 0x20, 0x2f, 0x06, 0x6f, 0x26, 0x01, 0x16, 0x6f, 0x6e, 0x03, 0x45, 0x42, 0xc0, + 0x90, 0x29, 0x2e, 0xce, 0x00, 0x9b, 0x52, 0x14, 0x2f, 0x9b, 0x5c, 0x00, 0x2e, 0x93, 0x41, 0x86, 0x41, 0xe3, 0x04, + 0xae, 0x07, 0x80, 0xab, 0x04, 0x2f, 0x80, 0x91, 0x0a, 0x2f, 0x86, 0x6f, 0x73, 0x0f, 0x07, 0x2f, 0x83, 0x6f, 0xc0, + 0xb2, 0x04, 0x2f, 0x54, 0x42, 0x45, 0x42, 0x12, 0x30, 0x04, 0x2c, 0x11, 0x30, 0x02, 0x2c, 0x11, 0x30, 0x11, 0x30, + 0x02, 0xbc, 0x0f, 0xb8, 0xd2, 0x7f, 0x00, 0xb2, 0x0a, 0x2f, 0x01, 0x2e, 0xfc, 0x00, 0x05, 0x2e, 0xc7, 0x01, 0x10, + 0x1a, 0x02, 0x2f, 0x21, 0x2e, 0xc7, 0x01, 0x03, 0x2d, 0x02, 0x2c, 0x01, 0x30, 0x01, 0x30, 0xb0, 0x6f, 0x98, 0x2e, + 0x95, 0xcf, 0xd1, 0x6f, 0xa0, 0x6f, 0x98, 0x2e, 0x95, 0xcf, 0xe2, 0x6f, 0x9f, 0x52, 0x01, 0x2e, 0xce, 0x00, 0x82, + 0x40, 0x50, 0x42, 0x0c, 0x2c, 0x42, 0x42, 0x11, 0x30, 0x23, 0x2e, 0xd2, 0x00, 0x01, 0x30, 0xb0, 0x6f, 0x98, 0x2e, + 0x95, 0xcf, 0xa0, 0x6f, 0x01, 0x30, 0x98, 0x2e, 0x95, 0xcf, 0x00, 0x2e, 0xfb, 0x6f, 0x00, 0x5f, 0xb8, 0x2e, 0x83, + 0x86, 0x01, 0x30, 0x00, 0x30, 0x94, 0x40, 0x24, 0x18, 0x06, 0x00, 0x53, 0x0e, 0x4f, 0x02, 0xf9, 0x2f, 0xb8, 0x2e, + 0xa9, 0x52, 0x00, 0x2e, 0x60, 0x40, 0x41, 0x40, 0x0d, 0xbc, 0x98, 0xbc, 0xc0, 0x2e, 0x01, 0x0a, 0x0f, 0xb8, 0xab, + 0x52, 0x53, 0x3c, 0x52, 0x40, 0x40, 0x40, 0x4b, 0x00, 0x82, 0x16, 0x26, 0xb9, 0x01, 0xb8, 0x41, 0x40, 0x10, 0x08, + 0x97, 0xb8, 0x01, 0x08, 0xc0, 0x2e, 0x11, 0x30, 0x01, 0x08, 0x43, 0x86, 0x25, 0x40, 0x04, 0x40, 0xd8, 0xbe, 0x2c, + 0x0b, 0x22, 0x11, 0x54, 0x42, 0x03, 0x80, 0x4b, 0x0e, 0xf6, 0x2f, 0xb8, 0x2e, 0x9f, 0x50, 0x10, 0x50, 0xad, 0x52, + 0x05, 0x2e, 0xd3, 0x00, 0xfb, 0x7f, 0x00, 0x2e, 0x13, 0x40, 0x93, 0x42, 0x41, 0x0e, 0xfb, 0x2f, 0x98, 0x2e, 0xa5, + 0xb7, 0x98, 0x2e, 0x87, 0xcf, 0x01, 0x2e, 0xd9, 0x00, 0x00, 0xb2, 0xfb, 0x6f, 0x0b, 0x2f, 0x01, 0x2e, 0x69, 0xf7, + 0xb1, 0x3f, 0x01, 0x08, 0x01, 0x30, 0xf0, 0x5f, 0x23, 0x2e, 0xd9, 0x00, 0x21, 0x2e, 0x69, 0xf7, 0x80, 0x2e, 0x7a, + 0xb7, 0xf0, 0x5f, 0xb8, 0x2e, 0x01, 0x2e, 0xc0, 0xf8, 0x03, 0x2e, 0xfc, 0xf5, 0x15, 0x54, 0xaf, 0x56, 0x82, 0x08, + 0x0b, 0x2e, 0x69, 0xf7, 0xcb, 0x0a, 0xb1, 0x58, 0x80, 0x90, 0xdd, 0xbe, 0x4c, 0x08, 0x5f, 0xb9, 0x59, 0x22, 0x80, + 0x90, 0x07, 0x2f, 0x03, 0x34, 0xc3, 0x08, 0xf2, 0x3a, 0x0a, 0x08, 0x02, 0x35, 0xc0, 0x90, 0x4a, 0x0a, 0x48, 0x22, + 0xc0, 0x2e, 0x23, 0x2e, 0xfc, 0xf5, 0x10, 0x50, 0xfb, 0x7f, 0x98, 0x2e, 0x56, 0xc7, 0x98, 0x2e, 0x49, 0xc3, 0x10, + 0x30, 0xfb, 0x6f, 0xf0, 0x5f, 0x21, 0x2e, 0xcc, 0x00, 0x21, 0x2e, 0xca, 0x00, 0xb8, 0x2e, 0x03, 0x2e, 0xd3, 0x00, + 0x16, 0xb8, 0x02, 0x34, 0x4a, 0x0c, 0x21, 0x2e, 0x2d, 0xf5, 0xc0, 0x2e, 0x23, 0x2e, 0xd3, 0x00, 0x03, 0xbc, 0x21, + 0x2e, 0xd5, 0x00, 0x03, 0x2e, 0xd5, 0x00, 0x40, 0xb2, 0x10, 0x30, 0x21, 0x2e, 0x77, 0x00, 0x01, 0x30, 0x05, 0x2f, + 0x05, 0x2e, 0xd8, 0x00, 0x80, 0x90, 0x01, 0x2f, 0x23, 0x2e, 0x6f, 0xf5, 0xc0, 0x2e, 0x21, 0x2e, 0xd9, 0x00, 0x11, + 0x30, 0x81, 0x08, 0x01, 0x2e, 0x6a, 0xf7, 0x71, 0x3f, 0x23, 0xbd, 0x01, 0x08, 0x02, 0x0a, 0xc0, 0x2e, 0x21, 0x2e, + 0x6a, 0xf7, 0x30, 0x25, 0x00, 0x30, 0x21, 0x2e, 0x5a, 0xf5, 0x10, 0x50, 0x21, 0x2e, 0x7b, 0x00, 0x21, 0x2e, 0x7c, + 0x00, 0xfb, 0x7f, 0x98, 0x2e, 0xc3, 0xb7, 0x40, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0xfb, 0x6f, 0xf0, 0x5f, 0x03, 0x25, + 0x80, 0x2e, 0xaf, 0xb7, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x01, 0x2e, 0x5d, 0xf7, 0x08, 0xbc, 0x80, 0xac, 0x0e, 0xbb, 0x02, 0x2f, + 0x00, 0x30, 0x41, 0x04, 0x82, 0x06, 0xc0, 0xa4, 0x00, 0x30, 0x11, 0x2f, 0x40, 0xa9, 0x03, 0x2f, 0x40, 0x91, 0x0d, + 0x2f, 0x00, 0xa7, 0x0b, 0x2f, 0x80, 0xb3, 0xb3, 0x58, 0x02, 0x2f, 0x90, 0xa1, 0x26, 0x13, 0x20, 0x23, 0x80, 0x90, + 0x10, 0x30, 0x01, 0x2f, 0xcc, 0x0e, 0x00, 0x2f, 0x00, 0x30, 0xb8, 0x2e, 0xb5, 0x50, 0x18, 0x08, 0x08, 0xbc, 0x88, + 0xb6, 0x0d, 0x17, 0xc6, 0xbd, 0x56, 0xbc, 0xb7, 0x58, 0xda, 0xba, 0x04, 0x01, 0x1d, 0x0a, 0x10, 0x50, 0x05, 0x30, + 0x32, 0x25, 0x45, 0x03, 0xfb, 0x7f, 0xf6, 0x30, 0x21, 0x25, 0x98, 0x2e, 0x37, 0xca, 0x16, 0xb5, 0x9a, 0xbc, 0x06, + 0xb8, 0x80, 0xa8, 0x41, 0x0a, 0x0e, 0x2f, 0x80, 0x90, 0x02, 0x2f, 0x2d, 0x50, 0x48, 0x0f, 0x09, 0x2f, 0xbf, 0xa0, + 0x04, 0x2f, 0xbf, 0x90, 0x06, 0x2f, 0xb7, 0x54, 0xca, 0x0f, 0x03, 0x2f, 0x00, 0x2e, 0x02, 0x2c, 0xb7, 0x52, 0x2d, + 0x52, 0xf2, 0x33, 0x98, 0x2e, 0xd9, 0xc0, 0xfb, 0x6f, 0xf1, 0x37, 0xc0, 0x2e, 0x01, 0x08, 0xf0, 0x5f, 0xbf, 0x56, + 0xb9, 0x54, 0xd0, 0x40, 0xc4, 0x40, 0x0b, 0x2e, 0xfd, 0xf3, 0xbf, 0x52, 0x90, 0x42, 0x94, 0x42, 0x95, 0x42, 0x05, + 0x30, 0xc1, 0x50, 0x0f, 0x88, 0x06, 0x40, 0x04, 0x41, 0x96, 0x42, 0xc5, 0x42, 0x48, 0xbe, 0x73, 0x30, 0x0d, 0x2e, + 0xd8, 0x00, 0x4f, 0xba, 0x84, 0x42, 0x03, 0x42, 0x81, 0xb3, 0x02, 0x2f, 0x2b, 0x2e, 0x6f, 0xf5, 0x06, 0x2d, 0x05, + 0x2e, 0x77, 0xf7, 0xbd, 0x56, 0x93, 0x08, 0x25, 0x2e, 0x77, 0xf7, 0xbb, 0x54, 0x25, 0x2e, 0xc2, 0xf5, 0x07, 0x2e, + 0xfd, 0xf3, 0x42, 0x30, 0xb4, 0x33, 0xda, 0x0a, 0x4c, 0x00, 0x27, 0x2e, 0xfd, 0xf3, 0x43, 0x40, 0xd4, 0x3f, 0xdc, + 0x08, 0x43, 0x42, 0x00, 0x2e, 0x00, 0x2e, 0x43, 0x40, 0x24, 0x30, 0xdc, 0x0a, 0x43, 0x42, 0x04, 0x80, 0x03, 0x2e, + 0xfd, 0xf3, 0x4a, 0x0a, 0x23, 0x2e, 0xfd, 0xf3, 0x61, 0x34, 0xc0, 0x2e, 0x01, 0x42, 0x00, 0x2e, 0x60, 0x50, 0x1a, + 0x25, 0x7a, 0x86, 0xe0, 0x7f, 0xf3, 0x7f, 0x03, 0x25, 0xc3, 0x52, 0x41, 0x84, 0xdb, 0x7f, 0x33, 0x30, 0x98, 0x2e, + 0x16, 0xc2, 0x1a, 0x25, 0x7d, 0x82, 0xf0, 0x6f, 0xe2, 0x6f, 0x32, 0x25, 0x16, 0x40, 0x94, 0x40, 0x26, 0x01, 0x85, + 0x40, 0x8e, 0x17, 0xc4, 0x42, 0x6e, 0x03, 0x95, 0x42, 0x41, 0x0e, 0xf4, 0x2f, 0xdb, 0x6f, 0xa0, 0x5f, 0xb8, 0x2e, + 0xb0, 0x51, 0xfb, 0x7f, 0x98, 0x2e, 0xe8, 0x0d, 0x5a, 0x25, 0x98, 0x2e, 0x0f, 0x0e, 0xcb, 0x58, 0x32, 0x87, 0xc4, + 0x7f, 0x65, 0x89, 0x6b, 0x8d, 0xc5, 0x5a, 0x65, 0x7f, 0xe1, 0x7f, 0x83, 0x7f, 0xa6, 0x7f, 0x74, 0x7f, 0xd0, 0x7f, + 0xb6, 0x7f, 0x94, 0x7f, 0x17, 0x30, 0xc7, 0x52, 0xc9, 0x54, 0x51, 0x7f, 0x00, 0x2e, 0x85, 0x6f, 0x42, 0x7f, 0x00, + 0x2e, 0x51, 0x41, 0x45, 0x81, 0x42, 0x41, 0x13, 0x40, 0x3b, 0x8a, 0x00, 0x40, 0x4b, 0x04, 0xd0, 0x06, 0xc0, 0xac, + 0x85, 0x7f, 0x02, 0x2f, 0x02, 0x30, 0x51, 0x04, 0xd3, 0x06, 0x41, 0x84, 0x05, 0x30, 0x5d, 0x02, 0xc9, 0x16, 0xdf, + 0x08, 0xd3, 0x00, 0x8d, 0x02, 0xaf, 0xbc, 0xb1, 0xb9, 0x59, 0x0a, 0x65, 0x6f, 0x11, 0x43, 0xa1, 0xb4, 0x52, 0x41, + 0x53, 0x41, 0x01, 0x43, 0x34, 0x7f, 0x65, 0x7f, 0x26, 0x31, 0xe5, 0x6f, 0xd4, 0x6f, 0x98, 0x2e, 0x37, 0xca, 0x32, + 0x6f, 0x75, 0x6f, 0x83, 0x40, 0x42, 0x41, 0x23, 0x7f, 0x12, 0x7f, 0xf6, 0x30, 0x40, 0x25, 0x51, 0x25, 0x98, 0x2e, + 0x37, 0xca, 0x14, 0x6f, 0x20, 0x05, 0x70, 0x6f, 0x25, 0x6f, 0x69, 0x07, 0xa2, 0x6f, 0x31, 0x6f, 0x0b, 0x30, 0x04, + 0x42, 0x9b, 0x42, 0x8b, 0x42, 0x55, 0x42, 0x32, 0x7f, 0x40, 0xa9, 0xc3, 0x6f, 0x71, 0x7f, 0x02, 0x30, 0xd0, 0x40, + 0xc3, 0x7f, 0x03, 0x2f, 0x40, 0x91, 0x15, 0x2f, 0x00, 0xa7, 0x13, 0x2f, 0x00, 0xa4, 0x11, 0x2f, 0x84, 0xbd, 0x98, + 0x2e, 0x79, 0xca, 0x55, 0x6f, 0xb7, 0x54, 0x54, 0x41, 0x82, 0x00, 0xf3, 0x3f, 0x45, 0x41, 0xcb, 0x02, 0xf6, 0x30, + 0x98, 0x2e, 0x37, 0xca, 0x35, 0x6f, 0xa4, 0x6f, 0x41, 0x43, 0x03, 0x2c, 0x00, 0x43, 0xa4, 0x6f, 0x35, 0x6f, 0x17, + 0x30, 0x42, 0x6f, 0x51, 0x6f, 0x93, 0x40, 0x42, 0x82, 0x00, 0x41, 0xc3, 0x00, 0x03, 0x43, 0x51, 0x7f, 0x00, 0x2e, + 0x94, 0x40, 0x41, 0x41, 0x4c, 0x02, 0xc4, 0x6f, 0xd1, 0x56, 0x63, 0x0e, 0x74, 0x6f, 0x51, 0x43, 0xa5, 0x7f, 0x8a, + 0x2f, 0x09, 0x2e, 0xd8, 0x00, 0x01, 0xb3, 0x21, 0x2f, 0xcb, 0x58, 0x90, 0x6f, 0x13, 0x41, 0xb6, 0x6f, 0xe4, 0x7f, + 0x00, 0x2e, 0x91, 0x41, 0x14, 0x40, 0x92, 0x41, 0x15, 0x40, 0x17, 0x2e, 0x6f, 0xf5, 0xb6, 0x7f, 0xd0, 0x7f, 0xcb, + 0x7f, 0x98, 0x2e, 0x00, 0x0c, 0x07, 0x15, 0xc2, 0x6f, 0x14, 0x0b, 0x29, 0x2e, 0x6f, 0xf5, 0xc3, 0xa3, 0xc1, 0x8f, + 0xe4, 0x6f, 0xd0, 0x6f, 0xe6, 0x2f, 0x14, 0x30, 0x05, 0x2e, 0x6f, 0xf5, 0x14, 0x0b, 0x29, 0x2e, 0x6f, 0xf5, 0x18, + 0x2d, 0xcd, 0x56, 0x04, 0x32, 0xb5, 0x6f, 0x1c, 0x01, 0x51, 0x41, 0x52, 0x41, 0xc3, 0x40, 0xb5, 0x7f, 0xe4, 0x7f, + 0x98, 0x2e, 0x1f, 0x0c, 0xe4, 0x6f, 0x21, 0x87, 0x00, 0x43, 0x04, 0x32, 0xcf, 0x54, 0x5a, 0x0e, 0xef, 0x2f, 0x15, + 0x54, 0x09, 0x2e, 0x77, 0xf7, 0x22, 0x0b, 0x29, 0x2e, 0x77, 0xf7, 0xfb, 0x6f, 0x50, 0x5e, 0xb8, 0x2e, 0x10, 0x50, + 0x01, 0x2e, 0xd4, 0x00, 0x00, 0xb2, 0xfb, 0x7f, 0x51, 0x2f, 0x01, 0xb2, 0x48, 0x2f, 0x02, 0xb2, 0x42, 0x2f, 0x03, + 0x90, 0x56, 0x2f, 0xd7, 0x52, 0x79, 0x80, 0x42, 0x40, 0x81, 0x84, 0x00, 0x40, 0x42, 0x42, 0x98, 0x2e, 0x93, 0x0c, + 0xd9, 0x54, 0xd7, 0x50, 0xa1, 0x40, 0x98, 0xbd, 0x82, 0x40, 0x3e, 0x82, 0xda, 0x0a, 0x44, 0x40, 0x8b, 0x16, 0xe3, + 0x00, 0x53, 0x42, 0x00, 0x2e, 0x43, 0x40, 0x9a, 0x02, 0x52, 0x42, 0x00, 0x2e, 0x41, 0x40, 0x15, 0x54, 0x4a, 0x0e, + 0x3a, 0x2f, 0x3a, 0x82, 0x00, 0x30, 0x41, 0x40, 0x21, 0x2e, 0x85, 0x0f, 0x40, 0xb2, 0x0a, 0x2f, 0x98, 0x2e, 0xb1, + 0x0c, 0x98, 0x2e, 0x45, 0x0e, 0x98, 0x2e, 0x5b, 0x0e, 0xfb, 0x6f, 0xf0, 0x5f, 0x00, 0x30, 0x80, 0x2e, 0xce, 0xb7, + 0xdd, 0x52, 0xd3, 0x54, 0x42, 0x42, 0x4f, 0x84, 0x73, 0x30, 0xdb, 0x52, 0x83, 0x42, 0x1b, 0x30, 0x6b, 0x42, 0x23, + 0x30, 0x27, 0x2e, 0xd7, 0x00, 0x37, 0x2e, 0xd4, 0x00, 0x21, 0x2e, 0xd6, 0x00, 0x7a, 0x84, 0x17, 0x2c, 0x42, 0x42, + 0x30, 0x30, 0x21, 0x2e, 0xd4, 0x00, 0x12, 0x2d, 0x21, 0x30, 0x00, 0x30, 0x23, 0x2e, 0xd4, 0x00, 0x21, 0x2e, 0x7b, + 0xf7, 0x0b, 0x2d, 0x17, 0x30, 0x98, 0x2e, 0x51, 0x0c, 0xd5, 0x50, 0x0c, 0x82, 0x72, 0x30, 0x2f, 0x2e, 0xd4, 0x00, + 0x25, 0x2e, 0x7b, 0xf7, 0x40, 0x42, 0x00, 0x2e, 0xfb, 0x6f, 0xf0, 0x5f, 0xb8, 0x2e, 0x70, 0x50, 0x0a, 0x25, 0x39, + 0x86, 0xfb, 0x7f, 0xe1, 0x32, 0x62, 0x30, 0x98, 0x2e, 0xc2, 0xc4, 0xb5, 0x56, 0xa5, 0x6f, 0xab, 0x08, 0x91, 0x6f, + 0x4b, 0x08, 0xdf, 0x56, 0xc4, 0x6f, 0x23, 0x09, 0x4d, 0xba, 0x93, 0xbc, 0x8c, 0x0b, 0xd1, 0x6f, 0x0b, 0x09, 0xcb, + 0x52, 0xe1, 0x5e, 0x56, 0x42, 0xaf, 0x09, 0x4d, 0xba, 0x23, 0xbd, 0x94, 0x0a, 0xe5, 0x6f, 0x68, 0xbb, 0xeb, 0x08, + 0xbd, 0xb9, 0x63, 0xbe, 0xfb, 0x6f, 0x52, 0x42, 0xe3, 0x0a, 0xc0, 0x2e, 0x43, 0x42, 0x90, 0x5f, 0xd1, 0x50, 0x03, + 0x2e, 0x25, 0xf3, 0x13, 0x40, 0x00, 0x40, 0x9b, 0xbc, 0x9b, 0xb4, 0x08, 0xbd, 0xb8, 0xb9, 0x98, 0xbc, 0xda, 0x0a, + 0x08, 0xb6, 0x89, 0x16, 0xc0, 0x2e, 0x19, 0x00, 0x62, 0x02, 0x10, 0x50, 0xfb, 0x7f, 0x98, 0x2e, 0x81, 0x0d, 0x01, + 0x2e, 0xd4, 0x00, 0x31, 0x30, 0x08, 0x04, 0xfb, 0x6f, 0x01, 0x30, 0xf0, 0x5f, 0x23, 0x2e, 0xd6, 0x00, 0x21, 0x2e, + 0xd7, 0x00, 0xb8, 0x2e, 0x01, 0x2e, 0xd7, 0x00, 0x03, 0x2e, 0xd6, 0x00, 0x48, 0x0e, 0x01, 0x2f, 0x80, 0x2e, 0x1f, + 0x0e, 0xb8, 0x2e, 0xe3, 0x50, 0x21, 0x34, 0x01, 0x42, 0x82, 0x30, 0xc1, 0x32, 0x25, 0x2e, 0x62, 0xf5, 0x01, 0x00, + 0x22, 0x30, 0x01, 0x40, 0x4a, 0x0a, 0x01, 0x42, 0xb8, 0x2e, 0xe3, 0x54, 0xf0, 0x3b, 0x83, 0x40, 0xd8, 0x08, 0xe5, + 0x52, 0x83, 0x42, 0x00, 0x30, 0x83, 0x30, 0x50, 0x42, 0xc4, 0x32, 0x27, 0x2e, 0x64, 0xf5, 0x94, 0x00, 0x50, 0x42, + 0x40, 0x42, 0xd3, 0x3f, 0x84, 0x40, 0x7d, 0x82, 0xe3, 0x08, 0x40, 0x42, 0x83, 0x42, 0xb8, 0x2e, 0xdd, 0x52, 0x00, + 0x30, 0x40, 0x42, 0x7c, 0x86, 0xb9, 0x52, 0x09, 0x2e, 0x70, 0x0f, 0xbf, 0x54, 0xc4, 0x42, 0xd3, 0x86, 0x54, 0x40, + 0x55, 0x40, 0x94, 0x42, 0x85, 0x42, 0x21, 0x2e, 0xd7, 0x00, 0x42, 0x40, 0x25, 0x2e, 0xfd, 0xf3, 0xc0, 0x42, 0x7e, + 0x82, 0x05, 0x2e, 0x7d, 0x00, 0x80, 0xb2, 0x14, 0x2f, 0x05, 0x2e, 0x89, 0x00, 0x27, 0xbd, 0x2f, 0xb9, 0x80, 0x90, + 0x02, 0x2f, 0x21, 0x2e, 0x6f, 0xf5, 0x0c, 0x2d, 0x07, 0x2e, 0x71, 0x0f, 0x14, 0x30, 0x1c, 0x09, 0x05, 0x2e, 0x77, + 0xf7, 0xbd, 0x56, 0x47, 0xbe, 0x93, 0x08, 0x94, 0x0a, 0x25, 0x2e, 0x77, 0xf7, 0xe7, 0x54, 0x50, 0x42, 0x4a, 0x0e, + 0xfc, 0x2f, 0xb8, 0x2e, 0x50, 0x50, 0x02, 0x30, 0x43, 0x86, 0xe5, 0x50, 0xfb, 0x7f, 0xe3, 0x7f, 0xd2, 0x7f, 0xc0, + 0x7f, 0xb1, 0x7f, 0x00, 0x2e, 0x41, 0x40, 0x00, 0x40, 0x48, 0x04, 0x98, 0x2e, 0x74, 0xc0, 0x1e, 0xaa, 0xd3, 0x6f, + 0x14, 0x30, 0xb1, 0x6f, 0xe3, 0x22, 0xc0, 0x6f, 0x52, 0x40, 0xe4, 0x6f, 0x4c, 0x0e, 0x12, 0x42, 0xd3, 0x7f, 0xeb, + 0x2f, 0x03, 0x2e, 0x86, 0x0f, 0x40, 0x90, 0x11, 0x30, 0x03, 0x2f, 0x23, 0x2e, 0x86, 0x0f, 0x02, 0x2c, 0x00, 0x30, + 0xd0, 0x6f, 0xfb, 0x6f, 0xb0, 0x5f, 0xb8, 0x2e, 0x40, 0x50, 0xf1, 0x7f, 0x0a, 0x25, 0x3c, 0x86, 0xeb, 0x7f, 0x41, + 0x33, 0x22, 0x30, 0x98, 0x2e, 0xc2, 0xc4, 0xd3, 0x6f, 0xf4, 0x30, 0xdc, 0x09, 0x47, 0x58, 0xc2, 0x6f, 0x94, 0x09, + 0xeb, 0x58, 0x6a, 0xbb, 0xdc, 0x08, 0xb4, 0xb9, 0xb1, 0xbd, 0xe9, 0x5a, 0x95, 0x08, 0x21, 0xbd, 0xf6, 0xbf, 0x77, + 0x0b, 0x51, 0xbe, 0xf1, 0x6f, 0xeb, 0x6f, 0x52, 0x42, 0x54, 0x42, 0xc0, 0x2e, 0x43, 0x42, 0xc0, 0x5f, 0x50, 0x50, + 0xf5, 0x50, 0x31, 0x30, 0x11, 0x42, 0xfb, 0x7f, 0x7b, 0x30, 0x0b, 0x42, 0x11, 0x30, 0x02, 0x80, 0x23, 0x33, 0x01, + 0x42, 0x03, 0x00, 0x07, 0x2e, 0x80, 0x03, 0x05, 0x2e, 0xd3, 0x00, 0x23, 0x52, 0xe2, 0x7f, 0xd3, 0x7f, 0xc0, 0x7f, + 0x98, 0x2e, 0xb6, 0x0e, 0xd1, 0x6f, 0x08, 0x0a, 0x1a, 0x25, 0x7b, 0x86, 0xd0, 0x7f, 0x01, 0x33, 0x12, 0x30, 0x98, + 0x2e, 0xc2, 0xc4, 0xd1, 0x6f, 0x08, 0x0a, 0x00, 0xb2, 0x0d, 0x2f, 0xe3, 0x6f, 0x01, 0x2e, 0x80, 0x03, 0x51, 0x30, + 0xc7, 0x86, 0x23, 0x2e, 0x21, 0xf2, 0x08, 0xbc, 0xc0, 0x42, 0x98, 0x2e, 0xa5, 0xb7, 0x00, 0x2e, 0x00, 0x2e, 0xd0, + 0x2e, 0xb0, 0x6f, 0x0b, 0xb8, 0x03, 0x2e, 0x1b, 0x00, 0x08, 0x1a, 0xb0, 0x7f, 0x70, 0x30, 0x04, 0x2f, 0x21, 0x2e, + 0x21, 0xf2, 0x00, 0x2e, 0x00, 0x2e, 0xd0, 0x2e, 0x98, 0x2e, 0x6d, 0xc0, 0x98, 0x2e, 0x5d, 0xc0, 0xed, 0x50, 0x98, + 0x2e, 0x44, 0xcb, 0xef, 0x50, 0x98, 0x2e, 0x46, 0xc3, 0xf1, 0x50, 0x98, 0x2e, 0x53, 0xc7, 0x35, 0x50, 0x98, 0x2e, + 0x64, 0xcf, 0x10, 0x30, 0x98, 0x2e, 0xdc, 0x03, 0x20, 0x26, 0xc0, 0x6f, 0x02, 0x31, 0x12, 0x42, 0xab, 0x33, 0x0b, + 0x42, 0x37, 0x80, 0x01, 0x30, 0x01, 0x42, 0xf3, 0x37, 0xf7, 0x52, 0xfb, 0x50, 0x44, 0x40, 0xa2, 0x0a, 0x42, 0x42, + 0x8b, 0x31, 0x09, 0x2e, 0x5e, 0xf7, 0xf9, 0x54, 0xe3, 0x08, 0x83, 0x42, 0x1b, 0x42, 0x23, 0x33, 0x4b, 0x00, 0xbc, + 0x84, 0x0b, 0x40, 0x33, 0x30, 0x83, 0x42, 0x0b, 0x42, 0xe0, 0x7f, 0xd1, 0x7f, 0x98, 0x2e, 0x58, 0xb7, 0xd1, 0x6f, + 0x80, 0x30, 0x40, 0x42, 0x03, 0x30, 0xe0, 0x6f, 0xf3, 0x54, 0x04, 0x30, 0x00, 0x2e, 0x00, 0x2e, 0x01, 0x89, 0x62, + 0x0e, 0xfa, 0x2f, 0x43, 0x42, 0x11, 0x30, 0xfb, 0x6f, 0xc0, 0x2e, 0x01, 0x42, 0xb0, 0x5f, 0xc1, 0x4a, 0x00, 0x00, + 0x6d, 0x57, 0x00, 0x00, 0x77, 0x8e, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xd3, 0xff, 0xff, 0xff, 0xe5, 0xff, 0xff, + 0xff, 0xee, 0xe1, 0xff, 0xff, 0x7c, 0x13, 0x00, 0x00, 0x46, 0xe6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, + 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, + 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, + 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, 0x2e, 0x00, 0xc1, 0x80, + 0x2e, 0x00, 0xc1 +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/sensors/bmi270/license.txt b/components/sensors/bmi270/license.txt new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/components/sensors/bmi270/license.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/components/sensors/bmi270/src/bmi270.c b/components/sensors/bmi270/src/bmi270.c new file mode 100644 index 000000000..cd2314835 --- /dev/null +++ b/components/sensors/bmi270/src/bmi270.c @@ -0,0 +1,483 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "driver/i2c_master.h" +#include "esp_err.h" +#include "esp_check.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "bmi270.h" +#include "bmi270_priv.h" + +#define I2C_CLK_SPEED 400000 + +/******************************************************************************* +* Function definitions +*******************************************************************************/ + +static esp_err_t soft_reset(const bmi270_handle_t *dev_handle); +static esp_err_t set_adv_pwr_save(const bmi270_handle_t *dev_handle, const bool enable); +static esp_err_t set_config_load(const bmi270_handle_t *dev_handle, const bool enable); +static esp_err_t upload_file(const bmi270_handle_t *dev_handle, const uint8_t *file, const size_t file_size); +static esp_err_t upload_chunk(const bmi270_handle_t *dev_handle, const uint8_t *file, uint16_t index, + uint16_t write_len); +static esp_err_t read_register(const bmi270_handle_t *dev_handle, const uint8_t reg, uint8_t data[], + const size_t data_len); +static esp_err_t write_register(const bmi270_handle_t *dev_handle, const uint8_t reg, const uint8_t data[], + const size_t data_len); + +/******************************************************************************* +* Local variables +*******************************************************************************/ + +static const char *TAG = "BMI270"; + +/******************************************************************************* +* Public functions +*******************************************************************************/ + +esp_err_t bmi270_create(const bmi270_driver_config_t *config, bmi270_handle_t **dev_handle) +{ + ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, TAG, + "Pointer to the configuration structure must not be NULL"); + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the handle pointer must not be NULL"); + + esp_err_t ret = ESP_OK; + uint8_t data = 0; + + bmi270_handle_t *handle = calloc(1, sizeof(bmi270_handle_t)); + ESP_RETURN_ON_FALSE(handle != NULL, ESP_ERR_NO_MEM, TAG, "Not enough memory"); + + if (config->interface == BMI270_USE_I2C) { + const i2c_device_config_t i2c_dev_cfg = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = config->addr, + .scl_speed_hz = I2C_CLK_SPEED, + }; + ESP_GOTO_ON_ERROR(i2c_master_bus_add_device(config->i2c_bus, &i2c_dev_cfg, &handle->i2c_handle), err, TAG, + "Failed to add new I2C device"); + assert(handle->i2c_handle); + } else if (config->interface == BMI270_USE_SPI) { + + // TODO: Reminder when implementing the SPI interface + // Dummy SPI read which sets the device to SPI mode + // ESP_GOTO_ON_ERROR(bmi270_get_chip_id(handle, &data), err, TAG, + // "Failed to perform a dummy read"); + ESP_RETURN_ON_ERROR(bmi270_delete(handle), TAG, "Clean up failure"); + return ESP_ERR_NOT_SUPPORTED; + } + + // Check for the BMI270 device on I2C bus + ESP_GOTO_ON_ERROR(bmi270_get_chip_id(handle, &data), err, TAG, + "Failed to read ID"); + ESP_GOTO_ON_FALSE(data == BMI270_CHIP_ID, ESP_ERR_NOT_FOUND, err, TAG, "Unexpected chip ID"); + + // Start the BMI270 and prepare it to load the configuration file + ESP_GOTO_ON_ERROR(soft_reset(handle), err, TAG, "Failed to request a soft reset"); + ESP_GOTO_ON_ERROR(set_adv_pwr_save(handle, false), err, TAG, "Failed to disable power saving"); + ESP_GOTO_ON_ERROR(set_config_load(handle, false), err, TAG, "Failed to disable configuration loading"); + + ESP_GOTO_ON_ERROR(upload_file(handle, bmi270_config_file, sizeof(bmi270_config_file)), err, TAG, + "Failed to send the configuration file"); + + handle->initialized = true; + *dev_handle = handle; + + ESP_LOGI(TAG, "Successfully initialized the BMI270 sensor driver"); + + return ret; + +err: + ESP_RETURN_ON_ERROR(bmi270_delete(handle), TAG, "Clean up failure"); + return ret; +} + +esp_err_t bmi270_delete(bmi270_handle_t *dev_handle) +{ + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the device handle must not be NULL"); + + esp_err_t ret = ESP_OK; + if (dev_handle->interface == BMI270_USE_I2C && dev_handle->i2c_handle) { + ret = i2c_master_bus_rm_device(dev_handle->i2c_handle); + } else if (dev_handle->interface == BMI270_USE_SPI && dev_handle->spi_handle) { + // SPI interface is currently not supported + return ESP_ERR_NOT_SUPPORTED; + } + free(dev_handle); + return ret; +} + +esp_err_t bmi270_get_chip_id(const bmi270_handle_t *dev_handle, uint8_t *chip_id) +{ + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the device handle must not be NULL"); + ESP_RETURN_ON_FALSE(chip_id != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the chip ID variable must not be NULL"); + return read_register(dev_handle, BMI270_CHIP_ID_REG, chip_id, 1); +} + +esp_err_t bmi270_start(bmi270_handle_t *dev_handle, const bmi270_config_t *config) +{ + + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the device handle must not be NULL"); + ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, TAG, + "Pointer to the configuration structure must not be NULL"); + + uint8_t data; + + if (!dev_handle->initialized) { + return ESP_ERR_INVALID_STATE; + } + + // Enable all internal BMI270 blocks (except for FIFO) + data = BMI270_ACC_EN_MSK | BMI270_GYR_EN_MSK | BMI270_TEMP_EN_MSK; + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_PWR_CTRL_REG, &data, 1), TAG, "Failed to start acquisition"); + + // Configure the accelerometer and gyroscope + ESP_RETURN_ON_ERROR(bmi270_set_acce_odr(dev_handle, config->acce_odr), TAG, ""); + ESP_RETURN_ON_ERROR(bmi270_set_acce_range(dev_handle, config->acce_range), TAG, ""); + ESP_RETURN_ON_ERROR(bmi270_set_gyro_odr(dev_handle, config->gyro_odr), TAG, ""); + ESP_RETURN_ON_ERROR(bmi270_set_gyro_range(dev_handle, config->gyro_range), TAG, ""); + + return ESP_OK; +} + +esp_err_t bmi270_stop(const bmi270_handle_t *dev_handle) +{ + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the device handle must not be NULL"); + + uint8_t data = 0; + + if (!dev_handle->initialized) { + return ESP_ERR_INVALID_STATE; + } + + // Disable all internal BMI270 blocks + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_PWR_CTRL_REG, &data, 1), TAG, "Failed to stop"); + + return ESP_OK; +} + +esp_err_t bmi270_get_acce_data(const bmi270_handle_t *dev_handle, float *x, float *y, float *z) +{ + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the device handle must not be NULL"); + ESP_RETURN_ON_FALSE(x != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to an axi data variable must not be NULL"); + ESP_RETURN_ON_FALSE(y != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to an axi data variable must not be NULL"); + ESP_RETURN_ON_FALSE(z != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to an axi data variable must not be NULL"); + + uint8_t data[BMI270_RAW_DATA_READ_LEN]; + float sensitivity; + + if (!dev_handle->initialized) { + return ESP_ERR_INVALID_STATE; + } + + ESP_RETURN_ON_ERROR(read_register(dev_handle, BMI270_ACC_X_LSB_REG, data, BMI270_RAW_DATA_READ_LEN), TAG, + "Failed to read raw accelerometer data"); + + // Get LSB sensitivity depending on the range + switch (dev_handle->acce_range) { + case BMI270_ACC_RANGE_2_G: + sensitivity = (float)INT16_MAX / 2.0f; + break; + case BMI270_ACC_RANGE_4_G: + sensitivity = (float)INT16_MAX / 4.0f; + break; + case BMI270_ACC_RANGE_8_G: + sensitivity = (float)INT16_MAX / 8.0f; + break; + case BMI270_ACC_RANGE_16_G: + sensitivity = (float)INT16_MAX / 16.0f; + break; + default: + sensitivity = 0; + } + + // Convert and normalize all values + *x = ((int16_t)(data[1] << 8 | data[0])) / sensitivity; + *y = ((int16_t)(data[3] << 8 | data[2])) / sensitivity; + *z = ((int16_t)(data[5] << 8 | data[4])) / sensitivity; + + return ESP_OK; +} + +esp_err_t bmi270_get_gyro_data(const bmi270_handle_t *dev_handle, float *x, float *y, float *z) +{ + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the device handle must not be NULL"); + ESP_RETURN_ON_FALSE(x != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to an axi data variable must not be NULL"); + ESP_RETURN_ON_FALSE(y != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to an axi data variable must not be NULL"); + ESP_RETURN_ON_FALSE(z != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to an axi data variable must not be NULL"); + + uint8_t data[BMI270_RAW_DATA_READ_LEN]; + float sensitivity; + + if (!dev_handle->initialized) { + return ESP_ERR_INVALID_STATE; + } + + ESP_RETURN_ON_ERROR(read_register(dev_handle, BMI270_GYR_X_LSB_REG, data, BMI270_RAW_DATA_READ_LEN), TAG, + "Failed to read raw gyroscope data"); + + // Get LSB sensitivity depending on the range + switch (dev_handle->gyro_range) { + case BMI270_GYR_RANGE_125_DPS: + sensitivity = (float)INT16_MAX / 125.0f; + break; + case BMI270_GYR_RANGE_250_DPS: + sensitivity = (float)INT16_MAX / 250.0f; + break; + case BMI270_GYR_RANGE_500_DPS: + sensitivity = (float)INT16_MAX / 500.0f; + break; + case BMI270_GYR_RANGE_1000_DPS: + sensitivity = (float)INT16_MAX / 1000.0f; + break; + case BMI270_GYR_RANGE_2000_DPS: + sensitivity = (float)INT16_MAX / 2000.0f; + break; + default: + sensitivity = 0; + } + + // Convert and normalize all values + *x = ((int16_t)(data[1] << 8 | data[0])) / sensitivity; + *y = ((int16_t)(data[3] << 8 | data[2])) / sensitivity; + *z = ((int16_t)(data[5] << 8 | data[4])) / sensitivity; + + return ESP_OK; +} + +esp_err_t bmi270_set_acce_odr(const bmi270_handle_t *dev_handle, bmi270_acce_odr_e odr) +{ + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the device handle must not be NULL"); + + uint8_t data; + + if (!dev_handle->initialized) { + return ESP_ERR_INVALID_STATE; + } + + ESP_RETURN_ON_ERROR(read_register(dev_handle, BMI270_ACC_CONF_REG, &data, 1), TAG, + "Failed to read accelerometer configuration"); + data = (data & BMI270_ODR_MSK) | odr; + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_ACC_CONF_REG, &data, 1), TAG, + "Failed to configure accelerometer ODR"); + + return ESP_OK; +} + +esp_err_t bmi270_set_gyro_odr(const bmi270_handle_t *dev_handle, bmi270_gyro_odr_e odr) +{ + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the device handle must not be NULL"); + + uint8_t data; + + if (!dev_handle->initialized) { + return ESP_ERR_INVALID_STATE; + } + + ESP_RETURN_ON_ERROR(read_register(dev_handle, BMI270_GYR_CONF_REG, &data, 1), TAG, + "Failed to read gyroscope configuration"); + data = (data & BMI270_ODR_MSK) | odr; + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_GYR_CONF_REG, &data, 1), TAG, + "Failed to configure gyroscope ODR"); + + return ESP_OK; +} + +esp_err_t bmi270_set_acce_range(bmi270_handle_t *dev_handle, bmi270_acce_range_e range) +{ + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the device handle must not be NULL"); + + uint8_t data; + + if (!dev_handle->initialized) { + return ESP_ERR_INVALID_STATE; + } + + data = range; + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_ACC_RANGE_REG, &data, 1), TAG, + "Failed to configure accelerometer range"); + dev_handle->acce_range = range; + + return ESP_OK; +} + +esp_err_t bmi270_set_gyro_range(bmi270_handle_t *dev_handle, bmi270_gyro_range_e range) +{ + ESP_RETURN_ON_FALSE(dev_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to the device handle must not be NULL"); + + uint8_t data; + + if (!dev_handle->initialized) { + return ESP_ERR_INVALID_STATE; + } + + ESP_RETURN_ON_ERROR(read_register(dev_handle, BMI270_GYR_RANGE_REG, &data, 1), TAG, + "Failed to read gyroscope range register"); + data = (data & BMI270_GYR_RANGE_MSK) | range; + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_GYR_RANGE_REG, &data, 1), TAG, + "Failed to configure gyroscope range"); + dev_handle->gyro_range = range; + + return ESP_OK; +} + +/******************************************************************************* +* Private functions +*******************************************************************************/ + +static esp_err_t soft_reset(const bmi270_handle_t *dev_handle) +{ + assert(dev_handle != NULL); + + uint8_t data = BMI270_CMD_SOFT_RESET; + + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_CMD_REG, &data, 1), TAG, + "Failed to soft reset the device"); + vTaskDelay(pdMS_TO_TICKS(BMI270_SOFT_RESET_TIME_MS)); + + return ESP_OK; +} + +static esp_err_t set_adv_pwr_save(const bmi270_handle_t *dev_handle, const bool enable) +{ + assert(dev_handle != NULL); + + uint8_t data; + + ESP_RETURN_ON_ERROR(read_register(dev_handle, BMI270_PWR_CONF_REG, &data, 1), TAG, + "Failed to read the power config"); + data = enable ? data | BIT0 : data & ~BIT0; + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_PWR_CONF_REG, &data, 1), TAG, + "Failed to set the power config"); + vTaskDelay(pdMS_TO_TICKS(BMI270_POWER_ON_TIME_MS)); + + return ESP_OK; +} + +static esp_err_t set_config_load(const bmi270_handle_t *dev_handle, const bool enable) +{ + assert(dev_handle != NULL); + + uint8_t data; + + ESP_RETURN_ON_ERROR(read_register(dev_handle, BMI270_INIT_CTRL_REG, &data, 1), TAG, + "Failed to read the initialization control register"); + data = enable ? data | BIT0 : data & ~BIT0; + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_INIT_CTRL_REG, &data, 1), TAG, + "Failed to set the initialization control register"); + return ESP_OK; +} + +static esp_err_t upload_file(const bmi270_handle_t *dev_handle, const uint8_t *file, const size_t file_size) +{ + assert(dev_handle != NULL); + assert(file != NULL); + + uint8_t data; + uint16_t remainder = 0; + uint16_t chunk_size = 0; + uint32_t file_index = 0; + uint8_t status_checks; + + remainder = file_size % BMI270_FILE_WRITE_LEN; + + // Pick write approach depending on it's divisibility by the write length + if (!remainder) { + for (file_index = 0; file_index < file_size; file_index += BMI270_FILE_WRITE_LEN) { + ESP_RETURN_ON_ERROR(upload_chunk(dev_handle, &file[file_index], file_index, BMI270_FILE_WRITE_LEN), TAG, + "Failed to upload a file chunk"); + } + } else { + chunk_size = file_size - remainder; + // Bulk write parts of the file + for (file_index = 0; file_index < chunk_size; file_index += BMI270_FILE_WRITE_LEN) { + ESP_RETURN_ON_ERROR(upload_chunk(dev_handle, &file[file_index], file_index, BMI270_FILE_WRITE_LEN), TAG, + "Failed to upload a file chunk"); + } + + // Switch to half-word granular write + for (file_index = chunk_size; file_index < file_size; file_index += 2) { + ESP_RETURN_ON_ERROR(upload_chunk(dev_handle, &file[file_index], file_index, BMI270_FILE_WRITE_LEN), TAG, + "Failed to upload a file chunk"); + } + } + + // Re-enable loading of the configuration file + ESP_RETURN_ON_ERROR(set_config_load(dev_handle, true), TAG, "Failed to enable configuration loading"); + + // Wait for the BMI270 to initialize + status_checks = BMI270_STATUS_CHECK_CNT; + do { + ESP_RETURN_ON_ERROR(read_register(dev_handle, BMI270_INTERNAL_STATUS_REG, &data, 1), TAG, + "Failed to read internal status"); + + vTaskDelay(pdMS_TO_TICKS(BMI270_STATUS_CHECK_PERIOD_MS)); + status_checks--; + } while ((data != BMI270_STATUS_INIT_OK) & (status_checks > 0)); + + ESP_RETURN_ON_FALSE(status_checks > 0, ESP_ERR_TIMEOUT, TAG, "Could not initialize the device in time"); + + return ESP_OK; +} + +static esp_err_t upload_chunk(const bmi270_handle_t *dev_handle, const uint8_t *file, uint16_t index, + uint16_t write_len) +{ + assert(dev_handle != NULL); + assert(file != NULL); + + uint8_t write_addr[2]; + + write_addr[0] = (uint8_t)((index / 2) & 0x0F); + write_addr[1] = (uint8_t)((index / 2) >> 4); + + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_INIT_ADDR_0_REG, write_addr, 2), TAG, + "Failed to set address for the config file"); + + ESP_RETURN_ON_ERROR(write_register(dev_handle, BMI270_INIT_DATA_REG, file, BMI270_FILE_WRITE_LEN), TAG, + "Failed to send config file data"); + + return ESP_OK; +} + +static esp_err_t read_register(const bmi270_handle_t *dev_handle, const uint8_t reg, uint8_t data[], + const size_t data_len) +{ + assert(dev_handle != NULL); + + if (dev_handle->interface == BMI270_USE_I2C) { + return i2c_master_transmit_receive(dev_handle->i2c_handle, ®, 1, data, data_len, -1); + } else if (dev_handle->interface == BMI270_USE_SPI) { + // SPI interface is currently not supported + return ESP_ERR_NOT_SUPPORTED; + } else { + return ESP_FAIL; + } +} + +static esp_err_t write_register(const bmi270_handle_t *dev_handle, const uint8_t reg, const uint8_t data[], + const size_t data_len) +{ + assert(dev_handle != NULL); + + size_t packet_len = data_len + 1; + uint8_t packet[BMI270_FILE_WRITE_LEN + 1]; + + ESP_RETURN_ON_FALSE(data_len <= BMI270_FILE_WRITE_LEN, ESP_ERR_INVALID_ARG, TAG, "Exceeded expected data length"); + + packet[0] = reg; + memcpy(&packet[1], data, data_len); + if (dev_handle->interface == BMI270_USE_I2C) { + return i2c_master_transmit(dev_handle->i2c_handle, packet, packet_len, -1); + } else if (dev_handle->interface == BMI270_USE_SPI) { + // SPI interface is currently not supported + return ESP_ERR_NOT_SUPPORTED; + } else { + return ESP_FAIL; + } +} diff --git a/components/sensors/bmi270/src/bmi270_sensor_hub.c b/components/sensors/bmi270/src/bmi270_sensor_hub.c new file mode 100644 index 000000000..3098117c3 --- /dev/null +++ b/components/sensors/bmi270/src/bmi270_sensor_hub.c @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_check.h" +#include "bmi270.h" +#include "iot_sensor_hub.h" + +/******************************************************************************* +* Function definitions +*******************************************************************************/ + +esp_err_t bmi270_impl_init(bus_handle_t bus_handle, uint8_t addr); +esp_err_t bmi270_impl_deinit(void); +esp_err_t bmi270_impl_test (void); +esp_err_t bmi270_impl_acquire_acce (float *acce_x, float *acce_y, float *acce_z); +esp_err_t bmi270_impl_acquire_gyro(float *gyro_x, float *gyro_y, float *gyro_z); + +/******************************************************************************* +* Local variables +*******************************************************************************/ + +static const char *TAG = "BMI270 sensor hub"; +static bmi270_handle_t *sensor_hub_bmi270_handle; + + +/******************************************************************************* +* Public functions +*******************************************************************************/ + +esp_err_t bmi270_impl_init(bus_handle_t bus_handle, uint8_t addr) +{ + esp_err_t ret = ESP_OK; + const bmi270_driver_config_t driver_config = { + .addr = addr, + .interface = BMI270_USE_I2C, + .i2c_bus = bus_handle + }; + ESP_RETURN_ON_ERROR(bmi270_create(&driver_config, &sensor_hub_bmi270_handle), TAG, + "Failed to initialize"); + + // Set a reasonable default configuration + const bmi270_config_t config = { + .acce_odr = BMI270_ACC_ODR_100_HZ, + .acce_range = BMI270_ACC_RANGE_4_G, + .gyro_odr = BMI270_GYR_ODR_100_HZ, + .gyro_range = BMI270_GYR_RANGE_1000_DPS + }; + ret = bmi270_start(sensor_hub_bmi270_handle, &config); + if (ret != ESP_OK) { + ESP_RETURN_ON_ERROR(bmi270_delete(sensor_hub_bmi270_handle), TAG, "Failed to clean up a handle"); + return ret; + } + return ESP_OK; +} + +esp_err_t bmi270_impl_deinit(void) +{ + bmi270_stop(sensor_hub_bmi270_handle); + bmi270_delete(sensor_hub_bmi270_handle); + return ESP_OK; +} + +esp_err_t bmi270_impl_test (void) +{ + uint8_t chip_id; + + ESP_RETURN_ON_ERROR(bmi270_get_chip_id(sensor_hub_bmi270_handle, &chip_id), TAG, + "Failed to read chip id"); + if (chip_id == BMI270_CHIP_ID) { + return ESP_OK; + } else { + return ESP_FAIL; + } +} + +esp_err_t bmi270_impl_acquire_acce (float *acce_x, float *acce_y, float *acce_z) +{ + ESP_RETURN_ON_ERROR(bmi270_get_acce_data(sensor_hub_bmi270_handle, acce_x, acce_y, acce_z), TAG, + "Failed to read accelerometer data"); + return ESP_OK; +} + +esp_err_t bmi270_impl_acquire_gyro(float *gyro_x, float *gyro_y, float *gyro_z) +{ + ESP_RETURN_ON_ERROR(bmi270_get_gyro_data(sensor_hub_bmi270_handle, gyro_x, gyro_y, gyro_z), TAG, + "Failed to read gyroscope data"); + return ESP_OK; +} + + +static imu_impl_t bmi270_impl = { + .init = bmi270_impl_init, + .deinit = bmi270_impl_deinit, + .test = bmi270_impl_test, + .acquire_acce = bmi270_impl_acquire_acce, + .acquire_gyro = bmi270_impl_acquire_gyro, +}; + +SENSOR_HUB_DETECT_FN(IMU_ID, sensor_hub_bmi270, &bmi270_impl); diff --git a/components/sensors/bmi270/test_app/CMakeLists.txt b/components/sensors/bmi270/test_app/CMakeLists.txt new file mode 100644 index 000000000..791050b02 --- /dev/null +++ b/components/sensors/bmi270/test_app/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) +set(COMPONENTS main) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_app_bmi270) diff --git a/components/sensors/bmi270/test_app/main/CMakeLists.txt b/components/sensors/bmi270/test_app/main/CMakeLists.txt new file mode 100644 index 000000000..a52ee6029 --- /dev/null +++ b/components/sensors/bmi270/test_app/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "test_app_bmi270.c" + REQUIRES unity + ) diff --git a/components/sensors/bmi270/test_app/main/idf_component.yml b/components/sensors/bmi270/test_app/main/idf_component.yml new file mode 100644 index 000000000..05f7eb5a7 --- /dev/null +++ b/components/sensors/bmi270/test_app/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=5.2" + bmi270: + version: "*" + override_path: "../../" diff --git a/components/sensors/bmi270/test_app/main/test_app_bmi270.c b/components/sensors/bmi270/test_app/main/test_app_bmi270.c new file mode 100644 index 000000000..13167b502 --- /dev/null +++ b/components/sensors/bmi270/test_app/main/test_app_bmi270.c @@ -0,0 +1,144 @@ +/* + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "driver/i2c_master.h" +#include "bmi270.h" +#include "esp_err.h" +#include "esp_system.h" +#include "esp_log.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#define I2C_MASTER_SDA_IO GPIO_NUM_31 +#define I2C_MASTER_SCL_IO GPIO_NUM_32 +#define I2C_MASTER_NUM I2C_NUM_0 + +#define TEST_MEMORY_LEAK_THRESHOLD (108) + +static const char *TAG = "BMI270 test"; + +static i2c_master_bus_handle_t i2c_handle = NULL; +static bmi270_handle_t *bmi270 = NULL; + +static void bmi270_sensor_init() +{ + esp_err_t ret; + + const i2c_master_bus_config_t bus_config = { + .i2c_port = I2C_MASTER_NUM, + .sda_io_num = I2C_MASTER_SDA_IO, + .scl_io_num = I2C_MASTER_SCL_IO, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + + ret = i2c_new_master_bus(&bus_config, &i2c_handle); + TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "Failed to add new I2C master"); + + const bmi270_driver_config_t bmi270_driver = { + .addr = BMI270_I2C_ADDRESS_L, + .interface = BMI270_USE_I2C, + .i2c_bus = i2c_handle + }; + ret = bmi270_create(&bmi270_driver, &bmi270); + TEST_ASSERT_EQUAL(ESP_OK, ret); + TEST_ASSERT_NOT_NULL_MESSAGE(bmi270, "Initialized BMI270 handle is NULL"); +} + +TEST_CASE("BMI270 memory leak test", "[bmi270]") +{ + unity_utils_set_leak_level(TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_record_free_mem(); + + esp_err_t ret; + float x, y, z; + + bmi270_sensor_init(); + + const bmi270_config_t bmi270_config = { + .acce_odr = BMI270_ACC_ODR_100_HZ, + .acce_range = BMI270_ACC_RANGE_4_G, + .gyro_odr = BMI270_GYR_ODR_100_HZ, + .gyro_range = BMI270_GYR_RANGE_1000_DPS + }; + + ret = bmi270_start(bmi270, &bmi270_config); + TEST_ASSERT_EQUAL(ESP_OK, ret); + + ret = bmi270_get_acce_data(bmi270, &x, &y, &z); + TEST_ASSERT_EQUAL(ESP_OK, ret); + ret = bmi270_get_gyro_data(bmi270, &x, &y, &z); + TEST_ASSERT_EQUAL(ESP_OK, ret); + + ret = bmi270_stop(bmi270); + TEST_ASSERT_EQUAL(ESP_OK, ret); + + ret = bmi270_delete(bmi270); + TEST_ASSERT_EQUAL(ESP_OK, ret); + + ret = i2c_del_master_bus(i2c_handle); + TEST_ASSERT_EQUAL(ESP_OK, ret); + + unity_utils_evaluate_leaks(); +} + +TEST_CASE("BMI270 unintialized handle", "[bmi270]") +{ + esp_err_t ret; + uint8_t chip_id; + bmi270_handle_t bmi270_handle; + bmi270_handle.initialized = false; + + const bmi270_config_t bmi270_config = { + .acce_odr = BMI270_ACC_ODR_100_HZ, + .acce_range = BMI270_ACC_RANGE_4_G, + .gyro_odr = BMI270_GYR_ODR_100_HZ, + .gyro_range = BMI270_GYR_RANGE_1000_DPS + }; + + ret = bmi270_start(&bmi270_handle, &bmi270_config); + TEST_ASSERT_NOT_EQUAL(ESP_OK, ret); + + ret = bmi270_stop(&bmi270_handle); + TEST_ASSERT_NOT_EQUAL(ESP_OK, ret); + + ret = bmi270_get_chip_id(&bmi270_handle, &chip_id); + TEST_ASSERT_NOT_EQUAL(ESP_OK, ret); + + ret = bmi270_set_acce_odr(&bmi270_handle, BMI270_ACC_ODR_100_HZ); + TEST_ASSERT_NOT_EQUAL(ESP_OK, ret); + + ret = bmi270_set_acce_range(&bmi270_handle, BMI270_ACC_RANGE_4_G); + TEST_ASSERT_NOT_EQUAL(ESP_OK, ret); + + ret = bmi270_set_gyro_odr(&bmi270_handle, BMI270_GYR_ODR_100_HZ); + TEST_ASSERT_NOT_EQUAL(ESP_OK, ret); + + ret = bmi270_set_gyro_range(&bmi270_handle, BMI270_GYR_RANGE_1000_DPS); + TEST_ASSERT_NOT_EQUAL(ESP_OK, ret); +} + +void setUp(void) +{ + +} + +void tearDown(void) +{ + +} + +void app_main(void) +{ + ESP_LOGI(TAG, "Running tests with [bmi270] tag"); + UNITY_BEGIN(); + unity_run_tests_by_tag("[bmi270]", false); + UNITY_END(); +} diff --git a/components/sensors/bmi270/test_app/sdkconfig.defaults b/components/sensors/bmi270/test_app/sdkconfig.defaults new file mode 100644 index 000000000..464ec855e --- /dev/null +++ b/components/sensors/bmi270/test_app/sdkconfig.defaults @@ -0,0 +1,13 @@ +CONFIG_IDF_TARGET="esp32p4" +CONFIG_COMPILER_OPTIMIZATION_PERF=y + +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 + +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE=n + +# SPIRAM for ESP32-P4 +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/examples/display_sensors/CMakeLists.txt b/examples/display_sensors/CMakeLists.txt index 3f82912cf..0712a1d7c 100644 --- a/examples/display_sensors/CMakeLists.txt +++ b/examples/display_sensors/CMakeLists.txt @@ -6,4 +6,4 @@ cmake_minimum_required(VERSION 3.5) set(COMPONENTS main) # "Trim" the build. Include the minimal set of components; main and anything it depends on. include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(bsp-azure-sensors-example) +project(bsp-sensors-example) diff --git a/examples/display_sensors/README.md b/examples/display_sensors/README.md index 97ba15f22..0cda239b4 100644 --- a/examples/display_sensors/README.md +++ b/examples/display_sensors/README.md @@ -1,20 +1,19 @@ # BSP: Sensors Example -This is an example usage of Azure-IoT-Kit board. +## Overview -## Sensors -All sensors are sampled and results are shown on OLED display. -User can switch between pages by pressing KEY_IO0 button. + + +
-### Magnetometer calibration -At the start of the program, magnetometer calibration is performed for 10 seconds. -Turn the board in every axis during this time to achieve best magnetometer results. +This example demonstrates the usage of sensors available in BSP packages. +All sensors are sampled based on the configured sampling period. +Measurement results are printed to the terminal and shown on the display, if available. -## LED and buzzer -On every press of KEY_IO0 button, the buzzer beeps and AZURE LED blinks. + + +
-## uSD card -If a uSD card is successfully mounted a hello.txt file is created and WIFI LED is turned on. ## Build and Flash @@ -29,6 +28,11 @@ Make sure the correct board name is set in the `main/idf_component.yml` file und You can also try this example using ESP Launchpad: - + Try it with ESP Launchpad + + + + + diff --git a/examples/display_sensors/doc/pic.webp b/examples/display_sensors/doc/pic.webp new file mode 100644 index 000000000..6b5b16a6d Binary files /dev/null and b/examples/display_sensors/doc/pic.webp differ diff --git a/examples/display_sensors/main/idf_component.yml b/examples/display_sensors/main/idf_component.yml index e276d02e8..bac682611 100644 --- a/examples/display_sensors/main/idf_component.yml +++ b/examples/display_sensors/main/idf_component.yml @@ -1,5 +1,4 @@ -description: BSP ESP32-Azure-IoT-Kit sensor example - +description: BSP sensor hub example dependencies: bsp_selector: version: '*' diff --git a/examples/display_sensors/main/main.c b/examples/display_sensors/main/main.c index 678198720..5f5aa8f74 100644 --- a/examples/display_sensors/main/main.c +++ b/examples/display_sensors/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ @@ -7,299 +7,242 @@ /** * @file * @brief BSP Sensors Example - * @details Display sensor data on a monochrome screen (LVGL) - * @example https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_sensors- + * @details Acquire sensor data using the sensor hub component + * @example https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=display_sensors */ #include -#include "hts221.h" -#include "mpu6050.h" -#include "fbm320.h" -#include "mag3110.h" -#include "bh1750.h" #include "bsp/esp-bsp.h" #include "esp_log.h" -#include "esp_timer.h" -#include "sdmmc_cmd.h" // for sdmmc_card_print_info -#include "esp_idf_version.h" // for backward compatibility of esp-timer -// Enable SD card test -#define EXAMPLE_TEST_SD_CARD 0 +#define IMU_SAMPLING_PERIOD 300 +#define HUMITURE_SAMPLING_PERIOD 500 static const char *TAG = "example"; -// Display -static lv_disp_t *disp = NULL; -static lv_obj_t *main_screen = NULL; -static lv_obj_t *main_label = NULL; - -static bh1750_handle_t bh1750_dev = NULL; -static hts221_handle_t hts221_dev = NULL; -static mpu6050_handle_t mpu6050_dev = NULL; -static fbm320_handle_t fbm320_dev = NULL; -static mag3110_handle_t mag3110_dev = NULL; - -static QueueHandle_t q_page_num; -static uint8_t g_page_num = 0; - -static mpu6050_acce_value_t acce; -static mpu6050_gyro_value_t gyro; -static complimentary_angle_t complimentary_angle; - -static void display_show_signs(void) -{ - bsp_display_lock(0); - lv_obj_clean(main_screen); - lv_obj_t *label = lv_label_create(main_screen); - lv_label_set_text_static(label, - LV_SYMBOL_WIFI" "LV_SYMBOL_USB" "LV_SYMBOL_BELL" "LV_SYMBOL_GPS" "LV_SYMBOL_BATTERY_EMPTY); - lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_set_width(label, lv_display_get_physical_horizontal_resolution(disp)); - lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 0); - bsp_display_unlock(); -} - -static void bh1750_init() -{ - bh1750_dev = bh1750_create(BSP_I2C_NUM, BH1750_I2C_ADDRESS_DEFAULT); - bh1750_power_on(bh1750_dev); - bh1750_set_measure_mode(bh1750_dev, BH1750_CONTINUE_4LX_RES); -} +#if BSP_CAPS_IMU +static sensor_handle_t imu_sensor_handle = NULL; +#endif +#if BSP_CAPS_HUMITURE +static sensor_handle_t humiture_sensor_handle = NULL; +#endif -static void app_hts221_init() -{ - hts221_dev = hts221_create(BSP_I2C_NUM); +#if BSP_CAPS_DISPLAY +static lv_obj_t *acc_x_bar, *acc_y_bar, * acc_z_bar; +static lv_obj_t *gyr_x_bar, *gyr_y_bar, * gyr_z_bar; +static lv_style_t bar_style; - const hts221_config_t hts221_config = { - .avg_h = HTS221_AVGH_32, .avg_t = HTS221_AVGT_16, .odr = HTS221_ODR_1HZ, .bdu_status = true - }; - hts221_init(hts221_dev, &hts221_config); -} - -static void mpu6050_init() -{ - mpu6050_dev = mpu6050_create(BSP_I2C_NUM, MPU6050_I2C_ADDRESS); - mpu6050_config(mpu6050_dev, ACCE_FS_4G, GYRO_FS_500DPS); - mpu6050_wake_up(mpu6050_dev); -} +static lv_obj_t *chart = NULL; +static lv_chart_series_t *temp_series = NULL; +static lv_chart_series_t *humid_series = NULL; -static void app_fbm320_init() -{ - fbm320_dev = fbm320_create(BSP_I2C_NUM, FBM320_I2C_ADDRESS_1); - fbm320_init(fbm320_dev); -} +static void chart_add_value(lv_chart_series_t *series, const int32_t value); +#endif -static void app_mag3110_init() -{ - // MAG3110 is started after its calibration in app_main - mag3110_dev = mag3110_create(BSP_I2C_NUM); +static void sensor_event_handler(void *handler_args, esp_event_base_t base, int32_t id, void *event_data) +{ + sensor_data_t *sensor_data = (sensor_data_t *)event_data; + + switch (id) { + case SENSOR_STARTED: + ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x STARTED", + sensor_data->timestamp, + sensor_data->sensor_name, + sensor_data->sensor_addr); + break; + case SENSOR_STOPED: + ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x STOPPED", + sensor_data->timestamp, + sensor_data->sensor_name, + sensor_data->sensor_addr); + break; + case SENSOR_HUMI_DATA_READY: + ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x HUMI_DATA_READY - " + "humidity=%.2f", + sensor_data->timestamp, + sensor_data->sensor_name, + sensor_data->sensor_addr, + sensor_data->humidity); +#if BSP_CAPS_DISPLAY & BSP_CAPS_HUMITURE + chart_add_value(humid_series, (int32_t)(sensor_data->humidity * 10.0f)); +#endif + break; + case SENSOR_TEMP_DATA_READY: + ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x TEMP_DATA_READY - " + "temperature=%.2f", + sensor_data->timestamp, + sensor_data->sensor_name, + sensor_data->sensor_addr, + sensor_data->temperature); +#if BSP_CAPS_DISPLAY & BSP_CAPS_HUMITURE + chart_add_value(temp_series, (int32_t)(sensor_data->temperature * 10.0f)); +#endif + break; + case SENSOR_ACCE_DATA_READY: + ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x ACCE_DATA_READY - " + "acce_x=%.2f, acce_y=%.2f, acce_z=%.2f", + sensor_data->timestamp, + sensor_data->sensor_name, + sensor_data->sensor_addr, + sensor_data->acce.x, sensor_data->acce.y, sensor_data->acce.z); +#if BSP_CAPS_DISPLAY & BSP_CAPS_IMU + lv_bar_set_value(acc_x_bar, (int32_t)(sensor_data->acce.x * 10.0f), LV_ANIM_OFF); + lv_bar_set_value(acc_y_bar, (int32_t)(sensor_data->acce.y * 10.0f), LV_ANIM_OFF); + lv_bar_set_value(acc_z_bar, (int32_t)(sensor_data->acce.z * 10.0f), LV_ANIM_OFF); +#endif + break; + case SENSOR_GYRO_DATA_READY: + ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x GYRO_DATA_READY - " + "gyro_x=%.2f, gyro_y=%.2f, gyro_z=%.2f", + sensor_data->timestamp, + sensor_data->sensor_name, + sensor_data->sensor_addr, + sensor_data->gyro.x, sensor_data->gyro.y, sensor_data->gyro.z); +#if BSP_CAPS_DISPLAY & BSP_CAPS_IMU + lv_bar_set_value(gyr_x_bar, (int32_t)(sensor_data->gyro.x * 10.0f), LV_ANIM_OFF); + lv_bar_set_value(gyr_y_bar, (int32_t)(sensor_data->gyro.y * 10.0f), LV_ANIM_OFF); + lv_bar_set_value(gyr_z_bar, (int32_t)(sensor_data->gyro.z * 10.0f), LV_ANIM_OFF); +#endif + break; + default: + ESP_LOGI(TAG, "Timestamp = %" PRIi64 " - event id = %" PRIi32, sensor_data->timestamp, id); + break; + } } -static void display_show_env_data(void) +#if BSP_CAPS_DISPLAY & BSP_CAPS_HUMITURE +static void chart_add_value(lv_chart_series_t *series, const int32_t value) { - int16_t temp; - int16_t humi; - float lumi; - - hts221_get_temperature(hts221_dev, &temp); - hts221_get_humidity(hts221_dev, &humi); - bh1750_get_data(bh1750_dev, &lumi); - ESP_LOGI(TAG, "temperature: %.1f, humidity: %.1f, luminance: %.1f", (float)temp / 10, (float)humi / 10, lumi); - - bsp_display_lock(0); - lv_label_set_text_fmt(main_label, "Temp: %.1f\nHumi: %.1f\nLumi: %.1f", (float)temp / 10, (float)humi / 10, lumi); - lv_obj_set_style_text_align(main_label, LV_TEXT_ALIGN_LEFT, 0); - bsp_display_unlock(); + assert(chart != NULL); + assert(series != NULL); + lv_chart_set_next_value(chart, series, value); + uint32_t p = lv_chart_get_point_count(chart); + uint32_t s = lv_chart_get_x_start_point(chart, series); + int32_t *a = lv_chart_get_series_y_array(chart, series); + a[(s + 1) % p] = LV_CHART_POINT_NONE; + a[(s + 2) % p] = LV_CHART_POINT_NONE; + lv_chart_refresh(chart); } -static void display_show_acce_data(void) +static void chart_init(lv_obj_t *scr) { - ESP_LOGI(TAG, "acce_x:%.2f, acce_y:%.2f, acce_z:%.2f", acce.acce_x, acce.acce_y, acce.acce_z); + chart = lv_chart_create(scr); + lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_CIRCULAR); + lv_obj_set_style_size(chart, 0, 0, LV_PART_INDICATOR); + lv_obj_set_size(chart, BSP_LCD_H_RES, (BSP_LCD_V_RES * 2) / 5); + lv_obj_align(chart, LV_ALIGN_TOP_MID, 0, BSP_LCD_V_RES / 10); - bsp_display_lock(0); - lv_label_set_text_fmt(main_label, "Acce_x: %.2f\nAcce_y: %.2f\nAcce_z: %.2f", acce.acce_x, acce.acce_y, acce.acce_z); - lv_obj_set_style_text_align(main_label, LV_TEXT_ALIGN_LEFT, 0); - bsp_display_unlock(); -} + lv_chart_set_point_count(chart, 250); + lv_chart_set_axis_range(chart, LV_CHART_AXIS_PRIMARY_Y, 100, 400); + lv_chart_set_axis_range(chart, LV_CHART_AXIS_SECONDARY_Y, 0, 1000); + lv_chart_set_div_line_count(chart, 5, 0); -static void display_show_gyro_data(void) -{ - ESP_LOGI(TAG, "gyro_x:%.2f, gyro_y:%.2f, gyro_z:%.2f", gyro.gyro_x, gyro.gyro_y, gyro.gyro_z); + temp_series = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y); + humid_series = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_BLUE), LV_CHART_AXIS_SECONDARY_Y); - bsp_display_lock(0); - lv_label_set_text_fmt(main_label, "Gyro_x: %.2f\nGyro_y: %.2f\nGyro_z: %.2f", gyro.gyro_x, gyro.gyro_y, gyro.gyro_z); - lv_obj_set_style_text_align(main_label, LV_TEXT_ALIGN_LEFT, 0); - bsp_display_unlock(); + lv_obj_t *chart_label = lv_label_create(scr); + lv_label_set_recolor(chart_label, true); + lv_label_set_text_static(chart_label, "#ff0000 Temperature#"); + lv_obj_align(chart_label, LV_ALIGN_TOP_LEFT, BSP_LCD_H_RES / 8, BSP_LCD_V_RES / 32); + chart_label = lv_label_create(scr); + lv_label_set_recolor(chart_label, true); + lv_label_set_text_static(chart_label, "#0000ff Humidity#"); + lv_obj_align(chart_label, LV_ALIGN_TOP_RIGHT, -BSP_LCD_H_RES / 8, BSP_LCD_V_RES / 32); } +#endif -static void display_show_complimentary_angle(void) +#if BSP_CAPS_DISPLAY & BSP_CAPS_IMU +static lv_obj_t *imu_bars_new(lv_obj_t *scr, const lv_style_t *style, const uint32_t range) { - ESP_LOGI(TAG, "roll:%.2f, pitch:%.2f", complimentary_angle.roll, complimentary_angle.pitch); - - bsp_display_lock(0); - lv_label_set_text_fmt(main_label, "Roll: %.2f\nPitch: %.2f", complimentary_angle.roll, complimentary_angle.pitch); - lv_obj_set_style_text_align(main_label, LV_TEXT_ALIGN_LEFT, 0); - bsp_display_unlock(); + lv_obj_t *bar = lv_bar_create(scr); + lv_bar_set_range(bar, -range, range); + lv_bar_set_mode(bar, LV_BAR_MODE_SYMMETRICAL); + lv_obj_add_style(bar, style, 0); + return bar; } -static void display_show_barometer_data(void) +static void imu_create_axis_label(lv_obj_t *bar, const char *text) { - int32_t real_p, real_t; - float pressure, temperature; - - if (ESP_OK == fbm320_get_data(fbm320_dev, FBM320_MEAS_PRESS_OSR_1024, &real_t, &real_p)) { - pressure = (float)real_p / 1000; - temperature = (float)real_t / 100; - ESP_LOGI(TAG, "pressure: %.1f, temperature: %.1f", pressure, temperature); - - bsp_display_lock(0); - lv_label_set_text_fmt(main_label, "Press: %.1f\nTemp: %.1f", pressure, temperature); - lv_obj_set_style_text_align(main_label, LV_TEXT_ALIGN_LEFT, 0); - bsp_display_unlock(); - } + lv_obj_t *bar_label; + bar_label = lv_label_create(lv_obj_get_screen(bar)); + lv_label_set_text_static(bar_label, text); + lv_obj_align_to(bar_label, bar, LV_ALIGN_OUT_LEFT_MID, -BSP_LCD_H_RES / 32, 0); } -static void display_show_magmeter_data(void) +static void imu_bars_init(lv_obj_t *scr) { - mag3110_result_t mag_induction; + lv_obj_t *static_label; - mag3110_get_magnetic_induction(mag3110_dev, &mag_induction); - ESP_LOGI(TAG, "mag_x:%i, mag_y:%i, mag_z:%i", mag_induction.x, mag_induction.y, mag_induction.z); + lv_style_init(&bar_style); + lv_style_set_width(&bar_style, BSP_LCD_H_RES / 3); + lv_style_set_height(&bar_style, BSP_LCD_V_RES / 16); - bsp_display_lock(0); - lv_label_set_text_fmt(main_label, "Mag_x: %5i\nMag_y: %5i\nMag_z: %5i", mag_induction.x, mag_induction.y, - mag_induction.z); - lv_obj_set_style_text_align(main_label, LV_TEXT_ALIGN_LEFT, 0); - bsp_display_unlock(); -} + acc_x_bar = imu_bars_new(scr, &bar_style, 15); + acc_y_bar = imu_bars_new(scr, &bar_style, 15); + acc_z_bar = imu_bars_new(scr, &bar_style, 15); + lv_obj_align(acc_z_bar, LV_ALIGN_BOTTOM_LEFT, BSP_LCD_H_RES / 8, -BSP_LCD_V_RES / 16); + lv_obj_align_to(acc_y_bar, acc_z_bar, LV_ALIGN_OUT_TOP_MID, 0, -BSP_LCD_V_RES / 16); + lv_obj_align_to(acc_x_bar, acc_y_bar, LV_ALIGN_OUT_TOP_MID, 0, -BSP_LCD_V_RES / 16); -static void display_show_task(void *pvParameters) -{ - uint8_t page_num = 0; - while (1) { - bsp_led_set(BSP_LED_AZURE, false); - bsp_buzzer_set(false); + static_label = lv_label_create(scr); + lv_label_set_text_static(static_label, "Accelerometer"); + lv_obj_align_to(static_label, acc_x_bar, LV_ALIGN_OUT_TOP_MID, 0, -BSP_LCD_V_RES / 32); + imu_create_axis_label(acc_x_bar, "X"); + imu_create_axis_label(acc_y_bar, "Y"); + imu_create_axis_label(acc_z_bar, "Z"); - if (xQueueReceive(q_page_num, &page_num, 1000 / portTICK_PERIOD_MS) == pdTRUE) { - // Turn on LED and Buzzer when button is pressed - bsp_led_set(BSP_LED_AZURE, true); - bsp_buzzer_set(true); - } + gyr_x_bar = imu_bars_new(scr, &bar_style, 5000); + gyr_y_bar = imu_bars_new(scr, &bar_style, 5000); + gyr_z_bar = imu_bars_new(scr, &bar_style, 5000); + lv_obj_align(gyr_z_bar, LV_ALIGN_BOTTOM_RIGHT, -BSP_LCD_H_RES / 16, -BSP_LCD_V_RES / 16); + lv_obj_align_to(gyr_y_bar, gyr_z_bar, LV_ALIGN_OUT_TOP_MID, 0, -BSP_LCD_V_RES / 16); + lv_obj_align_to(gyr_x_bar, gyr_y_bar, LV_ALIGN_OUT_TOP_MID, 0, -BSP_LCD_V_RES / 16); - switch (page_num) { - case 0: - display_show_env_data(); - break; - case 1: - display_show_acce_data(); - break; - case 2: - display_show_gyro_data(); - break; - case 3: - display_show_complimentary_angle(); - break; - case 4: - display_show_barometer_data(); - break; - case 5: - display_show_magmeter_data(); - break; - default: - break; - } - } -} - -static void mpu6050_read(void *pvParameters) -{ - mpu6050_get_acce(mpu6050_dev, &acce); - mpu6050_get_gyro(mpu6050_dev, &gyro); - mpu6050_complimentory_filter(mpu6050_dev, &acce, &gyro, &complimentary_angle); -} - -static void btn_handler(void *button_handle, void *usr_data) -{ - if (++g_page_num >= 6) { - g_page_num = 0; - } - xQueueSend(q_page_num, &g_page_num, 0); + static_label = lv_label_create(scr); + lv_label_set_text_static(static_label, "Gyroscope"); + lv_obj_align_to(static_label, gyr_x_bar, LV_ALIGN_OUT_TOP_MID, 0, -BSP_LCD_V_RES / 32); + imu_create_axis_label(gyr_x_bar, "X"); + imu_create_axis_label(gyr_y_bar, "Y"); + imu_create_axis_label(gyr_z_bar, "Z"); } +#endif void app_main(void) { - // Init all board components - bsp_i2c_init(); - disp = bsp_display_start(); - bh1750_init(); - app_hts221_init(); - mpu6050_init(); - app_fbm320_init(); - app_mag3110_init(); - bsp_leds_init(); - bsp_buzzer_init(); - -#if EXAMPLE_TEST_SD_CARD - // Mount uSD card, light up WiFi LED on success - if (ESP_OK == bsp_sdcard_mount()) { - bsp_led_set(BSP_LED_WIFI, true); // Signal successful SD card access - sdmmc_card_t *sdcard = bsp_sdcard_get_handle(); - sdmmc_card_print_info(stdout, sdcard); - FILE *f = fopen(BSP_SD_MOUNT_POINT "/hello.txt", "w"); - fprintf(f, "Hello %s!\n", sdcard->cid.name); - fclose(f); - bsp_sdcard_unmount(); - } -#endif - - main_screen = lv_disp_get_scr_act(NULL); - - // Show icons - display_show_signs(); - - // Display text - bsp_display_lock(0); - main_label = lv_label_create(main_screen); - lv_label_set_text_static(main_label, "Magnetometer\ncalibration"); - lv_obj_set_style_text_align(main_label, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_set_width(main_label, lv_display_get_physical_horizontal_resolution(disp)); - lv_obj_align(main_label, LV_ALIGN_TOP_MID, 0, 15); - bsp_display_unlock(); - - // Start magnetometer calibrating procedure - mag3110_calibrate(mag3110_dev, 10000); +#if BSP_CAPS_DISPLAY + bsp_display_start(); bsp_display_lock(0); - lv_label_set_text_static(main_label, "Magnetometer\ncalibration\nDone!"); + lv_obj_t *main_scr = lv_screen_active(); +#if BSP_CAPS_HUMITURE + chart_init(main_scr); +#endif +#if BSP_CAPS_IMU + imu_bars_init(main_scr); +#endif bsp_display_unlock(); - mag3110_start(mag3110_dev, MAG3110_DR_OS_10_128); // Magnetometer is stopped after calibration; it must be started here - - // Create FreeRTOS tasks and queues - q_page_num = xQueueCreate(10, sizeof(uint8_t)); - xTaskCreate(display_show_task, "display_show_task", 2048 * 2, NULL, 5, NULL); + bsp_display_backlight_on(); +#endif - // In order to get accurate calculation of complimentary angle we need fast reading (5ms) - // FreeRTOS resolution is 10ms, so esp_timer is used - const esp_timer_create_args_t cal_timer_config = { - .callback = mpu6050_read, - .arg = NULL, - .name = "MPU6050 timer", -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) - .skip_unhandled_events = true, +#if BSP_CAPS_IMU + bsp_sensor_config_t imu_config = { + .type = IMU_ID, + .mode = MODE_POLLING, + .period = IMU_SAMPLING_PERIOD + }; + ESP_ERROR_CHECK(bsp_sensor_init(&imu_config, &imu_sensor_handle)); + iot_sensor_handler_register(imu_sensor_handle, sensor_event_handler, NULL); + iot_sensor_start(imu_sensor_handle); #endif - .dispatch_method = ESP_TIMER_TASK + +#if BSP_CAPS_HUMITURE + bsp_sensor_config_t humiture_config = { + .type = HUMITURE_ID, + .mode = MODE_POLLING, + .period = HUMITURE_SAMPLING_PERIOD }; - esp_timer_handle_t cal_timer = NULL; - esp_timer_create(&cal_timer_config, &cal_timer); - esp_timer_start_periodic(cal_timer, 5000); // 5ms - /* Init buttons */ - button_handle_t btns[BSP_BUTTON_NUM]; - ESP_ERROR_CHECK(bsp_iot_button_create(btns, NULL, BSP_BUTTON_NUM)); - for (int i = 0; i < BSP_BUTTON_NUM; i++) { -#if BUTTON_VER_MAJOR >= 4 - ESP_ERROR_CHECK(iot_button_register_cb(btns[i], BUTTON_PRESS_DOWN, NULL, btn_handler, (void *) i)); -#else - ESP_ERROR_CHECK(iot_button_register_cb(btns[i], BUTTON_PRESS_DOWN, btn_handler, (void *) i)); + ESP_ERROR_CHECK(bsp_sensor_init(&humiture_config, &humiture_sensor_handle)); + iot_sensor_handler_register(humiture_sensor_handle, sensor_event_handler, NULL); + iot_sensor_start(humiture_sensor_handle); #endif - } } diff --git a/examples/display_sensors/pytest_display_display_sensors.py b/examples/display_sensors/pytest_display_display_sensors.py deleted file mode 100644 index 327199b30..000000000 --- a/examples/display_sensors/pytest_display_display_sensors.py +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD -# SPDX-License-Identifier: CC0-1.0 - -import pytest -from pytest_embedded import Dut - - -@pytest.mark.esp32_azure_iot_kit -def test_example_display_sensors(dut: Dut) -> None: - dut.expect_exact('main_task: Returned from app_main()') - dut.expect(r'example: temperature: (\d+[.]\d+), humidity: (\d+[.]\d+), luminance: (\d+[.]\d+)') diff --git a/examples/sensors/sdkconfig.bsp.esp-box-3 b/examples/display_sensors/sdkconfig.bsp.esp-box-3 similarity index 96% rename from examples/sensors/sdkconfig.bsp.esp-box-3 rename to examples/display_sensors/sdkconfig.bsp.esp-box-3 index f7449c739..443165a90 100644 --- a/examples/sensors/sdkconfig.bsp.esp-box-3 +++ b/examples/display_sensors/sdkconfig.bsp.esp-box-3 @@ -20,7 +20,6 @@ CONFIG_LV_USE_CLIB_STRING=y # Performance monitor CONFIG_LV_USE_OBSERVER=y CONFIG_LV_USE_SYSMON=y -CONFIG_LV_USE_PERF_MONITOR=y ## LVGL8 - uncomment, when using LVGL8 ## # CONFIG_LV_USE_PERF_MONITOR=y diff --git a/examples/display_sensors/sdkconfig.bsp.esp32_azure_iot_kit b/examples/display_sensors/sdkconfig.bsp.esp32_azure_iot_kit deleted file mode 100644 index e9d45d247..000000000 --- a/examples/display_sensors/sdkconfig.bsp.esp32_azure_iot_kit +++ /dev/null @@ -1,13 +0,0 @@ -# This file was generated using idf.py save-defconfig. It can be edited manually. -# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration -# -CONFIG_IDF_TARGET="esp32" - -## LVGL9 ## -CONFIG_LV_CONF_SKIP=y -CONFIG_LV_BUILD_EXAMPLES=n - -#CLIB default -CONFIG_LV_USE_CLIB_MALLOC=y -CONFIG_LV_USE_CLIB_SPRINTF=y -CONFIG_LV_USE_CLIB_STRING=y diff --git a/examples/sensors/CMakeLists.txt b/examples/sensors/CMakeLists.txt deleted file mode 100644 index 0712a1d7c..000000000 --- a/examples/sensors/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# For more information about build system see -# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html -# The following five lines of boilerplate have to be in your project's -# CMakeLists in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.5) - -set(COMPONENTS main) # "Trim" the build. Include the minimal set of components; main and anything it depends on. -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(bsp-sensors-example) diff --git a/examples/sensors/README.md b/examples/sensors/README.md deleted file mode 100644 index 24ac40712..000000000 --- a/examples/sensors/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# BSP: Sensors Example - -This is an example usage of sensors in BSP packages. - -## Sensors -All sensors are sampled depending on the configured sampling period. -Measurement results are printed on the terminal. - -## Build and Flash - -To build and flash the example for a specific `{board}` and `{port}`, use the following command: - -``` -idf.py -D SDKCONFIG_DEFAULTS=sdkconfig.bsp.{board} -p {port} flash monitor -``` -Make sure the correct board name is set in the `main/idf_component.yml` file under the `dependencies` section. - -## Launch Example - -You can also try this example using ESP Launchpad: - - - Try it with ESP Launchpad - diff --git a/examples/sensors/main/CMakeLists.txt b/examples/sensors/main/CMakeLists.txt deleted file mode 100644 index bd886e001..000000000 --- a/examples/sensors/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "main.c" - INCLUDE_DIRS "." - ) diff --git a/examples/sensors/main/idf_component.yml b/examples/sensors/main/idf_component.yml deleted file mode 100644 index bac682611..000000000 --- a/examples/sensors/main/idf_component.yml +++ /dev/null @@ -1,5 +0,0 @@ -description: BSP sensor hub example -dependencies: - bsp_selector: - version: '*' - override_path: ../../../components/bsp_selector diff --git a/examples/sensors/main/main.c b/examples/sensors/main/main.c deleted file mode 100644 index c48aa660c..000000000 --- a/examples/sensors/main/main.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -/** - * @file - * @brief BSP Sensors Example - * @details Acquire sensor data using the sensor hub component - * @example https://espressif.github.io/esp-launchpad/?flashConfigURL=https://espressif.github.io/esp-bsp/config.toml&app=sensors - */ - -#include -#include "bsp/esp-bsp.h" -#include "esp_log.h" - -#define IMU_SAMPLING_PERIOD 300 -#define HUMITURE_SAMPLING_PERIOD 500 - -static const char *TAG = "example"; - -static sensor_handle_t imu_sensor_handle = NULL; -static sensor_handle_t humiture_sensor_handle = NULL; - -void sensor_event_handler(void *handler_args, esp_event_base_t base, int32_t id, void *event_data) -{ - sensor_data_t *sensor_data = (sensor_data_t *)event_data; - - switch (id) { - case SENSOR_STARTED: - ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x STARTED", - sensor_data->timestamp, - sensor_data->sensor_name, - sensor_data->sensor_addr); - break; - case SENSOR_STOPED: - ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x STOPPED", - sensor_data->timestamp, - sensor_data->sensor_name, - sensor_data->sensor_addr); - break; - case SENSOR_HUMI_DATA_READY: - ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x HUMI_DATA_READY - " - "humidity=%.2f", - sensor_data->timestamp, - sensor_data->sensor_name, - sensor_data->sensor_addr, - sensor_data->humidity); - break; - case SENSOR_TEMP_DATA_READY: - ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x TEMP_DATA_READY - " - "temperature=%.2f", - sensor_data->timestamp, - sensor_data->sensor_name, - sensor_data->sensor_addr, - sensor_data->temperature); - break; - case SENSOR_ACCE_DATA_READY: - ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x ACCE_DATA_READY - " - "acce_x=%.2f, acce_y=%.2f, acce_z=%.2f", - sensor_data->timestamp, - sensor_data->sensor_name, - sensor_data->sensor_addr, - sensor_data->acce.x, sensor_data->acce.y, sensor_data->acce.z); - break; - case SENSOR_GYRO_DATA_READY: - ESP_LOGI(TAG, "Timestamp = %llu - %s_0x%x GYRO_DATA_READY - " - "gyro_x=%.2f, gyro_y=%.2f, gyro_z=%.2f", - sensor_data->timestamp, - sensor_data->sensor_name, - sensor_data->sensor_addr, - sensor_data->gyro.x, sensor_data->gyro.y, sensor_data->gyro.z); - break; - default: - ESP_LOGI(TAG, "Timestamp = %" PRIi64 " - event id = %" PRIi32, sensor_data->timestamp, id); - break; - } -} - -void app_main(void) -{ -#if BSP_CAPS_IMU - bsp_sensor_config_t imu_config = { - .type = IMU_ID, - .mode = MODE_POLLING, - .period = IMU_SAMPLING_PERIOD - }; - ESP_ERROR_CHECK(bsp_sensor_init(&imu_config, &imu_sensor_handle)); - iot_sensor_handler_register(imu_sensor_handle, sensor_event_handler, NULL); - iot_sensor_start(imu_sensor_handle); -#endif - -#if BSP_CAPS_HUMITURE - bsp_sensor_config_t humiture_config = { - .type = HUMITURE_ID, - .mode = MODE_POLLING, - .period = HUMITURE_SAMPLING_PERIOD - }; - - ESP_ERROR_CHECK(bsp_sensor_init(&humiture_config, &humiture_sensor_handle)); - iot_sensor_handler_register(humiture_sensor_handle, sensor_event_handler, NULL); - iot_sensor_start(humiture_sensor_handle); -#endif -} diff --git a/examples/sensors/sdkconfig.defaults.esp32 b/examples/sensors/sdkconfig.defaults.esp32 deleted file mode 100644 index 2f32168ca..000000000 --- a/examples/sensors/sdkconfig.defaults.esp32 +++ /dev/null @@ -1,14 +0,0 @@ -# This file was generated using idf.py save-defconfig. It can be edited manually. -# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration -# -CONFIG_IDF_TARGET="esp32" -CONFIG_LV_USE_FLOAT=y - -## LVGL9 ## -CONFIG_LV_CONF_SKIP=y -CONFIG_LV_BUILD_EXAMPLES=n - -#CLIB default -CONFIG_LV_USE_CLIB_MALLOC=y -CONFIG_LV_USE_CLIB_SPRINTF=y -CONFIG_LV_USE_CLIB_STRING=y