赞
踩
Xmodem 和 Ymodem 是串口通信中广泛用到的异步文件传输协议。这个协议包括了文件的识别、传送的起止时间、错误的判断与纠正等内容。Xmodem、Ymodem 和 Zmodem 协议是最常用的三种通信协议。详情可以参考ymodem.txt。本文只介绍 Xmodem 和 Ymodem 协议,Zmodem 协议后续添加。
Xmodem 和 Ymodem 协议是串口文件传输协议,顾名思义可用于通过串口相连的 ESP 设备与 MCU 之间的文件传输。当 MCU 设备作为接收端时,ESP 设备通过 WIFI、BLE 或者其他方式获取 MCU 固件或者配置文件,通过串口文件传输协议传输到 MCU 端,MCU 根据接收到的固件或者配置文件进行升级或配置;当 MCU 设备作为发送端时,通过串口文件传输协议将 MCU 的日志或者配置文件等传输到 ESP 设备端,ESP 设备上传文件至云平台或者服务器。
例如: ESP 设备将从 OTA 平台获取到的固件通过 Xmodem 协议传输到 MCU 设备,从而实现 MCU 固件的 OTA 升级
Xmodem 协议最早是以 128 字节块的形式传输数据,并且每个块都使用校验和进行错误检测。后面衍生出使用循环冗余校验方式 (CRC16) 和支持 1024 字节块的传输协议 (Xmodem-1k)。
Ymodem 协议是 Xmodem 协议的改良版,以 1024 字节块的形式传输数据,并且支持传输多个文件。一般 Ymodem 协议是使用 CRC16 进行校验。
Xmodem 和 Ymodem 协议传输由接收程序和发送程序完成。先由接收程序发送协商字符协商校验方式,协商通过之后,发送程序开始发送数据包。接收程序接收到一个完整的数据包之后,按照协商的校验方式对数据包进行校验,校验通过之后发送确认字符,校验失败则发送否认字符。发送程序收到确认字符后继续发送下一包,收到否认字符后重传数据包。
Xmodem 和 Ymodem 从控制符定义和帧包格式上是基本一致的。
定义 | 取值 | 作用 |
---|---|---|
SOH | 0x01 | modem 128 字节头标志 |
STX | 0x02 | modem 1024 字节头标志 |
EOT | 0x04 | 发送结束标志 |
ACK | 0x06 | 应答标志 |
NAK | 0x15 | 非应答标志 |
CAN | 0x18 | 取消发送标志 |
CRC16 | 0x43 | 使用 CRC16 校验标志 |
Byte 1 | Byte 2 | Byte 3 | Byte 4- Byte 131 | Byte 132- Byte 133 |
---|---|---|---|---|
头标志 | 包序列 | ~包序列 | 包数据 | CRC16(2 Byte) |
说明:
流程里 NAK, ACK, CAN, CRC16 和 EOT 是没有包头和数据,只是一个单独的字符数据。
Sender | Flow | Receiver |
---|---|---|
<— | NAK | |
Timeout after 3 seconds | ||
<— | NAK | |
| SOH | 0x01 | 0xFE | Data[0-127] | Checksum | | —> | Packet ok |
<— | ACK | |
| SOH | 0x02 | 0xFD | Data[0-127] | Checksum | | —> | Miss the packet |
<— | NAK | |
| SOH | 0x02 | 0xFD | Data[0-127] | Checksum | | —> | Packet ok |
Miss the ACK | <— | ACK |
| SOH | 0x02 | 0xFD | Data[0-127] | Checksum | | —> | Packet ignore |
<— | ACK | |
| SOH | 0x03 | 0xFC | Data[0-127] | Checksum | | —> | Checksum error |
<— | NAK | |
| SOH | 0x03 | 0xFC | Data[0-127] | Checksum | | —> | Packet ok |
<— | ACK | |
| EOT | | —> | Packet ok |
Finished | <— | ACK |
计算 CRC16 校验的除数多项式为X ^ 16 + X ^ 12 + X ^ 5 + 1,信息报中的 128 数据字节将参加 CRC16 校验的计算,在发送端 CRC16 的高字节在前,低字节在后。
Sender | Flow | Receiver |
---|---|---|
<— | ‘C’ | |
Timeout after 3 seconds | ||
<— | ‘C’ | |
| SOH | 0x01 | 0xFE | Data[0-127] | CRCH | CRCL | | —> | Packet ok |
<— | ACK | |
| SOH | 0x02 | 0xFD | Data[0-127] | CRCH | CRCL | | —> | Miss the packet |
<— | NAK | |
| SOH | 0x02 | 0xFD | Data[0-127] | CRCH | CRCL | | —> | Packet ok |
Miss the ACK | <— | ACK |
| SOH | 0x02 | 0xFD | Data[0-127] | CRCH | CRCL | | —> | Packet ignore |
<— | ACK | |
| SOH | 0x03 | 0xFC | Data[0-127] | CRCH | CRCL | | —> | Checksum error |
<— | NAK | |
| SOH | 0x03 | 0xFC | Data[0-127] | CRCH | CRCL | | —> | Packet ok |
<— | ACK | |
| EOT | | —> | Packet ok |
Finished | <— | ACK |
说明:
C
,而校验和发送控制字符 NAK
,并且 CRC16 校验字段占 2 Byte。Ymodem 协议的起始帧并不直接传输文件的数据,而是将文件名与文件的大小放在数据帧中传输。它的数据帧结构如下:
Byte 1 | Byte 2 | Byte 3 | Byte 4- Byte 131 | Byte 132- Byte 133 |
---|---|---|---|---|
SOH | 0x00 | 0xFF | Filename | Filesize | NUL | CRC16(2 Byte) |
说明:
1E00
,最后的 0x00 代表文件长度结束。Ymodem 协议的结束帧与起始帧类似,结构如下:
Byte 1 | Byte 2 | Byte 3 | Byte 4- Byte 131 | Byte 132- Byte 133 |
---|---|---|---|---|
SOH | 0x00 | 0xFF | NUL | CRC16(2 Byte) |
文件传输流程:
Sender | Flow | Receiver |
---|---|---|
<— | ‘C’ | |
Timeout after 3 seconds | ||
<— | ‘C’ | |
| SOH | 0x00 | 0xFF | Filename | Filesize | NUL | CRCH | CRCL | | —> | Packet ok |
<— | ACK | |
| STX | 0x01 | 0xFE | Data[0-1024] | CRCH | CRCL | | —> | Packet ok |
<— | ACK | |
| STX | 0x02 | 0xFD | Data[0-1024] | CRCH | CRCL | | —> | Packet ok |
Miss the ACK | <— | ACK |
| STX | 0x02 | 0xFD | Data[0-1024] | CRCH | CRCL | | —> | Packet ignore |
<— | ACK | |
| STX | 0x03 | 0xFC | Data[0-1024] | CRCH | CRCL | | —> | Packet ok |
<— | ACK | |
| SOH | 0x04 | 0xFB | Data[0-127] | CRCH | CRCL | | —> | Packet ok |
<— | ACK | |
| EOT | | —> | Packet ok |
<— | NAK | |
| EOT | | —> | Packet ok |
<— | ACK | |
<— | ‘C’ | |
| SOH | 0x00 | 0xFF | NUL | CRCH | CRCL | | —> | Packet ok |
Finished | <— | ACK |
方案 | AT 透传 | Xmodem |
---|---|---|
数据校验和 | 不支持 | 支持 |
文件的识别 | 不支持 | 支持 |
错误的判断 | 不支持 | 支持 |
序列化传输 | 不支持 | 支持 |
相比于 AT 透传实现 MCU 升级,Xmodem 协议具有每笔数据报文的完整性校验、数据报文的有序传输、数据报文的乱序丢包判断和传输文件的识别等等优势。
相关其他串口文件传输协议,请参考维基百科。
归纳总结:
license
是 Public domain
。使用 Xmodem 和 Ymodem 串口文件传输协议实现 MCU 升级,对于 MCU 侧可按照标准协议文档实现,对于 ESP 设备侧可参考文档后续章节介绍。
本文基于 Xmodem 和 Ymodem 协议规范,针对 ESP8266_RTOS_SDK 和 ESP-IDF 平台开发了基于 UART 传输的文件传输协议组件。支持以下 Xmodem 和 Ymodem 组合协议功能。
Xmodem | Ymodem | |
---|---|---|
校验和 | 支持 | 支持 |
CRC16 | 支持 | 支持 |
1K + CRC16 | 支持 | 支持 |
本文将介绍如何使用该组件提供的接口进行串口文件传输。
windows 用户可安装虚拟机,在虚拟机中安装 linux。
如果您熟悉 ESP 开发环境,可以很顺利理解下面步骤;如果您不熟悉某个部分,比如编译,烧录,需要您结合官方的相关文档来理解。如您需阅读 ESP-IDF 编程指南文档等。
toolchain 设置参考 ESP-IDF 编程指南。
./components/esptool_py/esptool/esptool.py
./components/esptool_py/esptool/esptool.py
esptool 功能参考:
$ ./components/esptool_py/esptool/esptool.py --help
功能框架如下:
Xmodem Sender 和 Xmodem Receiver 上层遵循 Xmodem 协议,数据传输通过 transport 层将协议数据写入 UART 串口,然后 Xmodem 主机和从机通过串口通信协议传输数据。
esp_xmodem_transport_config_t transport_config = {
.baud_rate = 921600,
#ifdef CONFIG_IDF_TARGET_ESP32
.uart_num = UART_NUM_1,
.swap_pin = true,
.tx_pin = 17,
.rx_pin = 16,
.cts_pin = 15,
.rts_pin = 14,
#endif
};
esp_xmodem_transport_handle_t transport_handle = esp_xmodem_transport_init(&transport_config);
swap_pin
功能,将传输接收口 swap 到 IO15 和 IO13 上,bootloader 的输出还是通过 UART0 口输出。baud_rate
值越大,传输速率就越快。recv_timeout
是串口读取 ring buffer 的超时时间,默认建议选择 100 ms。esp_xmodem_config_t config = {
.role = ESP_XMODEM_SENDER,
.event_handler = xmodem_sender_event_handler,
.support_xmodem_1k = true,
};
esp_xmodem_handle_t sender = esp_xmodem_init(&config, transport_handle);
role
代表是 sender 还是 receiver, 后续调用 esp_xmodem_start
会根据 role
去选择起 sender 还是 receiver 去处理。support_xmodem_1k
仅对于发送者 (sender) 有效,表示是否支持按照 Xmodem-1k 方式传输数据。如果不设置,默认 support_xmodem_1k
为 false
,数据按照 128 字节发送。 如果设置 support_xmodem_1k
为 true
,就会按照 1024 字节发送。如果数据少于 1024 字节,大于 128 字节,就会按照 1024 字节发送,不足 1024 字节部分填充 0x1A 后发送。如果数据少于 128 字节,就会按照 128 字节发送,不足 128 字节部分填充 0x1A 后发送。event_handler
用于注册事件 event,根据相应的 event 处理不同事件,相应逻辑处理可以参考示例。 esp_xmodem_config_t config = {
.role = ESP_XMODEM_RECEIVER,
.crc_type = ESP_XMODEM_CRC16,
.event_handler = xmodem_receiver_event_handler,
.recv_cb = xmodem_data_recv,
.cycle_max_retry = 25,
};
esp_xmodem_handle_t receiver = esp_xmodem_init(&config, transport_handle);
crc_type
是 receiver 支持的校验方式。recv_cb
仅对于接收者 (receiver) 有效,用于底层接收到文件给用户层的回调函数。相应逻辑处理可以参考 (recevier) 示例。esp_xmodem_start(sender);
ESP_OK
判断有没有启动成功.ESP_XMODEM_EVENT_CONNECTED
事件,然后处理相应逻辑。本例中是起了一个http client task 来下载文件。esp_xmodem_start(receiver);
crc_type
的值发 ‘C’ 或者 NAK,一旦有 sender 发送数据,就会上报 ESP_XMODEM_EVENT_CONNECTED
事件,并且数据会上报至注册的 recv_cb
中。如果是文件传输,会在 ESP_XMODEM_EVENT_ON_FILE
事件中上报文件名和文件长度。参考 工具链的设置
设置 IDF_PATH
,运行 $IDF_PATH/install.sh
安装相关工具,执行 $IDF_PATH/export.sh
导出路径。
make menuconfig
修改串口烧录配置cd esp-xmodem/examples/xmodem_receiver
make defconfig
make
idf.py menuconfig
修改串口烧录配置cd esp-xmodem/examples/xmodem_receiver
idf.py build
make flash
,对于 cmake 执行 idf.py flash
。make erase_flash
或者 idf.py erase_flash
擦除 flash。make monitor
或者 idf.py -p (PORT) monitor
查看串口输出。使用 Flash 下载工具(ESP8266 & ESP32) 烧录 Xmodem 示例固件。
打开 flash download tool, ESP8266 的烧录配置如下:
打开 flash download tool, ESP32 的烧录配置如下:
点击 start 进行烧录, 烧录成功后按 EN 键重启开发板。
示例可以通过 Linux 系统命令 rz
和 sz
配合测试。这两者支持 Xmodem,Ymodem 和 Zmodem 文件传输协议。如果用户发现 Linux 系统上找不到该命令,可以执行如下命令安装。
sudo apt-get install lrzsz
rz
用于接收 sender 发送来的文件,sz
用于发送文件至 receiver。详细命令可以参考 rz --help
或者 sz --help
。
该示例用于充当 receiver 来接收 sender 发送的文件,利用文件进行 OTA。详细操作请参考示例下对应的 README 文件。
对于 sender 可以使用如下命令进行发送 OTA 文件:
sz --ymodem (file name or pure data) >/dev/ttyUSB0 </dev/ttyUSB0
其中 ttyUSB0
是用于与 receiver 进行文件传输的串口。
设备侧log:
... [17:46:56.538][0;32mI (286) uart: queue free spaces: 10[0m [17:46:56.575][0;32mI (3286) xmodem_receive: Waiting for Xmodem sender to send data...[0m [17:46:59.580][0;32mI (6286) xmodem_receive: Waiting for Xmodem sender to send data...[0m [17:47:02.618][0;32mI (6292) xmodem_receive: ESP_XMODEM_EVENT_CONNECTED[0m [17:47:02.618][0;32mI (6294) xmodem_receive: This is a file begin transfer[0m [17:47:02.618][0;32mI (6298) xmodem_receive: ESP_XMODEM_EVENT_ON_FILE[0m [17:47:02.618][0;32mI (6306) xmodem_receive: file_name is xmodem_receiver.bin, file_length is 176704[0m [17:47:02.618][0;32mI (6339) xmodem_receive: Starting OTA...[0m [17:47:02.641][0;32mI (6340) xmodem_receive: Writing to partition subtype 17 at offset 0x110000[0m [17:47:02.641][0;32mI (9305) xmodem_receive: esp_ota_begin succeeded[0m [17:47:05.607][0;32mI (9306) xmodem_receive: Please Wait. This may take time[0m [17:47:05.607][0;32mI (18056) xmodem_receive: Receive EOT data[0m [17:47:14.388][0;32mI (18059) xmodem_receive: Receive EOT data again[0m [17:47:14.388][0;32mI (18066) xmodem_receive: This is a file end transfer[0m [17:47:14.388][0;32mI (18067) xmodem_receive: ESP_XMODEM_EVENT_FINISHED[0m ... [17:47:14.531][0;32mI (18224) xmodem_receive: esp_ota_set_boot_partition succeeded[0m
该示例用于充当 sender 来发送通过 http 下载的文件。Linux 电脑上可以通过命令 python -m SimpleHTTPServer
起一个 Http Server。详细操作请参考示例下对应的 README 文件。
对于 receiver 可以使用如下命令进行接收文件:
rz --ymodem >/dev/ttyUSB0 </dev/ttyUSB0
其中 ttyUSB0
是用于与 receiver 进行文件传输的串口。
设备侧 log:
...
[17:40:45.177][0;32mI (481) example_connect: Connecting to HUAWEI_888...[0m
[17:40:45.188][0;32mI (1768) wifi:state: 0 -> 2 (b0)
[17:40:46.472][0m[0;32mI (1782) wifi:state: 2 -> 3 (0)
[17:40:46.485][0m[0;32mI (1790) wifi:state: 3 -> 5 (10)
[17:40:46.493][0m[0;32mI (1829) wifi:connected with HUAWEI_888, aid = 1, channel 1, HT20, bssid = 34:29:12:43:c5:40
[17:40:46.549][0m[0;31mE (1839) wifi: AES PN: 0000000000000000 <= 0000000000000000[0m
[17:40:46.549][0;32mI (4270) tcpip_adapter: sta ip: 172.168.30.131, mask: 255.255.255.0, gw: 172.168.30.1[0m
[17:40:49.019][0;32mI (4275) example_connect: Connected to HUAWEI_888[0m
[17:40:49.019][0;32mI (4279) example_connect: IPv4 address: 172.168.30.131[0m
[17:40:49.019][0;32mI (4288) xmodem_send: Connected to AP, begin http client task[0m
[17:40:49.019][0;32mI (4298) uart: queue free spaces: 10[0m
[17:40:49.019][0;32mI (14300) xmodem_send: Connecting to Xmodem receiver(1/25)[0m
[17:40:59.011][0;32mI (23638) xmodem_send: ESP_XMODEM_EVENT_CONNECTED[0m
[17:41:08.349][0;32mI (24771) xmodem_send: Send image success[0m
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。