bitRead() in for loop to convert INT to BIN representation

Dear all,
I am writing for help considering bitRead() function. I created a little sketch which I use to convert Int value of max 7 bit to BINary representation saved as string. I was trying to avoid Strings however every approach to this problem I found has a string function incorporated at some of the steps.

My problem is:
When using below sketch Serial.println(strn); prints out seven reversed question marks ⸮⸮⸮⸮⸮⸮⸮ instead of for example 1111111 for int data = 127;
what is wrong with my code?

void setup() {

Serial.begin(9600);

}

void loop() {

int data = 1;                                              //max 127,  max 7 bits
String strn = "0000000";                            //final string has to contain exactly 7 characters


for(int i = 0; i <= 6; i++) {                        // a loop to read bit by bit from "data" variable
  
int b = bitRead(data, i);                             //read bit under "i" number
char c[1];                                                 //a char to store latest bit readout
itoa(b, c, 10);                                           //convert bit readout from int to ascii
strn.setCharAt(6-i, c );           //replace character in "str" string with just read value"c" under "6-i" index
                                                               //6-1 index is to keep correct order of bits.

}

Serial.println(strn);               //print resulting string containing binary representation of "data" variable.

}

You are correct to avoid String objects (capital “S”) in favor of null terminated character arrays. (c strings with a small “s”).

char strn[] = "0000000";

Your character string output array will be null terminated because of the declaration with quotation marks. If you look at sizeof(strn) you will see it equals 8.

The most simple way to convert the numerical 1/0 of bitRead() to the character 1/0 is to add 48 to the value which converts it to ascii. 48 is the ascii value “0”.

Finally, you have to deal with the issue of how a character array is indexed and how a byte value is indexed. The first character in an array is index 0, and it is printed first.
The byte value is ordered with the least significant bit as bit 0, and the most significant as bit 7.

Consequently, as you assign bit values to a character array you need to invert the order.

Your code can be simplified to this

void setup() {
  Serial.begin(9600);
}

void loop() {
  byte data = 33;
  //int data = 1;                                              //max 127,  max 7 bits
  //String strn = "0000000";//final string has to contain exactly 7 characters
  char strn[] = "0000000";
  for (int i = 0; i <= 6; i++) {
    //for (int i = 6; i >= 0; i--) {  // a loop to read bit by bit from "data" variable
    strn[i] = bitRead(data, 6 - i) + 48;
    //int b = bitRead(data, i);                             //read bit under "i" number
    //char c[1];                                                 //a char to store latest bit readout
    //itoa(b, c, 10);                                           //convert bit readout from int to ascii
    //strn.setCharAt(6-i, c );           //replace character in "str" string with just read value"c" under "6-i" index
    //6-1 index is to keep correct order of bits.
  }
  Serial.println(strn);               //print resulting string containing binary representation of "data" variable.
}

cattledog:
You are correct to avoid String objects (capital “S”) in favor of null terminated character arrays. (c strings with a small “s”).

char strn[] = "0000000";

Your character string output array will be null terminated because of the declaration with quotation marks. If you look at sizeof(strn) you will see it equals 8.

The most simple way to convert the numerical 1/0 of bitRead() to the character 1/0 is to add 48 to the value which converts it to ascii. 48 is the ascii value “0”.

Finally, you have to deal with the issue of how a character array is indexed and how a byte value is indexed. The first character in an array is index 0, and it is printed first.
The byte value is ordered with the least significant bit as bit 0, and the most significant as bit 8.

Consequently, as you assign bit values to a character array you need to invert the order.

Your code can be simplified to this

void setup() {

Serial.begin(9600);
}

void loop() {
 byte data = 33;
 //int data = 1;                                              //max 127,  max 7 bits
 //String strn = “0000000”;//final string has to contain exactly 7 characters
 char strn = “0000000”;
 for (int i = 0; i <= 6; i++) {
   //for (int i = 6; i >= 0; i–) {  // a loop to read bit by bit from “data” variable
   strn[i] = bitRead(data, 6 - i) + 48;
   //int b = bitRead(data, i);                             //read bit under “i” number
   //char c[1];                                                 //a char to store latest bit readout
   //itoa(b, c, 10);                                           //convert bit readout from int to ascii
   //strn.setCharAt(6-i, c );           //replace character in “str” string with just read value"c" under “6-i” index
   //6-1 index is to keep correct order of bits.
 }
 Serial.println(strn);               //print resulting string containing binary representation of “data” variable.
}

