What is i2c? How do I set it up on the Raspberry Pi?

Introduction

Inter-Integrated Circuit (I2C) is a serial communication protocol used to connect electronic components on the same printed circuit board or between different peripherals. On the Raspberry Pi, the I2C bus is a popular method of enabling the board to communicate with various sensors, modules and other external components. I2C uses two wires: SDA, which enables data transmission, and SCL, which transmits the clock between the raspberry pi board and your component.

The Raspberry Pi has dedicated I2C pins for easy connection to external peripherals. This bus enables bidirectional communication between the master (Raspberry Pi) and several slaves (peripherals). Each I2C device has a unique address, enabling the master to select the device with which it wishes to communicate.

Here is where the i2C pins are located on the raspberry pi:

Installation

We need to install the i2c-toolfs package to use the i2c on the Raspberry pi board:

sudo apt install -y i2c-tools 

We need to install the i2c-toolfs package to use the i2c on the Raspberry pi board:

sudo raspi-config

This is the raspi-config menu. Click on Interface Options:

Once in interface options, click on I2C:

Click yes to activate i2C on the raspberry Pi:

A confirmation window appears:

Test

We’ll now look at how to send data over the i2c to, or receive data from, a device connected to the raspberry pi.

First of all, using the i2cdetect function in the i2c-tools package, you can see which components and their addresses are connected to the raspberry pi’s i2c:

Here you can see our PSTN module connected with address 0x68. You can find the address of your device in its datasheet.

Reading the device

Here’s a program to read values from address 0x68. It reads the minutes, hours and seconds of an RTC module, but can be easily adapted to your needs:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_BUF 256

void readDS1307(int reg) {
    char command[MAX_BUF];
    FILE *fp;

    snprintf(command, sizeof(command), "sudo i2cget -y 1 0x68 %d", reg);
    fp = popen(command, "r");
    if (fp == NULL) {
        printf("Error during command execution.\n");
        exit(1);
    }

    char output[MAX_BUF];
    while (fgets(output, sizeof(output), fp) != NULL) {
        printf("Register value 0x%02X : %d\n", reg, (int)strtol(output, NULL, 16));
    }
    pclose(fp);
}

int main() {
    printf("Reading the time from the DS1307...\n");

    readDS1307(0x00); // Reading seconds (0x00)
    readDS1307(0x01); // Reading minutes (0x01)
    readDS1307(0x02); // Reading hours (0x02)

    return 0;
}

Send to device

Here’s a second program to send values through the i2c to address 0x10:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define SLAVE_ADDRESS "0x10" // Slave device I2C address

void sendValue(const char *value) {
    char command[64];
    FILE *fp;
    snprintf(command, sizeof(command), "i2cset -y 1 %s %s", SLAVE_ADDRESS, value);
    fp = popen(command, "r");
    if (fp == NULL) {
        printf("Error during command execution.\n");
        exit(1);
    }
    pclose(fp);
}

int main() {
    printf("Send values via I2C bus...\n");
    while (1) {
        // Sending the valuer 24 (0x18 en hexadécimal)
        sendValue("0x18");
        printf("Sending the value 24 via I2C\n");
        sleep(1);

        // Sending the value126 (0x7E en hexadécimal)
        sendValue("0x7E");
        printf("Sending the value 126 via I2C\n");
        sleep(1);
    }
    return 0;
}

Here is the result of the SDA and SCL on the oscilloscope: