You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
_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.
4
+
## Description
5
5
6
-
_"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:
6
+
An Arduino Library that facilitates packet-based serial communication using COBS or SLIP encoding.
7
+
8
+
## Features
9
+
10
+
_PacketSerial_ is an small, efficient, library that allows [Arduinos](http://www.arduino.cc/) to send and receive serial data packets (with COBS, SLIP or a user-defined encoding) that include bytes of any value (0 - 255). A _packet_ is simply an array of bytes.
11
+
12
+
## Background
13
+
14
+
_"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 carriage return `\r` and a line feed `\n`) before sending them. This is what happens if you call and `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 5 bytes total. As a result the receiver won't just receive a byte for the number and two bytes for the carriage return and new line character. Instead it will receive a stream of 5 bytes:
7
15
8
16
```
9
-
50 // ASCII 2
10
-
53 // ASCII 5
11
-
53 // ASCII 5
12
-
10 // Serial.println() appends a new line \n character.
17
+
50 // ASCII '2'
18
+
53 // ASCII '5'
19
+
53 // ASCII '5'
20
+
13 // ASCII '\r'
21
+
10 // ASCII '\n'
13
22
```
14
23
15
-
The receiver must then collect the 3 ASCII characters (2, 5, 5), combine them and convert them back into a single byte with a value of 255. This process can get complicated when the user wants to send large quantities of structured data between the Arduino and a receiver.
24
+
The receiver must then collect the 3 ASCII characters `{ '2', '5', '5' }`, combine them and convert them back into a single byte with a value of `255`. This process can get complicated when the user wants to send large quantities of structured data between the Arduino and a receiver.
16
25
17
-
One way to send a _packet_ of data without this library is to send each byte separated by a comma or space and terminate the sequence with a new line character. Thus, to send the value 255 and the value 10, one might call:
26
+
One way to send a _packet_ of data without this library is to send each byte separated by a comma or space and terminate the sequence with a new line character. Thus, to send the value `255` and the value `10`, one might call:
18
27
19
28
```
20
29
Serial.print(255);
@@ -23,41 +32,41 @@ Serial.print(10);
23
32
Serial.print('\n');
24
33
```
25
34
26
-
The receiver will actually see a stream of 8 bytes:
35
+
The receiver will actually see a stream of 7 bytes:
27
36
28
37
```
29
-
50 // ASCII 2
30
-
53 // ASCII 5
31
-
53 // ASCII 5
32
-
44 // ASCII ,
33
-
49 // ASCII 1
34
-
48 // ASCII 0
35
-
10 // ASCII \n
38
+
50 // ASCII '2'
39
+
53 // ASCII '5'
40
+
53 // ASCII '5'
41
+
44 // ASCII ','
42
+
49 // ASCII '1'
43
+
48 // ASCII '0'
44
+
10 // ASCII '\n'
36
45
```
37
46
38
-
In this case, the receiver must then collect the ASCII characters, combine them, skip the delimiter (the comma in this case) and then process the packet when a new line is encountered. While effective, this method doesn't scale well. Bytes with values larger than 9 require are encoded as 2 bytes and bytes with values larger than 99 are encoded as 3 bytes. If the user would like to send the number 4,294,967,295 (the maximum value of a 4 byte `unsigned long`), it would be encoded as 10 bytes. This means that there is an overhead of 6 extra bytes to transmit a 4 byte `unsigned long`.
47
+
In this case, the receiver must then collect the ASCII characters, combine them, skip the delimiter (the comma in this case) and then process the packet when a new line is encountered. While effective, this method doesn't scale particularly well. Bytes with values larger than 9 are encoded as 2 bytes and bytes with values larger than 99 are encoded as 3 bytes, etc. If the user would like to send the number 4,294,967,295 (the maximum value of a 4 byte `unsigned long`), it would be encoded as 10 bytes. This means that there is an overhead of 6 extra bytes to transmit a 4 byte `unsigned long`.
39
48
40
-
An alternative to ASCII encoding is to write the bytes directly to using the `Serial.write()` methods. These methods do not convert the byte values to ASCII. So if the user wants to send a single byte with the value of 255 and follow it with a new line character (i.e. `Serial.write(255); Serial.write('\n');`), the receiver will see a stream of 2 bytes:
49
+
An alternative to ASCII encoding is to write the bytes directly to using the `Serial.write()` methods. These methods do not convert the byte values to ASCII. So if the user wants to send a single byte with the value of 255 and follow it with a new line character (i.e. `Serial.write(255); Serial.write('\n');`), the receiver will see a stream of 2 bytes:
41
50
42
51
```
43
52
255 // The value transmitted.
44
53
10 // The new line character (\n).
45
54
```
46
55
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.
56
+
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
57
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.
49
58
50
-
## PacketSerial
59
+
## Use
60
+
### PacketSerial
51
61
52
-
The `PacketSerial` class wraps the standard Arduino `Serial` class to automatically encode and decode byte packets. Thus users can still call methods on the `Serial` object (e.g. `Serial.write()`, the built in `serialEvent()` callback etc), but it is not recommended. Users are advised to let PacketSerial manage all Serial communication via the packet handler callback for incoming packets and the `send(const uint8_t* buffer, size_t size)` method for outgoing packets. Mixing raw Serial calls with PacketSerial may lead to unexpected results, as the endpoint will not know what data is encoded and what data is not.
62
+
The `PacketSerial` class wraps the standard Arduino `Serial` class to automatically encode and decode byte packets. Thus users can still call methods on the `Serial` object (e.g. `Serial.write()`, the built in `serialEvent()` callback etc), but it is not recommended. Users are advised to let PacketSerial manage all Serial communication via the packet handler callback for incoming packets and the `send(const uint8_t* buffer, size_t size) const` method for outgoing packets. Mixing raw Serial calls with `PacketSerial` may lead to unexpected encoding and decoding results, as the endpoint will not know what data is encoded and what data is not.
53
63
54
-
55
-
## Setup
64
+
### Setup
56
65
57
66
For Arduino boards with more than one serial port, `PacketSerial` the desired serial port can be specified with the `begin` method, i.e.
58
67
59
68
```c++
60
-
voidbegin(unsigned long baud, size_t port = 0)
69
+
voidbegin(unsigned long speed, uint8_t config, size_t port = 0)
61
70
```
62
71
63
72
Where:
@@ -69,28 +78,27 @@ Where:
69
78
| `Serial2` | 2 |
70
79
| `Serial3` | 3 |
71
80
72
-
To use a software serial port you can also use the `begin` method, this time with only a Stream* as argument, i.e.
81
+
Alternatively, to use a software serial port or other pre-configured network stream you can use an alternate `begin` method, this time with only a `Stream*` as argument, e.g.:
73
82
```c++
74
83
void begin(Stream* serial)
75
84
```
76
85
77
86
Usage:
78
87
```c++
79
-
PacketSerial packet_serial;
80
-
SoftwareSerial software_serial(10, 11);
81
-
82
-
// in this case the serial port has to be initialized already when passing it to PacketSerial!
83
-
software_serial.begin(38400);
84
-
packet_serial.begin(&software_serial);
85
-
```
88
+
PacketSerial myPacketSerial;
89
+
SoftwareSerial mySoftwareSerial(10, 11);
86
90
91
+
// In this case the serial port has to be initialized before passing it to PacketSerial.
92
+
mySoftwareSerial.begin(38400);
93
+
myPacketSerial.begin(&mySoftwareSerial);
94
+
```
87
95
88
-
To receive decoded packets automatically, the user should register a packet callback. The packet callback should be placed in your main Arduino Sketch and should have a method that looks like this signatur that looks like:
96
+
To receive decoded packets, the user should register a packet callback. The packet callback should be placed in your main Arduino Sketch and should have a method that looks like this:
89
97
90
98
```c++
91
99
void onPacket(const uint8_t* buffer, size_t size)
92
100
{
93
-
/// Process your incoming packet here.
101
+
// Process your decoded incoming packet here.
94
102
}
95
103
```
96
104
@@ -100,111 +108,46 @@ Your callback can have any name and should be registered in the `setup()` method
In order to processing incoming serial packets, the user must call the `update()` method at the end of the `loop()` method.
106
114
107
115
```c++
108
116
void loop()
109
117
{
110
-
// Your program here.
111
-
118
+
// Your program here.
119
+
112
120
serial.update();
113
121
}
114
122
115
123
```
116
124
117
-
## Sending Packets
125
+
###Sending Packets
118
126
119
-
To send packets call the `send()` method. The send method will take a packet (an array of bytes, encode it, transmit the array and transmit the packet boundary marker (`0`). To send the values 255 and 10, one might do the following:
127
+
To send packets call the `send()` method. The send method will take a packet (an array of bytes), encode it, transmit it and send the packet boundary marker (usually `0`). To send the values 255 and 10, one might do the following:
120
128
121
129
```c++
122
130
123
131
// Make an array.
124
132
uint8_t myPacket[] { 255, 10 };
125
133
134
+
// Send the array.
126
135
serial.send(myPacket, 2);
127
136
```
128
-
129
-
# Example
130
137
131
-
In this "reverse echo" example, we listen for incoming packets. When a new packet arrives in the `onPacket` method, we reverse the contents and send it back to the sender.
138
+
## Examples
132
139
133
-
```c++
134
-
#include <PacketSerial.h>
135
-
136
-
137
-
// The PacketSerial object.
138
-
// It cleverly wraps one of the Serial objects.
139
-
// While it is still possible to use the Serial object
140
-
// directly, it is recommended that the user let the
141
-
// PacketSerial object manage all serial communication.
142
-
// Thus the user should not call Serial.write(), etc.
143
-
// Additionally the user should not use the serialEvent()
144
-
// callbacks.
145
-
PacketSerial serial;
146
-
147
-
148
-
void setup()
149
-
{
150
-
// We must specify a packet handler method so that
151
-
serial.setPacketHandler(&onPacket);
152
-
serial.begin(115200);
153
-
}
140
+
See the included examples for further usage options.
154
141
155
-
156
-
void loop()
157
-
{
158
-
// Do other things here.
159
-
160
-
// The update() method attempts to read in
161
-
// any incoming serial data and emits packets via
162
-
// the user's onPacket(const uint8_t* buffer, size_t size)
163
-
// method registered with the setPacketHandler() method.
164
-
//
165
-
// The update() method should be called at the end of the loop().
166
-
serial.update();
167
-
}
168
-
169
-
// This is our packet callback.
170
-
// The buffer is delivered already decoded.
171
-
void onPacket(const uint8_t* buffer, size_t size)
172
-
{
173
-
// Make a temporary buffer.
174
-
uint8_t tmp[size];
175
-
176
-
// Copy the packet into our temporary buffer.
177
-
memcpy(tmp, buffer, size);
178
-
179
-
// Reverse our buffer.
180
-
reverse(tmp, size);
181
-
182
-
// Send the reversed buffer back.
183
-
// The send() method will encode the buffer
184
-
// as a packet, set packet markers, etc.
185
-
serial.send(tmp, size);
186
-
}
187
-
188
-
/// \brief A simple array reversal method.
189
-
void reverse(uint8_t* buffer, size_t size)
190
-
{
191
-
uint8_t tmp;
192
-
193
-
for (int i=0; i < size / 2; i++)
194
-
{
195
-
tmp = buffer[i];
196
-
buffer[i] = buffer[size-i-1];
197
-
buffer[size-i-1] = tmp;
198
-
}
199
-
}
200
-
```
201
-
202
-
## Compatible libraries
142
+
## Compatible Libraries
203
143
204
144
- openFrameworks
205
145
- https://github.com/bakercp/ofxSerial
206
-
- See the `ofx::IO::PacketSerial` object which is directly compatible with this library.
146
+
- See the `ofx::IO::PacketSerial` object which is directly compatible with this library.
0 commit comments