Use I2C for communication between Arduinos

THE FIRST EXAMPLE - master sending to slave

The general principles can be understood from this example.

I have created a struct to hold the data that is to be sent. This is a convenient way to allow for any type of data in any combination as long as the total size does not exceed 32 bytes. In the example I have padded out the struct to exactly 32 bytes to make the point as clear as possible, but any size not exceeding 32 bytes can be sent. Note that the struct will be perfectly happy containing a single variable if that's all you need.

The address that this Arduino is listening on is identified in setup() with the line

Wire.begin(thisAddress);

where the variable thisAddress holds the number for the address.

Sending a message is achieved with these three lines

Wire.beginTransmission(otherAddress);
Wire.write((byte*) &sendData, sizeof(sendData));
Wire.endTransmission();

The first line identifies the device for which the message is intended. The second line identifies the data to be sent and the last line actually sends the message

The way I have written the program it updates the data in the struct at regular intervals and sets the variable newSendData to true to signal that there is something to be transmitted. You can choose any other basis for updating the struct.

For the slave the function to be called when an I2C message arrives is established with the line

Wire.onReceive(receiveEvent);

and the data is copied to the receiving struct with this line

Wire.readBytes( (byte*) &rxData, numBytesReceived);

For reasons that I hope are obvious the program receiving the message must save it to a struct that is identical to the struct of the sending message.

Note that Wire.readbytes() works although it is not mentioned in the Wire library documentation.

Note, also, how the use of a struct eliminates the need to parse the incoming data - the data automatically gets put in the right places in the struct.

Code for Master to Slave communication
Master

//===================
// Using I2C to send and receive structs between two Arduinos
//   SDA is the data connection and SCL is the clock connection
//   On an Uno  SDA is A4 and SCL is A5
//   On an Mega SDA is 20 and SCL is 21
//   GNDs must also be connected
//===================


        // data to be sent
struct I2cTxStruct {
    char textA[16];         // 16 bytes
    int valA;               //  2
    unsigned long valB;     //  4
    byte padding[10];       // 10
                            //------
                            // 32
};

I2cTxStruct txData = {"xxx", 236, 0};

bool newTxData = false;


        // I2C control stuff
#include <Wire.h>

const byte thisAddress = 8; // these need to be swapped for the other Arduino
const byte otherAddress = 9;


        // timing variables
unsigned long prevUpdateTime = 0;
unsigned long updateInterval = 500;

//=================================

void setup() {
    Serial.begin(115200);
    Serial.println("\nStarting I2C Master demo\n");

        // set up I2C
    Wire.begin(thisAddress); // join i2c bus

}

//============

void loop() {

        // this function updates the data in txData
    updateDataToSend();
        // this function sends the data if one is ready to be sent
    transmitData();
}

//============

void updateDataToSend() {

    if (millis() - prevUpdateTime >= updateInterval) {
        prevUpdateTime = millis();
        if (newTxData == false) { // ensure previous message has been sent

            char sText[] = "SendA";
            strcpy(txData.textA, sText);
            txData.valA += 10;
            if (txData.valA > 300) {
                txData.valA = 236;
            }
            txData.valB = millis();
            newTxData = true;
        }
    }
}

//============

void transmitData() {

    if (newTxData == true) {
        Wire.beginTransmission(otherAddress);
        Wire.write((byte*) &txData, sizeof(txData));
        Wire.endTransmission();    // this is what actually sends the data

            // for demo show the data that as been sent
        Serial.print("Sent ");
        Serial.print(txData.textA);
        Serial.print(' ');
        Serial.print(txData.valA);
        Serial.print(' ');
        Serial.println(txData.valB);

        newTxData = false;
    }
}

Slave

//===================
// Using I2C to send and receive structs between two Arduinos
//   SDA is the data connection and SCL is the clock connection
//   On an Uno  SDA is A4 and SCL is A5
//   On an Mega SDA is 20 and SCL is 21
//   GNDs must also be connected
//===================


        // data to be received

struct I2cRxStruct {
    char textB[16];         // 16 bytes
    int valC;               //  2
    unsigned long valD;     //  4
    byte padding[10];       // 10
                            //------
                            // 32
};

I2cRxStruct rxData;

bool newRxData = false;


        // I2C control stuff
#include <Wire.h>

const byte thisAddress = 9; // these need to be swapped for the other Arduino
const byte otherAddress = 8;



//=================================

void setup() {
    Serial.begin(115200);
    Serial.println("\nStarting I2C Slave demo\n");

    // set up I2C
    Wire.begin(thisAddress); // join i2c bus
    Wire.onReceive(receiveEvent); // register event
}

//============

void loop() {

        // this bit checks if a message has been received
    if (newRxData == true) {
        showNewData();
        newRxData = false;
    }
}


//=============

void showNewData() {

    Serial.print("This just in    ");
    Serial.print(rxData.textB);
    Serial.print(' ');
    Serial.print(rxData.valC);
    Serial.print(' ');
    Serial.println(rxData.valD);
}

//============

        // this function is called by the Wire library when a message is received
void receiveEvent(int numBytesReceived) {

    if (newRxData == false) {
            // copy the data to rxData
        Wire.readBytes( (byte*) &rxData, numBytesReceived);
        newRxData = true;
    }
    else {
            // dump the data
        while(Wire.available() > 0) {
            byte c = Wire.read();
        }
    }
}

Continues in next Post