I2C device addressing

ALL nodes running exactly the same software...

That's a reasonable requirement and doable and I understand why he wants to get away from having hardcoded and unique values programmed into each node, especially if this will be a product where maybe you don't know in advance how many will be plugged in and holding inventory of nodes with hardcoded addresses would be difficult.

I have a similar requirement at present (not with I2C) but the only way I know to do it is to bring the nodes online one at a time or "by controlled sequential startup of the slaves" as you say. As this is a one-off thing when you first build a system I think that's a reasonable approach.


Rob

Graynomad:
How do they know another slave/master has not already started transmitting? Is there a mechanism in I2C to handle that?

The AtMega's support HW multi-master bus arbitration. The scheme is based on masters listening for bus activity (start/stop) and then only claim the bus when idle. This generally works when arbitration is only required once every blue moon, but it will not work when two or more devices try to claim the bus at the exact same clock edge (such as may be the case when multiple identical devices are powered on at the same time). For this some software retry scheme will be required.

For most practical purposes (as opposed to academic), storing a unique address in EEPROM is a good option.

The OP seems supremely confident that he can hack the I2C bus protocol. All attempts to discourage / enlighten him seem to fail. He is bringing out all the old bus ideas that were rejected long ago. I don't know maybe he is young and doesn't realise many people have been round that block many times before.

Well you are living up to your moniker! Which is cool with me by the way, too much PC BS these days.

It's been a long time since anyone's called me anything close to "supremely confident". I going to have to take it as a compliment. So thanks, appreciate it!

Not young. Here's a hint: I bought a Motorola 6800/MIKBUG based computer for $1K in 1976 just after I graduated from high school. Taught myself how to program machine language (hand assembled of course). No it wasn't an Altair, a Commodore, a PET or an Apple I - the 6502 had just come out and the Z-80 was the next big thing.

Simply the wrong protocol for the job

I agree the SMBus is the right way to go. However it isn't clear if there is an Arduino library for it. If the SMBus and I2C are "compatible" as the Intel blurbs say, then I should be able to use I2C to replicate the SMBus protocol (which implies that someone from Intel "hacked" I2C to come up with SMBus). But that doesn't sound like a pleasant thing to do.

He could use I2C to have master assigned slave addresses, by controlled sequential startup of the slaves, as example.

I don't need a "controlled sequential startup" at all. I just want the master to assign the slave addresses.

For most practical purposes (as opposed to academic), storing a unique address in EEPROM is a good option.

I agree that storing the addresses in EEPROM is easier. But being nuts is more fun. And besides once an address is assigned to a slave, it can be stored in EEPROM and the whole situation becomes sane again.

