Wire Library Hanging Arduino

I am trying to Establish a Master-Slave Communication Between 4 Arduino Uno’s as One master and 3 Slaves

can anyone please Tell me why I am facing following Problems ?

Pro 1: Arduino Suddenly Hangs After 2 or 3 or 10 reads from Its Slaves

Pro 2: If I Disconnected the Power for One Slave Master Hangs. It is not giving me any Reading From Other arduino’s

Is there anyway I can tell the Master to Continue if One slave Didn’t respond?

Master Code :

#include<Wire.h>

#define slaveA 8
#define slaveB 9
#define slaveC 10

#define byteLength 2
int noOfValues = 0;
boolean chRead = false;
char ch;
unsigned long int interval = 10; // Duration for Requesting the Data
unsigned long int prevMillis = 0;
unsigned long int newMillis;

void setup() {
  Wire.begin();//Address is Optional For Master
  Serial.begin(9600);
  Serial.println("Please enter --> a to read the data from Slave 1 ");
  Serial.println("Pleae enter -->  b to read the data from slave 2 ");
  Serial.println("Please enter --> c to read the data from slave 3");
  Serial.println("Please enter --> i to Change the Duration (Default Duaration is 10 Seconds)");

}

void loop() {
  if (Serial.available()) {
    ch = Serial.read();
    chRead = true;
  }

  if (chRead) {
    switch (ch) {
      case 'a':
        requestSlaveA();
        break;

      case 'b':
        requestSlaveB();
        break;

      case 'c':
        requestSlaveC();
        break;

      case 'i':
        changeInterval();
        break;

      default:
        Serial.println("Please check the Command");

    }
    chRead = false;
  }

  newMillis = millis();
  if (newMillis - prevMillis >= interval * 1000) {
    prevMillis = newMillis;
    requestSlaveA();
    delay(100);
    requestSlaveB();
    delay(100);
    requestSlaveC();
    noOfValues++;
    Serial.print("The No Of vaues Received is -->\t");
    Serial.println(noOfValues);
  }

}

void requestSlaveA() {
  if (Wire.requestFrom(slaveA, byteLength)) {
    Serial.print("Data from Slave A is --> \t");
    Serial.println(getData());
  } else {
    Serial.println("Something Wrong with Slave A");
  }
}


void requestSlaveB() {
  if (Wire.requestFrom(slaveB, byteLength)) {
    Serial.print("Data from the Slave B is --> \t");
    Serial.println(getData());
  } else {
    Serial.println("Something Wrong with Slave B");
  }
}

void requestSlaveC() {
  if (Wire.requestFrom(slaveC, byteLength)) {
    Serial.print("Data from the Slave C is --> \t");
    Serial.println(getData());
  } else {
    Serial.println("Something wrong with Slave C");
  }
}


int getData() {
  int receivedValue  = Wire.read() << 8;
  receivedValue |= Wire.read();
  return receivedValue;
}


void changeInterval() {
  Serial.println("Please enter the New Duration In Seconds");
  while (!Serial.available())
  {
    //Wait until User Responds
  }
  interval = Serial.parseInt();
  if (interval) {
    Serial.print("The New Interval is -->\t");
    Serial.print(interval);
    Serial.println("  Seconds");

  } else {
    Serial.println("PLease Enter the Valid Number");
    changeInterval();
  }
}

Slave Code :

#include<Wire.h>
#define slaveA 8
#define AnalogInputPin A0

void setup() {
  Wire.begin(slaveA);
  Wire.onRequest(requestCallback);
}

void loop() {
  // Do nothing
}

void requestCallback() {
  int input = analogRead(AnalogInputPin);
  // To send multiple bytes from the slave,
  // you have to fill your own buffer and send it all at once.
  uint8_t buffer[2];
  buffer[0] = input >> 8;
  buffer[1] = input & 0xff;
  Wire.write(buffer, 2);
}

Where is your sketch hanging? What is the last message you get on the serial monitor?

SurferTim:
Where is your sketch hanging? What is the last message you get on the serial monitor?

it is Hanging after this line.

 Serial.print("The No Of vaues Received is -->\t");
    Serial.println(noOfValues);

After that it is not responding to the Serial Commands Also.

You need to put more debugging Serial.print calls in your code.

I don’t understand the purpose of your Serial stuff. You send a character to read one of the I2C devices, but you are reading all of them every 10 seconds anyway. ??

edit: I have not tested the I2C digital pins on the Uno. If the power is removed, it could cause the I2C lines to be pulled LOW by the powered down Uno. That would effectively disable the entire I2C bus. Just a thought.

This is when an oscilloscope comes in really handy.

SurferTim:
You need to put more debugging Serial.print calls in your code.

I don't understand the purpose of your Serial stuff. You send a character to read one of the I2C devices, but you are reading all of them every 10 seconds anyway. ??

This is when an oscilloscope comes in really handy.

The Master Send the Requests to the Each Slave with Fixed Duration (i used Millis() for that)

I used Serial Commands to Request Each Slave Separately To get the instantaneous values from specific Slaves.

Interval 10 sec is for Testing Purpose later I need to change that to the 30 min or So.

Perform a test on your I2C system. If you do not have an o-scope, power down one of the Unos, and disconnect the I2C lines from that Uno to the master. Does the master sketch continue to run?

edit: You don't need to power down the slave device. Just remove the I2C SCK and SDA lines that connect it to the master.

