Hexadecimal (base-16) is the native language of microcontroller registers, memory pointers, and digital bus protocols. When you are debugging an ESP32 I2C bus, setting SPI clock dividers, or parsing serial data, you need immediate translation between hex, decimal, and binary. The direct answer for the most common embedded hex lookup—0xFF—is 255 in decimal and 11111111 in binary, representing a full 8-bit HIGH state or a pull-up enabled mask. Below is the definitive reference for translating and applying these values on the bench.

The Complete Table of Hexadecimal Conversions (Base Nibbles and Queried Registers)

This table provides the foundational base-16 nibbles (0x00–0x0F) from which all 8-bit and 16-bit values are derived, alongside the most frequently queried hexadecimal addresses for standard embedded sensors. The ASCII column references the ISO/IEC 646 (ASCII) standard, while the I2C addresses are sourced from the NXP I2C-bus specification (UM10204) and respective manufacturer datasheets.

How to read this table: The Hex column is your C/C++ literal (prefix with 0x). The Decimal column is the base-10 human-readable integer. The Binary column shows the raw 8-bit or 4-bit logic states (HIGH/LOW). The Modifier / Application column tells you where this specific value physically appears in embedded systems, such as sensor addresses or bitwise masks.
Hex Decimal Binary ASCII / Control Embedded Application / Modifier
0x0000000NULGround / Logic LOW / Clear Register
0x0110001SOHBit 0 Mask / Enable Bit
0x0220010STXBit 1 Mask
0x0330011ETXUART Stop Bits (2) / I2C Start
0x0440100EOTBit 2 Mask
0x0550101ENQBit 0 + Bit 2 Mask
0x0660110ACKI2C Acknowledge (SDA pulled LOW)
0x0770111BEL3-Bit Mask (0b111)
0x0881000BSBit 3 Mask / SPI CS Active
0x0991001HTTab / Bit 0 + Bit 3
0x0A101010LFLine Feed / Newline (Serial)
0x0B111011VTVertical Tab
0x0C121100FFForm Feed / Bit 2 + Bit 3
0x0D131101CRCarriage Return (Serial)
0x0E141110SOShift Out
0x0F151111SI4-Bit Nibble Mask (Lower half-byte)
0x101600010000DLEBit 4 Mask / 16-byte boundary
0x203200100000SpaceASCII Space / Bit 5 Mask
0x3C6000111100<I2C: SSD1306 OLED Display (7-bit)
0x508001010000PI2C: AT24C32 EEPROM (Base Address)
0x6810401101000hI2C: DS3231 RTC / MPU6050 IMU
0x7611801110110vI2C: BME280 / BMP280 Sensor (SDO=HIGH)
0x7711901110111wI2C: BME280 / BMP280 Sensor (SDO=LOW)
0x7F12701111111DELMax 7-bit I2C Address / Max signed 8-bit int
0x8012810000000SPI Read Mask (OR with register address)
0xFF25511111111Max unsigned 8-bit int / Full PORT HIGH

Which Column Applies to Your Installation and Protocol

Choosing the wrong column representation is the root cause of most "sensor not found" errors on the bench. Your bus protocol dictates which column you must use in your C/C++ code.

  • Use the Decimal Column when interacting with user-facing math or Arduino abstraction layers. Functions like analogWrite(pin, 128) or ledcWriteTone() expect base-10 integers for PWM duty cycles and frequencies.
  • Use the Binary Column for direct port manipulation and pin state debugging. If you are reading PINB on an ATmega328P or setting GPIO direction registers on an ESP32, binary literals (e.g., B10100000) map 1:1 with the physical silicon pins.
  • Use the Hexadecimal Column for memory addresses, I2C device addressing, SPI register mapping, and RGB color codes. Wire libraries (like Wire.beginTransmission(0x3C)) and hardware abstraction layers expect hex literals to match the manufacturer's datasheet register maps.

How Modifier Operations "Derate" or Shift the Base Hex Value

In AC wiring tables, derating rows reduce ampacity based on ambient heat. In hexadecimal register tables, bit-shifting and masking modify the base hex value to accommodate protocol overhead. If you plug the raw 7-bit hex value from the table above directly into a logic analyzer's 8-bit decoder, it will fail. You must apply the protocol's modifier.

