Pages: 1 [2]   Go Down
Author Topic: I2C device addressing  (Read 5525 times)
0 Members and 1 Guest are viewing this topic.
Global Moderator
Offline Offline
Brattain Member
*****
Karma: 485
Posts: 18794
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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. smiley 

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.
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
That would be me, I think.
Excellent! You weren't throwing BS into the ether were you? (*)

Quote
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.

Quote
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.

Quote
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.

Quote
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.

Quote
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.
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 127
Posts: 8517
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Texas, land of the free, home of the stupid
Offline Offline
Sr. Member
****
Karma: 5
Posts: 268
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 485
Posts: 18794
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.

Quote
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
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Use an LPC processor, they have a unique ID burned into the silicon. smiley
Or use a beagle bone http://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?
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 127
Posts: 8517
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.

http://www.saleae.com/

Quote
The TI OMAPs have a ID burned into the die.
As does it seem the SAM on the Due I discovered this morning.

______
Rob

Logged

Rob Gray aka the GRAYnomad www.robgray.com

Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Saleae Logic
not bad for $150! Thanks for the link...

Quote
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.
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 127
Posts: 8517
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 http://dsscircuits.com/images/code/I2C_Rev5.zip .

(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.

Quote
The TWI protocol allows bus systems with several masters.
and
Quote
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:
Quote
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
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 127
Posts: 8517
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 smiley
  • 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)
Code:
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


Code:
// 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;
  }
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 18
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 http://burtleburtle.net/bob/hash/doobs.html 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:
Code:
=~=~=~=~=~=~=~=~=~=~=~= 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:
Code:
=~=~=~=~=~=~=~=~=~=~=~= 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


Code:
// 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);
  }
Logged

Pages: 1 [2]   Go Up
Jump to: