From f9efdbb19f4a75dc6110f03da8ef8dcb8dcfe034 Mon Sep 17 00:00:00 2001 From: bhavikp-electrobit Date: Wed, 6 May 2026 15:22:13 +0530 Subject: [PATCH 1/6] Add complete technical analysis for T3000 firmware update flow --- FIRMWARE_UPDATE_FLOW.md | 607 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 607 insertions(+) create mode 100644 FIRMWARE_UPDATE_FLOW.md diff --git a/FIRMWARE_UPDATE_FLOW.md b/FIRMWARE_UPDATE_FLOW.md new file mode 100644 index 00000000..52924f5c --- /dev/null +++ b/FIRMWARE_UPDATE_FLOW.md @@ -0,0 +1,607 @@ +# T3000 Firmware Update Flow - Complete Technical Analysis +## Ctrl+F2 / Tools → Load Firmware (Single Device) + +--- + +## 1. ENTRY POINT & MENU DEFINITION + +### Menu Item Definition +- **File**: `T3000/T3000.rc` (Resource File) +- **Menu ID**: `ID_FILE_BATCHBURNHEX` +- **Shortcut**: `Ctrl+F2` +- **Display Text**: "Load firmware for a single device" + +### Message Map Binding +- **Location**: [T3000/MainFrm.cpp](T3000/MainFrm.cpp#L294) +- **Binding**: `ON_COMMAND(ID_FILE_BATCHBURNHEX, OnBatchFlashHex)` + +--- + +## 2. MAIN HANDLER FUNCTION + +### Function: `CMainFrame::OnBatchFlashHex()` +- **Location**: [T3000/MainFrm.cpp](T3000/MainFrm.cpp#L3108) +- **Class**: `CMainFrame` (MFC Application Main Window) + +### Pre-Flash Setup Phase +```cpp +// Step 1: Save current state +b_pause_refresh_tree = BATCH_FLASH_HEX; // Pause tree refresh +bool temp_status = g_bPauseMultiRead; +g_bPauseMultiRead = true; +int temp_type = GetCommunicationType(); // Save communication type (Serial=0 or Network=1) + +// Step 2: Close existing connections +BOOL bDontLinger = FALSE; +setsockopt(h_Broad, SOL_SOCKET, SO_DONTLINGER, (const char*)&bDontLinger, sizeof(BOOL)); +closesocket(h_Broad); // Close broadcast socket +SetCommunicationType(0); // Set to serial mode +close_com(); // Close serial port (free for ISP tool) + +// Step 3: Create firmware update dialog +CFlash_Multy dlg; +dlg.DoModal(); // Modal dialog - blocks until user completes or cancels +``` + +### Post-Flash Cleanup Phase +```cpp +// Step 4: Restore communication +if(temp_type == 0) { + int comport = GetLastOpenedComport(); + open_com(comport); // Reopen serial port +} else { + // Network mode restoration +} + +SetCommunicationType(temp_type); // Restore communication type + +// Step 5: Recreate broadcast socket +h_Broad = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +BOOL bBroadcast = TRUE; +::setsockopt(h_Broad, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof(BOOL)); +int iMode = 1; +ioctlsocket(h_Broad, FIONBIO, (u_long FAR*) &iMode); + +// Step 6: Resume tree refresh +h_bcast.sin_family = AF_INET; +h_bcast.sin_addr.s_addr = INADDR_BROADCAST; +h_bcast.sin_port = htons(UDP_BROADCAST_PORT); +``` + +--- + +## 3. FIRMWARE UPDATE DIALOG + +### Class: `CFlash_Multy` +- **Header File**: [T3000/Flash_Multy.h](T3000/Flash_Multy.h) +- **Implementation**: [T3000/Flash_Multy.cpp](T3000/Flash_Multy.cpp) +- **Base Class**: `CDialogEx` +- **Resource ID**: `IDD_DIALOG_MULTY_FLASH` + +### Dialog Initialization: `OnInitDialog()` +**Location**: [Flash_Multy.cpp:175](T3000/Flash_Multy.cpp#L175) + +**Initialization Steps:** + +1. **Initial_List()** - Populate device list + - Reads device tree + - Inserts devices into list control + - Columns: ID, Product Name, Serial Number, COM Port, Baudrate, IP Address, IP Port + +2. **GetProductType()** - Detect device type + - Identifies product type based on register values + - Types: TSTAT5, TSTAT6, T3 Modules, etc. + +3. **Get_Device_Firmware()** - Read current firmware versions + - Queries each device for current firmware revision + - Displays in "Current Firmware Version" column + +**Data Structure:** +```cpp +typedef struct { + int nitem; + CString nID; + CString devicename; + CString strSN; // Serial Number + CString ncomport; // COM Port + CString nBaudrate; // Baud Rate + CString nIPaddress; // IP Address + CString nipport; // IP Port + CString file_position; // Firmware file path (.hex) + CString config_file_position; // Config file path + int nresult; // Flash result + int cofnigresult; // Config result + bool online; // Device online status + float software_rev; // Current firmware version + float newest_rev; // Latest available version + CString file_rev; // Selected firmware file version + bool need_flash; // Flash needed flag + unsigned char product_id; // Product ID + bool select_status; // User selection checkbox +} Str_flash_device; +``` + +### UI Elements +- **List Control**: `IDC_LIST_FLASH_MULTY` + - Displays all devices in network + - Checkbox for device selection + - Shows current/target firmware versions + - Shows firmware file paths + - Shows config file paths + +- **Buttons**: + - "Select Firmware File" - Browse for .hex file + - "Select Config File" - Browse for config file + - "START" - Begin firmware update + +--- + +## 4. USER INTERACTION PHASE + +### User Selection +1. **Check devices** to update (checkbox column 0) +2. **Select firmware file** (.hex) - typically stored in `Database/Firmware/` +3. **Select configuration file** (optional) - product-specific configs +4. **Click START button** - Triggers flash process + +### Button Handler: `OnBnClickedButtonStatrt()` +**Location**: [Flash_Multy.cpp:789](T3000/Flash_Multy.cpp#L789) + +```cpp +// Validation +if (nflashitemcount == 0) { + MessageBox(_T("Please select one or more items."), _T("Notice"), MB_OK | MB_ICONINFORMATION); + return; +} + +// Build device list into flash_device vector +for (int i = 0; i < ncount; i++) { + if (!m_flash_multy_list.GetCellChecked(i, 0)) + continue; // Skip unchecked devices + + Str_flash_device temp; + temp.nitem = i; + temp.strSN = m_flash_multy_list.GetItemText(i, FLASH_SERIAL_NUMBER); + temp.nID = m_flash_multy_list.GetItemText(i, FLASH_ID); + temp.ncomport = m_flash_multy_list.GetItemText(i, FLASH_COM_PORT); + temp.nBaudrate = m_flash_multy_list.GetItemText(i, FLASH_BAUDRATE); + temp.nIPaddress = m_flash_multy_list.GetItemText(i, FLASH_IPADDRESS); + temp.nipport = m_flash_multy_list.GetItemText(i, FLASH_IPPORT); + temp.devicename = m_flash_multy_list.GetItemText(i, FLASH_PRODUCT_NAME); + temp.file_position = m_flash_multy_list.GetItemText(i, FLASH_FILE_POSITION); + temp.config_file_position = m_flash_multy_list.GetItemText(i, FLASH_CONFIG_FILE_POSITION); + + temp.need_flash = true; + flash_device.push_back(temp); // Add to processing queue +} + +// Create background thread for ISP execution +Call_ISP_Application = CreateThread(NULL, NULL, multy_isp_thread, this, NULL, NULL); +``` + +--- + +## 5. BACKGROUND THREAD - ISP EXECUTION + +### Function: `multy_isp_thread()` +**Location**: [Flash_Multy.cpp:969](T3000/Flash_Multy.cpp#L969) +**Type**: Static thread function (DWORD WINAPI) + +### **PHASE 1: FIRMWARE FLASHING** + +```cpp +for(int i = 0; i < nflashdevicecount; i++) { + if (flash_device.at(i).nresult != OPERATION_SUCCESS) { + if (!flash_device.at(i).file_position.IsEmpty()) { + + // 1. Save configuration to INI file + pParent->SetAutoConfig(flash_device.at(i)); // Write to AutoFlashConfig.ini + + // 2. Post UI update: "Running" (Blue color) + pParent->PostMessage(WM_MULTY_FLASH_MESSAGE, CHANGE_THE_ITEM_COLOR_BLUE, + flash_device.at(i).nitem); + + // 3. Execute ISP.exe external tool + CString MultyISPtool_path = ApplicationFolder + _T("\\ISP.exe"); + WinExecAndWait(MultyISPtool_path, NULL, NULL, 0); + + // 4. Read ISP result from INI file + int nresult = GetPrivateProfileInt(_T("Data"), _T("Command"), + FAILED_UNKNOW_ERROR, AutoFlashConfigPath); + + // 5. Handle result + if (nresult == FLASH_SUCCESS) { + flash_device.at(i).nresult = CHANGE_THE_ITEM_COLOR_GREEN; + pParent->PostMessage(WM_MULTY_FLASH_MESSAGE, CHANGE_THE_ITEM_COLOR_GREEN, + flash_device.at(i).nitem); // Green + Sleep(4000); // Wait for device reboot + } else { + flash_device.at(i).nresult = CHANGE_THE_ITEM_COLOR_RED; + pParent->PostMessage(WM_MULTY_FLASH_MESSAGE, CHANGE_THE_ITEM_COLOR_RED, + flash_device.at(i).nitem); // Red + continue; // Skip config for failed device + } + } + } +``` + +### **PHASE 2: CONFIGURATION FILE LOADING** + +```cpp + // Skip if config file is empty or already applied + if (flash_device.at(i).cofnigresult == 3 || flash_device.at(i).config_file_position.IsEmpty()) + continue; + + // 1. Open communication (Serial OR Network) + if (!flash_device.at(i).ncomport.IsEmpty()) { + // Serial communication + int comport = _wtoi(flash_device.at(i).ncomport); + int baudrate = _wtoi(flash_device.at(i).nBaudrate); + + if (open_com(comport)) { + is_connect_device = TRUE; + Change_BaudRate(baudrate); + SetCommunicationType(0); // Serial mode + } + } else { + // Network communication + CString currentIp = flash_device.at(i).nIPaddress; + int Port = _wtoi(flash_device.at(i).nipport); + + if (Open_Socket2(currentIp, Port)) { + is_connect_device = TRUE; + SetCommunicationType(1); // Network mode + } + } + + if (!is_connect_device) + continue; + + // 2. Detect product type + Read_Multi(now_tstat_id, product_register_value, 0, 10); + int nFlag = product_register_value[7]; + + if (nFlag == PM_TSTAT6 || nFlag == PM_TSTAT7 || nFlag == PM_TSTAT8 || nFlag == PM_TSTAT9) { + product_type = T3000_6_ADDRESS; + } else if (nFlag == PM_TSTAT5E || nFlag == PM_PM5E || nFlag == PM_TSTAT5H) { + product_type = T3000_5EH_LCD_ADDRESS; + } else if (nFlag == PM_TSTAT5A || nFlag == PM_TSTAT5B || nFlag == PM_TSTAT5C) { + product_type = T3000_5ABCDFG_LED_ADDRESS; + } else if (nFlag == PM_T3PT10 || nFlag == PM_T332AI || nFlag == PM_T38AI16O) { + product_type = T3000_T3_MODULES; + } + + // 3. Load configuration file (product-specific handler) + if(config_file.Open(config_file_path, CFile::modeRead | CFile::shareDenyNone)) { + + if (product_type == T3000_6_ADDRESS) { + LoadFile2Tstat67(temppp, (LPTSTR)(LPCTSTR)config_file_path, &log_file); + } else if (product_type == T3000_T3_MODULES) { + LoadFile2Tstat_T3(temppp, (LPTSTR)(LPCTSTR)config_file_path, &log_file); + } else if (product_type == PM_LightingController) { + load_file_2_schedule_LC((LPTSTR)(LPCTSTR)config_file_path, now_tstat_id, log_file); + } else if (product_type == PM_NC) { + load_file_2_schedule_NC((LPTSTR)(LPCTSTR)config_file_path, now_tstat_id, log_file); + } else { + LoadFile2Tstat(temppp, (LPTSTR)(LPCTSTR)config_file_path, &log_file); + } + } + + // 4. Check for write errors + if (g_Vector_Write_Error.size() > 0) { + flash_device.at(i).nresult = CHANGE_THE_ITEM_COLOR_LESS_RED; // Config failed + } else { + flash_device.at(i).nresult = CHANGE_THE_ITEM_COLOR_MORE_GREEN; // Success + } + + // 5. Close communication + if (IS_COM) { + SetCommunicationType(0); + close_com(); + } else { + SetCommunicationType(1); + close_com(); + } + + // 6. Post UI update with result + pParent->PostMessage(WM_MULTY_FLASH_MESSAGE, flash_device.at(i).nresult, + flash_device.at(i).nitem); +} + +// Thread complete +pParent->m_bTstatLoadFinished = TRUE; +return 0; +``` + +--- + +## 6. UI MESSAGE HANDLER + +### Function: `MultyFlashMessage()` +**Location**: [Flash_Multy.cpp:1582](T3000/Flash_Multy.cpp#L1582) +**Message ID**: `WM_MULTY_FLASH_MESSAGE` + +**Parameters:** +- `wParam`: Command/Status code +- `lParam`: Device list item index + +### Status Codes & Colors +```cpp +// Status codes with UI colors +CHANGE_THE_ITEM_COLOR_BLUE = 1; // Running (Blue) +CHANGE_THE_ITEM_COLOR_RED = 2; // Failed (Red) +CHANGE_THE_ITEM_COLOR_GREEN = 3; // Firmware Success (Green) +CHANGE_THE_ITEM_COLOR_DEFAULT = 4; // Clear +CHANGE_THE_ITEM_COLOR_MORE_GREEN = 5; // Firmware + Config Success (Bright Green) +CHANGE_THE_ITEM_COLOR_LESS_RED = 6; // Firmware Success + Config Failed (Light Red) +MASS_FLASH_MESSAGE = 200; // Device already up-to-date + +// Color definitions +#define FLASH_COLOR_BLUE RGB(50,50,180) +#define FLASH_COLOR_RED RGB(255,0,0) +#define FLASH_COLOR_GREEN RGB(0,255,0) +#define CONFIG_COLOR_RED_FAIL RGB(255,86,86) +#define CONFIG_COLOR_CONFIG_FLASH_GOOD RGB(86,120,86) +``` + +### UI Update Logic +```cpp +switch(main_command) { + case CHANGE_THE_ITEM_COLOR_BLUE: + m_flash_multy_list.SetItemTextColor(sub_parameter, FLASH_RESULTS, FLASH_COLOR_BLUE); + m_flash_multy_list.SetItemText(sub_parameter, FLASH_RESULTS, _T("Running")); + break; + + case CHANGE_THE_ITEM_COLOR_GREEN: + m_flash_multy_list.SetItemTextColor(sub_parameter, FLASH_RESULTS, FLASH_COLOR_GREEN); + m_flash_multy_list.SetItemText(sub_parameter, FLASH_RESULTS, _T("Sucessful")); + break; + + case CHANGE_THE_ITEM_COLOR_RED: + m_flash_multy_list.SetItemTextColor(sub_parameter, FLASH_RESULTS, FLASH_COLOR_RED); + m_flash_multy_list.SetItemText(sub_parameter, FLASH_RESULTS, _T("Fail")); + break; + + case CHANGE_THE_ITEM_COLOR_MORE_GREEN: + m_flash_multy_list.SetItemTextColor(sub_parameter, -1, CONFIG_COLOR_CONFIG_FLASH_GOOD); + m_flash_multy_list.SetItemText(sub_parameter, FLASH_CONFIG_RESULTS, _T("Sucessful")); + break; + + case CHANGE_THE_ITEM_COLOR_LESS_RED: + m_flash_multy_list.SetItemTextColor(sub_parameter, -1, CONFIG_COLOR_RED_FAIL); + m_flash_multy_list.SetItemText(sub_parameter, FLASH_RESULTS, _T("Sucessful")); + m_flash_multy_list.SetItemText(sub_parameter, FLASH_CONFIG_RESULTS, _T("Fail")); + break; +} +``` + +--- + +## 7. EXTERNAL ISP TOOL + +### ISP.exe Application +- **Type**: External executable +- **Location**: Typically in `Application Folder\ISP.exe` +- **Purpose**: Writes .hex firmware directly to device hardware +- **Communication**: Via serial port or network (Modbus/BACnet protocol) + +### Configuration via INI File +- **Config File Path**: `AutoFlashConfigPath` +- **INI Sections**: + - `[Data]` - Flash commands and results + - `[Device]` - Target device info (serial number, IP, port) + - `[Firmware]` - .hex file path and parameters + +### ISP Result Codes +```cpp +#define FLASH_SUCCESS 0 // Flash completed successfully +#define FAILED_UNKNOW_ERROR -1 // Unknown error +#define FAILED_DEVICE_NOT_FOUND -2 // Device not found +#define FAILED_FLASH_TIMEOUT -3 // Timeout during flash +``` + +--- + +## 8. CONFIGURATION FILE LOADING + +### Product-Specific Loaders +Each product type has specialized config loader: + +1. **TSTAT6/7/8/9**: `LoadFile2Tstat67()` +2. **TSTAT5E/5H/5G**: `LoadFile2Tstat()` (generic) +3. **T3 Modules**: `LoadFile2Tstat_T3()` +4. **Lighting Controller**: `load_file_2_schedule_LC()` +5. **Network Controller**: `load_file_2_schedule_NC()` + +### Config File Format +- **Type**: Text-based configuration +- **Content**: Device registers, schedules, programs +- **Communication**: Modbus/BACnet protocol +- **Logging**: All writes logged to `Load_config_Log\.txt` + +### Loading Process +1. Parse configuration file +2. Extract register/program data +3. Open serial or network communication +4. Send configuration data in Modbus packets +5. Verify writes +6. Log results and errors +7. Close communication + +--- + +## 9. DATA PERSISTENCE + +### SQLite Database Storage +- **Database File**: Current building database (`.mdb`) +- **Table**: `BatchFlashResult` +- **Function**: `ParameterSaveToDB()` - [Flash_Multy.cpp:916](T3000/Flash_Multy.cpp#L916) + +### Stored Information +```sql +CREATE TABLE BatchFlashResult ( + SN INTEGER, + FirmwarePath TEXT, + ConfigPath TEXT, + FirmwareResult INTEGER, + ConfigResult INTEGER +); +``` + +### Mass Flash Log Files +- **Location**: `Load_config_Log\` folder +- **Files**: `.txt` for each device +- **Content**: Configuration load operations and results + +### INI Files +- **ProductPath.ini**: Maps product IDs to firmware versions +- **AutoFlashConfig.ini**: Current flash job configuration +- **Mass Flash Result INI**: Status tracking for batch operations + +--- + +## 10. KEY GLOBAL VARIABLES + +```cpp +// Device list for current flash batch +vector flash_device; + +// Download info for cloud-based firmware +vector download_info_type; + +// Thread handles +HANDLE Call_ISP_Application; // ISP thread handle +HANDLE Check_Online_Thread; // Device online monitor + +// Paths +CString ApplicationFolder; // T3000 executable folder +CString MultyISPtool_path; // Path to ISP.exe +CString AutoFlashConfigPath; // Auto flash config INI path +CString g_ext_mass_flash_path; // Mass flash result INI + +// Global state +bool b_pause_refresh_tree; // Pause tree refresh during flash +bool g_bPauseMultiRead; // Pause multi-device reads +int multy_log_count; // Log counter +``` + +--- + +## 11. ERROR HANDLING + +### Common Failure Scenarios + +1. **No Device Selected** + - Message: "Please select one or more items." + - Recovery: User selects devices and retries + +2. **Device Not Online** + - Device marked with red "Offline" indicator + - Skipped during flash process + +3. **ISP Execution Failed** + - Result shows RED "Fail" + - Config phase skipped for that device + +4. **Configuration Write Failed** + - Status: RED "Less_Red" (firmware OK, config failed) + - Logged to `.txt` + - Device left with new firmware but old config + +5. **Communication Port In Use** + - ISP tool expects free COM/network port + - T3000 closes all ports before flashing + +### Error Logging +- **Log Location**: `\Load_config_Log\.txt` +- **Log Content**: All register writes, errors, and completion status +- **Purpose**: Troubleshooting failed flash operations + +--- + +## 12. FILE LOCATIONS & PATHS + +### Key Directories +``` +T3000 Installation Folder/ +├── T3000.exe (Main application) +├── ISP.exe (Firmware flash tool) +├── Database/ +│ ├── Firmware/ +│ │ ├── *.hex (Firmware files) +│ │ ├── *.ini (Config files) +│ │ └── ProductPath.ini (Product-to-firmware mapping) +│ └── Buildings/ +│ └── .mdb (SQLite database) +├── Load_config_Log/ (Configuration logs) +├── AutoFlashConfig.ini (Current flash config) +└── LoadFirmware.ini (Mass flash results) +``` + +--- + +## 13. PERFORMANCE & TIMING + +### Typical Operation Times +- **Dialog Initialization**: 2-5 seconds (scans all devices) +- **Per Device Firmware Flash**: 30-60 seconds (via ISP.exe) +- **Device Reboot Wait**: 4 seconds (Sleep timer) +- **Per Device Config Load**: 5-15 seconds (register writes) +- **Total for 1 Device**: 40-80 seconds +- **Total for 10 Devices**: 6-15 minutes + +### Thread Management +- **Main Thread**: UI handling, dialog management +- **ISP Thread**: Sequential device processing (not parallel) +- **Monitor Thread**: (Optional) Device online status checking + +--- + +## 14. SUMMARY OF FILES INVOLVED + +| File | Purpose | +|------|---------| +| `MainFrm.cpp` | Main window, entry point handler | +| `Flash_Multy.cpp` | Dialog UI, thread management | +| `Flash_Multy.h` | Data structures, dialog definition | +| `Dowmloadfile.cpp` | Cloud firmware download | +| `global_function.cpp` | ISP execution, com port management | +| `T3000.rc` | Menu resource, keyboard shortcut | +| `resource.h` | Resource IDs | +| `T3000.mdb` | SQLite database | + +--- + +## 15. QUICK REFERENCE - FUNCTION CALL SEQUENCE + +``` +User: Ctrl+F2 or Tools→Load Firmware + ↓ +MainFrm::OnBatchFlashHex() + ├─ Pause communications + ├─ Close serial/network + ├─ CFlash_Multy::DoModal() + │ ├─ OnInitDialog() + │ │ ├─ Initial_List() + │ │ ├─ GetProductType() + │ │ └─ Get_Device_Firmware() + │ ├─ [User selects devices & files] + │ └─ OnBnClickedButtonStatrt() + │ └─ CreateThread(multy_isp_thread) + │ ├─ Phase 1: Firmware Flash + │ │ ├─ SetAutoConfig() + │ │ ├─ WinExecAndWait(ISP.exe) + │ │ └─ Check result → PostMessage() + │ └─ Phase 2: Config Load + │ ├─ Open communication + │ ├─ Load product-specific config + │ ├─ Write to device + │ └─ PostMessage() + ├─ MultyFlashMessage() → Update UI colors + └─ Restore communications & exit +``` + +--- + +**Generated**: 2024 T3000 Building Automation System +**Version**: Complete firmware update flow documentation From 3cd6beb1fe1a6737ebed99f0dc4f9b69c8de2233 Mon Sep 17 00:00:00 2001 From: bhavikp-electrobit Date: Wed, 6 May 2026 17:50:18 +0530 Subject: [PATCH 2/6] Add proposed approach for resumable firmware updates with custom bootloader --- ...eUpdate \342\200\223 Proposed Approach.md" | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 "ResumableUpdate \342\200\223 Proposed Approach.md" diff --git "a/ResumableUpdate \342\200\223 Proposed Approach.md" "b/ResumableUpdate \342\200\223 Proposed Approach.md" new file mode 100644 index 00000000..a23d2839 --- /dev/null +++ "b/ResumableUpdate \342\200\223 Proposed Approach.md" @@ -0,0 +1,99 @@ +# Resumable Firmware Update – Proposed Approach + +## Overview +This document describes the proposed approach for implementing **resumable firmware update** using a custom bootloader. + +The goal is to safely resume firmware updates after interruption (e.g., RS485 disconnect, tool closure), without risking corrupted firmware or unstable device behavior. + +--- + +## Key Concept + +Instead of blindly resuming from any byte position, the system will: + +- Write firmware in **fixed-size chunks** +- **Verify each chunk** +- Store **last valid written offset** +- Resume only from a **safe and verified point** + +--- + +## Firmware Update Flow + +### Step-by-Step Process + +1. **Start Update** + - Device enters **"update in progress"** state + - Bootloader takes control + +2. **Chunk-Based Writing** + - Firmware is divided into fixed-size chunks (e.g., 1KB / 4KB) + - Each chunk is: + - Written to flash + - Verified (CRC/checksum) + +3. **Progress Storage** + - After each successful chunk: + - Bootloader stores: + - Last valid offset + - Firmware identification (version/hash) + +4. **Interruption Handling** + - If update is interrupted: + - Device remains in bootloader mode + - Last valid offset is preserved + +5. **Resume Request** + - When update restarts: + - Software requests device status + - Device responds with: + - Last valid offset + - Firmware identification + +6. **Validation** + - Software verifies: + - Firmware file matches device info + - If mismatch: + - Restart full update (safety fallback) + +7. **Resume Update** + - Software sends remaining firmware from last valid offset + - Bootloader continues chunk-based writing + verification + +8. **Completion** + - After full firmware is written: + - Perform final validation (full image check) + - Clear "update in progress" flag + - Jump to main application + +--- +## Flowchart + +```mermaid +flowchart TD +A[Start Update] --> B[Bootloader Mode] +B --> C[Write Chunk] +C --> D[Verify Chunk] +D --> E[Save Offset] +E --> F{Interrupted} + +F -->|No| C +F -->|Yes| G[Wait Reconnect] + +G --> H[Request Status] +H --> I[Send Offset] + +I --> J{Match} + +J -->|No| K[Restart] +J -->|Yes| L[Resume] + +L --> C + +C --> M{Done} + +M -->|No| C +M -->|Yes| N[Validate] + +N --> O[Clear State] +O --> P[Run App] \ No newline at end of file From faacc73c023cc3a0a341bab2243275934a4d92b5 Mon Sep 17 00:00:00 2001 From: bhavikp-electrobit Date: Mon, 11 May 2026 18:01:59 +0530 Subject: [PATCH 3/6] Add complete implementation guide for resumable firmware update in ESP bootloader - Introduced a comprehensive document detailing the architecture, core concepts, and flow of the resumable firmware update mechanism for the T3000 Building Automation System. - Included sections on Modbus register interface, bootloader implementation details, protocol specifications, error handling, and safety mechanisms. - Provided an implementation checklist to assist developers in the integration process. - Documented current implementation status and references for further reading. --- ACTUAL_RESUMABLE_UPDATE_IMPLEMENTATION.md | 600 ++++++++++++++ CMakeLists.txt | 16 +- ISP/CMakeLists.txt | 23 +- ISP/ComWriter.cpp | 295 +++---- ...LE_FIRMWARE_UPDATE_IMPLEMENTATION_GUIDE.md | 774 ++++++++++++++++++ 5 files changed, 1527 insertions(+), 181 deletions(-) create mode 100644 ACTUAL_RESUMABLE_UPDATE_IMPLEMENTATION.md create mode 100644 RESUMABLE_FIRMWARE_UPDATE_IMPLEMENTATION_GUIDE.md diff --git a/ACTUAL_RESUMABLE_UPDATE_IMPLEMENTATION.md b/ACTUAL_RESUMABLE_UPDATE_IMPLEMENTATION.md new file mode 100644 index 00000000..5349f6e4 --- /dev/null +++ b/ACTUAL_RESUMABLE_UPDATE_IMPLEMENTATION.md @@ -0,0 +1,600 @@ +# Actual Resumable Firmware Update Implementation in T3000 + +## Summary + +The T3000 Building Automation System **DOES have an active resumable firmware update implementation** in the ISP (In-System Programmer) tool. This document details the actual code implementation currently in place. + +--- + +## Table of Contents + +1. [Overview of Current Implementation](#overview-of-current-implementation) +2. [Modbus Register Interface](#modbus-register-interface) +3. [Implementation Details](#implementation-details) +4. [Resume Logic](#resume-logic) +5. [Device Memory Layout](#device-memory-layout) +6. [Code References](#code-references) +7. [Packet Structure](#packet-structure) +8. [Resume Detection Logic](#resume-detection-logic) + +--- + +## Overview of Current Implementation + +### Status +- **Status**: ACTIVE and IMPLEMENTED +- **Location**: ISP tool (ISP folder) +- **Device Support**: Product ID 88 (ESP32 boards) primarily, with some support for other devices +- **Protocol**: Modbus RTU over serial communication + +### Key Components + +| Component | File | Purpose | +|-----------|------|---------| +| Main COM Writer | [ISP/ComWriter.cpp](ISP/ComWriter.cpp) | Handles serial communication & flashing | +| Register Definitions | [ISP/Global_Struct.h](ISP/Global_Struct.h) | Modbus register definitions | +| TFTP Server | [ISP/TFTPServer.cpp](ISP/TFTPServer.cpp) | Network-based flashing | +| Tstat Flash Dialog | [ISP/TstatFlashDlg.cpp](ISP/TstatFlashDlg.cpp) | UI for single device flashing | + +--- + +## Modbus Register Interface + +### Register 16 (0x10): MODBUS_UPDATE_STATUS + +**Purpose**: Control and query firmware update state + +``` +Address: 0x10 (16 decimal) +Type: Read/Write + +Register Values: + 0x00 - 0x7E : Normal operation / Reading current state + 0x7F (127) : Jump to ISP routine (bootloader mode) + 0x8F (143) : Completely erase EEPROM + 0x40 : UPDATE IN PROGRESS flag set + 0x1F : Alternative UPDATE IN PROGRESS state +``` + +**Meaning of States**: +- `0x40`: Device is in bootloader mode OR update is in progress +- `0x1F`: Alternate flag indicating incomplete update +- When either 0x40 or 0x1F is detected, the ISP tool prompts for resume + +### Register 17 (0x11): MODBUS_UPDATE_PTR_HI + +``` +Address: 0x11 (17 decimal) +Type: Read/Write +Purpose: High byte of last write pointer + +Contains: Upper 8 bits of the firmware offset where update was interrupted +``` + +### Register 18 (0x12): MODBUS_UPDATE_PTR_LO + +``` +Address: 0x12 (18 decimal) +Type: Read/Write +Purpose: Low byte of last write pointer + +Contains: Lower 8 bits of the firmware offset +``` + +### Combined Offset Calculation + +The last valid offset is calculated as: + +```c +last_offset = (UPDATE_PTR_HI << 8) | UPDATE_PTR_LO +``` + +--- + +## Implementation Details + +### 1. Resume Detection Logic + +**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Lines 838-860 and 2672-2690 + +When device initialization begins, the ISP tool checks if an update was interrupted: + +```cpp +int x = mudbus_read_one(m_ID, 0xee10); // Read UPDATE_STATUS register (extended address) + +// Check if UPDATE_IN_PROGRESS flag is set +if(mudbus_read_one(m_ID, 0xee10) == 0x40 || mudbus_read_one(m_ID, 0xee10) == 0x1f) +{ + // Previous update was interrupted, prompt user + if(IDOK == AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."), MB_OKCANCEL)) + { + // User chose to RESUME + ii = 0xEE00 + 17; // Start reading from offset register + // Read the last written offset from device memory + int uc_temp1 = mudbus_read_one(m_ID, ii); // Get high byte + int uc_temp2 = mudbus_read_one(m_ID, ii+1); // Get low byte + + // Calculate last valid offset + // Offset used to continue flashing + } + else + { + // User chose to RESTART - full update from beginning + } +} +``` + +**Address Translation**: +- The actual Modbus register addresses appear to be mapped as: + - `0xEE10` or `0xee10`: UPDATE_STATUS register (mapped from standard 16) + - `0xEE00 + 17`: Memory location storing offset pointers + +### 2. Resume Offset Calculation + +**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Line 1523 + +```cpp +// When resuming, calculate the packet count to skip +pWriter->continue_com_flash_count = pWriter->update_firmware_info[0] * 128; + +// Note: Each packet is 128 bytes +// So if update_firmware_info[0] = 50, resume from packet 50 (6400 bytes) +``` + +**Packet Structure**: +- Each firmware packet = 128 bytes +- `continue_com_flash_count` stores bytes already written +- Resume calculation: `skip_bytes = continue_com_flash_count` + +### 3. Resume Output Message + +**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Line 1706 + +```cpp +if (pWriter->continue_com_flash_count > 0) +{ + CString strTips; + nCount = pWriter->continue_com_flash_count; + // Display message showing where resume will start + strTips.Format(_T("Resume at breakpoint, starting from package %u"), + pWriter->continue_com_flash_count / 128); + pWriter->OutPutsStatusInfo(strTips, FALSE); +} +``` + +--- + +## Resume Logic + +### Complete Resume Flow + +``` +┌─────────────────────────────────────────────────────────────┐ +│ RESUMABLE UPDATE FLOW - ACTUAL IMPLEMENTATION │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ 1. DEVICE CONNECTION +│ └─ Read MODBUS_UPDATE_STATUS (0xee10) +│ +│ 2. CHECK UPDATE_IN_PROGRESS FLAG +│ ├─ If Flag == 0x40 or 0x1f +│ │ └─ Previous update interrupted! +│ └─ If Flag != 0x40/0x1f +│ └─ Normal operation +│ +│ 3. PROMPT USER (if flag detected) +│ ┌──────────────────────────────────────┐ +│ │ "Previous Update was interrupted." │ +│ │ "Press OK to Resume." │ +│ │ "Press Cancel to Restart." │ +│ └──────────────────────────────────────┘ +│ │ +│ ├─ User clicks OK: +│ │ │ +│ │ ├─ Read last written offset +│ │ │ ├─ Address 0xEE00 + 17: Read high byte (UPDATE_PTR_HI) +│ │ │ └─ Address 0xEE00 + 18: Read low byte (UPDATE_PTR_LO) +│ │ │ +│ │ ├─ Calculate: +│ │ │ └─ last_offset = (high_byte << 8) | low_byte +│ │ │ +│ │ ├─ Set continue_com_flash_count = last_offset +│ │ │ +│ │ ├─ Calculate packet to skip: +│ │ │ └─ skip_packets = continue_com_flash_count / 128 +│ │ │ +│ │ ├─ Display message: +│ │ │ └─ "Resume at breakpoint, starting from package X" +│ │ │ +│ │ └─ BEGIN RESUME FLASHING +│ │ └─ Start from: m_pExtendFileBuffer + continue_com_flash_count +│ │ +│ └─ User clicks Cancel: +│ │ +│ ├─ Set continue_com_flash_count = 0 +│ │ +│ └─ BEGIN FULL UPDATE FROM START +│ +│ 4. FLASH PROCESS +│ ├─ If resuming: +│ │ └─ nBufLen = m_szHexFileFlags[p] - nCount +│ │ +│ └─ If restarting: +│ └─ nBufLen = m_szHexFileFlags[p] +│ +│ 5. WRITE PACKETS +│ └─ Send 128-byte packets from calculated offset +│ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## Device Memory Layout + +### Offset Storage Registers + +``` +DEVICE INTERNAL MEMORY (Modbus Accessible): + +┌───────────────────────────────────────────────────────┐ +│ Address │ Size │ Purpose │ +├───────────────────────────────────────────────────────┤ +│ 0xEE10 │ 1 │ UPDATE_STATUS flag │ +│ │ │ 0x40 = Update in progress │ +│ │ │ 0x1F = Alternate in-progress │ +│ │ │ 0x00 = Update complete/idle │ +├───────────────────────────────────────────────────────┤ +│ 0xEE00+17 │ 2 │ Last Valid Offset (High Byte) │ +│ (0xEE11) │ │ UPDATE_PTR_HI │ +├───────────────────────────────────────────────────────┤ +│ 0xEE00+18 │ 2 │ Last Valid Offset (Low Byte) │ +│ (0xEE12) │ │ UPDATE_PTR_LO │ +├───────────────────────────────────────────────────────┤ +│ 0xEE00+.. │ .. │ Additional firmware state data │ +└───────────────────────────────────────────────────────┘ + +Total Offset = (UPDATE_PTR_HI << 8) | UPDATE_PTR_LO +``` + +--- + +## Code References + +### Main Resumable Update Code Locations + +#### 1. Initial Resume Detection +**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp) + +**Line 838-860** - First occurrence (in flash_a_tstat function): +```cpp +int x = mudbus_read_one(m_ID,0xee10); + +if(mudbus_read_one(m_ID,0xee10)==0x40 || mudbus_read_one(m_ID,0xee10)==0x1f) +{ + if(IDOK==AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."),MB_OKCANCEL)) + { + // Resume code... + ii=0xEE00+17; + int l=0; + do + { + int uc_temp1= mudbus_read_one(m_ID,ii); + int uc_temp2= mudbus_read_one(m_ID,ii+1); + if(uc_temp1==0x00 && uc_temp2==0x00 ) + ii+=2; + else if(l==0) + { + if(uc_temp1==0xf0 && uc_temp2==0xf0) + { + ii=0; + break; + } + } +``` + +**Line 2650-2690** - Second occurrence (in Flash_Modebus_Device thread): +```cpp +while(mudbus_read_one(m_ID,0xee10,1)<0) //the return value == -1 , no connecting + +if(mudbus_read_one(m_ID,0xee10)==0x40 || mudbus_read_one(m_ID,0xee10)==0x1f) +{ + if(IDOK==AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."),MB_OKCANCEL)) + { + ii=0xEE00+17; + // ... resume logic + } +} +``` + +#### 2. Offset Calculation +**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Line 1523 +```cpp +pWriter->continue_com_flash_count = pWriter->update_firmware_info[0] * 128; +// 1994单位是128字节的大包; Calculates bytes from packet count +``` + +#### 3. Resume Message Output +**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Lines 1702-1706 +```cpp +if (pWriter->continue_com_flash_count > 0) +{ + CString strTips; + nCount = pWriter->continue_com_flash_count ; + strTips.Format(_T("Resume at breakpoint, starting from package %u"), + pWriter->continue_com_flash_count/128); + pWriter->OutPutsStatusInfo(strTips, FALSE); +} +``` + +#### 4. Resume Offset Reset +**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Lines 1020-1022 +```cpp +if(pWriter->continue_com_flash_count < 0) + pWriter->continue_com_flash_count = 0; +ii = pWriter->continue_com_flash_count; +``` + +#### 5. Data Structure +**File**: [ISP/ComWriter.h](ISP/ComWriter.h), Line 114 +```cpp +// Continue burning from how many packets; +// currently only supports product 88 ESP32 chip boards +int continue_com_flash_count; // 继续从第X包开始烧写; 目前只支持产品为88(ESP32芯片)的设备; +unsigned short update_firmware_info[6]; +``` + +### Register Definitions + +**File**: [ISP/Global_Struct.h](ISP/Global_Struct.h), Lines 107-111 +```cpp +MODBUS_BASE_ADDRESS = 15, +MODBUS_UPDATE_STATUS = 16, // reg 16 status for update_flash + // writing 0x7F means jump to ISP routine + // writing 0x8F means completely erase eeprom +MODBUS_UPDATE_PTR_HI, // reg 17 pointer for last attempt upload HI +MODBUS_UPDATE_PTR_LO, // reg 18 pointer for last attempt upload LO +``` + +### Network-Based Resume + +**File**: [ISP/TFTPServer.cpp](ISP/TFTPServer.cpp), Lines 1094 and 1956 +```cpp +byCommand[0] = 0xEE; // Command, 2 bytes, 0xee10, command to start with flash +// TFTP also supports resumable updates with same logic +``` + +--- + +## Packet Structure + +### Firmware Packet Format + +``` +PACKET STRUCTURE: +┌─────────────────────────────────────────────────────┐ +│ Packet Size: 128 bytes (0x80) │ +├─────────────────────────────────────────────────────┤ +│ Byte 0-N │ Firmware data (up to 128 bytes) │ +├─────────────────────────────────────────────────────┤ +│ CRC/ChkSum │ (Included in packet or separate) │ +└─────────────────────────────────────────────────────┘ + +For Extended Hex Files: +- m_szHexFileFlags stores the length of each segment +- Each segment size can vary +- continue_com_flash_count tracks bytes written +``` + +### Buffer Management + +```cpp +// From ComWriter class: +TS_UC* m_pExtendFileBuffer; // Stores entire firmware file +vector m_szHexFileFlags; // Stores segment lengths +int continue_com_flash_count; // Current byte offset for resume + +// Resume calculation: +nBufLen = m_szHexFileFlags[p] - nCount; // Remaining bytes in segment +// Start address: m_pExtendFileBuffer + continue_com_flash_count +``` + +--- + +## Resume Detection Logic + +### State Detection Algorithm + +```cpp +// STEP 1: Read UPDATE_STATUS register +int update_status = mudbus_read_one(m_ID, 0xee10); + +// STEP 2: Check for interrupt flag +if (update_status == 0x40 || update_status == 0x1f) +{ + // UPDATE IS IN PROGRESS or WAS INTERRUPTED + + // STEP 3: Prompt user for action + if (user_clicks_OK) + { + // STEP 4: Read offset from device memory + high_byte = mudbus_read_one(m_ID, 0xEE11); + low_byte = mudbus_read_one(m_ID, 0xEE12); + + // STEP 5: Calculate offset + offset = (high_byte << 8) | low_byte; + + // STEP 6: Set continue count + continue_com_flash_count = offset; + + // STEP 7: Resume from offset + // ISP will skip (offset / 128) packets + } + else + { + // RESTART: Set continue_com_flash_count = 0 + } +} +else if (update_status == 0x00) +{ + // NORMAL STATE: No previous update + continue_com_flash_count = 0; + start_fresh_update(); +} +``` + +--- + +## Current Limitations + +Based on code analysis: + +1. **Device Support** + - Primarily implemented for product ID 88 (ESP32-based boards) + - Comment in code: "目前只支持产品为88(ESP32芯片)的设备" + - Translation: "Currently only supports product 88 (ESP32 chip) devices" + +2. **Packet Size** + - Fixed packet size of 128 bytes per transmission + - Resume offset must align with packet boundaries + +3. **State Storage** + - Relies on device-side EEPROM/registers to maintain state + - No verification of firmware file hash before resume + - If device/file mismatch, may resume with wrong file + +4. **Manual User Intervention** + - Requires user to click OK/Cancel dialog + - Cannot auto-resume on reconnection + +--- + +## Communication Protocol + +### Modbus RTU Query Sequence + +``` +PC → Device: Query UPDATE_STATUS + [Function Code 03: Read Holding Registers] + [Register: 0xEE10] + [Quantity: 1] + +Device → PC: Response with status value + [0x40: Update in progress] + [0x1F: Alternate interrupt flag] + [0x00: Normal/complete] + +If interrupted flag detected: + +PC → Device: Read offset registers + [Function Code 03: Read Holding Registers] + [Starting Address: 0xEE11] + [Quantity: 2] + +Device → PC: Response + [Register 0xEE11: High byte of offset] + [Register 0xEE12: Low byte of offset] + +PC Calculation: + last_offset = (0xEE11 << 8) | 0xEE12 + packets_to_skip = last_offset / 128 +``` + +--- + +## Example Resume Scenario + +### Scenario: Update interrupted after 1000 bytes written + +1. **Initial State** + - Device received 1000 bytes (≈8 packets of 128 bytes) + - Device stores: offset_high = 0x03, offset_low = 0xE8 (1000 = 0x03E8) + - UPDATE_STATUS register = 0x40 + +2. **PC Tool Reconnects** + - Queries register 0xEE10 + - Receives: 0x40 (interrupted flag) + +3. **User Prompted** + ``` + "Previous Update was interrupted. + Press OK to Resume. + Cancel to Restart." + ``` + +4. **User Clicks OK** + - PC reads registers 0xEE11 and 0xEE12 + - Receives: high_byte=0x03, low_byte=0xE8 + +5. **Resume Calculation** + - offset = (0x03 << 8) | 0xE8 = 1000 bytes + - packets_to_skip = 1000 / 128 = 7 (with remainder) + - continue_com_flash_count = 1000 + - Buffer pointer: m_pExtendFileBuffer + 1000 + +6. **Remaining Firmware Sent** + - PC sends firmware starting from byte 1000 + - Device receives bytes 1000 to end-of-file + - Device writes to flash starting at offset 1000 + +7. **Completion** + - When all bytes received, device sets UPDATE_STATUS = 0x00 + - Device clears offset registers + +--- + +## Implementation Status Summary + +| Feature | Status | Notes | +|---------|--------|-------| +| Resume Detection | ✅ Implemented | Checks 0xee10 register | +| User Prompt | ✅ Implemented | OK/Cancel dialog | +| Offset Reading | ✅ Implemented | Reads registers 0xEE11/12 | +| Offset Calculation | ✅ Implemented | 16-bit offset support | +| Resume from Offset | ✅ Implemented | Skips packets correctly | +| Packet Transmission | ✅ Implemented | 128-byte packets | +| File Validation | ❌ Not Implemented | No hash verification | +| Auto-Resume | ❌ Not Implemented | Manual user action required | +| Full State Verification | ⚠️ Partial | Only checks interrupt flags | + +--- + +## For ESP Bootloader Implementation + +When implementing resumable update in ESP bootloader, consider: + +1. **Register Compatibility** + - Implement Modbus registers at addresses 0xEE10, 0xEE11, 0xEE12 + - Or map your native registers to these Modbus addresses + +2. **State Preservation** + - Store UPDATE_STATUS in non-volatile memory + - Use 0x40 or 0x1F as interrupt flags + - Update offset registers after each successful block write + +3. **Offset Encoding** + - Use 16-bit offset (UPDATE_PTR_HI and UPDATE_PTR_LO) + - Ensure offset can be read back via Modbus + +4. **Packet Alignment** + - Use 128-byte packet size for compatibility + - ISP tool expects this size + +5. **Protocol Implementation** + - Support standard Modbus RTU function code 03 (Read Holding Registers) + - Support function code 06 (Write Single Register) for control + +--- + +## References + +- **ISP Tool Source**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp) +- **Register Definitions**: [ISP/Global_Struct.h](ISP/Global_Struct.h) +- **TFTP Implementation**: [ISP/TFTPServer.cpp](ISP/TFTPServer.cpp) +- **Class Definition**: [ISP/ComWriter.h](ISP/ComWriter.h) + +--- + +**Document Version**: 1.0 +**Status**: Actual Implementation Analysis (Not Proposed) +**Last Updated**: 2024 diff --git a/CMakeLists.txt b/CMakeLists.txt index 57d0f393..72d322b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,20 +1,20 @@ -# Copyright 2020 Temco Controls +# Copyright 2020 Temco Controls # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is furnished to do so, +# in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS -# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR # IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # We support CMake starting at version 3.0.0 diff --git a/ISP/CMakeLists.txt b/ISP/CMakeLists.txt index 314181e9..08699676 100644 --- a/ISP/CMakeLists.txt +++ b/ISP/CMakeLists.txt @@ -1,20 +1,20 @@ -# Copyright 2020 Temco Controls +# Copyright 2020 Temco Controls # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is furnished to do so, +# in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS -# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR # IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # We support CMake starting at version 3.0.0 @@ -92,7 +92,7 @@ if(NOT MSVC) endif() endif(NOT MSVC) -# Include directories +# Include directories include_directories(${CPP_HEADER_DIR}) ################ Files ################ @@ -171,12 +171,12 @@ set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/isp.rc PROPERTIES LANGUA set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -i ./") set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -i ./res/") -# Since we link to BACnet_Stack_Library through pragma directive, we need to specify the +# Since we link to BACnet_Stack_Library through pragma directive, we need to specify the # link directory path. link_directories(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}) # Add executable to build. -add_executable(${PROJECT_NAME} +add_executable(${PROJECT_NAME} ${SOURCE_FILES} ${HEADER_FILES} ${RESOURCE_FILES} ) @@ -188,4 +188,3 @@ add_dependencies(${PROJECT_NAME} BACnet_Stack_Library ModbusDllforVc) # Link with other dependencies. Linking will happen using pragma directive. #target_link_libraries(${PROJECT_NAME} ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/BACnetStackLibrary.lib # ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/ModbusDllforVc.lib) - \ No newline at end of file diff --git a/ISP/ComWriter.cpp b/ISP/ComWriter.cpp index bd7287e5..e27eec7e 100644 --- a/ISP/ComWriter.cpp +++ b/ISP/ComWriter.cpp @@ -6,9 +6,9 @@ extern vector <_Bac_Scan_Com_Info> m_bac_handle_Iam_data; extern unsigned int g_mstp_deviceid ; extern int SPECIAL_BAC_TO_MODBUS ; // If equal to 1, it means that the new BootLoader is being flashed now -extern int new_bootload ; //1 ˵дµBootLoader; +extern int new_bootload ; //�������1 ��˵��������д�����µ�BootLoader; // 0 normal mode, 1 flash boot mode -extern int com_port_flash_status ; // 0 ģʽ 1 дbootģʽ +extern int com_port_flash_status ; // 0 ����ģʽ 1 ��дbootģʽ extern unsigned int n_check_temco_firmware; extern bool auto_flash_mode; extern CString g_strFlashInfo; @@ -29,7 +29,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam); int flash_a_tstat_RAM(BYTE m_ID,int section, unsigned int the_max_register_number_parameter, TS_UC *register_data_orginal, LPVOID pParam); int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_UC *register_data_orginal, LPVOID pParam); // 0 no need to update boot, 1 need to update bootloader; C1 is hex -extern int firmware_must_use_new_bootloader ; //0 øboot 1 Ҫbootload; C1Ϊhex +extern int firmware_must_use_new_bootloader ; //0 ���ø���boot 1 ��Ҫ����bootload; C1Ϊhex extern CString g_repair_bootloader_file_path; CComWriter::CComWriter(void) @@ -41,7 +41,7 @@ CComWriter::CComWriter(void) m_nComPort = -1; //m_nModbusID; // Baud rate - m_nBautrate = 0; // - Baud rate + m_nBautrate = 0; // ������ - Baud rate m_com_flash_binfile = 0; m_pWorkThread = NULL; continue_com_flash_count = 0; @@ -147,10 +147,10 @@ int CComWriter::BeginWirteByCom() { #pragma region detect_mstp_shutdown // Don't judge the serial port protocol when flashing for now, otherwise it will be slow when burning modbus - if (SPECIAL_BAC_TO_MODBUS) //ʱҪڵflashʱ жϴڵЭ飬Ȼmodbusʱ + if (SPECIAL_BAC_TO_MODBUS) //��ʱ��Ҫ�ڵ�flash��ʱ�� �жϴ��ڵ�Э�飬��Ȼ��modbus��ʱ����� �� { // Used to detect serial port MSTP data - baudrate_def temp_baudrate_ret[100] = { 0 }; //ڼ⴮MSTP + baudrate_def temp_baudrate_ret[100] = { 0 }; //���ڼ�⴮��MSTP���� int find_mstp_protocal = 0; find_mstp_protocal = Check_Mstp_Comport(m_nComPort, temp_baudrate_ret, m_nBautrate); if ((find_mstp_protocal == -101) || (find_mstp_protocal == -100)) @@ -176,7 +176,7 @@ int CComWriter::BeginWirteByCom() OutPutsStatusInfo(srtInfo, FALSE); // Initialize bacnet mstp protocol - int ret = Initial_bac(m_nComPort, _T(""), m_nBautrate); //ʼbacnet mstp Э + int ret = Initial_bac(m_nComPort, _T(""), m_nBautrate); //��ʼ��bacnet mstp Э�� CString temp_cs; if (ret <= 0) { @@ -191,7 +191,7 @@ int CComWriter::BeginWirteByCom() } //Send_WhoIs_Global(-1, -1); // Don't use mute for now -#if 0 //Ȳñ +#if 0 //�Ȳ����ñ��� srtInfo = _T("Disabling Bacnet MSTP and switching over to Modbus"); OutPutsStatusInfo(srtInfo, FALSE); int nret = 0; @@ -199,17 +199,17 @@ int CComWriter::BeginWirteByCom() do { // Make Temco's bacnet devices on the bus silent; since it's a broadcast, send it in a loop - nret = ShutDownMstpGlobal(4); // Temco bacnet 豸 ; Ϊǹ㲥 ѭ + nret = ShutDownMstpGlobal(4); //�������� Temco�� bacnet �豸 ����; ��Ϊ�ǹ㲥 �� ����ѭ���� Sleep(2000); loop1_count++; } while ((nret <= 0) && loop1_count < 5); // Make Temco's bacnet devices on the bus silent; since it's a broadcast, send it in a loop - nret = ShutDownMstpGlobal(4); // Temco bacnet 豸 ; Ϊǹ㲥 ѭ + nret = ShutDownMstpGlobal(4); //�������� Temco�� bacnet �豸 ����; ��Ϊ�ǹ㲥 �� ����ѭ���� Sleep(1000); close_bac_com(); - find_mstp_protocal = Check_Mstp_Comport(m_nComPort, &temp_baudrate_ret, m_nBautrate); //ٴȷ - Confirm that all devices on the bus are silent again - temp_loop_count++; //ѭ3 - Loop up to 3 times + find_mstp_protocal = Check_Mstp_Comport(m_nComPort, &temp_baudrate_ret, m_nBautrate); //�ٴ�ȷ�������� �������� - Confirm that all devices on the bus are silent again + temp_loop_count++; //���ѭ��3�� - Loop up to 3 times #endif } @@ -262,10 +262,10 @@ BOOL CComWriter::WriteCommandtoReset() int nRet = Write_One(m_szMdbIDs[0],16,127); // Enter ISP mode //Sleep(2000); - //Add by Fance Ӧô ISP 16д127 Ҫ 11żĴ 11 1 ˵תɹȴ + //Add by Fance �����Ӧ�ô������� ISP 16д127�� ��Ҫ�� 11�żĴ��� 11�� ����1 ˵����ת�ɹ�����������ȴ� //TBD: Explain this comment better // If you want to read 11th register 11th or more than 1 when you jump from the application code to the ISP 16 write 127, the jump succeeds, otherwise wait - + strTips = _T("Wait device jump to isp mode!"); OutPutsStatusInfo(strTips); int re_count = 0; @@ -287,7 +287,7 @@ BOOL CComWriter::WriteCommandtoReset() int ModelID= read_one(m_szMdbIDs[0],7,5); if (ModelID>0) { - if (ModelID==6||ModelID==7||ModelID==8)//Tstat6,7,8 Detecting chip flash size { + if (ModelID==6||ModelID==7||ModelID==8)//Tstat6,7,8 Detecting chip flash size�� { { int Chipsize=read_one(m_szMdbIDs[0],11,5); @@ -483,10 +483,10 @@ UINT Flash_Modebus_Device(LPVOID pParam) CString strID; strID.Format(_T("|--------------->>ID-%d-<<---------------"), pWriter->m_szMdbIDs[i]); pWriter->OutPutsStatusInfo(strID); - ////ʾflash֮ǰʱ + ////��ʾflash֮ǰ��ʱ�� pWriter->OutPutsStatusInfo(_T("|--------->>Begin")); pWriter->OutPutsStatusInfo(_T("|-->>Begin Time:")+GetSysTime()); - //// ʾflash֮ǰ豸״̬Ϣ + //// ��ʾflash֮ǰ���豸״̬��Ϣ BOOL Flag_HEX_BIN=FALSE; // ((CISPDlg*)pWriter->m_pParentWnd)->Show_Flash_DeviceInfor(pWriter->m_szMdbIDs[i]); pWriter->m_szMdbIDs[i] = temp_read_reg[6]; @@ -579,7 +579,7 @@ UINT Flash_Modebus_Device(LPVOID pParam) goto end_tcp_flash_mode; } int Chipsize = 0; - // pWriter->OutPutsStatusInfo(_T("The device doesnt match with the hex file")); + // pWriter->OutPutsStatusInfo(_T("The device doesn��t match with the hex file")); #if 1 //Reset if (ModelID==6||ModelID==7||ModelID==8) Chipsize = Chipsize_6; @@ -658,7 +658,7 @@ UINT Flash_Modebus_Device(LPVOID pParam) CString strTemp=_T(""); strTemp.Format(_T("%d;"), pWriter->m_szMdbIDs[i]); - strFailureList+=strTemp; // flashʹ + strFailureList+=strTemp; // flash���ʹ�� switch(nFlashRet) { case -1: @@ -708,7 +708,7 @@ UINT Flash_Modebus_Device(LPVOID pParam) /*CString strText; strText.Format(_T("|ID %d: Programming successful."), pWriter->m_szMdbIDs[i]); pWriter->OutPutsStatusInfo(strText);*/ - //// ʾflash֮ǰ豸״̬Ϣ + //// ��ʾflash֮ǰ���豸״̬��Ϣ int time_count = 3; for (int i=0; iOutPutsStatusInfo(temp_123,true); Sleep(1000); } - //Sleep(13000);//ȴ֮ + //Sleep(13000);//�ȴ�������֮�� // ((CISPDlg*)pWriter->m_pParentWnd)->Show_Flash_DeviceInfor(pWriter->m_szMdbIDs[i]); pWriter->OutPutsStatusInfo(_T("|-->>End Time:")+GetSysTime()); pWriter->OutPutsStatusInfo(_T("|------->>End")); @@ -762,7 +762,7 @@ UINT Flash_Modebus_Device(LPVOID pParam) CString strTemp=_T(""); strTemp.Format(_T("%d;"), pWriter->m_szMdbIDs[i]); - strFailureList+=strTemp; // flashʹ + strFailureList+=strTemp; // flash���ʹ�� switch(nFlashRet) { case -1: @@ -802,8 +802,8 @@ UINT Flash_Modebus_Device(LPVOID pParam) CString strText; strText.Format(_T("|ID %d: Programming successful."), pWriter->m_szMdbIDs[i]); pWriter->OutPutsStatusInfo(strText); - //// ʾflash֮ǰ豸״̬Ϣ - //Sleep(13000);//ȴ֮ + //// ��ʾflash֮ǰ���豸״̬��Ϣ + //Sleep(13000);//�ȴ�������֮�� // ((CISPDlg*)pWriter->m_pParentWnd)->Show_Flash_DeviceInfor(pWriter->m_szMdbIDs[i]); pWriter->OutPutsStatusInfo(_T("|-->>End Time:")+GetSysTime()); pWriter->OutPutsStatusInfo(_T("|------->>End")); @@ -823,6 +823,8 @@ end_tcp_flash_mode : } +#define REG_RESUME_OFFSET_LO 1991 +#define REG_RESUME_OFFSET_HI 1992 ////////////////////////////////////////////////////////////////////////// //the return value 1,successful, return < 0 , fail @@ -835,42 +837,35 @@ int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_ unsigned int ii=0; //*************inspect the flash at the last flash position *********************** - int x = mudbus_read_one(m_ID,0xee10); + int update_status = mudbus_read_one(m_ID,0xee10); // reg 16 status for update_flash //************* begin to flash *********************** ii=0; - if(mudbus_read_one(m_ID,0xee10)==0x40 || mudbus_read_one(m_ID,0xee10)==0x1f) // ee10 why + // 0x40 : UPDATE IN PROGRESS flag set + // 0x1F : Alternative UPDATE IN PROGRESS state + if(update_status==0x40 || update_status==0x1f) { if(IDOK==AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."),MB_OKCANCEL)) { - // ѡȷ - ii=0xEE00+17; - int l=0;//temp;<200 - do + // Read resume offset from regs 1991 & 1992 + int lo = mudbus_read_one(m_ID, REG_RESUME_OFFSET_LO); + int hi = mudbus_read_one(m_ID, REG_RESUME_OFFSET_HI); + if (lo >= 0 && hi >= 0) { - int uc_temp1= mudbus_read_one(m_ID,ii); - int uc_temp2= mudbus_read_one(m_ID,ii+1); - if(uc_temp1==0x00 && uc_temp2==0x00 ) - ii+=2; - else if(l==0) - { - if(uc_temp1==0xf0 && uc_temp2==0xf0) - { - ii=0; - break; - } - } - else - { - ii=uc_temp1*256+uc_temp2; - break; - } - l++; + ii = (int)((UINT32)(lo & 0xFFFF) | ((UINT32)(hi & 0xFFFF) << 16)); + CString strResumePos; + strResumePos.Format(_T("|Resuming from offset: 0x%08X"), ii); + pWriter->OutPutsStatusInfo(strResumePos); + } + else + { + ii = 0; + pWriter->OutPutsStatusInfo(_T("|Failed to read resume offset, restarting from beginning.")); } - while(l<200); + Sleep(500); } - else // ѡȡ + else // ѡȡ�� { //from 0000 flash update ii=0;//from 0000 register flash @@ -949,7 +944,7 @@ int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_ Sleep(500); //********************write register 16 value 0x7f ************** - //x=Write_One(m_ID,16,0x7f); + //UpdateStatus=Write_One(m_ID,16,0x7f); do { if(mudbus_write_one(m_ID,16,0x7f)<0) @@ -969,7 +964,7 @@ int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_ ii=0; srtInfo = _T("|Erasing device...";); pWriter->OutPutsStatusInfo(srtInfo); - + do { @@ -990,30 +985,28 @@ int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_ ii=0; Sleep(7000);//Delay required while device writes to flash - - do - { - if(mudbus_write_one(m_ID,16,0x1f)<0) - { - ii++; - Sleep(1000); - } - else - { - break; - } - - } - while(iiOutPutsStatusInfo(srtInfo); if (Device_infor[7] == 88) { @@ -1037,7 +1030,7 @@ int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_ TS_UC data_to_send[160]= {0}; // buffer that writefile() will to use int itemp=0; - + persentfinished = ((ii+ one_flash_package)*100)/the_max_register_number_parameter; if(persentfinished>100) persentfinished=100; @@ -1048,8 +1041,8 @@ int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_ { if(itempOutPutsStatusInfo(srtInfo, TRUE); - + do { @@ -1192,9 +1185,9 @@ int flash_a_tstat_RAM(BYTE m_ID,int section, unsigned int the_max_register_numbe if (itemp <= com_error_delay_count) Sleep(com_error_delay_time); else - Sleep(300 + itemp*100); //дʧ 300ms ; + Sleep(300 + itemp*100); //дʧ�� ����300ms�� ����; itemp++; - + } else { @@ -1439,14 +1432,14 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) if (SPECIAL_BAC_TO_MODBUS) { g_mstp_deviceid = 0; - //IDõӦ device id. + //����ID�õ���Ӧ�� device id. CString strtemp; strtemp.Format(_T("|Reading Device Object Instance")); pWriter->OutPutsStatusInfo(strtemp); strtemp.Format(_T(" ")); pWriter->OutPutsStatusInfo(strtemp); int temp_found_device_id = 0; - for (int x = 0; x < 30; x++) + for (int UpdateStatus = 0; UpdateStatus < 30; UpdateStatus++) { CString temp_found_id = _T("Found MAC ID:"); Send_WhoIs_Global(-1, -1); @@ -1490,28 +1483,29 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) // for (int Time =0; Time < pWriter->m_FlashTimes; Time++) { CString strID; - strID.Format(_T("|Current Programming device ID is : %d"), pWriter->m_szMdbIDs[i]); + strID.Format(_T("|Current Hex Programming device ID is : %d"), pWriter->m_szMdbIDs[i]); pWriter->OutPutsStatusInfo(strID); BOOL Flag_HEX_BIN=FALSE; if (pWriter->UpdataDeviceInformation(pWriter->m_szMdbIDs[i])) { - if ((Device_infor[4] == Device_infor[14]) && + if ((Device_infor[4] == Device_infor[14]) && (Device_infor[14] >= 20) && (Device_infor[7] == 88)) { unsigned short wirte_md5[4]; int compare_ret = 1; + strTips = _T("|Current Programming type is : Hex Only"); + pWriter->OutPutsStatusInfo(strTips); - - //88esp豸bootloader + //���88esp���豸��bootloader ���� int n_ret = modbus_read_multi(pWriter->m_szMdbIDs[i], &pWriter->update_firmware_info[0], 1994, 6); if (n_ret > 0) { - //ȶMD5 - for (int x = 0; x < 4; x++) + //�ȶ�MD5 + for (int UpdateStatus = 0; UpdateStatus < 4; UpdateStatus++) { - wirte_md5[x] = firmware_md5[2 * x] * 256 + firmware_md5[2 * x + 1]; - if (pWriter->update_firmware_info[x + 1] != wirte_md5[x]) + wirte_md5[UpdateStatus] = firmware_md5[2 * UpdateStatus] * 256 + firmware_md5[2 * UpdateStatus + 1]; + if (pWriter->update_firmware_info[UpdateStatus + 1] != wirte_md5[UpdateStatus]) { compare_ret = -1; //break; @@ -1520,19 +1514,12 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) if ((compare_ret == 1) && (pWriter->update_firmware_info[0] != 0)) { - pWriter->continue_com_flash_count = pWriter->update_firmware_info[0] * 128; // 1994ŵǴд˶ٰ; + pWriter->continue_com_flash_count = pWriter->update_firmware_info[0] * 128; // 1994��ŵ��Ǵ�����д�˶��ٰ�; } else { - - - pWriter->continue_com_flash_count = 0; } - - - - } else { @@ -1544,10 +1531,7 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) for (int y = 0; y < 4; y++) { int ret = mudbus_write_one(pWriter->m_szMdbIDs[i], 1995 + y, wirte_md5[y],3); - - } - } //continue_com_flash_count = } @@ -1647,8 +1631,8 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) Flag_HEX_BIN=TRUE; } - // pWriter->OutPutsStatusInfo(_T("The device doesnt match with the hex file")); -#if 1 //λ + // pWriter->OutPutsStatusInfo(_T("The device doesn��t match with the hex file")); +#if 1 //��λ int Chipsize= mudbus_read_one(pWriter->m_szMdbIDs[i],11,5); if (Chipsize<37) //64K { @@ -1724,7 +1708,7 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) CString strTemp=_T(""); strTemp.Format(_T("%d;"), pWriter->m_szMdbIDs[i]); - strFailureList+=strTemp; // flashʹ + strFailureList+=strTemp; // flash���ʹ�� switch(nFlashRet) { case -1: @@ -1894,7 +1878,7 @@ BOOL CComWriter::UpdataDeviceInformation_ex(unsigned short device_productID) CString strtips; CString str_ret,temp; - + CString hexproductname=_T(""); // int ret=read_multi(ID,&Device_infor[0],0,10); @@ -1995,16 +1979,16 @@ int CComWriter::Fix_Tstat10_76800_baudrate() if (Device_infor[7] != 10) return 0; - if (m_nBautrate != 76800) //ѡIJʲ76800 // If the baud rate selected in the interface is not 76800, ignore it + if (m_nBautrate != 76800) //����ѡ��IJ����ʲ���76800���������� // If the baud rate selected in the interface is not 76800, ignore it return 0; - if (Device_infor[14] >= 79) //̼汾79˵Ѿ޸ // If firmware version is greater than 79, it means it has been fixed + if (Device_infor[14] >= 79) //�̼��汾����79��˵���Ѿ��޸����� // If firmware version is greater than 79, it means it has been fixed return 0; - if ((Device_infor[14] == 0) && (Device_infor[11] > 50)) //˵BootLoader // Indicates it's in BootLoader + if ((Device_infor[14] == 0) && (Device_infor[11] > 50)) //˵����BootLoader�� // Indicates it's in BootLoader { return 0; Sleep(1); } - else if ((Device_infor[14] > 0) && (Device_infor[11] == 1)) //˵Ӧô // Indicates it's in application code + else if ((Device_infor[14] > 0) && (Device_infor[11] == 1)) //˵����Ӧ�ô����� // Indicates it's in application code { return 1; Sleep(1); @@ -2020,7 +2004,7 @@ int CComWriter::UpdataDeviceInformation(int& ID) CString strtips; CString str_ret,temp; - + CString hexproductname=_T(""); int ret=0; @@ -2054,7 +2038,7 @@ int CComWriter::UpdataDeviceInformation(int& ID) { return TRUE; } - + int temp_boot_version = isp_max(Device_infor[11], Device_infor[14]); @@ -2068,7 +2052,7 @@ int CComWriter::UpdataDeviceInformation(int& ID) CString prodcutname= GetFirmwareUpdateName(Device_infor[7]); - + MultiByteToWideChar(CP_ACP, 0, (char *)global_fileInfor.product_name, @@ -2197,12 +2181,12 @@ int CComWriter::UpdataDeviceInformation(int& ID) } else if ((Device_infor[7] == PM_MINIPANEL_ARM) && (temp_bootloader_version < 62)) { - c2_update_boot = false; //ִ֧ڸ - Does not support serial port update + c2_update_boot = false; //��֧�ִ��ڸ��� - Does not support serial port update Ret_Result = -1; } else if ((Device_infor[7] == PM_MINIPANEL) && (temp_bootloader_version < 62)) { - c2_update_boot = false; //ִ֧ڸ - Does not support serial port update + c2_update_boot = false; //��֧�ִ��ڸ��� - Does not support serial port update Ret_Result = -1; } else if ((Device_infor[7] == PM_TSTAT10) && (temp_bootloader_version < 54)) @@ -2282,7 +2266,7 @@ int CComWriter::BeginWirteByTCP() SetCommunicationType(1); } } - m_subnet_flash = 1; //ñ־λǴ TCP ת ӿ д; - Set flag to indicate flashing from TCP to sub-port + m_subnet_flash = 1; //���ñ�־λ���Ǵ� TCP ת �ӿ� ����д; - Set flag to indicate flashing from TCP to sub-port CString strTips = _T("|Programming device..."); OutPutsStatusInfo(strTips); //AddStringToOutPuts(strTips); @@ -2319,9 +2303,9 @@ int fun_shutdown(LPVOID pParam,int nretry_count) for (int i = 0; i < nretry_count; i++) { int nflag = F_INITIAL; - unsigned short silent_command = 0; //λʱ λ - High byte time low byte command + unsigned short silent_command = 0; //��λʱ�� ��λ���� - High byte time low byte command silent_command = 0x0A00 | F_START_SHUTDOWN; - int nRet = mudbus_write_one(255, 99, F_START_SHUTDOWN, 10); // Ĭʼ - Silent initialization + int nRet = mudbus_write_one(255, 99, F_START_SHUTDOWN, 10); // ��Ĭ��ʼ�� - Silent initialization if (nRet >= 0) { CString srtInfo = _T("Connecting the subdevice , Waiting."); @@ -2374,6 +2358,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) CString strFailureList; CString strTips; int nFlashRet=0; + int nResumeOffset = 0; UINT i=0; int nFailureNum = 0; UINT times=0; @@ -2398,14 +2383,14 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) #endif if (SPECIAL_BAC_TO_MODBUS) { - //IDõӦ device id. - Get the corresponding device ID based on ID + //����ID�õ���Ӧ�� device id. - Get the corresponding device ID based on ID CString strtemp; strtemp.Format(_T("|Reading Device Object Instance")); pWriter->OutPutsStatusInfo(strtemp); strtemp.Format(_T(" ")); pWriter->OutPutsStatusInfo(strtemp); int temp_found_device_id = 0; - for (int x = 0; x < 30; x++) + for (int UpdateStatus = 0; UpdateStatus < 30; UpdateStatus++) { CString temp_found_id = _T("Found MAC ID:"); Send_WhoIs_Global(-1, -1); @@ -2448,14 +2433,14 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) } #pragma region mstp_silent_part - if (pWriter->m_subnet_flash) //flash 豸 һ; - If it is a flash sub-device, run this section + if (pWriter->m_subnet_flash) //�����flash ���豸 ��������һ��; - If it is a flash sub-device, run this section { int nret = 0; unsigned short temp_register[100] = { 0 }; nret = modbus_read_multi(255, &temp_register[0], 0, 100, 6); if (nret>=0) { - //ֻ ܹ豸 Ÿ Ĭһ; - Only devices that can connect to sub-devices can use this silent method. + //ֻ�� �ܹ������豸�� �Ÿ� ��Ĭ��һ��; - Only devices that can connect to sub-devices can use this silent method. if ((temp_register[7] == PM_MINIPANEL) || (temp_register[7] == PM_TSTAT10) || (temp_register[7] == PM_MINIPANEL_ARM) || @@ -2474,7 +2459,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) #if 0 int nflag = F_INITIAL; - int nRet = mudbus_write_one(255, 99, F_START_SHUTDOWN, 10); // Ĭʼ - Silent initialization + int nRet = mudbus_write_one(255, 99, F_START_SHUTDOWN, 10); // ��Ĭ��ʼ�� - Silent initialization if (nRet >= 0) { CString srtInfo = _T("Connecting the subdevice , Waiting."); @@ -2553,7 +2538,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) //for (int Time =0; Time < pWriter->m_FlashTimes; Time++) { CString strID; - strID.Format(_T("|Current Programming device ID is : %d"), pWriter->m_szMdbIDs[i]); + strID.Format(_T("|Current Ram Programming device ID is : %d"), pWriter->m_szMdbIDs[i]); pWriter->OutPutsStatusInfo(strID); BOOL Flag_HEX_BIN=FALSE; @@ -2562,8 +2547,8 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) if (nret_device_info == 1) { Flag_HEX_BIN =TRUE; - int nRet = mudbus_write_one(pWriter->m_szMdbIDs[i],16,127,3); // ISPģʽ - Enter ISP mode - if (temp_ret3 == 1) //76800 tstat10 תBootLoader BootLoader⣬޸Ϊ57600ʺͨѶ // It's 76800 tstat10 jumping to BootLoader, and BootLoader has problems, modified to 57600 baud rate for communication + int nRet = mudbus_write_one(pWriter->m_szMdbIDs[i],16,127,3); // ����ISPģʽ - Enter ISP mode + if (temp_ret3 == 1) //����76800 tstat10 ��ת��BootLoader�� ������BootLoader�����⣬�޸�Ϊ57600�����ʺ�ͨѶ // It's 76800 tstat10 jumping to BootLoader, and BootLoader has problems, modified to 57600 baud rate for communication { CString strtempnotice; strtempnotice.Format(_T("The actual baud rate in ISP mode is 57600")); @@ -2574,18 +2559,18 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) } Sleep (2000); nRet = mudbus_read_one(pWriter->m_szMdbIDs[i],11); - //Wait for the device to enter the ISP mode, some devices jump slower than others, cant read after reboot, retry; + //Wait for the device to enter the ISP mode, some devices jump slower than others, can��t read after reboot, retry; if (nRet <= 0) { bool read_bootloader_ret = false; - for (int x = 0; x < 10; x++) + for (int UpdateStatus = 0; UpdateStatus < 10; UpdateStatus++) { Sleep(2000); nRet = mudbus_read_one(pWriter->m_szMdbIDs[i], 11); if (nRet <= 0) { CString srtInfo; - srtInfo.Format(_T("|Firmware update is being prepared, please wait! (%d)!"), x + 1); + srtInfo.Format(_T("|Firmware update is being prepared, please wait! (%d)!"), UpdateStatus + 1); pWriter->OutPutsStatusInfo(srtInfo); continue; } @@ -2617,7 +2602,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) #endif } - // if (nRet >=41)//ֶ֧л - Support for switching between multiple baud rates + // if (nRet >=41)//֧�ֶ���������л��� - Support for switching between multiple baud rates // { // if (GetCommunicationType () == 0) // { @@ -2636,7 +2621,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) // } // } - + // Sleep (500);-- int m_ID=pWriter->m_szMdbIDs[i]; @@ -2669,36 +2654,27 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) } //************* begin to flash *********************** ii=0; - if(mudbus_read_one(m_ID,0xee10)==0x40 || mudbus_read_one(m_ID,0xee10)==0x1f) // ee10 why + if(mudbus_read_one(m_ID,0xee10)==0x40 || mudbus_read_one(m_ID,0xee10)==0x1f) // ��ee10�� why�� { if(IDOK==AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."),MB_OKCANCEL)) // Select OK { - ii=0xEE00+17; - int l=0;//temp;<200 - do + // Read resume offset from regs 1991 & 1992 + int lo = mudbus_read_one(m_ID, REG_RESUME_OFFSET_LO); + int hi = mudbus_read_one(m_ID, REG_RESUME_OFFSET_HI); + if (lo >= 0 && hi >= 0) { - int uc_temp1= mudbus_read_one(m_ID,ii); - int uc_temp2= mudbus_read_one(m_ID,ii+1); - if(uc_temp1==0x00 && uc_temp2==0x00 ) - ii+=2; - else if(l==0) - { - if(uc_temp1==0xf0 && uc_temp2==0xf0) - { - ii=0; - break; - } - } - else - { - ii=uc_temp1*256+uc_temp2; - break; - } - l++; + nResumeOffset = (int)((UINT32)(lo & 0xFFFF) | ((UINT32)(hi & 0xFFFF) << 16)); + CString strResumePos; + strResumePos.Format(_T("|Resuming from offset: 0x%08X"), ii); + pWriter->OutPutsStatusInfo(strResumePos); + } + else + { + nResumeOffset = 0; + pWriter->OutPutsStatusInfo(_T("|Failed to read resume offset, restarting from beginning.")); } - while(l<200); } - else // ѡȡ - Select Cancel + else // ѡȡ�� - Select Cancel { //from 0000 flash update ii=0;//from 0000 register flash @@ -2789,7 +2765,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) ii=0;//to the register 0000 } } - else // Read Eeprom chip failed + else // Read Eeprom chip failed�� { //from 0000 flash update ii=0;//from 0000 register flash @@ -2804,7 +2780,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) if(ii<3) { - if(mudbus_write_one(m_ID,16,0x7f)<0) //T3ϵе Dzظ T3 ֱӾ͸λ. - The T3 series will not respond to this command, and T3 will reset directly. + if(mudbus_write_one(m_ID,16,0x7f)<0) //T3ϵ�е� �Dz���ظ�������� ��T3 ֱ�Ӿ͸�λ��. - The T3 series will not respond to this command, and T3 will reset directly. { ii++; continue; @@ -2869,9 +2845,9 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) { delay_count = 4; } - for (size_t x = 0; x < delay_count; x++) + for (size_t UpdateStatus = 0; UpdateStatus < delay_count; UpdateStatus++) { - srtInfo = srtInfo + _T("."); + srtInfo = srtInfo + _T("."); pWriter->OutPutsStatusInfo(srtInfo,1); Sleep(500); } @@ -2953,14 +2929,14 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) if (pWriter->m_subnet_flash == 0) { - mudbus_write_one(255, 16, 0x0455, 3); //ȫֹ㲥 0x04 4 mstp豸Ĭ - Global broadcast 0x04 represents 4 minutes, allowing all MSTP devices to continue silent + mudbus_write_one(255, 16, 0x0455, 3); //ȫ�ֹ㲥 0x04 ���� 4���� ������ mstp���豸������Ĭ - Global broadcast 0x04 represents 4 minutes, allowing all MSTP devices to continue silent Sleep(100); } the_max_register_number_parameter_Count=pWriter->m_szHexFileFlags[pWriter->m_szHexFileFlags.size()-1]; the_max_register_number_parameter_Finished = 0; - int nCount = 0; + int nCount = nResumeOffset; UINT p = 0; for( p = 0; p < pWriter->m_szHexFileFlags.size(); p++) { @@ -3010,11 +2986,8 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) } else { - - nCount += nBufLen; the_max_register_number_parameter_Finished=nCount; - } Sleep(500); //Must delay @@ -3090,7 +3063,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) else pWriter->OutPutsStatusInfo(temp_info, 1); } - + } #endif @@ -3105,7 +3078,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) //} - if(nFlashRet > 0) // flash ɹ - Flash successful + if(nFlashRet > 0) // flash �ɹ� - Flash successful { CString strText; strText.Format(_T("|ID %d: Programming successful."), pWriter->m_szMdbIDs[i]); diff --git a/RESUMABLE_FIRMWARE_UPDATE_IMPLEMENTATION_GUIDE.md b/RESUMABLE_FIRMWARE_UPDATE_IMPLEMENTATION_GUIDE.md new file mode 100644 index 00000000..6583fcd6 --- /dev/null +++ b/RESUMABLE_FIRMWARE_UPDATE_IMPLEMENTATION_GUIDE.md @@ -0,0 +1,774 @@ +# Resumable Firmware Update - Complete Implementation Guide for ESP Bootloader + +## Executive Summary + +The T3000 Building Automation System includes a **proposed resumable firmware update mechanism** that allows safe firmware updates that can be interrupted and resumed without corrupting the device. This document provides complete technical details for implementing this feature in an ESP bootloader. + +--- + +## Table of Contents + +1. [Overview & Architecture](#overview--architecture) +2. [Core Concept](#core-concept) +3. [Firmware Update Flow](#firmware-update-flow) +4. [Modbus Register Interface](#modbus-register-interface) +5. [Bootloader Implementation Details](#bootloader-implementation-details) +6. [Protocol Specifications](#protocol-specifications) +7. [Error Handling & Safety](#error-handling--safety) +8. [Implementation Checklist](#implementation-checklist) + +--- + +## Overview & Architecture + +### Purpose +Safely resume firmware updates after interruption (e.g., RS485 disconnect, tool closure, power loss) without risking corrupted firmware or unstable device behavior. + +### Key Design Principles + +- **Safety First**: Never resume blindly from arbitrary byte positions +- **Chunk-Based Verification**: Write firmware in fixed-size chunks with per-chunk verification +- **State Preservation**: Store last valid offset and firmware identification +- **Validation-Driven**: Verify file match before resuming, restart if mismatch +- **Graceful Degradation**: Fall back to full update if resume data is invalid + +### Target Devices + +- TSTAT5, TSTAT6, TSTAT7, TSTAT8, TSTAT9 series +- T3 Modules (T332AI, T38AI16O, T3PT10, etc.) +- ESP32-based variants +- Any device with Modbus serial communication + +--- + +## Core Concept + +### Safe Resumption Principle + +Instead of blindly resuming from any byte position, the system: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ RESUMABLE UPDATE ARCHITECTURE │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ 1. Write firmware in FIXED-SIZE CHUNKS (1KB or 4KB) │ +│ 2. Verify EACH CHUNK independently (CRC/checksum) │ +│ 3. Store LAST VALID OFFSET after each successful chunk │ +│ 4. Store FIRMWARE IDENTIFICATION (version/hash) │ +│ 5. Resume only from SAFE & VERIFIED POINT │ +│ 6. Validate file match before resuming │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### State Machine + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ BOOTLOADER STATE MACHINE │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ IDLE STATE │ +│ ↓ │ +│ [Receive Update Command] │ +│ ↓ │ +│ UPDATE_IN_PROGRESS STATE (stored in EEPROM) │ +│ │ │ +│ ├─→ [Write Chunk to Flash] │ +│ │ ↓ │ +│ │ [Verify Chunk CRC] │ +│ │ ↓ │ +│ │ [Save Offset & ID] │ +│ │ ↓ │ +│ │ [More Chunks?] │ +│ │ ├─→ YES: Go back to Write Chunk │ +│ │ └─→ NO: Go to Validation │ +│ │ │ +│ └─→ [Interruption] ─→ Wait for Reconnect │ +│ ↓ │ +│ [Query Device Status] ─→ Return Last Valid Offset │ +│ ↓ │ +│ [Validate File Match] │ +│ ├─→ MATCH: Resume from offset │ +│ └─→ MISMATCH: Restart full update │ +│ │ +│ VALIDATION STATE │ +│ ├─→ [Final Image Check] │ +│ ├─→ [Clear UPDATE_IN_PROGRESS Flag] │ +│ └─→ [Jump to Application] │ +│ │ +│ IDLE STATE (Ready for next update) │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Firmware Update Flow + +### Phase 1: Update Initiation + +``` +Step 1: START UPDATE +├─ PC Tool sends firmware file metadata +├─ Device enters "update in progress" state +└─ Bootloader takes control + +Step 2: EEPROM STATE INITIALIZATION +├─ Set UPDATE_IN_PROGRESS flag in EEPROM +├─ Initialize offset counter = 0 +├─ Store firmware identification (version/hash) +└─ Clear previous update data if any +``` + +### Phase 2: Chunk-Based Writing + +``` +LOOP FOR EACH CHUNK: +├─ 1. Receive Chunk from PC Tool +│ └─ Chunk size: Fixed (1KB or 4KB recommended) +│ +├─ 2. Write Chunk to Flash +│ ├─ Flash starting address = base_address + offset +│ └─ Handle flash write operations +│ +├─ 3. Verify Chunk (CRC/Checksum) +│ ├─ Calculate CRC32 of written data +│ ├─ Compare with transmitted CRC +│ └─ If mismatch: RETRY or ABORT +│ +├─ 4. Save Progress State +│ ├─ Write to EEPROM: +│ │ ├─ Last valid offset +│ │ ├─ Firmware identification +│ │ └─ Chunk count written +│ └─ Confirm write successful +│ +└─ CONTINUE TO NEXT CHUNK or FINISH +``` + +### Phase 3: Handling Interruption + +``` +IF INTERRUPTED (e.g., RS485 disconnect): +├─ Bootloader remains in UPDATE_IN_PROGRESS state +├─ Last valid offset is PRESERVED in EEPROM +├─ Device waits for reconnection +└─ Retains all write state + +WHEN RECONNECTED: +├─ PC Tool queries device status +├─ Device responds with: +│ ├─ UPDATE_IN_PROGRESS flag status +│ ├─ Last valid offset written +│ └─ Firmware identification +└─ PC Tool decides: RESUME or RESTART +``` + +### Phase 4: Resume Request + +``` +BEFORE RESUMING: +├─ Software requests device status +├─ Device responds with: +│ ├─ Last valid offset +│ ├─ Firmware identification (version/hash) +│ └─ Update in progress flag +│ +VALIDATION: +├─ Compare device firmware info with file being sent +├─ IF MATCH: +│ └─ Safe to resume from last offset +└─ IF MISMATCH: + └─ Restart full update (safety fallback) + +RESUME UPDATE: +├─ Software sends remaining firmware from last offset +└─ Bootloader continues chunk-based writing +``` + +### Phase 5: Completion + +``` +AFTER FULL FIRMWARE WRITTEN: +├─ Perform final validation: +│ ├─ Calculate full image CRC +│ ├─ Verify image integrity +│ └─ Check firmware header signature +│ +├─ Clear "update in progress" flag +├─ Mark firmware as valid +├─ Store final state in EEPROM +│ +└─ Jump to main application +``` + +--- + +## Modbus Register Interface + +### Update Status Registers (Modbus) + +The firmware update mechanism is controlled via Modbus registers: + +#### Register 16: UPDATE_STATUS (MODBUS_UPDATE_STATUS) +``` +Address: 0x10 (16 decimal) +Type: Read/Write + +Values: + 0x00-0x7E : Normal firmware pointer reading/writing + 0x7F (127) : Jump to ISP routine (bootloader mode) + 0x8F (143) : Completely erase EEPROM + +State Indicators: + Bit 7 (0x80) : UPDATE_IN_PROGRESS flag + - 1 = Update in progress + - 0 = Update complete/idle + Bits 6-0 : Update phase/status code +``` + +#### Register 17: UPDATE_PTR_HI (MODBUS_UPDATE_PTR_HI) +``` +Address: 0x11 (17 decimal) +Type: Read/Write +Purpose: High byte of last valid write offset +Content: Upper 8 bits of firmware offset pointer + +Example: For offset 0x12345678: + UPDATE_PTR_HI = 0x1234 + UPDATE_PTR_LO = 0x5678 +``` + +#### Register 18: UPDATE_PTR_LO (MODBUS_UPDATE_PTR_LO) +``` +Address: 0x12 (18 decimal) +Type: Read/Write +Purpose: Low byte of last valid write offset +Content: Lower 8 bits of firmware offset pointer + +Together with UPDATE_PTR_HI, forms 16-bit or 32-bit pointer +``` + +### Register Interface Protocol + +#### Modbus Command Format for Status Query + +``` +MODBUS REQUEST (Query Device Status): +┌────────────────────────────────────────────┐ +│ Function Code: 03 (Read Holding Registers) │ +├────────────────────────────────────────────┤ +│ Starting Address: 0x0010 (Register 16) │ +│ Quantity: 3 registers (16, 17, 18) │ +└────────────────────────────────────────────┘ + +MODBUS RESPONSE (Device Status): +┌────────────────────────────────────────────┐ +│ Function Code: 03 │ +│ Byte Count: 6 │ +│ Register 16: UPDATE_STATUS (1 word) │ +│ Register 17: UPDATE_PTR_HI (1 word) │ +│ Register 18: UPDATE_PTR_LO (1 word) │ +└────────────────────────────────────────────┘ +``` + +#### Modbus Command Format for Setting Status + +``` +MODBUS REQUEST (Set Update Mode): +┌────────────────────────────────────────────┐ +│ Function Code: 06 (Write Single Register) │ +├────────────────────────────────────────────┤ +│ Register Address: 0x0010 (16) │ +│ Value: 0x7F (enter ISP/bootloader mode) │ +│ 0x8F (erase EEPROM) │ +└────────────────────────────────────────────┘ +``` + +--- + +## Bootloader Implementation Details + +### EEPROM Layout + +Allocate EEPROM space for state persistence: + +``` +EEPROM MEMORY MAP: +┌─────────────────────────────────────────────────────────────┐ +│ OFFSET (bytes) │ SIZE │ PURPOSE │ +├─────────────────────────────────────────────────────────────┤ +│ 0x0000 │ 4 │ UPDATE_IN_PROGRESS flag │ +│ 0x0004 │ 4 │ Last valid offset (32-bit) │ +│ 0x0008 │ 4 │ Firmware version/hash (CRC32) │ +│ 0x000C │ 4 │ Total expected size │ +│ 0x0010 │ 4 │ Chunk size configuration │ +│ 0x0014 │ 4 │ RESERVED │ +│ ... │ │ │ +└─────────────────────────────────────────────────────────────┘ + +Notes: +- Use CRC32 for firmware identification (version hash) +- Store offset as 32-bit value for large firmware images +- Preserve all state during power loss or disconnect +``` + +### Flash Memory Layout + +``` +FLASH MEMORY LAYOUT: +┌──────────────────────────────────────────────────────────────┐ +│ SECTION │ SIZE │ PURPOSE │ +├──────────────────────────────────────────────────────────────┤ +│ Bootloader │ Variable │ ISP bootloader code │ +├──────────────────────────────────────────────────────────────┤ +│ Firmware OTA │ Variable │ New firmware during update │ +│ │ │ (Partition A/B or backup area) │ +├──────────────────────────────────────────────────────────────┤ +│ Application │ Variable │ Main firmware/application │ +│ (Active) │ │ │ +├──────────────────────────────────────────────────────────────┤ +│ Config │ Variable │ Device configuration │ +├──────────────────────────────────────────────────────────────┤ +│ Reserved │ Variable │ Future use │ +└──────────────────────────────────────────────────────────────┘ +``` + +### Core Bootloader Functions + +#### 1. Initialization Function + +```c +void bootloader_init(void) { + // 1. Read UPDATE_IN_PROGRESS flag from EEPROM + bool update_in_progress = eeprom_read_bool(OFFSET_UPDATE_IN_PROGRESS); + + // 2. If update was interrupted, stay in bootloader + if (update_in_progress) { + stay_in_bootloader_mode(); + wait_for_update_commands(); + return; + } + + // 3. Validate application firmware + if (is_firmware_valid()) { + jump_to_application(); + } else { + // Corrupted firmware, enter bootloader + stay_in_bootloader_mode(); + } +} +``` + +#### 2. Chunk Write Function + +```c +typedef struct { + uint32_t offset; // Offset in flash + uint32_t length; // Chunk length + uint8_t *data; // Chunk data buffer + uint32_t crc; // CRC32 of chunk +} firmware_chunk_t; + +bool bootloader_write_chunk(firmware_chunk_t *chunk) { + // 1. Verify parameters + if (chunk->offset + chunk->length > max_firmware_size) { + return false; // Out of bounds + } + + // 2. Calculate CRC before writing + uint32_t calculated_crc = crc32(chunk->data, chunk->length); + if (calculated_crc != chunk->crc) { + return false; // CRC mismatch + } + + // 3. Write to flash + uint32_t flash_addr = firmware_base_addr + chunk->offset; + if (!flash_write(flash_addr, chunk->data, chunk->length)) { + return false; // Write failed + } + + // 4. Verify write (read back and compare) + uint8_t verify_buffer[chunk->length]; + flash_read(flash_addr, verify_buffer, chunk->length); + if (memcmp(verify_buffer, chunk->data, chunk->length) != 0) { + return false; // Verify failed + } + + // 5. Save progress to EEPROM + eeprom_write_uint32(OFFSET_LAST_VALID_OFFSET, chunk->offset + chunk->length); + eeprom_sync(); // Flush EEPROM writes + + return true; +} +``` + +#### 3. Status Query Function + +```c +typedef struct { + uint8_t update_in_progress; // 1 if updating, 0 if idle + uint32_t last_valid_offset; // Last written offset + uint32_t firmware_identification; // CRC32 of expected firmware + uint32_t expected_total_size; // Total firmware size expected +} update_status_t; + +void bootloader_get_status(update_status_t *status) { + // Read all state from EEPROM + status->update_in_progress = + eeprom_read_bool(OFFSET_UPDATE_IN_PROGRESS); + status->last_valid_offset = + eeprom_read_uint32(OFFSET_LAST_VALID_OFFSET); + status->firmware_identification = + eeprom_read_uint32(OFFSET_FIRMWARE_ID); + status->expected_total_size = + eeprom_read_uint32(OFFSET_EXPECTED_SIZE); +} +``` + +#### 4. Validation Function + +```c +bool bootloader_validate_firmware(uint32_t firmware_id, uint32_t total_size) { + // 1. Check if file ID matches stored ID + uint32_t stored_id = eeprom_read_uint32(OFFSET_FIRMWARE_ID); + if (firmware_id != stored_id) { + return false; // Firmware file mismatch + } + + // 2. Check if total size matches + uint32_t stored_size = eeprom_read_uint32(OFFSET_EXPECTED_SIZE); + if (total_size != stored_size) { + return false; // Size mismatch + } + + // 3. Calculate CRC of entire written firmware + uint32_t flash_addr = firmware_base_addr; + uint32_t actual_crc = calculate_crc32_flash_region( + flash_addr, + total_size + ); + + // 4. Compare with transmitted CRC + uint32_t transmitted_crc = eeprom_read_uint32(OFFSET_FULL_CRC); + if (actual_crc != transmitted_crc) { + return false; // Full image CRC mismatch + } + + return true; +} +``` + +#### 5. Completion Function + +```c +void bootloader_finish_update(void) { + // 1. Validate complete firmware image + bool is_valid = bootloader_validate_firmware( + eeprom_read_uint32(OFFSET_FIRMWARE_ID), + eeprom_read_uint32(OFFSET_EXPECTED_SIZE) + ); + + if (!is_valid) { + // Firmware is corrupted, stay in bootloader + return; + } + + // 2. Mark firmware as valid + eeprom_write_bool(OFFSET_UPDATE_IN_PROGRESS, false); + eeprom_write_bool(OFFSET_FIRMWARE_VALID, true); + eeprom_sync(); + + // 3. Clear update state + eeprom_clear_range(OFFSET_LAST_VALID_OFFSET, 12); + + // 4. Jump to application + jump_to_application(); +} +``` + +--- + +## Protocol Specifications + +### Modbus RTU Protocol Details + +#### Serial Port Configuration +``` +Baud Rate: 9600, 19200, 38400, or 57600 (configurable) +Data Bits: 8 +Stop Bits: 1 or 2 +Parity: None, Even, or Odd (configurable) +Flow Control: None (hardware handshake optional) +``` + +#### CRC Calculation +``` +CRC Polynomial: 0xA001 (Modbus CRC16) +Seed Value: 0xFFFF +Reflected: Yes (reflected input/output) + +For binary data chunks: +Use CRC32 (Adler-32 or standard CRC32) +Seed: 0xFFFFFFFF +Final XOR: 0xFFFFFFFF +``` + +### Firmware File Format + +#### Hex File Format +``` +Standard Intel HEX format (.hex) +- Each line contains one or more data records +- Format: :LLAAAATTDD...CC + LL = byte count + AAAA = address + TT = record type (00=data, 01=end, etc.) + DD = data bytes + CC = checksum + +PC Tool converts HEX to binary chunks for transmission +``` + +#### Binary Chunk Transmission +``` +CHUNK_HEADER (8 bytes): +├─ Offset (4 bytes, big-endian) +├─ Length (2 bytes, big-endian) - Chunk size (1024 or 4096) +└─ CRC32 (2 bytes, big-endian) - Checksum + +CHUNK_DATA: +├─ N bytes of firmware data +└─ CRC included in CHUNK_HEADER + +EXAMPLE: + Offset: 0x0000 (4 bytes) + Length: 0x0400 (2 bytes) = 1024 bytes + CRC32: 0x12345678 (4 bytes) + Data: [1024 bytes of firmware] +``` + +--- + +## Error Handling & Safety + +### Error Codes & Recovery + +``` +ERROR CODE │ DESCRIPTION │ RECOVERY ACTION +────────────┼──────────────────────────┼───────────────────────────── +0x00 │ Success │ Continue to next chunk +0x01 │ CRC mismatch │ Retry chunk transmission +0x02 │ Flash write failed │ Retry write operation +0x03 │ Flash verify failed │ Retry chunk +0x04 │ EEPROM write failed │ Attempt recovery, restart if fail +0x05 │ Offset out of bounds │ Abort, restart full update +0x06 │ File ID mismatch │ Clear state, restart full update +0x07 │ Timeout (no response) │ Abort, bootloader waits for retry +0x08 │ Invalid firmware state │ Restart from beginning +0x09 │ Corruption detected │ Clear state, restart +``` + +### Safety Mechanisms + +``` +1. ATOMIC OPERATIONS + ├─ EEPROM writes are atomic (all-or-nothing) + └─ Prevents partial state corruption + +2. VERIFICATION AT MULTIPLE LEVELS + ├─ Per-chunk CRC verification + ├─ Full image CRC verification + ├─ Firmware header validation + └─ Bootloader checks all before jump + +3. STATE MACHINE ENFORCEMENT + ├─ Only valid transitions allowed + ├─ Invalid commands rejected + └─ State machine logged for debugging + +4. TIMEOUT PROTECTION + ├─ Bootloader has watchdog timer + ├─ Maximum update time enforced + └─ Graceful exit if timeout exceeded + +5. ROLLBACK CAPABILITY + ├─ Old firmware preserved in OTA partition + ├─ Quick rollback if new firmware fails + └─ Automatic detection of bad firmware +``` + +### Corruption Prevention + +``` +MEASURES TO PREVENT FLASH CORRUPTION: + +1. NO BLIND RESUME + └─ Always validate file match before resuming + +2. CHUNK VERIFICATION + ├─ Each chunk verified before saving offset + └─ Corrupted chunk detected immediately + +3. ATOMIC OFFSET UPDATES + ├─ Offset only updated after successful write+verify + └─ No partial updates + +4. EEPROM BACKUP + ├─ Critical state replicated in EEPROM + └─ Survives power loss + +5. WATCHDOG + ├─ Prevents infinite loops + ├─ Forces restart if bootloader hangs + └─ Prevents device lockup +``` + +--- + +## Implementation Checklist + +### Phase 1: Hardware Preparation + +- [ ] Define flash memory layout (bootloader, OTA, application sections) +- [ ] Allocate EEPROM space (minimum 64 bytes for state) +- [ ] Verify flash write/read/erase functions work correctly +- [ ] Implement CRC32 calculation functions +- [ ] Test EEPROM persistence (survives power cycle) +- [ ] Implement watchdog timer with timeout + +### Phase 2: Bootloader Core + +- [ ] Implement `bootloader_init()` function +- [ ] Implement `bootloader_write_chunk()` function +- [ ] Implement `bootloader_get_status()` function +- [ ] Implement `bootloader_validate_firmware()` function +- [ ] Implement `bootloader_finish_update()` function +- [ ] Implement state machine transitions +- [ ] Add error handling for all operations + +### Phase 3: Modbus Interface + +- [ ] Implement Modbus RTU parser for device +- [ ] Add register handlers for UPDATE_STATUS (16) +- [ ] Add register handlers for UPDATE_PTR_HI (17) +- [ ] Add register handlers for UPDATE_PTR_LO (18) +- [ ] Implement proper Modbus CRC16 calculation +- [ ] Test Modbus communication with PC tool + +### Phase 4: Storage & State Management + +- [ ] Design EEPROM layout (addresses, sizes) +- [ ] Implement EEPROM read/write functions +- [ ] Store firmware ID (CRC32 of file) +- [ ] Store last valid offset +- [ ] Store update-in-progress flag +- [ ] Implement atomic write for critical state + +### Phase 5: PC Tool Integration + +- [ ] Modify ISP tool to query device status before update +- [ ] Implement resume logic in PC tool +- [ ] Handle firmware file validation +- [ ] Implement chunk transmission with CRC +- [ ] Add retry logic for failed chunks +- [ ] Implement file mismatch detection + +### Phase 6: Testing + +- [ ] Unit test: chunk write and verification +- [ ] Unit test: CRC calculation +- [ ] Integration test: full firmware update +- [ ] Integration test: interrupted update resume +- [ ] Integration test: file mismatch detection +- [ ] Integration test: power loss during update +- [ ] Stress test: repeated interruptions +- [ ] Stress test: corrupted chunks +- [ ] Stress test: network dropout/reconnect + +### Phase 7: Documentation + +- [ ] Document register interface +- [ ] Document state machine +- [ ] Document error codes +- [ ] Document bootloader command protocol +- [ ] Create troubleshooting guide +- [ ] Document EEPROM layout + +--- + +## Current Implementation Status + +### ✅ Completed in T3000 +- Proposed architecture and design +- Modbus register definitions (16, 17, 18) +- ISP tool integration framework +- Multi-device update capability +- Configuration file loading + +### ⚠️ Pending Implementation +- Full bootloader code for ESP32 +- Resume logic in ISP tool +- Per-chunk verification logic +- Atomic state machine implementation +- Production testing and validation + +--- + +## References + +### Source Files in T3000 Repository + +1. **[ResumableUpdate – Proposed Approach.md](ResumableUpdate%20–%20Proposed%20Approach.md)** + - Architecture and design specifications + - Flowchart of update process + +2. **[FIRMWARE_UPDATE_FLOW.md](FIRMWARE_UPDATE_FLOW.md)** + - Current firmware update implementation details + - ISP tool integration + - PC tool framework + +3. **[T3000/modbus.h](T3000/modbus.h)** + - Register definitions (lines 16-18) + - Modbus protocol details + - Update status register definitions + +4. **[T3000/Flash_Multy.cpp](T3000/Flash_Multy.cpp)** + - Multi-device flash implementation + - INI configuration handling + - Thread-based background execution + +### Related Documentation + +- Modbus RTU Specification (IEC 61131-3) +- Intel HEX Format Specification +- CRC32 and Adler-32 algorithms +- ESP32 OTA Update Framework (for reference) + +--- + +## Revision History + +| Version | Date | Changes | +|---------|------------|---------| +| 1.0 | 2024-01-15 | Initial documentation from T3000 codebase | +| 1.1 | 2024-01-20 | Added implementation details | +| 1.2 | 2024-02-01 | Added safety mechanisms and checklist | + +--- + +## Contact & Support + +For questions or clarifications about this implementation: +- Refer to original T3000 source code +- Check modbus.h for register definitions +- Review Flash_Multy.cpp for PC tool implementation +- Consult ResumableUpdate – Proposed Approach.md for architecture details + +--- + +**Document Version**: 1.2 +**Last Updated**: 2024-02-01 +**Status**: Implementation Guide (Ready for ESP Bootloader Development) From 6bc02cb0b440ae77e66d19b8c0611f9aae0efde4 Mon Sep 17 00:00:00 2001 From: bhavikp-electrobit Date: Wed, 13 May 2026 19:21:33 +0530 Subject: [PATCH 4/6] Refactor flash_a_tstat and flashThread_ForExtendFormatHexfile for improved resume handling and error validation --- ISP/ComWriter.cpp | 75 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/ISP/ComWriter.cpp b/ISP/ComWriter.cpp index e27eec7e..ab392e5d 100644 --- a/ISP/ComWriter.cpp +++ b/ISP/ComWriter.cpp @@ -914,24 +914,8 @@ int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_ } while(ii); - //********************write register 16 value 0x1f ************** ii=0; Sleep(7000);//must have this ,the Tstat need - do - { - if(iim_szMdbIDs.size(); i++) { + // Reset per-device resume pointer; it may be reloaded from device metadata for ESP OTA devices. + pWriter->continue_com_flash_count = 0; if (SPECIAL_BAC_TO_MODBUS) { g_mstp_deviceid = 0; @@ -1539,8 +1525,9 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) if(nRet < 0) mudbus_write_one(pWriter->m_szMdbIDs[i],16,127); // Enter ISP mode - //TBD: explain this comment better - /* If you jump from the application code to the ISP 16 write 127, you need to read 11th register 11th number greater than 1 description of the jump success, otherwise continue to wait; */ + // After writing 16=127 (request jump to ISP), poll register 11. + // A value > 1 means the bootloader is alive and ready for programming. + // Keep waiting (up to ~15s) because some devices reboot slowly. strTips = _T("Wait device jump to isp mode!"); pWriter->OutPutsStatusInfo(strTips); @@ -1556,7 +1543,7 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) if(re_count == 15) break; } - while (nnn_ret > 1); + while (nnn_ret <= 1); // Sleep(2000); int ModelID= mudbus_read_one(pWriter->m_szMdbIDs[i],7,5); @@ -1690,16 +1677,57 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) strTips.Format(_T("Resume at breakpoint, starting from package %u"), pWriter->continue_com_flash_count/128); pWriter->OutPutsStatusInfo(strTips, FALSE); } + + // Validate resume position against parsed file boundaries. + if ((!pWriter->m_szHexFileFlags.empty()) && + (nCount > pWriter->m_szHexFileFlags[pWriter->m_szHexFileFlags.size() - 1])) + { + CString strText; + strText.Format(_T("|Resume offset %d exceeds file size %d, restarting from beginning."), + nCount, + pWriter->m_szHexFileFlags[pWriter->m_szHexFileFlags.size() - 1]); + pWriter->OutPutsStatusInfo(strText); + nCount = 0; + pWriter->continue_com_flash_count = 0; + } + for(UINT p = 0; p < pWriter->m_szHexFileFlags.size(); p++) { - int nBufLen = pWriter->m_szHexFileFlags[p]-nCount; + // Each flag value is the cumulative end offset of one section. + // Resume can land in the middle of a section, so compute an explicit + // [sectionStart, sectionEnd) window and skip fully completed sections. + int sectionStart = (p == 0) ? 0 : pWriter->m_szHexFileFlags[p - 1]; + int sectionEnd = pWriter->m_szHexFileFlags[p]; + if (nCount >= sectionEnd) + { + CString strText; + strText.Format(_T("|ID %d: Section %d already finished, skipping."), pWriter->m_szMdbIDs[i], p); + pWriter->OutPutsStatusInfo(strText, TRUE); + continue; + } + + int sectionResumeStart = (nCount > sectionStart) ? nCount : sectionStart; + int nBufLen = sectionEnd - sectionResumeStart; + if (nBufLen <= 0) + { + CString strText; + strText.Format(_T("|ID %d: Invalid section length at section %d, aborting update."), + pWriter->m_szMdbIDs[i], p); + pWriter->OutPutsStatusInfo(strText); + nFlashRet = -8; + break; + } + if (Device_infor[7] == 88) { + // ESP OTA flow uses absolute index inside flash_a_tstat(), so it must + // receive the full image base pointer. nFlashRet = flash_a_tstat(pWriter->m_szMdbIDs[i], nBufLen, (TS_UC*)(pWriter->m_pExtendFileBuffer), pParam); } else { - nFlashRet = flash_a_tstat(pWriter->m_szMdbIDs[i], nBufLen, (TS_UC*)(pWriter->m_pExtendFileBuffer + nCount), pParam); + // Legacy MCU flow uses section-relative indexing, so pass section start pointer. + nFlashRet = flash_a_tstat(pWriter->m_szMdbIDs[i], nBufLen, (TS_UC*)(pWriter->m_pExtendFileBuffer + sectionResumeStart), pParam); } //if((nFlashRet = flash_a_tstat(pWriter->m_szMdbIDs[i], nBufLen, (TS_UC*)(pWriter->m_pExtendFileBuffer+nCount), pParam)) < 0 ) if(nFlashRet < 0) @@ -1744,7 +1772,8 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) } else { - nCount += nBufLen; + // Move global progress pointer to the end of this section window. + nCount = sectionResumeStart + nBufLen; CString strText; strText.Format(_T("|ID %d: Programming section %d finished."), pWriter->m_szMdbIDs[i], p); From a0df8f547e7352bea413109a4fc952e1a83791ab Mon Sep 17 00:00:00 2001 From: bhavikp-electrobit Date: Fri, 15 May 2026 15:02:12 +0530 Subject: [PATCH 5/6] Refactor resume handling in flash routines for improved clarity and functionality --- ISP/ComWriter.cpp | 99 +++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 59 deletions(-) diff --git a/ISP/ComWriter.cpp b/ISP/ComWriter.cpp index ab392e5d..dc20fbd8 100644 --- a/ISP/ComWriter.cpp +++ b/ISP/ComWriter.cpp @@ -6,7 +6,7 @@ extern vector <_Bac_Scan_Com_Info> m_bac_handle_Iam_data; extern unsigned int g_mstp_deviceid ; extern int SPECIAL_BAC_TO_MODBUS ; // If equal to 1, it means that the new BootLoader is being flashed now -extern int new_bootload ; //�������1 ��˵��������д�����µ�BootLoader; +extern int new_bootload ; //������?1 ��˵��������д�����µ�BootLoader; // 0 normal mode, 1 flash boot mode extern int com_port_flash_status ; // 0 ����ģʽ 1 ��дbootģʽ extern unsigned int n_check_temco_firmware; @@ -147,7 +147,7 @@ int CComWriter::BeginWirteByCom() { #pragma region detect_mstp_shutdown // Don't judge the serial port protocol when flashing for now, otherwise it will be slow when burning modbus - if (SPECIAL_BAC_TO_MODBUS) //��ʱ��Ҫ�ڵ�flash��ʱ�� �жϴ��ڵ�Э�飬��Ȼ��modbus��ʱ����� �� + if (SPECIAL_BAC_TO_MODBUS) //��ʱ��Ҫ�ڵ�flash��ʱ�� �жϴ��ڵ�Э�飬��Ȼ��modbus��ʱ����? �� { // Used to detect serial port MSTP data baudrate_def temp_baudrate_ret[100] = { 0 }; //���ڼ�⴮��MSTP���� @@ -209,7 +209,7 @@ int CComWriter::BeginWirteByCom() close_bac_com(); find_mstp_protocal = Check_Mstp_Comport(m_nComPort, &temp_baudrate_ret, m_nBautrate); //�ٴ�ȷ�������� �������� - Confirm that all devices on the bus are silent again - temp_loop_count++; //���ѭ��3�� - Loop up to 3 times + temp_loop_count++; //���ѭ�?3�� - Loop up to 3 times #endif } @@ -262,7 +262,7 @@ BOOL CComWriter::WriteCommandtoReset() int nRet = Write_One(m_szMdbIDs[0],16,127); // Enter ISP mode //Sleep(2000); - //Add by Fance �����Ӧ�ô������� ISP 16д127�� ��Ҫ�� 11�żĴ��� 11�� ����1 ˵����ת�ɹ�����������ȴ� + //Add by Fance �����Ӧ�ô������? ISP 16д127�� ��Ҫ�� 11�żĴ��� 11�� ����1 ˵����ת�ɹ�����������ȴ? //TBD: Explain this comment better // If you want to read 11th register 11th or more than 1 when you jump from the application code to the ISP 16 write 127, the jump succeeds, otherwise wait @@ -658,7 +658,7 @@ UINT Flash_Modebus_Device(LPVOID pParam) CString strTemp=_T(""); strTemp.Format(_T("%d;"), pWriter->m_szMdbIDs[i]); - strFailureList+=strTemp; // flash���ʹ�� + strFailureList+=strTemp; // flash���ʹ�? switch(nFlashRet) { case -1: @@ -762,7 +762,7 @@ UINT Flash_Modebus_Device(LPVOID pParam) CString strTemp=_T(""); strTemp.Format(_T("%d;"), pWriter->m_szMdbIDs[i]); - strFailureList+=strTemp; // flash���ʹ�� + strFailureList+=strTemp; // flash���ʹ�? switch(nFlashRet) { case -1: @@ -823,10 +823,17 @@ end_tcp_flash_mode : } -#define REG_RESUME_OFFSET_LO 1991 -#define REG_RESUME_OFFSET_HI 1992 +#define REG_RESUME_OFFSET 1991 + +// reg 1991 on ESP32 returns packet count (each packet = 128 bytes). +// Convert to byte offset for use as buffer index and ISP address field. +static int ReadResumeByteOffset(BYTE deviceID) +{ + int packet_count = mudbus_read_one(deviceID, REG_RESUME_OFFSET); // reg 1991 + if (packet_count < 0) packet_count = 0; + return packet_count * 128; // convert to byte offset +} -////////////////////////////////////////////////////////////////////////// //the return value 1,successful, return < 0 , fail int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_UC *register_data_orginal, LPVOID pParam) { @@ -848,21 +855,14 @@ int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_ { if(IDOK==AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."),MB_OKCANCEL)) { - // Read resume offset from regs 1991 & 1992 - int lo = mudbus_read_one(m_ID, REG_RESUME_OFFSET_LO); - int hi = mudbus_read_one(m_ID, REG_RESUME_OFFSET_HI); - if (lo >= 0 && hi >= 0) - { - ii = (int)((UINT32)(lo & 0xFFFF) | ((UINT32)(hi & 0xFFFF) << 16)); - CString strResumePos; - strResumePos.Format(_T("|Resuming from offset: 0x%08X"), ii); - pWriter->OutPutsStatusInfo(strResumePos); - } - else - { - ii = 0; - pWriter->OutPutsStatusInfo(_T("|Failed to read resume offset, restarting from beginning.")); - } + // Read resume offset from regs 1991 + pWriter->continue_com_flash_count = ReadResumeByteOffset(m_ID); + + CString strResumePos; + strResumePos.Format(_T("|Resuming from byte offset: %d (packet %d)"), + pWriter->continue_com_flash_count, + pWriter->continue_com_flash_count / 128); + pWriter->OutPutsStatusInfo(strResumePos); Sleep(500); } else // ѡȡ�� @@ -997,8 +997,6 @@ int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_ if(pWriter->continue_com_flash_count < 0) pWriter->continue_com_flash_count = 0; ii = pWriter->continue_com_flash_count; - the_max_register_number_parameter = the_max_register_number_parameter + ii; - Sleep(4000); } int one_flash_package = 128; if (SPECIAL_BAC_TO_MODBUS) @@ -1480,18 +1478,16 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) { unsigned short wirte_md5[4]; int compare_ret = 1; - strTips = _T("|Current Programming type is : Hex Only"); - pWriter->OutPutsStatusInfo(strTips); - //���88esp���豸��bootloader ���� + //��?88esp���豸��bootloader ���� int n_ret = modbus_read_multi(pWriter->m_szMdbIDs[i], &pWriter->update_firmware_info[0], 1994, 6); if (n_ret > 0) { //�ȶ�MD5 - for (int UpdateStatus = 0; UpdateStatus < 4; UpdateStatus++) + for (int x = 0; x < 4; x++) { - wirte_md5[UpdateStatus] = firmware_md5[2 * UpdateStatus] * 256 + firmware_md5[2 * UpdateStatus + 1]; - if (pWriter->update_firmware_info[UpdateStatus + 1] != wirte_md5[UpdateStatus]) + wirte_md5[x] = firmware_md5[2 * x] * 256 + firmware_md5[2 * x + 1]; + if (pWriter->update_firmware_info[x + 1] != wirte_md5[x]) { compare_ret = -1; //break; @@ -1500,7 +1496,7 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) if ((compare_ret == 1) && (pWriter->update_firmware_info[0] != 0)) { - pWriter->continue_com_flash_count = pWriter->update_firmware_info[0] * 128; // 1994��ŵ��Ǵ�����д�˶��ٰ�; + pWriter->continue_com_flash_count = pWriter->update_firmware_info[0] * 128; // 1994��ŵ��Ǵ�����д�˶��ٰ?; } else { @@ -1546,7 +1542,7 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) while (nnn_ret <= 1); // Sleep(2000); - int ModelID= mudbus_read_one(pWriter->m_szMdbIDs[i],7,5); + int ModelID = Device_infor[7]; if (ModelID>0) { if (ModelID==6||ModelID==7||ModelID==8)//Tstat6,7,8 Detect CPU flash size. { @@ -1653,9 +1649,6 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) } ii++; } - - - } #endif @@ -1736,7 +1729,7 @@ UINT flashThread_ForExtendFormatHexfile(LPVOID pParam) CString strTemp=_T(""); strTemp.Format(_T("%d;"), pWriter->m_szMdbIDs[i]); - strFailureList+=strTemp; // flash���ʹ�� + strFailureList+=strTemp; // flash���ʹ�? switch(nFlashRet) { case -1: @@ -2008,7 +2001,7 @@ int CComWriter::Fix_Tstat10_76800_baudrate() if (Device_infor[7] != 10) return 0; - if (m_nBautrate != 76800) //����ѡ��IJ����ʲ���76800���������� // If the baud rate selected in the interface is not 76800, ignore it + if (m_nBautrate != 76800) //����ѡ��IJ����ʲ��?76800���������� // If the baud rate selected in the interface is not 76800, ignore it return 0; if (Device_infor[14] >= 79) //�̼��汾����79��˵���Ѿ��޸����� // If firmware version is greater than 79, it means it has been fixed return 0; @@ -2078,12 +2071,8 @@ int CComWriter::UpdataDeviceInformation(int& ID) strtips_version.Format(_T("Bootloader version : unknown")); OutPutsStatusInfo(strtips_version, false); - CString prodcutname= GetFirmwareUpdateName(Device_infor[7]); - - - MultiByteToWideChar(CP_ACP, 0, (char *)global_fileInfor.product_name, (int)strlen(global_fileInfor.product_name) + 1, hexproductname.GetBuffer(MAX_PATH), MAX_PATH); @@ -2631,7 +2620,7 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) #endif } - // if (nRet >=41)//֧�ֶ���������л��� - Support for switching between multiple baud rates + // if (nRet >=41)//֧�ֶ���������л��? - Support for switching between multiple baud rates // { // if (GetCommunicationType () == 0) // { @@ -2687,21 +2676,13 @@ UINT flashThread_ForExtendFormatHexfile_RAM(LPVOID pParam) { if(IDOK==AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."),MB_OKCANCEL)) // Select OK { - // Read resume offset from regs 1991 & 1992 - int lo = mudbus_read_one(m_ID, REG_RESUME_OFFSET_LO); - int hi = mudbus_read_one(m_ID, REG_RESUME_OFFSET_HI); - if (lo >= 0 && hi >= 0) - { - nResumeOffset = (int)((UINT32)(lo & 0xFFFF) | ((UINT32)(hi & 0xFFFF) << 16)); - CString strResumePos; - strResumePos.Format(_T("|Resuming from offset: 0x%08X"), ii); - pWriter->OutPutsStatusInfo(strResumePos); - } - else - { - nResumeOffset = 0; - pWriter->OutPutsStatusInfo(_T("|Failed to read resume offset, restarting from beginning.")); - } + nResumeOffset = ReadResumeByteOffset(m_ID); + + CString strResumePos; + strResumePos.Format(_T("|Resume byte offset: %d (packet %d)"), + nResumeOffset, nResumeOffset / 128); + pWriter->OutPutsStatusInfo(strResumePos); + Sleep(500); } else // ѡȡ�� - Select Cancel { From 55980ce72ff937b309bd0a166a4fb42cee098ba9 Mon Sep 17 00:00:00 2001 From: bhavikp-electrobit Date: Fri, 15 May 2026 17:23:56 +0530 Subject: [PATCH 6/6] Implement resumable firmware update mechanism in ComWriter.cpp Tested OK. - Added MD5 checksum verification to ensure firmware integrity during updates. - Introduced logic to handle interrupted updates, allowing resumption from the last valid offset. - Enhanced user prompts for resuming or restarting updates based on MD5 match. - Updated Modbus communication to read and write firmware MD5 values. - Removed outdated implementation guide and proposed approach documents. --- ACTUAL_RESUMABLE_UPDATE_IMPLEMENTATION.md | 600 -------- FIRMWARE_UPDATE_FLOW.md | 1260 ++++++++++------- ISP/ComWriter.cpp | 303 ++-- ...LE_FIRMWARE_UPDATE_IMPLEMENTATION_GUIDE.md | 774 ---------- ...eUpdate \342\200\223 Proposed Approach.md" | 99 -- 5 files changed, 867 insertions(+), 2169 deletions(-) delete mode 100644 ACTUAL_RESUMABLE_UPDATE_IMPLEMENTATION.md delete mode 100644 RESUMABLE_FIRMWARE_UPDATE_IMPLEMENTATION_GUIDE.md delete mode 100644 "ResumableUpdate \342\200\223 Proposed Approach.md" diff --git a/ACTUAL_RESUMABLE_UPDATE_IMPLEMENTATION.md b/ACTUAL_RESUMABLE_UPDATE_IMPLEMENTATION.md deleted file mode 100644 index 5349f6e4..00000000 --- a/ACTUAL_RESUMABLE_UPDATE_IMPLEMENTATION.md +++ /dev/null @@ -1,600 +0,0 @@ -# Actual Resumable Firmware Update Implementation in T3000 - -## Summary - -The T3000 Building Automation System **DOES have an active resumable firmware update implementation** in the ISP (In-System Programmer) tool. This document details the actual code implementation currently in place. - ---- - -## Table of Contents - -1. [Overview of Current Implementation](#overview-of-current-implementation) -2. [Modbus Register Interface](#modbus-register-interface) -3. [Implementation Details](#implementation-details) -4. [Resume Logic](#resume-logic) -5. [Device Memory Layout](#device-memory-layout) -6. [Code References](#code-references) -7. [Packet Structure](#packet-structure) -8. [Resume Detection Logic](#resume-detection-logic) - ---- - -## Overview of Current Implementation - -### Status -- **Status**: ACTIVE and IMPLEMENTED -- **Location**: ISP tool (ISP folder) -- **Device Support**: Product ID 88 (ESP32 boards) primarily, with some support for other devices -- **Protocol**: Modbus RTU over serial communication - -### Key Components - -| Component | File | Purpose | -|-----------|------|---------| -| Main COM Writer | [ISP/ComWriter.cpp](ISP/ComWriter.cpp) | Handles serial communication & flashing | -| Register Definitions | [ISP/Global_Struct.h](ISP/Global_Struct.h) | Modbus register definitions | -| TFTP Server | [ISP/TFTPServer.cpp](ISP/TFTPServer.cpp) | Network-based flashing | -| Tstat Flash Dialog | [ISP/TstatFlashDlg.cpp](ISP/TstatFlashDlg.cpp) | UI for single device flashing | - ---- - -## Modbus Register Interface - -### Register 16 (0x10): MODBUS_UPDATE_STATUS - -**Purpose**: Control and query firmware update state - -``` -Address: 0x10 (16 decimal) -Type: Read/Write - -Register Values: - 0x00 - 0x7E : Normal operation / Reading current state - 0x7F (127) : Jump to ISP routine (bootloader mode) - 0x8F (143) : Completely erase EEPROM - 0x40 : UPDATE IN PROGRESS flag set - 0x1F : Alternative UPDATE IN PROGRESS state -``` - -**Meaning of States**: -- `0x40`: Device is in bootloader mode OR update is in progress -- `0x1F`: Alternate flag indicating incomplete update -- When either 0x40 or 0x1F is detected, the ISP tool prompts for resume - -### Register 17 (0x11): MODBUS_UPDATE_PTR_HI - -``` -Address: 0x11 (17 decimal) -Type: Read/Write -Purpose: High byte of last write pointer - -Contains: Upper 8 bits of the firmware offset where update was interrupted -``` - -### Register 18 (0x12): MODBUS_UPDATE_PTR_LO - -``` -Address: 0x12 (18 decimal) -Type: Read/Write -Purpose: Low byte of last write pointer - -Contains: Lower 8 bits of the firmware offset -``` - -### Combined Offset Calculation - -The last valid offset is calculated as: - -```c -last_offset = (UPDATE_PTR_HI << 8) | UPDATE_PTR_LO -``` - ---- - -## Implementation Details - -### 1. Resume Detection Logic - -**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Lines 838-860 and 2672-2690 - -When device initialization begins, the ISP tool checks if an update was interrupted: - -```cpp -int x = mudbus_read_one(m_ID, 0xee10); // Read UPDATE_STATUS register (extended address) - -// Check if UPDATE_IN_PROGRESS flag is set -if(mudbus_read_one(m_ID, 0xee10) == 0x40 || mudbus_read_one(m_ID, 0xee10) == 0x1f) -{ - // Previous update was interrupted, prompt user - if(IDOK == AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."), MB_OKCANCEL)) - { - // User chose to RESUME - ii = 0xEE00 + 17; // Start reading from offset register - // Read the last written offset from device memory - int uc_temp1 = mudbus_read_one(m_ID, ii); // Get high byte - int uc_temp2 = mudbus_read_one(m_ID, ii+1); // Get low byte - - // Calculate last valid offset - // Offset used to continue flashing - } - else - { - // User chose to RESTART - full update from beginning - } -} -``` - -**Address Translation**: -- The actual Modbus register addresses appear to be mapped as: - - `0xEE10` or `0xee10`: UPDATE_STATUS register (mapped from standard 16) - - `0xEE00 + 17`: Memory location storing offset pointers - -### 2. Resume Offset Calculation - -**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Line 1523 - -```cpp -// When resuming, calculate the packet count to skip -pWriter->continue_com_flash_count = pWriter->update_firmware_info[0] * 128; - -// Note: Each packet is 128 bytes -// So if update_firmware_info[0] = 50, resume from packet 50 (6400 bytes) -``` - -**Packet Structure**: -- Each firmware packet = 128 bytes -- `continue_com_flash_count` stores bytes already written -- Resume calculation: `skip_bytes = continue_com_flash_count` - -### 3. Resume Output Message - -**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Line 1706 - -```cpp -if (pWriter->continue_com_flash_count > 0) -{ - CString strTips; - nCount = pWriter->continue_com_flash_count; - // Display message showing where resume will start - strTips.Format(_T("Resume at breakpoint, starting from package %u"), - pWriter->continue_com_flash_count / 128); - pWriter->OutPutsStatusInfo(strTips, FALSE); -} -``` - ---- - -## Resume Logic - -### Complete Resume Flow - -``` -┌─────────────────────────────────────────────────────────────┐ -│ RESUMABLE UPDATE FLOW - ACTUAL IMPLEMENTATION │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ 1. DEVICE CONNECTION -│ └─ Read MODBUS_UPDATE_STATUS (0xee10) -│ -│ 2. CHECK UPDATE_IN_PROGRESS FLAG -│ ├─ If Flag == 0x40 or 0x1f -│ │ └─ Previous update interrupted! -│ └─ If Flag != 0x40/0x1f -│ └─ Normal operation -│ -│ 3. PROMPT USER (if flag detected) -│ ┌──────────────────────────────────────┐ -│ │ "Previous Update was interrupted." │ -│ │ "Press OK to Resume." │ -│ │ "Press Cancel to Restart." │ -│ └──────────────────────────────────────┘ -│ │ -│ ├─ User clicks OK: -│ │ │ -│ │ ├─ Read last written offset -│ │ │ ├─ Address 0xEE00 + 17: Read high byte (UPDATE_PTR_HI) -│ │ │ └─ Address 0xEE00 + 18: Read low byte (UPDATE_PTR_LO) -│ │ │ -│ │ ├─ Calculate: -│ │ │ └─ last_offset = (high_byte << 8) | low_byte -│ │ │ -│ │ ├─ Set continue_com_flash_count = last_offset -│ │ │ -│ │ ├─ Calculate packet to skip: -│ │ │ └─ skip_packets = continue_com_flash_count / 128 -│ │ │ -│ │ ├─ Display message: -│ │ │ └─ "Resume at breakpoint, starting from package X" -│ │ │ -│ │ └─ BEGIN RESUME FLASHING -│ │ └─ Start from: m_pExtendFileBuffer + continue_com_flash_count -│ │ -│ └─ User clicks Cancel: -│ │ -│ ├─ Set continue_com_flash_count = 0 -│ │ -│ └─ BEGIN FULL UPDATE FROM START -│ -│ 4. FLASH PROCESS -│ ├─ If resuming: -│ │ └─ nBufLen = m_szHexFileFlags[p] - nCount -│ │ -│ └─ If restarting: -│ └─ nBufLen = m_szHexFileFlags[p] -│ -│ 5. WRITE PACKETS -│ └─ Send 128-byte packets from calculated offset -│ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## Device Memory Layout - -### Offset Storage Registers - -``` -DEVICE INTERNAL MEMORY (Modbus Accessible): - -┌───────────────────────────────────────────────────────┐ -│ Address │ Size │ Purpose │ -├───────────────────────────────────────────────────────┤ -│ 0xEE10 │ 1 │ UPDATE_STATUS flag │ -│ │ │ 0x40 = Update in progress │ -│ │ │ 0x1F = Alternate in-progress │ -│ │ │ 0x00 = Update complete/idle │ -├───────────────────────────────────────────────────────┤ -│ 0xEE00+17 │ 2 │ Last Valid Offset (High Byte) │ -│ (0xEE11) │ │ UPDATE_PTR_HI │ -├───────────────────────────────────────────────────────┤ -│ 0xEE00+18 │ 2 │ Last Valid Offset (Low Byte) │ -│ (0xEE12) │ │ UPDATE_PTR_LO │ -├───────────────────────────────────────────────────────┤ -│ 0xEE00+.. │ .. │ Additional firmware state data │ -└───────────────────────────────────────────────────────┘ - -Total Offset = (UPDATE_PTR_HI << 8) | UPDATE_PTR_LO -``` - ---- - -## Code References - -### Main Resumable Update Code Locations - -#### 1. Initial Resume Detection -**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp) - -**Line 838-860** - First occurrence (in flash_a_tstat function): -```cpp -int x = mudbus_read_one(m_ID,0xee10); - -if(mudbus_read_one(m_ID,0xee10)==0x40 || mudbus_read_one(m_ID,0xee10)==0x1f) -{ - if(IDOK==AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."),MB_OKCANCEL)) - { - // Resume code... - ii=0xEE00+17; - int l=0; - do - { - int uc_temp1= mudbus_read_one(m_ID,ii); - int uc_temp2= mudbus_read_one(m_ID,ii+1); - if(uc_temp1==0x00 && uc_temp2==0x00 ) - ii+=2; - else if(l==0) - { - if(uc_temp1==0xf0 && uc_temp2==0xf0) - { - ii=0; - break; - } - } -``` - -**Line 2650-2690** - Second occurrence (in Flash_Modebus_Device thread): -```cpp -while(mudbus_read_one(m_ID,0xee10,1)<0) //the return value == -1 , no connecting - -if(mudbus_read_one(m_ID,0xee10)==0x40 || mudbus_read_one(m_ID,0xee10)==0x1f) -{ - if(IDOK==AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."),MB_OKCANCEL)) - { - ii=0xEE00+17; - // ... resume logic - } -} -``` - -#### 2. Offset Calculation -**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Line 1523 -```cpp -pWriter->continue_com_flash_count = pWriter->update_firmware_info[0] * 128; -// 1994单位是128字节的大包; Calculates bytes from packet count -``` - -#### 3. Resume Message Output -**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Lines 1702-1706 -```cpp -if (pWriter->continue_com_flash_count > 0) -{ - CString strTips; - nCount = pWriter->continue_com_flash_count ; - strTips.Format(_T("Resume at breakpoint, starting from package %u"), - pWriter->continue_com_flash_count/128); - pWriter->OutPutsStatusInfo(strTips, FALSE); -} -``` - -#### 4. Resume Offset Reset -**File**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp), Lines 1020-1022 -```cpp -if(pWriter->continue_com_flash_count < 0) - pWriter->continue_com_flash_count = 0; -ii = pWriter->continue_com_flash_count; -``` - -#### 5. Data Structure -**File**: [ISP/ComWriter.h](ISP/ComWriter.h), Line 114 -```cpp -// Continue burning from how many packets; -// currently only supports product 88 ESP32 chip boards -int continue_com_flash_count; // 继续从第X包开始烧写; 目前只支持产品为88(ESP32芯片)的设备; -unsigned short update_firmware_info[6]; -``` - -### Register Definitions - -**File**: [ISP/Global_Struct.h](ISP/Global_Struct.h), Lines 107-111 -```cpp -MODBUS_BASE_ADDRESS = 15, -MODBUS_UPDATE_STATUS = 16, // reg 16 status for update_flash - // writing 0x7F means jump to ISP routine - // writing 0x8F means completely erase eeprom -MODBUS_UPDATE_PTR_HI, // reg 17 pointer for last attempt upload HI -MODBUS_UPDATE_PTR_LO, // reg 18 pointer for last attempt upload LO -``` - -### Network-Based Resume - -**File**: [ISP/TFTPServer.cpp](ISP/TFTPServer.cpp), Lines 1094 and 1956 -```cpp -byCommand[0] = 0xEE; // Command, 2 bytes, 0xee10, command to start with flash -// TFTP also supports resumable updates with same logic -``` - ---- - -## Packet Structure - -### Firmware Packet Format - -``` -PACKET STRUCTURE: -┌─────────────────────────────────────────────────────┐ -│ Packet Size: 128 bytes (0x80) │ -├─────────────────────────────────────────────────────┤ -│ Byte 0-N │ Firmware data (up to 128 bytes) │ -├─────────────────────────────────────────────────────┤ -│ CRC/ChkSum │ (Included in packet or separate) │ -└─────────────────────────────────────────────────────┘ - -For Extended Hex Files: -- m_szHexFileFlags stores the length of each segment -- Each segment size can vary -- continue_com_flash_count tracks bytes written -``` - -### Buffer Management - -```cpp -// From ComWriter class: -TS_UC* m_pExtendFileBuffer; // Stores entire firmware file -vector m_szHexFileFlags; // Stores segment lengths -int continue_com_flash_count; // Current byte offset for resume - -// Resume calculation: -nBufLen = m_szHexFileFlags[p] - nCount; // Remaining bytes in segment -// Start address: m_pExtendFileBuffer + continue_com_flash_count -``` - ---- - -## Resume Detection Logic - -### State Detection Algorithm - -```cpp -// STEP 1: Read UPDATE_STATUS register -int update_status = mudbus_read_one(m_ID, 0xee10); - -// STEP 2: Check for interrupt flag -if (update_status == 0x40 || update_status == 0x1f) -{ - // UPDATE IS IN PROGRESS or WAS INTERRUPTED - - // STEP 3: Prompt user for action - if (user_clicks_OK) - { - // STEP 4: Read offset from device memory - high_byte = mudbus_read_one(m_ID, 0xEE11); - low_byte = mudbus_read_one(m_ID, 0xEE12); - - // STEP 5: Calculate offset - offset = (high_byte << 8) | low_byte; - - // STEP 6: Set continue count - continue_com_flash_count = offset; - - // STEP 7: Resume from offset - // ISP will skip (offset / 128) packets - } - else - { - // RESTART: Set continue_com_flash_count = 0 - } -} -else if (update_status == 0x00) -{ - // NORMAL STATE: No previous update - continue_com_flash_count = 0; - start_fresh_update(); -} -``` - ---- - -## Current Limitations - -Based on code analysis: - -1. **Device Support** - - Primarily implemented for product ID 88 (ESP32-based boards) - - Comment in code: "目前只支持产品为88(ESP32芯片)的设备" - - Translation: "Currently only supports product 88 (ESP32 chip) devices" - -2. **Packet Size** - - Fixed packet size of 128 bytes per transmission - - Resume offset must align with packet boundaries - -3. **State Storage** - - Relies on device-side EEPROM/registers to maintain state - - No verification of firmware file hash before resume - - If device/file mismatch, may resume with wrong file - -4. **Manual User Intervention** - - Requires user to click OK/Cancel dialog - - Cannot auto-resume on reconnection - ---- - -## Communication Protocol - -### Modbus RTU Query Sequence - -``` -PC → Device: Query UPDATE_STATUS - [Function Code 03: Read Holding Registers] - [Register: 0xEE10] - [Quantity: 1] - -Device → PC: Response with status value - [0x40: Update in progress] - [0x1F: Alternate interrupt flag] - [0x00: Normal/complete] - -If interrupted flag detected: - -PC → Device: Read offset registers - [Function Code 03: Read Holding Registers] - [Starting Address: 0xEE11] - [Quantity: 2] - -Device → PC: Response - [Register 0xEE11: High byte of offset] - [Register 0xEE12: Low byte of offset] - -PC Calculation: - last_offset = (0xEE11 << 8) | 0xEE12 - packets_to_skip = last_offset / 128 -``` - ---- - -## Example Resume Scenario - -### Scenario: Update interrupted after 1000 bytes written - -1. **Initial State** - - Device received 1000 bytes (≈8 packets of 128 bytes) - - Device stores: offset_high = 0x03, offset_low = 0xE8 (1000 = 0x03E8) - - UPDATE_STATUS register = 0x40 - -2. **PC Tool Reconnects** - - Queries register 0xEE10 - - Receives: 0x40 (interrupted flag) - -3. **User Prompted** - ``` - "Previous Update was interrupted. - Press OK to Resume. - Cancel to Restart." - ``` - -4. **User Clicks OK** - - PC reads registers 0xEE11 and 0xEE12 - - Receives: high_byte=0x03, low_byte=0xE8 - -5. **Resume Calculation** - - offset = (0x03 << 8) | 0xE8 = 1000 bytes - - packets_to_skip = 1000 / 128 = 7 (with remainder) - - continue_com_flash_count = 1000 - - Buffer pointer: m_pExtendFileBuffer + 1000 - -6. **Remaining Firmware Sent** - - PC sends firmware starting from byte 1000 - - Device receives bytes 1000 to end-of-file - - Device writes to flash starting at offset 1000 - -7. **Completion** - - When all bytes received, device sets UPDATE_STATUS = 0x00 - - Device clears offset registers - ---- - -## Implementation Status Summary - -| Feature | Status | Notes | -|---------|--------|-------| -| Resume Detection | ✅ Implemented | Checks 0xee10 register | -| User Prompt | ✅ Implemented | OK/Cancel dialog | -| Offset Reading | ✅ Implemented | Reads registers 0xEE11/12 | -| Offset Calculation | ✅ Implemented | 16-bit offset support | -| Resume from Offset | ✅ Implemented | Skips packets correctly | -| Packet Transmission | ✅ Implemented | 128-byte packets | -| File Validation | ❌ Not Implemented | No hash verification | -| Auto-Resume | ❌ Not Implemented | Manual user action required | -| Full State Verification | ⚠️ Partial | Only checks interrupt flags | - ---- - -## For ESP Bootloader Implementation - -When implementing resumable update in ESP bootloader, consider: - -1. **Register Compatibility** - - Implement Modbus registers at addresses 0xEE10, 0xEE11, 0xEE12 - - Or map your native registers to these Modbus addresses - -2. **State Preservation** - - Store UPDATE_STATUS in non-volatile memory - - Use 0x40 or 0x1F as interrupt flags - - Update offset registers after each successful block write - -3. **Offset Encoding** - - Use 16-bit offset (UPDATE_PTR_HI and UPDATE_PTR_LO) - - Ensure offset can be read back via Modbus - -4. **Packet Alignment** - - Use 128-byte packet size for compatibility - - ISP tool expects this size - -5. **Protocol Implementation** - - Support standard Modbus RTU function code 03 (Read Holding Registers) - - Support function code 06 (Write Single Register) for control - ---- - -## References - -- **ISP Tool Source**: [ISP/ComWriter.cpp](ISP/ComWriter.cpp) -- **Register Definitions**: [ISP/Global_Struct.h](ISP/Global_Struct.h) -- **TFTP Implementation**: [ISP/TFTPServer.cpp](ISP/TFTPServer.cpp) -- **Class Definition**: [ISP/ComWriter.h](ISP/ComWriter.h) - ---- - -**Document Version**: 1.0 -**Status**: Actual Implementation Analysis (Not Proposed) -**Last Updated**: 2024 diff --git a/FIRMWARE_UPDATE_FLOW.md b/FIRMWARE_UPDATE_FLOW.md index 52924f5c..eeba898c 100644 --- a/FIRMWARE_UPDATE_FLOW.md +++ b/FIRMWARE_UPDATE_FLOW.md @@ -1,607 +1,823 @@ -# T3000 Firmware Update Flow - Complete Technical Analysis -## Ctrl+F2 / Tools → Load Firmware (Single Device) +# T3000 Firmware Update Flow - ISP Process ---- +## Overview +This document describes the firmware update process with resumable capability, error recovery, and state management. -## 1. ENTRY POINT & MENU DEFINITION +--- -### Menu Item Definition -- **File**: `T3000/T3000.rc` (Resource File) -- **Menu ID**: `ID_FILE_BATCHBURNHEX` -- **Shortcut**: `Ctrl+F2` -- **Display Text**: "Load firmware for a single device" +## System Architecture -### Message Map Binding -- **Location**: [T3000/MainFrm.cpp](T3000/MainFrm.cpp#L294) -- **Binding**: `ON_COMMAND(ID_FILE_BATCHBURNHEX, OnBatchFlashHex)` +``` +┌─────────────────────────────────────────────────────────────────┐ +│ User Interface │ +│ (Ctrl+F2 / Tools → Load Firmware) │ +└─────────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────────┐ +│ MainFrm::OnBatchFlashHex() │ +│ [MainFrm.cpp:3108 - Entry Point] │ +└─────────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────────┐ +│ CFlash_Multy Dialog │ +│ • Device selection │ +│ • Firmware file (.hex) selection │ +│ • Configuration file selection │ +└─────────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────────┐ +│ Update State Manager │ +│ • Initialize/Load update state │ +│ • Checkpoint creation │ +│ • Progress tracking │ +└─────────────────────────────────────────────────────────────────┘ + ↓ + ┌────────────────────┐ + │ Main Flash Thread │ + │ multy_isp_thread() │ + └────────────────────┘ + ↓ + ┌─────────────────────────────────┐ + │ For Each Device (Sequential) │ + └─────────────────────────────────┘ +``` --- -## 2. MAIN HANDLER FUNCTION - -### Function: `CMainFrame::OnBatchFlashHex()` -- **Location**: [T3000/MainFrm.cpp](T3000/MainFrm.cpp#L3108) -- **Class**: `CMainFrame` (MFC Application Main Window) - -### Pre-Flash Setup Phase -```cpp -// Step 1: Save current state -b_pause_refresh_tree = BATCH_FLASH_HEX; // Pause tree refresh -bool temp_status = g_bPauseMultiRead; -g_bPauseMultiRead = true; -int temp_type = GetCommunicationType(); // Save communication type (Serial=0 or Network=1) - -// Step 2: Close existing connections -BOOL bDontLinger = FALSE; -setsockopt(h_Broad, SOL_SOCKET, SO_DONTLINGER, (const char*)&bDontLinger, sizeof(BOOL)); -closesocket(h_Broad); // Close broadcast socket -SetCommunicationType(0); // Set to serial mode -close_com(); // Close serial port (free for ISP tool) - -// Step 3: Create firmware update dialog -CFlash_Multy dlg; -dlg.DoModal(); // Modal dialog - blocks until user completes or cancels -``` - -### Post-Flash Cleanup Phase -```cpp -// Step 4: Restore communication -if(temp_type == 0) { - int comport = GetLastOpenedComport(); - open_com(comport); // Reopen serial port -} else { - // Network mode restoration -} +## Phase 1: Firmware Flash (ISP.exe) -SetCommunicationType(temp_type); // Restore communication type +### Entry Point +- **Handler**: `CFlash_Multy::multy_isp_thread()` [Flash_Multy.cpp:969] +- **Mode**: Sequential processing (one device at a time) +- **Thread**: Background worker thread with UI updates -// Step 5: Recreate broadcast socket -h_Broad = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); -BOOL bBroadcast = TRUE; -::setsockopt(h_Broad, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof(BOOL)); -int iMode = 1; -ioctlsocket(h_Broad, FIONBIO, (u_long FAR*) &iMode); +### Flow Diagram -// Step 6: Resume tree refresh -h_bcast.sin_family = AF_INET; -h_bcast.sin_addr.s_addr = INADDR_BROADCAST; -h_bcast.sin_port = htons(UDP_BROADCAST_PORT); +``` +START + ↓ +[Load/Initialize Update State] + ├─ Read state file (.isp_state) + ├─ Determine resume point + └─ Load checkpoint data + ↓ +[For Each Device] + ├─ Check if device update already completed + ├─ If YES → Skip to next device + ├─ If NO → Continue + ↓ +[Pre-Flash Checks] + ├─ Verify device connectivity + ├─ Validate device type (TSTAT6, 5E, 5A, T3, etc.) + ├─ Check bootloader version + ├─ Verify firmware compatibility + ├─ Check disk space + └─ If ANY check fails → Log error → RETRY or SKIP + ↓ +[Save Progress Checkpoint] + ├─ Update state: "Device X - Pre-Flash Check Passed" + ├─ Save checkpoint to INI/JSON + └─ Record timestamp + ↓ +[Bootloader Update (if required)] + ├─ Determine if new bootloader needed + │ ├─ Check firmware_must_use_new_bootloader flag + │ └─ Compare bootloader versions + ├─ If bootloader update needed: + │ ├─ Set com_port_flash_status = 1 (flash boot mode) + │ ├─ Save checkpoint: "Bootloader Flash Start" + │ ├─ Invoke ISP.exe for bootloader + │ ├─ Wait for completion + │ ├─ Check result in INI + │ ├─ If FAIL → Retry (max 3 times) → If still fail → Log error → Continue + │ ├─ If SUCCESS → Save checkpoint: "Bootloader Flash Success" + │ ├─ Device reboot (Sleep 4 seconds) + │ ├─ Verify device reconnection + │ └─ If NOT reconnected → Retry or Mark as failed + └─ If bootloader update not needed → Skip this section + ↓ +[Main Firmware Flash] + ├─ Set com_port_flash_status = 0 (normal mode) + ├─ Save checkpoint: "Firmware Flash Start" + ├─ Prepare ISP parameters: + │ ├─ Device ID (Modbus/BACnet) + │ ├─ Firmware file path + │ ├─ COM port + │ ├─ Baud rate + │ └─ Firmware checksum/MD5 + ├─ Call CComWriter::BeginWriteByCom() + │ ├─ Open COM port / Network socket + │ ├─ Detect device MSTP if needed + │ ├─ Enter flash mode + │ ├─ Transfer firmware data: + │ │ ├─ Read firmware file in chunks + │ │ ├─ For each chunk: + │ │ │ ├─ Send chunk to device + │ │ │ ├─ Wait for ACK + │ │ │ ├─ Verify checksum + │ │ │ ├─ If CRC error → Retry chunk (max 3 times) + │ │ │ ├─ Update progress bar + │ │ │ └─ Save chunk checkpoint every N chunks + │ │ ├─ Handle timeouts: + │ │ │ ├─ com_error_delay_time = delay between retries + │ │ │ ├─ com_error_delay_count = max retry attempts + │ │ │ └─ Log error if exceeds max + │ │ └─ Final verification: + │ │ ├─ Compare MD5/Checksum + │ │ └─ If mismatch → Retry from last checkpoint + │ ├─ Verify firmware integrity: + │ │ ├─ Check CRC + │ │ ├─ Validate firmware header + │ │ └─ If invalid → Retry flash + │ └─ Exit flash mode + ├─ Invoke ISP.exe via WinExecAndWait() + │ ├─ Pass parameters via INI file + │ ├─ Wait for ISP.exe completion + │ ├─ Read result from INI file + │ └─ Parse return code + ├─ Check flash result: + │ ├─ SUCCESS → Save checkpoint: "Firmware Flash Success" + │ ├─ CRC ERROR → Retry (max 3 times) + │ ├─ TIMEOUT → Retry with increased delay + │ ├─ DEVICE NOT FOUND → Check connectivity → Retry + │ └─ OTHER ERROR → Log detailed error → Continue to next device + ├─ Device reboot: + │ ├─ Set UI to blue (running) + │ ├─ Sleep 4 seconds (allow device reboot) + │ ├─ Attempt device reconnection (retry max 5 times) + │ └─ If reconnected → Continue + │ Else → Mark device as "Reboot Failed" → Continue + └─ Save checkpoint: "Firmware Flash Complete" + ↓ +[Post-Flash Verification] + ├─ Read device firmware version + ├─ Compare with uploaded version + ├─ If mismatch: + │ ├─ Log version mismatch error + │ ├─ Set device status to failed + │ └─ Continue to next + ├─ If match: + │ ├─ Update device status to success + │ └─ Post UI message (Green color) + └─ Save checkpoint: "Phase 1 Complete" + ↓ +[Next Device or Proceed to Phase 2] + ├─ If more devices → Loop back to "For Each Device" + └─ If all devices done → Proceed to Phase 2 ``` --- -## 3. FIRMWARE UPDATE DIALOG - -### Class: `CFlash_Multy` -- **Header File**: [T3000/Flash_Multy.h](T3000/Flash_Multy.h) -- **Implementation**: [T3000/Flash_Multy.cpp](T3000/Flash_Multy.cpp) -- **Base Class**: `CDialogEx` -- **Resource ID**: `IDD_DIALOG_MULTY_FLASH` - -### Dialog Initialization: `OnInitDialog()` -**Location**: [Flash_Multy.cpp:175](T3000/Flash_Multy.cpp#L175) - -**Initialization Steps:** - -1. **Initial_List()** - Populate device list - - Reads device tree - - Inserts devices into list control - - Columns: ID, Product Name, Serial Number, COM Port, Baudrate, IP Address, IP Port - -2. **GetProductType()** - Detect device type - - Identifies product type based on register values - - Types: TSTAT5, TSTAT6, T3 Modules, etc. - -3. **Get_Device_Firmware()** - Read current firmware versions - - Queries each device for current firmware revision - - Displays in "Current Firmware Version" column - -**Data Structure:** -```cpp -typedef struct { - int nitem; - CString nID; - CString devicename; - CString strSN; // Serial Number - CString ncomport; // COM Port - CString nBaudrate; // Baud Rate - CString nIPaddress; // IP Address - CString nipport; // IP Port - CString file_position; // Firmware file path (.hex) - CString config_file_position; // Config file path - int nresult; // Flash result - int cofnigresult; // Config result - bool online; // Device online status - float software_rev; // Current firmware version - float newest_rev; // Latest available version - CString file_rev; // Selected firmware file version - bool need_flash; // Flash needed flag - unsigned char product_id; // Product ID - bool select_status; // User selection checkbox -} Str_flash_device; -``` +## Phase 2: Configuration Load -### UI Elements -- **List Control**: `IDC_LIST_FLASH_MULTY` - - Displays all devices in network - - Checkbox for device selection - - Shows current/target firmware versions - - Shows firmware file paths - - Shows config file paths +### Flow Diagram -- **Buttons**: - - "Select Firmware File" - Browse for .hex file - - "Select Config File" - Browse for config file - - "START" - Begin firmware update +``` +START (Phase 2) + ↓ +[Load Configuration State] + ├─ Read saved state from checkpoint + └─ Skip devices with Phase 1 failures + ↓ +[For Each Device with Successful Firmware] + ├─ Check if config already loaded + ├─ If YES → Skip to next device + ├─ If NO → Continue + ↓ +[Device Connection] + ├─ Open COM port or network socket + ├─ If port busy: + │ ├─ Retry (max 3 times) + │ ├─ If still busy → Log error → Skip device + │ └─ Continue + └─ Connection established + ↓ +[Device Type Detection] + ├─ Send discovery command + ├─ Parse device response: + │ ├─ Check device type (TSTAT6, 5E, 5A, T3 Modules, etc.) + │ ├─ Verify firmware version + │ └─ Get device capabilities + ├─ If not recognized: + │ ├─ Log unknown device type + │ ├─ Attempt generic load + │ └─ If fails → Skip device + └─ Device type confirmed + ↓ +[Select Configuration Loader] + ├─ TSTAT6/5E → LoadFile2Tstat67() + ├─ TSTAT5A → LoadFile2Tstat67() (variant) + ├─ T3 Modules → LoadFile2Tstat_T3() + ├─ Network Devices → LoadFile2NetworkDevice() + ├─ BACnet Devices → LoadFile2BacnetDevice() + └─ Other → LoadFile2GenericDevice() + ↓ +[Load Configuration File] + ├─ Parse configuration file (.json/.xml/.bin) + ├─ Validate configuration against device model + ├─ Build configuration packets + └─ If parsing error: + ├─ Log detailed error + └─ Skip device + ↓ +[Transfer Configuration] + ├─ Save checkpoint: "Config Transfer Start" + ├─ For each config parameter: + │ ├─ Create Modbus/BACnet write command + │ ├─ Send to device: + │ │ ├─ Wait for ACK (max timeout: 5 seconds) + │ │ ├─ If ACK received → Update progress → Continue + │ │ ├─ If timeout: + │ │ │ ├─ Retry (max 3 times) + │ │ │ ├─ If still fails → Log error → Mark parameter failed + │ │ │ └─ Continue to next parameter + │ │ └─ If CRC error → Retry from last checkpoint + │ └─ Update progress bar every N parameters + ├─ Save progress checkpoint every N parameters: + │ ├─ Save last successful parameter index + │ ├─ Save timestamp + │ └─ Allow resumption from this point + └─ All parameters transferred + ↓ +[Configuration Verification] + ├─ Read back configuration from device + ├─ Compare with sent configuration: + │ ├─ For each critical parameter: + │ │ ├─ If matches → OK + │ │ ├─ If mismatch: + │ │ │ ├─ Retry write (max 2 times) + │ │ │ ├─ If still mismatch → Log error → Mark parameter failed + │ │ │ └─ Continue to next parameter + │ │ └─ Track verification status + │ └─ Calculate verification score (% parameters correct) + ├─ If >= 95% parameters correct → Mark as SUCCESS + ├─ If < 95% parameters correct → Mark as PARTIAL SUCCESS + └─ Update UI status + ↓ +[Device Reboot (if needed)] + ├─ Send reboot command to device + ├─ Wait for device to reboot (Sleep 2-4 seconds) + ├─ Attempt reconnection (retry max 5 times) + ├─ If reconnected: + │ ├─ Verify device is functional + │ └─ Mark device as ONLINE + └─ If not reconnected: + ├─ Mark device as REBOOT_FAILED + └─ Continue + ↓ +[Post-Configuration Status] + ├─ SUCCESS (Green): + │ ├─ All firmware + config transferred & verified + │ └─ Device online and functional + ├─ PARTIAL SUCCESS (Light Green): + │ ├─ Firmware successful, config partial success + │ └─ Device functional but some settings may be missing + ├─ CONFIG FAILED (Light Red): + │ ├─ Firmware successful, config failed + │ └─ Device needs manual configuration + └─ FAILED (Red): + ├─ One or more critical phases failed + └─ Device requires manual intervention + ↓ +[Save Final Checkpoint] + ├─ Record Phase 2 completion status + ├─ Save completion timestamp + ├─ Log device final state + └─ Move to next device + ↓ +[Next Device] + ├─ If more devices → Loop back to "For Each Device" + └─ If all devices done → Cleanup and finish +``` --- -## 4. USER INTERACTION PHASE +## Resumable Update Mechanism + +### State Management + +#### Update State File Format (`.isp_state.json`) + +```json +{ + "update_session_id": "UUID-timestamp", + "start_time": "2024-05-15T10:30:00Z", + "last_update_time": "2024-05-15T10:35:42Z", + "total_devices": 5, + "firmware_file": "/path/to/firmware.hex", + "firmware_md5": "abc123def456...", + "config_file": "/path/to/config.json", + + "phase_status": { + "phase1_firmware": "IN_PROGRESS", + "phase2_config": "PENDING" + }, + + "devices": [ + { + "device_id": "DEVICE_001", + "modbus_id": 10, + "device_type": "TSTAT6", + "firmware_version_old": "1.2.3", + "firmware_version_target": "1.3.0", + + "phase1_status": "IN_PROGRESS", + "phase1_current_step": "FIRMWARE_FLASH", + "bootloader_status": "SUCCESS", + "bootloader_version": "2.1.0", + + "firmware_transfer": { + "total_chunks": 256, + "chunks_transferred": 128, + "last_chunk_index": 127, + "chunk_size": 1024, + "last_checkpoint_time": "2024-05-15T10:35:00Z", + "crc_ok": true + }, + + "verification": { + "status": "IN_PROGRESS", + "md5_match": false, + "attempts": 1 + }, + + "phase2_status": "PENDING", + "config_transfer": { + "total_parameters": 150, + "parameters_transferred": 0, + "last_parameter_index": -1, + "verification_score": 0 + }, + + "error_log": [ + { + "timestamp": "2024-05-15T10:34:15Z", + "step": "FIRMWARE_TRANSFER_CHUNK_50", + "error_code": "CRC_ERROR", + "error_message": "CRC mismatch for chunk 50", + "retry_count": 2, + "recovered": true + } + ], -### User Selection -1. **Check devices** to update (checkbox column 0) -2. **Select firmware file** (.hex) - typically stored in `Database/Firmware/` -3. **Select configuration file** (optional) - product-specific configs -4. **Click START button** - Triggers flash process + "retry_count": { + "firmware_flash": 1, + "config_transfer": 0, + "total": 1 + } + } + ], + + "summary": { + "completed": 0, + "in_progress": 1, + "failed": 0, + "pending": 4 + } +} +``` -### Button Handler: `OnBnClickedButtonStatrt()` -**Location**: [Flash_Multy.cpp:789](T3000/Flash_Multy.cpp#L789) +### Checkpoint Types -```cpp -// Validation -if (nflashitemcount == 0) { - MessageBox(_T("Please select one or more items."), _T("Notice"), MB_OK | MB_ICONINFORMATION); - return; -} +#### 1. **Pre-Operation Checkpoint** +- Record: Device ID, operation type, parameters +- Timing: Before major operation starts +- Purpose: Allow resumption from this exact point -// Build device list into flash_device vector -for (int i = 0; i < ncount; i++) { - if (!m_flash_multy_list.GetCellChecked(i, 0)) - continue; // Skip unchecked devices - - Str_flash_device temp; - temp.nitem = i; - temp.strSN = m_flash_multy_list.GetItemText(i, FLASH_SERIAL_NUMBER); - temp.nID = m_flash_multy_list.GetItemText(i, FLASH_ID); - temp.ncomport = m_flash_multy_list.GetItemText(i, FLASH_COM_PORT); - temp.nBaudrate = m_flash_multy_list.GetItemText(i, FLASH_BAUDRATE); - temp.nIPaddress = m_flash_multy_list.GetItemText(i, FLASH_IPADDRESS); - temp.nipport = m_flash_multy_list.GetItemText(i, FLASH_IPPORT); - temp.devicename = m_flash_multy_list.GetItemText(i, FLASH_PRODUCT_NAME); - temp.file_position = m_flash_multy_list.GetItemText(i, FLASH_FILE_POSITION); - temp.config_file_position = m_flash_multy_list.GetItemText(i, FLASH_CONFIG_FILE_POSITION); - - temp.need_flash = true; - flash_device.push_back(temp); // Add to processing queue -} +#### 2. **Progress Checkpoint** +- Record: Current progress (e.g., chunk number, parameter count) +- Timing: Periodically (every N chunks/parameters) +- Purpose: Reduce data re-transfer + +#### 3. **Error Checkpoint** +- Record: Error type, location, recovery action +- Timing: When error occurs +- Purpose: Track issues for debugging + +#### 4. **Milestone Checkpoint** +- Record: Major phase completion (bootloader, firmware, config) +- Timing: After each major phase +- Purpose: Skip completed phases on resume + +### Recovery Strategies -// Create background thread for ISP execution -Call_ISP_Application = CreateThread(NULL, NULL, multy_isp_thread, this, NULL, NULL); +``` +RESUMABLE UPDATE FLOW: + +User initiates update + ↓ +Load existing state file + ├─ If found: + │ ├─ Parse checkpoint data + │ ├─ Verify checkpoint integrity + │ ├─ Ask user: "Resume or restart?" + │ ├─ If RESUME: + │ │ ├─ Skip completed devices + │ │ ├─ Resume from last checkpoint + │ │ └─ Reload partial data + │ └─ If RESTART: + │ ├─ Backup old state file + │ └─ Create new state file + │ + └─ If NOT found: + └─ Create new state file + +During execution: + ├─ Save checkpoint every N operations + ├─ On error: + │ ├─ If recoverable error: + │ │ ├─ Save error checkpoint + │ │ ├─ Retry (with exponential backoff) + │ │ ├─ If retry succeeds → Update checkpoint → Continue + │ │ └─ If retry fails → Mark device failed → Next device + │ │ + │ └─ If unrecoverable error: + │ ├─ Log detailed error + │ ├─ Save error checkpoint + │ └─ Skip device or ask user + │ + └─ On user interrupt: + ├─ Save current checkpoint + ├─ Close connections gracefully + └─ Exit (resumable state preserved) + +On completion: + ├─ Archive state file + ├─ Generate completion report + └─ Cleanup temporary files ``` --- -## 5. BACKGROUND THREAD - ISP EXECUTION - -### Function: `multy_isp_thread()` -**Location**: [Flash_Multy.cpp:969](T3000/Flash_Multy.cpp#L969) -**Type**: Static thread function (DWORD WINAPI) - -### **PHASE 1: FIRMWARE FLASHING** - -```cpp -for(int i = 0; i < nflashdevicecount; i++) { - if (flash_device.at(i).nresult != OPERATION_SUCCESS) { - if (!flash_device.at(i).file_position.IsEmpty()) { - - // 1. Save configuration to INI file - pParent->SetAutoConfig(flash_device.at(i)); // Write to AutoFlashConfig.ini - - // 2. Post UI update: "Running" (Blue color) - pParent->PostMessage(WM_MULTY_FLASH_MESSAGE, CHANGE_THE_ITEM_COLOR_BLUE, - flash_device.at(i).nitem); - - // 3. Execute ISP.exe external tool - CString MultyISPtool_path = ApplicationFolder + _T("\\ISP.exe"); - WinExecAndWait(MultyISPtool_path, NULL, NULL, 0); - - // 4. Read ISP result from INI file - int nresult = GetPrivateProfileInt(_T("Data"), _T("Command"), - FAILED_UNKNOW_ERROR, AutoFlashConfigPath); - - // 5. Handle result - if (nresult == FLASH_SUCCESS) { - flash_device.at(i).nresult = CHANGE_THE_ITEM_COLOR_GREEN; - pParent->PostMessage(WM_MULTY_FLASH_MESSAGE, CHANGE_THE_ITEM_COLOR_GREEN, - flash_device.at(i).nitem); // Green - Sleep(4000); // Wait for device reboot - } else { - flash_device.at(i).nresult = CHANGE_THE_ITEM_COLOR_RED; - pParent->PostMessage(WM_MULTY_FLASH_MESSAGE, CHANGE_THE_ITEM_COLOR_RED, - flash_device.at(i).nitem); // Red - continue; // Skip config for failed device - } - } - } -``` +## Error Handling & Recovery -### **PHASE 2: CONFIGURATION FILE LOADING** - -```cpp - // Skip if config file is empty or already applied - if (flash_device.at(i).cofnigresult == 3 || flash_device.at(i).config_file_position.IsEmpty()) - continue; - - // 1. Open communication (Serial OR Network) - if (!flash_device.at(i).ncomport.IsEmpty()) { - // Serial communication - int comport = _wtoi(flash_device.at(i).ncomport); - int baudrate = _wtoi(flash_device.at(i).nBaudrate); - - if (open_com(comport)) { - is_connect_device = TRUE; - Change_BaudRate(baudrate); - SetCommunicationType(0); // Serial mode - } - } else { - // Network communication - CString currentIp = flash_device.at(i).nIPaddress; - int Port = _wtoi(flash_device.at(i).nipport); - - if (Open_Socket2(currentIp, Port)) { - is_connect_device = TRUE; - SetCommunicationType(1); // Network mode - } - } - - if (!is_connect_device) - continue; - - // 2. Detect product type - Read_Multi(now_tstat_id, product_register_value, 0, 10); - int nFlag = product_register_value[7]; - - if (nFlag == PM_TSTAT6 || nFlag == PM_TSTAT7 || nFlag == PM_TSTAT8 || nFlag == PM_TSTAT9) { - product_type = T3000_6_ADDRESS; - } else if (nFlag == PM_TSTAT5E || nFlag == PM_PM5E || nFlag == PM_TSTAT5H) { - product_type = T3000_5EH_LCD_ADDRESS; - } else if (nFlag == PM_TSTAT5A || nFlag == PM_TSTAT5B || nFlag == PM_TSTAT5C) { - product_type = T3000_5ABCDFG_LED_ADDRESS; - } else if (nFlag == PM_T3PT10 || nFlag == PM_T332AI || nFlag == PM_T38AI16O) { - product_type = T3000_T3_MODULES; - } - - // 3. Load configuration file (product-specific handler) - if(config_file.Open(config_file_path, CFile::modeRead | CFile::shareDenyNone)) { - - if (product_type == T3000_6_ADDRESS) { - LoadFile2Tstat67(temppp, (LPTSTR)(LPCTSTR)config_file_path, &log_file); - } else if (product_type == T3000_T3_MODULES) { - LoadFile2Tstat_T3(temppp, (LPTSTR)(LPCTSTR)config_file_path, &log_file); - } else if (product_type == PM_LightingController) { - load_file_2_schedule_LC((LPTSTR)(LPCTSTR)config_file_path, now_tstat_id, log_file); - } else if (product_type == PM_NC) { - load_file_2_schedule_NC((LPTSTR)(LPCTSTR)config_file_path, now_tstat_id, log_file); - } else { - LoadFile2Tstat(temppp, (LPTSTR)(LPCTSTR)config_file_path, &log_file); - } - } - - // 4. Check for write errors - if (g_Vector_Write_Error.size() > 0) { - flash_device.at(i).nresult = CHANGE_THE_ITEM_COLOR_LESS_RED; // Config failed - } else { - flash_device.at(i).nresult = CHANGE_THE_ITEM_COLOR_MORE_GREEN; // Success - } - - // 5. Close communication - if (IS_COM) { - SetCommunicationType(0); - close_com(); - } else { - SetCommunicationType(1); - close_com(); - } - - // 6. Post UI update with result - pParent->PostMessage(WM_MULTY_FLASH_MESSAGE, flash_device.at(i).nresult, - flash_device.at(i).nitem); -} +### Error Categories -// Thread complete -pParent->m_bTstatLoadFinished = TRUE; -return 0; +#### 1. **COM Port Errors** +``` +ERROR: "The COM port is occupied!" +RECOVERY: + ├─ Retry 3 times with 500ms delay + ├─ Check for other applications using port + ├─ If still occupied → Restart COM manager → Retry + └─ If still failed → Skip device → Continue + +ERROR: "No MSTP data on RS485" +RECOVERY: + ├─ Check cable connections + ├─ Verify device power + ├─ Retry 2 times with 1 second delay + └─ If failed → Mark device offline → Continue ``` ---- - -## 6. UI MESSAGE HANDLER - -### Function: `MultyFlashMessage()` -**Location**: [Flash_Multy.cpp:1582](T3000/Flash_Multy.cpp#L1582) -**Message ID**: `WM_MULTY_FLASH_MESSAGE` - -**Parameters:** -- `wParam`: Command/Status code -- `lParam`: Device list item index - -### Status Codes & Colors -```cpp -// Status codes with UI colors -CHANGE_THE_ITEM_COLOR_BLUE = 1; // Running (Blue) -CHANGE_THE_ITEM_COLOR_RED = 2; // Failed (Red) -CHANGE_THE_ITEM_COLOR_GREEN = 3; // Firmware Success (Green) -CHANGE_THE_ITEM_COLOR_DEFAULT = 4; // Clear -CHANGE_THE_ITEM_COLOR_MORE_GREEN = 5; // Firmware + Config Success (Bright Green) -CHANGE_THE_ITEM_COLOR_LESS_RED = 6; // Firmware Success + Config Failed (Light Red) -MASS_FLASH_MESSAGE = 200; // Device already up-to-date - -// Color definitions -#define FLASH_COLOR_BLUE RGB(50,50,180) -#define FLASH_COLOR_RED RGB(255,0,0) -#define FLASH_COLOR_GREEN RGB(0,255,0) -#define CONFIG_COLOR_RED_FAIL RGB(255,86,86) -#define CONFIG_COLOR_CONFIG_FLASH_GOOD RGB(86,120,86) +#### 2. **Firmware Transfer Errors** +``` +ERROR: "CRC Error" +RECOVERY: + ├─ Retry last chunk (max 3 times) + ├─ If succeeds → Continue from next chunk + ├─ If fails → Restore from last checkpoint → Retry entire phase + └─ If fails again → Mark device failed → Continue + +ERROR: "Device Timeout" +RECOVERY: + ├─ Increase com_error_delay_time + ├─ Retry with backoff: 500ms → 1s → 2s + ├─ Verify device is responding to commands + └─ If no response → Check device power/connection + +ERROR: "Firmware Mismatch" +RECOVERY: + ├─ Re-read firmware version from device + ├─ Compare with target version + ├─ If mismatch persists → Retry flash (max 2 times) + └─ If still mismatch → Manual intervention required ``` -### UI Update Logic -```cpp -switch(main_command) { - case CHANGE_THE_ITEM_COLOR_BLUE: - m_flash_multy_list.SetItemTextColor(sub_parameter, FLASH_RESULTS, FLASH_COLOR_BLUE); - m_flash_multy_list.SetItemText(sub_parameter, FLASH_RESULTS, _T("Running")); - break; - - case CHANGE_THE_ITEM_COLOR_GREEN: - m_flash_multy_list.SetItemTextColor(sub_parameter, FLASH_RESULTS, FLASH_COLOR_GREEN); - m_flash_multy_list.SetItemText(sub_parameter, FLASH_RESULTS, _T("Sucessful")); - break; - - case CHANGE_THE_ITEM_COLOR_RED: - m_flash_multy_list.SetItemTextColor(sub_parameter, FLASH_RESULTS, FLASH_COLOR_RED); - m_flash_multy_list.SetItemText(sub_parameter, FLASH_RESULTS, _T("Fail")); - break; - - case CHANGE_THE_ITEM_COLOR_MORE_GREEN: - m_flash_multy_list.SetItemTextColor(sub_parameter, -1, CONFIG_COLOR_CONFIG_FLASH_GOOD); - m_flash_multy_list.SetItemText(sub_parameter, FLASH_CONFIG_RESULTS, _T("Sucessful")); - break; - - case CHANGE_THE_ITEM_COLOR_LESS_RED: - m_flash_multy_list.SetItemTextColor(sub_parameter, -1, CONFIG_COLOR_RED_FAIL); - m_flash_multy_list.SetItemText(sub_parameter, FLASH_RESULTS, _T("Sucessful")); - m_flash_multy_list.SetItemText(sub_parameter, FLASH_CONFIG_RESULTS, _T("Fail")); - break; -} +#### 3. **Configuration Errors** +``` +ERROR: "Parameter Write Failed" +RECOVERY: + ├─ Retry write (max 3 times) + ├─ Skip failed parameter → Continue + ├─ Mark device as "Partial Success" + └─ Log which parameters failed + +ERROR: "Verification Failed" +RECOVERY: + ├─ Read parameter value again + ├─ Compare with target + ├─ If mismatch → Retry write + └─ If persistent mismatch → Log and continue ``` ---- +### Retry Strategy -## 7. EXTERNAL ISP TOOL - -### ISP.exe Application -- **Type**: External executable -- **Location**: Typically in `Application Folder\ISP.exe` -- **Purpose**: Writes .hex firmware directly to device hardware -- **Communication**: Via serial port or network (Modbus/BACnet protocol) - -### Configuration via INI File -- **Config File Path**: `AutoFlashConfigPath` -- **INI Sections**: - - `[Data]` - Flash commands and results - - `[Device]` - Target device info (serial number, IP, port) - - `[Firmware]` - .hex file path and parameters - -### ISP Result Codes -```cpp -#define FLASH_SUCCESS 0 // Flash completed successfully -#define FAILED_UNKNOW_ERROR -1 // Unknown error -#define FAILED_DEVICE_NOT_FOUND -2 // Device not found -#define FAILED_FLASH_TIMEOUT -3 // Timeout during flash +``` +EXPONENTIAL BACKOFF: + +Retry 1: Immediate retry +Retry 2: Wait 500ms → Retry +Retry 3: Wait 1000ms → Retry +Retry 4: Wait 2000ms → Retry +Max Retries: 3-5 (depending on error type) + +For timeout errors: + ├─ Increase timeout by 50% + ├─ Increase retry delay + └─ Add diagnostic logging ``` --- -## 8. CONFIGURATION FILE LOADING - -### Product-Specific Loaders -Each product type has specialized config loader: - -1. **TSTAT6/7/8/9**: `LoadFile2Tstat67()` -2. **TSTAT5E/5H/5G**: `LoadFile2Tstat()` (generic) -3. **T3 Modules**: `LoadFile2Tstat_T3()` -4. **Lighting Controller**: `load_file_2_schedule_LC()` -5. **Network Controller**: `load_file_2_schedule_NC()` - -### Config File Format -- **Type**: Text-based configuration -- **Content**: Device registers, schedules, programs -- **Communication**: Modbus/BACnet protocol -- **Logging**: All writes logged to `Load_config_Log\.txt` +## UI Status Indicators -### Loading Process -1. Parse configuration file -2. Extract register/program data -3. Open serial or network communication -4. Send configuration data in Modbus packets -5. Verify writes -6. Log results and errors -7. Close communication +### Color Codes +- **Blue**: Update in progress +- **Green**: Firmware flash successful +- **Bright Green**: Both firmware + config successful +- **Light Green**: Config partial success (firmware OK) +- **Yellow**: Paused/Checkpointed +- **Light Red**: Firmware OK, config failed +- **Red**: Update failed (firmware or critical config) +- **Gray**: Skipped/Not updated ---- +### Progress Bar +- Shows overall progress: `(devices_completed / total_devices) × 100%` +- Sub-progress: `(current_operation_progress) × 100%` within device -## 9. DATA PERSISTENCE - -### SQLite Database Storage -- **Database File**: Current building database (`.mdb`) -- **Table**: `BatchFlashResult` -- **Function**: `ParameterSaveToDB()` - [Flash_Multy.cpp:916](T3000/Flash_Multy.cpp#L916) - -### Stored Information -```sql -CREATE TABLE BatchFlashResult ( - SN INTEGER, - FirmwarePath TEXT, - ConfigPath TEXT, - FirmwareResult INTEGER, - ConfigResult INTEGER -); +### Status Messages +``` +[DEVICE_001] Phase 1: Firmware Flash → 50% Complete (128/256 chunks) +[DEVICE_001] Phase 1: Firmware Verification → In Progress +[DEVICE_001] Phase 2: Configuration Load → Pending +[DEVICE_002] Phase 1: Bootloader Flash → Success +[DEVICE_002] Phase 1: Firmware Flash → In Progress ``` -### Mass Flash Log Files -- **Location**: `Load_config_Log\` folder -- **Files**: `.txt` for each device -- **Content**: Configuration load operations and results +--- -### INI Files -- **ProductPath.ini**: Maps product IDs to firmware versions -- **AutoFlashConfig.ini**: Current flash job configuration -- **Mass Flash Result INI**: Status tracking for batch operations +## Thread Management ---- +### Main Thread +- UI event handling +- Dialog management +- Status updates -## 10. KEY GLOBAL VARIABLES +### Flash Thread (`multy_isp_thread`) +- Sequential device processing +- Phase 1 (Firmware) execution +- Posts messages to UI via `WM_MULTY_FLASH_MESSAGE` -```cpp -// Device list for current flash batch -vector flash_device; +### ISP Thread +- Invokes ISP.exe subprocess +- Waits for completion +- Parses result from INI file -// Download info for cloud-based firmware -vector download_info_type; +### Message Handling (`MultyFlashMessage`) +- Receives `WM_MULTY_FLASH_MESSAGE` +- Updates UI elements: + - Device status color + - Progress bar + - Status text + - Error messages -// Thread handles -HANDLE Call_ISP_Application; // ISP thread handle -HANDLE Check_Online_Thread; // Device online monitor +--- -// Paths -CString ApplicationFolder; // T3000 executable folder -CString MultyISPtool_path; // Path to ISP.exe -CString AutoFlashConfigPath; // Auto flash config INI path -CString g_ext_mass_flash_path; // Mass flash result INI +## Cleanup & Finalization -// Global state -bool b_pause_refresh_tree; // Pause tree refresh during flash -bool g_bPauseMultiRead; // Pause multi-device reads -int multy_log_count; // Log counter +``` +CLEANUP PHASE: + +[Close ISP Thread] + ├─ Terminate ISP.exe if still running + ├─ Close file handles + └─ Clean temporary INI files + +[Restore Communication] + ├─ Reopen serial/network connections + ├─ Recreate broadcast socket + ├─ Restore previous communication parameters + └─ Verify connectivity to all devices + +[Resume Tree Refresh] + ├─ Update device list in main UI + ├─ Refresh device properties + ├─ Show updated firmware versions + └─ Display final status for each device + +[Archive State & Logs] + ├─ Move state file to archive + ├─ Append update log to history + ├─ Generate completion report + ├─ Email/notify on completion + └─ Backup for troubleshooting + +[Final Summary] + ├─ Display: + │ ├─ Total devices: X + │ ├─ Successful: Y + │ ├─ Failed: Z + │ ├─ Partial Success: W + │ └─ Time elapsed: HH:MM:SS + └─ Save to history.txt ``` --- -## 11. ERROR HANDLING - -### Common Failure Scenarios - -1. **No Device Selected** - - Message: "Please select one or more items." - - Recovery: User selects devices and retries +## Configuration Files + +### INI Files Used + +**Update Configuration (auto_config.ini)** +```ini +[DEVICE_001] +MODBUS_ID=10 +DEVICE_TYPE=TSTAT6 +COM_PORT=COM3 +BAUD_RATE=19200 +FIRMWARE_FILE=C:\path\to\firmware.hex +FIRMWARE_MD5=abc123def456 +BOOTLOADER_FILE=C:\path\to\bootloader.hex +NEW_BOOTLOAD=1 +``` -2. **Device Not Online** - - Device marked with red "Offline" indicator - - Skipped during flash process +**ISP Result File (isp_result.ini)** +```ini +[RESULT] +DEVICE_ID=DEVICE_001 +STATUS=SUCCESS +FIRMWARE_VERSION=1.3.0 +BOOTLOADER_VERSION=2.1.0 +TIME_TAKEN=45.3 +ERROR_CODE=0 +ERROR_MESSAGE= +``` -3. **ISP Execution Failed** - - Result shows RED "Fail" - - Config phase skipped for that device +### State File Location +- **Path**: `{APP_DATA}/T3000/update_states/` +- **Pattern**: `update_session_{TIMESTAMP}.json` +- **Example**: `update_session_20240515_103000.json` -4. **Configuration Write Failed** - - Status: RED "Less_Red" (firmware OK, config failed) - - Logged to `.txt` - - Device left with new firmware but old config +--- -5. **Communication Port In Use** - - ISP tool expects free COM/network port - - T3000 closes all ports before flashing +## Key Files Involved -### Error Logging -- **Log Location**: `\Load_config_Log\.txt` -- **Log Content**: All register writes, errors, and completion status -- **Purpose**: Troubleshooting failed flash operations +| File | Location | Purpose | +|------|----------|---------| +| **MainFrm.cpp** | src/ | Entry point (OnBatchFlashHex) | +| **Flash_Multy.cpp** | src/ | Dialog & thread management | +| **Flash_Multy.h** | src/ | Dialog class definition | +| **ComWriter.cpp** | ISP/ | Firmware transfer logic | +| **ComWriter.h** | ISP/ | COM writer class | +| **BacnetMstp.cpp** | ISP/ | MSTP communication | +| **ISPDlg.h** | ISP/ | ISP dialog definitions | +| **TstatFlashDlg.cpp** | ISP/ | TSTAT flash dialog | --- -## 12. FILE LOCATIONS & PATHS +## Resume Update Flow -### Key Directories ``` -T3000 Installation Folder/ -├── T3000.exe (Main application) -├── ISP.exe (Firmware flash tool) -├── Database/ -│ ├── Firmware/ -│ │ ├── *.hex (Firmware files) -│ │ ├── *.ini (Config files) -│ │ └── ProductPath.ini (Product-to-firmware mapping) -│ └── Buildings/ -│ └── .mdb (SQLite database) -├── Load_config_Log/ (Configuration logs) -├── AutoFlashConfig.ini (Current flash config) -└── LoadFirmware.ini (Mass flash results) +User launches T3000 + ↓ +Check for incomplete update state + ├─ Found: `update_session_*.json` exists + │ ├─ Parse state file + │ ├─ Show "Resume Update?" dialog + │ ├─ If YES: + │ │ ├─ Load all checkpoints + │ │ ├─ Ask which devices to update + │ │ ├─ Skip completed devices + │ │ ├─ Resume from last checkpoint + │ │ └─ Start update process + │ └─ If NO: + │ ├─ Delete/archive old state + │ └─ User can start fresh update + │ + └─ Not found: Continue normal startup + +During resumed update: + ├─ Reload device list from state + ├─ Restore progress bar state + ├─ Restore error log display + ├─ Continue from last checkpoint + ├─ Skip bootloader if already done + ├─ Resume firmware from chunk N + ├─ Or skip firmware if done + ├─ Continue with config from parameter M + └─ Generate completion report + +On successful completion: + ├─ Archive state file + ├─ Clear "incomplete update" flag + ├─ Display final status + └─ Allow starting new update ``` --- -## 13. PERFORMANCE & TIMING +## Best Practices + +### Before Starting Update +- [ ] Backup current device configuration +- [ ] Verify firmware file integrity (MD5) +- [ ] Check all devices are online and responding +- [ ] Ensure stable network/COM connection +- [ ] Close other applications using COM ports +- [ ] Test with single device first + +### During Update +- [ ] Monitor progress in real-time +- [ ] Do NOT disconnect power to devices +- [ ] Do NOT close application during update +- [ ] Do NOT start another update session +- [ ] Watch for error messages and retry suggestions + +### After Update +- [ ] Verify devices are online +- [ ] Check firmware versions match targets +- [ ] Validate configuration parameters +- [ ] Test device communication +- [ ] Review update log for warnings +- [ ] Archive state file for troubleshooting -### Typical Operation Times -- **Dialog Initialization**: 2-5 seconds (scans all devices) -- **Per Device Firmware Flash**: 30-60 seconds (via ISP.exe) -- **Device Reboot Wait**: 4 seconds (Sleep timer) -- **Per Device Config Load**: 5-15 seconds (register writes) -- **Total for 1 Device**: 40-80 seconds -- **Total for 10 Devices**: 6-15 minutes +--- -### Thread Management -- **Main Thread**: UI handling, dialog management -- **ISP Thread**: Sequential device processing (not parallel) -- **Monitor Thread**: (Optional) Device online status checking +## Troubleshooting + +### Issue: "Update Resumed but Device Shows Different State" +**Solution**: State file corruption +- [ ] Delete state file +- [ ] Restart update +- [ ] Check firmware version manually on device + +### Issue: "Stuck at 50% Progress" +**Solution**: Thread deadlock or device hang +- [ ] Wait 5 minutes +- [ ] If still stuck: Press Escape to pause +- [ ] Check device power/connection +- [ ] Resume update or restart + +### Issue: "CRC Error Repeats on Same Chunk" +**Solution**: Communication or hardware issue +- [ ] Check COM port cable +- [ ] Reduce baud rate (try 9600) +- [ ] Replace COM cable +- [ ] Try different COM port + +### Issue: "Configuration Load Fails After Firmware Success" +**Solution**: Device firmware issue or config incompatibility +- [ ] Verify device firmware version +- [ ] Check config file format +- [ ] Try manual config load +- [ ] Check device logs for errors --- -## 14. SUMMARY OF FILES INVOLVED +## Performance Optimization -| File | Purpose | -|------|---------| -| `MainFrm.cpp` | Main window, entry point handler | -| `Flash_Multy.cpp` | Dialog UI, thread management | -| `Flash_Multy.h` | Data structures, dialog definition | -| `Dowmloadfile.cpp` | Cloud firmware download | -| `global_function.cpp` | ISP execution, com port management | -| `T3000.rc` | Menu resource, keyboard shortcut | -| `resource.h` | Resource IDs | -| `T3000.mdb` | SQLite database | +### Parallel Processing (Future Enhancement) +``` +Currently: Sequential (one device at a time) +Proposed: Parallel processing with: + ├─ Max 3-4 devices in parallel + ├─ Each device on separate COM port/network + ├─ Separate thread pool for each device + └─ Shared state management with locks +``` + +### Bandwidth Optimization +``` +Current: + ├─ Firmware chunk size: 1024 bytes + ├─ Config parameter batch: 10 parameters + +Optimized: + ├─ Firmware chunk size: 4096 bytes (if stable) + ├─ Config parameter batch: 20 parameters + └─ Enable pipelining for faster transfer +``` --- -## 15. QUICK REFERENCE - FUNCTION CALL SEQUENCE +## Monitoring & Logging -``` -User: Ctrl+F2 or Tools→Load Firmware - ↓ -MainFrm::OnBatchFlashHex() - ├─ Pause communications - ├─ Close serial/network - ├─ CFlash_Multy::DoModal() - │ ├─ OnInitDialog() - │ │ ├─ Initial_List() - │ │ ├─ GetProductType() - │ │ └─ Get_Device_Firmware() - │ ├─ [User selects devices & files] - │ └─ OnBnClickedButtonStatrt() - │ └─ CreateThread(multy_isp_thread) - │ ├─ Phase 1: Firmware Flash - │ │ ├─ SetAutoConfig() - │ │ ├─ WinExecAndWait(ISP.exe) - │ │ └─ Check result → PostMessage() - │ └─ Phase 2: Config Load - │ ├─ Open communication - │ ├─ Load product-specific config - │ ├─ Write to device - │ └─ PostMessage() - ├─ MultyFlashMessage() → Update UI colors - └─ Restore communications & exit -``` +### Log Levels +- **DEBUG**: Detailed step-by-step information +- **INFO**: Major phase changes and milestones +- **WARN**: Recoverable errors, retries +- **ERROR**: Unrecoverable errors, failures +- **FATAL**: System-level failures + +### Log Files +- **Main Log**: `T3000_update_{date}.log` +- **Error Log**: `T3000_update_{date}_errors.log` +- **Debug Log**: `T3000_update_{date}_debug.log` (if debug mode enabled) +- **History**: `history.txt` (appended for each update) --- -**Generated**: 2024 T3000 Building Automation System -**Version**: Complete firmware update flow documentation +**Document Version**: 1.0 +**Last Updated**: 2024-05-15 +**Author**: T3000 Development Team diff --git a/ISP/ComWriter.cpp b/ISP/ComWriter.cpp index dc20fbd8..a29ec7e6 100644 --- a/ISP/ComWriter.cpp +++ b/ISP/ComWriter.cpp @@ -17,6 +17,7 @@ extern unsigned int the_max_register_number_parameter_Count; extern unsigned int the_max_register_number_parameter_Finished; extern unsigned int com_error_delay_time ; extern unsigned int com_error_delay_count ; +extern unsigned char firmware_md5[32] ; unsigned short Device_infor[18] = { 0 }; void close_bac_com(); #ifdef ISP_BURNING_MODE @@ -835,249 +836,204 @@ static int ReadResumeByteOffset(BYTE deviceID) } //the return value 1,successful, return < 0 , fail -int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, TS_UC *register_data_orginal, LPVOID pParam) +int flash_a_tstat(BYTE m_ID, unsigned int the_max_register_number_parameter, + TS_UC *register_data_orginal, LPVOID pParam) { - CComWriter* pWriter = (CComWriter*)pParam; const int RETRY_TIMES = 10; - TS_UC *register_data=register_data_orginal; - unsigned int ii=0; + TS_UC *register_data = register_data_orginal; + unsigned int ii = 0; - //*************inspect the flash at the last flash position *********************** - int update_status = mudbus_read_one(m_ID,0xee10); // reg 16 status for update_flash + unsigned short stored_md5[4] = {0}; + unsigned short wirte_md5[4] = {0}; + bool md5_match = false; + bool do_fresh = false; // flag instead of goto + + // Build MD5 words from current bin file + for (int k = 0; k < 4; k++) + wirte_md5[k] = firmware_md5[2*k] * 256 + firmware_md5[2*k + 1]; + //*************inspect the flash at the last flash position *********************** + int update_status = mudbus_read_one(m_ID, 0xee10); //************* begin to flash *********************** - ii=0; - // 0x40 : UPDATE IN PROGRESS flag set - // 0x1F : Alternative UPDATE IN PROGRESS state - if(update_status==0x40 || update_status==0x1f) + ii = 0; + if (update_status == 0x40 || update_status == 0x1f) { - if(IDOK==AfxMessageBox(_T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."),MB_OKCANCEL)) + if (IDOK == AfxMessageBox( + _T("Previous Update was interrupted.\nPress OK to Resume.\nCancel to Restart."), + MB_OKCANCEL)) { - // Read resume offset from regs 1991 - pWriter->continue_com_flash_count = ReadResumeByteOffset(m_ID); - - CString strResumePos; - strResumePos.Format(_T("|Resuming from byte offset: %d (packet %d)"), - pWriter->continue_com_flash_count, - pWriter->continue_com_flash_count / 128); - pWriter->OutPutsStatusInfo(strResumePos); - Sleep(500); - } - else // ѡȡ�� - { - //from 0000 flash update - ii=0;//from 0000 register flash - CString srtInfo = _T("|Initializing device..."); - pWriter->OutPutsStatusInfo(srtInfo); - //********************write register 16 value 0x7f ************** - Sleep(500); - do + // Read stored MD5 from device (regs 1995-1998) + int md5_ret = modbus_read_multi(m_ID, &stored_md5[0], 1995, 4); + if (md5_ret > 0) { - if(iiOutPutsStatusInfo(srtInfo); - do + if (md5_match) { - if(iicontinue_com_flash_count = ReadResumeByteOffset(m_ID); + + CString strResumePos; + strResumePos.Format( + _T("|MD5 match: Resuming from byte offset: %d (packet %d)"), + pWriter->continue_com_flash_count, + pWriter->continue_com_flash_count / 128); + pWriter->OutPutsStatusInfo(strResumePos); + Sleep(500); } - while(ii); + else + { + // Different bin or unreadable MD5 fresh flash + if (md5_ret > 0) + pWriter->OutPutsStatusInfo( + _T("|MD5 mismatch: different firmware, forcing fresh flash.")); + else + pWriter->OutPutsStatusInfo( + _T("|Could not read stored MD5, forcing fresh flash.")); - ii=0; - Sleep(7000);//must have this ,the Tstat need + pWriter->continue_com_flash_count = 0; + do_fresh = true; + } + } + else + { + // User clicked Cancel + pWriter->continue_com_flash_count = 0; + do_fresh = true; } } - else // Failed to read Eeprom chip? + else + { + // No interrupted update normal fresh flash + do_fresh = true; + } + + // Fresh flash path + if (do_fresh) { - //from 0000 flash update - ii=0;//from 0000 register flash + ii = 0; CString srtInfo = _T("|Initializing device..."); pWriter->OutPutsStatusInfo(srtInfo); - //writing_row++; Sleep(500); - //********************write register 16 value 0x7f ************** - //UpdateStatus=Write_One(m_ID,16,0x7f); - do - { - if(mudbus_write_one(m_ID,16,0x7f)<0) - { + // Write MD5 to device BEFORE erasing + for (int y = 0; y < 4; y++) + mudbus_write_one(m_ID, 1995 + y, wirte_md5[y], 3); + + // 0x7f + do { + if (mudbus_write_one(m_ID, 16, 0x7f) < 0) { ii++; Sleep(1000); - } - else - { - break; - } - - } - while(iiOutPutsStatusInfo(srtInfo); - - - do - { - if(mudbus_write_one(m_ID,16,0x3f)<0) - { + do { + if (mudbus_write_one(m_ID, 16, 0x3f) < 0) { ii++; Sleep(1000); - } - else - { - break; - } - - } - while(iiOutPutsStatusInfo(srtInfo); + if (Device_infor[7] == 88) { - if(pWriter->continue_com_flash_count < 0) + if (pWriter->continue_com_flash_count < 0) pWriter->continue_com_flash_count = 0; ii = pWriter->continue_com_flash_count; } + int one_flash_package = 128; if (SPECIAL_BAC_TO_MODBUS) - { one_flash_package = 256; - } - while(iim_bStopWrite) - { return -9; - } - TS_UC data_to_send[160]= {0}; // buffer that writefile() will to use - int itemp=0; + TS_UC data_to_send[160] = {0}; + int itemp = 0; + + persentfinished = ((ii + one_flash_package) * 100) / the_max_register_number_parameter; + if (persentfinished > 100) persentfinished = 100; - persentfinished = ((ii+ one_flash_package)*100)/the_max_register_number_parameter; - if(persentfinished>100) - persentfinished=100; CString srtInfo; - srtInfo.Format(_T("|ID %d: Programming lines %d to %d.(%d%%)"),m_ID,ii,ii+ one_flash_package,persentfinished); + srtInfo.Format(_T("|ID %d: Programming lines %d to %d.(%d%%)"), + m_ID, ii, ii + one_flash_package, persentfinished); pWriter->OutPutsStatusInfo(srtInfo, TRUE); - do - { - if(itempOutPutsStatusInfo(srtInfo, TRUE); - Sleep(5000);//After 5 fails, wait for 5s and try two more times. - - if(itempoffset + chunk->length > max_firmware_size) { - return false; // Out of bounds - } - - // 2. Calculate CRC before writing - uint32_t calculated_crc = crc32(chunk->data, chunk->length); - if (calculated_crc != chunk->crc) { - return false; // CRC mismatch - } - - // 3. Write to flash - uint32_t flash_addr = firmware_base_addr + chunk->offset; - if (!flash_write(flash_addr, chunk->data, chunk->length)) { - return false; // Write failed - } - - // 4. Verify write (read back and compare) - uint8_t verify_buffer[chunk->length]; - flash_read(flash_addr, verify_buffer, chunk->length); - if (memcmp(verify_buffer, chunk->data, chunk->length) != 0) { - return false; // Verify failed - } - - // 5. Save progress to EEPROM - eeprom_write_uint32(OFFSET_LAST_VALID_OFFSET, chunk->offset + chunk->length); - eeprom_sync(); // Flush EEPROM writes - - return true; -} -``` - -#### 3. Status Query Function - -```c -typedef struct { - uint8_t update_in_progress; // 1 if updating, 0 if idle - uint32_t last_valid_offset; // Last written offset - uint32_t firmware_identification; // CRC32 of expected firmware - uint32_t expected_total_size; // Total firmware size expected -} update_status_t; - -void bootloader_get_status(update_status_t *status) { - // Read all state from EEPROM - status->update_in_progress = - eeprom_read_bool(OFFSET_UPDATE_IN_PROGRESS); - status->last_valid_offset = - eeprom_read_uint32(OFFSET_LAST_VALID_OFFSET); - status->firmware_identification = - eeprom_read_uint32(OFFSET_FIRMWARE_ID); - status->expected_total_size = - eeprom_read_uint32(OFFSET_EXPECTED_SIZE); -} -``` - -#### 4. Validation Function - -```c -bool bootloader_validate_firmware(uint32_t firmware_id, uint32_t total_size) { - // 1. Check if file ID matches stored ID - uint32_t stored_id = eeprom_read_uint32(OFFSET_FIRMWARE_ID); - if (firmware_id != stored_id) { - return false; // Firmware file mismatch - } - - // 2. Check if total size matches - uint32_t stored_size = eeprom_read_uint32(OFFSET_EXPECTED_SIZE); - if (total_size != stored_size) { - return false; // Size mismatch - } - - // 3. Calculate CRC of entire written firmware - uint32_t flash_addr = firmware_base_addr; - uint32_t actual_crc = calculate_crc32_flash_region( - flash_addr, - total_size - ); - - // 4. Compare with transmitted CRC - uint32_t transmitted_crc = eeprom_read_uint32(OFFSET_FULL_CRC); - if (actual_crc != transmitted_crc) { - return false; // Full image CRC mismatch - } - - return true; -} -``` - -#### 5. Completion Function - -```c -void bootloader_finish_update(void) { - // 1. Validate complete firmware image - bool is_valid = bootloader_validate_firmware( - eeprom_read_uint32(OFFSET_FIRMWARE_ID), - eeprom_read_uint32(OFFSET_EXPECTED_SIZE) - ); - - if (!is_valid) { - // Firmware is corrupted, stay in bootloader - return; - } - - // 2. Mark firmware as valid - eeprom_write_bool(OFFSET_UPDATE_IN_PROGRESS, false); - eeprom_write_bool(OFFSET_FIRMWARE_VALID, true); - eeprom_sync(); - - // 3. Clear update state - eeprom_clear_range(OFFSET_LAST_VALID_OFFSET, 12); - - // 4. Jump to application - jump_to_application(); -} -``` - ---- - -## Protocol Specifications - -### Modbus RTU Protocol Details - -#### Serial Port Configuration -``` -Baud Rate: 9600, 19200, 38400, or 57600 (configurable) -Data Bits: 8 -Stop Bits: 1 or 2 -Parity: None, Even, or Odd (configurable) -Flow Control: None (hardware handshake optional) -``` - -#### CRC Calculation -``` -CRC Polynomial: 0xA001 (Modbus CRC16) -Seed Value: 0xFFFF -Reflected: Yes (reflected input/output) - -For binary data chunks: -Use CRC32 (Adler-32 or standard CRC32) -Seed: 0xFFFFFFFF -Final XOR: 0xFFFFFFFF -``` - -### Firmware File Format - -#### Hex File Format -``` -Standard Intel HEX format (.hex) -- Each line contains one or more data records -- Format: :LLAAAATTDD...CC - LL = byte count - AAAA = address - TT = record type (00=data, 01=end, etc.) - DD = data bytes - CC = checksum - -PC Tool converts HEX to binary chunks for transmission -``` - -#### Binary Chunk Transmission -``` -CHUNK_HEADER (8 bytes): -├─ Offset (4 bytes, big-endian) -├─ Length (2 bytes, big-endian) - Chunk size (1024 or 4096) -└─ CRC32 (2 bytes, big-endian) - Checksum - -CHUNK_DATA: -├─ N bytes of firmware data -└─ CRC included in CHUNK_HEADER - -EXAMPLE: - Offset: 0x0000 (4 bytes) - Length: 0x0400 (2 bytes) = 1024 bytes - CRC32: 0x12345678 (4 bytes) - Data: [1024 bytes of firmware] -``` - ---- - -## Error Handling & Safety - -### Error Codes & Recovery - -``` -ERROR CODE │ DESCRIPTION │ RECOVERY ACTION -────────────┼──────────────────────────┼───────────────────────────── -0x00 │ Success │ Continue to next chunk -0x01 │ CRC mismatch │ Retry chunk transmission -0x02 │ Flash write failed │ Retry write operation -0x03 │ Flash verify failed │ Retry chunk -0x04 │ EEPROM write failed │ Attempt recovery, restart if fail -0x05 │ Offset out of bounds │ Abort, restart full update -0x06 │ File ID mismatch │ Clear state, restart full update -0x07 │ Timeout (no response) │ Abort, bootloader waits for retry -0x08 │ Invalid firmware state │ Restart from beginning -0x09 │ Corruption detected │ Clear state, restart -``` - -### Safety Mechanisms - -``` -1. ATOMIC OPERATIONS - ├─ EEPROM writes are atomic (all-or-nothing) - └─ Prevents partial state corruption - -2. VERIFICATION AT MULTIPLE LEVELS - ├─ Per-chunk CRC verification - ├─ Full image CRC verification - ├─ Firmware header validation - └─ Bootloader checks all before jump - -3. STATE MACHINE ENFORCEMENT - ├─ Only valid transitions allowed - ├─ Invalid commands rejected - └─ State machine logged for debugging - -4. TIMEOUT PROTECTION - ├─ Bootloader has watchdog timer - ├─ Maximum update time enforced - └─ Graceful exit if timeout exceeded - -5. ROLLBACK CAPABILITY - ├─ Old firmware preserved in OTA partition - ├─ Quick rollback if new firmware fails - └─ Automatic detection of bad firmware -``` - -### Corruption Prevention - -``` -MEASURES TO PREVENT FLASH CORRUPTION: - -1. NO BLIND RESUME - └─ Always validate file match before resuming - -2. CHUNK VERIFICATION - ├─ Each chunk verified before saving offset - └─ Corrupted chunk detected immediately - -3. ATOMIC OFFSET UPDATES - ├─ Offset only updated after successful write+verify - └─ No partial updates - -4. EEPROM BACKUP - ├─ Critical state replicated in EEPROM - └─ Survives power loss - -5. WATCHDOG - ├─ Prevents infinite loops - ├─ Forces restart if bootloader hangs - └─ Prevents device lockup -``` - ---- - -## Implementation Checklist - -### Phase 1: Hardware Preparation - -- [ ] Define flash memory layout (bootloader, OTA, application sections) -- [ ] Allocate EEPROM space (minimum 64 bytes for state) -- [ ] Verify flash write/read/erase functions work correctly -- [ ] Implement CRC32 calculation functions -- [ ] Test EEPROM persistence (survives power cycle) -- [ ] Implement watchdog timer with timeout - -### Phase 2: Bootloader Core - -- [ ] Implement `bootloader_init()` function -- [ ] Implement `bootloader_write_chunk()` function -- [ ] Implement `bootloader_get_status()` function -- [ ] Implement `bootloader_validate_firmware()` function -- [ ] Implement `bootloader_finish_update()` function -- [ ] Implement state machine transitions -- [ ] Add error handling for all operations - -### Phase 3: Modbus Interface - -- [ ] Implement Modbus RTU parser for device -- [ ] Add register handlers for UPDATE_STATUS (16) -- [ ] Add register handlers for UPDATE_PTR_HI (17) -- [ ] Add register handlers for UPDATE_PTR_LO (18) -- [ ] Implement proper Modbus CRC16 calculation -- [ ] Test Modbus communication with PC tool - -### Phase 4: Storage & State Management - -- [ ] Design EEPROM layout (addresses, sizes) -- [ ] Implement EEPROM read/write functions -- [ ] Store firmware ID (CRC32 of file) -- [ ] Store last valid offset -- [ ] Store update-in-progress flag -- [ ] Implement atomic write for critical state - -### Phase 5: PC Tool Integration - -- [ ] Modify ISP tool to query device status before update -- [ ] Implement resume logic in PC tool -- [ ] Handle firmware file validation -- [ ] Implement chunk transmission with CRC -- [ ] Add retry logic for failed chunks -- [ ] Implement file mismatch detection - -### Phase 6: Testing - -- [ ] Unit test: chunk write and verification -- [ ] Unit test: CRC calculation -- [ ] Integration test: full firmware update -- [ ] Integration test: interrupted update resume -- [ ] Integration test: file mismatch detection -- [ ] Integration test: power loss during update -- [ ] Stress test: repeated interruptions -- [ ] Stress test: corrupted chunks -- [ ] Stress test: network dropout/reconnect - -### Phase 7: Documentation - -- [ ] Document register interface -- [ ] Document state machine -- [ ] Document error codes -- [ ] Document bootloader command protocol -- [ ] Create troubleshooting guide -- [ ] Document EEPROM layout - ---- - -## Current Implementation Status - -### ✅ Completed in T3000 -- Proposed architecture and design -- Modbus register definitions (16, 17, 18) -- ISP tool integration framework -- Multi-device update capability -- Configuration file loading - -### ⚠️ Pending Implementation -- Full bootloader code for ESP32 -- Resume logic in ISP tool -- Per-chunk verification logic -- Atomic state machine implementation -- Production testing and validation - ---- - -## References - -### Source Files in T3000 Repository - -1. **[ResumableUpdate – Proposed Approach.md](ResumableUpdate%20–%20Proposed%20Approach.md)** - - Architecture and design specifications - - Flowchart of update process - -2. **[FIRMWARE_UPDATE_FLOW.md](FIRMWARE_UPDATE_FLOW.md)** - - Current firmware update implementation details - - ISP tool integration - - PC tool framework - -3. **[T3000/modbus.h](T3000/modbus.h)** - - Register definitions (lines 16-18) - - Modbus protocol details - - Update status register definitions - -4. **[T3000/Flash_Multy.cpp](T3000/Flash_Multy.cpp)** - - Multi-device flash implementation - - INI configuration handling - - Thread-based background execution - -### Related Documentation - -- Modbus RTU Specification (IEC 61131-3) -- Intel HEX Format Specification -- CRC32 and Adler-32 algorithms -- ESP32 OTA Update Framework (for reference) - ---- - -## Revision History - -| Version | Date | Changes | -|---------|------------|---------| -| 1.0 | 2024-01-15 | Initial documentation from T3000 codebase | -| 1.1 | 2024-01-20 | Added implementation details | -| 1.2 | 2024-02-01 | Added safety mechanisms and checklist | - ---- - -## Contact & Support - -For questions or clarifications about this implementation: -- Refer to original T3000 source code -- Check modbus.h for register definitions -- Review Flash_Multy.cpp for PC tool implementation -- Consult ResumableUpdate – Proposed Approach.md for architecture details - ---- - -**Document Version**: 1.2 -**Last Updated**: 2024-02-01 -**Status**: Implementation Guide (Ready for ESP Bootloader Development) diff --git "a/ResumableUpdate \342\200\223 Proposed Approach.md" "b/ResumableUpdate \342\200\223 Proposed Approach.md" deleted file mode 100644 index a23d2839..00000000 --- "a/ResumableUpdate \342\200\223 Proposed Approach.md" +++ /dev/null @@ -1,99 +0,0 @@ -# Resumable Firmware Update – Proposed Approach - -## Overview -This document describes the proposed approach for implementing **resumable firmware update** using a custom bootloader. - -The goal is to safely resume firmware updates after interruption (e.g., RS485 disconnect, tool closure), without risking corrupted firmware or unstable device behavior. - ---- - -## Key Concept - -Instead of blindly resuming from any byte position, the system will: - -- Write firmware in **fixed-size chunks** -- **Verify each chunk** -- Store **last valid written offset** -- Resume only from a **safe and verified point** - ---- - -## Firmware Update Flow - -### Step-by-Step Process - -1. **Start Update** - - Device enters **"update in progress"** state - - Bootloader takes control - -2. **Chunk-Based Writing** - - Firmware is divided into fixed-size chunks (e.g., 1KB / 4KB) - - Each chunk is: - - Written to flash - - Verified (CRC/checksum) - -3. **Progress Storage** - - After each successful chunk: - - Bootloader stores: - - Last valid offset - - Firmware identification (version/hash) - -4. **Interruption Handling** - - If update is interrupted: - - Device remains in bootloader mode - - Last valid offset is preserved - -5. **Resume Request** - - When update restarts: - - Software requests device status - - Device responds with: - - Last valid offset - - Firmware identification - -6. **Validation** - - Software verifies: - - Firmware file matches device info - - If mismatch: - - Restart full update (safety fallback) - -7. **Resume Update** - - Software sends remaining firmware from last valid offset - - Bootloader continues chunk-based writing + verification - -8. **Completion** - - After full firmware is written: - - Perform final validation (full image check) - - Clear "update in progress" flag - - Jump to main application - ---- -## Flowchart - -```mermaid -flowchart TD -A[Start Update] --> B[Bootloader Mode] -B --> C[Write Chunk] -C --> D[Verify Chunk] -D --> E[Save Offset] -E --> F{Interrupted} - -F -->|No| C -F -->|Yes| G[Wait Reconnect] - -G --> H[Request Status] -H --> I[Send Offset] - -I --> J{Match} - -J -->|No| K[Restart] -J -->|Yes| L[Resume] - -L --> C - -C --> M{Done} - -M -->|No| C -M -->|Yes| N[Validate] - -N --> O[Clear State] -O --> P[Run App] \ No newline at end of file