Getting individual bits from bytes, and vice versa

I have these two functions that work, but they don't seem like the best way to go about the task. Especially the second function, byteToBits.

Essentially, given 8 boolean variables, I want to consider them as bits and convert them to a single byte, and then reverse the process.

byte bitsToByte(byte a, byte b, byte c, byte d, byte e, byte f, byte g, byte h)
{
  return ((a==true)*128 + (b==true)*64 + (c==true)*32 + (d==true)*16 + (e==true)*8 + (f==true)*4 + (g==true)*2 + (h==true));
}

void byteToBits(byte input, byte &a, byte &b, byte &c, byte &d, byte &e, byte &f, byte &g, byte &h)
{
  if(input>=128)
  {
    input -= 128;
    a = true;
  }
  if(input>=64)
  {
    input -= 64;
    b = true;
  }
  if(input>=32)
  {
    input -= 32;
    c = true;
  }
  if(input >= 16)
  {
    input -= 16;
    d = true;
  }
  if(input>=8)
  {
    input -= 8;
    e = true;
  }
  if(input>=4)
  {
    input -= 4;
    f = true;
  }
  if(input>=2)
  {
    input -= 2;
    g = true;
  }
  if(input>=1)
  {
    h = true;
  }
}

Again, it seems to work so far, but I'm wondering how someone else might do the same task more efficiently/condensed.

The standard library has std::bitset for this purpose.

This is included out of the box for ARM and Espressif boards, just #include .

You can use this port if you're using an AVR: Arduino Helpers.
Use #include <Arduino_Helpers.h> and #include <AH/STL/bitset>.

If you want to do it yourself, look into the bitwise AND and OR operators, as well as the bit shift operator.

Pieter

The standard Arduino core library provides some user friendly functions for this sort of thing:

I'd learn the basics provided by the language first: & | ~ ^ << >>
Everything else is built from these.

there is an official function called bitRead() that does this.
its similar to what you are doing. what you are doing is fine but this might ensure the most profficient way.

// prints the  individual bits inside 123
byte b = 123;

byte i = 0;
while(i<8){

Serial.print(bitRead(b,i));

i++;}

taterking:
there is an official function called bitRead() that does this.

It's neither "official" nor a function. It's a macro composed of the basic operators of the language:

#define bitRead(value, bit) (((value) >> (bit)) & 0x01)

As I said, I think it's important to learn the basics first.

gfvalvo:
It's neither "official"

This is the Arduino forum. If something is part of the "Arduino Language", it's official.

gfvalvo:
nor a function

Do you really think you being pedantic is productive in helping a beginner accomplish their goal in this case?

gfvalvo:
As I said, I think it's important to learn the basics first.

Yes, you already said it. We don't need it twice. You are welcome to offer your advice, others are welcome to offer theirs.

3 Likes

1. This is how it goes to form byte value out of bit values:

void setup()
{
  Serial.begin(9600);
  byte x = bitsToByte(1, 0, 1, 0, 1, 0, 1, 0);
  Serial.println(x, BIN); //shows: 10101010
  Serial.print(x, HEX);   //AA
}

void loop()
{
  
}

byte bitsToByte(bool a, bool b, bool c, bool d, bool e, bool f, bool g, bool h)
{
  return ((a == true) * 128 + (b == true) * 64 + (c == true) * 32 + (d == true) * 16 + (e == true) * 8 + (f == true) * 4 + (g == true) * 2 + (h == true));
}

2. This is how it goes to form bit values from byte value.

bool myBitArray[8];

void setup()
{
  Serial.begin(9600);
  bytesTobit(0x3A);
  for (byte j = 0; j < 8; j++)
  {
    Serial.print(myBitArray[j], BIN); //shows: 00111010
  }
}

void loop()
{

}

void bytesTobit(byte x)
{
  for (byte i = 0; i < 8; i++)
  {
    myBitArray[7 - i] = bitRead(x, i);
  }
}

GolamMostafa:
1. This is how it goes to form byte value out of bit values:

void bytesTobit(byte x)

{
 for (byte i = 0; i < 8; i++)
 {
   myBitArray[7 - i] = bitRead(x, i);
 }
}

Having all of my "bits" in one array makes them a bit easier to work with, but I can't figure out how to temporarily put my variables in the array and still have them change the value.

