Arduino Wire problem

Hi!

I'm trying to get two arduinos to talk to each other in a multi master configuration. I cannot for the life of me figure out how to get the second Arduino to talk to the first.

what happens is that Arduino 1 can send data to Arduino 2, but 2 can't send anything to 1. I understand that the master Arduino controls the clock, which lets it write data to the other. when I use the Wire.endTransmission function, the reference says it releases the I2C bus. does that mean it turns off the clock as well?

how do I switch master devices? how do I get the second arduino to talk back to the first one?

i can provide code if you need, i just want to know if there's a way at all to do it. The Wire library says its multi master, but i can't figure it out.

My opinion is that a Multi-Master bus with Arduino boards is a fairy tale.
Can you use a Serial/UART communication ?

I have not seen a reliable Multi-Master bus with Arduino boards yet. You will be one of the many that failed. You need a single Master. The Master can request data from the Slave, for example twice a second.
I can help with the sketches if you show them both, but the result might not be what you want. Tell us also which Arduino boards you have and how long the distance is and what cable you use and if you have pullup resistors and if there are other I2C modules connected (with links to where you bought them), and so on.

Why do you want two Arduino boards ? It will be easier if you can do your project with a single Arduino board.

Adding to what @Koepel said I think you are on the wrong track. I2C allows the master to request data from a slave then the master provides the clock for the slave to send with. Unless you can provide a very good reason I don't think you need multi master.

ok thank you very much! i'll try something else. i wanted to use one of the boards as a touscreen input, but that uses a lot of the pins. However my module does not use the SCL and SDA pins. I thought, "Hey, I2C would work!"

I know the I2C slave master approach would probably work, and that's probably what I will do. I just saw it was multi master capable, and thought "Hey! that's neat. I want to see if i can do that!"

Thank you. I'm using a mega board, so I have a bunch of digital pins to use, but not enough to comfortably use one board. Is there another way to have them talk to each other in a similar fashion to a multi master approach? one that doesn't use very many more wires?

Have you considered MQTT?

I don't know if it works, but both masters should initially be configured as slaves with Wire.begin(someAddress) so they can receive data. If Arduino1 wants to send, it will switch to master (Wire.end() followed by Wire.begin()) and send the data; after that it will switch back to slave mode (Wire.end() followed by Wire.begin(someAddress)). Similar for Arduino2.

You will need a mechanism to prevent both Arduinos of being master at the same time.

I doubt somehow that MultiMaster was designed for multiple masters communicating with each other, in my opinion it was designed for multiple masters communicating with one or more slaves. I might however be wrong.

This hobby is about experimenting so in my opinion that's a very good reason to try to do something.

Yes. In fact, the clock signal is only present during data transfers.

I managed to get a simple Multi master interface working. Sterretje really helped by informing me that a wire.end function existed. i didn't know, and then updated the client and boards. I switched the boards between master and slave, Slave by default, master only if there is data to be sent, then back to slave as quickly as possible. I put some delay in just to be safe though. it isn't really graceful, but it works.

thank all of you guys so so much. here is the code

#include <Wire.h>

#define Me 0x8
#define Other 0x9
bool inUse = false;
int buttonpin = 8;

void toMaster(){
  if (inUse == false){
    Serial.println("MasEnabled");
    Wire.end();
    Wire.begin();
    inUse = true;
  }
  return;
}

void toSlave(){
  Wire.end();
  Serial.println("SlavEnabled");
  Wire.begin(Me);
  Wire.onReceive(receiveEvent);
  inUse = false;
  return;
}


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  toSlave();
  pinMode(buttonpin, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  Serial.println("loop");
  int val = digitalRead(buttonpin);
  if (val == LOW && inUse == false){
    Serial.println("sent");
    toMaster();
    Wire.beginTransmission(Other);
    Wire.write(true);
    Wire.endTransmission();
  }
  toSlave();
 
}

void receiveEvent(int howMany){
  if(inUse == false){
  inUse = true;
  while (Wire.available()){
    bool push = Wire.read();
    Serial.println(push);
    digitalWrite(LED_BUILTIN, HIGH);

  }
  }
      toSlave();
    return;
 
}

