I2C Between Arduinos

829K630135

Intro: I2C Between Arduinos

Maybe sometimes we want to share the workload of one Arduino with another. Or maybe we want more digital or analog pins. Inter-Integrated Circuit or I2C (pronounced I squared C) is the best solution.

I2C is an interesting protocol. It's usually used to communicate between components on motherboards in cameras and in any embedded electronic system.

Here, we will make an I2C bus using two Arduinos. We will program one master Arduino to command the other slave Arduino to blink its built-in LED once or twice depending on the received value.

In order to build this we need the following "ingredients":

  • 2 Arduinos
  • Jumper cables
This instructable and many more can be found in my Arduino Development Cookbookavailable here. :D

STEP 1: How to Connect Them

Follow these steps to connect two Arduino UNOs using I2C:

  1. Connect pins A4 and A5 on one Arduino to the same pins on the other one.
  2. The GND line has to be common for both Arduinos. Connect it with a jumper.

We also have a schematic and a "breadboard" implementation, both easy to follow. Luckily, it's a simple implementation.

Remember never to connect 5 V and 3.3 V Arduinos together. It won't
hurt the 5V Arduino, but it will certainly annoy its 3.3 V brother!

STEP 2: Code

The following code is split in two parts: the master code and the slave code, which run on two different Arduinos. First, let's take a look at the master code:

// Include the required Wire library for I2C<br>#include 
int x = 0;
void setup() {
  // Start the I2C Bus as Master
  Wire.begin(); 
}
void loop() {
  Wire.beginTransmission(9); // transmit to device #9
  Wire.write(x);              // sends x 
  Wire.endTransmission();    // stop transmitting
  x++; // Increment x
  if (x > 5) x = 0; // `reset x once it gets 6
  delay(500);
}

And here is the slave code that interprets the characters sent from the master:

// Include the required Wire library for I2C<br>#include <Wire.h>
int LED = 13;
int x = 0;
void setup() {
  // Define the LED pin as Output
  pinMode (LED, OUTPUT);
  // Start the I2C Bus as Slave on address 9
  Wire.begin(9); 
  // Attach a function to trigger when something is received.
  Wire.onReceive(receiveEvent);
}
void receiveEvent(int bytes) {
  x = Wire.read();    // read one character from the I2C
}
void loop() {
  //If value received is 0 blink LED for 200 ms
  if (x == '0') {
    digitalWrite(LED, HIGH);
    delay(200);
    digitalWrite(LED, LOW);
    delay(200);
  }
  //If value received is 3 blink LED for 400 ms
  if (x == '3') {
    digitalWrite(LED, HIGH);
    delay(400);
    digitalWrite(LED, LOW);
    delay(400);
  }
}

STEP 3: Code Breakdown

First, let's look at the master. We need to include the required Wire.h library:

#include <Wire.h> 

Then, in the setup function, we begin the I2C bus using the Wire.begin() function. If no argument is provided in the function, Arduino will start as a master.

Lastly, we send a character x, which is between 0 and 5. We use the following functions to
begin a transmission to the device with the address 9, write the character, and then stop the transmission:

Wire.beginTransmission(9); // transmit to device #9
Wire.write(x);             // sends x
Wire.endTransmission();    // stop transmitting 

Now let's explore the slave Arduino code. We also include the Wire.h library here, but now we start the I2C bus using Wire.begin(9). The number in the argument is the address we want to use for the Arduino. All devices with address 9 will receive the transmission.

Now we need to react somehow when we receive an I2C transmission. The following function appends a trigger function whenever a character is received. Better said, whenever the Arduino receives a character on I2C, it will run the function we tell it to run:

Wire.onReceive(receiveEvent); 

And this is the function. Here, we simply store the value of the received character:

void receiveEvent(int bytes) { 
   x = Wire.read();
}

In loop(), we simply interpret that character to blink the built-in LED at different speeds depending on the received character.

STEP 4: More About I2C

To briefly go through the theory, I2C requires two digital lines: Serial Data line (SDA) to transfer data and Serial Clock Line (SCL) to keep the clock. Each I2C connection can have one master and multiple slaves. A master can write to slaves and request the slaves to give data, but no slave can directly write to the master or to another slave. Every slave has a unique address on the bus, and the master needs to know the addresses of each slave it wants to access.

