# Security Features Enablement Workflows

[中文]

## Introduction

When enabling security features on ESP32 SoCs, it is recommended that power supply be uninterrupted. Power failures during this process could cause issues that are hard to debug and, in some cases, may cause permanent boot-up failures.

This guide describes a set of workflows to enable security features on the device with the assistance of an external host machine. These workflows are broken down into various stages, with each stage generating signing/encryption keys on the host machine. This allows for greater chances of recovery in case of power or other failures. Furthermore, these workflows expedites the overall provisioning process via the use of the host machine (e.g., encrypting firmware on the host is quicker than on the device).

## Goals

1. Simplify the traditional workflow for enabling security features with stepwise instructions.

2. Design a more flexible workflow when compared to the traditional firmware-based workflow.

3. Improve reliability by dividing the workflow into small operations.

4. Eliminate dependency on Second Stage Bootloader.

## Prerequisites

- `esptool`: Please make sure the `esptool` has been installed. It can be installed by running:

    pip install esptool

## Scope

- Enable Flash Encryption and Secure Boot v2 Externally

- Enable Flash Encryption Externally

- Enable Secure Boot v2 Externally

- Enable NVS Encryption Externally

## Security Features Enablement

### Enable Flash Encryption and Secure Boot v2 Externally

Important

It is recommended to enable both Flash Encryption and Secure Boot v2 for a production use case.

When enabling the Flash Encryption and Secure Boot v2 together, they must be enabled in the following order:

1. Enable the Flash Encryption feature by following the steps listed in Enable Flash Encryption Externally.

2. Enable the Secure Boot v2 feature by following the steps listed in Enable Secure Boot v2 Externally.

The reason for this particular ordering is that when enabling Secure Boot (SB) v2, it is necessary to keep the SB v2 key readable. To protect the key's readability, the write protection for `RD_DIS` (`ESP_EFUSE_WR_DIS_RD_DIS`) is applied. However, this action poses a challenge when attempting to enable Flash Encryption, as the Flash Encryption (FE) key needs to remain unreadable. This conflict arises because the `RD_DIS` is already write-protected, making it impossible to read protect the FE key.

### Enable Flash Encryption Externally

In this case, all the eFuses related to Flash Encryption are written with help of the espefuse tool. More details about Flash Encryption process can be found in Flash Encryption.

1. Ensure that you have an ESP32-C5 device with default Flash Encryption eFuse settings as shown in Relevant eFuses

   > See how to check ESP32-C5 Flash Encryption Status.

   > At this point, the Flash Encryption must not be already enabled on the chip. Additionally, the flash on the chip needs to be erased, which can be done by running:

   >     esptool --port PORT erase-flash

2. Generate a Flash Encryption key

   > A random Flash Encryption key can be generated by running:

   > If Size of generated AES-XTS key is AES-128 (256-bit key):

   >     espsecure generate-flash-encryption-key my_flash_encryption_key.bin

   > else if Size of generated AES-XTS key is AES-256 (512-bit key):

   >     espsecure generate-flash-encryption-key --keylen 512 my_flash_encryption_key.bin