wow! thank You for ultra fast and professional reply! I am really amazed with such clever way of achieving same goal with so little code. Learning is much more “juicy” when there is someone to point my errors and show me a “right direction”.
Thank You very much cattledog

Even easier would be:

void setup() {

Serial.begin(9600);

}

void loop() {

int data = 1;                                              //max 127,  max 7 bits
char strn[33];                  // big enough for 32 bit unsigned long  Size yours as appropriate

itoa(data, strn, 2);   // itoa with binary as the base

Serial.println(strn);               //print resulting string containing binary representation of "data" variable.

}

Thank You Delta_G for reply. Before I came up with bitRead() i was using Your method, but it was dropping leading zeros. I have to keep specific bit quantity in resulting packet. Yes, packet... now I have to transform string represetation of binarny Value to actual binary Byte which can be sent to next device. Is there a loop time efficient method to do that?

For example1:

Byte data: 127
strn result: ”1111111”
Byte Binary packet: 0B1111111

For example 2:

Byte data: 9
strn result: ”0001001”
Byte Binary packet: 0B0001001

now I have to transform string represetation of binarny Value to actual binary Byte which can be sent to next device.

Why put the value into an intermediate string ?

UKHeliBob:
Why put the value into an intermediate string ?

Because so far it is the only way I found to preserve leading zeroes and maintain specific (7 in this case) char length representation.
Of course my problem posted in first post is part of bigger project.
What I am aiming to do is to have lets say 4 byte variables:

byte var0 = 56 //always represented exactly by 7bit binary value like 0111000
byte var1 = 3 //always represented exactly by 2bit binary value like 11. if var3 is 0, then bin value 00
byte var2 =1 //always represented exactly by 7bit binary value like 0000001
byte var3 = 127 //always represented exactly by 7bit binary value like 1111111

which I have to convert to binary values keeping specific length as per above //comments. Of course above variables are limited to values corresponding to maximum bit count, for example max var1 value is 127 because 127 is exactly 7 bit.
After conversion I have to join all the variables to create one binary variable. However I have to join them in reversed order but maintaining individual variable orders.
uint32_t will be created by var3 followed by var2 followed by var1 followed by var0.

uint32_t joinedvar = 0B11111110000001110111000

do You have any advices how to achieve this goal? thank You for any help!

If you just need a uint32_t in the end, why all the trouble to go to ascii first? Just smack it together in the right way. :slight_smile:

uint32_t joinedvar = ((var3 & 0x7FUL) << 16) | ((var2 & 0x7FUL) << 9) | ((var1 & 0x3) << 7) | (var0 & 0x7F);

As I understand it the OP does not want all of the bits from each of the source variables, in which case a couple of nested for loops with the variables in an array will do the job

byte var0 = 0b00111000;//   7 bits
byte var1 = 0b00000011; //  2 bita
byte var2 = 0b00000001; //  7 bits
byte var3 = 0b01111111; //  7 bits

byte byteValues[] = {var0, var1, var2, var3};

byte bitsToTake[] = {7, 2, 7, 7};

void setup()
{
  Serial.begin(115200);
  uint32_t joinedVar = 0;
  for (int var = 3; var >= 0; var--) //each variable in the array
  {
    for (int bit = bitsToTake[var] - 1; bit >= 0; bit--) //each bit of the variable
    {
      joinedVar = joinedVar << 1;
      bitWrite(joinedVar, 0, bitRead(byteValues[var], bit));
    }
  }
  report(joinedVar);
}

void report(uint32_t value)
{
  for (int bit = 31; bit >= 0; bit--)
  {
    Serial.print(bitRead(value, bit));
  }
  Serial.println();
}


void loop()
{
}

Possible, but I just used a mask to do so :slight_smile:

septillion:
Possible, but I just used a mask to do so :slight_smile:

OK, but I had already written my reply so I wasn't going to waste it !