Each I2C bus can support up to 112 devices. All devices need to share GND. The speed is around 100 kb/s—not very fast but still respectable and quite usable. It is possible to have more than one master on a bus, but it's really complicated and generally avoided.

A lot of sensors use I2C to communicate, typically Inertial Measurement Units, barometers,
temperature sensors, and some Sonars. Remember that I2C is not designed for long cable lengths. Depending on the cable type used, 2 meters might already cause problems.

I2C is a complicated transmission protocol, but it's very useful. All Arduinos implement it, with a few differences in pin mappings:

Board I2C pins

Uno, Pro Mini A4 (SDA), A5 (SCL)

Mega, Due 20 (SDA), 21 (SCL)

Leonardo, Yun 2 (SDA), 3 (SCL)

STEP 5: Connecting More Devices


If we need to connect more than two devices on an I2C bus, we just have to connect all SDA and SCL lines together. We will need the address of every slave to be addressed from the master Arduino.

Here is a video of a personal implementation using 1 master and 3 slaves.

More topics regarding Arduino communications such as Wireless Serial, SD cards or Ethernet can be found in my Arduino Development Cookbook available here. :D

120 Comments

The other thing that may trouble many to compile this code is that the wire library is not includced
قم بتوصيل جهازي Arduino يحتويان على لوحة مفاتيح وشاشة LCD.
Hi
This is a good Instructable, I have to admit I was stuck when it did not work however, when reading the comments, I found out why and deleted the ' ' and all was fine I think, people who criticise these instructables, they don't realise how helpful they are to novices. like me so
Thank you
I couldn't make your code work and then I looked closer. On your slave code, your "if" statements are incorrect. "if(x == '0')" refers to a char not an int. It should be "if(x == 0)". same for the "3" value. It's a good example of controlling one Arduino with another. I made mine work and thanks for the article!
Over half a million views and this tutorial still has two typos that mean it doesn't work as supplied.
Check for 0 and 3 not '0' and '3'.
Instructables.com really should fix this themselves - I cannot imagine the hours wasted due to this tiny (but critical error).
Many people will just copy and paste the code, and as supplied, the code is wrong.
well there is also the fact that x is an int (2 or 4 bytes depending on the architecture) and that the code is just using write(x) to send that data... it will only send the first byte of the data.. that works here because x is reset when it reaches 6 so the value always fit in the least significant byte....

so not a great example...
Hi,
i would like to connect 2 Arduiono Due mit D-sun CC1101 . I am not good with Programming and Microcontroller. Can you help me with my Project?
Thank you
Is there any possible way to do this wireless. I’m trying to make multiple arduinos react to each other by flashing LEDs when in close proximity to each other. Just curious..
Thank you.

Dear cornelam, thanks for your very instructive guide as to how to connect the Arduino Mega with the Arduino Mega. However. It is key that you test your software before you upload it :-)..

For newcomers they may be completely lost, when they now connect the things and they do not work.

In your example, the Master-code is sending a 0 and a 3, while the Slave-code is testing up against a '0' and a '3' (the slave is thereby testing the value 48 and the value 51, in ASCII, and not the values 0 and 3 sent by the master.)

Furthermore the import of the Wire.h library in both the good files you did, has ended up in the initial comment (line 1), and one of them is missing the Wire.h

