When you want to to get data from your device over an LPWAN network (such as LoRaWAN), you cannot use a plain JSON format over HTTP(S) or MQTT. Given their contrained nature, LPWAN networks expect that your device sends over a binary payload. The payload should be as compact as possible to minimise the Time-on-Air and reduce power consumption (for battery operated) devices.
This guide will help you to encode and decode different types of data in as little bytes as possible.
ABCL stands for AllThingsTalk Binary Conversion Language , and it’s a JSON-based, domain specific language, used for encoding and decoding of AllThingsTalk asset data to and from binary payloads. It gives you the freedom to specify your own decoding scheme on a per device level.
Mapping statements enclosed within a sense
block define how the binary data received from the edge, will be decoded into understandable asset format.
{"sense": [
// mapping statements
]}
Set the byte to parse. When bytelength is set, this is the first byte we look at from the left.
Note that selectors are zero-indexed. The first byte is
{"byte": 0 }
{"sense": [
{"asset": "temperature", "value": {"byte": 0}}
]}
By using the selector bytelength
we can specify how many bytes make up our value.
{"sense": [
{"asset": "temperature", "value": {"byte": 0, "bytelength": 2, "type": "integer"} }
]}
Hex | Decimal | Remark |
---|---|---|
0x08BD | 2237 | Start at byte 0 for a length of 2 bytes. So byte 0 and 1. |
Set the preferred byteorder
with this keyword. Big endian or Little endian. Supported options are
By default big endian is used
{"sense": [
{"asset": "big", "value": {"byte": 0, "bytelength": 2, "byteorder": "big", "type": "integer"}},
{"asset": "little", "value": {"byte": 0, "bytelength": 2, "byteorder": "little", "type": "integer"}}
]}
Hex | Decimal | Remark |
---|---|---|
0x08BD | 2237 | Big Endian 0*16^3 + 8*16^2 + 11*16^1 + 13*16^0 |
48392 | Little Endian 11*16^3 + 13*16^2 + 0*16^1 + 8*16^0 |
Select a specific bit
within a byte. With just a single bit, the output value can of course only be _0_ or _1_.
Same as byte, bit is zero-indexed
{"sense": [
{"asset": "test1", "value": {"byte": 0, "bit": 6}}
{"asset": "test2", "value": {"byte": 0, "bit": 5}}
]}
Hex | Bits | Selector | Output |
---|---|---|---|
0xB3 | 10110011 | bit 6 | 1 101100(1)1 |
bit 5 | 0 10110(0)11 |
Select multiple consecutive bits with the bitlength
selector.
Bitorder is always Most Significant Bit (MSD) first
{"sense": [
{"asset": "test1", "value": {"byte": 0, "bit": 3, "bitlength": 2}},
{"asset": "test2", "value": {"byte": 0, "bit": 3, "bitlength": 3}},
{"asset": "test3", "value": {"byte": 0, "bit": 2, "bitlength": 3}}
]}
Hex | Bits | Selector | Output |
---|---|---|---|
0xB3 | 10110011 | bit 3, bitlength 2 | 2 101(10)011 |
bit 3, bitlength 3 | 4 101(100)11 | ||
bit 2, bitlength 3 | 6 10(110)011 |
When signed
is
By default, values are signed
{"sense": [
{"asset": "test1", "value": {"byte": 0}},
{"asset": "test2", "value": {"byte": 0, "signed": true}},
{"asset": "test3", "value": {"byte": 0, "signed": false}}
]}
Hex | Decimal | |
---|---|---|
0x9B | 155 | Set test1 asset to -101 |
Set test2 asset to -101 | ||
Set test3 asset to 155 |
We can also send a fixed value, instead of parsing some bytes. This is particularly useful in a switch statement. Instead of setting bits and bytes to parse, simply set a fixed String in the value field. The example below shows how to use this.
Using a switch/case
statement, we can parse payloads differently depending on the value of certain bytes.
{"sense": [
{"switch": {"byte": 0}, "on": [
{"case": 0, "do": [ {"asset": "message", "value": "Running"} ]},
{"case": 1, "do": [ {"asset": "message", "value": "Debug mode"} ]}
]}
]}
Or if we also want the raw value, we can simple parse it as well
{"sense": [
{"asset": "raw", "value": {"byte": 0}},
{"switch": {"byte": 0}, "on": [
{"case": 0, "do": [ {"asset": "message", "value": "Running"} ]},
{"case": 1, "do": [ {"asset": "message", "value": "Debug mode"} ]}
]}
]}
{"sense": [
{"asset": "text", "value": {"byte": 0, "type": "string"}},
{"asset": "toggle", "value": {"byte": 0, "type": "boolean"}},
{"asset": "date", "value": {"byte": 0, "bytelength": 4, "type": "datetime"}}
]}
Hex | Decimal | |
---|---|---|
0x01 | 1 | Set toggle asset to **true** |
0x58 | 88 | Set text asset to **X** |
0x58D54158 | 1490370904 | Set date asset to "2017-03-24T15:55:04+00:00" |
{"sense": [
{"asset": "int", "value": {"byte": 0, "bytelength": 2, "type": "integer"}},
{"asset": "half", "value": {"byte": 0, "bytelength": 2, "type": "number"}}
]}
Hex | Decimal | |
---|---|---|
0x58D5 | 22741 | Set int asset to 22741 |
Set half asset to -85.5 |
{
"sense": [
{
"asset": "hardware_unique_id",
"value": {
"byte": 0,
"bytelength": 16,
"byteorder": "big",
"signed": "false",
"type": "hexstring"
}
}
]
}
Hex | |
---|---|
0x2B5259537751395A53346D364B466657 | Set hardware_unique_id asset to 2B5259537751395A53346D364B466657 |
Add simple calculations to your values before sending them to the cloud. Use val
as the value placeholder in your calculation.
Supported operators are
+
-
/
*
**
log
sqrt
{"sense": [{
"asset": "temperature",
"value": {"byte": 0, "bytelength": 2, "type": "integer", "calculation": "val/100"}
}]}
Hex | Decimal | |
---|---|---|
0x08BD | 2237 | Set the temperature asset to 22.37 |
Note that while the value is an integer at first, it becomes a number after the calculation. Make sure the asset profile in AllThingsTalk is set to number.
{ "type": "number", "unit": " °C" }
Using the keyword comment
, you can add comments anywhere in the json.
{
"comment": "a very basic weather station",
"sense": [{
"comment": "temperature in degrees Celcius",
"asset": "temperature",
"value": {"byte": 2, "type": "integer"}
}]
}
Sometimes an asset needs a more complex structure than just a single value, for example coordinates of a GPS or the x, y and z values of an accelerometer.
{
"sense": [{
"asset": "Angle",
"comment" : "3-axis accelerometer",
"value": {
"x": {"byte": 0, "type": "integer"},
"y": {"byte": 1, "type": "integer"},
"z": {"byte": 2, "type": "integer"}
}
}]
}
The Asset Profile corresponding this data type in AllThingsTalk would look like
{
"type": "object",
"properties": {
"x": {"type": "integer"},
"y": {"type": "integer"},
"z": {"type": "integer"}
}
}
Below you will find a few examples of off-the-shelf devices and how to use the ABCL to convert their binary data payloads. For each of them, the needed assets in Maker and payload definition are supplied.
Always make sure the assets names you use in Maker match the corresponding asset names in your Payload definition.
SMOVE is a ready to use LoRaWAN accelerometer for remote sensing of motions, vibrations and tilt. The compact enclosure holds all electronics including batteries and embedded antenna. The design has been optimized for Long range, Long battery life, and reliability. The device can be used in harsh indoor and outdoor environments since it has an extended temperature range and furthermore is dust and water proof. Pre-programmed user requested Lora-keys, and sensitivity settings make this device Plug and Play.
Name | Profile |
---|---|
force | {"type": "integer"} |
battery | {"type": "integer"} |
{"sense": [
{"asset": "force", "value": {"byte": 0, "type": "integer"}},
{"asset": "battery", "value": {"byte": 1, "type": "integer"}}
]}
1B 5D | ||
Byte | Hex | |
---|---|---|
0 | 1B | Set force to 27 |
1 | 5D | Set battery to 93 |
The ITalks MCS 1608 Full LoRa Sensor is designed to start quickly with (Private) Lora applications. This complete Lora modem in an IP67 enclosure, so it can also be used outside, already has numerous sensors, so it can be rapidly deployed to test the desired IoT application. On the basis of the successful testing can after that the appropriate sensor can be chosen for u. The nodes are programmed by us at cost or you can do this yourself if you MCS1608 programming cable ordering it. The device is supplied as standard ABP.
The MCS1608 has a large set of operating states. Below we have implemented the payload definition for the General Sensing setting. The state the device is operating in, is provided by the first byte of the payload, hence the switch/case on byte 0 in our payload definition.
Name | Profile |
---|---|
msgid | {"type": "string"} |
BaromBar | {"type": "number", "unit": " mBar"} |
Temperature | {"type": "number", "unit": " °C"} |
Humidity | {"type": "integer", "unit": " %"} |
Angle | {"type": "object", "properties": {"x": {"type": "integer"}, "y": {"type": "integer"}, "z": {"type": "integer"}}} |
Vibration | {"type": "object", "properties": {"amplitude": {"type": "integer"}, "frequency": {"type": "integer"}}} |
{
"sense": [{
"switch": {"byte": 0},
"on": [
{"case": 0, "do": [ {"asset": "msgid", "value": "Alive"} ] },
{"case": 1, "do": [ {"asset": "msgid", "value": "Tracking"} ] },
{"case": 2, "do": [
{"asset": "msgid", "value": "GenSens"},
{"asset": "BaromBar", "value": {"byte": 2, "bytelength": 2, "type": "integer", "calculation": "(val + 100000) / 100"}},
{"asset": "Temperature", "value": {"byte": 4, "bytelength": 2, "type": "integer", "calculation": "val / 100"}},
{"asset": "Humidity", "value": {"byte": 6, "type": "integer"}},
{"asset": "Angle",
"comment" : "approximate calculation from [-128,127] to [-90,90]",
"value": {
"x": {"byte": 7,"type": "integer", "signed": true, "calculation": "val/255*180"},
"y": {"byte": 8,"type": "integer", "signed": true, "calculation": "val/255*180"},
"z": {"byte": 9,"type": "integer", "signed": true, "calculation": "val/255*180"}
}
},
{"asset": "Vibration",
"value": {
"amplitude": {"byte": 10, "type": "integer"},
"frequency": {"byte": 11, "type": "integer"}
}
}
]},
{"case": 3, "do": [ {"asset": "msgid", "value": "Rot"} ] },
{"case": 4, "do": [ {"asset": "msgid", "value": "Alarm"} ] },
{"case": 6, "do": [ {"asset": "msgid", "value": "1WireT"} ] },
{"case": 7, "do": [ {"asset": "msgid", "value": "Running"} ] },
{"case": 8, "do": [ {"asset": "msgid", "value": "Vibrate"} ] },
{"case": 9, "do": [ {"asset": "msgid", "value": "Analog"} ] },
{"case": 10, "do": [ {"asset": "msgid", "value": "GenSensGravMsg"} ] },
{"case": 11, "do": [ {"asset": "msgid", "value": "DailyReport"} ] },
{"case": 14, "do": [ {"asset": "msgid", "value": "Reboot"} ] }
]}
]
}
02 00 02 37 07 FF 2B 00 00 FC 00 00 00 00 00 00 | ||
Byte | Hex | |
---|---|---|
0 | 02 | Enter the corresponding switch/case. Case 02 is fully implemented and sets the String value for the msgid asset. The other cases only update the message id asset showing the current device setting |
2-3 | 02 37 | Given the calculation (val+100000)/100, the BaromBar asset gets set to 1005.67 |
4-5 | 07 FF | Given the calculation val/100, the Temperature asset gets set to 22.37 |
6 | 2B | Set the Humidity asset to 43 |
7, 8, 9 | 00, 00, FC | Set the x, y and z fields of the Angle asset to 0, 0 and -4 respectively. Since we want our values between [-128, 127] rather than [0, 255], we add the selector "signed": true |
10, 11 | 00, 00 | Set the amplitude and frequency fields of the Vibration asset to 0 and 0 respectively |
No timestamp is sent in the message so the _at_ field is always empty. The timestamp will be set in the cloud when the message arrives.
'msgid': {'value': 'GenSens', 'at': None}
'BaromBar': {'value': 1005.67, 'at': None}
'Temperature': {'value': 20.47, 'at': None}
'Humidity': {'value': 43, 'at': None}
'Angle': {'value': {'x': 0, 'y': 0, 'z': -4}, 'at': None}
'Vibration': {'value': {'amplitude': 0, 'frequency': 0}, 'at': None}
Push is a low-power, long range, wireless push sensor is the ideal product to let customers interact with you. With a simple push on the button you know when the coffee machine needs a refilling, the copier is out paper or support is required at a certain location inside your store.
Name | Profile |
---|---|
push | {"type": "string"} |
count | {"type": "integer"} |
length | {"type": "integer"} |
{
"sense": [{
"switch": {"byte": 2},
"on": [
{"case": 10, "comment": "case 0A one short push", "do": [
{"asset": "push", "value": "Short"},
{"asset": "count", "value": {"byte": 3, "bytelength":2, "type": "integer"}}
]},
{"case": 11, "comment": "case 0B one long push", "do": [
{"asset": "push", "value": "Long"},
{"asset": "count", "value": {"byte": 3, "bytelength":2, "type": "integer"}}
]}
]},
{"asset": "length", "value": {"byte": 0, "type": "integer"}}
]
}
04 02 0A 00 08 | ||
Byte | Hex | |
---|---|---|
0 | 04 | Message length. We set the length asset to 4 |
2 | 0A | We enter switch/case 10 (0A in Hex) We set the push asset to "Short" |
3-4 | 00 08 | Set the count asset to 8 |
In partnership with Proximus Enco, we have developed a containerised payload format. A container describes a single sensor type and the data it measures.
It provides an easy way to upload sensor data over the Proximus Public LoRaWAN network.
The containerised payload format is fully implemented in our Arduino LoRAWAN SDK
When manually creating an asset in Maker, make sure you use the Asset name (the numbers in the table below), corresponding with the data type you need.
#define BINARY_TILT_SENSOR ((short)2)
Definition | Asset name | Data type | Asset profile |
---|---|---|---|
BINARY_SENSOR | 1 | bool | {"type":"boolean"} |
INTEGER_SENSOR | 15 | short | {"type":"integer"} |
NUMBER_SENSOR | 16 | float | {"type":"number"} |
Definition | Asset name | Data type | Asset profile |
---|---|---|---|
BINARY_TILT_SENSOR | 2 | bool | {"type":"boolean"} |
PUSH_BUTTON | 3 | bool | {"type":"boolean"} |
DOOR_SENSOR | 4 | bool | {"type":"boolean"} |
TEMPERATURE_SENSOR | 5 | float | {"type":"number"} |
LIGHT_SENSOR | 6 | float | {"type":"number"} |
PIR_SENSOR | 7 | bool | {"type":"boolean"} |
ACCELEROMETER | 8 | floats | {"type":"object", "properties": {"x":{"type":"number"},"y":{"type":"number"},"z":{"type":"number"}}} All fields are required |
GPS | 9 | floats | {"type":"object", "properties": {"latitude":{"type":"number"},"longitude":{"type":"number"},"altitude":{"type":"number"}, "timestamp":{"type":"number"}}} Latitude and longitude are required, altitude and timestamp are optional |
PRESSURE_SENSOR | 10 | float | {"type":"number"} |
HUMIDITY_SENSOR | 11 | float | {"type":"number"} |
LOUDNESS_SENSOR | 12 | float | {"type":"number"} |
AIR_QUALITY_SENSOR | 13 | short | {"type":"integer"} |
BATTERY_LEVEL | 14 | short | {"type":"integer"} |
{
"name": "containers",
"sense": [
{"switch": {"byte": 4}, "on": [
{"case": 1, "comment": "Binary sensor", "do": [
{"asset": "1", "value": {"byte": 5, "type": "boolean"}}]},
{"case": 2, "comment": "Tilt sensor", "do": [
{"asset": "2", "value": {"byte": 5, "type": "boolean"}}]},
{"case": 3, "comment": "Push button", "do": [
{"asset": "3", "value": {"byte": 5, "type": "boolean"}}]},
{"case": 4, "comment": "Door sensor", "do": [
{"asset": "4", "value": {"byte": 5, "type": "boolean"}}]},
{"case": 5, "comment": "Temp sensor", "do": [
{"asset": "5", "value": {"byte": 8, "bytelength":4, "type": "number"}}]},
{"case": 6, "comment": "Light sensor", "do": [
{"asset": "6", "value": {"byte": 8, "bytelength":4, "type": "number"}}]},
{"case": 7, "comment": "Temp sensor", "do": [
{"asset": "7", "value": {"byte": 5, "type": "boolean"}}]},
{"case": 8, "comment": "Accelerometer", "do": [
{"asset": "8", "value": {
"x": {"byte": 8, "bytelength": 4, "type": "number"},
"y": {"byte": 12, "bytelength": 4, "type": "number"},
"z": {"byte": 16, "bytelength": 4, "type": "number"}}}]},
{"case": 9, "comment": "GPS", "do": [
{"asset": "9", "value": {
"latitude": {"byte": 8, "bytelength": 4, "type": "number"},
"longitude": {"byte": 12, "bytelength": 4, "type": "number"},
"altitude": {"byte": 16, "bytelength": 4, "type": "number"}}}]},
{"case": 10, "comment": "Pressure sensor", "do": [
{"asset": "10", "value": {"byte": 8, "bytelength": 4, "type": "number"}}]},
{"case": 11, "comment": "Humidity sensor", "do": [
{"asset": "11", "value": {"byte": 8, "bytelength": 4, "type": "number"}}]},
{"case": 12, "comment": "Loudness sensor", "do": [
{"asset": "12", "value": {"byte": 8, "bytelength": 4, "type": "number"}}]},
{"case": 13, "comment": "Air quality sensor", "do": [
{"asset": "13", "value": {"byte": 7, "bytelength": 2, "type": "integer", "byteorder": "little"}}]},
{"case": 14, "comment": "Battery sensor", "do": [
{"asset": "14", "value": {"byte": 7, "bytelength": 2, "type": "integer", "byteorder": "little"}}]},
{"case": 15, "comment": "Integer sensor", "do": [
{"asset": "15", "value": {"byte": 7, "bytelength": 2, "type": "integer", "byteorder": "little"}}]},
{"case": 16, "comment": "Number sensor", "do": [
{"asset": "16", "value": {"byte": 8, "bytelength": 4, "type": "number"}}]}
]}
]
}
A few handy online tools that can help with setting up your own conversion