I will probably add a different line in the real project, but for tesiting purposes this setup worked fine.

You should not have done that :cry:

:point_right: Can you assign one Arduino board as Master and everything else as Slave ? There will still be about 20 things that you have to do right.

When a Arduino is set as a Slave, it is also a Master. There is no need for Wire.end().
The Wire.end() releases the SDA and SCL pins. They will no longer be I2C pins. It is used in case those pins are going to be used for something else.

Don't use the Serial functions in a interrupt routine. The receiveEvent() and requestEvent() functions are called from a interrupt routine and the Serial functions use interrupts themselves.

If you expect a certain number of bytes, then I prefer to check for that. If you are receiving one byte then you can do this:

volatile byte data;

void receiveEvent( int howMany)
{
  if( howMany == 1)    // extra safety check, one byte should be received.
  {
    data = (byte) Wire.read();
  }
}

You don't check the error returned by Wire.endTransmission(), so you don't know if the Wire library has detected a collision on the I2C bus.

The receiveEvent() is called from an interrupt routine from the Wire library. By calling Wire.end() from inside that interrupt routine, you disable the I2C hardware and stop the Wire library. That is not good. I can not tell what will happen, so for now let's say the result will be totally unpredictable.

@PerryBebbington You are more optimistic than me.
Until there is no good implemenation of a multi-master bus for Arduino boards, then there can be no good reason to use it.

@sterretje Could you read all these pages: How to use the Arduino Wire library

It's not about optimism, it's about opportunities to learn something. Learning is as good a reason as any to try something, regardless of whether there is any practical use for whatever is tried.

I don't want to send a beginner down a path that dozens have tried and have failed.

Why? A slave is perfectly capable if sending data without being a master. The only "limitation" is that the slave should only do so on request by the master - which is in fact a very desirable and necessary "limitation"!

@koraks, I'm sorry but I have to disagree.

When a Slave sends data, then it is a Master on the bus. That means it will be a multi-master bus.
If the "real" Master sends a command to a Slave that it can send data, then the Master can just as well request data and the Slave can stay a Slave.

Really, I have not seen a good implementation yet. What you describe requires timeouts and a very good description of the protocol. If there are timeouts, then the "real" Master can not use the I2C bus for some time. And so on, and so on.

Just one Master that requests data from Slaves once or a few times per second is straightforward.

I'm afraid we can start the same discussion about putting SDA and SCL next to each other in a flat ribbon cable :scream:

In my memory, in I2C a device that is sending back its own data by a command from the master is not called a "master".
It still remains a slave.
I think that the master refers only to the "device that makes the call".

At the request of the master, even if you operate the bus to start sending data, the device is still called a slave.
The responsibility for managing the bus lies with the master who called to send data to slave.

1 Like

That's exactly what I said.

@koraks Sorry, I misunderstood you.

Crap. I should have removed the Serial.println functions. Those were from me debugging it lol.
I’m still probably gonna use a master/slave setup for the original design. I haven’t tested this thing enough, but for the 5 minutes i was pushing buttons randomly, it seemed to work the way it should. Maybe what i’m doing is a bad habit, you guys are WAY better at this than me. I just wanted to see if i could get both to initiate contact with the other without a request event. It seemed to work, but it may be an unhealthy solution, just like baking a box cake without looking at the directions. You know how its done, but you may have missed something important.

When koepel told me i was barking up the wring tree, i started looking at the other methods he suggested. It wasn’t until i came back and saw that i could maybe/perhaps try one more thing, that i was like “welp! I’ve already tried all of these avenues, let me try one more! Whats the worst that can happen? Even if i somehow someway break the board, its only 30 something dollars for a new one.”

1 Like

In I2C Protocol -- who is sending data to whom?

The Master is that device which executes the Wire.beginTransmission() and Wire.requesFrom() commands along with the genertaion of SCL pulses to push-in/push-out data into/from target/slave device.

The Slave never sends data to the Master; rather the Master pushes-out data from the Slave.