Send large string through i2c protocol

I have achieve the i2c between arduino uno and nodemcu esp8266-12E.
Small strings are successfully send like,

char *str = "HELLO";

But long/large string like,

char *str = "HELLO ARDUINO IT's ME NODEMCU ESP8266 TALKING TO YOU THROUGH i2c PROTOCOL"

Does not get transmitted.
I am not understanding what is happening over here?

I think the Wire payload length is 32 bytes max (size of the buffer)

1 Like

okay, can't we increase it?
Or can we do this, that first send 32 bytes and then again 32 bytes.

No, the I2C interface is lazy. :slight_smile: Once 32 bytes have been sent out, the port becomes completely inoperative and no more data can be sent. :slight_smile: That is why only devices that use less than 32 bytes, like LCD displays, can be used... :slight_smile:

NOT! Of course you can send multiple messages of 32 bytes!!!! Just use the null terminator (or a designated symbol) as an end of message marker.

For variable length strings, you're probably better off sending individual bytes. Just for simplicity.

Does it imply that string length is > 32 bytes and I execute the following codes:

byte myData[35] = {0x12, ..., 0x67};
Wire.beginTransmission(slaveAddress);
for(int i = 0; i<35; i++)
{
    Wire.write(myData[i]);
}
Wire.endTransmission();

It is not working..

What is your explanation?

We don't have godlike powers that would allow us to see everything that you did... do not post snippets of code, they do not provide sufficient context and do not support third party testing. Post entire, compilable sketches. In this particular case, you need to provide the code for both sending and receiving.

My recevier code is:

while (true)
{
    if (0 < Wire.available())
    {
        c = Wire.read();
        if (c == '|')
        {
            // End the loop.
            break;
        }
        else if (c == '+')
        {
            // Add to array.
            IP[iter] = a;
            iter++;
            ic = 0;
            a[0] = '\0';
            c = ' ';
        }
        else if (c == ' ')
        {
            // Do nothing.
        }
        else
        {
            a[ic] = c;
            ic++;
        }
    }
}

Transmitter code is:

String load = client->getResponseBody();
char sen[load.length()];
Serial.println(load);
load.toCharArray(sen, load.length());
Wire.beginTransmission(8);
for(int i = 0; i < load.length(); i++)
{
    Wire.write(sen[i]);
}
Wire.endTransmission();

I have not submitted a sketch. It is hoped that the concerned poster will insert the codes at the appropriate place of the Master Sketch and then will report the functionality of the codes to establish the validity of the following excerpt of post #4.

Master Sketch:

#include<Wire.h>
#define slaveAddress 0b0010000

void setup()
{
    Serial.begin(9600);
    Wire.begin();
    //-------------------------------
   byte myData[35] = 
   {
      0x12, 0x34, 0x56, 0x67, 0x89, 0x12, 0x34, 0x56, 0x67, 0x89, 
      0x12, 0x34, 0x56, 0x67, 0x89, 0x12, 0x34, 0x56, 0x67, 0x89, 
      0x12, 0x34, 0x56, 0x67, 0x89, 0x12, 0x34, 0x56, 0x67, 0x89
      0x12, 0x34, 0x56, 0x67, 0x89
   };

   Wire.beginTransmission(slaveAddress);
   for(int i = 0; i<35; i++)
   {
      Wire.write(myData[i]);
   }
   Wire.endTransmission();
}

void loop()
{

}

Slave Sketch:
Left for the concerned poster.

Hey, @mohan_codes I told you:

do not post snippets of code, they do not provide sufficient context and do not support third party testing. Post entire, compilable sketches.

I also pointed out that

We don't have godlike powers that would allow us to see everything that you did

@mohan_codes you are in a lot of trouble.

Why do you want to use the I2C bus ? Using the I2C bus to communicate between microcontrollers is a bad idea. Can you use a Serial port ?

The newer Arduino boards have a maximum data size of 256 bytes, only the basic Arduino boards have a maximum of 32 bytes for the I2C bus. Perhaps you can solve the problem with a newer board, such as the MKR Zero.

