One of the challenging head-scratchers I came up against in learning embedded electronics was analyzing communications that utilized the I2C protocol. One challenge was realizing there was 7-bit and 8-bit address implementations. Not realizing this made for a very long debugging session. It was one those sessions where everything was working, but from my analysis, it shouldn’t have been working. Gremlins like that are frustrating as hell but also the most fun to hunt and troubleshoot.

Specifically, the I2C addresses I was seeing on my logic analyzer didn’t match the source code in my code editor. As an example, here is code for VEML6030 ambient light sensor. The defines the I2C address for the VEML6030 as 0x48.

But when I use a Saleae logic analyzer to snoop on the I2C bus between the sensor and a SparkFun Redboard Artemis Nano board, the address is 0x90.

So what is the difference?

After scouring the datasheet for the sensor, I find that the last bit in the address is used to indicate read (0b1) or write operation (0b0). Thus we must do a logical shift left to get the I2C address.

So 0x48 = 0b0100 1000

0b0100 1000 << 1 = 0x1001 0000 = 0x90 (write-operation)

Thus, 0x91 is a read-operation.

Translated to the 7-bit format it would be as follows:

0x48W (write operation) or 0x48R (read operation).

Fortunately, Saleae Logic makes it easy to toggle between 7-bit and 8-bit formats through the settings options:

And viola, the address matches what is in the source code: