一、前言
为什么要使用Ymodem
前面写了ML307 SDK开发,Modbus移植,对于实现普通的DTU已经够用了。
目前公司想要通过4G(ML307)网络,给子设备进行升级,而不再依赖其它家的网关。
当前传输协议X/Y/Zmodem,对比下来还是YModem最为合适,X每次最大支持128字节,Y支持1024字节,而Z采用串流方式,X太慢,Z难度高,Y较快开发成本低,故选择了Y。
实现思路
ML307通过http获取升级包大小,通过Ymodem传给设备,通讯成功后,每次下载一部分(如:10kb,因为直接下载RAM可能不够),通过协议发给设备,直至传输完成。
二、工作流程
在 YMODEM 协议的基本流程中,发送方会先发送一个起始帧,其中包含文件名和文件大小等信息。接收方在收到起始帧后,会发送一个确认信号(ACK)。然后,发送方开始以 1024 字节的块发送文件数据,每发送一个数据块后都会等待接收方的 ACK 信号。如果接收方成功接收到数据块并通过 CRC 校验,则发送 ACK 信号;否则,发送否定确认信号(NAK)请求重发。当文件传输完成后,发送方会发送一个结束帧(EOT),接收方在收到结束帧后会再次发送 ACK 信号进行确认。

三、安装依赖
没有找到ml307相关库,这里采用esp32的Ymodem进行修改。
// 原地址
https://github.com/loboris/ESP32_ymodem_example
// 修改地址
https://download.csdn.net/download/qq_35738093/91946635
主要修改
按部分下载,将下载的升级包放入内存,发送完内存中的包后,再从网络上下载升级包,直到传输完成。
四、快速使用
下载修改后的Ymodem
https://download.csdn.net/download/qq_35738093/91946635
编写回调函数
#define OTA_JSON_BUFFER_SIZE    1024    // JSON数据缓冲区大小
#define OTA_FIRMWARE_BUFFER_SIZE (10 * 1024)   // 固件下载缓冲区大小(分批下载缓冲区)
#define OTA_URL_MAX_LENGTH      256     // URL最大长度
#define OTA_VERSION_MAX_LENGTH  32      // 版本号最大长度
/****************************************************************************
 * Private Data
 ****************************************************************************/
static uint8_t ota_json_buffer[OTA_JSON_BUFFER_SIZE];       // JSON数据缓冲区
static uint8_t ota_firmware_buffer[OTA_FIRMWARE_BUFFER_SIZE]; // 固件数据缓冲区
// YModem传输相关的全局变量
static uint32_t g_firmware_total_size = 0;     // 固件总大小
static uint32_t g_firmware_downloaded = 0;     // 已下载的固件大小
static char g_firmware_url[OTA_URL_MAX_LENGTH]; // 固件下载URL
// 批量下载缓冲区管理变量
static uint32_t g_buffer_start_offset = 0;     // 缓冲区起始偏移量
static uint32_t g_buffer_valid_size = 0;       // 缓冲区有效数据大小
static bool g_buffer_valid = false;            // 缓冲区是否有效
// 本机升级进度相关变量
static uint32_t g_local_upgrade_total_size = 0;    // 本机升级固件总大小
static uint32_t g_local_upgrade_downloaded = 0;    // 本机升级已下载大小
static bool g_local_upgrade_in_progress = false;   // 本机升级是否正在进行
/**
 * @brief YModem数据回调函数
 * @param block_number 数据块编号(从1开始)
 * @param buffer 数据缓冲区
 * @param buffer_size 缓冲区大小
 * @return 实际获取的数据长度,0表示没有更多数据,-1表示错误
 */
