Arduino I2C and Multiple Slaves

Introduction: Arduino I2C and Multiple Slaves

About: I'm interested in making robots, I'm actively reading and learning about robotics and would like to share my knowledge. If you are interested in electronics or robotics, subscribe on my youtube channel and fol…

Here we would like to show you how to connect multiple Arduinos through I2C communication. We will start with two Arduinos, one Master and one Slave, than we will add to this another Slave and adjust the Master code, than we will add yet another Slave to this and adjust the Master code again. That we show how to connect multiple Arduinos via I2C where one Arduino is the Master and has multiple Slaves. At the end we will show how to have multiple Masters and multiple Slaves with four Arduinos.

Before all of that we would like to mentioned the reason why we would need so many Arduinos. In robotics for instance we prefer to have a single microcontroller controlling one set of sensors or actuators. One example would be to have one Arduino as a Master, two slave Arduinos each responsible for controlling two motors via Arduino bridge motor drivers, and a third Arduino connected with object avoidance ultrasonic sensors. Each Arduino would have different internal operations and their actions would be determined by the master.

Now let's start with the simple two Arduino wiring.

Supplies

  1. 4x Arduino Uno (or Arduino Nano)
  2. 5V power supply for powering our Arduinos (you can choose to use different ways to power up an Arduino, just remember to connect the grounds)
  3. 3x LEDs (blue, red, yellow)
  4. Jumper wires
  5. Breadboard

Step 1: I2C Wiring of Two Arduinos

Here we are going to show how to connect two Arduinos together with I2C connection. You can simply slide the images above or read the steps below.

  1. Connect the grounds of both Arduinos together. (Image 1)
  2. Now we are going to connect both A5 analog pins together, these pins are SCL, or clock pins explained later in the tutorial. (Image 2)
  3. Connect both A4 analog pins together, these pins are SDA, or data transfer pins. (Image 3)

To upload the code we need to connect to both Arduinos a USB cable, recognize which port is for which Arduino. Through the USB we power up both Arduinos and we are ready to start to upload the code.

Step 2: Master and One Slave Arduino

Now we will write a program which will enable a master to control an LED on the Slave Arduino. User will write in the serial monitor of the Master BlueOn or BlueOff to toggle the LED on the Slave. Simple enough, so let's write the code.

Here you can copy the master code or simply download it from the attachment down below. The attached code has an explanation for each line of the code, but in the next step of the tutorial we will explain the code in detail.

#include <Wire.h>                                                        
String readString;                                                          
byte I2C_OnOff;                                                             
void setup()
{
  Wire.begin();                                                             
  Serial.begin(9600);
  Serial.println("Type On to turn on the LED and Off to shut it down!");   
}
void loop()
{
  while (Serial.available())
  {
    delay(2);                                                               
    char c = Serial.read();                                                 
    readString += c;                                                        
  }
  if (readString == "BlueOn" or readString == "BLUEON" or readString == "blueon")
  {
    I2C_OnOff = 1;
  }
  else if (readString == "BlueOff" or readString == "BLUEOFF" or readString == "blueoff")
  {
    I2C_OnOff = 0;
  }
  if (readString.length() > 0)                                            
  {
    Serial.println(readString);                                            
    readString = "";                                                        
  }
  Wire.beginTransmission(1);                                                
  Wire.write(I2C_OnOff);                                                    
  Wire.endTransmission();                                                   
}

Here you can copy the slave code or simply download it from the attachment down below.

#include <Wire.h>                            
int DO_Blink = 13;
byte I2C_OnOff;                                 
void setup() 
{
  pinMode(DO_Blink, OUTPUT);                    
  Wire.begin(1);                                
  Wire.onReceive(BlinkLED);                     
}
void loop() 
{
delay(100);
}
void BlinkLED(int Press)
{
  I2C_OnOff = Wire.read();                      
  if (I2C_OnOff == 1)
  {
   digitalWrite(DO_Blink, HIGH);                 
  }
  else if (I2C_OnOff == 0)
  {
   digitalWrite(DO_Blink, LOW);                 
  } 
}

In the next step we will explain each line of the code in detail.

Step 3: Code Expalined: Two Arduinos

1. We include the wire library for I2C in our master code, we define a string variable called readString this variable will be used to store on or off commands given in the serial monitor.

