I2C requestFrom, Sending more than one integer to Master device

Hi Guys.
I`m trying to figure out how to send more than one integer thru I2C from slave to masted device.
The setup consist of one Mega as Master and Mini Pro as Slave.
So far i was able to send multiple data from master to slave, but only one sensor has been send successfully from Slave to Master.

Master code

// i2c Master Code Arduino Mega


#include <EEPROM.h>
#include <SPI.h> 
#include <ILI9341_due_gText.h>
#include <ILI9341_due.h>
#include <Wire.h>


#include "fonts\Arial_bold_14.h"

#define TFT_CS 53
#define TFT_DC 9
#define TFT_RESET 8

ILI9341_due myTFT(TFT_CS, TFT_DC, TFT_RESET);

//▼ Inputs Pins
const byte FLUp = 22;
const byte FLDown = 24;
int raw = 0;
byte RequestCalled = true;
char buffer[5];
void setup(void)
{
  Serial.begin(9600);

  Wire.begin();


  myTFT.begin();
  myTFT.fillScreen(ILI9341_BLACK);
  myTFT.setRotation(iliRotation0);

  ILI9341_due_gText t1(&myTFT);

  t1.selectFont(Arial_bold_14);
  t1.setFontLetterSpacing(5);
  t1.setFontColor(ILI9341_YELLOW, ILI9341_BLACK);
  t1.drawString("Hello World", gTextAlignMiddleCenter);
  pinMode(FLUp, INPUT_PULLUP);
  pinMode(FLDown, INPUT_PULLUP);


  delay(1000);
  myTFT.fillScreen(ILI9341_BLACK);

}

void loop(void)
{
  ILI9341_due_gText t1(&myTFT); // tezxt font etc
  t1.selectFont(Arial_bold_14);
  t1.setFontLetterSpacing(5);

  t1.setFontColor(ILI9341_YELLOW,ILI9341_BLACK);

  if(digitalRead (FLUp) == LOW)
  {
    t1.drawString("ON H", gTextAlignMiddleCenter, gTextEraseFullLine);
    Wire.beginTransmission(5);
    Wire.write('H');
    Wire.endTransmission();
  }
  else if(digitalRead (FLUp) == HIGH)
  { 
    t1.drawString("OFF L", gTextAlignMiddleCenter, gTextEraseFullLine);  
    Wire.beginTransmission(5);
    Wire.write('L');
    Wire.endTransmission();
  }
  if(digitalRead (FLDown) == LOW)
  {
    t1.drawString("ON F", 100, 100, gTextEraseFullLine);
    Wire.beginTransmission(5);
    Wire.write('F');
    Wire.endTransmission();
  }
  else if(digitalRead (FLDown) == HIGH)
  {
    t1.drawString("OFF D", 100, 100, gTextEraseFullLine);
    Wire.beginTransmission(5);
    Wire.write('D');
    Wire.endTransmission();
  }




  Wire.requestFrom(5,1);

  {
    int pressureFL;
    //int pressureFR
    pressureFL = Wire.read();
    //pressureFR = Wire.read();

    Serial.println(pressureFL);


    ILI9341_due_gText t1(&myTFT);
    t1.selectFont(Arial_bold_14);
    t1.setFontLetterSpacing(5);

    t1.setFontColor(ILI9341_CYAN,ILI9341_BLACK);
    dtostrf(pressureFL, 4, 0, buffer);
    t1.drawString(buffer, 100 ,200, gTextEraseFullLine); 
  }

Slave code

//i2c Slave Code Arduino Mini Pro

#include <Wire.h>

//▼Outputs Pins
const byte FLSolUp = 9;
const byte FLSolDown = 8;

//▼Sensor Inputs
const int sensor1 = A7;
const int sensor2 = A0;


//▼Other
char Val[5]; 
char buffer[5];
int pressureFL; //Pressure PSI
int pressureFR;


void setup()
{
  Serial.begin(9600);
  Wire.begin(5);
  Wire.onReceive(receiveEvent);//receive data from mega
  Wire.onRequest(requestEvent);//mega requesting data

  pinMode(FLSolUp,OUTPUT);
  digitalWrite(FLSolUp,LOW);
  pinMode(FLSolDown,OUTPUT);
  digitalWrite(FLSolDown,LOW);

  //▼Solenoids  
  pinMode(FLSolUp, OUTPUT);
  pinMode(FLSolDown, OUTPUT);

  //▼Pressure Sensors
  pinMode(sensor1, INPUT);
  pinMode(sensor2, INPUT);

}

void loop()
{
  delay(50);
  int raw;

  raw = analogRead(sensor1);
  raw -= 102;
  pressureFL = (raw * 2) / 11;
  dtostrf(pressureFL, 4, 0, buffer);
  Serial.print(pressureFL);

  int raw1;

  raw1 = analogRead(sensor2);
  raw1 -= 102;
  pressureFR = (raw1 * 2) / 11;
  dtostrf(pressureFR, 4, 0, buffer);
  Serial.print(" ");
  Serial.println(pressureFR);

}


void receiveEvent(int howMany)
{
  while(Wire.available())

  {
    char c = Wire.read();


    if (c == 'H')
    {
      digitalWrite(FLSolUp, HIGH);
    }
    else if (c == 'L')
    {
      digitalWrite(FLSolUp, LOW);
    }
    else if (c == 'F')
    {
      digitalWrite(FLSolDown,HIGH);
    }
    else if (c == 'D')
    {
      digitalWrite(FLSolDown,LOW);
    }
  }
}
void requestEvent()
{
  Wire.write(pressureFL);
  //second sensor needed to send data

}

Thanks a lot for your support, this is my first attempt to work with I2C

Master:

char buffer[6];

  // request 6 bytes
  int n = Wire.requestFrom(5,6);
  if ( n == 6 )
  {
    // 6 bytes are received.
    Wire.readBytes( buffer, 6);
  }

Slave:

char buffer[6];

void requestEvent()
{
  Wire.write(buffer, 6);
}

Instead of the buffer, also a struct, union, or other variables can be used.

Can this be implemented go more than 2 integers?
I have 6 of them

6 integers is 12 bytes, I don't know the size of the I2C buffer in the Wire library, but I think 12 bytes is possible.
When you fill the integers in the Slave in the loop() the requestEven() is able to interrupt writing those integers. So you have to disable interrupts when they are filled with data.

Use an array of 6 integers : int myData[6];
And convert it to a byte * or char * when using the Wire fuctions.

Master:

int myData[6];         // 12 bytes, sizeof(myData) is 12
...
   int n = Wire.requestFrom( 5, 12);
  if( n == 12 )
  {
    Wire.readBytes( (byte *) myData, 12);

Slave:

volatile int myData[6];
...
  Wire.write( (byte *) myData, 12);

Im afraid this is to advanced for me, and i dont get this concept.
Regarding single bytes as you posted before, is this how it should be??

master

  // request 6 bytes
int n = Wire.requestFrom(5,1);
  if (n == 1 )
  {
    // 6 bytes are received.
    Wire.readBytes(buffer, 1);
    Serial.println(n);
  }
  int m = Wire.requestFrom(5,2);
  if (m == 2)
  {
    Wire.readBytes(buffer,2);
    Serial.println(m);
  }
  int p = Wire.requestFrom(5,3);
  if (p == 3)
  {
    Wire.readBytes(buffer,3);
    Serial.println(p);
  }

Slave

void requestEvent()

{
  Wire.write(buffer,1);
  Wire.write(buffer,2);
  Wire.write(buffer,3);
}

i can see serial gives me value 1,2,3 but my head is empty so far in understanding it.

I understand the confusion, there are some things you have to know.

The Master code for read 1 or 2 or 3 bytes is okay.
You do not print the data to the serial monitor, you print the amount of bytes received.
For example this : int n = requestFrom( slave_address, 3);
The returned value 'n' is only the number of bytes that was received.
You see, 3 bytes are requested, but the Slave can decide to give less bytes ! The variable 'n' is the actual number of bytes that the Slave returned.

When the Master writes 7 bytes to the Slave, the Slave knows how many bytes are received. It is the 'howMany' parameter in the receiveEvent() function.
But when the Master request 7 bytes from the Slave, the Slave does not know that. The Slave just gets a request and can decide on its own how many bytes will be written, without knowing that the Master requested 7 bytes.

That is no problem when the Master and Slave both use a fixed amount of data.

It is also no problem when the Slave is EEPROM memory. The Slave would go on sending data until the Master stops the I2C.

When you want to request 1 or 2 or 3 bytes from the Slave, the Slave has to answer with 3 bytes. The Master can stop the I2C after just 1 bytes. That is according to the I2C protocol.

I hope my explanation makes sense.

Could you be so kind and make example code that i can understand it clearer??
The post above makes it clear, in Slave code i dont have to point every single byte, i can just make Wire.write(buffer,6); and master will return the same value as when i make it all individually. Now i would like to as you for starting point to the array of 6 integers, as im not so sure where to start.

Thanks a lot

I forgot to mention a few thing:
The Slave requestEvent() and receiveEvent() are called from an interrupt, it is not allowed to use delay() or a Serial function inside those functions.
In the requestEvent() the Wire.write() may be called just once. Perhaps that will be fixed someday in the Wire library, but at the moment, only one single Wire.write() will work.

I'm on my tablet now, can you use your code in your top-post, and combine that with my code for 6 integers (12 bytes).

The Wire.readBytes() and Wire.write() like to have a pointer to 'char' or 'byte'. So I cast the integer array to a byte pointer.

i`ll see what i can do my self.
Thanks

