TinyPostman
TinyPostman is a REST-like protocol for reading and writing data resources from microcontrollers via a serial port. It is based on the TinyPacks data serialization format and supports the REST methods GET, PUT, POST and DELETE.
The current C++ implementation for Arduino and Atmel AVR microcontrollers uses about 1.5 KB of Flash and 48 bytes of RAM.
Frame format
The TinyPostman requests and responses are encoded as a sequence of TinyPacks values as follow:
Request
< method:Integer > < token:Integer > < path:String > [ payload:* ]
Response
< code:Integer > < token:Integer > [ payload:* ]
References
method: An integer value representing one of the following methods:
1 Get
2 Post
3 Put
4 Delete
token: An integer value used as ID for the request and treated as
opaque value when processing the request.
path: The path of the addressed resource encoded as an UTF-8 string.
code: An integer value representing one of the following response
codes:
0x21 201 Created
0x22 202 Deleted
0x24 204 Changed
0x25 205 Content
0x40 400 Bad Request
0x41 401 Unauthorized
0x43 403 Forbidden
0x44 404 Not Found
0x45 405 Method Not Allowed
0x4D 413 Request Entity Too Large
payload: An optional payload encoded as one or more TinyPacks values.
Code examples
Python
from tinypostman import *
pm = tinypostman.Postman("/dev/ttyUSB0")
response = pm.get("")
if response[0] == TPM_205_Content:
resources = response[1]
for resource in resources:
print(resource + ":")
response = pm.get(resource)
if response[0] == TPM_205_Content:
print(repr(response[1]))
else:
print("Cannot get the %s resource." % resource)
else:
print("Cannot get the resource index.")
Arduino
#include <TinyPostman.h>
#define MAX_PACKET_LENGTH 128
uint8_t pack_buffer[MAX_PACKET_LENGTH];
Postman postman;
Framer framer(pack_buffer, MAX_PACKET_LENGTH);
class LED : public Resource {
uint8_t led_pin;
public:
LED() { led_pin = 13; pinMode(led_pin, OUTPUT); };
uint8_t get(Request &request) {
request.writer.openMap();
request.writer.putString("pin");
request.writer.putInteger(led_pin);
request.writer.putString("state");
request.writer.putBoolean(digitalRead(led_pin));
request.writer.close();
return TPM_205_Content;
}
uint8_t put(Request &request) {
request.reader.next();
if(request.reader.openMap()) {
while(request.reader.next()) {
if (request.reader.match("pin")) { led_pin = request.reader.getInteger(); pinMode(led_pin, OUTPUT); }
else if(request.reader.match("state")) digitalWrite(led_pin, request.reader.getBoolean());
else request.reader.next();
}
request.reader.close();
return TPM_204_Changed;
}
else
return TPM_400_Bad_Request;
}
} led_resource;
void setup()
{
postman.registerResource("led", led_resource);
Serial.begin(9600);
}
void loop()
{
if(framer.getState() == TPM_SENDING)
Serial.write(framer.getByteToSend());
while(framer.getState() == TPM_RECEIVING && Serial.available() > 0) {
if(framer.putReceivedByte(Serial.read()) && framer.getLength()) {
framer.setLength(postman.handlePack(pack_buffer, framer.getLength(), MAX_PACKET_LENGTH));
framer.setState(TPM_SENDING);
break;
}
}
}
Source code
Get the source code from TinyPacks repository on GitHub
TinyPacks
TinyPacks is data serialization format for constrained environments like 8-bit and 16-bit microcontrollers. It was designed to achieve these goals:
- Easy to traverse and to skip entire nested elements.
- Small serializated data footprint.
- Small encoder/decoder memory footprint.
- In-place parsing using static memory allocation.
- Easy translation to and from JSON
The current C++ implementation for Arduino and Atmel AVR microcontrollers uses about 2 KB of Flash and 9 bytes of RAM plus 14 bytes of RAM per nesting level in the source data.
Data types
None Null value.
Boolean Boolean True or False value.
Integer Signed integer 8, 16, 32 or 64 bits long.
Real Floating point number 32 or 64 bits long.
String Variable length string of characters encoded as UTF-8.
Bytes Variable length raw sequence of bytes.
List Sequence of values.
Map Sequence of key-value pairs.
Serialization format
Specification syntax
| Alternative
[ ] Repetition, zero or more elements
[n] Repetition, n elements
: Length in bits, element:length
* Arithmetic multiplication
@ Native C type using network byte order
- Range
# Define parameter, element#name
Format specification
document = element[]
element = none | boolean | integer | real | string | bytes | list | map
none = 0:3 0:5
boolean = false | true
false = 1:3 0:5
true = 1:3 1:5 1:8
integer = integer0 | integer8 | integer16 | integer32 | integer64
integer0 = 2:3 0:5
integer8 = 2:3 1:5 @int8_t:8
integer16 = 2:3 2:5 @int16_t:16
integer32 = 2:3 4:5 @int32_t:32
integer64 = 2:3 8:5 @int64_t:64
real = real0 | real32 | real64
real0 = 3:3 0:5
real32 = 3:3 4:5 @float:32
real64 = 3:3 8:5 @double:64
string = small_string | medium_string | big_string
small_string = 4:3 0-0x1E#n:5 @char[n]:8*n
medium_string = 4:3 0x1F:5 0-0xFFFE#n:16 @char[n]:8*n
big_string = 4:3 0x1F:5 0xFFFF:16 0-0xFFFFFFFE#n:32 @char[n]:8*n
bytes = small_bytes | medium_bytes | big_bytes
small_bytes = 5:3 0-0x1E#n:5 @uint8_t[n]:8*n
medium_bytes = 5:3 0x1F:5 0-0xFFFE#n:16 @uint8_t[n]:8*n
big_bytes = 5:3 0x1F:5 0xFFFF:16 0-0xFFFFFFFE#n:32 @uint8_t[n]:8*n
list = small_list | medium_list | big_list
small_list = 6:3 0-0x1E#n:5 element[]:8*n
medium_list = 6:3 0x1F:5 0-0xFFFE#n:16 element[]:8*n
big_list = 6:3 0x1F:5 0xFFFF:16 0-0xFFFFFFFE#n:32 element[]:8*n
map = small_map | medium_map | big_map
small_map = 7:3 0-0x1E#n:5 (element element)[]:8*n
medium_map = 7:3 0x1F:5 0-0xFFFE#n:16 (element element)[]:8*n
big_map = 7:3 0x1F:5 0xFFFF:16 0-0xFFFFFFFE#n:32 (element element)[]:8*n
Serialization examples
Python TinyPacks
------ ---------
None 00
0 40
123 7b
4567 42 11 d7
8.9 64 41 0e 66 66
0.0 60
True 21 01
False 20
"ABC" 83 41 42 43
"hello world!" 8c 68 65 6c 6c 6f 20 77 6f 72
6c 64 21
"A string longer than 30 characters." 9f 00 23 41 20 73 74 72 69 6e
67 20 6c 6f 6e 67 65 72 20 74
68 61 6e 20 33 30 20 63 68 61
72 61 63 74 65 72 73 2e
b"\x01\x02\x03" a3 01 02 03
[1, 2, 3] c6 41 01 41 02 41 03
[4, True, "fun"] c8 41 04 21 01 83 66 75 6e
{"a": 1, "b": False, "c": "foo"} ed 81 61 41 01 81 63 83 66 6f
6f 81 62 20
{"foo": [1, 2], "bar": {True: 3, False: 4}} f5 83 66 6f 6f c4 41 01 41 02
83 62 61 72 e7 20 41 04 21 01
41 03
Code examples
Python
import tinypacks
data = { "message": "Hello world!", "status": True, "count": 123 }
print("Data: " + repr(data))
packed_data = tinypacks.pack(data)
print("Packed: " + repr(packed_data))
(unpacked_data, remaining_data) = tinypacks.unpack(packed_data)
print("Unpacked: " + repr(unpacked_data))
Arduino
#include <TinyPacks.h>
PackWriter writer;
PackReader reader;
#define MAX_PACKED_DATA 256
unsigned char packed_data[MAX_PACKED_DATA];
int packed_data_length;
void setup()
{
Serial.begin(9600);
}
void loop()
{
#define MAX_TEXT_LENGTH 32
char text[MAX_TEXT_LENGTH] = "";
bool status = false;
int count = 0;
// Pack
writer.setBuffer(packed_data, MAX_PACKED_DATA);
writer.openMap();
writer.putString("text");
writer.putString("Hello world!");
writer.putString("status");
writer.putBoolean(true);
writer.putString("count");
writer.putInteger(123);
writer.close();
packed_data_length = writer.getOffset();
// Unpack
reader.setBuffer(packed_data, packed_data_length);
reader.next();
if(reader.openMap()) {
while(reader.next()) {
if (reader.match("status")) status = reader.getBoolean();
else if(reader.match("count")) count = reader.getInteger();
else if(reader.match("text")) reader.getString(text, MAX_TEXT_LENGTH);
else reader.next();
}
reader.close();
}
// Print unpacked data
Serial.println("Map content:");
Serial.print(" text: ");
Serial.println(text);
Serial.print(" status: ");
Serial.println(status);
Serial.print(" count: ");
Serial.println(count);
Serial.println();
delay(5000);
}
Source code
Get the source code from the TinyPacks repository on GitHub
Luminch One
Luminch One is an interactive lamp controlled by the movements of your hand. Wave your hand over it to turn it on or off, or move your hand up or down above it to change its brightness. Inside the lamp, an Arduino hooked to an infrared distance sensor tracks your hand and sets the state and the brightness of the LED lamp.
Get the source code, schematics and folding pattern (395 KB)
Luminch One repository at GitHub
View the step-by-step guide to build your own Luminch One

Low-tech Magazine: The bright future of solar powered factories
Most of the talk about renewable energy is aimed at electricity production. However, most of the energy we need is heat, which solar panels and wind turbines cannot produce efficiently. To power industrial processes like the making of chemicals, the smelting of metals or the production of microchips, we need a renewable source of thermal energy. Direct use of solar energy can be the solution, and it creates the possibility to produce renewable energy plants using only renewable energy plants, paving the way for a truly sustainable industrial civilization.
In Praise of Idleness
by Bertrand Russell - 1932