How to convert 4 bytes into a long?

I have a 24-bit A/D and put four 8-bit reads into a byte array:

long adc_value;
byte d[4];

digitalWrite(ADC_CS,LOW);
d[0]=shiftIn(SPI_MISO,SPI_CLK,MSBFIRST);
d[1]=shiftIn(SPI_MISO,SPI_CLK,MSBFIRST);
d[2]=shiftIn(SPI_MISO,SPI_CLK,MSBFIRST);
d[3]=shiftIn(SPI_MISO,SPI_CLK,MSBFIRST);
digitalWrite(ADC_CS,HIGH);

adc_value=long(d); // ← sketch does not compile
adc_value=long(d[0]); // ← sketch compiles
Serial.println(adc_value); // I get FF

So the conversion function “long” does not seem to accept arrays :frowning: and I wonder how to use it?

So the conversion function “long” does not seem to accept arrays and I wonder how to use it?

You don’t, because that is not what it is for.

The long that you want to construct is done by shifting one of the bytes in the array 24 places to the next. Then, the next byte is shifted 16 bits to the left, and added. Then, the next byte is shifted 8 bits to the left, and added. Then, the final byte is added.

long val = 0;
val += d[0] << 24;
val += d[1] << 16;
val += d[2] << 8;
val += d[3];

Depending on how the bytes were sent, the array may need to be used in the other order (3, 2, 1, 0).

Or use a union?

Lefty

Something like this should so the job.

long adc_value;
byte d[4];

/* Put the low byte in d[0].
 */
 d[0]= shiftIn()  // THE LOW BYTE
   :
 d[3] = shiftIn()  // THE HIGH BYTE

adc_value = *((long *)d);

I do get interesting results and am still looking for a solution. It looks like the shift operator is only 16 bit. The cast to a long swaps nibbles on some of the bytes, which is strange. Thanks everyone for your help so far.

byte d[4];
long adc_value;

d[0]=0x87;
d[1]=0x65;
d[2]=0x43;
d[3]=0x21;

adc_value=0;
adc_value += d[0] << 24;
adc_value += d[1] << 16;
adc_value += d[2] << 8;
adc_value += d[3];
Serial.println(adc_value,HEX); //–> prints 4321 (expected 87654321)

adc_value = *((long *)d);
Serial.println(adc_value,HEX); // → prints 21346587 (expected 21436587)

d[0]=0x12;
d[1]=0x34;
d[2]=0x56;
d[3]=0x78;

adc_value=0;
adc_value += d[0] << 24;
adc_value += d[1] << 16;
adc_value += d[2] << 8;
adc_value += d[3];
Serial.println(adc_value,HEX); //–> prints 5678 (expected 12345678)

adc_value = *((long *)d);
Serial.println(adc_value,HEX); // → prints 78563412 (expected 87654321)

What kind of results do you get if adc_value is unsigned long? Does it make sense for it not to be unsigned long?

Yes it should be: unsigned long adc_value; but I get the same results.

What if you cast the bytes to long before shifting?

prairiemystic:
adc_value += d[0] << 24;
adc_value += d[1] << 16;
adc_value += d[2] << 8;
adc_value += d[3];

these operations need to be cast to a long. Your left operand is of type byte, and your right operand is a const that the compiler defaults to type int, so the operation is performed as a 16 bit operation. change it to:

  adc_value += (long)d[0] << 24;
  adc_value += (long)d[1] << 16;
  adc_value += (long)d[2] << 8;
  adc_value += (long)d[3];

And you’ll get the expected outcome.

prairiemystic:
adc_value = *((long *)d);
Serial.println(adc_value,HEX); // → prints 21346587 (expected 21436587)

adc_value = *((long *)d);
Serial.println(adc_value,HEX); // → prints 78563412 (expected 87654321)

The printouts for these are as I would expect them.

prairiemystic:
I do get interesting results…

  d[0]=0x87;  

d[1]=0x65; 
  d[2]=0x43; 
  d[3]=0x21;

:

adc_value = *((long *)d);
  Serial.println(adc_value,HEX); // → prints 21346587 (expected 21436587)

21346587

I do not believe this is possible. If this is really what you got, there’s something wonky about your methodology.

What I get is: 0x21436587 which is the right answer.

byte d[4];
long adc_value;

<… stuff deleted>

adc_value = (long)d[0] << 24;
adc_value += (long)d[1] << 16;
adc_value += (long)d[2] << 8;
adc_value += (long)d[3];

This code works fine now. (Without proper casting to a long, I get swapped nibbles i.e. 21346587)
Thanks to everyone, esp. jraskell for the help.

OH MY WORD! I spent an entire evening searching thru many forum posts. If only I saw this one 1st!

Just shifting them as you go is way easier…

long adc_value = 0;

digitalWrite(ADC_CS,LOW);
for(byte i = 0; i < 4; i++){
  adc_value =<< 8;
  adc_value |= shiftIn(SPI_MISO,SPI_CLK,MSBFIRST);
}
digitalWrite(ADC_CS,HIGH);

And if you are already calling it SPI, why not USE the SPI? :wink:

If you take care to get the order of the bytes correct (not sure about the AVR endianness), then you can cast the array as a pointer to long and dereference it:

adc_value=long(d[]);  // <-- sketch does not compile

adc_value = * (long *) d; // try this instead

An equivalent way to do this is using a union

union foo {
  byte as_array[4];
  long as_long;
}
d;

  d.as_array[0]=0x87; 
  d.as_array[1]=0x65; 
  d.as_array[2]=0x43; 
  d.as_array[3]=0x21;  

Serial.println(d.as_long,HEX);

If you get weird values, just put in one byte to work out if it goes into element 0 or element 3 of the array.