There are many ways to encode a value, especially when it comes to sending negative values and the order of the bits and bytes (Most or Least Significant Bit/Byte First; see MSB and endianness). For your test value, the order is the problem.
You’re seeing the result of the following calculation, with <<
being a left shift of 8 positions, which is basically the same as multiplying by 28, which is 256:
0x09 + (0x29 << 8) = 0x2909
In hexadecimal notation, 0x29 << 8
yields 0x2900
, to which adding 0x09
results in 0x2909
. Or, represented using decimal values: 9 + 41 × 256 = 10505 (and then divided by 100 to get your {"celcius": 105.05}
).
However, you expected:
0x29 + (0x09 << 8) = 0x0929
…or: 41 + 9 × 256 = 2345.
So, somewhere along the line the 0x09
and 0x29
were swapped, either when encoding or when decoding.
If you’re using GitHub - thesolarnomad/lora-serialization: LoraWAN serialization/deserialization library for The Things Network to encode, then you should also use its decoder functions to reverse the operation in the payload functions. And you should also test with negative values.
Alternatively, similar to the TTN workshop example (which uses the Arduino highByte and lowByte), encode as:
float temp = 23.45; // Something like 0x41bb999a in IEEE 754 floating-point
int16_t celciusInt = temp * 100; // convert to signed 16 bits integer: 0x0929
uint8_t buffer[2]; // reserve 2 bytes in memory
// Handle high byte (MSB) first; 0x09 for 23.45
// 0x0929 >> 8 shifts the 0x29 out of memory, leaving 0x0009
// 0x0009 does not fit in a single byte, so only 0x09 is stored in buffer[0]:
buffer[0] = celciusInt >> 8;
// Handle low byte (LSB) next; 0x29 for 23.45
// 0x0929 does not fit in a single byte, so only 0x29 is stored in buffer[1]:
buffer[1] = celciusInt;
// Send on port 1, without asking for confirmation:
LMIC_setTxData2(1, buffer, sizeof(buffer), 0); // 0x0929 for 23.45
The workshop example (erroneously) decodes as:
var celciusInt = bytes[0] << 8 | bytes[1];
Here, |
is the bitwise OR, which in this calculation (and with the proper grouping parentheses as, unlike the bitwise OR, addition has a higher precedence than the shift operator) is effectively the same as:
var celciusInt = (bytes[0] << 8) + bytes[1];
…or:
var celciusInt = (bytes[0] * 256) + bytes[1];
All are fine for positive numbers, but will fail for negative numbers as JavaScript’s bitwise operators always work with 32 bits and hence expect 4 bytes, where for negative numbers the missing bytes should become 0xFF
(sign extension).
So, to also allow for negative numbers (such as 0xF6D7
for -2345, where 2345 is 0x0929
), decode using:
// Test using 0x0929 for 2345, or 0xF6D7 for -2345
function Decoder(bytes, port) {
// Sign-extend 16 bits to 32 bits
// Fewer parentheses needed when using bitwise OR rather than addition
var celciusInt = (bytes[0] & 0x80 ? 0xFFFF<<16 : 0) | bytes[0]<<8 | bytes[1];
return {
celcius: celciusInt / 100
};
}
Above, the ternary operator in the code translates to: if the leftmost 8th bit of the first byte is set, then start the result with 0xFFFF
, otherwise with 0x0000.
Alternatively, shift byte[0]
16 bits too far to the left, and then shift it back, which will do the sign extension on the fly, as the bitwise operator >>
is the sign-propagating right shift:
var celciusInt = (bytes[0]<<24) >> 16 | bytes[1];
…which can even be written as:
var celciusInt = bytes[0]<<24>>16 | bytes[1];
(I know this might be a lot of information if this is new to you, I hope the links help… Not tested.)