#include <Wire.h>                                                            
String readString;

2. We define a byte variable called I2C_OnOFF with which we will send information to the slave via I2C. We start the I2C communication and Serial communication, and write on the monitor for note for a user.

byte I2C_OnOff;                                                             
void setup() 
{
  Wire.begin();                                                             
  Serial.begin(9600);
  Serial.println("Type On to turn on the LED and Off to shut it down!");
}

3. Now we have to check if the user has inserted anything in the serial monitor and read it. We store this information through char c variable letter by letter and move these characters inside a readString string.

while (Serial.available())  
  {
    delay(2);                                                               
    char c = Serial.read();                                                 
    readString += c;                                                        
  }

4. Now we create an if statement which will determine the value of a I2C_OnOff variable depending on the users input. If the user inserts "BlueOn", "BLUEON" or "blueon" the I2C_OnOff variable will store number 1. If the user inserts "BlueOff", "BLUEOFF" or "blueoff" the I2C_OnOff variable will store 0.

if(readString == "BlueOn" or readString == "BLUEON" or readString == "blueon")  
  {
    I2C_OnOff = 1;
  }
  else if(readString == "BlueOff" or readString == "BLUEOFF" or readString == "blueoff")
  {
    I2C_OnOff = 0;
  }

5. Now we write a if statement for displaying the users input on the Serial Monitor of the Master. If the readString string has any value display this value and empty the string.

if (readString.length() >0)                                             
  {
    Serial.println(readString);                                            
    readString="";                                                          
  }

6. Now we will open the transmission with the slave that has an address 1, send it a byte I2C_OnOff value and end the transmission.

  Wire.beginTransmission(1);                                               
  Wire.write(I2C_OnOff);                                               
  Wire.endTransmission();
}

Now we will talk about the Slave code, this same Slave code will be used in all of the Slaves later in the tutorial, we will only have to change the address of the Slave. Here the address of our first slave is 1.

1. We load the library as with the master, assign a variable to the digital pin 13 where our LED is connected (or built in). We define the same byte variable I2C_OnOff as in the master. This variable will be used with an if statement to toggle the LED.

#include <Wire.h>                           
int DO_Blink = 13;
byte I2C_OnOff;

2. We need to set the pin mode of the LED as an Output, start the I2C communication by stating that our slave address is 1. Than we have to call register an event, here called BlinkLED.

void setup()
{
  pinMode(DO_Blink, OUTPUT);                    
  Wire.begin(1);                               
  Wire.onReceive(BlinkLED);                     
}

3. We place a delay inside a loop, so that we don't "overwhelm" the Arduino.

void loop()
{
delay(100);
}

4. We have to create a function for the event called BlinkLED which will output an integer Press.

void BlinkLED(int Press)
{

5. Than we read the data that the master sends to the slave, we store that data in the byte I2C_OnOff.

I2C_OnOff = Wire.read();

6. We write an if statement to turn on or off the LED depending on the received data from the master.

  if (I2C_OnOff == 1)
  {
   digitalWrite(DO_Blink, HIGH);
  }
  else if (I2C_OnOff == 0)
  {
   digitalWrite(DO_Blink, LOW);
  }
}

Now we have to simply connect the two Arduinos as before. After we have uploaded the code to both boards and made sure that both have grounds connected and that both are powered up, we will choose the port of the master and turn on the serial monitor. Here we input blueon or blueoff statements to toggle the LED.

Step 4: I2C Wiring of Three Arduinos

Now we will connect multiple Arduinos together, let's start with the three. There are couple of way to charge all of the Arduinos, the simplest is shown here. As usually we need to connect all the grounds together, and here we will connect all the 5V pins together.

  1. Connect the 5V and GND of the Master Arduino with the breadboard. (Image 1)
  2. Connect the A4 pin (SDA pin) with one line on the breadboard. Here we have used a white wire to show which is A4 connection. Connect the A5 pin (SCL pin) with another line on the breadboard. Here we have used a purple wire to show which is A5 connection. (Image 2)
  3. Place a first Slave Arduino near the Master. Connect a Blue LED on the Arduino. The long lead goes to the D13 and the short lead goes to the GND. (Image 3)
  4. Connect the Slave Arduino in exactly the same way as the Master. 5V pin where the 5V pin of the Master is, GND pin where the GND pin of the Master is, A4 pin where A4 of the Master is and A5 where A5 of the Master is. (Image 4)
  5. Place the second Slave Arduino near the Breadboard. Connect the Red LED on it in the same way that we have done with the first Slave. (Image 5)
  6. Connect the second Slave Arduino in exactly the same way as the first Slave. Each wire goes on the same path where we have already placed a Master and a first Slave. (Image 6)

