Trouble with LONG to BYTE array

I'm sure i'm missing something obvious here , but cant find it...
Ive a sketch here, where I convert a an unsigned long into a 4 byte array and then try and turn it back to a long, But as you can see from the output, the re-assembly of the variable doesn't work . I have tried two routes as shown.
thx.

( Arduino UNO. IDE 1.8) -i know i got hex/dec the wrong way on the end print

unsigned long  VolFlow = 123456789;
unsigned long  MassFlow;// do this one later!

void setup()
{

  Serial.begin(9600);
  delay(200);
  Serial.print(" Check Volume flow = ");
  Serial.print(VolFlow, DEC);
  Serial.print("   and in HEX...   ");
  Serial.println(VolFlow, HEX);


  Serial.println("!!");
  //convert each to 4 byte sized  VolFlow numbers)

  byte Voldata[4];
  Voldata[0] = (VolFlow);
  Voldata[1] = (VolFlow >> 8);
  Voldata[2] = (VolFlow >> 16);
  Voldata[3] = (VolFlow >> 24);

  Serial.println("");
  Serial.println(" These Vol flow bytes are, starting at Voldata[0] = ");
  Serial.println(Voldata[0], HEX);
  Serial.println(Voldata[1], HEX);
  Serial.println(Voldata[2], HEX);
  Serial.println(Voldata[3], HEX);
  Serial.println("");

  //check its worked ! (it doesn't :( )

  unsigned long  VolFlow2 = ((Voldata[3] << 24) | (Voldata[2] << 16) | (Voldata[1] << 8) | Voldata[0]);

  Serial.print("Vol flow recovered = ");
  Serial.print(VolFlow2, HEX);
  Serial.print("   and in HEX...   ");
  Serial.println(VolFlow2, DEC);


  // and try other way around:

  byte AltVoldata[4];// try another way
  AltVoldata[0] = (VolFlow >> 24);
  AltVoldata[1] = (VolFlow >> 16);
  AltVoldata[2] = (VolFlow >> 8);
  AltVoldata[3] = (VolFlow);


  Serial.println("");  Serial.println("");
  Serial.println(" These AltVol flow bytes are , starting at AltVoldata[0]= ");
  Serial.println(AltVoldata[0], HEX);
  Serial.println(AltVoldata[1], HEX);
  Serial.println(AltVoldata[2], HEX);
  Serial.println(AltVoldata[3], HEX);
  Serial.println("");

  //check its worked ! (it doesn't :( )
  unsigned long  VolFlow3 = (unsigned long)((AltVoldata[0] << 24) | (unsigned long)(AltVoldata[1] << 16) | (unsigned long)(AltVoldata[2] << 8) | (unsigned long)AltVoldata[3]);

  Serial.print("AltVol flow recovered = ");
  Serial.print(VolFlow3, HEX);
  Serial.print("   and in HEX...   ");
  Serial.println(VolFlow3, DEC);

}

void loop()
{

}

and the output is:

 Check Volume flow = 123456789   and in HEX...   75BCD15
!!

 These Vol flow bytes are, starting at Voldata[0] = 
15
CD
5B
7

Vol flow recovered = FFFFCD15   and in HEX...   4294954261


 These AltVol flow bytes are , starting at AltVoldata[0]= 
7
5B
CD
15

AltVol flow recovered = FFFFCD15   and in HEX...   4294954261

if you compile with all warnings you'll likely see

warning: left shift count >= width of type [-Wshift-count-overflow]

you need to promote your bytes to uint32_t before shifting them left (and remember the memory is organised in little endian)

On a Arduino Uno ? Then the compiler promotes the byte to integer when shifting, so the first two will work. There might be a difference when the compiler can do it with a constant or when you do this runtime.

I see code that shifts a byte more that 8 position a lot :grimacing:

try this

unsigned long  VolFlow = 0x12345678;
unsigned long  MassFlow;// do this one later!

