This page describes experiments in communicating between a Raspberry Pi (as a master) and an Arduino (as a slave) over I²C. The Raspberry Pi is programmed in Python, the Arduino using a sketch (the Arduino's own language).
I based this experiment on an article found on The Robotics Back-End (see References below), but have modified it to use Python rather than C on the Raspberry Pi, since that's the main language I've been using to write the operating system of the KR01 robot. Some of the ideas I found online tended to lock up the I²C bus or cause other kinds of problems. When I stumbled onto an example using the WiringPi library things seemed to even out.
WiringPi is a C++ library for communicating with a Raspberry Pi. It's been ported to Python as the WiringPi-Python library. In particular, I tried using the wiringPiI2CRead() and wiringPiI2CWrite() methods. For documentation, see The I²C Reference Library.
It should be noted that the I²C read and write methods send a single integer. In order to communicate strings or other data, you'll need to serialise the data and then deserialise it on the other end.
For an example, see: How do I serialize a Python dictionary into a string, and then back to a dictionary?
In the end I developed a solution called pimaster2ardslave that has been posted as a project on github, which includes a Raspberry Pi Python 3 script and an Arduino sketch.
I found to my consternation that the Arduino Nano BLE Sense that I bought for the purpose of being used as the slave processor can't be used as a slave: the Wire library it uses hasn't implemented the I²C slave protocol, though this is not documented anywhere (I found this out on the Arduino forum). One of the benefits of the Nano BLE Sense was that it's a 3.3V device, whereas the regular Nano is 5V. I'm still searching for the right Arduino board now... (😠) maybe I'll use a Teensy or an Adafruit board.
WiringPi for Python can be installed via:
sudo pip3 install wiringpi
You'll need to install an Arduino sketch on your Arduino using the Arduino IDE. As an example, I modified the original source to blink the Arduino's built-in LEDs when either a transmit (TX) or receive (RX) event occurs.
#include <Wire.h> #define SLAVE_ADDRESS 0x08 byte data_to_echo = 0; // this slave code is meant to work on I2C address 0x08 // with rpi_arduino_wiringpi_i2c.cpp as described at: // https://roboticsbackend.com/raspberry-pi-master-arduino-slave-i2c-communication-with-wiringpi/ void setup() { Wire.begin(SLAVE_ADDRESS); Wire.onReceive(receiveData); Wire.onRequest(sendData); ready_blink(); } void loop() { } void receiveData(int bytecount) { for (int i = 0; i < bytecount; i++) { data_to_echo = Wire.read(); } blink_rx(); } void sendData() { Wire.write(data_to_echo); blink_tx(); } // status displays ..................................................... void ready_blink() { int i = 0; while ( i < 800 ) { blink_builtin(); delay(i); i = i + 50; } } void blink_builtin() { digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) delay(100); // wait 100ms digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW } void blink_tx() { digitalWrite(LED_BUILTIN_TX, HIGH); // turn the LED on (HIGH is the voltage level) delay(100); // wait 100ms digitalWrite(LED_BUILTIN_TX, LOW); // turn the LED off by making the voltage LOW } void blink_rx() { digitalWrite(LED_BUILTIN_RX, HIGH); // turn the LED on (HIGH is the voltage level) delay(100); // wait 100ms digitalWrite(LED_BUILTIN_RX, LOW); // turn the LED off by making the voltage LOW }