Snprintf clarification

I'll preface this by saying I'm a mechanical engineer who's been asked to build a device and also look at the programming, the mechanical bit is easy, I'm struggling a little with some of the programming;

I'm using a Nano R4 and LoRa wan shield to send some data to a "things" server, I've pulled in the demo code and got the whole thing working, I can send data and it appears okay in the things server.

I could do with some help understanding how a section of the code works;

    int randomNumberOne = 99;
    int randomNumberTwo = 11;

    Serial.print(F("Random Number 1: "));
    Serial.println(randomNumberOne);
    Serial.print(F("Random Number 2: "));
    Serial.println(randomNumberTwo);
    
    char sensor_data_buff[128] = "\0";

    snprintf(sensor_data_buff, 128, "AT+SENDB=%d,%d,%d,%02X%02X%02X%02X", 0, 2, 4, (short)(randomNumberOne*100)>>8 & 0xFF, (short)(randomNumberOne*100) & 0xFF, (short)(randomNumberTwo*10)>>8 & 0xFF, (short)(randomNumberTwo*10) & 0xFF);

"randomnumberone" and "randomnumbertwo" are integers that represent real life data.

I understand that snprintf takes everything and converts it to a string.

I don't understand how its turning the integers into hex, specifically this bit;

(short)(randomNumberOne*100)>>8 & 0xFF, (short)(randomNumberOne*100) & 0xFF, (short)(randomNumberTwo*10)>>8 & 0xFF, (short)(randomNumberTwo*10) & 0xFF);

for random values 99 and 11, the returned hex data in sensor_data_buff is 26AC006E, I can't understand how to break the hex down, in order to convert it back to decimal.

Welcome to the forum

It is the %02X format string that converts the input into a HEX string of 2 characters with a leading zero added if necessary. The shifts and masking of the random values splits them into sections ready to be formatted as HEX

You do not need to convert the HEX as you already have the values in the two variables

Are you saying that you don't want to print the values as HEX ?

i suppose my question would be, what would a line of code look like that "decodes" 26AC006E back into 99 and 11.

You don't need to decode it back as you already have the values in the two variables.

Incidentally the two HEX values 26AC and 006E are not 99 and 11, they are 9900 and 110 so the conversion to HEX is not correct anyway

It is not clear what problem you are trying to solve

sorry, probably wasn't clear with my intentions.

the hex values are transmitted to the cloud, they're then forwarded to another Arduino, the second arduino only has the hex string, so i was trying to understand how to decode it.

doing some digging this morning, the LoRa shield uses AT commands, the "SendB" is to transmit a string of hex, it seems there's also a "Send" command which allows you to send the values as plain text instead, which i think would then then simplify the snprintf line.

you convert your int data to a text string "26AC006E"
to convert back

  1. convert the text string to a byte array
  2. convert the byte array to int values

e.g. implement a simple test program

void setup() {
  Serial.begin(115200);
  delay(2000);
  int i = 99, j = 11;
  char data[20] = { 0 };
  // convert 16 bit data to text string
  snprintf(data, 20, "%02X%02X%02X%02X", (i * 100) >> 8 & 0xff, (i * 100) & 0xff, (j * 10) >> 8 & 0xff, (j * 10) & 0xff);
  Serial.println(data);
  // convert text string to b
  char data2[20] = { 0 };
  sscanf(data, "%02X%02X%02X%02X", &data2[0], &data2[1], &data2[2], &data2[3]);
  for (i = 0; i < 4; i++)
    Serial.print(data2[i], HEX);
  Serial.println();
  // convert bytes to 16bit int
  int l = (int)data2[0] << 8 | data2[1];
  int m = (int)data2[2] << 8 | data2[3];
  Serial.println(l);
  Serial.println(m);
}

void loop() {}

serial monitor output

26AC006E
26AC06E
9900
110

as @UKHeliBob pointed out the stored values are 9900 and 110 not 99 and 11

if you are transmitting over LoRa not sure why you are converting your int values to a text string - why not to a byte array and then convert back to int values at The Things V3 server using a payload format decoder?

simple reason that i'd never played with programming or arduinos until about a week ago and I've had this dropped on me with expectations to make it work, so I'm trying to learn as I go along.

you have a steep learning curve - Arduino + LoRa + the Things server

recommend you transmit a byte array (four bytes for your two int values) then at The Things server use a payload formatter to decode
e.g. example payload formatter decoding 10 bytes to 5 int values then to float values

function decodeUplink(input) {
  var data = {};
  data.temperature = ((input.bytes[1] <<8 | input.bytes[0])/100.0)-100.0; 
  data.pressure = (input.bytes[3] <<8 | input.bytes[2]);
  data .humidity = (input.bytes[5] <<8 | input.bytes[4])/100.0; 
  data.DStemperature = ((input.bytes[9] <<8 | input.bytes[8])/100.0)-100.0; 
  data.distance = ((input.bytes[7] <<8 | input.bytes[6])/100.0)-100.0; 
  return {
    data: data
  };
}

EDIT: looking back I see you are using a Nano R4 as the host microcontroller
the Nano R4 uses 5V logic - most LoRa modules use 3.3V logic
if so the Nano R4 may damage the LoRa module