[quote\Otherwise, WHY HAVE MULTIPLE SLAVES if they are all doung the same thing and calling home with the same names???[/quote]
It is very reasonable to have a large array of slaves doing all the same thing. It is very unreasonable to have them call home with the same address -- that's why I'd like to have the addresses assigned by the master.

As BenF says, multi-master bus arbitration is available on the AtMega. Still unknown if the Arduino I2C library can handle it in a reasonable way.

The link I gave clearly shows that it is possible for an Arduino to be both Master and Slave at the same time (unless that guy was throwing some BS out into the ether).

Like I said, those two aspects open up a lot of opportunities....

This generally works when arbitration is only required once every blue moon, but it will not work when two or more devices try to claim the bus at the exact same clock edge (such as may be the case when multiple identical devices are powered on at the same time).

I think the scheme I proposed would prevent them from hitting the exact same clock edge nearly all the time, but there is a very small probability it could happen. Each slave is quiet until the master kicks the process off with the first message. The slaves all back off randomly, so with high probability, the first slave to respond after that will be the only one to respond. If the other slaves don't blindly rush forward at that point, then there will be a second slave to respond, and again with high probability the only one to respond. And so on.

This process will be slow, but once they have their addresses in EEPROMs, it doesn't need to occur again until a brand new device is added to the array.

For this some software retry scheme will be required.

Yes, as long as the collision can reliably be detected by all the colliding parties. If so, then random back-off would work, even if it had to be repeated until it did. It all depends on the Arduino's I2C library ability to handle collisions in a clear and reasonable way.

JohnA

johnA:
The link I gave clearly shows that it is possible for an Arduino to be both Master and Slave at the same time (unless that guy was throwing some BS out into the ether).

That would be me, I think. :slight_smile:

You sound like you must be my age or thereabouts.

Can you define the problem more clearly? You want multiple slaves running the same software. So far so good. Are you gradually going to add them? You could have some sort of "give me an address" button that, when pressed, asks the master for a free address, and then writes that into EEPROM.

The master could do a broadcast (address 0). I'm not too clear about whether multiple slaves of the same address would cause a problem there, but I suspect not (need testing to confirm).

The broadcast could be "any slaves need an address?". The slaves could then wait a random time and then respond. Hopefully one will get in sooner and be assigned an address before the others respond. I think it is achievable, but there might be simpler ways. Like, a simple program that writes a slave address to EEPROM before deploying the "main" program.

Remember, you don't have heaps of program memory. You don't want to take half of it up with a smart "address allocation" routine if there are simpler ways.

That would be me, I think.

Excellent! You weren't throwing BS into the ether were you? (*)

You sound like you must be my age or thereabouts.

I was going to make a crack about an Age Check for posting to this forum but I won't.

Can you define the problem more clearly? You want multiple slaves running the same software. So far so good. Are you gradually going to add them?

Not really, I was hoping to add them all at once. But I guess the scenario would come up: I run to one Radio Shack, wipe out their store's inventory of Arduino's, add the batch of them to the existing array, run to the next Radio Shack, etc., having the NSA show up at my door asking why I'm buying all the damn Arduinos.

It would be interesting to work out a "hot plug" like scheme though...

In short, I was just balking at the sheer number. Trying to keep a dozen, let alone 32, identical boards straight and having absolutely no address collision when doing that manually, just seems... unlikely.

You could have some sort of "give me an address" button that, when pressed, asks the master for a free address, and then writes that into EEPROM.

That sounds much easier but not as much fun! If a nice software solution doesn't fall out, I'll probably use this and maybe light an LED to indicate the board is missing an address.

The master could do a broadcast (address 0). I'm not too clear about whether multiple slaves of the same address would cause a problem there, but I suspect not (need testing to confirm).

In your testing for your site, did you run any multiple master configurations? I'm very curious on what the Arduino I2C library does in that case.

I bet it would be very difficult to set up a test. There would have to be some other artificial mechanism to get multiple boards to reliably start communicating at exactly the same time.

Remember, you don't have heaps of program memory. You don't want to take half of it up with a smart "address allocation" routine if there are simpler ways.

Good point. But my experience has been that half the fun is dreaming these things up and the other half is, once in a while, an elegant solution just comes out of the conversation that solves everything quite nicely. In the meantime, we're poking at I2C and finding the limits of it's capabilities. Seems win-win to me.

In any case, got to go...

(*) My humour style is deadpan hyperbole. May not translate into ASCII very well; I haven't checked Unicode. Anyone visiting your site knows my comment was tongue-in-cheek.

Use an LPC processor, they have a unique ID burned into the silicon. :slight_smile:


Rob

I still do not see that you can avoid collisions, even with your random back-off idea. You have no garantee without some external control/arbitration. My first response is to do it the simple way with EPROM'd addresses, but I also see that that does not facilitate adding of devices in an easy manner.
And,, I get the impression that you are trying to learn from doing it the hard way (most commendable).

If you do get it to work, I, for one, would be interrested in how you went about it ( I don't want to see your code, I just want to hear you explain the logic that made it work). Good Luck. I hope to see notice of your success.

In your testing for your site, did you run any multiple master configurations? I'm very curious on what the Arduino I2C library does in that case.

One of my code samples had both ends acting as a master. It's quite possible as such, I2C doesn't really have a master, more like "I'm master right now".

I didn't try too hard to get collisions. It wouldn't have been ... fun.

The protocol itself is supposed to detect it, but of course you would want to randomize a retry a bit, or they would keep happening.

There was a guy here a while back who posted a whole lot of boards connected together to do different things (they weren't identical). He used I2C, but I think he had a "assign me an address" button to work around the problem you are describing.

There was a guy here a while back who posted a whole lot of boards connected together to do different things (they weren't identical). He used I2C, but I think he had a "assign me an address" button to work around the problem you are describing.

It does seem like the way to go, at least the easiest.

One of my code samples had both ends acting as a master. It's quite possible as such, I2C doesn't really have a master, more like "I'm master right now".
I didn't try too hard to get collisions. It wouldn't have been ... fun.

Well the I2C spec does say it's ok and handled by the standard. Although the proof will be in the pudding; the way it is actually handled in the Arduino I2C library is going to be critical.

I'm thinking a test setup with two boards to start with. The first setup is just to try a simple master-slave configuration. I'll use one of the digital pins to set that board as either master or slave. It'll be a simple: the master sends a byte, the slave echoes the byte, the master checks what it sent is what it received, repeat. This should flush out any wiring and other screw-ups I can/will do.

The next setup is to make them both masters and see if I can force a collision (somehow!) to occur. I think it's going to be difficult to come up with a messaging scheme that clearly shows a collision occurred and that the master/bus arbitration worked correctly. I don't think a simple send-and-echo test is going to be enough. Any help here would be greatly appreciated. In any case, I can kick it off and let it run for a day or so. That should be enough time to capture a collision.

John

Graynomad:
Use an LPC processor, they have a unique ID burned into the silicon. :slight_smile:

Or use a beagle bone BeagleBoard.org - bone The TI OMAPs have a ID burned into the die. At work we're using it to generate an ethernet MAC address.

It seems like it's cheating somehow... isn't it?

Any help here would be greatly appreciated.

I really think you will need a logic analyser to get this working. Without one you will be shooting in the dark for weeks.

Both Nick and I use the Saleae Logic, it's great.

The TI OMAPs have a ID burned into the die.

As does it seem the SAM on the Due I discovered this morning.


Rob

Saleae Logic

not bad for $150! Thanks for the link...

As does it seem the SAM on the Due I discovered this morning.

Sorry I don't know what a "SAM" is. I searched google but no joy.

SAM in the context of this forum means SAM3X8E, aka the processor used on the new Due board.


Rob

Well it's been a while since I posted. I've been working on another project using a Motor Shield and a IR tachometer.

In the meantime, I've found out a couple of things.

I've been reading the code at Wayne Truchsess' site http://dsscircuits.com/articles/arduino-i2c-master-library.html and DssCircuits.com is for sale | HugeDomains .

(Note his code references Nick Gammon for the scan of the devices on the bus.)

The code seems to trap one return value (LOST_ARBTRTN) related to bus arbitration. This value is part of the AVR TWI interface, which in turn is the C/C++ front end to the ATMega chip's I2C built-in interface. All in all, it might be a very simple effort to change Wayne's i2c code to let that particular return value through to let the calling code handle it.

I've also been reading the ATMega328p datasheet. There is a section that specifically talks about multi-master arbitration.

The TWI protocol allows bus systems with several masters.

and

The losing Master should immediately go to Slave mode, checking if it is being addressed by the winning Master.

I knew this already from Nick's site, but it clearly shows the ATMega folks designed these scenarios into the chip, i.e. it's not a case of trying to make the chip do something it wasn't designed to do.

A very interesting quote is:

Note that arbitration is not allowed between:
• A REPEATED START condition and a data bit.
• A STOP condition and a data bit.
• A REPEATED START and a STOP condition.

It is the user software’s responsibility to ensure that these illegal arbitration conditions never
occur. This implies that in multi-master systems, all data transfers must use the same composi-
tion of SLA+R/W and data packets. In other words: All transmissions must contain the same
number of data packets, otherwise the result of the arbitration is undefined.

To me this means that during those times when the slaves could act as masters, I have to ensure that all the data transmitted have the same length. And to achieve that, there needs to be a very clear start and end to those periods to tell all the slaves they can start transmitting their fixed-length packets.

I know, not much progress, but still, it's looking more possible the more I look into it.

It's also becoming crystal clear that Rob's advice to pick up a logic analyzer is bang-on. Having read all this, I can't imagine trying to debug the code with Serial.println's!

One concern I have come up with is that the random number generator may not be random enough. Using a random number to fake a processor id depends on the first random number coming out of it (after power on) to be different from all the other processor's first number. I haven't looked into Arduino or AVR code to determine how it seeds the generator but it's important for the first number coming out of the random number generator to be ... random. Depending on how it's seeded, it may be ok or this might be a showstopper.

JohnA

I can't imagine trying to debug the code with Serial.println's!

No chance :slight_smile:

Re random numbers, there are 2 or 3 mega threads with various ways to get reasonable random numbers most of which would be good enough for this I would think, as you surmised just calling random() on all the Arduinos won't cut it.


Rob

It's been a while, had to bring up my new website...

But I have made a little progress. Bought a Saleae Logic to help out and then wrote some code.

I first tried the I2C library here http://dsscircuits.com/articles/arduino-i2c-master-library.html and found out that the Slave side of the API isn't there yet: "The new library does not support Slave functions as of yet." This particular application needs both the Master and Slave API, so I had to go with the Wire library. This will cause me some problems later.

To test Wire API, I wrote a single routine that "plays catch" between two Arduinos. Whichever one has the "Ball", lights it's LED.

  • I compile once. Connect the serial port to one Arduino, download. Connect to the other Arduino, download. Get's tedious very quickly.
  • The initial master is gotten from Pin 5. After that the master and slave swap roles.
  • Note that I had to declare the Wire.onReceive(receiveEvent); even though I don't use the interrupt routine. That took a while to figure out.
  • It is just as gratifying watching the LEDs light up on the two Arduino's as it was for the very first LED blinker program :slight_smile:
  • There is a lot of output to the screen so you can follow along as the ball goes back and forth. Note this output for one Arduino (the initial master)
A
B
C
M1: passing the ball
M2: wait for slave...
recv: 1
M3: got it...
M4: and it is an ack; Slave now has the ball.
S1: waiting for ball
recv: 1
S2: got it...
S3: and it is the ball
S4: told master we got the ball
M1: passing the ball
M2: wait for slave...
recv: 1
M3: got it...
M4: and it is an ack; Slave now has the ball.
<and so on>

Next step: try seeding the random number generator on both Arduino's to see if a simple AnalogRead(0) works.

John

// for I2C stuff: http://www.gammon.com.au/forum/?id=10896
// for i2c lib:   http://dsscircuits.com/articles/arduino-i2c-master-library.html
#include "WProgram.h"
#include "Wire.h"

#define CONFIGPIN 5
#define THEBALL 0x88
#define GOTTHEBALL  0x66

static enum
  {
  master = 0,
  slave = 1,
  } state;
int myaddr = 1;
int destaddr = 2;

//===========================
void receiveEvent(int howMany)
  {
  Serial.print("recv: ");
  Serial.println(howMany, DEC);
  }

//===========================
void setup()
  {
  Serial.begin(9600);

  Serial.println("A");

// Pin 13 has an LED connected on most Arduino boards:
  pinMode(13, OUTPUT);
  pinMode(CONFIGPIN, INPUT);

  // the Config pin indicates who starts off as the master
  int v = digitalRead(CONFIGPIN);
  if (v == HIGH)
    {
    state = master;
    myaddr = 1;
    destaddr = 2;
    // the LED indicates who has the ball (the master)
    digitalWrite(13, HIGH);
    }
  else
    {
    state = slave;
    myaddr = 2;
    destaddr = 1;
    // the LED indicates who has the ball
    digitalWrite(13, LOW);
    }
  Serial.println("B");

  //Start the wire session using our own address
  Wire.begin(myaddr);

  //Even though receiveEvent is empty, got to use it in the setup
  Wire.onReceive(receiveEvent);

  // empty the input buffer of any garbage
  while (Wire.available() > 0)
    {
    Wire.receive();
    }

  Serial.println("C");
  }

//===========================
void loop()
  {
  if (state == master)
    {
    // send the ball to the slave
    Serial.println("M1: passing the ball");
    Wire.beginTransmission(destaddr);
    Wire.send(THEBALL);
    Wire.endTransmission();

    // wait for slave to acknowledge it has the ball
    Serial.println("M2: wait for slave...");
    int timeout = 0;
    const int maxtimeout = 500;
    while (timeout < maxtimeout && Wire.available() < 1)
      {
      delay(1);
      timeout++;
      }
    if (timeout >= maxtimeout)
      {
      Serial.println("M3: timeout, trying the send again");
      }
    else
      {
      Serial.println("M3: got it...");

      // check it is a good value (GOTTHEBALL)
      byte c = Wire.receive();
      if (c == GOTTHEBALL)
        {
        Serial.println("M4: and it is an ack; Slave now has the ball.");
        }
      else
        {
        Serial.print("M4: error unexpected byte: ");
        Serial.println(c, HEX);
        }

      // convert to a slave
      state = slave;

      // don't have the ball anymore, so clear the LED
      digitalWrite(13, LOW);
      }
    }
  else // state == slave
    {
    // wait for the incoming ball
    Serial.println("S1: waiting for ball");

    int count = 0;
    while (Wire.available() < 1)
      {
      delay(1);
      count++;
      if (count > 500)
        {
        count = 0;
        Serial.println("S2: still waiting...");
        }
      }
    Serial.println("S2: got it...");

    // consume the byte that was read
    byte c = Wire.receive();
    if (c == THEBALL)
      {
      Serial.println("S3: and it is the ball");
      }
    else
      {
      Serial.print("Sx: error unexpected byte: ");
      Serial.println(c, HEX);
      }

    //  send response to master
    Wire.beginTransmission(destaddr);
    Wire.send(GOTTHEBALL);
    Wire.endTransmission();
    Serial.println("S4: told master we got the ball");

    // convert to the master
    state = master;

    // we have the ball now, so indicate the we have it
    digitalWrite(13, HIGH);

    // wait for a while before passing the ball back...
    delay(500);
    }
  }

//===========================
int main()
  {
  init();
  setup();
  for (;;)
    {
    loop();
    }
  return 0;
  }

I worked on the 32-bit random seed, see below for the code.

I originally tried a simple shift and OR of four calls to analogRead() but the results were anything but random.

I then tried a hash algorithm to randomize the 32-bit seed. It came from the One-at-a-Time Hash in A Hash Function for Hash Table Lookup I chose the One-at-a-Time Hash because it seemed fast and just as importantly didn't require a static table of values.

In any case, these are the values from Arduino #1:

=~=~=~=~=~=~=~=~=~=~=~= PuTTY log 2012.12.30 13:47:47 =~=~=~=~=~=~=~=~=~=~=~=
Setup: starting
seed: 6A60AA2E  val: 1883564177
seed: C4FE6FC4  val: 475762328
seed: 2632BF6C  val: 1303731979
Setup: starting
seed: 82D6BB73  val: 1605122770
seed: 4A19CB4C  val: 1668419733
seed: 8C4A9682  val: 1960768884
seed: 278A61DB  val: 1847493924
Setup: starting
seed: E317FC10  val: 1000697756
seed: C4881F55  val: 1153492722
Setup: starting
seed: EE2A2EAB  val: 324916071
seed: 3CA34E8A  val: 91841824
seed: 6BCDC084  val: 286937447

I pushed the reset button every so often (that's the "Setup: starting"). But a software reset does not change the state of the analog pin and therefore is not the same as a power cycle of the chip. So I tried power cycling the Arduino several times and there was no duplication in any of the seeds that I could see.

I was also concerned about the values across multiple Arduinos. I loaded the code into Arduino #2 and got similar (but different) output:

=~=~=~=~=~=~=~=~=~=~=~= PuTTY log 2012.12.30 13:49:00 =~=~=~=~=~=~=~=~=~=~=~=
Setup: starting
seed: CB93CE3C  val: 1422436928
seed: EC10751D  val: 746109105
seed: 37225535  val: 789063386
Setup: starting
seed: 9A7DAC36  val: 817158441
seed: 4A19CB4C  val: 1668419733
Setup: starting
seed: 16561981  val: 1856270235
seed: 732AA691  val: 1964881064
seed: 4A19CB4C  val: 1668419733
seed: 53EACBA0  val: 1541316714

One thing I considered to get even more randomness was to use a hash of the contents of SRAM. Given the results here, it doesn't seem necessary, for my purposes anyway.

Note: there is not a hint of formality in this testing, take it with a grain of salt.

John

// for hash algorithm used in getSeed, see One-at-a-Time Hash
//    http://burtleburtle.net/bob/hash/doobs.html
#include "WProgram.h"

//===========================
void setup()
  {
  Serial.begin(115200);
  Serial.println("Starting");
  }

//===========================
// calculate a 32 bit seed from 4 reads from analog pin
// use a hash algorithm to mix/combine the reads into a "more random" value
unsigned long getSeed(const int pin)
  {
  unsigned long seed;
  unsigned long i;
  for (seed = 0, i = 0; i < 4; ++i)
    {
    seed += analogRead(pin);
    seed += (seed << 10);
    seed ^= (seed >> 6);
    }
  seed += (seed << 3);
  seed ^= (seed >> 11);
  seed += (seed << 15);
  return seed;
  }

//===========================
void loop()
  {
  unsigned long seed = getSeed(0);

  Serial.print("seed: ");
  Serial.print(seed, HEX);
  srandom(seed);

  unsigned long val = random();
  Serial.print("  val: ");
  Serial.println(val);

  delay(1000);
  }

Hi All,

Old topic, but I came across this forum when I was looking for a similar work around to OP's problem. I just posted a solution on my own post, but it's a solution to OP's problem as well. Hope someone finds it useful.

http://forum.arduino.cc/index.php?topic=277939.0

Pat

Grumpy_Mike:
If you are going to do that then why not just store an address in the first place.

I see why it could be useful: if you were for example to commercialize the product, you would probably want to allocate a long address to ensure that many years of production will never produce two devices with the same address. Such an address, would cost to the communication protocol because each time they want to refer to a individual device, this whole address would need to be sent again. Using this dynamic allocation therefore should allow for shorter, local addresses.