void byteToBits(byte inputByte, byte &a, byte &b, byte &c, byte &d, byte &e, byte &f, byte &g, byte &h)
{
  byte bitArray[] = {a, b, c, d, e, f, g, h};
  for(byte i = 0; i < 8; i++)
  {
    bitArray[7-i] = bitRead(inputByte, i);
  }
}

This doesn't actually update the values of a-h. I could go line by line for each reference parameter, ie

a = bitRead(inputByte, 7);
b = bitRead(inputByte, 6);

etc. But I like the cleanliness of the above code.

This sounds like an XY problem. What do you actually want to do?
What is the point of using separate variables? That's just a waste of space and doesn't help with readability.

There is no reason to reinvent the wheel here, just use std::bitset:

#include <Arduino_Helpers.h>
#include <AH/STL/bitset>

void setup() {
  Serial.begin(115200);

  uint8_t data_in = 0b01011001;
  // Create the bit set from the bits of data_in
  std::bitset<8> bits = data_in;
  // Manipulate the bits
  bits[7] = 1;
  // Get the value of the bits
  bool bit4 = bits[4];
  // Print the bits
  Serial << bits << endl;
  // Convert the bits to a byte again
  uint8_t data_out = bits.to_ulong();
}

void loop() {}

PieterP:
This sounds like an XY problem. What do you actually want to do?
What is the point of using separate variables? That's just a waste of space and doesn't help with readability.

My 8 variables could be the states of 5 magnet sensors, a button, and two random other boolean values. Or any other combination of 8 (or less) variables that wouldn't normally make sense to be together in an array. The point is just to make a clean function that condenses 8 bytes (effectively bits for my purposes) into a single byte. The reason for THAT is to reduce the size of the message structure in an rs485 network (I'm specifically using the Rolling Master code).

So the following works and is probably what I will use.

void byteToBits(byte inputByte, byte &a, byte &b, byte &c, byte &d, byte &e, byte &f, byte &g, byte &h)
{
  a = bitRead(inputByte, 7);
  b = bitRead(inputByte, 6);
  c = bitRead(inputByte, 5);
  d = bitRead(inputByte, 4);
  e = bitRead(inputByte, 3);
  f = bitRead(inputByte, 2);
  g = bitRead(inputByte, 1);
  h = bitRead(inputByte, 0);
}

I was mostly trying to understand how I would go about putting a through h in an array for the sake of following Golam's example.

Fair enough.

There's no real way to put them in an array (unless you pass them as an array, of course).
The reason is that you can't have an array of references. You could use an array of pointers, but that wouldn't be an improvement over just writing the same code 8 times, if you ask me.
The reason your code in #8 didn't work is because you create copies of a-h, and you update those copies, not the originals.

Well, you could do something like this, using recursion and variadic arguments. It's shorter and with less repetitions, but I don't think it's any more readable ...

template <class... Args>
void byteToBits(byte data, bool &bit0, Args&... otherBits) {
  bit0 = data & 1; // check if the first bit is set
  byteToBits(data >> 1, otherBits...); // recursive call
}

void byteToBits(byte data, bool &bitN) { // base case
  bitN = data & 1;
}

void setup() {
  Serial.begin(115200);
  byte data = 0b10011010;
  bool bit0, bit1, bit2, bit3, bit4, bit5, bit6, bit7;
  byteToBits(data, bit0, bit1, bit2, bit3, bit4, bit5, bit6, bit7);
  Serial.println(bit0);
  Serial.println(bit1);
  Serial.println(bit2);
  Serial.println(bit3);
  Serial.println(bit4);
  Serial.println(bit5);
  Serial.println(bit6);
  Serial.println(bit7);
}

void loop() {}

If you're set against an array, and the input bytes are really booleans (0 or 1 only) you can do:

byte bitsToByte(byte a, byte b, byte c, byte d, byte e, byte f, byte g, byte h)
{
  byte result =          a;
  result = (result<<1) + b;
  result = (result<<1) + c;
  result = (result<<1) + d;

  result = (result<<1) + e;
  result = (result<<1) + f;
  result = (result<<1) + g;
  result = (result<<1) + h;
}

This is probably much faster than individual bitWrite() calls, but if efficiency is a concern you should probably worry that passing 8 arguments to a subroutine is probably "expensive."

There's a similar method for going the other direction.
See Horner's Method

Recursive version of westfw's function:

template <class... Args>
byte bitsToByte(bool bit0, Args... otherBits) {
  return bit0 + (bitsToByte(otherBits...) << 1);
}

byte bitsToByte(bool bitN) {
  return bitN;
}