How to send more than 32 bytes with I2C-Anything

Hi all ,

I’m trying to send 53 bytes of date from master to slave between two arduino nano ,but don’t find the solution.
It don’t work.
I can collect some data if i modify #define TWI_BUFFER_LENGTH 32 in twi.h and #define BUFFER_LENGTH 32
in wire.h but my master nano freeze all time , and i lost data because i don’t have enought memory available

I use a Struct to collect all my sensor datas

MASTER :

/ Data transfer to Nano2

// collect new data
sensorData[0].hour = GPS.hour;
sensorData[0].minute = GPS.minute;
sensorData[0].sec = GPS.seconds;
sensorData[0].day = GPS.day;
sensorData[0].month = GPS.month;
sensorData[0].year = (GPS.year + 2000 );
sensorData[0].lat = (GPS.latitude / 100);
sensorData[0].dirlat = dirlat2;
sensorData[0].longi = (GPS.longitude / 100);
sensorData[0].dirlong = dirlong2;
sensorData[0].alt = GPS.altitude;
sensorData[0].temp = t;
sensorData[0].hum = h;
sensorData[0].sqm = SQMreading;

for( int sensor = 0; sensor < 1; sensor++)
{
// transmit the data
Wire.beginTransmission( SLAVE_ADDRESS);
I2C_writeAnything( sensorData[sensor]);
int error = Wire.endTransmission ();
delay (2000);
if( error != 0)
{
Serial.println(F(“Error, no acknowledge from Slave”));
}

delay(2000);
}
delay(5000);

SLAVE :

// The struct must be the same in the Master and in the Slave
struct sensorData_STRUCT
{
byte sensorNumber;
float hour;
float minute;
float sec;
float day;
float month;
float year;
float lat;
int dirlat;
float longi;
int dirlong;
float alt;
float temp;
float hum;
float sqm;

};

void receiveEvent( int howMany)
{
if( howMany == sizeof( sensorData_STRUCT))
{

I2C_readAnything( sensorDataNew);
haveData = true;
}
else
{

Serial.print( howMany);
// something is wrong. Wrong number of bytes received.
// it is possible to set a volatile variable to an error.
}

}

Is it possible to send my datas in 2 differents buffer ?
How can i do this ?

Thanks

You don't have to send all 53 bytes in one I2C transmission. You could send the first 8 items in the struct in the first message (29 bytes) and then send the remaining 7 items (24 bytes) in the next message. That way you won't have to meddle with the I2C buffer size.

Pete

Thanks for you fast answer .
How do you make those two message from the Struct ?

François

One way would be to split the struct into two structs and then use I2C_writeAnything to write them one after the other.
Alternatively, you could just write the content of the struct yourself.

Pete

I would split it at 'float year' and 'float lat' and use two structures, sensorData_STRUCT1 and sensorData_STRUCT2. You could add a leading ID byte to each structure so the receiver knows which one was sent. You might also pad the end of the shorter one so the size of each is always the same.

Try to do this , but i don't know how to do .

How do you format the 2 Struct ?

struct sensorData_STRUCT_1
{
  byte sensorNumber;
float hour;      
float minute;
float sec;
float day;
float month;
float year;
float lat;
};

struct sensorData_STRUCT_2
{
int dirlat;
float longi;
int dirlong;
float alt;
float temp;
float hum;
float sqm;
};

Pete

Ok , thanks.
Made it like that :

struct sensorData_STRUCT1
{
   byte sensorNumber;
float hour;      
float minute;
float sec;
float day;
float month;
float year;
float lat;

} 

sensorData[2];

struct sensorData_STRUCT2
{
   byte sensorNumber;
int dirlat;
float longi;
int dirlong;
float alt;
float temp;
float hum;
float sqm;
} 
sensorData1[2];

How do i send my two struct to slave ?