Peter if i understand what i have to do:
I have to put mu sensors into array an than convert this array into char?

I don't have a IL19341, so I can't use your sketch to test it runtime.
This is only a small test with two Uno boards.

Master

// tested with Arduino Uno as Master and Arduino 1.6.0

#include <Wire.h>

int sensorData[6];

void setup(void)
{
  Serial.begin(9600);
  Serial.println("\nMaster started");

  Wire.begin();
}

void loop(void)
{
  int n;
  
  // Read first sensor
  Serial.print("First sensor : ");
  n = Wire.requestFrom(5,2);
  if( n == 2 )
  {
    Wire.readBytes( (byte *) sensorData, 2);
  }
  Serial.println(sensorData[0]);  
  delay(500);


  // Read all sensors
  Serial.print("All sensors : ");
  n = Wire.requestFrom(5, 12);
  if( n == 12 )
  {
    Wire.readBytes( (byte *) sensorData, 12);
  }
  for( int i=0; i<6; i++)
  {
    Serial.print(sensorData[i]);  
    Serial.print(", ");
  }
  Serial.println();
  Serial.println();
  
  delay(1500);
}

Slave

// tested with Arduino Uno as Slave and Arduino 1.6.0

#include <Wire.h>

const int pinSensor[6] = { A0, A1, A2, A3, A6, A7 };

volatile int sensorData[6];

void setup()
{
  Serial.begin(9600);
  Serial.println("\nSlave started");
  Wire.begin(5);

  for( int i=0; i<6; i++)
  {
    sensorData[i] = 0;
  }

  Wire.onReceive(receiveEvent);  //receive data from mega
  Wire.onRequest(requestEvent);  //mega requesting data
}

void loop()
{
  // Reading the sensors could be done in the 'background' when there is nothing else to do.
  // Or maybe a few times a second, using millis().
  // The sensorData[] array is updated with the newest sensor data.
  for( int i=0; i<6; i++)
  {
    int pin = pinSensor[i];
    int raw = analogRead( pin);
    
    noInterrupts();
    sensorData[i] = raw;
    interrupts();
  }
  
  delay( 20);
}


void receiveEvent(int howMany)
{
  for( int i = 0; i < howMany; i++)
  {
    char c = Wire.read();

    switch( c)
    {
      case 'H':
//        digitalWrite( FLSolUp, HIGH);
        break;
      case 'L':
//        digitalWrite( FLSolUp, LOW);
        break;
      case 'F':
//        digitalWrite(FLSolDown,HIGH);
        break;
      case 'D':
//        digitalWrite(FLSolDown,LOW);
        break;
    }
  }
}

void requestEvent()
{
  Wire.write( (byte *) sensorData, 12);
}

I'm not very happy with it, because I use '2' and '12' for one integer or 6 integers. With a large sketch it is not clear what the numbers are. They should be 'sizeof(int)' and 'sizeof(sensorData)'.

