Changing I2C address in code.

Hey!
I'm using the Wire-library to send and receive I2C messages in a system with multiple hosts and I'm wondering if there is any way to change the address in "runtime".

Right now I'm setting the address with Wire.begin(), but I guess you can do a more low-level approach. (Setting registers maybe)
Any info will be good info. Also if there are any limitations like delays and so on I would really like to know.

Also, my first post here, so hello to all reading this!

Right now I'm setting the address with Wire.begin(), but I guess you can do a more low-level approach.

I'm sure you could read the source code to figure out how the library is doing it, but, why? Is there a reason not to use Wire.begin to set the address?

Also if there are any limitations like delays and so on I would really like to know.

There may be limitations imposed by the I2C device you are interfacing with. These, of course, depend on the particular device, and should be documented in the data sheet for the device. Things like the time it takes for the device to generate a useful result come to mind.

Also, my first post here, so hello to all reading this!

Hello, yourself. :slight_smile:

Maybe I wasn't clear about what I needed to do. What I want to do is to change the address multiple times. To use Wire.begin multiple times isn't viable, it's not meant to be done. That's why I thought about some more low level way to access the hardware.
I guess this isn't anything the normal arduino-user knows about.

it's not meant to be done.

I'm not trying to be dense. It's just that I've never used I2C.

The Wire.begin command allows you to join the bus as a slave, if you specify an address, or as the master, if you don't.

I can not envision where you want the Arduino to be a slave to too many masters, so I can not see changing the address too often.

On the other hand, if the Arduino is the master, changing it's address is not necessary. The address of the device to talk to is not specified using the Wire.begin command. It is specified using the Wire.beginTransmission or Wire.requestFrom functions.

So, I'm really not sure what you are trying to do. Can you try again to explain?

Sure, no problem.
I don't think this problem is very common since it's not a very normal use of the I2C bus on the ATMega/Arduino for most users.

Why I need to be able to change the I2C-address of the chip is so that I can conform to a "closed" I2C protocol used by Older Märklin Digital devices. It has to do with digitally controlled model trains. It uses a multiple master system where different types of commands are sent using different addresses.

I will give you an example.

Say I want to send a command for setting the speed of a train. This is what I would have to send on the bus.

  • Address of Control unit.
  • Command specific response address. (The address which is sending the command)
  • Locomotive data
  • Speed data

In a normal case this could equal this code:

Wire.beginTransmission(Receiver);
Wire.send(Sender); // Command specific
Wire.send(decoderAddress);
Wire.send(speedData);
Wire.endTransmission();

An immediate response is then sent flipping the two first bytes, effectively sending a message to the address you sent as the first byte in the command. And then depending on the result of the command follows up with 1-3 bytes of data.

A log of a potential command & response.

// First command
FE+ 06+ 48+ 4B+

// Response
06+ FE+ 48+ 2B+
  • is an Acknowledge.

Sorry for dropping the text-bomb. Basically what I need to do is either be able to always listen in on the bus, or change the address to suit my needs.

Hi,
this doesn't look like a normal I2C-protocol at all. I2C is a standard protocol where everything from the recommend wiring to the layout of the commands is defined. There is nothing like a closed protocol for I2C. Even the simplest hardware logic analyzer has functions to decode what is being sent on the I2C bus.

Do you have a link to a page that has some more background information about this Märklin Protocol?

Eberhard

Ok, well what I meant with closed is the fact that It wasn't publicly documented with all it's commands and such.

Here is the info I have. The site have some html-problems so just copy all text and you can read it easier.
http://home.arcor.de/dr.koenig/digital/ei2c.htm

I have not done anything like this before, but I had a look at the code in Wire.cpp, and it looks straightforward.

There is a function called
twi_setAddress(address);
used in
void TwoWire::begin(uint8_t address)

You'd probably need to #include <twi.h> somehow to get at.

This would skip all the other stuff that
void TwoWire::begin(void)
and
void TwoWire::begin(uint8_t address)
and
void TwoWire::begin(int address)
do.

HTH
GB

Thanks. I looked into the function and there is only one command.
TWAR = whatever 8bit value you wish. Just mind the TWGCE bit.

Now I'm set for more work.