Due to the limited size of a LoRa message KELLER can send up to five float values per transmission. If a device is configured to send more than 5 float values then two messages have to be transmitted. Be aware that this doubles the message rate and therefore you might have to adjust the sending interval. KELLER recommends not more than 1 message per 10min per device; Independently, if there is one device or one thousand devices.
The actual protocol can be found here (V02.2020 English)
The actual protocol can be found here (V08.2020 English)
In the TTN console the payload and the decrypted values can be seen in the DATA tab:
TTN here already decrypts the payload, and displays the channel values as floats. It does it because it uses KELLER’s TTN payload decoder Javascript code. This decoder SW works because TTN already decodes the payload (string) firstly to a byte array.
Payload string: “AQUA079gQlk9vCn8QajZAD93x6RBuAAA”
Base64 decrypted byte array: 01 05 00 D3 BF 60 42 59 3D BC 29 FC 41 A8 D9 00 3F 77 C7 A4 41 B8 00 00
// code example with C#:
byte[] decodedPayload = System.Convert.FromBase64String(payload);
The payload size varies depending of the count of transmitted values
The payload is divided into the following groups:
01 05 00 D3 BF 60 42 59 3D BC 29 FC 41 A8 D9 00 3F 77 C7 A4 41 B8 00 00
–> Function Code = 1
01 05 00 D3 BF 60 42 59 3D BC 29 FC 41 A8 D9 00 3F 77 C7 A4 41 B8 00 00
–> Device Type = 5
01 05 00 D3 BF 60 42 59 3D BC 29 FC 41 A8 D9 00 3F 77 C7 A4 41 B8 00 00
–> 00 D3 –> 0000'0000 1101‘0011
Transmitted Channels:
01 05 00 D3 BF 60 42 59 3D BC 29 FC 41 A8 D9 00 3F 77 C7 A4 41 B8 00 00
–> BF 60 42 59 –> -0.876012384…
Test it with one of many online converters such as
//code example with C#
private static float ExtractFloat(byte[] payLoadBytes, int pos)
{
var bytes = new byte[4];
Array.Copy(payLoadBytes, pos, bytes, 0, bytes.Length);
if (BitConverter.IsLittleEndian)
{
bytes = bytes.Reverse().ToArray();
}
return BitConverter.ToSingle(bytes, 0);
}
//code example with Javascript
//
// Based on https://stackoverflow.com/a/37471538 by Ilya Bursov
function bytesToFloat(bytes) {
// JavaScript bitwise operators yield a 32 bits integer, not a float.
// Assume MSB (most significant byte first).
var bits = bytes[0]<<24 | bytes[1]<<16 | bytes[2]<<8 | bytes[3];
var sign = (bits>>>31 === 0) ? 1.0 : -1.0;
var e = bits>>>23 & 0xff;
var m = (e === 0) ? (bits & 0x7fffff)<<1 : (bits & 0x7fffff) | 0x800000;
var f = sign * m * Math.pow(2, e - 150);
return f;
}
BF 60 42 59 –> -0.876012384…
3D BC 29 FC –> 0.091876953…
41 A8 D9 00 –> 21.105957031…
3F 77 C7 A4 –> 0.967890024…
41 B8 00 00 –> 23.0
It is important to know that order and the definition of the float values corresponds to the order of the transmitted channels.
Above example payload identified:
Now, to map the channels to real named channels with physical units it is needed to lookup the given device type. See [protocol pdf](../../ADT1 LoRa data communication protocol 02_2020.pdf) or the table below.
The given channels are therefore:
Pressure values from the API are always in bar
Temperature values from the API are always in °C
It is exactly the same as above except the payload string doesn’t have to be decrypted from Base64, but converted from a string with hex-Values into a byte array.
// code example in c#
private static byte[] ConvertHexStringToByteArray(string hexString)
{
var hexAsBytes = new byte[hexString.Length / 2];
for (var i = 0; i < hexAsBytes.Length; i++)
{
string byteValue = hexString.Substring(i * 2, 2);
hexAsBytes[i] = byte.Parse(byteValue, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
return hexAsBytes;
}
Id | Device Type |
---|---|
0 | RS485 |
1 | RS485 & 2 Dig.Inp. |
2 | RS485 & Baro (P1-P2) & Dig.Inp. 1 |
3 | RS485 & Baro (P1-PB) & Dig.Inp.1 |
4 | RS485 & Baro (P1-P2) & Dig.Inp.1 & Volt Inp. |
5 | RS485 & Baro (P1-PB) & Dig.Inp.1 & Volt Inp. 1 |
6 | RS485(x5) & Baro (P1-P2) & Dig.Inp1/2 = Counter Inp. & Volt Inp. |
7 | SDI12 & Baro & Digital Inp.1 & Volt Inp |
8 | RS485 (5xP1+TOB1) & Baro & Dig.Inp. 1/2 |
9 | RS485 CTD & Baro (P1-P2) & Dig.Inp. 1 & Volt. Inp. 1 |
10 | RS485 CTD & Baro (P1-PB) & Dig.Inp. 1 & Volt. Inp. |
11 | RS485 CTD & Baro (3x P1+TOB1+Cond comp+Tcon) & Baro & Counter Inp. |
12 | RS485 & Baro (P1-PBaro) & Modbus ABB Aquamaster |
13 | RS485 (2x(P1+P2+TOB1+TOB2)) & Counter Inp. & Volt Inp. |
Id | MeasurementDefinition |
---|---|
1 | Pd (P1-P2) |
2 | P1 |
3 | P2 |
4 | T |
5 | TOB1 |
6 | TOB2 |
7 | PBaro |
8 | TBaro |
9 | Volt Inp. 1 |
10 | Volt Inp. 2 |
11 | Pd (P1-PBaro) |
12 | Conductivity Tc |
13 | Conductivity raw |
14 | T (Conductivity) |
15 | P1 (2) |
16 | P1 (3) |
17 | P1 (4) |
18 | P1 (5) |
19 | Counter input |
20 | SDI12 CH1 |
21 | SDI12 CH2 |
22 | SDI12 CH3 |
23 | SDI12 CH4 |
24 | SDI12 CH5 |
25 | SDI12 CH6 |
26 | SDI12 CH7 |
27 | SDI12 CH8 |
28 | SDI12 CH9 |
29 | SDI12 CH10 |
30 | TOB1 (2) |
31 | TOB1 (3) |
32 | TOB1 (4) |
33 | TOB1 (5) |
34 | E |
35 | F |
36 | G |
37 | mH20 (Pbaro) |
38 | mH20 (P1-P2) |
39 | mH20 (P1-P3) |
40 | mH20 (P1-P4) |
41 | mH20 (P1-P5) |
42 | Conductivity Tc (2) |
43 | Conductivity Tc (3) |
44 | T (Conductivity) (2) |
45 | T (Conductivity) (3) |
46 | P2 (2) |
47 | TOB2 (2) |
48 | AquaMaster Flow Rate |
49 | AquaMaster Pressure |
50 | AquaMaster Custom Flow Units |
51 | AquaMaster External Supply Voltage |
52 | Tank Content 1 |
53 | Tank Content 2 |
54 | Tank Content 3 |
ChannelNumber | DeviceTypeId | MeasurementDefinitionId |
---|---|---|
1 | 0 | 1 |
2 | 0 | 2 |
3 | 0 | 3 |
4 | 0 | 4 |
5 | 0 | 5 |
6 | 0 | 6 |
1 | 1 | 1 |
2 | 1 | 2 |
3 | 1 | 3 |
4 | 1 | 4 |
5 | 1 | 5 |
6 | 1 | 6 |
1 | 2 | 11 |
2 | 2 | 2 |
3 | 2 | 3 |
4 | 2 | 4 |
5 | 2 | 5 |
6 | 2 | 6 |
7 | 2 | 7 |
8 | 2 | 8 |
1 | 5 | 11 |
2 | 5 | 2 |
3 | 5 | 3 |
4 | 5 | 4 |
5 | 5 | 5 |
6 | 5 | 6 |
7 | 5 | 7 |
8 | 5 | 8 |
9 | 5 | 9 |
10 | 5 | 10 |
1 | 10 | 11 |
2 | 10 | 2 |
3 | 10 | 3 |
4 | 10 | 14 |
5 | 10 | 5 |
6 | 10 | 6 |
7 | 10 | 7 |
8 | 10 | 8 |
9 | 10 | 9 |
10 | 10 | 10 |
11 | 10 | 12 |
12 | 10 | 13 |
1 | 3 | 11 |
2 | 3 | 2 |
3 | 3 | 3 |
4 | 3 | 4 |
5 | 3 | 5 |
6 | 3 | 6 |
7 | 3 | 7 |
8 | 3 | 8 |
1 | 4 | 1 |
2 | 4 | 2 |
3 | 4 | 3 |
4 | 4 | 4 |
5 | 4 | 5 |
6 | 4 | 6 |
7 | 4 | 7 |
8 | 4 | 8 |
9 | 4 | 9 |
10 | 4 | 10 |
1 | 6 | 1 |
2 | 6 | 2 |
3 | 6 | 3 |
4 | 6 | 4 |
5 | 6 | 5 |
6 | 6 | 6 |
7 | 6 | 7 |
8 | 6 | 8 |
9 | 6 | 9 |
10 | 6 | 10 |
11 | 6 | 15 |
12 | 6 | 16 |
13 | 6 | 17 |
14 | 6 | 18 |
15 | 6 | 19 |
2 | 7 | 7 |
3 | 7 | 8 |
4 | 7 | 9 |
5 | 7 | 10 |
6 | 7 | 20 |
7 | 7 | 21 |
8 | 7 | 22 |
9 | 7 | 23 |
10 | 7 | 24 |
11 | 7 | 25 |
12 | 7 | 26 |
13 | 7 | 27 |
14 | 7 | 28 |
15 | 7 | 29 |
1 | 8 | 2 |
2 | 8 | 5 |
3 | 8 | 15 |
4 | 8 | 30 |
5 | 8 | 16 |
6 | 8 | 31 |
7 | 8 | 17 |
8 | 8 | 32 |
9 | 8 | 18 |
10 | 8 | 33 |
11 | 8 | 9 |
12 | 8 | 10 |
13 | 8 | 7 |
14 | 8 | 8 |
15 | 8 | 19 |
1 | 9 | 1 |
2 | 9 | 2 |
3 | 9 | 3 |
4 | 9 | 14 |
5 | 9 | 5 |
6 | 9 | 6 |
7 | 9 | 7 |
8 | 9 | 8 |
9 | 9 | 9 |
10 | 9 | 10 |
11 | 9 | 12 |
12 | 9 | 13 |
1 | 11 | 2 |
2 | 11 | 5 |
3 | 11 | 12 |
4 | 11 | 14 |
5 | 11 | 15 |
6 | 11 | 30 |
7 | 11 | 42 |
8 | 11 | 44 |
9 | 11 | 16 |
10 | 11 | 31 |
11 | 11 | 43 |
12 | 11 | 45 |
13 | 11 | 7 |
14 | 11 | 8 |
15 | 11 | 19 |
1 | 13 | 2 |
2 | 13 | 3 |
3 | 13 | 5 |
4 | 13 | 6 |
5 | 13 | 15 |
6 | 13 | 46 |
7 | 13 | 30 |
8 | 13 | 47 |
9 | 13 | 7 |
10 | 13 | 8 |
11 | 13 | 9 |
12 | 13 | 10 |
13 | 13 | 19 |