Peter
this is what i get from master code

MEGA_I2C_TO_MINI_PRO.ino: In function 'void loop()':
MEGA_I2C_TO_MINI_PRO:64: error: invalid conversion from 'byte*' to 'char*'
MEGA_I2C_TO_MINI_PRO:64: error: initializing argument 1 of 'size_t Stream::readBytes(char*, size_t)'
MEGA_I2C_TO_MINI_PRO:75: error: invalid conversion from 'byte*' to 'char*'
MEGA_I2C_TO_MINI_PRO:75: error: initializing argument 1 of 'size_t Stream::readBytes(char*, size_t)'

Slave compile ok

Im just thinking, isnt possible to specify individual address to every single pin?? this way should be a lot simpler right?
What about begin.transmission?? is that not possible??

An individual address on the I2C bus ? No that is not possible. A Slave has one address, that's it.

Are you using the Arduino IDE 1.6.0 ?
Do you have some includes that are not okay ?
When I compile the Master code from my Reply #10 for a Arduino Mega 2560 it is (of course) okay.

If you give links to the libraries that I have to install, I could try to compile your whole sketch.
As a workaround, you can use (char *) instead of (byte *). But as far as I know, both should be valid.

Wire.readBytes( (byte *) sensorData, 12);

i used char* instead byte* and it started to print data but for some reason they not in order, some of the values change together, for example one pot drive 2 values

ILI9341(new)SPI library for Due supporting DMA transfer(Uno, Mega,.. compatible) - Displays - Arduino Forum tft library and eeprom library you should have

void requestEvent()
{
  Wire.write(pressureFL);
  //second sensor needed to send data

}

You only write one byte here. There are ways of writing more.

Peter, decide to drop one sensor, so 5 sensors in total

master

// i2c Master Code Arduino Mega


#include <EEPROM.h>
#include <SPI.h> 
#include <ILI9341_due_gText.h>
#include <ILI9341_due.h>
#include <Wire.h>


#include "fonts\Arial_bold_14.h"

#define TFT_CS 53
#define TFT_DC 9
#define TFT_RESET 8

ILI9341_due myTFT(TFT_CS, TFT_DC, TFT_RESET);

//▼ Inputs Pins
const byte FLUp = 22;
const byte FLDown = 24;


char buffer[5];
int sensorData[5];


void setup(void)
{
  Serial.begin(9600);
  Serial.println("\nMaster started");

  Wire.begin();
  myTFT.begin();
  myTFT.fillScreen(ILI9341_BLACK);
  myTFT.setRotation(iliRotation0);

  ILI9341_due_gText t1(&myTFT);

  t1.selectFont(Arial_bold_14);
  t1.setFontLetterSpacing(5);
  t1.setFontColor(ILI9341_YELLOW, ILI9341_BLACK);
  t1.drawString("Hello World", gTextAlignMiddleCenter);
  pinMode(FLUp, INPUT_PULLUP);
  pinMode(FLDown, INPUT_PULLUP);


  delay(1000);
  myTFT.fillScreen(ILI9341_BLACK);

}

void loop(void)
{
  ILI9341_due_gText t1(&myTFT); // tezxt font etc
  t1.selectFont(Arial_bold_14);
  t1.setFontLetterSpacing(5);

  t1.setFontColor(ILI9341_YELLOW,ILI9341_BLACK);

  if(digitalRead (FLUp) == LOW)
  {
    t1.drawString("ON H", gTextAlignMiddleCenter, gTextEraseFullLine);
    Wire.beginTransmission(5);
    Wire.write('H');
    Wire.endTransmission();
  }
  else if(digitalRead (FLUp) == HIGH)
  { 
    t1.drawString("OFF L", gTextAlignMiddleCenter, gTextEraseFullLine);  
    Wire.beginTransmission(5);
    Wire.write('L');
    Wire.endTransmission();
  }
  if(digitalRead (FLDown) == LOW)
  {
    t1.drawString("ON F", 100, 100, gTextEraseFullLine);
    Wire.beginTransmission(5);
    Wire.write('F');
    Wire.endTransmission();
  }
  else if(digitalRead (FLDown) == HIGH)
  {
    t1.drawString("OFF D", 100, 100, gTextEraseFullLine);
    Wire.beginTransmission(5);
    Wire.write('D');
    Wire.endTransmission();
  }
/////////////////////////////////////////////////////////////////////////////////
int n;
  
  // Read first sensor
  Serial.print("First sensor : ");
  n = Wire.requestFrom(5,2);
  if( n == 2 )
  {
    Wire.readBytes( (char *) sensorData, 2);
  }
  Serial.println(sensorData[0]);  
  delay(50);


  // Read all sensors
  Serial.print("All sensors : ");
  n = Wire.requestFrom(5, 10);
  if( n == 10 )
  {
    Wire.readBytes( (char *) sensorData, 10);
  }
  for( int i=0; i<5; i++)
  {
    Serial.print(sensorData[i]);  
    Serial.print(", ");
  }
  Serial.println();
  Serial.println();
  
  delay(50);
}

