Bits and Bytes
Arduino provides a set of built-in functions for manipulating individual bits and bytes. These are essential when working with registers, sensor data, communication protocols, or any situation where you need fine-grained control over binary data. Bits are the smallest unit of data (0 or 1), and a byte is a group of 8 bits.
bit()
The bit() function computes the value of a specific bit position. It returns 2 raised to the power of n, which is the decimal value of bit n when set. Bit positions are zero-indexed, so bit(0) equals 1, bit(1) equals 2, bit(2) equals 4, and so on.
void setup() {
Serial.begin(9600);
for (int i = 0; i < 8; i++) {
Serial.print("bit(");
Serial.print(i);
Serial.print(") = ");
Serial.println(bit(i));
}
}
void loop() {
// nothing to do here
}
This sketch prints the decimal value of each bit position from 0 to 7. The output shows bit(0) = 1, bit(1) = 2, bit(2) = 4, bit(3) = 8, and so on up to bit(7) = 128. You can use bit() together with the bitwise OR operator | to construct a byte with specific bits set.
bitClear()
The bitClear() function clears (sets to 0) a specific bit in a numeric value. It takes two arguments: the variable to modify and the bit position to clear. The function returns the modified value without changing the original variable.
void setup() {
Serial.begin(9600);
byte x = 0b11111111; // all bits set
byte y = bitClear(x, 3); // clear bit 3
Serial.println(x, BIN); // prints 11111111 (unchanged)
Serial.println(y, BIN); // prints 11110111 (bit 3 cleared)
}
void loop() {
// nothing to do here
}
In this example, x starts as 0b11111111 (all bits on). After calling bitClear(x, 3), the returned value y has bit 3 cleared to 0, becoming 0b11110111. The original variable x is not modified.
bitRead()
The bitRead() function reads the value of a specific bit in a number. It returns either 0 or 1 depending on whether the bit at the given position is cleared or set.
void setup() {
Serial.begin(9600);
byte x = 0b10101010;
for (int i = 7; i >= 0; i--) {
Serial.print(bitRead(x, i));
}
Serial.println();
}
void loop() {
// nothing to do here
}
This sketch reads each bit of 0b10101010 from position 7 down to 0 and prints them in order, outputting 10101010. You can use bitRead() to inspect individual flags in a status register or to decode data received from sensors.
bitSet()
The bitSet() function sets (sets to 1) a specific bit in a numeric value. It takes two arguments: the variable to modify and the bit position to set. Like bitClear(), it returns the modified value without altering the original variable.
void setup() {
Serial.begin(9600);
byte x = 0b00000000; // all bits cleared
byte y = bitSet(x, 4); // set bit 4
Serial.println(x, BIN); // prints 0 (unchanged)
Serial.println(y, BIN); // prints 10000
}
void loop() {
// nothing to do here
}
Here, x starts as 0. The call to bitSet(x, 4) returns a new value with bit 4 set, which is 0b00010000 (decimal 16). This function is commonly used to enable specific hardware features by setting bits in control registers.
bitWrite()
The bitWrite() function writes a specific value (0 or 1) to a specific bit in a number. Unlike bitSet() and bitClear() which are fixed operations, bitWrite() lets you choose the bit value dynamically.
void setup() {
Serial.begin(9600);
byte x = 0b00000000;
byte y = bitWrite(x, 2, 1); // write 1 to bit 2
byte z = bitWrite(y, 5, 1); // write 1 to bit 5
Serial.println(z, BIN); // prints 100100
}
void loop() {
// nothing to do here
}
In this example, we start with all bits cleared, then write a 1 to bit 2 (y = 0b00000100), then write a 1 to bit 5 (z = 0b00100100). The bitWrite() function is useful when the bit value comes from a variable โ for example, setting an LED state based on a sensor reading.
highByte()
The highByte() function extracts the high-order (most significant) byte from a two-byte (16-bit) value. For a 16-bit integer, bits 8โ15 form the high byte. This is commonly used when reading data from sensors or communication protocols that split multi-byte values.
void setup() {
Serial.begin(9600);
int value = 0xAABB; // two-byte value
byte high = highByte(value);
Serial.println(high, HEX); // prints AA
}
void loop() {
// nothing to do here
}
Here, the 16-bit value 0xAABB is split: highByte() returns 0xAA (the upper 8 bits). This is often paired with lowByte() to reconstruct data received over I2C or SPI.
lowByte()
The lowByte() function extracts the low-order (least significant) byte from a two-byte (16-bit) value. For a 16-bit integer, bits 0โ7 form the low byte.
void setup() {
Serial.begin(9600);
int value = 0xAABB; // two-byte value
byte low = lowByte(value);
Serial.println(low, HEX); // prints BB
}
void loop() {
// nothing to do here
}
In this example, lowByte(0xAABB) returns 0xBB. Together, highByte() and lowByte() let you pack and unpack 16-bit values for transmission or storage. For example, when reading a 16-bit temperature sensor over I2C, you can combine the two received bytes back into a single integer using (highByte(received) << 8) | lowByte(received).