static int ymodem_data_callback(uint32_t block_number, uint8_t *buffer, uint32_t buffer_size)
{
    int ret = 0;
    uint32_t current_offset = 0;
    uint32_t copy_len = 0;
    uint32_t firmware_size = 0;
    uint32_t downloaded_size = 0;
    
    if (buffer == NULL || buffer_size == 0) {
        hal_error_log("[OTA] Invalid buffer parameters");
        return -1;
    }
    
    // 计算当前块的文件偏移量
    current_offset = (block_number - 1) * PACKET_1K_SIZE;
    
    // 检查是否已经下载完成
    if (current_offset >= g_firmware_total_size) {
        hal_info_log("[OTA] All firmware data downloaded, offset: %d, total: %d", current_offset, g_firmware_total_size);
        return 0; // 没有更多数据
    }
    
    // 检查当前请求的数据是否在缓冲区中
    if (!g_buffer_valid || 
        current_offset < g_buffer_start_offset || 
        current_offset >= (g_buffer_start_offset + g_buffer_valid_size)) {
        
        // 需要重新下载数据到缓冲区
        uint32_t download_offset = current_offset;
        uint32_t download_len = OTA_FIRMWARE_BUFFER_SIZE; // 下载10K数据
        
        // 确保不超过文件总大小
        if (download_offset + download_len > g_firmware_total_size) {
            download_len = g_firmware_total_size - download_offset;
        }
        
        hal_info_log("[OTA] Downloading new batch: offset=%d, len=%d", download_offset, download_len);
        
        // 清空缓冲区
        memset(ota_firmware_buffer, 0, OTA_FIRMWARE_BUFFER_SIZE);
        
        // 下载固件数据到10K缓冲区
        ret = hal_httpfile_download_sync(g_firmware_url,
                                       ota_firmware_buffer,
                                       OTA_FIRMWARE_BUFFER_SIZE,
                                       download_offset, 
                                       download_offset + download_len - 1, 
                                       0,
                                       &firmware_size, &downloaded_size);
        
        if (ret != 0) {
            hal_error_log("[OTA] Failed to download firmware batch, ret: %d", ret);
            g_buffer_valid = false;
            return -1;
        }
        
        if (downloaded_size == 0) {
            hal_error_log("[OTA] Downloaded size is 0");
            g_buffer_valid = false;
            return -1;
        }
        
        // 更新缓冲区状态
        g_buffer_start_offset = download_offset;
        g_buffer_valid_size = downloaded_size;
        g_buffer_valid = true;
        
        hal_info_log("[OTA] Batch downloaded: %d bytes, buffer range: %d-%d", 
                    downloaded_size, g_buffer_start_offset, g_buffer_start_offset + g_buffer_valid_size - 1);
    }
    
    // 从缓冲区复制数据到输出buffer
    uint32_t buffer_offset = current_offset - g_buffer_start_offset;
    copy_len = buffer_size;
    
    // 确保不超过缓冲区有效数据范围
    if (buffer_offset + copy_len > g_buffer_valid_size) {
        copy_len = g_buffer_valid_size - buffer_offset;
    }
    
    // 确保不超过文件总大小
    if (current_offset + copy_len > g_firmware_total_size) {
        copy_len = g_firmware_total_size - current_offset;
    }
    
    // 清空输出缓冲区并复制数据
    memset(buffer, 0, buffer_size);
    memcpy(buffer, ota_firmware_buffer + buffer_offset, copy_len);
    
    g_firmware_downloaded = current_offset + copy_len;
    hal_info_log("[OTA] Block %d: offset=%d, copy_len=%d, progress: %d/%d", 
                block_number, current_offset, copy_len, g_firmware_downloaded, g_firmware_total_size);
    
    return copy_len;
}
调用Ymodem_Transmit_WithCallback函数
Ymodem_Transmit_WithCallback(firmware_filename, total_file_size, ymodem_data_callback);
五、进行测试
常用的测试工具有SecureCRT,Tera Term但在使用SecureCRT最大直接收到16kb,之后不再接收了,不知道是不是软件的BUG,所以这里采用Tera Term。
选择:文件→传输→YMODEM

                  
                  
                  
                  
                            
                    
      
          
                
                
                
                
              
                
                
                
                
                
              
                
                
              
            
                  
					6718
					
被折叠的  条评论
		 为什么被折叠?
		 
		 
		
    
  
    
  
            


            