ESP-IoT-Solution Programming Guide

[中文]

This is the documentation for ESP-IoT-Solution Development Framework.

ESP-IoT-Solution contains device drivers and code frameworks for the development of IoT system, which works as extra components of ESP-IDF and much easier to start.

Get Started

Display

USB Host&Device

Get Started

Display

USB Host&Device

GUI

Input

Sensors

GUI

Input

Sensors

Audio

Security&Encryption

Contribute

Audio

Security&Encryption

Contribute

Get Started

[中文]

This document is intended to help you set up the development environment for ESP-IoT-Solution (Espressif IoT Solution). After that, a simple example will show you how to use ESP-IoT-Solution to set up environment, create a project, build and flash firmware onto an ESP series board, etc.

ESP-IoT-Solution Introduction

ESP-IoT-Solution contains peripheral drivers and code frameworks commonly used in IoT system development, which provide complementary components to ESP-IDF to facilitate simpler development, mainly including the following contents:

  • Device drivers such as sensors, screens, audio devices, input devices, actuators, and etc.

  • Code framework and related documents of low power management, security encryption, storage and etc.

  • Entrance guideline for Espressif’s open-source solutions from the perspective of practical application.

ESP-IoT-Solution Versions

Specifications of different ESP-IoT-Solution versions are listed in the following table:

ESP-IoT-Solution version

Corresponding ESP-IDF version

Main changes

Status

master

>=v4.4

support component manager and new chips

New features development branch

release/v1.1

v4.0.1

IDF version updated, deleted codes that have been moved to other repos

Stop maintenance

release/v1.0

v3.2.2

Legacy version

Stop maintenance

Since the master branch uses the ESP Component Manager to manager components, each of them is a separate package, and each package may support a different version of the ESP-IDF, which will be declared in the component’s idf_component.yml file.

ESP-IDF Introduction

ESP-IDF is the IoT development framework for ESP series SoCs provided by Espressif, including:

  • A series of libraries and header files, providing core components required for building software projects based on ESP SoC;

  • Common tools and functions used during the development and manufacturing processes, e.g., build, flashing, debugging, measurement and etc.

Note

For detailed information, please go to ESP-IDF Programming Guide.

ESP Series SoC Introduction

You can select any development board from ESP series to get started with ESP-IoT-Solution, or select a supported board from the Boards Component directly for a quick start.

ESP series SoC support the following features:

  • 2.4 GHz Wi-Fi

  • Bluetooth

  • High-performance single core, dual-core processor, capable of running at 240 MHz

  • Ultra-low-power co-processor

  • Various peripherals including GPIO, I2C, I2S, SPI ,UART, SDIO, RMT, LEDC PWM, Ethernet, TWAI®, Touch, USB OTG and etc.

  • Rich memory resources, including up to 520 KB internal RAM and can support external PSRAM

  • Support security functions, e.g., hardware encryption

ESP series of SoC are designed with the 40nm technology, showing the best power and RF performance, versatility and reliability in a wide variety of application and power scenarios.

Note

The configuration varies by SoC series, please refer to ESP Product Selector for details.

Setting up Development Environment

1. Get ESP-IDF

As ESP-IoT-Solution relies on ESP-IDF basic functions and build tools, please set up ESP-IDF development environment first following ESP-IDF Installation Step by Step. Please note that different versions of ESP-IoT-Solution may rely on different ESP-IDF versions, please refer to ESP-IoT-Solution Versions for specifications.

2. Get ESP-IoT-Solution

For master version, please use the following command:

git clone --recursive https://github.com/espressif/esp-iot-solution

For release/v1.1 version, please use the following command:

git clone -b release/v1.1 --recursive https://github.com/espressif/esp-iot-solution

For other versions, please also use this command with release/v1.1 replaced by your target branch name.

Use ESP-IoT-Solution Components

If you just want to use the components in ESP-IoT-Solution, we recommend you use it from the ESP Component Registry.

The registered components in ESP-IoT-Solution are listed in README.md , You can directly add the components from the Component Registry to your project by using the idf.py add-dependency command under your project’s root directory. eg run idf.py add-dependency "espressif/usb_stream" to add the usb_stream, the component will be downloaded automatically during the CMake step.

Please refer to IDF Component Manager for details.

Build and Download

1. Set up the environment variables

The tools installed in above steps are not yet added to the PATH environment variables. To make the tools usable from the command line, please follow the following steps to add environment variables:

  • Add ESP-IDF environment variables:

    For Windows system, please open the Command Prompt and run:

    %userprofile%\esp\esp-idf\export.bat
    

    For Linux and macOS, please run:

    . $HOME/esp/esp-idf/export.sh
    

    Please remember to replace the paths in above commands as your actual paths.

  • Add IOT_SOLUTION_PATH environment variables:

    For Windows system, please open the Command Prompt and run:

    set IOT_SOLUTION_PATH=C:\esp\esp-iot-solution
    

    For Linux and macOS, please run:

    export IOT_SOLUTION_PATH=~/esp/esp-iot-solution
    

Note

The environment variables set by the above method are only valid in the current terminal. Please repeat above steps if you open a new terminal.

2. Set build target

ESP-IDF supports multiple chips as esp32, esp32s2 and others, please set your target chip before building (the default target is esp32). For example, you can set the build target as esp32s2.

idf.py set-target esp32s2

For examples in ESP-IoT-Solution developed based on Boards Component, you can go to Board Options -> Choose Target Board in menuconfig to choose a target board:

idf.py menuconfig

3. Build and download the program

Use the idf.py tool to build and download the program with:

idf.py -p PORT build flash

Please replace PORT with your board’s port name. Serial ports have the following patterns in their names: Windows is like COMx; Linux starting with /dev/ttyUSBx; macOS usually is /dev/cu..

4. Serial print log

Use the idf.py tool to see logs:

idf.py -p PORT monitor

Do not forget to replace PORT with your serial port name (COMx for Windows; /dev/ttyUSBx for Linux; /dev/cu. for macOS).

Basic Component

[中文]

Communication Bus

[中文]

The communication bus component (Bus) is a set of application-layer code built on top of the ESP-IDF peripheral driver code, including i2c_bus, spi_bus and etc. 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

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, with the SPI bus addressing devices based on CS pins and the I2C bus addressing devices based on their addresses, thus achieving software independence between different devices on the same bus.

_images/i2c_bus.png

i2c_bus Connection Diagram

_images/spi_bus.png

spi_bus Connection Diagram

How to Use i2c_bus

  1. Create a bus: create a bus object using i2c_bus_create(). During the process, you need to specify the I2C port number and the bus configuration option i2c_config_t, which includes SDA and SCL pin numbers, pull-up and pull-down modes, as these are determined when the system is designed and normally will not be changed at runtime. The bus configuration option also includes the default clock frequency of the bus, which is used when the device does not specify a frequency.

  2. Create a device: use i2c_bus_device_create() to create a device on the bus object created in the first step. During the process, you need to specify bus handle, the I2C address of the device, and the clock frequency when the device is running. The frequency will be dynamically changed during I2C transmission based on device configuration options. The device clock frequency can be configured as 0, indicating the current bus frequency is used by default.

  3. Data reading: use i2c_bus_read_byte() or i2c_bus_read_bytes() to read Byte data; use i2c_bus_read_bit() or i2c_bus_read_bits() to read bit data. During the process, you only need to pass in the device handle, the device register address, a buffer to hold the read data, the read length, etc. The register address can be configured as NULL_I2C_MEM_ADDR for devices without internal registers.

  4. Data writing: use i2c_bus_write_byte() or i2c_bus_write_bytes() to write Byte data; use i2c_bus_write_bit() or i2c_bus_write_bits() to write bit data. During the process, you only need to pass in the device handle, the device register address, the data location to be written, the write length, etc. The register address can be configured as NULL_I2C_MEM_ADDR for devices without internal registers.

  5. Delete device and bus: if all i2c_bus communication has been completed, you can free your system resources by deleting devices and bus objects. Use i2c_bus_device_delete() to delete created devices respectively, then use i2c_bus_delete() to delete bus resources. If the bus is deleted with the device not being deleted yet, this operation will not take effect.

Example:

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 = 100000,
}; // i2c_bus configurations

uint8_t data_rd[2] = {0};
uint8_t data_wr[2] = {0x01, 0x21};

i2c_bus_handle_t i2c0_bus = i2c_bus_create(I2C_NUM_0, &conf); // create i2c_bus
i2c_bus_device_handle_t i2c_device1 = i2c_bus_device_create(i2c0_bus, 0x28, 400000); // create device1, address: 0x28 , clk_speed: 400000
i2c_bus_device_handle_t i2c_device2 = i2c_bus_device_create(i2c0_bus, 0x32, 0); // create device2, address: 0x32 , clk_speed: no-specified

i2c_bus_read_bytes(i2c_device1, NULL_I2C_MEM_ADDR, 2, data_rd); // read bytes from device1 with no register address
i2c_bus_write_bytes(i2c_device2, 0x10, 2, data_wr); // write bytes to device2 register 0x10

i2c_bus_device_delete(&i2c_device1); //delete device1
i2c_bus_device_delete(&i2c_device2); //delete device2
i2c_bus_delete(&i2c0_bus);  //delete i2c_bus

Note

For some special application scenarios:

  1. When the address of a register is 16-bit, you can use i2c_bus_read_reg16() or i2c_bus_write_reg16() to read or write its data;

  2. For devices that need to skip the address phase or need to add a command phase, you can operate using i2c_bus_cmd_begin() combined with I2C command link.

How to Use spi_bus

  1. Create a bus: use spi_bus_create() to create a bus object. During the process, you need to specify the SPI port number (can choose between SPI2_HOST and SPI3_HOST) and the bus configuration option spi_config_t, which includes the pin numbers of MOSI, MISO and SCLK, as these are determined when the system is designed and normally will not be changed at runtime. The bus configuration option also includes max_transfer_sz to configure the maximum data size during a transmission. When max_transfer_sz is configured to 0, it means the maximum size will be the default value 4096.

  2. Create a device: use spi_bus_device_create() to create a device on the bus object created in the first step. During the process, you need to specify the bus handle, the CS pin number of the device, device operation mode, the clock frequency when the device is running. The device mode and frequency will be dynamically changed during SPI transmissions based on device configuration options.

  3. Data transmission: use spi_bus_transfer_byte(), spi_bus_transfer_bytes(), spi_bus_transfer_reg16() or spi_bus_transfer_reg32() to transfer data directly. Data send and receive can be operated at the same time since SPI communication is a full-duplex communication. During the process, you only need to pass in the device handle, data to be transmitted, a buffer to hold the read data, transmission length, etc.

  4. Delete device and bus: if all spi_bus communication has been completed, you can free your system resources by deleting devices and bus objects. Use spi_bus_device_delete() to delete created devices respectively, then use spi_bus_delete() to delete bus resources. If the bus is deleted with the device not being deleted yet, this operation will not take effect.

Example:

spi_bus_handle_t bus_handle = NULL;
spi_bus_device_handle_t device_handle = NULL;
uint8_t data8_in = 0;
uint8_t data8_out = 0xff;
uint16_t data16_in = 0;
uint32_t data32_in = 0;

spi_config_t bus_conf = {
    .miso_io_num = 19,
    .mosi_io_num = 23,
    .sclk_io_num = 18,
}; // spi_bus configurations

spi_device_config_t device_conf = {
    .cs_io_num = 19,
    .mode = 0,
    .clock_speed_hz = 20 * 1000 * 1000,
}; // spi_device configurations

bus_handle = spi_bus_create(SPI2_HOST, &bus_conf); // create spi bus
device_handle = spi_bus_device_create(bus_handle, &device_conf); // create spi device

spi_bus_transfer_bytes(device_handle, &data8_out, &data8_in, 1); // transfer 1 byte with spi device
spi_bus_transfer_bytes(device_handle, NULL, &data8_in, 1); // only read 1 byte with spi device
spi_bus_transfer_bytes(device_handle, &data8_out, NULL, 1); // only write 1 byte with spi device
spi_bus_transfer_reg16(device_handle, 0x1020, &data16_in); // transfer 16-bit value with the device
spi_bus_transfer_reg32(device_handle, 0x10203040, &data32_in); // transfer 32-bit value with the device

spi_bus_device_delete(&device_handle);
spi_bus_delete(&bus_handle);

Note

For some special application scenarios, you can operate using spi_bus_transmit_begin() combined with spi_transaction_t directly.

Adapted IDF Versions

  • ESP-IDF v4.0 and later versions.

Supported Chips

  • ESP32

  • ESP32-S2

API Reference

i2c_bus API Reference
Header File
Functions
i2c_bus_handle_t i2c_bus_create(i2c_port_t port, const i2c_config_t *conf)

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.

Return

i2c_bus_handle_t Return the I2C bus handle if created successfully, return NULL if failed.

Parameters
  • port: I2C port number

  • conf: Pointer to I2C bus configuration

esp_err_t i2c_bus_delete(i2c_bus_handle_t *p_bus_handle)

Delete and release the I2C bus resource.

Return

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • p_bus_handle: Point to the I2C bus handle, if delete succeed handle will set to NULL.

uint8_t i2c_bus_scan(i2c_bus_handle_t bus_handle, uint8_t *buf, uint8_t num)

Scan i2c devices attached on i2c bus.

Return

uint8_t Total number of devices found on the I2C bus

Parameters
  • bus_handle: I2C bus handle

  • buf: Pointer to a buffer to save devices’ address, if NULL no address will be saved.

  • num: Maximum number of addresses to save, invalid if buf set to NULL, higer addresses will be discarded if num less-than the total number found on the I2C bus.

uint32_t i2c_bus_get_current_clk_speed(i2c_bus_handle_t bus_handle)

Get current active clock speed.

Return

uint32_t current clock speed

Parameters
  • bus_handle: I2C bus handle

uint8_t i2c_bus_get_created_device_num(i2c_bus_handle_t bus_handle)

Get created device number of the bus.

Return

uint8_t created device number of the bus

Parameters
  • bus_handle: I2C bus handle

i2c_bus_device_handle_t i2c_bus_device_create(i2c_bus_handle_t bus_handle, uint8_t dev_addr, uint32_t clk_speed)

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.

Return

i2c_bus_device_handle_t return a device handle if created successfully, return NULL if failed.

Parameters
  • bus_handle: Point to the I2C bus handle

  • dev_addr: i2c device address

  • clk_speed: device specified clock frequency the i2c_bus will switch to during each transfer. 0 if use current bus speed.

esp_err_t i2c_bus_device_delete(i2c_bus_device_handle_t *p_dev_handle)

Delete and release the I2C device resource, i2c_bus_device_delete should be used in pairs with i2c_bus_device_create.

Return

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • p_dev_handle: Point to the I2C device handle, if delete succeed handle will set to NULL.

uint8_t i2c_bus_device_get_address(i2c_bus_device_handle_t dev_handle)

Get device’s I2C address.

Return

uint8_t I2C address, return NULL_I2C_DEV_ADDR if dev_handle is invalid.

Parameters
  • dev_handle: I2C device handle

esp_err_t i2c_bus_read_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t *data)

Read single byte from i2c device with 8-bit internal register/memory address.

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.

Parameters
  • dev_handle: I2C device handle

  • mem_address: The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.

  • data: Pointer to a buffer to save the data that was read

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)

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.

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.

Parameters
  • dev_handle: I2C device handle

  • mem_address: The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.

  • data_len: Number of bytes to read

  • data: Pointer to a buffer to save the data that was read

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)

Read single bit of a byte from i2c device with 8-bit internal register/memory address.

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.

Parameters
  • dev_handle: I2C device handle

  • mem_address: The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.

  • bit_num: The bit number 0 - 7 to read

  • data: Pointer to a buffer to save the data that was read. *data == 0 -> bit = 0, *data !=0 -> bit = 1.

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)

Read multiple bits of a byte from i2c device with 8-bit internal register/memory address.

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.

Parameters
  • dev_handle: I2C device handle

  • mem_address: The internal reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.

  • bit_start: The bit to start from, 0 - 7, MSB at 0

  • length: The number of bits to read, 1 - 8

  • data: Pointer to a buffer to save the data that was read

esp_err_t i2c_bus_write_byte(i2c_bus_device_handle_t dev_handle, uint8_t mem_address, uint8_t data)

Write single byte to i2c device with 8-bit internal register/memory address.

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.

Parameters
  • dev_handle: I2C device handle

  • mem_address: The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.

  • data: The byte to write.

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)

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.

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.

Parameters
  • dev_handle: I2C device handle

  • mem_address: The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.

  • data_len: Number of bytes to write

  • data: Pointer to the bytes to write.

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)

Write single bit of a byte to an i2c device with 8-bit internal register/memory address.

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.

Parameters
  • dev_handle: I2C device handle

  • mem_address: The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.

  • bit_num: The bit number 0 - 7 to write

  • data: The bit to write, data == 0 means set bit = 0, data !=0 means set bit = 1.

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)

Write multiple bits of a byte to an i2c device with 8-bit internal register/memory address.

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.

Parameters
  • dev_handle: I2C device handle

  • mem_address: The internal reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.

  • bit_start: The bit to start from, 0 - 7, MSB at 0

  • length: The number of bits to write, 1 - 8

  • data: The bits to write.

esp_err_t i2c_bus_cmd_begin(i2c_bus_device_handle_t dev_handle, i2c_cmd_handle_t cmd)

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

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.

Parameters
  • dev_handle: I2C device handle

  • cmd: I2C command handler

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)

Write date to an i2c device with 16-bit internal reg/mem address.

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.

Parameters
  • dev_handle: I2C device handle

  • mem_address: The internal 16-bit reg/mem address to write to, set to NULL_I2C_MEM_ADDR if no internal address.

  • data_len: Number of bytes to write

  • data: Pointer to the bytes to write.

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)

Read date from i2c device with 16-bit internal reg/mem address.

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.

Parameters
  • dev_handle: I2C device handle

  • mem_address: The internal 16-bit reg/mem address to read from, set to NULL_I2C_MEM_ADDR if no internal address.

  • data_len: Number of bytes to read

  • data: Pointer to a buffer to save the data that was read

Macros
NULL_I2C_MEM_ADDR

set mem_address to NULL_I2C_MEM_ADDR if i2c device has no internal address during read/write

NULL_I2C_DEV_ADDR

invalid i2c device address

gpio_pad_select_gpio
portTICK_RATE_MS
Type Definitions
typedef void *i2c_bus_handle_t

i2c bus handle

typedef void *i2c_bus_device_handle_t

i2c device handle

spi_bus API Reference
Header File
Functions
spi_bus_handle_t spi_bus_create(spi_host_device_t host_id, const spi_config_t *bus_conf)

Create and initialize a spi bus and return the spi bus handle.

Return

spi_bus_handle_t handle for spi bus operation, NULL if failed.

Parameters
  • host_id: SPI peripheral that controls this bus, SPI2_HOST or SPI3_HOST

  • bus_conf: spi bus configurations details in spi_config_t

esp_err_t spi_bus_delete(spi_bus_handle_t *p_bus_handle)

Deinitialize and delete the spi bus.

Return

esp_err_t

  • ESP_ERR_INVALID_ARG if parameter is invalid

  • ESP_FAIL Fail

  • ESP_OK Success

Parameters
  • p_bus_handle: pointer to spi bus handle, if delete succeed handle will set to NULL.

spi_bus_device_handle_t spi_bus_device_create(spi_bus_handle_t bus_handle, const spi_device_config_t *device_conf)

Create and add a device on the spi bus.

Return

spi_bus_device_handle_t handle for device operation, NULL if failed.

Parameters
  • bus_handle: handle for spi bus operation.

  • device_conf: spi device configurations details in spi_device_config_t

esp_err_t spi_bus_device_delete(spi_bus_device_handle_t *p_dev_handle)

Deinitialize and remove the device from spi bus.

Return

esp_err_t

  • ESP_ERR_INVALID_ARG if parameter is invalid

  • ESP_FAIL Fail

  • ESP_OK Success

Parameters
  • p_dev_handle: pointer to device handle, if delete succeed handle will set to NULL.

esp_err_t spi_bus_transfer_byte(spi_bus_device_handle_t dev_handle, uint8_t data_out, uint8_t *data_in)

Transfer one byte with the device.

Return

esp_err_t

  • ESP_ERR_INVALID_ARG if parameter is invalid

  • ESP_ERR_TIMEOUT if bus is busy

  • ESP_OK on success

Parameters
  • dev_handle: handle for device operation.

  • data_out: data will send to device.

  • data_in: pointer to receive buffer, set NULL to skip receive phase.

esp_err_t spi_bus_transfer_bytes(spi_bus_device_handle_t dev_handle, const uint8_t *data_out, uint8_t *data_in, uint32_t data_len)

Transfer multi-bytes with the device.

Return

esp_err_t

  • ESP_ERR_INVALID_ARG if parameter is invalid

  • ESP_ERR_TIMEOUT if bus is busy

  • ESP_OK on success

Parameters
  • dev_handle: handle for device operation.

  • data_out: pointer to sent buffer, set NULL to skip sent phase.

  • data_in: pointer to receive buffer, set NULL to skip receive phase.

  • data_len: number of bytes will transfer.

esp_err_t spi_bus_transmit_begin(spi_bus_device_handle_t dev_handle, spi_transaction_t *p_trans)

Send a polling transaction, wait for it to complete, and return the result.

Note

Only call this function when spi_bus_transfer_xx do not meet the requirements

Return

esp_err_t

  • ESP_ERR_INVALID_ARG if parameter is invalid

  • ESP_ERR_TIMEOUT if bus is busy

  • ESP_OK on success

Parameters
  • dev_handle: handle for device operation.

  • p_trans: Description of transaction to execute

esp_err_t spi_bus_transfer_reg16(spi_bus_device_handle_t dev_handle, uint16_t data_out, uint16_t *data_in)

Transfer one 16-bit value with the device. using msb by default. For example 0x1234, 0x12 will send first then 0x34.

Return

esp_err_t

  • ESP_ERR_INVALID_ARG if parameter is invalid

  • ESP_ERR_TIMEOUT if bus is busy

  • ESP_OK on success

Parameters
  • dev_handle: handle for device operation.

  • data_out: data will send to device.

  • data_in: pointer to receive buffer, set NULL to skip receive phase.

esp_err_t spi_bus_transfer_reg32(spi_bus_device_handle_t dev_handle, uint32_t data_out, uint32_t *data_in)

Transfer one 32-bit value with the device. using msb by default. For example 0x12345678, 0x12 will send first, 0x78 will send in the end.

Return

esp_err_t

  • ESP_ERR_INVALID_ARG if parameter is invalid

  • ESP_ERR_TIMEOUT if bus is busy

  • ESP_OK on success

Parameters
  • dev_handle: handle for device operation.

  • data_out: data will send to device.

  • data_in: pointer to receive buffer, set NULL to skip receive phase.

Structures
struct spi_config_t

spi bus initialization parameters.

Public Members

gpio_num_t miso_io_num

GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used.

gpio_num_t mosi_io_num

GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used.

gpio_num_t sclk_io_num

GPIO pin for Spi CLocK signal, or -1 if not used

int max_transfer_sz

<Maximum length of bytes available to send, if < 4096, 4096 will be set

struct spi_device_config_t

spi device initialization parameters.

Public Members

gpio_num_t cs_io_num

GPIO pin to select this device (CS), or -1 if not used

uint8_t mode

modes (0,1,2,3) that correspond to the four possible clocking configurations

int clock_speed_hz

