Rock Pi 4B: I2C Communication

Getting I2C to work between a Rock Pi 4B and an Arduino can be a challenge for those who are new to the world of Single-Board Computers. Many tutorials can be found for getting I2C ready on a Raspberry Pi. Those tutorials, however, do not translate directly to the Rock Pi 4B because of differences in hardware.

For reference, I am running Armbian 5.67. I will be using a Python script to demonstrate I2C communication between a Rock Pi and an Arduino-based Pro Mini. In my demonstration, the Rock Pi will be the master device, sending data to the Arduino. The Arduino will be the slave device, accepting data from the Rock Pi.

Wiring for I2C

Rock Pi 4B

I have attached a GPIO map for the Rock Pi 4B. This diagram was sourced from Radxa’s official hardware page for this board. For our I2C connection, we will be using pins 27 (SDA) and 28 (SCL). The labels for these pins let us know that we will be connecting to the I2C-2 interface. The extension, 2, is important to note as we will have to specify which I2C interface to join later on.

There is more than one I2C bus available via the GPIO pins on the Rock Pi 4B.

Arduino

For the Arduino-based Pro Mini that I am using, pins A4 (SDA) and A5 (SCL) are used for I2C. The same pins can be used for other boards such as the Uno and Nano. To connect the devices, we will connect the following pins from Rock Pi -> Arduino.

  • SDA -> SDA
  • SCL -> SCL
  • GND -> GND

Dependencies

Before writing any code, we should install a few dependencies onto the Rock Pi. We will be installing the packages python3-pip and i2c-tools. Pip is a Python package manager that makes installing necessary packages very easy.

# Update and upgrade apt.
sudo apt update && sudo apt upgrade
# Install python3-pip
sudo apt install python3-pip

With Pip installed, we can install the Python package, SMBus, that will allow us to join the I2C bus easily.

sudo pip3 install smbus

Arduino Sketch

With our dependencies now installed, we can move onto writing some code. Lets start off the Arduino sketch with a necessary import.

#include <Wire.h>  // Import Wire for I2C.

Then, we can initialize some variables to hold an address and any incoming data.

#define ADDRESS 0x04  // This device's I2C address.
int incomingInt  // To store incoming integers.

Next, lets write the setup function. We start a serial connection with a baud rate of 9600, for displaying received data to the serial monitor. We also join the I2C bus on our defined address by calling Wire.begin, passing in ADDRESS as our only parameter. Then, we define a function to call whenever data is received. That function, in my example is named printData.

void setup() {
  Serial.begin(9600);  // For displaying data.
  Wire.begin(ADDRESS);  // Join I2C bus on ADDRESS.
  Wire.onReceive(printData); // Function to call when data is received.
}

Now lets write the printData function. This function, as the name suggests, will print the data that is received over the I2C bus. We use Wire.read to read in data that is sent over the I2C bus.

void printData()
{
  incomingInt = Wire.read();  // Store data in incomingInt.
  Serial.println(incomingInt);  // Print incomingInt to console. 
}

Finally, we can write our loop function. This one is easy…

void loop() {
  // Nothing to do here.
  // Whenever data is received, printData will be called.
}

Altogether now!

#include <Wire.h>  // Import Wire for I2C.

#define ADDRESS 0x04  // This device's I2C address.
int incomingInt;  // To store incoming integers.

void setup() {
  Serial.begin(9600);  // For displaying data.
  Wire.begin(ADDRESS);  // Join I2C bus on ADDRESS.
  Wire.onReceive(printData); // Function to call when data is received.
}

void printData()
{
  incomingInt = Wire.read();  // Store data in incomingInt.
  Serial.println(incomingInt);  // Print incomingInt to console. 
}

void loop() {
  // Nothing to do here.
  // Whenever data is received, printData will be called.
}

Python Script

With our Arduino sketch finished, we can write a short Python script that sends integer data to the Arduino. Just as before, we will start by importing a few libraries.

from smbus import SMBus  # Import SMBus for I2C.
from time import sleep  # Import sleep for a delay.

To be able to use functions from the SMBus library, we have to create an object from that library. We will pass in the number 2 as a parameter because that is the I2C interface that we are utilizing.

bus = SMBus(2)  # Join I2C-2 interface.

Now we will initialize a variable to hold an address to the slave device. This address should match that which we defined in our Arduino sketch.

ADDRESS = 0x04  # Address to slave device. Send data here.

Finally, we create a for-loop that will send the Arduino numbers in an incremental fashion. To send data over I2C, in Python, we will use our bus object. Because we created this object using the SMBus class, we have access to the methods of that class.

for x in range(255):  # Will increment from 0 to 254.
    bus.write_byte(ADDRESS, x)  # Send x to ADDRESS.
    sleep(0.25)  # Delay to slow down the script. 

Altogether now, again!

from smbus import SMBus  # Import SMBus for I2C.
from time import sleep  # Import sleep for a delay.

bus = SMBus(2)  # Join I2C bus number 2.
ADDRESS = 0x04  # Address to slave device. Send data here.

for x in range(255):  # Will increment from 0 to 254.
    bus.write_byte(ADDRESS, x)  # Send x to ADDRESS.
    sleep(0.25)  # Delay to slow down the script. 

Running The Program

For our circuit and program to run correctly, upload the sketch to your Arduino and open a serial monitor on port 9600. Then, from a terminal on the Rock Pi, execute the following command.

cd
nano i2cScript.py

You should find yourself in the Nano text editor. Copy the Python script into the editor. After you have copied the script, save and exit Nano by pressing Ctrl-X, followed by the letter y, and finalized with Enter.

Once the script is ready to go, you can run the program by executing the following command.

sudo python3 i2cScript.py

If you followed along, correctly, you will see a count showing up on the serial monitor.

Thanks for following along!