Hi All,
I figured out how to have an I2C master assign slave addresses in the order that they’re plugged in. I looked across forums and found several people with similar problems but no solution; so I created a method that involves the EEPROM. The code works in the following sequence:
The master Arduino occassionally requests a response from the generic slave address (in this case 0x01) to see if anything responds. Meanwhile, when a new slave is plugged in, the slave reads a value in the EEPROM to be its address, which initially will be the generic address. When it hears the request from the master, it pings the master to let it know it needs an address. The master, which keeps track of how many slaves are attached, then sends over a new address. The slave then reads the new address, saves it to EEPROM, and then does a quick reset via software - this is done because I read on a forum that restarting the Wire.begin() with a new address isn’t possible. Once reset, the new slave sees the new, non-generic address, and initializes a different set of Wire.onRequest() and Wire.onReceive() functions, to correspond with the actual I2C functions it will be going through now that it’s initialized. Once the new address is read, however, it’s important that the EEPROM is rewritten with the generic address (0x01) so that if/when it’s powered down / powered back on, it can repeat the process to find an address (which may be different if it’s plugged in in a different order). This doesn’t affect the already initialized slave.
The only thing the code / hardware is missing is the use of an Arduino pin to turn on/off the I2C clock to the next slave (probably via a logic gate). This is done since each “uninitialized” slave must be looked at separately as to not share new addresses with each other. So slave #4 will cut off connection of the I2C to slave #5, since if slave #4 was just plugged in, so was slave #5, #6, etc, since their power run off of each other, and must mean they all need to be initialized. This way, slave #4 will get address 4, slave #5 will get address 5, etc.
I wanted to make sure I went in depth because I saw several forums of people looking for a solution to this problem, and while this is catered to my specifications, I feel as if it can help people looking for a similar solution.
Anyways, here’s my code. Hope someone else finds this useful!
MASTER:
#include <Wire.h>
byte slaveAdd = 0x01; // Holds the number of slaves attached
void setup()
{
Wire.begin(0x70); // join i2c bus (address optional for master)
}
void loop()
{
checkNewSlave();
}
void checkNewSlave() {
Wire.requestFrom(0x01, 1); // Requests new slaves to respond
if(Wire.available() > 0) // Slave with Address 0x01 has responded
{
slaveAdd++;
Wire.beginTransmission(0x01);
Wire.write(slaveAdd); // Send new slave a new Address
Wire.endTransmission();
delay(250);
}
}
SLAVE:
#include <EEPROM.h>
#include <Wire.h>
int I2Caddress;
void(* resetFunc) (void) = 0; // Needed for software reset
void setup()
{
getAddress();
Wire.begin(I2Caddress);
wireInit();
}
void getAddress() {
I2Caddress = EEPROM.read(0x00); // Slave address is stored at EEPROM address 0x00
if (I2Caddress == 0x00) { I2Caddress = 0x01; }
}
void wireInit() {
if (I2Caddress == 0x01) { // If slave address = 0x01 (address not assigned) then:
Wire.onRequest(resetRequest); // make this function execute when ping requested from master,
Wire.onRequest(resetReceive); } // and make this function execute to receive new address.
else { EEPROM.write(0x00, 0x01); // Else, make stored address in EEPROM 0x01, so that when shut off,
Wire.onReceive(normalReceive); // the process is repeated.
Wire.onRequest(normalRequest); }
}
void resetRequest() { Wire.write(0xFF); } // Ping to master ("I need an address!")
void resetReceive(int howMany) {
while(0 < Wire.available()) {
I2Caddress = Wire.read(); // Read new address from master,
EEPROM.write(0x00, I2Caddress); // set it to EEPROM,
resetFunc(); // and reset Arduino slave.
}
}
}
Pat