It is possible to split the data into chunks of maximum 32 bytes, but they have to be glued together in the Slave. That means you have to add extra code for the administration.

Can you tell more about your project ?
If you can do your whole project with a single Arduino board, then it will be easier. Maybe you have to buy a new Arduino board, but the goal is to have a working project.

No one uses a variable text over I2C (well, almost no one). Using fixed sized packages of binary data is more suitable for the I2C bus.

Splitting a project over two Arduino boards with a I2C bus in between makes it ten times harder (just kidding, it is more than ten times). It might not even work if you use a library in the Slave that disables the interrupts (Neopixel, FastLED, OneWire, DHT, SoftwareSerial, and so on).

The ESP8266 runs at 3.3V and the Arduino Uno runs at 5V. That means you are connecting a 3.3V I2C bus to a 5V I2C bus. It is better if you use a I2C level shifter between them.
If you are going to add motors to your project, then things get very nasty and you might have to redesign your whole project.

This is a bug:

char sen[load.length()];
load.toCharArray(sen, load.length());

The zero-terminator will be written into memory that you don't own.

no, that's only one transaction you need multiple blocks like this

Wire.beginTransmission();
Wire.write(); ... // less than 32
Wire.endTransmission();

and so a small protocol around it that says "I'll be sending 150 bytes in 5 transactions so make a note that the next 5 data transfer are actually just one big payload delivered in packets"

I am expecting to have (yes/no) answers of the following two questions without doing any experiments in the context of your this statement of post #4: "For variable length strings, you're probably better off sending individual bytes. Just for simplicity".

1. Do you think that the following Master and Slave sketches will exchange 35-byte data?
2. Do you think that the variable howMany in the Slave sketch will hold 35?

Master Sketch:

#include<Wire.h>
#define slaveAddress 0b0010000

void setup()
{
    Serial.begin(9600);
    Wire.begin();
    //-------------------------------
   byte myData[35] = 
   {
      0x12, 0x34, 0x56, 0x67, 0x89, 0x12, 0x34, 0x56, 0x67, 0x89, 
      0x12, 0x34, 0x56, 0x67, 0x89, 0x12, 0x34, 0x56, 0x67, 0x89, 
      0x12, 0x34, 0x56, 0x67, 0x89, 0x12, 0x34, 0x56, 0x67, 0x89,
      0x12, 0x34, 0x56, 0x67, 0x89
   };

   Wire.beginTransmission(slaveAddress);
   for(int i = 0; i<35; i++)
   {
      Wire.write(myData[i]);
   }
   Wire.endTransmission();
}

void loop()
{

}

//======================================
Slave/Receiver Sketch:

#include<Wire.h>
#define slaveAddress 0b0010000
byte myData[35];

void setup()
{
    Serial.begin(9600);
    Wire.begin(slaveAddress);
    Wire.onReceive(receiveEvent);
}

void loop()
{

}

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

why wouldn't you do the experiments? that's the easiest way to see what will happen :slight_smile:

Answer 1: Yes.
Answer 2: Yes.
I think.

I hope that you have sensed my pulse. :slight_smile:

Now, you connect two Arduinos, upload the sketches of post #14, and verify your above answers by printing the value of the variable howMany at the Slave's Serial Monitor. The Slave knowns how many bytes it has received from Master (in one transaction bounded by beginTransmission()/endTransmission()) and then copies it into variable howMany.

You need to add codes with Slave Sketch (shown below) to transfer the value of howMany into a global variable and then print it in the loop() function testing the true state of a flag activated in the receiveEvent() routine.

volatile bool flag = false;
int count;

void loop()
{
  if (flag == true)
  {
    Serial.println(count);
    flag = false;
  }
}

void receiveEvent(int howMany)
{
  count = howMany;
  for (int i = 0; i < howMany; i++)
  {
    myData[i] = Wire.read();
  }
  flag = true;
}

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