fix(usb-deployer): DTR timing 50ms+100ms, CDC drain, fail-fast guard, retries 20

This commit is contained in:
a2nr 2026-07-19 16:49:27 +07:00
parent ee9563dbd7
commit eb44a83dd5
4 changed files with 53 additions and 9 deletions

View File

@ -26,6 +26,10 @@ static bool read_exact(uint8_t *buf, size_t resp_len, uint32_t timeout_ms)
int n = usb_host_read_cdc(buf + got, resp_len - got, chunk_to); int n = usb_host_read_cdc(buf + got, resp_len - got, chunk_to);
if (n < 0) { if (n < 0) {
ESP_LOGD(TAG, "read_exact: got %d/%d (n=%d)", (int)got, (int)resp_len, n); ESP_LOGD(TAG, "read_exact: got %d/%d (n=%d)", (int)got, (int)resp_len, n);
if (!usb_host_arduino_connected()) {
ESP_LOGE(TAG, "read_exact: device disconnected mid-read");
return false;
}
continue; continue;
} }
if (n > 0) { if (n > 0) {
@ -210,8 +214,15 @@ bool stk500v1_flash_buffer(const uint8_t *buffer, size_t size, uint16_t page_siz
ESP_LOGI(TAG, "Starting flash: %d bytes, page %d", (int)size, page_size); ESP_LOGI(TAG, "Starting flash: %d bytes, page %d", (int)size, page_size);
/* 1. Auto-reset Arduino to (re)enter optiboot. */ /* 1. Auto-reset Arduino to (re)enter optiboot. */
if (!usb_host_arduino_connected()) {
ESP_LOGE(TAG, "flash_buffer: Arduino not connected — abort before reset");
return false;
}
usb_host_reset_arduino(); usb_host_reset_arduino();
/* 1b. Drain any stale bytes left in CDC ringbuffer from prior serial bridge session. */
usb_host_drain_cdc(256, 50);
/* 2. Get sync (retry within optiboot ~1s window). */ /* 2. Get sync (retry within optiboot ~1s window). */
if (!cmd_get_sync()) { if (!cmd_get_sync()) {
return false; return false;

View File

@ -43,8 +43,8 @@
#define ATMEGA328P_PAGE_SIZE 128 /* bytes */ #define ATMEGA328P_PAGE_SIZE 128 /* bytes */
/* Timing / retries */ /* Timing / retries */
#define STK_SYNC_RETRIES 10 /* get_sync attempts (optiboot window ~1s) */ #define STK_SYNC_RETRIES 20 /* was 10 — give cold-boot optiboot more chances */
#define STK_CMD_TIMEOUT_MS 200 /* per-command response timeout */ #define STK_CMD_TIMEOUT_MS 250 /* was 200 — slightly more per-attempt slack */
#define STK_PAGE_TIMEOUT_MS 500 /* prog_page timeout (page write ~4ms) */ #define STK_PAGE_TIMEOUT_MS 500 /* prog_page timeout (page write ~4ms) */
#define STK_LEAVE_TIMEOUT_MS 100 /* leave_progmode (OK may be absent) */ #define STK_LEAVE_TIMEOUT_MS 100 /* leave_progmode (OK may be absent) */

View File

@ -1,5 +1,6 @@
#include <string.h> #include <string.h>
#include "esp_log.h" #include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "usbh_core.h" #include "usbh_core.h"
@ -163,6 +164,23 @@ int usb_host_read_cdc(uint8_t *buf, size_t len, uint32_t timeout_ms)
return ret; /* >=0 bytes, <0 on error/timeout */ return ret; /* >=0 bytes, <0 on error/timeout */
} }
/* Drain up to N bytes from CDC ringbuffer with short timeout. Returns bytes drained. */
size_t usb_host_drain_cdc(size_t max_bytes, uint32_t total_ms)
{
size_t total = 0;
int64_t deadline = esp_timer_get_time() + (int64_t)total_ms * 1000;
uint8_t buf[64];
while (total < max_bytes && esp_timer_get_time() < deadline) {
int n = usb_host_read_cdc(buf, sizeof(buf), 10);
if (n <= 0) break;
total += (size_t)n;
}
if (total > 0) {
ESP_LOGI(TAG, "drained %d stale bytes from CDC", (int)total);
}
return total;
}
void usb_host_set_serial_callback(usb_data_cb_t cb) void usb_host_set_serial_callback(usb_data_cb_t cb)
{ {
serial_callback = cb; serial_callback = cb;
@ -209,19 +227,22 @@ void usb_host_reset_arduino(void)
ESP_LOGW(TAG, "reset_arduino: no device — skipping DTR pulse"); ESP_LOGW(TAG, "reset_arduino: no device — skipping DTR pulse");
return; return;
} }
/* Mirrors avrdude stk500v1 + frontend USBHardwareDeployer.resetArduino.
/* Drive DTR+RTS low to assert RESET (Arduino autoreset circuit). * 100nF/10 RC on Uno R3 (~1ms τ) needs DTR low long enough to pull RESET
* TIOCMSET expects a pointer to uint32_t flags NEVER pass flags * below V_IL reliably across CH340/CP2102 clones.
* cast directly as the pointer (that was bug B10, a NULL+small deref). */ * Sequence: DTR+RTS low (50ms) -> high -> settle 100ms for optiboot. */
uint32_t flags_low = 0; uint32_t flags_low = 0;
usbh_serial_control(serial_dev, USBH_SERIAL_CMD_TIOCMSET, &flags_low); usbh_serial_control(serial_dev, USBH_SERIAL_CMD_TIOCMSET, &flags_low);
vTaskDelay(pdMS_TO_TICKS(1)); ESP_LOGI(TAG, "reset_arduino: DTR/RTS low (asserting RESET)");
vTaskDelay(pdMS_TO_TICKS(50));
uint32_t flags_high = USBH_SERIAL_TIOCM_DTR | USBH_SERIAL_TIOCM_RTS; uint32_t flags_high = USBH_SERIAL_TIOCM_DTR | USBH_SERIAL_TIOCM_RTS;
usbh_serial_control(serial_dev, USBH_SERIAL_CMD_TIOCMSET, &flags_high); usbh_serial_control(serial_dev, USBH_SERIAL_CMD_TIOCMSET, &flags_high);
vTaskDelay(pdMS_TO_TICKS(50)); ESP_LOGI(TAG, "reset_arduino: DTR/RTS high (releasing RESET)");
ESP_LOGI(TAG, "Arduino DTR pulse sent (autoreset)"); vTaskDelay(pdMS_TO_TICKS(100));
ESP_LOGI(TAG, "reset_arduino: DTR pulse done (autoreset, 150ms total)");
} }
void usb_host_set_baud_rate(uint32_t baud) void usb_host_set_baud_rate(uint32_t baud)

View File

@ -54,6 +54,18 @@ bool usb_host_write_cdc(const uint8_t *data, size_t len);
*/ */
int usb_host_read_cdc(uint8_t *buf, size_t len, uint32_t timeout_ms); int usb_host_read_cdc(uint8_t *buf, size_t len, uint32_t timeout_ms);
/**
* @brief Drain up to N stale bytes from CDC ringbuffer with short timeout.
*
* Reads and discards bytes left in the CherryUSB ringbuffer from a prior
* serial bridge session. Returns number of bytes drained.
*
* @param max_bytes Maximum bytes to drain
* @param total_ms Total time budget for draining
* @return number of bytes drained
*/
size_t usb_host_drain_cdc(size_t max_bytes, uint32_t total_ms);
/** /**
* @brief Register callback for incoming CDC serial data (serial bridge). * @brief Register callback for incoming CDC serial data (serial bridge).
*/ */