The Digital Cave

Primitive Life in a Technological World

 

Arduino I2C Sketches

This page details a few methods of connecting multiple Arduino devices together over an I2C bus.

(Note that this page remains for historic purposes only; I have since stopped using the Arduino framework in favour of coding AVRs directly in C and assembly. Arduino is a great learning platform, but is too inefficient for time-constrained systems. For a twi implementation on bare AVR hardware, see my AVR libraries.

Master / Slave

The first example is a simple Master / Slave setup, where a button pushed on the master will toggle a LED on the slave. This sketch should give you an idea of how to set up a master device to send data, and a slave device to receive it.

/**
 *
 * Sample I2C implementation.  Sends a button state over I2C to a slave Arduino
 * with an LED flashing showing button state.
 * 
 * Connections: Arduino analog pins 4 and 5 are connected between the two Arduinos, 
 * with a 1k pullup resistor connected to each line.  On the Master, connect a push 
 * button between digital pin 10 and ground.  On the slave, connect an LED (with a
 * resistor) to digital pin 9.
 * 
 */

#include <Wire.h>

#define BUTTON 10
#define ADDRESS 42

void setup() {
  Serial.begin(112500);           // start serial for output
  pinMode(BUTTON, INPUT);
  digitalWrite(BUTTON, HIGH);
  Wire.begin();
}

boolean last_state = HIGH;
void loop() {
  if (digitalRead(BUTTON) != last_state){
    last_state = digitalRead(BUTTON);
    Serial.println("Start");
    Wire.beginTransmission(ADDRESS);
    Serial.println("Beginning transmission");
    Wire.send(last_state);
    Serial.println("Sent Data");
    Wire.endTransmission();
    Serial.println("Ended transmission");
  }
}
Download i2c-master.pde /**
 *
 * Sample I2C implementation.  Sends a button state over I2C to a slave Arduino
 * with an LED flashing showing button state.
 * 
 * Connections: Arduino analog pins 4 and 5 are connected between the two Arduinos, 
 * with a 1k pullup resistor connected to each line.  On the Master, connect a push 
 * button between digital pin 10 and ground.  On the slave, connect an LED (with a
 * resistor) to digital pin 9.
 * 
 */

#include <Wire.h>

#define LED_OUT 9
#define ADDRESS 42

void setup() {
  pinMode(LED_OUT, OUTPUT);
  digitalWrite(LED_OUT, LOW);
  Serial.begin(112500);
  Wire.begin(ADDRESS);
  Wire.onReceive(receiveEvent);
}


void loop() {

}

void receiveEvent(int howMany){
  while (Wire.available() > 0){
    boolean b = Wire.receive();
    Serial.print(b, DEC);
    digitalWrite(LED_OUT, !b);
  }
  Serial.println(); 
}
Download i2c-slave.pde

Multi Master

The I2C protocol is a multi-master protocol; however, the Wire library in the Arduino distribution does not make it clear on how to implement this functionality. The board setup is very similar to the above Master / Slave, except that each board has a button and an LED attached to it. When you push the button on one, the LED on the other should light up.

It turns out that the Wire library which comes with the Arduino IDE is perfectly capable of doing this; unfortunately, the documentation on the Arduino webpage is slightly misleading in how you would go about doing it. The documentation as currently written states that for Wire.begin(address), the address is optional and 'if not specified, join the bus as a master'. However, what is not stated is that if you transmit something from a device which has already joined as a slave, it will assume the role of Master when it sends the data to another slave.

Code which demonstrates this is included below. You can use the same code for both devices; just swap the THIS_ADDRESS and OTHER_ADDRESS definitions when you use it on the other device. /**
 *
 * Sample Multi Master I2C implementation.  Sends a button state over I2C to another
 * Arduino, which flashes an LED correspinding to button state.
 * 
 * Connections: Arduino analog pins 4 and 5 are connected between the two Arduinos, 
 * with a 1k pullup resistor connected to each line.  Connect a push button between 
 * digital pin 10 and ground, and an LED (with a resistor) to digital pin 9.
 * 
 */

#include <Wire.h>

#define LED 9
#define BUTTON 10

#define THIS_ADDRESS 0x8
#define OTHER_ADDRESS 0x9

boolean last_state = HIGH;

void setup() {
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  
  pinMode(BUTTON, INPUT);
  digitalWrite(BUTTON, HIGH);
  
  Wire.begin(THIS_ADDRESS);
  Wire.onReceive(receiveEvent);
}

void loop() {
  if (digitalRead(BUTTON) != last_state){
    last_state = digitalRead(BUTTON);
    Wire.beginTransmission(OTHER_ADDRESS);
    Wire.send(last_state);
    Wire.endTransmission();
  }
}

void receiveEvent(int howMany){
  while (Wire.available() > 0){
    boolean b = Wire.receive();
    Serial.print(b, DEC);
    digitalWrite(LED, !b);
  }
  Serial.println(); 
}
Download i2c-multimaster-device1.pde
Download i2c-multimaster-device2.pde