How to read data from Arduino using raspberry pi through I2C

I hooked up a raspberry pi 2 B model with an arduino uno through a bi-directional level shift.

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

I hope my I2C connection is correct?

and my Arduino is connected to an 8 channel relay board.

Now I have written code in which I can control the Raspberry pi relay board. For ex if I Press '1', relay 1 will be high.

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

My rpi code

 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 dial 1 and due to some loose connection, relay 1 does not get high, so in this case I want arduino to receive data from the relay board and send it to raspberry pi each time.

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

Hope I could explain the problem. I did a lot of research, but could not find the answer.

I am a beginner in python, so please help me out.

Thanks in advance.

+5
source share
4 answers

The problem is that you are doing too much inside receiveData , which is called from the I2C utility code interrupt utility, twi.c You must quickly process the data and not call any other routines that depend on the inclusion of interrupts (they are disabled during this ISR).

This means that you cannot call Serial.print , and you cannot call other methods of sending Wire. Even calling millis() or micros() not recommended, as they take a lot of time, and they depend on the TIMER interrupts being processed.

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

Essentially, your receivedData subroutine can read data inside a subroutine if you process it quickly. Otherwise, you can only set the (volatile) flag, and then look at it in a loop . From what I see in your sketch, you can 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 ); } } 

delay in loop not required and can cause problems if you add something else to the loop .

The volatile keyword allows the compiler to optimize the loop . Without this keyword, the test for the newData loop in the loop will disappear, because the compiler believes that newData does not change during loop . Why check it out? volatile newData tells the compiler that newData can change at any time, for example, during receiveData ISR.

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

+2
source

In arduino code, change the sendData () function as follows

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

Also, one 4th digital contact (or any other free I / O pins) is connected to the hardware for relaying the input.

hope this helps :)

0
source

Good, it looks good. Two things I want to offer here.

First, in your python program, you must type number so that you can see the change in value. It saves your feedback from Arduino, so you want to display this feedback on the screen. It is as simple as changing number = readNumber() to print readNumber() .

Secondly, in your Arduino program, are you sure that calling Wire.read() returns what you think of it? It seems to me that read () returns a byte. Most likely, when you enter 1, it really is sent as "1", not 1. Char vs. Int. It makes sense?

So you can check if(number == '1') . Just my 2 ยข.

0
source

Your i2c bus is not connected correctly. Remove the level switch and add 4.7k suspenders to 3.3v vcc on the scl and sda lines. I2c chips use only a low line and require external resistors to pull the line high. This makes it easy to mix voltage levels on the i2c bus. You can then return to figuring out what your code is doing.

0
source

Source: https://habr.com/ru/post/1244641/


All Articles