Hi all,
I have been trying to make a Raspberry Pi communicate with an Arduino Uno via i2c in order to make a kind of "RF card" for the pi.
The idea is to communicate with a distant arduino (with 434MHz RF emitter/receptors). I tried to make the Pi communicate with the arduino directly via RF but as it is not able to cope with real time (at least with the default kernel, as far as I know) I had trouble making it work reliably with a "home made" radio protocol.
VirtualWire works fine between 2 arduinos, hence the idea to put an arduino in between, serving as an i2c RF extension for the Pi.
My problem is that i2c works well for a time, then after a while the arduino disappears from the i2c bus (as seen from Pi perspective at least, it doesn't appear with i2cdetect command anymore) and I have to reset the arduino for it to rejoin the bus.
I started from a simple "ping" application in which the pi sends a string to the Arduino, then the Arduino copies it to a buffer and sends it back when the pi requests it. As I plan to make a bidirectional communication with the other arduino I put a random "latency" before the data is ready (to simulate an acknoledge via radio).
Here is the code for arduino as slave device
#include <Wire.h>
volatile boolean inputAvailable = false;
volatile boolean outputReady = false;
volatile char buffer[32];
char buf[32];
long randomWait;
long heartbeat;
void setup()
{
Wire.begin(4); // join i2c bus with address #4
Wire.onReceive(receiveEvent); // register event
Wire.onRequest(requestEvent); // register event
Serial.begin(9600); // start serial for output
randomSeed(analogRead(0));
heartbeat = millis();
}
void loop()
{
long target;
if (inputAvailable){
randomWait = random(200,2500);
target = millis()+randomWait; // random latency
while (millis() < target); // active waiting
inputAvailable = false;
outputReady = true;
}
if (millis()>= heartbeat){
Serial.println("still alive");
heartbeat = millis()+5000;
}
}
void receiveEvent(int howMany)
{
if (!outputReady && !inputAvailable){
int i = 0;
while(0 < Wire.available() && i < 32)
{
char c = Wire.read();
buffer[i++] = c;
}
inputAvailable = true;
}
else{
// inconsistant state, dump
while (0 < Wire.available()){
Wire.read();
}
}
}
void requestEvent()
{
if (outputReady){
memcpy((void*)buf, (void*)buffer, strlen((const char*)buffer));
Wire.write((const uint8_t*)buf, strlen(buf));
outputReady = false;
}
}
And the code for the pi (master) :
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <time.h>
int deviceHandle;
int main (int argc, char** argv)
{
int readBytes;
int i;
int errCode = 0;
// initialize buffer
char str[32] = "hello";
char buffer[32]; // reponse
memset (buffer, 0, 32);
time_t temps;
if (argc > 1){
sprintf(str,argv[1]);
}
// open device on /dev/i2c-1
deviceHandle = open("/dev/i2c-1", O_RDWR);
if (deviceHandle < 0){
printf("problem with the deviceHandle...\n");
}
// connection to the arduino as i2c slave
int deviceI2CAddress = 0x04;
int res = ioctl(deviceHandle, I2C_SLAVE, deviceI2CAddress);
if (res < 0){
printf("problem with ioctl\n");
}
int writtenBytes;
printf("send %s\n", str);
writtenBytes = write(deviceHandle, str, strlen(str));
if (writtenBytes < 1){
printf("Error!\n");
}
usleep(20000);
char finished = 0;
printf("polling i2c\n");
time_t start, current, totalTime, prevTime;
time (&start);
do{
int answer = read(deviceHandle, buffer, strlen(str));
time (¤t);
totalTime = difftime(current, start);
if(answer != strlen(str) || buffer[0] == 0x00){
if (prevTime < totalTime) // write a dot every second
printf(".");
}else{
printf ("\nReceived: %s\n---------------\n",buffer);
finished = 1;
}
prevTime = totalTime;
}
while(!finished && totalTime < 5);
if (!finished){
fprintf(stderr,"TIME OUT!\n");
errCode = 1;
}
// close connection and exit
close(deviceHandle);
return errCode;
}
The code works ok for some time. I test it with :
$ while [ $? -eq 0 ];do ./a.out anticonstitutionnellement;done
after a while (few dozains of seconds to few minutes) the output is :
send anticonstitutionnellement
polling i2c
...
Received: anticonstitutionnellement
send anticonstitutionnellement
polling i2c
..
Received: anticonstitutionnellement
send anticonstitutionnellement
Error!
polling i2c
Received: ?????????????????????????
send anticonstitutionnellement
Error!
polling i2c
TIME OUT!
The arduino keeps writing "still alive" on the Serial monitor so it's not crashed...
I have a level shifter between the Pi and Arduino, but it worked as "well" without.
I don't have an osciloscope and I don't know if the problem is a timing issue, a clock stretching lock or any other problem.
Does somebody have a clue on this ? I'm out of ideas...