We will now go back to programming.

Step 5: Master and Two Slaves

As mentioned earlier all of the Slaves in this example have the same code, only the address is changed. On the first Arduino Slave we have used the address 1, on the second Arduino Slave we have used the address 2.

The Slave Code for the second Arduino Slave:

#include <Wire.h>                               
int DO_Blink = 13;
byte I2C_OnOff;                                 
void setup() 
{
  pinMode(DO_Blink, OUTPUT);                    
  Wire.begin(2);                                
  Wire.onReceive(BlinkLED);                     
}
void loop() 
{
delay(100);
}
void BlinkLED(int Press)
{
  I2C_OnOff = Wire.read();                      
  if (I2C_OnOff == 1)
  {
   digitalWrite(DO_Blink, HIGH);                 
  }
  else if (I2C_OnOff == 0)
  {
   digitalWrite(DO_Blink, LOW);                 
  } 
}

The Master code is changed, and it looks like this:

#include <Wire.h>                                                           
String readString;                                                          
/*NEW********************************/
int ChoiceOfTransmission;                                                    
/************************************/
byte I2C_OnOff;                                                            
void setup() 
{
  Wire.begin();                                                             
  Serial.begin(9600);
  Serial.println("Type On to turn on the LED and Off to shut it down!");    
}
void loop() 
{
  while (Serial.available()) 
  {
    delay(2);                                                               
    char c = Serial.read();                                                
    readString += c;                                                        
  }
  if (readString == "BlueOn" or readString == "BLUEON" or readString == "blueon")
  {
    I2C_OnOff = 1;
    ChoiceOfTransmission = 1;
  }
/*NEW*********************************/
  else if (readString == "RedOn" or readString == "REDON" or readString == "redon")
  {
    I2C_OnOff = 1;
    ChoiceOfTransmission = 2; 
  }
/*************************************/
  else if (readString == "BlueOff" or readString == "BLUEOFF" or readString == "blueoff")
  {
    I2C_OnOff = 0;
    ChoiceOfTransmission = 1; 
  }
/*NEW*********************************/
  else if (readString == "RedOff" or readString == "REDOFF" or readString == "redoff")
  {
    I2C_OnOff = 0;
    ChoiceOfTransmission = 2;    
  }
/*************************************/
  if (readString.length() >0)                                                     
  {
    Serial.println(readString);                                                 
    readString="";                                                              
  }
/*NEW**********************************/
  switch (ChoiceOfTransmission)
  {
    case 1:               
      Wire.beginTransmission(1);                                                                               
      Wire.write(I2C_OnOff);                                                    
      Wire.endTransmission();                                                   
      ChoiceOfTransmission = 0;                                                  
      break;
    case 2:               
      Wire.beginTransmission(2);                                                                               
      Wire.write(I2C_OnOff);                                                    
      Wire.endTransmission();                                                   
      ChoiceOfTransmission = 0;                                                 
      break;
    default:                                   
    break;              
  }
/*************************************/
}

We would like to explain the details of the change in the Master code. Main part of the Master code remains the same as before. The difference here is that we have to choose depending on the input to which Slave the Master sends the code. This is done with a Switch statement.

1. We have to define a new variable first, this variable will store data about the Slave address which Master needs to contact next.

int ChoiceOfTransmission;

2. We do everything in the same way as before until we get to the first if statement. Here we have to double our code, with the first slave we only had toggle blue LED statements now we have to add toggle red LED statements. In addition we need to insert in each if statement (new and old) a new variable called ChoiceOftransmission. With this variable we know to which Arduino Slave we have to send.