thank You all for super fast and helpful replies!
Bitwise approach to my problem proposed by Septilion seems to be ultra nice. Hovewer bitwise operations was always a mystery to me. Trying to understand them with help of Septilions code is good lesson. However I came to a problem. Code:

void setup() {
  Serial.begin(9600);
}

void loop() {

byte var0 = 0;        //always represented exactly by 7bit binary value like 0111000 
byte var1 = 0   ;        //always represented exactly by 2bit binary value like 11. if var3 is 0, then bin value 00
byte var2 =0   ;         //always represented exactly by 7bit binary value like 0000001
byte var3 = 0  ;     //always represented exactly by 7bit binary value like 1111111

uint32_t joinedvar = ((var3 & 0x7F) << 16) | ((var2 & 0x7F) << 9) | ((var1 & 0x3) << 7) | (var0 & 0x7F);
  
 
  Serial.println(joinedvar, BIN);               //print resulting string containing binary representation of "data" variable.
}

seems to not work as expected. it prints only I have to be able to create joinedvar out of 23 zeros when needed.

what is wrong? my only idea is that Serial.println(joinedvar, BIN) will not print leading zeroes, because BIN 0 is invisible for print function as it means basically “nothing”. Am I right? does it mean that all 23 zeroes byte is stored under joinedval? if set only value0=1, and rest values to =0 and then I write it to device will it see it as 0b00000000000000000000001?
thanks!

my only idea is that Serial.println(joinedvar, BIN) will not print leading zeroes,

That is true but it does not mean that the zeroes are not there.

Note how the report() function in my code deal with this

That's the same as saying 0 is not the same as 00000 (in normal English). Or 0005 is different then 5. Leading zero's don't mean anything to us as a human :slight_smile: That's why we tend to leave them off :slight_smile:

UKHeliBob, Your report() function is briliant piece of code, thanks!
It makes sense now. Also Septilion, nicely said about zeroes!

I will go back to programming when I come back from work, but for now another question is appearing in mymind.

My device library function expect command send as binary format 0b00000000000000000000001. Communication is done via softwareSerial. Is there a difference for device if I write just (joinedval, BIN) instead of 0b0(...)0 ?
Thanks

I don't understand your last question. Because

(joinedval, BIN)

does nothing.

If used with the print() or println() mothodes of the Stream-class (like Serial) it turns the 'joinedval' into a base-2 ascii representation (and sends it out over Serial). Aka, it will send up to 32 ascii characters over Serial.

So what is that device? And does it expect bytes or ascii?

UKHeliBob, Your report() function is briliant piece of code, thanks!

Really ?

I thought it was just a for loop iterating through the bits of a variable !

Starting since beginning…
I am building my own sensor platform for fast RC EDF aircraft. I am using Jumper T16 transmitter and FR Sky X8R receiver. I am using passthrough telemetry protocol (described in this spreadsheet) which talks with third party telemetry script installed on transmitter. Arduino with attached sensors is connected to smart port on X8R receiver. It is kind of UART transmission with baud rate of 57600.
Spreadsheet description is clear that all parametrs for given sensor id (for example 0x5002 for GPS) are included in single maximum 32bit value.

I am trying to make use of this library. It is designed to work with standard SmartPort telemetry, however if I manually create packet which I send by use of below command it works good. Of course I am able to manually write any packet, however the point is to create variables for sensor data input, then convert each variable to binary representation and merge all variable binary representations into one to write it to receiver by use of below command:

FrskySP.sendData (0x5002, 0b00000000000000000000000000000000); //example data

This command calls below function from FRskySP.cpp:

void FrskySP::sendData (uint16_t id, int32_t val) {
    //this->sendData (id, (uint32_t) val);
   this->sendData (0x10, id, (uint32_t) val);   //mod
}

/*
 * Packet format:
 * content   | length | remark
 * --------- | ------ | ------
 * type      | 8 bit  | always 0x10 at now
 * sensor ID | 16 bit | sensor's logical ID (see FrskySP.h for values)
 * data      | 32 bit | preformated data
 * crc       | 8 bit  | calculated by CRC()
*/

