Legible encoding for addressable packets documentation
For python:
pip install leap-protocol
For javascript:
node install leap-protocol
Packets are human interprettable/enterable:
S10c0:00:cf000000\n
The protocol is configured using a yaml/json/toml file.
The L3aP protocol is designed to:
The json file has the following root schema:
Version is a semvar version with major, minor and patch numbers. This version relates to the L3aP protocol itself. Its purpose is to determine compatability. In leap packages, major and minor versions should always match the leap protocol spec, patch versions may not.
Category characters determine how the receiving application should interpret a packet.
The L3aP protocol defines six categories by default. However, an application may define more categories when needed.
These configuration items define the utf-8 characters used in encoded packets. They may be changed to suit a users application if they meet the following rules:
[0-9a-f]
The data
field is a heirachical structure of user defined data.
"data" : [
{ "sensor" : { "addr": "8000", "data": [
{ "imu": { "addr": "00A0", "data": [
{ "accel": { "data": [
{ "x": { "type": "float" } },
{ "y": { "type": "float" } },
{ "z": { "type": "float" } }
]}},
{ "gyros": { "data": [
{ "x": { "type": "float" } },
{ "y": { "type": "float" } },
{ "z": { "type": "float" } }
]}},
]}},
{ "temperature": { "addr": "00C0", "type": "float" } },
{ "barometer": { "type": "float" } }
]}},
{ "timestamp_ms": { "addr": "9000", "type": "u64" }}
]
Results in the following address mapping:
path | 16-bit address | type |
---|---|---|
sensor | 0x8000 | - |
sensor/imu | 0x80A0 | - |
sensor/imu/accel | 0x80A1 | - |
sensor/imu/accel/x | 0x80A2 | float |
sensor/imu/accel/y | 0x80A3 | float |
sensor/imu/accel/z | 0x80A4 | float |
sensor/imu/gyros | 0x80A5 | - |
sensor/imu/gyros/x | 0x80A6 | float |
sensor/imu/gyros/y | 0x80A7 | float |
sensor/imu/gyros/z | 0x80A8 | float |
sensor/temperature | 0x80C0 | float |
sensor/barometer | 0x80C1 | float |
timestamp_ms | 0x9000 | unsigned integer 64-bit |
Addresses:
/[0-9a-f]{4}/i
Items
"data"
array or "type"
string/^[a-z][\w\-_]*$/i
Valid types:
For enumerations, the item names are user defined - these get encoded as integers. An enumeration can have no more than 256 entries.
None types can be sent when the path itself is the command. For example, an application may have a path of control/mode/disable
which does not require an accompanying payload.
A typical packet looks like:
S13e7:00:cf000000\n
Where:
S
is the packet’s category13e7
is a data address:
are item separation characters00:cf000000
is encoded payload data\n
is the packet’s end characterThe first character of a packet is always a category character. This specifies how the packet should be interpretted.
L3aP defines the following catergories by default:
Category | Default encoding | Description |
---|---|---|
set | S | Sets a data value, a response of nak or ack are recommended (though optional) |
ack/nak | A/N | Responds to set packets. They should contain the same address(es) as the set packet, but no payload data |
get | G | Requests a data value once, a response of pub with all contained data is expected. <ul><li>If the requested path has no children: The response payload should contain only one item. (eg: path sensor/imu/accel/x carries only one data item in the payload)</li><li>If the requested path has children: The response payload should contain multiple items. (e.g. path sensor/imu from above carries six items in the payload)</li></ul> |
sub | B | Is the same as get but expects the data to be sent continously The publishing rate is at the discretion of the sending application. |
pub | P | Is used for sending application data values |
It is not necessary that an application implements all categories (eg. ack and nak can easily be omitted in many cases)
Addresses are encoded as a four character big-endian hexidecimal string.
Separator characters go between addresses and data values. The default separator character is :
Encoding of data depends on the data type. All data gets encoded into some big endian hexidecimal string. Data typing is inferred from the address and configuration, it is not carried in the packet itself.
Type | Encoding |
---|---|
Unsigned integers | 1 character per 4-bits, zero padded |
Signed integers | 2’s complement, 1 character per 4-bits, zero padded |
Float | IEEE 754 binary 32, 8 characters |
Double | IEEE 754 binary 64, 16 characters |
Bool | Single character, only ‘0’ or ‘1’ is valid |
Enumeration | 2 characters to represent an unsigned 8-bit integer - enumerations are limited to 256 values |
None | Not applicable |
Packets terminate with an end character. The default end character is \n
.
A typical compound packet looks like:
P13e7:00:cf000000|8100:132a|8210:a8903456:b499ff00\n
Instead of an end charactor, packets are combined using a compound character.
The advantage of compound packets is to ensure sets of data are processed together.
The default compound character is |
.
L3aP can be configured using yaml, json or toml files.
Yaml files are probably the easiest to maintain.
Example yaml file
separator: ":"
compound: "|"
end: "\n"
version:
major: 1
minor: 0
patch: 0
category:
get: "G"
set: "S"
ack: "A"
nak: "N"
sub: "B"
pub: "P"
data:
- item-1:
addr: "0000"
data:
- child-1:
data:
- grand-child-1: { type: "u8" }
- grand-child-2: { type: "float" }
- child-2: { type: "string" }
- item-2: { addr: "2000", type: "none" }
Example json file
{
"separator": ":",
"compound": "|",
"end": "\n",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"category": {
"get": "G",
"set": "S",
"ack": "A",
"nak": "N",
"sub": "B",
"pub": "P"
},
"data": [
{ "item-1": { "addr": "0000", "data": [
{ "child-1": { "data": [
{ "grand-child-1": { "type": "u8" } },
{ "grand-child-2": { "type": "float" } }
] } },
{ "child-2": { "type": "none" } }
] } },
{ "item-2": { "addr": "2000", "type": "none" } }
]
}
Example toml file
separator = ":"
compound = "|"
end = "\n"
[version]
major = 1
minor = 0
patch = 0
[category]
get = "G"
set = "S"
ack = "A"
nak = "N"
sub = "B"
pub = "P"
[[data]]
[data.item-1]
addr = "0000"
[[data.item-1.data]]
[data.item-1.data.child-1]
[[data.item-1.data.child-1.data]]
grand-child-1 = { type = "u8" }
[[data.item-1.data.child-1.data]]
grand-child-2 = { type = "float" }
[[data.item-1.data]]
child-2 = { type = "none" }
[[data]]
item-2 = { addr = "2000", type = "none" }