Отправка строки из 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;
}
@Andy Theimer, 👍2
Обсуждение3 ответа
onReceive предназначен для использования подчиненными. ведущий должен запросить данные у ведомого устройства и дождаться ответа. посмотрите на пример MasterReader из библиотеки Wire
Вот рабочий код. Проблема заключалась в том, что мастер 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
извините за сообщение здесь спустя годы, но мне нужна помощь
Это мой код 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
- Как использовать SPI на Arduino?
- Разница между массивом char и массивом unsigned char
- Как отправлять и получать данные с Arduino Uno на Arduino Mega с помощью I2C?
- Сбой связи SPI
- Код I2C работает на Mega 2560, но не на Uno и nano
- GPS автономный робот
- Сколько соединений может создать метод Arduino I2C?
- Ошибка в связи I2C Между Arduino и Raspberry Pi
Нам нужно увидеть и то, и другое: скетч мастера и скетч слэйва. Не используйте объект String и, конечно же, не в функции receiveEvent. Не используйте Serial.print также из receiveEvent. receiveEvent - это функция прерывания, а Serial.print использует прерывания. Используйте параметр howMany или Wire.available для заполнения массива temp[]. Обязательно обнулите текст в массиве. Установите флаг. В цикле считайте флаг и выводите массив temp[]. Убрав задержку из цикла, вы можете проверять флаг как можно чаще. Имеет ли текст фиксированную длину?, @Jot
Большое вам спасибо за беседу.. очень полезно знать, что важно, особенно на новом (старом) языке. Благодаря этому все заработало., @Andy Theimer