Отправка строки из RPi в Arduino - Рабочий код

Я уже 2 дня занимаюсь исследованиями, пытаясь выяснить, как получить строку через I2C на Arduino. В Интернете есть много вопросов, но нет реальных решений или полных руководств... и, к сожалению, я выбросил свою книгу по C ++ много лет назад.

Приведенный ниже код компилируется без ошибок и получает строку правильно. (Есть несколько закомментированных блоков, которые я использовал для проверки получения данных.) Проблема заключается в печати полной полученной командной строки. Я не могу заставить его печатать в receiveEvent() или в цикле (). В конечном счете, мне это нужно в виде строки, такой как "WIN-23-200", где я разорву строку с помощью "-", чтобы что-то сделать, но я все еще не могу ее получить.

Если кто-нибудь может подсказать мне правильное направление, я был бы признателен.

Спасибо!

#include <Wire.h>

#define SLAVE_ADDRESS 0x04

volatile boolean receiveFlag = false;
char temp[32];
String command;

void setup() {
  // инициализировать i2c как подчиненный
  Wire.begin(SLAVE_ADDRESS);

  // определение обратных вызовов для связи i2c
  Wire.onReceive(receiveEvent);

  Serial.begin(9600);
  Serial.println("Ready!");

}

void loop() {
  delay(1000);
  if (receiveFlag == true) {
    Serial.println("inFlag");
    Serial.println(command);
    receiveFlag = false;
  }
}

void receiveEvent(int howMany) {
  // работает так, как ожидалось
  /*while (0 < Wire.available()) { // перебирать все
    char c = Wire.read();
    Serial.print(c);
    }*/

  int count = 0;
  while (0 < Wire.available()) { // перебирать все
    temp[count] = Wire.read();
    count++;
  }
  //температура[32-1]='\0'; // убедитесь, что его значение равно 0, не имеет значения
  command = temp; //поместите массив символов в строковую переменную с именем command

  Serial.print(temp); // ничего не печатает....

  //Убедитесь, что массив загружен..отлично работает.
  //for (int i = 0; i < count; i++)
  //{
  // Serial.println(temp[i]);
  //}
  receiveFlag = true;
}

, 👍2

Обсуждение

Нам нужно увидеть и то, и другое: скетч мастера и скетч слэйва. Не используйте объект String и, конечно же, не в функции receiveEvent. Не используйте Serial.print также из receiveEvent. receiveEvent - это функция прерывания, а Serial.print использует прерывания. Используйте параметр howMany или Wire.available для заполнения массива temp[]. Обязательно обнулите текст в массиве. Установите флаг. В цикле считайте флаг и выводите массив temp[]. Убрав задержку из цикла, вы можете проверять флаг как можно чаще. Имеет ли текст фиксированную длину?, @Jot

Большое вам спасибо за беседу.. очень полезно знать, что важно, особенно на новом (старом) языке. Благодаря этому все заработало., @Andy Theimer


3 ответа


1

onReceive предназначен для использования подчиненными. ведущий должен запросить данные у ведомого устройства и дождаться ответа. посмотрите на пример MasterReader из библиотеки Wire

,

2

Вот рабочий код. Проблема заключалась в том, что мастер Raspberry Pi добавлял "cmd-байт" к строке перед передачей. Поэтому, когда я отправлял "test", Arduino получал 5 байт; "0test". Основываясь на отзывах здесь, я добавил null после символа ea, а затем сдвинул массив, чтобы исключить первый байт, и Serial.print начал работать.

Вот рабочий код RPi Master на Python:

# -*- coding: utf-8 -*-
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

#http://www.raspberry-projects.com/pi/programming-in-python/i2c-programming-in-python/using-the-i2c-interface-2
def writeData(value):
    byteValue = StringToBytes(value)    
    bus.write_i2c_block_data(address,0x00,byteValue) #first byte is 0=command byte.. just is.
    return -1


def StringToBytes(val):
        retVal = []
        for c in val:
                retVal.append(ord(c))
        return retVal

while True:
    print("sending")
    writeData("test")   
    time.sleep(5)

    print('OPEN');
    writeData("OPEN-00-00")
    time.sleep(7)

    print('WIN');
    writeData("WIN-12-200")
    time.sleep(7)

И подчиненный код Arduino

#include <Wire.h>

#define SLAVE_ADDRESS 0x04

