3

I have multiple variable length strings which do not exceed 32 bytes each which need to be sent via I2C between 2 Arduino Nanos. I use # to terminate each string. I can successfully send and receive the first string, but for the life of me I can't figure out how to send the succeeding strings. The samples I am testing with look like this:

    String answer = "12345,123.4,50.6,12345#";
    String answerV = "4.10,4.15,4.13,4.05,4.18,0#";

Master Code

    #include <Wire.h> 
    int esc = 0;
    int throttle = 80;    

    void setup() {
    Wire.begin();
    Serial.begin(115200);
    Serial.println("I2C Master ready!");
    esc = throttle * 100 / 180;
    }

    void loop() {
      delay(500);
      Serial.println("Receive data");
      Wire.requestFrom(9,32);
      String response = "";
      Serial.println(Wire.available());  
      while (Wire.available()) 
      {
        char b = Wire.read();
        response += b;
      }
      String dataString = response;
      int hashIndex = dataString.indexOf('#');
      String SportData = dataString.substring(0, hashIndex);
      Serial.println(SportData);

      String SDdata = String (esc) + "," + String(SportData);
      Serial.println(SDdata);
    }

Slave Code

    #include <Wire.h>

    byte ANSWERSIZE= 22;

    String answer = "12345,123.4,50.6,12345#";
    String answerV = "4.10,4.15,4.13,4.05,4.18,0#";

    void setup() {
      Wire.begin(9);
      Wire.onRequest(requestEvent); // data request from Master
      Serial.begin(115200);
      Serial.println("I2C Slave ready!");
      ANSWERSIZE = answer.length();
    }

    void requestEvent() {
      byte response[ANSWERSIZE];
      for (byte i=0;i<ANSWERSIZE;i++) 
      {
        response[i] = (byte)answer.charAt(i);
      }
      Wire.write(response,sizeof(response));
    }

    void loop() {
      delay(50);
    }

Can someone please show me how this can be done?

Roger McClurg
  • 51
  • 1
  • 5

1 Answers1

1

A simple idea is to keep track of the number of times requestEvent() is called, and use that to decide what to send back to the master.


Here is the code (n.b. I took the liberty to optimise it a bit):

Master:

#include <Wire.h> 

/**
 * globals
 */

int esc = 0;
int throttle = 80;    
char i2c_buffer[33];

/**
 * setup & loop
 */

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

    esc = throttle * 100 / 180;
    i2c_buffer[32] = '\0';

    Serial.println("I2C Master ready!");
}

void loop()
{
    delay(500);

    Wire.requestFrom(9, 32);

    for (byte i = 0; i < 32 && Wire.available(); ++i)
    {
        i2c_buffer[i] = Wire.read();

        if (i2c_buffer[i] == '#') {
            i2c_buffer[i] = '\0';
            break;
        }
    }

    Serial.print(esc);
    Serial.print(',');
    Serial.println(i2c_buffer);
}

Slave:

#include <Wire.h>

/**
 * globals
 */

const byte DATA_SIZE = 2;
String data[DATA_SIZE] = {
    "12345,123.4,50.6,12345#",
    "4.10,4.15,4.13,4.05,4.18,0#"
};

/**
 * setup & loop
 */

void setup()
{
    Serial.begin(115200);
    Wire.begin(9);
    Wire.onRequest(requestEvent); // data request from Master

    Serial.println("I2C Slave ready!");
}

void loop()
{
    delay(50);
}

/**
 * help functions
 */

void requestEvent()
{
    static byte req_number = 0;

    Wire.write(reinterpret_cast<const unsigned char*>(data[req_number].c_str()),
               data[req_number].length());

    req_number = (req_number + 1) % DATA_SIZE;
}

Note: I don't have two Arduino devices, so I could not test this code. If you spot some bugs, report back and I'll fix them.

Patrick Trentin
  • 7,126
  • 3
  • 23
  • 40
  • Your method works a treat. Now I just need to work it into the main sketches. Thanks so much for the help (and the optimization). – Roger McClurg Feb 26 '17 at 17:50
  • I'm trying to do this in stages as my existing master/slave are really complicated. I incorporated the code you provided in my slave and used the master you provided. When I use the test data array you provided it works just fine. Once I use an array that I fill with real time data the master gets the first array entry data[0] from the slave, but data[1] doesn't show. Here is how I defined the array: const byte DATA_SIZE = 2; String data[DATA_SIZE]; – Roger McClurg Feb 26 '17 at 19:54
  • @RogerMcClurg just open a new question with a new code example, possibly removing irrelevant stuff again.. this question is already solved, though the answer is not accepted, so it's not the right place to discuss new issues :) – Patrick Trentin Feb 26 '17 at 19:57
  • 1
    Old, post, but I'm taking a chance anyhow, two quick questions. Why is the size of the buffer 33 instead of 32? And is the variable name "buffer" picked for a specific reason? This turns orange in the Arduino IDE, so it seems to be a keyword of some kind, and the code still works if I rename it to something else like i2cBuffer. – HKrg May 14 '20 at 12:57
  • @HKrg AFAIK, buffer isn't an Arduino or C++ keyword, but I admit that I might be wrong about this. Renaming the variable is just fine. Arduino developers sometimes forget that a C string requires an extra terminator byte `\0` at the end. I wasn't sure whether OP had included this in his calculation when he said that the strings would be at most `32` bytes long, so I simply increased the size of the buffer by one to provide a safe-by-design solution. – Patrick Trentin May 14 '20 at 13:14