Split up bits from within a byte

I am trying to send information over beacon which is limited to a message of 2 bytes. Lets say I want the information in byte 1 to include 2 booleans, and 2 numbers in the range of 0-7.

How would I split that information up so that it is split like this [00000000] where the

  • first split shown as the black bits consist of a number 0-7
  • the second split shown as the blue bits consist of a number 0-7
  • the third split shown as the green bit consist of a boolean
  • the forth split shown as the red bit consist of a boolean.

I figured out how to put this information together within the packet sent by arduino 1 by using this snippet line

secsSince1900 = highWord << 16 | lowWord;

from the example on NTP udp, but I haven't been able to find out how to separate what I need on the arduino2 that reads the info once I put it together.

Any guidance will be greatly appreciated.

 byte byte1 = ((num1 & 7) << 5) | ((num2 & 7) << 2) | (bool1 ? 0x02 : 0x00) | (bool2 ? 0x01 : 0x00) ;

Anding with 7 ensures the numbers don't leak to the left, the left-shifts position the bits, and the conditional
expressions convert booleans to bit patterns (expressed in hexadecimal). All the fields are ORed
together with |

Separation is the exact inverse:

  byte num1 = (byte1 >> 5) & 7 ;
  byte num2 = (byte1 >> 2) & 7 ;
  boolean bool1 = (byte1 & 0x02) != 0 ;
  boolean bool2 = (byte1 & 0x01) != 0 ;

You want to perform what's called bit masking using the bitwise operators. For example, suppose the base number is 11. Its bit pattern is:

000001011

Using the bitwise AND operator:

byte mask = 1;
byte redBit = base & 0x01;

which becomes:

  • 000001011 // base*
    & 000000001 // mask
  • ---------------*
  • 000000001 // redBit*

If you want the green bit:

  • 000001011 // base*
    & 000000010 // mask = 2
  • ---------------*
  • 000000010 // greenBit*

Just Google the bitwaise operators and you'll see what I mean

MarkT

Thank you very much for that clear and short example.

Just to make sure I understand how this works, if I wanted to split the information up so that it is split like this [00000000] where the

  • first split shown as the blue bits consist of a number 0-15
  • the second split shown as the green bits consist of a number 0-7
  • the third split shown as the red bit consist of a boolean.

The I would I write it this way?

byte byte1 = ((num1 & 15) << 5) | ((num2 & 7) << 2) | (bool1 ? 0x02 : 0x00);

and separate it this way?

byte num1 = (byte1 >> 5) & 15 ;
byte num2 = (byte1 >> 2) & 7 ;
boolean bool1 = (byte1 & 0x02) != 0 ;

econjack

I remember how the operators work from reading a programming book a while back, I haven't quite figured out what the bitwise AND operator has to do with the code. I think you are explaining what the & symbol in (byte1 >> 2) & 7 ; does so that it knows the rest of the bits except the ones that are in the byte of interest are looked at. And that is why you list the highest possible number of the bits that are looked at so it knows that all the 1 bits in that section are to be looked at?

It would have been a little clearer if you had demonstrated the example to look at a number in the range of 1 to 7 rather than a boolean, but I think I understand the concept. Correct me if I have something mixed up below.

You want to perform what's called bit masking using the bitwise operators. For example, suppose the base number is 165. Its bit pattern is:

10100101

Using the bitwise AND operator:

byte mask = 240;

byte blueInt = base & 0x11110000;

which becomes:

10100101 // base
& 11110000 // mask

10100000 // blueInt

and because we tell it to go over 5 with byte num1 = (byte1 >> 5) & 15 ; it knows to ignore the 5 0's so instead of being 101000000 which is 240, it knows that it is just 1010 which is Ten.

If you want the green int:

byte GreenInt = base & 0x0000110;

10100101 // base
& 00001110 // mask = 2

00000100 // greenInt

and because we told it go over 2 in this code
byte num2 = (byte1 >> 2) & 7 ;

it knows to ignore the first 0 so instead of being 00000100 which is four it knows it is just 0000010 which is two.

This would have been a good example to add to the examples that come pre-loaded when you download the compiler, it not only teaches binary but explains some really interesting things that aren't explained in any of the examples you can easily find.

There is a chance that I misunderstood something in the logic, if so please point it out.

Thomas499:
It would have been a little clearer if you had demonstrated the example to look at a number in the range of 1 to 7 rather than a boolean, but I think I understand the concept.

I used a byte, not a boolean. Since a byte is unsigned, it can assume values 0-255. A boolean only has True or False states and my second example set the mask to 2, which would not be a valid boolean value in the strict sense.

Let the compiler do the work...

typedef struct
{
  uint8_t red:1;
  uint8_t green:1;
  uint8_t blue:3;
  uint8_t black:3;
}
packet_t;

packet_t packet;

void setup() 
{
  packet.black = 4;
  packet.blue = 3;
  packet.green = false;
  packet.red = false;

  Serial.begin( 250000 );
}

void loop() 
{
  Serial.print( packet.black );
  Serial.print( packet.blue );
  Serial.print( packet.green );
  Serial.print( packet.red );
  delay( 1000 );
}

Thomas499:
[00000000]

Usually we put the bits from high on the left to low on the right, same way we write decimal numbers.

high digit ---> 63 <--- low digit

high bit ---> 00111111 <--- low bit

@GoForSmoke was your post meant for me or @Thomas499?

Hey, no way you don't know that. I didn't quote you either.

