Skip to content

boot: boot_serial: add COBS framing for raw serial recovery#2773

Draft
JPHutchins wants to merge 1 commit into
mcu-tools:mainfrom
intercreate:feature-smp-serial-framing
Draft

boot: boot_serial: add COBS framing for raw serial recovery#2773
JPHutchins wants to merge 1 commit into
mcu-tools:mainfrom
intercreate:feature-smp-serial-framing

Conversation

@JPHutchins

Copy link
Copy Markdown
Contributor

This is a permanent draft PR made available to the community to gauge interest in this feature. A new PR and implementation would be provided if there is interest. We will be shipping it internally as a patch for now and adding support for it to smpclient.

Add BOOT_SERIAL_RAW_PROTOCOL_COBS, a framing layer on top of the raw (non-console) SMP recovery protocol that wraps each SMP packet as:

COBS(header || payload || CRC16) || 0x00

The CRC16 is CRC-16/XMODEM (polynomial 0x1021, initial value 0x0000, no reflection), computed over the header and payload and appended big-endian, identical to the CRC used by the SMP over console encoding. The packet and its CRC are then COBS-encoded, which removes every 0x00 from the stream, so a single trailing 0x00 is an unambiguous frame delimiter.

Unlike the bare raw protocol, this encoding is self-synchronising: the delimiter gives a resync point after any corruption or truncation, and the CRC16 rejects damaged packets. It therefore takes over the role of the input timeout, and the two are mutually exclusive. The existing BOOT_SERIAL_RAW_PROTOCOL_INPUT_TIMEOUT option becomes one member of a new choice, alongside the COBS option and BOOT_SERIAL_RAW_PROTOCOL_NONE (the original unframed behaviour). Migration: a configuration that previously set CONFIG_BOOT_SERIAL_RAW_PROTOCOL_INPUT_TIMEOUT=n to obtain the unframed raw behaviour must now set CONFIG_BOOT_SERIAL_RAW_PROTOCOL_NONE=y; configurations that leave the timeout at its default, or set it =y, are unaffected.

The COBS codec is a small dependency-free module (boot_serial/src/cobs.c) rather than Zephyr's CONFIG_COBS, which is net_buf-based and would pull the net_buf subsystem into serial recovery. It decodes in place, so the receive buffer grows only by the COBS overhead (at most one byte per 254 input bytes, under 1%) plus the delimiter.

The SMP client must be configured to use a matching COBS+CRC16 transport. No stock mcumgr client speaks this encoding yet, so this option is provided for evaluation and community review; the PR is intentionally a draft to gauge interest.

A serial_recovery_raw_cobs.conf fragment and a matching Twister build test (sample.bootloader.mcuboot.serial_recovery_raw_cobs) are added, along with a native_sim ztest for the COBS codec (boot/zephyr/tests/cobs).

Add BOOT_SERIAL_RAW_PROTOCOL_COBS, a framing layer on top of the raw
(non-console) SMP recovery protocol that wraps each SMP packet as:

    COBS(header || payload || CRC16) || 0x00

The CRC16 is CRC-16/XMODEM (polynomial 0x1021, initial value 0x0000, no
reflection), computed over the header and payload and appended big-endian,
identical to the CRC used by the SMP over console encoding. The packet and
its CRC are then COBS-encoded, which removes every 0x00 from the stream, so
a single trailing 0x00 is an unambiguous frame delimiter.

Unlike the bare raw protocol, this encoding is self-synchronising: the
delimiter gives a resync point after any corruption or truncation, and the
CRC16 rejects damaged packets. It therefore takes over the role of the input
timeout, and the two are mutually exclusive. The existing
BOOT_SERIAL_RAW_PROTOCOL_INPUT_TIMEOUT option becomes one member of a new
choice, alongside the COBS option and BOOT_SERIAL_RAW_PROTOCOL_NONE (the
original unframed behaviour). Migration: a configuration that previously set
CONFIG_BOOT_SERIAL_RAW_PROTOCOL_INPUT_TIMEOUT=n to obtain the unframed raw
behaviour must now set CONFIG_BOOT_SERIAL_RAW_PROTOCOL_NONE=y; configurations
that leave the timeout at its default, or set it =y, are unaffected.

The COBS codec is a small dependency-free module (boot_serial/src/cobs.c)
rather than Zephyr's CONFIG_COBS, which is net_buf-based and would pull the
net_buf subsystem into serial recovery. It decodes in place, so the receive
buffer grows only by the COBS overhead (at most one byte per 254 input bytes,
under 1%) plus the delimiter.

The SMP client must be configured to use a matching COBS+CRC16 transport. No
stock mcumgr client speaks this encoding yet, so this option is provided for
evaluation and community review; the PR is intentionally a draft to gauge
interest.

A serial_recovery_raw_cobs.conf fragment and a matching Twister build test
(sample.bootloader.mcuboot.serial_recovery_raw_cobs) are added, along with a
native_sim ztest for the COBS codec (boot/zephyr/tests/cobs).

Signed-off-by: JP Hutchins <jp@intercreate.io>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@nordicjm nordicjm left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

frankly if you want a transport that has the delimiter then you should use SMP over console. The point in SMP itself is it's a binary protocol, raw data over any transport (e.g. UDP, UART, bluetooth, LoRaWAN, CAN, etc.) the only reason the UART one diverged is to allow support for messages over a serial terminal where a console is also used, there should not be custom parts on top of transports except specifically for SMP over console

@JPHutchins

JPHutchins commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

frankly if you want a transport that has the delimiter then you should use SMP over console. The point in SMP itself is it's a binary protocol, raw data over any transport (e.g. UDP, UART, bluetooth, LoRaWAN, CAN, etc.) the only reason the UART one diverged is to allow support for messages over a serial terminal where a console is also used, there should not be custom parts on top of transports except specifically for SMP over console

Yeah, I do agree, generally, because the upload is not necessarily bound by the transport speed, it's usually bound by the flash write speed. We do have a situation here where we 1) want the smaller raw serial size (no base64) and 2) have a raw UART that benefits from the COBS + CRC16. This is not intended for merge because I think it muddies the waters for implementers to have not just 2, but 3 different serial transport options to deal with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants