5

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.

3

First off, the wiring is wrong. Raspberry Pi will burn if you put 5V at its 3.3V power pins. However due to how the I2C protocol works you don't need a logic level translator for the I2C lines.

Also, it would be a better idea to use a GPIO pin on each end to function as a interrupt pin so your Pi don't have to poll on the I2C bus. You can do something like this:

schematic

simulate this circuit – Schematic created using CircuitLab

The Raspberry Pi have built-in pull-ups to 3.3V rail so you don't need your own pull-ups. The 4.7kΩ resistor on the IRQ# line will prevent the current flowing from the 5V Arduino from burning out the 3.3V circuit, saving a translation mechanism.

As of the code, it is mostly right. I cannot spot any issues yet.

  • I think we need to use Logic level conveter when Arduino sends data to Raspberry pi. I took the connection diagram from blog.oscarliang.net/raspberry-pi-arduino-connected-i2c – shivam Mar 9 '16 at 5:15
  • Hey I also know that this code works fine, I need addition in this code that is how to send data back to raspberry pi from arduino. – shivam Mar 9 '16 at 5:17
  • @shivam You don't really need level conversion as the I2C standard defined a compatible 5V and 3.3V logic levels (not your standard TTL or CMOS levels) and as long as the pull-up don't shoot over either end's supply rail there is no risk of burning anything. I have a similar project that an ATmega328P at 5V and a Pi talk over I2C this way. You can use additional level clamping diodes if you want to. – Maxthon Chan Mar 11 '16 at 6:31
  • Thanks @Maxthon Chan but When i send data from Arduino to raspberry pi i.e. sending data from 5v to 3.3v, it may burn up the raspberry pi if the voltage is above 3.3v so I used I2C level converter. Well do you know about how to send the Digital Read pins data from arduino to raspberry pi?. I mean how to use the DigitalRead() command. I am pretty new to Python laguage. – shivam Mar 11 '16 at 6:36
  • 1
    @shivam The entire I2C bus operate at 3.3V even though the 328P is powered by a 5V rail. – Maxthon Chan Mar 11 '16 at 6:38
2

I'm sorry for the late answer but here goes anyway for people trying to solve this task of reading data from the Arduino in a RPi using i2c. I am using a bi-directional shift level converter.

This is what worked pretty well for me. In my application I am sending 4 floats through i2c to the Pi and then pushing those values to Home Assistant using MQTT.

My sensors are two classic Dallas temperature sensors, a water PH probe and an analog pressure transducer sensor.

My Arduino code:

#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>

#define SensorPinPH 0 //pH meter Analog output to Arduino Analog Input 0
#define OffsetPH 1.20  //deviation compensate for PH probe
#define SensorPinPress 1 //pressure meter Analog output to Arduino Analog Input 1
#define OffsetPress 0.0  //deviation compensate for pressure probe

#define FLOATS_SENT 4

float data[FLOATS_SENT];

float voltagePH,voltagePress;
float temp_eau, temp_air, ph_eau, pump_press;

bool debug = true; //Display log message if True
long lastMsg = 0;   
long lastRecu = 0;

OneWire ds_1(3);  //sensor 1 on pin 3 (temp eau)
OneWire ds_2(4);  //sensor 2 on pin 4 (temp air)

//Then search every pin for its address and request temps
DallasTemperature sensor_1(&ds_1);
DallasTemperature sensor_2(&ds_2);

// i2c connexion
#define SLAVE_ADDRESS 0x08 // cet arduino sera esclave 1 sur le bus i2c

//================

void setup() {
  Serial.begin(9600);
  Wire.begin(SLAVE_ADDRESS);
  Wire.onRequest(i2cSend_rasp);
  sensor_1.begin();
  sensor_2.begin();
}

//================

void loop() {
  long now = millis();
  // Read sensors every 5s
  if (now - lastMsg > 1000 * 5) {
    lastMsg = now;
    // Reading data:
    getData();
    // Sending data through serial port:
    sendDataSerial();
    //delay(2000);
  }
}

//================

void getData() {
  // Reading temperatures
  sensor_1.requestTemperatures();
  temp_eau = sensor_1.getTempCByIndex(0);
  sensor_2.requestTemperatures();
  temp_air = sensor_2.getTempCByIndex(0);

  // Reading PH probe
  voltagePH = analogRead(SensorPinPH)*5.0/1024;
  ph_eau = 3.5*voltagePH+OffsetPH;

  // Reading pump pressure
  voltagePress = analogRead(SensorPinPress); 
  pump_press = (voltagePress - 102.0)*2.06843 / (921.0 - 102.0);

  data[0] = temp_eau;
  data[1] = temp_air;
  data[2] = ph_eau;
  data[3] = pump_press;
}

//================

void sendDataSerial() {
  //Serial.print("Water temp: ");
  Serial.print(temp_eau);
  Serial.print(",");
  //Serial.print(" // Air temp: ");
  Serial.print(temp_air);
  Serial.print(",");
  //Serial.print(" // Water PH: ");
  Serial.print(ph_eau);
  Serial.print(",");
  //Serial.print(" // Pump pressure: ");
  Serial.println(pump_press);
}

//================

void i2cSend_rasp() { // the maximum block that can be transmitted is 32bytes!
  /*uint8_t buf[32]; // buffer in which to build block.
  uint8_t len=0;
  memmove(&buf[len],(uint8_t*)&temp_eau,sizeof(temp_eau));
  len += sizeof(temp_eau);
  memmove(&buf[len],(uint8_t*)&temp_air,sizeof(temp_air));
  len += sizeof(temp_air);
  memmove(&buf[len],(uint8_t*)&ph_eau,sizeof(ph_eau));
  len += sizeof(ph_eau);
  memmove(&buf[len],(uint8_t*)&pump_press,sizeof(pump_press));
  len += sizeof(pump_press);
  Wire.write(buf,len);*/
  Wire.write((byte*) &data, FLOATS_SENT*sizeof(float));
}

And my RPi Python code:

import time
from datetime import datetime
import paho.mqtt.client as mqtt
from smbus2 import SMBus
import struct

I2C_Bus = SMBus(1) # Remplacer 0 par 1 pour Raspberry Pi 3
SLAVE_ADD = 0x8 # Direction esclave

mqttc=mqtt.Client()
mqttc.connect("xxx.xxx.xx.xx",keepalive=60)
mqttc.loop_start()

lastMsg = 0

def get_data():
    return I2C_Bus.read_i2c_block_data(SLAVE_ADD,0,16)

def get_float(data, index):
    bytes = bytearray(data[4*index:(index+1)*4])
    return struct.unpack("f", bytes)[0]

try:
    while True:
        data = get_data()
        #print(get_float(data, 0))
        #print(get_float(data, 1))
        #print(get_float(data, 2))
        #print(get_float(data, 3))

        temp_eau = get_float(data, 0)
        temp_air_out = get_float(data, 1)
        ph_eau = get_float(data, 2)
        pump_press = get_float(data, 3)

        dt = datetime.today()  # Get timezone naive now
        now_seconds = dt.timestamp()
        if (now_seconds - lastMsg > 60 * 5): # MQTT messages every 5 minutes...
            lastMsg = now_seconds
            (result,mid) = mqttc.publish("sensor/temp_air_out", payload=temp_air_out, qos=2, retain=True)
            (result,mid) = mqttc.publish("sensor/temp_eau", payload=temp_eau, qos=2, retain=True)
            (result,mid) = mqttc.publish("sensor/ph_eau", payload=ph_eau, qos=2, retain=True)
            (result,mid) = mqttc.publish("sensor/pump_press", payload=pump_press, qos=2, retain=True)
            print("LOG: Date: "+str(dt)+", Temp: "+str(temp_air_out)+", WaterTemp: "+str(temp_eau)+", WaterPH: "+str(ph_eau)+", PumpPress: "+str(pump_press))
        time.sleep(5) 

except KeyboardInterrupt:
    print(" exiting...")
    client.disconnect()
    client.loop_stop()
    I2C_Bus.close()
  • Wow, very interesting code, thanks a lot! – brasofilo May 7 at 5:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.