Compare commits
13 Commits
78a85cdb5b
...
9fabddc6c8
Author | SHA1 | Date |
---|---|---|
King Kévin | 9fabddc6c8 | |
King Kévin | 81b36f1e9c | |
King Kévin | 804b986573 | |
King Kévin | 77f60304f5 | |
King Kévin | 2a34c37181 | |
King Kévin | 5c0e27b9e2 | |
King Kévin | 8f31c8a740 | |
King Kévin | 64813c7228 | |
King Kévin | d9e7ceffbd | |
King Kévin | b25024e71f | |
King Kévin | d80d4877f5 | |
King Kévin | 24f7828766 | |
King Kévin | 3865612f0c |
97
README.md
97
README.md
|
@ -1,6 +1,48 @@
|
|||
This is a [USB DFU](https://www.usb.org/document-library/device-firmware-upgrade-11-new-version-31-aug-2004) (DFU mode) implementation for [ESP32-S2](https://www.espressif.com/en/products/socs/esp32-s2) based on [tinyUSB](https://docs.tinyusb.org/en/latest/index.html).
|
||||
It allows flashing the firmware using [dfu-util](http://dfu-util.sourceforge.net/).
|
||||
|
||||
background
|
||||
==========
|
||||
|
||||
ESP-ROM
|
||||
-------
|
||||
|
||||
The ESP-S2 comes with a ROM bootloader that already allows you to flash over USB using the serial CDC ACM profile.
|
||||
But this method does not let you restart into the main firmware.
|
||||
The ROM bootloader USB stack even offers DFU capability, and you can flash using dfu-util.
|
||||
But this one also does not let you restart into the main firmware.
|
||||
|
||||
To flash using USB serial (can't restart the device):
|
||||
|
||||
~~~
|
||||
idf.py -p /dev/ttyACM0 flash
|
||||
...
|
||||
WARNING: ESP32-S2 chip was placed into download mode using GPIO0.
|
||||
esptool.py can not exit the download mode over USB. To run the app, reset the chip manually.
|
||||
To suppress this note, set --after option to 'no_reset'.
|
||||
~~~
|
||||
|
||||
And to [flash using DFU](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/dfu.html):
|
||||
|
||||
~~~
|
||||
# generate the DFU binary (not the same as the usual flash binary)
|
||||
idf.py dfu
|
||||
dfu-util --device 303a:0002 --download build/dfu.bin
|
||||
# or
|
||||
idf.py dfu-flash
|
||||
~~~
|
||||
|
||||
Note: after detaching, the bootloader claims for be in runtime mode, but this is still the bootloader, and not the flashed firmware.
|
||||
|
||||
mode switch
|
||||
-----------
|
||||
|
||||
To switch from the ESP32-S2 ROM bootloader back to the firmware flashed over USB, you need to press the reset button.
|
||||
This is cumbersome when developing firmware, and sometimes impossible if the board is encased or installed remotely.
|
||||
|
||||
This USB DFU implementation allows to switch back to runtime mode without pressing a button.
|
||||
The runtime firmware can also reboot into the DFU mode without pressing a button.
|
||||
|
||||
install
|
||||
=======
|
||||
|
||||
|
@ -24,7 +66,7 @@ idf.py -B_build/wemos_s2_mini -DFAMILY=esp32s2 -DBOARD=wemos_s2_mini -DIDF_TARG
|
|||
flash
|
||||
-----
|
||||
|
||||
To flash the compiled fimware:
|
||||
To flash the compiled firmware:
|
||||
|
||||
~~~
|
||||
idf.py -B_build/wemos_s2_mini -DFAMILY=esp32s2 -DBOARD=wemos_s2_mini -DIDF_TARGET=esp32s2 -p /dev/ttyACM0 flash
|
||||
|
@ -37,8 +79,8 @@ esptool.py -p /dev/ttyACM0 --before no_reset --after no_reset --chip esp32s2 wr
|
|||
~~~
|
||||
|
||||
This will flash the DFU firmware to the `factory` partition.
|
||||
The DFU firmware will in turn flash the downladed image onto the [OTA0](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/ota.html) partition.
|
||||
For that a custom [partition table](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/partition-tables.html) is used.
|
||||
The DFU firmware will in turn flash the downloaded image onto the [OTA0](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/system/ota.html) partition.
|
||||
For that, a custom [partition table](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/partition-tables.html) is used.
|
||||
|
||||
config
|
||||
------
|
||||
|
@ -53,10 +95,27 @@ runtime
|
|||
-------
|
||||
|
||||
This firmware is just for the DFU mode.
|
||||
The main application needs to implement the runtime mode seperately.
|
||||
The main application needs to implement the runtime mode separately.
|
||||
|
||||
To switch from runtime to DFU mode during detach, set the boot partition in `otadata` to `factory`, and restart the ESP.
|
||||
|
||||
force DFU
|
||||
---------
|
||||
|
||||
The USB DFU firmware does not act as bootloader.
|
||||
Meaning, the ESP bootloader does not start the USB DFU firmware, which in turn starts the main firmware.
|
||||
The main firmware in the OTA0 partition is directly loaded by the ESP bootloader.
|
||||
Thus is it up to the main firmware to start the DFU mode, as described in `runtime`.
|
||||
|
||||
In case the main firmware is defective, and does not allow to switch back to DFU mode, you can still force booting the USB DFU firmware.
|
||||
For that, boot the ESP ROM bootloader (i.e. download mode), and invalidate the OTA data partition:
|
||||
|
||||
~~~
|
||||
otatool.py --esptool-args after=no_reset_stub --port /dev/ttyACM0 erase_otadata
|
||||
~~~
|
||||
|
||||
When restating the ESP by pressing the reset button, the ESP bootloader will start the firmware which is in the factory partition, which should be the DFU firmware previously flashed.
|
||||
|
||||
design choice
|
||||
=============
|
||||
|
||||
|
@ -94,36 +153,6 @@ Feel free to reuse the 'partitions.csv' file as example.
|
|||
alternatives
|
||||
============
|
||||
|
||||
ESP-ROM
|
||||
-------
|
||||
|
||||
The ESP-S2 comes with a ROM bootloader that already allows you to flash over USB using the serial CDC ACM profile.
|
||||
But this method does not let you restart into the main firmware.
|
||||
The ROM bootloader USB stack even offers DFU capability, and you can flash using dfu-util.
|
||||
But this one also does not let you restart into the main firmware.
|
||||
|
||||
To flash using USB serial (can't restart the device):
|
||||
|
||||
~~~
|
||||
idf.py -p /dev/ttyACM0 flash
|
||||
...
|
||||
WARNING: ESP32-S2 chip was placed into download mode using GPIO0.
|
||||
esptool.py can not exit the download mode over USB. To run the app, reset the chip manually.
|
||||
To suppress this note, set --after option to 'no_reset'.
|
||||
~~~
|
||||
|
||||
And to [flash using DFU](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-guides/dfu.html):
|
||||
|
||||
~~~
|
||||
# generate the DFU binary (not the same as the usual flash binary)
|
||||
idf.py dfu
|
||||
dfu-util --device 303a:0002 --download build/dfu.bin
|
||||
# or
|
||||
idf.py dfu-flash
|
||||
~~~
|
||||
|
||||
Note: after detaching, the bootloader claims for be in runtime mode, but this is still the bootloader, and not the flashed firmware.
|
||||
|
||||
TinyUF2
|
||||
-------
|
||||
|
||||
|
|
|
@ -14,3 +14,15 @@ CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
|
|||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
|
||||
#
|
||||
# UART console
|
||||
#
|
||||
CONFIG_ESP_CONSOLE_UART_NONE=y
|
||||
CONFIG_CONSOLE_UART_NUM=-1
|
||||
|
||||
#
|
||||
# CPU frequency
|
||||
#
|
||||
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ=240
|
||||
|
|
|
@ -87,6 +87,9 @@ void usb_device_task(void* param);
|
|||
// used to download image onto OTA partition
|
||||
esp_ota_handle_t ota_handle = 0;
|
||||
|
||||
// how to program the downloaded data (0 = using OTA tools, 1 = using partition tools)
|
||||
static uint8_t dl_method = 0;
|
||||
|
||||
// download blocks to program
|
||||
static uint8_t dl_block[CFG_TUD_DFU_XFER_BUFSIZE] = {0};
|
||||
static uint16_t dl_block_len = 0;
|
||||
|
@ -99,42 +102,139 @@ static void dl_task(void* arg)
|
|||
esp_err_t rc;
|
||||
while (true) {
|
||||
if (dl_block_len) { // we received a new block
|
||||
// get handle for OTA update
|
||||
if (0 == ota_handle) {
|
||||
const esp_partition_t *ota_part = esp_ota_get_next_update_partition(NULL);
|
||||
if (NULL == ota_part) {
|
||||
ESP_LOGE(TAG, "OTA not found");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_PROG);
|
||||
return;
|
||||
const esp_partition_t *ota_part = esp_ota_get_next_update_partition(NULL);
|
||||
if (NULL == ota_part) {
|
||||
ESP_LOGE(TAG, "OTA not found");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_PROG);
|
||||
continue;
|
||||
}
|
||||
if (0 == dl_method) { // using OTA tools
|
||||
// get handle for OTA update
|
||||
if (0 == ota_handle) {
|
||||
ESP_LOGD(TAG, "init OTA flash");
|
||||
rc = esp_ota_begin(ota_part, OTA_SIZE_UNKNOWN, &ota_handle);
|
||||
if (ESP_OK != rc) {
|
||||
ESP_LOGE(TAG, "init OTA failed");
|
||||
esp_ota_abort(ota_handle);
|
||||
ota_handle = 0;
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_ERASE);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
rc = esp_ota_begin(ota_part, OTA_SIZE_UNKNOWN, &ota_handle);
|
||||
|
||||
// write data to partition
|
||||
rc = esp_ota_write_with_offset(ota_handle, dl_block, dl_block_len, dl_block_num * CFG_TUD_DFU_XFER_BUFSIZE);
|
||||
if (ESP_OK != rc) {
|
||||
ESP_LOGE(TAG, "init OTA failed");
|
||||
ESP_LOGE(TAG, "writing OTA failed");
|
||||
esp_ota_abort(ota_handle);
|
||||
ota_handle = 0;
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_ERASE);
|
||||
return;
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_WRITE);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// write data to partition
|
||||
rc = esp_ota_write_with_offset(ota_handle, dl_block, dl_block_len, dl_block_num * CFG_TUD_DFU_XFER_BUFSIZE);
|
||||
if (ESP_OK != rc) {
|
||||
ESP_LOGE(TAG, "writing OTA failed");
|
||||
esp_ota_abort(ota_handle);
|
||||
ota_handle = 0;
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_WRITE);
|
||||
return;
|
||||
tud_dfu_finish_flashing(DFU_STATUS_OK); // flashing op for download complete without error
|
||||
} else if (1 == dl_method) { // using partition tools
|
||||
// read current data
|
||||
uint8_t flash_block[CFG_TUD_DFU_XFER_BUFSIZE];
|
||||
rc = esp_partition_read(ota_part, dl_block_num * CFG_TUD_DFU_XFER_BUFSIZE, flash_block, dl_block_len);
|
||||
if (ESP_OK != rc) {
|
||||
ESP_LOGE(TAG, "reading range failed");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_CHECK_ERASED);
|
||||
continue;
|
||||
}
|
||||
// check if the data is different and we need to write it
|
||||
if (0 == memcmp(dl_block, flash_block, dl_block_len)) { // data is the same
|
||||
ESP_LOGD(TAG, "data already flashed");
|
||||
dl_block_len = 0; // ready for next block
|
||||
tud_dfu_finish_flashing(DFU_STATUS_OK); // flashing op for download complete without error
|
||||
continue;
|
||||
}
|
||||
// check if the area to be written is already erased
|
||||
bool erased = true;
|
||||
for (uint16_t i = 0; i < dl_block_len && erased; i++) {
|
||||
if (0xff != flash_block[i]) {
|
||||
erased = false;
|
||||
}
|
||||
}
|
||||
// get range when some of it is not erased
|
||||
if (!erased) {
|
||||
#define RANGE_SIZE 4096U // range/page size for the flash
|
||||
// get range data
|
||||
const uint32_t range_start = (dl_block_num * CFG_TUD_DFU_XFER_BUFSIZE) - ((dl_block_num * CFG_TUD_DFU_XFER_BUFSIZE) % RANGE_SIZE);
|
||||
uint8_t range_data[RANGE_SIZE];
|
||||
rc = esp_partition_read(ota_part, range_start, range_data, RANGE_SIZE);
|
||||
if (ESP_OK != rc) {
|
||||
ESP_LOGE(TAG, "reading range failed");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_CHECK_ERASED);
|
||||
continue;
|
||||
}
|
||||
// erase range
|
||||
rc = esp_partition_erase_range(ota_part, range_start, RANGE_SIZE); // erase page (block is smaller than page)
|
||||
if (ESP_OK != rc) {
|
||||
ESP_LOGE(TAG, "erase range failed");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_ERASE);
|
||||
continue;
|
||||
}
|
||||
// write range data before downloaded block
|
||||
rc = esp_partition_write(ota_part, range_start, range_data, dl_block_num * CFG_TUD_DFU_XFER_BUFSIZE - range_start); // write old
|
||||
if (ESP_OK != rc) {
|
||||
ESP_LOGE(TAG, "writing existing range failed");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_WRITE);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
rc = esp_partition_write(ota_part, dl_block_num * CFG_TUD_DFU_XFER_BUFSIZE, dl_block, dl_block_len); // write new data
|
||||
if (ESP_OK != rc) {
|
||||
ESP_LOGE(TAG, "writing range failed");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_WRITE);
|
||||
continue;
|
||||
}
|
||||
ESP_LOGD(TAG, "writing range OK");
|
||||
dl_block_len = 0; // ready for next block
|
||||
tud_dfu_finish_flashing(DFU_STATUS_OK); // flashing op for download complete without error
|
||||
} else { // unknown method
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_TARGET);
|
||||
}
|
||||
|
||||
dl_block_len = 0; // ready for next block
|
||||
tud_dfu_finish_flashing(DFU_STATUS_OK); // flashing op for download complete without error
|
||||
} else { // no new block
|
||||
vTaskDelay(1); // allow other tasks to run (and watchdog reset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// task to complete flashing, used when entering manifestation
|
||||
static void complete_task(void* arg)
|
||||
{
|
||||
esp_err_t rc;
|
||||
|
||||
// finish flashing
|
||||
if (ota_handle) {
|
||||
rc = esp_ota_end(ota_handle);
|
||||
ota_handle = 0;
|
||||
if (ESP_OK != rc) {
|
||||
ESP_LOGE(TAG, "close OTA failed");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_PROG);
|
||||
vTaskDelete(NULL); // close task
|
||||
}
|
||||
}
|
||||
|
||||
// switch to OTA app
|
||||
const esp_partition_t *ota = esp_ota_get_next_update_partition(NULL);
|
||||
esp_app_desc_t ota_desc;
|
||||
esp_ota_get_partition_description(ota, &ota_desc);
|
||||
if (ESP_APP_DESC_MAGIC_WORD == ota_desc.magic_word) {
|
||||
ESP_LOGI(TAG, "set boot to valid app");
|
||||
esp_ota_set_boot_partition(ota);
|
||||
xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_IDLE), 0);
|
||||
tud_dfu_finish_flashing(DFU_STATUS_OK);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "no valid app");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_VERIFY);
|
||||
}
|
||||
|
||||
vTaskDelete(NULL); // close task
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Main
|
||||
//--------------------------------------------------------------------+
|
||||
|
@ -142,12 +242,13 @@ static void dl_task(void* arg)
|
|||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
|
||||
const esp_partition_t *next = esp_ota_get_next_update_partition(NULL);
|
||||
esp_app_desc_t next_desc;
|
||||
esp_ota_get_partition_description(next, &next_desc);
|
||||
|
||||
if (ESP_APP_DESC_MAGIC_WORD == next_desc.magic_word) {
|
||||
ESP_LOGI(TAG, "boot set to valid app");
|
||||
ESP_LOGI(TAG, "set app for next boot");
|
||||
esp_ota_set_boot_partition(next);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "no valid app");
|
||||
|
@ -166,7 +267,7 @@ int main(void)
|
|||
#endif
|
||||
|
||||
// create task to handle download progress
|
||||
xTaskCreate(dl_task, "dl_task", 2 * 1024, NULL, 8, NULL);
|
||||
xTaskCreate(dl_task, "dl_task", 7 * 1024, NULL, 8, NULL);
|
||||
|
||||
ESP_LOGI(TAG, "DFU mode");
|
||||
|
||||
|
@ -250,13 +351,13 @@ void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const* data, u
|
|||
|
||||
ESP_LOGD(TAG, "download, alt=%u block=%u", alt, block_num);
|
||||
|
||||
if (alt > 0) {
|
||||
if (alt > 1) {
|
||||
ESP_LOGW(TAG, "download to invalid alt %u", alt);
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_ADDRESS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == length) { // there is nothing to programm
|
||||
if (0 == length) { // there is nothing to program
|
||||
// finish flashing
|
||||
if (ota_handle) {
|
||||
rc = esp_ota_end(ota_handle);
|
||||
|
@ -271,16 +372,17 @@ void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const* data, u
|
|||
tud_dfu_finish_flashing(DFU_STATUS_OK); // flashing op for download complete without error
|
||||
} else if (length > CFG_TUD_DFU_XFER_BUFSIZE) { // more data than we can handle
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_PROG);
|
||||
} else if (dl_block_len) { // there is already a block to programm
|
||||
} else if (dl_block_len) { // there is already a block to program
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_STALLEDPKT);
|
||||
} else { // all is fine to programm
|
||||
} else { // all is fine to program
|
||||
if (!dl_started) {
|
||||
xTimerChangePeriod(blinky_tm, pdMS_TO_TICKS(BLINK_DOWNLOAD), 0);
|
||||
dl_started = true;
|
||||
}
|
||||
memcpy(dl_block, data, length); // copy data for dl_task
|
||||
dl_method = alt; // remember how to flash
|
||||
dl_block_num = block_num; // remember offset
|
||||
dl_block_len = length; // notitfy dl_task block is ready
|
||||
dl_block_len = length; // notify dl_task block is ready
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,38 +391,9 @@ void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const* data, u
|
|||
// Once finished flashing, application must call tud_dfu_finish_flashing()
|
||||
void tud_dfu_manifest_cb(uint8_t alt)
|
||||
{
|
||||
(void) alt;
|
||||
esp_err_t rc;
|
||||
(void)alt; // not used
|
||||
ESP_LOGI(TAG, "download completed, enter manifestation");
|
||||
blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_IDLE), true, NULL, led_blinky_cb, &blinky_tmdef);
|
||||
|
||||
// finish flashing
|
||||
if (ota_handle) {
|
||||
rc = esp_ota_end(ota_handle);
|
||||
ota_handle = 0;
|
||||
if (ESP_OK != rc) {
|
||||
ESP_LOGE(TAG, "close OTA failed");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_PROG);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// switch to OTA app
|
||||
const esp_partition_t *ota = esp_ota_get_next_update_partition(NULL);
|
||||
esp_app_desc_t ota_desc;
|
||||
esp_ota_get_partition_description(ota, &ota_desc);
|
||||
if (ESP_APP_DESC_MAGIC_WORD == ota_desc.magic_word) {
|
||||
ESP_LOGI(TAG, "boot set to valid app");
|
||||
esp_ota_set_boot_partition(ota);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "no valid app");
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_VERIFY);
|
||||
return;
|
||||
}
|
||||
|
||||
// flashing op for manifest is complete without error
|
||||
// Application can perform checksum, should it fail, use appropriate status such as errVERIFY.
|
||||
tud_dfu_finish_flashing(DFU_STATUS_OK);
|
||||
xTaskCreate(complete_task, "complete_task", 3 * 1024, NULL, 8, NULL);
|
||||
}
|
||||
|
||||
// Invoked when received DFU_UPLOAD request
|
||||
|
|
|
@ -82,7 +82,7 @@ uint8_t const * tud_descriptor_device_cb(void)
|
|||
//--------------------------------------------------------------------+
|
||||
|
||||
// Number of Alternate Interface (each for 1 flash partition)
|
||||
#define ALT_COUNT 1
|
||||
#define ALT_COUNT 2
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -123,7 +123,8 @@ char const* string_desc_arr [] =
|
|||
"CuVoodoo", // 1: Manufacturer
|
||||
"ESP32-S2 DFU", // 2: Product
|
||||
"", // 3: Serials, should use chip ID
|
||||
"ota0", // 4: DFU Partition 1
|
||||
"app (erase all)", // 4: DFU Partition (erase the complete partition, slow)
|
||||
"app (overwrite)", // 5: DFU Partition (just overwrite new data, fast)
|
||||
};
|
||||
|
||||
static uint16_t _desc_str[32];
|
||||
|
@ -136,35 +137,39 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
|
|||
|
||||
size_t chr_count;
|
||||
|
||||
if ( index == 0)
|
||||
{
|
||||
if (index == 0) { // get language (not a string)
|
||||
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||
chr_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else { // get other description (strings)
|
||||
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
|
||||
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
|
||||
|
||||
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
|
||||
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; // check if requested descriptor exists
|
||||
const char* str = string_desc_arr[index]; // load requested string
|
||||
if (3 == index) { // serial
|
||||
// set MAC as serial
|
||||
uint8_t mac[6];
|
||||
esp_read_mac(mac, ESP_MAC_ETH);
|
||||
char usb_serial[13] = {0};
|
||||
snprintf(usb_serial, sizeof(usb_serial), "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
str = usb_serial;
|
||||
}
|
||||
|
||||
const char* str = string_desc_arr[index];
|
||||
|
||||
// Cap at max char
|
||||
// check string length
|
||||
chr_count = strlen(str);
|
||||
if ( chr_count > 31 ) {
|
||||
if ( chr_count > 31 ) { // cap at max char
|
||||
chr_count = 31;
|
||||
}
|
||||
|
||||
// Convert ASCII string into UTF-16
|
||||
for(uint8_t i=0; i<chr_count; i++)
|
||||
// convert ASCII string into UTF-16
|
||||
for(uint8_t i = 0; i < chr_count; i++)
|
||||
{
|
||||
_desc_str[1+i] = str[i];
|
||||
_desc_str[1 + i] = str[i];
|
||||
}
|
||||
}
|
||||
|
||||
// first byte is length (including header), second byte is string type
|
||||
_desc_str[0] = (uint16_t)((((uint16_t)TUSB_DESC_STRING) << 8 ) | (2u*chr_count + 2u));
|
||||
_desc_str[0] = (uint16_t)((((uint16_t)TUSB_DESC_STRING) << 8 ) | (2u * chr_count + 2u));
|
||||
|
||||
return _desc_str;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue