I2C Master Slave communication

Greetings all,

After a long time ago i was starting again with my arduino and I2C

My idea is that i want a slave arduino that holds an array of multiple names and that needs to go to the master arduino where is should get back in an array to get placed on the right LCD

I did want to send from the master which array i want but that didnt work as i wanted.

so i did write a send command from master to set a buffer ready on the slave side and after that i ask the slave to give me the info in the buffer.

i get stuck each time when i try to send and receive the multiple array's
Maybe someone sees directly where it goes wrong?

Slave arduino

char* ding[5] = {"", "Dit is display 1" , "Dit is display 2" , "Dit is display 3" , "Dit is display 4"};
char dataIn[16] = "";
#include <Wire.h>

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

void loop() {
  delay(100);
}

void requestEvent() {
  Wire.write(dataIn);
}

void receiveEvent(int howMany) {
  while (1 < Wire.available()) {
    char c = Wire.read();

  }
  int x = Wire.read();

  strcpy(dataIn, ding[x]);

}

Master Arduino

#include <Wire.h>

// Set the LCD address to 0x27 for a 16 chars and 2 line display
char dataIn[16] = "";
char* Receive[9] = {" ", " ", " ", " ", " ", " ", " ", " ", " "};

#include <Wire.h>

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

void loop() {
  //  SendToDisplay();
  ShowArray();
  delay(1000);

}
void ShowArray() {
  GetText(3);
  GetText(2);
  Serial.println(Receive[0]);
  Serial.println(Receive[1]);
  Serial.println(Receive[2]);
  Serial.println(Receive[3]);
  Serial.println(Receive[4]);
}


int GetText(int x) {
  Wire.beginTransmission(1); 
  Wire.write(x);              
  Wire.endTransmission();    


  Wire.begin();
  Wire.requestFrom(1, 16);    // request 6 bytes from slave device #8

  for (byte i = 0; i < 16 && Wire.available(); ++i)
  {
    dataIn[i] = Wire.read();
  }
    Wire.endTransmission();

  Receive[x] = dataIn;
  return dataIn;

  delay(1000);

}

this is what i get from the serial monitor on the master

Dit is display 2Dit is display 2
Dit is display 2Dit is display 2
 
 
 
Dit is display 2Dit is display 2
Dit is display 2Dit is display 2
 
 
 
Dit is display 2Dit is display 2
Dit is display 2Dit is display 2

Your Master (MEGA) and Slave (UNO) codes are simplified slightly. Now, the Master requests the Slave at every 1-sec interval to send the item-3 and item-2 of the array of the arrays. You may consult these codes to get rid of your confusion. (It is recommended to use values 0x08 to 0x7F as slave address.)

A: MEGA Master Codes (your codes but slightly modified)

#include <Wire.h>

// Set the LCD address to 0x27 for a 16 chars and 2 line display
char dataIn[16] = "";
char* Receive[9] = {" ", " ", " ", " ", " ", " ", " ", " ", " "};

#include <Wire.h>

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

void loop()
{
  //  SendToDisplay();
  ShowArray();
  delay(1000);
  Serial.println("=================");

}

void ShowArray()
{
  GetText(3);
  Serial.println(Receive[3]);
  GetText(2);
  Serial.println(Receive[2]);
  //  Serial.println(Receive[0]);
  //  Serial.println(Receive[1]);


  // Serial.println(Receive[4]);
}


int GetText(int x)
{
  Wire.beginTransmission(1);
  Wire.write(x);
  Wire.endTransmission();
  //Wire.begin();
  Wire.requestFrom(1, 16);    // request 6 bytes from slave device #8

  for (byte i = 0; i < 16; ++i)
  {
    dataIn[i] = Wire.read();
  }
  // Wire.endTransmission();

  Receive[x] = dataIn;
  // return dataIn;

  delay(1000);

}

B: UNO Slave Codes (your codes but slightly modified)

char* ding[5] = {"", "Dit is display 1" , "Dit is display 2" , "Dit is display 3" , "Dit is display 4"};
char dataIn[16] = "";
#include <Wire.h>

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

void loop() 
{
  delay(100);
}

void requestEvent() 
{
  Wire.write(dataIn);
}

void receiveEvent(int howMany) 
{
 // while (1 < Wire.available()) 
 // {
  //  char c = Wire.read();

 // }
  int x = Wire.read();

  strcpy(dataIn, ding[x]);

}

Thanks GolamMostafa,

