Problem in sending negative values in the payload

I’ve a LoRaWAN end-node (Feather M0 LoRa) sending data to the gateway (IMST+Pi3). I’m using MCCI LoRaWAN LMiC library to send solar battery current data. The code I’m using is an adapted version of https://github.com/mcci-catena/arduino-lmic/blob/master/examples/ttn-otaa-feather-us915-dht22/ttn-otaa-feather-us915-dht22.ino to suit to my application.

The battery current is positive during charging time and negative during discharging time. I could see battery charging current properly in my application server (i.e., positive values), however when it comes to discharging current, I’m seeing the values at +645 in the application server instead of negative values.

I carefully looked at my code and I suspect if the following line could be the problem.
LMIC_setTxData2(1, (uint8_t*)payload, sizeof(payload), 0);

As such uint8_t has a range of 0 to 255, and it cannot take negative values. Am I right here? How do I overcome this error?

Probably you are sending more than one byte, in that case just send an as positive value, and in dedicated byte send a sign - this is the easiest way.
More complex solution is present in your example:

int16_t payloadTemp = …;
byte tempLow = lowByte(payloadTemp);
byte tempHigh = highByte(payloadTemp);
// place the bytes into the payload
payload[0] = tempLow;
payload[1] = tempHigh;

Please provide a larger sample of your code including the calculation of the payload values. (Or post a link to the entire code)
Also post the code you are using to decode the received payload in your application,

The code you are quoting is probably not the cause of your issues.

Please don’t suggest wasting a byte just to send a minus sign. Sending negative values works without overhead if both the encoding and decoding side are crafted correctly.

1 Like

For non-commercial projects, it really does not matter - make it work as soon as possible. You will have time for optimization later on.

For both commercial and non commercial projects it makes sense to do it right the first time. After it works there is no incentive to make improvements, as it is probably being used so changes will interrupt operation.

2 Likes

Also, the following still does not guarantee that the value will be decoded correctly. In fact, negatives values will erroneously decode into very large positive values if the decoder does not support negative values.

I’m posting my full code here: Final_circuit_final_code.ino - Google Drive and decoder function here: Decoder function.txt - Google Drive

I’m actually measuring the voltage and current of load, battery and solar of my standalone solar PV system. In addition to that I’m measuring ambient temperature and relative humidity, thus making the total number of measurements to eight. That’s why I’ve declared an array of int type of size 8.

value[3]= (val[3]/2.0)+1000.0;
value[4]= (val[4]/2.0)+1000.0;
value[5]= (val[5]/2.0)+1000.0;

val[3], val[4] and val[5] are the load, battery and solar currents respectively. The reason I’ve added 1000 is to account for the negative values; I’ve made sure that this 1000 is taken care of in the decoder function.

LMIC_setTxData2(1, (uint8_t*)payload, sizeof(payload), 0);

Again, could this be the issue?

You’re right @arjanvanb, the negative values of val[4] i.e., battery current are erroneously being decoded to very large positive values, while there’s no issue with positive values. How do we address it?

Like @kersing already wrote: no. The above “cast” only tells the compiler that you know what you’re doing, and that whatever is in the variable payload can be handled as if it were of type uint8_t. That happens to be what this LMIC function expects, but your code has declared payload to be of a different type:

byte payload[16];

Even though that is really the same (both uint8_t and byte define 8 bits unsigned integer values, from 0 thru 255), you need to tell the compiler that you know it’s okay. You could also use:

uint8_t payload[16];
...
// No need to cast now:
LMIC_setTxData2(1, payload, sizeof(payload), 0);

So what’s your code doing? I’ve added some comments:

// Each val holds the sum of 200 samples.
float val[6] = {0};
...
// Arduino int is 16 or 32 bits, depending on the board.
int value[8];
...
// Take the average of the 200 samples and multiply by 100, to
// send 12.35 as 1235. Add +1000 to always send positive values,
// hence assuming the average is larger than -1000. For a 16 bits
// unsigned integer, which supports 0 thru 65,535, this allows
// for actual averages between -10.00 and +645.35. To allow for,
// e.g., -300.00 thru +355.35, one would add 30,000 instead.
value[3]= (val[3] / 2.0) + 1000.0;
...
// Send MSB first
payload[6] = highByte(value[3]);
// ...and LSB next
payload[7] = lowByte(value[3]);

Like I linked to earlier, for the last two lines, you could also use:

// MSB
payload[6] = value[3]>>8;
payload[7] = value[3];

All looks good so far. You’re using two bytes to send each value, MSB first. And you’re not sending negative values, so no magic needed in the decoder.

However, while decoding, you’re using:

var LC = (bytes[6]<<15) | bytes[7];
...
return {
  ...
  LC: (LC-1000)/100,
  ...
}

That’s wrong: why shift the MSB 15 bits to the left when concatening the two bytes into a single value? I’d say this should read:

var LC = bytes[6]<<8 | bytes[7];

(No parentheses needed either. It’s okay to add them, of course.)

If in the Arduino code you would not add the 1,000, so if you would actually be sending signed 16 bits values, then in a JavaScript decoder you would need:

var LC = bytes[6]<<24>>16 | bytes[7];

See Decrypting messages for dummies - #4 by arjanvanb that I linked to before.

2 Likes