void setup()
{

  Serial.begin(115200);
  Serial.print(" Check Volume flow in HEX = 0x");
  Serial.println(VolFlow, HEX);


  byte Voldata[4];
  Voldata[0] = (VolFlow);
  Voldata[1] = (VolFlow >> 8);
  Voldata[2] = (VolFlow >> 16);
  Voldata[3] = (VolFlow >> 24);

  Serial.println("");
  Serial.println(" These Vol flow bytes are, starting at Voldata[0] = ");
  Serial.println(Voldata[0], HEX);
  Serial.println(Voldata[1], HEX);
  Serial.println(Voldata[2], HEX);
  Serial.println(Voldata[3], HEX);


  unsigned long  VolFlow2 = (((uint32_t) Voldata[3]) << 24) | (((uint32_t) Voldata[2]) << 16) | (((uint32_t) Voldata[1]) << 8) | (((uint32_t) Voldata[0]) << 0);

  Serial.print("Vol flow recovered Hex = 0x");
  Serial.print(VolFlow2, HEX);
}

void loop() {}

So .. as below ( edit on phone so I just used β€œlong”)


byte Voldata[4];
  Voldata[0] = (long) (VolFlow);
  Voldata[1] = (long)(VolFlow >> 8);

yeah kinda ➜ not for taking it apart, to bring it back together

see the code I posted above

  unsigned long  VolFlow2 = (((uint32_t) Voldata[3]) << 24) | (((uint32_t) Voldata[2]) << 16) | (((uint32_t) Voldata[1]) << 8) | (((uint32_t) Voldata[0]) << 0);

1 Like

@J-M-L

Thank you , got that now ( not as I did ) ; just on re assembly .

yeah

➜ basically trunking happen when you do

  byte Voldata[4];
  Voldata[0] = (VolFlow);
  Voldata[1] = (VolFlow >> 8);
  Voldata[2] = (VolFlow >> 16);
  Voldata[3] = (VolFlow >> 24);

VolFlow is an unsigned long, when you shift its content right it remains an unsigned long. So you are trying to stuff 32 bits into 8 and the specification says you'll get the least significant byte in that case

➜ that's what you wanted

details = the C++ specification is clear on what happens when you try to stuff a wider integral type into a narrower intergral type. it's described in Implicit conversions - cppreference.com under Integral conversions

An alternative is to shift the result while adding bytes.

Code by @J-M-L in post #4 has this:

unsigned long  VolFlow2 = (((uint32_t) Voldata[3]) << 24) | (((uint32_t) Voldata[2]) << 16) | (((uint32_t) Voldata[1]) << 8) | (((uint32_t) Voldata[0]) << 0);

that can be replaced by this:

  unsigned long VolFlow2 = (unsigned long) Voldata[3];
  VolFlow2 <<= 8;
  VolFlow2 |= (unsigned long) Voldata[2];
  VolFlow2 <<= 8;
  VolFlow2 |= (unsigned long) Voldata[1];
  VolFlow2 <<= 8;
  VolFlow2 |= (unsigned long) Voldata[0];

Thanks guys

All working now:

 Check Volume flow = 123456789   and in HEX...   75BCD15
!!

 These Vol flow bytes are, starting at Voldata[0] = 
15
CD
5B
7

Vol flow recovered = 75BCD15   and in HEX...   123456789


 These AltVol flow bytes are , starting at AltVoldata[0]= 
7
5B
CD
15

AltVol flow recovered = 75BCD15   and in HEX...   123456789

HEX and DEC still wrong way around !

That is because you told it to print HEX for the decimal value, and DEC for the hex value:

  unsigned long  VolFlow2 = ((Voldata[3] << 24) | (Voldata[2] << 16) | (Voldata[1] << 8) | Voldata[0]);

  Serial.print("Vol flow recovered = ");
  Serial.print(VolFlow2, HEX);
  Serial.print("   and in HEX...   ");
  Serial.println(VolFlow2, DEC);

You can also use memcpy to do the conversion:

  //convert each to 4 byte sized  VolFlow numbers)

  byte Voldata[sizeof(unsigned long)];
  memcpy(Voldata, &VolFlow, sizeof(unsigned long));

  Serial.println("");
  Serial.println(" These Vol flow bytes are, starting at Voldata[0] = ");
  Serial.println(Voldata[0], HEX);
  Serial.println(Voldata[1], HEX);
  Serial.println(Voldata[2], HEX);
  Serial.println(Voldata[3], HEX);
  Serial.println("");

  //check its worked !
  
  unsigned long  VolFlow2;
  memcpy(&VolFlow2, Voldata, sizeof(unsigned long));

  Serial.print("Vol flow recovered = ");
  Serial.print(VolFlow2, DEC);
  Serial.print("   and in HEX...   ");
  Serial.println(VolFlow2, HEX);