this works for some reason ( still can't find what is really changed why it does work)

but still got the problem it looks like the array get rewrited.
when i change

 GetText(3);
  Serial.println(Receive[3]);
  GetText(2);
  Serial.println(Receive[2]);

to

GetText(3);

GetText(2);
  Serial.println(Receive[2]);
  Serial.println(Receive[3]);
=================
Dit is display 2
Dit is display 2
=================

maybe good to know what the idea should be.
the array's should be placed in that way because the LCD on I2C should grab there info for the display out of that array.

it does look like he just copy the last to both array places..

maybe any idea why?

it does look like he just copy the last to both array places..

You don't copy the string but only a pointer to it:

  Receive[x] = dataIn;

Use strcpy as in your slave code to fix that.

Hi Pylon,

Thanks for your response.
But when i do change it like you said, i got more problems.

even the serial monitor doesnt give me the right things.

i did try this :

  Wire.requestFrom(1, 16);    // request 6 bytes from slave device #8

  for (byte i = 0; i < 16; ++i)
  {
    dataIn[i] = Wire.read();
  }
  // Wire.endTransmission();

strcpy(Receive[x],dataIn);

or did i understand you wrong?

Greetings,

  Wire.requestFrom(1, 16);    // request 6 bytes from slave device #8

That is NOT what that code does!

  for (byte i = 0; i < 16; ++i)
  {
    dataIn[i] = Wire.read();
  }

Read 16 bytes, regardless of whether or not ANY bytes are available. Does that REALLY seem like a good idea?

PaulS:

  Wire.requestFrom(1, 16);    // request 6 bytes from slave device #8

That is NOT what that code does!

  for (byte i = 0; i < 16; ++i)

{
   dataIn[i] = Wire.read();
 }



Read 16 bytes, regardless of whether or not ANY bytes are available. Does that REALLY seem like a good idea?

After the execution of Wire.requestFrom(1, 16); instruction, the Master brings 16-byte data (n bytes from the Slave and 16-n bytes are always 0xFFs if Slave has less than 16-byte to deliver) in its local buffer from the Slave buffer. The execution of Wire.available() instruction always returns 16 (the 2nd argument). Under this circumstance, is it really necessary to check the status of 'data availability' by executing the instruction Wire.available().

Wire.requestFrom(1, 16);    // request [s]6[/s] 16 bytes from slave device [s]#8[/s] #1
  Serial.println(Wire.available());    //shows: 16
  for (byte i = 0; i < 16; ++i)
  {
    dataIn[i] = Wire.read();
  }

After the execution of Wire.requestFrom(1, 16); instruction, the Master brings 16-byte data (n bytes from the Slave and 16-n bytes are always 0xFFs if Slave has less than 16-byte to deliver) in its local buffer from the Slave buffer.

How soon after? Are there guaranteed to be 16 bytes to be read as soon as the requestFrom() method ends?

PaulS:
How soon after? Are there guaranteed to be 16 bytes to be read as soon as the requestFrom() method ends?

It is always guaranteed if the working principle of the requestFrom() method stands as:

1. The Wire.requestFrom(slaveAddress, 16); instruction asserts the slaveAddress and keeps waiting for the ACK signal which has not yet been generated by the Slave. In this sense, the Wire.requestFrom() is a looping instruction.

2. The Slave goes to the void sendEvent(int howmany) routine (ISR) and stores n-byte data (as has been designed by the programmer) in its local buffer by executing Wire.write() instructions.

3. The Slave returns to the calling program (the Main Line Program) and generates the ACK signal.

4. The Master senses/samples the ACK signal, and it brings 16-byte ( n bytes from the Slave and 16-n bytes are always 0xFFs if Slave has less than 16-byte to deliver) data into its buffer from the buffer of the Slave.

5. After the execution of Step-4, the Master executes the next instructions that follows the requestFrom() method.

6. Therefore, the 16-byte is ready there in the buffer of the Master whether we execute this byte n = Wire.available(); instruction or not?

Wire.requestFrom(1, 16);    // request 6 bytes from slave device #8
  byte n = Wire.available();  
  Serial.println(n);          //shows: 16
  for (byte i = 0; i < 16; ++i)
  {
    dataIn[i] = Wire.read();
  }

wavl.png

wavl.png

Hi all,

thanks for the responses,

i've changed my code as you said but still got the problem, that it won't hold the right value in the right array..

What did i miss here?, sorry the maybe idiot questions i ask.

greets,

Master.

#include <Wire.h>

char dataIn[16];
char* Receive[9] = {" ", " ", " ", " ", " ", " ", " ", " ", " "};

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

void loop()
{
  //  SendToDisplay();
  ShowArray();
  delay(1000);
  Serial.println("=================");

}

void ShowArray()
{
  GetText(1);
  GetText(3);
  Serial.println(Receive[1]);
  Serial.println(Receive[3]);

}


int GetText(int x)
{
  Wire.beginTransmission(8);
  Wire.write(x);
  Wire.endTransmission();
  
  Wire.requestFrom(8, 16);    // request 16 bytes from slave device #8
  byte n = Wire.available();
  Serial.println(n);          //shows: 16
  for (byte i = 0; i < 16; ++i)
  {
    dataIn[i] = Wire.read();
  }
  Receive[x] = dataIn;
  Serial.println(Receive[x]);

  delay(1000);

}

Output Serial.

=================
16
Dit is display 1
16
Dit is display 3
Dit is display 3
Dit is display 3
=================
  Receive[x] = dataIn;

I have no idea what you think this is doing. But, it isn't.

dataIn is NOT a string. You should NOT be pointing a string pointer at a non-string.

Your initial values for the pointers are complete nonsense.

Hi PaulS,

Thanks for your response and did some reading about it.
but i need to say, i think im a newb:(

Could you maybe give me a bit of help in the right direction.

Problem is i need to receive the right information and place it in the array so i can later use the array for the next futher programming.

need to receive the 4x text from the i2c slave to the master and the master needs to place it in 4 diffrent places in the array of maybe something else to use the text later to transmit that texts to the diffrent i2c displays.

Hopefully you would like to help me.

Greetings,

  for (byte i = 0; i < 16; ++i)
  {
    dataIn[i] = Wire.read();
  }

Here, you are reading 16 bytes from the device, and storing the data in an array.

  Receive[x] = dataIn;

Here, you are making the xth element of an array point to that array.

  GetText(1);
  GetText(3);

Once this code is done executing, Receive[ 1 ] and Receive[ 3 ] point to the SAME global array. This is like writing, on a card, that the current US president lives at 1600 Pennsylvania Ave., twice.

  Serial.println(Receive[1]);
  Serial.println(Receive[3]);

No one would realistically expect these two statements to print different things. That would be like reading the two cards, and finding the Nixon and Trump live at the same place. No surprise there, even though one is dead and the other only brain-dead.

Try again to explain what you are trying to do, in English, NOT in code.

I suspect, though, that you want to COPY the data in dataIn to two (or more) different places. That will NOT be done using pointers. You MUST allocate space for all the copies.