3. Program the generated Flash Encryption key into the device

   > 1. If you intend to use the Key Manager to store the Flash Encryption key, generate the Key Recovery Information for the Flash Encryption key and store it in the flash memory at the address 0x0 using the command:

   >     esptool --port PORT write-flash 0x0 key_recovery_info.bin

   > After storing the Key Recovery Information in flash memory, you also need to program the `KM_XTS_KEY_LENGTH_256` and the `FORCE_USE_KEY_MANAGER_KEY` eFuses.

   > Warning

   > This action cannot be reverted.

   > Bit 1 of the `FORCE_USE_KEY_MANAGER_KEY` eFuse is used to force using a Key Manager-based XTS-AES key. Once this eFuse is burned, eFuse-based Flash Encryption keys can no longer be used; the device will exclusively use the Key Manager for Flash Encryption key management.

   >     espefuse --port PORT burn-efuse FORCE_USE_KEY_MANAGER_KEY 2

   > The `KM_XTS_KEY_LENGTH_256` eFuse controls the length of the Key-Manager based XTS-AES key. Set this eFuse to 1 to use a 128-bit key, and to 0 to use a 256-bit key.

   > To use a 128-bit key, set the `KM_XTS_KEY_LENGTH_256` eFuse to 1.

   >     espefuse --port PORT burn-efuse KM_XTS_KEY_LENGTH_256 1

   > Otherwise, to use a 256-bit key, set the `KM_XTS_KEY_LENGTH_256` eFuse to 0.

   >     espefuse --port PORT burn-efuse KM_XTS_KEY_LENGTH_256 0

   > 2. To store the Flash Encryption key in the eFuses, run the following commands:

   > Warning

   > This action cannot be reverted.

   >     espefuse --port PORT burn-key BLOCK my_flash_encryption_key.bin KEYPURPOSE

   > where `BLOCK` is a free keyblock between `BLOCK_KEY0` and `BLOCK_KEY5`. And `KEYPURPOSE` is either `XTS_AES_256_KEY_1`, `XTS_AES_256_KEY_2`, `XTS_AES_128_KEY`. See ESP32-C5 Technical Reference Manual for a description of the key purposes.

   > For AES-128 (256-bit key) - `XTS_AES_128_KEY`:

   >     espefuse --port PORT burn-key BLOCK my_flash_encryption_key.bin XTS_AES_128_KEY

   > For AES-256 (512-bit key) - `XTS_AES_256_KEY_1` and `XTS_AES_256_KEY_2`. `espefuse` supports burning both these two key purposes together with a 512-bit key to two separate key blocks via the virtual key purpose `XTS_AES_256_KEY`. When this is used `espefuse` will burn the first 256 bits of the key to the specified `BLOCK` and burn the corresponding block key purpose to `XTS_AES_256_KEY_1`. The last 256 bits of the key will be burned to the first free key block after `BLOCK` and the corresponding block key purpose to `XTS_AES_256_KEY_2`

   >     espefuse --port PORT burn-key BLOCK my_flash_encryption_key.bin XTS_AES_256_KEY

   > If you wish to specify exactly which two blocks are used then it is possible to divide the key into two 256-bit keys, and manually burn each half with `XTS_AES_256_KEY_1` and `XTS_AES_256_KEY_2` as key purposes:

   >     split -b 32 my_flash_encryption_key.bin my_flash_encryption_key.bin
   >     espefuse --port PORT burn-key BLOCK my_flash_encryption_key.bin.aa XTS_AES_256_KEY_1
   >     espefuse --port PORT burn-key BLOCK+1 my_flash_encryption_key.bin.ab XTS_AES_256_KEY_2

4. Burn the `SPI_BOOT_CRYPT_CNT` eFuse

   > If you only want to enable Flash Encryption in Development mode and want to keep the ability to disable it in the future, Update the SPI_BOOT_CRYPT_CNT value in the below command from 7 to 0x1 (not recommended for production).

   >     espefuse --port PORT --chip esp32c5 burn-efuse SPI_BOOT_CRYPT_CNT 7

5. Burn Flash Encryption-related security eFuses as listed below

   > 1. Burn security eFuses

   > Important

   > For production use cases, it is highly recommended to burn all the eFuses listed below.

   > - `DIS_DIRECT_BOOT`: Disable direct boot (legacy SPI boot mode)

   > - `DIS_USB_JTAG`: Disable USB switch to JTAG

   > - `DIS_PAD_JTAG`: Disable JTAG permanently

   > - `DIS_DOWNLOAD_MANUAL_ENCRYPT`: Disable UART bootloader encryption access

   > - `XTS_DPA_PSEUDO_LEVEL`: Enable the pseudo rounds function of the XTS-AES peripheral. The value to be burned in the eFuse can be 1, 2 or 3, denoting the security level. By default ESP-IDF's bootloader configures the value of this eFuse to 1 while enabling flash encryption release mode during boot-up.

   > The respective eFuses can be burned by running:

   >     espefuse burn-efuse --port PORT EFUSE_NAME 0x1

   > Note

   > Please update the `EFUSE_NAME` with the eFuse that you need to burn. Multiple eFuses can be burned at the same time by appending them to the above command (e.g., `EFUSE_NAME VAL EFUSE_NAME2 VAL2`). More documentation about espefuse can be found here.

   > 2. Write protect security eFuses

   > After burning the respective eFuses we need to write_protect the security configurations. It can be done by burning following eFuse

   >     espefuse --port PORT write-protect-efuse DIS_ICACHE

   > Note

   > The write protection of above eFuse also write protects multiple other eFuses, Please refer to the ESP32-C5 eFuse table for more details.

