I2C Communication with Arduino

Clarus Goldsmith, June 2024

What is I2C?

The physical interface of I2C has two lines: SDA and SCL. SCL (Serial Clock) is the bus master's clock signal, and SDA (Serial Data) is the data signal.

All Arduino boards have two pins that function as SCL and SDA.

For the OpenCM, SCL is D24 and SDA is D25.

Hardware setup for I2C

Because I2C relies on pulling the voltage down to communicate, you need to supply voltage to the lines using pull-up resistors. Otherwise there's no voltage to pull down, and the communication won't work. I recommend placing the resistors directly connecting to the input pins of whatever device you're communicating with.

An example circuit of how to wire the SCL and SDA pins on an OpenCM with pull-up resistors to communicate with a digital potentiometer. The potentiometer has its SCL at pin 2 and its SDA at pin 3.

The resistance value required will vary depending on the communication speed you're using. I2C has four possible speeds:

Mode
Speed

Standard

100 Kbit/s

Full Speed

400 Kbit/s

Fast

1 Mbit/s

High Speed

3.2 Mbit/s

For standard speed with a single device, I have found that 2kΩ resistors work well. For multiple devices, a lower resistance might be required.

I2C communication with Wire

Wire comes pre-installed with Arduino.

To use, must include this line at the start of the code, before setup:

#include <Wire.h>

During setup, include:

Wire.begin();

Sending commands using Wire

A write command to a device using Wire should always be in the form:

Wire.beginTransmission(address);
Wire.write(byte(0x00));
Wire.write(byte(value));
errorID = Wire.endTransmission();

Where address and value are variables set by the user. This block of code functions as follows:

Line 1: Begins assembling the command signal for the device of address address on the I2C bus. Line 2: Instruction bit; tells the peripheral (formerly known as 'slave') device this is a write command. To specify a read command instead, replace with a 1. However, read commands generally do not follow the same structure as write commands. See the Arduino reference materials on the requestFrom() command for more details. Line 3: The commanded value (e.g., the wiper value for a digital potentiometer). Must be given in binary, which is what the byte() function does in this line. Line 4: Ends the command and sends it. The errorID variable stores the return message from endTransmission(), which is an integer between 0-5. A return of 0 indicates the command was received successfully. Otherwise, some sort of error occurred. Consult the Arduino reference materials to determine exactly what went wrong if receiving an error between 1-5.

Determining device address for beginTransmission()

beginTransmission() requires us to know the address of the device we want to communicate with. This address, often called the 'peripheral/slave address,' will be 7 bytes long. You can find the address in the manual for the device you want to control. For example, here is the peripheral address table for the family of digital potentiometers I used in the hardware section:

Devices meant to communicate over I2C will have 1-3 of their pins dedicated to determining the final few digits of their address. These pins are digital inputs; connecting one to ground sets that digit of the address to 0, while connecting it to the supply voltage sets it to 1. Typically these connections are hard-wired on the PCB to give each device a static ID. The end user can use this functionality to assign their devices different IDs, allowing multiple devices to communicate over the same I2C connection. For example, the digital potentiometer we're considering, MCP4531, has one address pin, A0. So it can have two unique addresses: 0101110 if you connect A0 to ground, and 0101111 if you connect A0 to voltage. We'll consider the former case.

To enter the address into the beginTransmission() function, we need to convert the binary address to base-10. For our example, 0101110 is 46 in base-10. So the first line of our write command for this particular digital potentiometer is:

Wire.beginTransmission(46);

Last updated

Was this helpful?