Creating an I2C Slave Device - Why does the master need resetting?

Hi,

This is my first post on the forum, but I've found it very useful in the past reading through other peoples problems and projects - thank you everyone!

So the project: I'm trying to design a slave I2C device that will respond with various ADC values and measured data values, dependent on what command the master I2C device sends to it. All seems to be working OK but the system only works if the slave devices code starts running before the masters code starts running? If the master code is running when the slave starts up, the master has to be reset before the I2C starts to work.

Here is the code, I've trimmed it right back to bare bones to try and help troubleshoot the problem:

Master:

#include <Wire.h>

  byte z = (0x22);
  
  void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() {

    Wire.beginTransmission(8); 
    Wire.write(z);
    Wire.write(0x23);
    Wire.endTransmission();

    delay(10);
    
    Wire.requestFrom(8, 2);
    while(Wire.available() == 0); //wait for reply from slave
    byte c = Wire.read();
    byte d = Wire.read();

    Serial.println(c);
    Serial.println(d);

    //Combinte byte c & d to form to display current measurement
    int reportedCurrent = (int)d << 8; 
    reportedCurrent = reportedCurrent + c;
    Serial.println(reportedCurrent);

    z=z+1;  // Increment x each loop to check value reported is 'live'
    
    delay(500);
}

Slave:

#include <Wire.h>

volatile byte x = 0;  //Volatile so variable can be changed in interupt
volatile byte y = 0;  //Volatile so variable can be changed in interupt

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output

}

void loop() {
  delay(100);
  //Serial prints to check x & y can be read outside I2C interupt:
  Serial.print("X is now:");
  Serial.println(x); 
  Serial.print("Y is now:");
  Serial.println(y); 
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
   while(Wire.available()) { 
        x = Wire.read();    
        y = Wire.read();
  }
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {

  Wire.write(x+1);
  Wire.write(y+1);
}

So in essence the master is sending a couple of bytes of data, the slave receives this data, it would then do something with this data (in this example it's just adding 1 but it would probably look up an ADC value) and then sends some data back to the master.

If I run the master code first, this is what I see on the slave serial monitor once the slave starts up:

X is now:0
Y is now:0
X is now:0
Y is now:0
X is now:0
Y is now:0
...

It will continue doing this forever.

If I now reset the master, this is what the slave prints out:

X is now:34
Y is now:35

and X will gradually increment up which is what should be happening! You can also now see the values being correctly reported back to the Master.

Can anyone see what is going wrong?

To be clear both boards are fully powered up all the time, it's just the order that the boards are reset in - this could cause issues in the use of the slave gadget in future.

Does not matter in what order you execute the sketches of Master (UNO) and Slave (NANO), the Serial Monitors of the Arduinos show result as you are expecting. It has been possible by doing some moderation on your sketches. You may study the attached file to get better knowledge on I2C Operation and Programming. You may have to wait for a couple of seconds to give time for the Master and Slave to get synchronized. There is no need to press the reset button of Master.

Modified Master Sketch:

#include <Wire.h>

byte z = 0x22;//(0x22);

void setup() 
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() 
{
  Wire.beginTransmission(8);
  Wire.write(z);  //chnges by 1 in every 2.5 sec
  Wire.write(0x23);  //35 remains constant
  Wire.endTransmission();

//  delay(10);

  Wire.requestFrom(8, 2);
 // while (Wire.available() == 0); //wait for reply from slave
  byte c = Wire.read();
  byte d = Wire.read();

  Serial.println(c);   //will show 35, 36, ....
  Serial.println(d);  //will show 36 always
  Serial.println("=======================");

  //Combinte byte c & d to form to display current measurement
 // int reportedCurrent = (int)d << 8;
 // reportedCurrent = reportedCurrent + c;
 // Serial.println(reportedCurrent);

  z = z + 1; // Increment x each loop to check value reported is 'live'

  delay(2500);
}

Modified Slave Sketches:

#include <Wire.h>

volatile byte x = 0;  //Volatile so variable can be changed in interupt
volatile byte y = 0;  //Volatile so variable can be changed in interupt
volatile bool flag1 = false;

void setup()
{
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
}

void loop()
{
  //delay(100);
  //Serial prints to check x & y can be read outside I2C interupt:
  if (flag1 == true)
  {
    Serial.print("X is now:");
    Serial.println(x);
    Serial.print("Y is now:");
    Serial.println(y);
    Serial.println("=====================");
    flag1 = false;
  }
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  //while (Wire.available())
  // {

  x = Wire.read();
  y = Wire.read();
  flag1 = true;
  // }
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent()
{
  Wire.write(x + 1);  //Master will show incrementing value
  Wire.write(y + 1);  //Master will show always 36
}

Master Serial Monitor:
smwM.png
Figure-1:

Slave Serial Monitor:


Figure-2:

smwM.png

Ch-6(I2C)doc.doc (1.43 MB)

Thank you Golam, that did the trick! :smiley:
That document is definitely worth a read for anyone else trying to understand I2C Master/Slave on Arduino.