The binary number system is a base-2 mathematical framework that represents all numerical values using only two digits, 0 and 1, where each position signifies a successive power of two. When you write digitalWrite(LED_BUILTIN, HIGH) in Arduino, you are abstracting a binary 1 into a physical voltage. But when you drop down to bare-metal register manipulation, configure a motor driver, or debug a corrupted SPI bus, that abstraction vanishes. This binary number system tutorial skips the academic fluff and focuses on how base-2 math directly dictates hardware behavior, logic thresholds, and memory states in modern embedded systems.

The Core Mechanics: Base-2 Weights and Register Mapping

In base-10 (decimal), each column represents a power of 10 (ones, tens, hundreds). In base-2, each column represents a power of 2. The rightmost bit is the Least Significant Bit (LSB) and represents $2^0$ (1). Moving left, the weights double: 2, 4, 8, 16, 32, 64, 128. An 8-bit binary number (one byte) can represent $2^8$ distinct values, ranging from 0 to 255.

Key Constraint: An 8-bit register maxes out at 255 (binary 11111111). To represent 256, you must overflow into a 9th bit or a secondary register.

Microcontroller datasheets rarely list raw binary; they use hexadecimal (base-16) as a shorthand. However, the physical silicon only understands binary high/low states. Here is a reference table mapping common binary bit-masks to their decimal and hex equivalents, which you will constantly encounter when configuring hardware registers.

Binary (8-Bit) Hexadecimal Decimal Hardware Application Example
00000001 0x01 1 Enable Bit 0 (e.g., I2C EN bit in a control register)
00000010 0x02 2 Enable Bit 1 (e.g., SPI Clock Phase select)
00000100 0x04 4 Enable Bit 2 (e.g., GPIO Pin 2 output mask)
10000000 0x80 128 Set MSB High (e.g., Triggering a software reset bit)
10101010 0xAA 170 Alternating bit pattern (used for bus calibration/testing)
11111111 0xFF 255 All bits high (e.g., setting all Port pins to OUTPUT)

Worked Numeric Example: Decoding an I2C Payload

Suppose your logic analyzer captures an 8-bit payload from a temperature sensor: 10110101. What is the actual decimal value?

  • Bit 7 (1): $1 \times 128 = 128$
  • Bit 6 (0): $0 \times 64 = 0$
  • Bit 5 (1): $1 \times 32 = 32$
  • Bit 4 (1): $1 \times 16 = 16$
  • Bit 3 (0): $0 \times 8 = 0$
  • Bit 2 (1): $1 \times 4 = 4$
  • Bit 1 (0): $0 \times 2 = 0$
  • Bit 0 (1): $1 \times 1 = 1$

Sum: $128 + 32 + 16 + 4 + 1 = 181$. In hexadecimal, this is 0xB5. If the sensor datasheet specifies a 0.5°C scaling factor, this binary string represents 90.5°C.

Endianness Warning: When dealing with 16-bit or 32-bit registers (like on the ESP32), always check if the bus protocol uses Big-Endian (Most Significant Byte first) or Little-Endian (Least Significant Byte first). Swapping them will turn 0x0001 (1) into 0x0100 (256).

Where You Meet Binary in Practical Electronics

You interact with binary states constantly, even if your IDE hides the math. Here is where base-2 logic physically manifests on your workbench:

1. Bare-Metal GPIO Registers

When you need to toggle a pin on an ESP32 or STM32 without the overhead of digitalWrite(), you write directly to the memory-mapped GPIO register. To set GPIO 5 high without altering the states of GPIO 4 or 6, you use a bitwise OR operation with a binary mask. You shift a binary 1 left by 5 positions (1 << 5), yielding 00100000 (decimal 32, hex 0x20). Writing this mask to the GPIO_OUT_W1TS_REG (Write 1 to Set) flips only that specific physical pin high.

2. DIP Switches on Motor Drivers

Look at the side of a TB6600 stepper motor driver or an A4988 carrier board. Those tiny sliding switches are physical binary inputs. A 3-switch block for microstepping (e.g., ON-ON-OFF) is literally a 3-bit binary string (110) read by the driver's internal logic gates to determine if the motor should step at 1/4, 1/8, or 1/16 resolution.

3. Logic Analyzer Bus Decoding