When it did not work, I debugged the Slave first, and copied the blinking function into the receiving interrupt function - and now it started blinking. My code looked like this: (The digitalWrite's etc. made by me). Then I knew that something was transmitted from the master to the slave. The rest is history.

Finally, on the Arduino Mega, the pins 20 and 21 are the ones to be used. As I did not have throughput, I mounted pull-up resistors on both pins, but whether they are there or not, it works.

I hope the above small pointers can sharpen your already great article, which was indeed very very helpful.

void receiveEvent(int bytes) {

x = Wire.read(); // read one character from the I2C

digitalWrite(LED, HIGH);

delay(200);

digitalWrite(LED, LOW);

delay(200);

}

Humbly,

David Svarrer

Chief Solutions Architect

Rational Intuitive IVS (Denmark)

hi

i would like to make an I2C connection between an arduino uno and CJMCU Atiny85, do you think is it possible? Another question is that if I want to use 3meters of wire between 2 arduinos an I2C booster is necessary?

Goggle ATtiny85 pinout and data sheet then if they have I2C pins surely they can be connected through I2C with arduino.

I joined 3 Nanos as slaves, to a yun sa the master, i did find it heeps easier to run them all to one centeral bread bourd and go from there.

018 12:21 am

Hi, I am making a hat that has a servo, a DC motor, your 2.2" TFT with SD card https://www.adafruit.com/product/1480 , your FX Sound Board https://www.adafruit.com/product/2341 with a separate amp https://www.adafruit.com/product/1712 , and speakers https://www.adafruit.com/product/1669 , and some neo-pixels.

I want to use I2C to control all of this. I don't know much about I2C and have only used it for simple things, following the instructions in the tutorial. This is more complex and I have been studying about how to use I2C with Arduino and wire.h. I get the jist, but not the specifics.

Instead of finding all the I2C registers for all the devices I'm using, I have decided to put a program for each of the above devices on a separate micro-controller, Flora, Gemma, Trinket Pro, and regular Trinket, whatever I already have around. I have done this and have each of the devices running on their own with their own micro-controller.

I purchased one of your I2C multiplexers https://www.adafruit.com/product/2717 and would like to use it to turn on/off each of the micro-controllers at its appointed time.

Can I do this? Can I give each of the microcontrollers an address from the multiplexer, Ox70 to Ox77, bypassing any addresses that the chip related to any of the various controllers has already? And if the answer is yes, then when I address any of the micro-controllers, the only data I want to send is to turn itself on and perform it's program. Would a simple Wire.write(1); turn the micro-controller and its program on, and a Wire.write(0); turn it off?


To give you a general overview here is what I have going:

Its a top hat that has 8 neopixels scattered around it that fade in and out randomly, controlled by a Gemma. These run all the time and are not related to the motors and music I want to synchronize. The TFT LCD is used as a diadem in the hat band, controlled by a Trinket Pro 5V. It also runs all the time.

I want hide a push button switch in the hat band that when pressed would initiate the music to begin playing. It is a 1 minute piece. Once the music begins I want the servo to turn 87 degrees, I wanted to use a Trinket 5V to do this, but for some reason the program would not compile on a Trinket or a Gemma (both USBTiny) but it would compile on a Trinket Pro and a Flora. Right now it is on a Flora. The turning of the servo moves an inner circle up and out of the top of the hat, using parts that I designed and generated with a 3D printer. Its quite amazing. Once the servo stops I want the DC motor (connected to a regular Trinket 5V and your Motor Driver breakout board https://www.adafruit.com/product/3297 , to spin the, now exposed, inner circle until near the end of the song. At which point I want it to stop and have the servo descend the inner circle back into the hat, waiting for the next time the button is pushed.

I am using an Aruino UNO for the master. I'm sure there is a more efficient way to do this without using so many controllers, but I am trying to get this hat finished in 2 days and this is the most simple way I could think of, as I was having a hard time finding all the I2C addresses, plus I don't know how to use I2C really. However, I am so close and the only thing I need to do is to get the master to control the slaves. As for the timing of the music and the motors, I am using the length of the piece of music as the guide. Once the music is initiated, the the I2C will turn on the servo, which will turn 87 degrees, wait for 50 sec, and turn back 87 degrees. The DC motor will begin to spin 4 sec after it is initiated and spin for 50 sec, then stop. So you see I don't need to have a lot of communication between the master and slaves other than turn on and off.

What I'm asking for here is to know if I can do these things and what commands do I use. Thanks.

Replace in the example the conditions from x == '0' and x == '3' to x == 0 and x == 3, otherwise it does not work.

Was just about to say this too

How do I know or find the device address number, like your #9 here? How is it determined?

I guess you don't have to determine. You just assign any random number that comes to your mind.

More Comments