Binary is a base-2 numbering system using only 0s and 1s to represent electrical states (LOW/OFF and HIGH/ON) in digital circuits and microcontrollers. This binary cheat sheet gives you the exact translations, bitmasks, and logic operations needed to map physical pin voltages to logical data on the bench. What this changes in a real circuit or installation is your ability to read parallel sensors, configure hardware addresses, and manipulate microcontroller port registers directly—bypassing slow, abstracted library functions to achieve deterministic timing. Beginners commonly confuse pure binary with hexadecimal (which is merely a human-friendly shorthand for binary) or Binary-Coded Decimal (BCD), which restricts 4-bit nibbles to represent only the digits 0-9.
The 8-Bit Binary Cheat Sheet Reference Table
When you are writing embedded C or C++ for an Arduino Uno (ATmega328P) or an ESP32, you rarely need to count to 255 in binary. Instead, you need to recognize powers of two (single-bit masks) and nibble boundaries (hex conversions). Keep this table bookmarked for your next logic analyzer session.
| Decimal | Hex | Binary (8-Bit) | Common Application / Bitmask |
|---|---|---|---|
| 1 | 0x01 | 00000001 | Bit 0 mask (LSB) |
| 2 | 0x02 | 00000010 | Bit 1 mask |
| 4 | 0x04 | 00000100 | Bit 2 mask |
| 8 | 0x08 | 00001000 | Bit 3 mask |
| 15 | 0x0F | 00001111 | Lower nibble mask (0-3) |
| 16 | 0x10 | 00010000 | Bit 4 mask |
| 32 | 0x20 | 00100000 | Bit 5 mask |
| 64 | 0x40 | 01000000 | Bit 6 mask |
| 127 | 0x7F | 01111111 | Max 7-bit I2C address |
| 128 | 0x80 | 10000000 | Bit 7 mask (MSB) |
| 240 | 0xF0 | 11110000 | Upper nibble mask (4-7) |
| 255 | 0xFF | 11111111 | All pins HIGH / Pull-up enable |
Worked Example: Bitwise Port Manipulation on an ATmega328P
Let’s look at a real-world scenario where this binary cheat sheet saves you from writing eight separate digitalWrite() commands. Suppose you are using an Arduino Uno and need to update a parallel LCD bus connected to PORTD (digital pins 0 through 7).
The Scenario: You want to set Pin 5 and Pin 2 HIGH, and force Pin 3 LOW, without altering the current state of the other pins on that port.
Step 1: Identify the current port state.
Assume a logic analyzer shows PORTD is currently reading 11001010 (Pins 7, 6, 3, and 1 are HIGH).
Step 2: Set Pins 5 and 2 HIGH using Bitwise OR (|).
To force specific bits HIGH without touching the rest, you OR the current state with a mask containing 1s only at your target positions. Pin 5 is 00100000 (0x20) and Pin 2 is 00000100 (0x04). Combined, the mask is 00100100.
11001010 (Current PORTD)
| 00100100 (Mask for Pins 5 & 2)
----------
11101110 (New state: Pins 5, 3, 2, 1 are HIGH)
Step 3: Force Pin 3 LOW using Bitwise AND (&) and NOT (~).
To force a bit LOW, you AND the port with a mask that has a 0 at the target bit and 1s everywhere else. Pin 3 is 00001000. Applying the NOT operator (~) flips it to 11110111.
11101110 (State from Step 2)
& 11110111 (NOT mask for Pin 3)
----------
11100110 (Final state: Pin 3 is now LOW)
In embedded C, this entire operation is executed in two CPU cycles:
PORTD |= 0b00100100; // Set pins 5 and 2
PORTD &= ~0b00001000; // Clear pin 3
Where You Meet Binary in Practice
You will run into raw binary and bitwise logic constantly when moving beyond basic blink sketches. Here are the three most common jobsite and bench encounters:
1. I2C Hardware Addressing
Modules like the MCP23017 I/O expander or the PCF8574 use physical pins (usually labeled A0, A1, A2) to set their I2C bus address. These pins act as a 3-bit binary number appended to a base address. If the base address is 0x20 (00100000) and you wire A2=HIGH, A1=LOW, A0=HIGH, the binary offset is 101 (Decimal 5). The final address becomes 0x25. Understanding this prevents you from blindly running I2C scanner scripts when a sensor simply has the wrong jumper configuration.
2. Shift Registers (74HC595)
When expanding output pins via SPI or bit-banging, shift registers take serial binary data and output it in parallel. The shiftOut() function pushes bits one by one. If you need to turn on the 4th and 8th relays on an 8-channel board, you don't send decimal "136"; you conceptually send 10001000 (0x88), shifting out the Most Significant Bit (MSB) first.
3. DMX512 and Industrial DIP Switches
Lighting controllers and industrial PLC input cards use 9-pin or 10-pin DIP switches to set device addresses. These are pure binary representations. Switch 1 represents 1, Switch 2 represents 2, Switch 3 represents 4, and so on. To set a DMX address of 45, you flip Switch 6 (32), Switch 4 (8), Switch 3 (4), and Switch 1 (1). 32 + 8 + 4 + 1 = 45.
Common Confusions: Hex, BCD, and 7-Bit I2C
0xF is just 1111. Microcontrollers do not "understand" hex; the compiler translates it to binary before flashing the silicon.
Binary-Coded Decimal (BCD): Often found in real-time clock (RTC) modules like the DS3231. In pure binary, decimal 25 is 00011001. In BCD, the number is split into two nibbles: the tens digit (2) and the ones digit (5), resulting in 00100101 (0x25). If you read a BCD register and treat it as pure binary, your time calculations will drift wildly.
The 7-Bit vs. 8-Bit I2C Trap: The I2C specification defines addresses as 7-bit, meaning the maximum address is 127 (01111111). However, many legacy datasheets (and some silicon vendors) list the address as an 8-bit byte, where the 8th bit is the Read/Write flag. If a datasheet claims the address is 0xA0 (10100000), the actual 7-bit address you pass to the Arduino Wire library is 0x50 (shifted right by one bit). Always consult the NXP I2C-bus specification to verify how the vendor formats their addressing tables.
Frequently Asked Questions (Binary Cheat Sheet)
How do I convert a decimal number to binary without a calculator?
Use the subtraction method with powers of two. Write down the binary place values: 128, 64, 32, 16, 8, 4, 2, 1. If your number is 53, ask: "Does 64 fit into 53?" No (write 0). "Does 32 fit?" Yes (write 1, subtract 32, remainder is 21). "Does 16 fit into 21?" Yes (write 1, remainder 5). "Does 8 fit?" No (0). "Does 4 fit?" Yes (1, remainder 1). "Does 2 fit?" No (0). "Does 1 fit?" Yes (1). The result is 00110101. This mental model is much faster on the bench than trying to do modulo-2 division in your head.
What does the 0b or 0x prefix mean in microcontroller code?
These prefixes tell the C/C++ compiler how to interpret the literal number you typed. 0b forces the compiler to read the following digits as base-2 binary (e.g., 0b00000011 equals 3). 0x forces base-16 hexadecimal (e.g., 0x0F equals 15). If you omit the prefix and just type 10, the compiler assumes decimal ten, which in binary is 00001010, not 00000001 and 00000000. Mixing these up is the number one cause of "my GPIO pin isn't toggling" bugs in embedded systems.
Why do my I2C sensor addresses in the datasheet not match the Wire scanner results?
This happens because of the 7-bit vs. 8-bit addressing confusion mentioned earlier, combined with how the Wire.h library handles the Read/Write bit. The Arduino Wire library expects strictly 7-bit addresses. If your sensor's hardware address pins are set to 000, the base 7-bit address might be 0x40. However, the physical bus transmits an 8-bit byte: the 7-bit address shifted left by one, with the LSB acting as the Read (1) or Write (0) flag. Therefore, a write command to 0x40 actually puts 10000000 (0x80) on the physical SDA line. If you are debugging with an oscilloscope or logic analyzer, you must mentally shift the captured 8-bit hex values right by one bit to match your Arduino code.