When debugging an I2C or SPI bus with a Saleae or DSLogic analyzer, the raw traces are just voltage transitions. The software groups these 8 physical channels into a single binary byte, translating the physical voltage highs and lows into the NXP I2C specification payload format. If your pull-up resistors are too weak, the logic analyzer will show undefined states (neither a clean binary 1 nor 0), resulting in corrupted hex outputs.

What Binary Changes in a Real Circuit Design

Understanding binary isn't just about software math; it dictates physical component selection and circuit tolerances. Here is what base-2 logic changes in your hardware design.

Voltage Thresholds and Noise Margins

A binary '0' is not strictly 0.00V, and a '1' is not strictly 5.00V. In standard 5V TTL logic, a binary '0' is guaranteed to be read correctly as long as the voltage is below 0.8V. A binary '1' is guaranteed as long as it is above 2.0V. The gap between 0.8V and 2.0V is the undefined transition zone. If your PCB trace is too long, or you lack proper decoupling capacitors, electromagnetic interference (EMI) can push a floating input pin to 1.4V. The microcontroller's internal Schmitt trigger won't know if that's a binary 0 or 1, causing ghost interrupts.

ADC Resolution and Step Size

An Analog-to-Digital Converter (ADC) translates continuous analog voltage into discrete binary steps. The number of bits dictates your physical measurement precision. As detailed in standard digital electronics principles, a 10-bit ADC (like on the ATmega328P) yields $2^{10}$ (1024) steps. A 12-bit ADC (like on the ESP32) yields $2^{12}$ (4096) steps.

The Precision Trap: If you are measuring a 3.3V reference with a 12-bit ADC, each binary increment represents $3.3V / 4096 = 0.805mV. If your analog sensor outputs a 2mV change per degree, that equates to a binary change of roughly 2.48 steps. The ADC truncates this to 2, meaning you lose 0.48mV of precision per reading. To fix this, you must either amplify the sensor signal via an op-amp or use an external 16-bit ADC (like the ADS1115) which offers 65,536 binary steps.

Common Confusions: Binary, Hex, and Gray Code

Makers frequently conflate binary with related concepts. Clarifying these distinctions prevents critical errors in sensor integration and bus debugging.

Hexadecimal vs. Binary

Hexadecimal (base-16) is not a different physical state; it is purely a human-readable compression of binary. Hardware does not know what "0xFF" is; it only sees eight high voltage rails (11111111). We use hex because reading a 32-bit memory address in binary (1100101011110001...) is prone to human miscounting, whereas 0xCAF1 is easily parsed. Always remember: when calculating bit-masks, convert hex to binary first, apply your logical AND/OR operations, and convert back.

Standard Binary vs. Gray Code

In standard binary, transitioning from decimal 3 (011) to decimal 4 (100) requires three bits to flip simultaneously. In a physical rotary encoder, mechanical bounce and slight sensor misalignment mean the microcontroller might read 111 or 000 during the transition, causing massive position errors. Gray code solves this by ensuring only one single bit changes between any adjacent values (e.g., 3 is 010, 4 is 110). If you are wiring an absolute encoder to a PLC or microcontroller, you must configure the input register to decode Gray code, not standard binary.

Bit Rate vs. Baud Rate

While binary defines the state (1 or 0), bit rate and baud rate define the speed. Bit rate is the number of binary 1s and 0s transmitted per second. Baud rate is the number of signal transitions per second. In simple UART serial, they are identical. But in advanced modulation schemes, one signal transition can encode multiple binary bits simultaneously.

Frequently Asked Questions

Why do we count from Bit 0 instead of Bit 1?
In computer science and digital logic, the LSB is Bit 0 because it represents $2^0$ (which equals 1). Counting from zero aligns the bit index directly with the exponent of the base, making bitwise shift operations (1 << n) mathematically consistent.

What happens if I send a 9-bit binary value to an 8-bit register?
The 9th bit (the MSB) will be truncated and silently discarded. If you try to write decimal 256 (100000000) to an 8-bit register, the lower 8 bits are all zeros, and the register will store 0. Always apply a bitwise AND mask (value & 0xFF) to prevent unexpected overflows.

Can I use binary math for PWM duty cycles?
Yes. An 8-bit PWM timer (like Timer0 on an AVR) counts from 0 to 255 in binary. Setting the compare register to 127 (01111111) yields an approximately 50% duty cycle. The hardware comparator flips the output pin low the exact moment the binary counter matches your register value.