6. Configure the project

   > The bootloader and the application binaries for the project must be built with Flash Encryption release mode with default configurations.

   > Flash Encryption release mode can be set in the menuconfig as follows:

   > - Enable Flash Encryption on boot.

   > - Select release mode (Note that once release mode is selected, the `EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT` eFuse bit will be burned to disable Flash Encryption hardware in ROM download mode).

   > - Select UART ROM download mode (permanently switch to Secure mode (recommended)). This is the default option, and is recommended. It is also possible to change this configuration setting to permanently disable UART ROM download mode, if this mode is not needed.

   > - Select the appropriate bootloader log verbosity.

   > - Save the configuration and exit.

7. Build, Encrypt and Flash the binaries

   > The binaries can be encrypted on the host machine by running:

   >     espsecure encrypt-flash-data --aes-xts --keyfile my_flash_encryption_key.bin --address 0x2000 --output bootloader-enc.bin build/bootloader/bootloader.bin

   >     espsecure encrypt-flash-data --aes-xts --keyfile my_flash_encryption_key.bin --address 0x8000 --output partition-table-enc.bin build/partition_table/partition-table.bin

   >     espsecure encrypt-flash-data --aes-xts --keyfile my_flash_encryption_key.bin --address 0x10000 --output my-app-enc.bin build/my-app.bin

   > Note

   > If secure boot is enabled, perform secure boot signing of the firmware before carrying out the above encryption operation.

   > In the above command, the offsets are used for a sample firmware, and the actual offset for your firmware can be obtained by checking the partition table entry or by running idf.py partition-table. Please note that not all the binaries need to be encrypted, the encryption applies only to those generated from the partitions which are marked as `encrypted` in the partition table definition file. Other binaries are flashed unencrypted, i.e., as a plain output of the build process.

   > The above files can then be flashed to their respective offset using `esptool`. To see all of the command line options recommended for `esptool`, see the output printed when `idf.py build` succeeds.

   > When the application contains the following partition: `otadata` and `nvs_encryption_keys`, they need to be encrypted as well. Please refer to Encrypted Partitions for more details about encrypted partitions.

   > Note

   > If the flashed ciphertext file is not recognized by the ESP32-C5 when it boots, check that the keys match and that the command line arguments match exactly, including the correct offset. It is important to provide the correct offset as the ciphertext changes when the offset changes.

   > The command `espsecure decrypt-flash-data` can be used with the same options (and different input or output files), to decrypt ciphertext flash contents or a previously encrypted file.

8. Secure the ROM download mode

   > Warning

   > Please perform the following step at the very end. After this eFuse is burned, the espefuse tool can no longer be used to burn additional eFuses.

   > Enable security download mode:

   > > - `ENABLE_SECURITY_DOWNLOAD`: Enable secure ROM download mode

   > > The eFuse can be burned by running:

   > >     espefuse --port PORT burn-efuse ENABLE_SECURITY_DOWNLOAD

Important

9. Delete Flash Encryption key on host

   > Once the Flash Encryption has been enabled for the device, the key must be deleted immediately. This ensures that the host can't produce encrypted binaries for the same device going forward. This step is important to reduce the vulnerability of the Flash Encryption key.

#### Flash Encryption Guidelines

- It is recommended to generate a unique Flash Encryption key for each device for production use-cases.

- It is recommended to ensure that the RNG used by host machine to generate the Flash Encryption key has good entropy.

- See Limitations of Flash Encryption for more details.

### Enable Secure Boot v2 Externally

In this workflow we shall use `espsecure` tool to generate signing keys and use the `espefuse` tool to burn the relevant eFuses. The details about the Secure Boot v2 process can be found at Secure Boot v2.

