6
votes

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.

4
Do you have any access to an electrical measurement equipment? A logic analyzer would be ideal in this case to determine which side of the fence the problem exists on. Also, can you give the part number of your bidirectional level shifter?Chad Kennedy

4 Answers

2
votes

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!

0
votes

In arduino code change sendData() function like this

void sendData(){
    int relay_status;
    relay_status=digitalRead(4);
    Wire.write(relay_status);
    }

Also in hardware connect one 4th digital pin(or any other free I/O pins) to relay input.

hope it helps:)

0
votes

Ok, looks like a pretty good start. Two things I want to suggest here.

First, in your python program you should print number so that you can see it's value changing. It's storing your feedback from the Arduino, so you want to display that feedback to the screen. This is as simple as changing number = readNumber() to print readNumber().

Second, in your Arduino program, are you sure that calling Wire.read() returns what you think it does? It looks to me like read() returns a byte. Chances are that when you type 1 it's really being sent as '1', not 1. Char vs. Int. Make sense?

So you might want to check if(number == '1') instead. Just my 2¢.

0
votes

Your i2c bus is not connected properly. Remove the level shifter, and add 4.7k pull-ups to the 3.3v vcc on scl and sda lines. I2c chips only drive the line low, and require external resistors to pull the line high. This lets you mix voltage levels on your i2c bus fairly easily. Then you can go back to figuring out what your code is doing.