RaspberryPi到Arduino -send并接收字符串

时间:2014-07-17 15:31:20

标签: python arduino raspberry-pi i2c

这是我目前用于使用i2C从RaspberryPi向Arduino发送和接收int值的代码。它适用于0-255的值,但由于1字节限制,任何更大的值都会失败 为了避免这种情况,我想发送和接收字符串值,然后在必要时转换回int 我需要在下面做些什么改变?
这是我的RPi Python代码

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:
    try:
        var = int(raw_input("Enter 1 - 9: "))
    except ValueError:
        print "Could you at least give me an actual number?"
        continue

    writeNumber(var)
    print "RPI: Hi Arduino, I sent you ", var
    # sleep one second
    #time.sleep(1)

    number = readNumber()
    print "Arduino: Hey RPI, I received a digit ", number
    print

这是我的Arduino代码

#include <Wire.h>

#define SLAVE_ADDRESS 0x04
int number = 0;
int state = 0;

void setup() {
    pinMode(13, 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();
        if (Wire.available() > 1)  // at least 2 bytes
        {
          number = Wire.read() * 256 + Wire.read();
        }
        Serial.print("data received: ");
        Serial.println(number);
        //sendData();
        if (number == 1){

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

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

3 个答案:

答案 0 :(得分:1)

检查以下链接:

[http://www.i2c-bus.org/][1]

当我使用I2C向前和向后发送数据时,我将字符串字符转换为bytearrays,反之亦然。所以,因为你总是发送字节。它始终有效,因为您发送的数字介于0-255之间。

不确定这会有所帮助,但至少可以给你一个想法。

答案 1 :(得分:1)

这个问题基本上有两个部分:将一个整数分成它的字节,然后从字节重组一个整数。必须在Pi和Arduino上复制这些部分。我将首先在Python中解决Pi端:

拆分整数:

def writeNumber(value):

    # assuming we have an arbitrary size integer passed in value
    for character in str(val): # convert into a string and iterate over it
        bus.write_byte(address, ord(character)) # send each char's ASCII encoding

    return -1

从字节重组整数:

def readNumber():

    # I'm not familiar with the SMbus library, so you'll have to figure out how to
    # tell if any more bytes are available and when a transmission of integer bytes
    # is complete. For now, I'll use the boolean variable "bytes_available" to mean
    # "we are still transmitting a single value, one byte at a time"

    byte_list = []
    while bytes_available:
        # build list of bytes in an integer - assuming bytes are sent in the same
        # order they would be written. Ex: integer '123' is sent as '1', '2', '3'
        byte_list.append(bus.read_byte(address))

    # now recombine the list of bytes into a single string, then convert back into int
    number = int("".join([chr(byte) for byte in byte_list]))
    return number

Arduino Side,在C

拆分整数:

void sendData(){
    int i = 0;
    String outString = String(number); /* convert integer to string */
    int len = outString.length()+1     /* obtain length of string w/ terminator */
    char ascii_num[len];               /* create character array */

    outString.toCharArray(ascii_num, len); /* copy string to character array */

    for (i=0; i<len); ++i){
        Wire.write(ascii_num[i]);
    }
}

重新组合收到的整数: 注意:我在理解你的其他代码在这个例程中正在做什么时遇到了一些麻烦,所以我要把它简化为只是组装整数。

void receiveData(int byteCount){
    int inChar;
    String inString = "";

    /* As with the Python receive routine, it will be up to you to identify the
       terminating condition for this loop - "bytes_available" means the same thing
       as before */

    while(bytes_available){ 
        inChar = Wire.read();
        inString += char(inChar);
    }

    number = inString.toInt();
}

我手头没有材料来测试这个,所以我可以在一个或另一个例程中翻转字节顺序。如果你发现有东西进出,最简单的修复方法是在Python脚本中使用字符串或列表上的内置函数reversed()

引用(我使用了Arduino示例中的一些代码):
Arduino String objects
Arduino String Constructors
Python内置chr()ord()

答案 2 :(得分:0)

您可以将数字转换为您所说的数字字符串。但你也可以发送原始字节。

数字字符串

<强>优点

  • 数字可以有无限数字。请注意,当Arduino将数字作为字符串读取时,它是无限的,但如果它溢出16位范围(或32位for Due),则无法将其全部转换为整数。

<强>缺点

  • 可变尺寸,因此需要更多精力阅读。
  • 浪费字节,因为每个十进制数字都是一个字节,加上空终结符总计(digits + 1)大小。
  • 必须使用十进制算术(实际上只对人类计数有用),请注意“数字到字符串”操作也使用十进制算术。
  • 您不能发送/接收负数(除非您发送减号信号,浪费更多时间和字节)。

原始字节

<强>优点

  • 每个整数发送的字节数始终为4.
  • 您可以发送/接收负数。
  • C ++中用于从数字中提取每个字节的按位运算非常快。
  • Python已经有struct库,它将数字中的每个字节打包/解包到一个字符串中发送/接收,因此您不需要像在C ++中那样进行算术。

<强>缺点

  • Number的范围有限(在我们的示例中为带符号的32位整数,范围从-21474836482147483647)。但这并不重要,因为没有Arduino无论如何都不能处理超过32位。

所以我会使用raw bytes方法,我可以在这里提供一些未经测试的函数:

import struct

# '<i' stands for litle-endian signed integer

def writeNumber(value):
  strout = struct.pack('<i', value)
  for i in range(4):
    bus.write_byte(address, strout[i])
  return -1

def readNumber():
  strin = ""
  for _ in range(4):
    strin += bus.read_byte(address)
  return struct.unpack('<i', strin)[0]

Arduino部分:

void receiveData(int byteCount)
{
    // Check if we have a 32-bit number (4 bytes) in queue
    while(Wire.available() >= 4)
    {
        number = 0;
        for(int i = 0; i < 32; i += 8)
        {
            // This is merging the bytes into a single integer
            number |= ((int)Wire.read() << i);
        }

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

        // ...
    }
}

void sendData()
{
    for(int i = 0; i < 32; i += 8)
    {
        // This is extracting each byte from the number
        Wire.write((number >> i) & 0xFF);
    }
}

我对I2C没有任何经验,但如果它的队列是FIFO,那么代码应该可以工作。