1. Generate Secure Boot v2 Signing Private Key

   > The Secure Boot v2 signing key for the RSA3072 scheme can be generated by running:

   >     espsecure generate-signing-key --version 2 --scheme rsa3072 secure_boot_signing_key.pem

   > The Secure Boot v2 signing key for ECDSA scheme can be generated by running:

   >     espsecure generate-signing-key --version 2 --scheme ecdsa256 secure_boot_signing_key.pem

   > The scheme in the above command can be changed to `ecdsa384` to generate an ecdsa384 private key.

   > A total of 3 keys can be used for Secure Boot v2 at once. These should be computed independently and stored separately. The same command with different key file names can be used to generate multiple Secure Boot v2 signing keys. It is recommended to use multiple keys in order to reduce dependency on a single key.

2. Generate Public Key Digest

   > The public key digest for the private key generated in the previous step can be generated by running:

   >     espsecure digest-sbv2-public-key --keyfile secure_boot_signing_key.pem --output digest.bin

   > In case of multiple digests, each digest should be kept in a separate file.

3. Burn the key digest in eFuse

   > The public key digest can be burned in the eFuse by running:

   >     espefuse --port PORT --chip esp32c5 burn-key BLOCK digest.bin SECURE_BOOT_DIGEST0

   > where `BLOCK` is a free keyblock between `BLOCK_KEY0` and `BLOCK_KEY5`.

   > In case of multiple digests, the other digests can be burned sequentially by changing the key purpose to `SECURE_BOOT_DIGEST1` and `SECURE_BOOT_DIGEST2` respectively.

4. Enable Secure Boot v2

   > Secure Boot v2 eFuse can be enabled by running:

> >     espefuse --port PORT --chip esp32c5 burn-efuse SECURE_BOOT_EN

> In case Secure Boot v2 is enabled with the ECDSA-P384 signature scheme, SHA-384 must be used to calculate the digest of the image. Thus, the following eFuse needs to be burned:

>     espefuse --port PORT --chip esp32c5 burn-efuse SECURE_BOOT_SHA384_EN

5. Burn relevant eFuses

   > 1. Burn security eFuses

   > Important

   > For production use cases, it is highly recommended to burn all the eFuses listed below.

   > - `SOFT_DIS_JTAG`: Disable software access to JTAG peripheral.

   > - `DIS_DIRECT_BOOT`: Disable direct boot (legacy SPI boot mode).

   > - `DIS_USB_JTAG`: Disable USB switch to JTAG.

   > - `DIS_PAD_JTAG`: Disable JTAG permanently.

   > - `SECURE_BOOT_AGGRESSIVE_REVOKE`: Aggressive revocation of key digests, see Aggressive Approach for more details.

   > - `WR_DIS_SECURE_BOOT_SHA384_EN`: Disable writing to the SHA-384 Secure Boot eFuse bit. As this write protection bit is shared with `XTS_DPA_PSEUDO_LEVEL` and `ECC_FORCE_CONST_TIME`, it is recommended to write protect this bit only after configuring all the other shared eFuses.

   > The respective eFuses can be burned by running:

   >     espefuse burn-efuse --port PORT EFUSE_NAME 0x1

   > Note

   > Please update the EFUSE_NAME with the eFuse that you need to burn. Multiple eFuses can be burned at the same time by appending them to the above command (e.g., EFUSE_NAME VAL EFUSE_NAME2 VAL2). More documentation about espefuse can be found here

   > 2. Secure Boot v2-related eFuses

   > 1. Disable the read-protection option:

   > The Secure Boot digest burned in the eFuse must be kept readable otherwise the Secure Boot operation would result in a failure. To prevent the accidental enabling of read protection for this key block, the following eFuse needs to be burned:

   >     espefuse -p $ESPPORT write-protect-efuse RD_DIS

   > Important

   > After burning above-mentioned eFuse, the read protection can't be enabled for any key. For example, if Flash Encryption which requires read protection for its key is not enabled at this point, then it can't be enabled afterwards. Please ensure that no eFuse keys are going to need read protection after completing this step.

   > 2. Revoke key digests:

   > The unused digest slots need to be revoked when we are burning the Secure Boot key. The respective slots can be revoked by running

   >     espefuse --port PORT --chip esp32c5 burn-efuse EFUSE_REVOKE_BIT

   > The `EFUSE_REVOKE_BIT` in the above command can be `SECURE_BOOT_KEY_REVOKE0` or `SECURE_BOOT_KEY_REVOKE1` or `SECURE_BOOT_KEY_REVOKE2`. Please note that only the unused key digests must be revoked. Once revoked, the respective digest cannot be used again.

