Отправка и получение различных типов данных через I2C в Arduino
Я нашел несколько хороших руководств по отправке и получению данных через I2C на подключенных Arduino. Теперь мое ограничение заключается в том, как передавать различные типы данных, такие как, например, long, float и т. д. Прямо сейчас связь через I2C с использованием проводной библиотеки позволяет передавать только числа от 0 до 255. Как можно передавать разные типы чисел? .
В моем коде мастер получает три типа данных от подчиненного устройства и отправляет подчиненному данные четвертой переменной.
вот код мастера
#include <Wire.h>
const int SLAVE_ADDRESS = 8; //Идентификатор ведомого ардуино
int table[]={0,0,0}; // данные будут передаваться через таблицу, чтобы можно было передавать разные данные.
void setup ()
{
Wire.begin ();
Serial.begin (9600); // запускаем сериал для вывода
} // конец настройки
void loop()
{
Wire.requestFrom(SLAVE_ADDRESS, 3);// запрашиваем 3 байта у ведомого устройства №8
for(int i=0;i<3;i++)//упорядочивает данные с подчиненного устройства в таблицу
{
int c = Wire.read(); // получаем байт как символ
table[i]=c;
}
//выводит данные
Serial.print('\n');
Serial.print(table[0]);
Serial.print('\t');
Serial.print(table[1]);
Serial.print('\t');
Serial.print(table[2]);
Serial.print('\n');
delay (500);
// передаем ведомому только эту переменную
int Work=1;
Wire.beginTransmission (8);
Wire.write (Work);
Wire.endTransmission ();
}
вот код подчиненного устройства
#include <Wire.h>
int table[]={0,0,0};
int Work=0;
void setup() {
Wire.begin(8); // присоединяемся к шине i2c с адресом #8
Wire.onRequest(requestEvent); // регистрируем событие
Wire.onReceive(receiveEvent); // регистрируем событие
}
void loop() {
int x=120;
int y=1200;// преобразуется в странный #, потому что больше 255
int z=3;
table[0]=x;
table[1]=y;
table[2]=z;
}
void requestEvent()
{
uint8_t Buffer[3];
Buffer[0]=table[0];
Buffer[1]=table[1];
Buffer[2]=table[2];
Wire.write(Buffer,3);
}
void receiveEvent(int howMany)
{Work = Wire.read();}
@Camilo, 👍7
3 ответа
Лучший ответ:
I2C — действительно мощная опция Arduino по слишком многим причинам; тем не менее количество доступных руководств не так уж много и, к сожалению, они слишком сложны для среднего человека.
Поработав над этим в течение двух дней, я думаю, что у меня есть способ передавать практически все что угодно между ведущим и подчиненным и наоборот. Обратите внимание, что I2C не передает числа с плавающей запятой или даже целые числа больше 255, есть несколько способов сделать это, и вот хороший учебник. http://www.gammon.com.au/i2c и даже библиотека: http://forum.arduino.cc/index.php?topic=171682.0
Решение, которое я нашел, было проще. В основном мы конвертируем любое значение, строку, число, текст, число с плавающей запятой, вы называете это и превращаете в переменную char, которую можно передать через I2C. После передачи вы можете преобразовать обратно в число, хотя в моем случае ниже я просто хотел отобразить данные из подчиненного устройства.
Вот код. Я даю комментарии по разным частям для ясности. Надеюсь, это поможет. это сработало для меня.
//мастер
#include <Wire.h>
char t[10]={};//пустой массив куда поместить числа приходящие от слейва
volatile int Val; // переменная, используемая мастером для отправки данных слейву
void setup() {
Wire.begin(); // подключение к шине i2c (адрес необязателен для мастера)
Serial.begin(9600); // запускаем сериал для вывода
}
void loop() {
Wire.requestFrom(8, 3); // запрашиваем 3 байта у ведомого устройства №8
// собирает данные, поступающие от ведомого
int i=0; //счетчик для каждого укуса по мере его поступления
while (Wire.available()) {
t[i] = Wire.read(); // каждый пришедший символ упорядочивает в пустом массиве "t"
i=i+1;
}
Serial.println(t); //показывает данные в массиве t
delay(500); // даем немного времени, чтобы расслабиться
// отправляем данные ведомому. здесь я просто посылаю номер 2
Val=2;
Wire.beginTransmission (8);
Wire.write (Val);
Wire.endTransmission ();
}
здесь другая часть //раб
#include <Wire.h>
char t[10]; //пустой массив куда поместить числа идущие к мастеру
volatile int Val; // переменная, используемая мастером для отправки данных слейву
void setup() {
Wire.begin(8); // Идентификатор подчиненного устройства #8
Wire.onRequest(requestEvent); // функция для запуска при запросе данных
Wire.onReceive(receiveEvent); // что делать при получении данных
Serial.begin(9600); // Serial для вывода данных на ваш экран
}
void loop() {
int aRead = analogRead(A0); // подключите потенциометр или резистор к контакту A0, чтобы вы могли видеть, как передаются данные
float x = aRead/1024.0*5.0; // генерируем число с плавающей запятой, с помощью этого метода вы можете использовать любое время данных в значительной степени
dtostrf(x, 3, 2, t); // преобразует число с плавающей запятой или целое число в строку. (floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, пустой массив);
Serial.println(Val); // напечатать символ
delay(500);
}
// функция: что делать при запросе данных
void requestEvent() {
Wire.write(t);
}
// что делать при получении данных от мастера
void receiveEvent(int howMany)
{Val = Wire.read();}
Я считаю, что для вашего сценария, т. е. для отправки различных типов переменных, библиотека EasyTransfer (автор Билл Портер) была бы элегантным решением. Библиотеку можно найти здесь: https://github.com/madsci1016/Arduino-EasyTransfer. Существуют реализации для последовательного интерфейса, I2C и VirtualWire, и приведенные примеры говорят сами за себя.
Обработка очень проста. После включения библиотеки вы создаете объект EasyTransfer, определяете структуру данных, которая в основном представляет собой «контейнер» ваших данных и может состоять из различных переменных.
struct RECEIVE_DATA_STRUCTURE{
//поместите сюда свои определения переменных для данных, которые вы хотите получить
// ЭТО ДОЛЖНО БЫТЬ ТАК ЖЕ НА ДРУГОЙ ARDUINO
int blinks;
int pause;
};
После того, как "контейнер" заполнен, т.е. переменные заданы на стороне отправителя, они отправляются с помощью простой команды
ET.sendData(I2C_SLAVE_ADDRESS);
На стороне получателя вы определяете структуру идентификационных данных и получаете данные с помощью
ET.receiveData();
Надеюсь, это поможет.
Спасибо, эта библиотека многообещающая. Я просмотрел его, но не смог найти учебник для связи через I2C. Учебник, который я нашел, использовал tx/rx. есть ли пример, о котором вы знаете?, @Camilo
Я обнаружил, что простая передача не позволяет выполнять передачу от ведомого к ведущему. ведомый может получать данные от ведущего, но ведущий не может получать данные от ведомого. библиотека потребует некоторых изменений, которые не устарели: http://forum.arduino.cc/index.php?topic=171682.0, @Camilo
На странице github есть папка EasyTransferI2C, которая также включается при загрузке ZIP-файла. В этой папке есть папка «Примеры», в которой вы найдете скетчи для отправителя (который называется EasyTransfer_TX_Example), а другой — для получателя (EasyTransfer_RX_Example)., @hobie
Я попробовал пример, но он отправляет данные только от ведущего к ведомому, он не предлагает возможности отправки от ведомого к ведущему. На форуме arduino выше человек сказал, что он улучшил библиотеку, чтобы иметь эту функцию, но она не отображается в github., @Camilo
Извините, я не могу помочь вам в этом., @hobie
Я использовал его только в одном направлении (главный --> подчиненный). Не уверен, что следующее может работать. Вы пытались определить как на ведущей, так и на ведомой стороне две структуры данных с одинаковым содержимым, одну для получения и одну для отправки, и попытаться отправить их в обоих направлениях?, @hobie
Хм, я не пробовал. Я просматриваю библиотеку, и для меня не было очевидно, что связь ведомого -> ведущего возможна. В итоге я вернулся к проводной библиотеке и передал значения, преобразованные в символы, которые можно легко передать через i2c. Спасибо за помощь., @Camilo
Используйте библиотеку I2C_Anything, доступную здесь. Я использую его во всех своих проектах I2C. Это очень просто и отлично работает. Вот весь файл .h
// Автор: Ник Гэммон
// май 2012 г.
#include <Arduino.h>
#include <Wire.h>
template <typename T> unsigned int I2C_writeAnything (const T& value)
{
Wire.write((byte *) &value, sizeof (value));
return sizeof (value);
} // конец I2C_writeAnything
template <typename T> unsigned int I2C_readAnything(T& value)
{
byte * p = (byte*) &value;
unsigned int i;
for (i = 0; i < sizeof value; i++)
*p++ = Wire.read();
return i;
} // конец I2C_readAnything
- I2C связь между Arduino Uno и Nodemcu32-s (ESP32)
- Соединение i2c для MCP4725 (Dac) с Esp8266 wemos d1 mini
- Протокол I2C не работает должным образом
- Направление по компасу не изменяется линейно
- Float печатается только 2 десятичных знака после запятой
- Как работают функции вне цикла void?
- Как отображать переменные на 0,96-дюймовом OLED-дисплее с библиотекой u8glib?
- Как выбрать альтернативные контакты I2C на ESP32?
Как вы запрашиваете тот же размер (3) строки, что и созданная вами? где нулевой терминатор?, @Curnelious
Более того, ваш dtostrf очень неверен, как общий размер строки равен 3, а у вас есть 2 числа после точки? 1.2 - это 3 символа, как он может иметь 2 символа после точки?, @Curnelious
Это не очень хороший способ, разбивайте значение на байты, делайте его байтовым массивом и возвращайте обратно, например, в число с плавающей запятой. float myFloat = *(float *)&byteArray; Что он делает, берет адрес массива байтов и приводит массив байтов (например, uint8_t byteArray[4];) к указателю с плавающей запятой и получает доступ к его содержимому. Вам действительно не нужен такой дорогой перевод в строку., @Codebeat
Я на 90% уверен, что базовая библиотека поддерживает отправку большего количества типов данных, чем просто байт. Также существует метод, который принимает произвольные данные и «размер», который представляет собой количество байтов, необходимых для отправки этих данных. См. https://www.arduino.cc/en/Reference/WireWrite., @RDragonrydr