mirror of
https://github.com/alexandrebobkov/ESP-Nodes.git
synced 2025-10-25 16:03:20 +00:00
.
This commit is contained in:
@@ -1,213 +0,0 @@
|
||||
components:
|
||||
- name: "app_trace"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/app_trace"
|
||||
- name: "app_update"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/app_update"
|
||||
- name: "bootloader"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/bootloader"
|
||||
- name: "bootloader_support"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/bootloader_support"
|
||||
- name: "bt"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/bt"
|
||||
- name: "cmock"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/cmock"
|
||||
- name: "console"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/console"
|
||||
- name: "cxx"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/cxx"
|
||||
- name: "driver"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/driver"
|
||||
- name: "efuse"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/efuse"
|
||||
- name: "esp-tls"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp-tls"
|
||||
- name: "esp_adc"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_adc"
|
||||
- name: "esp_app_format"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_app_format"
|
||||
- name: "esp_bootloader_format"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_bootloader_format"
|
||||
- name: "esp_coex"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_coex"
|
||||
- name: "esp_common"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_common"
|
||||
- name: "esp_driver_ana_cmpr"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_ana_cmpr"
|
||||
- name: "esp_driver_cam"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_cam"
|
||||
- name: "esp_driver_dac"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_dac"
|
||||
- name: "esp_driver_gpio"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_gpio"
|
||||
- name: "esp_driver_gptimer"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_gptimer"
|
||||
- name: "esp_driver_i2c"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_i2c"
|
||||
- name: "esp_driver_i2s"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_i2s"
|
||||
- name: "esp_driver_isp"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_isp"
|
||||
- name: "esp_driver_jpeg"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_jpeg"
|
||||
- name: "esp_driver_ledc"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_ledc"
|
||||
- name: "esp_driver_mcpwm"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_mcpwm"
|
||||
- name: "esp_driver_parlio"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_parlio"
|
||||
- name: "esp_driver_pcnt"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_pcnt"
|
||||
- name: "esp_driver_ppa"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_ppa"
|
||||
- name: "esp_driver_rmt"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_rmt"
|
||||
- name: "esp_driver_sdio"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_sdio"
|
||||
- name: "esp_driver_sdm"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_sdm"
|
||||
- name: "esp_driver_sdmmc"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_sdmmc"
|
||||
- name: "esp_driver_sdspi"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_sdspi"
|
||||
- name: "esp_driver_spi"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_spi"
|
||||
- name: "esp_driver_touch_sens"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_touch_sens"
|
||||
- name: "esp_driver_tsens"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_tsens"
|
||||
- name: "esp_driver_uart"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_uart"
|
||||
- name: "esp_driver_usb_serial_jtag"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_driver_usb_serial_jtag"
|
||||
- name: "esp_eth"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_eth"
|
||||
- name: "esp_event"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_event"
|
||||
- name: "esp_gdbstub"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_gdbstub"
|
||||
- name: "esp_hid"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_hid"
|
||||
- name: "esp_http_client"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_http_client"
|
||||
- name: "esp_http_server"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_http_server"
|
||||
- name: "esp_https_ota"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_https_ota"
|
||||
- name: "esp_https_server"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_https_server"
|
||||
- name: "esp_hw_support"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_hw_support"
|
||||
- name: "esp_lcd"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_lcd"
|
||||
- name: "esp_local_ctrl"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_local_ctrl"
|
||||
- name: "esp_mm"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_mm"
|
||||
- name: "esp_netif"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_netif"
|
||||
- name: "esp_netif_stack"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_netif_stack"
|
||||
- name: "esp_partition"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_partition"
|
||||
- name: "esp_phy"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_phy"
|
||||
- name: "esp_pm"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_pm"
|
||||
- name: "esp_psram"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_psram"
|
||||
- name: "esp_ringbuf"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_ringbuf"
|
||||
- name: "esp_rom"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_rom"
|
||||
- name: "esp_security"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_security"
|
||||
- name: "esp_system"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_system"
|
||||
- name: "esp_timer"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_timer"
|
||||
- name: "esp_vfs_console"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_vfs_console"
|
||||
- name: "esp_wifi"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esp_wifi"
|
||||
- name: "espcoredump"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/espcoredump"
|
||||
- name: "esptool_py"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/esptool_py"
|
||||
- name: "fatfs"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/fatfs"
|
||||
- name: "freertos"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/freertos"
|
||||
- name: "hal"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/hal"
|
||||
- name: "heap"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/heap"
|
||||
- name: "http_parser"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/http_parser"
|
||||
- name: "idf_test"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/idf_test"
|
||||
- name: "ieee802154"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/ieee802154"
|
||||
- name: "json"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/json"
|
||||
- name: "linux"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/linux"
|
||||
- name: "log"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/log"
|
||||
- name: "lwip"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/lwip"
|
||||
- name: "mbedtls"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/mbedtls"
|
||||
- name: "mqtt"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/mqtt"
|
||||
- name: "newlib"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/newlib"
|
||||
- name: "nvs_flash"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/nvs_flash"
|
||||
- name: "nvs_sec_provider"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/nvs_sec_provider"
|
||||
- name: "openthread"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/openthread"
|
||||
- name: "partition_table"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/partition_table"
|
||||
- name: "perfmon"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/perfmon"
|
||||
- name: "protobuf-c"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/protobuf-c"
|
||||
- name: "protocomm"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/protocomm"
|
||||
- name: "pthread"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/pthread"
|
||||
- name: "riscv"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/riscv"
|
||||
- name: "rt"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/rt"
|
||||
- name: "sdmmc"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/sdmmc"
|
||||
- name: "soc"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/soc"
|
||||
- name: "spi_flash"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/spi_flash"
|
||||
- name: "spiffs"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/spiffs"
|
||||
- name: "tcp_transport"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/tcp_transport"
|
||||
- name: "touch_element"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/touch_element"
|
||||
- name: "ulp"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/ulp"
|
||||
- name: "unity"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/unity"
|
||||
- name: "usb"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/usb"
|
||||
- name: "vfs"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/vfs"
|
||||
- name: "wear_levelling"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/wear_levelling"
|
||||
- name: "wifi_provisioning"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/wifi_provisioning"
|
||||
- name: "wpa_supplicant"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/wpa_supplicant"
|
||||
- name: "xtensa"
|
||||
path: "/home/abobkov/esp/v5.4.1/esp-idf/components/xtensa"
|
||||
- name: "main"
|
||||
path: "/home/abobkov/MyProjects/ESP-Nodes/ESP32-IDF_Temperture-Node-v2/main"
|
||||
@@ -0,0 +1 @@
|
||||
873d97d0bd30004f45d1653f078a4bafe39c1767e57d4bae0f0a13bc3a4d5e3d
|
||||
@@ -0,0 +1,13 @@
|
||||
# ChangeLog
|
||||
|
||||
## v0.1.1 - 2024-12-23
|
||||
|
||||
### Bug Fixes:
|
||||
|
||||
* Fix the issue in README.md where the usage example for bme280 lacks the default initialization.
|
||||
|
||||
## v0.1.0 - 2024-11-5
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Initial version
|
||||
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-05-21T17:09:18.488972+00:00", "files": [{"path": "CMakeLists.txt", "size": 160, "hash": "b9af3a241cecba82dda3b44e32c70b1aac28890f41eb00768f518a000f163357"}, {"path": "CHANGELOG.md", "size": 211, "hash": "d61f738a9a542e7c0f7d69fcc0b14e20c15d96688f0e4e067c943e853e3657e8"}, {"path": "idf_component.yml", "size": 580, "hash": "71fa62fa58af333761e45146271e5ed3c90e7e845740391711bfe276266b9131"}, {"path": "README.md", "size": 1713, "hash": "51df9e655bb500962a5736b65e49e6920d713216556c8bbf5d63b3132e1d3862"}, {"path": "license.txt", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "bme280.c", "size": 13819, "hash": "47aac27b35df43fdad8551d1f8759d774362d291859685a77a331aa6433eb163"}, {"path": "include/bme280.h", "size": 10161, "hash": "043eda239a4686220cff149054b678bd736bf7c5f2edd290c7c703c8a1c156f9"}, {"path": "test_apps/CMakeLists.txt", "size": 350, "hash": "02be4ce8d0c8034408017c3948d13d93d3be6fdc40ada18906a5f15fc4ef0dad"}, {"path": "test_apps/sdkconfig.defaults", "size": 213, "hash": "9a34a6cb08c49ec24007587e0c5d492f44b5a862d9c0f583cf9f6f643669b564"}, {"path": "test_apps/main/CMakeLists.txt", "size": 143, "hash": "802c3b217fc1bd9ed8c615353f70d9084caf0b4216288e05fc18af11e6bf7abf"}, {"path": "test_apps/main/bme280_test.c", "size": 2447, "hash": "ee63d7fbd09167de421eb0c416acb6a9f0da44789f7d453b603b3fd74989cf4c"}]}
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(SRCS "bme280.c"
|
||||
INCLUDE_DIRS include)
|
||||
|
||||
include(package_manager)
|
||||
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
|
||||
@@ -0,0 +1,51 @@
|
||||
# Component: BME280
|
||||
|
||||
The BME280 is as combined digital humidity, pressure and temperature sensor based on proven sensing principles. The sensor module is housed in an extremely compact metal-lid LGA package with a footprint of only 2.5 × 2.5 mm² with a height of 0.93 mm. Its small dimensions and its low power consumption allow the implementation in battery driven devices such as handsets, GPS modules or watches.
|
||||
|
||||
|
||||
## Add component to your project
|
||||
|
||||
Please use the component manager command `add-dependency` to add the `bme280` to your project's dependency, during the `CMake` step the component will be downloaded automatically
|
||||
|
||||
```
|
||||
idf.py add-dependency "espressif/bme280=*"
|
||||
```
|
||||
|
||||
## Example of BME280 usage
|
||||
|
||||
|
||||
Pin assignment:
|
||||
|
||||
* master:
|
||||
* GPIO2 is assigned as the clock signal of i2c master port
|
||||
* GPIO1 is assigned as the data signal of i2c master port
|
||||
* Connection:
|
||||
* connect sda of sensor with GPIO1
|
||||
* connect scl of sensor with GPIO2
|
||||
|
||||
|
||||
```c
|
||||
static i2c_bus_handle_t i2c_bus = NULL;
|
||||
static bme280_handle_t bme280 = NULL;
|
||||
|
||||
//Step1: Init I2C bus
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
|
||||
|
||||
//Step2: Init bme280
|
||||
bme280 = bme280_create(i2c_bus, BME280_I2C_ADDRESS_DEFAULT);
|
||||
bme280_default_init(bme280);
|
||||
|
||||
//Step3: Read temperature, humidity and pressure
|
||||
float temperature = 0.0, humidity = 0.0, pressure = 0.0;
|
||||
bme280_read_temperature(bme280, &temperature);
|
||||
bme280_read_humidity(bme280, &humidity);
|
||||
bme280_read_pressure(bme280, &pressure);
|
||||
```
|
||||
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "i2c_bus.h"
|
||||
#include "bme280.h"
|
||||
#include "math.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
bme280_handle_t bme280_create(i2c_bus_handle_t bus, uint8_t dev_addr)
|
||||
{
|
||||
bme280_dev_t *sens = (bme280_dev_t *) calloc(1, sizeof(bme280_dev_t));
|
||||
sens->i2c_dev = i2c_bus_device_create(bus, dev_addr, i2c_bus_get_current_clk_speed(bus));
|
||||
if (sens->i2c_dev == NULL) {
|
||||
free(sens);
|
||||
return NULL;
|
||||
}
|
||||
sens->dev_addr = dev_addr;
|
||||
return (bme280_handle_t)sens;
|
||||
}
|
||||
|
||||
esp_err_t bme280_delete(bme280_handle_t *sensor)
|
||||
{
|
||||
if (*sensor == NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
bme280_dev_t *sens = (bme280_dev_t *)(*sensor);
|
||||
i2c_bus_device_delete(&sens->i2c_dev);
|
||||
free(sens);
|
||||
*sensor = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t bme280_read_uint16(bme280_handle_t sensor, uint8_t addr, uint16_t *data)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
uint8_t data0, data1;
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, addr, &data0) != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, addr + 1, &data1) != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
*data = (data0 << 8) | data1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t bme280_read_uint16_le(bme280_handle_t sensor, uint8_t addr, uint16_t *data)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
uint8_t data0, data1;
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, addr, &data0) != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, addr + 1, &data1) != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
*data = (data1 << 8) | data0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
unsigned int bme280_getconfig(bme280_handle_t sensor)
|
||||
{
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
return (sens->config_t.t_sb << 5) | (sens->config_t.filter << 3) | sens->config_t.spi3w_en;
|
||||
}
|
||||
|
||||
unsigned int bme280_getctrl_meas(bme280_handle_t sensor)
|
||||
{
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
return (sens->ctrl_meas_t.osrs_t << 5) | (sens->ctrl_meas_t.osrs_p << 3) | sens->ctrl_meas_t.mode;
|
||||
}
|
||||
|
||||
unsigned int bme280_getctrl_hum(bme280_handle_t sensor)
|
||||
{
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
return (sens->ctrl_hum_t.osrs_h);
|
||||
}
|
||||
|
||||
bool bme280_is_reading_calibration(bme280_handle_t sensor)
|
||||
{
|
||||
uint8_t rstatus = 0;
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_STATUS, &rstatus) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
return (rstatus & (1 << 0)) != 0;
|
||||
}
|
||||
|
||||
esp_err_t bme280_read_coefficients(bme280_handle_t sensor)
|
||||
{
|
||||
uint8_t data = 0;
|
||||
uint8_t data1 = 0;
|
||||
uint16_t data16 = 0;
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_T1, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_t1 = data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_T2, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_t2 = (int16_t) data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_T3, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_t3 = (int16_t) data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_P1, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_p1 = data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_P2, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_p2 = (int16_t) data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_P3, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_p3 = (int16_t) data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_P4, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_p4 = (int16_t) data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_P5, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_p5 = (int16_t) data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_P6, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_p6 = (int16_t) data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_P7, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_p7 = (int16_t) data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_P8, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_p8 = (int16_t) data16;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_P9, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_p9 = (int16_t) data16;
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_DIG_H1, &data) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_h1 = data;
|
||||
if (bme280_read_uint16_le(sensor, BME280_REGISTER_DIG_H2, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_h2 = (int16_t) data16;
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_DIG_H3, &data) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_h3 = data;
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_DIG_H4, &data) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_DIG_H4 + 1, &data1) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_h4 = (data << 4) | (data1 & 0xF);
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_DIG_H5 + 1, &data) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_DIG_H5, &data1) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_h5 = (data << 4) | (data1 >> 4);
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_DIG_H6, &data) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
sens->data_t.dig_h6 = (int8_t) data;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bme280_set_sampling(bme280_handle_t sensor, bme280_sensor_mode mode, bme280_sensor_sampling tempSampling, bme280_sensor_sampling pressSampling, bme280_sensor_sampling humSampling, bme280_sensor_filter filter, bme280_standby_duration duration)
|
||||
{
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
sens->ctrl_meas_t.mode = mode;
|
||||
sens->ctrl_meas_t.osrs_t = tempSampling;
|
||||
sens->ctrl_meas_t.osrs_p = pressSampling;
|
||||
sens->ctrl_hum_t.osrs_h = humSampling;
|
||||
sens->config_t.filter = filter;
|
||||
sens->config_t.t_sb = duration;
|
||||
// you must make sure to also set REGISTER_CONTROL after setting the
|
||||
// CONTROLHUMID register, otherwise the values won't be applied (see DS 5.4.3)
|
||||
if (i2c_bus_write_byte(sens->i2c_dev, BME280_REGISTER_CONTROLHUMID, bme280_getctrl_hum(sensor)) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (i2c_bus_write_byte(sens->i2c_dev, BME280_REGISTER_CONFIG, bme280_getconfig(sensor)) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (i2c_bus_write_byte(sens->i2c_dev, BME280_REGISTER_CONTROL, bme280_getctrl_meas(sensor)) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bme280_default_init(bme280_handle_t sensor)
|
||||
{
|
||||
// check if sensor, i.e. the chip ID is correct
|
||||
uint8_t chipid = 0;
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_CHIPID, &chipid) != ESP_OK) {
|
||||
ESP_LOGI("BME280:", "bme280_default_init->bme280_read_byte ->BME280_REGISTER_CHIPID failed!!!!:%x", chipid);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (chipid != BME280_DEFAULT_CHIPID) {
|
||||
ESP_LOGI("BME280:", "bme280_default_init->BME280_DEFAULT_CHIPID:%x", chipid);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
// reset the sens using soft-reset, this makes sure the IIR is off, etc.
|
||||
if (i2c_bus_write_byte(sens->i2c_dev, BME280_REGISTER_SOFTRESET, 0xB6) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
// wait for chip to wake up.
|
||||
vTaskDelay(300 / portTICK_RATE_MS);
|
||||
// if chip is still reading calibration, delay
|
||||
while (bme280_is_reading_calibration(sensor)) {
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
}
|
||||
if (bme280_read_coefficients(sensor) != ESP_OK) { // read trimming parameters, see DS 4.2.2
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (bme280_set_sampling(sensor, BME280_MODE_NORMAL, BME280_SAMPLING_X16, BME280_SAMPLING_X16, BME280_SAMPLING_X16, BME280_FILTER_OFF, BME280_STANDBY_MS_0_5) != ESP_OK) { // use defaults
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bme280_take_forced_measurement(bme280_handle_t sensor)
|
||||
{
|
||||
uint8_t data = 0;
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
if (sens->ctrl_meas_t.mode == BME280_MODE_FORCED) {
|
||||
// set to forced mode, i.e. "take next measurement"
|
||||
if (i2c_bus_write_byte(sens->i2c_dev, BME280_REGISTER_CONTROL, bme280_getctrl_meas(sensor)) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
// wait until measurement has been completed, otherwise we would read, the values from the last measurement
|
||||
if (i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_STATUS, &data) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
while (data & 0x08) {
|
||||
i2c_bus_read_byte(sens->i2c_dev, BME280_REGISTER_STATUS, &data);
|
||||
vTaskDelay(10 / portTICK_RATE_MS);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bme280_read_temperature(bme280_handle_t sensor, float *temperature)
|
||||
{
|
||||
int32_t var1, var2;
|
||||
uint8_t data[3] = { 0 };
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
if (i2c_bus_read_bytes(sens->i2c_dev, BME280_REGISTER_TEMPDATA, 3, data) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int32_t adc_T = (data[0] << 16) | (data[1] << 8) | data[2];
|
||||
if (adc_T == 0x800000) { // value in case temp measurement was disabled
|
||||
return ESP_FAIL;
|
||||
}
|
||||
adc_T >>= 4;
|
||||
|
||||
var1 = ((((adc_T >> 3) - ((int32_t) sens->data_t.dig_t1 << 1)))
|
||||
* ((int32_t) sens->data_t.dig_t2)) >> 11;
|
||||
|
||||
var2 = (((((adc_T >> 4) - ((int32_t) sens->data_t.dig_t1))
|
||||
* ((adc_T >> 4) - ((int32_t) sens->data_t.dig_t1))) >> 12)
|
||||
* ((int32_t) sens->data_t.dig_t3)) >> 14;
|
||||
|
||||
sens->t_fine = var1 + var2;
|
||||
*temperature = ((sens->t_fine * 5 + 128) >> 8) / 100.0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bme280_read_pressure(bme280_handle_t sensor, float *pressure)
|
||||
{
|
||||
int64_t var1, var2, p;
|
||||
uint8_t data[3] = { 0 };
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
float temp = 0.0;
|
||||
if (bme280_read_temperature(sensor, &temp) != ESP_OK) {
|
||||
// must be done first to get t_fine
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (i2c_bus_read_bytes(sens->i2c_dev, BME280_REGISTER_PRESSUREDATA, 3, data) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int32_t adc_P = (data[0] << 16) | (data[1] << 8) | data[2];
|
||||
if (adc_P == 0x800000) { // value in case pressure measurement was disabled
|
||||
return ESP_FAIL;
|
||||
}
|
||||
adc_P >>= 4;
|
||||
var1 = ((int64_t) sens->t_fine) - 128000;
|
||||
var2 = var1 * var1 * (int64_t) sens->data_t.dig_p6;
|
||||
var2 = var2 + ((var1 * (int64_t) sens->data_t.dig_p5) << 17);
|
||||
var2 = var2 + (((int64_t) sens->data_t.dig_p4) << 35);
|
||||
var1 = ((var1 * var1 * (int64_t) sens->data_t.dig_p3) >> 8) + ((var1 * (int64_t) sens->data_t.dig_p2) << 12);
|
||||
var1 = (((((int64_t) 1) << 47) + var1)) * ((int64_t) sens->data_t.dig_p1) >> 33;
|
||||
if (var1 == 0) {
|
||||
return ESP_FAIL; // avoid exception caused by division by zero
|
||||
}
|
||||
p = 1048576 - adc_P;
|
||||
p = (((p << 31) - var2) * 3125) / var1;
|
||||
var1 = (((int64_t) sens->data_t.dig_p9) * (p >> 13) * (p >> 13)) >> 25;
|
||||
var2 = (((int64_t) sens->data_t.dig_p8) * p) >> 19;
|
||||
p = ((p + var1 + var2) >> 8) + (((int64_t) sens->data_t.dig_p7) << 4);
|
||||
p = p >> 8; // /256
|
||||
*pressure = (float) p / 100;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bme280_read_humidity(bme280_handle_t sensor, float *humidity)
|
||||
{
|
||||
uint16_t data16;
|
||||
bme280_dev_t *sens = (bme280_dev_t *) sensor;
|
||||
float temp = 0.0;
|
||||
if (bme280_read_temperature(sensor, &temp) != ESP_OK) {
|
||||
// must be done first to get t_fine
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (bme280_read_uint16(sensor, BME280_REGISTER_HUMIDDATA, &data16) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int32_t adc_H = data16;
|
||||
if (adc_H == 0x8000) { // value in case humidity measurement was disabled
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int32_t v_x1_u32r;
|
||||
v_x1_u32r = (sens->t_fine - ((int32_t) 76800));
|
||||
v_x1_u32r = (((((adc_H << 14) - (((int32_t) sens->data_t.dig_h4) << 20)
|
||||
- (((int32_t) sens->data_t.dig_h5) * v_x1_u32r))
|
||||
+ ((int32_t) 16384)) >> 15)
|
||||
* (((((((v_x1_u32r * ((int32_t) sens->data_t.dig_h6)) >> 10)
|
||||
* (((v_x1_u32r * ((int32_t) sens->data_t.dig_h3)) >> 11) + ((int32_t) 32768))) >> 10) + ((int32_t) 2097152))
|
||||
* ((int32_t) sens->data_t.dig_h2) + 8192) >> 14));
|
||||
v_x1_u32r = (v_x1_u32r
|
||||
- (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7)
|
||||
* ((int32_t) sens->data_t.dig_h1)) >> 4));
|
||||
v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r;
|
||||
v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r;
|
||||
*humidity = (v_x1_u32r >> 12) / 1024.0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bme280_read_altitude(bme280_handle_t sensor, float seaLevel, float *altitude)
|
||||
{
|
||||
float pressure = 0.0;
|
||||
float temp = 0.0;
|
||||
if (bme280_read_pressure(sensor, &temp) != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
float atmospheric = pressure / 100.0F;
|
||||
*altitude = 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bme280_calculates_pressure(bme280_handle_t sensor, float altitude,
|
||||
float atmospheric, float *pressure)
|
||||
{
|
||||
*pressure = atmospheric / pow(1.0 - (altitude / 44330.0), 5.255);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
dependencies:
|
||||
cmake_utilities: 0.*
|
||||
i2c_bus:
|
||||
public: true
|
||||
idf: '>=4.4'
|
||||
description: I2C driver for BME280 preesure sensor
|
||||
documentation: https://docs.espressif.com/projects/esp-iot-solution/en/latest/sensors/pressure.html
|
||||
issues: https://github.com/espressif/esp-iot-solution/issues
|
||||
repository: git://github.com/espressif/esp-iot-solution.git
|
||||
repository_info:
|
||||
commit_sha: 1f4206cfe0ff480fedd4fa7860dc41ece6768812
|
||||
path: components/sensors/pressure/bme280
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/components/sensors/pressure/bme280
|
||||
version: 0.1.1
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _BME280_H_
|
||||
#define _BME280_H_
|
||||
|
||||
#include "i2c_bus.h"
|
||||
|
||||
#define BME280_I2C_ADDRESS_DEFAULT (0x76) /*The device's I2C address is either 0x76 or 0x77.*/
|
||||
#define BME280_DEFAULT_CHIPID (0x60)
|
||||
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||
|
||||
#define BME280_REGISTER_DIG_T1 0x88
|
||||
#define BME280_REGISTER_DIG_T2 0x8A
|
||||
#define BME280_REGISTER_DIG_T3 0x8C
|
||||
|
||||
#define BME280_REGISTER_DIG_P1 0x8E
|
||||
#define BME280_REGISTER_DIG_P2 0x90
|
||||
#define BME280_REGISTER_DIG_P3 0x92
|
||||
#define BME280_REGISTER_DIG_P4 0x94
|
||||
#define BME280_REGISTER_DIG_P5 0x96
|
||||
#define BME280_REGISTER_DIG_P6 0x98
|
||||
#define BME280_REGISTER_DIG_P7 0x9A
|
||||
#define BME280_REGISTER_DIG_P8 0x9C
|
||||
#define BME280_REGISTER_DIG_P9 0x9E
|
||||
|
||||
#define BME280_REGISTER_DIG_H1 0xA1
|
||||
#define BME280_REGISTER_DIG_H2 0xE1
|
||||
#define BME280_REGISTER_DIG_H3 0xE3
|
||||
#define BME280_REGISTER_DIG_H4 0xE4
|
||||
#define BME280_REGISTER_DIG_H5 0xE5
|
||||
#define BME280_REGISTER_DIG_H6 0xE7
|
||||
|
||||
#define BME280_REGISTER_CHIPID 0xD0
|
||||
#define BME280_REGISTER_VERSION 0xD1
|
||||
#define BME280_REGISTER_SOFTRESET 0xE0
|
||||
|
||||
#define BME280_REGISTER_CAL26 0xE1 // R calibration stored in 0xE1-0xF0
|
||||
|
||||
#define BME280_REGISTER_CONTROLHUMID 0xF2
|
||||
#define BME280_REGISTER_STATUS 0XF3
|
||||
#define BME280_REGISTER_CONTROL 0xF4
|
||||
#define BME280_REGISTER_CONFIG 0xF5
|
||||
#define BME280_REGISTER_PRESSUREDATA 0xF7
|
||||
#define BME280_REGISTER_TEMPDATA 0xFA
|
||||
#define BME280_REGISTER_HUMIDDATA 0xFD
|
||||
|
||||
typedef struct {
|
||||
uint16_t dig_t1;
|
||||
int16_t dig_t2;
|
||||
int16_t dig_t3;
|
||||
|
||||
uint16_t dig_p1;
|
||||
int16_t dig_p2;
|
||||
int16_t dig_p3;
|
||||
int16_t dig_p4;
|
||||
int16_t dig_p5;
|
||||
int16_t dig_p6;
|
||||
int16_t dig_p7;
|
||||
int16_t dig_p8;
|
||||
int16_t dig_p9;
|
||||
|
||||
uint8_t dig_h1;
|
||||
int16_t dig_h2;
|
||||
uint8_t dig_h3;
|
||||
int16_t dig_h4;
|
||||
int16_t dig_h5;
|
||||
int8_t dig_h6;
|
||||
} bme280_data_t;
|
||||
|
||||
typedef enum {
|
||||
BME280_SAMPLING_NONE = 0b000,
|
||||
BME280_SAMPLING_X1 = 0b001,
|
||||
BME280_SAMPLING_X2 = 0b010,
|
||||
BME280_SAMPLING_X4 = 0b011,
|
||||
BME280_SAMPLING_X8 = 0b100,
|
||||
BME280_SAMPLING_X16 = 0b101
|
||||
} bme280_sensor_sampling;
|
||||
|
||||
typedef enum {
|
||||
BME280_MODE_SLEEP = 0b00,
|
||||
BME280_MODE_FORCED = 0b01,
|
||||
BME280_MODE_NORMAL = 0b11
|
||||
} bme280_sensor_mode;
|
||||
|
||||
typedef enum {
|
||||
BME280_FILTER_OFF = 0b000,
|
||||
BME280_FILTER_X2 = 0b001,
|
||||
BME280_FILTER_X4 = 0b010,
|
||||
BME280_FILTER_X8 = 0b011,
|
||||
BME280_FILTER_X16 = 0b100
|
||||
} bme280_sensor_filter;
|
||||
|
||||
// standby durations in ms
|
||||
typedef enum {
|
||||
BME280_STANDBY_MS_0_5 = 0b000,
|
||||
BME280_STANDBY_MS_10 = 0b110,
|
||||
BME280_STANDBY_MS_20 = 0b111,
|
||||
BME280_STANDBY_MS_62_5 = 0b001,
|
||||
BME280_STANDBY_MS_125 = 0b010,
|
||||
BME280_STANDBY_MS_250 = 0b011,
|
||||
BME280_STANDBY_MS_500 = 0b100,
|
||||
BME280_STANDBY_MS_1000 = 0b101
|
||||
} bme280_standby_duration;
|
||||
|
||||
// The config register
|
||||
typedef struct config {
|
||||
// inactive duration (standby time) in normal mode
|
||||
// 000 = 0.5 ms
|
||||
// 001 = 62.5 ms
|
||||
// 010 = 125 ms
|
||||
// 011 = 250 ms
|
||||
// 100 = 500 ms
|
||||
// 101 = 1000 ms
|
||||
// 110 = 10 ms
|
||||
// 111 = 20 ms
|
||||
unsigned int t_sb : 3;
|
||||
|
||||
// filter settings
|
||||
// 000 = filter off
|
||||
// 001 = 2x filter
|
||||
// 010 = 4x filter
|
||||
// 011 = 8x filter
|
||||
// 100 and above = 16x filter
|
||||
unsigned int filter : 3;
|
||||
|
||||
// unused - don't set
|
||||
unsigned int none : 1;
|
||||
unsigned int spi3w_en : 1;
|
||||
} bme280_config_t;
|
||||
|
||||
// The ctrl_meas register
|
||||
typedef struct ctrl_meas {
|
||||
// temperature oversampling
|
||||
// 000 = skipped
|
||||
// 001 = x1
|
||||
// 010 = x2
|
||||
// 011 = x4
|
||||
// 100 = x8
|
||||
// 101 and above = x16
|
||||
unsigned int osrs_t : 3;
|
||||
|
||||
// pressure oversampling
|
||||
// 000 = skipped
|
||||
// 001 = x1
|
||||
// 010 = x2
|
||||
// 011 = x4
|
||||
// 100 = x8
|
||||
// 101 and above = x16
|
||||
unsigned int osrs_p : 3;
|
||||
|
||||
// device mode
|
||||
// 00 = sleep
|
||||
// 01 or 10 = forced
|
||||
// 11 = normal
|
||||
unsigned int mode : 2;
|
||||
} bme280_ctrl_meas_t;
|
||||
|
||||
// The ctrl_hum register
|
||||
typedef struct ctrl_hum {
|
||||
// unused - don't set
|
||||
unsigned int none : 5;
|
||||
|
||||
// pressure oversampling
|
||||
// 000 = skipped
|
||||
// 001 = x1
|
||||
// 010 = x2
|
||||
// 011 = x4
|
||||
// 100 = x8
|
||||
// 101 and above = x16
|
||||
unsigned int osrs_h : 3;
|
||||
} bme280_ctrl_hum_t;
|
||||
|
||||
typedef struct {
|
||||
i2c_bus_device_handle_t i2c_dev;
|
||||
uint8_t dev_addr;
|
||||
bme280_data_t data_t;
|
||||
bme280_config_t config_t;
|
||||
bme280_ctrl_meas_t ctrl_meas_t;
|
||||
bme280_ctrl_hum_t ctrl_hum_t;
|
||||
int32_t t_fine;
|
||||
} bme280_dev_t;
|
||||
|
||||
typedef void *bme280_handle_t; /*handle of bme280*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create bme280 handle_t
|
||||
*
|
||||
* @param object handle of I2C
|
||||
* @param device address
|
||||
*
|
||||
* @return
|
||||
* - bme280_handle_t
|
||||
*/
|
||||
bme280_handle_t bme280_create(i2c_bus_handle_t bus, uint8_t dev_addr);
|
||||
|
||||
/**
|
||||
* @brief delete bme280 handle_t
|
||||
*
|
||||
* @param point to object handle of bme280
|
||||
* @param whether delete i2c bus
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t bme280_delete(bme280_handle_t *sensor);
|
||||
|
||||
/**
|
||||
* @brief Get the value of BME280_REGISTER_CONFIG register
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
*
|
||||
* @return
|
||||
* - unsigned int: the value of BME280_REGISTER_CONFIG register
|
||||
*/
|
||||
unsigned int bme280_getconfig(bme280_handle_t sensor);
|
||||
|
||||
/**
|
||||
* @brief Get the value of BME280_REGISTER_CONTROL measure register
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
*
|
||||
* @return
|
||||
* - unsigned int the value of BME280_REGISTER_CONTROL register
|
||||
*/
|
||||
unsigned int bme280_getctrl_meas(bme280_handle_t sensor);
|
||||
|
||||
/**
|
||||
* @brief Get the value of BME280_REGISTER_CONTROLHUMID measure register
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
*
|
||||
* @return
|
||||
* - unsigned int the value of BME280_REGISTER_CONTROLHUMID register
|
||||
*/
|
||||
unsigned int bme280_getctrl_hum(bme280_handle_t sensor);
|
||||
|
||||
/**
|
||||
* @brief return true if chip is busy reading cal data
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
*
|
||||
* @return
|
||||
* - true chip is busy
|
||||
* - false chip is idle or wrong
|
||||
*/
|
||||
bool bme280_is_reading_calibration(bme280_handle_t sensor);
|
||||
|
||||
/**
|
||||
* @brief Reads the factory-set coefficients
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t bme280_read_coefficients(bme280_handle_t sensor);
|
||||
|
||||
/**
|
||||
* @brief setup sensor with gien parameters / settings
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
* @param Sensor working mode
|
||||
* @param the sample of temperature measure
|
||||
* @param the sample of pressure measure
|
||||
* @param the sample of humidity measure
|
||||
* @param Sensor filter multiples
|
||||
* @param standby duration of sensor
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t bme280_set_sampling(bme280_handle_t sensor, bme280_sensor_mode mode,
|
||||
bme280_sensor_sampling tempsampling,
|
||||
bme280_sensor_sampling presssampling,
|
||||
bme280_sensor_sampling humsampling, bme280_sensor_filter filter,
|
||||
bme280_standby_duration duration);
|
||||
|
||||
/**
|
||||
* @brief init bme280 device
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t bme280_default_init(bme280_handle_t sensor);
|
||||
|
||||
/**
|
||||
* @brief Take a new measurement (only possible in forced mode)
|
||||
* If we are in forced mode, the BME sensor goes back to sleep after each
|
||||
* measurement and we need to set it to forced mode once at this point, so
|
||||
* it will take the next measurement and then return to sleep again.
|
||||
* In normal mode simply does new measurements periodically.
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t bme280_take_forced_measurement(bme280_handle_t sensor);
|
||||
|
||||
/**
|
||||
* @brief Returns the temperature from the sensor
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
* @param temperature pointer to temperature
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t bme280_read_temperature(bme280_handle_t sensor, float *temperature);
|
||||
|
||||
/**
|
||||
* @brief Returns the temperature from the sensor
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
* @param pressure pointer to pressure value
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t bme280_read_pressure(bme280_handle_t sensor, float *pressure);
|
||||
|
||||
/**
|
||||
* @brief Returns the humidity from the sensor
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
* @param humidity pointer to humidity value
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t bme280_read_humidity(bme280_handle_t sensor, float *humidity);
|
||||
|
||||
/**
|
||||
* @brief Calculates the altitude (in meters) from the specified atmospheric
|
||||
* pressure (in hPa), and sea-level pressure (in hPa).
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
* @param seaLevel: Sea-level pressure in hPa
|
||||
* @param altitude pointer to altitude value
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t bme280_read_altitude(bme280_handle_t sensor, float seaLevel, float *altitude);
|
||||
|
||||
/**
|
||||
* Calculates the pressure at sea level (in hPa) from the specified altitude
|
||||
* (in meters), and atmospheric pressure (in hPa).
|
||||
*
|
||||
* @param sensor object handle of bme280
|
||||
* @param altitude Altitude in meters
|
||||
* @param atmospheric Atmospheric pressure in hPa
|
||||
* @param pressure pointer to pressure value
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t bme280_calculates_pressure(bme280_handle_t sensor, float altitude,
|
||||
float atmospheric, float *pressure);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -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.
|
||||
@@ -0,0 +1,9 @@
|
||||
# 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(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
|
||||
"../../bme280")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(bme280_test)
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
PRIV_INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES unity test_utils bme280)
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "esp_log.h"
|
||||
#include "bme280.h"
|
||||
#include "i2c_bus.h"
|
||||
|
||||
#define I2C_MASTER_SCL_IO GPIO_NUM_2 /*!< gpio number for I2C master clock IO2*/
|
||||
#define I2C_MASTER_SDA_IO GPIO_NUM_1 /*!< gpio number for I2C master data IO1*/
|
||||
#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master bme280 */
|
||||
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
|
||||
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
|
||||
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
|
||||
|
||||
static i2c_bus_handle_t i2c_bus = NULL;
|
||||
static bme280_handle_t bme280 = NULL;
|
||||
|
||||
void bme280_test_init()
|
||||
{
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
|
||||
bme280 = bme280_create(i2c_bus, BME280_I2C_ADDRESS_DEFAULT);
|
||||
ESP_LOGI("BME280:", "bme280_default_init:%d", bme280_default_init(bme280));
|
||||
}
|
||||
|
||||
void bme280_test_deinit()
|
||||
{
|
||||
bme280_delete(&bme280);
|
||||
i2c_bus_delete(&i2c_bus);
|
||||
}
|
||||
|
||||
void bme280_test_getdata()
|
||||
{
|
||||
int cnt = 10;
|
||||
while (cnt--) {
|
||||
float temperature = 0.0, humidity = 0.0, pressure = 0.0;
|
||||
if (ESP_OK == bme280_read_temperature(bme280, &temperature)) {
|
||||
ESP_LOGI("BME280", "temperature:%f ", temperature);
|
||||
}
|
||||
vTaskDelay(300 / portTICK_RATE_MS);
|
||||
if (ESP_OK == bme280_read_humidity(bme280, &humidity)) {
|
||||
ESP_LOGI("BME280", "humidity:%f ", humidity);
|
||||
}
|
||||
vTaskDelay(300 / portTICK_RATE_MS);
|
||||
if (ESP_OK == bme280_read_pressure(bme280, &pressure)) {
|
||||
ESP_LOGI("BME280", "pressure:%f\n", pressure);
|
||||
}
|
||||
vTaskDelay(300 / portTICK_RATE_MS);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Device bme280 test", "[bme280][iot][device]")
|
||||
{
|
||||
bme280_test_init();
|
||||
bme280_test_getdata();
|
||||
bme280_test_deinit();
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("BME280 TEST \n");
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# For IDF 5.0
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
|
||||
# For IDF4.4
|
||||
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
@@ -0,0 +1 @@
|
||||
351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f
|
||||
@@ -0,0 +1,89 @@
|
||||
## v0.5.3 - 2023-09-15
|
||||
|
||||
* fix `add_dependencies called with incorrect number of arguments` in `relinker.cmake`
|
||||
* `include(cmake_utilities)` is not suggested now, to avoid cmake_utilities dependency issue
|
||||
|
||||
## v0.5.2 - 2023-09-15
|
||||
|
||||
* Support work on older ESP-IDF, eg: 4.3.x
|
||||
|
||||
## v0.5.1 - 2023-08-22
|
||||
|
||||
* Add string 1-byte align support
|
||||
|
||||
## v0.5.0 - 2023-08-02
|
||||
|
||||
* Add GCC LTO support
|
||||
|
||||
## v0.4.8 - 2023-05-24
|
||||
|
||||
* Add unit test app
|
||||
|
||||
### Bugfix:
|
||||
|
||||
* fix customer target redefinition issue
|
||||
|
||||
## v0.4.7 - 2023-04-21
|
||||
|
||||
* gen_compressed_ota: support the addition of a v2 compressed OTA header to compressed firmware.
|
||||
|
||||
## v0.4.6 - 2023-04-20
|
||||
|
||||
* relinker: add IDF v4.3.x support
|
||||
|
||||
## v0.4.5 - 2023-04-17
|
||||
|
||||
* gen_compressed_ota: remove slash use in gen_custom_ota.py so that the script can be used in the Windows cmd terminal.
|
||||
|
||||
## v0.4.4 - 2023-04-07
|
||||
|
||||
* relinker: suppressing the creation of `__pycache__`
|
||||
* relinker: support same name objects in one library
|
||||
|
||||
## v0.4.3 - 2023-03-24
|
||||
|
||||
* relinker: support decoding to get IRAM excluded libraries
|
||||
|
||||
## v0.4.2 - 2023-03-20
|
||||
|
||||
### Bugfix:
|
||||
|
||||
* gen_compressed_ota: Fix the number of bytes reserved in the v3 compressed image header
|
||||
* gen_compressed_ota: Fix definition of MD5 length in compressed image header for different versions.
|
||||
|
||||
## v0.4.1 - 2023-03-15
|
||||
|
||||
* relinker: add option to use customized configuration files
|
||||
* relinker: add option to print error information instead of throwing exception when missing function
|
||||
* relinker: move functions of SPI flash from IRAM to flash only when enable CONFIG_SPI_FLASH_ROM_IMPL
|
||||
* relinker: move some functions of esp_timer from IRAM to flash only when disable CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
|
||||
|
||||
## v0.4.0 - 2023-03-13
|
||||
|
||||
### Feature:
|
||||
|
||||
* Add command `idf.py gen_single_bin` to generate merged bin file
|
||||
* Add command `idf.py flash_single_bin` to flash generated merged bin
|
||||
* Add config `Color in diagnostics` to control the GCC color output
|
||||
|
||||
## v0.3.0 - 2023-03-10
|
||||
|
||||
* Add gen_compressed_ota functionality, please refer to [gen_compressed_ota.md](https://github.com/espressif/esp-iot-solution/tree/master/tools/cmake_utilities/docs/gen_compressed_ota.md)
|
||||
|
||||
## v0.2.1 - 2023-03-09
|
||||
|
||||
### Bugfix:
|
||||
|
||||
* package manager: Fix the compile issue when the name of the component has `-`, just like esp-xxx
|
||||
|
||||
## v0.2.0 - 2023-02-23
|
||||
|
||||
* Add relinker functionality, please refer to [relinker.md](https://github.com/espressif/esp-iot-solution/tree/master/tools/cmake_utilities/docs/relinker.md)
|
||||
|
||||
## v0.1.0 - 2023-01-12
|
||||
|
||||
### Feature:
|
||||
|
||||
* Add function cu_pkg_get_version
|
||||
* Add macro cu_pkg_define_version
|
||||
* Add cmake script to CMAKE_MODULE_PATH
|
||||
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-05-21T16:40:48.657779+00:00", "files": [{"path": "package_manager.cmake", "size": 2125, "hash": "1e7f6ba9cdcc8fc9f0a7dde5c5e1ec03ef699c210caf381181cc4a35f95f0ca1"}, {"path": "CMakeLists.txt", "size": 25, "hash": "1b07b2a81841a6e12fc589126fca551dfa39cd6db0646f802740972b48a2cf1b"}, {"path": "CHANGELOG.md", "size": 2592, "hash": "4779bc99678035cbd48addd07e6c5246213d7a93fb34ec469eb838fb64840c63"}, {"path": "idf_component.yml", "size": 255, "hash": "8c21ccd7648224f955d0f8e694fca3e5c469db87ceea389ebbf66b6e5c5da239"}, {"path": "Kconfig", "size": 2524, "hash": "e776e604c4395f8aa8d869a363d042e08eeea372887ff0e2b57535455f1bf3c0"}, {"path": "README.md", "size": 1340, "hash": "26229d274cc1e1add77cfe70eab0997059458894aff066ee2e227bf2c1fd9b7e"}, {"path": "project_include.cmake", "size": 354, "hash": "ca2f4fbba0ae2524cabe17844a79021d587cce6ec5fef53b36c21ae4d7257972"}, {"path": "gen_single_bin.cmake", "size": 1104, "hash": "34bc2100e339538edc47e86aa63cbeb7bc98c06a93105027a4603ba7f96b1dc2"}, {"path": "relinker.cmake", "size": 3288, "hash": "52ac755b429ee9cf6a45fd22121485d69f883d56c3b9f5d8eaa325156a070ad3"}, {"path": "license.txt", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "cmake_utilities.cmake", "size": 137, "hash": "307c1feb59598ff2351967009f1c6424820178b1da6903f40cc01dd52eb703ef"}, {"path": "gen_compressed_ota.cmake", "size": 727, "hash": "8309efee996bd2e5da0f55d26a0df1d199efb9c562eba07e410d49744401659a"}, {"path": "gcc.cmake", "size": 3585, "hash": "47674577242d2be68d6bc1c71bbe664a7e1d0047170ee1fe78f71b987c810620"}, {"path": "test_apps/CMakeLists.txt", "size": 374, "hash": "a851b56d24811c45175a960701596367c9a575b34a6fa8b7ab01bc2c1334c129"}, {"path": "test_apps/pytest_cmake_utilities.py", "size": 583, "hash": "5f60d02cdcaed3503bc259c51cff9af0c267887c86be825c097ed8f57a6a13fa"}, {"path": "docs/gen_compressed_ota.md", "size": 1620, "hash": "c7779477fb4989379898ad18b70af7738ff2bffe624e5d549a3b776aa983ca0b"}, {"path": "docs/gcc.md", "size": 3494, "hash": "908f5108283c7e3d0a046711ab8215fe6ccd59f09924d55cef0bbaf6a5a4ff3a"}, {"path": "docs/relinker.md", "size": 2963, "hash": "0cbffe09354d7e66ac05cf6de02bcdcf76dd2db69072e998e15ee268a5dd2384"}, {"path": "scripts/gen_custom_ota.py", "size": 13418, "hash": "64822db67089a6fdcde610d5ec31369aaa62441441f09d2ed8d4aae2a396e353"}, {"path": "scripts/relinker/configuration.py", "size": 6406, "hash": "7f5c80c0dd3504bcab89b585786b805544a8e39b17b59fc0ef911cb0813fba09"}, {"path": "scripts/relinker/relinker.py", "size": 9978, "hash": "07c1ec82d9f1843d3a12ade88295610ab78cc21c2dd90b02fe314d88d9085eac"}, {"path": "scripts/relinker/examples/esp32c2/object.csv", "size": 6783, "hash": "761467fbbe3adbf544adcfaa50b2fca1a79208786070784b000961c43a23744d"}, {"path": "scripts/relinker/examples/esp32c2/library.csv", "size": 1156, "hash": "5ca43f2ded3f5b3e1d1398949c86080ac1ff7d06e7d147dc5ecf21d0ec034e61"}, {"path": "scripts/relinker/examples/esp32c2/function.csv", "size": 22369, "hash": "5d608e6d35d7a2a6f5ac317ea5e993443b5baef0ce788eb2464aca37246bcb29"}, {"path": "test_apps/main/CMakeLists.txt", "size": 157, "hash": "1b582805eb1d7d515bac70643308b0bacf18dccee565df745051267635b09c1a"}, {"path": "test_apps/main/test_cmake_utilities.c", "size": 1848, "hash": "6ff7025750c8c59bbd379af839def4427c6f59149e19885ac674740e45eccdac"}, {"path": "test_apps/components/TEST-component2/CMakeLists.txt", "size": 285, "hash": "441a6b60573d3146386d56ccbf12cf4451eeef01cac2905b18259bb2a1912b64"}, {"path": "test_apps/components/TEST-component2/idf_component.yml", "size": 230, "hash": "dbbd16387c7b0ea7378126e87cafdf8b3bc58fdcf472e7d853b38ea24d813a2b"}, {"path": "test_apps/components/TEST-component2/test_component2.h", "size": 213, "hash": "42a6a9a527a8b78a6373a432a1d2c8c540bae91d7c604bbc12be5f709a16fb37"}, {"path": "test_apps/components/TEST-component2/test_component2.c", "size": 256, "hash": "d949b85dd1ce045b724cd463ac6549b372db9935ed07dc3dad250660d3b3f27f"}, {"path": "test_apps/components/test_component1/CMakeLists.txt", "size": 285, "hash": "441a6b60573d3146386d56ccbf12cf4451eeef01cac2905b18259bb2a1912b64"}, {"path": "test_apps/components/test_component1/test_component1.h", "size": 213, "hash": "e77976253127f05cbbfae7a635c9455d584b66cceae69ef8f8465fd4ce85aadf"}, {"path": "test_apps/components/test_component1/idf_component.yml", "size": 230, "hash": "70631b0633adbaa92bb26785dee0369f889cc93bc9a22635f1553f864522607b"}, {"path": "test_apps/components/test_component1/test_component1.c", "size": 256, "hash": "fe9ce9a01d53cfbc1970f0a9ee9c8d382c399e0c9aeb93ce843af01bd70b5411"}]}
|
||||
@@ -0,0 +1 @@
|
||||
idf_component_register()
|
||||
@@ -0,0 +1,65 @@
|
||||
menu "CMake Utilities"
|
||||
|
||||
config CU_RELINKER_ENABLE
|
||||
bool "Enable relinker"
|
||||
default n
|
||||
help
|
||||
"Enable relinker to linker some IRAM functions to Flash"
|
||||
|
||||
if CU_RELINKER_ENABLE
|
||||
config CU_RELINKER_ENABLE_PRINT_ERROR_INFO_WHEN_MISSING_FUNCTION
|
||||
bool "Print error information when missing function"
|
||||
default y
|
||||
help
|
||||
"Enable this option to print error information instead of
|
||||
throwing exception when missing function"
|
||||
|
||||
config CU_RELINKER_ENABLE_CUSTOMIZED_CONFIGURATION_FILES
|
||||
bool "Enable customized relinker configuration files"
|
||||
default n
|
||||
help
|
||||
"Enable this option to use customized relinker configuration
|
||||
files instead of default ones"
|
||||
|
||||
if CU_RELINKER_ENABLE_CUSTOMIZED_CONFIGURATION_FILES
|
||||
config CU_RELINKER_CUSTOMIZED_CONFIGURATION_FILES_PATH
|
||||
string "Customized relinker configuration files path"
|
||||
default ""
|
||||
help
|
||||
"Customized relinker configuration files path. This path is
|
||||
evaluated relative to the project root directory."
|
||||
endif
|
||||
endif
|
||||
|
||||
choice CU_DIAGNOSTICS_COLOR
|
||||
prompt "Color in diagnostics"
|
||||
default CU_DIAGNOSTICS_COLOR_ALWAYS
|
||||
help
|
||||
Use color in diagnostics. "never", "always", or "auto". If "always", GCC will output
|
||||
with color defined in GCC_COLORS environment variable. If "never", only output plain
|
||||
text. If "auto", only output with color when the standard error is a terminal and when
|
||||
not executing in an emacs shell.
|
||||
|
||||
config CU_DIAGNOSTICS_COLOR_NEVER
|
||||
bool "never"
|
||||
config CU_DIAGNOSTICS_COLOR_ALWAYS
|
||||
bool "always"
|
||||
config CU_DIAGNOSTICS_COLOR_AUTO
|
||||
bool "auto"
|
||||
endchoice
|
||||
|
||||
config CU_GCC_LTO_ENABLE
|
||||
bool "Enable GCC link time optimization(LTO)"
|
||||
default n
|
||||
help
|
||||
"Enable this option, users can enable GCC link time optimization(LTO)
|
||||
feature for target components or dependencies.
|
||||
|
||||
config CU_GCC_STRING_1BYTE_ALIGN
|
||||
bool "GCC string 1-byte align"
|
||||
default n
|
||||
help
|
||||
"Enable this option, user can make string in designated components align
|
||||
by 1-byte instead 1-word(4-byte), this can reduce unnecessary filled data
|
||||
so that to reduce firmware size."
|
||||
endmenu
|
||||
@@ -0,0 +1,31 @@
|
||||
# Cmake utilities
|
||||
|
||||
[](https://components.espressif.com/components/espressif/cmake_utilities)
|
||||
|
||||
This component is aiming to provide some useful CMake utilities outside of ESP-IDF.
|
||||
|
||||
## Use
|
||||
|
||||
1. Add dependency of this component in your component or project's idf_component.yml.
|
||||
|
||||
```yml
|
||||
dependencies:
|
||||
espressif/cmake_utilities: "0.*"
|
||||
```
|
||||
|
||||
2. Include the CMake file you need in your component's CMakeLists.txt after `idf_component_register`, or in your project's CMakeLists.txt
|
||||
|
||||
```cmake
|
||||
// Note: should remove .cmake postfix when using include(), otherwise the requested file will not found
|
||||
// Note: should place this line after `idf_component_register` function
|
||||
// only include the one you needed.
|
||||
include(package_manager)
|
||||
```
|
||||
|
||||
3. Then you can use the corresponding CMake function which is provided by the CMake file.
|
||||
|
||||
## Supported features
|
||||
|
||||
1. [relinker](https://github.com/espressif/esp-iot-solution/blob/master/tools/cmake_utilities/docs/relinker.md)
|
||||
2. [gen_compressed_ota](https://github.com/espressif/esp-iot-solution/blob/master/tools/cmake_utilities/docs/gen_compressed_ota.md)
|
||||
3. [GCC Optimization](https://github.com/espressif/esp-iot-solution/blob/master/tools/cmake_utilities/docs/gcc.md)
|
||||
@@ -0,0 +1,7 @@
|
||||
# Include all cmake modules
|
||||
|
||||
include(gcc)
|
||||
include(gen_compressed_ota)
|
||||
include(gen_single_bin)
|
||||
include(package_manager)
|
||||
include(relinker)
|
||||
@@ -0,0 +1,108 @@
|
||||
# Link Time Optimization(LTO)
|
||||
|
||||
Link time optimization(LTO) improves the optimization effect of GCC, such as reducing binary size, increasing performance, and so on. For more details please refer to related [GCC documents](https://gcc.gnu.org/onlinedocs/gccint/LTO.html).
|
||||
|
||||
## Use
|
||||
|
||||
To use this feature, you need to include the required CMake file in your project's CMakeLists.txt after `project(XXXX)`.
|
||||
|
||||
```cmake
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
project(XXXX)
|
||||
|
||||
include(gcc)
|
||||
```
|
||||
|
||||
The LTO feature is disabled by default. To use it, you should enable the option `CU_GCC_LTO_ENABLE` in menuconfig. Then specify target components or dependencies to be optimized by LTO after `include(gcc)` as follows:
|
||||
|
||||
```cmake
|
||||
include(gcc)
|
||||
|
||||
cu_gcc_lto_set(COMPONENTS component_a component_b
|
||||
DEPENDS dependence_a dependence_b)
|
||||
|
||||
cu_gcc_string_1byte_align(COMPONENTS component_c component_d
|
||||
DEPENDS dependence_c dependence_d)
|
||||
```
|
||||
|
||||
Based on your requirement, set compiling optimization level in the option `COMPILER_OPTIMIZATION`.
|
||||
|
||||
* Note
|
||||
|
||||
```
|
||||
1. Reducing firmware size may decrease performance
|
||||
2. Increasing performance may increase firmware size
|
||||
3. Enable LTO cause compiling time cost increases a lot
|
||||
4. Enable LTO may increase task stack cost
|
||||
5. Enable string 1-byte align may decrease string process speed
|
||||
```
|
||||
|
||||
## Limitation
|
||||
|
||||
At the linking stage, the LTO generates new function indexes instead of the file path as follows:
|
||||
|
||||
- LTO
|
||||
|
||||
```txt
|
||||
.text 0x00000000420016f4 0x6 /tmp/ccdjwYMH.ltrans51.ltrans.o
|
||||
0x00000000420016f4 app_main
|
||||
```
|
||||
|
||||
- Without LTO
|
||||
|
||||
```txt
|
||||
.text.app_main 0x00000000420016f4 0x6 esp-idf/main/libmain.a(app_main.c.obj)
|
||||
0x00000000420016f4 app_main
|
||||
```
|
||||
|
||||
So tools used to relink functions between flash and IRAM can't affect these optimized components and dependencies again. It is recommended that users had better optimize application components and dependencies than kernel and hardware driver ones.
|
||||
|
||||
## Example
|
||||
|
||||
The example applies LTO in `light` of `esp-matter` because its application code is much larger. Add LTO configuration into project script `CMakeLists.txt` as follows:
|
||||
|
||||
```cmake
|
||||
|
||||
project(light)
|
||||
|
||||
include(gcc)
|
||||
|
||||
# Add
|
||||
set(app_lto_components main chip esp_matter)
|
||||
# Add
|
||||
set(idf_lto_components lwip wpa_supplicant nvs_flash)
|
||||
# Add
|
||||
set(lto_depends mbedcrypto)
|
||||
|
||||
# Add
|
||||
cu_gcc_lto_set(COMPONENTS ${app_lto_components} ${idf_lto_components}
|
||||
DEPENDS ${lto_depends})
|
||||
```
|
||||
|
||||
Configure `ESP32-C2` as the target platform, enable `CU_GCC_LTO_ENABLE` and `CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE`, set `COMPILER_OPTIMIZATION` to be `-Os`.
|
||||
Increase the `main` task stack size to `5120` by option `ESP_MAIN_TASK_STACK_SIZE`.
|
||||
Compile the project, and then you can see the firmware size decrease a lot:
|
||||
|
||||
Option | Firmware size | Stask cost
|
||||
|:-:|:-:|:-:|
|
||||
-Os | 1,113,376 | 2508
|
||||
-Os + LTO | 1,020,640 | 4204
|
||||
|
||||
Then add `cu_gcc_string_1byte_align` after `cu_gcc_lto_set`:
|
||||
|
||||
```cmake
|
||||
# Add
|
||||
cu_gcc_lto_set(COMPONENTS ${app_lto_components} ${idf_lto_components}
|
||||
DEPENDS ${lto_depends})
|
||||
|
||||
cu_gcc_string_1byte_align(COMPONENTS ${app_lto_components} ${idf_lto_components}
|
||||
DEPENDS ${lto_depends})
|
||||
```
|
||||
|
||||
Build the project and the firmware size is:
|
||||
|
||||
Option | Firmware size |
|
||||
|:-:|:-:|
|
||||
-Os + LTO | 1,020,640 |
|
||||
-Os + LTO + string 1-byte align | 1,018,340 |
|
||||
@@ -0,0 +1,41 @@
|
||||
# Gen Compressed OTA
|
||||
|
||||
When using the compressed OTA, we need to generate the compressed app firmware. This document mainly describes how to generate the compressed app firmware.
|
||||
|
||||
For more information about compressed OTA, refer to [bootloader_support_plus](https://github.com/espressif/esp-iot-solution/tree/master/components/bootloader_support_plus).
|
||||
|
||||
## Use
|
||||
In order to use this feature, you need to include the needed CMake file in your project's CMakeLists.txt after `project(XXXX)`.
|
||||
|
||||
```cmake
|
||||
project(XXXX)
|
||||
|
||||
include(gen_compressed_ota)
|
||||
```
|
||||
|
||||
Generate the compressed app firmware in an ESP-IDF "project" directory by running:
|
||||
|
||||
```plaintext
|
||||
idf.py gen_compressed_ota
|
||||
```
|
||||
|
||||
This command will compile your project first, then it will generate the compressed app firmware. For example, run the command under the project `simple_ota_examples` folder. If there are no errors, the `custom_ota_binaries` folder will be created and contains the following files:
|
||||
|
||||
```plaintext
|
||||
simple_ota.bin.xz
|
||||
simple_ota.bin.xz.packed
|
||||
```
|
||||
|
||||
The file named `simple_ota.bin.xz.packed` is the actual compressed app binary file to be transferred.
|
||||
|
||||
In addition, if [secure boot](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/security/secure-boot-v2.html) is enabled, the command will generate the signed compressed app binary file:
|
||||
|
||||
```plaintext
|
||||
simple_ota.bin.xz.packed.signed
|
||||
```
|
||||
|
||||
you can also use the script [gen_custom_ota.py](https://github.com/espressif/esp-iot-solution/tree/master/tools/cmake_utilities/scripts/gen_custom_ota.py) to compress the specified app:
|
||||
|
||||
```plaintext
|
||||
python3 gen_custom_ota.py -i simple_ota.bin
|
||||
```
|
||||
@@ -0,0 +1,66 @@
|
||||
# Relinker
|
||||
|
||||
In ESP-IDF, some functions are put in SRAM when link stage, the reason is that some functions are critical, we need to put them in SRAM to speed up the program, or the functions will be executed when the cache is disabled. But actually, some functions can be put into Flash, here, we provide a script to let the user set the functions which are located in SRAM by default to put them into Flash, in order to save more SRAM which can be used as heap region later. This happens in the linker stage, so we call it as relinker.
|
||||
|
||||
## Use
|
||||
|
||||
In order to use this feature, you need to include the needed CMake file in your project's CMakeLists.txt after `project(XXXX)`.
|
||||
|
||||
```cmake
|
||||
project(XXXX)
|
||||
|
||||
include(relinker)
|
||||
```
|
||||
|
||||
The relinker feature is disabled by default, in order to use it, you need to enable the option `CU_RELINKER_ENABLE` in menuconfig.
|
||||
|
||||
Here are the default configuration files in the folder `cmake_utilities/scripts/relinker/examples/esp32c2`, it's just used as a reference. If you would like to use your own configuration files, please enable option `CU_RELINKER_ENABLE_CUSTOMIZED_CONFIGURATION_FILES` and set the path of your configuration files as following, this path is evaluated relative to the project root directory:
|
||||
|
||||
```
|
||||
[*] Enable customized relinker configuration files
|
||||
(path of your configuration files) Customized relinker configuration files path
|
||||
```
|
||||
|
||||
> Note: Currently only esp32c2 is supported.
|
||||
|
||||
## Configuration Files
|
||||
|
||||
You can refer to the files in the directory of `cmake_utilities/scripts/relinker/examples/esp32c2`:
|
||||
|
||||
- library.csv
|
||||
- object.csv
|
||||
- function.csv
|
||||
|
||||
For example, if you want to link function `__getreent` from SRAM to Flash, firstly you should add it to `function.csv` file as following:
|
||||
|
||||
```
|
||||
libfreertos.a,tasks.c.obj,__getreent,
|
||||
```
|
||||
|
||||
This means function `__getreent` is in object file `tasks.c.obj`, and object file `tasks.c.obj` is in library `libfreertos.a`.
|
||||
|
||||
If function `__getreent` depends on the option `FREERTOS_PLACE_FUNCTIONS_INTO_FLASH` in menuconfig, then it should be:
|
||||
|
||||
```
|
||||
libfreertos.a,tasks.c.obj,__getreent,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
```
|
||||
|
||||
This means when only `FREERTOS_PLACE_FUNCTIONS_INTO_FLASH` is enabled in menuconfig, function `__getreent` will be linked from SRAM to Flash.
|
||||
|
||||
Next step you should add the path of the object file to `object.csv`:
|
||||
|
||||
```
|
||||
libfreertos.a,tasks.c.obj,esp-idf/freertos/CMakeFiles/__idf_freertos.dir/FreeRTOS-Kernel/tasks.c.obj
|
||||
```
|
||||
|
||||
This means the object file `tasks.c.obj` is in library `libfreertos.a` and its location is `esp-idf/freertos/CMakeFiles/__idf_freertos.dir/FreeRTOS-Kernel/tasks.c.obj` relative to directory of `build`.
|
||||
|
||||
Next step you should add path of library to `library.csv`:
|
||||
|
||||
```
|
||||
libfreertos.a,./esp-idf/freertos/libfreertos.a
|
||||
```
|
||||
|
||||
This means library `libfreertos.a`'s location is `./esp-idf/freertos/libfreertos.a` relative to `build`.
|
||||
|
||||
If above related data has exists in corresponding files, please don't add this repeatedly.
|
||||
@@ -0,0 +1,84 @@
|
||||
if(CONFIG_CU_GCC_LTO_ENABLE)
|
||||
# Enable cmake interprocedural optimization(IPO) support to check if GCC supports link time optimization(LTO)
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
include(CheckIPOSupported)
|
||||
|
||||
# Compare to "ar" and "ranlib", "gcc-ar" and "gcc-ranlib" integrate GCC LTO plugin
|
||||
set(CMAKE_AR ${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar)
|
||||
set(CMAKE_RANLIB ${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib)
|
||||
|
||||
macro(cu_gcc_lto_set)
|
||||
check_ipo_supported(RESULT result)
|
||||
if(result)
|
||||
message(STATUS "GCC link time optimization(LTO) is enable")
|
||||
|
||||
set(multi_value COMPONENTS DEPENDS)
|
||||
cmake_parse_arguments(LTO "" "" "${multi_value}" ${ARGN})
|
||||
|
||||
# Use full format LTO object file
|
||||
set(GCC_LTO_OBJECT_TYPE "-ffat-lto-objects")
|
||||
# Set compression level 9(min:0, max:9)
|
||||
set(GCC_LTO_COMPRESSION_LEVEL "-flto-compression-level=9")
|
||||
# Set partition level max to removed used symbol
|
||||
set(GCC_LTO_PARTITION_LEVEL "-flto-partition=max")
|
||||
|
||||
# Set mode "auto" to increase compiling speed
|
||||
set(GCC_LTO_COMPILE_OPTIONS "-flto=auto"
|
||||
${GCC_LTO_OBJECT_TYPE}
|
||||
${GCC_LTO_COMPRESSION_LEVEL})
|
||||
|
||||
# Enable GCC LTO and plugin when linking stage
|
||||
set(GCC_LTO_LINK_OPTIONS "-flto"
|
||||
"-fuse-linker-plugin"
|
||||
${GCC_LTO_OBJECT_TYPE}
|
||||
${GCC_LTO_PARTITION_LEVEL})
|
||||
|
||||
message(STATUS "GCC LTO for components: ${LTO_COMPONENTS}")
|
||||
foreach(c ${LTO_COMPONENTS})
|
||||
idf_component_get_property(t ${c} COMPONENT_LIB)
|
||||
target_compile_options(${t} PRIVATE ${GCC_LTO_COMPILE_OPTIONS})
|
||||
endforeach()
|
||||
|
||||
message(STATUS "GCC LTO for dependencies: ${LTO_DEPENDS}")
|
||||
foreach(d ${LTO_DEPENDS})
|
||||
target_compile_options(${d} PRIVATE ${GCC_LTO_COMPILE_OPTIONS})
|
||||
endforeach()
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "4.4")
|
||||
target_link_libraries(${project_elf} PRIVATE ${GCC_LTO_LINK_OPTIONS})
|
||||
else()
|
||||
target_link_libraries(${project_elf} ${GCC_LTO_LINK_OPTIONS})
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "GCC link time optimization(LTO) is not supported")
|
||||
endif()
|
||||
endmacro()
|
||||
else()
|
||||
macro(cu_gcc_lto_set)
|
||||
message(STATUS "GCC link time optimization(LTO) is not enable")
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
if(CONFIG_CU_GCC_STRING_1BYTE_ALIGN)
|
||||
macro(cu_gcc_string_1byte_align)
|
||||
message(STATUS "GCC string 1-byte align is enable")
|
||||
|
||||
set(multi_value COMPONENTS DEPENDS)
|
||||
cmake_parse_arguments(STR_ALIGN "" "" "${multi_value}" ${ARGN})
|
||||
|
||||
message(STATUS "GCC string 1-byte align for components: ${STR_ALIGN_COMPONENTS}")
|
||||
foreach(c ${STR_ALIGN_COMPONENTS})
|
||||
idf_component_get_property(t ${c} COMPONENT_LIB)
|
||||
target_compile_options(${t} PRIVATE "-malign-data=natural")
|
||||
endforeach()
|
||||
|
||||
message(STATUS "GCC string 1-byte align for dependencies: ${STR_ALIGN_DEPENDS}")
|
||||
foreach(d ${STR_ALIGN_DEPENDS})
|
||||
target_compile_options(${d} PRIVATE "-malign-data=natural")
|
||||
endforeach()
|
||||
endmacro()
|
||||
else()
|
||||
macro(cu_gcc_string_1byte_align)
|
||||
message(STATUS "GCC string 1-byte align is not enable")
|
||||
endmacro()
|
||||
endif()
|
||||
@@ -0,0 +1,18 @@
|
||||
if (NOT TARGET gen_compressed_ota)
|
||||
add_custom_target(gen_compressed_ota)
|
||||
|
||||
if (CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT OR CONFIG_SECURE_BOOT_V2_ENABLED)
|
||||
set(COMPRESSED_OTA_BIN_SIGN_PARA --sign_key ${PROJECT_DIR}/${CONFIG_SECURE_BOOT_SIGNING_KEY})
|
||||
else()
|
||||
set(COMPRESSED_OTA_BIN_SIGN_PARA )
|
||||
endif()
|
||||
|
||||
set(GEN_COMPRESSED_BIN_CMD ${CMAKE_CURRENT_LIST_DIR}/scripts/gen_custom_ota.py ${COMPRESSED_OTA_BIN_SIGN_PARA} --add_app_header)
|
||||
|
||||
add_custom_command(TARGET gen_compressed_ota
|
||||
POST_BUILD
|
||||
COMMAND ${PYTHON} ${GEN_COMPRESSED_BIN_CMD}
|
||||
COMMENT "The gen compresssed bin cmd is: ${GEN_COMPRESSED_BIN_CMD}"
|
||||
)
|
||||
add_dependencies(gen_compressed_ota gen_project_binary)
|
||||
endif()
|
||||
@@ -0,0 +1,27 @@
|
||||
# Extend command to idf.py
|
||||
|
||||
# Generate single bin with name ${CMAKE_PROJECT_NAME}_merged.bin
|
||||
if (NOT TARGET gen_single_bin)
|
||||
add_custom_target(
|
||||
gen_single_bin
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Merge bin files to ${CMAKE_PROJECT_NAME}_merged.bin"
|
||||
COMMAND ${ESPTOOLPY} --chip ${IDF_TARGET} merge_bin -o ${CMAKE_PROJECT_NAME}_merged.bin @flash_args
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Merge bin done"
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
DEPENDS gen_project_binary bootloader
|
||||
VERBATIM USES_TERMINAL
|
||||
)
|
||||
endif()
|
||||
|
||||
# Flash bin ${CMAKE_PROJECT_NAME}_merged.bin to target chip
|
||||
if (NOT TARGET flash_single_bin)
|
||||
add_custom_target(
|
||||
flash_single_bin
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Flash merged bin ${CMAKE_PROJECT_NAME}_merged.bin to address 0x0"
|
||||
COMMAND ${ESPTOOLPY} --chip ${IDF_TARGET} write_flash 0x0 ${CMAKE_PROJECT_NAME}_merged.bin
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Flash merged bin done"
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
DEPENDS gen_single_bin
|
||||
VERBATIM USES_TERMINAL
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,7 @@
|
||||
dependencies:
|
||||
idf:
|
||||
version: '>=4.1'
|
||||
description: A collection of useful cmake utilities
|
||||
issues: https://github.com/espressif/esp-iot-solution/issues
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/tools/cmake_utilities
|
||||
version: 0.5.3
|
||||
@@ -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.
|
||||
@@ -0,0 +1,45 @@
|
||||
# cu_pkg_get_version
|
||||
#
|
||||
# @brief Get the package's version information, the package is installed by component manager.
|
||||
#
|
||||
# @param[in] pkg_path the package's path, normally it's ${CMAKE_CURRENT_LIST_DIR}.
|
||||
#
|
||||
# @param[out] ver_major the major version of the package
|
||||
# @param[out] ver_minor the minor version of the package
|
||||
# @param[out] ver_patch the patch version of the package
|
||||
function(cu_pkg_get_version pkg_path ver_major ver_minor ver_patch)
|
||||
set(yml_file "${pkg_path}/idf_component.yml")
|
||||
if(EXISTS ${yml_file})
|
||||
file(READ ${yml_file} ver)
|
||||
string(REGEX MATCH "(^|\n)version: \"?([0-9]+).([0-9]+).([0-9]+)\"?" _ ${ver})
|
||||
set(${ver_major} ${CMAKE_MATCH_2} PARENT_SCOPE)
|
||||
set(${ver_minor} ${CMAKE_MATCH_3} PARENT_SCOPE)
|
||||
set(${ver_patch} ${CMAKE_MATCH_4} PARENT_SCOPE)
|
||||
else()
|
||||
message(WARNING " ${yml_file} not exist")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# cu_pkg_define_version
|
||||
#
|
||||
# @brief Add the package's version definitions using format ${NAME}_VER_MAJOR ${NAME}_VER_MINOR ${NAME}_VER_PATCH,
|
||||
# the ${NAME} will be inferred from package path, and namespace like `espressif__` will be removed if the package download from esp-registry
|
||||
# eg. espressif__usb_stream and usb_stream will generate same version definitions USB_STREAM_VER_MAJOR ...
|
||||
#
|
||||
# @param[in] pkg_path the package's path, normally it's ${CMAKE_CURRENT_LIST_DIR}.
|
||||
#
|
||||
macro(cu_pkg_define_version pkg_path)
|
||||
cu_pkg_get_version(${pkg_path} ver_major ver_minor ver_patch)
|
||||
get_filename_component(pkg_name ${pkg_path} NAME)
|
||||
string(FIND ${pkg_name} "__" pkg_name_pos)
|
||||
if(pkg_name_pos GREATER -1)
|
||||
math(EXPR pkg_name_pos "${pkg_name_pos} + 2")
|
||||
string(SUBSTRING ${pkg_name} ${pkg_name_pos} -1 pkg_name)
|
||||
endif()
|
||||
string(TOUPPER ${pkg_name} pkg_name)
|
||||
string(REPLACE "-" "_" pkg_name ${pkg_name})
|
||||
message(STATUS "${pkg_name}: ${ver_major}.${ver_minor}.${ver_patch}")
|
||||
list(LENGTH pkg_name_list len)
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC
|
||||
-D${pkg_name}_VER_MAJOR=${ver_major} -D${pkg_name}_VER_MINOR=${ver_minor} -D${pkg_name}_VER_PATCH=${ver_patch})
|
||||
endmacro()
|
||||
@@ -0,0 +1,9 @@
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_MODULE_PATH})
|
||||
|
||||
if(CONFIG_CU_DIAGNOSTICS_COLOR_ALWAYS)
|
||||
add_compile_options(-fdiagnostics-color=always)
|
||||
elseif(CONFIG_CU_DIAGNOSTICS_COLOR_AUTO)
|
||||
add_compile_options(-fdiagnostics-color=auto)
|
||||
elseif(CONFIG_CU_DIAGNOSTICS_COLOR_NEVER)
|
||||
add_compile_options(-fdiagnostics-color=never)
|
||||
endif()
|
||||
@@ -0,0 +1,73 @@
|
||||
# @brief Link designated functions from SRAM to Flash to save SRAM
|
||||
if(CONFIG_CU_RELINKER_ENABLE)
|
||||
# project_elf variable is only in project.cmake
|
||||
if(NOT TARGET customer_sections AND DEFINED project_elf)
|
||||
message(STATUS "Relinker is enabled.")
|
||||
if(CONFIG_IDF_TARGET_ESP32C2)
|
||||
set(target "esp32c2")
|
||||
else()
|
||||
message(FATAL_ERROR "Other targets are not supported.")
|
||||
endif()
|
||||
|
||||
if(CONFIG_CU_RELINKER_ENABLE_CUSTOMIZED_CONFIGURATION_FILES)
|
||||
idf_build_get_property(project_dir PROJECT_DIR)
|
||||
get_filename_component(cfg_file_path "${CONFIG_CU_RELINKER_CUSTOMIZED_CONFIGURATION_FILES_PATH}"
|
||||
ABSOLUTE BASE_DIR "${project_dir}")
|
||||
|
||||
if(NOT EXISTS "${cfg_file_path}")
|
||||
message(FATAL_ERROR "Relinker Configuration files path ${cfg_file_path} is not found.")
|
||||
endif()
|
||||
else()
|
||||
set(cfg_file_path ${PROJECT_DIR}/relinker/${target})
|
||||
if(NOT EXISTS ${cfg_file_path})
|
||||
set(cfg_file_path ${CMAKE_CURRENT_LIST_DIR}/scripts/relinker/examples/${target})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Relinker configuration files: ${cfg_file_path}")
|
||||
|
||||
set(library_file "${cfg_file_path}/library.csv")
|
||||
set(object_file "${cfg_file_path}/object.csv")
|
||||
set(function_file "${cfg_file_path}/function.csv")
|
||||
set(relinker_script "${CMAKE_CURRENT_LIST_DIR}/scripts/relinker/relinker.py")
|
||||
|
||||
set(cmake_objdump "${CMAKE_OBJDUMP}")
|
||||
set(link_path "${CMAKE_BINARY_DIR}/esp-idf/esp_system/ld")
|
||||
set(link_src_file "${link_path}/sections.ld")
|
||||
set(link_dst_file "${link_path}/customer_sections.ld")
|
||||
|
||||
set(relinker_opts --input ${link_src_file}
|
||||
--output ${link_dst_file}
|
||||
--library ${library_file}
|
||||
--object ${object_file}
|
||||
--function ${function_file}
|
||||
--sdkconfig ${sdkconfig}
|
||||
--objdump ${cmake_objdump})
|
||||
|
||||
if(CONFIG_CU_RELINKER_ENABLE_PRINT_ERROR_INFO_WHEN_MISSING_FUNCTION)
|
||||
list(APPEND relinker_opts --missing_function_info True)
|
||||
endif()
|
||||
|
||||
idf_build_get_property(link_depends __LINK_DEPENDS)
|
||||
|
||||
add_custom_command(OUTPUT ${link_dst_file}
|
||||
COMMAND ${python} -B ${relinker_script}
|
||||
${relinker_opts}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${link_dst_file}
|
||||
${link_src_file}
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
/*relinker*/ >>
|
||||
${link_dst_file}
|
||||
DEPENDS "${link_depends}"
|
||||
"${library_file}"
|
||||
"${object_file}"
|
||||
"${function_file}"
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(customer_sections DEPENDS ${link_dst_file})
|
||||
add_dependencies(${project_elf} customer_sections)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Relinker isn't enabled.")
|
||||
endif()
|
||||
@@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import sys
|
||||
import struct
|
||||
import argparse
|
||||
import binascii
|
||||
import hashlib
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
import json
|
||||
import lzma
|
||||
|
||||
# src_file = 'hello-world.bin'
|
||||
# compressed_file = 'hello-world.bin.xz'
|
||||
# packed_compressed_file = 'hello-world.bin.xz.packed'
|
||||
# siged_packed_compressed_file = 'hello-world.bin.xz.packed.signed'
|
||||
|
||||
binary_compress_type = {'none': 0, 'xz':1}
|
||||
header_version = {'v1': 1, 'v2': 2, 'v3': 3}
|
||||
|
||||
SCRIPT_VERSION = '1.0.0'
|
||||
ORIGIN_APP_IMAGE_HEADER_LEN = 288 # sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t). See esp_app_format.h
|
||||
# At present, we calculate the checksum of the first 4KB data of the old app.
|
||||
OLD_APP_CHECK_DATA_SIZE = 4096
|
||||
|
||||
# v1 compressed data header:
|
||||
# Note: Encryption_type field is deprecated, the field is reserved for compatibility.
|
||||
# |------|---------|---------|----------|------------|------------|-----------|----------|----------------|----------------|--------------|------------|
|
||||
# | | Magic | header | Compress | delta | Encryption | Reserved | Firmware | The length of | The MD5 of | The CRC32 for| compressed |
|
||||
# | | number | version | type | type | type | | version | compressed data| compressed data| the header | data |
|
||||
# |------|---------|---------|----------|------------|------------|-----------|----------|----------------|----------------|--------------|------------|
|
||||
# | Size | 4 bytes | 1 byte | 4 bits | 4 bits | 1 bytes | 1 bytes | 32 bytes | 4 bytes | 32 bytes | 4 bytes | |
|
||||
# |------|---------|---------|----------|------------|------------|-----------|----------|----------------|----------------|--------------|------------|
|
||||
# | Data | String | | | | | | String | little-endian | byte string | little-endian| |
|
||||
# | type | ended | | | | | | ended | integer | | integer | |
|
||||
# | |with ‘\0’| | | | | | with ‘\0’| | | | Binary data|
|
||||
# |------|---------|---------|----------|------------|------------|-----------|----------|----------------|----------------|--------------|------------|
|
||||
# | Data | “ESP” | 1 | 0/1 | 0/1 | | | | | | | |
|
||||
# |------|---------|---------|----------|------------|------------|-----------|----------|----------------|----------------|--------------|------------|
|
||||
|
||||
# v2 compressed data header
|
||||
# Note: Encryption_type field is deprecated, the field is reserved for compatibility.
|
||||
# |------|---------|---------|----------|------------|------------|-----------|----------|----------------|----------------|---------------|---------------|--------------|------------|
|
||||
# | | Magic | header | Compress | delta | Encryption | Reserved | Firmware | The length of | The MD5 of | base app check| The CRC32 for | The CRC32 for| compressed |
|
||||
# | | number | version | type | type | type | | version | compressed data| compressed data| data len | base app data | the header | data |
|
||||
# |------|---------|---------|----------|------------|------------|-----------|----------|----------------|----------------|---------------|---------------|--------------|------------|
|
||||
# | Size | 4 bytes | 1 byte | 4 bits | 4 bits | 1 bytes | 1 bytes | 32 bytes | 4 bytes | 32 bytes | 4 bytes | 4 bytes | 4 bytes | |
|
||||
# |------|---------|---------|----------|------------|------------|-----------|----------|----------------|----------------|---------------|---------------|--------------|------------|
|
||||
# | Data | String | | | | | | String | little-endian | byte string | little-endian | little-endian | little-endian| |
|
||||
# | type | ended | | | | | | ended | integer | | integer | integer | integer | |
|
||||
# | |with ‘\0’| | | | | | with ‘\0’| | | | | | Binary data|
|
||||
# |------|---------|---------|----------|------------|------------|-----------|----------|----------------|----------------|---------------|---------------|--------------|------------|
|
||||
# | Data | “ESP” | 1 | 0/1 | 0/1 | | | | | | | | | |
|
||||
# |------|---------|---------|----------|------------|------------|-----------|----------|----------------|----------------|---------------|---------------|--------------|------------|
|
||||
|
||||
# v3 compressed data header:
|
||||
# |------|---------|---------|----------|------------|-----------|----------------|----------------|--------------|------------|
|
||||
# | | Magic | header | Compress | Reserved | Reserved | The length of | The MD5 of | The CRC32 for| compressed |
|
||||
# | | number | version | type | | | compressed data| compressed data| the header | data |
|
||||
# |------|---------|---------|----------|------------|-----------|----------------|----------------|--------------|------------|
|
||||
# | Size | 4 bytes | 1 byte | 4 bits | 4 bits | 8 bytes | 4 bytes | 16 bytes | 4 bytes | |
|
||||
# |------|---------|---------|----------|------------|-----------|----------------|----------------|--------------|------------|
|
||||
# | Data | String | integer | | | | little-endian | byte string | little-endian| |
|
||||
# | type | ended | | | | | integer | | integer | |
|
||||
# | |with ‘\0’| | | | | | | | Binary data|
|
||||
# |------|---------|---------|----------|------------|-----------|----------------|----------------|--------------|------------|
|
||||
# | Data | “ESP” | 3 | 0/1 | | | | | | |
|
||||
# |------|---------|---------|----------|------------|-----------|----------------|----------------|--------------|------------|
|
||||
|
||||
def xz_compress(store_directory, in_file):
|
||||
compressed_file = ''.join([in_file,'.xz'])
|
||||
if(os.path.exists(compressed_file)):
|
||||
os.remove(compressed_file)
|
||||
|
||||
xz_compressor_filter = [
|
||||
{"id": lzma.FILTER_LZMA2, "preset": 6, "dict_size": 64*1024},
|
||||
]
|
||||
with open(in_file, 'rb') as src_f:
|
||||
data = src_f.read()
|
||||
with lzma.open(compressed_file, "wb", format=lzma.FORMAT_XZ, check=lzma.CHECK_CRC32, filters=xz_compressor_filter) as f:
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
if not os.path.exists(os.path.join(store_directory, os.path.split(compressed_file)[1])):
|
||||
shutil.copy(compressed_file, store_directory)
|
||||
print('copy xz file done')
|
||||
|
||||
def secure_boot_sign(sign_key, in_file, out_file):
|
||||
ret = subprocess.call('espsecure.py sign_data --version 2 --keyfile {} --output {} {}'.format(sign_key, out_file, in_file), shell = True)
|
||||
if ret:
|
||||
raise Exception('sign failed')
|
||||
|
||||
def get_app_name():
|
||||
with open('flasher_args.json') as f:
|
||||
try:
|
||||
flasher_args = json.load(f)
|
||||
return flasher_args['app']['file']
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
return ''
|
||||
|
||||
def get_script_version():
|
||||
return SCRIPT_VERSION
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-hv', '--header_ver', nargs='?', choices = ['v1', 'v2', 'v3'],
|
||||
default='v3', help='the version of the packed file header [default:v3]')
|
||||
parser.add_argument('-c', '--compress_type', nargs= '?', choices = ['none', 'xz'],
|
||||
default='xz', help='compressed type [default:xz]')
|
||||
parser.add_argument('-i', '--in_file', nargs = '?',
|
||||
default='', help='the new app firmware')
|
||||
parser.add_argument('--sign_key', nargs = '?',
|
||||
default='', help='the sign key used for secure boot')
|
||||
parser.add_argument('-fv', '--fw_ver', nargs='?',
|
||||
default='', help='the version of the compressed data(this field is deprecated in v3)')
|
||||
parser.add_argument('--add_app_header', action="store_true", help='add app header to use native esp_ota_* & esp_https_ota_* APIs')
|
||||
parser.add_argument('-v', '--version', action='version', version=get_script_version(), help='the version of the script')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
compress_type = args.compress_type
|
||||
firmware_ver = args.fw_ver
|
||||
src_file = args.in_file
|
||||
sign_key = args.sign_key
|
||||
header_ver = args.header_ver
|
||||
add_app_header = args.add_app_header
|
||||
|
||||
if src_file == '':
|
||||
origin_app_name = get_app_name()
|
||||
if(origin_app_name == ''):
|
||||
print('get origin app name fail')
|
||||
return
|
||||
|
||||
if os.path.exists(origin_app_name):
|
||||
src_file = origin_app_name
|
||||
else:
|
||||
print('origin app.bin not found')
|
||||
return
|
||||
|
||||
print('src file is: {}'.format(src_file))
|
||||
|
||||
# rebuild the cpmpressed_app directroy
|
||||
cpmoressed_app_directory = 'custom_ota_binaries'
|
||||
if os.path.exists(cpmoressed_app_directory):
|
||||
shutil.rmtree(cpmoressed_app_directory)
|
||||
|
||||
os.mkdir(cpmoressed_app_directory)
|
||||
print('The compressed file will store in {}'.format(cpmoressed_app_directory))
|
||||
|
||||
#step1: compress
|
||||
if compress_type == 'xz':
|
||||
xz_compress(cpmoressed_app_directory, os.path.abspath(src_file))
|
||||
|
||||
origin_app_name = os.path.split(src_file)[1]
|
||||
|
||||
compressed_file_name = ''.join([origin_app_name, '.xz'])
|
||||
compressed_file = os.path.join(cpmoressed_app_directory, compressed_file_name)
|
||||
else:
|
||||
compressed_file = ''.join(src_file)
|
||||
|
||||
print('compressed file is: {}'.format(compressed_file))
|
||||
|
||||
#step2: packet the compressed image header
|
||||
with open(src_file, 'rb') as s_f:
|
||||
src_image_header = bytearray(s_f.read(ORIGIN_APP_IMAGE_HEADER_LEN))
|
||||
src_image_header[1] = 0x00
|
||||
|
||||
packed_file = ''.join([compressed_file,'.packed'])
|
||||
with open(compressed_file, 'rb') as src_f:
|
||||
data = src_f.read()
|
||||
f_len = src_f.tell()
|
||||
# magic number
|
||||
bin_data = struct.pack('4s', b'ESP')
|
||||
# header version
|
||||
bin_data += struct.pack('B', header_version[header_ver])
|
||||
# Compress type
|
||||
bin_data += struct.pack('B', binary_compress_type[compress_type])
|
||||
print('compressed type: {}'.format(binary_compress_type[compress_type]))
|
||||
# in header v1/v2, there is a field "Encryption type", this field has been deprecated in v3
|
||||
if (header_version[header_ver] < 3):
|
||||
bin_data += struct.pack('B', 0)
|
||||
# Reserved
|
||||
bin_data += struct.pack('?', 0)
|
||||
# Firmware version
|
||||
bin_data += struct.pack('32s', firmware_ver.encode())
|
||||
else:
|
||||
# Reserved
|
||||
bin_data += struct.pack('10s', b'0')
|
||||
# The length of the compressed data
|
||||
bin_data += struct.pack('<I', f_len)
|
||||
print('compressed data len: {}'.format(f_len))
|
||||
# The MD5 for the compressed data
|
||||
if (header_version[header_ver] < 3):
|
||||
bin_data += struct.pack('32s', hashlib.md5(data).digest())
|
||||
if (header_version[header_ver] == 2):
|
||||
# Todo, if it's diff OTA, write base app check data len
|
||||
bin_data += struct.pack('<I', 0)
|
||||
# Todo, if it's diff OTA, write base app crc32 checksum
|
||||
bin_data += struct.pack('<I', 0)
|
||||
else:
|
||||
bin_data += struct.pack('16s', hashlib.md5(data).digest())
|
||||
# The CRC32 for the header
|
||||
bin_data += struct.pack('<I', binascii.crc32(bin_data, 0x0))
|
||||
|
||||
# write compressed data
|
||||
bin_data += data
|
||||
with open(packed_file, 'wb') as dst_f:
|
||||
# write compressed image header and compressed dada
|
||||
dst_f.write(bin_data)
|
||||
|
||||
print('packed file is: {}'.format(packed_file))
|
||||
|
||||
#step3: if need sign, then sign the packed image
|
||||
if sign_key != '':
|
||||
signed_file = ''.join([packed_file,'.signed'])
|
||||
secure_boot_sign(sign_key, packed_file, signed_file)
|
||||
print('signed_file is: {}'.format(signed_file))
|
||||
else:
|
||||
signed_file = ''.join(packed_file)
|
||||
|
||||
if (header_version[header_ver] == 3) and add_app_header:
|
||||
with open(signed_file, 'rb+') as src_f:
|
||||
packed_data = src_f.read()
|
||||
src_f.seek(0)
|
||||
# write origin app image header
|
||||
src_f.write(src_image_header)
|
||||
# write compressed image header and compressed dada
|
||||
src_f.write(packed_data)
|
||||
print('app image header has been added')
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
sys.exit(2)
|
||||
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
from io import StringIO
|
||||
|
||||
OPT_MIN_LEN = 7
|
||||
|
||||
espidf_objdump = None
|
||||
espidf_missing_function_info = True
|
||||
|
||||
class sdkconfig_c:
|
||||
def __init__(self, path):
|
||||
lines = open(path).read().splitlines()
|
||||
config = dict()
|
||||
for l in lines:
|
||||
if len(l) > OPT_MIN_LEN and l[0] != '#':
|
||||
mo = re.match( r'(.*)=(.*)', l, re.M|re.I)
|
||||
if mo:
|
||||
config[mo.group(1)]=mo.group(2).replace('"', '')
|
||||
self.config = config
|
||||
|
||||
def index(self, i):
|
||||
return self.config[i]
|
||||
|
||||
def check(self, options):
|
||||
options = options.replace(' ', '')
|
||||
if '&&' in options:
|
||||
for i in options.split('&&'):
|
||||
if i[0] == '!':
|
||||
i = i[1:]
|
||||
if i in self.config:
|
||||
return False
|
||||
else:
|
||||
if i not in self.config:
|
||||
return False
|
||||
else:
|
||||
i = options
|
||||
if i[0] == '!':
|
||||
i = i[1:]
|
||||
if i in self.config:
|
||||
return False
|
||||
else:
|
||||
if i not in self.config:
|
||||
return False
|
||||
return True
|
||||
|
||||
class object_c:
|
||||
def read_dump_info(self, pathes):
|
||||
new_env = os.environ.copy()
|
||||
new_env['LC_ALL'] = 'C'
|
||||
dumps = list()
|
||||
print('pathes:', pathes)
|
||||
for path in pathes:
|
||||
try:
|
||||
dump = StringIO(subprocess.check_output([espidf_objdump, '-t', path], env=new_env).decode())
|
||||
dumps.append(dump.readlines())
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError('cmd:%s result:%s'%(e.cmd, e.returncode))
|
||||
return dumps
|
||||
|
||||
def get_func_section(self, dumps, func):
|
||||
for dump in dumps:
|
||||
for l in dump:
|
||||
if ' %s'%(func) in l and '*UND*' not in l:
|
||||
m = re.match(r'(\S*)\s*([glw])\s*([F|O])\s*(\S*)\s*(\S*)\s*(\S*)\s*', l, re.M|re.I)
|
||||
if m and m[6] == func:
|
||||
return m[4].replace('.text.', '')
|
||||
if espidf_missing_function_info:
|
||||
print('%s failed to find section'%(func))
|
||||
return None
|
||||
else:
|
||||
raise RuntimeError('%s failed to find section'%(func))
|
||||
|
||||
def __init__(self, name, pathes, libray):
|
||||
self.name = name
|
||||
self.libray = libray
|
||||
self.funcs = dict()
|
||||
self.pathes = pathes
|
||||
self.dumps = self.read_dump_info(pathes)
|
||||
|
||||
def append(self, func):
|
||||
section = self.get_func_section(self.dumps, func)
|
||||
if section != None:
|
||||
self.funcs[func] = section
|
||||
|
||||
def functions(self):
|
||||
nlist = list()
|
||||
for i in self.funcs:
|
||||
nlist.append(i)
|
||||
return nlist
|
||||
|
||||
def sections(self):
|
||||
nlist = list()
|
||||
for i in self.funcs:
|
||||
nlist.append(self.funcs[i])
|
||||
return nlist
|
||||
|
||||
class library_c:
|
||||
def __init__(self, name, path):
|
||||
self.name = name
|
||||
self.path = path
|
||||
self.objs = dict()
|
||||
|
||||
def append(self, obj, path, func):
|
||||
if obj not in self.objs:
|
||||
self.objs[obj] = object_c(obj, path, self.name)
|
||||
self.objs[obj].append(func)
|
||||
|
||||
class libraries_c:
|
||||
def __init__(self):
|
||||
self.libs = dict()
|
||||
|
||||
def append(self, lib, lib_path, obj, obj_path, func):
|
||||
if lib not in self.libs:
|
||||
self.libs[lib] = library_c(lib, lib_path)
|
||||
self.libs[lib].append(obj, obj_path, func)
|
||||
|
||||
def dump(self):
|
||||
for libname in self.libs:
|
||||
lib = self.libs[libname]
|
||||
for objname in lib.objs:
|
||||
obj = lib.objs[objname]
|
||||
print('%s, %s, %s, %s'%(libname, objname, obj.path, obj.funcs))
|
||||
|
||||
class paths_c:
|
||||
def __init__(self):
|
||||
self.paths = dict()
|
||||
|
||||
def append(self, lib, obj, path):
|
||||
if '$IDF_PATH' in path:
|
||||
path = path.replace('$IDF_PATH', os.environ['IDF_PATH'])
|
||||
|
||||
if lib not in self.paths:
|
||||
self.paths[lib] = dict()
|
||||
if obj not in self.paths[lib]:
|
||||
self.paths[lib][obj] = list()
|
||||
self.paths[lib][obj].append(path)
|
||||
|
||||
def index(self, lib, obj):
|
||||
if lib not in self.paths:
|
||||
return None
|
||||
if '*' in self.paths[lib]:
|
||||
obj = '*'
|
||||
return self.paths[lib][obj]
|
||||
|
||||
def generator(library_file, object_file, function_file, sdkconfig_file, missing_function_info, objdump='riscv32-esp-elf-objdump'):
|
||||
global espidf_objdump, espidf_missing_function_info
|
||||
espidf_objdump = objdump
|
||||
espidf_missing_function_info = missing_function_info
|
||||
|
||||
sdkconfig = sdkconfig_c(sdkconfig_file)
|
||||
|
||||
lib_paths = paths_c()
|
||||
for p in csv.DictReader(open(library_file, 'r')):
|
||||
lib_paths.append(p['library'], '*', p['path'])
|
||||
|
||||
obj_paths = paths_c()
|
||||
for p in csv.DictReader(open(object_file, 'r')):
|
||||
obj_paths.append(p['library'], p['object'], p['path'])
|
||||
|
||||
libraries = libraries_c()
|
||||
for d in csv.DictReader(open(function_file, 'r')):
|
||||
if d['option'] and sdkconfig.check(d['option']) == False:
|
||||
print('skip %s(%s)'%(d['function'], d['option']))
|
||||
continue
|
||||
lib_path = lib_paths.index(d['library'], '*')
|
||||
obj_path = obj_paths.index(d['library'], d['object'])
|
||||
if not obj_path:
|
||||
obj_path = lib_path
|
||||
libraries.append(d['library'], lib_path[0], d['object'], obj_path, d['function'])
|
||||
return libraries
|
||||
|
||||
def main():
|
||||
argparser = argparse.ArgumentParser(description='Libraries management')
|
||||
|
||||
argparser.add_argument(
|
||||
'--library', '-l',
|
||||
help='Library description file',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--object', '-b',
|
||||
help='Object description file',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--function', '-f',
|
||||
help='Function description file',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--sdkconfig', '-s',
|
||||
help='sdkconfig file',
|
||||
type=str)
|
||||
|
||||
args = argparser.parse_args()
|
||||
|
||||
libraries = generator(args.library, args.object, args.function, args.sdkconfig)
|
||||
# libraries.dump()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,365 @@
|
||||
library,object,function,option
|
||||
libble_app.a,ble_hw.c.o,r_ble_hw_resolv_list_get_cur_entry,
|
||||
libble_app.a,ble_ll_adv.c.o,r_ble_ll_adv_set_sched,
|
||||
libble_app.a,ble_ll_adv.c.o,r_ble_ll_adv_sync_pdu_make,
|
||||
libble_app.a,ble_ll_adv.c.o,r_ble_ll_adv_sync_calculate,
|
||||
libble_app.a,ble_ll_conn.c.o,r_ble_ll_conn_is_dev_connected,
|
||||
libble_app.a,ble_ll_ctrl.c.o,r_ble_ll_ctrl_tx_done,
|
||||
libble_app.a,ble_lll_adv.c.o,r_ble_lll_adv_aux_scannable_pdu_payload_len,
|
||||
libble_app.a,ble_lll_adv.c.o,r_ble_lll_adv_halt,
|
||||
libble_app.a,ble_lll_adv.c.o,r_ble_lll_adv_periodic_schedule_next,
|
||||
libble_app.a,ble_lll_conn.c.o,r_ble_lll_conn_cth_flow_free_credit,
|
||||
libble_app.a,ble_lll_conn.c.o,r_ble_lll_conn_update_encryption,
|
||||
libble_app.a,ble_lll_conn.c.o,r_ble_lll_conn_set_slave_flow_control,
|
||||
libble_app.a,ble_lll_conn.c.o,r_ble_lll_init_rx_pkt_isr,
|
||||
libble_app.a,ble_lll_conn.c.o,r_ble_lll_conn_get_rx_mbuf,
|
||||
libble_app.a,ble_lll_rfmgmt.c.o,r_ble_lll_rfmgmt_enable,
|
||||
libble_app.a,ble_lll_rfmgmt.c.o,r_ble_lll_rfmgmt_timer_reschedule,
|
||||
libble_app.a,ble_lll_rfmgmt.c.o,r_ble_lll_rfmgmt_timer_exp,
|
||||
libble_app.a,ble_lll_scan.c.o,r_ble_lll_scan_targeta_is_matched,
|
||||
libble_app.a,ble_lll_scan.c.o,r_ble_lll_scan_rx_isr_on_legacy,
|
||||
libble_app.a,ble_lll_scan.c.o,r_ble_lll_scan_rx_isr_on_aux,
|
||||
libble_app.a,ble_lll_scan.c.o,r_ble_lll_scan_process_rsp_in_isr,
|
||||
libble_app.a,ble_lll_scan.c.o,r_ble_lll_scan_rx_pkt_isr,
|
||||
libble_app.a,ble_lll_sched.c.o,r_ble_lll_sched_execute_check,
|
||||
libble_app.a,ble_lll_sync.c.o,r_ble_lll_sync_event_start_cb,
|
||||
libble_app.a,ble_phy.c.o,r_ble_phy_set_rxhdr,
|
||||
libble_app.a,ble_phy.c.o,r_ble_phy_update_conn_sequence,
|
||||
libble_app.a,ble_phy.c.o,r_ble_phy_set_sequence_mode,
|
||||
libble_app.a,ble_phy.c.o,r_ble_phy_txpower_round,
|
||||
libble_app.a,os_mempool.c.obj,r_os_memblock_put,
|
||||
libbootloader_support.a,bootloader_flash.c.obj,bootloader_read_flash_id,
|
||||
libbootloader_support.a,flash_encrypt.c.obj,esp_flash_encryption_enabled,
|
||||
libbt.a,bt_osi_mem.c.obj,bt_osi_mem_calloc,CONFIG_BT_ENABLED
|
||||
libbt.a,bt_osi_mem.c.obj,bt_osi_mem_malloc,CONFIG_BT_ENABLED
|
||||
libbt.a,bt_osi_mem.c.obj,bt_osi_mem_malloc_internal,CONFIG_BT_ENABLED
|
||||
libbt.a,bt_osi_mem.c.obj,bt_osi_mem_free,CONFIG_BT_ENABLED
|
||||
libbt.a,bt.c.obj,esp_reset_rpa_moudle,CONFIG_BT_ENABLED
|
||||
libbt.a,bt.c.obj,osi_assert_wrapper,CONFIG_BT_ENABLED
|
||||
libbt.a,bt.c.obj,osi_random_wrapper,CONFIG_BT_ENABLED
|
||||
libbt.a,nimble_port.c.obj,nimble_port_run,CONFIG_BT_NIMBLE_ENABLED
|
||||
libbt.a,nimble_port.c.obj,nimble_port_get_dflt_eventq,CONFIG_BT_NIMBLE_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,os_callout_timer_cb,CONFIG_BT_ENABLED && !CONFIG_BT_NIMBLE_USE_ESP_TIMER
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_event_run,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_callout_stop,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_time_get,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_callout_reset,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_callout_is_active,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_callout_get_ticks,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_callout_remaining_ticks,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_time_delay,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_eventq_is_empty,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_mutex_pend,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_mutex_release,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_sem_init,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_sem_pend,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_sem_release,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_callout_init,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_callout_deinit,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_event_is_queued,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_event_get_arg,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_time_ms_to_ticks32,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_time_ticks_to_ms32,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_hw_is_in_critical,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_get_time_forever,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_os_started,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_get_current_task_id,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_event_reset,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_callout_mem_reset,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_event_init,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_sem_get_count,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_eventq_remove,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_hw_exit_critical,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_mutex_init,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_hw_enter_critical,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_eventq_put,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_eventq_get,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_sem_deinit,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_mutex_deinit,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_eventq_deinit,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_eventq_init,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_event_deinit,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_time_ticks_to_ms,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_time_ms_to_ticks,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_callout_set_arg,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_event_set_arg,CONFIG_BT_ENABLED
|
||||
libbt.a,npl_os_freertos.c.obj,npl_freertos_eventq_put,CONFIG_BT_ENABLED
|
||||
libdriver.a,gpio.c.obj,gpio_intr_service,
|
||||
libesp_app_format.a,esp_app_desc.c.obj,esp_app_get_elf_sha256,
|
||||
libesp_hw_support.a,cpu.c.obj,esp_cpu_wait_for_intr,
|
||||
libesp_hw_support.a,cpu.c.obj,esp_cpu_reset,
|
||||
libesp_hw_support.a,esp_clk.c.obj,esp_clk_cpu_freq,
|
||||
libesp_hw_support.a,esp_clk.c.obj,esp_clk_apb_freq,
|
||||
libesp_hw_support.a,esp_clk.c.obj,esp_clk_xtal_freq,
|
||||
libesp_hw_support.a,esp_memory_utils.c.obj,esp_ptr_byte_accessible,
|
||||
libesp_hw_support.a,hw_random.c.obj,esp_random,
|
||||
libesp_hw_support.a,intr_alloc.c.obj,shared_intr_isr,
|
||||
libesp_hw_support.a,intr_alloc.c.obj,esp_intr_noniram_disable,
|
||||
libesp_hw_support.a,intr_alloc.c.obj,esp_intr_noniram_enable,
|
||||
libesp_hw_support.a,intr_alloc.c.obj,esp_intr_enable,
|
||||
libesp_hw_support.a,intr_alloc.c.obj,esp_intr_disable,
|
||||
libesp_hw_support.a,periph_ctrl.c.obj,wifi_bt_common_module_enable,
|
||||
libesp_hw_support.a,periph_ctrl.c.obj,wifi_bt_common_module_disable,
|
||||
libesp_hw_support.a,regi2c_ctrl.c.obj,regi2c_ctrl_read_reg,
|
||||
libesp_hw_support.a,regi2c_ctrl.c.obj,regi2c_ctrl_read_reg_mask,
|
||||
libesp_hw_support.a,regi2c_ctrl.c.obj,regi2c_ctrl_write_reg,
|
||||
libesp_hw_support.a,regi2c_ctrl.c.obj,regi2c_ctrl_write_reg_mask,
|
||||
libesp_hw_support.a,regi2c_ctrl.c.obj,regi2c_enter_critical,
|
||||
libesp_hw_support.a,regi2c_ctrl.c.obj,regi2c_exit_critical,
|
||||
libesp_hw_support.a,regi2c_ctrl.c.obj,regi2c_analog_cali_reg_read,
|
||||
libesp_hw_support.a,regi2c_ctrl.c.obj,regi2c_analog_cali_reg_write,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_32k_enable_external,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_fast_src_set,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_slow_freq_get_hz,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_slow_src_get,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_slow_src_set,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_dig_clk8m_disable,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_cpu_freq_set_config_fast,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_8m_enable,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_8md256_enabled,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_bbpll_configure,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_bbpll_enable,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_cpu_freq_get_config,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_cpu_freq_mhz_to_config,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_cpu_freq_set_config,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_cpu_freq_to_8m,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_cpu_freq_to_pll_mhz,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_cpu_freq_set_xtal,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_xtal_freq_get,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_cpu_freq_to_xtal,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_bbpll_disable,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,rtc_clk_apb_freq_update,
|
||||
libesp_hw_support.a,rtc_clk.c.obj,clk_ll_rtc_slow_get_src,FALSE
|
||||
libesp_hw_support.a,rtc_init.c.obj,rtc_vddsdio_set_config,
|
||||
libesp_hw_support.a,rtc_module.c.obj,rtc_isr,
|
||||
libesp_hw_support.a,rtc_module.c.obj,rtc_isr_noniram_disable,
|
||||
libesp_hw_support.a,rtc_module.c.obj,rtc_isr_noniram_enable,
|
||||
libesp_hw_support.a,rtc_sleep.c.obj,rtc_sleep_pu,
|
||||
libesp_hw_support.a,rtc_sleep.c.obj,rtc_sleep_finish,
|
||||
libesp_hw_support.a,rtc_sleep.c.obj,rtc_sleep_get_default_config,
|
||||
libesp_hw_support.a,rtc_sleep.c.obj,rtc_sleep_init,
|
||||
libesp_hw_support.a,rtc_sleep.c.obj,rtc_sleep_low_init,
|
||||
libesp_hw_support.a,rtc_sleep.c.obj,rtc_sleep_start,
|
||||
libesp_hw_support.a,rtc_time.c.obj,rtc_clk_cal,
|
||||
libesp_hw_support.a,rtc_time.c.obj,rtc_clk_cal_internal,
|
||||
libesp_hw_support.a,rtc_time.c.obj,rtc_time_get,
|
||||
libesp_hw_support.a,rtc_time.c.obj,rtc_time_us_to_slowclk,
|
||||
libesp_hw_support.a,rtc_time.c.obj,rtc_time_slowclk_to_us,
|
||||
libesp_hw_support.a,sleep_modes.c.obj,periph_ll_periph_enabled,
|
||||
libesp_hw_support.a,sleep_modes.c.obj,flush_uarts,
|
||||
libesp_hw_support.a,sleep_modes.c.obj,suspend_uarts,
|
||||
libesp_hw_support.a,sleep_modes.c.obj,resume_uarts,
|
||||
libesp_hw_support.a,sleep_modes.c.obj,esp_sleep_start,
|
||||
libesp_hw_support.a,sleep_modes.c.obj,esp_deep_sleep_start,
|
||||
libesp_hw_support.a,sleep_modes.c.obj,esp_light_sleep_inner,
|
||||
libesp_phy.a,phy_init.c.obj,esp_phy_common_clock_enable,
|
||||
libesp_phy.a,phy_init.c.obj,esp_phy_common_clock_disable,
|
||||
libesp_phy.a,phy_init.c.obj,esp_wifi_bt_power_domain_on,
|
||||
libesp_phy.a,phy_override.c.obj,phy_i2c_enter_critical,
|
||||
libesp_phy.a,phy_override.c.obj,phy_i2c_exit_critical,
|
||||
libesp_pm.a,pm_locks.c.obj,esp_pm_lock_acquire,
|
||||
libesp_pm.a,pm_locks.c.obj,esp_pm_lock_release,
|
||||
libesp_pm.a,pm_impl.c.obj,get_lowest_allowed_mode,
|
||||
libesp_pm.a,pm_impl.c.obj,esp_pm_impl_switch_mode,
|
||||
libesp_pm.a,pm_impl.c.obj,on_freq_update,
|
||||
libesp_pm.a,pm_impl.c.obj,vApplicationSleep,CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
libesp_pm.a,pm_impl.c.obj,do_switch,
|
||||
libesp_ringbuf.a,ringbuf.c.obj,prvCheckItemAvail,
|
||||
libesp_ringbuf.a,ringbuf.c.obj,prvGetFreeSize,
|
||||
libesp_ringbuf.a,ringbuf.c.obj,prvReceiveGenericFromISR,
|
||||
libesp_ringbuf.a,ringbuf.c.obj,xRingbufferGetMaxItemSize,
|
||||
libesp_rom.a,esp_rom_systimer.c.obj,systimer_hal_init,
|
||||
libesp_rom.a,esp_rom_systimer.c.obj,systimer_hal_set_alarm_period,
|
||||
libesp_rom.a,esp_rom_systimer.c.obj,systimer_hal_set_alarm_target,
|
||||
libesp_rom.a,esp_rom_systimer.c.obj,systimer_hal_set_tick_rate_ops,
|
||||
libesp_rom.a,esp_rom_uart.c.obj,esp_rom_uart_set_clock_baudrate,
|
||||
libesp_system.a,brownout.c.obj,rtc_brownout_isr_handler,
|
||||
libesp_system.a,cache_err_int.c.obj,esp_cache_err_get_cpuid,
|
||||
libesp_system.a,cpu_start.c.obj,call_start_cpu0,
|
||||
libesp_system.a,crosscore_int.c.obj,esp_crosscore_int_send,
|
||||
libesp_system.a,crosscore_int.c.obj,esp_crosscore_int_send_yield,
|
||||
libesp_system.a,esp_system.c.obj,esp_restart,
|
||||
libesp_system.a,esp_system.c.obj,esp_system_abort,
|
||||
libesp_system.a,reset_reason.c.obj,esp_reset_reason_set_hint,
|
||||
libesp_system.a,reset_reason.c.obj,esp_reset_reason_get_hint,
|
||||
libesp_system.a,ubsan.c.obj,__ubsan_include,
|
||||
libesp_timer.a,esp_timer.c.obj,esp_timer_get_next_alarm_for_wake_up,
|
||||
libesp_timer.a,esp_timer.c.obj,timer_list_unlock,!CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
|
||||
libesp_timer.a,esp_timer.c.obj,timer_list_lock,!CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
|
||||
libesp_timer.a,esp_timer.c.obj,timer_armed,
|
||||
libesp_timer.a,esp_timer.c.obj,timer_remove,
|
||||
libesp_timer.a,esp_timer.c.obj,timer_insert,!CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
|
||||
libesp_timer.a,esp_timer.c.obj,esp_timer_start_once,
|
||||
libesp_timer.a,esp_timer.c.obj,esp_timer_start_periodic,
|
||||
libesp_timer.a,esp_timer.c.obj,esp_timer_stop,
|
||||
libesp_timer.a,esp_timer.c.obj,esp_timer_get_expiry_time,
|
||||
libesp_timer.a,esp_timer_impl_systimer.c.obj,esp_timer_impl_get_time,!CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
|
||||
libesp_timer.a,esp_timer_impl_systimer.c.obj,esp_timer_impl_set_alarm_id,!CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
|
||||
libesp_timer.a,esp_timer_impl_systimer.c.obj,esp_timer_impl_update_apb_freq,
|
||||
libesp_timer.a,esp_timer_impl_systimer.c.obj,esp_timer_impl_get_min_period_us,
|
||||
libesp_timer.a,ets_timer_legacy.c.obj,timer_initialized,
|
||||
libesp_timer.a,ets_timer_legacy.c.obj,ets_timer_arm_us,
|
||||
libesp_timer.a,ets_timer_legacy.c.obj,ets_timer_arm,
|
||||
libesp_timer.a,ets_timer_legacy.c.obj,ets_timer_disarm,
|
||||
libesp_timer.a,system_time.c.obj,esp_system_get_time,
|
||||
libesp_wifi.a,esp_adapter.c.obj,semphr_take_from_isr_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,wifi_realloc,
|
||||
libesp_wifi.a,esp_adapter.c.obj,coex_event_duration_get_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,coex_schm_interval_set_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,esp_empty_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,wifi_calloc,
|
||||
libesp_wifi.a,esp_adapter.c.obj,wifi_zalloc_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,env_is_chip_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,is_from_isr_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,semphr_give_from_isr_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,mutex_lock_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,mutex_unlock_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,task_ms_to_tick_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,wifi_apb80m_request_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,wifi_apb80m_release_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,timer_arm_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,wifi_malloc,
|
||||
libesp_wifi.a,esp_adapter.c.obj,timer_disarm_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,timer_arm_us_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,wifi_rtc_enable_iso_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,wifi_rtc_disable_iso_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,malloc_internal_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,realloc_internal_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,calloc_internal_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,zalloc_internal_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,coex_status_get_wrapper,
|
||||
libesp_wifi.a,esp_adapter.c.obj,coex_wifi_release_wrapper,
|
||||
libfreertos.a,list.c.obj,uxListRemove,FALSE
|
||||
libfreertos.a,list.c.obj,vListInitialise,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,list.c.obj,vListInitialiseItem,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,list.c.obj,vListInsert,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,list.c.obj,vListInsertEnd,FALSE
|
||||
libfreertos.a,port.c.obj,vApplicationStackOverflowHook,FALSE
|
||||
libfreertos.a,port.c.obj,vPortYieldOtherCore,FALSE
|
||||
libfreertos.a,port.c.obj,vPortYield,
|
||||
libfreertos.a,port_common.c.obj,xPortcheckValidStackMem,
|
||||
libfreertos.a,port_common.c.obj,vApplicationGetTimerTaskMemory,
|
||||
libfreertos.a,port_common.c.obj,esp_startup_start_app_common,
|
||||
libfreertos.a,port_common.c.obj,xPortCheckValidTCBMem,
|
||||
libfreertos.a,port_common.c.obj,vApplicationGetIdleTaskMemory,
|
||||
libfreertos.a,port_systick.c.obj,vPortSetupTimer,
|
||||
libfreertos.a,port_systick.c.obj,xPortSysTickHandler,FALSE
|
||||
libfreertos.a,queue.c.obj,prvCopyDataFromQueue,
|
||||
libfreertos.a,queue.c.obj,prvGetDisinheritPriorityAfterTimeout,
|
||||
libfreertos.a,queue.c.obj,prvIsQueueEmpty,
|
||||
libfreertos.a,queue.c.obj,prvNotifyQueueSetContainer,
|
||||
libfreertos.a,queue.c.obj,xQueueReceive,
|
||||
libfreertos.a,queue.c.obj,prvUnlockQueue,
|
||||
libfreertos.a,queue.c.obj,xQueueSemaphoreTake,
|
||||
libfreertos.a,queue.c.obj,xQueueReceiveFromISR,
|
||||
libfreertos.a,queue.c.obj,uxQueueMessagesWaitingFromISR,
|
||||
libfreertos.a,queue.c.obj,xQueueIsQueueEmptyFromISR,
|
||||
libfreertos.a,queue.c.obj,xQueueGiveFromISR,
|
||||
libfreertos.a,tasks.c.obj,__getreent,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,pcTaskGetName,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,prvAddCurrentTaskToDelayedList,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,prvDeleteTLS,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,pvTaskIncrementMutexHeldCount,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,taskSelectHighestPriorityTaskSMP,FALSE
|
||||
libfreertos.a,tasks.c.obj,taskYIELD_OTHER_CORE,FALSE
|
||||
libfreertos.a,tasks.c.obj,vTaskGetSnapshot,CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,vTaskInternalSetTimeOutState,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,vTaskPlaceOnEventList,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,vTaskPlaceOnEventListRestricted,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,vTaskPlaceOnUnorderedEventList,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,vTaskPriorityDisinheritAfterTimeout,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,vTaskReleaseEventListLock,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,vTaskTakeEventListLock,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,xTaskCheckForTimeOut,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,xTaskGetCurrentTaskHandle,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,xTaskGetSchedulerState,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,xTaskGetTickCount,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,xTaskPriorityDisinherit,FALSE
|
||||
libfreertos.a,tasks.c.obj,xTaskPriorityInherit,CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH
|
||||
libfreertos.a,tasks.c.obj,prvGetExpectedIdleTime,FALSE
|
||||
libfreertos.a,tasks.c.obj,vTaskStepTick,FALSE
|
||||
libhal.a,brownout_hal.c.obj,brownout_hal_intr_clear,
|
||||
libhal.a,efuse_hal.c.obj,efuse_hal_chip_revision,
|
||||
libhal.a,efuse_hal.c.obj,efuse_hal_get_major_chip_version,
|
||||
libhal.a,efuse_hal.c.obj,efuse_hal_get_minor_chip_version,
|
||||
libheap.a,heap_caps.c.obj,dram_alloc_to_iram_addr,
|
||||
libheap.a,heap_caps.c.obj,heap_caps_free,
|
||||
libheap.a,heap_caps.c.obj,heap_caps_realloc_base,
|
||||
libheap.a,heap_caps.c.obj,heap_caps_realloc,
|
||||
libheap.a,heap_caps.c.obj,heap_caps_calloc_base,
|
||||
libheap.a,heap_caps.c.obj,heap_caps_calloc,
|
||||
libheap.a,heap_caps.c.obj,heap_caps_malloc_base,
|
||||
libheap.a,heap_caps.c.obj,heap_caps_malloc,
|
||||
libheap.a,heap_caps.c.obj,heap_caps_malloc_default,
|
||||
libheap.a,heap_caps.c.obj,heap_caps_realloc_default,
|
||||
libheap.a,heap_caps.c.obj,find_containing_heap,
|
||||
libheap.a,multi_heap.c.obj,_multi_heap_lock,
|
||||
libheap.a,multi_heap.c.obj,multi_heap_in_rom_init,
|
||||
liblog.a,log_freertos.c.obj,esp_log_timestamp,
|
||||
liblog.a,log_freertos.c.obj,esp_log_impl_lock,
|
||||
liblog.a,log_freertos.c.obj,esp_log_impl_unlock,
|
||||
liblog.a,log_freertos.c.obj,esp_log_impl_lock_timeout,
|
||||
liblog.a,log_freertos.c.obj,esp_log_early_timestamp,
|
||||
liblog.a,log.c.obj,esp_log_write,
|
||||
libmbedcrypto.a,esp_mem.c.obj,esp_mbedtls_mem_calloc,!CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC
|
||||
libmbedcrypto.a,esp_mem.c.obj,esp_mbedtls_mem_free,!CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC
|
||||
libnewlib.a,assert.c.obj,__assert_func,
|
||||
libnewlib.a,assert.c.obj,newlib_include_assert_impl,
|
||||
libnewlib.a,heap.c.obj,_calloc_r,
|
||||
libnewlib.a,heap.c.obj,_free_r,
|
||||
libnewlib.a,heap.c.obj,_malloc_r,
|
||||
libnewlib.a,heap.c.obj,_realloc_r,
|
||||
libnewlib.a,heap.c.obj,calloc,
|
||||
libnewlib.a,heap.c.obj,cfree,
|
||||
libnewlib.a,heap.c.obj,free,
|
||||
libnewlib.a,heap.c.obj,malloc,
|
||||
libnewlib.a,heap.c.obj,newlib_include_heap_impl,
|
||||
libnewlib.a,heap.c.obj,realloc,
|
||||
libnewlib.a,locks.c.obj,_lock_try_acquire_recursive,
|
||||
libnewlib.a,locks.c.obj,lock_release_generic,
|
||||
libnewlib.a,locks.c.obj,_lock_release,
|
||||
libnewlib.a,locks.c.obj,_lock_release_recursive,
|
||||
libnewlib.a,locks.c.obj,__retarget_lock_init,
|
||||
libnewlib.a,locks.c.obj,__retarget_lock_init_recursive,
|
||||
libnewlib.a,locks.c.obj,__retarget_lock_close,
|
||||
libnewlib.a,locks.c.obj,__retarget_lock_close_recursive,
|
||||
libnewlib.a,locks.c.obj,check_lock_nonzero,
|
||||
libnewlib.a,locks.c.obj,__retarget_lock_acquire,
|
||||
libnewlib.a,locks.c.obj,lock_init_generic,
|
||||
libnewlib.a,locks.c.obj,__retarget_lock_acquire_recursive,
|
||||
libnewlib.a,locks.c.obj,__retarget_lock_try_acquire,
|
||||
libnewlib.a,locks.c.obj,__retarget_lock_try_acquire_recursive,
|
||||
libnewlib.a,locks.c.obj,__retarget_lock_release,
|
||||
libnewlib.a,locks.c.obj,__retarget_lock_release_recursive,
|
||||
libnewlib.a,locks.c.obj,_lock_close,
|
||||
libnewlib.a,locks.c.obj,lock_acquire_generic,
|
||||
libnewlib.a,locks.c.obj,_lock_acquire,
|
||||
libnewlib.a,locks.c.obj,_lock_acquire_recursive,
|
||||
libnewlib.a,locks.c.obj,_lock_try_acquire,
|
||||
libnewlib.a,reent_init.c.obj,esp_reent_init,
|
||||
libnewlib.a,time.c.obj,_times_r,
|
||||
libnewlib.a,time.c.obj,_gettimeofday_r,
|
||||
libpp.a,pp_debug.o,wifi_gpio_debug,
|
||||
libpthread.a,pthread.c.obj,pthread_mutex_lock_internal,
|
||||
libpthread.a,pthread.c.obj,pthread_mutex_lock,
|
||||
libpthread.a,pthread.c.obj,pthread_mutex_unlock,
|
||||
libriscv.a,interrupt.c.obj,intr_handler_get,
|
||||
libriscv.a,interrupt.c.obj,intr_handler_set,
|
||||
libriscv.a,interrupt.c.obj,intr_matrix_route,
|
||||
libspi_flash.a,flash_brownout_hook.c.obj,spi_flash_needs_reset_check,FALSE
|
||||
libspi_flash.a,flash_brownout_hook.c.obj,spi_flash_set_erasing_flag,FALSE
|
||||
libspi_flash.a,flash_ops.c.obj,spi_flash_guard_set,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,flash_ops.c.obj,spi_flash_malloc_internal,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,flash_ops.c.obj,spi_flash_rom_impl_init,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,flash_ops.c.obj,esp_mspi_pin_init,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,flash_ops.c.obj,spi_flash_init_chip_state,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,spi_flash_os_func_app.c.obj,delay_us,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,spi_flash_os_func_app.c.obj,get_buffer_malloc,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,spi_flash_os_func_app.c.obj,release_buffer_malloc,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,spi_flash_os_func_app.c.obj,main_flash_region_protected,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,spi_flash_os_func_app.c.obj,main_flash_op_status,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,spi_flash_os_func_app.c.obj,spi1_flash_os_check_yield,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,spi_flash_os_func_app.c.obj,spi1_flash_os_yield,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,spi_flash_os_func_noos.c.obj,start,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,spi_flash_os_func_noos.c.obj,end,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
libspi_flash.a,spi_flash_os_func_noos.c.obj,delay_us,CONFIG_SPI_FLASH_ROM_IMPL
|
||||
|
@@ -0,0 +1,24 @@
|
||||
library,path
|
||||
libble_app.a,$IDF_PATH/components/bt/controller/lib_esp32c2/esp32c2-bt-lib/libble_app.a
|
||||
libpp.a,$IDF_PATH/components/esp_wifi/lib/esp32c2/libpp.a
|
||||
libbootloader_support.a,./esp-idf/bootloader_support/libbootloader_support.a
|
||||
libbt.a,./esp-idf/bt/libbt.a
|
||||
libdriver.a,./esp-idf/driver/libdriver.a
|
||||
libesp_app_format.a,./esp-idf/esp_app_format/libesp_app_format.a
|
||||
libesp_hw_support.a,./esp-idf/esp_hw_support/libesp_hw_support.a
|
||||
libesp_phy.a,./esp-idf/esp_phy/libesp_phy.a
|
||||
libesp_pm.a,./esp-idf/esp_pm/libesp_pm.a
|
||||
libesp_ringbuf.a,./esp-idf/esp_ringbuf/libesp_ringbuf.a
|
||||
libesp_rom.a,./esp-idf/esp_rom/libesp_rom.a
|
||||
libesp_system.a,./esp-idf/esp_system/libesp_system.a
|
||||
libesp_timer.a,./esp-idf/esp_timer/libesp_timer.a
|
||||
libesp_wifi.a,./esp-idf/esp_wifi/libesp_wifi.a
|
||||
libfreertos.a,./esp-idf/freertos/libfreertos.a
|
||||
libhal.a,./esp-idf/hal/libhal.a
|
||||
libheap.a,./esp-idf/heap/libheap.a
|
||||
liblog.a,./esp-idf/log/liblog.a
|
||||
libmbedcrypto.a,./esp-idf/mbedtls/mbedtls/library/libmbedcrypto.a
|
||||
libnewlib.a,./esp-idf/newlib/libnewlib.a
|
||||
libpthread.a,./esp-idf/pthread/libpthread.a
|
||||
libriscv.a,./esp-idf/riscv/libriscv.a
|
||||
libspi_flash.a,./esp-idf/spi_flash/libspi_flash.a
|
||||
|
@@ -0,0 +1,66 @@
|
||||
library,object,path
|
||||
libbootloader_support.a,bootloader_flash.c.obj,esp-idf/bootloader_support/CMakeFiles/__idf_bootloader_support.dir/bootloader_flash/src/bootloader_flash.c.obj
|
||||
libbootloader_support.a,flash_encrypt.c.obj,esp-idf/bootloader_support/CMakeFiles/__idf_bootloader_support.dir/src/flash_encrypt.c.obj
|
||||
libbt.a,bt_osi_mem.c.obj,esp-idf/bt/CMakeFiles/__idf_bt.dir/porting/mem/bt_osi_mem.c.obj
|
||||
libbt.a,bt.c.obj,esp-idf/bt/CMakeFiles/__idf_bt.dir/controller/esp32c2/bt.c.obj
|
||||
libbt.a,npl_os_freertos.c.obj,esp-idf/bt/CMakeFiles/__idf_bt.dir/porting/npl/freertos/src/npl_os_freertos.c.obj
|
||||
libbt.a,nimble_port.c.obj,esp-idf/bt/CMakeFiles/__idf_bt.dir/host/nimble/nimble/porting/nimble/src/nimble_port.c.obj
|
||||
libdriver.a,gpio.c.obj,esp-idf/driver/CMakeFiles/__idf_driver.dir/gpio/gpio.c.obj
|
||||
libesp_app_format.a,esp_app_desc.c.obj,esp-idf/esp_app_format/CMakeFiles/__idf_esp_app_format.dir/esp_app_desc.c.obj
|
||||
libesp_hw_support.a,cpu.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/cpu.c.obj
|
||||
libesp_hw_support.a,esp_clk.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/esp_clk.c.obj
|
||||
libesp_hw_support.a,esp_memory_utils.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/esp_memory_utils.c.obj
|
||||
libesp_hw_support.a,hw_random.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/hw_random.c.obj
|
||||
libesp_hw_support.a,intr_alloc.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/intr_alloc.c.obj
|
||||
libesp_hw_support.a,periph_ctrl.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/periph_ctrl.c.obj
|
||||
libesp_hw_support.a,regi2c_ctrl.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/regi2c_ctrl.c.obj
|
||||
libesp_hw_support.a,rtc_clk.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/port/esp32c2/rtc_clk.c.obj
|
||||
libesp_hw_support.a,rtc_init.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/port/esp32c2/rtc_init.c.obj
|
||||
libesp_hw_support.a,rtc_module.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/rtc_module.c.obj
|
||||
libesp_hw_support.a,rtc_sleep.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/port/esp32c2/rtc_sleep.c.obj
|
||||
libesp_hw_support.a,rtc_time.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/port/esp32c2/rtc_time.c.obj
|
||||
libesp_hw_support.a,sleep_modes.c.obj,esp-idf/esp_hw_support/CMakeFiles/__idf_esp_hw_support.dir/sleep_modes.c.obj
|
||||
libesp_phy.a,phy_init.c.obj,esp-idf/esp_phy/CMakeFiles/__idf_esp_phy.dir/src/phy_init.c.obj
|
||||
libesp_phy.a,phy_override.c.obj,esp-idf/esp_phy/CMakeFiles/__idf_esp_phy.dir/src/phy_override.c.obj
|
||||
libesp_pm.a,pm_locks.c.obj,esp-idf/esp_pm/CMakeFiles/__idf_esp_pm.dir/pm_locks.c.obj
|
||||
libesp_pm.a,pm_impl.c.obj,esp-idf/esp_pm/CMakeFiles/__idf_esp_pm.dir/pm_impl.c.obj
|
||||
libesp_ringbuf.a,ringbuf.c.obj,esp-idf/esp_ringbuf/CMakeFiles/__idf_esp_ringbuf.dir/ringbuf.c.obj
|
||||
libesp_rom.a,esp_rom_systimer.c.obj,esp-idf/esp_rom/CMakeFiles/__idf_esp_rom.dir/patches/esp_rom_systimer.c.obj
|
||||
libesp_rom.a,esp_rom_uart.c.obj,esp-idf/esp_rom/CMakeFiles/__idf_esp_rom.dir/patches/esp_rom_uart.c.obj
|
||||
libesp_system.a,brownout.c.obj,esp-idf/esp_system/CMakeFiles/__idf_esp_system.dir/port/brownout.c.obj
|
||||
libesp_system.a,cache_err_int.c.obj,esp-idf/esp_system/CMakeFiles/__idf_esp_system.dir/port/soc/esp32c2/cache_err_int.c.obj
|
||||
libesp_system.a,cpu_start.c.obj,esp-idf/esp_system/CMakeFiles/__idf_esp_system.dir/port/cpu_start.c.obj
|
||||
libesp_system.a,crosscore_int.c.obj,esp-idf/esp_system/CMakeFiles/__idf_esp_system.dir/crosscore_int.c.obj
|
||||
libesp_system.a,esp_system.c.obj,esp-idf/esp_system/CMakeFiles/__idf_esp_system.dir/esp_system.c.obj
|
||||
libesp_system.a,reset_reason.c.obj,esp-idf/esp_system/CMakeFiles/__idf_esp_system.dir/port/soc/esp32c2/reset_reason.c.obj
|
||||
libesp_system.a,ubsan.c.obj,esp-idf/esp_system/CMakeFiles/__idf_esp_system.dir/ubsan.c.obj
|
||||
libesp_timer.a,esp_timer.c.obj,esp-idf/esp_timer/CMakeFiles/__idf_esp_timer.dir/src/esp_timer.c.obj
|
||||
libesp_timer.a,esp_timer_impl_systimer.c.obj,esp-idf/esp_timer/CMakeFiles/__idf_esp_timer.dir/src/esp_timer_impl_systimer.c.obj
|
||||
libesp_timer.a,ets_timer_legacy.c.obj,esp-idf/esp_timer/CMakeFiles/__idf_esp_timer.dir/src/ets_timer_legacy.c.obj
|
||||
libesp_timer.a,system_time.c.obj,esp-idf/esp_timer/CMakeFiles/__idf_esp_timer.dir/src/system_time.c.obj
|
||||
libesp_wifi.a,esp_adapter.c.obj,esp-idf/esp_wifi/CMakeFiles/__idf_esp_wifi.dir/esp32c2/esp_adapter.c.obj
|
||||
libfreertos.a,list.c.obj,esp-idf/freertos/CMakeFiles/__idf_freertos.dir/FreeRTOS-Kernel/list.c.obj
|
||||
libfreertos.a,port.c.obj,esp-idf/freertos/CMakeFiles/__idf_freertos.dir/FreeRTOS-Kernel/portable/riscv/port.c.obj
|
||||
libfreertos.a,port_common.c.obj,esp-idf/freertos/CMakeFiles/__idf_freertos.dir/FreeRTOS-Kernel/portable/port_common.c.obj
|
||||
libfreertos.a,port_systick.c.obj,esp-idf/freertos/CMakeFiles/__idf_freertos.dir/FreeRTOS-Kernel/portable/port_systick.c.obj
|
||||
libfreertos.a,queue.c.obj,esp-idf/freertos/CMakeFiles/__idf_freertos.dir/FreeRTOS-Kernel/queue.c.obj
|
||||
libfreertos.a,tasks.c.obj,esp-idf/freertos/CMakeFiles/__idf_freertos.dir/FreeRTOS-Kernel/tasks.c.obj
|
||||
libhal.a,brownout_hal.c.obj,esp-idf/hal/CMakeFiles/__idf_hal.dir/esp32c2/brownout_hal.c.obj
|
||||
libhal.a,efuse_hal.c.obj,esp-idf/hal/CMakeFiles/__idf_hal.dir/efuse_hal.c.obj
|
||||
libhal.a,efuse_hal.c.obj,esp-idf/hal/CMakeFiles/__idf_hal.dir/esp32c2/efuse_hal.c.obj
|
||||
libheap.a,heap_caps.c.obj,esp-idf/heap/CMakeFiles/__idf_heap.dir/heap_caps.c.obj
|
||||
libheap.a,multi_heap.c.obj,./esp-idf/heap/CMakeFiles/__idf_heap.dir/multi_heap.c.obj
|
||||
liblog.a,log_freertos.c.obj,esp-idf/log/CMakeFiles/__idf_log.dir/log_freertos.c.obj
|
||||
liblog.a,log.c.obj,esp-idf/log/CMakeFiles/__idf_log.dir/log.c.obj
|
||||
libmbedcrypto.a,esp_mem.c.obj,esp-idf/mbedtls/mbedtls/library/CMakeFiles/mbedcrypto.dir/$IDF_PATH/components/mbedtls/port/esp_mem.c.obj
|
||||
libnewlib.a,assert.c.obj,esp-idf/newlib/CMakeFiles/__idf_newlib.dir/assert.c.obj
|
||||
libnewlib.a,heap.c.obj,esp-idf/newlib/CMakeFiles/__idf_newlib.dir/heap.c.obj
|
||||
libnewlib.a,locks.c.obj,esp-idf/newlib/CMakeFiles/__idf_newlib.dir/locks.c.obj
|
||||
libnewlib.a,reent_init.c.obj,esp-idf/newlib/CMakeFiles/__idf_newlib.dir/reent_init.c.obj
|
||||
libnewlib.a,time.c.obj,esp-idf/newlib/CMakeFiles/__idf_newlib.dir/time.c.obj
|
||||
libpthread.a,pthread.c.obj,esp-idf/pthread/CMakeFiles/__idf_pthread.dir/pthread.c.obj
|
||||
libriscv.a,interrupt.c.obj,esp-idf/riscv/CMakeFiles/__idf_riscv.dir/interrupt.c.obj
|
||||
libspi_flash.a,flash_brownout_hook.c.obj,esp-idf/spi_flash/CMakeFiles/__idf_spi_flash.dir/flash_brownout_hook.c.obj
|
||||
libspi_flash.a,flash_ops.c.obj,esp-idf/spi_flash/CMakeFiles/__idf_spi_flash.dir/flash_ops.c.obj
|
||||
libspi_flash.a,spi_flash_os_func_app.c.obj,esp-idf/spi_flash/CMakeFiles/__idf_spi_flash.dir/spi_flash_os_func_app.c.obj
|
||||
libspi_flash.a,spi_flash_os_func_noos.c.obj,esp-idf/spi_flash/CMakeFiles/__idf_spi_flash.dir/spi_flash_os_func_noos.c.obj
|
||||
|
@@ -0,0 +1,311 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
import logging
|
||||
import argparse
|
||||
import csv
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
from io import StringIO
|
||||
import configuration
|
||||
|
||||
sys.path.append(os.environ['IDF_PATH'] + '/tools/ldgen')
|
||||
sys.path.append(os.environ['IDF_PATH'] + '/tools/ldgen/ldgen')
|
||||
from entity import EntityDB
|
||||
|
||||
espidf_objdump = None
|
||||
|
||||
def lib_secs(lib, file, lib_path):
|
||||
new_env = os.environ.copy()
|
||||
new_env['LC_ALL'] = 'C'
|
||||
dump = StringIO(subprocess.check_output([espidf_objdump, '-h', lib_path], env=new_env).decode())
|
||||
dump.name = lib
|
||||
|
||||
sections_infos = EntityDB()
|
||||
sections_infos.add_sections_info(dump)
|
||||
|
||||
secs = sections_infos.get_sections(lib, file.split('.')[0] + '.c')
|
||||
if len(secs) == 0:
|
||||
secs = sections_infos.get_sections(lib, file.split('.')[0])
|
||||
if len(secs) == 0:
|
||||
raise ValueError('Failed to get sections from lib %s'%(lib_path))
|
||||
|
||||
return secs
|
||||
|
||||
def filter_secs(secs_a, secs_b):
|
||||
new_secs = list()
|
||||
for s_a in secs_a:
|
||||
for s_b in secs_b:
|
||||
if s_b in s_a:
|
||||
new_secs.append(s_a)
|
||||
return new_secs
|
||||
|
||||
def strip_secs(secs_a, secs_b):
|
||||
secs = list(set(secs_a) - set(secs_b))
|
||||
secs.sort()
|
||||
return secs
|
||||
|
||||
def func2sect(func):
|
||||
if ' ' in func:
|
||||
func_l = func.split(' ')
|
||||
else:
|
||||
func_l = list()
|
||||
func_l.append(func)
|
||||
|
||||
secs = list()
|
||||
for l in func_l:
|
||||
if '.iram1.' not in l:
|
||||
secs.append('.literal.%s'%(l,))
|
||||
secs.append('.text.%s'%(l, ))
|
||||
else:
|
||||
secs.append(l)
|
||||
return secs
|
||||
|
||||
class filter_c:
|
||||
def __init__(self, file):
|
||||
lines = open(file).read().splitlines()
|
||||
self.libs_desc = ''
|
||||
self.libs = ''
|
||||
for l in lines:
|
||||
if ') .iram1 EXCLUDE_FILE(*' in l and ') .iram1.*)' in l:
|
||||
desc = '\(EXCLUDE_FILE\((.*)\) .iram1 '
|
||||
self.libs_desc = re.search(desc, l)[1]
|
||||
self.libs = self.libs_desc.replace('*', '')
|
||||
return
|
||||
|
||||
def match(self, lib):
|
||||
if lib in self.libs:
|
||||
print('Remove lib %s'%(lib))
|
||||
return True
|
||||
return False
|
||||
|
||||
def add(self):
|
||||
return self.libs_desc
|
||||
|
||||
class target_c:
|
||||
def __init__(self, lib, lib_path, file, fsecs):
|
||||
self.lib = lib
|
||||
self.file = file
|
||||
|
||||
self.lib_path = lib_path
|
||||
self.fsecs = func2sect(fsecs)
|
||||
self.desc = '*%s:%s.*'%(lib, file.split('.')[0])
|
||||
|
||||
secs = lib_secs(lib, file, lib_path)
|
||||
if '.iram1.' in self.fsecs[0]:
|
||||
self.secs = filter_secs(secs, ('.iram1.', ))
|
||||
else:
|
||||
self.secs = filter_secs(secs, ('.iram1.', '.text.', '.literal.'))
|
||||
self.isecs = strip_secs(self.secs, self.fsecs)
|
||||
|
||||
def __str__(self):
|
||||
s = 'lib=%s\nfile=%s\lib_path=%s\ndesc=%s\nsecs=%s\nfsecs=%s\nisecs=%s\n'%(\
|
||||
self.lib, self.file, self.lib_path, self.desc, self.secs, self.fsecs,\
|
||||
self.isecs)
|
||||
return s
|
||||
|
||||
class relink_c:
|
||||
def __init__(self, input, library_file, object_file, function_file, sdkconfig_file, missing_function_info):
|
||||
self.filter = filter_c(input)
|
||||
|
||||
libraries = configuration.generator(library_file, object_file, function_file, sdkconfig_file, missing_function_info, espidf_objdump)
|
||||
self.targets = list()
|
||||
for i in libraries.libs:
|
||||
lib = libraries.libs[i]
|
||||
|
||||
if self.filter.match(lib.name):
|
||||
continue
|
||||
|
||||
for j in lib.objs:
|
||||
obj = lib.objs[j]
|
||||
self.targets.append(target_c(lib.name, lib.path, obj.name,
|
||||
' '.join(obj.sections())))
|
||||
# for i in self.targets:
|
||||
# print(i)
|
||||
self.__transform__()
|
||||
|
||||
def __transform__(self):
|
||||
iram1_exclude = list()
|
||||
iram1_include = list()
|
||||
flash_include = list()
|
||||
|
||||
for t in self.targets:
|
||||
secs = filter_secs(t.fsecs, ('.iram1.', ))
|
||||
if len(secs) > 0:
|
||||
iram1_exclude.append(t.desc)
|
||||
|
||||
secs = filter_secs(t.isecs, ('.iram1.', ))
|
||||
if len(secs) > 0:
|
||||
iram1_include.append(' %s(%s)'%(t.desc, ' '.join(secs)))
|
||||
|
||||
secs = t.fsecs
|
||||
if len(secs) > 0:
|
||||
flash_include.append(' %s(%s)'%(t.desc, ' '.join(secs)))
|
||||
|
||||
self.iram1_exclude = ' *(EXCLUDE_FILE(%s %s) .iram1.*) *(EXCLUDE_FILE(%s %s) .iram1)' % \
|
||||
(self.filter.add(), ' '.join(iram1_exclude), \
|
||||
self.filter.add(), ' '.join(iram1_exclude))
|
||||
self.iram1_include = '\n'.join(iram1_include)
|
||||
self.flash_include = '\n'.join(flash_include)
|
||||
|
||||
logging.debug('IRAM1 Exclude: %s'%(self.iram1_exclude))
|
||||
logging.debug('IRAM1 Include: %s'%(self.iram1_include))
|
||||
logging.debug('Flash Include: %s'%(self.flash_include))
|
||||
|
||||
def __replace__(self, lines):
|
||||
def is_iram_desc(l):
|
||||
if '*(.iram1 .iram1.*)' in l or (') .iram1 EXCLUDE_FILE(*' in l and ') .iram1.*)' in l):
|
||||
return True
|
||||
return False
|
||||
|
||||
iram_start = False
|
||||
flash_done = False
|
||||
|
||||
for i in range(0, len(lines) - 1):
|
||||
l = lines[i]
|
||||
if '.iram0.text :' in l:
|
||||
logging.debug('start to process .iram0.text')
|
||||
iram_start = True
|
||||
elif '.dram0.data :' in l:
|
||||
logging.debug('end to process .iram0.text')
|
||||
iram_start = False
|
||||
elif is_iram_desc(l):
|
||||
if iram_start:
|
||||
lines[i] = '%s\n%s\n'%(self.iram1_exclude, self.iram1_include)
|
||||
elif '(.stub .gnu.warning' in l:
|
||||
if not flash_done:
|
||||
lines[i] = '%s\n\n%s'%(self.flash_include, l)
|
||||
elif self.flash_include in l:
|
||||
flash_done = True
|
||||
else:
|
||||
if iram_start:
|
||||
new_l = self._replace_func(l)
|
||||
if new_l:
|
||||
lines[i] = new_l
|
||||
|
||||
return lines
|
||||
|
||||
def _replace_func(self, l):
|
||||
for t in self.targets:
|
||||
if t.desc in l:
|
||||
S = '.literal .literal.* .text .text.*'
|
||||
if S in l:
|
||||
if len(t.isecs) > 0:
|
||||
return l.replace(S, ' '.join(t.isecs))
|
||||
else:
|
||||
return ' '
|
||||
|
||||
S = '%s(%s)'%(t.desc, ' '.join(t.fsecs))
|
||||
if S in l:
|
||||
return ' '
|
||||
|
||||
replaced = False
|
||||
for s in t.fsecs:
|
||||
s2 = s + ' '
|
||||
if s2 in l:
|
||||
l = l.replace(s2, '')
|
||||
replaced = True
|
||||
s2 = s + ')'
|
||||
if s2 in l:
|
||||
l = l.replace(s2, ')')
|
||||
replaced = True
|
||||
if '( )' in l or '()' in l:
|
||||
return ' '
|
||||
if replaced:
|
||||
return l
|
||||
else:
|
||||
index = '*%s:(EXCLUDE_FILE'%(t.lib)
|
||||
if index in l and t.file.split('.')[0] not in l:
|
||||
for m in self.targets:
|
||||
index = '*%s:(EXCLUDE_FILE'%(m.lib)
|
||||
if index in l and m.file.split('.')[0] not in l:
|
||||
l = l.replace('EXCLUDE_FILE(', 'EXCLUDE_FILE(%s '%(m.desc))
|
||||
if len(m.isecs) > 0:
|
||||
l += '\n %s(%s)'%(m.desc, ' '.join(m.isecs))
|
||||
return l
|
||||
|
||||
return False
|
||||
|
||||
def save(self, input, output):
|
||||
lines = open(input).read().splitlines()
|
||||
lines = self.__replace__(lines)
|
||||
open(output, 'w+').write('\n'.join(lines))
|
||||
|
||||
def main():
|
||||
argparser = argparse.ArgumentParser(description='Relinker script generator')
|
||||
|
||||
argparser.add_argument(
|
||||
'--input', '-i',
|
||||
help='Linker template file',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--output', '-o',
|
||||
help='Output linker script',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--library', '-l',
|
||||
help='Library description directory',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--object', '-b',
|
||||
help='Object description file',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--function', '-f',
|
||||
help='Function description file',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--sdkconfig', '-s',
|
||||
help='sdkconfig file',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--objdump', '-g',
|
||||
help='GCC objdump command',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--debug', '-d',
|
||||
help='Debug level(option is \'debug\')',
|
||||
default='no',
|
||||
type=str)
|
||||
|
||||
argparser.add_argument(
|
||||
'--missing_function_info',
|
||||
help='Print error information instead of throwing exception when missing function',
|
||||
default=False,
|
||||
type=bool)
|
||||
|
||||
args = argparser.parse_args()
|
||||
|
||||
if args.debug == 'debug':
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
logging.debug('input: %s'%(args.input))
|
||||
logging.debug('output: %s'%(args.output))
|
||||
logging.debug('library: %s'%(args.library))
|
||||
logging.debug('object: %s'%(args.object))
|
||||
logging.debug('function: %s'%(args.function))
|
||||
logging.debug('sdkconfig:%s'%(args.sdkconfig))
|
||||
logging.debug('objdump: %s'%(args.objdump))
|
||||
logging.debug('debug: %s'%(args.debug))
|
||||
logging.debug('missing_function_info: %s'%(args.missing_function_info))
|
||||
|
||||
global espidf_objdump
|
||||
espidf_objdump = args.objdump
|
||||
|
||||
relink = relink_c(args.input, args.library, args.object, args.function, args.sdkconfig, args.missing_function_info)
|
||||
relink.save(args.input, args.output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,8 @@
|
||||
# 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(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
|
||||
"../../cmake_utilities")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(cmake_utilities_test_apps)
|
||||
@@ -0,0 +1,10 @@
|
||||
idf_component_register( SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES cmake_utilities)
|
||||
|
||||
include(gcc)
|
||||
include(gen_compressed_ota)
|
||||
include(gen_single_bin)
|
||||
include(package_manager)
|
||||
include(relinker)
|
||||
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
|
||||
@@ -0,0 +1,6 @@
|
||||
version: "3.2.1"
|
||||
description: Test2 for cmake utilities
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/tools/cmake_utilities
|
||||
issues: https://github.com/espressif/esp-iot-solution/issues
|
||||
dependencies:
|
||||
idf: ">=4.1"
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int test_component2_version_major()
|
||||
{
|
||||
return TEST_COMPONENT2_VER_MAJOR;
|
||||
}
|
||||
|
||||
int test_component2_version_minor()
|
||||
{
|
||||
return TEST_COMPONENT2_VER_MINOR;
|
||||
}
|
||||
|
||||
int test_component2_version_patch()
|
||||
{
|
||||
return TEST_COMPONENT2_VER_PATCH;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <stdio.h>
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int test_component2_version_major();
|
||||
int test_component2_version_minor();
|
||||
int test_component2_version_patch();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
idf_component_register( SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES cmake_utilities)
|
||||
|
||||
include(gcc)
|
||||
include(gen_compressed_ota)
|
||||
include(gen_single_bin)
|
||||
include(package_manager)
|
||||
include(relinker)
|
||||
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
|
||||
@@ -0,0 +1,6 @@
|
||||
version: "1.2.3"
|
||||
description: Test1 for cmake utilities
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/tools/cmake_utilities
|
||||
issues: https://github.com/espressif/esp-iot-solution/issues
|
||||
dependencies:
|
||||
idf: ">=4.1"
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int test_component1_version_major()
|
||||
{
|
||||
return TEST_COMPONENT1_VER_MAJOR;
|
||||
}
|
||||
|
||||
int test_component1_version_minor()
|
||||
{
|
||||
return TEST_COMPONENT1_VER_MINOR;
|
||||
}
|
||||
|
||||
int test_component1_version_patch()
|
||||
{
|
||||
return TEST_COMPONENT1_VER_PATCH;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <stdio.h>
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int test_component1_version_major();
|
||||
int test_component1_version_minor();
|
||||
int test_component1_version_patch();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES unity test_utils test_component1 TEST-component2)
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "test_component1.h"
|
||||
#include "test_component2.h"
|
||||
|
||||
|
||||
/* USB PIN fixed in esp32-s2, can not use io matrix */
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-400)
|
||||
|
||||
TEST_CASE("Test package manager version", "[cmake_utilities][package_manager]")
|
||||
{
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
TEST_ASSERT_EQUAL_INT(test_component1_version_major(), 1);
|
||||
TEST_ASSERT_EQUAL_INT(test_component1_version_minor(), 2);
|
||||
TEST_ASSERT_EQUAL_INT(test_component1_version_patch(), 3);
|
||||
TEST_ASSERT_EQUAL_INT(test_component2_version_major(), 3);
|
||||
TEST_ASSERT_EQUAL_INT(test_component2_version_minor(), 2);
|
||||
TEST_ASSERT_EQUAL_INT(test_component2_version_patch(), 1);
|
||||
}
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("Cmake Utilities TEST \n");
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
'''
|
||||
Steps to run these cases:
|
||||
- Build
|
||||
- . ${IDF_PATH}/export.sh
|
||||
- pip install idf_build_apps
|
||||
- python tools/build_apps.py tools/cmake_utilities/test_apps -t esp32s2
|
||||
- Test
|
||||
- pip install -r tools/requirements/requirement.pytest.txt
|
||||
- pytest tools/cmake_utilities/test_apps --target esp32s2
|
||||
'''
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
@pytest.mark.target('esp32s3')
|
||||
@pytest.mark.env('generic')
|
||||
def test_cmake_utilities(dut: Dut)-> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests.')
|
||||
dut.write('*')
|
||||
dut.expect_unity_test_output(timeout = 1000)
|
||||
@@ -0,0 +1 @@
|
||||
fe8f85b3859544fabb960d7bcec24278cd3c88e3f1777e8a2075b0aca202fd9f
|
||||
@@ -0,0 +1,51 @@
|
||||
# ChangeLog
|
||||
|
||||
## v1.4.2 - 2025-8-26
|
||||
|
||||
### Bug Fix:
|
||||
|
||||
- Remove the dependency of `I2C_BUS_BACKWARD_CONFIG` on the IDF version, and add CMake messages for I2C driver information.
|
||||
|
||||
## V1.4.1 - 2025-8-14
|
||||
|
||||
### Bug Fix:
|
||||
|
||||
- Soft i2c supports removing the restriction on ``NULL_I2C_MEM_ADDR``, allowing users to refer to all eligible register addresses.
|
||||
- Modify the `ESP_IDF_VERSION` naming in Kconfig to avoid conflicts with other components.
|
||||
|
||||
## v1.4.0 - 2025-3-13
|
||||
|
||||
### Enhancements:
|
||||
|
||||
- Support removing the restriction on ``NULL_I2C_MEM_ADDR``, allowing users to refer to all eligible register addresses.
|
||||
|
||||
## v1.3.0 - 2025-2-13
|
||||
|
||||
### Enhancements:
|
||||
|
||||
- ``i2c_bus_v2`` supports initialization using the bus_handle provided by ``esp_driver_i2c``, and also supports returning the internal bus_handle of ``esp_driver_i2c``.
|
||||
|
||||
## v1.2.0 - 2025-1-14
|
||||
|
||||
### Enhancements:
|
||||
|
||||
- Support enabling software I2C to extend the number of I2C ports.
|
||||
|
||||
## v1.1.0 - 2024-11-22
|
||||
|
||||
### Enhancements:
|
||||
|
||||
- Support manual selection of ``driver/i2c`` or ``esp_driver_i2c`` in idf v5.3 and above.
|
||||
|
||||
## v1.0.0 - 2024-9-19
|
||||
|
||||
### Enhancements:
|
||||
|
||||
- Component version maintenance and documentation enhancement.
|
||||
- Support `esp_driver_i2c` driver.
|
||||
|
||||
## v0.1.0 - 2024-5-27
|
||||
|
||||
First release version.
|
||||
|
||||
- Support I2C bus
|
||||
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-08-27T10:45:51.231944+00:00", "files": [{"path": "CHANGELOG.md", "size": 1261, "hash": "3b1da89b95b6772f863f63da4d4c4c82001f28b5c93ea572c67089f657761578"}, {"path": "CMakeLists.txt", "size": 753, "hash": "fd22669ac3cece12a92742949c4608fb2e1da1439a8739a4a90c194dbc984c7b"}, {"path": "Kconfig", "size": 1931, "hash": "d25036af205c7f65f7e59938d28cc9526fd40a676b22ce3ff5da0dffc65827c8"}, {"path": "README.md", "size": 2910, "hash": "0dad1b9de95d531ddbbdf5daacd8b8ad0db8a908c0e1f716ef623d803995054c"}, {"path": "i2c_bus.c", "size": 25008, "hash": "f4f27a81aa3bca438b75240be396812b50f014a77e73f2e564ff54735559dd82"}, {"path": "i2c_bus_soft.c", "size": 15138, "hash": "3781e4dbb8971cbe94942a7a492042d0d30eef0a0b1a987f04f0e765264dcd6b"}, {"path": "i2c_bus_v2.c", "size": 27979, "hash": "ada5ca779030fcb26d7dd44955e6b3d348eae4d24cf54a4152c8f7c7e7536227"}, {"path": "idf_component.yml", "size": 543, "hash": "6ea3402fd8b698b1f2d508917af9cd9447c55dbfacce41d56757096005399205"}, {"path": "license.txt", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "include/i2c_bus.h", "size": 15491, "hash": "c5dd23aa628ab072f6e2b7edfa7e00fcc4233d7d234564c97156415447cb4833"}, {"path": "private_include/i2c_bus_soft.h", "size": 5089, "hash": "32433f38ca82faee70bfba1da0e1ad2fedea7c8477ab602fd337962ac4aec8f6"}, {"path": "test_apps/CMakeLists.txt", "size": 352, "hash": "7216690bc36de976c448f44ed31931ef8457acda922537c5e895f73897726cbe"}, {"path": "test_apps/pytest_i2c_bus.py", "size": 759, "hash": "910d205af99012638fae7c71356f79aa23a60655cf72d1df43ac0b1348468285"}, {"path": "test_apps/sdkconfig.defaults", "size": 257, "hash": "b3f9660085595b907f7411e205468220c2dfb867de4de5d7fb3cbb406651dcab"}, {"path": "test_apps/main/CMakeLists.txt", "size": 144, "hash": "82bd519669b040da7894ea50dc37fa85d2055a009e511ececa92a4e93497b4a1"}, {"path": "test_apps/main/test_i2c_bus.c", "size": 21112, "hash": "a5aa7e26f41845ad597867c9ce5e83a90975049cbc8a83a39434288b3d8ba084"}]}
|
||||
@@ -0,0 +1,21 @@
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "5.3" OR CONFIG_I2C_BUS_BACKWARD_CONFIG)
|
||||
set(SRC_FILE "i2c_bus.c")
|
||||
set(REQ driver)
|
||||
message(STATUS "Using driver/i2c (SRC_FILE=i2c_bus.c, REQ=driver)")
|
||||
else()
|
||||
set(SRC_FILE "i2c_bus_v2.c")
|
||||
set(REQ esp_driver_i2c driver)
|
||||
message(STATUS "Using esp_driver_i2c (SRC_FILE=i2c_bus_v2.c, REQ=esp_driver_i2c driver)")
|
||||
endif()
|
||||
|
||||
if (CONFIG_I2C_BUS_SUPPORT_SOFTWARE)
|
||||
list(APPEND SRC_FILE "i2c_bus_soft.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${SRC_FILE}
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "private_include"
|
||||
REQUIRES ${REQ})
|
||||
|
||||
include(package_manager)
|
||||
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
|
||||
@@ -0,0 +1,48 @@
|
||||
menu "Bus Options"
|
||||
|
||||
menu "I2C Bus Options"
|
||||
config I2C_BUS_DYNAMIC_CONFIG
|
||||
bool "enable dynamic configuration"
|
||||
default y
|
||||
help
|
||||
If enable, i2c_bus will dynamically check configs and re-install i2c driver before each transfer,
|
||||
hence multiple devices with different configs on a single bus can be supported.
|
||||
|
||||
config I2C_MS_TO_WAIT
|
||||
int "mutex block time"
|
||||
default 200
|
||||
range 50 5000
|
||||
help
|
||||
task block time when try to take the bus, unit:milliseconds
|
||||
|
||||
config I2C_BUS_BACKWARD_CONFIG
|
||||
bool "Enable backward compatibility for the I2C driver (force use of the old i2c_driver above v5.3)"
|
||||
default n
|
||||
help
|
||||
Enable this option for backward compatibility with the old I2C driver (only valid in IDF v5.3 and above).
|
||||
|
||||
config I2C_BUS_SUPPORT_SOFTWARE
|
||||
bool "Enable software I2C support"
|
||||
default n
|
||||
help
|
||||
Enable this option to use a software-implemented I2C driver. This can be useful for scenarios where
|
||||
hardware I2C is unavailable or additional I2C buses are needed beyond the hardware support.
|
||||
|
||||
config I2C_BUS_SOFTWARE_MAX_PORT
|
||||
int "Maximum number of software I2C ports"
|
||||
default 2
|
||||
range 1 5
|
||||
depends on I2C_BUS_SUPPORT_SOFTWARE
|
||||
help
|
||||
Set the maximum number of software I2C ports that can be used. This option is only applicable when
|
||||
software I2C support is enabled.
|
||||
|
||||
config I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
bool "Remove the limitation of NULL_MEM_ADDR, any register address will be sent"
|
||||
default n
|
||||
help
|
||||
Enable this option to disable NULL_MEM_ADDR. This allows any register address to be sent.
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,70 @@
|
||||
# Component: I2C BUS
|
||||
[Online documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/basic/bus/i2c_bus.html)
|
||||
|
||||
The I2C bus component (Bus) is a set of application-layer code built on top of the ESP-IDF peripheral driver code, It is mainly used for bus communication between ESP chips and external devices. From the point of application development, this component has the following features:
|
||||
|
||||
1. Simplified peripheral initialization processes
|
||||
2. Thread-safe device operations
|
||||
3. Simple and flexible RW operations
|
||||
4. Compatible with `driver/i2c` and `esp_driver_i2c`
|
||||
5. Supports additional software I2C
|
||||
|
||||
This component abstracts the following concepts:
|
||||
|
||||
1. Bus: the resource and configuration option shared between devices during communication
|
||||
2. Device: device specific resource and configuration option during communication
|
||||
|
||||
Each physical peripheral bus can mount one or more devices if the electrical condition allows, the I2C bus addressing devices based on their addresses, thus achieving software independence between different devices on the same bus.
|
||||
|
||||
## Add component to your project
|
||||
|
||||
Please use the component manager command `add-dependency` to add the `i2c_bus` to your project's dependency, during the `CMake` step the component will be downloaded automatically
|
||||
|
||||
```
|
||||
idf.py add-dependency "espressif/i2c_bus=*"
|
||||
```
|
||||
|
||||
Alternatively, you can create `idf_component.yml`. More is in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
|
||||
|
||||
## Example use
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "i2c_bus.h"
|
||||
|
||||
#define I2C_MASTER_SCL_IO (gpio_num_t)15 /*!< gpio number for I2C master clock */
|
||||
#define I2C_MASTER_SDA_IO (gpio_num_t)16 /*!< gpio number for I2C master data */
|
||||
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
|
||||
#define ESP_SLAVE_ADDR 0x28 /*!< ESP32 slave address, you can set any 7bit value */
|
||||
#define DATA_LENGTH 64 /*!<Data buffer length for test buffer*/
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
|
||||
uint8_t *data_wr = (uint8_t *)malloc(DATA_LENGTH);
|
||||
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
|
||||
i2c_bus_handle_t i2c0_bus = i2c_bus_create(I2C_NUM_0, &conf);
|
||||
i2c_bus_device_handle_t i2c0_device1 = i2c_bus_device_create(i2c0_bus, ESP_SLAVE_ADDR, 0);
|
||||
for (int i = 0; i < DATA_LENGTH; i++)
|
||||
{
|
||||
data_wr[i] = i;
|
||||
}
|
||||
|
||||
i2c_bus_write_bytes(i2c0_device1, NULL_I2C_MEM_ADDR, DATA_LENGTH, data_wr);
|
||||
free(data_wr);
|
||||
i2c_bus_device_delete(&i2c0_device1);
|
||||
i2c_bus_delete(&i2c0_bus);
|
||||
}
|
||||
|
||||
```
|
||||
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "i2c_bus.h"
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
#include "i2c_bus_soft.h"
|
||||
#endif
|
||||
|
||||
#define I2C_ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define I2C_ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define I2C_BUS_FLG_DEFAULT (0)
|
||||
#define I2C_BUS_MASTER_BUF_LEN (0)
|
||||
#define I2C_BUS_MS_TO_WAIT CONFIG_I2C_MS_TO_WAIT
|
||||
#define I2C_BUS_TICKS_TO_WAIT (I2C_BUS_MS_TO_WAIT/portTICK_RATE_MS)
|
||||
#define I2C_BUS_MUTEX_TICKS_TO_WAIT (I2C_BUS_MS_TO_WAIT/portTICK_RATE_MS)
|
||||
|
||||
typedef struct {
|
||||
i2c_port_t i2c_port; /*!< I2C port number */
|
||||
bool is_init; /*!< if bus is initialized */
|
||||
i2c_config_t conf_active; /*!< I2C active configuration */
|
||||
SemaphoreHandle_t mutex; /*!< mutex to achieve thread-safe */
|
||||
int32_t ref_counter; /*!< reference count */
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
i2c_master_soft_bus_handle_t soft_bus_handle; /*!< I2C master soft bus handle */
|
||||
#endif
|
||||
} i2c_bus_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t dev_addr; /*!< device address */
|
||||
i2c_config_t conf; /*!< I2C active configuration */
|
||||
i2c_bus_t *i2c_bus; /*!< I2C bus */
|
||||
} i2c_bus_device_t;
|
||||
|
||||
static const char *TAG = "i2c_bus";
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
static i2c_bus_t s_i2c_bus[I2C_NUM_SW_MAX]; /*!< If software I2C is enabled, additional space is required to store the port. */
|
||||
#else
|
||||
static i2c_bus_t s_i2c_bus[I2C_NUM_MAX];
|
||||
#endif
|
||||
|
||||
#define I2C_BUS_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_CHECK_GOTO(a, str, label) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
goto label; \
|
||||
}
|
||||
|
||||
#define I2C_BUS_INIT_CHECK(is_init, ret) if(!is_init) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):i2c_bus has not inited", __FILE__, __LINE__, __FUNCTION__); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_MUTEX_TAKE(mutex, ret) if (!xSemaphoreTake(mutex, I2C_BUS_MUTEX_TICKS_TO_WAIT)) { \
|
||||
ESP_LOGE(TAG, "i2c_bus take mutex timeout, max wait = %"PRIu32"ms", I2C_BUS_MUTEX_TICKS_TO_WAIT); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_MUTEX_TAKE_MAX_DELAY(mutex, ret) if (!xSemaphoreTake(mutex, portMAX_DELAY)) { \
|
||||
ESP_LOGE(TAG, "i2c_bus take mutex timeout, max wait = %"PRIu32"ms", portMAX_DELAY); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_MUTEX_GIVE(mutex, ret) if (!xSemaphoreGive(mutex)) { \
|
||||
ESP_LOGE(TAG, "i2c_bus give mutex failed"); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
static esp_err_t i2c_driver_reinit(i2c_port_t port, const i2c_config_t *conf);
|
||||
static esp_err_t i2c_driver_deinit(i2c_port_t port);
|
||||
static esp_err_t i2c_bus_write_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data);
|
||||
static esp_err_t i2c_bus_read_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data);
|
||||
inline static bool i2c_config_compare(i2c_port_t port, const i2c_config_t *conf);
|
||||
/**************************************** Public Functions (Application level)*********************************************/
|
||||
|
||||
i2c_bus_handle_t i2c_bus_create(i2c_port_t port, const i2c_config_t *conf)
|
||||
{
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", NULL);
|
||||
#else
|
||||
I2C_BUS_CHECK(port < I2C_NUM_MAX, "I2C port error", NULL);
|
||||
#endif
|
||||
I2C_BUS_CHECK(conf != NULL, "pointer = NULL error", NULL);
|
||||
I2C_BUS_CHECK(conf->mode == I2C_MODE_MASTER, "i2c_bus only supports master mode", NULL);
|
||||
|
||||
if (s_i2c_bus[port].is_init) {
|
||||
/**if i2c_bus has been inited and configs not changed, return the handle directly**/
|
||||
if (i2c_config_compare(port, conf)) {
|
||||
ESP_LOGW(TAG, "i2c%d has been inited, return handle directly, ref_counter=%"PRIi32"", port, s_i2c_bus[port].ref_counter);
|
||||
return (i2c_bus_handle_t)&s_i2c_bus[port];
|
||||
}
|
||||
} else {
|
||||
s_i2c_bus[port].mutex = xSemaphoreCreateMutex();
|
||||
I2C_BUS_CHECK(s_i2c_bus[port].mutex != NULL, "i2c_bus xSemaphoreCreateMutex failed", NULL);
|
||||
s_i2c_bus[port].ref_counter = 0;
|
||||
}
|
||||
|
||||
esp_err_t ret = i2c_driver_reinit(port, conf);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "init error", NULL);
|
||||
s_i2c_bus[port].conf_active = *conf;
|
||||
s_i2c_bus[port].i2c_port = port;
|
||||
ESP_LOGI(TAG, "I2C Bus Config Succeed, Version: %d.%d.%d", I2C_BUS_VER_MAJOR, I2C_BUS_VER_MINOR, I2C_BUS_VER_PATCH);
|
||||
return (i2c_bus_handle_t)&s_i2c_bus[port];
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_delete(i2c_bus_handle_t *p_bus)
|
||||
{
|
||||
I2C_BUS_CHECK(p_bus != NULL && *p_bus != NULL, "pointer = NULL error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)(*p_bus);
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, ESP_FAIL);
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
|
||||
/** if ref_counter == 0, de-init the bus**/
|
||||
if ((i2c_bus->ref_counter) > 0) {
|
||||
ESP_LOGW(TAG, "i2c%d is also handled by others ref_counter=%"PRIi32", won't be de-inited", i2c_bus->i2c_port, i2c_bus->ref_counter);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ret = i2c_driver_deinit(i2c_bus->i2c_port);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "deinit error", ret);
|
||||
vSemaphoreDelete(i2c_bus->mutex);
|
||||
*p_bus = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t i2c_bus_scan(i2c_bus_handle_t bus_handle, uint8_t *buf, uint8_t num)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Handle error", 0);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, 0);
|
||||
uint8_t device_count = 0;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, 0);
|
||||
|
||||
for (uint8_t dev_address = 1; dev_address < 127; dev_address++) {
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (i2c_bus->i2c_port > I2C_NUM_MAX) {
|
||||
ret = i2c_master_soft_bus_probe(i2c_bus->soft_bus_handle, dev_address);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (dev_address << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT);
|
||||
}
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "found i2c device address = 0x%02x", dev_address);
|
||||
if (buf != NULL && device_count < num) {
|
||||
*(buf + device_count) = dev_address;
|
||||
}
|
||||
device_count++;
|
||||
}
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_bus->mutex, 0);
|
||||
return device_count;
|
||||
}
|
||||
|
||||
uint32_t i2c_bus_get_current_clk_speed(i2c_bus_handle_t bus_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", 0);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, 0);
|
||||
return i2c_bus->conf_active.master.clk_speed;
|
||||
}
|
||||
|
||||
uint8_t i2c_bus_get_created_device_num(i2c_bus_handle_t bus_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", 0);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, 0);
|
||||
return i2c_bus->ref_counter;
|
||||
}
|
||||
|
||||
i2c_bus_device_handle_t i2c_bus_device_create(i2c_bus_handle_t bus_handle, uint8_t dev_addr, uint32_t clk_speed)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", NULL);
|
||||
I2C_BUS_CHECK(clk_speed <= 400000, "clk_speed must <= 400000", NULL);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, NULL);
|
||||
i2c_bus_device_t *i2c_device = calloc(1, sizeof(i2c_bus_device_t));
|
||||
I2C_BUS_CHECK(i2c_device != NULL, "calloc memory failed", NULL);
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, NULL);
|
||||
i2c_device->dev_addr = dev_addr;
|
||||
i2c_device->conf = i2c_bus->conf_active;
|
||||
|
||||
/*if clk_speed == 0, current active clock speed will be used, else set a specified value*/
|
||||
if (clk_speed != 0) {
|
||||
i2c_device->conf.master.clk_speed = clk_speed;
|
||||
}
|
||||
|
||||
i2c_device->i2c_bus = i2c_bus;
|
||||
i2c_bus->ref_counter++;
|
||||
I2C_BUS_MUTEX_GIVE(i2c_bus->mutex, NULL);
|
||||
return (i2c_bus_device_handle_t)i2c_device;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_device_delete(i2c_bus_device_handle_t *p_dev_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(p_dev_handle != NULL && *p_dev_handle != NULL, "Null Device Handle", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)(*p_dev_handle);
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
i2c_device->i2c_bus->ref_counter--;
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
free(i2c_device);
|
||||
*p_dev_handle = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t i2c_bus_device_get_address(i2c_bus_device_handle_t dev_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", NULL_I2C_DEV_ADDR);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
return i2c_device->dev_addr;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_bytes(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
return i2c_bus_read_reg8(dev_handle, mem_address, data_len, data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t *data)
|
||||
{
|
||||
return i2c_bus_read_reg8(dev_handle, mem_address, 1, data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_bit(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_num, uint8_t *data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_reg8(dev_handle, mem_address, 1, &byte);
|
||||
*data = byte & (1 << bit_num);
|
||||
*data = (*data != 0) ? 1 : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_bits(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_start, uint8_t length, uint8_t *data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_byte(dev_handle, mem_address, &byte);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t mask = ((1 << length) - 1) << (bit_start - length + 1);
|
||||
byte &= mask;
|
||||
byte >>= (bit_start - length + 1);
|
||||
*data = byte;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t data)
|
||||
{
|
||||
return i2c_bus_write_reg8(dev_handle, mem_address, 1, &data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_bytes(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
return i2c_bus_write_reg8(dev_handle, mem_address, data_len, data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_bit(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_num, uint8_t data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_byte(dev_handle, mem_address, &byte);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
byte = (data != 0) ? (byte | (1 << bit_num)) : (byte & ~(1 << bit_num));
|
||||
return i2c_bus_write_byte(dev_handle, mem_address, byte);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_bits(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_start, uint8_t length, uint8_t data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_byte(dev_handle, mem_address, &byte);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t mask = ((1 << length) - 1) << (bit_start - length + 1);
|
||||
data <<= (bit_start - length + 1); // shift data into correct position
|
||||
data &= mask; // zero all non-important bits in data
|
||||
byte &= ~(mask); // zero all important bits in existing byte
|
||||
byte |= data; // combine data with existing byte
|
||||
return i2c_bus_write_byte(dev_handle, mem_address, byte);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C master send queued commands.
|
||||
* This function will trigger sending all queued commands.
|
||||
* The task will be blocked until all the commands have been sent out.
|
||||
* If I2C_BUS_DYNAMIC_CONFIG enable, i2c_bus will dynamically check configs and re-install i2c driver before each transfer,
|
||||
* hence multiple devices with different configs on a single bus can be supported.
|
||||
* @note
|
||||
* Only call this function in I2C master mode
|
||||
*
|
||||
* @param i2c_num I2C port number
|
||||
* @param cmd_handle I2C command handler
|
||||
* @param ticks_to_wait maximum wait ticks.
|
||||
* @param conf pointer to I2C parameter settings
|
||||
* @return esp_err_t
|
||||
*/
|
||||
inline static esp_err_t i2c_master_cmd_begin_with_conf(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, TickType_t ticks_to_wait, const i2c_config_t *conf)
|
||||
{
|
||||
esp_err_t ret;
|
||||
#ifdef CONFIG_I2C_BUS_DYNAMIC_CONFIG
|
||||
/*if configs changed, i2c driver will reinit with new configuration*/
|
||||
if (conf != NULL && false == i2c_config_compare(i2c_num, conf)) {
|
||||
ret = i2c_driver_reinit(i2c_num, conf);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "reinit error", ret);
|
||||
s_i2c_bus[i2c_num].conf_active = *conf;
|
||||
}
|
||||
#endif
|
||||
ret = i2c_master_cmd_begin(i2c_num, cmd_handle, ticks_to_wait);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**************************************** Public Functions (Low level)*********************************************/
|
||||
|
||||
esp_err_t i2c_bus_cmd_begin(i2c_bus_device_handle_t dev_handle, i2c_cmd_handle_t cmd)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(cmd != NULL, "I2C command error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
esp_err_t ret = i2c_master_cmd_begin_with_conf(i2c_device->i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT, &i2c_device->conf);
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_bus_read_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (i2c_device->i2c_bus->i2c_port > I2C_NUM_MAX) {
|
||||
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->conf.master.clk_speed);
|
||||
ret = i2c_master_soft_bus_read_reg8(i2c_device->i2c_bus->soft_bus_handle, i2c_device->dev_addr, mem_address, data_len, data);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_ADDR) {
|
||||
#endif
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
|
||||
i2c_master_write_byte(cmd, mem_address, I2C_ACK_CHECK_EN);
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
} else {
|
||||
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_ADDR);
|
||||
}
|
||||
#endif
|
||||
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_READ, I2C_ACK_CHECK_EN);
|
||||
i2c_master_read(cmd, data, data_len, I2C_MASTER_LAST_NACK);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin_with_conf(i2c_device->i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT, &i2c_device->conf);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_reg16(i2c_bus_device_handle_t dev_handle, uint16_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
uint8_t memAddress8[2];
|
||||
esp_err_t ret = ESP_OK;
|
||||
memAddress8[0] = (uint8_t)((mem_address >> 8) & 0x00FF);
|
||||
memAddress8[1] = (uint8_t)(mem_address & 0x00FF);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (i2c_device->i2c_bus->i2c_port > I2C_NUM_MAX) {
|
||||
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->conf.master.clk_speed);
|
||||
ret = i2c_master_soft_bus_read_reg16(i2c_device->i2c_bus->soft_bus_handle, i2c_device->dev_addr, mem_address, data_len, data);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
|
||||
#endif
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
|
||||
i2c_master_write(cmd, memAddress8, 2, I2C_ACK_CHECK_EN);
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
} else {
|
||||
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_16BIT_ADDR);
|
||||
}
|
||||
#endif
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_READ, I2C_ACK_CHECK_EN);
|
||||
i2c_master_read(cmd, data, data_len, I2C_MASTER_LAST_NACK);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin_with_conf(i2c_device->i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT, &i2c_device->conf);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_bus_write_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
esp_err_t ret = ESP_OK;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (i2c_device->i2c_bus->i2c_port > I2C_NUM_MAX) {
|
||||
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->conf.master.clk_speed);
|
||||
ret = i2c_master_soft_bus_write_reg8(i2c_device->i2c_bus->soft_bus_handle, i2c_device->dev_addr, mem_address, data_len, data);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_ADDR) {
|
||||
#endif
|
||||
i2c_master_write_byte(cmd, mem_address, I2C_ACK_CHECK_EN);
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
} else {
|
||||
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_ADDR);
|
||||
}
|
||||
#endif
|
||||
i2c_master_write(cmd, (uint8_t *)data, data_len, I2C_ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin_with_conf(i2c_device->i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT, &i2c_device->conf);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_reg16(i2c_bus_device_handle_t dev_handle, uint16_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
uint8_t memAddress8[2];
|
||||
esp_err_t ret = ESP_OK;
|
||||
memAddress8[0] = (uint8_t)((mem_address >> 8) & 0x00FF);
|
||||
memAddress8[1] = (uint8_t)(mem_address & 0x00FF);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (i2c_device->i2c_bus->i2c_port > I2C_NUM_MAX) {
|
||||
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->conf.master.clk_speed);
|
||||
ret = i2c_master_soft_bus_write_reg16(i2c_device->i2c_bus->soft_bus_handle, i2c_device->dev_addr, mem_address, data_len, data);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, (i2c_device->dev_addr << 1) | I2C_MASTER_WRITE, I2C_ACK_CHECK_EN);
|
||||
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
|
||||
#endif
|
||||
i2c_master_write(cmd, memAddress8, 2, I2C_ACK_CHECK_EN);
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
} else {
|
||||
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_16BIT_ADDR);
|
||||
}
|
||||
#endif
|
||||
|
||||
i2c_master_write(cmd, (uint8_t *)data, data_len, I2C_ACK_CHECK_EN);
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin_with_conf(i2c_device->i2c_bus->i2c_port, cmd, I2C_BUS_TICKS_TO_WAIT, &i2c_device->conf);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**************************************** Private Functions*********************************************/
|
||||
static esp_err_t i2c_driver_reinit(i2c_port_t port, const i2c_config_t *conf)
|
||||
{
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", ESP_ERR_INVALID_ARG);
|
||||
#else
|
||||
I2C_BUS_CHECK(port < I2C_NUM_MAX, "i2c port error", ESP_ERR_INVALID_ARG);
|
||||
#endif
|
||||
I2C_BUS_CHECK(conf != NULL, "pointer = NULL error", ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
if (s_i2c_bus[port].is_init) {
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (port > I2C_NUM_MAX) {
|
||||
i2c_del_master_soft_bus(s_i2c_bus[port].soft_bus_handle);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
i2c_driver_delete(port);
|
||||
}
|
||||
s_i2c_bus[port].is_init = false;
|
||||
ESP_LOGI(TAG, "i2c%d bus deinited", port);
|
||||
}
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (port > I2C_NUM_MAX) {
|
||||
ret = i2c_new_master_soft_bus(conf, &s_i2c_bus[port].soft_bus_handle);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "i2c software driver install failed", ret);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ret = i2c_param_config(port, conf);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "i2c param config failed", ret);
|
||||
ret = i2c_driver_install(port, conf->mode, I2C_BUS_MASTER_BUF_LEN, I2C_BUS_MASTER_BUF_LEN, I2C_BUS_FLG_DEFAULT);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "i2c driver install failed", ret);
|
||||
}
|
||||
s_i2c_bus[port].is_init = true;
|
||||
ESP_LOGI(TAG, "i2c%d bus inited", port);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_driver_deinit(i2c_port_t port)
|
||||
{
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", ESP_ERR_INVALID_ARG);
|
||||
#else
|
||||
I2C_BUS_CHECK(port < I2C_NUM_MAX, "i2c port error", ESP_ERR_INVALID_ARG);
|
||||
#endif
|
||||
I2C_BUS_CHECK(s_i2c_bus[port].is_init == true, "i2c not inited", ESP_ERR_INVALID_STATE);
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (port > I2C_NUM_MAX) {
|
||||
esp_err_t ret = i2c_del_master_soft_bus(s_i2c_bus[port].soft_bus_handle);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "i2c software driver delete failed", ret);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
i2c_driver_delete(port); //always return ESP_OK
|
||||
}
|
||||
s_i2c_bus[port].is_init = false;
|
||||
ESP_LOGI(TAG, "i2c%d bus deinited", port);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief compare with active i2c_bus configuration
|
||||
*
|
||||
* @param port choose which i2c_port's configuration will be compared
|
||||
* @param conf new configuration
|
||||
* @return true new configuration is equal to active configuration
|
||||
* @return false new configuration is not equal to active configuration
|
||||
*/
|
||||
inline static bool i2c_config_compare(i2c_port_t port, const i2c_config_t *conf)
|
||||
{
|
||||
if (s_i2c_bus[port].conf_active.master.clk_speed == conf->master.clk_speed
|
||||
&& s_i2c_bus[port].conf_active.sda_io_num == conf->sda_io_num
|
||||
&& s_i2c_bus[port].conf_active.scl_io_num == conf->scl_io_num
|
||||
&& s_i2c_bus[port].conf_active.scl_pullup_en == conf->scl_pullup_en
|
||||
&& s_i2c_bus[port].conf_active.sda_pullup_en == conf->sda_pullup_en) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "i2c_bus_soft.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
static const char*TAG = "i2c_bus_soft";
|
||||
|
||||
static esp_err_t i2c_master_soft_bus_wait_ack(i2c_master_soft_bus_handle_t bus_handle)
|
||||
{
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
|
||||
bool ack = !gpio_get_level(bus_handle->sda_io); /*!< SDA should be low for ACK */
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 0), TAG, "Failed to set SCL low");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
|
||||
return ack ? ESP_OK : ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_master_soft_bus_send_ack(i2c_master_soft_bus_handle_t bus_handle, bool ack)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, ack ? 0 : 1), TAG, "Failed to set SDA for ACK/NACK"); /*!< Set SDA line to ACK (low) or NACK (high) */
|
||||
|
||||
// Generate clock pulse for ACK/NACK
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high during ACK/NACK");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 0), TAG, "Failed to set SCL low after ACK/NACK");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_master_soft_bus_start(i2c_master_soft_bus_handle_t bus_handle)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high");
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 1), TAG, "Failed to set SDA high");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 0), TAG, "Failed to set SDA low");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 0), TAG, "Failed to set SCL low");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_master_soft_bus_stop(i2c_master_soft_bus_handle_t bus_handle)
|
||||
{
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 0), TAG, "Failed to set SDA low");
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 1), TAG, "Failed to set SDA high");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_master_soft_bus_write_byte(i2c_master_soft_bus_handle_t bus_handle, uint8_t byte)
|
||||
{
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (byte & 0x80) {
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 1), TAG, "Failed to set SDA high");
|
||||
} else {
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 0), TAG, "Failed to set SDA low");
|
||||
}
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 0), TAG, "Failed to set SCL low");
|
||||
|
||||
if (i == 7) {
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 1), TAG, "Failed to release SDA"); /*!< Release SDA */
|
||||
}
|
||||
|
||||
byte <<= 1;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_master_soft_bus_read_byte(i2c_master_soft_bus_handle_t bus_handle, uint8_t *byte)
|
||||
{
|
||||
uint8_t value = 0;
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->sda_io, 1), TAG, "Failed to release SDA"); /*!< First release SDA */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
value <<= 1;
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 1), TAG, "Failed to set SCL high");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
|
||||
if (gpio_get_level(bus_handle->sda_io)) {
|
||||
value ++;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(bus_handle->scl_io, 0), TAG, "Failed to set SCL low");
|
||||
esp_rom_delay_us(bus_handle->time_delay_us);
|
||||
}
|
||||
|
||||
*byte = value;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_master_soft_bus_write_reg8(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint8_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate start signal");
|
||||
|
||||
// Send device address with write bit (0)
|
||||
uint8_t address_byte = (dev_addr << 1) | 0;
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, address_byte), TAG, "Failed to write device address");
|
||||
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
|
||||
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_ADDR) {
|
||||
#endif
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, mem_address), TAG, "Failed to write memory address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
}
|
||||
#endif
|
||||
// Write data
|
||||
for (size_t i = 0; i < data_len; i++) {
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, data[i]), TAG, "Failed to write data byte");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for data byte");
|
||||
}
|
||||
|
||||
// Generate STOP condition
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_stop(bus_handle), TAG, "Failed to initiate stop signal");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_master_soft_bus_write_reg16(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint16_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate start signal");
|
||||
|
||||
// Send device address with write bit (0)
|
||||
uint8_t address_byte = (dev_addr << 1) | 0;
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, address_byte), TAG, "Failed to write device address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
|
||||
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
|
||||
#endif
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, (uint8_t)((mem_address >> 8) & 0x00FF)), TAG, "Failed to write memory address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for mem address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, (uint8_t)(mem_address & 0x00FF)), TAG, "Failed to write memory address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for mem address");
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write data
|
||||
for (size_t i = 0; i < data_len; i++) {
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, data[i]), TAG, "Failed to write data byte");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for data byte");
|
||||
}
|
||||
|
||||
// Generate STOP condition
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_stop(bus_handle), TAG, "Failed to initiate stop signal");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_master_soft_bus_read_reg8(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint8_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
|
||||
|
||||
// Send memory address
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_ADDR) {
|
||||
#endif
|
||||
// Generate START condition
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate start signal");
|
||||
|
||||
// Send device address with write bit (0) to write the memory address
|
||||
uint8_t write_address_byte = (dev_addr << 1) | 0;
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, write_address_byte), TAG, "Failed to write device address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, mem_address), TAG, "Failed to write memory address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for mem address");
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
}
|
||||
#endif
|
||||
|
||||
// Generate RESTART condition
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate repeated start signal");
|
||||
|
||||
// Send device address with read bit (1)
|
||||
uint8_t read_address_byte = (dev_addr << 1) | 1;
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, read_address_byte), TAG, "Failed to write device address with read bit");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
|
||||
|
||||
// Read data bytes
|
||||
for (size_t i = 0; i < data_len; i++) {
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_read_byte(bus_handle, &data[i]), TAG, "Failed to read data byte");
|
||||
|
||||
// Send ACK for all but the last byte
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_send_ack(bus_handle, i != data_len - 1), TAG, "Failed to send ACK/NACK");
|
||||
}
|
||||
|
||||
// Generate STOP condition
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_stop(bus_handle), TAG, "Failed to initiate stop signal");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_master_soft_bus_read_reg16(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint16_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
|
||||
|
||||
// Send memory address
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
|
||||
#endif
|
||||
// Generate START condition
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate start signal");
|
||||
|
||||
// Send device address with write bit (0) to write the memory address
|
||||
uint8_t write_address_byte = (dev_addr << 1) | 0;
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, write_address_byte), TAG, "Failed to write device address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, (uint8_t)((mem_address >> 8) & 0x00FF)), TAG, "Failed to write memory address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for mem address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, (uint8_t)(mem_address & 0x00FF)), TAG, "Failed to write memory address");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for mem address");
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
}
|
||||
#endif
|
||||
|
||||
// Generate RESTART condition
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate repeated start signal");
|
||||
|
||||
// Send device address with read bit (1)
|
||||
uint8_t read_address_byte = (dev_addr << 1) | 1;
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, read_address_byte), TAG, "Failed to write device address with read bit");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "No ACK for device address during read");
|
||||
|
||||
// Read data bytes
|
||||
for (size_t i = 0; i < data_len; i++) {
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_read_byte(bus_handle, &data[i]), TAG, "Failed to read data byte");
|
||||
|
||||
// Send ACK for all but the last byte
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_send_ack(bus_handle, i != data_len - 1), TAG, "Failed to send ACK/NACK");
|
||||
}
|
||||
|
||||
// Generate STOP condition
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_stop(bus_handle), TAG, "Failed to initiate stop signal");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_master_soft_bus_probe(i2c_master_soft_bus_handle_t bus_handle, uint8_t address)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_start(bus_handle), TAG, "Failed to initiate start signal");
|
||||
|
||||
// Send device address with write bit (0)
|
||||
uint8_t address_byte = (address << 1) | 0;
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_write_byte(bus_handle, address_byte), TAG, "Failed to write address byte");
|
||||
|
||||
// Wait for ACK
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_wait_ack(bus_handle), TAG, "Failed to wait for ACK");
|
||||
|
||||
// Generate STOP condition
|
||||
ESP_RETURN_ON_ERROR(i2c_master_soft_bus_stop(bus_handle), TAG, "Failed to initiate stop signal");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_new_master_soft_bus(const i2c_config_t *conf, i2c_master_soft_bus_handle_t *ret_soft_bus_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(conf->scl_io_num) && GPIO_IS_VALID_GPIO(conf->sda_io_num), ESP_ERR_INVALID_ARG, TAG, "Invalid SDA/SCL pin number");
|
||||
ESP_RETURN_ON_FALSE(conf->master.clk_speed > 0, ESP_ERR_INVALID_ARG, TAG, "Invalid scl frequency");
|
||||
|
||||
gpio_config_t scl_io_conf = {
|
||||
.mode = GPIO_MODE_OUTPUT_OD,
|
||||
.pull_up_en = conf->scl_pullup_en,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.pin_bit_mask = (1ULL << conf->scl_io_num),
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&scl_io_conf), TAG, "Failed to configure scl gpio");
|
||||
|
||||
gpio_config_t sda_io_conf = {
|
||||
.mode = GPIO_MODE_INPUT_OUTPUT_OD,
|
||||
.pull_up_en = conf->sda_pullup_en,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.pin_bit_mask = (1ULL << conf->sda_io_num),
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&sda_io_conf), TAG, "Failed to configure sda gpio");
|
||||
|
||||
i2c_master_soft_bus_handle_t soft_bus_handle = calloc(1, sizeof(struct i2c_master_soft_bus_t));
|
||||
if (soft_bus_handle == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate soft bus handle");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
soft_bus_handle->scl_io = conf->scl_io_num;
|
||||
soft_bus_handle->sda_io = conf->sda_io_num;
|
||||
soft_bus_handle->time_delay_us = (uint32_t)((1e6f / conf->master.clk_speed) / 2.0f + 0.5f);
|
||||
*ret_soft_bus_handle = soft_bus_handle;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_master_soft_bus_change_frequency(i2c_master_soft_bus_handle_t bus_handle, uint32_t frequency)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "Invalid I2C bus handle");
|
||||
ESP_RETURN_ON_FALSE(frequency > 0, ESP_ERR_INVALID_ARG, TAG, "Invalid scl frequency");
|
||||
bus_handle->time_delay_us = (uint32_t)((1e6f / frequency) / 2.0f + 0.5f);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2c_del_master_soft_bus(i2c_master_soft_bus_handle_t bus_handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(bus_handle, ESP_ERR_INVALID_ARG, TAG, "no memory for i2c master soft bus");
|
||||
free(bus_handle);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,608 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "i2c_bus.h"
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
#include "i2c_bus_soft.h"
|
||||
#endif
|
||||
|
||||
#define I2C_ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define I2C_ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define I2C_BUS_FLG_DEFAULT (0)
|
||||
#define I2C_BUS_MASTER_BUF_LEN (0)
|
||||
#define I2C_BUS_MS_TO_WAIT CONFIG_I2C_MS_TO_WAIT
|
||||
#define I2C_BUS_TICKS_TO_WAIT (I2C_BUS_MS_TO_WAIT/portTICK_PERIOD_MS)
|
||||
#define I2C_BUS_MUTEX_TICKS_TO_WAIT (I2C_BUS_MS_TO_WAIT/portTICK_PERIOD_MS)
|
||||
|
||||
typedef struct {
|
||||
i2c_master_bus_config_t bus_config; /*!< I2C master bus specific configurations */
|
||||
i2c_master_bus_handle_t bus_handle; /*!< I2C master bus handle */
|
||||
i2c_device_config_t device_config; /*!< I2C device configuration, in order to get the frequency information of I2C */
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
i2c_master_soft_bus_handle_t soft_bus_handle; /*!< I2C master soft bus handle */
|
||||
#endif
|
||||
bool is_init; /*!< if bus is initialized */
|
||||
i2c_config_t conf_activate; /*!< I2C active configuration */
|
||||
SemaphoreHandle_t mutex; /*!< mutex to achieve thread-safe */
|
||||
int32_t ref_counter; /*!< reference count */
|
||||
} i2c_bus_t;
|
||||
|
||||
typedef struct {
|
||||
i2c_device_config_t device_config; /*!< I2C device configuration */
|
||||
i2c_master_dev_handle_t dev_handle; /*!< I2C master bus device handle */
|
||||
i2c_device_config_t conf; /*!< I2C active configuration */
|
||||
i2c_bus_t *i2c_bus; /*!< I2C bus */
|
||||
} i2c_bus_device_t;
|
||||
|
||||
static const char *TAG = "i2c_bus";
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
static i2c_bus_t s_i2c_bus[I2C_NUM_SW_MAX]; /*!< If software I2C is enabled, additional space is required to store the port. */
|
||||
#else
|
||||
static i2c_bus_t s_i2c_bus[I2C_NUM_MAX];
|
||||
#endif
|
||||
|
||||
#define I2C_BUS_CHECK(a, str, ret) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_CHECK_GOTO(a, str, label) if(!(a)) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
|
||||
goto label; \
|
||||
}
|
||||
|
||||
#define I2C_BUS_INIT_CHECK(is_init, ret) if(!is_init) { \
|
||||
ESP_LOGE(TAG,"%s:%d (%s):i2c_bus has not inited", __FILE__, __LINE__, __FUNCTION__); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_MUTEX_TAKE(mutex, ret) if (!xSemaphoreTake(mutex, I2C_BUS_MUTEX_TICKS_TO_WAIT)) { \
|
||||
ESP_LOGE(TAG, "i2c_bus take mutex timeout, max wait = %"PRIu32"ms", I2C_BUS_MUTEX_TICKS_TO_WAIT); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_MUTEX_TAKE_MAX_DELAY(mutex, ret) if (!xSemaphoreTake(mutex, portMAX_DELAY)) { \
|
||||
ESP_LOGE(TAG, "i2c_bus take mutex timeout, max wait = %"PRIu32"ms", portMAX_DELAY); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
#define I2C_BUS_MUTEX_GIVE(mutex, ret) if (!xSemaphoreGive(mutex)) { \
|
||||
ESP_LOGE(TAG, "i2c_bus give mutex failed"); \
|
||||
return (ret); \
|
||||
}
|
||||
|
||||
static esp_err_t i2c_driver_reinit(i2c_port_t port, const i2c_config_t *conf);
|
||||
static esp_err_t i2c_driver_deinit(i2c_port_t port);
|
||||
static esp_err_t i2c_bus_write_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data);
|
||||
static esp_err_t i2c_bus_read_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data);
|
||||
inline static bool i2c_config_compare(i2c_port_t port, const i2c_config_t *conf);
|
||||
/**************************************** Public Functions (Application level)*********************************************/
|
||||
|
||||
i2c_bus_handle_t i2c_bus_create(i2c_port_t port, const i2c_config_t *conf)
|
||||
{
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", NULL);
|
||||
#else
|
||||
I2C_BUS_CHECK(port < I2C_NUM_MAX, "I2C port error", NULL);
|
||||
#endif
|
||||
I2C_BUS_CHECK(conf != NULL, "pointer = NULL error", NULL);
|
||||
I2C_BUS_CHECK(conf->mode == I2C_MODE_MASTER, "i2c_bus only supports master mode", NULL);
|
||||
|
||||
if (i2c_master_get_bus_handle(port, &s_i2c_bus[port].bus_handle) == ESP_OK) {
|
||||
s_i2c_bus[port].is_init = true;
|
||||
s_i2c_bus[port].conf_activate = *conf;
|
||||
s_i2c_bus[port].bus_config.i2c_port = port;
|
||||
s_i2c_bus[port].device_config.scl_speed_hz = conf->master.clk_speed;
|
||||
s_i2c_bus[port].mutex = xSemaphoreCreateMutex();
|
||||
I2C_BUS_CHECK(s_i2c_bus[port].mutex != NULL, "i2c_bus xSemaphoreCreateMutex failed", NULL);
|
||||
s_i2c_bus[port].ref_counter = 0;
|
||||
ESP_LOGI(TAG, "I2C Bus V2 uses the externally initialized bus handle");
|
||||
return (i2c_bus_handle_t)&s_i2c_bus[port];
|
||||
}
|
||||
|
||||
if (s_i2c_bus[port].is_init) {
|
||||
// if i2c_bus has been inited and configs not changed, return the handle directly
|
||||
if (i2c_config_compare(port, conf)) {
|
||||
ESP_LOGW(TAG, "i2c%d has been inited, return handle directly, ref_counter=%"PRIi32"", port, s_i2c_bus[port].ref_counter);
|
||||
return (i2c_bus_handle_t)&s_i2c_bus[port];
|
||||
}
|
||||
} else {
|
||||
s_i2c_bus[port].mutex = xSemaphoreCreateMutex();
|
||||
I2C_BUS_CHECK(s_i2c_bus[port].mutex != NULL, "i2c_bus xSemaphoreCreateMutex failed", NULL);
|
||||
s_i2c_bus[port].ref_counter = 0;
|
||||
}
|
||||
|
||||
esp_err_t ret = i2c_driver_reinit(port, conf); /*!< Reconfigure the I2C parameters and initialise the bus. */
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "init error", NULL);
|
||||
s_i2c_bus[port].conf_activate = *conf;
|
||||
s_i2c_bus[port].bus_config.i2c_port = port;
|
||||
s_i2c_bus[port].device_config.scl_speed_hz = conf->master.clk_speed; /*!< Stores the frequency configured in the bus, overrides the value if the device has a separate frequency */
|
||||
ESP_LOGI(TAG, "I2C Bus V2 Config Succeed, Version: %d.%d.%d", I2C_BUS_VER_MAJOR, I2C_BUS_VER_MINOR, I2C_BUS_VER_PATCH);
|
||||
return (i2c_bus_handle_t)&s_i2c_bus[port];
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_delete(i2c_bus_handle_t *p_bus)
|
||||
{
|
||||
I2C_BUS_CHECK(p_bus != NULL && *p_bus != NULL, "pointer = NULL error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)(*p_bus);
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, ESP_FAIL);
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
|
||||
// if ref_counter == 0, de-init the bus
|
||||
if ((i2c_bus->ref_counter) > 0) {
|
||||
ESP_LOGW(TAG, "i2c%d is also handled by others ref_counter=%"PRIi32", won't be de-inited", i2c_bus->bus_config.i2c_port, i2c_bus->ref_counter);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ret = i2c_driver_deinit(i2c_bus->bus_config.i2c_port);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "deinit error", ret);
|
||||
vSemaphoreDelete(i2c_bus->mutex);
|
||||
*p_bus = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
i2c_master_bus_handle_t i2c_bus_get_internal_bus_handle(i2c_bus_handle_t bus_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", NULL);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, NULL);
|
||||
return i2c_bus->bus_handle;
|
||||
}
|
||||
|
||||
uint32_t i2c_bus_get_current_clk_speed(i2c_bus_handle_t bus_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", 0);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, 0);
|
||||
return i2c_bus->conf_activate.master.clk_speed;
|
||||
}
|
||||
|
||||
uint8_t i2c_bus_get_created_device_num(i2c_bus_handle_t bus_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", 0);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, 0);
|
||||
return i2c_bus->ref_counter;
|
||||
}
|
||||
|
||||
i2c_bus_device_handle_t i2c_bus_device_create(i2c_bus_handle_t bus_handle, uint8_t dev_addr, uint32_t clk_speed)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Null Bus Handle", NULL);
|
||||
I2C_BUS_CHECK(clk_speed <= 400000, "clk_speed must <= 400000", NULL);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, NULL);
|
||||
i2c_bus_device_t *i2c_device = calloc(1, sizeof(i2c_bus_device_t));
|
||||
I2C_BUS_CHECK(i2c_device != NULL, "calloc memory failed", NULL);
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, NULL);
|
||||
i2c_device->device_config.device_address = dev_addr;
|
||||
i2c_device->device_config.dev_addr_length = I2C_ADDR_BIT_LEN_7;
|
||||
i2c_device->device_config.scl_speed_hz = i2c_bus->device_config.scl_speed_hz; /*!< Transfer the frequency information in the bus to the device. */
|
||||
i2c_device->device_config.flags.disable_ack_check = i2c_bus->device_config.flags.disable_ack_check; /*!< Transfer the ack check in the bus to the device. */
|
||||
|
||||
if (clk_speed != 0) {
|
||||
i2c_device->device_config.scl_speed_hz = clk_speed;
|
||||
}
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
// For software I2C, it is only necessary to save the device configuration.
|
||||
if (i2c_bus->bus_config.i2c_port < I2C_NUM_MAX)
|
||||
#endif
|
||||
{
|
||||
esp_err_t ret = i2c_master_bus_add_device(i2c_bus->bus_handle, &i2c_device->device_config, &i2c_device->dev_handle);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "add device error", NULL);
|
||||
}
|
||||
i2c_device->i2c_bus = i2c_bus;
|
||||
i2c_bus->ref_counter++;
|
||||
I2C_BUS_MUTEX_GIVE(i2c_bus->mutex, NULL);
|
||||
return (i2c_bus_device_handle_t)i2c_device;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_device_delete(i2c_bus_device_handle_t *p_dev_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(p_dev_handle != NULL && *p_dev_handle != NULL, "Null Device Handle", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)(*p_dev_handle);
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (i2c_device->i2c_bus->bus_config.i2c_port < I2C_NUM_MAX)
|
||||
#endif
|
||||
{
|
||||
esp_err_t ret = i2c_master_bus_rm_device(i2c_device->dev_handle);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "remove device error", ret);
|
||||
}
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
i2c_device->i2c_bus->ref_counter--; /*!< ref_counter is reduced only when the device is removed successfully. */
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
free(i2c_device);
|
||||
*p_dev_handle = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t i2c_bus_scan(i2c_bus_handle_t bus_handle, uint8_t *buf, uint8_t num)
|
||||
{
|
||||
I2C_BUS_CHECK(bus_handle != NULL, "Handle error", 0);
|
||||
i2c_bus_t *i2c_bus = (i2c_bus_t *)bus_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_bus->is_init, 0);
|
||||
uint8_t device_count = 0;
|
||||
esp_err_t ret = ESP_OK;
|
||||
I2C_BUS_MUTEX_TAKE_MAX_DELAY(i2c_bus->mutex, 0);
|
||||
|
||||
for (uint8_t dev_address = 1; dev_address < 127; dev_address++) {
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (i2c_bus->bus_config.i2c_port > I2C_NUM_MAX) {
|
||||
ret = i2c_master_soft_bus_probe(i2c_bus->soft_bus_handle, dev_address);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ret = i2c_master_probe(i2c_bus->bus_handle, dev_address, I2C_BUS_TICKS_TO_WAIT);
|
||||
}
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "found i2c device address = 0x%02x", dev_address);
|
||||
if (buf != NULL && device_count < num) {
|
||||
*(buf + device_count) = dev_address;
|
||||
}
|
||||
device_count++;
|
||||
}
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_bus->mutex, 0);
|
||||
return device_count;
|
||||
}
|
||||
|
||||
uint8_t i2c_bus_device_get_address(i2c_bus_device_handle_t dev_handle)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", NULL_I2C_DEV_ADDR);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
return i2c_device->device_config.device_address;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_bytes(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
return i2c_bus_read_reg8(dev_handle, mem_address, data_len, data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t *data)
|
||||
{
|
||||
return i2c_bus_read_reg8(dev_handle, mem_address, 1, data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_bit(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_num, uint8_t *data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_reg8(dev_handle, mem_address, 1, &byte);
|
||||
*data = byte & (1 << bit_num);
|
||||
*data = (*data != 0) ? 1 : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_bits(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_start, uint8_t length, uint8_t *data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_byte(dev_handle, mem_address, &byte);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t mask = ((1 << length) - 1) << (bit_start - length + 1);
|
||||
byte &= mask;
|
||||
byte >>= (bit_start - length + 1);
|
||||
*data = byte;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t data)
|
||||
{
|
||||
return i2c_bus_write_reg8(dev_handle, mem_address, 1, &data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_bytes(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
return i2c_bus_write_reg8(dev_handle, mem_address, data_len, data);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_bit(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_num, uint8_t data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_byte(dev_handle, mem_address, &byte);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
byte = (data != 0) ? (byte | (1 << bit_num)) : (byte & ~(1 << bit_num));
|
||||
return i2c_bus_write_byte(dev_handle, mem_address, byte);
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_bits(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_start, uint8_t length, uint8_t data)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
esp_err_t ret = i2c_bus_read_byte(dev_handle, mem_address, &byte);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t mask = ((1 << length) - 1) << (bit_start - length + 1);
|
||||
data <<= (bit_start - length + 1); // shift data into correct position
|
||||
data &= mask; // zero all non-important bits in data
|
||||
byte &= ~(mask); // zero all important bits in existing byte
|
||||
byte |= data; // combine data with existing byte
|
||||
return i2c_bus_write_byte(dev_handle, mem_address, byte);
|
||||
}
|
||||
|
||||
/**************************************** Public Functions (Low level)*********************************************/
|
||||
|
||||
static esp_err_t i2c_bus_read_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
// Need to distinguish between hardware I2C and software I2C via port
|
||||
if (i2c_device->i2c_bus->bus_config.i2c_port > I2C_NUM_MAX) {
|
||||
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.scl_speed_hz);
|
||||
ret = i2c_master_soft_bus_read_reg8(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.device_address, mem_address, data_len, data);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_ADDR) {
|
||||
#endif
|
||||
ret = i2c_master_transmit_receive(i2c_device->dev_handle, &mem_address, 1, data, data_len, I2C_BUS_TICKS_TO_WAIT);
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
} else {
|
||||
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_ADDR);
|
||||
ret = i2c_master_receive(i2c_device->dev_handle, data, data_len, I2C_BUS_TICKS_TO_WAIT);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_read_reg16(i2c_bus_device_handle_t dev_handle, uint16_t mem_address, size_t data_len, uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
uint8_t memAddress8[2];
|
||||
memAddress8[0] = (uint8_t)((mem_address >> 8) & 0x00FF);
|
||||
memAddress8[1] = (uint8_t)(mem_address & 0x00FF);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
// Need to distinguish between hardware I2C and software I2C via port
|
||||
if (i2c_device->i2c_bus->bus_config.i2c_port > I2C_NUM_MAX) {
|
||||
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.scl_speed_hz);
|
||||
ret = i2c_master_soft_bus_read_reg16(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.device_address, mem_address, data_len, data);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
|
||||
#endif
|
||||
ret = i2c_master_transmit_receive(i2c_device->dev_handle, memAddress8, 2, data, data_len, I2C_BUS_TICKS_TO_WAIT);
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
} else {
|
||||
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_16BIT_ADDR);
|
||||
ret = i2c_master_receive(i2c_device->dev_handle, data, data_len, I2C_BUS_TICKS_TO_WAIT);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_bus_write_reg8(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
// Need to distinguish between hardware I2C and software I2C via port
|
||||
if (i2c_device->i2c_bus->bus_config.i2c_port > I2C_NUM_MAX) {
|
||||
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.scl_speed_hz);
|
||||
ret = i2c_master_soft_bus_write_reg8(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.device_address, mem_address, data_len, data);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_ADDR) {
|
||||
#endif
|
||||
uint8_t *data_addr = malloc(data_len + 1);
|
||||
if (data_addr == NULL) {
|
||||
ESP_LOGE(TAG, "data_addr memory alloc fail");
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ESP_ERR_NO_MEM; /*!< If the memory request fails, unlock it immediately and return an error. */
|
||||
}
|
||||
data_addr[0] = mem_address;
|
||||
for (int i = 0; i < data_len; i++) {
|
||||
data_addr[i + 1] = data[i];
|
||||
}
|
||||
ret = i2c_master_transmit(i2c_device->dev_handle, data_addr, data_len + 1, I2C_BUS_TICKS_TO_WAIT);
|
||||
free(data_addr);
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
} else {
|
||||
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_ADDR);
|
||||
ret = i2c_master_transmit(i2c_device->dev_handle, data, data_len, I2C_BUS_TICKS_TO_WAIT);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t i2c_bus_write_reg16(i2c_bus_device_handle_t dev_handle, uint16_t mem_address, size_t data_len, const uint8_t *data)
|
||||
{
|
||||
I2C_BUS_CHECK(dev_handle != NULL, "device handle error", ESP_ERR_INVALID_ARG);
|
||||
I2C_BUS_CHECK(data != NULL, "data pointer error", ESP_ERR_INVALID_ARG);
|
||||
i2c_bus_device_t *i2c_device = (i2c_bus_device_t *)dev_handle;
|
||||
I2C_BUS_INIT_CHECK(i2c_device->i2c_bus->is_init, ESP_ERR_INVALID_STATE);
|
||||
uint8_t memAddress8[2];
|
||||
memAddress8[0] = (uint8_t)((mem_address >> 8) & 0x00FF);
|
||||
memAddress8[1] = (uint8_t)(mem_address & 0x00FF);
|
||||
I2C_BUS_MUTEX_TAKE(i2c_device->i2c_bus->mutex, ESP_ERR_TIMEOUT);
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (i2c_device->i2c_bus->bus_config.i2c_port > I2C_NUM_MAX) {
|
||||
ret = i2c_master_soft_bus_change_frequency(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.scl_speed_hz);
|
||||
ret = i2c_master_soft_bus_write_reg16(i2c_device->i2c_bus->soft_bus_handle, i2c_device->device_config.device_address, mem_address, data_len, data);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
if (mem_address != NULL_I2C_MEM_16BIT_ADDR) {
|
||||
#endif
|
||||
uint8_t *data_addr = malloc(data_len + 2);
|
||||
if (data_addr == NULL) {
|
||||
ESP_LOGE(TAG, "data_addr memory alloc fail");
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ESP_ERR_NO_MEM; /*!< If the memory request fails, unlock it immediately and return an error. */
|
||||
}
|
||||
data_addr[0] = memAddress8[0];
|
||||
data_addr[1] = memAddress8[1];
|
||||
for (int i = 0; i < data_len; i++) {
|
||||
data_addr[i + 2] = data[i];
|
||||
}
|
||||
ret = i2c_master_transmit(i2c_device->dev_handle, data_addr, data_len + 2, I2C_BUS_TICKS_TO_WAIT);
|
||||
free(data_addr);
|
||||
#if !CONFIG_I2C_BUS_REMOVE_NULL_MEM_ADDR
|
||||
} else {
|
||||
ESP_LOGD(TAG, "register address 0x%X is skipped and will not be sent", NULL_I2C_MEM_16BIT_ADDR);
|
||||
ret = i2c_master_transmit(i2c_device->dev_handle, data, data_len, I2C_BUS_TICKS_TO_WAIT);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
I2C_BUS_MUTEX_GIVE(i2c_device->i2c_bus->mutex, ESP_FAIL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**************************************** Private Functions*********************************************/
|
||||
|
||||
static esp_err_t i2c_driver_reinit(i2c_port_t port, const i2c_config_t *conf)
|
||||
{
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", ESP_ERR_INVALID_ARG);
|
||||
#else
|
||||
I2C_BUS_CHECK(port < I2C_NUM_MAX, "i2c port error", ESP_ERR_INVALID_ARG);
|
||||
#endif
|
||||
I2C_BUS_CHECK(conf != NULL, "pointer = NULL error", ESP_ERR_INVALID_ARG);
|
||||
|
||||
if (s_i2c_bus[port].is_init) {
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (port > I2C_NUM_MAX) {
|
||||
i2c_del_master_soft_bus(s_i2c_bus[port].soft_bus_handle);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
i2c_del_master_bus(s_i2c_bus[port].bus_handle);
|
||||
}
|
||||
s_i2c_bus[port].is_init = false;
|
||||
ESP_LOGI(TAG, "i2c%d bus deinited", port);
|
||||
}
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (port > I2C_NUM_MAX) {
|
||||
s_i2c_bus[port].bus_config.i2c_port = port; /*!< Similarly, it is necessary to preserve the I2C_PORT for later distinction. */
|
||||
esp_err_t ret = i2c_new_master_soft_bus(conf, &s_i2c_bus[port].soft_bus_handle);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "i2c software driver install failed", ret);
|
||||
s_i2c_bus[port].is_init = true;
|
||||
ESP_LOGI(TAG, "i2c%d software bus inited", port);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Convert i2c_config_t information to i2c_master_bus_config_t and i2c_device_config_t
|
||||
s_i2c_bus[port].bus_config.clk_source = I2C_CLK_SRC_DEFAULT;
|
||||
s_i2c_bus[port].bus_config.i2c_port = port;
|
||||
s_i2c_bus[port].bus_config.scl_io_num = conf->scl_io_num;
|
||||
s_i2c_bus[port].bus_config.sda_io_num = conf->sda_io_num;
|
||||
s_i2c_bus[port].bus_config.glitch_ignore_cnt = 7; /*!< Set the burr cycle of the host bus */
|
||||
s_i2c_bus[port].bus_config.flags.enable_internal_pullup = (conf->scl_pullup_en | conf->sda_pullup_en);
|
||||
s_i2c_bus[port].device_config.scl_speed_hz = conf->master.clk_speed;
|
||||
s_i2c_bus[port].device_config.flags.disable_ack_check = false;
|
||||
|
||||
esp_err_t ret = i2c_new_master_bus(&s_i2c_bus[port].bus_config, &s_i2c_bus[port].bus_handle);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "i2c driver install failed", ret);
|
||||
s_i2c_bus[port].is_init = true;
|
||||
ESP_LOGI(TAG, "i2c%d bus inited", port);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t i2c_driver_deinit(i2c_port_t port)
|
||||
{
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
I2C_BUS_CHECK(((i2c_sw_port_t)port < I2C_NUM_SW_MAX) || (port == I2C_NUM_MAX), "I2C port error", ESP_ERR_INVALID_ARG);
|
||||
#else
|
||||
I2C_BUS_CHECK(port < I2C_NUM_MAX, "i2c port error", ESP_ERR_INVALID_ARG);
|
||||
#endif
|
||||
I2C_BUS_CHECK(s_i2c_bus[port].is_init == true, "i2c not inited", ESP_ERR_INVALID_STATE);
|
||||
|
||||
// Need to distinguish between hardware I2C and software I2C via port
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (port > I2C_NUM_MAX) {
|
||||
esp_err_t ret = i2c_del_master_soft_bus(s_i2c_bus[port].soft_bus_handle);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "i2c software driver delete failed", ret);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
esp_err_t ret = i2c_del_master_bus(s_i2c_bus[port].bus_handle);
|
||||
I2C_BUS_CHECK(ret == ESP_OK, "i2c driver delete failed", ret);
|
||||
}
|
||||
s_i2c_bus[port].is_init = false;
|
||||
ESP_LOGI(TAG, "i2c%d bus deinited", port);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief compare with active i2c_bus configuration
|
||||
*
|
||||
* @param port choose which i2c_port's configuration will be compared
|
||||
* @param conf new configuration
|
||||
* @return true new configuration is equal to active configuration
|
||||
* @return false new configuration is not equal to active configuration
|
||||
*/
|
||||
inline static bool i2c_config_compare(i2c_port_t port, const i2c_config_t *conf)
|
||||
{
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
if (port > I2C_NUM_MAX) {
|
||||
if (s_i2c_bus[port].conf_activate.master.clk_speed == conf->master.clk_speed
|
||||
&& s_i2c_bus[port].conf_activate.sda_io_num == conf->sda_io_num
|
||||
&& s_i2c_bus[port].conf_activate.scl_io_num == conf->scl_io_num
|
||||
&& s_i2c_bus[port].conf_activate.scl_pullup_en == conf->scl_pullup_en
|
||||
&& s_i2c_bus[port].conf_activate.sda_pullup_en == conf->sda_pullup_en) {
|
||||
return true;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (s_i2c_bus[port].bus_config.sda_io_num == conf->sda_io_num
|
||||
&& s_i2c_bus[port].bus_config.scl_io_num == conf->scl_io_num
|
||||
&& s_i2c_bus[port].bus_config.flags.enable_internal_pullup == (conf->scl_pullup_en | conf->sda_pullup_en)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
dependencies:
|
||||
cmake_utilities: 0.*
|
||||
idf: '>=4.0'
|
||||
description: The I2C Bus Driver supports both hardware and software I2C.
|
||||
documentation: https://docs.espressif.com/projects/esp-iot-solution/en/latest/basic/bus/i2c_bus.html
|
||||
issues: https://github.com/espressif/esp-iot-solution/issues
|
||||
repository: git://github.com/espressif/esp-iot-solution.git
|
||||
repository_info:
|
||||
commit_sha: 36792767ff23111c8fae8e57401d716e8da83e71
|
||||
path: components/i2c_bus
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/components/i2c_bus
|
||||
version: 1.4.2
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _I2C_BUS_H_
|
||||
#define _I2C_BUS_H_
|
||||
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
#if CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
#include "driver/i2c.h"
|
||||
#else
|
||||
#include "driver/i2c_master.h"
|
||||
#endif
|
||||
#else
|
||||
#include "driver/i2c.h"
|
||||
#endif
|
||||
|
||||
#define NULL_I2C_MEM_ADDR 0xFF /*!< set mem_address to NULL_I2C_MEM_ADDR if i2c device has no internal address during read/write */
|
||||
#define NULL_I2C_MEM_16BIT_ADDR 0XFFFF /*!< set 16bit mem_address to NULL_I2C_MEM_16BIT_ADDR if i2c device has no internal address during read/write */
|
||||
#define NULL_I2C_DEV_ADDR 0xFF /*!< invalid i2c device address */
|
||||
typedef void *i2c_bus_handle_t; /*!< i2c bus handle */
|
||||
typedef void *i2c_bus_device_handle_t; /*!< i2c device handle */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
typedef enum {
|
||||
I2C_NUM_SW_0 = I2C_NUM_MAX + 1,
|
||||
#if CONFIG_I2C_BUS_SOFTWARE_MAX_PORT >= 2
|
||||
I2C_NUM_SW_1,
|
||||
#endif
|
||||
#if CONFIG_I2C_BUS_SOFTWARE_MAX_PORT >= 3
|
||||
I2C_NUM_SW_2,
|
||||
#endif
|
||||
#if CONFIG_I2C_BUS_SOFTWARE_MAX_PORT >= 4
|
||||
I2C_NUM_SW_3,
|
||||
#endif
|
||||
#if CONFIG_I2C_BUS_SOFTWARE_MAX_PORT >= 5
|
||||
I2C_NUM_SW_4,
|
||||
#endif
|
||||
I2C_NUM_SW_MAX,
|
||||
} i2c_sw_port_t;
|
||||
#endif
|
||||
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
|
||||
#define portTICK_RATE_MS portTICK_PERIOD_MS
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
#if !CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
/**
|
||||
* @brief I2C initialization parameters
|
||||
*/
|
||||
typedef struct {
|
||||
i2c_mode_t mode; /*!< I2C mode */
|
||||
int sda_io_num; /*!< GPIO number for I2C sda signal */
|
||||
int scl_io_num; /*!< GPIO number for I2C scl signal */
|
||||
bool sda_pullup_en; /*!< Internal GPIO pull mode for I2C sda signal*/
|
||||
bool scl_pullup_en; /*!< Internal GPIO pull mode for I2C scl signal*/
|
||||
struct {
|
||||
uint32_t clk_speed; /*!< I2C clock frequency for master mode, (no higher than 1MHz for now) */
|
||||
} master; /*!< I2C master config */
|
||||
uint32_t clk_flags; /*!< Bitwise of ``I2C_SCLK_SRC_FLAG_**FOR_DFS**`` for clk source choice*/
|
||||
} i2c_config_t;
|
||||
#endif
|
||||
|
||||
typedef void *i2c_cmd_handle_t; /*!< I2C command handle */
|
||||
#endif
|
||||
|
||||
/**************************************** Public Functions (Application level)*********************************************/
|
||||
|
||||
/**
|
||||
* @brief Create an I2C bus instance then return a handle if created successfully. Each I2C bus works in a singleton mode,
|
||||
* which means for an i2c port only one group parameter works. When i2c_bus_create is called more than one time for the
|
||||
* same i2c port, following parameter will override the previous one.
|
||||
*
|
||||
* @param port I2C port number. Please note that enabling I2C_BUS_SUPPORT_SOFTWARE in menuconfig allows you to use ports in i2c_sw_port_t to enable software I2C.
|
||||
* @param conf Pointer to I2C bus configuration
|
||||
* @return i2c_bus_handle_t Return the I2C bus handle if created successfully, return NULL if failed.
|
||||
*/
|
||||
i2c_bus_handle_t i2c_bus_create(i2c_port_t port, const i2c_config_t *conf);
|
||||
|
||||
/**
|
||||
* @brief Delete and release the I2C bus resource.
|
||||
*
|
||||
* @param p_bus_handle Point to the I2C bus handle, if delete succeed handle will set to NULL.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t i2c_bus_delete(i2c_bus_handle_t *p_bus_handle);
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
#if !CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
/**
|
||||
* @brief Get internal idf bus_handle from i2c_bus_handle
|
||||
*
|
||||
* @param bus_handle I2C bus handle
|
||||
* @return i2c_master_bus_handle_t Return the idf bus_handle if obtained successfully, return NULL if failed.
|
||||
*/
|
||||
i2c_master_bus_handle_t i2c_bus_get_internal_bus_handle(i2c_bus_handle_t bus_handle);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Scan i2c devices attached on i2c bus
|
||||
*
|
||||
* @param bus_handle I2C bus handle
|
||||
* @param buf Pointer to a buffer to save devices' address, if NULL no address will be saved.
|
||||
* @param num Maximum number of addresses to save, invalid if buf set to NULL,
|
||||
* higher addresses will be discarded if num less-than the total number found on the I2C bus.
|
||||
* @return uint8_t Total number of devices found on the I2C bus
|
||||
*/
|
||||
uint8_t i2c_bus_scan(i2c_bus_handle_t bus_handle, uint8_t *buf, uint8_t num);
|
||||
|
||||
/**
|
||||
* @brief Get current active clock speed.
|
||||
*
|
||||
* @param bus_handle I2C bus handle
|
||||
* @return uint32_t current clock speed
|
||||
*/
|
||||
uint32_t i2c_bus_get_current_clk_speed(i2c_bus_handle_t bus_handle);
|
||||
|
||||
/**
|
||||
* @brief Get created device number of the bus.
|
||||
*
|
||||
* @param bus_handle I2C bus handle
|
||||
* @return uint8_t created device number of the bus
|
||||
*/
|
||||
uint8_t i2c_bus_get_created_device_num(i2c_bus_handle_t bus_handle);
|
||||
|
||||
/**
|
||||
* @brief Create an I2C device on specific bus.
|
||||
* Dynamic configuration must be enable to achieve multiple devices with different configs on a single bus.
|
||||
* menuconfig:Bus Options->I2C Bus Options->enable dynamic configuration
|
||||
*
|
||||
* @param bus_handle Point to the I2C bus handle
|
||||
* @param dev_addr i2c device address
|
||||
* @param clk_speed device specified clock frequency the i2c_bus will switch to during each transfer. 0 if use current bus speed.
|
||||
* @return i2c_bus_device_handle_t return a device handle if created successfully, return NULL if failed.
|
||||
*/
|
||||
i2c_bus_device_handle_t i2c_bus_device_create(i2c_bus_handle_t bus_handle, uint8_t dev_addr, uint32_t clk_speed);
|
||||
|
||||
/**
|
||||
* @brief Delete and release the I2C device resource, i2c_bus_device_delete should be used in pairs with i2c_bus_device_create.
|
||||
*
|
||||
* @param p_dev_handle Point to the I2C device handle, if delete succeed handle will set to NULL.
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Fail
|
||||
*/
|
||||
esp_err_t i2c_bus_device_delete(i2c_bus_device_handle_t *p_dev_handle);
|
||||
|
||||
/**
|
||||
* @brief Get device's I2C address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @return uint8_t I2C address, return NULL_I2C_DEV_ADDR if dev_handle is invalid.
|
||||
*/
|
||||
uint8_t i2c_bus_device_get_address(i2c_bus_device_handle_t dev_handle);
|
||||
|
||||
/**
|
||||
* @brief Read single byte from i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data Pointer to a buffer to save the data that was read
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_read_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Read multiple bytes from i2c device with 8-bit internal register/memory address.
|
||||
* If internal reg/mem address is 16-bit, please refer i2c_bus_read_reg16
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data_len Number of bytes to read
|
||||
* @param data Pointer to a buffer to save the data that was read
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_read_bytes(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Read single bit of a byte from i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param bit_num The bit number 0 - 7 to read
|
||||
* @param data Pointer to a buffer to save the data that was read. *data == 0 -> bit = 0, *data !=0 -> bit = 1.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_read_bit(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_num, uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Read multiple bits of a byte from i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param bit_start The bit to start from, 0 - 7, MSB at 0
|
||||
* @param length The number of bits to read, 1 - 8
|
||||
* @param data Pointer to a buffer to save the data that was read
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_read_bits(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_start, uint8_t length, uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Write single byte to i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data The byte to write.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_write_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t data);
|
||||
|
||||
/**
|
||||
* @brief Write multiple byte to i2c device with 8-bit internal register/memory address
|
||||
* If internal reg/mem address is 16-bit, please refer i2c_bus_write_reg16
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data_len Number of bytes to write
|
||||
* @param data Pointer to the bytes to write.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_write_bytes(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, size_t data_len, const uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Write single bit of a byte to an i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param bit_num The bit number 0 - 7 to write
|
||||
* @param data The bit to write, data == 0 means set bit = 0, data !=0 means set bit = 1.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_write_bit(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_num, uint8_t data);
|
||||
|
||||
/**
|
||||
* @brief Write multiple bits of a byte to an i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param bit_start The bit to start from, 0 - 7, MSB at 0
|
||||
* @param length The number of bits to write, 1 - 8
|
||||
* @param data The bits to write.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_write_bits(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t bit_start, uint8_t length, uint8_t data);
|
||||
|
||||
/**************************************** Public Functions (Low level)*********************************************/
|
||||
|
||||
/**
|
||||
* @brief I2C master send queued commands create by ``i2c_cmd_link_create`` .
|
||||
* This function will trigger sending all queued commands.
|
||||
* The task will be blocked until all the commands have been sent out.
|
||||
* If I2C_BUS_DYNAMIC_CONFIG enable, i2c_bus will dynamically check configs and re-install i2c driver before each transfer,
|
||||
* hence multiple devices with different configs on a single bus can be supported.
|
||||
* @note
|
||||
* Only call this function when ``i2c_bus_read/write_xx`` do not meet the requirements
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param cmd I2C command handler
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_cmd_begin(i2c_bus_device_handle_t dev_handle, i2c_cmd_handle_t cmd);
|
||||
|
||||
/**
|
||||
* @brief Write date to an i2c device with 16-bit internal reg/mem address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal 16-bit reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data_len Number of bytes to write
|
||||
* @param data Pointer to the bytes to write.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_write_reg16(i2c_bus_device_handle_t dev_handle, uint16_t mem_address, size_t data_len, const uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Read date from i2c device with 16-bit internal reg/mem address
|
||||
*
|
||||
* @param dev_handle I2C device handle
|
||||
* @param mem_address The internal 16-bit reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.
|
||||
* @param data_len Number of bytes to read
|
||||
* @param data Pointer to a buffer to save the data that was read
|
||||
* @return esp_err_t
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
|
||||
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
|
||||
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
|
||||
*/
|
||||
esp_err_t i2c_bus_read_reg16(i2c_bus_device_handle_t dev_handle, uint16_t mem_address, size_t data_len, uint8_t *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -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.
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "i2c_bus.h"
|
||||
|
||||
struct i2c_master_soft_bus_t {
|
||||
gpio_num_t scl_io; /*!< SCL GPIO PIN */
|
||||
gpio_num_t sda_io; /*!< SDA GPIO PIN */
|
||||
uint32_t time_delay_us; /*!< Interval between SCL GPIO toggles in microseconds, determining the SCL frequency */
|
||||
};
|
||||
|
||||
typedef struct i2c_master_soft_bus_t *i2c_master_soft_bus_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Allocate an I2C master soft bus
|
||||
*
|
||||
* @param conf I2C master soft bus configuration
|
||||
* @param ret_soft_bus_handle I2C soft bus handle
|
||||
* @return
|
||||
* - ESP_OK: I2C master soft bus initialized successfully
|
||||
* - ESP_ERR_INVALID_ARG: I2C soft bus initialization failed because of invalid argument
|
||||
* - ESP_ERR_NO_MEM: Create I2C soft bus failed because of out of memory
|
||||
*/
|
||||
esp_err_t i2c_new_master_soft_bus(const i2c_config_t *conf, i2c_master_soft_bus_handle_t *ret_soft_bus_handle);
|
||||
|
||||
/**
|
||||
* @brief Delete the I2C master soft bus
|
||||
*
|
||||
* @param bus_handle I2C soft bus handle
|
||||
* @return
|
||||
* - ESP_OK: Delete I2C soft bus success
|
||||
* - Otherwise: Some module delete failed
|
||||
*/
|
||||
esp_err_t i2c_del_master_soft_bus(i2c_master_soft_bus_handle_t bus_handle);
|
||||
|
||||
/**
|
||||
* @brief Change I2C soft bus frequency
|
||||
*
|
||||
* @param bus_handle I2C soft bus handle
|
||||
* @param frequency I2C bus frequency
|
||||
* @return
|
||||
* - ESP_OK: Change I2C soft bus frequency success
|
||||
* - ESP_ERR_INVALID_ARG: I2C soft bus change frequency failed because of invalid argument
|
||||
*/
|
||||
esp_err_t i2c_master_soft_bus_change_frequency(i2c_master_soft_bus_handle_t bus_handle, uint32_t frequency);
|
||||
|
||||
/**
|
||||
* @brief Probe I2C address, if address is correct and ACK is received, this function will return ESP_OK
|
||||
*
|
||||
* @param bus_handle I2C soft bus handle
|
||||
* @param address I2C device address that you want to probe
|
||||
* @return
|
||||
* - ESP_OK: I2C device probe successfully
|
||||
* - ESP_ERR_INVALID_ARG: I2C probe failed because of invalid argument
|
||||
* - ESP_ERR_NOT_FOUND: I2C probe failed, doesn't find the device with specific address you gave
|
||||
*/
|
||||
esp_err_t i2c_master_soft_bus_probe(i2c_master_soft_bus_handle_t bus_handle, uint8_t address);
|
||||
|
||||
/**
|
||||
* @brief Write multiple byte to i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param bus_handle I2C soft bus handle
|
||||
* @param dev_addr I2C device address
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address
|
||||
* @param data_len Number of bytes to write
|
||||
* @param data Pointer to the bytes to write
|
||||
* @return
|
||||
* - ESP_OK: I2C master write success
|
||||
* - ESP_ERR_INVALID_ARG: I2C master write failed because of invalid argument
|
||||
* - Otherwise: I2C master write failed
|
||||
*/
|
||||
esp_err_t i2c_master_soft_bus_write_reg8(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint8_t mem_address, size_t data_len, const uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Write multiple byte to i2c device with 16-bit internal register/memory address
|
||||
*
|
||||
* @param bus_handle I2C soft bus handle
|
||||
* @param dev_addr I2C device address
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_16BIT_ADDR if no internal address
|
||||
* @param data_len Number of bytes to write
|
||||
* @param data Pointer to the bytes to write
|
||||
* @return
|
||||
* - ESP_OK: I2C master write success
|
||||
* - ESP_ERR_INVALID_ARG: I2C master write failed because of invalid argument
|
||||
* - Otherwise: I2C master write failed
|
||||
*/
|
||||
esp_err_t i2c_master_soft_bus_write_reg16(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint16_t mem_address, size_t data_len, const uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Read multiple byte to i2c device with 8-bit internal register/memory address
|
||||
*
|
||||
* @param bus_handle I2C soft bus handle
|
||||
* @param dev_addr I2C device address
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address
|
||||
* @param data_len Number of bytes to read
|
||||
* @param data Pointer to the bytes to read
|
||||
* @return
|
||||
* - ESP_OK: I2C master read success
|
||||
* - ESP_ERR_INVALID_ARG: I2C master read failed because of invalid argument
|
||||
* - Otherwise: I2C master read failed
|
||||
*/
|
||||
esp_err_t i2c_master_soft_bus_read_reg8(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint8_t mem_address, size_t data_len, uint8_t *data);
|
||||
|
||||
/**
|
||||
* @brief Read multiple byte to i2c device with 16-bit internal register/memory address
|
||||
*
|
||||
* @param bus_handle I2C soft bus handle
|
||||
* @param dev_addr I2C device address
|
||||
* @param mem_address The internal reg/mem address to write to, set to NULL_I2C_MEM_16BIT_ADDR if no internal address
|
||||
* @param data_len Number of bytes to read
|
||||
* @param data Pointer to the bytes to read
|
||||
* @return
|
||||
* - ESP_OK: I2C master read success
|
||||
* - ESP_ERR_INVALID_ARG: I2C master read failed because of invalid argument
|
||||
* - Otherwise: I2C master read failed
|
||||
*/
|
||||
esp_err_t i2c_master_soft_bus_read_reg16(i2c_master_soft_bus_handle_t bus_handle, uint8_t dev_addr, uint16_t mem_address, size_t data_len, uint8_t *data);
|
||||
@@ -0,0 +1,9 @@
|
||||
# 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(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
|
||||
"../../i2c_bus")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(i2c_bus_test)
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
PRIV_INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES unity i2c_bus test_utils)
|
||||
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "unity_config.h"
|
||||
#include "i2c_bus.h"
|
||||
#include "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
#include "driver/i2c_slave.h"
|
||||
#include "test_utils.h"
|
||||
#endif
|
||||
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-460)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
#define I2C_MASTER_SCL_IO (gpio_num_t)4 /*!< gpio number for I2C master clock */
|
||||
#define I2C_MASTER_SDA_IO (gpio_num_t)5 /*!< gpio number for I2C master data */
|
||||
|
||||
#define DATA_LENGTH 512 /*!<Data buffer length for test buffer*/
|
||||
#define RW_TEST_LENGTH 129 /*!<Data length for r/w test, any value from 0-DATA_LENGTH*/
|
||||
#define DELAY_TIME_BETWEEN_ITEMS_MS 1234 /*!< delay time between different test items */
|
||||
|
||||
#define I2C_SLAVE_SCL_IO (gpio_num_t)4 /*!<gpio number for i2c slave clock */
|
||||
#define I2C_SLAVE_SDA_IO (gpio_num_t)5 /*!<gpio number for i2c slave data */
|
||||
#define I2C_MASTER_NUM I2C_NUM_0 /*!<I2C port number for master dev */
|
||||
#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
|
||||
#define I2C_SLAVE_NUM I2C_NUM_0 /*!<I2C port number for slave dev */
|
||||
#define I2C_SLAVE_TX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave tx buffer size */
|
||||
#define I2C_SLAVE_RX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave rx buffer size */
|
||||
#define I2C_SCAN_ADDR_NUM (100) /*!< Number of slave addresses scanned by I2C */
|
||||
|
||||
#define ESP_SLAVE_ADDR 0x28 /*!< ESP32 slave address, you can set any 7bit value */
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
static QueueHandle_t s_receive_queue;
|
||||
|
||||
static IRAM_ATTR bool test_i2c_rx_done_callback(i2c_slave_dev_handle_t channel, const i2c_slave_rx_done_event_data_t *edata, void *user_data)
|
||||
{
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
QueueHandle_t receive_queue = (QueueHandle_t)user_data;
|
||||
xQueueSendFromISR(receive_queue, edata, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
void i2c_bus_init_deinit_test()
|
||||
{
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus_handle_t i2c0_bus_1 = i2c_bus_create(I2C_NUM_0, &conf);
|
||||
TEST_ASSERT(i2c0_bus_1 != NULL);
|
||||
/** configs not change**/
|
||||
i2c0_bus_1 = i2c_bus_create(I2C_NUM_0, &conf);
|
||||
TEST_ASSERT(i2c0_bus_1 != NULL);
|
||||
/** configs change**/
|
||||
conf.master.clk_speed *= 2;
|
||||
i2c0_bus_1 = i2c_bus_create(I2C_NUM_0, &conf);
|
||||
TEST_ASSERT(i2c0_bus_1 != NULL);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c0_bus_1));
|
||||
TEST_ASSERT(i2c0_bus_1 == NULL);
|
||||
}
|
||||
|
||||
void i2c_bus_device_add_test()
|
||||
{
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus_handle_t i2c0_bus_1 = i2c_bus_create(I2C_NUM_0, &conf);
|
||||
TEST_ASSERT(i2c0_bus_1 != NULL);
|
||||
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c0_bus_1, 0x01, 400000);
|
||||
TEST_ASSERT(i2c_device1 != NULL);
|
||||
i2c_bus_device_handle_t i2c_device2 = i2c_bus_device_create(i2c0_bus_1, 0x01, 100000);
|
||||
TEST_ASSERT(i2c_device2 != NULL);
|
||||
i2c_bus_device_delete(&i2c_device1);
|
||||
TEST_ASSERT(i2c_device1 == NULL);
|
||||
i2c_bus_device_delete(&i2c_device2);
|
||||
TEST_ASSERT(i2c_device2 == NULL);
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c0_bus_1));
|
||||
TEST_ASSERT(i2c0_bus_1 == NULL);
|
||||
}
|
||||
|
||||
// print the reading buffer
|
||||
static void disp_buf(uint8_t *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
printf("%02x ", buf[i]);
|
||||
|
||||
if ((i + 1) % 16 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void i2c_master_write_test(void)
|
||||
{
|
||||
uint8_t *data_wr = (uint8_t *) malloc(DATA_LENGTH);
|
||||
int i;
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus_handle_t i2c0_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
|
||||
TEST_ASSERT(i2c0_bus != NULL);
|
||||
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c0_bus, ESP_SLAVE_ADDR, 0);
|
||||
TEST_ASSERT(i2c_device1 != NULL);
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
unity_wait_for_signal("i2c slave init finish");
|
||||
unity_send_signal("master write");
|
||||
#endif
|
||||
|
||||
for (i = 0; i < DATA_LENGTH / 2; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
|
||||
i2c_bus_write_bytes(i2c_device1, NULL_I2C_MEM_ADDR, DATA_LENGTH / 2, data_wr);
|
||||
disp_buf(data_wr, i);
|
||||
free(data_wr);
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
unity_wait_for_signal("ready to delete");
|
||||
#endif
|
||||
i2c_bus_device_delete(&i2c_device1);
|
||||
TEST_ASSERT(i2c_device1 == NULL);
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c0_bus));
|
||||
TEST_ASSERT(i2c0_bus == NULL);
|
||||
}
|
||||
|
||||
static void i2c_slave_read_test(void)
|
||||
{
|
||||
uint8_t *data_rd = (uint8_t *) malloc(DATA_LENGTH);
|
||||
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) || CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
int len = 0;
|
||||
int size_rd = 0;
|
||||
|
||||
i2c_config_t conf_slave = {
|
||||
.mode = I2C_MODE_SLAVE,
|
||||
.sda_io_num = I2C_SLAVE_SDA_IO,
|
||||
.scl_io_num = I2C_SLAVE_SCL_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.slave.addr_10bit_en = 0,
|
||||
.slave.slave_addr = ESP_SLAVE_ADDR,
|
||||
};
|
||||
|
||||
TEST_ESP_OK(i2c_param_config(I2C_SLAVE_NUM, &conf_slave));
|
||||
TEST_ESP_OK(i2c_driver_install(I2C_SLAVE_NUM, I2C_MODE_SLAVE,
|
||||
I2C_SLAVE_RX_BUF_LEN,
|
||||
I2C_SLAVE_TX_BUF_LEN, 0));
|
||||
|
||||
while (1) {
|
||||
len = i2c_slave_read_buffer(I2C_SLAVE_NUM, data_rd + size_rd, DATA_LENGTH, 10000 / portTICK_RATE_MS);
|
||||
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_rd += len;
|
||||
}
|
||||
|
||||
disp_buf(data_rd, size_rd);
|
||||
|
||||
for (int i = 0; i < size_rd; i++) {
|
||||
TEST_ASSERT(data_rd[i] == i);
|
||||
}
|
||||
|
||||
free(data_rd);
|
||||
TEST_ESP_OK(i2c_driver_delete(I2C_SLAVE_NUM));
|
||||
#else
|
||||
i2c_slave_config_t i2c_slv_config = {
|
||||
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = I2C_SLAVE_NUM,
|
||||
.send_buf_depth = 256,
|
||||
.scl_io_num = I2C_SLAVE_SCL_IO,
|
||||
.sda_io_num = I2C_SLAVE_SDA_IO,
|
||||
.slave_addr = ESP_SLAVE_ADDR,
|
||||
};
|
||||
i2c_slave_dev_handle_t slave_handle;
|
||||
TEST_ESP_OK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
|
||||
|
||||
s_receive_queue = xQueueCreate(1, sizeof(i2c_slave_rx_done_event_data_t));
|
||||
i2c_slave_event_callbacks_t cbs = {
|
||||
.on_recv_done = test_i2c_rx_done_callback,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(slave_handle, &cbs, s_receive_queue));
|
||||
i2c_slave_rx_done_event_data_t rx_data;
|
||||
TEST_ESP_OK(i2c_slave_receive(slave_handle, data_rd, DATA_LENGTH / 2));
|
||||
unity_send_signal("i2c slave init finish");
|
||||
unity_wait_for_signal("master write");
|
||||
xQueueReceive(s_receive_queue, &rx_data, pdMS_TO_TICKS(1000));
|
||||
disp_buf(data_rd, DATA_LENGTH / 2);
|
||||
for (int i = 0; i < DATA_LENGTH / 2; i++) {
|
||||
TEST_ASSERT(data_rd[i] == i);
|
||||
}
|
||||
vQueueDelete(s_receive_queue);
|
||||
unity_send_signal("ready to delete");
|
||||
free(data_rd);
|
||||
TEST_ESP_OK(i2c_del_slave_device(slave_handle));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE_MULTIPLE_DEVICES("I2C master write slave test", "[i2c_bus]", i2c_master_write_test, i2c_slave_read_test);
|
||||
|
||||
static void master_read_slave_test(void)
|
||||
{
|
||||
uint8_t *data_rd = (uint8_t *) malloc(DATA_LENGTH);
|
||||
memset(data_rd, 0, DATA_LENGTH);
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
|
||||
i2c_bus_handle_t i2c0_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
|
||||
TEST_ASSERT(i2c0_bus != NULL);
|
||||
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c0_bus, ESP_SLAVE_ADDR, 0);
|
||||
TEST_ASSERT(i2c_device1 != NULL);
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
unity_send_signal("i2c master init finish");
|
||||
unity_wait_for_signal("slave write");
|
||||
#endif
|
||||
|
||||
i2c_bus_read_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_rd);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
|
||||
disp_buf(data_rd, RW_TEST_LENGTH);
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
unity_send_signal("ready to delete");
|
||||
#endif
|
||||
i2c_bus_device_delete(&i2c_device1);
|
||||
TEST_ASSERT(i2c_device1 == NULL);
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c0_bus));
|
||||
TEST_ASSERT(i2c0_bus == NULL);
|
||||
free(data_rd);
|
||||
}
|
||||
|
||||
static void slave_write_buffer_test(void)
|
||||
{
|
||||
uint8_t *data_wr = (uint8_t *) malloc(RW_TEST_LENGTH);
|
||||
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) || CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
i2c_config_t conf_slave = {
|
||||
.mode = I2C_MODE_SLAVE,
|
||||
.sda_io_num = I2C_SLAVE_SDA_IO,
|
||||
.scl_io_num = I2C_SLAVE_SCL_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.slave.addr_10bit_en = 0,
|
||||
.slave.slave_addr = ESP_SLAVE_ADDR,
|
||||
};
|
||||
|
||||
TEST_ESP_OK(i2c_param_config(I2C_SLAVE_NUM, &conf_slave));
|
||||
TEST_ESP_OK(i2c_driver_install(I2C_SLAVE_NUM, I2C_MODE_SLAVE,
|
||||
I2C_SLAVE_RX_BUF_LEN,
|
||||
I2C_SLAVE_TX_BUF_LEN, 0));
|
||||
|
||||
for (int i = 0; i < RW_TEST_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
|
||||
i2c_slave_write_buffer(I2C_SLAVE_NUM, data_wr, RW_TEST_LENGTH, 2000 / portTICK_RATE_MS);
|
||||
#else
|
||||
i2c_slave_config_t i2c_slv_config = {
|
||||
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = I2C_SLAVE_NUM,
|
||||
.send_buf_depth = 256,
|
||||
.scl_io_num = I2C_SLAVE_SCL_IO,
|
||||
.sda_io_num = I2C_SLAVE_SDA_IO,
|
||||
.slave_addr = ESP_SLAVE_ADDR,
|
||||
};
|
||||
i2c_slave_dev_handle_t slave_handle;
|
||||
TEST_ESP_OK(i2c_new_slave_device(&i2c_slv_config, &slave_handle));
|
||||
|
||||
for (int i = 0; i < RW_TEST_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
|
||||
unity_wait_for_signal("i2c master init finish");
|
||||
unity_send_signal("slave write");
|
||||
|
||||
i2c_slave_transmit(slave_handle, data_wr, RW_TEST_LENGTH, 100 / portTICK_PERIOD_MS);
|
||||
#endif
|
||||
|
||||
disp_buf(data_wr, RW_TEST_LENGTH);
|
||||
free(data_wr);
|
||||
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) || CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
i2c_driver_delete(I2C_SLAVE_NUM);
|
||||
#else
|
||||
unity_wait_for_signal("ready to delete");
|
||||
TEST_ESP_OK(i2c_del_slave_device(slave_handle));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE_MULTIPLE_DEVICES("I2C master read slave test", "[i2c_bus]", master_read_slave_test, slave_write_buffer_test);
|
||||
|
||||
TEST_CASE("I2C master write under different frequency test", "[i2c_bus]")
|
||||
{
|
||||
uint8_t *data_wr = (uint8_t *) malloc(RW_TEST_LENGTH);
|
||||
for (int i = 0; i < RW_TEST_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus_handle_t i2c_bus = i2c_bus_create(I2C_NUM_0, &conf);
|
||||
TEST_ASSERT(i2c_bus != NULL);
|
||||
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c_bus, 0x01, I2C_MASTER_FREQ_HZ);
|
||||
TEST_ASSERT(i2c_device1 != NULL);
|
||||
i2c_bus_write_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
|
||||
vTaskDelay(300 / portTICK_RATE_MS);
|
||||
|
||||
conf.master.clk_speed = 40 * 100;
|
||||
i2c_bus = i2c_bus_create(I2C_NUM_0, &conf);
|
||||
i2c_bus_device_handle_t i2c_device2 = i2c_bus_device_create(i2c_bus, 0x02, 40 * 100);
|
||||
TEST_ASSERT(i2c_device2 != NULL);
|
||||
i2c_bus_write_bytes(i2c_device2, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
|
||||
i2c_bus_device_delete(&i2c_device1);
|
||||
TEST_ASSERT(i2c_device1 == NULL);
|
||||
i2c_bus_device_delete(&i2c_device2);
|
||||
TEST_ASSERT(i2c_device2 == NULL);
|
||||
free(data_wr);
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
|
||||
TEST_ASSERT(i2c_bus == NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("i2c bus init-deinit test", "[bus][i2c_bus]")
|
||||
{
|
||||
i2c_bus_init_deinit_test();
|
||||
i2c_bus_device_add_test();
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && !CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
TEST_CASE("I2C bus uses external bus handle test", "[bus][i2c_bus]")
|
||||
{
|
||||
uint8_t *data_wr = (uint8_t *) malloc(RW_TEST_LENGTH);
|
||||
for (int i = 0; i < RW_TEST_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
|
||||
i2c_master_bus_config_t i2c_mst_config = {
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.i2c_port = I2C_MASTER_NUM,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.flags.enable_internal_pullup = true,
|
||||
};
|
||||
i2c_master_bus_handle_t bus_handle;
|
||||
|
||||
TEST_ESP_OK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
|
||||
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
|
||||
i2c_bus_handle_t i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
|
||||
|
||||
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c_bus, 0x01, 400000);
|
||||
TEST_ASSERT(i2c_device1 != NULL);
|
||||
i2c_bus_write_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
|
||||
i2c_bus_device_delete(&i2c_device1);
|
||||
free(data_wr);
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
|
||||
TEST_ASSERT(i2c_bus == NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("I2C bus scan test", "[i2c_bus][scan]")
|
||||
{
|
||||
uint8_t addrs[I2C_SCAN_ADDR_NUM] = {0};
|
||||
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus_handle_t i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf);
|
||||
i2c_bus_scan(i2c_bus, addrs, I2C_SCAN_ADDR_NUM);
|
||||
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
|
||||
TEST_ASSERT(i2c_bus == NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("I2C bus register address restriction test", "[i2c_bus][NULL_I2C_MEM_ADDR]")
|
||||
{
|
||||
uint8_t *data_wr = (uint8_t *) malloc(RW_TEST_LENGTH);
|
||||
for (int i = 0; i < RW_TEST_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus_handle_t i2c_bus = i2c_bus_create(I2C_NUM_0, &conf);
|
||||
TEST_ASSERT(i2c_bus != NULL);
|
||||
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c_bus, 0x01, I2C_MASTER_FREQ_HZ);
|
||||
TEST_ASSERT(i2c_device1 != NULL);
|
||||
i2c_bus_write_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
|
||||
i2c_bus_device_delete(&i2c_device1);
|
||||
TEST_ASSERT(i2c_device1 == NULL);
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
|
||||
free(data_wr);
|
||||
TEST_ASSERT(i2c_bus == NULL);
|
||||
}
|
||||
|
||||
#if CONFIG_I2C_BUS_SUPPORT_SOFTWARE
|
||||
|
||||
TEST_CASE("I2C soft bus init-deinit test", "[soft][bus][i2c_bus]")
|
||||
{
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus_handle_t i2c0_bus_1 = i2c_bus_create(I2C_NUM_SW_1, &conf);
|
||||
TEST_ASSERT(i2c0_bus_1 != NULL);
|
||||
/** configs not change**/
|
||||
i2c0_bus_1 = i2c_bus_create(I2C_NUM_SW_1, &conf);
|
||||
TEST_ASSERT(i2c0_bus_1 != NULL);
|
||||
/** configs change**/
|
||||
conf.master.clk_speed *= 2;
|
||||
i2c0_bus_1 = i2c_bus_create(I2C_NUM_SW_0, &conf);
|
||||
TEST_ASSERT(i2c0_bus_1 != NULL);
|
||||
vTaskDelay(100 / portTICK_RATE_MS);
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c0_bus_1));
|
||||
TEST_ASSERT(i2c0_bus_1 == NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("I2C soft bus device add test", "[soft][bus][device][i2c_bus]")
|
||||
{
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus_handle_t i2c0_bus_1 = i2c_bus_create(I2C_NUM_SW_0, &conf);
|
||||
TEST_ASSERT(i2c0_bus_1 != NULL);
|
||||
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c0_bus_1, 0x01, 400000);
|
||||
TEST_ASSERT(i2c_device1 != NULL);
|
||||
i2c_bus_device_handle_t i2c_device2 = i2c_bus_device_create(i2c0_bus_1, 0x01, 100000);
|
||||
TEST_ASSERT(i2c_device2 != NULL);
|
||||
i2c_bus_device_delete(&i2c_device1);
|
||||
TEST_ASSERT(i2c_device1 == NULL);
|
||||
i2c_bus_device_delete(&i2c_device2);
|
||||
TEST_ASSERT(i2c_device2 == NULL);
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c0_bus_1));
|
||||
TEST_ASSERT(i2c0_bus_1 == NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("I2C soft bus scan test", "[soft][i2c_bus][scan]")
|
||||
{
|
||||
uint8_t addrs[I2C_SCAN_ADDR_NUM] = {0};
|
||||
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus_handle_t i2c_bus = i2c_bus_create(I2C_NUM_SW_0, &conf);
|
||||
i2c_bus_scan(i2c_bus, addrs, I2C_SCAN_ADDR_NUM);
|
||||
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
|
||||
TEST_ASSERT(i2c_bus == NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("I2C soft bus write under different frequency test", "[soft][i2c_bus]")
|
||||
{
|
||||
uint8_t *data_wr = (uint8_t *) malloc(RW_TEST_LENGTH);
|
||||
for (int i = 0; i < RW_TEST_LENGTH; i++) {
|
||||
data_wr[i] = i;
|
||||
}
|
||||
|
||||
i2c_config_t conf = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = I2C_MASTER_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_MASTER_SCL_IO,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = I2C_MASTER_FREQ_HZ,
|
||||
};
|
||||
i2c_bus_handle_t i2c_bus = i2c_bus_create(I2C_NUM_SW_0, &conf);
|
||||
TEST_ASSERT(i2c_bus != NULL);
|
||||
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c_bus, 0x01, I2C_MASTER_FREQ_HZ);
|
||||
TEST_ASSERT(i2c_device1 != NULL);
|
||||
i2c_bus_write_bytes(i2c_device1, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
|
||||
vTaskDelay(300 / portTICK_RATE_MS);
|
||||
|
||||
i2c_bus_device_handle_t i2c_device2 = i2c_bus_device_create(i2c_bus, 0x02, 40 * 100);
|
||||
TEST_ASSERT(i2c_device2 != NULL);
|
||||
i2c_bus_write_bytes(i2c_device2, NULL_I2C_MEM_ADDR, RW_TEST_LENGTH, data_wr);
|
||||
i2c_bus_device_delete(&i2c_device1);
|
||||
TEST_ASSERT(i2c_device1 == NULL);
|
||||
i2c_bus_device_delete(&i2c_device2);
|
||||
TEST_ASSERT(i2c_device2 == NULL);
|
||||
free(data_wr);
|
||||
TEST_ASSERT(ESP_OK == i2c_bus_delete(&i2c_bus));
|
||||
TEST_ASSERT(i2c_bus == NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) || CONFIG_I2C_BUS_BACKWARD_CONFIG
|
||||
printf("I2C BUS TEST \n");
|
||||
#else
|
||||
printf("I2C BUS V2 TEST \n");
|
||||
#endif
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
# SPDX-FileCopyrightText: 2024Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'''
|
||||
Steps to run these cases:
|
||||
- Build
|
||||
- . ${IDF_PATH}/export.sh
|
||||
- pip install idf_build_apps
|
||||
- python tools/build_apps.py components/sensors/i2c_bus/test_apps -t esp32
|
||||
- Test
|
||||
- pip install -r tools/requirements/requirement.pytest.txt
|
||||
- pytest components/sensors/i2c_bus/test_apps --target esp32
|
||||
'''
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
@pytest.mark.target('esp32')
|
||||
@pytest.mark.target('esp32c3')
|
||||
@pytest.mark.target('esp32c6')
|
||||
@pytest.mark.target('esp32s3')
|
||||
@pytest.mark.env('generic')
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'defaults',
|
||||
],
|
||||
)
|
||||
def test_i2c_bus(dut: Dut)-> None:
|
||||
dut.run_all_single_board_cases()
|
||||
@@ -0,0 +1,10 @@
|
||||
# For IDF 5.0
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096
|
||||
|
||||
# For IDF4.4
|
||||
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
@@ -0,0 +1,106 @@
|
||||
# ESP-MQTT Build Test Rules
|
||||
# Consolidated manifest covering all examples and test apps
|
||||
|
||||
.default_rules: &default_rules
|
||||
disable:
|
||||
- if: IDF_TARGET in ["esp32h21", "esp32h4"]
|
||||
temporary: true
|
||||
reason: not supported yet
|
||||
- if: IDF_TARGET in ["esp32p4", "esp32h2"] and IDF_VERSION_MAJOR == 5 and IDF_VERSION_MINOR == 3
|
||||
temporary: true
|
||||
reason: esp32p4/esp32h2 example dependencies require IDF versions other than 5.3
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32"]
|
||||
reason: Tests run only on ESP32 with ethernet runners
|
||||
|
||||
# Basic TCP MQTT example
|
||||
examples/tcp:
|
||||
<<: *default_rules
|
||||
|
||||
# SSL/TLS MQTT example
|
||||
examples/ssl:
|
||||
<<: *default_rules
|
||||
disable_test:
|
||||
- if: IDF_TARGET == IDF_TARGET
|
||||
reason: CN verification enabled, tests disabled during server migration
|
||||
|
||||
# WebSocket MQTT example
|
||||
examples/ws:
|
||||
<<: *default_rules
|
||||
|
||||
# WebSocket Secure MQTT example
|
||||
examples/wss:
|
||||
<<: *default_rules
|
||||
disable_test:
|
||||
- if: IDF_TARGET == IDF_TARGET
|
||||
reason: CN verification enabled, tests disabled during server migration
|
||||
|
||||
# MQTT 5.0 protocol example
|
||||
examples/mqtt5:
|
||||
<<: *default_rules
|
||||
|
||||
# SSL with mutual authentication
|
||||
examples/ssl_mutual_auth:
|
||||
<<: *default_rules
|
||||
disable_test:
|
||||
- if: IDF_TARGET == IDF_TARGET
|
||||
reason: Advanced feature demonstration, only build
|
||||
|
||||
# SSL with pre-shared keys
|
||||
examples/ssl_psk:
|
||||
<<: *default_rules
|
||||
disable_test:
|
||||
- if: IDF_TARGET == IDF_TARGET
|
||||
reason: Advanced feature demonstration, only build
|
||||
|
||||
# SSL with Digital Signature peripheral
|
||||
examples/ssl_ds:
|
||||
<<: *default_rules
|
||||
disable:
|
||||
- if: SOC_DIG_SIGN_SUPPORTED != 1
|
||||
reason: DS not present
|
||||
disable_test:
|
||||
- if: IDF_TARGET == IDF_TARGET
|
||||
reason: Advanced feature demonstration, only build
|
||||
|
||||
# Custom outbox implementation example
|
||||
examples/custom_outbox:
|
||||
<<: *default_rules
|
||||
disable_test:
|
||||
- if: IDF_TARGET == IDF_TARGET
|
||||
reason: Advanced feature demonstration, only build
|
||||
|
||||
test/apps:
|
||||
disable:
|
||||
- if: IDF_TARGET not in ["esp32"]
|
||||
reason: Test apps build only for esp32
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32"
|
||||
temporary: false
|
||||
reason: Only esp32 target has ethernet runners for integration tests
|
||||
|
||||
# C++ compatibility build test
|
||||
test/apps/build_test:
|
||||
enable:
|
||||
- if: IDF_TARGET in ["esp32", "esp32c3"]
|
||||
reason: C++ compatibility build test
|
||||
disable_test:
|
||||
- if: IDF_TARGET != IDF_TARGET
|
||||
temporary: false
|
||||
reason: Build only test
|
||||
|
||||
# Publish/Connect integration test
|
||||
test/apps/publish_connect_test:
|
||||
enable:
|
||||
- if: IDF_TARGET in ["esp32"]
|
||||
reason: Integration test for publish/connect functionality
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32"
|
||||
temporary: false
|
||||
reason: Only esp32 target has ethernet runners for integration tests
|
||||
|
||||
# Host tests (unit tests with mocks)
|
||||
test/host:
|
||||
enable:
|
||||
- if: IDF_TARGET in ["linux"]
|
||||
reason: Host-based unit tests with mocked ESP-IDF components
|
||||
@@ -0,0 +1 @@
|
||||
ffdad5659706b4dc14bc63f8eb73ef765efa015bf7e9adf71c813d52a2dc9342
|
||||
@@ -0,0 +1,34 @@
|
||||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[{*.md,*.rst}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{Makefile,*.mk,*.bat}]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
|
||||
[*/freertos/**]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[{*/freertos/**.S,**/FreeRTOSConfig.h}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.pem]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.py]
|
||||
max_line_length = 119
|
||||
112
ESP32-IDF_Temperture-Node-v2/managed_components/espressif__mqtt/.gitignore
vendored
Normal file
112
ESP32-IDF_Temperture-Node-v2/managed_components/espressif__mqtt/.gitignore
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
|
||||
# ESP-IDF Build System
|
||||
build/
|
||||
sdkconfig
|
||||
sdkconfig.old
|
||||
dependencies.lock
|
||||
managed_components/
|
||||
|
||||
# ESP-IDF build directories (but not source directories like build_test)
|
||||
**/build/
|
||||
**/build_esp32*/
|
||||
**/build_esp32c*/
|
||||
**/build_esp32s*/
|
||||
**/build_esp32h*/
|
||||
**/build_esp32p*/
|
||||
**/build_linux*/
|
||||
|
||||
# Examples and test app builds
|
||||
examples/**/build/
|
||||
examples/**/build_esp32*/
|
||||
examples/**/build_esp32c*/
|
||||
examples/**/build_esp32s*/
|
||||
examples/**/build_esp32h*/
|
||||
examples/**/build_esp32p*/
|
||||
examples/**/sdkconfig
|
||||
examples/**/sdkconfig.old
|
||||
examples/**/dependencies.lock
|
||||
examples/**/managed_components/
|
||||
|
||||
# Test application builds (specific build directories, not source)
|
||||
test/apps/**/build/
|
||||
test/apps/**/build_esp32*/
|
||||
test/apps/**/build_esp32c*/
|
||||
test/apps/**/build_esp32s*/
|
||||
test/apps/**/build_esp32h*/
|
||||
test/apps/**/build_esp32p*/
|
||||
test/apps/**/sdkconfig
|
||||
test/apps/**/sdkconfig.old
|
||||
test/apps/**/dependencies.lock
|
||||
test/apps/**/managed_components/
|
||||
|
||||
# Host test builds
|
||||
test/host/build/
|
||||
test/host/sdkconfig
|
||||
test/host/sdkconfig.old
|
||||
test/host/dependencies.lock
|
||||
test/host/managed_components/
|
||||
|
||||
# idf-ci generated files
|
||||
app_info_*.txt
|
||||
size_info_*.txt
|
||||
compile_commands.json
|
||||
*.log
|
||||
|
||||
# Python cache and environments
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
.venv/
|
||||
.cache/
|
||||
|
||||
# Coverage and test results
|
||||
**/coverage.xml
|
||||
**/coverage.html
|
||||
**/junit.xml
|
||||
**/pytest_*.xml
|
||||
**/test_results_*.xml
|
||||
|
||||
# Documentation builds
|
||||
docs/_build/
|
||||
docs/build/
|
||||
|
||||
# Distribution/packaging
|
||||
dist/
|
||||
build/
|
||||
*.egg-info/
|
||||
|
||||
# CI/CD artifacts
|
||||
build_child_pipeline.yml
|
||||
@@ -0,0 +1,27 @@
|
||||
# Note: No need to run build and test on master branch since we use FastForward merge strategy and so each merge request
|
||||
# is tested and then merged onto top of master branch.
|
||||
|
||||
.build_template:
|
||||
stage: build
|
||||
tags:
|
||||
- build
|
||||
- internet
|
||||
script:
|
||||
- pip install -U 'idf-ci<1'
|
||||
- idf-ci build run
|
||||
|
||||
build_idf_v5.3:
|
||||
extends: .build_template
|
||||
image: espressif/idf:release-v5.3
|
||||
|
||||
build_idf_v5.4:
|
||||
extends: .build_template
|
||||
image: espressif/idf:release-v5.4
|
||||
|
||||
build_idf_v5.5:
|
||||
extends: .build_template
|
||||
image: espressif/idf:release-v5.5
|
||||
|
||||
build_idf_latest:
|
||||
extends: .build_template
|
||||
image: espressif/idf:latest
|
||||
@@ -0,0 +1,29 @@
|
||||
push_master_to_github:
|
||||
stage: deploy
|
||||
image: ${CI_DOCKER_REGISTRY}/esp32-ci-env
|
||||
tags:
|
||||
- build
|
||||
only:
|
||||
refs:
|
||||
- master
|
||||
- idf
|
||||
when: on_success
|
||||
variables:
|
||||
GIT_STRATEGY: clone
|
||||
script:
|
||||
- source ${CI_PROJECT_DIR}/.gitlab/ci/utils.sh
|
||||
- add_github_remote "$GH_PUSH_KEY" "$GH_PUSH_REPO"
|
||||
- git push github HEAD:${CI_COMMIT_REF_NAME}
|
||||
|
||||
upload_to_component_manager:
|
||||
stage: deploy
|
||||
image: python:3.10-alpine
|
||||
tags:
|
||||
- deploy
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "master"'
|
||||
script:
|
||||
- pip install idf-component-manager
|
||||
- export IDF_COMPONENT_API_TOKEN=${MQTT_COMPONENT_API_KEY}
|
||||
- export COMP_VERSION=$(grep 'version:' idf_component.yml | head -n 1 | awk '{print $2}' | tr -d '"')
|
||||
- compote component upload --namespace=espressif --name=mqtt --allow-existing --version=${COMP_VERSION}
|
||||
@@ -0,0 +1,82 @@
|
||||
variables:
|
||||
# System environment
|
||||
ESP_DOCS_ENV_IMAGE: "$CI_DOCKER_REGISTRY/esp-idf-doc-env-v5.4:1-1"
|
||||
ESP_DOCS_PATH: "$CI_PROJECT_DIR"
|
||||
|
||||
docs_build:
|
||||
stage: build
|
||||
image: $ESP_DOCS_ENV_IMAGE
|
||||
tags:
|
||||
- build_docs
|
||||
variables:
|
||||
# Set Python buffering for better CI output
|
||||
PYTHONUNBUFFERED: 1
|
||||
TYPE: "preview"
|
||||
DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- docs/_build/*/*/*.txt
|
||||
- docs/_build/*/*/html/*
|
||||
expire_in: 4 days
|
||||
before_script:
|
||||
# Install ESP-IDF documentation build tool
|
||||
- pip install -U pip
|
||||
- pip install esp-docs linuxdoc
|
||||
script:
|
||||
- cd docs
|
||||
- build-docs -t esp32 -l en
|
||||
|
||||
.deploy_docs_template:
|
||||
image: $ESP_DOCS_ENV_IMAGE
|
||||
variables:
|
||||
DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
|
||||
PYTHONUNBUFFERED: 1
|
||||
# ensure all tags are fetched, need to know the latest/stable tag for the docs
|
||||
GIT_STRATEGY: clone
|
||||
GIT_DEPTH: 0
|
||||
stage: test_deploy
|
||||
tags:
|
||||
- brew
|
||||
- amd64
|
||||
script:
|
||||
- source ${CI_PROJECT_DIR}/.gitlab/ci/utils.sh
|
||||
# ensure all tags are fetched, need to know the latest/stable tag for the docs
|
||||
- add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
|
||||
- export GIT_VER=$(git describe --always ${PIPELINE_COMMIT_SHA} --)
|
||||
- pip install esp-docs
|
||||
- deploy-docs
|
||||
|
||||
deploy_docs_preview:
|
||||
extends:
|
||||
- .deploy_docs_template
|
||||
except:
|
||||
refs:
|
||||
- master
|
||||
needs:
|
||||
- docs_build
|
||||
variables:
|
||||
TYPE: "preview"
|
||||
DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
|
||||
DOCS_DEPLOY_PRIVATEKEY: "$DOCS_PREVIEW_PRIVATEKEY"
|
||||
DOCS_DEPLOY_SERVER: "$DOCS_PREVIEW_SERVER"
|
||||
DOCS_DEPLOY_SERVER_USER: "$DOCS_PREVIEW_SERVER_USER"
|
||||
DOCS_DEPLOY_PATH: "$DOCS_PREVIEW_PATH"
|
||||
DOCS_DEPLOY_URL_BASE: "$DOCS_PREVIEW_URL_BASE"
|
||||
|
||||
deploy_docs_prod:
|
||||
extends:
|
||||
- .deploy_docs_template
|
||||
stage: deploy
|
||||
only:
|
||||
refs:
|
||||
- master
|
||||
needs:
|
||||
- docs_build
|
||||
variables:
|
||||
TYPE: "production"
|
||||
DOCS_DEPLOY_PRIVATEKEY: "$DOCS_PROD_PRIVATEKEY"
|
||||
DOCS_DEPLOY_SERVER: "$DOCS_PROD_SERVER"
|
||||
DOCS_DEPLOY_SERVER_USER: "$DOCS_PROD_SERVER_USER"
|
||||
DOCS_DEPLOY_PATH: "$DOCS_PROD_PATH"
|
||||
DOCS_DEPLOY_URL_BASE: "$DOCS_PROD_URL_BASE"
|
||||
@@ -0,0 +1,5 @@
|
||||
Warning: Deprecated: Option '--flash_size' is deprecated. Use '--flash-size' instead.
|
||||
Warning: Deprecated: Option '--flash_mode' is deprecated. Use '--flash-mode' instead.
|
||||
Warning: Deprecated: Option '--flash_freq' is deprecated. Use '--flash-freq' instead.
|
||||
Warning: Deprecated: Command 'sign_data' is deprecated. Use 'sign-data' instead.
|
||||
Warning: Deprecated: Command 'extract_public_key' is deprecated. Use 'extract-public-key' instead.
|
||||
@@ -0,0 +1,50 @@
|
||||
.add_gh_key_remote: &add_gh_key_remote |
|
||||
curl -sSL ${CIT_LOADER_URL} | sh
|
||||
source citools/import_functions
|
||||
cit_add_ssh_key "${GH_PUSH_KEY}"
|
||||
git remote remove github || true
|
||||
git remote add github ${GH_PUSH_REPO}
|
||||
|
||||
host_tests:
|
||||
image: espressif/idf:latest
|
||||
stage: test
|
||||
tags: [build]
|
||||
timeout: 1h
|
||||
variables:
|
||||
GIT_DEPTH: 1
|
||||
needs: []
|
||||
artifacts:
|
||||
paths:
|
||||
- "**/coverage.xml"
|
||||
- "**/coverage.html"
|
||||
expire_in: 1 week
|
||||
when: always
|
||||
reports:
|
||||
junit: "**/junit.xml"
|
||||
coverage_report:
|
||||
coverage_format: cobertura
|
||||
path: "**/coverage.xml"
|
||||
before_script:
|
||||
- pip install -U gcovr 'idf-ci<1'
|
||||
script:
|
||||
- idf-ci build run -t linux -p test/host
|
||||
- cd test/host
|
||||
- ./build_linux_coverage/host_mqtt_client_test.elf -r junit -o junit.xml
|
||||
- cd ../..
|
||||
- gcovr --gcov-ignore-parse-errors -g -k -r . --html coverage.html -x coverage.xml
|
||||
|
||||
check_remotes_sync:
|
||||
stage: test_deploy
|
||||
image: espressif/idf:latest
|
||||
tags:
|
||||
- build
|
||||
- internet
|
||||
needs: []
|
||||
except:
|
||||
- master
|
||||
- idf
|
||||
script:
|
||||
- *add_gh_key_remote
|
||||
- git fetch --depth=1 origin master
|
||||
- git fetch --depth=1 github master
|
||||
- test "$(git rev-parse origin/master)" == "$(git rev-parse github/master)"
|
||||
@@ -0,0 +1,26 @@
|
||||
function add_ssh_keys() {
|
||||
local key_string="${1}"
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
echo -n "${key_string}" >~/.ssh/id_rsa_base64
|
||||
base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 >~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
}
|
||||
function add_doc_server_ssh_keys() {
|
||||
local key_string="${1}"
|
||||
local server_url="${2}"
|
||||
local server_user="${3}"
|
||||
add_ssh_keys "${key_string}"
|
||||
echo -e "Host ${server_url}\n\tStrictHostKeyChecking no\n\tUser ${server_user}\n" >>~/.ssh/config
|
||||
}
|
||||
|
||||
function add_github_remote() {
|
||||
local key_string="${1}"
|
||||
local remote_url="${2}"
|
||||
add_ssh_keys "${key_string}"
|
||||
if ! grep -q "Host github.com" ~/.ssh/config 2>/dev/null; then
|
||||
printf "Host github.com\n\tStrictHostKeyChecking no\n" >>~/.ssh/config
|
||||
fi
|
||||
git remote remove github || true
|
||||
git remote add github "${remote_url}"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# check the latest documentation at
|
||||
# https://docs.espressif.com/projects/idf-build-apps/en/latest/references/config_file.html
|
||||
|
||||
config_rules = ['sdkconfig.ci=default', 'sdkconfig.ci.*=', '=default']
|
||||
|
||||
recursive = true
|
||||
check_warnings = true
|
||||
keep_going = true
|
||||
|
||||
build_dir = "build_@t_@w"
|
||||
build_log_filename = "build.log"
|
||||
size_json_filename = "size.json"
|
||||
|
||||
collect_app_info_filename = "app_info_@p.txt"
|
||||
|
||||
check_manifest_rules = true
|
||||
manifest_filepatterns = ['**/.build-test-rules.yml']
|
||||
ignore_warning_files = ['.gitlab/ci/ignore_build_warnings.txt']
|
||||
@@ -0,0 +1,21 @@
|
||||
# ESP-MQTT idf-ci Configuration
|
||||
[gitlab.artifacts]
|
||||
build_job_filepatterns = [
|
||||
"**/build*/partition_table/*.bin",
|
||||
"**/build*/bootloader/*.bin",
|
||||
"**/build*/bootloader/*.map",
|
||||
"**/build*/bootloader/*.elf",
|
||||
"**/build*/config/sdkconfig.json",
|
||||
"**/build*/*.map",
|
||||
"**/build*/*.bin",
|
||||
"**/build*/*.elf",
|
||||
"**/build*/flasher_args.json",
|
||||
]
|
||||
|
||||
test_job_filepatterns = [
|
||||
"**/test_logs",
|
||||
"**/XUNIT_RESULT_*.xml",
|
||||
]
|
||||
|
||||
[gitlab.build_pipeline]
|
||||
runs_per_job = 15
|
||||
@@ -0,0 +1,65 @@
|
||||
sudo: false
|
||||
language: bash
|
||||
os:
|
||||
- linux
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gperf
|
||||
- python
|
||||
- python-serial
|
||||
|
||||
before_install:
|
||||
# Save path to the git respository
|
||||
- PROJECT_PATH=$(pwd)
|
||||
# Have to checkout a temp branch for later in tree reference
|
||||
- git checkout -b temporary_ref_branch
|
||||
- CI_COMMIT_SHA=$(git rev-parse HEAD)
|
||||
# Test building with latest (stable == v3.3 for now) IDF
|
||||
- LTS_IDF=release/v3.3
|
||||
|
||||
install:
|
||||
# Install ESP32 toochain following steps as desribed
|
||||
# in http://esp-idf.readthedocs.io/en/latest/linux-setup.html
|
||||
#
|
||||
# Get required packages - already done above, see addons: apt: packages:
|
||||
# - sudo apt-get install git wget make libncurses-dev flex bison gperf python python-serial
|
||||
# Prepare directory for the toolchain
|
||||
- mkdir -p ~/esp
|
||||
- cd ~/esp
|
||||
# Download binary toolchain for the ESP32
|
||||
- wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
|
||||
- tar -xzf xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
|
||||
# Get ESP-IDF from github (non-recursive to save time, later we update submodules for different versions)
|
||||
- git clone https://github.com/espressif/esp-idf.git
|
||||
# Set the path to ESP-IDF directory
|
||||
- export IDF_PATH=~/esp/esp-idf
|
||||
- python -m pip install --user -r $IDF_PATH/requirements.txt
|
||||
# Setup build tool: xtensa-esp32-elf and idf.py
|
||||
- export PATH=$PATH:$HOME/esp/xtensa-esp32-elf/bin:$IDF_PATH/tools
|
||||
|
||||
script:
|
||||
# Legacy build with IDF < 3.2
|
||||
- cd $IDF_PATH
|
||||
- git checkout v3.1 && git submodule update --init --recursive
|
||||
- cd $PROJECT_PATH
|
||||
- ./ci/modify_for_legacy_idf.sh ${LTS_IDF} || true
|
||||
- cd $PROJECT_PATH/examples/tcp
|
||||
- make defconfig
|
||||
- make -j4
|
||||
# Build with v3.3 (LTS) IDF
|
||||
- cd $IDF_PATH
|
||||
- git checkout ${LTS_IDF} && git submodule update --init --recursive
|
||||
- cd $IDF_PATH/components/mqtt/esp-mqtt
|
||||
- git remote add local $PROJECT_PATH/.git
|
||||
- git fetch local
|
||||
- git reset --hard $CI_COMMIT_SHA
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/tcp
|
||||
- idf.py build
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/ssl
|
||||
- idf.py build
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/ws
|
||||
- idf.py build
|
||||
- cd $IDF_PATH/examples/protocols/mqtt/wss
|
||||
- idf.py build
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,14 @@
|
||||
set(srcs mqtt_client.c lib/mqtt_msg.c lib/mqtt_outbox.c lib/platform_esp32_idf.c)
|
||||
|
||||
if(CONFIG_MQTT_PROTOCOL_5)
|
||||
list(APPEND srcs lib/mqtt5_msg.c mqtt5_client.c)
|
||||
endif()
|
||||
|
||||
list(TRANSFORM srcs PREPEND ${CMAKE_CURRENT_LIST_DIR}/)
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/include
|
||||
PRIV_INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/lib/include
|
||||
REQUIRES esp_event tcp_transport
|
||||
PRIV_REQUIRES esp_timer http_parser esp_hw_support heap
|
||||
KCONFIG ${CMAKE_CURRENT_LIST_DIR}/Kconfig
|
||||
)
|
||||
@@ -0,0 +1,190 @@
|
||||
menu "ESP-MQTT Configurations"
|
||||
|
||||
config MQTT_PROTOCOL_311
|
||||
bool "Enable MQTT protocol 3.1.1"
|
||||
default y
|
||||
help
|
||||
If not, this library will use MQTT protocol 3.1
|
||||
|
||||
config MQTT_PROTOCOL_5
|
||||
bool "Enable MQTT protocol 5.0"
|
||||
default n
|
||||
help
|
||||
If not, this library will not support MQTT 5.0
|
||||
|
||||
config MQTT_TRANSPORT_SSL
|
||||
bool "Enable MQTT over SSL"
|
||||
default y
|
||||
help
|
||||
Enable MQTT transport over SSL with mbedtls
|
||||
|
||||
config MQTT_TRANSPORT_WEBSOCKET
|
||||
bool "Enable MQTT over Websocket"
|
||||
default y
|
||||
depends on WS_TRANSPORT
|
||||
help
|
||||
Enable MQTT transport over Websocket.
|
||||
|
||||
config MQTT_TRANSPORT_WEBSOCKET_SECURE
|
||||
bool "Enable MQTT over Websocket Secure"
|
||||
default y
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET
|
||||
depends on MQTT_TRANSPORT_SSL
|
||||
help
|
||||
Enable MQTT transport over Websocket Secure.
|
||||
|
||||
config MQTT_MSG_ID_INCREMENTAL
|
||||
bool "Use Incremental Message Id"
|
||||
default n
|
||||
help
|
||||
Set this to true for the message id (2.3.1 Packet Identifier) to be generated
|
||||
as an incremental number rather then a random value (used by default)
|
||||
|
||||
config MQTT_SKIP_PUBLISH_IF_DISCONNECTED
|
||||
bool "Skip publish if disconnected"
|
||||
default n
|
||||
help
|
||||
Set this to true to avoid publishing (enqueueing messages) if the client is disconnected.
|
||||
The MQTT client tries to publish all messages by default, even in the disconnected state
|
||||
(where the qos1 and qos2 packets are stored in the internal outbox to be published later)
|
||||
The MQTT_SKIP_PUBLISH_IF_DISCONNECTED option allows applications to override this behaviour
|
||||
and not enqueue publish packets in the disconnected state.
|
||||
|
||||
config MQTT_REPORT_DELETED_MESSAGES
|
||||
bool "Report deleted messages"
|
||||
default n
|
||||
help
|
||||
Set this to true to post events for all messages which were deleted from the outbox
|
||||
before being correctly sent and confirmed.
|
||||
|
||||
config MQTT_USE_CUSTOM_CONFIG
|
||||
bool "MQTT Using custom configurations"
|
||||
default n
|
||||
help
|
||||
Custom MQTT configurations.
|
||||
|
||||
config MQTT_TCP_DEFAULT_PORT
|
||||
int "Default MQTT over TCP port"
|
||||
default 1883
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Default MQTT over TCP port
|
||||
|
||||
config MQTT_SSL_DEFAULT_PORT
|
||||
int "Default MQTT over SSL port"
|
||||
default 8883
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
depends on MQTT_TRANSPORT_SSL
|
||||
help
|
||||
Default MQTT over SSL port
|
||||
|
||||
config MQTT_WS_DEFAULT_PORT
|
||||
int "Default MQTT over Websocket port"
|
||||
default 80
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET
|
||||
help
|
||||
Default MQTT over Websocket port
|
||||
|
||||
config MQTT_WSS_DEFAULT_PORT
|
||||
int "Default MQTT over Websocket Secure port"
|
||||
default 443
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET
|
||||
depends on MQTT_TRANSPORT_WEBSOCKET_SECURE
|
||||
help
|
||||
Default MQTT over Websocket Secure port
|
||||
|
||||
config MQTT_BUFFER_SIZE
|
||||
int "Default MQTT Buffer Size"
|
||||
default 1024
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
This buffer size using for both transmit and receive
|
||||
|
||||
config MQTT_TASK_STACK_SIZE
|
||||
int "MQTT task stack size"
|
||||
default 6144
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
MQTT task stack size
|
||||
|
||||
config MQTT_DISABLE_API_LOCKS
|
||||
bool "Disable API locks"
|
||||
default n
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Default config employs API locks to protect internal structures. It is possible to disable
|
||||
these locks if the user code doesn't access MQTT API from multiple concurrent tasks
|
||||
|
||||
config MQTT_TASK_PRIORITY
|
||||
int "MQTT task priority"
|
||||
default 5
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
MQTT task priority. Higher number denotes higher priority.
|
||||
|
||||
config MQTT_POLL_READ_TIMEOUT_MS
|
||||
int "MQTT transport poll read timeut"
|
||||
default 1000
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Timeout when polling underlying transport for read.
|
||||
|
||||
config MQTT_EVENT_QUEUE_SIZE
|
||||
int "Number of queued events."
|
||||
default 1
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
A value higher than 1 enables multiple queued events.
|
||||
|
||||
config MQTT_TASK_CORE_SELECTION_ENABLED
|
||||
bool "Enable MQTT task core selection"
|
||||
help
|
||||
This will enable core selection
|
||||
|
||||
choice MQTT_TASK_CORE_SELECTION
|
||||
depends on MQTT_TASK_CORE_SELECTION_ENABLED
|
||||
prompt "Core to use ?"
|
||||
config MQTT_USE_CORE_0
|
||||
bool "Core 0"
|
||||
config MQTT_USE_CORE_1
|
||||
bool "Core 1"
|
||||
endchoice
|
||||
|
||||
config MQTT_OUTBOX_DATA_ON_EXTERNAL_MEMORY
|
||||
bool "Use external memory for outbox data"
|
||||
default n
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Set to true to use external memory for outbox data.
|
||||
|
||||
config MQTT_CUSTOM_OUTBOX
|
||||
bool "Enable custom outbox implementation"
|
||||
default n
|
||||
help
|
||||
Set to true if a specific implementation of message outbox is needed (e.g. persistent outbox in NVM or
|
||||
similar).
|
||||
Note: Implementation of the custom outbox must be added to the mqtt component. These CMake commands
|
||||
could be used to append the custom implementation to lib-mqtt sources:
|
||||
idf_component_get_property(mqtt mqtt COMPONENT_LIB)
|
||||
set_property(TARGET ${mqtt} PROPERTY SOURCES ${PROJECT_DIR}/custom_outbox.c APPEND)
|
||||
|
||||
config MQTT_OUTBOX_EXPIRED_TIMEOUT_MS
|
||||
int "Outbox message expired timeout[ms]"
|
||||
default 30000
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Messages which stays in the outbox longer than this value before being published will be discarded.
|
||||
|
||||
config MQTT_TOPIC_PRESENT_ALL_DATA_EVENTS
|
||||
bool "Enable publish topic in all data events"
|
||||
default n
|
||||
depends on MQTT_USE_CUSTOM_CONFIG
|
||||
help
|
||||
Set to true to have publish topic in all data events. This changes the behaviour
|
||||
when the message is bigger than the receive buffer size. The first event of the sequence
|
||||
always have the topic.
|
||||
Note: This will allocate memory to store the topic only in case of messge bigger than the buffer size.
|
||||
|
||||
endmenu
|
||||
@@ -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 2016 Tuan PM
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,52 @@
|
||||
# ESP32 MQTT Library
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- Based on: <https://github.com/tuanpmt/esp_mqtt>
|
||||
- Support MQTT over TCP, SSL with mbedtls, MQTT over Websocket, MQTT over Websocket Secure
|
||||
- Easy to setup with URI
|
||||
- Multiple instances (Multiple clients in one application)
|
||||
- Support subscribing, publishing, authentication, will messages, keep alive pings and all 3 QoS levels (it should be a fully functional client).
|
||||
- Support for MQTT 3.1.1 and 5.0
|
||||
|
||||
## How to use
|
||||
|
||||
ESP-MQTT is available through the [ESP-IDF Component Manager](https://components.espressif.com/) and ships as a standard [ESP-IDF](https://github.com/espressif/esp-idf) component.
|
||||
|
||||
- To add it via the Component Manager (recommended), declare the dependency in your project's `idf_component.yml`, for example:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
espressif/mqtt: "*"
|
||||
```
|
||||
|
||||
Replace `*` with the version constraint you want to track, or run `idf.py add-dependency espressif/mqtt`.
|
||||
- For local development, clone this repository as `mqtt` so the component name matches:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/espressif/esp-mqtt.git mqtt
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- Documentation of ESP-MQTT API: <https://docs.espressif.com/projects/esp-mqtt/en/master/esp32/index.html>
|
||||
|
||||
## License
|
||||
|
||||
- Apache License 2.0
|
||||
- MQTT package origin: [Stephen Robinson - contiki-mqtt](https://github.com/esar/contiki-mqtt)
|
||||
- Additional contributions by [@tuanpmt](https://twitter.com/tuanpmt)
|
||||
|
||||
## Older IDF versions
|
||||
|
||||
For [ESP-IDF](https://github.com/espressif/esp-idf) versions prior to IDFv3.2, please clone as a component of [ESP-IDF](https://github.com/espressif/esp-idf):
|
||||
|
||||
```
|
||||
git submodule add https://github.com/espressif/esp-mqtt.git components/espmqtt
|
||||
```
|
||||
|
||||
and checkout the [ESP-MQTT_FOR_IDF_3.1](https://github.com/espressif/esp-mqtt/tree/ESP-MQTT_FOR_IDF_3.1) tag
|
||||
@@ -0,0 +1,57 @@
|
||||
# This is Doxygen configuration file
|
||||
#
|
||||
# Doxygen provides over 260 configuration statements
|
||||
# To make this file easier to follow,
|
||||
# it contains only statements that are non-default
|
||||
#
|
||||
# NOTE:
|
||||
# It is recommended not to change defaults unless specifically required
|
||||
# Test any changes how they affect generated documentation
|
||||
# Make sure that correct warnings are generated to flag issues with documented code
|
||||
#
|
||||
# For the complete list of configuration statements see:
|
||||
# http://doxygen.nl/manual/config.html
|
||||
|
||||
|
||||
PROJECT_NAME = "ESP-MQTT Programming Guide"
|
||||
|
||||
## The 'INPUT' statement below is used as input by script 'gen-df-input.py'
|
||||
## to automatically generate API reference list files heder_file.inc
|
||||
## These files are placed in '_inc' directory
|
||||
## and used to include in API reference documentation
|
||||
|
||||
INPUT = \
|
||||
$(PROJECT_PATH)/include/mqtt_client.h \
|
||||
$(PROJECT_PATH)/include/mqtt5_client.h \
|
||||
$(PROJECT_PATH)/include/mqtt_supported_features.h
|
||||
|
||||
## Get warnings for functions that have no documentation for their parameters or return value
|
||||
##
|
||||
WARN_NO_PARAMDOC = YES
|
||||
|
||||
## Enable preprocessing and remove __attribute__(...) expressions from the INPUT files
|
||||
##
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
PREDEFINED = \
|
||||
$(ENV_DOXYGEN_DEFINES) \
|
||||
|
||||
## Do not complain about not having dot
|
||||
##
|
||||
HAVE_DOT = NO
|
||||
|
||||
## Generate XML that is required for Breathe
|
||||
##
|
||||
GENERATE_XML = YES
|
||||
XML_OUTPUT = xml
|
||||
|
||||
GENERATE_HTML = NO
|
||||
HAVE_DOT = NO
|
||||
GENERATE_LATEX = NO
|
||||
GENERATE_MAN = YES
|
||||
GENERATE_RTF = NO
|
||||
|
||||
## Skip distracting progress messages
|
||||
##
|
||||
QUIET = YES
|
||||
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Common (non-language-specific) configuration for Sphinx
|
||||
#
|
||||
|
||||
# type: ignore
|
||||
# pylint: disable=wildcard-import
|
||||
# pylint: disable=undefined-variable
|
||||
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
from esp_docs.conf_docs import * # noqa: F403,F401
|
||||
|
||||
# IDF_PATH validation removed - not needed for standalone component docs
|
||||
# Only required when using ESP-IDF extensions that depend on IDF environment
|
||||
|
||||
|
||||
extensions += ['sphinx_copybutton',
|
||||
# Needed as a trigger for running doxygen
|
||||
'esp_docs.esp_extensions.dummy_build_system',
|
||||
'esp_docs.esp_extensions.run_doxygen'
|
||||
]
|
||||
|
||||
# link roles config
|
||||
github_repo = 'espressif/esp-mqtt'
|
||||
|
||||
# context used by sphinx_idf_theme
|
||||
html_context['github_user'] = 'espressif'
|
||||
html_context['github_repo'] = 'esp-mqtt'
|
||||
|
||||
# Extra options required by sphinx_idf_theme
|
||||
project_slug = 'esp-mqtt'
|
||||
versions_url = './_static/mqtt_docs_versions.js'
|
||||
|
||||
idf_targets = [ 'esp32' ]
|
||||
languages = ['en']
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Known doxygen warnings for ESP-MQTT documentation build
|
||||
# Currently no known doxygen warnings expected
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# English Language RTD & Sphinx config file
|
||||
#
|
||||
# Uses ../conf_common.py for most non-language-specific settings.
|
||||
|
||||
# Importing conf_common adds all the non-language-specific
|
||||
# parts to this conf module
|
||||
try:
|
||||
from conf_common import * # noqa: F403,F401
|
||||
except ImportError:
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('../'))
|
||||
from conf_common import * # noqa: F403,F401
|
||||
|
||||
import datetime
|
||||
|
||||
current_year = datetime.datetime.now().year
|
||||
|
||||
# General information about the project.
|
||||
project = u'ESP-MQTT Programming Guide'
|
||||
copyright = u'2019 - {}, Espressif Systems (Shanghai) Co., Ltd'.format(current_year)
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = 'en'
|
||||
@@ -0,0 +1,208 @@
|
||||
ESP-MQTT
|
||||
========
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
ESP-MQTT is an implementation of `MQTT <https://mqtt.org/>`__ protocol client, which is a lightweight publish/subscribe messaging protocol. Now ESP-MQTT supports `MQTT v5.0 <https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html>`__.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Support MQTT over TCP, SSL with Mbed TLS, MQTT over WebSocket, and MQTT over WebSocket Secure
|
||||
* Easy to setup with URI
|
||||
* Multiple instances (multiple clients in one application)
|
||||
* Support subscribing, publishing, authentication, last will messages, keep alive pings, and all 3 Quality of Service (QoS) levels (it should be a fully functional client)
|
||||
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`tcp` demonstrates how to implement MQTT communication over TCP (default port 1883).
|
||||
|
||||
- :example:`ssl` demonstrates how to use SSL transport to implement MQTT communication over TLS (default port 8883).
|
||||
|
||||
- :example:`ssl_ds` demonstrates how to use digital signature peripheral for authentication to implement MQTT communication over TLS (default port 8883).
|
||||
|
||||
- :example:`ssl_mutual_auth` demonstrates how to use certificates for authentication to implement MQTT communication (default port 8883).
|
||||
|
||||
- :example:`ssl_psk` demonstrates how to use pre-shared keys for authentication to implement MQTT communication over TLS (default port 8883).
|
||||
|
||||
- :example:`ws` demonstrates how to implement MQTT communication over WebSocket (default port 80).
|
||||
|
||||
- :example:`wss` demonstrates how to implement MQTT communication over WebSocket Secure (default port 443).
|
||||
|
||||
- :example:`mqtt5` demonstrates how to use ESP-MQTT library to connect to broker with MQTT v5.0.
|
||||
|
||||
- :example:`custom_outbox` demonstrates how to customize the outbox in the ESP-MQTT library.
|
||||
|
||||
MQTT Message Retransmission
|
||||
---------------------------
|
||||
|
||||
A new MQTT message can be created by calling :cpp:func:`esp_mqtt_client_publish <esp_mqtt_client_publish()>` or its non-blocking counterpart :cpp:func:`esp_mqtt_client_enqueue <esp_mqtt_client_enqueue()>`.
|
||||
|
||||
Messages with QoS 0 are sent only once. QoS 1 and 2 behave differently since the protocol requires additional steps to complete the process.
|
||||
|
||||
The ESP-MQTT library opts to always retransmit unacknowledged QoS 1 and 2 publish messages to prevent data loss in faulty connections, even though the MQTT specification requires the re-transmission only on reconnect with Clean Session flag been set to 0 (set :cpp:member:`disable_clean_session <esp_mqtt_client_config_t::session_t::disable_clean_session>` to true for this behavior).
|
||||
|
||||
QoS 1 and 2 messages that may need retransmission are always enqueued, but first transmission try occurs immediately if :cpp:func:`esp_mqtt_client_publish <esp_mqtt_client_publish>` is used. A transmission retry for unacknowledged messages will occur after :cpp:member:`message_retransmit_timeout <esp_mqtt_client_config_t::session_t::message_retransmit_timeout>`. After :ref:`CONFIG_MQTT_OUTBOX_EXPIRED_TIMEOUT_MS` messages will expire and be deleted. If :ref:`CONFIG_MQTT_REPORT_DELETED_MESSAGES` is set, an event will be sent to notify the user.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The configuration is made by setting fields in :cpp:class:`esp_mqtt_client_config_t` struct. The configuration struct has the following sub structs to configure different aspects of the client operation.
|
||||
|
||||
* :cpp:class:`esp_mqtt_client_config_t::broker_t` - Allow to set address and security verification.
|
||||
* :cpp:class:`esp_mqtt_client_config_t::credentials_t` - Client credentials for authentication.
|
||||
* :cpp:class:`esp_mqtt_client_config_t::session_t` - Configuration for MQTT session aspects.
|
||||
* :cpp:class:`esp_mqtt_client_config_t::network_t` - Networking related configuration.
|
||||
* :cpp:class:`esp_mqtt_client_config_t::task_t` - Allow to configure FreeRTOS task.
|
||||
* :cpp:class:`esp_mqtt_client_config_t::buffer_t` - Buffer size for input and output.
|
||||
|
||||
In the following sections, the most common aspects are detailed.
|
||||
|
||||
Broker
|
||||
^^^^^^^^^^^
|
||||
|
||||
===========
|
||||
Address
|
||||
===========
|
||||
|
||||
Broker address can be set by usage of :cpp:class:`address <esp_mqtt_client_config_t::broker_t::address_t>` struct. The configuration can be made by usage of :cpp:member:`uri <esp_mqtt_client_config_t::broker_t::address_t::uri>` field or the combination of :cpp:member:`hostname <esp_mqtt_client_config_t::broker_t::address_t::hostname>`, :cpp:member:`transport <esp_mqtt_client_config_t::broker_t::address_t::transport>` and :cpp:member:`port <esp_mqtt_client_config_t::broker_t::address_t::port>`. Optionally, :cpp:member:`path <esp_mqtt_client_config_t::broker_t::address_t::path>` could be set, this field is useful in WebSocket connections.
|
||||
|
||||
The :cpp:member:`uri <esp_mqtt_client_config_t::broker_t::address_t::uri>` field is used in the format ``scheme://hostname:port/path``.
|
||||
|
||||
- Currently support ``mqtt``, ``mqtts``, ``ws``, ``wss`` schemes
|
||||
- MQTT over TCP samples:
|
||||
|
||||
- ``mqtt://mqtt.eclipseprojects.io``: MQTT over TCP, default port 1883
|
||||
- ``mqtt://mqtt.eclipseprojects.io:1884``: MQTT over TCP, port 1884
|
||||
- ``mqtt://username:password@mqtt.eclipseprojects.io:1884``: MQTT over TCP,
|
||||
port 1884, with username and password
|
||||
|
||||
- MQTT over SSL samples:
|
||||
|
||||
- ``mqtts://mqtt.eclipseprojects.io``: MQTT over SSL, port 8883
|
||||
- ``mqtts://mqtt.eclipseprojects.io:8884``: MQTT over SSL, port 8884
|
||||
|
||||
- MQTT over WebSocket samples:
|
||||
|
||||
- ``ws://mqtt.eclipseprojects.io:80/mqtt``
|
||||
|
||||
- MQTT over WebSocket Secure samples:
|
||||
|
||||
- ``wss://mqtt.eclipseprojects.io:443/mqtt``
|
||||
|
||||
- Minimal configurations:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
const esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.broker.address.uri = "mqtt://mqtt.eclipseprojects.io",
|
||||
};
|
||||
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
|
||||
esp_mqtt_client_start(client);
|
||||
|
||||
.. note::
|
||||
|
||||
By default MQTT client uses event loop library to post related MQTT events (connected, subscribed, published, etc.).
|
||||
|
||||
============
|
||||
Verification
|
||||
============
|
||||
|
||||
For secure connections with TLS used, and to guarantee Broker's identity, the :cpp:class:`verification <esp_mqtt_client_config_t::broker_t::verification_t>` struct must be set.
|
||||
The broker certificate may be set in PEM or DER format. To select DER, the equivalent :cpp:member:`certificate_len <esp_mqtt_client_config_t::broker_t::verification_t::certificate_len>` field must be set. Otherwise, a null-terminated string in PEM format should be provided to :cpp:member:`certificate <esp_mqtt_client_config_t::broker_t::verification_t::certificate>` field.
|
||||
|
||||
- Get certificate from server, example: ``mqtt.eclipseprojects.io``
|
||||
.. code::
|
||||
|
||||
openssl s_client -showcerts -connect mqtt.eclipseprojects.io:8883 < /dev/null \
|
||||
2> /dev/null | openssl x509 -outform PEM > mqtt_eclipse_org.pem
|
||||
|
||||
- Check the sample application: :example:`ssl`
|
||||
- Configuration:
|
||||
|
||||
.. code:: c
|
||||
|
||||
const esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.broker = {
|
||||
.address.uri = "mqtts://mqtt.eclipseprojects.io:8883",
|
||||
.verification.certificate = (const char *)mqtt_eclipse_org_pem_start,
|
||||
},
|
||||
};
|
||||
|
||||
For details about other fields, please check the `API Reference`_ and :ref:`esp_tls_server_verification`.
|
||||
|
||||
Client Credentials
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
All client related credentials are under the :cpp:class:`credentials <esp_mqtt_client_config_t::credentials_t>` field.
|
||||
|
||||
* :cpp:member:`username <esp_mqtt_client_config_t::credentials_t::username>`: pointer to the username used for connecting to the broker, can also be set by URI
|
||||
* :cpp:member:`client_id <esp_mqtt_client_config_t::credentials_t::client_id>`: pointer to the client ID, defaults to ``ESP32_%CHIPID%`` where ``%CHIPID%`` are the last 3 bytes of MAC address in hex format
|
||||
|
||||
==============
|
||||
Authentication
|
||||
==============
|
||||
|
||||
It is possible to set authentication parameters through the :cpp:class:`authentication <esp_mqtt_client_config_t::credentials_t::authentication_t>` field. The client supports the following authentication methods:
|
||||
|
||||
* :cpp:member:`password <esp_mqtt_client_config_t::credentials_t::authentication_t::password>`: use a password by setting
|
||||
* :cpp:member:`certificate <esp_mqtt_client_config_t::credentials_t::authentication_t::certificate>` and :cpp:member:`key <esp_mqtt_client_config_t::credentials_t::authentication_t::key>`: mutual authentication with TLS, and both can be provided in PEM or DER format
|
||||
* :cpp:member:`use_secure_element <esp_mqtt_client_config_t::credentials_t::authentication_t::use_secure_element>`: use secure element (ATECC608A) interfaced to ESP32 series
|
||||
* :cpp:member:`ds_data <esp_mqtt_client_config_t::credentials_t::authentication_t::ds_data>`: use Digital Signature Peripheral available in some Espressif devices
|
||||
|
||||
Session
|
||||
^^^^^^^^^^^
|
||||
|
||||
For MQTT session-related configurations, :cpp:class:`session <esp_mqtt_client_config_t::session_t>` fields should be used.
|
||||
|
||||
=======================
|
||||
Last Will and Testament
|
||||
=======================
|
||||
|
||||
MQTT allows for a last will and testament (LWT) message to notify other clients when a client ungracefully disconnects. This is configured by the following fields in the :cpp:class:`last_will <esp_mqtt_client_config_t::session_t::last_will_t>` struct.
|
||||
|
||||
* :cpp:member:`topic <esp_mqtt_client_config_t::session_t::last_will_t::topic>`: pointer to the LWT message topic
|
||||
* :cpp:member:`msg <esp_mqtt_client_config_t::session_t::last_will_t::msg>`: pointer to the LWT message
|
||||
* :cpp:member:`msg_len <esp_mqtt_client_config_t::session_t::last_will_t::msg_len>`: length of the LWT message, required if :cpp:member:`msg <esp_mqtt_client_config_t::session_t::last_will_t::msg>` is not null-terminated
|
||||
* :cpp:member:`qos <esp_mqtt_client_config_t::session_t::last_will_t::qos>`: quality of service for the LWT message
|
||||
* :cpp:member:`retain <esp_mqtt_client_config_t::session_t::last_will_t::retain>`: specifies the retain flag of the LWT message
|
||||
|
||||
Change Settings in Project Configuration Menu
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The settings for MQTT can be found using :code:`idf.py menuconfig`, under ``Component config`` > ``ESP-MQTT Configuration``.
|
||||
|
||||
The following settings are available:
|
||||
|
||||
- :ref:`CONFIG_MQTT_PROTOCOL_311`: enable 3.1.1 version of MQTT protocol
|
||||
|
||||
- :ref:`CONFIG_MQTT_TRANSPORT_SSL` and :ref:`CONFIG_MQTT_TRANSPORT_WEBSOCKET`: enable specific MQTT transport layer, such as SSL, WEBSOCKET, and WEBSOCKET_SECURE
|
||||
|
||||
- :ref:`CONFIG_MQTT_CUSTOM_OUTBOX`: disable default implementation of mqtt_outbox, so a specific implementation can be supplied
|
||||
|
||||
|
||||
Events
|
||||
------
|
||||
The following events may be posted by the MQTT client:
|
||||
|
||||
* ``MQTT_EVENT_BEFORE_CONNECT``: The client is initialized and about to start connecting to the broker.
|
||||
* ``MQTT_EVENT_CONNECTED``: The client has successfully established a connection to the broker. The client is now ready to send and receive data.
|
||||
* ``MQTT_EVENT_DISCONNECTED``: The client has aborted the connection due to being unable to read or write data, e.g., because the server is unavailable.
|
||||
* ``MQTT_EVENT_SUBSCRIBED``: The broker has acknowledged the client's subscribe request. The event data contains the message ID of the subscribe message.
|
||||
* ``MQTT_EVENT_UNSUBSCRIBED``: The broker has acknowledged the client's unsubscribe request. The event data contains the message ID of the unsubscribe message.
|
||||
* ``MQTT_EVENT_PUBLISHED``: The broker has acknowledged the client's publish message. This is only posted for QoS level 1 and 2, as level 0 does not use acknowledgements. The event data contains the message ID of the publish message.
|
||||
* ``MQTT_EVENT_DATA``: The client has received a publish message. The event data contains: message ID, name of the topic it was published to, received data and its length. For data that exceeds the internal buffer, multiple ``MQTT_EVENT_DATA`` events are posted and :cpp:member:`current_data_offset <esp_mqtt_event_t::current_data_offset>` and :cpp:member:`total_data_len <esp_mqtt_event_t::total_data_len>` from event data updated to keep track of the fragmented message.
|
||||
* ``MQTT_EVENT_ERROR``: The client has encountered an error. The field :cpp:type:`error_handle <esp_mqtt_error_codes_t>` in the event data contains :cpp:type:`error_type <esp_mqtt_error_type_t>` that can be used to identify the error. The type of error determines which parts of the :cpp:type:`error_handle <esp_mqtt_error_codes_t>` struct is filled.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/mqtt_client.inc
|
||||
.. include-build-file:: inc/mqtt5_client.inc
|
||||
@@ -0,0 +1,37 @@
|
||||
mqtt_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:type:: struct esp_mqtt_event_t esp_mqtt_event_t'.
|
||||
mqtt_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:type:: struct esp_mqtt_client_config_t esp_mqtt_client_config_t'.
|
||||
mqtt_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:enum:: esp_mqtt_event_id_t'.
|
||||
mqtt_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:enum:: esp_mqtt_connect_return_code_t'.
|
||||
mqtt_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:enum:: esp_mqtt_error_type_t'.
|
||||
mqtt_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:enum:: esp_mqtt_transport_t'.
|
||||
mqtt_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:enum:: esp_mqtt_protocol_ver_t'.
|
||||
mqtt5_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:enumerator:: __attribute__'.
|
||||
index.rst:line: CRITICAL: Duplicate ID: "mqtt5__client_8h_1a4504a6557b6b27d66613101e758693f4a8c1dfc1ccf00a08192611433ee7f17b4".
|
||||
index.rst:line: WARNING: Duplicate explicit target name: "mqtt5__client_8h_1a4504a6557b6b27d66613101e758693f4a8c1dfc1ccf00a08192611433ee7f17b4".
|
||||
mqtt5_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:enumerator:: __attribute__'.
|
||||
index.rst:line: CRITICAL: Duplicate ID: "mqtt5__client_8h_1a4504a6557b6b27d66613101e758693f4a8c1dfc1ccf00a08192611433ee7f17b4".
|
||||
index.rst:line: WARNING: Duplicate explicit target name: "mqtt5__client_8h_1a4504a6557b6b27d66613101e758693f4a8c1dfc1ccf00a08192611433ee7f17b4".
|
||||
mqtt5_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:enumerator:: __attribute__'.
|
||||
index.rst:line: CRITICAL: Duplicate ID: "mqtt5__client_8h_1a4504a6557b6b27d66613101e758693f4a8c1dfc1ccf00a08192611433ee7f17b4".
|
||||
index.rst:line: WARNING: Duplicate explicit target name: "mqtt5__client_8h_1a4504a6557b6b27d66613101e758693f4a8c1dfc1ccf00a08192611433ee7f17b4".
|
||||
mqtt5_client.inc:line: WARNING: Duplicate C++ declaration, also defined at index:line.
|
||||
Declaration is '.. cpp:enumerator:: __attribute__'.
|
||||
index.rst:line: CRITICAL: Duplicate ID: "mqtt5__client_8h_1a4504a6557b6b27d66613101e758693f4a8c1dfc1ccf00a08192611433ee7f17b4".
|
||||
index.rst:line: WARNING: Duplicate explicit target name: "mqtt5__client_8h_1a4504a6557b6b27d66613101e758693f4a8c1dfc1ccf00a08192611433ee7f17b4".
|
||||
index.rst:line: WARNING: undefined label: 'config_mqtt_outbox_expired_timeout_ms'
|
||||
index.rst:line: WARNING: undefined label: 'config_mqtt_report_deleted_messages'
|
||||
index.rst:line: WARNING: undefined label: 'esp_tls_server_verification'
|
||||
index.rst:line: WARNING: undefined label: 'config_mqtt_protocol_311'
|
||||
index.rst:line: WARNING: undefined label: 'config_mqtt_transport_ssl'
|
||||
index.rst:line: WARNING: undefined label: 'config_mqtt_transport_websocket'
|
||||
index.rst:line: WARNING: undefined label: 'config_mqtt_custom_outbox'
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# English Language RTD & Sphinx config file
|
||||
#
|
||||
# Uses ../conf_common.py for most non-language-specific settings.
|
||||
|
||||
# Importing conf_common adds all the non-language-specific
|
||||
# parts to this conf module
|
||||
try:
|
||||
from conf_common import * # noqa: F403,F401
|
||||
except ImportError:
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('../'))
|
||||
from conf_common import * # noqa: F403,F401
|
||||
|
||||
import datetime
|
||||
|
||||
current_year = datetime.datetime.now().year
|
||||
|
||||
# General information about the project.
|
||||
project = u'ESP-MQTT Programming Guide'
|
||||
copyright = u'2019 - {}, Espressif Systems (Shanghai) Co., Ltd'.format(current_year)
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = 'zh_CN'
|
||||
@@ -0,0 +1,208 @@
|
||||
ESP-MQTT
|
||||
========
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
概述
|
||||
--------
|
||||
|
||||
ESP-MQTT 是 `MQTT <https://mqtt.org/>`__ 协议客户端的实现,MQTT 是一种基于发布/订阅模式的轻量级消息传输协议。ESP-MQTT 当前支持 `MQTT v5.0 <https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html>`__。
|
||||
|
||||
|
||||
特性
|
||||
--------
|
||||
|
||||
* 支持基于 TCP 的 MQTT、基于 Mbed TLS 的 SSL、基于 WebSocket 的 MQTT 以及基于 WebSocket Secure 的 MQTT
|
||||
* 通过 URI 简化配置流程
|
||||
* 多个实例(一个应用程序中有多个客户端)
|
||||
* 支持订阅、发布、认证、遗嘱消息、保持连接心跳机制以及 3 个服务质量 (QoS) 级别(组成全功能客户端)
|
||||
|
||||
|
||||
应用示例
|
||||
-------------------
|
||||
|
||||
- :example:`tcp` 演示了如何通过 TCP 实现 MQTT 通信(默认端口 1883)。
|
||||
|
||||
- :example:`ssl` 演示了如何使用 SSL 传输来实现基于 TLS 的 MQTT 通信(默认端口 8883)。
|
||||
|
||||
- :example:`ssl_ds` 演示了如何使用数字签名外设进行身份验证,以实现基于 TLS 的 MQTT 通信(默认端口 8883)。
|
||||
|
||||
- :example:`ssl_mutual_auth` 演示了如何使用证书进行身份验证实现 MQTT 通信(默认端口 8883)。
|
||||
|
||||
- :example:`ssl_psk` 演示了如何使用预共享密钥进行身份验证,以实现基于 TLS 的 MQTT 通信(默认端口 8883)。
|
||||
|
||||
- :example:`ws` 演示了如何通过 WebSocket 实现 MQTT 通信(默认端口 80)。
|
||||
|
||||
- :example:`wss` 演示了如何通过 WebSocket Secure 实现 MQTT 通信(默认端口 443)。
|
||||
|
||||
- :example:`mqtt5` 演示了如何使用 ESP-MQTT 库通过 MQTT v5.0 连接到代理。
|
||||
|
||||
- :example:`custom_outbox` 演示了如何自定义 ESP-MQTT 库中的 outbox。
|
||||
|
||||
MQTT 消息重传
|
||||
--------------------------
|
||||
|
||||
调用 :cpp:func:`esp_mqtt_client_publish <esp_mqtt_client_publish()>` 或其非阻塞形式 :cpp:func:`esp_mqtt_client_enqueue <esp_mqtt_client_enqueue()>`,可以创建新的 MQTT 消息。
|
||||
|
||||
QoS 0 的消息将只发送一次,QoS 1 和 2 具有不同行为,因为协议需要执行额外步骤来完成该过程。
|
||||
|
||||
ESP-MQTT 库将始终重新传输未确认的 QoS 1 和 2 发布消息,以避免连接错误导致信息丢失,虽然 MQTT 规范要求仅在重新连接且 Clean Session 标志设置为 0 时重新传输(针对此行为,将 :cpp:member:`disable_clean_session <esp_mqtt_client_config_t::session_t::disable_clean_session>` 设置为 true)。
|
||||
|
||||
可能需要重传的 QoS 1 和 2 消息总是处于排队状态,但若使用 :cpp:func:`esp_mqtt_client_publish <esp_mqtt_client_publish>` 则会立即进行第一次传输尝试。未确认消息的重传将在 :cpp:member:`message_retransmit_timeout <esp_mqtt_client_config_t::session_t::message_retransmit_timeout>` 之后进行。在 :ref:`CONFIG_MQTT_OUTBOX_EXPIRED_TIMEOUT_MS` 之后,消息会过期并被删除。如已设置 :ref:`CONFIG_MQTT_REPORT_DELETED_MESSAGES`,则会发送事件来通知用户。
|
||||
|
||||
配置
|
||||
-------------
|
||||
|
||||
通过设置 :cpp:class:`esp_mqtt_client_config_t` 结构体中的字段来进行配置。配置结构体包含以下子结构体,用于配置客户端的多种操作。
|
||||
|
||||
* :cpp:class:`esp_mqtt_client_config_t::broker_t` - 允许设置地址和安全验证。
|
||||
* :cpp:class:`esp_mqtt_client_config_t::credentials_t` - 用于身份验证的客户端凭据。
|
||||
* :cpp:class:`esp_mqtt_client_config_t::session_t` - MQTT 会话相关配置。
|
||||
* :cpp:class:`esp_mqtt_client_config_t::network_t` - 网络相关配置。
|
||||
* :cpp:class:`esp_mqtt_client_config_t::task_t` - 允许配置 FreeRTOS 任务。
|
||||
* :cpp:class:`esp_mqtt_client_config_t::buffer_t` - 输入输出的缓冲区大小。
|
||||
|
||||
下文将详细介绍不同配置。
|
||||
|
||||
服务器
|
||||
^^^^^^^^^^^^
|
||||
|
||||
===========
|
||||
地址
|
||||
===========
|
||||
|
||||
通过 :cpp:class:`address <esp_mqtt_client_config_t::broker_t::address_t>` 结构体的 :cpp:member:`uri <esp_mqtt_client_config_t::broker_t::address_t::uri>` 字段或者 :cpp:member:`hostname <esp_mqtt_client_config_t::broker_t::address_t::hostname>`、:cpp:member:`transport <esp_mqtt_client_config_t::broker_t::address_t::transport>` 以及 :cpp:member:`port <esp_mqtt_client_config_t::broker_t::address_t::port>` 的组合,可以设置服务器地址。也可以选择设置 :cpp:member:`path <esp_mqtt_client_config_t::broker_t::address_t::path>`,该字段对 WebSocket 连接而言非常有用。
|
||||
|
||||
使用 :cpp:member:`uri <esp_mqtt_client_config_t::broker_t::address_t::uri>` 字段的格式为 ``scheme://hostname:port/path``。
|
||||
|
||||
- 当前支持 ``mqtt``、``mqtts``、``ws`` 和 ``wss`` 协议
|
||||
- 基于 TCP 的 MQTT 示例:
|
||||
|
||||
- ``mqtt://mqtt.eclipseprojects.io``:基于 TCP 的 MQTT,默认端口 1883
|
||||
- ``mqtt://mqtt.eclipseprojects.io:1884``:基于 TCP 的 MQTT,端口 1884
|
||||
- ``mqtt://username:password@mqtt.eclipseprojects.io:1884``:基于 TCP 的 MQTT,
|
||||
端口 1884,带有用户名和密码
|
||||
|
||||
- 基于 SSL 的 MQTT 示例:
|
||||
|
||||
- ``mqtts://mqtt.eclipseprojects.io``:基于 SSL 的 MQTT,端口 8883
|
||||
- ``mqtts://mqtt.eclipseprojects.io:8884``:基于 SSL 的 MQTT,端口 8884
|
||||
|
||||
- 基于 WebSocket 的 MQTT 示例:
|
||||
|
||||
- ``ws://mqtt.eclipseprojects.io:80/mqtt``
|
||||
|
||||
- 基于 WebSocket Secure 的 MQTT 示例:
|
||||
|
||||
- ``wss://mqtt.eclipseprojects.io:443/mqtt``
|
||||
|
||||
- 最简配置:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
const esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.broker.address.uri = "mqtt://mqtt.eclipseprojects.io",
|
||||
};
|
||||
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
|
||||
esp_mqtt_client_start(client);
|
||||
|
||||
.. note::
|
||||
|
||||
默认情况下,MQTT 客户端使用事件循环库来发布相关 MQTT 事件(已连接、已订阅、已发布等)。
|
||||
|
||||
=============
|
||||
验证
|
||||
=============
|
||||
|
||||
为验证服务器身份,对于使用 TLS 的安全链接,必须设置 :cpp:class:`verification <esp_mqtt_client_config_t::broker_t::verification_t>` 结构体。
|
||||
服务器证书可设置为 PEM 或 DER 格式。如要选择 DER 格式,必须设置等效 :cpp:member:`certificate_len <esp_mqtt_client_config_t::broker_t::verification_t::certificate_len>` 字段,否则应在 :cpp:member:`certificate <esp_mqtt_client_config_t::broker_t::verification_t::certificate>` 字段传入以空字符结尾的 PEM 格式字符串。
|
||||
|
||||
- 从服务器获取证书,例如:``mqtt.eclipseprojects.io``
|
||||
.. code::
|
||||
|
||||
openssl s_client -showcerts -connect mqtt.eclipseprojects.io:8883 < /dev/null \
|
||||
2> /dev/null | openssl x509 -outform PEM > mqtt_eclipse_org.pem
|
||||
|
||||
- 检查示例应用程序::example:`ssl`
|
||||
- 配置:
|
||||
|
||||
.. code:: c
|
||||
|
||||
const esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.broker = {
|
||||
.address.uri = "mqtts://mqtt.eclipseprojects.io:8883",
|
||||
.verification.certificate = (const char *)mqtt_eclipse_org_pem_start,
|
||||
},
|
||||
};
|
||||
|
||||
了解其他字段的详细信息,请查看 `API 参考`_ 以及 :ref:`esp_tls_server_verification`。
|
||||
|
||||
客户端凭据
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
:cpp:class:`credentials <esp_mqtt_client_config_t::credentials_t>` 字段下包含所有客户端相关凭据。
|
||||
|
||||
* :cpp:member:`username <esp_mqtt_client_config_t::credentials_t::username>`:指向用于连接服务器用户名的指针,也可通过 URI 设置
|
||||
* :cpp:member:`client_id <esp_mqtt_client_config_t::credentials_t::client_id>`:指向客户端 ID 的指针,默认为 ``ESP32_%CHIPID%``,其中 ``%CHIPID%`` 是十六进制 MAC 地址的最后 3 个字节
|
||||
|
||||
===============
|
||||
认证
|
||||
===============
|
||||
|
||||
可以通过 :cpp:class:`authentication <esp_mqtt_client_config_t::credentials_t::authentication_t>` 字段设置认证参数。客户端支持以下认证方式:
|
||||
|
||||
* :cpp:member:`password <esp_mqtt_client_config_t::credentials_t::authentication_t::password>`:使用密码
|
||||
* * :cpp:member:`certificate <esp_mqtt_client_config_t::credentials_t::authentication_t::certificate>` 和 :cpp:member:`key <esp_mqtt_client_config_t::credentials_t::authentication_t::key>`:进行双向 TLS 身份验证,PEM 或 DER 格式均可
|
||||
* :cpp:member:`use_secure_element <esp_mqtt_client_config_t::credentials_t::authentication_t::use_secure_element>`:使用 ESP32 系列中的安全元素 (ATECC608A)
|
||||
* :cpp:member:`ds_data <esp_mqtt_client_config_t::credentials_t::authentication_t::ds_data>`:使用某些乐鑫设备的数字签名外设
|
||||
|
||||
会话
|
||||
^^^^^^^^^^^^
|
||||
|
||||
使用 :cpp:class:`session <esp_mqtt_client_config_t::session_t>` 字段进行 MQTT 会话相关配置。
|
||||
|
||||
========================
|
||||
遗嘱消息 (LWT)
|
||||
========================
|
||||
|
||||
通过设置 :cpp:class:`last_will <esp_mqtt_client_config_t::session_t::last_will_t>` 结构体的以下字段,MQTT 会在一个客户端意外断开连接时通过遗嘱消息通知其他客户端。
|
||||
|
||||
* :cpp:member:`topic <esp_mqtt_client_config_t::session_t::last_will_t::topic>`:指向 LWT 消息主题的指针
|
||||
* :cpp:member:`msg <esp_mqtt_client_config_t::session_t::last_will_t::msg>`:指向 LWT 消息的指针
|
||||
* :cpp:member:`msg_len <esp_mqtt_client_config_t::session_t::last_will_t::msg_len>`:LWT 消息的长度,:cpp:member:`msg <esp_mqtt_client_config_t::session_t::last_will_t::msg>` 不以空字符结尾时需要该字段
|
||||
* :cpp:member:`qos <esp_mqtt_client_config_t::session_t::last_will_t::qos>`:LWT 消息的服务质量
|
||||
* :cpp:member:`retain <esp_mqtt_client_config_t::session_t::last_will_t::retain>`:指定 LWT 消息的保留标志
|
||||
|
||||
在项目配置菜单中设置 MQTT
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
通过 :code:`idf.py menuconfig`,可以在 ``Component config`` > ``ESP-MQTT Configuration`` 中找到 MQTT 设置。
|
||||
|
||||
相关设置如下:
|
||||
|
||||
- :ref:`CONFIG_MQTT_PROTOCOL_311`:启用 MQTT 协议 3.1.1 版本
|
||||
|
||||
- :ref:`CONFIG_MQTT_TRANSPORT_SSL` 和 :ref:`CONFIG_MQTT_TRANSPORT_WEBSOCKET`:启用特定 MQTT 传输层,例如 SSL、WEBSOCKET 和 WEBSOCKET_SECURE
|
||||
|
||||
- :ref:`CONFIG_MQTT_CUSTOM_OUTBOX`:禁用 mqtt_outbox 默认实现,因此可以提供特定实现
|
||||
|
||||
|
||||
事件
|
||||
------------
|
||||
MQTT 客户端可能会发布以下事件:
|
||||
|
||||
* ``MQTT_EVENT_BEFORE_CONNECT``:客户端已初始化并即将开始连接至服务器。
|
||||
* ``MQTT_EVENT_CONNECTED``:客户端已成功连接至服务器。客户端已准备好收发数据。
|
||||
* ``MQTT_EVENT_DISCONNECTED``:由于无法读取或写入数据,例如因为服务器无法使用,客户端已终止连接。
|
||||
* ``MQTT_EVENT_SUBSCRIBED``:服务器已确认客户端的订阅请求。事件数据将包含订阅消息的消息 ID。
|
||||
* ``MQTT_EVENT_UNSUBSCRIBED``:服务器已确认客户端的退订请求。事件数据将包含退订消息的消息 ID。
|
||||
* ``MQTT_EVENT_PUBLISHED``:服务器已确认客户端的发布消息。消息将仅针对 QoS 级别 1 和 2 发布,因为级别 0 不会进行确认。事件数据将包含发布消息的消息 ID。
|
||||
* ``MQTT_EVENT_DATA``:客户端已收到发布消息。事件数据包含:消息 ID、发布消息所属主题名称、收到的数据及其长度。对于超出内部缓冲区的数据,将发布多个 ``MQTT_EVENT_DATA``,并更新事件数据的 :cpp:member:`current_data_offset <esp_mqtt_event_t::current_data_offset>` 和 :cpp:member:`total_data_len<esp_mqtt_event_t::total_data_len>` 以跟踪碎片化消息。
|
||||
* ``MQTT_EVENT_ERROR``:客户端遇到错误。使用事件数据 :cpp:type:`error_handle <esp_mqtt_error_codes_t>` 字段中的 :cpp:type:`error_type <esp_mqtt_error_type_t>`,可以发现错误。错误类型决定 :cpp:type:`error_handle <esp_mqtt_error_codes_t>` 结构体的哪些部分会被填充。
|
||||
|
||||
API 参考
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/mqtt_client.inc
|
||||
.. include-build-file:: inc/mqtt5_client.inc
|
||||
@@ -0,0 +1,20 @@
|
||||
# The following four 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.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(mqtt_tcp_custom_outbox)
|
||||
|
||||
# Add custom outbox implementation to mqtt component
|
||||
idf_component_get_property(mqtt mqtt COMPONENT_LIB)
|
||||
target_sources(${mqtt} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/main/custom_outbox.cpp)
|
||||
|
||||
# Our C++ needs an extra dependency to mqtt component, so we add it to mqtt component.
|
||||
# This is needed because we are adding another source to the mqtt component and the build
|
||||
# system needs to be aware of it to be able to compile and link the mqtt component.
|
||||
# First we get our dependency
|
||||
idf_component_get_property(pthread pthread COMPONENT_LIB)
|
||||
# And them we link the components
|
||||
target_link_libraries(${mqtt} ${pthread})
|
||||
@@ -0,0 +1,100 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# ESP-MQTT custom outbox sample application
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example is a slightly modified version of the tcp example to show how to configure a custom outbox.
|
||||
This example connects to the broker URI selected using `idf.py menuconfig` (using mqtt tcp transport) and as a demonstration subscribes/unsubscribes and send a message on certain topic.
|
||||
(Please note that the public broker is maintained by the community so may not be always available, for details please see this [disclaimer](https://iot.eclipse.org/getting-started/#sandboxes))
|
||||
|
||||
Note: If the URI equals `FROM_STDIN` then the broker address is read from stdin upon application startup (used for testing)
|
||||
|
||||
It uses ESP-MQTT library which implements mqtt client to connect to mqtt broker.
|
||||
|
||||
## Necessary changes to customize the outbox
|
||||
|
||||
To customize the outbox the first step is to enable it in the menuconfig option.
|
||||
|
||||
With this option enabled, the default implementation isn't defined and the function definition needs to be added to mqtt component.
|
||||
Any extra dependencies needed by the new sources also need to be added to the mqtt component. Refer to the example CMakeLists.txt file
|
||||
for the details on how to do it.
|
||||
|
||||
## The custom outbox in the example
|
||||
|
||||
For the sake of this example the customized outbox implements the same functionalits of the regular but using C++ as a language.
|
||||
|
||||
The implementation uses [C++ Polymorphic memory resources]() to control memory allocations and limit the usage of the memory.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example can be executed on any ESP32 board, the only required interface is WiFi and connection to internet.
|
||||
|
||||
### Configure the project
|
||||
|
||||
* Open the project configuration menu (`idf.py menuconfig`)
|
||||
* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details.
|
||||
|
||||
Note that the mandatory configurations for this example, mqtt custom outbox and C++ exceptions are automatically added by the `sdkconfig.defaults` file.
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (4635) example_common: Connected to example_netif_sta
|
||||
I (4645) example_common: - IPv4 address: 192.168.33.206,
|
||||
I (4645) example_common: - IPv6 address: fe80:0000:0000:0000:7e9e:bdff:fecf:00c0, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (4655) Monotonic: Monotonic: 400 bytes allocated, 400 total bytes in use
|
||||
I (4665) Monotonic: Monotonic: 1000 bytes allocated, 1400 total bytes in use
|
||||
I (4675) Monotonic: Monotonic: 128 bytes allocated, 1528 total bytes in use
|
||||
I (4685) Pool: Pool: 32 bytes allocated, 32 total bytes in use
|
||||
I (4685) Monotonic: Monotonic: 7688 bytes allocated, 9216 total bytes in use
|
||||
I (4695) Monotonic: Monotonic: 128 bytes allocated, 9344 total bytes in use
|
||||
I (4705) Pool: Pool: 480 bytes allocated, 512 total bytes in use
|
||||
I (4715) Monotonic: Monotonic: 992 bytes allocated, 10336 total bytes in use
|
||||
I (4715) Monotonic: Monotonic: 128 bytes allocated, 10464 total bytes in use
|
||||
I (4725) Pool: Pool: 23 bytes allocated, 535 total bytes in use
|
||||
I (4735) MQTT_EXAMPLE: Enqueued msg_id=14345
|
||||
I (4735) Pool: Pool: 29 bytes allocated, 564 total bytes in use
|
||||
I (4745) MQTT_EXAMPLE: Enqueued msg_id=3507
|
||||
I (4745) MQTT_EXAMPLE: Other event id:7
|
||||
I (4755) main_task: Returned from app_main()
|
||||
I (5085) MQTT_EXAMPLE: MQTT_EVENT_CONNECTED
|
||||
I (5085) Pool: Pool: 23 bytes allocated, 587 total bytes in use
|
||||
I (5085) MQTT_EXAMPLE: sent publish successful, msg_id=47425
|
||||
I (5085) Pool: Pool: 18 bytes allocated, 605 total bytes in use
|
||||
I (5095) MQTT_EXAMPLE: sent subscribe successful, msg_id=60709
|
||||
I (5105) Pool: Pool: 18 bytes allocated, 623 total bytes in use
|
||||
I (5105) MQTT_EXAMPLE: sent subscribe successful, msg_id=33273
|
||||
I (5395) Pool: Pool: 23 bytes deallocated, 623 total bytes in use
|
||||
I (5395) MQTT_EXAMPLE: MQTT_EVENT_PUBLISHED, msg_id=47425
|
||||
I (6005) Pool: Pool: 18 bytes deallocated, 623 total bytes in use
|
||||
I (6005) MQTT_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=60709
|
||||
I (6005) MQTT_EXAMPLE: sent publish successful, msg_id=0
|
||||
I (6015) Pool: Pool: 18 bytes deallocated, 623 total bytes in use
|
||||
I (6015) MQTT_EXAMPLE: MQTT_EVENT_SUBSCRIBED, msg_id=33273
|
||||
I (6025) MQTT_EXAMPLE: sent publish successful, msg_id=0
|
||||
I (6035) MQTT_EXAMPLE: MQTT_EVENT_DATA
|
||||
TOPIC=/topic/qos1
|
||||
DATA=data_3
|
||||
I (6315) MQTT_EXAMPLE: MQTT_EVENT_DATA
|
||||
TOPIC=/topic/qos1
|
||||
DATA=data_3
|
||||
I (6315) Pool: Pool: 23 bytes deallocated, 623 total bytes in use
|
||||
I (6315) MQTT_EXAMPLE: MQTT_EVENT_PUBLISHED, msg_id=14345
|
||||
I (6615) MQTT_EXAMPLE: MQTT_EVENT_DATA
|
||||
TOPIC=/topic/qos0
|
||||
DATA=data
|
||||
```
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "app_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES mqtt nvs_flash esp_netif
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config BROKER_URL
|
||||
string "Broker URL"
|
||||
default "mqtt://mqtt.eclipseprojects.io"
|
||||
help
|
||||
URL of the broker to connect to
|
||||
|
||||
config BROKER_URL_FROM_STDIN
|
||||
bool
|
||||
default y if BROKER_URL = "FROM_STDIN"
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
/* MQTT (over TCP) Example with custom outbox
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "protocol_examples_common.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "mqtt_client.h"
|
||||
|
||||
static const char *TAG = "MQTT_EXAMPLE";
|
||||
|
||||
|
||||
static void log_error_if_nonzero(const char *message, int error_code)
|
||||
{
|
||||
if (error_code != 0) {
|
||||
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Event handler registered to receive MQTT events
|
||||
*
|
||||
* This function is called by the MQTT client event loop.
|
||||
*
|
||||
* @param handler_args user data registered to the event.
|
||||
* @param base Event base for the handler(always MQTT Base in this example).
|
||||
* @param event_id The id for the received event.
|
||||
* @param event_data The data for the event, esp_mqtt_event_handle_t.
|
||||
*/
|
||||
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
|
||||
{
|
||||
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32 "", base, event_id);
|
||||
esp_mqtt_event_handle_t event = event_data;
|
||||
esp_mqtt_client_handle_t client = event->client;
|
||||
int msg_id;
|
||||
switch ((esp_mqtt_event_id_t)event_id) {
|
||||
case MQTT_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
|
||||
msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
|
||||
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
|
||||
|
||||
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
|
||||
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
|
||||
|
||||
msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);
|
||||
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
|
||||
|
||||
break;
|
||||
case MQTT_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
|
||||
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_UNSUBSCRIBED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_PUBLISHED:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||
break;
|
||||
case MQTT_EVENT_DATA:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
|
||||
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
||||
printf("DATA=%.*s\r\n", event->data_len, event->data);
|
||||
break;
|
||||
case MQTT_EVENT_ERROR:
|
||||
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
|
||||
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
|
||||
log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
|
||||
log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
|
||||
log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
|
||||
ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mqtt_app_start(void)
|
||||
{
|
||||
esp_mqtt_client_config_t mqtt_cfg = {
|
||||
.broker.address.uri = CONFIG_BROKER_URL,
|
||||
};
|
||||
#if CONFIG_BROKER_URL_FROM_STDIN
|
||||
char line[128];
|
||||
|
||||
if (strcmp(mqtt_cfg.broker.address.uri, "FROM_STDIN") == 0) {
|
||||
int count = 0;
|
||||
printf("Please enter url of mqtt broker\n");
|
||||
while (count < 128) {
|
||||
int c = fgetc(stdin);
|
||||
if (c == '\n') {
|
||||
line[count] = '\0';
|
||||
break;
|
||||
} else if (c > 0 && c < 127) {
|
||||
line[count] = c;
|
||||
++count;
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
mqtt_cfg.broker.address.uri = line;
|
||||
printf("Broker url: %s\n", line);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Configuration mismatch: wrong broker url");
|
||||
abort();
|
||||
}
|
||||
#endif /* CONFIG_BROKER_URL_FROM_STDIN */
|
||||
|
||||
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
|
||||
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
|
||||
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
|
||||
|
||||
/*Let's enqueue a few messages to the outbox to see the allocations*/
|
||||
int msg_id;
|
||||
msg_id = esp_mqtt_client_enqueue(client, "/topic/qos1", "data_3", 0, 1, 0, true);
|
||||
ESP_LOGI(TAG, "Enqueued msg_id=%d", msg_id);
|
||||
msg_id = esp_mqtt_client_enqueue(client, "/topic/qos2", "QoS2 message", 0, 2, 0, true);
|
||||
ESP_LOGI(TAG, "Enqueued msg_id=%d", msg_id);
|
||||
|
||||
/* Now we start the client and it's possible to see the memory usage for the operations in the outbox. */
|
||||
esp_mqtt_client_start(client);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "[APP] Startup..");
|
||||
ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
|
||||
ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
|
||||
|
||||
esp_log_level_set("*", ESP_LOG_INFO);
|
||||
esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("MQTT_EXAMPLE", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("TRANSPORT_BASE", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("esp-tls", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
|
||||
esp_log_level_set("custom_outbox", ESP_LOG_VERBOSE);
|
||||
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi or Ethernet Connection" section in
|
||||
* examples/protocols/README.md for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
mqtt_app_start();
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <deque>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory_resource>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "mqtt_outbox.h"
|
||||
|
||||
constexpr auto TAG = "custom_outbox";
|
||||
|
||||
/*
|
||||
* The trace resource class is created here as an example on how to build a custom memory resource
|
||||
* The class is only needed to show where we are allocating from and to track allocations and deallocations.
|
||||
*/
|
||||
class trace_resource : public std::pmr::memory_resource {
|
||||
public:
|
||||
explicit trace_resource(std::string resource_name, std::pmr::memory_resource *upstream_resource = std::pmr::get_default_resource()) : upstream{upstream_resource}, name{std::move(resource_name)} {}
|
||||
[[nodiscard]] std::string_view get_name() const noexcept
|
||||
{
|
||||
return std::string_view(name);
|
||||
}
|
||||
[[nodiscard]] auto upstream_resource() const
|
||||
{
|
||||
return upstream;
|
||||
}
|
||||
private:
|
||||
void *do_allocate(std::size_t bytes, std::size_t alignment) override
|
||||
{
|
||||
auto *allocated = upstream->allocate(bytes, alignment);
|
||||
allocated_total += bytes;
|
||||
ESP_LOGI(name.c_str(), "%s: %zu bytes allocated, %zu total bytes in use", name.c_str(), bytes, allocated_total);
|
||||
return allocated;
|
||||
}
|
||||
void do_deallocate(void *ptr, std::size_t bytes, std::size_t alignment) override
|
||||
{
|
||||
upstream->deallocate(ptr, bytes, alignment);
|
||||
ESP_LOGI(name.c_str(), "%s: %zu bytes deallocated, %zu total bytes in use", name.c_str(), bytes, allocated_total);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool do_is_equal(const std::pmr::memory_resource &other) const noexcept override
|
||||
{
|
||||
return this == &other;
|
||||
}
|
||||
size_t allocated_total{};
|
||||
std::pmr::memory_resource *upstream;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct outbox_item {
|
||||
/* Defining the allocator_type to let compiler know that our type is allocator aware,
|
||||
* This way the allocator used for the outbox is propagated to the messages*/
|
||||
using allocator_type = std::pmr::polymorphic_allocator<>;
|
||||
|
||||
/* Few strong types to diferetiate parameters*/
|
||||
enum class id_t : int {};
|
||||
enum class type_t : int {};
|
||||
enum class qos_t : int {};
|
||||
|
||||
/* Allocator aware constructors */
|
||||
outbox_item(
|
||||
std::pmr::vector<uint8_t> message,
|
||||
id_t msg_id,
|
||||
type_t msg_type,
|
||||
qos_t msg_qos,
|
||||
outbox_tick_t tick,
|
||||
pending_state_t pending_state,
|
||||
allocator_type alloc = {}
|
||||
) : message(std::move(message), alloc), id(msg_id), type(msg_type), qos(msg_qos), tick(tick), pending_state(pending_state) {}
|
||||
|
||||
/*Copy and move constructors have an extra allocator parameter, for copy default and allocator aware are the same.*/
|
||||
outbox_item(const outbox_item &other, allocator_type alloc = {}) : message(other.message, alloc), id(other.id), type(other.type), qos(other.qos), tick(other.tick), pending_state(other.pending_state) {}
|
||||
outbox_item(outbox_item &&other, allocator_type alloc) noexcept : message(std::move(other.message), alloc), id(other.id), type(other.type), qos(other.qos), tick(other.tick), pending_state(other.pending_state)
|
||||
{}
|
||||
|
||||
outbox_item(const outbox_item &) = default;
|
||||
outbox_item(outbox_item &&other) = default;
|
||||
outbox_item &operator=(const outbox_item &rhs) = default;
|
||||
outbox_item &operator=(outbox_item &&other) = default;
|
||||
~outbox_item() = default;
|
||||
|
||||
/* Getters to support outbox operation */
|
||||
[[nodiscard]] auto state() const noexcept
|
||||
{
|
||||
return pending_state;
|
||||
}
|
||||
|
||||
[[nodiscard]] allocator_type get_allocator() const
|
||||
{
|
||||
return message.get_allocator();
|
||||
}
|
||||
|
||||
void set(pending_state state) noexcept
|
||||
{
|
||||
pending_state = state;
|
||||
}
|
||||
|
||||
void set(outbox_tick_t n_tick) noexcept
|
||||
{
|
||||
tick = n_tick;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_id() const noexcept
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_type() const noexcept
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_tick() const noexcept
|
||||
{
|
||||
return tick;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_data(size_t *len, uint16_t *msg_id, int *msg_type, int *msg_qos)
|
||||
{
|
||||
*len = message.size();
|
||||
*msg_id = static_cast<uint16_t>(id);
|
||||
*msg_type = static_cast<int>(type);
|
||||
*msg_qos = static_cast<int>(qos);
|
||||
return message.data();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_size() const noexcept
|
||||
{
|
||||
return message.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::pmr::vector<uint8_t> message;
|
||||
id_t id;
|
||||
type_t type;
|
||||
qos_t qos;
|
||||
outbox_tick_t tick;
|
||||
pending_state_t pending_state;
|
||||
};
|
||||
|
||||
/*
|
||||
* For the outbox_t we let the special member functions as default and
|
||||
* we don't extend the allocator aware versions for the sake of the simplicity, since the operations are not needed in the usage.
|
||||
*/
|
||||
struct outbox_t {
|
||||
using allocator_type = std::pmr::polymorphic_allocator<>;
|
||||
explicit outbox_t(allocator_type alloc = {}) : queue(alloc) {}
|
||||
|
||||
outbox_item_handle_t get(outbox_item::id_t msg_id)
|
||||
{
|
||||
if (auto item = std::ranges::find_if(queue, [msg_id](auto & item) {
|
||||
return item.get_id() == msg_id;
|
||||
});
|
||||
item != std::end(queue)) {
|
||||
return &(*item);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int delete_expired(outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
return std::erase_if(queue, [current_tick, timeout, this](const outbox_item & item) {
|
||||
if (current_tick - item.get_tick() > timeout) {
|
||||
total_size -= item.get_size();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
outbox_item::id_t delete_single_expired(outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
if (auto erase = std::ranges::find_if(queue, [current_tick, timeout](auto & item) {
|
||||
return (current_tick - item.get_tick() > timeout);
|
||||
}); erase != std::end(queue)) {
|
||||
auto msg_id = erase->get_id();
|
||||
total_size -= erase->get_size();
|
||||
queue.erase(erase);
|
||||
return msg_id;
|
||||
}
|
||||
return outbox_item::id_t{-1};
|
||||
}
|
||||
|
||||
auto erase(outbox_item_handle_t to_erase)
|
||||
{
|
||||
return erase_if([to_erase](auto & item) {
|
||||
return &item == to_erase;
|
||||
});
|
||||
}
|
||||
|
||||
auto erase(outbox_item::id_t msg_id, outbox_item::type_t msg_type)
|
||||
{
|
||||
return erase_if([msg_id, msg_type](auto & item) {
|
||||
return (item.get_id() == msg_id && (item.get_type() == msg_type));
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] auto size() const noexcept
|
||||
{
|
||||
return total_size;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
outbox_item_handle_t enqueue(outbox_message_handle_t message, outbox_tick_t tick) noexcept
|
||||
{
|
||||
try {
|
||||
auto &item =
|
||||
queue.emplace_back(std::pmr::vector<uint8_t> {message->data, message->data + message->len},
|
||||
outbox_item::id_t{message->msg_id},
|
||||
outbox_item::type_t{message->msg_type},
|
||||
outbox_item::qos_t{message->msg_qos},
|
||||
tick,
|
||||
QUEUED
|
||||
);
|
||||
total_size += item.get_size();
|
||||
ESP_LOGD(TAG, "ENQUEUE msgid=%d, msg_type=%d, len=%d, size=%" PRIu64, message->msg_id, message->msg_type, message->len + message->remaining_len, outbox_get_size(this));
|
||||
return &item;
|
||||
} catch (const std::exception &e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
outbox_item_handle_t dequeue(pending_state_t state, outbox_tick_t *tick)
|
||||
{
|
||||
if (auto item = std::ranges::find_if(queue, [state](auto & item) {
|
||||
return item.state() == state;
|
||||
});
|
||||
item != std::end(queue)) {
|
||||
if (tick != nullptr) {
|
||||
*tick = item->get_tick();
|
||||
}
|
||||
return &(*item);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
[[nodiscard]] allocator_type get_allocator() const
|
||||
{
|
||||
return queue.get_allocator();
|
||||
}
|
||||
private:
|
||||
[[nodiscard]] esp_err_t erase_if(std::predicate<outbox_item &> auto &&predicate)
|
||||
{
|
||||
if (auto to_erase = std::ranges::find_if(queue, predicate); to_erase != std::end(queue)) {
|
||||
total_size -= to_erase->get_size();
|
||||
queue.erase(to_erase);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
std::size_t total_size{};
|
||||
std::pmr::deque<outbox_item> queue ;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
outbox_handle_t outbox_init()
|
||||
{
|
||||
/* First we create a fixed size memory buffer to be used. */
|
||||
static constexpr auto work_memory_size = 16 * 1024;
|
||||
static std::array<std::byte, work_memory_size> resource_buffer{};
|
||||
try {
|
||||
/*
|
||||
* Since the outbox is managed by a C API we can't rely on C++ automatic cleanup and smart pointers but, on production code it would be better to add the
|
||||
* memory resources to outbox_t, applying RAII principles, and make only outbox_item allocator aware. For the sake of the example we are keeping them
|
||||
* separated to explictly show the relations.
|
||||
* First we create the monotonic buffer and add null_memory_resource as upstream. This way if our working memory is exausted an exception is thrown.
|
||||
*/
|
||||
auto *monotonic_resource = new std::pmr::monotonic_buffer_resource{resource_buffer.data(), resource_buffer.size(), std::pmr::null_memory_resource()};
|
||||
/*Here we add our custom trace wrapper type to trace allocations and deallocations*/
|
||||
auto *trace_monotonic = new trace_resource("Monotonic", monotonic_resource);
|
||||
|
||||
/* We compose monotonic buffer with pool resource, since the monotonic deallocate is a no-op and we need to remove messages to not go out of memory.*/
|
||||
auto *pool_resource = new std::pmr::unsynchronized_pool_resource{trace_monotonic};
|
||||
auto *trace_pool = new trace_resource("Pool", pool_resource);
|
||||
/* Our outbox class is created using the trace_pool as memory resource */
|
||||
auto *outbox = new outbox_t{trace_pool};
|
||||
return outbox;
|
||||
} catch (const std::exception &e) {
|
||||
ESP_LOGD(TAG, "Not enough memory to construct the outbox, review the resource_buffer size");
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_enqueue(outbox_handle_t outbox, outbox_message_handle_t message, outbox_tick_t tick)
|
||||
{
|
||||
return outbox->enqueue(message, tick);
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_get(outbox_handle_t outbox, int msg_id)
|
||||
{
|
||||
return outbox->get(outbox_item::id_t{msg_id});
|
||||
}
|
||||
|
||||
outbox_item_handle_t outbox_dequeue(outbox_handle_t outbox, pending_state_t pending, outbox_tick_t *tick)
|
||||
{
|
||||
return outbox->dequeue(pending, tick);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *outbox_item_get_data(outbox_item_handle_t item, size_t *len, uint16_t *msg_id, int *msg_type, int *qos)
|
||||
{
|
||||
if (item == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return item->get_data(len, msg_id, msg_type, qos);
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete_item(outbox_handle_t outbox, outbox_item_handle_t item_to_delete)
|
||||
{
|
||||
return outbox->erase(item_to_delete);
|
||||
|
||||
}
|
||||
|
||||
esp_err_t outbox_delete(outbox_handle_t outbox, int msg_id, int msg_type)
|
||||
{
|
||||
return outbox->erase(outbox_item::id_t{msg_id}, outbox_item::type_t{msg_type});
|
||||
}
|
||||
|
||||
int outbox_delete_single_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
return static_cast<int>(outbox->delete_single_expired(current_tick, timeout));
|
||||
}
|
||||
|
||||
int outbox_delete_expired(outbox_handle_t outbox, outbox_tick_t current_tick, outbox_tick_t timeout)
|
||||
{
|
||||
return outbox->delete_expired(current_tick, timeout);
|
||||
}
|
||||
|
||||
esp_err_t outbox_set_pending(outbox_handle_t outbox, int msg_id, pending_state_t pending)
|
||||
{
|
||||
if (auto *item = outbox->get(outbox_item::id_t{msg_id}); item != nullptr) {
|
||||
item->set(pending);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
pending_state_t outbox_item_get_pending(outbox_item_handle_t item)
|
||||
{
|
||||
if (item != nullptr) {
|
||||
return item->state();
|
||||
}
|
||||
return QUEUED;
|
||||
}
|
||||
|
||||
esp_err_t outbox_set_tick(outbox_handle_t outbox, int msg_id, outbox_tick_t tick)
|
||||
{
|
||||
if (auto *item = outbox->get(outbox_item::id_t{msg_id}); item != nullptr) {
|
||||
item->set(tick);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint64_t outbox_get_size(outbox_handle_t outbox)
|
||||
{
|
||||
return outbox->size();
|
||||
}
|
||||
|
||||
void outbox_delete_all_items(outbox_handle_t outbox)
|
||||
{
|
||||
outbox->clear();
|
||||
}
|
||||
|
||||
void outbox_destroy(outbox_handle_t outbox)
|
||||
{
|
||||
auto *trace_pool = static_cast<trace_resource *>(outbox->get_allocator().resource());
|
||||
auto *pool_resource = static_cast<std::pmr::unsynchronized_pool_resource *>(trace_pool->upstream_resource());
|
||||
auto *trace_monotonic = static_cast<trace_resource *>(pool_resource->upstream_resource());
|
||||
auto *monotonic_resource = static_cast<std::pmr::monotonic_buffer_resource *>(trace_monotonic->upstream_resource());
|
||||
|
||||
delete monotonic_resource;
|
||||
delete trace_monotonic;
|
||||
delete pool_resource;
|
||||
delete trace_pool;
|
||||
delete outbox;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user