6. Configure the project

   > By default, the first stage (ROM) bootloader would only verify the Second Stage Bootloader. The second stage bootloader would verify the app partition only when the CONFIG_SECURE_BOOT option is enabled (and CONFIG_SECURE_BOOT_VERSION is set to `SECURE_BOOT_V2_ENABLED`) while building the bootloader.

   > 1. Open the Editing the Configuration, in `Security features` set `Enable hardware Secure Boot in bootloader` to enable Secure Boot.

   > The `Secure Boot v2` option will be selected and the `App Signing Scheme` will be set to RSA by default.

   > 2. Disable the option CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES for the project in the Editing the Configuration. This shall make sure that all the generated binaries are secure padded and unsigned. This step is done to avoid generating signed binaries as we are going to manually sign the binaries using `espsecure` tool.

7. Build, Sign and Flash the binaries

   > After the above configurations, the bootloader and application binaries can be built with `idf.py build` command.

   > The Secure Boot v2 workflow only verifies the `bootloader` and `application` binaries, hence only those binaries need to be signed. The other binaries (e.g., `partition-table.bin`) can be flashed as they are generated in the build stage.

   > The `bootloader.bin` and `app.bin` binaries can be signed by running:

   >     espsecure sign-data --version 2 --keyfile secure_boot_signing_key.pem --output bootloader-signed.bin build/bootloader/bootloader.bin

   >     espsecure sign-data --version 2 --keyfile secure_boot_signing_key.pem --output my-app-signed.bin build/my-app.bin

   > If multiple keys Secure Boot keys are to be used then the same signed binary can be appended with a signature block signed with the new key as follows:

   >     espsecure sign-data --keyfile secure_boot_signing_key2.pem --version 2 --append-signatures -o bootloader-signed2.bin bootloader-signed.bin

   >     espsecure sign-data --keyfile secure_boot_signing_key2.pem --version 2 --append-signatures -o my-app-signed2.bin my-app-signed.bin

   > The same process can be repeated for the third key. Note that the names of the input and output files must not be the same.

   > The signatures attached to a binary can be checked by running:

   >     espsecure signature-info-v2 bootloader-signed.bin

   > The above files along with other binaries (e.g., partition table) can then be flashed to their respective offset using `esptool`. To see all of the command line options recommended for `esptool`, see the output printed when `idf.py build` succeeds. The flash offset for your firmware can be obtained by checking the partition table entry or by running `idf.py partition-table`.

8. Secure the ROM download mode

   > Warning

   > Please perform the following step at the very end. After this eFuse is burned, the espefuse tool can no longer be used to burn additional eFuses.

   > Enable security download mode:

   > - `ENABLE_SECURITY_DOWNLOAD`: Enable secure ROM download mode

   > The eFuse can be burned by running:

   >     espefuse --port PORT burn-efuse ENABLE_SECURITY_DOWNLOAD

#### Secure Boot v2 Guidelines

- It is recommended to store the Secure Boot key in a highly secure place. A physical or a cloud HSM may be used for secure storage of the Secure Boot private key. Please take a look at Remote Signing of Images for more details.

- It is recommended to use all the available digest slots to reduce dependency on a single private key.

### Enable NVS Encryption Externally

The details about NVS encryption and related schemes can be found at NVS Encryption.

#### Enable NVS Encryption Based on HMAC

1. Generate the HMAC key and NVS encryption key

   > In the HMAC based NVS scheme, there are two keys:

   > - HMAC key - this is a 256-bit HMAC key that shall be stored in the eFuse.

   > - NVS Encryption key - This is the NVS encryption key that is used to encrypt the NVS partition. This key is derived at run-time using the HMAC key.

   > The above keys can be generated with the nvs_flash/nvs_partition_generator/nvs_partition_gen.py script with help of the following command:

   >     python3 nvs_partition_gen.py generate-key --key_protect_hmac --kp_hmac_keygen --kp_hmac_keyfile hmac_key.bin --keyfile nvs_encr_key.bin

   > This shall generate the respective keys in the `keys` folder.