volatile boolean receiveFlag = false;
char temp[32];
String command;

void setup() {
  // инициализировать i2c как подчиненный
  Wire.begin(SLAVE_ADDRESS);

  // определение обратных вызовов для связи i2c
  Wire.onReceive(receiveEvent);

  Serial.begin(9600);
  Serial.println("Ready!");

}

void loop() {

  if (receiveFlag == true) {
    Serial.println(temp);
    receiveFlag = false;
  }
}

void receiveEvent(int howMany) {

  for (int i = 0; i < howMany; i++) {
    temp[i] = Wire.read();
    temp[i + 1] = '\0'; // добавьте null после советника. обугленный
  }

  //Первый байт RPi - это cmd-байт, поэтому сдвиньте все влево на 1 поз, чтобы temp содержал нашу строку
  for (int i = 0; i < howMany; ++i)
    temp[i] = temp[i + 1];

  receiveFlag = true;
}

Надеюсь, это поможет кому-то еще, я искал время, чтобы собрать это вместе!

,

Пара ресурсов, которые также были полезны для меня: список команд шины i2c: [link](http://www.raspberry-projects.com/pi/programming-in-python/i2c-programming-in-python/using-the-i2c-interface-2) Параметр Raspberry Pi bus cmd хорошо объяснен: [link](https://raspberrypi.stackexchange.com/questions/8469/meaning-of-cmd-param-in-write-i2c-block-data), @Andy Theimer

Молодец. Вам не нужно перекладывать его в receiveEvent. Вы можете выполнить команду "int cmd=Wire.read();" перед первым циклом for. Вы можете использовать "cmd" или нет, и использовать его только для чтения первого байта. Я бы просто прочитал данные в массив и решил любую проблему или любой cmd-байт в цикле. Когда вы печатаете полученные данные, во время этого может произойти прерывание с новыми данными, и вы будете печатать первую половину старых данных и вторую половину новых данных. Этого можно избежать, скопировав temp[] в локальный массив, пока прерывания отключены. Можете ли вы сделать temp[] также "изменчивым"? просто чтобы быть уверенным., @Jot

Только одно замечание: ваш код безопасен только потому, что библиотека Wire имеет 32-байтовый буфер, в противном случае это может привести к загрязнению памяти. Мое предложение состоит в том, чтобы изменить код одним из следующих способов: 1) char temp[32]; становится char temp [BUFFER_LENGTH]; (BUFFER_LENGTH определяется в Wire.h) или 2) проверьте, что Сколько меньше длины временного буфера. 2 более надежен, но 1 выполняет меньше операций, @frarugi87


0

извините за сообщение здесь спустя годы, но мне нужна помощь

Это мой код arduino, код Raspberry pi такой же, как и выше.

#включить <Wire.h>

#определить SLAVE_ADDRESS 0x04

изменчивое логическое значение receiveFlag = false; char temp[32]; Строковая команда;

void setup() { // инициализировать i2c как подчиненный провод.begin(SLAVE_ADDRESS);

// определить обратные вызовы для канала связи i2c.onReceive(receiveEvent);

Serial.begin(1200); Serial.println("Готово!");

}

void loop() {

if (receiveFlag == true) { Serial.println(температура); if (temp == 'тест'){ Serial.print("это работает"); } receiveFlag = false; } }

void receiveEvent(int сколько) {

for (int i = 0; i < Сколько; i++) { temp[i] = Wire.read(); temp[i + 1] = '\0'; // добавьте null после советника. обугленный }

// Первый байт RPi - это cmd-байт, поэтому сдвиньте все влево на 1 пункт, чтобы temp содержал нашу строку для (int i = 0; i <Сколько; ++i) temp[i] = temp[i + 1];

receiveFlag = true; }

я хочу заставить его делать что-то вроде простого включения светодиода, когда полученные данные являются тестовыми (например).Я получаю текст (тест) на последовательном мониторе, но он ничего не делает, когда я использую эту команду

if (temp == 'тест'){ Serial.print("это работает"); }

любая помощь будет оценена по достоинству

,

Это не дает ответа на поставленный вопрос. Чтобы задать другой вопрос, просто нажмите [Задать вопрос] (https://arduinoprosto.ru/q/ask ) выше. Если вы хотите, включите ссылку на этот вопрос, чтобы помочь обеспечить контекст., @sempaiscuba