Making Embedded Communication Reliable: A Reusable COBS Library

Reusable COBS library

What is the issue?

Embedded systems often communicate over serial connections like UART, SPI, or USB. These communication channels are prone to issues when binary data gets misinterpreted as control characters. Especially when using protocols that rely on delimiters (0x00) to signify the end of a message. In these scenarios, a single errant byte can cause synchronization loss, garbled data, or dropped messages. This is especially problematic in low-level embedded systems, where memory is constrained, error handling is minimal, and debugging is difficult. Developers are often forced to write custom encoding schemes or rely on inefficient workarounds to ensure their messages are interpreted correctly.

In many communication protocols, the null byte is used as a packet delimiter, marking the boundary between messages. The problem arises when raw binary data includes the same byte value: the receiver might mistakenly interpret it as the end of the message, even though it’s just part of the payload. This is where Consistent Overhead Byte Stuffing (COBS) comes in as a clean and elegant solution.

Book a Call with Dojo Five Embedded Experts

What is the proposed solution?

COBS solves this by removing all instances of a special character byte (e.g. 0x00) from the data during encoding, replacing them with offsets to the next special character byte being replaced in the data. The offsets are a compact representation that avoids ambiguity. This encodes the input data into a format that guarantees no zero bytes will appear in the output (except as the final delimiter). This ensures that receivers can unambiguously identify message boundaries, regardless of the data being transmitted. See the example below:

Say we are trying to encode the following message:

{0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x57, 0x6F, 0x72, 0x6C, 0x64}

Encoding it with COBS would result in the following message:

{0x06, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x06, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x00}

The message has been encoded so that 0x00 character bytes have been replaced with offset and basically reserving 0x00 to denote the end of the frame. Another way to describe it would be:

[offset=0x06][5 bytes of non-zero][offset=0x06][5 bytes of non-zero][delimiter=0x00]

Another strength of COBS is that it does this with a small and consistent overhead. Unlike escape-based schemes, where special characters are replaced with multi-byte sequences that can vary in length and reduce throughput, COBS ensures that every message incurs a predictable overhead – typically no more than one byte per 254 bytes of input. This makes it an efficient and elegant solution, especially for embedded systems where very byte counts.

The decoding process is equally straightforward and fast, making COBS a good fit for low-power, real-time environments. Its simplicity also reduces the likelihood of bugs, which is critical in systems where stability and uptime are key.

In short, COBS effectively transforms arbitrary binary data into a well structured format that’s safe for delimiter-based serial protocols-no escaping, no guesswork and no synchronization headaches.

The reusable COBS library

To address this issue, we’ve developed a lightweight, reusable COBS library designed specifically for embedded systems. It’s written in C, with minimal dependencies and a clean API that’s easy to integrate into both bare-metal and RTOS-based environments.

The library provides two primary functions: cobs_encode() and cobs_decode(). These take input buffers and output buffers, along with their respective lengths. It’s optimized for performance, making it suitable for microcontrollers with limited resources.

To make integration seamless, the library leverages CMake to support easy inclusion in other CMake-based projects. This allows client applications to build and link the library by adding it via add_subdirectory() or CMake’s FetchContent.

In addition to the core functionality, the library is well-documented and includes a suite of unit tests to validate edge cases and error handling. It’s intended to be reused across projects and can serve as a shared component in a larger communication stack or protocol.

A simple example for integrating this COBS library in an embedded project would be to use it as part of a Zephyr project. We created a nRF Connect SDK-based project demo built with CMake where we set up a  nRF5340 Development Kit (DK) board to receive and echo serial data encoded with our COBS library over a USB serial connection. To do this, we use Zephyr’s uart and usb device drivers, which activates the USB stack and exposes the board as a virtual serial device (typically via the CDC-ACM driver). This makes it easy to connect to the embedded device from a PC. To build the project with the COBS library, we followed the following simple steps:

  1. Clone the COBS library repo into libs in the root of your project. This should result in a cobs folder added to your project
  2. Update the parent CMakeLists.txt to:
    • add_subdirectory(lib/cobs) <- build the library as subdirectory
    • target_link_libraries(app PRIVATE COBS) <- link the COBS target to the app
  3. Build and flash your NCS project as you would normally (e.g. with nRF Connect SDK extension for VS Code)

And just like that you can get started calling the API’s cob_encode and cob_decode functions. When dealing with COBS encoding the user needs to be mindful of the size of the buffer being used for encoding. The library conveniently provides the COBS_ENCODED_MAX_LENGTH(input_len) macro for users to double check their encoding buffer sizes ahead of time.

Conclusion

COBS is a powerful but often overlooked solution for reliable serial communication in embedded systems. By turning into a reusable library, we’re not just solving a technical problem, we’re also promoting good software engineering practices like modularity, reuse, and testing. Whether you’re working on a one-off prototype or scaling up a fleet of connected devices, having a solid COBS implementation in your toolbox is a smart move.

Let’s Work Together

At Dojo Five, we’re focused on modernizing embedded firmware. 

We bring modern tools, techniques, and best practices and pair them with leading-edge innovations in firmware. 

We help our customers build successful products. Book a Call with Dojo Five and let’s talk about your project.

Discover why Dojo Five EmbedOps is the embedded enterprise choice for build tool and test management.

Sign up to receive a free account to the EmbedOps platform and start building with confidence..

  • Connect a repo
  • Use Dev Containers with your Continuous Integration (CI) provider
  • Analyze memory usage
  • Integrate and visualize static analysis results
  • Perform Hardware-in-the-Loop (HIL) tests
  • Install the Command Line Interface for a developer-friendly experience

Subscribe to our Monthly Newsletter

Subscribe to our monthly newsletter for development insights delivered straight to your inbox.

Interested in learning more?

Best-in-class embedded firmware content, resources and best practices

Laptop with some code on screen

I want to write my first embedded program. Where do I start?

The boom in the Internet of Things (IoT) commercial devices and hobbyist platforms like the Raspberry Pi and Arduino have created a lot of options, offering inexpensive platforms with easy to use development tools for creating embedded projects. You have a lot of options to choose from. An embedded development platform is typically a microcontroller chip mounted on a circuit board designed to show off its features. There are typically two types out there: there are inexpensive versions, sometimes called

Read More »
Medical device monitoring vitals

IEC-62304 Medical Device Software – Software Life Cycle Processes Primer – Part 1

IEC-62304 Software Lifecycle requires a lot of self-reflection to scrutinize and document your development processes. There is an endless pursuit of perfection when it comes to heavily regulated industries. How can you guarantee something will have zero defects? That’s a pretty hefty task. The regulatory approach for the medical device industry is process control. The concept essentially states that if you document how every step must be completed, and provide checks to show every step has been completed properly, you

Read More »
Operating room filled with medical devices

IEC-62304 Medical Device Software – Software Life Cycle Processes Primer – Part II

Part I provides some background to IEC-62304. Part II provides a slightly more in-depth look at some of the specifics. The IEC 62304 Medical Device Software – Software Lifecycle Processes looks into your development processes for creating and maintaining your software. The standard is available for purchase here. So what activities does the standard look at? Here are some of the major topics. For any given topic, there will be a lot more specifics. This will look at a few

Read More »