I2c - arduino i2c communication uno to mega

Hi there – been working on sending data back and forth between a mega & uno – can't quite head around why every thing seems to happen in the setup, wire.onRequest(requestevent, and some websites have wire.read in the main function, others use a differnet void function, everything seems to work, but I can understand why it keeps sending data across (with nothing in the main loop), when void setup is meant for things to happen once. Is that right ? - as any one got any simlar code ?

Post the examples and maybe we can help.

Not really. This statement installs a request handler that is activated whenever the slave receives data from a master. The master has to start a transmission what does not require a callback function.

Apart from dedicated master and slave(s) each Arduino can act both as a master and slave, with multi-master conflicts detected in hardware.

Hi - thank you y=for your interest. Working with a dab radio shield on arduino uno, to mega on i2c communication. This seems to work ok, dab.time is sent from the uno to the mega, and the mega sends back a trigger to say radio, when the trigger is on not sure at the moment where to the code to sent the station info back to the mega. ( eg wire.write (dab.station) in the main loop, the same requestEvent() ?

// radio codeed sending data to SLAVE/mega
// dervice on address 9,
#include <SPI.h>
#include <DABShield.h>
//#include <SoftwareSerial.h>
#define DAB_SPI_BITBANG
#include <Wire.h>
/
void setup() {
Wire.begin(9); //i2c address 9
Serial.begin(9600);

Wire.onRequest(requestEvent);// register event
Wire.onRequest(seedstation);
Wire.onReceive(oncheck);

}
void requestEvent() {
Wire.write (totalst);
delay (50);
Wire.write (dabtime.Hours);
delay (50);
Wire.write(dabtime.Minutes);
delay (50);
Wire.write (dabtime.Days);
delay (15);
Wire.write (dabtime.Months);
delay (15);
Wire.write (dabtime.Year);
delay (15);

}

void loop() {

if (onoff != 0) {
dabradioon ();

}
mega code
#include <Wire.h>
#define SLAVE_ADDR 9

void setup() {

Wire.begin();Serial.begin(9600); Wire.beginTransmission(SLAVE_ADDR);
totalst = Wire.read(); Serial.print (totalst);
dhr = Wire.read (); Serial.print (dhr); delay (15);
dmin = Wire.read ();Serial.print (dmin); delay (15);
dsec = Wire.read ();;Serial.print (dsec); delay (15);
}
void loop() {
if (trigger == 1) {
Wire.write(1);
}
}

The MEGA codes are not complete. Please, post full codes with code tags like below:

To read data from Slave's buffer, the Wire.read() code must go under void receiveEvent(int howMany){} routine.

To write data into Wire.buffer for onward transmssion to Slave, the Wire.write() code must go under void sendEvent(){} routine.

Wire.beginTransmission(sAddr) must have Wire.endTransmission() as terminating code.

//mega code
#include <Wire.h>
#define SLAVE_ADDR 9

void setup() 
{
  Wire.begin(); 
  Serial.begin(9600); 
  
  Wire.beginTransmission(SLAVE_ADDR);
  totalst = Wire.read(); Serial.print (totalst);
  dhr = Wire.read (); Serial.print (dhr); delay (15);
  dmin = Wire.read (); Serial.print (dmin); delay (15);
  dsec = Wire.read ();; Serial.print (dsec); delay (15);
}

void loop() 
{
  if (trigger == 1) 
  {
    Wire.write(1);
  }
}

Please see the IDE Examples for the Wire library.

You can take a look at I2C Communication between Two Arduino Boards | microdigisoft.com example.

Thanks for your help. Have used a sub-routine, which is working better, have looked at lots of examples, but not sure how to use the trigger for the uno to send data back, to put it in the setup loop or the requestEvent loop ?

Best tutorial for sending multiple variable's back and forth.

Yes everything does happen just once in the setup. The setup is "setting up" the "wire.onRequest" and/or "Wire.onReceive" interrupt routines. Every time there is a packet of data being sent from the master to the slave, the Wire.onReceive routine on the Slave gets called. When it is called, we are temporarily exiting the "void loop(){" and going into the Wire.onReceive routine to collect the data. We then return to what we are doing in the loop. That's why it's called an interrupt routine, because it interrupts whatever other task is being tended to in the loop. You can then do whatever you want with this data when you return to the void loop.

The Wire.onRequest interrupt routine is the same process. When the master requests data from the slave, the slave exists the main loop and into the specified interrupt routine. Once in this routine, it sends data to the master.

When you see "wire.read" in the main loop, it is for when the Master has just made a request from the slave, and is now reading this data that the slave is providing it. This can be done in the main loop OR also occur in another function that is called to from the main loop. See the example I linked above where the master is making a request while in the "void requestData() {" function. This "void requestData() {" function is not like the other functions I mentioned, in that it isn't an interrupt routine.

When you see "wire.read" outside of the main loop (or not in a function called to from the main loop), it is for when the slave has been sent data from the master, and it's now just entered into it's Wire.onReceive interrupt routine, and is reading the data that the master has just sent it.

Here is a simple tutorial for you; where, you may find some answers of your queries.

1. This is the I2C Bus wiring connection (Fig-1) between MEGA (the MAster) and UNO (the Slave). The Slave is assigned the 7-bit I2C address of: 0010011 (0x13 in hex).

i2cMegaUno
Figure-1:

2. MEGA executes the following codes in the setup() function to check that Slave is present (Roll Calling):

Wire.beginTransmission(0x13);  //slave's 7-bit I2C address
byte busStatus = Wire.endTransmission();
if(busStatus !=0)
{
    Serial.print("Slave is not found...!");
    while(1);   //wait for ever
}
Serial.println("Slave is presnt.");

The value 0 is automatically assigned to the variable busStatus when Master receives an ACK (acknowledgement) signal (detection of LOW level on SDA line during 9th SCL pulse), which is assered by the Slave when the upper7-bit of the transmitted I2C Bus address (I2C address + 0 at the right most position = 00100110) matches with Slave's pre-programmed 7-bit I2C address (0010011).

3. MEGA sends a data byte (0x1234) to the Slave by executing the following codes in the setup() function:

int y = 0x1234;
Wire.beginTransmission(0x13);  //slave's 7-bit I2C address
Wire.write(highByte(y)); //higher byte is being sent first
Wire.write(lowByte(y));
busStatus = Wire.endTransmission();

Three ACK signals will be coming from Slave. When all these signal are individually 0, then the busStatus will be asigned 0. The user may check it; but, it is not really needed as the MEGA has already checked that the Slave is present.

4. The data bytes (0x12 and 0x34) of Step-3 are automatically stored in the Wire Buffer of the Slave via unseen interrupt process. In the Slave sketch, the user calls the following rotuine/codes to read the arrived data bytes from the Wire Buffer and save them into user variables for further processing/displaying.

volatile bool flag = false;    //declare above setup() function
byte xh, xl;     //declared above setup() function
Wire.onReceive(receiveEvent);  //should be declared in the setup() function
void receiveEvent(int howMany)   //howMany is always equal to data bytes received = 2
{
     xh = Wire.read();
     xl = Wire.read();
     flag = true;     //to indicate that the Slave visited the receiveEvent() routine to get data
}

5. The Slave UNO executes the following codes in the loop() function to show the collected data of Step-4 onto its Serial Monitor.

if(flag == true)
{
   Serial.print(xh, HEX);
   Serial.println(xl, HEX);
   flag = false;
}

6. The Master MEGA requests the Slave to get a data item (say, 0x89AB). This may be done by executing the following code in the setup() or loop() function.

Wire.requestFrom(0x13, 2);    //arg1 = Slave address, arg2 = number of bytes requested

7. In response to the command of Step-6, the Slave enters into the following ISR routine and puts the data item (0x89AB) into its Wire Buffer for onward transmission to the Master.

int y = 0x89AB;   //declare above setup() function
Wire.onRequest(sendEvent);   //must be declared in the setup() function
void sendEvent()
{
    Wire.write(highByte(y)); //Wire.write(y) is not valid as I2C is a byte oriented protocol
    Write.write(lowByte(y));  
}

Data from Slave's Wire Buffer are automatically transferred into the Master's Wire Buffer over the I2C Bus.

8. Master MEGA executes the following codes (after the execution of requestFrom() command) to collect and display the arrived data from its Wire Buffer.

byte h = Wire.read();
byte l = Wire.read();
Serial.print("Data received from Slave: ");
Serial.print(h, HEX);  //shows: 89
Serial.println(l, HEX);   //shows: AB

9. Now, open IDEs for both Master and Slave; place the above codes (in addition to other codes not shown above) in their appropriate places; execute the resultant sketches and observe the results.

10. Master Sketch:

#include<Wire.h>

void setup()
{
  Serial.begin(9600);
  Wire.begin();
  Wire.beginTransmission(0x13);  //slave's 7-bit I2C address
  byte busStatus = Wire.endTransmission();
  if (busStatus != 0)
  {
    Serial.print("Slave is not found...!");
    while (1);  //wait for ever
  }
  Serial.println("Slave is present.");

  int y = 0x1234;
  Wire.beginTransmission(0x13);  //slave's 7-bit I2C address
  Wire.write(highByte(y)); //higher byte is being sent first
  Wire.write(lowByte(y));
  busStatus = Wire.endTransmission();

  Wire.requestFrom(0x13, 2);    //arg1 = Slave address, arg2 = number of bytes requested
  byte h = Wire.read();
  byte l = Wire.read();
  Serial.print("Data received from Slave: ");
  Serial.print(h, HEX);  //shows: 89
  Serial.println(l, HEX);   //shows: AB
}

void loop()
{

}

11. Slave Sketch:

#include<Wire.h>

volatile bool flag = false;    //declare above setup() function
byte xh, xl;     //declared above setup() function
int y = 0x89AB;   //declare above setup() function

void setup()
{
  Serial.begin(9600);
  Wire.begin(0x13);
  Wire.onReceive(receiveEvent);  //should be declared in the setup() function
  Wire.onRequest(sendEvent);   //must be declared in the setup() function
}

void loop()
{
  if (flag == true)
  {
    Serial.print("Data Received from Master: ");
    Serial.print(xh, HEX);
    Serial.println(xl, HEX);
    flag = false;
  }
}

void receiveEvent(int howMany)   //howMany is always equal to data bytes received = 2
{
  xh = Wire.read();
  xl = Wire.read();
  flag = true;     //to indicate that the Slave visited the receiveEvent() routine to get data
}

void sendEvent()
{
  Wire.write(highByte(y)); //Wire.write(y) is not valid as I2C is a byte oriented protocol
  Wire.write(lowByte(y));
}

Thank you very much GigaNerdTheReckoning for a nice simple detailed explanation,
GolamMostafa - thank you that looks some to work, which I will do tonight.

2 Likes

hi there - been working on the code - can't work out why it only sending to bytes of data ? would it be in the Wire.beginTransmission(0x13) (address, data) ? Thank should the answer is right in front of an being thick. Many thanks

Hi there - notice that in your examples you don't use the wire.RequestFrom.

Do you know the purpose of the Wire.requestFrom(slaveAddress, n) command?

Sorry - thanks for that - see it now - just getting myself in a knot !

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.