Hexadecimal adding is the mathematical process of summing base-16 numbers, where digits range from 0-9 and A-F, carrying over to the next column when a sum reaches 16 instead of 10. While hex math doesn't alter the physical electrons flowing through a circuit, it directly dictates how you calculate memory offsets, configure I2C register addresses, and size DMA buffers when programming microcontrollers. People commonly confuse hexadecimal adding with binary addition or standard decimal math, leading to catastrophic off-by-six errors when calculating memory pointers or buffer limits on the workbench.
When you are staring at a logic analyzer trace or debugging a hard fault on an ESP32, the compiler doesn't care if you think in decimal. The silicon operates on binary, but we use hex as the human-readable bridge. If your mental math defaults to base-10 when calculating a buffer offset, you will overwrite adjacent memory, corrupt file systems, or brick a sensor configuration. This guide breaks down the exact mechanics of base-16 addition so you can calculate offsets in your head while probing a board.
The Core Mechanics of Hexadecimal Adding
To add hex numbers reliably, you must internalize that the carry threshold is 16, not 10. The digits A through F represent decimal values 10 through 15. Let's walk through a concrete numeric example that you might encounter when calculating a memory address offset: 0x3F + 0x1C.
0x in your code and your notebook. Dropping the prefix is the fastest way to accidentally pass a decimal 34 into a function expecting 0x34 (which is decimal 52).
- Align the columns: Write the numbers vertically, aligning the least significant digits (the rightmost column).
3F + 1C ----
- Add the rightmost column (F + C): Convert to decimal for the sum. F is 15, C is 12. The sum is 15 + 12 = 27.
- Calculate the carry and remainder: Divide the sum by 16. 27 ÷ 16 = 1 with a remainder of 11. The remainder 11 translates back to the hex digit B. Write down B and carry the 1 to the next column.
- Add the leftmost column (3 + 1 + carry): Sum the digits and the carry: 3 + 1 + 1 = 5. Since 5 is less than 16, there is no further carry.
- Final Result: Combine the columns to get 0x5B.
If you were to verify this using standard decimal conversion: 0x3F is 63, and 0x1C is 28. 63 + 28 = 91. Converting 91 back to hex (91 ÷ 16 = 5 remainder 11) yields 0x5B. The math holds up, but doing the decimal conversion in your head for 32-bit addresses is a waste of time and a prime source of errors.
Where You Meet This in Practice
You won't use hex addition when wiring a 120V AC outlet or sizing a breaker, but it is inescapable in embedded systems and digital logic. Here is where base-16 math directly impacts your hardware setup:
1. I2C and SPI Register Mapping
When configuring a sensor like the MPU6050 accelerometer over I2C, you often need to calculate the address of a specific data register. If the base configuration block starts at 0x6B and the gyroscope offset registers are located at an offset of 0x13, you must add them to find the exact target address (0x7E). Sending a command to the wrong register because of a decimal-brain math error will result in the sensor returning 0x00 or throwing an I2C NACK.
2. Memory Pointers and DMA Buffers
Microcontrollers like the STM32 or ESP32 map their SRAM and Flash into specific hex address spaces. According to the Espressif ESP-IDF Memory Types Guide, the ESP32's internal SRAM starts at 0x3FFB0000. If you are manually allocating a DMA buffer and need to advance a pointer by 512 bytes (0x200), you must add 0x200 to your base address. Miscalculating this overlap causes memory corruption that manifests as random, untraceable reboots.
3. Addressable LED Color Mixing
When programming WS2812B (NeoPixel) LED strips, colors are defined as 24-bit hex values (e.g., 0xFF0000 for red). If you are writing a custom blending algorithm that adds a dim green (0x001100) to a dim red (0x220000), you are performing hex addition to arrive at the final yellow-orange hex code (0x221100). Forgetting how the bytes align will result in bizarre color shifts across your LED matrix.
Real-World Scenario Walkthrough: The SPI Flash Overwrite
Abstract math is easy; applying it under pressure on the bench is where mistakes happen. Here is a real-world scenario involving an external SPI flash chip (like the ubiquitous W25Q32) used for data logging.
The Setup: You are writing a custom data logger on an ESP32. You are appending 256-byte pages of sensor data to the flash. Your current write pointer (the next available empty byte) is at 0x0F8A. You need to advance the pointer by a payload size of 0x0085 bytes to calculate the start of the next empty block.
The Numbers (Correct Math):
Let's add 0x0F8A + 0x0085:
- Rightmost: A(10) + 5 = 15. In hex, 15 is F.
- Second digit: 8 + 8 = 16. In hex, 16 is
0x10. Write 0, carry 1. - Third digit: F(15) + 0 + 1 (carry) = 16. Write 0, carry 1.
- Leftmost: 0 + 0 + 1 (carry) = 1.
- Correct Result: 0x100F.
The Outcome & What Went Wrong:
The developer, fatigued and thinking in decimal, adds the second column (8 + 8 = 16) and writes down 6 with a carry of 1. Then they add the third column: F(15) + 1 (carry) = 16. They again write down 6 and carry 1. Their calculated result is 0x106F.
The code updates the file allocation table to say the next free block is at 0x106F. However, the actual data was written ending at 0x100F. This creates a 96-byte gap (0x106F - 0x100F = 0x60) of uninitialized flash memory, which reads back as 0xFF. When the LittleFS file system attempts to mount the drive on the next reboot, it hits this gap of garbage data in the allocation table, throws a CRC mismatch error, and formats the entire drive, wiping a month of field sensor data.
Common Confusions and How to Avoid Them
As detailed in the All About Circuits digital logic textbook, the primary hurdle with hex is unlearning base-10 reflexes. Here is a comparison matrix to keep your mental models separated:
| Criteria | Decimal (Base-10) | Hexadecimal (Base-16) | Binary (Base-2) |
|---|---|---|---|
| Digit Set | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 | 0-9, A, B, C, D, E, F | 0, 1 |
| Carry Threshold | Carry at 10 | Carry at 16 | Carry at 2 |
| Common Use Case | Human counting, analog multimeter readings | Memory addresses, I2C registers, MAC addresses | Logic analyzer traces, bitwise masking |
| The "Off-By" Trap | Treating 'A' as a variable instead of 10 | Carrying at 10 instead of 16 (e.g., 8+8=16 -> writing 6) | Carrying at 10 instead of 2 |
The "Adding F" Shortcut: If you need to add 0xF (15) to a hex number in your head, use the complement trick. Adding 15 is mathematically identical to adding 16 (0x10) and then subtracting 1. For example, 0x24 + 0x0F becomes 0x34 - 0x01 = 0x33. This bypasses the need to do mental division by 16 entirely.
FAQ: Hex Math on the Workbench
How do I add hex numbers in Arduino or ESP32 C++ code?
You don't need special functions for the math itself. The C++ compiler handles the underlying binary addition automatically. You simply write uint32_t newAddr = baseAddr + offset;. The hex notation is just for your readability. However, when you need to print the result to the Serial monitor to verify it, you must use the Serial.print() HEX formatter: Serial.println(newAddr, HEX);.
Why do we use hex instead of binary for memory addresses?
Because 16 is a power of 2 (2^4), exactly four binary bits map to one single hex digit. A 32-bit memory address written in binary is a 32-character string of ones and zeros that is impossible to read or verify visually. In hex, that same address is exactly 8 characters long (e.g., 0x3FFB0000). It compresses the visual footprint while maintaining a direct, lossless translation to the silicon's binary reality.
What happens if I add two hex colors and exceed FF?
If you are adding RGB values (like 0x80 + 0x90) for an LED and the sum exceeds 0xFF (255), you will experience overflow. In standard 8-bit integer math, 0x80 + 0x90 equals 0x110. If stored in an 8-bit variable, the leading '1' is truncated, leaving 0x10. Your LED will suddenly jump from near-maximum brightness to almost completely off. Always clamp your hex addition results to 0xFF when mixing color channels.






