0

I use the Wire Library and need to have multi-master setting. I see multi-master examples, where the masters have their own ID's -- which make sense.

Now I have just connected three devices on the bus with the same ID (for example 0x05) and they can communicate! Every node can listen to all traffic, which is not common but this was exactly what I want.

The question is: Will I have trouble in the future? Is my setup something like "multi-slave"?

4
  • 3
    If all the slaves do is listen that shouldn't be a problem. If you should ever want the slaves to talk back to the master then you will instantly get collisions and it will all fall apart.
    – Majenko
    Aug 10, 2017 at 21:09
  • actually this problem ist solved by a string-based protocol and any nodes sends only if it get requested. But didn't tested, what happends if they try to send in the same time. I use Wire.write and receiveEvent.
    – Andy
    Aug 10, 2017 at 21:21
  • 2
    Andy, that is a problem. @Majenko, perhaps the Wire library accepts a wrong ack/nak at this moment, but that might change in the future. What about the clock pulse stretching for different slaves ? I see too many problems. When sending data to multiple slaves, you can use the broadcast. It is not enabled in the Wire library, but Nick Gammon shows how to enable it in the slaves: gammon.com.au/i2c Requesting data has to be done for each slave seperately. You have to stick to the standard rules. If you can make it work, it might fail in the future if the Wire libraries is improved.
    – Jot
    Aug 11, 2017 at 5:37
  • I didn't knew about broadcasting, thanks. Your source seems to be very detailled
    – Andy
    Aug 11, 2017 at 5:59

1 Answer 1

1
const byte MY_ADDRESS = x;  // where x is whatever unique address you like  

byte RxByte[32]  //32 byte buffer