Hi

If I remember correctly on the slave side when you call the Wire.begin(slaveA); function, it does set indeed the slave address and also call twi_attachSlaveTxEvent(onRequestService); and twi_attachSlaveRxEvent(onReceiveService); so I would be tempted to reverse the order of the calls in the slave

void setup() {
  Wire.onRequest(requestCallback);
  Wire.begin(slaveA);
}

also when you do

 int receivedValue  = Wire.read() << 8;

in

int getData() {
  int receivedValue  = Wire.read() << 8;
  receivedValue |= Wire.read();
  return receivedValue;
}
Wire.read()

is a byte, so when you switch it left by 8 bits you (probably I’m not 100% sure) empty it (becomes zero) and then you assign and cast that into the int. I think you need to do that in 2 steps

int getData() {
int receivedValue  = (int) Wire.read(); // now it's an int
receivedValue<<=8; // move to the MSB what we just received
receivedValue |= (int) (Wire.read()); // add the LSB piece
}

SurferTim:
Perform a test on your I2C system. If you do not have an o-scope, power down one of the Unos, and disconnect the I2C lines from that Uno to the master. Does the master sketch continue to run?

edit: You don't need to power down the slave device. Just remove the I2C SCK and SDA lines that connect it to the master.

I will let you know the Output As Soon as i Tested it.

J-M-L:
Hi

If I remember correctly on the slave side when you call the

Wire.begin(slaveA);

functions, it does set indeed the slave address and also call twi_attachSlaveTxEvent(onRequestService); and twi_attachSlaveRxEvent(onReceiveService);
so I would be tempted to reverse the order of the calls in the slave

Thanks for that … i Will try that also ans post you the Output.

SurferTim:
Perform a test on your I2C system. If you do not have an o-scope, power down one of the Unos, and disconnect the I2C lines from that Uno to the master. Does the master sketch continue to run?

edit: You don't need to power down the slave device. Just remove the I2C SCK and SDA lines that connect it to the master.

Hi,

When I removed the I2C lines from the Slave a It is Showing a Error Code as :
There is a problem with Slave A As per the code.

But when i removed the power from the slave the Master Hangs..

After this if I removed the Wires from Slaves (When the Slave is in Powered Down) Master is receiving the data From Slaves with Error Message.

So Is there any I can overcome that hanging when slave is powered Down? :slight_smile:

I can reproduce your problem with my Mega 2560. If the SDA or SCL lines are held LOW (like powered off), the I2C bus freezes. Another user may be reporting this as a bug.

I managed to reproduce your issue using a RTC/EEPROM board (ZS-042) by disconnecting the power at random times. I could get it to hang at endTransmission() and requestFrom().

You can try the following before you call endTransmission() and before you call requestFrom().

  // check if the I2C lines are LOW
  if (digitalRead(SCL) == LOW && digitalRead(SDA) == LOW)
  {
    Serial.println("Bus error");
    return;
  }

The code tests if both I2C lines are low. This condition occurs if a slave is powered down and it can occur if a transmission is in progress(it can happen in a multimaster setup). In the first case, you don’t want to try as the code ‘hangs’, in the second case it’s an additional prevention of collisions (though the Arduino hardware and library do handle it).

Below was my eeprom read code to test

byte readEeprom(byte devAddress, int memAddress, byte * ptr, int len)
{
  // clear receive buffer
  memset(ptr, 0, len);

  // setup writing to eeprom
  Serial.print("beginTransmission");
  Wire.beginTransmission(devAddress);
  Serial.println(" done");
  // setup to write to given memory address
  Serial.print("write");
  Wire.write(memAddress & 0xFF);
  Wire.write(memAddress >> 8);
  Serial.println(" done");


  // if both lines are low, assume the eeprom is powered down
  if (digitalRead(SCL) == LOW && digitalRead(SDA) == LOW)
  {
    Serial.println("Bus error");
    return -1;
  }


  // send the memory address to eeprom
  Serial.print("endTransmission");
  byte rc = Wire.endTransmission();
  Serial.println(" done");
  // check result
  if (rc != 0)
    return rc;

  // if both lines are low, assume the eeprom is powered down
  if (digitalRead(SCL) == LOW && digitalRead(SDA) == LOW)
  {
    Serial.println("Bus error");
    return -1;
  }


  Serial.print("requestFrom");
  Wire.requestFrom(EEP_ADDRESS, len);
  Serial.println(" done");

  int index = 0;
  while (Wire.available())
  {
    if (index < len)
      ptr[index++] = Wire.read();
  }

  return 0;
}

// Edit: changed A4 and A5 (for Uno) to SCL and SDA so it should be board independent.
// Edit 2: change address byte sequence

Nice sterretje! However, I modified you code slightly. I used an OR so if either line is LOW, it aborts. Seems to work fine.

  // check if the I2C lines are LOW
  if (digitalRead(SDA) == LOW || digitalRead(SCL) == LOW)
  {
    Serial.println("Bus error");
    return;
  }

edit: But it does not free up the I2C bus. The other slave devices on the I2C bus will still be unreachable. :frowning:

I had that initially; decided to change it to AND as the error condition is actually when both lines are low.

Either the SDA or SCL lines LOW will cause the freeze. But like I said above, it still does not free up the bus though. It will be locked up until that device is powered up or removed from the I2C bus clock and data lines.