Converting a 5-byte big number to a number?

I'm reading data from a device that returns a lot of bytes. 5 bytes are representing a big number that I need. For example, I receive 24C9A75A2E and it means the value is 158002010670.
These bytes are in a byte array positions 5 to 9.

I tried converting this number to a long like this:

long long val = 0;
val += (long)dataRx[9] << 32;
val += (long)dataRx[8] << 24;
val += (long)dataRx[7] << 16;
val += (long)dataRx[6] << 8;
val += (long)dataRx[5];

But that did 't seem to work.

Can anyone help me out?

Well, what did you get?

Post a small sketch that shows what went wrong. At a glance you snippet seems to be plausible, so.

Or

are those bytes in positions 9 to 5?

a7

Are the bytes binary representation of the number or ASCII?

I see why you think you need a cast, maybe you do. But would not long long then be the type to choose?

a7

3 Likes

If I print the bytes like this:

Serial.println(dataRx[9]);
Serial.println(dataRx[8]);
Serial.println(dataRx[7]);
Serial.println(dataRx[6]);
Serial.println(dataRx[5]);

I'm getting these values:

46
90
167
201
36

Trying to get the number with the snipper in my first post, I get this value:
1520945444

If I swap the order I'm getting a negative number...

If you had printed in hex it would have made more sense.
2E
5A
A7
C9
24

and
1520945444 = 5AA7C924 hex
So the result is truncated to 4 bytes and the digits are reverse order in 2 byte chunks. Probably due to using the brain dead Intel ordering. (What? Me? have a preference in the big vs little endian war?)

3 Likes

YMMD :+1:

Ok. So now we know the result is truncated to 4 bytes etc... But how can I fix it? I'm still unsure what's happening here or what I do wrong.

Maybe a long long cast as suggested in post 4. With a long the 32 bit shift will be zero (you shifted all the bits out of the 32 bit long).

try

int64_t val = 0;
val += ((int64_t)dataRx[9]) << 32;
val += ((int64_t)dataRx[8]) << 24;
val += ((int64_t)dataRx[7]) << 16;
val += ((int64_t)dataRx[6]) << 8;
val += ((int64_t)dataRx[5]);

(int64 = long long)

Check GitHub - RobTillaart/printHelpers: Arduino library to help formatting data for printing for printing the data

1 Like

It's something about the sign bits.

The below prints the right answer, you figure it out. I tried everything, so everything is still in there. Translation to Arduino-land an exercise for the reader.

It has nothgin to do with endianess.

# include <stdio.h>

// 24C9A75A2E and it means the value is 158002010670

unsigned char dataRx[10];

int main() {

  printf("Jello Whirled!\n\n");
	
  dataRx[9] = 0x2e;
  dataRx[8] = 0x5a;
  dataRx[7] = 0xa7;
  dataRx[6] = 0xc9;
  dataRx[5] = 0x24;

  long long val = 0;

  val |= ((unsigned long long) dataRx[5]) << 32;
  printf("1 val = %llx\n", val);
  val |= ((unsigned long long) dataRx[6]) << 24;
  printf("2 val = %llx\n", val);
  val |= ((unsigned long long) dataRx[7]) << 16;
  printf("3 val = %llx\n", val);
  val |= ((unsigned long long) dataRx[8]) << 8;
  printf("4 val = %llx\n", val);
  val |= (unsigned long long) dataRx[9];

  printf("\n\n");

  printf("val = %lld\n", val);
  printf("val = %llx\n", val);
}

yields

Jello Whirled!

1 val = 2400000000
2 val = 24c9000000
3 val = 24c9a70000
4 val = 24c9a75a00


val = 158002010670
val = 24c9a75a2e


...Program finished with exit code 0
Press ENTER to exit console.

HTH, time for something else. A pill of some kind, probably. :expressionless:

a7

1 Like

While I was banging away incompetently the way I will.

THX.

a7

Quite likely. Beware shifting signed integer types.

This helped, was in fact the last straw. Or oh I don't know, going mad trying stuff.

a7

I was not able to produce the correct answer without changing dataRx to unsigned.

But I am late, and it's getting tired, so I may be wrongly concluding it is necessary.

a7

1 Like

@AKnuts
Can the 5 byte value be a negative value?
Does it have a sign bit?

@alto777
You're probably right, signed shift might cause trouble.

Below a variant that does not shift the received bytes but val
As val stays far from having a sign bit, there is no overflow.

int64_t
for (int i = 9; i >= 5; i--)
{
  val <<= 8;
  val += dataRx[i];
}

This works:

int64_t val = 0;
val += ((int64_t)dataRx[5]) << 32;
val += ((int64_t)dataRx[6]) << 24;
val += ((int64_t)dataRx[7]) << 16;
val += ((int64_t)dataRx[8]) << 8;
val += ((int64_t)dataRx[9]);

didn't work here, I'll try again later.

That works better with the bytes in the right order.

int64_t val = 0;
for (int i = 5; i <= 9; i++)
{
  val <<= 8;
  val += dataRx[i];
}

I had also changed to |= , my habit when it is more like bit twiddling than arithmetic. Unecessary.

But I did still need to make dataRx unsigned.

I am running these using the online C++ interpreter at https://www.onlinegdb.com.

a7

1 Like

That did not work until I made dataRx unsigned.

What Arduino board are you running this on?

OK, I ran it on the ESP32 - could it be... yes, argh! I can break your code that works by making dataRx signed.

So, do we have:

UNO char is signed by default, it must be told unsigned.

ESP32 char is unsigned by default, making it signed causes some, um, excitement.

Learn something every day.

a7

1 Like

The lesson to be learned? Always use int8_t, uint8_t, etc. Then you'll know signed / unsigned. Use char only for ASCII characters. Those are <= 127 so signed / unsigned doesn't matter.

2 Likes