The I2C 7-Bit to 8-Bit Shift

The I2C specification defines addresses as 7-bit values. However, the physical bus transmits 8 bits. The 8th bit is the Read/Write (R/W) flag. To get the actual byte placed on the SDA line, you must shift the base hex value left by one bit (<< 1) and append the R/W bit.

  • Base Value: SSD1306 OLED is 0x3C (Binary: 0111100).
  • Write Modifier (R/W = 0): Shift left -> 1111000. Add 0 -> 0x78.
  • Read Modifier (R/W = 1): Shift left -> 1111000. Add 1 -> 0x79.

Bench Tip: If your logic analyzer shows a transaction to 0x78 but your Arduino code says Wire.beginTransmission(0x3C), the hardware is working perfectly. The Wire library handles the shift automatically; the logic analyzer shows the raw shifted bus state.

SPI Register Read Masking

When reading a register over SPI (e.g., the WHO_AM_I register on an MPU6050), the base hex address is 0x75. However, SPI requires you to set the most significant bit (MSB) HIGH to indicate a read operation. You must bitwise OR the base value with 0x80.

  • Base Address: 0x75 (01110101)
  • Read Mask: 0x80 (10000000)
  • Modified Value Sent: 0x75 | 0x80 = 0xF5 (11110101)

What This Table Cannot Tell You: Endianness and Signed Limits

A standard table of hexadecimal conversions shows raw mathematical magnitude. It completely hides two critical architecture-level realities that will corrupt your data if ignored: Endianness and Signedness.

Warning: 5V vs 3.3V Logic Levels
When probing hex bus traffic with an oscilloscope or logic analyzer, ensure your microcontroller's VCC matches the sensor. Feeding a 5V I2C bus into the 3.3V GPIO pins of an ESP32-WROOM-32 will permanently damage the silicon. Use a bidirectional logic level shifter (like the BSS138 MOSFET circuit) or run the bus at 3.3V with 2.2kΩ pull-up resistors to 3.3V.

Endianness (Byte Order in Memory)

If you are reading a 16-bit hex value like 0x1234 from an I2C sensor, the table tells you it equals 4660 in decimal. But how is it stored in the ESP32's RAM? Most modern microcontrollers (ARM Cortex, ESP32, AVR) are Little-Endian. They store the least significant byte first. Therefore, 0x1234 is stored in memory as 0x34 followed by 0x12. If you cast a raw byte buffer directly to a uint16_t pointer without using a union or bitwise shifting, your decimal output will be 0x3412 (13330), completely ruining your sensor calibration.

Signedness (Two's Complement)

The table lists 0x80 as 128. This is only true if your variable is an unsigned 8-bit integer (uint8_t). If your C++ code defines the variable as a signed 8-bit integer (int8_t), the MSB acts as a sign bit. In Two's Complement arithmetic, 0x80 evaluates to -128, and 0xFF evaluates to -1. Always explicitly define your integer types using <stdint.h> to prevent the compiler from guessing your signedness intent.

Decision Path: Selecting the Correct Hex Format for Your Code

Use this decision tree to determine exactly how to format and manipulate your hexadecimal literals in your next firmware build.

If your task is... And your hardware protocol is... Then use this exact code format
Setting an I2C sensor address I2C (Wire library) Wire.beginTransmission(0x3C); (Use raw 7-bit hex)
Reading an SPI sensor register SPI (Hardware or BitBang) SPI.transfer(reg | 0x80); (OR base hex with 0x80 mask)
Setting a GPIO pin HIGH via register Direct Port Manipulation PORTD |= (1 << PD5); (Use bit-shift, avoid raw hex)
Parsing a 16-bit Big-Endian I2C payload I2C (Wire.readBytes) val = (buf[0] << 8) | buf[1]; (Manually reconstruct endianness)
Defining an RGB LED color WS2812B / NeoPixel strip.setPixelColor(0, 0xFF0000); (Use 24-bit packed hex)

By anchoring your code to the correct hexadecimal representation and applying the necessary bitwise modifiers for your specific bus protocol, you eliminate the most common class of embedded communication errors. Keep the base nibble table bookmarked, and always verify your logic analyzer captures against the shifted 8-bit bus values, not just the raw 7-bit datasheet addresses.