void FrskySP::sendData (uint8_t type, uint16_t id, int32_t val) {
    int i = 0;
	uint16_t crc = 0;
	 uint8_t *u8p;
 
	// type
	FrskySP::sendByte (type, &crc);
	
	// id
	u8p = (uint8_t*)&id;
	FrskySP::sendByte (u8p[0], &crc);
	FrskySP::sendByte (u8p[1], &crc);
	
	// val
	u8p = (uint8_t*)&val;
	FrskySP::sendByte (u8p[0], &crc);
	FrskySP::sendByte (u8p[1], &crc);
	FrskySP::sendByte (u8p[2], &crc);
	FrskySP::sendByte (u8p[3], &crc);

	// crc
	FrskySP::sendByte (0xFF - (uint8_t)crc, NULL);
}

that’s more or less what it is all about…

Considering bitwise operations based on all the advices I created sketch:

void setup() {
 
Serial.begin(9600);
}

void loop() {
 
byte var0 = 0; //4 bit

byte var1 = 0; // 2 bits

byte var2 = 0;  // 1 bit ok

byte var3 = 0; // 7 bits ok

byte var4 = 0; //1 bit ok

byte var5 = 1; // 7 bits

byte var6 = 3;   //2 bits

byte var7 = 25; //7 bits

byte var8 = 1; //1 bit




uint32_t joinedvar = ((var8 & 0x1) << 31) | ((var7 & 0x7F) << 24) | ((var6 & 0x3) << 22) | ((var5 & 0x7F) << 15) | ((var4 & 0x1) << 14) | ((var3 & 0x7F) << 7) | ((var2 & 0x1) << 6) | ((var1 & 0x3) <<4) | (var0 & 0x0F);

for(int i = 0; i <= 31; i++) 
{ 

int b =bitRead(joinedvar, i); 
Serial.print(b);
    
}
Serial.println();
}

when I was testing this sketch by varying byte variables data it was working good until I changed var5 to zero. Serial monitor printed 32 zeroes

00000000000000000000000000000000

which is not what I expected because at least three variables was not set to 0.
My understanding of bitwise shifting is restricted to few hours of learning, that is most probable cause I can’t find an error…

what do You think?

//uint32_t joinedvar = ((var8 & 0x1) << 31) | ((var7 & 0x7F) << 24) | ((var6 & 0x3) << 22) | ((var5 & 0x7F) << 15) | ((var4 & 0x1) << 14) | ((var3 & 0x7F) << 7) | ((var2 & 0x1) << 6) | ((var1 & 0x3) <<4) | (var0 & 0x0F);

uint32_t joinedvar = (((uint32_t)var8 & 0x1) << 31) | (((uint32_t)var7 & 0x7F) << 24) | (((uint32_t)var6 & 0x3) << 22) | (((uint32_t)var5 & 0x7F) << 15) | (((uint32_t)var4 & 0x1) << 14) | ((var3 & 0x7F) << 7) | ((var2 & 0x1) << 6) | ((var1 & 0x3) <<4) | (var0 & 0x0F);

You are bit shifting your bytes into oblivion as the bits are shifted out of the variable to the left. See the discussion here
https://www.arduino.cc/reference/en/language/structure/bitwise-operators/bitshiftleft/

To fix this you recast the variable to be shifted to the size of the result.

There is an other thing to consider which is called “integer promotion”, where the C/C++ language standard and the compiler promote the 8 bit variables to 16 bits with arithmetic operations. The left shift is considered one of these operations, so you would not need to recast a byte for a bitshift << 8 or less.

You can see this here

void setup() {
 Serial.begin(9600);
 byte num = 127;
 int numShifted = num << 8; //is num bitshifted to 0's?
 Serial.print(numShifted,BIN);
}
void loop() {}

Do understand that this:

FrskySP.sendData (0x5002, 0b00000000000000000000000000000000); //example data

is exactly the same as this:

FrskySP.sendData (0x5002, 0b0); //example data

Writing all those zeros doesn't make them exist any more or less. They're always there. Even though the print function doesn't print them, they're there. If you've got a 32 bit number, it's gonna have 32 bits. And all the ones up front will be zero (unless it's negative then they're 1's).

I think you're doing a lot of work to attack a problem that doesn't exist. You've let the output from print fool you into thinking something silly.