Binary is the base-2 language of logic gates, and hexadecimal is the base-16 shorthand engineers use to read and write those bits without losing their minds. When you are staring at a datasheet for an ESP32 or an I2C sensor, you are looking at memory addresses, register masks, and pin states. Understanding this duality does not change the physical electrons moving through your circuit, but it completely changes how you interface with the silicon. Writing 0xFF to a GPIO register sets 8 pins high instantly; doing it in binary is 11111111, and in decimal it is 255—a number that tells you absolutely nothing about the individual pin states.
The Core Math: Grouping Bits into Nibbles
Microcontrollers process data in 8-bit, 16-bit, or 32-bit chunks. Staring at a 32-bit binary string like 00111111010000001000000000000100 to find a single configuration bit is a recipe for bench errors. Hexadecimal solves this by grouping binary bits into sets of four, called nibbles. Because four binary bits can represent exactly 16 values (0 through 15), they map perfectly to the 16 characters of the hexadecimal system: 0-9 and A-F.
0-9 = 0-9
10 = A | 11 = B | 12 = C | 13 = D | 14 = E | 15 = F
Worked Numeric Example: Converting a Status Byte
Let us say your logic analyzer captures an 8-bit I2C status byte: 1011 0100. To convert this to hex, split it down the middle.
- Left Nibble (
1011): Calculate the decimal value. (1×8) + (0×4) + (1×2) + (1×1) = 11. In hex, 11 is B. - Right Nibble (
0100): Calculate the decimal value. (0×8) + (1×4) + (0×2) + (0×1) = 4. In hex, 4 is 4. - Combine: The binary byte
10110100is 0xB4 in hexadecimal (which equals 180 in decimal).
By looking at 0xB4, an experienced engineer instantly sees the top nibble is B (meaning the top four bits are 1011) without doing mental math.
Where You Meet This in Practice
You will encounter base-16 notation constantly when moving from basic Arduino sketches to bare-metal register manipulation or advanced sensor integration.
- I2C Device Addresses: An SSD1306 OLED display typically listens at 0x3C or 0x3D. If you pass
60(the decimal equivalent of 0x3C) into a library that expects hex, your display will not initialize. - Memory-Mapped Registers: According to the Espressif ESP32 GPIO API Reference, the GPIO output register is located at memory address
0x3FF44004. You cannot write to this using standard decimal pointers. - Addressable LEDs: When programming WS2812B NeoPixels, color is passed as a 24-bit hex value. Pure green is 0x00FF00. The first two zeros are red, the FF is green, and the last two zeros are blue.
Real-World Scenario: The MCP23017 Direction Register Trap
Hexadecimal errors rarely cause syntax failures; they cause physical hardware to behave unpredictably. Here is a classic bench failure involving the popular MCP23017 16-bit I/O expander.
The Setup
You are wiring an MCP23017 to an Arduino Nano via I2C (using 4.7kΩ pull-up resistors on the SDA and SCL lines) to drive a bank of 12V relays. Port A (pins GPA0-GPA7) is wired to the relay driver transistors. Port B is wired to tactile pushbuttons. You need Port A to be outputs and Port B to be inputs.
The Numbers
According to the Microchip MCP23017 Datasheet, the I/O Direction Register for Port A (IODIRA) is at address 0x00. In this register, a 1 configures a pin as an input, and a 0 configures it as an output.
The Outcome
You write the following Wire library code to "turn everything on" for a quick test:
Wire.write(0xFF);
The relays immediately begin chattering randomly, and the MCP23017 chip becomes hot to the touch.
What Went Wrong
You confused the hex value for "output high" with the hex value for "pin direction." By sending 0xFF (binary 11111111) to the IODIRA register at 0x00, you did not turn the pins on. You configured all eight Port A pins as high-impedance inputs. Because the relay driver transistors were now connected to floating, un-driven pins, ambient electromagnetic noise caused the transistors to partially turn on and off, creating a erratic current draw that heated the silicon. The correct hex value to send to 0x00 to make Port A outputs was 0x00 (binary 00000000).
Common Confusions: Hex, Decimal, and the Missing Prefix
The most common mistake makers make is confusing decimal and hexadecimal literals in C/C++ code, leading to silent failures.
| Notation | Code Example | Actual Binary Value | Result on 8-bit Port |
|---|---|---|---|
| Decimal | 10 |
0000 1010 |
Pins 1 and 3 go HIGH |
| Hexadecimal | 0x10 |
0001 0000 |
Pin 4 goes HIGH |
| Binary | 0b00010000 |
0001 0000 |
Pin 4 goes HIGH |
People commonly confuse the number 10 with 0x10. In decimal, 10 is ten. In hex, 0x10 is sixteen. If a datasheet tells you to write 10 to a configuration register, you must check the context. If the datasheet column is labeled "Hex", you must write 0x10 in your code. Always use the 0x prefix in your IDE to force the compiler to interpret the digits as base-16.
255, you have no idea if that means all pins are high, or if it is a specific error code. Always force hex output in your debug statements using Serial.println(val, HEX); to see FF instead.
FAQ: Binary and Hexadecimal Bench Questions
Q: Why do we use hexadecimal instead of octal (base-8)?
A: Octal groups bits into threes, which does not align cleanly with standard 8-bit, 16-bit, or 32-bit microcontroller architectures. Hexadecimal groups bits into fours (nibbles). Two hex digits perfectly fill one 8-bit byte, making memory alignment and bitwise masking vastly simpler for hardware engineers.
Q: How do I perform bitwise masking with hex values?
A: Use the bitwise AND (&) and OR (|) operators. If you want to check if the 3rd bit of a status register is high, you mask it with 0x04 (binary 00000100). The code if (status & 0x04) will evaluate to true only if that specific bit is a 1, ignoring the rest of the byte.
Q: My I2C scanner shows address 60, but the datasheet says 0x3C. Are they the same?
A: Yes. 60 is the decimal equivalent of the hex value 0x3C. Some basic Arduino I2C scanner sketches print addresses in decimal by default. Always convert the scanner output to hex (or configure the sketch to print in hex) before comparing it to a manufacturer datasheet.






