Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Read Data from Arduino with Raspberry pi via I2C

I have connected Raspberry pi 2 model B with arduino uno via Bi-Directional Level shifter.

Raspberry pi    GND    ----------   GND     Arduino
                3.3v   ----------   5v
                SCL    ----------   A5
                SDA    ----------   A4

Hope my I2C connection is correct ?

and my Arduino is connected to 8-Channel Relay Board.

Now I have written code in which I can control the Relay board by Raspberry pi. For ex if i Press '1' the Relay 1 goes high.

Now I want to send data back from arduino to raspberry pi in order to cross check if Relay 1 is high or not, if Relay 1 is high then it should send some data back to Raspberry pi or else not.

My Rpi code is

import smbus
import time
# for RPI version 1, use "bus = smbus.SMBus(0)"
bus = smbus.SMBus(1)

# This is the address we setup in the Arduino Program
address = 0x04

def writeNumber(value):
    bus.write_byte(address, value)
    # bus.write_byte_data(address, 0, value)
    return -1

def readNumber():
    number = bus.read_byte(address)
    # number = bus.read_byte_data(address, 1)
    return number

while True:
    var = input("")
    if not var:
        continue

    writeNumber(var)
    number = readNumber()

My Arduino code:

#include <Wire.h>

#define SLAVE_ADDRESS 0x04
#define RELAY1 9

int number = 0;
int state = 0;

void setup() {
    pinMode(RELAY1, OUTPUT);

    Serial.begin(9600); // start serial for output
    // initialize i2c as slave
    Wire.begin(SLAVE_ADDRESS);

    // define callbacks for i2c communication
    Wire.onReceive(receiveData);
    Wire.onRequest(sendData);

    Serial.println("Ready!");
}

void loop() {
    delay(100);
}

// callback for received data
void receiveData(int byteCount){

    while(Wire.available()) {
        number = Wire.read();
        Serial.print("data received: ");
        Serial.println(number);

        if (number == 1){

            if (state == 0){
                digitalWrite(RELAY1, HIGH); // set the LED on
                state = 1;
            }
            else{
                digitalWrite(RELAY1, LOW); // set the LED off
                state = 0;
            }
        }
    }
}

// callback for sending data
void sendData(){
    Wire.write(number);
}

Now if I type 1 and due to some loose connection Relay 1 doesn't goes high, So in this case I want the arduino to take data from relay board and send it to Raspberry pi every time.

It will be great if someone can explain also that how it works.

Hope I was able to explain the problem. I have done lots of research but was not able to find some answer.

I am a beginner in python so please help me.

Thanks in advance.

like image 393
shivam Avatar asked Mar 08 '16 07:03

shivam


People also ask

How do I communicate with Arduino with I2C?

Follow these steps to connect two Arduino UNOs using I2C: Connect pins A4 and A5 on one Arduino to the same pins on the other one. The GND line has to be common for both Arduinos. Connect it with a jumper.

How do I transfer data from Arduino to Raspberry Pi?

In common, the connections are fairly easy. Just connect Arduino USB Plug to Raspberry PI with USB cable and check the connection between Arduino and Raspberry pi by type "ls /dev/tty*" in Raspberry Pi terminal, the result should be content "/dev/ttyACM0" and you are good to go.

Can Raspberry Pi read I2C?

We can access I2C bus on Raspberry Pi using SMBus. SMBus is a subset of I2C bus/interface. SMBus provides support for I2C based devices. While writing program to access I2C based device, make use of SMBus commands.


1 Answers

The problem is that you are doing too much inside receiveData, which is called from the Interrupt Service Routine of the I2C utility code, twi.c. You must handle the data quickly, and don't call any other routines that depend on interrupts being enabled (they are disabled during this ISR).

This means you can't call Serial.print, and you can't call any other Wire sending methods. Even calling millis() or micros() is discouraged, as they do take a fair amount of time, and they depend on the TIMER interrupts being handled.

Of course, you are free call Wire.available() and Wire.read(). Actually, byteCount tells you how many bytes are available, so you don't need to call Wire.available() again.

Essentially, your receivedData routine can read the data inside the routine if you're quick about processing it. Otherwise, you can only set a (volatile) flag and then watch for it in loop. From what I see in your sketch, you could do something like this:

// variables that allow signalling between receiveData ISR and loop
volatile bool    newData = false;
volatile uint8_t state   = false;

// callback for received data
void receiveData(int byteCount)
{
    // Read all the bytes; only the last one changes the relay state
    while (byteCount-- > 0)
      number = Wire.read();

    if (state != number) {
      state   = number;
      newData = true;
    }
}

// callback for sending data
void sendData(){
    Wire.write(number);
}

void loop()
{
  if (newData) {
    newData = false; // clear the flag for next time

    if (number == 1){
        digitalWrite(RELAY1, HIGH); // set the LED on
    } else {
        digitalWrite(RELAY1, LOW); // set the LED off
    }

    Serial.print("data received: ");
    Serial.println( number );
  }
}

The delay in loop is unnecessary, and may cause problems if you add something else to loop.

The volatile keyword keeps the compiler from optimizing loop. Without that keyword, the test for newData in loop would disappear because the compiler thinks that newData doesn't change during loop. Why test it? volatile newData tells the compiler that newData can change at any time, like during the receiveData ISR.

And be sure to print the number in the rpi code, as pholtz suggested!

like image 86
slash-dev Avatar answered Oct 03 '22 18:10

slash-dev