slave

//i2c Slave Code Arduino Mini Pro

#include <Wire.h>

//▼Outputs Pins
const byte FLSolUp = 9;
const byte FLSolDown = 8;

//▼Sensor Inputs
const int sensor1 = A7;
const int sensor2 = A0;


//▼Other
char Val[5]; 
char buffer[5];
int pressureFL; //Pressure PSI
int pressureFR;


#include <Wire.h>

const int pinSensor[6] = { A0, A1, A2, A3, A7 };

volatile int sensorData[5];

void setup()
{
  Serial.begin(9600);
  Serial.println("\nSlave started");
  Wire.begin(5);

  for( int i=0; i<5; i++)
  {
    sensorData[i] = 0;
  }

  Wire.onReceive(receiveEvent);  //receive data from mega
  Wire.onRequest(requestEvent);  //mega requesting data
}

void loop()
{
  // Reading the sensors could be done in the 'background' when there is nothing else to do.
  // Or maybe a few times a second, using millis().
  // The sensorData[] array is updated with the newest sensor data.
  for( int i=0; i<5; i++)
  {
    int pin = pinSensor[i];
    int raw = analogRead( pin);
    
    noInterrupts();
    sensorData[i] = raw;
    interrupts();
  }
  
  delay( 20);
}


void receiveEvent(int howMany)
{
  while(Wire.available())

  {
    char c = Wire.read();


    if (c == 'H')
    {
      digitalWrite(FLSolUp, HIGH);
    }
    else if (c == 'L')
    {
      digitalWrite(FLSolUp, LOW);
    }
    else if (c == 'F')
    {
      digitalWrite(FLSolDown,HIGH);
    }
    else if (c == 'D')
    {
      digitalWrite(FLSolDown,LOW);
    }
  }
}

void requestEvent()
{
  Wire.write( (byte *) sensorData, 10);
}

How do i know now which data is which in order to send it to the display??

Reading 12 bytes is almost just as fast as reading 2 bytes, because of all the overhead.
You can read them all at once.

If you want to make it faster, use Wire.setClock();
Call that after the Wire.begin();
The default is 100000, but you can try 200000 or 400000.

Do you sometimes want to read only sensor1 and at another moment only sensor2 ?
That is possible, if you simulate registers inside the Arduino that work just like registers in a hardware I2C sensor.
It is possible to create a piece of memory with status-registers and command-registers, and with a "register address" (an index to the memory).
When the Master want to read certain data, it writes the "registers address" and after that it requests the data.
That requires writing and reading for every sensor, it will slow things down.
It also requires good code for the requestEvent() and receiveEvent(), that uses 'volatile' whenever needed, and flags and disabling interrupts at the right moments. But it can be done.

As far as I know, that is the only way to read different things seperately.
That is how the I2C protocol is and that how almost every I2C sensor works.

The pressures are the most important thing to monitor and all 5 of them (plus one additional dependended from aplication) must be on the display real time. I decide to run I2C to cut down signal wires as i had 16 of them on my first project, than i tried to cut that number down, but i think that my original intention is going to be more reliable since 6 sensors and 8 i/o pins that need to be ready every now and then. Plus with my design i cannot use any delay. Plus my coding knowlegde and english level is just on the edge:) what is your opinion??
thanks