Why is addr = 0x8 for RPi/Arduino i2c code?

In this article, why is the particular i2c address chosen to be = 8 ?
Is it simply picked arbitrarily ?

https://dronebotworkshop.com/i2c-arduino-raspberry-pi/

In other words, many of the examples I see seem to pick the slave address of a single Arduino as 8. Is there a reason for that ?

If I had a system of 1 Rpi and 4 slave Arduinos, could I simply set the slave Arduino addresses as 10, 11, 12, and 13 ?

Here's the relevant portions from the Arduino & Rpi code:

relevant Arduino code:

/*
Arduino Slave for Raspberry Pi Master
i2c_slave_ard.ino
Connects to Raspberry Pi via I2C

DroneBot Workshop 2019
https://dronebotworkshop.com
*/

// Include the Wire library for I2C
#include <Wire.h>

// LED on pin 13
const int ledPin = 13;

void setup() {
// Join I2C bus as slave with address 8
Wire.begin(0x8);

// Call receiveEvent when data received
Wire.onReceive(receiveEvent);

relevant RPi code:

from smbus import SMBus

addr = 0x8 # bus address
bus = SMBus(1) # indicates /dev/ic2-1

I assume it is arbitrary.
8 is the lowest actual i2c address according to the i2c spec.
You may see some code using address lower than that but those addresses technically don't exist since the signal pattern formed on the SDA wire for addresses lower than 8 have special meaning.
The only way addresses below 8 work is if both the master and the slave are non conformant to the i2c spec and do not honor or implement the reserved functions that should get triggered when attempting to use those lower addresses.

--- bill

1 Like

Thanks !

So is it the slave code on the Arduino that is actually dictating that it is at address 8 ?
In other words, what dictates that an i2c device is actually connected - such that running i2cdetect -y 1 would actually show something at a given address ? I guess it is the combination of the Arduino being connected on the SDA/SCL pins to the appropriate pins on the RPi, AND the Arduino code declaring it is at address == 8 ?

The slave has a reprogrammed address. It looks at the i2c signals and when it interprets the address on the line, it responds appropriately.
i.e. the MASTER initiates and controls all transactions.
It either reads or writes data from/to the slave.
When scanning, the master is starting a transaction on the address for writing zero bytes.
It looks for the status response from the slave to see if anyone responds.

The i2c spec specifies how/when to wiggle the signals and how to interpret the signals. Things like how to start a transaction, send out and address, and how to report status, and stop a transaction. It specifies the relationship between SCL and SDA and how the master and slave are supposed to control the signals and how to interpret them.
The reason there are no valid addresses below 8 is that the spec defines that the signal patterns for SCL and SDA can have special timing and meanings for special functions like START, stop, status bits, clock stretching, or some addresses are used for things like mode toggles or reserved for future unspecified use.

If you were to wiggle the signals the way it is done to talk to slaves on addresses 8 and above for a slave using addresses below 8 you can potentially have issues since the spec has reserved for those signal patterns for special purposes.
One address that can never be used is zero as that pattern represents START.

While some s/w and devices support those lower addresses they are not properly handling the i2c bus. They run the risk of having issues.
For example there are a few real-world devices/chips out there that actually hard coded a slave address below 8 in them. It can't be changed.
Those devices can never be used with a master implementation that fully implements the i2c spec.

Here is a link to the latest spec:
https://www.nxp.com/docs/en/user-guide/UM10204.pdf

Thanks once again bperrybap !

So, to ask a probably very dumb question:

Let's say I set up my RPi with the i2c enabled on the RPi preferences.
And I also connect 3 Arduinos - using the appropriate i2c pins - to the RPi.

Would I then be able to see those Arduinos listed at some addresses when I run the command i2cdetect -y 1 on the RPi ?

Or do I need the Arduinos to run some kind of initialization code to specify their addresses before they are seen by the RPi i2cdetect -y 1 command ?

Specifically I am trying to figure why I see addresses listed for the RPi Sense Hat, but have yet to see any addresses listed when I only connect 1 Arduino Uno to my RPi. Is the ground connection required ? I have been told it is not, but that doesn't sound correct, to me.

Thanks again, and thanks for your patience.

If the Pi is the master each of the Arduino slaves would have a unique address and that is assigned in each Arduino slave's Wire.begin(slaveAddress); function in its setup() code.

For I2C to work the SDA, SCL and ground need to be connected master and every slave. Without ground there is no return for the SDA and SCL signals. There also needs to be at least 1 set of pullup resistors, but not too many.

And they all should be running the same voltage on the signals.
I.e. all 3v or all 5v devices.

If not, then the ideal thing is to create two i2c buses using a level shifter that will bridge between the two buses.
A 5v one where you connect the 5v devices and the 3v ones where you connect the 3v devices.

You can often get away mixing 3v and 5v as long as the pullups used go to 3v and not 5v but it may not be as reliable as using a level shifter since the signal levels are boarder line of being in/out of spec for the 5v devices.

--- bill

1 Like

@jcdeen
1. Theoretically, every I2C device (Arduino or Controller or Sensor) coulde be assigned a 7-bit address (range: 0000000 - 1111111 = 0x00 - 0x7F = 0 - 127) known as Slave/Device address. However, the lower 8 addresses (0000000 - 0000111 = 0x00 - 0x07 = 0 - 7) and (111 1000 - 111 1111 = 0x78 - 0x7F = 120 - 127) are reserved addresses.

2. While the Master intends to send data to the Slave (write mode), it appends a 0 at the right most postion of the slave address to build 8-bit I2C (Bus) Address. For example: if the Slave Address is 1010001 (0x51 = 81), then the write mode I2C Address would be 10100010 (0xA2 = 162). //edit (0x52 = 82).

3. This is the 8-bit I2C Address which is propagated to the Slave. The Slave receives the I2C Address and compares the upper 7-bit with its pre-programmed "7-bit Slave Address'. When there is a match, the Slave asserts ACK signal on the SDA line which is sensed by the Master to take next action.

4. While the Master intends to read data from the Slave (read mode), it appends a 1 at the right most postion of the slave address to build 8-bit I2C (Bus) Address. For example: if the Slave Address is 1010001 (0x51 = 81), then the read mode I2C Address would be 10100011 (0xA3 = 163). //edit (0x53 = 83).

5. Before the execution of the following codes, the 8-bit write mode I2C Address is built from the 7-bit Slave Address and is queued in the buffer. The data byte is also queued. The queued bytes are then asserted on the bus after the execution of Wire.endTransmissio() command.

Wire.beginTransmission(slaveAddress); //slaveAddress = 1010001 *for example)
Wire.write(0x23);    //data byte for Slave
Wire.endTransmission();

6. Before the execution of the following code, the 8-bit read mode I2C Address is built from the 7-bit Slave Address and is transmitted to the Slave.

Wire.requestFrom(slaveAddress, n);   //slaveAddress = 1010001 (for example)

:roll_eyes:
So left shifting is like doing +1 instead of x2 those days?
(double Check your point #4 too)

This site has good information about i2c:
https://www.i2c-bus.org

Here is the direct link about the addressing:
https://www.i2c-bus.org/addressing/

--- bill

1 Like

Saying "append" I have wanted to mean that the 7-bit Slave Address is shifted to the left by 1-bit and then 0 or 1 (write mode or read mode) is placed at the LSBit position.

My mental arithmetic was wrong. Left shift by 1-bit doubles the quantity (x2) and right shift by 1-bit halves the quantity (/2).

Thank you for pointing the mistakes which you always do as a mentor. I am correcting my post.

1. I2C Addressing and Data Transfer Timing during Write Mode


Figure-1:

2. I2C Addressing and Data Transfer Timing during Read Mode
During the execution of this code: Wire.requestFrom(slaveAddress, n), the Slave recognizes its address and after that the Master collects data items from the Slave's buffer. In this case of data transfer, does the Master (the receiver) pulls the SDA line LOW to generate ACK signal?

Yes. It signals that the receiver has got the data byte.

1 Like

In case others are curious:

Yep, that table shows why slave addresses 0 to 7 technically don't exist and should not be used for slave devices.
When those addresses are used by a master, the bit patterns and their timing created on the bus represent signal patterns that are used or are reserved for special purposes.

BTW,
Same information to the table that I linked to back in post #11

While the Wire API doesn't restrict their use, and on the AVR platform addresses 1 to 7 will work, this isn't the case on all Arduino platforms.
For example, using some of these addresses on the pic32 platform will cause the i2c h/w to lock up.

--- bill

image

1. What does that START byte do? Does it bring START condition on the I2C Bus?

2. The execution of the following high level codes brings START condition on the I2C Bus, then makes the roll call of the Slave, and then brings STOP condition on the bus. Is the given START byte (00000001) somehow embedded in the following codes?

Wire.beginTransmission(slaveAddress);
Wire.endTransmission():

3. The equivalent register level codes for the high level codes of Step-2 are:
(1) START Condition on I2C Bus by activating the START-bit (TWSTA) of TWCR Register.

TWCR = 0b10100100;	//TWI bus formation; TWINT-bit is cleared; START condition is asserted
//TWCR = TWINT TWEA TWSTA TWSTO TWWC TWEN X TWIE
//Execution order: TWEN, TWINT, TWSTA, ...
While(bitRead(TWCR, 7) != HIGH) //checking if process is done by looking LH for TWINT-bit
	;			//wait until TWINT-bit becomes LH	
Serial.print((TWSR & 0b11111000), HEX);	//Serial Monitor should show: 08 (8)

(2) Roll Calling of Slave by putting slave address 1010010 into TWDR Register

TWDR = 0b10100100;					//slaveAddres + Write-bit	
TWCR = 0b10000100;	//TWI bus remains enabled; TWINT-bit is cleared; START bit is OFF
//TWCR = TWINT TWEA TWSTA TWSTO TWWC TWEN X TWIE
//Execution order: TWEN, TWINT, TWSTA, ...
While(bitRead(TWCR, 7) != HIGH)		//checking if process is completed
		;							//wait until the process is completed
Serial.print((TWSR & 0b11111000), HEX);	//Serial Monitor shows correct status word 18

(3) STOP condition on I2C Bus by activating the STOP-bit (TWSTO) of TWCR.

TWCR = 0b10010100;	//TWI bus enabled; TWINT-bit cleared; START-bit OFF, STOP command

Where these "reserved addresses" also reserved in the original versions of the I2C spec? "Different Bus format", "HS-mode"... All "new" features.

Is there a document describing I2C in more detail from the philosophical "why" and "how" PoV? I guess I'm looking for something like the v1 DEC/Intel/Xerox Ethernet Spec, which was a thing of unusual detail and clarity!

It's in the spec that was linked back in #5 (section 3.1.15)
The "start byte" is different than the "start condition." It looks like a sort of nop/sync byte used to "stretch" the start of a data transfer - the "start condition" would normally be a single 0 bit; by following it with the "start byte", the 0 is extended to 8 bit times (the start bit, plus 7 zeros in the start byte.)

1 Like

I believe they are slowly updating the spec to use the reserved area addresses.

I have a document from Phillips from 1997 that shows all the i2c addresses.
(remember that you have to license official addresses for your slave h/w)
In that document it shows that 0-7 were reserved but only 0 was actually assigned at that point in time.

10 years later by June 2007 in the V3 spec, they allocated about half the reserved 0-7 addresses and taken a few high addresses for 10 bit addressing mode.

The table you posted is from the October 2021 V7.0 spec.
If you look at the difference between the June 2007 V3 spec vs the latest October 2021 V7.0 spec, they have assigned some of the remaining reserved space for a "Device ID" function.

--- bill