Hexadecimal addition is the process of summing base-16 numbers using digits 0-9 and letters A-F, where a carry to the next column occurs at 16 instead of 10. While the physical silicon in your ESP32, STM32, or Arduino only understands binary voltage states, we use hexadecimal (base-16) to group those bits into readable, 4-bit nibbles. When you are calculating I2C bus offsets, configuring PWM timer registers, or debugging memory pointers in C++, getting a hexadecimal add wrong means writing to the wrong hardware register, which can silently misconfigure a peripheral or trigger a hard fault.
Hexadecimal Addition Carry-Over Reference Table
Before running through a multi-column addition, you need to internalize how carries propagate in base-16. Unlike decimal math where you carry a 1 when a column exceeds 9, in hex you carry a 1 to the next nibble only when the sum reaches 16. The table below maps the most common edge-case additions you will encounter when calculating memory offsets or bitwise register masks.
| Operand A (Hex) | Operand B (Hex) | Decimal Equivalent | Sum (Hex) | Carry to Next Nibble |
|---|---|---|---|---|
| 0x8 | 0x7 | 8 + 7 = 15 | 0xF | 0 |
| 0x9 | 0x8 | 9 + 8 = 17 | 0x1 | 1 |
| 0xA | 0xC | 10 + 12 = 22 | 0x6 | 1 |
| 0xF | 0x1 | 15 + 1 = 16 | 0x0 | 1 |
| 0xF | 0xF | 15 + 15 = 30 | 0xE | 1 |
| 0x1F | 0x01 | 31 + 1 = 32 | 0x0 (with 1 to 3rd digit) | 1 |
Reference standard: Base-16 arithmetic logic as defined in TI I2C Multiplexer Datasheets and standard digital logic design.
The Mechanics of a Hexadecimal Add: Worked Example
Let us walk through a realistic scenario. Suppose you are manually calculating a memory address offset for a DMA (Direct Memory Access) buffer on an ARM Cortex-M microcontroller. Your base address is 0x20004B8 and your buffer size offset is 0x27D. You need to find the end address by performing a hexadecimal add.
0x4B8 + 0x27D (ignoring the upper 2000 prefix for the math, as it remains unchanged if no carry propagates that far).
Step 1: The Rightmost Column (Least Significant Nibble)
Add 8 and D. Remember that D equals 13 in decimal.
8 + 13 = 21.
Since 21 is greater than 15, we subtract 16 to get the digit for this column: 21 - 16 = 5.
We carry a 1 to the next column.
Result so far: _ _ 5
Step 2: The Middle Column
Add B, 7, and the carried 1. B equals 11 in decimal.
11 + 7 + 1 = 19.
Subtract 16 to find the digit: 19 - 16 = 3.
We carry a 1 to the next column.
Result so far: _ 3 5
Step 3: The Leftmost Column
Add 4, 2, and the carried 1.
4 + 2 + 1 = 7.
7 is less than 16, so no carry is generated.
Final Result: 0x735
If your base address was 0x20004B8, your calculated end address is 0x2000735. If you had mistakenly used decimal addition rules (where 8+13=21, write 1 carry 2), you would have calculated 0x745, pointing your DMA controller 16 bytes past your actual buffer and causing a memory corruption hard fault.
Where You Meet This in Practice
Hexadecimal addition is not just academic; it directly dictates what physical hardware registers you interact with on a PCB. Here is where you will use it on the bench.
1. I2C Address Offsets and Multiplexing
When wiring multiple identical sensors to an I2C bus, you often use a multiplexer like the TCA9548A. The base I2C address of the TCA9548A is 0x70. The chip has three hardware pins (A0, A1, A2) that act as binary inputs. If you tie A0 to VCC (logic 1), you are effectively adding 0x01 to the base address, making it 0x71. If you tie A0, A1, and A2 all to VCC, you are adding binary 111 (which is 0x07 in hex) to the base address. 0x70 + 0x07 = 0x77. If you miscalculate this hex add in your Arduino Wire.beginTransmission() call, the microcontroller will talk to a dead address and return a NACK error.
2. Pointer Arithmetic in Embedded C/C++
This is where beginners brick their peripherals. In C++, pointer addition is scaled by the size of the data type. If you have a 32-bit integer pointer (uint32_t *) pointing to an ESP32 GPIO register at 0x3FF44000, adding 1 to the pointer does not yield 0x3FF44001. Because a 32-bit integer is 4 bytes long, the compiler performs a hexadecimal add of 0x04. The new address is 0x3FF44004. If you want to advance the pointer by 4 registers (16 bytes), you add 4 to the pointer, which results in a hex add of 0x10, landing at 0x3FF44010. Forgetting this scaling rule leads to reading garbage data from unmapped memory spaces.
3. Bitmasking and Register Configuration
When configuring a baud rate generator or a PWM timer, you often need to combine a base configuration word with a specific prescaler value using addition or bitwise OR operations. If your base config is 0x8000 and you need to add a prescaler offset of 0x0A3, the hex add yields 0x80A3. Writing this exact value to the ESP32 Technical Reference Manual defined register ensures the silicon clock divider behaves exactly as intended.
Common Confusions and Troubleshooting
When debugging logic analyzer traces or reviewing embedded code, engineers frequently make three specific mistakes regarding hexadecimal math.
What do people commonly confuse hexadecimal addition with?
The most common confusion is treating hex letters (A-F) as variables rather than fixed numeric values, or applying decimal carry-over rules (carrying at 10 instead of 16). A secondary confusion is mixing up hex literals with ASCII hex values. For example, the hex number 0x41 equals 65 in decimal. But if you are parsing a UART string, the ASCII character 'A' is also represented by 0x41. Adding 0x01 to the hex number yields 0x42 (66). Adding 0x01 to the ASCII character yields 'B'. The math is identical, but the context changes how your debugger displays the result.
What does a miscalculated hex add actually change in a real circuit?
It changes the exact physical silicon register or memory block your microcontroller targets. For example, on a Texas Instruments INA219 current sensor, the configuration register is at 0x00, but the shunt voltage register is at 0x01, and the calibration register is at 0x05. If your code calculates a register pointer using a hex add and accidentally overshoots by one (e.g., targeting 0x06), you are writing to an undefined or reserved memory space. The sensor will ignore the write, your calibration will fail, and your current readings will return raw, unscaled integer garbage.
How do I verify my hex math on the bench?
Do not rely on mental math for critical memory offsets. Use the programmer mode on your multimeter's companion software, or the built-in Windows/Mac calculator in 'Programmer' mode. Set the calculator to HEX, input your base address, add your offset, and verify the output. When writing C++ code, use Serial.printf("Target Address: 0x%08X\n", target_address); to print the resolved 32-bit hex address to your serial monitor before executing the memory write. This catches pointer arithmetic errors before they trigger a watchdog reset.
Mastering the hexadecimal add is a non-negotiable skill for embedded systems design. Whether you are routing I2C buses on a custom PCB, writing bare-metal register drivers, or debugging DMA memory faults, treating base-16 math with the same rigor as your physical wiring will save you hours of logic analyzer debugging.






