I2C communication control variables

Hello!

I have an Udoo and arduino connected via SCL and SDA pins, communicating one to each other via I2c Protocol. The udoo is the master and the arduino is the slave.

First of all I ask if the slave is available:

#include <Wire.h>

const int16_t I2C_SLAVE = 0x08;

void setup() {
  Serial.begin(115200);                      // start serial for output
  Wire.begin();  // join i2c bus 
}

void loop() {



  if (Serial.available() > 0){
    Wire.requestFrom(I2C_SLAVE, 1);  // request 1 bytes from slave device #8

    
    }
  }
}

SLAVE

#include <Wire.h>

bool available;

const int16_t I2C_SLAVE = 0x08;

void setup() {
  Wire.begin(I2C_SLAVE);  // join i2c bus with address #8
  Wire.onRequest(requestEvent);             // register event
}

void loop() {}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  Wire.write(available);  // respond with 1 byte
  // as expected by master
}

If it's available then I write to the slave some instructions. The slave then changes the available variable se and starts doing some tasks that take some time; when the task is finished it changes available again. The problem comes when the master tries to ask if the slave is busy when its performing those tasks. It always says is free even If I changed the value.

I've read it's because I2C calls are interrupts so the variable should be a volatile, but that doesn't seem to work.
I'm not doing anything inside the loop, everything is coded using OOP. I can provide more code if necessary.

Any clue why this is happening?

Where is the actual state assigned to this variable?

It's usually 0, then 1 if its busy.
Master reads it accurately because If i don't change the variable back to 0 when the task is done , it reads 1.

After all I don't understand your problem.

When master ask the first slave , it always reads is available.
The communication is working because if I don't change the variable back at the end of the perform task, it does what is supposed to do.(go to slave 2)

1.

As slave Address is always 7-bit, then why not the following code:
#define I2C_SLAVE 0b0001000

2.

As the Wire.write() method asks for 8-bit argument, then why not
byte available;

3.
You may aadd the following codes in the Master Sketch to see if something is coming from Slave in response to Wire.requestFrom() command:

if (Serial.available() > 0)
{
    Wire.requestFrom(I2C_SLAVE, 1);  // request 1 bytes from slave device #8
    byte y = Wire.read();
    Serial.println(y, HEX);
}

4.
And include the following codes in the Slave Sketch:

byte available = 0;
void requestEvent() 
{
    Wire.write(++available);  //
}

This may help you understand:

1 Like

How do you distinguish the slaves?
If a slave does not respond then the master receives 0xFF, what may mean "is available"?

Please show master code that better reflects what you want to do.

Why do you not initialize available=true?
How does a slave receive values sent by the master?

If you can not provide code that reflects halfway what you pretend to do then I'm out.

  1. 0x08 is more readable than 0b000100
  2. You are right , I will try this.
  3. The communication is working , master reads 0 the byte correctly , 0 for false and 1 for true.
  4. Will try to use this to debug.
    Thank you for the suggestions.

Welcome to the forum.

Can you give a link to the Udoo board ? Is that board running Arduino code ?

I think you can read the value of the variable now, it is solved ?

A few tips:

  • Please do not use "available" when using the I2C bus, because there is a "Wire.available()". Can you use a "busy" variable and say that a Slave can be "busy" ?
  • Please do not use the term "SerialFlush", because there is a "Serial.flush()" function, but that does not do what you think it does. Just keep it out of the flow chart and deal with extra characters in the code.

There is probably another way to deal with this. If you send a command to a busy Slave, then the Slave could buffer the command and execute the command whenever the Slave is ready for it.

1 Like

Each slave has its own adress
const int16_t Slave = 0x08;
currently I have 2 slaves only ,using 0x08 and 0x16
The slave joins the bus via the line:
Wire.begin(Slave); // join i2c bus (as slave)

No, changed the byte it sends to 1 and 2 instead of 0 and 1 ;still always reads 1;

Master recieves Serial data then sends it to slaves.
MASTER CODE:

