diff --git a/velxio-deployer-firmware/main/stk500v1.c b/velxio-deployer-firmware/main/stk500v1.c index 51db2b8..8b74b32 100644 --- a/velxio-deployer-firmware/main/stk500v1.c +++ b/velxio-deployer-firmware/main/stk500v1.c @@ -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); if (n < 0) { 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; } 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); /* 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(); + /* 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). */ if (!cmd_get_sync()) { return false; diff --git a/velxio-deployer-firmware/main/stk500v1.h b/velxio-deployer-firmware/main/stk500v1.h index 98d1ee7..cf829ec 100644 --- a/velxio-deployer-firmware/main/stk500v1.h +++ b/velxio-deployer-firmware/main/stk500v1.h @@ -43,8 +43,8 @@ #define ATMEGA328P_PAGE_SIZE 128 /* bytes */ /* Timing / retries */ -#define STK_SYNC_RETRIES 10 /* get_sync attempts (optiboot window ~1s) */ -#define STK_CMD_TIMEOUT_MS 200 /* per-command response timeout */ +#define STK_SYNC_RETRIES 20 /* was 10 — give cold-boot optiboot more chances */ +#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_LEAVE_TIMEOUT_MS 100 /* leave_progmode (OK may be absent) */ diff --git a/velxio-deployer-firmware/main/usb_host.c b/velxio-deployer-firmware/main/usb_host.c index d81a0bc..325bccf 100644 --- a/velxio-deployer-firmware/main/usb_host.c +++ b/velxio-deployer-firmware/main/usb_host.c @@ -1,5 +1,6 @@ #include #include "esp_log.h" +#include "esp_timer.h" #include "freertos/FreeRTOS.h" #include "freertos/task.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 */ } +/* 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) { serial_callback = cb; @@ -209,19 +227,22 @@ void usb_host_reset_arduino(void) ESP_LOGW(TAG, "reset_arduino: no device — skipping DTR pulse"); return; } - - /* Drive DTR+RTS low to assert RESET (Arduino autoreset circuit). - * TIOCMSET expects a pointer to uint32_t flags — NEVER pass flags - * cast directly as the pointer (that was bug B10, a NULL+small deref). */ + /* Mirrors avrdude stk500v1 + frontend USBHardwareDeployer.resetArduino. + * 100nF/10kΩ RC on Uno R3 (~1ms τ) needs DTR low long enough to pull RESET + * below V_IL reliably across CH340/CP2102 clones. + * Sequence: DTR+RTS low (50ms) -> high -> settle 100ms for optiboot. */ uint32_t flags_low = 0; 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; 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) diff --git a/velxio-deployer-firmware/main/usb_host.h b/velxio-deployer-firmware/main/usb_host.h index 9ddb846..67cfb48 100644 --- a/velxio-deployer-firmware/main/usb_host.h +++ b/velxio-deployer-firmware/main/usb_host.h @@ -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); +/** + * @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). */