It is not clear whether you meant 4 bytes per int or 2 bytes per int which totals 4 bytes.

An int on the Uno R4 is 32 bits, ie 4 bytes

depends on actual number of bits use to store the application data, e.g. byte, 16bits or 32bits
I was assuming 16 bit integer data
the critical thing is to keep the amount of data transmitted over LoRa as small as possible

EDIT: in post 8 I gave an example of The Things format decoder - the associated coder was

extern float temperature, pressure, humidity,dist_cm, DS18B20_temperature;
/* Prepares the payload of the frame */
static void prepareTxFrame(uint8_t port) {
  /*appData size is LORAWAN_APP_DATA_MAX_SIZE which is defined in "commissioning.h".
  *appDataSize max value is LORAWAN_APP_DATA_MAX_SIZE.
  *if enabled AT, don't modify LORAWAN_APP_DATA_MAX_SIZE, it may cause system hanging or failure.
  *if disabled AT, LORAWAN_APP_DATA_MAX_SIZE can be modified, the max value is reference to lorawan region and SF.
  *for example, if use REGION_CN470, 
  *the max value for different DR can be found in MaxPayloadOfDatarateCN470 refer to DataratesCN470 and BandwidthsCN470 in "RegionCN470.h".
  */
  appDataSize = 10;
  // copy temperature and pressure to LoRaWAN buffer
  Serial.printf("temp = %f, pressure = %f humidity = %f\n", temperature, pressure, humidity );
  int dist = (int)((dist_cm+100) * 100.0), DStemp = (int)((DS18B20_temperature+100) * 100.0), temp = (int)((temperature+100) * 100.0), press = (int)(pressure), hum=humidity*100;
  appData[0] = (temp & 0xff);
  appData[1] = (temp >> 8) & 0xff;
  appData[2] = press & 0xFF;
  appData[3] = (press >> 8) & 0xff;
  appData[4] = hum & 0xFF;
  appData[5] = (hum >> 8) & 0xff;
  appData[6] = dist & 0xFF;
  appData[7] = (dist >> 8) & 0xff;
  appData[8] = (DStemp & 0xff);
  appData[9] = (DStemp >> 8) & 0xff;
  Serial.printf("Sending %x %x %x %x %x %x %x %x", appData[0], appData[1], appData[2], appData[3], appData[4], appData[5], 
                     appData[6], appData[7], appData[8], appData[9]);
  Serial.printf(" %d %d temperature = %d pressure %d  ", temp, press, appData[1] << 8 | appData[0], appData[3] << 8 | appData[2]);
}

the float data is normalized and converted to int then bytes for transmission
serial monitor output

Temperature = 27.94 °C
Pressure    = 1016.15 hPa
Approx Altitude = -24.11 m

Sending ea a b1 27 2794 10161 temperature = 2794 pressure 10161  confirmed uplink sending ...

Totally agree, but if you don't try you won't learn.

i did wonder that, but i've currently got the shield plugged into an uno R4 to power it, and using jumpers, linked the TX and RX to a nano R4 on a breadboard and its working okay.

I've designed a pcb for all this stuff to go on and included some logic level shifters that can be connected with jumpers in case it was a problem.

As a matter of interest which pins of the R4 are you using for the serial interface to the LoRa module ?

which specific LoRa module are you using?
when connecting 3.3V logic to 5V logic modules may die immediately, after 10 seconds, a day, a year or work for ever - you don't know
unless a module is specified as 5V tolerant best to use level shifters or even better use a host microcontroller which uses 3.3V logic

on the nano, D0 RX and D1 TX which I initialize by using the "Serial1" command.

i learned yesterday that the dragino LoRa shield doesn't work with the Uno R4 wifi. the shield pcb is laid out to use Uno pins 10 and 11 as softserial for communicating, but the Uno R4 cant do softserial on those pins for some reason.

the ultimate goal is to use the dragino LoRawan module and a nano R4 both surface mounted to a tiny PCB, but i wanted to start out easy with plug together shields and example code to see if i could learn enough make it work

to save designing and implementing a PCB why not just get an off-the-shelf module with microcontroller and LoRa on the PCB, e.g. Heltec LoRa32 V3 which has an onboard ESP32S3 microcontroller, a SX1262 LoRa module and OLED
at present we are looking at using a Heltec LoRa32 V3 or V4 as a plug daughter board on a custom PCB which has various IO devices for a water monitoring system

how much data will you be transmitting and how often?

maybe worth changing the thread title to indicate it is a LoRa based problem - this could bring in other posts from the LoRa community

that's really interesting, didn't know that even existed.

it'll probably wake up, transmit once a minute, wait for a response and then either sleep or do an action.

thanks very much for the help.

make sure you keep within the LoRaWAN fair access policy

do you need to use LoRaWAN?
if the target system is within a local network zone why not use WiFi?

no wifi due to location, solar power + batteries to gateway then 4g connection from the gateway to the internet.

have you setup the LoRaWAN gateway?
if so why not cut out the LoRa and use a module with microcontroller and 4G modem onboard, e.g. ESP32-S3-SIM7670G-4G