if (readString == "BlueOn" or readString == "BLUEON" or readString == "blueon")
  {
    I2C_OnOff = 1;
    ChoiceOfTransmission = 1;
  }
  else if (readString == "RedOn" or readString == "REDON" or readString == "redon")
  {
    I2C_OnOff = 1;
    ChoiceOfTransmission = 2; 
  }
  else if (readString == "BlueOff" or readString == "BLUEOFF" or readString == "blueoff")
  {
    I2C_OnOff = 0;
    ChoiceOfTransmission = 1; 
  }
  else if (readString == "RedOff" or readString == "REDOFF" or readString == "redoff")
  {
    I2C_OnOff = 0;
    ChoiceOfTransmission = 2;    
  }

3. At the end of the code we will insert a switch (case) statement. Depending on the value of the ChoiceOfTransmission variable the case 1 or case 2 will be chosen and the transmission will be send to the appropriate slave. We will first show you the first case. If the value of the ChoiceOfTransmission is 1, than we have choosen the case 1, the case 1 will begin transmission with the slave 1, send the I2C_OnOff variable, end the transmission and reset the value of ChoiceOfTransmission to 0, at the end of the case statement we insert a break command which will exit the switch loop, in our case here this will end the current void loop and start a new one.

  switch (ChoiceOfTransmission)
  {
    case 1:               
      Wire.beginTransmission(1);                                                                              
      Wire.write(I2C_OnOff);                                                    
      Wire.endTransmission();                                                   
      ChoiceOfTransmission = 0;                                                 
      break;

4. The second case is almost identical to the first one, here we only choose to send the I2C_OnOff value to the slave number 2.

    case 2:              
      Wire.beginTransmission(2);                                                                             
      Wire.write(I2C_OnOff);                                                    
      Wire.endTransmission();                                                   
      ChoiceOfTransmission = 0;                                                 
      break;

5. The switch statement ends with the default command. This default statement is run when no choice is made, if the ChoiceOfTransmission has any other value than 1 or 2. Which in this code can only be 0. This statement does not do anything, it only breaks the code and finishes. But it's mandatory to place it here.

    default:                                                                                         
      break;              
  }
}

You can download all the code from the attachment below. Now let's move to three slaves. To run this code you simply need to connect an USB to each Arduino in turn, start from the Slaves and insert Slave code with different address to each of them. Than connect the Master with the USB, you only need to connect one Arduino to power up the rest. Upload the Master code, open up the Serial Monitor and write BlueOn and RedOn. You can see that it is working.

Now let's see how we can add yet another Arduino to this network.

Step 6: I2C Wiring of Four Arduinos

We will now add yet another Arduino Uno to our previous network. I believe you have already noticed a trend. Simply said we can connect any number of Arduinos together in the same way that we have already connected three of them. All we have to make sure is that they all share the same ground with the Master, that they all are powered in any way that you prefer and that all A4 are connected together and all A5 also.

  1. Place the fourth Arduino beside the Breadboard. Connect a yellow LED to it, by placing a long lead into D13 and short lead into GND. (Image 1)
  2. Connect all the wiring in the same way as all other Arduinos before this one. (Image 2)

Now we have 4 Arduinos connected in a I2C network. Now we will show how to program them by creating one Master and three Slaves.

Step 7: Master and Three Slaves

As mentioned earlier all of the Slaves in this example have the same code, only the address is changed. On the first Arduino Slave we have used the address 1, on the second Arduino Slave we have used the address 2, and on the third Arduino Slave we will use the address 3.

The Slave Code for the third Arduino Slave:

#include <Wire.h>                               
int DO_Blink = 13;
byte I2C_OnOff;                                 
void setup() 
{
  pinMode(DO_Blink, OUTPUT);                    
  Wire.begin(3); 			// NEW   
  Wire.onReceive(BlinkLED);                     
}
void loop() 
{
delay(100);
}
void BlinkLED(int Press)
{
  I2C_OnOff = Wire.read();                      
  if (I2C_OnOff == 1)
  {
   digitalWrite(DO_Blink, HIGH);                 
  }
  else if (I2C_OnOff == 0)
  {
   digitalWrite(DO_Blink, LOW);                 
  } 
}

The Master code is changed, and it looks like this:

