如何使用Raspberry pi通过I2C从Arduino读取数据

时间:2016-03-08 07:17:24

标签: python arduino raspberry-pi i2c

我通过Bi-Directional Level Shifter将Raspberry pi 2模型B与arduino uno连接。

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

希望我的I2C连接正确吗?

我的Arduino连接到8通道继电器板。

现在我编写了代码,我可以通过Raspberry pi控制Relay板。如果我按下' 1'继电器1走高。

现在我想将数据从arduino发送回raspberry pi,以便交叉检查Relay 1是否为高,如果Relay 1为高,那么它应该将一些数据发送回Raspberry pi,否则不会。

我的Rpi代码是

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()

我的Arduino代码:

#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);
}

现在如果我输入1并且由于一些松散的连接,继电器1不会变高,所以在这种情况下我希望arduino从中继板获取数据并每次都将它发送到Raspberry pi。

如果有人能够解释它是如何运作的话会很棒。

希望我能够解释这个问题。我做了很多研究,但未能找到答案。

我是python的初学者所以请帮助我。

提前致谢。

4 个答案:

答案 0 :(得分:2)

问题是你在receiveData内部做了太多,这是从I2C实用程序代码的中断服务程序twi.c调用的。您必须快速处理数据,并且不要调用依赖于启用中断的任何其他例程(在此ISR期间它们被禁用)。

这意味着您无法拨打Serial.print,也无法拨打任何其他电汇发送方式。不鼓励调用millis()micros(),因为它们确实需要相当长的时间,并且它们依赖于正在处理的TIMER中断。

当然,您可以免费致电Wire.available()Wire.read()。实际上,byteCount会告诉您可用的字节数,因此您无需再次致电Wire.available()

基本上,如果你快速处理它,你的receivedData例程可以读取例程中的数据。 否则,您只能设置一个(易失性)标志,然后在loop中进行监听。从我在你的草图中看到的,你可以做这样的事情:

// 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中的loop是不必要的,如果您向loop添加其他内容,则可能会出现问题。

volatile关键字使编译器无法优化loop。如果没有该关键字,循环中newData的测试将会消失,因为编译器认为newDataloop期间没有发生变化。为什么测试呢? volatile newData告诉编译器newData可以随时更改,例如在receiveData ISR期间。

请务必按照pholtz的建议在rpi代码中打印number

答案 1 :(得分:0)

在arduino代码中 像这样改变sendData()函数

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

同样在硬件中连接一个第4个数字引脚(或任何其他空闲I / O引脚)来继电器输入。

希望它有所帮助:)

答案 2 :(得分:0)

好的,看起来是个不错的开始。我想在这里提出两件事。

首先,在你的python程序中,你应该打印number,这样你才能看到它的价值在变化。它存储了Arduino的反馈,因此您希望将反馈显示在屏幕上。这就像将number = readNumber()更改为print readNumber()一样简单。

其次,在你的Arduino程序中,你确定调用Wire.read()会返回你认为它的作用吗?在我看来,read()返回一个字节。有可能当你输入1时,它实际上被发送为'1',而不是1. Char vs. Int。有意义吗?

因此,您可能需要检查if(number == '1')。只是我2¢。

答案 3 :(得分:0)

您的i2c总线连接不正确。移除电平移位器,并在scl和sda线上的3.3v vcc上添加4.7k引线。 I2c芯片仅将线路驱动为低电平,并需要外部电阻将线路拉高。这使您可以非常轻松地在i2c总线上混合电压电平。然后你可以回过头来弄清楚你的代码在做什么。