2. Burn the HMAC key in the eFuse

   > The NVS key can be burned in the eFuse of ESP32-C5 with help of following command:

   >     espefuse --port PORT burn-key BLOCK hmac_key.bin HMAC_UP

   > Here, `BLOCK` is a free keyblock between `BLOCK_KEY0` and `BLOCK_KEY5`.

3. Generate the encrypted NVS partition

   > We shall generate the actual encrypted NVS partition on the host. More details about generating the encryption NVS partition can be found at Generate Encrypted NVS Partition.For this purpose, the contents of the NVS file shall be available in a CSV file. Please check out CSV File Format for more details.

   > The encrypted NVS partition can be generated with following command:

   >     python3 nvs_partition_gen.py encrypt sample_singlepage_blob.csv nvs_encr_partition.bin 0x3000 --inputkey keys/nvs_encr_key.bin

   > Some command arguments are explained below:

   > - CSV file name - In this case, `sample_singlepage_blob.csv` is the CSV file which contains the NVS data. Please replace this with the file you wish to choose.

   > - NVS partition size - This is the size of the NVS partition in bytes. Please update the sample value of `0x3000` in the above-provided command to the correct size of your NVS partition.

4. Configure the project

   > - Enable NVS Encryption by enabling CONFIG_NVS_ENCRYPTION.

   > - Enable the HMAC based NVS encryption by setting CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME to `CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`.

   > - Set the HMAC efuse key ID at CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID to the one in which the eFuse key was burned in Step 2.

5. Flash NVS partition

   > The NVS partition (`nvs_encr_partition.bin`) generated in Step 3 can then be flashed to its respective offset using `esptool`. To see all of the command line options recommended for `esptool`, check the output printed when `idf.py build` succeeds.

   > If Flash Encryption is enabled for the chip, please encrypt the partition first before flashing. More details please refer to the flashing related steps of Flash Encryption workflow.

#### Enable NVS Encryption Based on Flash Encryption

In this case we generate NVS Encryption keys on a host. This key is then flashed on the chip and protected with the help of Flash Encryption features.

1. Generate the NVS encryption key

   > For generation of respective keys, we shall use NVS partition generator utility. We shall generate the encryption key on host and this key shall be stored on the flash of ESP32-C5 in encrypted state.

   > The key can be generated with the nvs_flash/nvs_partition_generator/nvs_partition_gen.py script with the help of the following command:

   >     python3 nvs_partition_gen.py generate-key --keyfile nvs_encr_key.bin

   > This shall generate the respective key in the `keys` folder.

2. Generate the encrypted NVS partition

   > We shall generate the actual encrypted NVS partition on host. More details about generating the encrypted NVS partition can be found at Generate Encrypted NVS Partition.For this, the contents of the NVS file shall be available in a CSV file. Please refer to CSV File Format for more details.

   > The encrypted NVS partition can be generated with following command:

   >     python3 nvs_partition_gen.py encrypt sample_singlepage_blob.csv nvs_encr_partition.bin 0x3000 --inputkey keys/nvs_encr_key.bin

   > Some command arguments are explained below:

   > - CSV file name - In this case sample_singlepage_blob.csv is the CSV file which contains the NVS data. Please replace it with the file you wish to choose.

   > - NVS partition size - This is the size of the NVS partition in bytes. Please update the sample value of `0x3000` in the above-provided command to the correct size of your NVS partition.

3. Configure the project

   > - Enable NVS Encryption by enabling CONFIG_NVS_ENCRYPTION.

   > - Set NVS to use Flash Encryption based scheme by setting CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME to `CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC`.

4. Flash NVS partition and NVS encryption keys

   > The NVS partition (`nvs_encr_partition.bin`) and NVS encryption key (`nvs_encr_key.bin`) can then be flashed to their respective offset using `esptool`. To see all of the command line options recommended for `esptool`, check the output print when `idf.py build` succeeds.

   > If Flash Encryption is enabled for the chip, then please encrypt the NVS key partition first before flashing. You may refer the flashing related steps of Flash Encryption workflow.

Was this page helpful?

- Thank you! We received your feedback.
- If you have any comments, fill in Espressif Documentation Feedback Form.

- We value your feedback.
- Let us know how we can improve this page by filling in Espressif Documentation Feedback Form.