Community Tip - When posting, your subject should be specific and summarize your question. Here are some additional tips on asking a great question. X
This small tutorial enables you to manage payload decoding for Adeunis Devices within ThingWorx Composer in less than 10 minutes.
Adeunis Devices communicates on LPWAN networks (LoRaWAN / Sigfox) covering sectors such as smart building, smart industry and smart city.
The encoding is also possible but it will be covered in another article.
1. Get Adeunis Codec
Adeunis is providing a codec enabling payload encoding and decoding.
Download here the resource file containing the codec.
Unzip the file and edit "script.txt" with your favorite text editor.
Copy all text contained in the file.
2. Create AdeunisCodec Thing
Create a Thing called "AdeunisCodec" based on the GenericThing Template.
3. Create a service called "Decode"
Create a Decode Service with the following setup:
Past the previously copied "script.txt" content
Save
4. Correct a couple of Warnings
Remove all "var codec;" occurences except first one at line 1191.
Remove semi columns at lines 985,1088, 1096 and 1172
5. Remove the following section
The codec relies on implementing functions on JavaScript prototypes which is not supported by ThingWorx Rhino JavaScript Engine. See the following documentation section, here.
Remove from line 1109 to 1157.
The following classes overrides will be removed:
6. Add new implementations of the removed functions
The functions are adapted from a JavaScript framework which contains resources that helps dealing with binary data, here.
Insert the following section at the top of the "Decode" script.
function readInt16BE (payload,offset) {
checkOffset(offset, 2, payload.length);
var val = payload[offset + 1] | (payload[offset] << 8);
return (val & 0x8000) ? val | 0xFFFF0000 : val;
}
function readUInt32BE (payload,offset) {
checkOffset(offset, 4, payload.length);
return (payload[offset] * 0x1000000) +
((payload[offset + 1] << 16) |
(payload[offset + 2] << |
payload[offset + 3]);
}
function readUInt16BE (payload,offset) {
checkOffset(offset, 2, payload.length);
return (payload[offset] << | payload[offset + 1];
}
function readUInt8 (payload,offset) {
checkOffset(offset, 1, payload.length);
return payload[offset];
}
function writeUInt16BE (payload,value, offset) {
value = +value;
offset = offset >>> 0;
checkInt(payload, value, offset, 2, 0xffff, 0);
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = (value >>> 8);
payload[offset + 1] = value;
} else objectWriteUInt16(payload, value, offset, false);
return offset + 2;
}
function writeUInt8 (payload,value, offset) {
value = +value;
offset = offset >>> 0;
checkInt(payload, value, offset, 1, 0xff, 0);
if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value);
payload[offset] = value;
return offset + 1;
}
function writeUInt32BE (payload,value, offset) {
value = +value;
offset = offset >>> 0;
checkInt(payload, value, offset, 4, 0xffffffff, 0);
if (Buffer.TYPED_ARRAY_SUPPORT) {
payload[offset] = (value >>> 24);
payload[offset + 1] = (value >>> 16);
payload[offset + 2] = (value >>> 8);
payload[offset + 3] = value;
} else objectWriteUInt32(payload, value, offset, false);
return offset + 4;
}
function objectWriteUInt16 (buf, value, offset, littleEndian) {
if (value < 0) value = 0xffff + value + 1;
for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) {
buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> (littleEndian ? i : 1 - i) * 8;
}
}
function objectWriteUInt32 (buf, value, offset, littleEndian) {
if (value < 0) value = 0xffffffff + value + 1;
for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) {
buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * & 0xff;
}
}
7. Add the following function to support previous inserted functions
function checkOffset (offset, ext, length) {
if ((offset % 1) !== 0 || offset < 0) throw new Error ('offset is not uint');
if (offset + ext > length) throw new Error ('Trying to access beyond buffer length');
}
8. Add the following function for casting String to Bytes
function splitInBytes(data) {
var bytes = [];
var bytesAsString = '';
for (var i = 0, j = 0;
i < data.length;
i += 2,
j++) {
bytes[j] = parseInt(data.substr(i, 2), 16);
bytesAsString += bytes[j] + ' ';
}
return bytes;
}
9. Remap function calls to newly inserted functions
Use the built-in script editor replace feature for the following, see below:
Within the service script perform a Replace for each of the following lines.
Search | Replace by |
payload.readInt16BE( | readInt16BE(payload, |
payload.readUInt32BE( | readUInt32BE(payload, |
payload.readUInt16BE( | readUInt16BE(payload, |
payload.readUInt8( | readUInt8(payload, |
payload.writeUInt16BE( | writeUInt16BE(payload, |
payload.writeUInt8( | writeUInt8(payload, |
payload.writeUInt32BE( | writeUInt32BE(payload, |
10. At the Bottom update the following
Replace : decoder.setDeviceType("temp");
By : decoder.setDeviceType(type);
11. Insert the following at the bottom
var result = Decoder(splitInBytes(payload), 0);
12. Save Service and Thing
13. Create a test Service for Adeunis Temp Device
Within "AdeunisCodec" Thing
Create a new service called "test_decode_temp" with Output as String
Insert the following code:
// result: STRING
var result = me.Decode({type: "temp" /* STRING */,payload: "43400100F40200F1" /* STRING */});
Save & Execute
The expected result is:
{"temperatures":[{"unit":"°C","name":"probe 1","id":0,"value":24.4},{"unit":"°C","name":"probe 2","id":0,"value":24.1}],"type":"0x43 Temperature data","status":{"frameCounter":2,"lowBattery":false,"hardwareError":false,"probe1Alarm":false,"configurationDone":false,"probe2Alarm":false}}
Please visit the Decoder test section of Adeunis website to see the reference for the Temp device test case, here.