-1

I am trying to use LoRAWAN and sending tempurature values as less bytes. I found that way and tested it works fine it can send negative and positive numbers as 2 bytes instead of "float" as 4 bytes or "double" as 8 bytes but still I can not understood how this code produce int celciusInt = (bytes[0] & 0x80 ? 0xFFFF0000 : 0) | bytes[0] << 8 | bytes[1]; negative numbers. as a result it makes 4 bytes for nagetive numbers but ttn network still see this as 2 bytes and mind blowing

TEST code:

byte bytes[2];
int derece = -3556;

void setup() { // put your setup code here, to run once: Serial.begin(115200); delay(3000); }

void loop() { // put your main code here, to run repeatedly:

bytes[0] = derece >> 8; bytes[1] = derece & 0xFF;

int celciusInt = (bytes[0] & 0x80 ? 0xFFFF0000 : 0) | bytes[0] << 8 | bytes[1];

Serial.println(celciusInt);

delay(3000); }

OUTPUT:

-3556

As a result -3556 number is returning 0xFFFFF0E4 value and how this make negative numbers still I did not understant. So many times I read chatgpt or etc. but still not understood.

Can you explain it to me like you would to a 5-year-old?

mehmet
  • 297
  • 2
  • 19

2 Answers2

4

I won't explain it like you are 5-years old because it would take to much effort, you are a grown up, and you are capable of reading articles yourself. So start by putting aside ChatGPT, and instead read the Wikipedia article Two's complement. I mean, read carefully, taking all the time you need to reach a complete understanding.

Now, the number −3556 would be written in binary as either

11110010.00011100

or

11111111.11111111.11110010.00011100

depending on whether your Arduino uses 16-bit or 32-bit integers (it varies across Arduino boards). The assignments to bytes[0] and bytes[1] extract the two least-significant bytes of this number:

bytes[0] = 11110010 (in binary)
bytes[1] = 00011100 (in binary)

The assignment to celciusInt reconstructs the number by ORing together three terms:

11111111.11111111.00000000.00000000 = (bytes[0] & 0x80 ? 0xFFFF0000 : 0)
00000000.00000000.11110010.00000000 = bytes[0] << 8
00000000.00000000.00000000.00011100 = bytes[1]
───────────────────────────────────
11111111.11111111.11110010.00011100 → assigned to celciusInt

Note that the first term in sign extension (you know what this is, because you read the article I mentioned, didn't you?). It is only needed if you want the result as a 32-bit integer. It would be simpler to forego sign extension and instead store the result in a 16-bit integer:

int16_t celciusInt = uint16_t(bytes[0]) << 8 | bytes[1];

Note that the code may work even without the cast of bytes[0] to uint16_t: this cast is only needed to avoid signed integer overflow, which in C++ is undefined behavior.

Edit: Note that a yet simpler way to split and reconstruct the number is to use a union. This lets you have a 16-bit integer and an array of two bytes share the same memory. You can then write to the integer and read back the bytes or vice-versa:

// A 16-bit integer and a pair of bytes sharing their memory.
union {
    int16_t value;
    uint8_t bytes[2];
} packet;

// Split a 16-bit integer in two bytes. packet.value = -3556; Serial.println(packet.bytes[0]); // -> 28 Serial.println(packet.bytes[1]); // -> 242

// Reconstruct a 16-bit integer from its bytes. packet.bytes[0] = 214; packet.bytes[1] = 255; Serial.println(packet.value); // -> -42

Beware of endianness though: unlike in the previous code, bytes[0] here is the least-significant byte. Practically every Arduino and computer in use today is little-endian, so this may not be an issue. However, there are some communication protocols that are big-endian, so you should be aware of the issue.

Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81
0

I suspect you mixed-up with the Endianness. I've seen many people get it wrong when they deal with Lora communication.

For Lora communication, data are send in Big Endian, while Arduino (and almost all modern computers) are storing data in Little Endian.

So if you are sending an integer 7410 (which is 0x1cf2 in hex) via Lora, 0x1c get send first, followed by 0xf2. However, when stored in the Arduino, if you are storing the data based on the transmission sequence, you would end-up storing 0x1c as first byte in data[0], and 0xfc as the 2nd byte in data[1]. So the data would looks like as 0xf21c when retrieve from Arduino in Little Endian manner, 0xf21c is the 2-compliment of -3556 in a sign integer!

hcheung
  • 1,933
  • 8
  • 15