Pointers could also be used:

// Extract/manipulate bytes from 32-bit variable with a pointer

// https://forum.arduino.cc/index.php?topic=630682.msg4279573#msg4279573

// This approach can replace the use of a union to accomplish the same thing.

unsigned long bigVar = 0x55cc33aa; // 4 bytes in RAM interpreted as an unsigned long

// Take the address of bigVar and cast it to a byte pointer which can
// point to any byte in RAM as a byte regardless of how it was declared

byte *bp = (byte *)(&bigVar);
// *bp is now the low order byte of bigVar
// *bp+1 is the next byte
// *bp+2 is the next
// *bp+3 is high order byte of bigVar

// Do the same thing except cast to an unsigned pointer
// Take the address of bigVar and cast it to a word pointer which can
// point to any word in RAM as a word regardless of how it was declared

uint16_t *wp = (uint16_t *)(&bigVar);
// *wp is now the low order word of bigVar
// *wp+1 is the high order byte // verify this next byte
// xxx *bp+2 is the next
// xxx *bp+3 is high order byte of bigVar

void setup() {
  Serial.begin(115200);
  Serial.flush();
  Serial.print("\n  bigVar - \t");
  Serial.println(bigVar, HEX);

  Serial.println("\n*bp+i\tbp[i]\twhich byte\taddress");
  for (byte i = 0; i < sizeof(bigVar); i++) {
    Serial.print(*(bp + i), HEX); // Display contents of the pointed address within bigVar
    Serial.print("\t");
    Serial.print(bp[i], HEX); // Same result using array notation
    Serial.print("\t");
    Serial.print((int) ((bp + i) - bp), HEX); // subtraction of pointers
    Serial.print("\t\t");
    Serial.println((int)( bp + i ), HEX); // Show physical memory location of byte
  }
  
  *bp = 0x66; // Modify the low order byte of bigVar
  Serial.print("\nmodified bigVar ");
  Serial.println(bigVar, HEX);
  bp[3] = 0x80; // Modify high byte of bigVar
  Serial.print("modified again\t");
  Serial.println(bigVar, HEX);
  Serial.println("-----------------\nbigVar's bytes in reverse order");
  // print bigVar's bytes in reverse order
  Serial.println("\n*bp+i\tbp[i]\twhich byte\taddress");
  
  for (char i = sizeof(bigVar) - 1; i >= 0 ; --i) {
    Serial.print(*(bp + i), HEX); // Display contents of the pointed address within bigVar
    Serial.print("\t");
    Serial.print(bp[i], HEX); // Same result using array notation
    Serial.print("\t");
    Serial.print((int) ((bp + i) - bp), HEX); // subtraction of pointers
    Serial.print("\t\t");
    Serial.println((int)( bp + i ), HEX); // Show physical memory location of byte
  }
  Serial.println("\n-------- 16-bit operations ------");

  //  --------    word pointer    --------
  Serial.print("\n  bigVar - \t");
  Serial.println(bigVar, HEX);
  Serial.println("\n*bp+i\tbp[i]\twhich byte\taddress");

  for (byte i = 0; i < sizeof(bigVar) / 2; i++) {
    Serial.print(*(wp + i), HEX); // Display contents of the pointed address within bigVar
    Serial.print("\t");
    Serial.print(wp[i], HEX); // Same result using array notation
    Serial.print("\t");
    Serial.print((int) ((wp + i) - wp), HEX); // subtraction of pointers
    Serial.print("\t\t");
    Serial.println((int)( wp + i ), HEX); // Show physical memory location of byte
  }
  *wp = 0x9900; // Modify the low order word of bigVar
  Serial.print("\nlow word modified ");
  Serial.println(bigVar, HEX);
  wp[1] = 0x1144; // Modify high byte of bigVar
  Serial.print("hi word modified  ");
  Serial.println(bigVar, HEX);
}

void loop() {
}

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.