void setup() {  
  //insert unique ddress                            
  Wire.begin(MY_ADDRESS);  

// this line allows the arduino to listen for i2c broadcast at address 0

TWAR = (MY_ADDRESS << 1) | 1;  
//set up a receive handler  
 Wire.onReceive(receive_data);

then call 
Wire.beginTransmission (0)  //transmit broadcast so all devices will receive 

Wire.write( address of destination) // make the first byte the destination 

Wire.endTransmission ();

then you need a receive handler 

void receive_data() { 
//use a buffer to temporarily store the data 

for ( int d = 0; Wire.available() > 0 ;d++){ 

RxByte[d] = Wire.read();} // READ WIRE BUFFER UPTO 32BYTES, INTO AN ARRAY
datareceived = 1;

//then test if it was for this device
if (RxByte[0] == MYADDRESS){ do somthing with RxBytes[1-31];}
else {}

I use this extensively its very handy and works well I have a complex transmission table which has a 4 byte header and the rest of the bytes can be used as data send

destination, myaddress, a command, and how many data bytes to follow


Addendum to Original answer. below is a complete working example.

//so the basics of the tranmission table are the first 4 bytes (ie Rx or Tx byte 0-3 depending on if we are talking about the sender or recipient) //are used as a header 0= recipient (0 for broadcast. or address) 1= senders address 2= is the command (ie tells the recipient what to do) //and 4= the number of data bytes which follow in the packet //not all of these need to be specified at any time but its usefull to have a structure to follow at each end which never changes //copy and past this upload to 2 arduinos connected by i2c change the 2 addresses and it should work. its a n example of how data can be moved around in a multi master situation that should hopefully give you some ideas. it looks complicated but half of it is just generating some example data and triggering method and the transmit part is repeated 3 times to send out different data.. you should be able to create a more general purpose transmit block for your application

to send data just attach a button or wire to pin 2 directly to ground

watch the 2 serial windows

#include <Wire.h>
//device address  MAKE DIFFERENT FOR EACH

const byte MY_ADDRESS = 20;
const byte PARTNER_DEVICE = 25;

// transmission table stuff
byte RxByte[32]; //setup a receive buffer to hold data
byte TxByte[32]; //setup a transmit buffer to hold data
byte datareceived;

// stuff to generate some data
const int Sensor1 = A0;
byte SensorVal1 = 0;

//stuff to debounce pin 2
const byte digitalpin = 2;
byte pinstate = 0;
byte lastpinstate = 0;
unsigned long checktime = 0;
const byte debouncetime = 50;

// timer
unsigned long timing_loop = 5000;

void setup() {
  Wire.begin (MY_ADDRESS);
  TWAR = (MY_ADDRESS << 1) | 1;                                                      // ENABLE BROADCASTS TO BE RECEIVED
  Wire.onReceive(ReceiveData);     // INTERUPT ON I2C DATA RECEIVE
  Serial.begin(9600);
  pinMode (  digitalpin  , INPUT_PULLUP );// set the pin to input pullup so we can trigger the code by connecting directly to ground
  Serial.println( "READY" );
}

void loop() {
  // generate some random data

  GenRandData();
  //Serial.print(  pinstate);Serial.print( " " );Serial.println( lastpinstate );
  // debounces pin 2 so we can trigger a data broadcast
  pinstate = digitalRead (digitalpin);
  if ( pinstate != lastpinstate && millis() >= checktime) {
    if ( pinstate == 0 && lastpinstate == 1 ) {
      checktime = millis() + debouncetime;
      Serial.println( " button working" );
      REQUEST();
    }
    lastpinstate = pinstate ;
  }


  // this triggers the process data method after receive of data
  if (datareceived == 1  ) {
    datareceived = 0 ;
    ProcessData();
  }

  // 10 second timer triggers transmission of some data
  if ( millis() >= timing_loop ) {
    BROADCAST();
  }
}
void GenRandData() {
  // this just reads analog0 and maps it to a byte then sequentially shifts the transmit buffer
  SensorVal1 = random(255);
  TxByte[7] = TxByte[6];
  TxByte[6] = TxByte[5];
  TxByte[5] = TxByte[4];
  TxByte[4] = SensorVal1;
}

void ReceiveData() {
  // READ AVAILABLE BYTES INTO 32BYTE BUFFER (while data is available)
  for ( byte d = 0; Wire.available() > 0 ; d++) {
    RxByte[d] = Wire.read();
  }
  //set a trigger event
  datareceived = 1;
  Serial.println( "receive" );
}

void ProcessData() {
  Serial.println( "process" );
  // check what the data is an who its for then do something with it (RECEIVE TABLE)
  if (RxByte[0] == MY_ADDRESS) { // for this device
    switch (RxByte[2]) {
      case 1:       Serial.print("RECEIVED DATA FOR THIS DEVICE, = "); Serial.println( RxByte[4] ); break;
      case 255:       Serial.println("RECEIVED A REQUEST FOR DATA, SENDING READ FROM  > Sensor1"  ); ActAsMaster();              break;
      default:      Serial.print("D-Type byte = error"); Serial.println(RxByte[2]);                   break;
    }
  }

  else if (RxByte[0] == 0) {       //handle broardcast
    Serial.print( "RECEIVED BROADCAST FROM DEVICE = " ); Serial.println( RxByte[1] );
    Serial.print( "DATASIZE = " ); Serial.print( RxByte[3] ); Serial.println( " Bytes" );
    Serial.print( "DATABYTE 1 = " ); Serial.println( RxByte[4] );
    Serial.print( "DATABYTE 2 = " ); Serial.println( RxByte[5] );
    Serial.print( "DATABYTE 3 = " ); Serial.println( RxByte[6] );
    Serial.print( "DATABYTE 4 = " ); Serial.println( RxByte[7] );
    Serial.println( "DONE" );
  }
  Serial.println(  );
}

void BROADCAST() {
  // this is transmission table to send data to everyone
  Serial.println("Sending broadcast");
  byte count = 0;
  byte errorcode;
Tx:
  Wire.beginTransmission (0);                       // broadcast
  Wire.write(0);                                    // ADDRESS =0 BROARDCAST
  Wire.write(MY_ADDRESS);                           // SEND my address
  Wire.write(0);                                    // DATA TYPE CODE
  Wire.write(4);                                    //number of bytes being sent
  Wire.write(TxByte[4]);                            //data
  Wire.write(TxByte[5]);                            //data
  Wire.write(TxByte[6]);                            //data
  Wire.write(TxByte[7]);                            //data
  errorcode = Wire.endTransmission ();
  if (errorcode != 0 && count <= 2) {
    count++;
    Serial.println( "fail" );
    goto Tx;
  }
  //reset timer to run this again
  timing_loop = millis() + 10000 ;
  Serial.println(  );
}

void ActAsMaster() {
  //this is transmission table to send data to specific device OR return a result of a task
  Serial.println("Sending data");
  byte count = 0;
  byte errorcode;
Tx:
  Wire.beginTransmission (RxByte[1]);               //the return address
  Wire.write(RxByte[1]);                            //recipient address (return)
  Wire.write(MY_ADDRESS);                           // SEND MASTER ADDRESS
  Wire.write(1);                                  // DATA TYPE CODE
  Wire.write(1);
  Wire.write(SensorVal1);
  errorcode = Wire.endTransmission ();
  if (errorcode != 0 && count <= 2) {
    count++;  // if fail and not exceed count try again
    Serial.println( "fail" );
    goto Tx;
  }
  Serial.println(  );

}
void REQUEST() {
  //this is transmission table to send data request of partner device
  Serial.println("Sending request");
  byte count = 0;
  byte errorcode;
Tx:
  Wire.beginTransmission (PARTNER_DEVICE);               //the return address
  Wire.write(PARTNER_DEVICE);                            //recipient address (return)
  Wire.write(MY_ADDRESS);                           // SEND MASTER ADDRESS
  Wire.write(255);                                  // DATA TYPE CODE (MASTER ADDRESS)
  Wire.write(0);
  errorcode = Wire.endTransmission ();
  if (errorcode != 0 && count <= 2) {
    count++;  // if fail and not exceed count try again
    Serial.println( "fail" );
    goto Tx;
  }
  Serial.println(  );
}
6
  • i tried to keep it minimal but still show complete capability.. i guess im not very good at minimal.
    – R Lloyd
    Aug 12, 2017 at 7:35
  • //so the basics of the tranmission table - are you writing code, or answering a question?
    – Nick Gammon
    Aug 12, 2017 at 8:01
  • just trying to clearly explain whats going on.. this code is just a massively edited piece of a major project so it was significantly easier to quickly assemble this than try to explain an answer.. after previous comment >>Please, post a minimal but complete sketch that compile OK. Your code is confusing for beginners, because it mix free text with code and have code not inside any function (like loop). – Look Alterno yesterday<<
    – R Lloyd
    Aug 12, 2017 at 13:25
  • thanks for detailled example. My code is the same like the first example code and works. I have to test the second example. But i dont see any software colision prevention. I would prefer to ignore collided and corupted frames just by a "software master", also there will be a changeable master, which will check communication and if there is collision than will try to fix it. Actually any software solution is better than a third line for me.
    – Andy
    Aug 12, 2017 at 22:22
  • i havent done anything with collision detection and correcting in software as my hardware solution prevents it but this part >>errorcode = Wire.endTransmission (); if (errorcode != 0 && count <= 2) { count++; // if fail and not exceed count try again Serial.println( "fail" ); goto Tx; } should at least try to make sure the packet is delivered as its probably unlikely for the collision to happen 3 times.
    – R Lloyd
    Aug 13, 2017 at 23:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.