Skip to content

Commit fe633e0

Browse files
committed
Merge pull request #4 from avilleret/feature/SLIPEncoding
add SLIP encoding
2 parents a4e4e5e + d1d7c81 commit fe633e0

File tree

3 files changed

+158
-3
lines changed

3 files changed

+158
-3
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PacketSerial
22
============
33

4-
_PacketSerial_ is an small, efficient, library that allows [Arduinos](http://www.arduino.cc/) to send and receive serial data packets that include bytes with any value (0-255). A _packet_ is simply an array of bytes.
4+
_PacketSerial_ is an small, efficient, library that allows [Arduinos](http://www.arduino.cc/) to send and receive serial data packets (with COBS or SLIP encoding) that include bytes with any value (0-255). A _packet_ is simply an array of bytes.
55

66
_"Why do I need this?"_ you may ask. The truth is that you may not need it if you are converting your values to ASCII strings and separating them with a known character (like a new line `\n`) before sending them. This is what happens if you call `Serial.print()` or `Serial.println()`. For instance, if you just want to send a byte with the value of 255 and follow it with a new line character (i.e. `Serial.println(255)`) the Arduino automatically converts the number to the equivalent printable ASCII characters, sending 4 bytes total. As a result the receiver won't just receive a byte for the number and a byte for the new line character. Instead it will receive a stream of 4 bytes:
77

@@ -44,7 +44,8 @@ An alternative to ASCII encoding is to write the bytes directly to using the `Se
4444
10 // The new line character (\n).
4545
```
4646

47-
This is much more compact but can create problems when the user wants to send a _packet_ of data. If the user wants to send a packet consisting of two values such as 255 and 10, we run into problems if we also use the new line ('\n' ASCII 10) character as a packet boundary. This essentially means that the receiver will incorrectly think that a new packet is beginning when it receives the _value_ of 10. Thus, to use this more compact form of sending bytes while reserving one value for a packet boundary marker. Several unambiguous packet boundary marking encodings exist, but one with a small predictable overhead is called [Consistent Overhead Byte Stuffing](http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing). For a raw packet of length `SIZE`, the maximum encoded buffer size will only be `SIZE + SIZE / 254 + 1`. This is significantly less than ASCII encoding and the encoding / decoding algorithm is simple and fast. In its default mode, the COBS encoding process simply removes all _zeros_ from the packet, allowing the sender and receiver to use the value of _zero_ as a packet boundary marker.
47+
This is much more compact but can create problems when the user wants to send a _packet_ of data. If the user wants to send a packet consisting of two values such as 255 and 10, we run into problems if we also use the new line ('\n' ASCII 10) character as a packet boundary. This essentially means that the receiver will incorrectly think that a new packet is beginning when it receives the _value_ of 10. Thus, to use this more compact form of sending bytes while reserving one value for a packet boundary marker. Several unambiguous packet boundary marking encodings exist, but one with a small predictable overhead is called [Consistent Overhead Byte Stuffing](http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing). For a raw packet of length `SIZE`, the maximum encoded buffer size will only be `SIZE + SIZE / 254 + 1`. This is significantly less than ASCII encoding and the encoding / decoding algorithm is simple and fast. In its default mode, the COBS encoding process simply removes all _zeros_ from the packet, allowing the sender and receiver to use the value of _zero_ as a packet boundary marker.
48+
Another coding available in PacketSerial is [Serial Line Internet Protocol](https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol) which is often used to send OSC over serial or TCP connection. To use SLIP encoding instead of COBS, use `SLIPPacketSerial` instead of `PacketSerial`. You can find some example about sending OSC data over serial in the [ofxSerial](https://github.com/bakercp/ofxSerial) repository.
4849

4950
## PacketSerial
5051

src/Encoding/SLIP.h

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// =============================================================================
2+
//
3+
// Copyright (c) 2010-2014 Christopher Baker <http://christopherbaker.net>
4+
// 2016 Antoine Villeret added Christopher's ofx/IO/SLIPEncoding.h here
5+
//
6+
// see : https://github.com/bakercp/ofxIO/blob/master/libs/ofxIO/include/ofx/IO/SLIPEncoding.h
7+
//
8+
// Portions:
9+
// Copyright (c) 2011, Jacques Fortier. All rights reserved.
10+
// https://github.com/jacquesf/COBS-Consistent-Overhead-Byte-Stuffing
11+
//
12+
// Permission is hereby granted, free of charge, to any person obtaining a copy
13+
// of this software and associated documentation files (the "Software"), to deal
14+
// in the Software without restriction, including without limitation the rights
15+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16+
// copies of the Software, and to permit persons to whom the Software is
17+
// furnished to do so, subject to the following conditions:
18+
//
19+
// The above copyright notice and this permission notice shall be included in
20+
// all copies or substantial portions of the Software.
21+
//
22+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28+
// THE SOFTWARE.
29+
//
30+
// =============================================================================
31+
32+
33+
#pragma once
34+
35+
36+
#include "Arduino.h"
37+
38+
39+
40+
/// \brief A Serial Line IP (SLIP) Encoder.
41+
///
42+
/// Serial Line IP (SLIP) is a packet framing protocol: SLIP defines a
43+
/// sequence of characters that frame IP packets on a serial line and
44+
/// nothing more. It provides no addressing, packet type identification,
45+
/// error detection/correction or compression mechanisms. Because the
46+
/// protocol does so little, though, it is usually very easy to
47+
/// implement.
48+
///
49+
/// \sa http://tools.ietf.org/html/rfc1055
50+
class SLIP
51+
{
52+
public:
53+
static size_t encode(const uint8_t* buffer, size_t size, uint8_t* encoded)
54+
{
55+
if (size == 0)
56+
return 0;
57+
58+
size_t read_index = 0;
59+
size_t write_index = 0;
60+
61+
// double-ENDed, flush any data that may have accumulated due to line noise
62+
encoded[write_index++] = END;
63+
64+
while (read_index < size)
65+
{
66+
if(buffer[read_index] == END)
67+
{
68+
encoded[write_index++] = ESC;
69+
encoded[write_index++] = ESC_END;
70+
read_index++;
71+
}
72+
else if(buffer[read_index] == ESC)
73+
{
74+
encoded[write_index++] = ESC;
75+
encoded[write_index++] = ESC_ESC;
76+
read_index++;
77+
}
78+
else
79+
{
80+
encoded[write_index++] = buffer[read_index++];
81+
}
82+
}
83+
84+
encoded[write_index++] = END;
85+
86+
return write_index;
87+
}
88+
89+
static size_t decode(const uint8_t* buffer, size_t size, uint8_t* decoded)
90+
{
91+
if (size == 0)
92+
return 0;
93+
94+
size_t read_index = 0;
95+
size_t write_index = 0;
96+
97+
while (read_index < size)
98+
{
99+
if (buffer[read_index] == END)
100+
{
101+
// flush or done
102+
read_index++;
103+
}
104+
else if (buffer[read_index] == ESC)
105+
{
106+
if (buffer[read_index+1] == ESC_END)
107+
{
108+
decoded[write_index++] = END;
109+
read_index += 2;
110+
}
111+
else if (buffer[read_index+1] == ESC_ESC)
112+
{
113+
decoded[write_index++] = ESC;
114+
read_index += 2;
115+
}
116+
else
117+
{
118+
// considered a protocol violation
119+
}
120+
}
121+
else
122+
{
123+
decoded[write_index++] = buffer[read_index++];
124+
}
125+
}
126+
127+
return write_index;
128+
}
129+
130+
/// \brief Get the maximum encoded buffer size needed for a given source size.
131+
///
132+
/// SLIP has a start and a end markers (192 and 219).
133+
/// Marker value is replaced by 2 bytes in the encoded buffer.
134+
/// So in the worst case of sending a buffer with only '192' or '219',
135+
/// the encoded buffer length will be 2 * buffer.size() + 2
136+
/// \param sourceSize The size of the buffer to be encoded.
137+
/// \returns the maximum size of the required encoded buffer.
138+
static size_t getEncodedBufferSize(size_t sourceSize)
139+
{
140+
return sourceSize * 2 + 2;
141+
}
142+
143+
enum
144+
{
145+
END = 0300,
146+
ESC = 0333,
147+
ESC_END = 0334,
148+
ESC_ESC = 0335
149+
};
150+
151+
};

src/PacketSerial.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include <Arduino.h>
2929
#include "Encoding/COBS.h"
30+
#include "Encoding/SLIP.h"
3031

3132

3233
template<typename EncoderType, uint8_t PacketMarker = 0, int BufferSize = 256>
@@ -145,4 +146,6 @@ class PacketSerial_
145146
};
146147

147148

148-
typedef PacketSerial_<COBS> PacketSerial;
149+
typedef PacketSerial_<COBS> PacketSerial;
150+
typedef PacketSerial_<COBS> COBSPacketSerial;
151+
typedef PacketSerial_<SLIP, SLIP::END> SLIPPacketSerial;

0 commit comments

Comments
 (0)