I’ve try this :

 for( int sensor = 0; sensor < 2; sensor++)
  {
    // transmit the data
    Wire.beginTransmission( SLAVE_ADDRESS);
 I2C_writeAnything( sensorData[sensor]);

  Wire.endTransmission ();

   Wire.beginTransmission( SLAVE_ADDRESS);

 I2C_writeAnything( sensorData1[sensor]);


  Wire.endTransmission ();
 

    int error = Wire.endTransmission ();
 
   
    if( error != 0)

it seems to work but i’ve a problem when i read data in the slave

here is what i’ve made :

// A special struct for the received sensor data
volatile sensorData_STRUCT1 sensorDataNew;
volatile sensorData_STRUCT2 sensorDataNew2;
// The normal sensor data
sensorData_STRUCT1 sensorData[2];
sensorData_STRUCT2 sensorData1[2];
void receiveEvent( int howMany)
{
  if( howMany == sizeof( sensorData_STRUCT1))
  {
   
    I2C_readAnything( sensorDataNew);   
    haveData = true;
  }


 if( howMany ==  sizeof( sensorData_STRUCT2))
  {
   
    I2C_readAnything( sensorDataNew2);   
    haveData = true;
  }
 Wire.begin( SLAVE_ADDRESS);
  Wire.onReceive (receiveEvent);


 
  // fill fixed data
  sensorData[0].sensorNumber = 0;
  sensorData1[1].sensorNumber = 0;
int sensor;
 
  if( haveData)
  {
    // New data has arrived, copy it in one of the two sensorData.
    // Disable interrups as short as possible
    // The interrupts are disabled to avoid an receiveEvent interrupt that writes new data.
    
    noInterrupts();
    sensor = sensorDataNew.sensorNumber;
    sensor = sensorDataNew2.sensorNumber;
    if( sensor == 0 || sensor == 1)
    {
      memcpy( &sensorData[sensor], (const void *) &sensorDataNew, sizeof( sensorData_STRUCT1)); // destination, source, size
        
    memcpy( &sensorData1[sensor], (const void *) &sensorDataNew2, sizeof( sensorData_STRUCT2)); // destination, source, size
  
    }
    haveData = false;
    interrupts();

When i read my data in my loop , here is what i get :

1st loop :

only data of the first struct

2nd loop :

only data of the second struct

3loop loop :

All the datas

in the next loops , loop2 and loop3 aleternate.

Where is my mistake ?

Hi,

Isnt the spec of I2C such that you never send more than a certain number of bits in one frame? In fact, i think you send a certain number like 8 per frame and then in the next frame you can send another 8 or something like that. So there should never be a time when you send say 100 bits all at once, there is probably an ACK in there several times between frames. Been a while since i read up on this.

Been a while since i read up on this.

Obviously.

Pete

In both structs, have the first byte of your block be a block type identifier. Have your slave read that to work out what type of block it's getting - don't just rely on the sequence of transmissions being correct.

Be sure to put the structs in a common .h file.

One handy way to organise your code is to use a C union:

struct SensorData {
  byte whichPart;
  union  WhichPartUnion {
    struct Part1 {
      int x,y,z;
    } part1;
    struct Part2 { 
      char a_title[20]
    } part2;
  } theParts;
};

The items in the union "overlap". You use whichPart to determine which kind of item the item is.

In the sender:

SensorData someData;
someData.whichPart = 1;
someData.theParts.part1.x = x;
someData.theParts.part1.y = y;
someData.theParts.part1.z = z;

// send someData

And in the reciever

SensorData someData;
// recieve someData
if(somedata.whichpart ==1) {
  setX(someData.theParts.part1.x);
}

If you make your buffer sizeof(struct SomeData), then it will be big enough to handle any of your data structures. Conversely, sizeof(struct SomeData) will tell you if your design exceeds the limit that you can send down the wire.

And if it;s all nested a bit too deeply, you ca always use a reference:

SensorData someData;
// recieve someData
if(somedata.whichpart ==1) {
  Part1 &p = someData.theParts.part1;
  setXYZ(p.x, p.y, p.z);
}

I know this thread is old and blablabla
but i’ve been dealing with this (sending over 32 bytes) so here is an idea on how to do it, it’s not perfect but it work.

arduino connected on A4 and A5
master:

#include <Wire.h>

String currRates = "";
int lastStringLength = currRates.length();
char message[61];
boolean readingRates = false;

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

void loop() {
  currRates = "";  // Reset for reading
  connectToServer();
  delay(100); // Wait 100ms
  currRates.toCharArray(message, 61);
  Serial.println(message); // Print the fetched result
  char messagePartOne[31];
  for (int i = 0; i < 30; i++)
  {
    messagePartOne[i] = message[i];
  }
  messagePartOne[30] = '\0';
  char messagePartTwo[31];
  for (int i = 0; i < 30; i++)
  {
    messagePartTwo[i] = message[i + 30];
  }
  messagePartTwo[30] = '\0';

  Serial.print("Message Part One: ");
  Serial.println(messagePartOne);
  Serial.print("Message Part Two: ");
  Serial.println(messagePartTwo);
  
  Wire.beginTransmission(8); // transmit to device #8
  Wire.write(messagePartOne); // sends the message to slave
  Wire.endTransmission(); // stop transmitting
  delay(500);
  Wire.beginTransmission(8); // transmit to device #8
  Wire.write(messagePartTwo); // sends the message to slave
  Wire.endTransmission(); // stop transmitting
  delay(60000); // Delay before next loop (one minute).
}

void connectToServer() {
  currRates = "012345678901234567890123456789012345678901234567890123456789";
}

slave:

#include <Wire.h>

String currRates = "";
char message[61];
int var;
unsigned long lastAvailable = 0;

void setup() {
  Serial.begin(9600); // Open serial communications
  // put your setup code here, to run once:
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onReceive(receiveEvent); // register event
  currRates = "";
}

void loop() {
  // put your main code here, to run repeatedly:

}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
  if (currRates.length() <= 32)
  {
    while (Wire.available())
    {
      char _ch = (char)Wire.read();
      if (_ch == ' ' || _ch == '\n' || _ch == '\r' || _ch == '\0')
      {

      }
      else {
        currRates += _ch;
        Serial.println(currRates);
      }
      //if (currRates.length() == 60)
      //  break;
    }
  }
  else {
    currRates = "";
    while (Wire.available())
    {
      char _ch = (char)Wire.read();
      if (_ch == ' ' || _ch == '\n' || _ch == '\r' || _ch == '\0')
      {

      }
      else {
        currRates += _ch;
        Serial.println(currRates);
      }
      //if (currRates.length() == 60)
      //  break;
    }
  }
}

It take the string, split it, and send it in two i2c transmission, the slave just continue to assemble it.

That's what I said in message #1 about one year ago. Try to keep up.

Pete