# pamoja-serial
Serial-line packet framing for pamoja: SLIP (RFC 1055) and COBS byte-stuffing with streaming frame decoders, so a raw UART byte stream carries discrete, self-delimiting packets to and from motor controllers, GPS, and LiDAR, no_std and allocation-free. The framing half ahead of the serial driver.
<a href="https://pamoja.molex.cloud/docs/reference/rust/pamoja_serial/index.html"><img height="28" alt="API reference" src="https://raw.githubusercontent.com/molexxxx/pamoja/main/.github/badges/btn-api.svg"></a>
<a href="https://pamoja.molex.cloud/docs/guides/serial.html"><img height="28" alt="read the guide" src="https://raw.githubusercontent.com/molexxxx/pamoja/main/.github/badges/btn-guide.svg"></a>
<a href="https://crates.io/crates/pamoja-serial"><img height="28" alt="crates.io" src="https://raw.githubusercontent.com/molexxxx/pamoja/main/.github/badges/btn-cratesio.svg"></a>
<a href="https://docs.rs/pamoja-serial"><img height="28" alt="docs.rs" src="https://raw.githubusercontent.com/molexxxx/pamoja/main/.github/badges/btn-docsrs.svg"></a>
## The same capability in every language
| Rust | [`pamoja-serial`](https://crates.io/crates/pamoja-serial) | [reference](https://pamoja.molex.cloud/docs/reference/rust/pamoja_serial/index.html), [docs.rs](https://docs.rs/pamoja-serial), [install](https://pamoja.molex.cloud/docs/reference/rust.html#rust-serial) |
| TypeScript | [`@pamoja/serial`](https://www.npmjs.com/package/@pamoja/serial) | [reference](https://pamoja.molex.cloud/docs/reference/node/modules/_pamoja_serial.html), [install](https://pamoja.molex.cloud/docs/reference/node.html#node-serial) |
| Python | [`pamoja-serial`](https://pypi.org/project/pamoja-serial/) | [reference](https://pamoja.molex.cloud/docs/reference/python/pamoja/serial.html), [install](https://pamoja.molex.cloud/docs/reference/python.html#python-serial) |
| C# | [`Pamoja.Serial`](https://www.nuget.org/packages/Pamoja.Serial) | [reference](https://pamoja.molex.cloud/docs/reference/dotnet/api/Pamoja.Serial.html), [install](https://pamoja.molex.cloud/docs/reference/dotnet.html#dotnet-serial) |
Serial-line packet framing for the pamoja SDK.
A serial line, whether a bare UART, an RS232/RS485 link, or a USB-serial bridge, is a
raw stream of bytes with no notion of where one message ends and the next begins. The
parts a robot or a field node talks to over that line, motor controllers, GPS
receivers, LiDAR, and a long tail of cheap sensors, each send packets, so something has
to mark packet boundaries in the stream and survive the noise of a long cable. The
answer is byte stuffing: reserve one byte value as the frame delimiter and encode the
payload so that value can never occur inside it.
This crate is that framing layer, as pure logic with no serial port and no allocation:
- `slip` - SLIP (RFC 1055), the simplest serial framing there is: an `END` byte ends
a packet, and an escape pair carries `END` or the escape byte itself when they appear
in the data. Ubiquitous and trivial, at a worst case of doubling the payload.
- `cobs` - COBS (Consistent Overhead Byte Stuffing), which removes the zero byte from
the payload so a single zero delimits packets unambiguously, at a bounded worst case
of one byte of overhead per 254. This is the framing motor-control and robotics links
reach for when the overhead has to stay small and predictable.
Each module both encodes a packet into a frame and decodes
one back, rejecting a frame that arrived corrupt. Each also offers a streaming decoder,
`slip::SlipDecoder` and `cobs::CobsDecoder`, that reassembles whole frames from the
stream a byte at a time, because a UART hands an application arbitrary chunks rather
than tidy packets. It is the streaming decoder, not the one-shot call, that a real
serial read loop uses.
Everything is exact byte work over caller-provided buffers, so the same framing runs on
the smallest microcontroller hanging off the bus. Driving the serial line itself, the
baud rate and the bytes on the wire, arrives with the hardware-I/O layer; this is the
framing half ahead of it.
**Examples**
```rust
use pamoja_serial::{cobs, slip};
let payload = b"gps:37.42,-122.08";
let mut framed = [0u8; 64];
let mut restored = [0u8; 64];
// SLIP frames a packet with a delimiter byte, escaping any that appear in the data.
let n = slip::encode(payload, &mut framed)?;
let m = slip::decode(&framed[..n], &mut restored)?;
assert_eq!(&restored[..m], payload);
// COBS frames the same packet with bounded overhead and a single zero delimiter.
let n = cobs::encode(payload, &mut framed)?;
assert_eq!(framed[n - 1], 0x00); // the frame delimiter
let m = cobs::decode(&framed[..n], &mut restored)?;
assert_eq!(&restored[..m], payload);
```
## License
MIT - part of the [pamoja](https://github.com/molexxxx/pamoja) workspace: one memory-safe Rust core with bindings for every language.