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"?
-
3If 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– Majenko2017年08月10日 21:09:23 +00:00Commented 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– Andy2017年08月10日 21:21:36 +00:00Commented Aug 10, 2017 at 21:21
-
2Andy, 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– Jot2017年08月11日 05:37:47 +00:00Commented Aug 11, 2017 at 5:37
-
I didn't knew about broadcasting, thanks. Your source seems to be very detailledAndy– Andy2017年08月11日 05:59:43 +00:00Commented Aug 11, 2017 at 5:59
1 Answer 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( );
}
-
i tried to keep it minimal but still show complete capability.. i guess im not very good at minimal.R Lloyd– R Lloyd2017年08月12日 07:35:54 +00:00Commented Aug 12, 2017 at 7:35
-
//so the basics of the tranmission table
- are you writing code, or answering a question?2017年08月12日 08:01:05 +00:00Commented 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– R Lloyd2017年08月12日 13:25:38 +00:00Commented 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– Andy2017年08月12日 22:22:11 +00:00Commented 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– R Lloyd2017年08月13日 23:09:23 +00:00Commented Aug 13, 2017 at 23:09