From 5a821a751897d7031a65da08cbab5f6e41789259 Mon Sep 17 00:00:00 2001 From: randy197 <95954158+randy197@users.noreply.github.com> Date: Sun, 26 Jul 2026 19:03:15 +0930 Subject: [PATCH 1/3] Update to work with newer version of tools --- CCLoader/src/CCLoader.ino | 82 +++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/CCLoader/src/CCLoader.ino b/CCLoader/src/CCLoader.ino index def5263..75f6388 100644 --- a/CCLoader/src/CCLoader.ino +++ b/CCLoader/src/CCLoader.ino @@ -162,7 +162,7 @@ unsigned char read_debug_byte(void) if(HIGH == digitalRead(DD)) { data |= 0x01; - } + } digitalWrite(DC, LOW); // DC low } return data; @@ -520,27 +520,34 @@ void ProgrammerInit(void) digitalWrite(LED, LOW); } -void setup() -{ - ProgrammerInit(); +void setup() +{ + ProgrammerInit(); Serial.begin(115200); - // If using Leonado as programmer, + // If using Leonado as programmer, //it should add below code,otherwise,comment it. while(!Serial); } -void loop() +void loop() { unsigned char chip_id = 0; unsigned char debug_config = 0; unsigned char Continue = 0; unsigned char Verify = 0; - + + // Drop any stale bytes (e.g. boot-time noise) sitting in the RX buffer + // ahead of a real handshake attempt from the host. + while(Serial.available()) Serial.read(); + while(!Continue) // Wait for starting - { - - if(Serial.available()==2) - { + { + + if(Serial.available()>=2) // was strictly ==2: a single stray byte + // ahead of the real handshake could make + // this condition never trigger, hanging + // forever. + { if(Serial.read() == SBEGIN) { Verify = Serial.read(); @@ -555,43 +562,43 @@ void loop() debug_init(); chip_id = read_chip_id(); - if(chip_id == 0) + if(chip_id == 0) { - Serial.write(ERRO); + Serial.write(ERRO); return; // No chip detected, run loop again. } - + RunDUP(); debug_init(); - + chip_erase(); RunDUP(); debug_init(); - + // Switch DUP to external crystal osc. (XOSC) and wait for it to be stable. // This is recommended if XOSC is available during programming. If // XOSC is not available, comment out these two lines. write_xdata_memory(DUP_CLKCONCMD, 0x80); while (read_xdata_memory(DUP_CLKCONSTA) != 0x80);//0x80) - + // Enable DMA (Disable DMA_PAUSE bit in debug configuration) debug_config = 0x22; debug_command(CMD_WR_CONFIG, &debug_config, 1); - + // Program data (start address must be word aligned [32 bit]) Serial.write(SRSP); // Request data blocks - digitalWrite(LED, HIGH); + digitalWrite(LED, HIGH); unsigned char Done = 0; unsigned char State = WAITING; - unsigned char rxBuf[514]; + unsigned char rxBuf[514]; unsigned int BufIndex = 0; unsigned int addr = 0x0000; while(!Done) { while(Serial.available()) { - unsigned char ch; - ch = Serial.read(); + unsigned char ch; + ch = Serial.read(); switch (State) { // Bootloader is waiting for a new block, each block begin with a flag byte @@ -606,15 +613,15 @@ void loop() Done = 1; // Exit while(1) in main function } break; - } - // Bootloader is receiving block data + } + // Bootloader is receiving block data case RECEIVING: - { + { rxBuf[BufIndex] = ch; - BufIndex++; + BufIndex++; if (BufIndex == 514) // If received one block, write it to flash { - BufIndex = 0; + BufIndex = 0; uint16_t CheckSum = 0x0000; for(unsigned int i=0; i<512; i++) { @@ -624,43 +631,42 @@ void loop() if(CheckSum_t != CheckSum) { State = WAITING; - Serial.write(ERRO); + Serial.write(ERRO); chip_erase(); return; - } - write_flash_memory_block(rxBuf, addr, 512); // src, address, count + } + write_flash_memory_block(rxBuf, addr, 512); // src, address, count if(Verify) { unsigned char bank = addr / (512 * 16); unsigned int offset = (addr % (512 * 16)) * 4; unsigned char read_data[512]; - read_flash_memory_block(bank, offset, 512, read_data); // Bank, address, count, dest. - for(unsigned int i = 0; i < 512; i++) + read_flash_memory_block(bank, offset, 512, read_data); // Bank, address, count, dest. + for(unsigned int i = 0; i < 512; i++) { - if(read_data[i] != rxBuf[i]) + if(read_data[i] != rxBuf[i]) { // Fail State = WAITING; - Serial.write(ERRO); + Serial.write(ERRO); chip_erase(); return; } } } - addr += (unsigned int)128; + addr += (unsigned int)128; State = WAITING; Serial.write(SRSP); } break; - } + } default: break; } } } - + digitalWrite(LED, LOW); RunDUP(); } - From 798925604a4a423107fdfa41c68b51dbc6e37c03 Mon Sep 17 00:00:00 2001 From: randy197 <95954158+randy197@users.noreply.github.com> Date: Sun, 26 Jul 2026 19:04:41 +0930 Subject: [PATCH 2/3] Enhance RS232 communication with timestamps Added timestamp functions and improved error handling for RS232 communication. --- SourceCode/Linux/main.c | 72 +++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/SourceCode/Linux/main.c b/SourceCode/Linux/main.c index b6755a1..c229a00 100644 --- a/SourceCode/Linux/main.c +++ b/SourceCode/Linux/main.c @@ -12,6 +12,7 @@ extern "C" { #include #include #include +#include int RS232_OpenComport(int, int); int RS232_PollComport(int, unsigned char *, int); @@ -54,13 +55,28 @@ char comports[30][16]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3","/dev void ProcessProgram(void); +static long now_ms(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +/* Timestamp (ms) of the last byte we received, used to detect a stalled + * handshake and retry instead of hanging forever - the board never re-sends + * SBEGIN on its own if the first attempt is dropped. */ +static long last_rx_ms = 0; +static int sbegin_retries = 0; +#define SBEGIN_RETRY_INTERVAL_MS 1000 +#define SBEGIN_MAX_RETRIES 10 + /* * argv[0]----.exe file name * argv[1]----ComPort number * argv[2]----file path */ int main(int arg, char *argv[]) -{ +{ int fLen = 0; int device = 0; @@ -111,7 +127,7 @@ int main(int arg, char *argv[]) char form[5] = ".bin"; char format[5] = " "; fLen = strlen(argv[2]); - if(fLen < 5) + if(fLen < 5) { printf("File path is invalid!\n"); return 0; // file path is not valid @@ -119,7 +135,7 @@ int main(int arg, char *argv[]) format[3] = argv[2][fLen-1]; format[2] = argv[2][fLen-2]; format[1] = argv[2][fLen-3]; - format[0] = argv[2][fLen-4]; + format[0] = argv[2][fLen-4]; if(0 != strcmp(form, format)) { printf("File format must be .bin"); @@ -147,10 +163,19 @@ int main(int arg, char *argv[]) { BlkTot = fsize / 512; } - + printf("Block total: %d\n", BlkTot); BlkNum = 0; + /* Opening the port (RS232_OpenComport() above) and toggling DTR/RTS can + * trigger a reset on ESP8266/NodeMCU-style auto-reset boards. Give the + * board a moment to finish rebooting and discard any boot-time noise + * before starting the handshake, instead of racing it. */ + printf("Waiting for device reset to settle...\n"); + fflush(stdout); + usleep(2000000); /* 2s */ + tcflush(Cport[com], TCIFLUSH); + printf("Enable transmission...\n"); unsigned char buf[2] = {SBEGIN, 0}; // Enable transmission, do not verify if(RS232_SendBuf(com, buf, 2) != 2) @@ -166,10 +191,38 @@ int main(int arg, char *argv[]) { printf("Request sent already! Waiting for respond...\n"); } - + last_rx_ms = now_ms(); + while(!end) { ProcessProgram(); + + /* The board never re-sends SBEGIN on its own, so if the first + * attempt is dropped the original code just polled forever with no + * feedback. Resend periodically, then give up cleanly instead of + * hanging indefinitely. */ + if(!DownloadProgress && !end) + { + long t = now_ms(); + if(t - last_rx_ms > SBEGIN_RETRY_INTERVAL_MS) + { + sbegin_retries++; + if(sbegin_retries > SBEGIN_MAX_RETRIES) + { + printf("\nNo response after %d attempts (%d ms) - giving up.\n" + "Check the port/wiring, and that the device is running " + "(not stuck in its bootloader - reset it with esptool.py " + "if needed).\n", + sbegin_retries, SBEGIN_RETRY_INTERVAL_MS * SBEGIN_MAX_RETRIES); + fclose(pfile); + RS232_CloseComport(com); + return 1; + } + RS232_SendBuf(com, buf, 2); + last_rx_ms = t; + } + } + usleep(2000); /* don't busy-spin the CPU at 100% while polling */ } printf("Program successfully!\n"); BlkNum = 0; @@ -189,6 +242,8 @@ void ProcessProgram() len = RS232_PollComport(com, &rx, 1); if(len > 0) { + last_rx_ms = now_ms(); + sbegin_retries = 0; switch(rx) { case SRSP: @@ -202,7 +257,7 @@ void ProcessProgram() else { if(BlkNum == 0) - { + { printf("Begin programming...\n"); } DownloadProgress = 1; @@ -223,7 +278,7 @@ void ProcessProgram() { fread(buf+1, 512, 1, pfile); } - + unsigned short CheckSum = 0x0000; //unsigned int i; @@ -233,7 +288,7 @@ void ProcessProgram() } buf[513] = (CheckSum >> 8) & 0x00FF; buf[514] = CheckSum & 0x00FF; - + RS232_SendBuf(com, buf, 515); BlkNum++; printf("%d ", BlkNum); @@ -535,4 +590,3 @@ void RS232_disableRTS(int comport_number) #ifdef __cplusplus } /* extern "C" */ #endif - From 67b5db92e3b03a95d88e527c4d0da5c8166956b9 Mon Sep 17 00:00:00 2001 From: randy197 <95954158+randy197@users.noreply.github.com> Date: Sun, 26 Jul 2026 19:09:23 +0930 Subject: [PATCH 3/3] Update README with known issue and workaround Added a known issue regarding CCLoader hanging after USB reconnect and provided a workaround using esptool for ESP8266/NodeMCU boards. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0c1e4ef..c751754 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,10 @@ CCLoader Burn CC25xx/HM10 firmware using a NodeMCU or Wemos D1 mini. +Known issue: CCLoader hangs with no response after a cold USB reconnect. +========================== +On ESP8266/NodeMCU-style boards (e.g. Wemos D1 Mini), the DTR/RTS auto-reset circuit that resets the chip when the serial port opens doesn't always reliably boot it back into normal run mode after a full USB unplug/replug — it can leave the chip stuck in its ROM bootloader, where it will never respond to CCLoader no matter how long you wait. This is a hardware/driver quirk, not a CCLoader bug, and a plain reset button press doesn't fix it either. The fix is to let esptool perform its own (more robust) reset handling once before running CCLoader: run `esptool --port /dev/ttyXXX chip_id` (a quick, read-only command) after any reconnect, then run CCLoader normally. This is only needed after a physical reconnect/power-cycle — it's not required right after flashing, since the upload process already leaves the board in a working state. + Flashing CC2530 or CC2531 ========================== Use the files provided in folder [`Bin`](/Bin). The BIN files are already converted and ready to flash with CCLoader