Wire I2C Arduino Slaves - Self-Defining Address [SOLVED]

Hi All,

I’m going to be brainstorming ideas today, but figured I’d see if the community had any insight.

I’m looking to have a master Arduino that sends out data to other slave Arduinos, and the specific data will vary depending on which slave it’s sending to. Even though all I2C lines will be wired directly to the master, each slave will have an “output” and “input” port, where:

(master) output <-> input (slave 1) output <-> input (slave 2) output <-> etc.

The tricky part is that I need the slave to self-define its address depending on what order slave it is, since that will define what data goes to which slave. Now I figured the best way to approach this is probably hardware based, since I don’t believe self-defining is possible with strictly the Wire library / software. I’m thinking since there’ll be an input / output on each slave, that it’s possible to do something with a gate that turns on I2C communication to the next slave only if the previous slave has an address defined, and then that next slave will take the next address. I’d prefer to not use extra lines to communicate between devices, but if the only option involves adding an extra line, like a line similar to Chip Select, I’ll take it.

I’m just toying around with ideas and figure I’ll come across something eventually, but I’m sure something like this has been done, so I wanted to seek any possible advice. Any input is greatly appreciated, thanks in advance.

Pat

How about something like this:

where you manually set the address of each slave?

Thanks for the reply Paul. Perhaps I should've specified more clearly that I'd like each individual slave to be placed in any order with the same results. So today, I may have slave A be first in the order, and slave B be second, but perhaps tomorrow I may plug them in differently and have B first and A second. Since all the data sent from the Master will just be stored in RAM, once they're turned off/unplugged from the master, they no longer should have anything specifying their order/address. Whichever slave is plugged in first in the order should always receive the data meant for the first slave.

Hope that makes it a little clearer, thanks again for the reply.

Pat

cannot be done, sorry
the I2C bus is abstract in its design and does not contain position information .

That said, there is one trick you might find useful. You could switch off all devices except the first one.
It then gets it new address automagically.
Then you switch on the second one and it gets it new address automagically.
etc.
This is tricky but could be done.

What is the behavior if one module breaks?
e.g. you have address 1..20 and module 10 breaks?
would all remaining devices get a new address?

Thanks Rob, yes thats what I was touching up upon a bit in my last post with the "Chip Select" example. I figured theres no way to do it strictly with I2C. And if the tenth unit fails while they're all powered up, the 11 - 20 should still maintain their same addresses, so there won't be anything tricky there. Good question though, although I won't expect that to happen, since by way of the input/output I was talking about earlier, if the tenth were to somehow fail, the 11 - 20 should also fail as well (if, say, there's a lose connection at 10).

Do you have an idea of a code that does what you explained? I understand shutting off the other slaves until the previous slave has its address, that should be easy. But how would the second slave know that there's only one slave in front of it? Is there a way?

Thanks a lot for your reply, I feel like we're on the right track.

Pat

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

Very similar to DHCP. If you gave each device a unique hardware ID you could even implement reservations.

The way this would work is the MASTER would keep a table of which I2C addresses it's already dished out. It keeps a separate table of "reservations". When the slave boots up, it would respond to the "broadcast" address with it's hardware ID. The master then checks it's reservation list. If the device doesn't appear there, it just issues an address from the pool. But the MASTER can also potentially issue the same address every time for certain devices.

KenF:
If you gave each device a unique hardware ID you could even implement reservations.

I’m vaguely familiar with DHCP, but using hardware addresses is missing a bit of the point in this case. With my method, you could build identical units, with identical software and hardware, and have each separately identifiable. It’s a bit more complicated, but there’s no extra programming needed to give each unit a separate address, and especially no extra hardware; everything is done automatically, with no user input needed.

I’m unsure if what I explained was 100% clear, but it seems that what you described is all done by my program; the master in my method also keeps track of what addresses are in use and what addresses are available to be sent out to new slaves. My method just removes the need for the master to monitor hardware IDs to know if a slave’s been addressed properly, when the slaves can just send a generic code saying they don’t have IDs.

Pat

Hi Pat

What will happen if power fails and is then restored to multiple units while they are still connected to the bus? Won't they all respond simultaneously to the 0x01 poll from the master?

Regards

Ray

Hackscribble:
What will happen if power fails and is then restored to multiple units while they are still connected to the bus? Won't they all respond simultaneously to the 0x01 poll from the master?

Thanks for the question, Ray. I explained this a tiny bit in one of the last paragraphs, but to avoid this, there's a section of code (that I'm currently working on) that says "if I'm an unaddressed slave, then turn pin X off". This pin will be hooked up to a logic gate or some other electronic switch to disconnect the I2C SDA/SCK to the next connector (the next slave). If slave #3 doesn't have an address, that means it was just connected to the system, which means anything after it (slaves 4, 5, etc) were also just connected, just as you mentioned, since each connection is daisy-chained through the previous one. So by cutting off I2C connection to slaves 4, 5, and so on, you won't be resetting any slaves that have already been addressed, and it forces the master to communicate solely with one unaddressed unit at a time. Then, once the unaddressed slave has gathered its address, it pulls up pin X, and the I2C communication flows to the next slave until there are no other slaves to address.

This seemed like the easiest way to attack this; it involves an extra piece of hardware, but a logic gate should be one of the easiest to implement.

Thanks,

Pat

hi there guys. I have been using Arduino for quite some time and know the basics. So I was searching for a solution to a problem that I am having.

I have 3 gateway Modbus and each gateway has 23 to 35 Modbus Arduino slaves. at the moment I don't have a master for the Arduino slaves as the gateways gives me the full access for configurations and controlling them. my problem is that every time an arduino goes down I have to reprogram with a static Slave ID. is there a way where I can just put a variable and have the master give the slave a slave ID?

I was checking out the 2 code MASTER AND SLAVE that PattoBrien posted and that is exactly what I need. Will this work for MODBUS as it does with i2c?

if not, would it be okay if I ask for some help or some guidance.