Skip to main content
2 of 3
added 2535 characters in body
yoloy
  • 127
  • 4

How to send an array of unknown size via I2C (from slave to master)?

When I try something like this, everything works.

Slave:

# define DEFAULT_DATA_SIZE 256

byte DATA[DEFAULT_DATA_SIZE];

void requestEvent()
{
  Wire.write( (byte*) DATA, sizeof(DATA));
}

void receiveEvent(int numBytes)
{
  for (int i = 0; i < numBytes; i++) {
    DATA[i] = Wire.read();
  }
}

Master:

# define DEFAULT_DATA_SIZE 5

void writeData(byte data[], int numBytes) {
  Wire.beginTransmission(I2C_SLAVE_ADDRESS);
  for (int i = 0; i < numBytes; i++)
  {
    Wire.write(data[i]);
  }
  Wire.endTransmission();
}

void readData() {
  byte data[DEFAULT_DATA_SIZE];
  Wire.requestFrom( I2C_SLAVE_ADDRESS, DEFAULT_DATA_SIZE );
  if ( Wire.available() == sizeof( data))
  {
    Wire.readBytes( (byte *) data, DEFAULT_DATA_SIZE);
  }
  Serial.print("Byte Array: ");
  for (int i = 0; i < DEFAULT_DATA_SIZE; i++ ) {
    Serial.print(data[i]);
    Serial.print(" ");
  }
  Serial.println();
}

byte data[5] = {0x1, 0x2, 0x3, 0x4, 0x5 };

void setup()
{
  Wire.begin();
  Serial.begin(9600); 

  writeData(data, sizeof(data));
  delay(500);
  readData();
}

void loop() {

}

But this only works when the master knows how long the array needs to be. If I set the value of DEFAULT_DATA_SIZE not to 5, but more, then I get a set of random numbers.

I tried to make the slave pass the length of the array first, and then the array itself. But for some reason it doesn't work and I also get random numbers.

Slave:

#include <Wire.h>
# define I2C_SLAVE_ADDRESS 12
# define DEFAULT_DATA_SIZE 256

byte DATA[DEFAULT_DATA_SIZE];
int RECEIVED_NUMBYTES = 0;
bool isNumBytesSended = false;

void setup()
{
  Wire.begin(I2C_SLAVE_ADDRESS);
  delay(1000);
  Wire.onRequest(requestEvent);
  Wire.onReceive(receiveEvent);
}

void loop()
{
}

void requestEvent()
{
  if (!isNumBytesSended) {
    isNumBytesSended = true;
    Wire.write(RECEIVED_NUMBYTES); 
  }
  else {
    isNumBytesSended = false;
    Wire.write( (byte*) DATA, sizeof(DATA)); 
  }
}

void receiveEvent(int numBytes)
{
  RECEIVED_NUMBYTES = numBytes;
  for (int i = 0; i < numBytes; i++) {
    DATA[i] = Wire.read();
  }
}

Master:

#include <Wire.h>
# define I2C_SLAVE_ADDRESS 12
# define DEFAULT_DATA_SIZE 10


void writeData(byte data[], int numBytes) {
  Wire.beginTransmission(I2C_SLAVE_ADDRESS);
  for (int i = 0; i < numBytes; i++)
  {
    Wire.write(data[i]);
  }
  Wire.endTransmission();
}

void readData() {
  Wire.requestFrom( I2C_SLAVE_ADDRESS, 1 );
  int numBytes = Wire.read();
  Serial.println("numBytes: " + String(numBytes));
  
  byte data[numBytes];
  if ( Wire.available() == sizeof( data))
  {
    Wire.readBytes( (byte *) data, numBytes);
  }
  Serial.print("Byte Array: ");
  for (int i = 0; i < numBytes; i++ ) {
    Serial.print(data[i]);
    Serial.print(" ");
  }
  Serial.println();
} 

byte data[5] = {0x1, 0x2, 0x3, 0x4, 0x5 };

void setup()
{
  Wire.begin();
  Serial.begin(9600); 

  writeData(data, sizeof(data));
  delay(100);
  readData();
}

void loop() {

}

Master outputs:

01:20:13.844 -> numBytes: 5
01:20:13.878 -> Byte Array: 11 8 212 8 22 
01:21:51.222 -> numBytes: 0
01:21:51.255 -> Byte Array: 
01:21:54.323 -> numBytes: 5
01:21:54.323 -> Byte Array: 11 8 212 8 22 
01:22:01.682 -> numBytes: 0
01:22:01.717 -> Byte Array: 
01:22:04.280 -> numBytes: 5
01:22:04.314 -> Byte Array: 11 8 212 8 22 
01:22:06.879 -> numBytes: 0
01:22:06.912 -> Byte Array: 
01:22:10.386 -> numBytes: 5
01:22:10.420 -> Byte Array: 11 8 212 8 22 

Please tell me the best way to solve this problem. Maybe transfer all values one byte at a time, and put a special delimiter between the beginning of new data?

yoloy
  • 127
  • 4