Thomas, it will make a difference, and you can check these kinds of things easily in setup().

void setup() {
  // put your setup code here, to run once:
  Serial.begin( 115200 );

  int x = 175; 
  Serial.print( "decimal " );
  Serial.print( x, DEC );
  Serial.print( " = binary " );
  Serial.println( x, BIN );

  x = 0b11110101;
  Serial.print( "binary " );
  Serial.print( x, BIN );
  Serial.print( " = decimal " );
  Serial.println( x, DEC );

  Serial.println( "bit order makes a difference in the code!" );
}

void loop() {
  // put your main code here, to run repeatedly:
}

decimal 175 = binary 10101111
binary 11110101 = decimal 245
bit order makes a difference in the code!

Let the compiler do the work...

In your example it shows how to make the compiler split up the individual uint8_t, but I need to split that uint8_t into two different numbers.

The first data split is three bits
The second data split is four bits

I have to do this because there is very little data that I can send through beacon so I have to compress it as much as I can, so it doesn't make since to use 8 bits for a 3 bit number.

Is there a way to get the compiler to do that kind of work? I tried using uintt1_t; uint2_t; uint4_t and so on but they didn't work.

Also, if I wanted to make a keyword file so that or now on uint8_t would look like int in the terms of it showing that the computer recognizes it as a a keyword, would that mean I would have to make a library and include the library or does the IDE recognize the keyword files regardless if you include the library in the sketch?

The colours in the IDE are just colours. It doesn't tell you if the compiler is going to recognise the identifiers.

The point with the ":3" variables in the struct is that they only use 3 bits. Since there's only 8 bits used in the struct, the total size will be 1 byte. You can check this with the sizeof() operator.

A little editing after Morgan's post after this one....

typedef struct
{
  uint8_t red:1;
  uint8_t green:1;
  uint8_t blue:3;
  uint8_t black:3;
}
packet_t;

packet_t packet;

void setup()
{
  packet.black = 4;
  packet.blue = 3;
  packet.green = false;
  packet.red = true;

  Serial.begin( 250000 );

  Serial.print( "black = " );
  Serial.println( packet.black );
  Serial.print( "blue = " );
  Serial.println( packet.blue );
  Serial.print( "green = " );
  Serial.println( packet.green );
  Serial.print( "red = " );
  Serial.println( packet.red );
  Serial.println( );
  
  Serial.print("The struct occupies ");
  Serial.print(sizeof(packet_t));
  Serial.print(" bytes.");
  Serial.println();

}

void loop()
{
}

black = 4
blue = 3
green = 0
red = 1

The struct occupies 1 bytes.

That is one byte, 8 bits, divided up so that 1 bit is named red, 1 named green, 3 bits named blue and 3 bits named black.

It fits in 1 byte and you can get any part by name.

If you had run the given code and messed with it then you should have seen it, did you load it and run it?

  Serial.print("The struct occupies ");
  Serial.print(sizeof(packet_t));
  Serial.print(" bytes.");
  Serial.println();

Thomas, it will make a difference, and you can check these kinds of things easily in setup().

I am trying to figure out why this code doesn't work which is what I tried to modify from your example

Serial.print("The struct occupies ");
Serial.print(sizeof(packet_t));
Serial.println(" bytes.");
Serial.println(packet_t,BIN); /* look at binary of whole byte, which is the format that the information will come in from the beacon. */
Serial.println();

The point with the ":3" variables in the struct is that they only use 3 bits. Since there's only 8 bits used in the struct, the total size will be 1 byte. You can check this with the sizeof() operator.

oh, I thought that the unit8_t part mean that each was 8 bits.

That is one byte, 8 bits, divided up so that 1 bit is named red, 1 named green, 3 bits named blue and 3 bits named black.

I thought I understood the logic of this until I saw

black = 4
blue = 3
green = 0
red = 1

The struct occupies 1 bytes.

if black only has 3 bits, how is it storing the number 4?

If you had run the given code and messed with it then you should have seen it, did you load it and run it?

I did. But that black 4 really confused me. black holds only 3 bits, how does it hold a 4?

May be the end of line character or eOF at the end of string

Thomas499:
I thought I understood the logic of this until I saw if black only has 3 bits, how is it storing the number 4?

The maximum unsigned integer value that can be stored in 3 bits is...

Thomas499:
oh, I thought that the unit8_t part mean that each was 8 bits.

Thomas499:
But that black 4 really confused me. black holds only 3 bits, how does it hold a 4?

The three bits carry the values 1, 2 and 4, corresponding to 20, 21 and 22.
The value is their sum, so the range is from 0 to 7 or 23-1.

I did. But that black 4 really confused me. black holds only 3 bits, how does it hold a 4?

Can't believe I said that. I don't know what I was thinking there.

I am trying to figure out why this code doesn't work which is what I tried to modify from your example

Serial.print("The struct occupies ");
Serial.print(sizeof(packet_t));
Serial.println(" bytes.");
Serial.println(packet_t,BIN); /* look at binary of whole byte, which is the format that the information will come in from the beacon. */
Serial.println();

Also, does uint8_t mean reserve 8 bits?

I thought I understood the logic of this until I saw

black = 4
blue = 3
green = 0
red = 1

The struct occupies 1 bytes.

if black only has 3 bits, how is it storing the number 4?

Because in 3 bits you can store 0 to 7; 000, 001, 010, 011, 100, 101, 110, 111 and 0b100 == 4.

Read the code, it stored that value to black, that's what the = in the printout is for, it's the value.