RaspberryPi для Arduino - отправка и получение строки

Это код, который я сейчас использую для отправки и получения значений int от RaspberryPi к Arduino с использованием i2C. Он отлично работает для значений 0–255, но из-за ограничения в 1 байт все, что больше, не работает.
Чтобы обойти это, я хотел бы вместо этого отправлять и получать строковые значения, а затем при необходимости конвертировать обратно в int.< br> Какие изменения мне нужно внести в следующее?
Вот мой код 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);
}

person Bachalo    schedule 17.07.2014    source источник
comment
Извините за (возможно) несвязанный вопрос, но почему бы вместо этого напрямую не использовать серийный номер? Вы вынуждены использовать i2c?   -  person briosheje    schedule 27.07.2014


Ответы (3)


По сути, эта задача состоит из двух частей: разбиения целого числа на его байты и повторной сборки целого числа из байтов. Эти части должны быть воспроизведены как на Pi, так и на Arduino. Сначала я обращусь к стороне Pi на Python:

Разделение целого числа:

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, в 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
Строковые конструкторы Arduino
Встроенные модули Python chr() и ord()

person skrrgwasme    schedule 25.07.2014

Проверьте следующую ссылку:

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

Когда я отправлял данные туда и обратно с помощью I2C, я преобразовывал строковые символы в массивы байтов и наоборот. Итак, поскольку вы всегда отправляете байты. Это всегда будет работать, так как вы отправляете числа от 0 до 255.

Не уверен, что это помогает, но, по крайней мере, может дать вам представление.

person rch    schedule 17.07.2014
comment
Если вы используете «юникод», это 2 байта или более. Вам придется сдвинуть значения либо первого элемента, либо второго элемента. Или их, и вы получите 16-битный эквивалент. Я добавлю к вопросу быстрый пример. - person rch; 17.07.2014
comment
На самом деле. Я провел быстрый поиск, и вы сможете использовать следующий модуль Python для некоторых преобразований: Преобразование между двоичным кодом и ASCII - person rch; 17.07.2014

Вы можете преобразовать число в строку цифр, как вы сказали. Но вы также можете отправить необработанные байты.

Строка цифр

Преимущества

  • Число может иметь бесконечные цифры. Обратите внимание, что когда Arduino считывает число как строку, оно бесконечно, но вы не можете преобразовать его все в целое число, если оно выходит за пределы 16-битного диапазона (или 32-битного для Due).

Недостатки

  • Переменный размер, что требует больше усилий при чтении.
  • Пустая трата байтов, потому что каждая десятичная цифра будет байтом плюс нуль-терминатор в сумме (digits + 1).
  • При необходимости использовать десятичную арифметику (которая на самом деле полезна только для человеческого счета), обратите внимание, что операция «число в строку» также использует десятичную арифметику.
  • Вы не можете отправлять/получать отрицательные числа (если вы не отправляете отрицательный сигнал, тратя больше времени и байтов).

Необработанные байты

Преимущества

  • Количество байтов, отправляемых для каждого целого числа, всегда равно 4.
  • Вы можете отправлять/получать отрицательные числа.
  • Побитовая арифметика в C++ для извлечения каждого байта из числа выполняется очень быстро.
  • В Python уже есть библиотека struct, которая упаковывает/распаковывает каждый байт числа в строку для отправки/получения, поэтому вам не нужно выполнять арифметические действия, как в C++.

Недостатки

  • Число имеет ограниченный диапазон (в нашем случае это 32-битное целое число со знаком, которое находится в диапазоне от -2147483648 до 2147483647). Но это не имеет значения, потому что ни один Arduino не может работать с более чем 32-битным процессором.

Поэтому я бы использовал метод необработанных байтов, для которого я могу предоставить некоторые непроверенные функции:

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, то код должен работать.

person Toribio    schedule 26.07.2014