spi clock speed, divisors of 80MHz, in Hz. See `SPI_MASTER_FREQ_*

Macros
NULL_SPI_CS_PIN

set cs_io_num to NULL_SPI_CS_PIN if spi device has no CP pin

Type Definitions
typedef void *spi_bus_handle_t

spi bus handle

typedef void *spi_bus_device_handle_t

spi device handle

I2S LCD 驱动

API 参考

Header File
Functions
i2s_lcd_handle_t i2s_lcd_driver_init(const i2s_lcd_config_t *config)

Initilize i2s lcd driver.

Return

A handle to the created i2s lcd driver, or NULL in case of error.

Parameters
  • config: configuration of i2s

esp_err_t i2s_lcd_driver_deinit(i2s_lcd_handle_t handle)

Deinit i2s lcd driver.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG handle is invalid

Parameters
  • handle: i2s lcd driver handle to deinitilize

esp_err_t i2s_lcd_write_data(i2s_lcd_handle_t handle, uint16_t data)

Write a data to LCD.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG handle is invalid

Parameters
  • handle: i2s lcd driver handle

  • data: Data to write

esp_err_t i2s_lcd_write_cmd(i2s_lcd_handle_t handle, uint16_t cmd)

Write a command to LCD.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG handle is invalid

Parameters
  • handle: Handle of i2s lcd driver

  • cmd: command to write

esp_err_t i2s_lcd_write_command(i2s_lcd_handle_t handle, const uint8_t *cmd, uint32_t length)

Write a command to LCD.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG handle is invalid

Parameters
  • handle: Handle of i2s lcd driver

  • cmd: command to write

  • length: length of command

esp_err_t i2s_lcd_write(i2s_lcd_handle_t handle, const uint8_t *data, uint32_t length)

Write block data to LCD.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG handle is invalid

Parameters
  • handle: Handle of i2s lcd driver

  • data: Pointer of data

  • length: length of data

esp_err_t i2s_lcd_acquire(i2s_lcd_handle_t handle)

acquire a lock

Return

Always return ESP_OK

Parameters
  • handle: Handle of i2s lcd driver

esp_err_t i2s_lcd_release(i2s_lcd_handle_t handle)

release a lock

Return

Always return ESP_OK

Parameters
  • handle: Handle of i2s lcd driver

Structures
struct i2s_lcd_config_t

Configuration of i2s lcd mode.

Handle of i2s lcd driver

Public Members

int8_t data_width

Parallel data width, 16bit or 8bit available

int8_t pin_data_num[16]

Parallel data output IO

int8_t pin_num_cs

CS io num

int8_t pin_num_wr

Write clk io

int8_t pin_num_rs

RS io num

int clk_freq

I2s clock frequency

i2s_port_t i2s_port

I2S port number

bool swap_data

Swap the 2 bytes of RGB565 color

uint32_t buffer_size

DMA buffer size

Macros
LCD_CMD_LEV
LCD_DATA_LEV
Type Definitions
typedef void *i2s_lcd_handle_t

Boards Component

[中文]

This document mainly introduces the use of a board support component (Boards). As a common component of examples, this component can provide unified pin macro definitions and hardware-independent initialization operations to applications. Applications developed based on this component are compatible with different development boards at the same time with the following features:

  1. Provides unified macro definitions for pins

  2. Provides default peripheral configuration parameters

  3. Provides unified board-level initialization interfaces

  4. Provides hardware control interfaces for development boards

The following figure shows the structure of the Boards component:

_images/boards_diagram.png

Boards Component Diagram

  • The Boards component contains the following:

    • board_common.h, contains the function declaration of the common API;

    • board_common.c, contains the function implementation of the common API (weak function);

    • Kconfig.projbuild, contains common configuration items;

  • The subfolders named after the development board name includes the following:

    • iot_board.h provides the gpio definition of the development board, and the board’s unique custom API function declaration

    • board.c provides user implementation of common API (Covering default weak function), custom API function implementation

    • kconfig.in provides custom configuration items unique to the development board.

Note

The Boards component is provided in examples/common_components/boards.

Instructions

  1. Initialize development board: use iot_board_init in app_main to initialize the development board. you can also do some configurations regarding this process using The Switch and Configuration of a Development Board in menuconfig;

  2. Get the handle of a peripheral: use iot_board_get_handle and board_res_id_t to get peripheral resources. NULL will be returned if this peripheral is not initialized;

  3. Operate on peripherals with handles directly.

Example:

void app_main(void)
{
    /*initialize board with default parameters,
    you can use menuconfig to choose a target board*/
    esp_err_t err = iot_board_init();
    if (err != ESP_OK) {
        goto error;
    }

    /*get the i2c0 bus handle with a board_res_id,
    BOARD_I2C0_ID is declared in board_res_id_t in each iot_board.h*/
    bus_handle_t i2c0_bus_handle = (bus_handle_t)iot_board_get_handle(BOARD_I2C0_ID);
    if (i2c0_bus_handle == NULL) {
        goto error;
    }

    /*
    * use initialized peripheral with handles directly,
    * no configurations required anymore.
    */
}

The Switch and Configuration of a Development Board

For applications developed basing on Boards, the following steps can be used to switch and configure boards:

  1. Select the target development board: select a development board in menuconfig->Board Options->Choose Target Board;

  2. Configure the development board parameters: Board Common Options contains common configurations, such as if enable i2c0 during the initialization of the development board; XXX Board Options contains the development board-specific configurations, such as switching the power supply status of the development board, etc.

  3. Use idf.py build flash monitor to recompile and download the code.

Note

The default target of this build system is ESP32, please set the target before building via idf.py set-target esp32s2 if you need to use ESP32-S2.

Supported Development Boards

ESP32 Development Boards

esp32-devkitc

esp32-meshkit-sense

esp32-devkitc

esp32-meshkit-sense

esp32-lcdkit

esp32-lcdkit

ESP32-S2 Development Boards

esp32s2-saola

esp32s2-saola

ESP32-S3 Development Boards

esp32s3-devkitc-v1

esp32s3_usb_otg_ev

esp32s3-devkitc-v1

esp32s3_usb_otg_ev

Add a New Development Board

A new development board can be added to quickly adapt to applications developed basing on the Boards component.

The main process is as follows:

  1. Prepare the necessary iot_board.h based on existing example;

  2. Add board specific functions or cover the common weak function in board_xxx.c according to the requirements;

  3. Add configuration options specific to this board in kconfig.in according to your needs;

  4. Add the information of this board to Kconfig.projbuild for users;

  5. Add the directory of this board to CMakeLists.txt so that it can be indexed by the build system. Please also update component.mk if you need to support the old make system.

Note

An easy way is to directly copy files of the existing development boards in Boards and make simple modifications to add your new board.

Component Dependencies

  • Common dependencies: bus, button, led_strip

Adapted IDF Versions

  • ESP-IDF v4.4 and later versions.

Supported Chips

  • ESP32

  • ESP32-S2

  • ESP32-S3

Display

[中文]

Screen

[中文]

Screen is a very important display device as many information from various applications needs to be displayed to users. Both ESP32 and ESP32-S2 chips support screens driven by I2C interface, 8080 parallel interface, SPI interface and etc. The supported types of screen controllers are listed in the following table:

Controller

Max Resolution

Type

NT35510

480 x 865

Color

ILI9806

480 x 865

Color

RM68120

480 x 865

Color

ILI9486

320 x 480

Color

ILI9341

240 x 320

Color

ST7789

240 x 320

Color

ST7796

320 x 480

Color

SSD1351

128 x 128

Color

SSD1306

128 x 64

Mono

SSD1307

128 x 39

Mono

SSD1322

480 x 128

Gray

Note

The 8080 parallel interface is implemented via the LCD mode in the I2S of ESP32, so sometimes it is called I2S interface in this document.

Screen Driver Structure

_images/screen_driver_structure.png

Screen Driver Structure Diagram

In order to be more in line with the actual situation where a screen controller has multiple interfaces, the screen driver is divided into two parts: the interface driver and the controller driver.

  • The interface driver: conduct basic reads and writes of commands and data

  • The controller driver: display information on screen via interfaces

A controller driver can be designed to switch between different interfaces in hardware level by calling corresponding interface drivers.

Screen Types

A discussion about screen types will help us to have a clear understanding of drivers. Here, we use colors that can be displayed on the screen to classify screens, rather than the panel material of them such as OLED, LCD and etc. In general, the colors displayed on screen determines the BPP (Bits Per Pixel), and the differences in BPP lead to differences in how the program handles it. Here, we list some ways in which GRAM is mapped to pixel points in below:

_images/screen_driver_RGB565.png

BPP = 16 GRAM Structure

_images/screen_driver_mono.png

BPP = 1 GRAM Structure

_images/screen_driver_gray.png

BPP = 4 GRAM Structure

From above figures, we can see that there are mainly two types of mapping:

  • When BPP >= 8, it is usually a color screen that supports RGB888, RGB666, RGB565 and other codings.

  • When BPP < 8, it is usually a mono screen that may either be black-and-white or gray.

When BPP < 8, a byte is mapped to multiple pixels, so a single pixel cannot be controlled directly. In this case, draw_pixel() is not supported in the driver, and the parameters of set_window() are also limited. When BPP >= 8, each single pixel can be accessed easily.

Attention

For color screens, the driver only supports RGB565 color coding.

Interface Driver

A screen controller usually has multiple interfaces. On ESP32, three kinds of interfaces as 8080 parallel interface, SPI and I2C are typically used to connect to the screen. You can choose one of them as the interface when creating interface drivers via scr_interface_create().

Note

Please remember to select corresponding parameter types when creating different interfaces using scr_interface_create(), e.g., select i2s_lcd_config_t for I2S interface; select scr_interface_spi_config_t for SPI interface.

To facilitate the use of these interfaces in the driver, all interfaces are defined in display/screen/screen_utility/interface_drv_def.h, which can be called easily by less parameters.

Note

Most screens use big-endian order to store data, while ESP32 uses small-endian mode. You can switch between them in the interface driver you used, based on swap_data configurations. Please note: when using the SPI interface, the received data must be stored in RAM because the IDF’s SPI driver itself does not support this swapping function and an additional program in the interface driver will do the work, which require the data to be writable.

Controller Driver

Some common functions of the screen are abstracted using scr_driver_t in this section according to display and other functions of different screen controllers, in order to port these common functions to different GUI libraries easily. For some non-generic functions of the screen, you need to call its specific functions.

Not all screens have implemented these common functions, since different screen controller has their own functions. For example, for screens with BPP < 8, the function draw_pixel() is not supported. And calling an unsupported function will return ESP_ERR_NOT_SUPPORTED.

Display Direction

The screen display direction set here is implemented entirely by the screen hardware, and this feature varies from one screen controller to another. There are 8 possible display directions. A display can be rotated by 0°, 90°, 180° or 270° and can also be viewed from the top or bottom, with 0° and top view as its default direction. These 8 (4 × 2) directions can also be represented as a combination of 3 binary switches: X-mirroring, Y-mirroring and X/Y swapping.

The total 8 combinations of display directions are listed in the following table. If the direction of your display is not correct, please check the configuration switches below to make it work properly.

original

0 [SCR_DIR_LRTB]

mirror_y

SCR_MIRROR_Y [SCR_DIR_LRBT]

mirror_x

SCR_MIRROR_X [SCR_DIR_RLTB]

mirror_xy

SCR_MIRROR_X| SCR_MIRROR_Y [SCR_DIR_RLBT]

swap_xy

SCR_SWAP_XY [SCR_DIR_TBLR]

swap_xy_mirror_y

SCR_SWAP_XY| SCR_MIRROR_Y [SCR_DIR_BTLR]

swap_xy_mirror_x

SCR_SWAP_XY| SCR_MIRROR_X [SCR_DIR_TBRL]

swap_xy_mirror_xy

SCR_SWAP_XY| SCR_MIRROR_X| SCR_MIRROR_Y [SCR_DIR_BTRL]

The implementations of display directions are not exactly the same for different screen controllers, and are usually divided into the following cases:

  • For color screens, 8 directions are supported.

  • For mono screens, e.g., SSD1306, only the first 4 directions defined in scr_dir_t are supported, which means they do not support X/Y swapping.

Note

The display direction is also related to the screen panel you used, and you may encounter two types of abnormal cases:

  • The display direction is set to SCR_DIR_LRTB, but the screen does not show as what listed in the above table. This may be because the alignment on the screen panel is mirrored in the X/Y direction, in which case you need to adjust the rotation to get the desired direction.

  • After rotated, the screen does not show anything any more. This may be because the resolution of the screen panel is smaller than that of the screen controller, making the display area not falling completely on the screen panel, in which case you need to set a proper offset for the display area.

Offset of the Display Area

In some small screens, the resolution of the display area is usually smaller than that of the controller window. Please refer to the following figure:

_images/screen_offset.png

In this figure, Controller window is the window for screen controller, with its resolution as 240 × 320; Panel window is the window for screen panel, with its resolution as 135 × 240, which is the display area. From this figure, we can see that the display area is shifted by 52 pixels horizontally and by 40 pixels vertically.

When the screen is rotated 90° anticlockwise, the display area is shifted by 40 pixels horizontally and by 53 pixels vertically, as shown in the figure below:

_images/screen_offset_rotate.png

The screen controller driver will help you to change the offset value automatically according to the rotation of the screen to maintain a proper display. All you need to do is to properly configure the screen offset and the screen panel size in scr_controller_config_t when it is in SCR_DIR_LRTB direction.

Note

  • This only supports screens with BPP >= 8.

  • When the resolution of your screen controller is configurable and you find something wrong with the offset, it may be because the selected resolution does not match the actual one, and you should make modifications accordingly, for example, set the ILI9806_RESOLUTION_VER in ili9806.c as the actual resolution for ILI9806.

Application Example

Note

The following examples are no longer maintained. For LCD and LVGL examples, please refer to: i80_controllerrgb_panel And spi_lcd_touch

Initialize the Screen
scr_driver_t g_lcd; // A screen driver
esp_err_t ret = ESP_OK;

/** Initialize 16bit 8080 interface */
i2s_lcd_config_t i2s_lcd_cfg = {
    .data_width  = 16,
    .pin_data_num = {
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
    },
    .pin_num_cs = 45,
    .pin_num_wr = 34,
    .pin_num_rs = 33,
    .clk_freq = 20000000,
    .i2s_port = I2S_NUM_0,
    .buffer_size = 32000,
    .swap_data = false,
};
scr_interface_driver_t *iface_drv;
scr_interface_create(SCREEN_IFACE_8080, &i2s_lcd_cfg, &iface_drv);

/** Find screen driver for ILI9806 */
ret = scr_find_driver(SCREEN_CONTROLLER_ILI9806, &g_lcd);
if (ESP_OK != ret) {
    ESP_LOGE(TAG, "screen find failed");
    return;
}

/** Configure screen controller */
scr_controller_config_t lcd_cfg = {
    .interface_drv = iface_drv,
    .pin_num_rst = -1,      // The reset pin is not connected
    .pin_num_bckl = -1,     // The backlight pin is not connected
    .rst_active_level = 0,
    .bckl_active_level = 1,
    .offset_hor = 0,
    .offset_ver = 0,
    .width = 480,
    .height = 854,
    .rotate = SCR_DIR_LRBT,
};

/** Initialize ILI9806 screen */
g_lcd.init(&lcd_cfg);

Note

By default, only the driver of ILI9341 screen is enabled. If you need to use other drivers, please go to menuconfig -> Component config -> LCD Drivers -> Select Screen Controller to enable the corresponding screen drivers.

Display Images
/** Draw a red point at position (10, 20) */
lcd.draw_pixel(10, 20, COLOR_RED);

/** Draw a bitmap */
lcd.draw_bitmap(0, 0, width_of_pic, height_of_pic, pic_data);
Obtain Screen Information
scr_info_t lcd_info;
lcd.get_info(&lcd_info);
ESP_LOGI(TAG, "Screen name:%s | width:%d | height:%d", lcd_info.name, lcd_info.width, lcd_info.height);

API Reference

Header File
Functions
esp_err_t scr_find_driver(scr_controller_t controller, scr_driver_t *out_screen)

Find a screen driver.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is NULL.

  • ESP_ERR_NOT_FOUND Screen controller was not found.

Parameters
  • controller: Screen controller to initialize

  • out_screen: Pointer to a screen driver

Structures
struct scr_controller_config_t

configuration of screen controller

Public Members

scr_interface_driver_t *interface_drv

Interface driver for screen

int8_t pin_num_rst

Pin to hardreset LCD

int8_t pin_num_bckl

Pin for control backlight

uint8_t rst_active_level

Reset pin active level

uint8_t bckl_active_level

Backlight active level

uint16_t width

Screen width

uint16_t height

Screen height

uint16_t offset_hor

Offset of horizontal

uint16_t offset_ver

Offset of vertical

scr_dir_t rotate

Screen rotate direction

struct scr_info_t

Information of screen.

Public Members

uint16_t width

Current screen width, it may change when apply to rotate

uint16_t height

Current screen height, it may change when apply to rotate

scr_dir_t dir

Current screen direction

scr_color_type_t color_type

Color type of the screen, See scr_color_type_t struct

uint8_t bpp

Bits per pixel

const char *name

Name of the screen

struct scr_driver_t

Define a screen common function.

Public Members

esp_err_t (*init)(const scr_controller_config_t *lcd_conf)

Initialize screen.

Return

  • ESP_OK on success

  • ESP_FAIL Driver not installed

Parameters

esp_err_t (*deinit)(void)

Deinitialize screen.

Return

  • ESP_OK on success

  • ESP_FAIL Deinitialize failed

  • ESP_ERR_NOT_SUPPORTED unsupported

esp_err_t (*set_direction)(scr_dir_t dir)

Set screen direction of rotation.

Note

Not all screens support eight directions, it depends on the screen controller.

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters
  • dir: Pointer to a scr_dir_t structure. You can set the direction in two ways, for example, set it to “SCR_DIR_LRBT” or “SCR_MIRROR_Y”, They are the same, depending on which expression you want to use

esp_err_t (*set_window)(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)

Set screen window.

Note

When the BPP of the screen controller is less than 8, the coordinate value is limited to a multiple of some number

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters
  • x0: Starting point in X direction

  • y0: Starting point in Y direction

  • x1: End point in X direction

  • y1: End point in Y direction

esp_err_t (*write_ram_data)(uint16_t color)

Write a RAM data.

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters
  • color: New color of a pixel

esp_err_t (*draw_pixel)(uint16_t x, uint16_t y, uint16_t color)

Draw one pixel in screen with color.

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters
  • x: X co-ordinate of set orientation

  • y: Y co-ordinate of set orientation

  • color: New color of the pixel

esp_err_t (*draw_bitmap)(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)

Fill the pixels on LCD screen with bitmap.

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters
  • x: Starting point in X direction

  • y: Starting point in Y direction

  • w: width of image in bitmap array

  • h: height of image in bitmap array

  • bitmap: pointer to bitmap array

esp_err_t (*get_info)(scr_info_t *info)

Get screen information.

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters

Macros
COLOR_BLACK
COLOR_NAVY
COLOR_DARKGREEN
COLOR_DARKCYAN
COLOR_MAROON
COLOR_PURPLE
COLOR_OLIVE
COLOR_LIGHTGREY
COLOR_DARKGREY
COLOR_BLUE
COLOR_GREEN
COLOR_CYAN
COLOR_RED
COLOR_MAGENTA
COLOR_YELLOW
COLOR_WHITE
COLOR_ORANGE
COLOR_GREENYELLOW
COLOR_PINK
COLOR_SILVER
COLOR_GRAY
COLOR_LIME
COLOR_TEAL
COLOR_FUCHSIA
COLOR_ESP_BKGD
Enumerations
enum scr_dir_t

Define all screen direction.

Values:

SCR_DIR_LRTB

From left to right then from top to bottom, this consider as the original direction of the screen

SCR_DIR_LRBT

From left to right then from bottom to top

SCR_DIR_RLTB

From right to left then from top to bottom

SCR_DIR_RLBT

From right to left then from bottom to top

SCR_DIR_TBLR

From top to bottom then from left to right

SCR_DIR_BTLR

From bottom to top then from left to right

SCR_DIR_TBRL

From top to bottom then from right to left

SCR_DIR_BTRL

From bottom to top then from right to left

SCR_DIR_MAX
SCR_MIRROR_X = 0x40

Mirror X-axis

SCR_MIRROR_Y = 0x20

Mirror Y-axis

SCR_SWAP_XY = 0x80

Swap XY axis

enum scr_color_type_t

The types of colors that can be displayed on the screen.

Values:

SCR_COLOR_TYPE_MONO

The screen is monochrome

SCR_COLOR_TYPE_GRAY

The screen is gray

SCR_COLOR_TYPE_RGB565

The screen is colorful

enum scr_controller_t

All supported screen controllers.

Values:

SCREEN_CONTROLLER_ILI9341
SCREEN_CONTROLLER_ILI9342
SCREEN_CONTROLLER_ILI9806
SCREEN_CONTROLLER_ILI9486
SCREEN_CONTROLLER_ILI9488
SCREEN_CONTROLLER_NT35510
SCREEN_CONTROLLER_RM68120
SCREEN_CONTROLLER_ST7789
SCREEN_CONTROLLER_ST7796
SCREEN_CONTROLLER_SSD1351
SCREEN_CONTROLLER_SSD1963
SCREEN_CONTROLLER_SSD1306
SCREEN_CONTROLLER_SSD1307
SCREEN_CONTROLLER_SSD1322
Header File
Functions
esp_err_t scr_interface_create(scr_interface_type_t type, void *config, scr_interface_driver_t **out_driver)

Create screen interface driver.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is NULL.

  • ESP_FAIL Initialize failed

  • ESP_ERR_NO_MEM: Cannot allocate memory.

Parameters
  • type: Type of screen interface

  • config: configuration of interface driver

  • out_driver: Pointer to a screen interface driver

esp_err_t scr_interface_delete(const scr_interface_driver_t *driver)

Delete screen interface driver.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is NULL.

Parameters
  • driver: screen interface driver to delete

Structures
struct scr_interface_spi_config_t

SPI interface configuration.

Public Members

spi_bus_handle_t spi_bus

Handle of spi bus

int8_t pin_num_cs

SPI Chip Select Pin

int8_t pin_num_dc

Pin to select Data or Command for LCD

int clk_freq

SPI clock frequency

bool swap_data

Whether to swap data

struct scr_interface_i2c_config_t

I2C interface configuration.

Public Members

i2c_bus_handle_t i2c_bus

Handle of i2c bus

uint32_t clk_speed

I2C clock frequency for master mode, (no higher than 1MHz for now)

uint16_t slave_addr

I2C slave address

struct scr_interface_driver_t

Define common function for screen interface driver.

Public Members

scr_interface_type_t type

Interface bus type, see scr_interface_type_t struct

esp_err_t (*write_command)(void *handle, const uint8_t *cmd, uint32_t length)

Function to write command

esp_err_t (*write_data)(void *handle, uint16_t data)

Function to write a data

esp_err_t (*write)(void *handle, const uint8_t *data, uint32_t length)

Function to write a block data

esp_err_t (*read)(void *handle, uint8_t *data, uint32_t length)

Function to read a block data

esp_err_t (*bus_acquire)(void *handle)

Function to acquire interface bus

esp_err_t (*bus_release)(void *handle)

Function to release interface bus

Enumerations
enum scr_interface_type_t

Type of screen interface.

Values:

SCREEN_IFACE_I2C

I2C interface

SCREEN_IFACE_8080

8080 parallel interface

SCREEN_IFACE_SPI

SPI interface

Digital Tube

[中文]

Digital tube and dot matrix LEDs are common display solutions in embedded systems, which occupy fewer pins and memory resources than LCD displays and are simpler to implement, making them more suitable for application scenarios with single display requirements such as timing, counting, status display and etc.

The digital tube and LED display drivers adapted to ESP-IoT-Solution are shown in the following table:

Name

Features

Interface

Driver

Datasheet

CH450

Digital tube display driving chip, supports 6-bit digital tube

I2C

ch450

CH450

HT16C21

20×4/16×8 LCD controller, supports RAM mapping

I2C

ht16c21

HT16C21

IS31FL3XXX

Dot matrix LED controller

I2C

is31fl3xxx

IS31FL3XXX

CH450 Driver

CH450 is a digital tube display driving chip that can be used to drive a 6-bit digital tube or a 48-dot LED matrix and can communicate with ESP32 via the I2C interface.

_images/ch450_typical_application_circuit.png

CH450 Typical Application Circuit

This driver encapsulates the basic operations of CH450, and users can directly call ch450_write() or ch450_write_num() to display numbers on the digital tube.

Example
i2c_bus_handle_t i2c_bus = NULL;
ch450_handle_t seg = NULL;
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);
seg = ch450_create(i2c_bus);

for (size_t i = 0; i < 10; i++) {
    for (size_t index = 0; index < 6; index++) {
        ch450_write_num(seg, index, i);
    }
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

ch450_delete(seg);
i2c_bus_delete(&i2c_bus);

HT16C21 Driver

HT16C21 is a LCD control/driver chip which supports RAM mapping and can be used to drive 20 x 4 or 16 x 8 segmented LCDs. The chip communicates with ESP32 via the I2C interface.

_images/ht16c21_drive_mode_waveform.png

HT16C21 Typical Drive Model

This driver encapsulates the basic operations of HT16C21. After creating an example using ht16c21_create, users can configure its parameters via ht16c21_param_config and then call ht16c21_ram_write directly to write data.

Example
i2c_bus_handle_t i2c_bus = NULL;
ht16c21_handle_t seg = NULL;
uint8_t lcd_data[8] = { 0x10, 0x20, 0x30, 0x50, 0x60, 0x70, 0x80 };

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);
seg = ht16c21_create(i2c_bus, HT16C21_I2C_ADDRESS_DEFAULT);

ht16c21_config_t ht16c21_conf = {
    .duty_bias = HT16C21_4DUTY_3BIAS;
    .oscillator_display = HT16C21_OSCILLATOR_ON_DISPLAY_ON;
    .frame_frequency = HT16C21_FRAME_160HZ;
    .blinking_frequency = HT16C21_BLINKING_OFF;
    .pin_and_voltage = HT16C21_VLCD_PIN_VOL_ADJ_ON;
    .adjustment_voltage = 0;
};
ht16c21_param_config(seg, &ht16c21_conf);
ht16c21_ram_write(seg, 0x00, lcd_data, 8);

ht16c21_delete(seg);
i2c_bus_delete(&i2c_bus);

IS31FL3XXX Driver

The IS31FL3XXX series chips can be used to drive dot matrix LED screens with different sizes. The IS31FL3218 supports 18 constant current channels, with each channel controlled by an independent PWM. It has a maximum output current of 38 mA and can directly drive LEDs for display. The IS31FL3736 supports more channels and can compose a maximum size of LED matrix as 12 x 8. With each channel driven by an 8-bit PWM driver, the IS31FL3736 can support up to 256 gradients.

_images/IS31FL3218_typical_application_circuit.png

IS31FL3218 Typical Application Circuit

This driver encapsulates the basic operations of IS31FL3XXX. The example is shown in the next section.

IS31FL3218 Example
i2c_bus_handle_t i2c_bus = NULL;
is31fl3218_handle_t fxled = NULL;
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);
fxled = is31fl3218_create(i2c_bus);
is31fl3218_channel_set(fxled, 0x00ff, 128); // set PWM 1 ~ PWM 8 duty cycle 50%
is31fl3218_delete(fxled);
i2c_bus_delete(&i2c_bus);

LED Indicator

[中文]

As one of the simplest output peripherals, LED indicators can indicate the current operating state of the system by blinking in different types. ESP-IoT-Solution provides an LED indicator component with the following features:

  • Can define multiple groups of different blink types

  • Can define the priority of blink types

  • Can set up multiple indicators

  • LEDC and other drivers support adjustable brightness, gradient

Instructions

Adjustment of Gamma

The way human eyes perceive brightness is not linear but has certain nonlinear characteristics. Under normal conditions, the human eye is more sensitive to darker areas and less sensitive to brighter areas. However, on digital display devices such as monitors, the brightness values of images are usually encoded in a linear manner. This leads to issues of brightness distortion or loss of details when converting the linearly encoded brightness values to the perceived brightness by the human eye. To address this problem, gamma correction is applied to the image. Gamma correction involves adjusting the brightness values nonlinearly to correct the image display. By applying a gamma value (typically ranging from 2.2 to 2.4), the linearly encoded brightness values are mapped to a nonlinear brightness curve that better matches the perception of the human eye. This improves the visibility of details in darker areas and enhances the overall visual accuracy and balance of the image.

_images/led_indicator_gamma_correction.png

Gamma Curve

float gamma = 2.3;
led_indicator_new_gamma_table(gamma);

The default gamma table is 2.3, and a new gamma table can be generated using the led_indicator_new_gamma_table() function.

API Reference

Header File

Functions

led_indicator_handle_t led_indicator_create(const led_indicator_config_t *config)

create a LED indicator instance with GPIO number and configuration

Return

led_indicator_handle_t handle of the LED indicator, NULL if create failed.

Parameters
  • config: configuration of the LED, eg. GPIO level when LED off

led_indicator_handle_t led_indicator_get_handle(void *hardware_data)

get the handle of created led_indicator with hardware data

Return

led_indicator_handle_t handle of the created LED indicator, NULL if not created.

Parameters
  • hardware_data: user hardware data for LED

esp_err_t led_indicator_delete(led_indicator_handle_t handle)

delete the LED indicator and release resource

Return

esp_err_t

  • ESP_ERR_INVALID_ARG if parameter is invalid

  • ESP_OK Success

  • ESP_FAIL Delete fail

Parameters
  • handle: pointer to LED indicator handle

esp_err_t led_indicator_start(led_indicator_handle_t handle, int blink_type)

start a new blink_type on the LED indicator. if mutiple blink_type started simultaneously, it will be executed according to priority.

Return

esp_err_t

  • ESP_ERR_INVALID_ARG if parameter is invalid

  • ESP_ERR_NOT_FOUND no predefined blink_type found

  • ESP_OK Success

Parameters
  • handle: LED indicator handle

  • blink_type: predefined blink type

esp_err_t led_indicator_stop(led_indicator_handle_t handle, int blink_type)

stop a blink_type. you can stop a blink_type at any time, no matter it is executing or waiting to be executed.

Return

esp_err_t

  • ESP_ERR_INVALID_ARG if parameter is invalid

  • ESP_ERR_NOT_FOUND no predefined blink_type found

  • ESP_OK Success

Parameters
  • handle: LED indicator handle

  • blink_type: predefined blink type

esp_err_t led_indicator_preempt_start(led_indicator_handle_t handle, int blink_type)

Immediately execute an action of any priority. Until the action is executed, or call led_indicator_preempt_stop().

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_INVALID_ARG if parameter is invalid

Parameters
  • handle: LED indicator handle

  • blink_type: predefined blink type

esp_err_t led_indicator_preempt_stop(led_indicator_handle_t handle, int blink_type)

Stop the current preemptive action.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_INVALID_ARG if parameter is invalid

Parameters
  • handle: LED indicator handle

  • blink_type: predefined blink type

uint8_t led_indicator_get_current_fade_value(led_indicator_handle_t handle)

Get the current fade value of the LED indicator.

Return

uint8_t Current fade value: 0-255 if handle is null return 0

Parameters
  • handle: LED indicator handle

Structures

one blink step, a meaningful signal consists of a group of steps

Public Members

action type in this step

hold on or off, set NULL if LED_BLINK_STOP or LED_BLINK_LOOP

hold time(ms), set NULL if not LED_BLINK_HOLD,

struct led_indicator_config_t

LED indicator specified configurations, as a arg when create a new indicator.

Public Members

led_indicator_mode_t mode

LED work mode, eg. GPIO or pwm mode

led_indicator_gpio_config_t *led_indicator_gpio_config

LED GPIO configuration

led_indicator_ledc_config_t *led_indicator_ledc_config

LED LEDC configuration

led_indicator_custom_config_t *led_indicator_custom_config

LED custom configuration

union led_indicator_config_t::[anonymous] [anonymous]

LED configuration

user defined LED blink lists

number of blink lists

Type Definitions

typedef void *led_indicator_handle_t

LED indicator operation handle

Enumerations

enum [anonymous]

LED state: 0-100, only hardware that supports to set brightness can adjust brightness.

Values:

LED_STATE_OFF = 0

turn off the LED

LED_STATE_25_PERCENT = 64

25% brightness, must support to set brightness

LED_STATE_50_PERCENT = 128

50% brightness, must support to set brightness

LED_STATE_75_PERCENT = 191

75% brightness, must support to set brightness

LED_STATE_ON = UINT8_MAX

turn on the LED

actions in this type

Values:

stop the blink

hold the on-off state

breathe state

set the brightness

loop from first step

enum led_indicator_mode_t

LED indicator blink mode, as a member of led_indicator_config_t.

Values:

LED_GPIO_MODE

blink with max brightness

LED_LEDC_MODE

blink with LEDC driver

LED_CUSTOM_MODE

blink with custom driver

USB Host & Device

[中文]

ESP USB 外设介绍

USB 简介

USB(Universal Serial Bus)是一种通用的总线标准,用于连接主机和外设设备。USB 主机可以通过 USB 接口与 USB 设备连接,实现数据传输、电源供给等功能。

USB IF(USB Implementers Forum)是 USB 标准的制定者,它制定了 USB 标准,包括 USB 1.1、USB 2.0、USB 3.0 等,定义了 USB 接口的物理层、数据链路层、传输层、会话层、表示层等协议,以及 USB 设备类(Device Class)标准,常见的设备类包括 HID(Human Interface Device,人机接口设备)、MSC(Mass Storage Class,大容量存储设备)、CDC(Communication Device Class,通信设备)、Audio、Video 等。

乐鑫 ESP32-S2/S3/C3 等芯片均已内置 USB-OTG 或 USB-Serial-JTAG 外设,支持各种各样的 USB 应用,包括 USB 多媒体类应用,USB 通信类应用,USB 存储类应用,USB 人机交互类应用等。

USB Solution

USB 电气属性

Type-A 接口的 USB 电气属性如下:

Pin

Name

Cable color

Description

1

VBUS

Red

+5V

2

D-

White

Data-(0或3.3V)

3

D+

Green

Data+(0或3.3V)

4

GND

Black

Ground

USB-OTG Full-speed 控制器简介

USB OTG Full-speed 控制器是指同时具有 USB-OTG,USB Host 和 USB Device 模式的控制器,支持模式的协商和切换。支持 Full-speed (12Mbps) 和 Low-speed (1.5Mbps) 两种速率,支持 USB 1.1 和 USB 2.0 协议。

ESP-IDF 从 v4.4 开始已经包含 USB Host 和 USB Device 协议栈和各种设备类驱动,支持用户二次开发。

更多介绍,请参考USB-OTG 控制器介绍

USB-Serial-JTAG 控制器简介

USB-Serial-JTAG Controller:同时具有 USB Serial 和 USB JTAG 功能的专用 USB 控制器,支持通过 USB 接口下载固件、打印 log、CDC 传输和 JTAG 调试,不支持修改 USB 功能、修改描述符等二次开发。

更多介绍,请参考USB-Serial-JTAG 控制器介绍

USB Full-speed PHY 简介

USB Full-speed PHY:也称 USB Full-speed Transceiver,用于 USB Controller 数字信号到 USB 总线信号电平转换,提供总线驱动能力等。内部 USB Full-speed PHY 连接到外部固定 IO 引脚。

更多介绍,请参考USB-PHY 介绍

ESP32-S/C 系列 USB 外设支持情况

USB OTG High-speed

USB OTG Full-speed

USB-Serial-JTAG

Fulls-peed PHY

High-speed PHY

ESP32-P4

ESP32-S3

X

X

ESP32-S2

X

X

X

ESP32-C6

X

X

X

ESP32-C3

X

X

X

ESP32-C2

X

X

X

X

X

ESP32

X

X

X

X

X

ESP8266

X

X

X

X

X

  • √ : Supported

  • X : Not Supported

ESP32-S2 USB 功能简介

ESP32-S2 内置 USB OTG Full-speed ControllerUSB Full-speed PHY,内部结构如下:

esp32s2_usb

ESP32-C3 USB 功能简介

ESP32-C3 内置 USB-Serial-JTAG ControllerUSB Full-speed PHY,内部结构如下:

esp32c3_usb

ESP32-S3 USB 功能简介

ESP32-S3 内置两个 USB 控制器,分别是 USB OTG Full-speed ControllerUSB-Serial-JTAG Controller,内置一个 USB Full-speed PHY。内部 USB PHY 默认连接到 USB-Serial-JTAG 控制器,可通过烧写 eFuse 修改默认,或配置寄存器动态切换,也可通过增加外部 PHY,同时启用两个控制器。内部 USB PHY 的切换详情,参考 USB PHY 切换

esp32s3_usb

USB-OTG 外设介绍

ESP32-S2/S3 等芯片内置 USB-OTG 外设,它包含了 USB 控制器和 USB PHY,支持通过 USB 线连接到 PC,实现 USB Host 和 USB Device 功能。

USB-OTG 传输速率

ESP32-S2/S3 USB-OTG Full Speed 总线传输速率为 12 Mbps,但由于 USB 传输存在一些校验和同步机制,实际的有效传输速率将低于 12 Mbps。具体数值和传输类型相关,如下表所示:

传输类型

控制

中断

批量

同步

适用场合

设备初始化和管理

鼠标和键盘

打印机和批量存储

流式音频和视频

支持低速

校验重传

保证传输速度

使用固定带宽

有(10%)

有(90%)

有(90%)

减少延迟时间

传输的最大尺寸

64字节

64字节

64字节

~512字节

每毫秒传输包数量

1

19

1

理论有效速率

64000 Bytes/s

1216000 Bytes/s

512000 Bytes/s

  • 传输速率的计算公式为:传输速率 (Bytes/s) = 传输的最大尺寸 * 每毫秒传输包数量 * 1000

  • 控制传输用于传输设备控制信息,包含多个阶段,有效传输速率需要按照协议栈的实现来计算。

USB-OTG 外设内置功能

使用 USB OTG Console 下载固件和打印 LOG

ESP32-S2/S3 等内置 USB-OTG 外设的芯片,ROM Code 中内置了 USB 通信设备类 (CDC) 的功能,该功能可用于替代 UART 接口,实现 Log、Console 和固件下载功能。

  1. 由于 USB OTG Console 默认为关闭状态,如需使用它下载固件,需要通过以下方法完成初次下载:

    1. menuconfig 中先使能 USB OTG Console 功能,然后编译固件

    2. 手动芯片的 Boot 控制引脚拉低,然后将芯片通过 USB 线连接到 PC,进入下载模式。PC 会发现新的串口设备,Windows 为 COM*,Linux 为 /dev/ttyACM*, MacOS 为 /dev/cu*

    3. 使用 esptool 工具(或直接使用 idf.py flash)配置设备对应的串口号下载固件。

  2. 初次下载完成以后,USB OTG Console 功能将自动使能,即可通过 USB 线连接到 PC,PC 会发现新的串口设备,Windows 为 COM*,Linux 为 /dev/ttyACM*, MacOS 为 /dev/cu*,LOG 数据将从该虚拟串口打印。

  3. 用户无需再手动拉低 Boot 控制引脚,使用 esptool 工具(或直接使用 idf.py flash)配置设备对应的串口号即可下载固件,下载期间,esptool 通过 USB 控制协议自动将设备 Reset 并切换到下载模式。

更多详细信息,请参考:USB OTG Console

使用 USB OTG DFU 下载固件

ESP32-S2/S3 等内置 USB-OTG 外设的芯片,ROM Code 中内置了 USB DFU(Device Firmware Upgrade)功能,可用于实现标准的 DFU 下载模式。

  1. 使用 DFU 下载固件,用户每次都需要手动进入下载模式,将芯片的 Boot 控制引脚拉低,然后通过 USB 线连接到 PC。

  2. 在工程目录下运行指令 idf.py dfu 生成 DFU 固件,然后使用 idf.py dfu-flash 下载固件。

  3. 如果存在多个 DFU 设备,用户可以使用 idf.py dfu-list 查看 DFU 设备列表,然后使用 idf.py dfu-flash --path <path> 指定下载端口。

更多详细信息,请参考:Device Firmware Upgrade via USB

使用 USB-OTG 外设进行 USB Host 开发

USB-OTG 外设支持 USB Host 功能,用户可以通过 USB 接口直接连接到外部 USB 设备。ESP-IDF 从 v4.4 版本开始,已经支持 USB Host Driver,用户可以参考 ESP-IDF USB Host,开发 USB Class Driver。

此外乐鑫也已经官方支持 USB Host HID,USB Host MSC,USB Host CDC,USB Host UVC 等设备类驱动,用户可以直接使用这些驱动进行应用开发。

USB Host 方案详情,请参考 USB Host Solution

使用 USB-OTG 外设进行 USB Device 开发

USB-OTG 外设支持 USB Device 功能,乐鑫已经官方适配了 TinyUSB 协议栈,用户可以直接使用基于 TinyUSB 开源协议栈开发的 USB 标准设备或自定义设备,例如 HID,MSC,CDC,ECM, UAC 等。

USB Device 方案详情,请参考 USB Device Solution

USB-Serial-JTAG 外设介绍

ESP32-S3/C3 等芯片内置 USB-Serial-JTAG 外设,它包含了 USB-to-serial 转换器和 USB-to-JTAG 转换器,支持通过 USB 线连接到 PC,实现固件下载、调试和打印系统 LOG 等功能。USB-Serial-JTAG 外设的内部结构可参考 ESP32-C3 技术参考手册-USB Serial/JTAG Controller

USB-Serial-JTAG 外设驱动

  • Linux 和 MacOS 系统下,无需手动安装驱动

  • Windows 10 及以上系统,联网将自动安装驱动

  • Windows 7/8 系统,需要手动安装驱动,驱动下载地址:esp32-usb-jtag-2021-07-15。用户也可以使用 ESP-IDF Windows Installer,勾选 USB-Serial-JTAG 驱动进行安装,

USB-Serial-JTAG 外设内置功能

USB-Serial-JTAG 外设接入 PC 后,设备管理器将新增两个设备:

Windows 如下图所示:

device_manager_usb_serial_jtag_cn

Linux 如下图所示:

device_manager_usb_serial_jtag_cn

使用 USB-Serial-JTAG 下载固件

  • 默认情况下,USB-Serial-JTAG 下载功能处于使能状态,可以直接使用 USB 线连接到 PC,然后使用 esptool 工具(或直接使用 idf.py flash)配置 USB-Serial-JTAG 设备对应的串口号(Windows 为 COM*,Linux 为 /dev/ttyACM*, MacOS 为 /dev/cu*)下载固件。下载期间,esptool 通过 USB 控制协议自动将设备 Reset 并切换到下载模式。

  • 如果在应用程序中将 USB-Serial-JTAG 对应的 USB 引脚用作了其它功能,例如用作普通 GPIO 或其它外设 IO,USB-Serial-JTAG 将无法与 USB 主机建立连接,因此无法通过 USB 将设备切换到下载模式,用户必须通过 Boot 控制引脚,将设备手动切换到下载模式,然后再使用 esptool 下载固件。

  • 为了避免在应用程序中将 USB-Serial-JTAG 对应的 USB 引脚用作其它功能,导致无法通过 USB 自动进入下载模式,用户需要在硬件设计时,引出 Boot 控制引脚。

  • 默认情况下,通过 USB 接口下载不同的芯片,COM 号将递增,可能对量产造成不便,用户可参考 阻止 Windows 依据 USB 设备序列号递增 COM 编号

使用 USB-Serial-JTAG 调试代码

USB-Serial-JTAG 支持通过 JTAG 接口调试代码,用户仅需使用 USB 线连接到 PC,然后使用 OpenOCD 工具即可调试代码。配置方法请参考 配置 ESP32-C3 内置 JTAG 接口

使用 USB-Serial-JTAG 打印系统 LOG

  • 用户可通过 menuconfig-> Component config ESP System Settings Channel for console secondary output 配置 USB-Serial-JTAG LOG 功能的使能状态。

  • LOG 功能使能以后,可以直接使用 USB 线连接到 PC,然后使用 idf.py monitor 或其它串口工具打开 USB-Serial-JTAG 设备对应的串口号(Windows 为 COM*,Linux 为 /dev/ttyACM*, MacOS 为 /dev/cu*),即可打印系统 LOG。

  • USB-Serial-JTAG 仅在主机接入后才会打印 LOG,如果主机未接入,USB-Serial-JTAG 不会被初始化,也不会打印 LOG。

  • USB-Serial-JTAG LOG 功能无法在睡眠模式下使用(包括 deep sleep 和 light sleep 模式),如果需要在睡眠模式下打印 LOG,可以使用 UART 接口。

使用 USB-Serial-JTAG 引脚作为普通 GPIO

如果用户需要在应用程序中将 USB-Serial-JTAG 对应的 USB 引脚用作其它功能,例如用作普通 GPIO。需要注意 USB D+ 接口默认上拉 USB 电阻,该电阻会导致 USB D+ 引脚常为高电平,因此需要在用作 GPIO 时将其禁用。

  • ESP-IDF v4.4 及以后版本 GPIO 驱动默认会禁用 USB D+ 上拉电阻,用户使用 GPIO 驱动时无需额外配置。

  • 用户也可以修改寄存器值 USB_SERIAL_JTAG.conf0.dp_pullup = 0; 将 USB D+ 上拉电阻禁用。

需要特别注意的是 USB D+ 引脚的上拉电阻在上电时刻即存在,用户在调用软件禁用上拉电阻之前,USB D+ 引脚已经被拉高,导致 D+ 引脚做为 GPIO 时,初始阶段为高电平,如果用户需要在上电后 USB D+ 引脚立即为低电平,需要在硬件设计时,将 USB D+ 引脚通过外部电路拉低。

USB PHY/Transceiver 介绍

USB Full-speed PHY/Transceiver 的功能是将 USB 控制器的数字信号转换为 USB 总线信号电平,提供总线驱动能力,检测接收错误等。ESP32-S2/S3 等芯片已内置一个 USB Full-speed PHY,用户可直接使用芯片指定的 USB D+ D- 与外部 USB 系统通信。此外,ESP32-S2/S3 还保留了外部 PHY 的扩展接口,用户可在需要时连接外部 PHY。

使用内部 PHY

ESP32-S2/S3/C3 内部集成了 USB PHY,因此无需外接 PHY 芯片,可以直接与外部 USB 主机或设备通过 USB D+/D- 连接。但对于集成两个 USB 控制器的芯片,例如 ESP32-S3 内置 USB-OTG 和 USB-Serial-JTAG,两者共用一个内部 PHY,同一时间只能有一个工作。

esp32s3_usb

内部 USB-PHY 对应固定的 GPIO,如下表所示:

D+

D-

ESP32-S2

20

19

ESP32-S3

20

19

ESP32-C3

19

18

ESP32-C6

13

12

使用外部 PHY

通过增加一个外部 PHY,可以实现 USB-OTG 和 USB-Serial-JTAG 两个外设同时工作。

ESP32S2/S3 支持 SP5301 或同等功能的 USB PHY。外部 PHY 的典型电路图如下:

usb_fs_phy_sp5301

使用外部 USB PHY,将占用至少 6 个 GPIO

USB PHY 默认配置

  1. 对于同时具有 USB-OTG 和 USB-Serial-JTAG 两个外设的芯片,默认情况下 USB-Serial-JTAG 与内部 USB-PHY 连接。用户可以直接通过 USB 接口进行下载或调试,无需额外配置。

  2. 如需使用 USB Host Driver 或 TinyUSB 协议栈开发 USB-OTG 应用,在协议栈初始化时,USB-PHY 连接会自动切换为 USB-OTG,用户不必再进行配置。在 USB-OTG 模式下,如果用户需要使用 USB-Serial-JTAG 的下载功能,需要手动 Boot 到下载模式。

修改 USB PHY 默认配置

方法 1: 通过配置寄存器,将 USB-PHY 连接切换为 USB-OTG。

  • USB Host Driver 或 TinyUSB 协议栈内部通过配置 USB PHY 寄存器,将内部 USB-PHY 连接切换为 USB-OTG,如需了解更多信息,请参考 USB PHY 配置 API

方法 2:通过烧写 efuse usb_phy_sel 位为 1,将 USB-PHY 默认连接切换为 USB-OTG:

  • 仅当用户需要在 Boot 模式下使用 USB-OTG 功能时,才需要烧写此 efuse 位。烧写完成后,芯片进入 Boot 模式后,将使用 USB-OTG 提供的下载功能,例如 USB DFU。

  • 注意,烧写 efuse 为 1 后不可恢复为 0。当 USB-PHY 默认连接切换为 USB-OTG,芯片进入 Boot 模式后,将使用 USB-OTG 功能,USB-Serial-JTAG 功能将无法使用。

  • 注意:对于在 DateCode 2219 生产的 ESP32-S3 模组和开发板 (PW No. 早于 PW-2022-06-XXXX), 由于 EFUSE_DIS_USB_OTG_DOWNLOAD_MODE (BLK0 B19[7]) 已经被烧录为 1 (USB OTG 下载被禁用),用户如果再烧写 efuse_usb_phy_sel 位为 1,将导致芯片进入 Boot 模式后,USB-Serial-JTAG 和 USB-OTG 下载功能均无法使用。

USB VID 和 PID

VID 和 PID 是 USB 设备的唯一标识符,用于区分不同的 USB 设备。一般 VID 和 PID 由 USB-IF 分配,USB-IF 是 USB 设备的标准制定者。

以下情况您可以免申请 VID 和 PID

  • 如果您的产品使用的是 USB Host 模式,您不需要申请 VID 和 PID。

  • 如果您的产品使用的是 USB Device 模式,准备使用乐鑫的 VID(0x303A), 并且基于 TinyUSB 协议栈开发 USB 标准设备,那您不需要申请 PID,使用 TinyUSB 默认 PID 即可。

申请 VID 和 PID

如果您使用的产品需要使用 USB 设备模式,您可按照以下过程申请 VID (Vendor ID) 或 PID (Product ID)。

  • 如果您的产品需要使用 USB-IF 分配的 VID,您需要先 注册成为 USB-IF 成员,然后按照 USB-IF 的流程申请 VID 和 PID。

  • 如果您的产品考虑使用乐鑫的 VID,您可以直接申请 PID(免费), 申请流程请参考 申请乐鑫 PID

注意:使用乐鑫的 VID 和 TinyUSB 的默认 PID, 并不意味着您的产品符合 USB 规范,您仍然需要进行 USB 认证。

USB 认证

USB 认证是由 USB Implementers Forum(USB-IF)进行管理的,旨在确保产品符合 USB 规范,以保证设备之间的互操作性和兼容性。

USB 认证是一个可选的过程,但在以下情况下必须进行产品认证:

  • 如果产品标榜自己符合 USB 规范并使用官方 USB 标志

  • 如果产品打算使用 USB 标志、商标或宣传资料中提及 USB 认证。

具体认证流程和要求,请查阅 https://www.usb.org 或联系 USB-IF 授权测试实验室

USB Host 方案

ESP32-S2/S3 等芯片内置 USB-OTG 外设,支持通过 USB 接口连接多种多样的 USB 设备。以下介绍了 ESP32-S2/S3 芯片支持的 USB Host 解决方案。

ESP USB Camera 视频方案

支持通过 USB 接口连接摄像头模组,实现 MJPEG 格式视频流获取和传输,最高可支持 480*800@15fps。适用于猫眼门铃、智能门锁、内窥镜、倒车影像等场景。

特性:
  • 快速启动

  • 支持热插拔

  • 支持 UVC1.1/1.5 规范的摄像头

  • 支持自动解析描述符

  • 支持动态配置分辨率

  • 支持 MJPEG 视频流传输

  • 支持批量和同步两种传输模式

硬件:
链接:

ESP USB Audio 音频方案

支持通过 USB 接口连接 USB 音频设备,实现 PCM 格式音频流获取和传输,可同时支持多路 48KHz 16bit 扬声器和多路 48KHz 16bit 马克风。支持 Type-C 接口耳机,适用于音频播放器等场景。支持和 UVC 同时工作,适用于门铃对讲等场景。

特性:
  • 快速启动

  • 支持热插拔

  • 支持自动解析描述符

  • 支持 PCM 音频流传输

  • 支持动态修改采样率

  • 支持多通道扬声器

  • 支持多通道麦克风

  • 支持音量、静音控制

  • 支持和 USB Camera 同时工作

硬件:
  • 芯片: ESP32-S2,ESP32-S3

  • 外设:USB-OTG

  • USB 音频设备:支持 PCM 格式

链接:

ESP USB 4G 联网方案

支持通过 USB 接口连接 4G Cat.1,Cat.4 模组,实现 PPP 拨号上网。支持通过 Wi-Fi SoftAP 热点共享互联网给其它设备。适用于物联网网关、MiFi 移动热点、智慧储能、广告灯箱等场景。

特性:
  • 快速启动

  • 支持热插拔

  • 支持 Modem+AT 双接口(需要模组支持)

  • 支持 PPP 标准协议 (大部分 4G 模组均支持)

  • 支持 4G 转 Wi-Fi 热点

  • 支持 NAPT 网络地址转换

  • 支持电源管理

  • 支持网络自动恢复

  • 支持卡检测、信号质量检测

  • 支持网页配置界面

硬件:
  • 芯片: ESP32-S2,ESP32-S3

  • 外设:USB-OTG

  • 4G 模组:支持 Cat.1 Cat.4 等网络制式 4G 模组,需要模组支持 PPP 协议

链接:

ESP USB 存储方案

支持通过 USB 接口连接标准 U 盘设备(兼容 USB3.1/3.0/2.0 协议 U 盘),支持将 U 盘挂载到 FatFS 文件系统,实现文件的读写。适用于户外广告灯牌、考勤机、移动音响、记录仪等应用场景。

特性:
  • 兼容 USB3.1/3.0/2.0 U 盘

  • 默认支持最大 32G

  • 支持热插拔

  • 支持 Fat32/exFat 格式

  • 支持文件系统读写

  • 支持 U 盘 OTA

硬件:
  • 芯片: ESP32-S2,ESP32-S3

  • 外设:USB-OTG

  • U 盘:格式化为 Fat32 格式,默认支持 32GB 以内 U 盘。大于 32GB 需要在文件系统开启 exFat

链接:

USB Device 方案

自供电 USB 设备解决方案

按照 USB 协议要求,USB 自供电设备必须通过检测 5V VBUS 电压来判断设备是否拔出,进而实现热插拔。对于主机供电设备,由于主机 VBUS 断电之后,设备直接掉电关机,无需实现该逻辑。

USB 设备 VBUS 检测方法一般有两种方法:由 USB PHY 硬件检测,或借助 ADC/GPIO 由软件检测

由于 ESP32S2/S3 内部 USB PHY 不支持硬件检测逻辑,该功能需要借助 ADC/GPIO 由软件实现,其中使用 GPIO 检测方法最为简便,实现方法如下:

对于 ESP-IDF 4.4 及更早版本:

  1. 硬件上,需要额外占用一个 IO(任意指定,特殊引脚除外),通过两个电阻分压(例如两个 100KΩ )与 ESP32S2/S3 相连 (ESP32S2/S3 IO 最大可输入电压为 3.3v);

  2. tinyusb_driver_install 之后,需要调用 usbd_vbus_detect_gpio_enable 函数使能 VBUS 检测,该函数实现代码如下,请直接复制到需要调用的位置:

/**
 *
 * @brief For USB Self-power device, the VBUS voltage must be monitored to achieve hot-plug,
 *        The simplest solution is detecting GPIO level as voltage signal.
 *        A divider resistance Must be used due to ESP32S2/S3 has no 5V tolerate pin.
 *
 *   5V VBUS ┌──────┐    ┌──────┐   GND
 *    ───────┤ 100K ├─┬──┤ 100K ├──────
 *           └──────┘ │  └──────┘
 *                    │           GPIOX
 *                    └───────────────
 *        The API Must be Called after tinyusb_driver_install to overwrite the default config.
 * @param gpio_num, The gpio number used for vbus detect
 *
 */
static void usbd_vbus_detect_gpio_enable(int gpio_num)
{
    gpio_config_t io_conf = {
        .intr_type = GPIO_INTR_DISABLE,
        .pin_bit_mask = (1ULL<<gpio_num),
        //set as input mode
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = 0,
        .pull_down_en = 0,
    };
    gpio_config(&io_conf);
    esp_rom_gpio_connect_in_signal(gpio_num, USB_OTG_VBUSVALID_IN_IDX, 0);
    esp_rom_gpio_connect_in_signal(gpio_num, USB_SRP_BVALID_IN_IDX, 0);
    esp_rom_gpio_connect_in_signal(gpio_num, USB_SRP_SESSEND_IN_IDX, 1);
    return;
}

对于 ESP-IDF 5.0 及以上版本:

  1. 同上,硬件上需要额外占用一个 IO(任意指定,特殊引脚除外),通过两个电阻分压(例如两个 100KΩ )与 ESP32S2/S3 相连;

  2. 将用于检测 VBUS 的 IO 初始化为 GPIO 输入模式;

  3. 直接将 IO 配置到 tinyusb_config_t 中(详情可参考):

#define VBUS_MONITORING_GPIO_NUM GPIO_NUM_4
// Configure GPIO Pin for vbus monitoring
const gpio_config_t vbus_gpio_config = {
    .pin_bit_mask = BIT64(VBUS_MONITORING_GPIO_NUM),
    .mode = GPIO_MODE_INPUT,
    .intr_type = GPIO_INTR_DISABLE,
    .pull_up_en = false,
    .pull_down_en = false,
};
ESP_ERROR_CHECK(gpio_config(&vbus_gpio_config));
const tinyusb_config_t tusb_cfg = {
    .device_descriptor = &descriptor_config,
    .string_descriptor = string_desc_arr,
    .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]),
    .external_phy = false,
    .configuration_descriptor = desc_configuration,
    .self_powered = true,
    .vbus_monitor_io = VBUS_MONITORING_GPIO_NUM,
};
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));

阻止 Windows 依据 USB 设备序列号递增 COM 编号

由于任何连接到 Windows PC 的设备都通过其 VID、PID 和 Serial 号进行识别。如果这 3 个参数中的任何一个发生了变化,那么 PC 将检测到新的硬件,并与该设备关联一个不同的 COM 端口,详情请参考 Windows USB device registry entries

ESP ROM Code 中对 USB 描述符配置如下:

ESP32S2

ESP32S3

ESP32C3

VID

0x303a

0x303a

0x303a

PID

0x0002

0x1001

0x1001

Serial

0

MAC 地址字符串

MAC 地址字符串

  • ESP32S2(usb-otg)Serial 为常量 0,每个设备相同,COM 号一致

  • ESP32S3(usb-serial-jtag)、ESP32C3(usb-serial-jtag)Serial 为设备 MAC 地址,每个设备均不同,COM 号默认递增

递增 COM 编号,为量产烧录带来额外工作。对于需要使用 USB 进行固件下载的客户,建议修改 Windows 的递增 COM 编号的规则,阻止依据 Serial 号递增编号。

解决方案

管理员方式打开 Windows CMD , 执行以下指令。该指令将添加注册表项,阻止依据 Serial 号递增编号,设置完成后请重启电脑使能修改

REG ADD HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags\303A10010101 /V IgnoreHWSerNum /t REG_BINARY /d 01

用户也可下载 ignore_hwserial_esp32s3c3.bat 脚本,右键选择管理员方式运行。

USB Stream Component

[中文]

usb_stream is an USB UVC + UAC host driver for ESP32-S2/ESP32-S3, which supports read/write/control multimedia streaming from usb device. For example, at most one UVC + one Microphone + one Speaker streaming can be supported at the same time.

Features:

  1. Support video stream through UVC Stream interface, support both isochronous and bulk mode

  2. Support microphone stream and speaker stream through the UAC Stream interface

  3. Support volume, mute and other features control through the UAC Control interface

  4. Supports automatic parse of device configuration descriptors

  5. Support stream separately suspend and resume

USB Stream User Guide

  • Development board

    1. Any ESP32-S2 / ESP32-S3 board with USB Host port can be used. Note that the port must be able to output voltage.

  • USB UVC function

    1. Camera must be compatible with USB1.1 full-speed mode

    2. Camera must support MJPEG output

    3. Users can manually specify the camera interface, transmission mode, and image frame params through uvc_streaming_config()

    4. For isochronous mode camera, interface max packet size should no more than 512 bytes

    5. For isochronous mode camera, frame stream bandwidth should be less than 4 Mbps (500 KB/s)

    6. For bulk mode camera, frame stream bandwidth should be less than 8.8 Mbps (1100 KB/s)

    7. Please refer example readme for special camera requirements

  • USB UAC function

    1. Audio function must be compatible with UAC1.0 protocol

    2. Users need to manually specify the spk/mic sampling rate, bit width params through uac_streaming_config() function

  • USB UVC + UAC

    1. The UVC and UAC functions can be enabled separately. For example, only the UAC be configured to drive a usb headset, or only the UVC be configured to drive a USB camera

    2. If you need to enable UVC and UAC at the same time, note that the driver currently only supports Composite devices with both camera and audio interfaces, rather than two separate devices.

USB Stream Config Reference

  1. Using uvc_config_t to configure camera resolution and frame rate parameters. Using uac_config_t to configure the audio sampling rate, bit width and other parameters. The parameters are described as follows:

uvc_config_t uvc_config = {
    .frame_width = 320, // mjpeg width pixel, for example 320
    .frame_height = 240, // mjpeg height pixel, for example 240
    .frame_interval = FPS2INTERVAL(15), // frame interval (100µs units), such as 15fps
    .xfer_buffer_size = 32 * 1024, // single frame image size, need to be determined according to actual testing, 320 * 240 generally less than 35KB
    .xfer_buffer_a = pointer_buffer_a, // the internal transfer buffer
    .xfer_buffer_b = pointer_buffer_b, // the internal transfer buffer
    .frame_buffer_size = 32 * 1024, // single frame image size, need to determine according to actual test
    .frame_buffer = pointer_frame_buffer, // the image frame buffer
    .frame_cb = &camera_frame_cb, //camera callback, can block in here
    .frame_cb_arg = NULL, // camera callback args
}

uac_config_t uac_config = {
    .mic_bit_resolution = 16, //microphone resolution, bits
    .mic_samples_frequence = 16000, //microphone frequence, Hz
    .spk_bit_resolution = 16, //speaker resolution, bits
    .spk_samples_frequence = 16000, //speaker frequence, Hz
    .spk_buf_size = 16000, //size of speaker send buffer, should be a multiple of spk_ep_mps
    .mic_buf_size = 0, //mic receive buffer size, 0 if not use, else should be a multiple of mic_min_bytes
    .mic_cb = &mic_frame_cb, //mic callback, can not block in here
    .mic_cb_arg = NULL, //mic callback args
};
  1. Use the uvc_streaming_config() to config the UVC driver, or use the uac_streaming_config() to config the UAC driver

  2. Use the usb_streaming_start() to turn on the stream, then the driver will handle USB connection and negotiation.

  3. The host will matches the descriptors of the connected devices according to the user parameters. If the device fails to meet the configuration requirements, the driver prompt warning message

  4. If the device meets user configuration requirements, the Host will continue to receive the IN stream (UVC and UAC mic), and will call the user’s callbacks when new frames ready.

    1. The camera callback will be triggered after a new MJPEG image ready. The callback can block during processing, because which works in an independent task context.

    2. The mic callback will be triggered after mic_min_bytes() bytes data received. But the callback here must not block in any way, otherwise it will affect the reception of the next frame. If the block operations for mic is necessary, you can use the polling mode instead of the callback mode through uac_mic_streaming_read() api.

  5. User can send speaker OUT stream using uac_spk_streaming_write() through a ringbuffer, the Host will fetch the data when USB is free to send.

  6. Use the usb_streaming_control() to control the stream suspend/resume, uac volume/mute control can also be support if the USB device has such feature unit;

  7. Use the usb_streaming_stop() to stop the usb stream, USB resource will be completely released.

Bug report

ESP32-S2 ECO0 Chip SPI screen jitter when work with usb camera
  1. In earlier versions of the ESP32-S2 chip, USB transfers can cause SPI data contamination (esp32s2>=ECO1 and esp32s3 do not have this bug)

  2. Software workaround

  • spi_ll.h add below function

//components/hal/esp32s2/include/hal/spi_ll.h
static inline uint32_t spi_ll_tx_get_fifo_cnt(spi_dev_t *hw)
{
    return hw->dma_out_status.out_fifo_cnt;
}
  • modify spi_new_trans implement as below

// The function is called to send a new transaction, in ISR or in the task.
// Setup the transaction-specified registers and linked-list used by the DMA (or FIFO if DMA is not used)
static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_t *trans_buf)
{
    //...................
    spi_hal_setup_trans(hal, hal_dev, &hal_trans);
    spi_hal_prepare_data(hal, hal_dev, &hal_trans);

    //Call pre-transmission callback, if any
    if (dev->cfg.pre_cb) dev->cfg.pre_cb(trans);
#if 1
    //USB Bug workaround
    //while (!((spi_ll_tx_get_fifo_cnt(SPI_LL_GET_HW(host->id)) == 12) || (spi_ll_tx_get_fifo_cnt(SPI_LL_GET_HW(host->id)) == trans->length / 8))) {
    while (trans->length && spi_ll_tx_get_fifo_cnt(SPI_LL_GET_HW(host->id)) == 0) {
        __asm__ __volatile__("nop");
        __asm__ __volatile__("nop");
        __asm__ __volatile__("nop");
    }
#endif
    //Kick off transfer
    spi_hal_user_start(hal);
}

Examples

usb/host/usb_camera_mic_spk

API Reference

Header File
Functions
esp_err_t uvc_streaming_config(const uvc_config_t *config)

Config UVC streaming with user defined parameters.For normal use, user only need to specify no-optional parameters, and set optional parameters to 0 (the driver will find the correct value from the device descriptors). For quick start mode, user should specify all parameters manually to skip get and process descriptors steps.

Return

esp_err_t ESP_ERR_INVALID_STATE USB streaming is running, user need to stop streaming first ESP_ERR_INVALID_ARG Invalid argument ESP_OK Success

Parameters
  • config: parameters defined in uvc_config_t

esp_err_t uac_streaming_config(const uac_config_t *config)

Config UAC streaming with user defined parameters.For normal use, user only need to specify no-optional parameters, and set optional parameters to 0 (the driver will find the correct value from the device descriptors). For quick start mode, user should specify all parameters manually to skip get and process descriptors steps.

Return

esp_err_t ESP_ERR_INVALID_STATE USB streaming is running, user need to stop streaming first ESP_ERR_INVALID_ARG Invalid argument ESP_OK Success

Parameters
  • config: parameters defined in uvc_config_t

esp_err_t usb_streaming_start(void)

Start usb streaming with pre-configs, usb driver will create internal tasks to handle usb data from stream pipe, and run user’s callback after new frame ready.

Return

ESP_ERR_INVALID_STATE streaming not configured, or streaming running already ESP_FAIL start failed ESP_OK start succeed

esp_err_t usb_streaming_stop(void)

Stop current usb streaming, internal tasks will be delete, related resourse will be free.

Return

ESP_ERR_INVALID_STATE streaming not started ESP_ERR_TIMEOUT stop wait timeout ESP_OK stop succeed

esp_err_t usb_streaming_connect_wait(size_t timeout_ms)

Wait for USB device connection.

Return

esp_err_t ESP_ERR_INVALID_STATE: usb streaming not started ESP_ERR_TIMEOUT: timeout ESP_OK: device connected

Parameters
  • timeout_ms: timeout in ms

esp_err_t usb_streaming_state_register(state_callback_t *cb, void *user_ptr)

This function registers a callback for USB streaming, please note that only one callback can be registered, the later registered callback will overwrite the previous one.

Return

esp_err_t

  • ESP_OK Success

  • ESP_ERR_INVALID_STATE USB streaming is running, callback need register before start

Parameters
  • cb: A pointer to a function that will be called when the USB streaming state changes.

  • user_ptr: user_ptr is a void pointer.

esp_err_t usb_streaming_control(usb_stream_t stream, stream_ctrl_t ctrl_type, void *ctrl_value)

Control USB streaming with specific stream and control type.

Return

ESP_ERR_INVALID_ARG invalid arg ESP_ERR_INVALID_STATE driver not configured or not started ESP_ERR_NOT_SUPPORTED current device not support this control type ESP_FAIL control failed ESP_OK succeed

Parameters
  • stream: stream type defined in usb_stream_t

  • ctrl_type: control type defined in stream_ctrl_t

  • ctrl_value: control value

esp_err_t uac_spk_streaming_write(void *data, size_t data_bytes, size_t timeout_ms)

Write data to the speaker buffer, will be send out when USB device is ready.

Return

ESP_ERR_INVALID_STATE spk stream not config ESP_ERR_NOT_FOUND spk interface not found ESP_ERR_TIMEOUT spk ringbuf full ESP_OK succeed

Parameters
  • data: The data to be written.

  • data_bytes: The size of the data to be written.

  • timeout_ms: The timeout value for writing data to the buffer.

esp_err_t uac_mic_streaming_read(void *buf, size_t buf_size, size_t *data_bytes, size_t timeout_ms)

Read data from internal mic buffer, the actual size will be returned.

Return

ESP_ERR_INVALID_ARG parameter error ESP_ERR_INVALID_STATE mic stream not config ESP_ERR_NOT_FOUND mic interface not found ESP_TIMEOUT timeout ESP_OK succeed

Parameters
  • buf: pointer to the buffer to store the received data

  • buf_size: The size of the data buffer.

  • data_bytes: The actual size read from buffer

  • timeout_ms: The timeout value for the read operation.

esp_err_t uac_frame_size_list_get(usb_stream_t stream, uac_frame_size_t *frame_list, size_t *list_size, size_t *cur_index)

Get the audio frame size list of current stream, the list contains audio channel number, bit resolution and samples frequence. IF list_size equals 1 and the samples_frequence equals 0, which means the frequency can be set to any value between samples_frequence_min and samples_frequence_max.

Return

esp_err_t

  • ESP_ERR_INVALID_ARG Parameter error

  • ESP_ERR_INVALID_STATE USB device not active

  • ESP_OK Success

Parameters
  • stream: the stream type

  • frame_list: the output frame list, NULL to only get the list size

  • list_size: frame list size

  • cur_index: current frame index

esp_err_t uac_frame_size_reset(usb_stream_t stream, uint8_t ch_num, uint16_t bit_resolution, uint32_t samples_frequence)

Reset audio channel number, bit resolution and samples frequence, please reset when the streaming in suspend state. The new configs will be effective after streaming resume.

Return

esp_err_t

  • ESP_ERR_INVALID_ARG Parameter error

  • ESP_ERR_INVALID_STATE USB device not active

  • ESP_ERR_NOT_FOUND frequency not found

  • ESP_OK Success

  • ESP_FAIL Reset failed

Parameters
  • stream: stream type

  • ch_num: audio channel numbers

  • bit_resolution: audio bit resolution

  • samples_frequence: audio samples frequence

esp_err_t uvc_frame_size_list_get(uvc_frame_size_t *frame_list, size_t *list_size, size_t *cur_index)

Get the frame size list of current connected camera.

Return

esp_err_t ESP_ERR_INVALID_ARG parameter error ESP_ERR_INVALID_STATE uvc stream not config or not active ESP_OK succeed

Parameters
  • frame_list: the frame size list, can be NULL if only want to get list size

  • list_size: the list size

  • cur_index: current frame index

esp_err_t uvc_frame_size_reset(uint16_t frame_width, uint16_t frame_height, uint32_t frame_interval)

Reset the expected frame size and frame interval, please reset when uvc streaming in suspend state.The new configs will be effective after streaming resume.

Note: frame_width and frame_height can be set to 0 at the same time, which means no change on frame size.

Return

esp_err_t

Parameters
  • frame_width: frame width, FRAME_RESOLUTION_ANY means any width

  • frame_height: frame height, FRAME_RESOLUTION_ANY means any height

  • frame_interval: frame interval, 0 means no change

Structures
struct uvc_config

UVC configurations, for params with (optional) label, users do not need to specify manually, unless there is a problem with descriptors, or users want to skip the get and process descriptors steps.

Public Members

uint16_t frame_width

Picture width, set FRAME_RESOLUTION_ANY for any resolution

uint16_t frame_height

Picture height, set FRAME_RESOLUTION_ANY for any resolution

uint32_t frame_interval

Frame interval in 100-ns units, 666666 ~ 15 Fps

uint32_t xfer_buffer_size

Transfer buffer size, using double buffer here, must larger than one frame size

uint8_t *xfer_buffer_a

Buffer a for usb payload

uint8_t *xfer_buffer_b

Buffer b for usb payload

uint32_t frame_buffer_size

Frame buffer size, must larger than one frame size

uint8_t *frame_buffer

Buffer for one frame

uvc_frame_callback_t *frame_cb

callback function to handle incoming frame

void *frame_cb_arg

callback function arg Optional configs, Users need to specify parameters manually when they want to skip the get and process descriptors steps (used to speed up startup)

uvc_xfer_t xfer_type

(optional) UVC stream transfer type, UVC_XFER_ISOC or UVC_XFER_BULK

uint8_t format_index

(optional) Format index of MJPEG

uint8_t frame_index

(optional) Frame index, to choose resolution

uint16_t interface

(optional) UVC stream interface number

uint16_t interface_alt

(optional) UVC stream alternate interface, to choose MPS (Max Packet Size), bulk fix to 0

uint8_t ep_addr

(optional) endpoint address of selected alternate interface

uint32_t ep_mps

(optional) MPS of selected interface_alt

uint32_t flags

(optional) flags to control the driver behavers

struct mic_frame_t

mic frame type

Public Members

void *data

mic data

uint32_t data_bytes

mic data size

uint16_t bit_resolution

bit resolution in buffer

uint32_t samples_frequence

mic sample frequency

struct uvc_frame_size_t

uvc frame type

Public Members

uint16_t width

frame width

uint16_t height

frame height

struct uac_frame_size_t

uac frame type

Public Members

uint8_t ch_num

channel numbers

uint16_t bit_resolution

bit resolution in buffer

uint32_t samples_frequence

sample frequency

uint32_t samples_frequence_min

min sample frequency

uint32_t samples_frequence_max

max sample frequency

struct uac_config_t

UAC configurations, for params with (optional) label, users do not need to specify manually, unless there is a problem with descriptor parse, or a problem with the device descriptor.

Public Members

uint8_t spk_ch_num

speaker channel numbers, UAC_CH_ANY for any channel number

uint8_t mic_ch_num

microphone channel numbers, UAC_CH_ANY for any channel number

uint16_t mic_bit_resolution

microphone resolution(bits), UAC_BITS_ANY for any bit resolution

uint32_t mic_samples_frequence

microphone frequence(Hz), UAC_FREQUENCY_ANY for any frequency

uint16_t spk_bit_resolution

speaker resolution(bits), UAC_BITS_ANY for any

uint32_t spk_samples_frequence

speaker frequence(Hz), UAC_FREQUENCY_ANY for any frequency

uint32_t spk_buf_size

size of speaker send buffer, should be a multiple of spk_ep_mps

uint32_t mic_buf_size

mic receive buffer size, 0 if not use

mic_callback_t *mic_cb

mic callback, can not block in here!, NULL if not use

void *mic_cb_arg

mic callback args, NULL if not use Optional configs, Users need to specify parameters manually when they want to skip the get and process descriptors steps (used to speed up startup)

uint16_t mic_interface

(optional) microphone stream interface number, set 0 if not use

uint8_t mic_ep_addr

(optional) microphone interface endpoint address

uint32_t mic_ep_mps

(optional) microphone interface endpoint mps

uint16_t spk_interface

(optional) speaker stream interface number, set 0 if not use

uint8_t spk_ep_addr

(optional) speaker interface endpoint address

uint32_t spk_ep_mps

(optional) speaker interface endpoint mps

uint16_t ac_interface

(optional) audio control interface number, set 0 if not use

uint8_t mic_fu_id

(optional) microphone feature unit id, set 0 if not use

uint8_t spk_fu_id

(optional) speaker feature unit id, set 0 if not use

uint32_t flags

(optional) flags to control the driver behavers

Macros
FRAME_RESOLUTION_ANY

any uvc frame resolution

UAC_FREQUENCY_ANY

any uac sample frequency

UAC_BITS_ANY

any uac bit resolution

UAC_CH_ANY

any uac channel number

FPS2INTERVAL(fps)

convert fps to uvc frame interval

FRAME_INTERVAL_FPS_5

5 fps

FRAME_INTERVAL_FPS_10

10 fps

FRAME_INTERVAL_FPS_15

15 fps

FRAME_INTERVAL_FPS_20

20 fps

FRAME_INTERVAL_FPS_30

25 fps

FLAG_UVC_SUSPEND_AFTER_START

suspend uvc after usb_streaming_start

FLAG_UAC_SPK_SUSPEND_AFTER_START

suspend uac speaker after usb_streaming_start

FLAG_UAC_MIC_SUSPEND_AFTER_START

suspend uac microphone after usb_streaming_start

Type Definitions
typedef struct uvc_config uvc_config_t

UVC configurations, for params with (optional) label, users do not need to specify manually, unless there is a problem with descriptors, or users want to skip the get and process descriptors steps.

typedef void() mic_callback_t(mic_frame_t *frame, void *user_ptr)

user callback function to handle incoming mic frames

typedef void() state_callback_t(usb_stream_state_t state, void *user_ptr)

user callback function to handle usb device connection status

Enumerations
enum uvc_xfer_t

UVC stream usb transfer type, most camera using isochronous mode, bulk mode can also be support for higher bandwidth.

Values:

UVC_XFER_ISOC = 0

Isochronous Transfer Mode

UVC_XFER_BULK

Bulk Transfer Mode

UVC_XFER_UNKNOWN

Unknown Mode

enum usb_stream_t

Stream id, used for control.

Values:

STREAM_UVC = 0

usb video stream

STREAM_UAC_SPK

usb audio speaker stream

STREAM_UAC_MIC

usb audio microphone stream

STREAM_MAX

max stream id

enum usb_stream_state_t

USB device connection status.

Values:

STREAM_CONNECTED = 0
STREAM_DISCONNECTED
enum stream_ctrl_t

Stream control type, which also depends on if device support.

Values:

CTRL_NONE = 0

None

CTRL_SUSPEND

streaming suspend control. ctrl_data NULL

CTRL_RESUME

streaming resume control. ctrl_data NULL

CTRL_UAC_MUTE

mute control. ctrl_data (false/true)

CTRL_UAC_VOLUME

volume control. ctrl_data (0~100)

CTRL_MAX

max type value

Audio

[中文]

PWM Audio

[中文]

Supported Socs

ESP32

ESP32-S2

ESP32-S3

ESP32-C3

The PWM audio function uses the internal LEDC peripheral in ESP32 to generate PWM audio, which does not need an external audio Codec chip. This is mainly used for cost-sensitive applications with low audio quality requirements.

Features

  • Allows any GIPO with output capability as an audio output pin

  • Supports 8-bit ~ 10-bit PWM resolution

  • Supports stereo

  • Supports 8 ~ 48 KHz sampling rate

Structure

_images/pwm_audio_diagram.png

Structure

  1. First, the data is recoded to meet the PWM input requirements, including the shift and offset of the data;

  2. Send data to the ISR (Interrupt Service Routines) function of the Timer Group via Ring Buffer;

  3. The Timer Group reads data from the Ring Buffer according to the pre-defined sampling rate, and write the data into LEDC.

Note

Since the output is a PWM signal, it needs to be low-pass filtered to get the audio waveform.

PWM Frequency

The frequency of the output PWM cannot be configured directly, but needs to be calculated by configuring the number of PWM resolution bits, as shown below:

\[f_{pwm}=\frac{f_{APB\_CLK}}{2^{res\_bits}}-\left (\frac{f_{APB\_CLK}}{2^{res\_bits}} MOD 1000\right )\]

The \(f_{APB\_CLK}\) here is 80 MHz, and \(res\_bits\) is the number of PWM resolution bits. When the resolution is LEDC_TIMER_10_BIT, the PWM frequency is 78 KHz. As we all know, a higher PWM frequency and resolution can better reproduce the audio signal. However, this formula shows that increasing PWM frequency means lower resolution and increasing resolution means lower PWM frequency. Thus, please adjust these parameters according to the actual application scenario to achieve a good balance.

Application Example

pwm_audio_config_t pac;
pac.duty_resolution    = LEDC_TIMER_10_BIT;
pac.gpio_num_left      = LEFT_CHANNEL_GPIO;
pac.ledc_channel_left  = LEDC_CHANNEL_0;
pac.gpio_num_right     = RIGHT_CHANNEL_GPIO;
pac.ledc_channel_right = LEDC_CHANNEL_1;
pac.ledc_timer_sel     = LEDC_TIMER_0;
pac.tg_num             = TIMER_GROUP_0;
pac.timer_num          = TIMER_0;
pac.ringbuf_len        = 1024 * 8;

pwm_audio_init(&pac));             /**< Initialize pwm audio */
pwm_audio_set_param(48000, 8, 2);  /**< Set sample rate, bits and channel numner */
pwm_audio_start();                 /**< Start to run */

while(1) {

    //< Perpare audio data, such as decode mp3/wav file

    /**< Write data to paly */
    pwm_audio_write(audio_data, length, &written, 1000 / portTICK_PERIOD_MS);
}

API Reference

Header File
Functions
esp_err_t pwm_audio_init(const pwm_audio_config_t *cfg)

Initializes and configure the pwm audio.

Return

  • ESP_OK Success

  • ESP_FAIL timer_group or ledc initialize failed

  • ESP_ERR_INVALID_ARG if argument wrong

  • ESP_ERR_INVALID_STATE The pwm audio already configure

  • ESP_ERR_NO_MEM Memory allocate failed

Parameters

esp_err_t pwm_audio_deinit(void)

Deinitialize LEDC timer_group and output gpio.

Return

  • ESP_OK Success

  • ESP_FAIL pwm_audio not initialized

esp_err_t pwm_audio_start(void)

Start to run pwm audio.

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_STATE pwm_audio not initialized or it already running

esp_err_t pwm_audio_stop(void)

Stop pwm audio.

Attention

Only stop timer, and the pwm will keep to output. If you want to stop pwm output, call pwm_audio_deinit function

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_STATE pwm_audio not initialized or it already idle

esp_err_t pwm_audio_write(uint8_t *inbuf, size_t len, size_t *bytes_written, TickType_t ticks_to_wait)

Write data to play.

Return

  • ESP_OK Success

  • ESP_FAIL Write encounter error

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • inbuf: Pointer source data to write

  • len: length of data in bytes

  • [out] bytes_written: Number of bytes written, if timeout, the result will be less than the size passed in.

  • ticks_to_wait: TX buffer wait timeout in RTOS ticks. If this many ticks pass without space becoming available in the DMA transmit buffer, then the function will return (note that if the data is written to the DMA buffer in pieces, the overall operation may still take longer than this timeout.) Pass portMAX_DELAY for no timeout.

esp_err_t pwm_audio_set_param(int rate, ledc_timer_bit_t bits, int ch)

Set audio parameter, Similar to pwm_audio_set_sample_rate(), but also sets bit width.

Attention

After start pwm audio, it can’t modify parameters by call function pwm_audio_set_param. If you want to change the parameters, must stop pwm audio by call function pwm_audio_stop.

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • rate: sample rate (ex: 8000, 44100…)

  • bits: bit width

  • ch: channel number

esp_err_t pwm_audio_set_sample_rate(int rate)

Set sample rate.

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • rate: sample rate (ex: 8000, 44100…)

esp_err_t pwm_audio_set_volume(int8_t volume)

Set volume for pwm audio.

Attention

when the volume is too small, it will produce serious distortion

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • volume: Volume to set (-16 ~ 16). Set to 0 for original output; Set to less then 0 for attenuation, and -16 is mute; Set to more than 0 for enlarge, and 16 is double output

esp_err_t pwm_audio_get_volume(int8_t *volume)

Get current volume.

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • volume: Pointer to volume

esp_err_t pwm_audio_get_param(int *rate, int *bits, int *ch)

Get parameter for pwm audio.

Return

  • Always return ESP_OK

Parameters
  • rate: sample rate, if you don’t care about this parameter, set it to NULL

  • bits: bit width, if you don’t care about this parameter, set it to NULL

  • ch: channel number, if you don’t care about this parameter, set it to NULL

esp_err_t pwm_audio_get_status(pwm_audio_status_t *status)

get pwm audio status

Return

  • ESP_OK Success

Parameters
  • status: current pwm_audio status

Structures
struct pwm_audio_config_t

Configuration parameters for pwm_audio_init function.

Public Members

int gpio_num_left

the LEDC output gpio_num, Left channel

int gpio_num_right

the LEDC output gpio_num, Right channel

ledc_channel_t ledc_channel_left

LEDC channel (0 - 7), Corresponding to left channel

ledc_channel_t ledc_channel_right

LEDC channel (0 - 7), Corresponding to right channel

ledc_timer_t ledc_timer_sel

Select the timer source of channel (0 - 3)

ledc_timer_bit_t duty_resolution

ledc pwm bits

uint32_t ringbuf_len

ringbuffer size

Enumerations
enum pwm_audio_status_t

pwm audio status

Values:

PWM_AUDIO_STATUS_UN_INIT = 0

pwm audio uninitialized

PWM_AUDIO_STATUS_IDLE = 1

pwm audio idle

PWM_AUDIO_STATUS_BUSY = 2

pwm audio busy

enum pwm_audio_channel_t

pwm audio channel define

Values:

PWM_AUDIO_CH_MONO = 0

1 channel (mono)

PWM_AUDIO_CH_STEREO = 1

2 channel (stereo)

PWM_AUDIO_CH_MAX

DAC Audio

[中文]

ESP32 has two independent DAC channels and can play audio using I2S directly via DMA. The APIs in this document have been simplified on the basis of ESP-IDF, and the related data has been recoded to support more types of sampling bit width.

API Reference

Header File
Functions
esp_err_t dac_audio_init(dac_audio_config_t *cfg)

initialize i2s build-in dac to play audio with

Attention

only support ESP32, because i2s of ESP32S2 not have a build-in dac

Return

  • ESP_OK Success

  • ESP_FAIL Encounter error

  • ESP_ERR_INVALID_ARG Parameter error

  • ESP_ERR_NO_MEM Out of memory

Parameters

esp_err_t dac_audio_deinit(void)

deinitialize dac

Return

  • ESP_OK Success

  • ESP_FAIL Encounter error

esp_err_t dac_audio_start(void)

Start dac to play.

Return

  • ESP_OK Success

  • ESP_FAIL Encounter error

esp_err_t dac_audio_stop(void)

Stop play.

Return

  • ESP_OK Success

  • ESP_FAIL Encounter error

esp_err_t dac_audio_set_param(int rate, int bits, int ch)

Configuration dac parameter.

Return

  • ESP_OK Success

  • ESP_FAIL Encounter error

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • rate: sample rate (ex: 8000, 44100…)

  • bits: bit width

  • ch: channel number

esp_err_t dac_audio_set_volume(int8_t volume)

Set volume.

Attention

Using volume greater than 0 may cause variable overflow and distortion Usually you should enter a volume less than or equal to 0

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • volume: Volume to set (-16 ~ 16), see Macro VOLUME_0DB Set to 0 for original output; Set to less then 0 for attenuation, and -16 is mute; Set to more than 0 for enlarge, and 16 is double output

esp_err_t dac_audio_write(uint8_t *inbuf, size_t len, size_t *bytes_written, TickType_t ticks_to_wait)

Write data to play.

Return

  • ESP_OK Success

  • ESP_FAIL Write encounter error

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • inbuf: Pointer source data to write

  • len: length of data in bytes

  • [out] bytes_written: Number of bytes written, if timeout, the result will be less than the size passed in.

  • ticks_to_wait: TX buffer wait timeout in RTOS ticks. If this many ticks pass without space becoming available in the DMA transmit buffer, then the function will return (note that if the data is written to the DMA buffer in pieces, the overall operation may still take longer than this timeout.) Pass portMAX_DELAY for no timeout.

Structures
struct dac_audio_config_t

Configuration parameters for dac_audio_init function.

Public Members

i2s_port_t i2s_num

I2S_NUM_0, I2S_NUM_1

int sample_rate

I2S sample rate

i2s_bits_per_sample_t bits_per_sample

I2S bits per sample

i2s_dac_mode_t dac_mode

DAC mode configurations - see i2s_dac_mode_t

int dma_buf_count

DMA buffer count, number of buffer

int dma_buf_len

DMA buffer length, length of each buffer

uint32_t max_data_size

one time max write data size

GUI

[中文]

LVGL Graphics Library

[中文]

LVGL is an open-source free graphics library in C language providing everything you need to create embedded GUI with easy-to-use graphical elements, beautiful visual effects and low memory footprint.

Features

LVGL has the following features:

  • More than 30 powerful, fully customizable widgets, e.g., button, slider, text area, keyboard and so on

  • Supports various screens with any resolution

  • Simple interface and low memory usage

  • Supports multiple input device for the same screen

  • Provides various drawing features, e.g., anti-aliasing, polygon, shadow, etc.

  • Supports UTF-8 coding, multi language and multi font text

  • Supports various image formats, can read images from flash or SD card

  • provides online image converter

  • Supports Micropython

Requirements

The minimum requirements for running LVGL are listed as follows:

  • 16, 32 or 64 bit micro-controller or processor

  • Clock frequency: > 16 MHz

  • Flash/ROM: > 64 kB (180 kB is recommended)

  • RAM: 8 kB (24 kB is recommended)

  • 1 frame buffer

  • Graphics buffer: > “horizontal resolution” pixels

  • C99 or newer compiler

Online Tools

LVGL provides online Font Converter and Image Converter.

Demo Examples

Note

The following examples are no longer maintained. For LCD and LVGL examples, please refer to: i80_controllerrgb_panel And spi_lcd_touch

Official Demo

LVGL provides a demo project of using LVGL on ESP32 in LVGL project for ESP32.

On top of that, ESP-IoT-Solution also provides some application examples of using LVGL:

Thermostat

A thermostat control interface designed using LVGL:

_images/thermostat.jpg

Please find details of this example in hmi/lvgl_thermostat.

Coffee

An interactive interface of a coffee machine designed using LVGL:

_images/lvgl_coffee.jpg

Please find details of this example in hmi/lvgl_coffee.

Wificonfig

When connecting Wi-Fi with ESP32, a Wi-Fi connection interface designed using LVGL can show information of the neighboring Wi-Fi, and you can type in Wi-Fi password on this interface.

_images/lvgl_wificonfig0.jpg
_images/lvgl_wificonfig1.jpg

Please find details of this example in hmi/lvgl_wificonfig.

Input Device

[中文]

Button

[中文]

The button component supports GPIO and ADC mode, and it allows the creation of two different kinds of the button at the same time. The following figure shows the hardware design of the button:

_images/button_hardware.png
  • GPIO button: The advantage of the GPIO button is that each button occupies an independent IO and therefore does not affect each other, and has high stability however when the number of buttons increases, it may take too many IO resources.

  • ADC button: The advantage of using the ADC button is that one ADC channel can share multiple buttons and occupy fewer IO resources. The disadvantages include that you cannot press multiple buttons at the same time, and instability increases due to increase in the closing resistance of the button due to oxidation and other factors.

Note

  • The GPIO button needs to pay attention to the problem of pull-up and pull-down resistor inside the chip, which will be enabled by default. But there is no such resistor inside the IO that only supports input, external connection requires.

  • The voltage of the ADC button should not exceed the ADC range.

Button event

Triggering conditions for each button event are enlisted in the table below:

Event

Trigger Conditions

BUTTON_PRESS_DOWN

Button press down

BUTTON_PRESS_UP

Button release

BUTTON_PRESS_REPEAT

Button press down and release (>= 2-times)

BUTTON_SINGLE_CLICK

Button press down and release (single time)

BUTTON_DOUBLE_CLICK

Button press down and release (double times)

BUTTON_LONG_PRESS_START

Button press reaches the long-press threshold

BUTTON_LONG_PRESS_HOLD

Button press for long time

BUTTON_PRESS_REPEAT_DONE

Multiple press down and release

Each button supports call-back and pooling mode.

  • Call-back: Each event of a button can register a call-back function for it, and the call-back function will be called when an event is generated. This method has high efficiency and real-time performance, and no events will be lost.

  • Polling: Periodically call iot_button_get_event() in the program to query the current event of the button. This method is easy to use and is suitable for occasions with simple tasks

Note

you can also combine the above two methods.

Attention

No blocking operations such as TaskDelay are allowed in the call-back function

Configuration

  • BUTTON_PERIOD_TIME_MS : scan cycle

  • BUTTON_DEBOUNCE_TICKS : debounce time

  • BUTTON_SHORT_PRESS_TIME_MS : short press down effective time

  • BUTTON_LONG_PRESS_TIME_MS : long press down effective time

  • ADC_BUTTON_MAX_CHANNEL : maximum number of channel for ADC

  • ADC_BUTTON_MAX_BUTTON_PER_CHANNEL : maximum number of ADC buttons per channel

  • ADC_BUTTON_SAMPLE_TIMES : ADC sample time

  • BUTTON_SERIAL_TIME_MS : call-back interval triggered by long press time

Demonstration

Create a button
// create gpio button
button_config_t gpio_btn_cfg = {
    .type = BUTTON_TYPE_GPIO,
    .long_press_ticks = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
    .short_press_ticks = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
    .gpio_button_config = {
        .gpio_num = 0,
        .active_level = 0,
    },
};
button_handle_t gpio_btn = iot_button_create(&gpio_btn_cfg);
if(NULL == gpio_btn) {
    ESP_LOGE(TAG, "Button create failed");
}

// create adc button
button_config_t adc_btn_cfg = {
    .type = BUTTON_TYPE_ADC,
    .long_press_ticks = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
    .short_press_ticks = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
    .adc_button_config = {
        .adc_channel = 0,
        .button_index = 0,
        .min = 100,
        .max = 400,
    },
};
button_handle_t adc_btn = iot_button_create(&adc_btn_cfg);
if(NULL == adc_btn) {
    ESP_LOGE(TAG, "Button create failed");
}

Note

please pass adc_handle and adc_channel , when there are other places in the project that use ADC1.

Register call-back
static void button_single_click_cb(void *arg,void *usr_data)
{
    ESP_LOGI(TAG, "BUTTON_SINGLE_CLICK");
}

iot_button_register_cb(gpio_btn, BUTTON_SINGLE_CLICK, button_single_click_cb,NULL);
Find an event
button_event_t event;
event = iot_button_get_event(button_handle);

API Reference

Header File
Functions
button_handle_t iot_button_create(const button_config_t *config)

Create a button.

Return

A handle to the created button, or NULL in case of error.

Parameters
  • config: pointer of button configuration, must corresponding the button type

esp_err_t iot_button_delete(button_handle_t btn_handle)

Delete a button.

Return

  • ESP_OK Success

  • ESP_FAIL Failure

Parameters
  • btn_handle: A button handle to delete

esp_err_t iot_button_register_cb(button_handle_t btn_handle, button_event_t event, button_cb_t cb, void *usr_data)

Register the button event callback function.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

  • ESP_ERR_INVALID_STATE The Callback is already registered. No free Space for another Callback.

Parameters
  • btn_handle: A button handle to register

  • event: Button event

  • cb: Callback function.

  • usr_data: user data

esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event)

Unregister the button event callback function.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

  • ESP_ERR_INVALID_STATE The Callback is already registered. No free Space for another Callback.

Parameters
  • btn_handle: A button handle to unregister

  • event: Button event

size_t iot_button_count_cb(button_handle_t btn_handle)

how many Callbacks are still registered.

Return

0 if no callbacks registered, or 1 .. (BUTTON_EVENT_MAX-1) for the number of Registered Buttons.

Parameters
  • btn_handle: A button handle to unregister

button_event_t iot_button_get_event(button_handle_t btn_handle)

Get button event.

Return

Current button event. See button_event_t

Parameters
  • btn_handle: Button handle

uint8_t iot_button_get_repeat(button_handle_t btn_handle)

Get button repeat times.

Return

button pressed times. For example, double-click return 2, triple-click return 3, etc.

Parameters
  • btn_handle: Button handle

uint16_t iot_button_get_ticks_time(button_handle_t btn_handle)

Get button ticks time.

Return

Actual time from press down to up (ms).

Parameters
  • btn_handle: Button handle

uint16_t iot_button_get_long_press_hold_cnt(button_handle_t btn_handle)

Get button long press hold count.

Return

Count of trigger cb(BUTTON_LONG_PRESS_HOLD)

Parameters
  • btn_handle: Button handle

Structures
struct button_custom_config_t

custom button configuration

Public Members

uint8_t active_level

active level when press down

esp_err_t (*button_custom_init)(void *param)

user defined button init

uint8_t (*button_custom_get_key_value)(void *param)

user defined button get key value

esp_err_t (*button_custom_deinit)(void *param)

user defined button deinit

void *priv

private data used for custom button, MUST be allocated dynamically and will be auto freed in iot_button_delete

struct button_config_t

Button configuration.

Public Members

button_type_t type

button type, The corresponding button configuration must be filled

uint16_t long_press_time

Trigger time(ms) for long press, if 0 default to BUTTON_LONG_PRESS_TIME_MS

uint16_t short_press_time

Trigger time(ms) for short press, if 0 default to BUTTON_SHORT_PRESS_TIME_MS

button_gpio_config_t gpio_button_config

gpio button configuration

button_adc_config_t adc_button_config

adc button configuration

button_custom_config_t custom_button_config

custom button configuration

union button_config_t::[anonymous] [anonymous]

button configuration

Type Definitions
typedef void (*button_cb_t)(void *button_handle, void *usr_data)
typedef void *button_handle_t
Enumerations
enum button_event_t

Button events.

Values:

BUTTON_PRESS_DOWN = 0
BUTTON_PRESS_UP
BUTTON_PRESS_REPEAT
BUTTON_PRESS_REPEAT_DONE
BUTTON_SINGLE_CLICK
BUTTON_DOUBLE_CLICK
BUTTON_LONG_PRESS_START
BUTTON_LONG_PRESS_HOLD
BUTTON_EVENT_MAX
BUTTON_NONE_PRESS
enum button_type_t

Supported button type.

Values:

BUTTON_TYPE_GPIO
BUTTON_TYPE_ADC
BUTTON_TYPE_CUSTOM

Knob

[English]

Knob is the component that provides the software PCNT, it can be used on chips(esp32c2, esp32c3) that do not have PCNT hardware capabilities. By using knob you can quickly use a physical encoder, such as the EC11 encoder.

Applicable Scenarios

This is suitable for low-speed rotary knob counting scenarios where the pulse rate is less than 30 pulses per second, such as the EC11 encoder. It is suitable for scenarios where 100% accuracy of pulse counting is not required.

Note

For precise or fast pulse counting, please use the hardware PCNT function. The hardware PCNT is supported by ESP32, ESP32-C6, ESP32-H2, ESP32-S2, ESP32-S3 chips.

Knob Event

Each knob has the 5 events in the following table.

Events

Trigger Conditions

KNOB_LEFT

Left

KNOB_RIGHT

Right

KNOB_H_LIM

Count reaches maximum limit

KNOB_L_LIM

Count reaches the minimum limit

KNOB_ZERO

Count back to 0

Each knob can have Callback usage.

  • Callbacks: Each event of a knob can have a callback function registered for it, and the callback function will be called when the event is generated. This approach is efficient and real-time, and no events are lost.

Attention

No blocking operations such as TaskDelay in the callback function

Configuration items

  • KNOB_PERIOD_TIME_MS : Scan cycle

  • KNOB_DEBOUNCE_TICKS : Number of de-shaking

  • KNOB_HIGH_LIMIT : The highest number that can be counted by the knob

  • KNOB_LOW_LIMIT : The lowest number that can be counted by the knob

Application Examples

Create Knob
// create knob
knob_config_t cfg = {
    .default_direction =0,
    .gpio_encoder_a = GPIO_KNOB_A,
    .gpio_encoder_b = GPIO_KNOB_B,
};
s_knob = iot_knob_create(&cfg);
if(NULL == s_knob) {
    ESP_LOGE(TAG, "knob create failed");
}
Register callback function
static void _knob_left_cb(void *arg, void *data)
{
    ESP_LOGI(TAG, "KONB: KONB_LEFT,count_value:%"PRId32"",iot_knob_get_count_value((button_handle_t)arg));
}
iot_knob_register_cb(s_knob, KNOB_LEFT, _knob_left_cb, NULL);

API Reference

Header File
Functions
knob_handle_t iot_knob_create(const knob_config_t *config)

create a knob

Return

A handle to the created knob

Parameters
  • config: pointer of knob configuration

esp_err_t iot_knob_delete(knob_handle_t knob_handle)

Delete a knob.

Return

  • ESP_OK Success

  • ESP_FAIL Failure

Parameters
  • knob_handle: A knob handle to delete

esp_err_t iot_knob_register_cb(knob_handle_t knob_handle, knob_event_t event, knob_cb_t cb, void *usr_data)

Register the knob event callback function.

Return

  • ESP_OK Success

  • ESP_FAIL Failure

Parameters
  • knob_handle: A knob handle to register

  • event: Knob event

  • cb: Callback function

  • usr_data: user data

esp_err_t iot_knob_unregister_cb(knob_handle_t knob_handle, knob_event_t event)

Unregister the knob event callback function.

Return

  • ESP_OK Success

  • ESP_FAIL Failure

Parameters
  • knob_handle: A knob handle to register

  • event: Knob event

knob_event_t iot_knob_get_event(knob_handle_t knob_handle)

Get knob event.

Return

knob_event_t Knob event

Parameters
  • knob_handle: A knob handle to register

int iot_knob_get_count_value(knob_handle_t knob_handle)

Get knob count value.

Return

int count_value

Parameters
  • knob_handle: A knob handle to register

esp_err_t iot_knob_clear_count_value(knob_handle_t knob_handle)

Clear knob cout value to zero.

Return

  • ESP_OK Success

  • ESP_FAIL Failure

Parameters
  • knob_handle: A knob handle to register

Structures
struct knob_config_t

Knob config.

Public Members

uint8_t default_direction

0:positive increase 1:negative increase

uint8_t gpio_encoder_a

Encoder Pin A

uint8_t gpio_encoder_b

Encoder Pin B

Type Definitions
typedef void (*knob_cb_t)(void *, void *)
typedef void *knob_handle_t
Enumerations
enum knob_event_t

Knob events.

Values:

KNOB_LEFT = 0

EVENT: Rotate to the left

KNOB_RIGHT

EVENT: Rotate to the right

KNOB_H_LIM

EVENT: Count reaches maximum limit

KNOB_L_LIM

EVENT: Count reaches the minimum limit

KNOB_ZERO

EVENT: Count back to 0

KNOB_EVENT_MAX

EVENT: Number of events

Touch Panel

[中文]

Touch panels are now standard components in display applications. ESP-IoT-Solution provides drivers for common types of touch panels and currently supports the following controllers:

Resistive Touch Panel

Capacitive Touch Panel

XPT2046

FT5216

NS2016

FT5436

FT6336

FT5316

The capacitive touch panel controllers listed above can usually be driven by FT5x06.

Similar to the screen driver, some common functions are encapsulated in the touch_panel_driver_t structure, in order to port them to different GUI libraries easily. After initializing the touch panel, users can conduct operations by calling functions inside the structure, without paying attention to specific touch panel models.

Touch Panel Calibration

In actual applications, resistive touch panels must be calibrated before use, while capacitive touch panels are usually calibrated by controllers and do not require extra calibration steps. A calibration algorithm is integrated in the resistive touch panel driver. During the process, three points are used to calibrate, in which one point is used for verification. If the verified error exceeds a certain threshold value, it means the calibration has failed and a new round of calibration is started automatically until it succeeds.

The calibration process will be started by calling calibration_run(). After finished, the parameters are stored in NVS for next initialization to avoid repetitive work.

Press Touch Panel

Generally, there is an interrupt pin inside the touch panel controller (both resistive and capacitive) to signal touch events. However, this is not used in the touch panel driver, because IOs should be saved for other peripherals in screen applications as much as possible; on the other hand the information in this signal is not as accurate as data in registers.

For resistive touch panels, when the pressure in the Z direction exceeds the configured threshold, it is considered as pressed; for capacitive touch panels, the detection of over one touch point will be considered as pressed.

Touch Panel Rotation

A touch panel has eight directions, like the screen, defined in touch_panel_dir_t. The rotation of a touch panel is achieved by software, which usually sets the direction of a touch panel and a screen as the same. But this should not be fixed, for example, when using a capacitive touch panel, the inherent direction of the touch panel may not fit with the original display direction of the screen. Simply setting these two directions as the same may not show the desired contents. Therefore, please adjust the directions according to the actual situation.

On top of that, the configuration of its resolution is also important since the converted display after a touch panel being rotated relies on the resolution of its width and height. An incorrect configuration of the resolution may give you a distorted display.

Note

If you are using a resistive touch panel, the touch position can become inaccurate after it being rotated, since the resistance value in each direction may not be distributed uniformly. It is recommended to not rotate a resistive touch panel after it being calibrated.

Application Example

Initialize a Touch Panel
touch_panel_driver_t touch; // a touch panel driver

i2c_config_t i2c_conf = {
    .mode = I2C_MODE_MASTER,
    .sda_io_num = 35,
    .sda_pullup_en = GPIO_PULLUP_ENABLE,
    .scl_io_num = 36,
    .scl_pullup_en = GPIO_PULLUP_ENABLE,
    .master.clk_speed = 100000,
};
i2c_bus_handle_t i2c_bus = i2c_bus_create(I2C_NUM_0, &i2c_conf);

touch_panel_config_t touch_cfg = {
    .interface_i2c = {
        .i2c_bus = i2c_bus,
        .clk_freq = 100000,
        .i2c_addr = 0x38,
    },
    .interface_type = TOUCH_PANEL_IFACE_I2C,
    .pin_num_int = -1,
    .direction = TOUCH_DIR_LRTB,
    .width = 800,
    .height = 480,
};

/* Initialize touch panel controller FT5x06 */
touch_panel_find_driver(TOUCH_PANEL_CONTROLLER_FT5X06, &touch);
touch.init(&touch_cfg);

/* start to run calibration */
touch.calibration_run(&lcd, false);

Note

  • When using a capacitive touch panel, the call to the calibration function will return ESP_OK directly.

  • By default, only FT5x06 touch panel driver is enabled, please go to menuconfig -> Component config -> Touch Screen Driver -> Choose Touch Screen Driver to do configurations if you need to enable other drivers.

To Know If a Touch Panel is Pressed and Its Corresponding Position
touch_panel_points_t points;
touch.read_point_data(&points);
int32_t x = points.curx[0];
int32_t y = points.cury[0];
if(TOUCH_EVT_PRESS == points.event) {
    ESP_LOGI(TAG, "Pressed, Touch point at (%d, %d)", x, y);
}

API Reference

Header File
Functions
esp_err_t touch_panel_find_driver(touch_panel_controller_t controller, touch_panel_driver_t *out_driver)

Find a touch panel controller driver.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is NULL.

  • ESP_ERR_NOT_FOUND: Touch panel controller was not found.

Parameters
  • controller: Touch panel controller to initialize

  • out_driver: Pointer to a touch driver

Structures
struct touch_panel_points_t

Information of touch panel.

Public Members

touch_panel_event_t event

Event of touch

uint8_t point_num

Touch point number

uint16_t curx[TOUCH_MAX_POINT_NUMBER]

Current x coordinate

uint16_t cury[TOUCH_MAX_POINT_NUMBER]

Current y coordinate

struct touch_panel_config_t

Configuration of touch panel.

Public Members

i2c_bus_handle_t i2c_bus

Handle of i2c bus

int clk_freq

i2c clock frequency

spi clock frequency

uint8_t i2c_addr

screen i2c slave adddress

struct touch_panel_config_t::[anonymous]::[anonymous] interface_i2c

I2c interface

spi_bus_handle_t spi_bus

Handle of spi bus

int8_t pin_num_cs

SPI Chip Select Pin

struct touch_panel_config_t::[anonymous]::[anonymous] interface_spi

SPI interface

union touch_panel_config_t::[anonymous] [anonymous]

Interface configuration

touch_panel_interface_type_t interface_type

Interface bus type, see touch_interface_type_t struct

int8_t pin_num_int

Interrupt pin of touch panel. NOTE: You can set to -1 for no connection with hardware. If PENIRQ is connected, set this to pin number.

touch_panel_dir_t direction

Rotate direction

uint16_t width

touch panel width

uint16_t height

touch panel height

struct touch_panel_driver_t

Define screen common function.

Public Members

esp_err_t (*init)(const touch_panel_config_t *config)

Initial touch panel.

Attention

If you have been called function touch_panel_init() that will call this function automatically, and should not be called it again.

Return

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • config: Pointer to a structure with touch config arguments.

esp_err_t (*deinit)(void)

Deinitial touch panel.

Return

  • ESP_OK Success

  • ESP_FAIL Fail

esp_err_t (*calibration_run)(const scr_driver_t *screen, bool recalibrate)

Start run touch panel calibration.

Return

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • screen: Screen driver for display prompts

  • recalibrate: Is mandatory, set true to force calibrate

esp_err_t (*set_direction)(touch_panel_dir_t dir)

Set touch rotate rotation.

Return

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • dir: rotate direction

esp_err_t (*read_point_data)(touch_panel_points_t *point)

Get current touch information, see struct touch_panel_points_t.

Return

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters

Macros
TOUCH_MAX_POINT_NUMBER

max point number on touch panel

Enumerations
enum touch_panel_event_t

Touch events.

Values:

TOUCH_EVT_RELEASE = 0x0

Release event

TOUCH_EVT_PRESS = 0x1

Press event

enum touch_panel_dir_t

Define all screen direction.

Values:

TOUCH_DIR_LRTB

From left to right then from top to bottom, this consider as the original direction of the touch panel

TOUCH_DIR_LRBT

From left to right then from bottom to top

TOUCH_DIR_RLTB

From right to left then from top to bottom

TOUCH_DIR_RLBT

From right to left then from bottom to top

TOUCH_DIR_TBLR

From top to bottom then from left to right

TOUCH_DIR_BTLR

From bottom to top then from left to right

TOUCH_DIR_TBRL

From top to bottom then from right to left

TOUCH_DIR_BTRL

From bottom to top then from right to left

TOUCH_DIR_MAX
TOUCH_MIRROR_X = 0x40

Mirror X-axis

TOUCH_MIRROR_Y = 0x20

Mirror Y-axis

TOUCH_SWAP_XY = 0x80

Swap XY axis

enum touch_panel_interface_type_t

Values:

TOUCH_PANEL_IFACE_I2C

I2C interface

TOUCH_PANEL_IFACE_SPI

SPI interface

enum touch_panel_controller_t

All supported touch panel controllers.

Values:

TOUCH_PANEL_CONTROLLER_FT5X06
TOUCH_PANEL_CONTROLLER_XPT2046
TOUCH_PANEL_CONTROLLER_NS2016

Sensors

[中文]

Sensor Hub

[中文]

Sensor Hub is a sensor management component that can realize hardware abstraction, device management and data distribution for sensor devices. When developing applications based on Sensor Hub, users do not have to deal with complex sensor implementations, but only need to make simple selections for sensor operation, acquisition interval, range, etc. and then register callback functions to the event messages of your interests. By doing so, users can receive notifications when sensor states are switched or when data is collected.

_images/sensor_hub_diagram.png

Sensor Hub Programming Model

The Sensor Hub provides hardware abstraction for common sensor categories, based on which users can switch sensor models without modifying the upper layer of the application. And it allows adding new sensors to the Sensor Hub by implementing their corresponding sensor interface at hardware abstraction layer. This component can be used as a basic component for sensor applications in various intelligent scenarios such as environment monitoring, motion detection, health management and etc. as it simplifies operation and improves operating efficiency by centralized management of sensors.

_images/sensor_hub.png

Sensor Hub Driver

Instructions

  1. Create a sensor instance: use iot_sensor_create() to create a sensor instance. The related parameters include the sensor ID defined in sensor_id_t, configuration options for the sensor and its handler pointer. The sensor ID is used to find and load the corresponding driver, and each ID can only be used for one sensor instance. In configuration options, bus is used to specify the bus location on which the sensor is mounted; mode is used to specify the operating mode of the sensor; min_delay is used to specify the acquisition interval of the sensor, while other items inside are all non-required options. After the instance is created, the sensor handler is obtained;

  2. Register callback functions for sensor events: when a sensor event occurs, the callback functions will be called in sequence. There are two ways to register a callback function, and the instance handler of the event callback function will be returned after the registration succeed:

  3. Start a sensor: use iot_sensor_start() to start a specific sensor. After started, it will trigger a SENSOR_STARTED event, then it will collect the sensor data continuously with a set of period and trigger SENSOR_XXXX_DATA_READY event. The event callback function can obtain the specific data of each event via the event_data parameter;

  4. Stop a sensor: use iot_sensor_stop() to stop a specified sensor temporarily. After stopped, the sensor will send out a SENSOR_STOPED event and then stop the data collecting work. If the driver of this sensor supports power management, the sensor will be set to sleep mode in this stage;

  5. Unregister callback functions for sensor events: the user program can unregister an event at any time using the instance handler of this event callback function, and this callback function will not be called again when this event occurs afterwards. There are also two ways to do so:

  6. Delete sensors: use iot_sensor_delete() to delete the corresponding sensor to release the allocated memory and other resources.

Examples

  1. Sensor control LED example: sensors/sensor_control_led.

  2. Sensor hub monitor example: sensors/sensor_hub_monitor.

API Reference

Header File
Structures
struct sensor_data_t

sensor data type

Public Members

int64_t timestamp

timestamp

uint8_t sensor_id

sensor id

int32_t event_id

reserved for future use

uint32_t min_delay

minimum delay between two events, unit: ms

axis3_t acce

Accelerometer. unit: G

axis3_t gyro

Gyroscope. unit: dps

axis3_t mag

Magnetometer. unit: Gauss

float temperature

Temperature. unit: dCelsius

float humidity

Relative humidity. unit: percentage

float baro

Pressure. unit: pascal (Pa)

float light

Light. unit: lux

rgbw_t rgbw

Color. unit: lux

uv_t uv

ultraviole unit: lux

float proximity

Distance. unit: centimeters

float hr

Heat rate. unit: HZ

float tvoc

TVOC. unit: permillage

float noise

Noise Loudness. unit: HZ

float step

Step sensor. unit: 1

float force

Force sensor. unit: mN

float current

Current sensor unit: mA

float voltage

Voltage sensor unit: mV

float data[4]

for general use

struct sensor_data_group_t

sensor data group type

Public Members

uint8_t number

effective data number

sensor_data_t sensor_data[SENSOR_DATA_GROUP_MAX_NUM]

data buffer

Macros
SENSOR_EVENT_ANY_ID

register handler for any event id

Type Definitions
typedef void *sensor_driver_handle_t

hal level sensor driver handle

typedef void *bus_handle_t

i2c/spi bus handle

Enumerations
enum sensor_type_t

sensor type

Values:

NULL_ID

NULL

HUMITURE_ID

humidity or temperature sensor

IMU_ID

gyro or acc sensor

LIGHT_SENSOR_ID

light illumination or uv or color sensor

SENSOR_TYPE_MAX

max sensor type

enum sensor_command_t

sensor operate command

Values:

COMMAND_SET_MODE

set measure mdoe

COMMAND_SET_RANGE

set measure range

COMMAND_SET_ODR

set output rate

COMMAND_SET_POWER

set power mode

COMMAND_SELF_TEST

sensor self test

COMMAND_MAX

max sensor command

enum sensor_power_mode_t

sensor power mode

Values:

POWER_MODE_WAKEUP

wakeup from sleep

POWER_MODE_SLEEP

set to sleep

POWER_MAX

max sensor power mode

enum sensor_mode_t

sensor acquire mode

Values:

MODE_DEFAULT

default work mode

MODE_POLLING

polling acquire with a interval time

MODE_INTERRUPT

interrupt mode, acquire data when interrupt comes

MODE_MAX

max sensor mode

enum sensor_range_t

sensor acquire range

Values:

RANGE_DEFAULT

default range

RANGE_MIN

minimum range for high-speed or high-precision

RANGE_MEDIUM

medium range for general use

RANGE_MAX

maximum range for full scale

enum sensor_event_id_t

sensor general events

Values:

SENSOR_STARTED

sensor started, data acquire will be started

SENSOR_STOPED

sensor stoped, data acquire will be stoped

SENSOR_EVENT_COMMON_END = 9

max common events id

enum sensor_data_event_id_t

sensor data ready events

Values:

SENSOR_ACCE_DATA_READY = 10

Accelerometer data ready

SENSOR_GYRO_DATA_READY

Gyroscope data ready

SENSOR_MAG_DATA_READY

Magnetometer data ready

SENSOR_TEMP_DATA_READY

Temperature data ready

SENSOR_HUMI_DATA_READY

Relative humidity data ready

SENSOR_BARO_DATA_READY

Pressure data ready

SENSOR_LIGHT_DATA_READY

Light data ready

SENSOR_RGBW_DATA_READY

Color data ready

SENSOR_UV_DATA_READY

ultraviolet data ready

SENSOR_PROXI_DATA_READY

Distance data ready

SENSOR_HR_DATA_READY

Heat rate data ready

SENSOR_TVOC_DATA_READY

TVOC data ready

SENSOR_NOISE_DATA_READY

Noise Loudness data ready

SENSOR_STEP_DATA_READY

Step data ready

SENSOR_FORCE_DATA_READY

Force data ready

SENSOR_CURRENT_DATA_READY

Current data ready

SENSOR_VOLTAGE_DATA_READY

Voltage data ready

SENSOR_EVENT_ID_END

max common events id

Header File
Functions
esp_err_t iot_sensor_create(sensor_id_t sensor_id, const sensor_config_t *config, sensor_handle_t *p_sensor_handle)

Create a sensor instance with specified sensor_id and desired configurations.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor_id: sensor’s id detailed in sensor_id_t.

  • config: sensor’s configurations detailed in sensor_config_t

  • p_sensor_handle: return sensor handle if succeed, NULL if failed.

esp_err_t iot_sensor_start(sensor_handle_t sensor_handle)

start sensor acquisition, post data ready events when data acquired. if start succeed, sensor will start to acquire data with desired mode and post events in min_delay(ms) intervals SENSOR_STARTED event will be posted.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor_handle: sensor handle for operation

esp_err_t iot_sensor_stop(sensor_handle_t sensor_handle)

stop sensor acquisition, and stop post data events. if stop succeed, SENSOR_STOPED event will be posted.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor_handle: sensor handle for operation

esp_err_t iot_sensor_delete(sensor_handle_t *p_sensor_handle)

delete and release the sensor resource.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • p_sensor_handle: point to sensor handle, will set to NULL if delete suceed.

uint8_t iot_sensor_scan(bus_handle_t bus, sensor_info_t *buf[], uint8_t num)

Scan for valid sensors attached on bus.

Return

uint8_t total number of valid sensors found on the bus

Parameters
  • bus: bus handle

  • buf: Pointer to a buffer to save sensors’ information, if NULL no information will be saved.

  • num: Maximum number of sensor information to save, invalid if buf set to NULL, latter sensors will be discarded if num less-than the total number found on the bus.

esp_err_t iot_sensor_handler_register(sensor_handle_t sensor_handle, sensor_event_handler_t handler, sensor_event_handler_instance_t *context)

Register a event handler to a sensor’s event with sensor_handle.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor_handle: sensor handle for operation

  • handler: the handler function which gets called when the sensor’s any event is dispatched

  • context: An event handler instance object related to the registered event handler and data, can be NULL. This needs to be kept if the specific callback instance should be unregistered before deleting the whole event loop. Registering the same event handler multiple times is possible and yields distinct instance objects. The data can be the same for all registrations. If no unregistration is needed but the handler should be deleted when the event loop is deleted, instance can be NULL.

esp_err_t iot_sensor_handler_unregister(sensor_handle_t sensor_handle, sensor_event_handler_instance_t context)

Unregister a event handler from a sensor’s event.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor_handle: sensor handle for operation

  • context: the instance object of the registration to be unregistered

esp_err_t iot_sensor_handler_register_with_type(sensor_type_t sensor_type, int32_t event_id, sensor_event_handler_t handler, sensor_event_handler_instance_t *context)

Register a event handler with sensor_type instead of sensor_handle. the api only care about the event type, don’t care who post it.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor_type: sensor type decleared in sensor_type_t.

  • event_id: sensor event decleared in sensor_event_id_t and sensor_data_event_id_t

  • handler: the handler function which gets called when the event is dispatched

  • context: An event handler instance object related to the registered event handler and data, can be NULL. This needs to be kept if the specific callback instance should be unregistered before deleting the whole event loop. Registering the same event handler multiple times is possible and yields distinct instance objects. The data can be the same for all registrations. If no unregistration is needed but the handler should be deleted when the event loop is deleted, instance can be NULL.

esp_err_t iot_sensor_handler_unregister_with_type(sensor_type_t sensor_type, int32_t event_id, sensor_event_handler_instance_t context)

Unregister a event handler from a event. the api only care about the event type, don’t care who post it.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor_type: sensor type decleared in sensor_type_t.

  • event_id: sensor event decleared in sensor_event_id_t and sensor_data_event_id_t

  • context: the instance object of the registration to be unregistered

Structures
struct sensor_info_t

sensor information type

Public Members

const char *name

sensor name

const char *desc

sensor descriptive message

sensor_id_t sensor_id

sensor id

const uint8_t *addrs

sensor address list

struct sensor_config_t

sensor initialization parameter

Public Members

bus_handle_t bus

i2c/spi bus handle

sensor_mode_t mode

set acquire mode detiled in sensor_mode_t

sensor_range_t range

set measuring range

uint32_t min_delay

set minimum acquisition interval

int intr_pin

set interrupt pin

int intr_type

set interrupt type

Type Definitions
typedef void *sensor_handle_t

sensor handle

typedef void *sensor_event_handler_instance_t

sensor event handler handle

typedef const char *sensor_event_base_t

unique pointer to a subsystem that exposes events

typedef void (*sensor_event_handler_t)(void *event_handler_arg, sensor_event_base_t event_base, int32_t event_id, void *event_data)

function called when an event is posted to the queue

Enumerations
enum sensor_id_t

sensor id, used for iot_sensor_create

Values:

Humidity and Temperature Sensor

[中文]

The humidity and temperature sensor can be used as a temperature sensor, a humidity sensor or a sensor with both functions. It is mainly used for environmental temperature and humidity detections in smart home, smart farm and smart factory applications.

Adapted Products

Name

Function

Bus

Vendor

Datasheet

HAL

HDC2010

Temperature, Humidity

I2C

TI

Datasheet

HTS221

Temperature, Humidity

I2C

ST

Datasheet

SHT3X

Temperature, Humidity

I2C

Sensirion

Datasheet

MVH3004D

Temperature, Humidity

I2C

API Reference

The following APIs have implemented hardware abstraction on the humidity and temperature sensor. Users can call the code from this layer directly to write a sensor application, or use the sensor interface in sensor_hub for easier development.

Header File
Functions
sensor_humiture_handle_t humiture_create(bus_handle_t bus, int id)

Create a humiture/temperature/humidity sensor instance. Same series’ sensor or sensor with same address can only be created once.

Return

sensor_humiture_handle_t return humiture sensor handle if succeed, return NULL if create failed.

Parameters
  • bus: i2c bus handle the sensor attached to

  • id: id declared in humiture_id_t

esp_err_t humiture_delete(sensor_humiture_handle_t *sensor)

Delete and release the sensor resource.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor: point to humiture sensor handle, will set to NULL if delete succeed.

esp_err_t humiture_test(sensor_humiture_handle_t sensor)

Test if sensor is active.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor: humiture sensor handle to operate

esp_err_t humiture_acquire_humidity(sensor_humiture_handle_t sensor, float *humidity)

Acquire humiture sensor relative humidity result one time.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: humiture sensor handle to operate.

  • humidity: result data (unit:percentage)

esp_err_t humiture_acquire_temperature(sensor_humiture_handle_t sensor, float *sensor_data)

Acquire humiture sensor temperature result one time.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: humiture sensor handle to operate.

  • sensor_data: result data (unit:dCelsius)

esp_err_t humiture_sleep(sensor_humiture_handle_t sensor)

Set sensor to sleep mode.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: humiture sensor handle to operate

esp_err_t humiture_wakeup(sensor_humiture_handle_t sensor)

Wakeup sensor from sleep mode.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: humiture sensor handle to operate

esp_err_t humiture_acquire(sensor_humiture_handle_t sensor, sensor_data_group_t *data_group)

acquire a group of sensor data

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor: humiture sensor handle to operate

  • data_group: acquired data

esp_err_t humiture_control(sensor_humiture_handle_t sensor, sensor_command_t cmd, void *args)

control sensor mode with control commands and args

Parameters
  • sensor: humiture sensor handle to operate

  • cmd: control commands detailed in sensor_command_t

  • args: control commands args

    • ESP_OK Success

    • ESP_FAIL Fail

    • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Type Definitions
typedef void *sensor_humiture_handle_t

humiture sensor handle

Enumerations
enum humiture_id_t

humiture sensor id, used for humiture_create

Values:

SHT3X_ID = 0x01

sht3x humiture sensor id

HTS221_ID

hts221 humiture sensor id

HUMITURE_MAX_ID

max humiture sensor id

Inertial Measurement Unit (IMU)

[中文]

The Inertial Measurement Unit (IMU) can be used as a gyroscope sensor, an acceleration sensor, a sensor with multiple functions or etc. It is mainly used to measure the acceleration and angular velocity of an object, and then calculate the motion attitude of the object.

Adapted Products

Name

Function

Bus

Vendor

Datasheet

HAL

LIS2DH12

3-axis acceler

I2C

ST

Datasheet

MPU6050

3-axis acceler + 3-axis gyro

I2C

InvenSense

Datasheet

API Reference

The following APIs have implemented hardware abstraction on the IMU. Users can call the code from this layer directly to write a sensor application, or use the sensor interface in sensor_hub for easier development.

Header File
Functions
sensor_imu_handle_t imu_create(bus_handle_t bus, int imu_id)

Create a Inertial Measurement Unit sensor instance. Same series’ sensor or sensor with same address can only be created once.

Return

sensor_imu_handle_t return imu sensor handle if succeed, NULL is failed.

Parameters
  • bus: i2c bus handle the sensor attached to

  • imu_id: id declared in imu_id_t

esp_err_t imu_delete(sensor_imu_handle_t *sensor)

Delete and release the sensor resource.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor: point to imu sensor handle, will set to NULL if delete succeed.

esp_err_t imu_test(sensor_imu_handle_t sensor)

Test if sensor is active.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor: imu sensor handle to operate

esp_err_t imu_acquire_acce(sensor_imu_handle_t sensor, axis3_t *acce)

Acquire imu sensor accelerometer result one time.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: imu sensor handle to operate

  • acce: result data (unit:g)

esp_err_t imu_acquire_gyro(sensor_imu_handle_t sensor, axis3_t *gyro)

Acquire imu sensor gyroscope result one time.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: imu sensor handle to operate

  • gyro: result data (unit:dps)

esp_err_t imu_sleep(sensor_imu_handle_t sensor)

Set sensor to sleep mode.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: imu sensor handle to operate

esp_err_t imu_wakeup(sensor_imu_handle_t sensor)

Wakeup sensor from sleep mode.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: imu sensor handle to operate

esp_err_t imu_acquire(sensor_imu_handle_t sensor, sensor_data_group_t *data_group)

acquire a group of sensor data

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor: imu sensor handle to operate

  • data_group: acquired data

esp_err_t imu_control(sensor_imu_handle_t sensor, sensor_command_t cmd, void *args)

control sensor mode with control commands and args

Parameters
  • sensor: imu sensor handle to operate

  • cmd: control commands detailed in sensor_command_t

  • args: control commands args

    • ESP_OK Success

    • ESP_FAIL Fail

    • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Type Definitions
typedef void *sensor_imu_handle_t

imu sensor handle

Enumerations
enum imu_id_t

imu sensor id, used for imu_create

Values:

MPU6050_ID = 0x01

MPU6050 imu sensor id

LIS2DH12_ID

LIS2DH12 imu sensor id

IMU_MAX_ID

max imu sensor id

Ambient Light Sensor

[中文]

The ambient light sensor can be used as a light intensity sensor, a color sensor, a UV sensor or a sensor with multiple functions.

Adapted Products

Name

Function

Bus

Vendor

Datasheet

HAL

BH1750

Light

I2C

rohm

Datasheet

VEML6040

Light RGBW

I2C

Vishay

Datasheet

VEML6075

Light UVA UVB

I2C

Vishay

Datasheet

API Reference

The following APIs have implemented hardware abstraction on the ambient light sensor. Users can call the code from this layer directly to write a sensor application, or use the sensor interface in sensor_hub for easier development.

Header File
Functions
sensor_light_handle_t light_sensor_create(bus_handle_t bus, int id)

Create a light sensor instance. same series’ sensor or sensor with same address can only be created once.

Return

sensor_light_handle_t return light sensor handle if succeed, return NULL if failed.

Parameters
  • bus: i2c bus handle the sensor attached to

  • id: id declared in light_sensor_id_t

esp_err_t light_sensor_delete(sensor_light_handle_t *sensor)

Delete and release the sensor resource.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor: point to light sensor handle, will set to NULL if delete succeed.

esp_err_t light_sensor_test(sensor_light_handle_t sensor)

Test if sensor is active.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor: light sensor handle to operate.

esp_err_t light_sensor_acquire_light(sensor_light_handle_t sensor, float *lux)

Acquire light sensor illuminance result one time.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: light sensor handle to operate.

  • lux: result data (unit:lux)

esp_err_t light_sensor_acquire_rgbw(sensor_light_handle_t sensor, rgbw_t *rgbw)

Acquire light sensor color result one time. light color includes red green blue and white.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: light sensor handle to operate.

  • rgbw: result data (unit:lux)

esp_err_t light_sensor_acquire_uv(sensor_light_handle_t sensor, uv_t *uv)

Acquire light sensor ultra violet result one time. light Ultraviolet includes UVA UVB and UV.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: light sensor handle to operate.

  • uv: result data (unit:lux)

esp_err_t light_sensor_sleep(sensor_light_handle_t sensor)

Set sensor to sleep mode.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: light sensor handle to operate.

esp_err_t light_sensor_wakeup(sensor_light_handle_t sensor)

Wakeup sensor from sleep mode.

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

  • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Parameters
  • sensor: light sensor handle to operate.

esp_err_t light_sensor_acquire(sensor_light_handle_t sensor, sensor_data_group_t *data_group)

acquire a group of sensor data

Return

esp_err_t

  • ESP_OK Success

  • ESP_FAIL Fail

Parameters
  • sensor: light sensor handle to operate

  • data_group: acquired data

esp_err_t light_sensor_control(sensor_light_handle_t sensor, sensor_command_t cmd, void *args)

control sensor mode with control commands and args

Parameters
  • sensor: light sensor handle to operate

  • cmd: control commands detailed in sensor_command_t

  • args: control commands args

    • ESP_OK Success

    • ESP_FAIL Fail

    • ESP_ERR_NOT_SUPPORTED Function not supported on this sensor

Type Definitions
typedef void *sensor_light_handle_t

light sensor handle

Enumerations
enum light_sensor_id_t

light sensor id, used for light_sensor_create

Values:

BH1750_ID = 0x01

BH1750 light sensor id

VEML6040_ID

VEML6040 light sensor id

VEML6075_ID

VEML6075 light sensor id

LIGHT_MAX_ID

max light sensor id

Pressure Sensor

[中文]

The pressure sensor can be used to detect the absolute pressure of gases, calculate altitude and etc. It is mainly used in environmental monitoring, altitude measurement and space positioning equipments.

Adapted Products

Name

Function

Bus

Vendor

Datasheet

HAL

BME280

Pressure

I2C/SPI

BOSCH

Datesheet

Gesture Sensor

[中文]

Gesture sensors are generally sensors that convert measurements of reflected infrared light into relevant physical motions and can be used to achieve non-contact interaction between human and machines, etc.

Adapted Products

Name

Function

Bus

Vendor

Datasheet

HAL

APDS9960

Light, RGB and Gesture Sensor

I2C

Avago

Datesheet

Storage

[中文]

Storage Media

[中文]

The supported storage media is listed in the following table:

Name

Key features

Application scenario

Size

Transmission

Speed

Driver

Note

SPI Flash

Can be shared with code, no extra cost

Store parameters, text or images

MB

SPI

40/80 MHz 4-line

SPI Flash Driver

SD Card

Large capacity, pluggable

Store audio or video files

GB

SDIO/SPI

20/40 MHz 1/4-line

SD/SDIO/MMC Driver

1

eMMC

Large capacity, high-speed read/write

Store audio or video files

GB

SDIO

20/40 MHz 1/4/8-line

SD/SDIO/MMC Driver

2

EEPROM

Can address by byte, low cost

Store parameters

MB

I2C

100 ~ 400 KHz

eeprom

Note

  1. Only SPI mode is supported for ESP32-S2

  2. Not supported for ESP32-S2

SPI Flash

By default, the ESP32/ESP32-S/ESP32-C series chips use NOR flash to store and access users’ code and data. The flash can be integrated into the module or chip and is typically 4 MB, 8 MB or 16 MB. For ESP-IDF v4.0 and later versions, the SPI flash component not only supports read and write operations to the main flash, but can also connect to an another external flash for data storage.

Flash can be partitioned using the partition table. Based on functions of the partition table, flash can not only be used to store the binary code generated by users, but can also act as a non-volatile storage (NVS) to store application programming parameters. On top of that, specific flash areas can be mounted to a file system (e.g., FatFS) to store text, images and other files.

The flash chip supports 2-line (DOUT/DIO) and 4-line (QOUT/QIO) operation modes and can be configured to work in 40 MHz or 80 MHz modes. Since the main flash chip can be used directly for data storage without needs for additional memory chips, it is particularly suitable for cost-sensitive applications with small capacity requirements (MB) and high integration needs.

Related documents:

SD Card

The ESP32 supports using either the SDIO or SPI interface to access SD cards. The SDIO interface supports 1/4/8-line modes and supports both the default rate of 20 MHz and the high-speed rate of 40 MHz. Please note that this interface occupies at least 6 GPIOs and only uses fixed pins. The SPI interface can assign any IO for SD cards via the GPIO matrix and supports accessing multiple SD cards via CS pins. In hardware design level, the SPI interface is more flexible for development, but with lower access rate than that of the SDIO interface.

The SD/SDIO/MMC Driver in ESP-IDF is wrapped at the protocol layer based on the two access modes of the SD card, and provides the initialization interface and protocol-layer APIs for the SD card. The SD card, with features as large capacity and being pluggable, is widely used in application scenarios with large storage needs such as smart speaker, electronic album and etc.

Related documents:

Examples:

eMMC

The eMMC (embedded MMC) memory chip uses the similar protocol to SD cards and can use the same driver SD/SDIO/MMC Driver as SD cards. However, please note that the eMMC chip can only use SDIO mode and does not support SPI mode. Currently, the eMMC chip supports the default rate of 20 MHz and high-speed rate of 40 MHz in 8-line mode, and supports high-speed rate of 40 MHz in 4-line DDR mode.

The eMMC is generally soldered to the main board as a chip, which is more integrated than SD cards, and is suitable for wearable devices and other scenarios with high storage needs and certain requirements for system integration in the meantime.

Related documents:

EEPROM

EEPROM (e.g., AT24C0X series) is a 1024-16384 bits of serial erasable memory, which can also operate in read-only mode by configuring pin levels. Generally, its storage space is distributed by word, with each word containing 8-bit spaces. The EEPROM supports byte addressing and is easy to read and write, making it especially suitable for saving configuration parameters and etc. On top of that, it can also be used in industrial and commercial scenarios with requirements for power consumption and reliability after being optimized.

Adapted EEPROM chips:

Name

Function

Bus

Vendor

Datasheet

Driver

AT24C01/02

1024/2048 bits EEPROM

I2C

Atmel

Datasheet

eeprom

File System

[中文]

Supported file systems:

Key Features

NVS Library

FAT File System

SPIFFS File System

Features

Operates on key-value pairs, with safe interfaces

Operation system supported, strong compatibility

Developed for embedded systems, low resource occupancy

Application Scenarios

Stores parameters

Stores audio, video and other files

Stores audio, video and other files

Size

KB-MB

GB

< 128 MB

Directory Support

X

X

Wear Levelling

Optional

R/W Efficiency

0

0

0

Resources Occupancy

0

0

1

Power Failure Protection

X

X

Encryption

X

Note

  • 0: data not available or not for comparison.

  • 1: low RAM occupancy.

NVS Library

Non-volatile storage (NVS) is used to read and write data stored in the flash NVS partition. NVS operated on key-value pairs. Keys are ASCII strings; values can be integers, strings and variable binary large object (BLOB). NVS supports power loss protection and data encryption, and works best for storing many small values, such as application parameters. If you need to store large blobs or strings, please consider using the facilities provided by the FAT file system on top of the wear levelling library.

Related documents:

Examples:

FAT File System

ESP-IDF uses the FatFs library to work with FAT file system. FatFs is a file system layer independent to platform and storage media that can realize access to physical devices (e.g., flash, SD card) via a unified interface. Although the library can be used directly, many of its features can be accessed via VFS, using the C standard library and POSIX API functions.

The operating system of FAT is compatible with a wide range of mobile storage devices such as USB memory disc or SD cards. And ESP32 series chips can access these common storage devices by supporting the FAT file system.

Related documents:

Examples:

SPIFFS File System

SPIFFS is a file system intended for SPI NOR flash devices on embedded targets. It supports wear levelling, file system consistency checks, and more. Users can directly use the Posix interfaces provided by SPIFFS, or use many of its features via VFS.

As a dedicated file system for SPI NOR flash devices on embedded targets, the SPIFFS occupies less RAM resources than FAT and is only used to support flash chips with capacities less than 128 MB.

Related documents:

Examples:

Virtual File System (VFS)

The Virtual File System (VFS) component from ESP-IDF provides a unified interface for different file systems (FAT, SPIFFS), and also provides a file-like interface for device drivers.

Related documents:

Motor

[中文]

Servo

[中文]

This component uses the LEDC peripheral to generate PWM signals for independent control of servos with up to 16 channels (ESP32 chips support 16 channels and ESP32-S2 chips support 8 channels) at a selectable frequency of 50 ~ 400 Hz. When using this layer of APIs, users only need to specify the servo group, channel and target angle to realize the angle control of a servo.

Generally, there is a reference signal inside the servo generating a fixed period and pulse width, which is used to compare with the input PWM signal to output a voltage difference so as to control the rotation direction and angle of a motor. A common 180 angular rotation servo usually takes 20 ms (50 Hz) as a clock period and 0.5 ~ 2.5 ms as its high level pulse, making it rotates between 0 ~ 180 degrees.

This component can be used in scenarios with lower control accuracy requirements, such as toy cars, remote control robots, home automation, etc.

Instructions

  1. Initialization: Use servo_init() to initialize a channel. Please note that ESP32 contains two sets of channels as LEDC_LOW_SPEED_MODE and LEDC_HIGH_SPEED_MODE, while some chip may only support one channel. The configuration items in this step mainly include maximum angle, signal frequency, and minimum and maximum input pulse width to calculate the correspondence between angle and duty cycle; as well as pins and channels to specify the correspondence with chip pins and LEDC channels, respectively;

  2. Set a target angle: use servo_write_angle() to specify the servo group, channel and target angle so as to realize angle control of the servo;

  3. Read the current angle: you can use servo_read_angle() to read the current angle of the servo. Please note that this is a theoretical number calculated based on the input signal;

  4. De-initialization: you can use servo_deinit() to de-initialize a group of channels when a group of servos is used any more.

Application Example

servo_config_t servo_cfg = {
    .max_angle = 180,
    .min_width_us = 500,
    .max_width_us = 2500,
    .freq = 50,
    .timer_number = LEDC_TIMER_0,
    .channels = {
        .servo_pin = {
            SERVO_CH0_PIN,
            SERVO_CH1_PIN,
            SERVO_CH2_PIN,
            SERVO_CH3_PIN,
            SERVO_CH4_PIN,
            SERVO_CH5_PIN,
            SERVO_CH6_PIN,
            SERVO_CH7_PIN,
        },
        .ch = {
            LEDC_CHANNEL_0,
            LEDC_CHANNEL_1,
            LEDC_CHANNEL_2,
            LEDC_CHANNEL_3,
            LEDC_CHANNEL_4,
            LEDC_CHANNEL_5,
            LEDC_CHANNEL_6,
            LEDC_CHANNEL_7,
        },
    },
    .channel_number = 8,
} ;
iot_servo_init(LEDC_LOW_SPEED_MODE, &servo_cfg);

float angle = 100.0f;

// Set angle to 100 degree
iot_servo_write_angle(LEDC_LOW_SPEED_MODE, 0, angle);

// Get current angle of servo
iot_servo_read_angle(LEDC_LOW_SPEED_MODE, 0, &angle);

//deinit servo
iot_servo_deinit(LEDC_LOW_SPEED_MODE);

API Reference

Header File
Functions
esp_err_t iot_servo_init(ledc_mode_t speed_mode, const servo_config_t *config)

Initialize ledc to control the servo.

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

  • ESP_FAIL Configure ledc failed

Parameters
  • speed_mode: Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode.

  • config: Pointer of servo configure struct

esp_err_t iot_servo_deinit(ledc_mode_t speed_mode)

Deinitialize ledc for servo.

Return

  • ESP_OK Success

Parameters
  • speed_mode: Select the LEDC channel group with specified speed mode.

esp_err_t iot_servo_write_angle(ledc_mode_t speed_mode, uint8_t channel, float angle)

Set the servo motor to a certain angle.

Note

This API is not thread-safe

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • speed_mode: Select the LEDC channel group with specified speed mode.

  • channel: LEDC channel, select from ledc_channel_t

  • angle: The angle to go

esp_err_t iot_servo_read_angle(ledc_mode_t speed_mode, uint8_t channel, float *angle)

Read current angle of one channel.

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • speed_mode: Select the LEDC channel group with specified speed mode.

  • channel: LEDC channel, select from ledc_channel_t

  • angle: Current angle of the channel

Structures
struct servo_channel_t

Configuration of servo motor channel.

Public Members

gpio_num_t servo_pin[LEDC_CHANNEL_MAX]

Pin number of pwm output

ledc_channel_t ch[LEDC_CHANNEL_MAX]

The ledc channel which used

struct servo_config_t

Configuration of servo motor.

Public Members

uint16_t max_angle

Servo max angle

uint16_t min_width_us

Pulse width corresponding to minimum angle, which is usually 500us

uint16_t max_width_us

Pulse width corresponding to maximum angle, which is usually 2500us

uint32_t freq

PWM frequency

ledc_timer_t timer_number

Timer number of ledc

servo_channel_t channels

Channels to use

uint8_t channel_number

Total channel number

Security & Encryption

[中文]

Flash 加密

概述

  • 使能 flash encryption 后,使用物理手段(如串口)从 SPI flash 中读取的数据都是经过加密的,大部分数据无法恢复出真实数据。

  • flash encryption 使用 256-bit AES key 加密 flash 数据,key 保存在芯片的 efuse 中,生成之后变成软件读写保护。

  • 用户烧写 flash 时烧写的是数据明文,第一次 boot 时,软件 bootloader 会对 flash 中的数据在原处加密。

  • 一般使用情况下一共有4次机会通过串口烧写 flash ,通过 OTA 更新 flash数据没有次数限制。开发阶段可以在 menuconfig中设置无烧写次数限制,但不要在产品中这么做。

使用步骤

  1. make menuconfig 中选择 “Security features”->”Enable flash encryption on boot”

  2. 按通常操作编译出 bootloader, partition table 和 app image 并烧写到 flash 中

  3. 第一次 boot 时 flash 中被指定加密的数据被加密(大的 partition加密过程可能需要花费超过1分钟) ,之后就可以正常使用被加密的flash数据。

加密过程(第一次 boot 时进行)

  1. bootloader 读取到 efuse 中的 FLASH_CRYPT_CNT 为0,于是利用硬件随机数生成器产生加密用的 key ,此 key 被保存在 efuse 中,对于软件是读写保护的。

  2. bootloader 对所有需要被加密的 partition 在 flash 中原处加密

  3. 默认情况下 efuse 中的 DISABLE_DL_ENCRYPT, DISABLE_DL_DECRYPT 和 DISABLE_DL_CACHE 会被烧写为1,这样 UART bootloader 时就不能读取到解密后的 flash 数据

  4. efuse 中的 FLASH_CRYPT_CONFIG 被烧写成 0xf,此标志用于决定加密 key 的多少位被用于计算每一个 flash 块(32字节)对应的秘钥,设置为 0xf 时使用所有256位

  5. efuse 中的 FLASH_CRYPT_CNT 被烧写成 0x01,此标志用于 flash 烧写次数限制以及加密控制,详见“FLASH_CRYPT_CNT”一节

  6. bootloader 将自己重启,从加密的 flash 执行软件 bootloader

串口重烧 flash (3次重烧机会)

  • 串口重烧 flash 过程

    1. make menuconfig 中选择 “Security features”->”Enable flash encryption on boot”

    2. 编译工程,将所有之前加密的 images (包括 bootloader)烧写到 flash 中。

    3. 在 esp-idf 的 components/esptool_py/esptool 路径下使用命令 espefuse.py burn\_efuse FLASH\_CRYPT\_CNT 烧写 efuse 中的 FLASH_CRYPT_CNT

    4. 重启设备,bootloader 根据 FLASH_CRYPT_CNT 的值重新加密 flash 数据。

  • 若用户确定不再需要通过串口重烧 flash,可以在 esp-idf 的 components/esptool\_py/esptool 路径下使用命令 espefuse.py --port PORT write\_protect\_efuse FLASH\_CRYPT\_CNT FLASH\_CRYPT\_CNT 设置为读写保护(注意此步骤必须在 bootloader 已经完成对 flash 加密后进行)

FLASH_CRYPT_CNT

  • FLASH_CRYPT_CNT 是 flash 加密方案中非常重要的控制标志,它是 8-bit 的值,它的值一方面决定 flash 中的值是否马上需要加密,另一方面控制 flash 烧写次数限制。

  • 当 FLASH_CRYPT_CNT 有(0,2,4,6,8)位被烧写为1时,bootloader 会对 flash 中的内容进行加密。

  • 当 FLASH_CRYPT_CNT 有(1,3,5,7)位被烧写为1时,bootloader 知道 flash 的内容已经过加密,直接读取 flash 中的数据解密后使用。

  • FLASH_CRYPT_CNT 的变化过程:

    1. 没有使能 flash 加密时,永远是0

    2. 使能了 flash 加密,在第一次 boot 时 bootloader 发现它的值是 0x00,于是知道 flash 中的数据还未加密,利用硬件随机数生成器产生 key,然后加密 flash,最后将它的最低位置1(取值为0x01)

    3. 后续 boot 时,bootloader 发现它的值是 0x01,知道 flash 中的数据已加密,可以解密后直接使用

    4. 用户需要串口重烧 flash ,于是使用命令行手动烧写 FLASH_CRYPT_CNT,此时2个 bit 被置为 1(取值为0x03)

    5. 重启设备,bootloader 发现 FLASH_CRYPT_CNT 的值是 0x03(2 bit 1),于是重新加密 flash 数据,加密完成后 bootloader 将 FLASH_CRYPT_CNT 烧写为0x07(3 bit 1),flash 加密正常使用

    6. 用户需要串口重烧 flash ,于是使用命令行手动烧写 FLASH_CRYPT_CNT,此时4个 bit 被置为 1(取值为0x0f)

    7. 重启设备,bootloader 发现 FLASH_CRYPT_CNT 的值是 0x0f(4 bit 1),于是重新加密 flash 数据,加密完成后 bootloader 将 FLASH_CRYPT_CNT 烧写为0x1f(5 bit 1),flash 加密正常使用

    8. 用户需要串口重烧 flash ,于是使用命令行手动烧写 FLASH_CRYPT_CNT,此时6个 bit 被置为 1(取值为0x3f)

    9. 重启设备,bootloader 发现 FLASH_CRYPT_CNT 的值是 0x4f(6 bit 1),于是重新加密 flash 数据,加密完成后 bootloader 将 FLASH_CRYPT_CNT 烧写为0x7f(7 bit 1),flash 加密正常使用

    10. 注意!此时不能再使用命令行烧写 FLASH_CRYPT_CNT,bootloader 读到 FLASH_CRYPT_CNT 为 0xff(8 bit 1)时,会停止后续的 boot。

被加密的数据

  • Bootloader

  • Secure boot bootloader digest(若 Secure Boot 被使能, flash 中会多出这一项, 具体查看“Secure Boot”中“执行过程”的步骤3)

  • Partition table

  • Partition table 中指向的所有 Type 域标记为“app”的部分

  • Partition table 中指向的所有 Flags 域标记为“encrypted”的部分(用于非易失性存储(NVS)部分的 flash 在任何情况下都不会被加密)

哪些方式读到解密后的数据(真实数据)

  • 通过内存管理单元的 flash 缓存读取的 flash 数据都是经过解密后的数据,包括:

    • flash 中的可执行应用程序代码

    • 存储在 flash 中的只读数据

    • 任何通过 API esp\_spi\_flash\_mmap() 读取的数据

    • 由 ROM bootloader 读取的软件 bootloader image 数据

  • 如果调用 API esp\_partition\_read() 读取被加密区域的数据,则读取的 flash 数据是经过解密后的数据

哪些方式读到不解密的数据(无法使用的脏数据)

  • 通过 API esp_spi_flash_read() 读取的数据

  • ROM 中的函数 SPIRead() 读取的数据

软件写入加密数据

  • 调用 API esp\_partition\_write() 时,只有写到被加密的 partition 的数据才会被加密

  • 函数 esp\_spi\_flash\_write() 根据参数 write\_encrypted 是否被设为 true 决定是否对数据加密

  • ROM 函数 esp\_rom\_spiflash\_write\_encrypted() 将加密后的数据写入 flash 中,而 SPIWrite() 将不加密的数据写入到 flash 中

安全启动

概述

  • Secure Boot 的目的是保证芯片只运行用户指定的程序,芯片每次启动时都会验证从 flash 中加载的 partition table 和 app images 是否是用户指定的

  • Secure Boot 中采用 ECDSA 签名算法对 partition table 和 app images 进行签名和验证,ECDSA 签名算法使用公钥/私钥对,秘钥用于对指定的二进制文件签名,公钥用于验证签名

  • 由于 partition table 和 app images 是在软件 bootloader 中被验证的,所以为了防止攻击者篡改软件 bootloader 从而跳过签名验证,Secure Boot 过程中会在 ROM bootloader 时检查软件 bootloader image 是否被篡改,检查用到的 secure boot key 由硬件随机数生成器产生,保存的 efuse 中,对于软件是读写保护的

所用资源

  • ECDSA 算法公钥/私钥对

    • 烧写 flash 前在 PC 端生成

    • 公钥会被编译到 bootloader image 中,软件 bootloader 在执行时会读取公钥,使用公钥验证 flash 中partition table 和 app images 是否是经过相应的私钥签名的

    • 私钥在编译时被用于对 partition table 和 app images 签名,私钥必须被保密好,一旦泄露任何使用此私钥签名的 image 都能通过 boot 时的签名验证

  • secure bootloader key

    • 这是一个 256-bit AES key,在第一次 Secure Boot 时由硬件随机数生成,保存在 efuse 中,软件无法读取

    • 使用此 key 验证软件 bootloader image 是否被修改

执行过程

  1. 编译 bootloader image 时发现 menuconfig 中使能了 secure boot,于是根据 menuconfig 中指定的公钥/秘钥文件路径将公钥编译到 bootloader image 中,bootloader 被编译成支持 secure boot

  2. 编译 partition table 和 app images 时使用秘钥计算出签名,将签名编译到相应的二进制文件中

  3. 芯片第一次 boot 时,软件 bootloader 根据一下步骤使能 secure boot:

    • 硬件产生一个 secure boot key,将这个 key 保存在 efuse 中,利用这个 key、一个随机数 IV 和 bootloader image 计算出 secure digest

    • secure digest 与随机数 IV 保存在 flash 的 0x0 地址,用于在后续 boot 时验证 bootloader image 是否被篡改

    • 若 menuconfig 中选择了禁止 JTAG 中断和 ROM BASIC 中断,bootloader 会将 efuse 中的一些标志位设置为禁止这些中断(强烈建议禁止这些中断)

    • bootloader 通过烧写 efuse 中的 ABS_DONE_0 永久使能 secure boot

  4. 芯片在后面的 boot 中,ROM bootloader 发现 efuse 中的 ABS_DONE_0 被烧写,于是从 flash 的地址 0x0 读取第一次 boot 时保存的 secure digest 和随机数 IV,硬件使用 efuse 中的 secure boot key 、随机数 IV 与当前的 bootloader image 计算当前的 secure digest,若与 flash 中的 secure digest 不同,则 boot 不会继续,否则就执行软件 bootloader。

  5. 软件 bootloader 使用 bootloader image 中保存的公钥对 flash 中的 partition table 和 app images 签字进行验证,验证成功之后才会 boot 到 app 代码中

使用步骤

  1. make menuconfig 选择 “enable secure boot in bootloader”

  2. make menuconfig 设置保存公钥/秘钥对的文件

  3. 生成公钥和秘钥,先执行 “make” 命令,此时由于还没有公钥/秘钥对,所以命令行中会提示生成公钥/秘钥对的命令,按提示执行命令即可。但在产品级使用中,建议使用 openssl 或者其他工业级加密程序生成公钥/秘钥对。例如使用 openssl:“openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key.pem”(若使用现有的公钥/秘钥对文件,可以跳过此步)

  4. 运行命令 “make bootloader” 产生一个使能 secure boot 的 bootloader image

  5. 执行完4后命令行会提示下一步烧写 bootloader image 的命令,按提示烧写即可

  6. 运行命令 “make flash” 编译并烧写 partition table 和 app images

  7. 重启芯片,软件 bootloader 会使能 secure boot ,查看串口打印确保 secure boot 成功启用。

注意事项

  • 正常使用情况下, bootloader image 只能烧写一次,partition table 和 app images 可以重复烧写

  • 秘钥必须保密,一旦泄露 secure boot 将失去作用

  • 用于 OTA 的 image 必须进行秘钥签名,OTA 时会使用公钥进行验证

  • 在默认设置下, bootloader的从0x1000地址开始,最大长度为 28KB(bootloader)。如果发现 Secure Boot 发送错误, 请先检查是否因为 Bootloader 地址过大。 通过在menuconfig中调整Bootloader的log等级,可以有效降低编译后的Bootloader大小。

可重复烧写 bootloader

  • 默认情况下 bootloader image 只能烧写一次,在产品中强烈建议这样做,因为 bootloader image 可以重新烧写的情况下可以通过修改 bootloader 跳过后续 image 的验证过程,这样 secure boot 就失去作用

  • 可重复烧写 bootloader 模式下,secure bootloader key 是在 PC 端产生的,此 key 必须保密,一旦 key 被泄露,其它使用此 key 生成digest 的 bootloader image 也能通过硬件检查

  • 使用步骤:

    1. make menuconfig 中选择 “secure bootloader mode”->”Reflashable”

    2. 按“使用步骤”一节步骤2和3生成公钥与秘钥

    3. 运行指令 “make bootloader” ,一个 256-bit secure boot key 会根据用于签名的私钥计算出,命令行会打印两个后续步骤,按循序执行:

      • 将 PC 端生成的 secure boot key 烧入 efuse 中的命令

      • 将编译好的带有预计算出的 secure digest 的 bootloader image 烧写到 flash 中

    4. 从“使用步骤”一节的步骤6继续执行

Secure Boot 与 Flash Encryption 流程图

  • 第一次 boot 时 secure boot 与 flash encrypt 的生效过程如下图所示,图中蓝色框是 secure boot 的步骤,绿色框是 flash encrypt 的步骤

_images/secure_encrypt_first_boot.png
  • 后续 boot 时流程图如下,图中绿色框中的步骤会执行解密,解密是由硬件自动完成的

_images/secure_encrypt_subsequent_boot.png

开发阶段使用可重复烧写 flash 的 Secure Boot 与 Flash encryption

  1. make menuconfig 中使能 secure boot 和 flash encrypt,“Secure bootloader mode”选择“Reflashable”,并设置你的公钥/私钥.pem文件路径

  2. 编译 bootloader 并生成 secure boot key:

    make bootloader
    
  3. 使用 key 和 bootloader 计算带 digest 的 bootloader

    python $IDF_PATH/components/esptool_py/esptool/espsecure.py digest_secure_bootloader --keyfile ./build/bootloader/secure_boot_key.bin -o ./build/bootloader/bootloader_with_digest.bin ./build/bootloader/bootloader.bin
    
  4. 编译 partition_table 与 app

    make partition_table
    make app
    
  5. 加密三个 bin 文件

    python $IDF_PATH/components/esptool_py/esptool/espsecure.py encrypt_flash_data --keyfile flash_encrypt_key.bin --address 0x0 -o build/bootloader/bootloader_digest_encrypt.bin build/bootloader/bootloader_with_digest.bi
    python $IDF_PATH/components/esptool_py/esptool/espsecure.py encrypt_flash_data --keyfile flash_encrypt_key.bin --address 0x8000 -o build/partitions_singleapp_encrypt.bin build/partitions_singleapp.bin
    python $IDF_PATH/components/esptool_py/esptool/espsecure.py encrypt_flash_data --keyfile flash_encrypt_key.bin --address 0x10000 -o build/iot_encrypt.bin build/iot.bin
    
  6. 烧写三个加密后的 bin 文件

    python $IDF_PATH/components/esptool_py/esptool/esptool.py --baud 1152000 write_flash 0x0 build/bootloader/bootloader_digest_encrypt.bin
    python $IDF_PATH/components/esptool_py/esptool/esptool.py --baud 1152000 write_flash 0x8000 build/partitions_singleapp_encrypt.bin
    python $IDF_PATH/components/esptool_py/esptool/esptool.py --baud 1152000 write_flash 0x10000 build/iot_encrypt.bin
    
  7. 将 flash_encryption_key 烧入 efuse (仅在第一次boot前烧写):

    python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_key flash_encryption flash_encrypt_key.bin
    
  8. 将 secure boot key 烧入efuse(仅在第一次boot前烧写):

    python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_key secure_boot ./build/bootloader/secure_boot_key.bin
    
  9. 烧写 efuse 中的控制标志(仅在第一次boot前烧写)

    python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_efuse ABS_DONE_0
    python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_efuse FLASH_CRYPT_CNT
    python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_efuse FLASH_CRYPT_CONFIG 0xf
    python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_efuse DISABLE_DL_ENCRYPT
    python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_efuse DISABLE_DL_DECRYPT
    python $IDF_PATH/components/esptool_py/esptool/espefuse.py burn_efuse DISABLE_DL_CACHE
    

启用安全加密的生产方案

Windows 平台的下载工具

  • 乐鑫提供windows平台的下载工具,能够在工厂生产环境中批量烧写固件

  • 生产下载工具的配置文件在 configure 文件夹内,涉及安全特性的配置在 security.conf 中,目前涉及的配置内容如下表:

ITEM

Function

default

debug_enable

是否开启debug模式,在debug模式下,工具会根据pem文件产生相同密钥,否则随机生成密钥

True

debug_pem_path

设置证书地址,用于生成可重复烧写的密钥,尽在debug模式下有效

SECURE BOOT

secure_boot_en

开启secure boot功能

False

burn_secure_boot_key

使能secure boot key烧写

False

secure_boot_force_write

是否不检查secure boot key block,强制烧写key

False

secure_boot_rw_protect

开启secure boot key区域的读写保护

False

FLASH ENCRYPTION

flash_encryption_en

开启flash加密功能

False

burn_flash_encryption_key

使能flash encrypt key烧写

False

flash_encrypt_force_write

是否不检查flash encrypt key block,强制烧写key

False

flash_encrypt_rw_protect

开启flash encrypt key区域的读写保护

False

AES KEY

Not used yet

DISABLE FUNC

jtag_disable

是否关闭JTAG调试功能

False

dl_encrypt_disable

是否关闭下载模式下flash加密功能

False

dl_decrypt_disable

是否关闭下载模式下flash解密功能

False

dl_cache_disable

是否关闭下载模式下的flash cache功能

False

  • 下载工具的内部逻辑和流程如下:

_images/download_tool_flow_chart.png

操作步骤

准备工作
  • 安装eptool

    • esptool默认路径在$IDF_PATH/components/esptool_py/esptool/

    • 也可以通过python安装:

      pip install esptool
      或者
      pip3 install esptool
      
方案1: 通过 bootloader 完成 security 特性初始化
  • 优势:可以批量进行 flash 烧录,初始化的固件相同,密钥在第一次上电有在设备内随机生成。

  • 缺陷:设备在首次初始化过程所用时间较长,如果在首次初始化过程发生掉电等意外情况,设备可能无法正常启动。

  • 由芯片端自动随机生成 secure boot 与 flash encrypton 密钥,并写入芯片 efuse 中, 密钥写入后,对应的 efuse block 会被设置为读写保护状态,软件与工具都无法读取出密钥。

  • 所有编译出的 images 都按正常情况烧写,芯片会在第一次 boot 时进行配置。

  • 通过 make menuconfig 配置 secure boot 和 flash encryption,按照第一、二节介绍的步骤执行即可,具体操作步骤如下,如果了解第一、二节的内容,可以跳过:

    1. 随机生成RSA密钥文件:

    espsecure.py generate_signing_key secure_boot_signing_key.pem
    or
    openssl ecparam -name prime256v1 -genkey -noout -out secure_boot_signing_key.pem
    
    1. 在 menuconfig 中,选择 Sign binaries during build,并指定刚才生成的密钥路径, 如下图。

    _images/menuconfig_02.png
    1. 分别编译bootloader与应用代码

      make bootloader
      make
      
    2. 使用 esptool 将编译生成的bin文件写入flash对应地址,以example中hellow-world工程为例:

      bootloader.bin -->   0x1000
      partition.bin  -->   0x8000
      app.bin        -->  0x10000
      python $IDF_PATH/components/esptool_py/esptool/esptool.py --chip esp32 --port /dev/cu.SLAB_USBtoUART --baud 1152000 --before default_reset --after no_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 $IDS_PATH/esp-idf/examples/get-started/hello_world/build/bootloader/bootloader.bin 0xf000 $IDF_PATH/esp-idf/examples/get-started/hello_world/build/phy_init_data.bin 0x10000 $IDF_PATH/examples/get-started/hello_world/build/hello-world.bin 0x8000 $IDF_PATH/examples/get-started/hello_world/build/partitions_singleapp.bin
      

    Note

    以上命令仅是示例代码,请在使用时,替换其中的文件路径以及所选参数,包括串口、波特率、SPI 模式和频率等。

    1. 我们也可以使用 window 平台的下载工具来完成工厂下载。需要在配置文件中,关闭工具的 security 功能,这样工具端就不会操作 security 相关特性,完全由硬件和 bootloader 来完成初始化:

      [SECURE BOOT]
      secure_boot_en * False
      [FLASH ENCRYPTION]
      flash_encryption_en * False
      

    Note

    修改并保存参数前,请先关闭下载工具,配置文件修改完成并保存后,再开启运行下载工具。

    _images/download_frame_02.png
    1. 或者我们可以通过下载工具的 combine 功能,将多个 bin 文件打包为一个文件,再由工厂 flash 烧录器烧录进 flash 进行批量生产。

      • 选择bin文件并制定 flash 中的地址

      • 选中 ‘DoNotChgBin’ 选项,这样工具不会对bin文件的配置(SPI模式速率等)进行任何修改。

      • 点击 ‘CombineBin’ 按键,生产合并后的bin文件。

      • 在 ‘combine’ 文件夹下,生成 target.bin,将其烧写到 Flash 的 0x0 地址即可。

      • 工具只会对填写的最大地址范围内的空白区域填充 0xff。并将文件按地址组合。

    _images/combine_01.png
    1. 下载完成后,需要运行一次程序,使 bootloader 完成 security 相关特性的初始化,包括AES密钥的随机生成并写入EFUSE,以及对明文的flash进行首次加密。

    Note

    请误在首次启动完成前,将芯片断电,以免造成芯片无法启动的情况。

  • 注意事项

    • 用于签名的私钥需要保密,如果泄漏,app.bin有被伪造的可能性。

    • 使用者不能遗失私钥,必须使用私钥用于对 OTA app 签名(如果有OTA功能)。

    • 芯片通过软件 bootloader 对 flash 加密是一个比较缓慢的过程,对于较大的 partition 可能需要花费一分钟左右

    • 若第一次执行 bootloader, flash 加密进行到一半芯片掉电

      • 没有使能 secure boot 时,可重新将 images 明文烧写到 flash 中,让芯片下次 boot 时重新加密 flash

      • 使能了 secure boot 时,由于无法重新烧写 flash,芯片将永久无法 boot

方案2: 通过下载工具初始化 security 特性
  • 优势: 工具进行密钥的随机生成,直接将 image 密文烧写进 flash,然后配置 efuse. 避免过程中掉电造成无法启动的情况。

  • 缺陷: 每个设备必须通过下载工具进行烧写,因为密钥不同,无法预先烧写相同的固件到 flash 中。

  • 使用下载工具应用 secure boot 和 flash encryption,这时用户只需要的在 make menuconfig 中选择“enable secure boot in bootloader”并设置公钥/秘钥路径即可

  • 下载工具在运行时,会随机产生 secure boot 与 flash encryption 密钥,并烧写到对应的 EFUSE 位置中。

  • 操作步骤:

    1. 随机生成RSA密钥文件,用于签名固件:

      espsecure.py generate_signing_key secure_boot_signing_key.pem
      or
      openssl ecparam -name prime256v1 -genkey -noout -out secure_boot_signing_key.pem
      
    2. 在 menuconfig 中,选择Sign binaries during build,并指定刚才生成的密钥路径, 如下图。

    _images/menuconfig_02.png
    1. 分别编译 bootloader 与应用代码

      make bootloader
      make
      
    2. 设置下载工具的安全配置文件

      [DEBUG MODE]
      debug_enable * False                #关闭debug模式,工具随机生成密钥。否则根据pem文件产生相同密钥
      debug_pem_path *                    #debug模式下,设置证书地址,用于生成可重复烧写的密钥
      [SECURE BOOT]
      secure_boot_en * True               #开启secure boot功能
      burn_secure_boot_key * True         #使能secure boot key烧写
      secure_boot_force_write * False     #是否不检查secure boot key block,强制烧写key
      secure_boot_rw_protect * True       #开启secure boot key区域的读写保护
      [FLASH ENCRYPTION]
      flash_encryption_en * True          #开启flash加密功能
      burn_flash_encryption_key * True    #使能flash encrypt key烧写
      flash_encrypt_force_write * False   #是否不检查flash encrypt key block,强制烧写key
      flash_encrypt_rw_protect * True     #开启flash encrypt key区域的读写保护
      [AES KEY]
      aes_key_en * False                  #目前未实现,仅保留该选项
      burn_aes_key * False                #目前未实现,仅保留该选项
      [DISABLE FUNC]
      jtag_disable * True                 #是否关闭JTAG调试功能
      dl_encrypt_disable * True           #是否关闭下载模式下flash加密功能
      dl_decrypt_disable * True           #是否关闭下载模式下flash解密功能
      dl_cache_disable * True             #是否关闭下载模式下的flash cache功能
      
      注意:
      修改并保存参数前,请先关闭下载工具,配置文件修改完成并保存后,再开启运行下载工具。
      
    3. 使用下载工具进行下载,若不希望工具修改任何配置参数(比如 flash 频率和模式),请勾选 ‘DoNotChgBin’ 选项。下载工具会更具配置文件的设置,在下载过程中完成固件加密下载和密钥随机生成与烧写。

  • 注意事项:

    • 用于签名的私钥需要保密,如果泄漏,app.bin 有被伪造的可能性。

    • 使用者不能遗失私钥,必须使用私钥用于对 OTA app 签名(如果有 OTA 功能)。

    • 用户可以选择不启用 app image 的签名校验,只需要关闭 menuconfig 中的 secure boot 功能即可。下载工具会更具配置文件,通过 efuse 启用 secure boot。禁用 app image 的签名校验会存在安全隐患。

Other Resources

[中文]

GPIO Expander

[中文]

With further expansions of the ESP32 chip family, more application scenarios with diverse demands are being introduced, including some that have more requirements on GPIO numbers. The subsequence release of ESP32-S2 and other products have included up to 43 GPIOs, which can greatly alleviate the problem of GPIO resource constraint. If this still could not meet your demand, you can also add GPIO expansion chips to ESP32 to have more GPIO resources, such as using the I2C-based GPIO expansion module MCP23017, which can expand 16 GPIO ports per module and mount up to 8 expansion modules simultaneously thus expanding additional 128 GPIO ports totally.

Adapted Products

Name

Function

Bus

Vendor

Datasheet

Driver

MCP23017

16-bit I/O expander

I2C

Microchip

Datasheet

mcp23017

ADC Range Extension Solution

[中文]

ESP32-S3 ADC Range Extension

The maximum effective range of the ESP32-S3 ADC is 0 ~ 3100 mV. Through the external voltage divider circuit, it can meet most of the functions such as the ADC button or battery voltage detection. However, for applications such as NTC (Negative Temperature Coefficient) based temperature measurement, it may need to support full-scale (0 ~ 3300 mV) measurement. ESP32-S3 can adjust the ADC offset through registers, and combined with the nonlinear compensation method of the high voltage area, the expansion of the ADC range can be implemented.

The process is as follows:

  1. Measure the first voltage value using the default offset

  2. If the measured voltage is less than 2900 mV, the first voltage is directly output as the measurement result

  3. Else if the measured voltage is greater than 2900 mV, increase the offset value to take the secondary measurement. Then carried out the nonlinear correction calculation on the secondary value, will be output as the final measurement result.

  4. Restore the offset value once measurement is completed

Overall, during each ADC measurement, there will be 1-2 times ADC reading. For most application scenarios, the measurement delay introduced by this scheme is negligible.

Patch Use Guide

At present, the patch file is developed based on ESP-IDF release/v4.4 branch:

  1. Please make sure ESP-IDF has been checked out to the release/v4.4 branch

  2. Please download file esp32s3_adc_range_to_3100.patch to anywhere you want

  3. Using command git am --signoff < esp32s3_adc_range_to_3100.patch to apply the patch to ESP-IDF

API Guide

  1. To get the range expansion result, users must directly use esp_adc_cal_get_voltage to get the voltage of ADC1 or ADC2.

  2. Other APIs of ESP-IDF ADC are not affected, and the read results are consistent with the default results

Contributions Guide

We welcome contributions to the esp-iot-solution project!

How to Contribute

Contributions to esp-iot-solution - fixing bugs, adding features, adding documentation - are welcome. We accept contributions via Github Pull Requests.

Before Contributing

Before sending us a Pull Request, please consider this list of points:

  • Is the contribution entirely your own work, or already licensed under an Apache License 2.0 compatible Open Source License? If not then we unfortunately cannot accept it.

  • Does any new code conform to the esp-idf : Style Guide ?

  • Does the code documentation follow requirements in Documenting-code ?

  • Is the code adequately commented for people to understand how it is structured?

  • Are comments and documentation written in clear English, with no spelling or grammar errors?

  • If the contribution contains multiple commits, are they grouped together into logical changes (one major change per pull request)? Are any commits with names like “fixed typo” squashed into previous commits?

  • If you’re unsure about any of these points, please open the Pull Request anyhow and then ask us for feedback.

Pull Request Process

After you open the Pull Request, there will probably be some discussion in the comments field of the request itself.

Once the Pull Request is ready to merge, it will first be merged into our internal git system for in-house automated testing.

If this process passes, it will be merged onto the public github repository.