#include <Wire.h>                                                           
String readString;                                                         
int ChoiceOfTransmission;                                            
byte I2C_OnOff;                                                            
void setup() 
{
  Wire.begin();                                                                        
  Serial.begin(9600);    
  Serial.println("Type BlueOn to turn on the Blue LED and BlueOff to shut it down!");   
  Serial.println("Type YellowOn to turn on the Blue LED and YellowOff to shut it down!");
  Serial.println("Type RedOn to turn on the Blue LED and RedOff to shut it down!");
}
void loop() 
{
  while (Serial.available()) 
  {
    delay(2);                                                    
    char c = Serial.read();                                       
    readString += c;                                               
  }

  if (readString == "BlueOn" or readString == "BLUEON" or readString == "blueon")
  {
    I2C_OnOff = 1;
    ChoiceOfTransmission = 1;
  }
  else if (readString == "RedOn" or readString == "REDON" or readString == "redon")
  {
    I2C_OnOff = 1;
    ChoiceOfTransmission = 2; 
  }
/* NEW ******************************/
  else if (readString == "YellowOn" or readString == "YELLOWON" or readString == "yellowon")
  {
    I2C_OnOff = 1;
    ChoiceOfTransmission = 3; 
  }
/************************************/
  else if (readString == "BlueOff" or readString == "BLUEOFF" or readString == "blueoff")
  {
    I2C_OnOff = 0;
    ChoiceOfTransmission = 1; 
  }
  else if (readString == "RedOff" or readString == "REDOFF" or readString == "redoff")
  {
    I2C_OnOff = 0;
    ChoiceOfTransmission = 2;    
  }
/* NEW *******************************/
  else if (readString == "YellowOff" or readString == "YELLOWOFF" or readString == "yellowoff")
  {
    I2C_OnOff = 0;
    ChoiceOfTransmission = 3;   
  }
/*************************************/
  if (readString.length() >0)                                           
  {
    Serial.println(readString);                                         
    readString="";                                                         
  }

  switch (ChoiceOfTransmission)
  {
    case 1:              
      Wire.beginTransmission(1);                                                                      
      Wire.write(I2C_OnOff);                                                   
      Wire.endTransmission();                                              
      ChoiceOfTransmission = 0;                                              
      break;
    case 2:              
      Wire.beginTransmission(2);                                                                         
      Wire.write(I2C_OnOff);                                                  
      Wire.endTransmission();                                                  
      ChoiceOfTransmission = 0;                                                
      break;
/* NEW ********************************/
    case 3:              
      Wire.beginTransmission(3);                                                                             
      Wire.write(I2C_OnOff);                                                    
      Wire.endTransmission();                                                  
      ChoiceOfTransmission = 0;                                               
      break;
/**************************************/
    default:                                
      break;              
  }
}

We would like to explain the details of the change in the Master code.

1. We have just added another option to be read from the Serial Monitor, now we as an user can input YellowOn or YellowOff and toggle the third slaves LED.

else if (readString == "YellowOn" or readString == "YELLOWON" or readString == "yellowon")<br>  {
    I2C_OnOff = 1;
    ChoiceOfTransmission = 3; 
  }
else if (readString == "YellowOff" or readString == "YELLOWOFF" or readString == "yellowoff")<br>  {
    I2C_OnOff = 0;
    ChoiceOfTransmission = 3;   
  }

2. We have also added a way to contact a third slave in the switch statement.

case 3:              <br>      Wire.beginTransmission(3);                                                                             
      Wire.write(I2C_OnOff);                                                    
      Wire.endTransmission();                                                  
      ChoiceOfTransmission = 0;                                               
      break;

As you have noticed we did little extra work to add another slave, we basically just added some extra decisions and a way to contact the third Arduino via address number 3. From this we can easily conclude that adding a fourth, fifth, ... , eight Slave is just a matter of extending the Master code in the same matter as before. If for instance we added a forth Slave which has a motor attached to it, we can write in the Serial Monitor MotorOn or MotorOff and add a case number 4 with an address of transmission number 4, and so on.

You can download all the code from the attachment below.

Step 8: More!

In the next I2C tutorial we are going to show you how to have multiple masters.

Until then, please check out our other tutorials and take a look at our YouTube channel. Thank you for reading.

Be the First to Share

    Recommendations

    • Pumpkins & Gourds Speed Challenge

      Pumpkins & Gourds Speed Challenge
    • 3D Printed Student Design Challenge

      3D Printed Student Design Challenge
    • Back to School: Student Design Challenge

      Back to School: Student Design Challenge

    Comments

    0
    CheapLazyHobbyist
    CheapLazyHobbyist

    Question 9 months ago on Step 8

    What software do you use for your diagrams? It looks very neat.