How to reduce payload size

Hi Guys,

I have the following sensor values that I would like to transmit:

  1. Battery reading of 3.7V LiPo via adc. {reasonable range of 3.2V to 4.4V → can do a mapping to 0-255 with one int8_t variable}
  2. Temperature reading with a physically reasonable range of 0-40°C {can map to 0-255 with one int8_t variable}
  3. 10bit adc reading {idk know what to do here, can I avoid using an int value in arduino that takes 2 byte?}

My goal is to reduce the payload as much as possible, down to my resolution/accuracy constraints, which are provided in curly brackets above.

My platform is ArduinoIDE and the recent MCCI LoRaWAN LMIC library that is also discussed in this topic by @bluejedi

My code looks like this:

uint8_t message[8];
  uint16_t tempe = 10 * temperature;
  uint16_t bat = 10 * batteryLevel;
  uint16_t hall = 10 * hallvoltage;
  uint16_t finalbat = 10 * finalBattery;
    
  message [0] = tempe >> 8;
  message [1] = tempe;
  message [2] = bat >> 8;
  message [3] = bat;
  message [4] = hall >> 8;
  message [5] = hall;
  message [6] = finalbat >> 8;
  message [7] = finalbat;
     Serial.println(F("Sending uplink packet..."));
   //  digitalWrite(5, LOW);
   wdt_reset();
     LMIC_setTxData2(1, message, sizeof(message) - 1, 0);

Is there a chance that I reduce the payload of the hall (=adc value) from 16 bit to 10bit?

Thank you!

In practical terms, all of the data is encoded in bytes, so 10 bits still needs 2 bytes.

Keep in mind that LoRaWAN adds a minimum of 13 bytes. So even if you reduce your user payload bij half, e.g. from (8+13) → (4+13), or from 21 → 17 bytes, you actually save only very little in the number of bytes transferred, only 20%.

The LoRa modulation encodes data not in bytes but in symbols, which may transfer up to 12 bit at a time. This has the effect that sometimes you can add an additional an byte without the over-the-air-length actually increasing. This also depends on the spreading factor I think. This is a bit too tricky to keep into account IMO.

Fewer bytes is better of course, but there is a strong effect of diminishing return if you try to squeeze out the last bit of encoding compactness.

@bertrik I see what you mean, the only reason for reducing the payload is to reduce the airtime to comply with the TTN fair use policy. I’m still not exactly sure when the TTN network recognizes an end node as ‘violation’. I believe 35-40s airtime might eventually still be OK per day while after 60sec are maybe not. Anyways, I can understand why you wouldn’t want to share any information on how much you can violate a limit.

For my application, I carefully need to tinker with the sending interval and the airtime.

Reducing your application payload below 4 bytes won’t make a large difference given that 3/4 of the packet duration is protocol overhead - you should almost transmit more information less often, though that has tradeoffs considering packets get lost and individual confirmation cannot be budgeted.

There is also a granularity but it’s in units of 4 bytes - changes that don’t cross a 4 byte boundary dont actually change the packet duration, unless fopts inserted behind the scenes increase the actual packet size across a boundary.

For a larger packet the thing to do with your 10 bit value would be to find two spare bits in some other readings field to put them in, but you don’t have that.

You could however encode the 4 possibilities of those bits in the use of LoRaWan ports 1-4 since you’re paying to move a bit over 7 bits of port regardless if you use it or not. Keep in mind 0 is not an allowed port for application traffic.

Edit: realized your packet is longer than the four bytes involved in your specific questions but this still mostly holds true.

Is it a problem to map 10bit ADC to 8 bit?

  1. In my case since the device I want to last six months. I don’t care to have so much detail for the battery (0-255 or 0-100). 2bits are enough. 0-25, 25-50, 50-75, 75-100. Since I don’t want to recharge the battery at 0% 25% granularity is fine with me.
  2. Same thing for temperature. 0-40 is needed to go 0-255? You can lower the resolution with 2 steps. 0,2,4,… e.t.c. In that case you need only 5bits. With 2.7 steps you need only 4bits.

If you can lower the resolution, you will need 4(temperature)+2(battery) bits in one byte. One more byte for ADC and you are ready with two bytes.

You can go down to one byte. The 2bits of the battery can be stored in ports.

Idea for ports #1
Port 1 0-25%
Port 2 25-50%
Port 3 50-75%
Port 4 75-100%

Idea for ports #2
Or you can store the battery with ports 1-101 (maps to 0-100).

Idea for ports #3 (multiple data)
You can also store Battery and temperature to ports. But with less steps.

Port 0x01, 0x02 Battery (4 steps. Aka 25%)
Port 0x04, 0x08, 0x10, 0x20, 0x40 temperature (stores 0-31 you can map to 0-40)

You can also reserve 3 ports for temperature. In that case you will have two ports so you can store MSB’s from 10-bit ADC there. Again you need to transmit only one byte.

ATTENTION: You can use only 1-224 ports

In other words. If you can ‘sacrifice’ some resolution and use ports you send only one byte.

It is worth it?

Check here the two graphs.
https://www.thethingsnetwork.org/forum/uploads/default/original/3X/9/7/977376adf9bbec169ebcf6ddac821ff4d20fe22b.png

https://www.thethingsnetwork.org/forum/uploads/default/original/3X/8/d/8da8701dbe4a840e093c50d6551b56e8642a6c13.png

1 Like

Just don’t look at the battery too hard if it’s at 4.4V, just in case it decides to become exothermic. Reality is that they rarely stay at 4.2V for long and tend to drop to ~3.8V quite quickly. So you could represent fully charged as a number and then run 3.8 - 3.2 = 0.6V, for two decimal places, aka 60mV with a base of 3.2V. Which fits nicely in to 6 bits with three values assigned to something like full, empty and over-discharged.

Must be quite a warm place where you are! How about -10 to 40 which would cover where I live in sunny Bolton. Single decimal place? So 500 - which is 9 bits.

Assuming you want all the bits, if applicable, look at the sensor to see what accuracy it can give.

So 6 + 9 + 10 = 25 bits = 3.125 bytes. Which ends up being 4 bytes. But the air time boundaries aren’t uniform across the different data rates, so DR5 has two bytes before increasing, then has 4 bytes, whereas DR3’s window is the first four bytes.

Fundamentally, with such a small payload, this would be over optimising unless you can be totally sure what the DR is going to be. But using such schemes for much larger payloads works really well, particularly if you have lots of temperatures or ADC readings.

I use the port number to identify the message format which again works well for larger payloads with different update frequencies on different sensors - so a format for some faster moving readings and other formats for slower moving, less frequent readings. But I find code gets messy if I try to fold values in to the port number instead or as well.

That does not match my recollection of the airtime spreadsheet - do you have a cite for data rate being involved and not simply 4/5 coding granularity with the count of groups scaled by the data rate to determine airtime?

I’m using this:

https://avbentem.github.io/airtime-calculator/ttn/eu868/4

Does seem so and I see sf appearing in the formula where I hadn’t expected it, as well as where I did. Interesting puzzle for a slow day - thanks for the correction

@clv storing the battery as 2bits in ports is a nice idea, thank you. @descartes the place here isn’t super hot (South Germany… :)) but I’m only interested more or less in that temperature window. Thanks for providing this very nifty tool for airtime calculation.
I think in the end, I will just stay with a payload of 4byte, which seems a good trade-off for me.