X/YMODEM Programming Guide
The xymodem component implements the XMODEM and YMODEM protocol layer. It handles handshaking, packet framing, checksum or CRC verification, and retries. Applications provide a transport for the physical link and a source or sink for the payload.
Currently supported features:
- XMODEM
- XMODEM-CRC (CRC-16)
- XMODEM-1K (1024-byte blocks)
Not supported yet:
- YMODEM
- YMODEM-G
Add the Component to Your Project
idf.py add-dependency "espressif/xymodem"
XMODEM Framework
The protocol layer sits between application data and the communication channel. Users provide a transport plus a data source (send) or data sink (receive):
+----------------+ +----------------+ +----------------+
| Data Source | | | | Transport |
| | | | | |
| read() | ---> | XMODEM | ---> | send() |
+----------------+ | | | |
| | | |
+----------------+ | | | |
| Data Sink | | | | |
| | | | | |
| write() | <--- | | <--- | recv() |
+----------------+ +----------------+ +----------------+
- Transport (
xymodem_transport_t):send()andrecv()over UART, UHCI, or any other byte stream. Timeout onrecv()is not treated as an error; a short read is only allowed after the timeout expires. - Data source (
xmodem_data_source_t): where the sender reads payload bytes. The source may be an array, a file, or any other stream. - Data sink (
xmodem_data_sink_t): where the receiver delivers payload bytes. XMODEM cannot distinguish trailing padding (0x1A) from real data; the padding is also passed towrite(). The sink may store the data or process it on the fly.
All transport and source/sink callbacks are user-provided and run in task context.
Receiver-Driven Model and Timeouts
The original XMODEM specification recommends a receiver-driven model. After sending a packet, the sender does not retransmit unless it receives an explicit NAK. If data is corrupted or lost, retransmission is always initiated by the receiver using a relatively short timeout. The sender uses a much longer timeout; if no valid response arrives, it assumes the link is completely lost. This helps avoid the confusion that can arise when both sides attempt retransmission independently.
This component follows that design. The sender configuration response_timeout_ms is the time to wait for the handshake or for ACK / NAK. It is not a retransmission timeout. If it expires with no valid response, the sender treats the link as lost (ESP_ERR_XYMODEM_NO_RESPONSE). The receiver configuration packet_timeout_ms is the time to wait for a packet before sending NAK. It is recommended to set response_timeout_ms several times longer than packet_timeout_ms.
The XYMODEM_SEND_CONFIG_DEFAULT() and XYMODEM_RECV_CONFIG_DEFAULT() macros provide a set of configurations suitable for reliable connections and modern peer devices. If different behavior is required, you can override individual settings or provide your own configuration.
Using XMODEM
Provide a transport, a send or receive configuration, and a source or sink, then call xmodem_send() or xmodem_recv(). Note that both functions block the current task until the transfer completes.
Sender:
#include "xymodem.h"
xymodem_transport_t transport = {
.recv = my_uart_recv,
.send = my_uart_send,
.ctx = uart_ctx,
};
/**
* Default sender configuration:
* - Use 1024-byte blocks by default, and switch to 128-byte blocks if the remaining data is no more than 128 bytes.
* - Wait for handshake or ACK/NAK for 30 seconds.
* - Maximum 10 extra retries after the first attempt.
* - Send 2 CAN bytes when aborting.
*/
xymodem_send_config_t send_config = XYMODEM_SEND_CONFIG_DEFAULT();
xmodem_data_source_t source = {
.read = my_read,
.ctx = file_ctx,
};
ESP_ERROR_CHECK(xmodem_send(&transport, &send_config, &source));
Receiver:
#include "xymodem.h"
xymodem_transport_t transport = {
.recv = my_uart_recv,
.send = my_uart_send,
.ctx = uart_ctx,
};
/**
* Default receiver configuration:
* - Use CRC-16 by default, and fall back to SUM if the peer does not support it.
* - Wait up to 3 seconds for the packet. If none is received, send a NAK to request retransmission.
* - Maximum 10 extra retries after the first attempt.
* - Send 2 CAN bytes when aborting.
*/
xymodem_recv_config_t recv_config = XYMODEM_RECV_CONFIG_DEFAULT();
xmodem_data_sink_t sink = {
.write = my_write,
.ctx = file_ctx,
};
ESP_ERROR_CHECK(xmodem_recv(&transport, &recv_config, &sink));
The examples/xmodem project shows a complete transport on the UART driver.
If the payload is already a contiguous buffer, you do not need a source or sink. xmodem_send_buffer() sends len bytes from buf as the XMODEM payload. xmodem_recv_buffer() writes the incoming stream into buf (at most len bytes) and reports how many bytes were stored. Use the generic read / write callbacks instead when data comes from a file, is generated on the fly, or must be processed as each packet arrives.
XMODEM cannot tell payload from trailing 0x1A padding, so xmodem_recv_buffer() stores that padding as well. If the last packet is larger than the remaining buffer space, the extra bytes in that packet are discarded (they may be padding). If another packet arrives after the buffer is already full, the transfer is aborted with ESP_ERR_NO_MEM.
API Reference
See API Reference for types, error codes, and function details.
API Reference
Header files
File include/xymodem.h
Structures and Types
| Type | Name |
|---|---|
| struct | xmodem_data_sink_t The abstraction representing where data received by XMODEM is delivered. |
| struct | xmodem_data_source_t The abstraction representing the source of data to be sent via XMODEM. |
| enum | xymodem_block_size_t Block size as defined by the X/YMODEM protocol. |
| enum | xymodem_check_type_t Verification types as defined by the X/YMODEM protocol. |
| typedef esp_err_t(* | xymodem_read_fn_t Read data from a source, such as a file or a simple array. The protocol layer obtains the data to be sent through this function. |
| struct | xymodem_recv_config_t Receiver configuration. |
| struct | xymodem_send_config_t Sender configuration. |
| struct | xymodem_transport_t The abstraction of the low-level communication channel used by X/YMODEM protocol layer. |
| typedef esp_err_t(* | xymodem_write_fn_t Write data to a destination, such as a file or a simple array. The data received by the protocol layer is passed to the upper-layer application through this function. |
Functions
| Type | Name |
|---|---|
| esp_err_t | xmodem_recv (const xymodem_transport_t *transport, const xymodem_recv_config_t *config, const xmodem_data_sink_t *sink) Receive data using the XMODEM protocol. |
| esp_err_t | xmodem_recv_buffer (const xymodem_transport_t *transport, const xymodem_recv_config_t *config, void *buf, size_t len, size_t *recv_len) Directly receive data into a buffer using XMODEM. |
| esp_err_t | xmodem_send (const xymodem_transport_t *transport, const xymodem_send_config_t *config, const xmodem_data_source_t *source) Send data using the XMODEM protocol. |
| esp_err_t | xmodem_send_buffer (const xymodem_transport_t *transport, const xymodem_send_config_t *config, const void *buf, size_t len) Directly send the contents of a buffer using XMODEM. |
Macros
| Type | Name |
|---|---|
| define | ESP_ERR_XYMODEM_BAD_PACKET (ESP_ERR_XYMODEM_BASE + 3) Received data is not a valid packet. |
| define | ESP_ERR_XYMODEM_BASE 0x1E000 |
| define | ESP_ERR_XYMODEM_CANCEL_BY_PEER (ESP_ERR_XYMODEM_BASE + 1) Cancelled by the peer (two consecutive CAN bytes). |
| define | ESP_ERR_XYMODEM_NO_RESPONSE (ESP_ERR_XYMODEM_BASE + 4) Peer did not respond within the timeout. |
| define | ESP_ERR_XYMODEM_OUT_OF_SYNC (ESP_ERR_XYMODEM_BASE + 5) Packet sequence number is neither expected nor previous. |
| define | ESP_ERR_XYMODEM_RETRY_EXCEEDED (ESP_ERR_XYMODEM_BASE + 2) Retry limit reached. |
| define | XYMODEM_RECV_CONFIG_DEFAULT () Default XMODEM receiver configuration. |
| define | XYMODEM_SEND_CONFIG_DEFAULT () Default XMODEM sender configuration. |
Structures and Types Documentation
struct xmodem_data_sink_t
The abstraction representing where data received by XMODEM is delivered.
Variables:
-
void * ctx
User-defined context passed towrite(). -
xymodem_write_fn_t write
Deliver received payload bytes to the application.
Note:
The XMODEM protocol cannot distinguish the payload from trailing padding. The padding is also delivered through this function.
struct xmodem_data_source_t
The abstraction representing the source of data to be sent via XMODEM.
Variables:
-
void * ctx
User-defined context passed toread(). -
xymodem_read_fn_t read
Read the next chunk of payload to transmit.
enum xymodem_block_size_t
Block size as defined by the X/YMODEM protocol.
enum xymodem_block_size_t {
XYMODEM_BLOCK_SIZE_MIXED = 0,
XYMODEM_BLOCK_SIZE_128 = 128,
XYMODEM_BLOCK_SIZE_1024 = 1024
};
enum xymodem_check_type_t
Verification types as defined by the X/YMODEM protocol.
enum xymodem_check_type_t {
XYMODEM_CHECK_AUTO = 0,
XYMODEM_CHECK_SUM8,
XYMODEM_CHECK_CRC16
};
typedef xymodem_read_fn_t
Read data from a source, such as a file or a simple array. The protocol layer obtains the data to be sent through this function.
typedef esp_err_t(* xymodem_read_fn_t) (void *ctx, void *buf, size_t len, size_t *read_len);
Parameters:
ctxUser-defined context.bufBuffer to store read data.lenRequested length of data to read.read_lenActual length of data read.
Returns:
ESP_OK if the data was successfully read. Returning an error will abort the X/YMODEM transfer.
Note:
This function attempts to read exactly len bytes, but may return fewer bytes if the end of the source is reached. A short read (read_len < len) is only allowed when no further data is available. read_len must not exceed len
struct xymodem_recv_config_t
Receiver configuration.
Variables:
-
uint32_t can_interval_ms
Max interval between two CAN bytes when recognizing peer cancel. Must be > 0. -
uint8_t cancel_can_count
Number of CAN bytes to send when aborting. Must be >= 2. -
xymodem_check_type_t check_type
Checksum or CRC mode to request during handshake. -
uint32_t max_retry_cnt
Maximum number of extra retries after the first attempt. -
uint32_t packet_timeout_ms
Wait for a packet before sending NAK. Must be > 0. -
uint32_t purge_idle_timeout_ms
RX idle time required to finish a purge. Set based on the physical link rate.
struct xymodem_send_config_t
Sender configuration.
Variables:
-
xymodem_block_size_t block_size
Packet payload size to use when sending. -
uint32_t can_interval_ms
Max interval between two CAN bytes when recognizing peer cancel. Must be > 0. -
uint8_t cancel_can_count
Number of CAN bytes to send when aborting. Must be >= 2. -
uint32_t max_retry_cnt
Maximum number of extra retries after the first attempt. -
uint32_t purge_idle_timeout_ms
RX idle time required to finish a purge. Set based on the physical link rate. -
uint32_t response_timeout_ms
Wait for handshake or ACK/NAK. Must be > 0. Set several times longer than the receiver packet timeout.
struct xymodem_transport_t
The abstraction of the low-level communication channel used by X/YMODEM protocol layer.
Variables:
-
void * ctx
User-defined context passed torecv()_ and_send(). -
esp_err_t(* recv
Receive data through some interface, such as a serial port.
Parameters:
ctxUser-defined context.bufBuffer to store received data.lenRequested length of data to receive.recv_lenActual length of data received.timeout_msTimeout in milliseconds. 0 means non-blocking.
Returns:
- ESP_OK if the requested amount of data is received in full, or if the timeout expires.
- others if an error occurs during the transfer. Timeout is NOT considered as an error.
Note:
This function attempts to receive the full requested amount within the timeout period. If the timeout is reached, return whatever data has been received. Returning early with a short read before the timeout is not allowed.
- esp_err_t(* send
Send data through some interface, such as a serial port.
Parameters:
ctxUser-defined context.bufBuffer to send.lenLength of data to send.
Returns:
- ESP_OK if all data was successfully sent or queued for transmission.
- others if an error occurs during the transfer.
typedef xymodem_write_fn_t
Write data to a destination, such as a file or a simple array. The data received by the protocol layer is passed to the upper-layer application through this function.
typedef esp_err_t(* xymodem_write_fn_t) (void *ctx, const void *buf, size_t len);
Parameters:
ctxUser-defined context.bufBuffer containing the data to write.lenLength of data to write.
Returns:
ESP_OK if all data was successfully written. Returning an error will abort the X/YMODEM transfer.
Note:
This function can also process the data on the fly, without necessarily storing it anywhere.
Functions Documentation
function xmodem_recv
Receive data using the XMODEM protocol.
esp_err_t xmodem_recv (
const xymodem_transport_t *transport,
const xymodem_recv_config_t *config,
const xmodem_data_sink_t *sink
)
This function performs a complete XMODEM reception using the provided transport and data sink, and blocks until the transmission completes, fails, or is cancelled.
Parameters:
transportTransport interface used for sending and receiving protocol data.configXMODEM receiver configuration.sinkData sink that receives the transmitted payload.
Returns:
- ESP_OK: Reception completed successfully.
- ESP_ERR_INVALID_ARG: One or more arguments or configuration values are invalid.
- ESP_ERR_NO_MEM: Not enough memory to complete the reception.
- ESP_ERR_XYMODEM_NO_RESPONSE: The peer did not respond within the configured timeout.
- ESP_ERR_XYMODEM_CANCEL_BY_PEER: The transmission was cancelled by the peer.
- ESP_ERR_XYMODEM_RETRY_EXCEEDED: The maximum number of retries was exceeded.
- ESP_ERR_XYMODEM_OUT_OF_SYNC: The received packet sequence is out of sync.
- Other errors returned by the transport or data sink.
function xmodem_recv_buffer
Directly receive data into a buffer using XMODEM.
esp_err_t xmodem_recv_buffer (
const xymodem_transport_t *transport,
const xymodem_recv_config_t *config,
void *buf,
size_t len,
size_t *recv_len
)
Parameters:
transportThe transport layer to use.configThe receive configuration.bufThe buffer to receive the data.lenThe length of the buffer.recv_lenNumber of bytes stored inbuf. May be NULL.
Note:
XMODEM cannot distinguish payload data from trailing padding, so the last packet may be larger than the remaining buffer space. Bytes from that packet that fit are stored; the rest of the packet is discarded. If another packet arrives after buf is already full, the transfer is aborted.
Returns:
- ESP_OK if the transfer completed.
recv_len(when not NULL) is the number of bytes stored inbuf, which is at mostlen. - ESP_ERR_NO_MEM:
bufwas filled and the peer sent another packet. - others if an error occurs (same as
xmodem_recv()).recv_lenstill reports how many bytes were stored before the failure.
function xmodem_send
Send data using the XMODEM protocol.
esp_err_t xmodem_send (
const xymodem_transport_t *transport,
const xymodem_send_config_t *config,
const xmodem_data_source_t *source
)
This function performs a complete XMODEM transmission using the provided transport and data source, and blocks until the transmission completes, fails, or is cancelled.
Parameters:
transportTransport interface used for sending and receiving protocol data.configXMODEM sender configuration.sourceData source providing the payload to be transmitted.
Returns:
- ESP_OK: Transmission completed successfully.
- ESP_ERR_INVALID_ARG: One or more arguments or configuration values are invalid.
- ESP_ERR_NO_MEM: Not enough memory to complete the transmission.
- ESP_ERR_XYMODEM_NO_RESPONSE: The peer did not respond within the configured timeout.
- ESP_ERR_XYMODEM_CANCEL_BY_PEER: The transmission was cancelled by the peer.
- ESP_ERR_XYMODEM_RETRY_EXCEEDED: The maximum number of retries was exceeded.
- Other errors returned by the transport or data source.
function xmodem_send_buffer
Directly send the contents of a buffer using XMODEM.
esp_err_t xmodem_send_buffer (
const xymodem_transport_t *transport,
const xymodem_send_config_t *config,
const void *buf,
size_t len
)
Parameters:
transportThe transport layer to use.configThe send configuration.bufThe buffer to send.lenThe length of the buffer.
Returns:
- ESP_OK if the data was successfully sent.
- others if an error occurs (same as
xmodem_send()).
Macros Documentation
define ESP_ERR_XYMODEM_BAD_PACKET
Received data is not a valid packet.
#define ESP_ERR_XYMODEM_BAD_PACKET ( ESP_ERR_XYMODEM_BASE + 3)
define ESP_ERR_XYMODEM_BASE
#define ESP_ERR_XYMODEM_BASE 0x1E000
define ESP_ERR_XYMODEM_CANCEL_BY_PEER
Cancelled by the peer (two consecutive CAN bytes).
#define ESP_ERR_XYMODEM_CANCEL_BY_PEER ( ESP_ERR_XYMODEM_BASE + 1)
define ESP_ERR_XYMODEM_NO_RESPONSE
Peer did not respond within the timeout.
#define ESP_ERR_XYMODEM_NO_RESPONSE ( ESP_ERR_XYMODEM_BASE + 4)
define ESP_ERR_XYMODEM_OUT_OF_SYNC
Packet sequence number is neither expected nor previous.
#define ESP_ERR_XYMODEM_OUT_OF_SYNC ( ESP_ERR_XYMODEM_BASE + 5)
define ESP_ERR_XYMODEM_RETRY_EXCEEDED
Retry limit reached.
#define ESP_ERR_XYMODEM_RETRY_EXCEEDED ( ESP_ERR_XYMODEM_BASE + 2)
define XYMODEM_RECV_CONFIG_DEFAULT
Default XMODEM receiver configuration.
#define XYMODEM_RECV_CONFIG_DEFAULT (
) { \
.check_type = XYMODEM_CHECK_AUTO , \
.packet_timeout_ms = 3000, \
.max_retry_cnt = 10, \
.cancel_can_count = 2, \
.can_interval_ms = 100, \
.purge_idle_timeout_ms = 10, \
}
Note:
These defaults are intended for a good-quality link to a modern peer (CRC-16, 1K blocks). For noisy links, slower devices, or legacy implementations, adjust the fields as needed.
define XYMODEM_SEND_CONFIG_DEFAULT
Default XMODEM sender configuration.
#define XYMODEM_SEND_CONFIG_DEFAULT (
) { \
.block_size = XYMODEM_BLOCK_SIZE_MIXED , \
.response_timeout_ms = 30000, \
.max_retry_cnt = 10, \
.cancel_can_count = 2, \
.can_interval_ms = 100, \
.purge_idle_timeout_ms = 10, \
}
Note:
These defaults are intended for a good-quality link to a modern peer (CRC-16, 1K blocks). For noisy links, slower devices, or legacy implementations, adjust the fields as needed.