Отправка и получение различных типов данных через 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();}

, 👍7


3 ответа


Лучший ответ:

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();}
,

Как вы запрашиваете тот же размер (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


0

Я считаю, что для вашего сценария, т. е. для отправки различных типов переменных, библиотека 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


0

Используйте библиотеку 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
,