void loop()
{
  if (Serial.available() > 0) {
    Serial.readBytesUntil('@',buffer,2);  
    target = buffer[0] << 8 | buffer[1];

    String a = String(target);

    //Ask the first slave 
    Wire.requestFrom(Slave0, 1);
    byte x = Wire.read();
    Serial.println(x);  //Always returns 0
    if (x == 1) {
      Wire.beginTransmission(Slave0);  // transmit
      for (int i = 0; i < a.length(); i ++) {
        Wire.write(a[i]);
      }
      Wire.endTransmission();          // stop transmitting
    }else{
      
// Ask second slave
    Wire.requestFrom(Slave1,1);
    int y = Wire.read();
    if( y == 1){
      Wire.beginTransmission(Slave1);
     for (int i = 0; i < a.length(); i ++) {
        Wire.write(a[i]);
      }
      Wire.endTransmission();          // stop transmitting
    }else{
      serialFlush();
    }
    }
    }
  }

Slave CODE:

void receiveEvent(size_t howMany) {

  busy = 2;
  (void)howMany;
  int aux = 0;
  while (0 < Wire.available()) {  // loop through all but the last
    buffer[aux] = Wire.read();
    aux++;
  }

  s = buffer;
  memset(buffer, 0, sizeof(buffer));

 //data manipulation
  pos = s.toInt();
  pos = map(pos, 0, 785, 0, M.limSup);

  //FUNCTION CALL
  M.gotox(pos);
  busy = 1;

}

void requestEvent() {

  Wire.write(busy); //Always writes 1 

}

This part works fine, the part that doesn't work are the control variables that are getting changed back to 1 before the function call finishes.

Hi! Thanks for the reply

Yes.

No.

I changed it to busy

Serial flush is just a simple function that is supposed to discard the values from the serial that don't get read, it never gets to that point atm so not a concern.

void serialFlush() {
  while (Serial.available() > 0) {
    char t = Serial.read();
  }
}


I've made some discoverys of why is behaving the way it is, I will write another post about it to try to illustrate it.

This may be the cause of my problems, the tranmission waits for the full execution of the slave receiveEvent to close the transmission, so the master is not reading a new value from the slave until it has finished the task.
Any suggestions of how to deal with this?

//Ask the first slave 
    Wire.requestFrom(Slave0, 1);
    byte x = Wire.read();
    Serial.println(x);  //Always returns 0
    if (x == 1) {
      Wire.beginTransmission(Slave0);  // transmit
      for (int i = 0; i < a.length(); i ++) {
        Wire.write(a[i]);
      }
      
      //Calls recieve event
      //Value is changed to 2
      //performs activity
      //Waits until the full execution of the function.
      //Value is changed to 1;
           
      Wire.endTransmission(); 
      
      //In the next iteration the value is 1 because it has finished the transmission.

I've solved it.
the commuication is synchronus so is waiting for the full execution of the function before going through the loop again. The workaround I did is to just change the variables in the slave master communication and then in the loop function check if they are true, perform the steps then change the variable back to false.

Something like:

void receiveEvent(size_t howMany) {

  (void)howMany;
  int aux = 0;
  while (0 < Wire.available()) {  // loop through all but the last
    buffer[aux] = Wire.read();
    aux++;
  }

  s = buffer;
  memset(buffer, 0, sizeof(buffer));

 //data manipulation
  pos = s.toInt();
  pos = map(pos, 0, 785, 0, M.limSup);
 byte = 2;

}
void loop() {
  if(busy == 2){
    Serial.println("I'm in.");
    M.gotox(pos); // function call
    busy = 1;
  }
  
}

Thank you everyone for the sugestions, getting the perspective of another person in a problem really helps. Hope you have a great day. :smiley:

I have seen, for the first time, in your post for the variable howMany to be of type size_t instead of int; is there any advantage or technical reason for doing so?

Why do you send data to the second slave only if the first slave is busy? Why is the second slave blocked by the first slave?

1 Like

No, I just followed the Wire.h github examples, they are for esp8266 but took it as a reference. I remember having problems with the compilation of that function when trying to use int. In the latest arduino version 2.0.3 I had some compilation problems so I switched back to 1.8.9.

Honestly the functions and the syntaxis are very different(functions don't take parameters, half of the addresses are not necessary, you can't define pins...) but the arduino language reference
lacks some examples of the functions should work or the communication between multiple arduinos.

Because if the first slave can take care of the task, the second doesn't have to.
The second slave only does the task if the first one is busy with another task.

Arduino Platform has provided a workspace in the form Arduino Forum, Arduino Reference Manual, Arduino UNO, IDE, and Serial Monitor. The MCU manufacturer has given the comprehensive data sheets. It is the liberty of the user to play around with his ideas in the given workspace and post the findings in the Forum.

1 Like

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