bit shifting, unsigned long and int convertions

Hello guys,

I've been trying to pack 3 rotations (ranging from 0 - 360) and 3 button states into a unsigned long variable. This worked great, and to make it more clear, here is a visual representation:

bits stored in a 32 bit unsigned long. I mapped the bits with square brackets, and from right to left:

  • 3 bits for 3 button states, 1 is on, 0 is off
  • 9 bits for Z axis (0-360 degrees), and 2 other groups for Y and X

00[000000 000][00000 0000][0000 00000][000]

I used this code to map those values:

where x, y and z are int values, and packedInfo is an unsigned long value

       x = (ypr[0] * 180 / M_PI);
       if( x < 0) x += 360;

       y = (ypr[1] * 180 / M_PI);
       if( y < 0) y += 360;
       
       z = (ypr[2] * 180 / M_PI);
       if( z < 0) z += 360;

       packedInfo = (x << 21UL | 
                     y << 12UL | 
                     z << 3 | 
                    (digitalRead(5) == LOW ? 1 : 0) << 2 | 
                    (digitalRead(6) == LOW ? 1 : 0) << 1 | 
                    (digitalRead(7) == LOW ? 1 : 0));

Now I want to unpack those values, and this is where everything goes wrong. First I had problems assigning the x and y values by bitshifting, but this was due to a problem with 21 and 12 (bitshift values for x and y) being normal integers but those are 16 bit, while I want to assign it to a 32 bit unsigned long, so the data got lost. I think I fixed this by converting the number to a unsigned long by adding 'UL' to it, altho I'm not 100% sure this works.

So, to unpack these values, I tried this piece of code:

        unsigned long mask = 511;
        
        Serial.print("x1: ");
        Serial.print(x);
        
        Serial.print(" y1: ");
        Serial.print(y);
        
        Serial.print(" z1: ");
        Serial.print(z);

        Serial.println("");
        
        Serial.print("x: ");
        int xResult = packedInfo >> 21UL;
        Serial.print((unsigned long)xResult);
        
        Serial.print(" y: ");
        int yResult = packedInfo >> 12UL & mask;
        Serial.print((unsigned long)yResult);
        
        Serial.print(" z: ");
        int zResult = packedInfo >> 3 & mask;
        Serial.print((unsigned long)zResult);
        Serial.println("");

Which works perfectly for my button states, and Z axis, but y and x (which are the bits packed at the most elft side of the unsigned long) are number bigger than 360. X mostly is around 2047 and y around 511.

I think this has something to do with converting datatypes, from unsigned to signed ? Would be nice if someone could explain my struggle here!

Thanks a lot :slight_smile:

   packedInfo = ((uint32_t)x << 21 |
                     (uint32_t)y << 12 |
                     (uint32_t)z << 3 |
                    (digitalRead(5) == LOW ? 1 : 0) << 2 |
                    (digitalRead(6) == LOW ? 1 : 0) << 1 |
                    (digitalRead(7) == LOW ? 1 : 0));

Yes, this works!

Could you explain why this works with an unsigned int, but not with an unsigned long?

uint32_t is "unsigned long".