The binary system is a base-2 numbering scheme that uses only two digits, 0 and 1, to represent all data, instructions, and logic states in digital electronics. When you ask how does the binary system work on a workbench, you aren't just looking at abstract math; you are looking at the physical reality of voltage thresholds, memory registers, and microcontroller pin states. Every line of C++ you flash to an ESP32 and every I2C signal you probe with an oscilloscope is ultimately governed by this two-state logic.
The Core Mechanism: Base-2 Math on the Workbench
In our everyday decimal (base-10) system, each column represents a power of 10 (ones, tens, hundreds). In binary, each column represents a power of 2, doubling as you move from right to left. The rightmost bit is the Least Significant Bit (LSB) representing $2^0$ (1), and the leftmost bit in an 8-bit byte is the Most Significant Bit (MSB) representing $2^7$ (128).
Let's map a real sensor reading of 173 to an 8-bit binary register. We subtract the highest possible powers of 2:
- 128 ($2^7$): 173 - 128 = 45. (Bit 7 = 1)
- 64 ($2^6$): 45 is less than 64. (Bit 6 = 0)
- 32 ($2^5$): 45 - 32 = 13. (Bit 5 = 1)
- 16 ($2^4$): 13 is less than 16. (Bit 4 = 0)
- 8 ($2^3$): 13 - 8 = 5. (Bit 3 = 1)
- 4 ($2^2$): 5 - 4 = 1. (Bit 2 = 1)
- 2 ($2^1$): 1 is less than 2. (Bit 1 = 0)
- 1 ($2^0$): 1 - 1 = 0. (Bit 0 = 1)
Reading from MSB to LSB, the decimal value 173 is written in binary as 10101101. In C++ or Arduino IDE, you would write this as 0b10101101 or in hexadecimal as 0xAD.
What Binary Actually Changes in Your Circuit
Binary is not just a software concept; it dictates the physical design of silicon logic gates and how we wire microcontrollers. In a real circuit, a binary '0' and '1' map directly to specific voltage ranges defined by the logic family (like CMOS or TTL).
For a standard 3.3V CMOS device (like an ESP32 or a modern 3.3V shift register), the binary states change the circuit's behavior based on strict threshold voltages:
- Logic LOW (0): The voltage must be below the $V_{IL}$ (Voltage Input Low) threshold, typically 0.3 × VDD. For a 3.3V system, any voltage below 0.99V is guaranteed to be read as a binary 0.
- Logic HIGH (1): The voltage must exceed the $V_{IH}$ (Voltage Input High) threshold, typically 0.7 × VDD. Any voltage above 2.31V is guaranteed to be read as a binary 1.
If your signal sits at 1.5V, it falls into the 'undefined' region. The microcontroller might read it as a 0, a 1, or oscillate wildly between the two, causing phantom interrupts. Understanding this physical reality is what separates a software developer from an embedded hardware engineer.
Where You Meet This in Practice
You will interact with raw binary constantly when working outside the safety of high-level Arduino libraries. Here is where base-2 math directly impacts your build:
- Bitwise Masking: When you need to change a single configuration bit in a microcontroller's hardware register without altering the other 7 bits, you use binary AND (
&), OR (|), and NOT (~) operators. - I2C Addressing: I2C peripherals use 7-bit binary addresses. If a sensor's datasheet says the address is
0x68(binary1101000), you must understand that the physical I2C bus shifts this left by one bit to make room for the Read/Write bit, resulting in11010000(0xD0) on the wire. - Shift Registers: When daisy-chaining 74HC595 shift registers to drive 16 LEDs, you are literally pushing a 16-bit binary string out of a single GPIO pin, one clock cycle at a time.
Bench Walkthrough: The MCP23017 Configuration Bug
Theory is clean; the workbench is messy. Here is a real-world scenario where a misunderstanding of binary bit-ordering causes a hardware failure.
Setup: You are using a Microchip MCP23017 16-bit I/O expander to add more pins to your ESP32. You wire 4 LEDs to Port A (GPA0 through GPA3) and 4 pushbuttons to Port B (GPB0 through GPB3). You need to configure the IODIRA register to set Port A as outputs and Port B as inputs.
The Numbers: In the IODIR register, a binary 0 sets a pin as an output, and a 1 sets it as an input. You want GPA0-GPA3 to be outputs (0) and GPA4-GPA7 to be inputs (1). You write the configuration byte as 0b11110000 (Hex 0xF0).
The Outcome: You upload the code. The LEDs on GPA0 through GPA3 remain completely dark. Probing the pins with a multimeter shows them floating at 1.6V. The pushbuttons on Port B work fine.
What Went Wrong: You fell victim to the Left-to-Right reading trap. When you wrote 0b11110000, you assumed the leftmost bits mapped to GPA0-GPA3. In reality, binary strings map from right-to-left (LSB to MSB). Bit 0 is GPA0. By sending 0xF0, you actually configured GPA4-GPA7 as outputs and GPA0-GPA3 as high-impedance inputs. The correct byte to make GPA0-GPA3 outputs and GPA4-GPA7 inputs is 0b00001111 (Hex 0x0F).
Common Confusions: Binary vs. Hex vs. Logic Levels
When troubleshooting digital circuits, hobbyists frequently conflate three distinct concepts. Clearing these up will save you hours of oscilloscope debugging.
- Confusing Binary with Hexadecimal: Hex (base-16) is just a human-friendly shorthand for binary. One hex digit perfectly represents four binary bits (a nibble).
0xAis just1010. The microcontroller never 'sees' hex; it only sees the underlying binary voltages. - Confusing Binary Values with Logic Voltages: A binary '1' on a 5V Arduino Uno means ~5V. A binary '1' on a 3.3V ESP32 means ~3.3V. If you connect a 5V output directly to a 3.3V ESP32 input, you are forcing a binary '1' that exceeds the absolute maximum ratings of the silicon, which will permanently brick the GPIO pin via latch-up or gate oxide breakdown.
- Confusing Active-High with Active-Low: A binary '1' doesn't always mean 'ON'. Many microcontroller reset pins and interrupt lines are active-low, meaning a binary '0' (0V) triggers the action, while a binary '1' (VDD) is the idle state.
FAQ: Binary System in Embedded Design
Why do we use 8-bit bytes instead of 10-bit or 12-bit?
Early computing history settled on 8 bits because it is a power of 2 (making binary math clean) and it provides 256 unique states, which is exactly enough to map the entire ASCII character set and leave room for control codes. Hardware memory architectures are built around powers of 2 (8, 16, 32, 64) because binary address decoding relies on clean bitwise splits.
How do I read a binary number off an oscilloscope?
Set your trigger to the clock line. For every rising edge of the clock, look at the data line. If the voltage is high (above $V_{IH}$), write down a 1. If it is low (below $V_{IL}$), write down a 0. Read the bits in the order dictated by the protocol (MSB-first for SPI, LSB-first for I2C) to reconstruct the byte.
What happens if I mix up MSB and LSB in my code?
Your data will be mirrored. For example, the binary byte 00011000 (24) reversed becomes 00011000 (a palindrome), but 00000011 (3) reversed becomes 11000000 (192). This is the most common cause of 'garbage data' when interfacing with SPI displays or shift registers.






