I Can't ADD But I Can OR

I've got a pressure sensor. When triggered it transmits a status byte then 3 bytes of pressure data, MSB first. My objective is to take those 3 bytes of pressure data and stuff them into a LONG var (x). Done by: Starting with x=0, x is shifted left by 1 byte and then the next pressure byte is added to x. Do that shift & add 3 times and I've got the pressure data in x. Except it doesn't work. Here's the code:


void loop() {
  Wire.beginTransmission(id);
  int stat = Wire.write(cmd, 3);  // write command to the sensor stat |= 
  Wire.endTransmission();
  delay(10);
  Wire.requestFrom(id, 4);  // will read back 4-byte Sensor data: Status byte + pressure (3 bytes).

  int i;
  long x = 0;
  uint8_t y;
  data[0] = Wire.read();  //Read out and discard Status Reg. (data[] is uint8_t)
  for (i = 1; i < 4; i++) {
    data[i] = Wire.read();    // get next byte from sensor into data[i].
    y = data[i];    // Copy that byte to y.
    x = x<<8 + y;   // Shift x left 8 bits then add y to x.
    Serial.print(y,HEX);   // Print the byte just processed, in HEX.
    Serial.print("  ");
  }

Serial.print(x);   // Print the 24-bit value.
Serial.println();

Here is what we get:
1B 7D 85 0
1B 7F E3 0
1B 7A B7 0
1B 7B 4A 0
1B 7A 89 0
1B 7B 7A 0

However, if we change "x = x<<8 + y;" to "x = x<<8 | y;" we get:
1B 7A 59 1800793
1B 7A 54 1800788
1B 7D CC 1801676
1B 7C FF 1801471
1B 7B 21 1800993
1B 7E A1 1801889
1B 79 EA 1800682

So why does "+" not work but "|" does?

What type is the variable "data"?

If it is a signed type, it will treat the values that have the high bit set as negative numbers. When right-shifted the result you get will be unexpected.

Why not just use y?

Properly declare the appropriate variables as "long" and both approaches will work. I suspect you are using an Arduino where the default int is 16 bits (e.g. Uno R3).

I said in the comments that data[] is uint8_t, same as y.

Not right shifting. Left shift only.

When refined I probably will just use y. I got here after many many trail code pieces trying to figure out my problem.

Yes, an Uno R3.

So, I can't add a uint8_t to a long???

It could be that the compiler is interpreting
x = x<<8 + y
as
x= x<< (8+y)

Try putting in parentheses.

Bingo! That works.

Another example as to why I prefer assembly over anything else. Chalk up another one for the compiler. Thanks DC

Compiler: 23,745
Me: 3

Yes, you can. @dccontrarian is correct.

See C++ Operator Precedence - cppreference.com

As a style note -- and this may cause some disagreement -- I don't like the use of bit-wise operators.

I think you're better off doing explicit math. First, the bitwise operators are implementation-specific, they won't give the same result on different machines. Second, straight math is just simpler, and less depended upon the implementation of the datatype. Ideally your code should work regardless of the underlying data type.

Some will say that bitwise operators are more efficient, I would only worry about that if, after implementation, performance is a problem.

You didn't specify, but I assume the sensor readings are unsigned (positive numbers only). Also, I don't have your sensor so I'm "simulating" the readings. Since Arduinos are Little Endian (note how the array is filled):

void setup() {
  uint8_t sensorData[4];
  uint32_t sensorReading;

  Serial.begin(115200);
  delay(1000);

  // Simulate reading sensor data:
  sensorData[3] = 0x1B;  // Status (discard)
  sensorData[2] = 0x7A;  // MSB
  sensorData[1] = 0x59;  // Middle Byte
  sensorData[0] = 0x18;  // LSB

  sensorReading = 0;
  memcpy(&sensorReading, sensorData, 3);
  Serial.print("Sensor Data: 0x00");
  Serial.print(sensorReading, HEX);
  Serial.print(", ");
  Serial.print(sensorReading);
  Serial.println(" Decimal");
}

void loop() {
}

Result:

Sensor Data: 0x007A5918, 8018200 Decimal

If sensor results are signed, you'd need to extend the sign bit all the way through the MSB of sensorReading

Mostly incorrect. Stick with unsigned integers (of the same size) and bitwise operators work as expected across processors. Right shift of signed integers is implementation-dependent and I always avoid it.

1 Like

That's tossing a hole buncha baby out with the bath water.

Perhaps you'd like to clarify.

a7

And this is one reason why I require parentheses whenever I review code that uses bit shift operators. Actually, I recommend parentheses on anything more complex than + and *. Never hurts to make your intention explicit.

I'm going to grab some popcorn, a bev and flag "watching." Last time I suggested parenthesis on a condition on the forum, I almost lost my firstborn.

2 Likes

Yeah, that whole "real programmers have order of operations memorized for all cases" doesn't hold any water with me.

I like code that works, not code that shows off how smart the programmer was.

1 Like

I'm a fan of parentheses, too, especially after taking a close look at the C++ operator precedence table, some of which seems pretty arbitrary.

Me too. It is a good habit to get into when using different languages, especially ones with C like constructs like Processing (sort of Java like) and Java itself. Much better than restricting syntax to a single language and implementation. It also works with Python.

My c++ compiler flags superfluous brackets...
Very annoying...
It does warn that << has lower precedence (which is handy...).

Division by factors of 2 (or multiplication with factors of 2) is implemented as bitshift by the compiler...
So no need to write bitshifts...
In this case you really intend to shift the info from right to left. So bitshifts reflect the intention...
Keep them...
And maybe remember the precedence thing for next time...

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