Главный считыватель/ведомый передатчик с датчиком расстояния
В настоящее время я работаю над проектом и хочу передать информацию с датчика расстояния от одного Arduino к другому, но то, что я получаю на основной плате, не является правильными измерениями.
Подчиненный код
#include <Wire.h>
const int trig = 12;
const int echo = 13;
int state = 0;
int count = 0;
int contor = 0;
int set_distance = 5;
long duration, Distanceincm;
void setup()
{
Wire.begin(4); // присоединяемся к шине i2c с адресом #4
Wire.onReceive(receiveEvent); // регистрируем событие
pinMode(trig, OUTPUT);
pinMode(echo, INPUT);
Serial.begin(9600); // запускаем сериал для вывода
}
void loop()
{
digitalWrite(trig, HIGH);
delay(15);
digitalWrite(trig, LOW);
duration = pulseIn(echo, HIGH);
Distanceincm = (duration / 58);
if (state == 0 && (Distanceincm < set_distance) ) {
state = 1;
count++;
contor++;
Serial.print("Distance=");
Serial.println(Distanceincm);
Serial.print("Count=");
Serial.println(count);
Serial.print("ConuntDay=");
Serial.println(contor);
}
if (Distanceincm > set_distance) {
state = 0;
}
delay(100);
}
// функция, которая выполняется всякий раз, когда данные получены от мастера
// эта функция регистрируется как событие, см. setup()
void receiveEvent(int howMany)
{
while(1 < Wire.available()) // перебираем все, кроме последнего
{
Wire.write(Distanceincm);
Wire.write(count);
Wire.write(contor);
}
Мастер-код
#include <Wire.h>
void setup()
{
Wire.begin(); // подключение к шине i2c (адрес необязателен для мастера)
Serial.begin(9600); // запускаем Serial для вывода
}
void loop()
{
Wire.requestFrom(2, 6); // запрашиваем 6 байт от ведомого устройства №2
while(Wire.available()) // ведомое устройство может отправить меньше, чем запрошено
{
long a = Wire.read();
Serial.print ("Distance = ");
Serial.println(a);
int b = Wire.read();
Serial.print ("Count = ");
Serial.println(b);
int c = Wire.read();
Serial.print ("CountDay = ");
Serial.println(c);
}
delay(500);
}
На последовательном мониторе я получаю следующее:
@George R., 👍1
Обсуждение1 ответ
В вашем коде есть недостатки, о которых я расскажу здесь:
Основная проблема, которую вы видите здесь, заключается в том, что вы позволяете подчиненному устройству записывать данные в буфер только тогда, когда оно получает данные. Но вы никогда не отправляете на него данные. Так что в буфере никогда ничего нет.
В подчиненном коде, который у вас есть
void receiveEvent(int howMany) { while(1 < Wire.available()) // перебираем все, кроме последнего { Wire.write(Distanceincm); Wire.write(count); Wire.write(contor); }
Помимо несбалансированных скобок (которые наводят меня на мысль, что это может быть не настоящий код, который вы сейчас используете), вы записываете в буфер только внутри
receiveEvent()
. Это вызывается только тогда, когда мастер выполняет передачу Master-Write (черезWire.beginTransmission()
иWire.endTransmission()
). В основном коде вы используете толькоWire.requestFrom()
. Если вы хотите отправить данные по запросу, вам нужно использовать обратный вызовonRequest
библиотекиWire
. Вам необходимо установить соответствующую функцию обратного вызова:Wire.onRequest(onRequestEvent);
А затем запишите данные внутри этой функции обратного вызова:
void onRequestEvent(){ Wire.write(...) }
Я объясню, как правильно записывать значения, в другом месте ниже.
В функции
receiveEvent()
вы хотите перебрать все полученные байты, кроме последнего. Но вы ничего не делаете с данными. Ты даже не читаешь это. В текущем состоянии вашего кода эта строка не имеет никакого смысла.В своем мастер-коде вы запрашиваете данные у ведомого устройства с адресом
2
. Ваш подчиненный код инициирует библиотекуWire
с адресом4
. С этого момента не должно быть успешной передачи I2C, если только у вас нет другого подчиненного устройства на шине с адресом2
(еще один момент, где я подозреваю, что эти коды не совсем те, которые используются для тест).В мастер-коде, который вы используете
while(Wire.available())
потому что ведомое устройство могло отправить меньше данных, чем было запрошено. Вы проверяете, что количество доступных байтов не равно нулю. Но тогда вы читаете 3 байта без повторной проверки, а значит, получив 2 байта, вы все равно попытаетесь прочитать 3 байта, что не может дать хороших результатов.
Условие должно быть
Wire.available() >= 3
, чтобы проверить, есть ли хотя бы 3 байта в буфере, потому что вы читаете из него 3 байта. Но и петля не имеет для меня особого смысла. Поскольку у вас здесь двоичный протокол, как бы вы обработали дополнительные данные (имеется в виду избыточные данные, отправленные после первого действительного кадра данных)? Вы хотите получать 3 значения за передачу и считываете 3 значения за одну итерацию цикла. Если бы в буфере было больше данных, вы бы читали следующие 3 байта, отбрасывая первые 3 байта. Я не думаю, что это то, что вам нужно.Вместо этого используйте возвращаемое значение
Wire.requestFrom()
, которое представляет собой количество полученных байтов. Используйте его, чтобы отличать действительные передачи от слишком коротких:uint8_t n = Wire.requestFrom(4, 3); if(n >= 3){ // как минимум 3 байта, где получено uint8_t value1 = Wire.read(); uint8_t value2 = Wire.read(); uint8_t value3 = Wire.read(); }
Любая слишком маленькая передача будет проигнорирована. Лишние данные в слишком длинных сообщениях также будут игнорироваться. Обратите внимание, что я использовал
uint8_t
, который представляет собой целое число без знака с 8 битами (1 байт) здесь для простоты логики этих точек. Подробнее о типах данных и их влиянии на код ниже.Для отправки типов данных на подчиненное устройство у вас есть
int
иlong
.int
— целое число со знаком из 16 бит,long
— длинное целое со знаком из 32 бит. (Я подозреваю, что вам не нужендлинный
для расстояния, так как ультразвуковые датчики в основном достигают только 3 м, но это на ваше усмотрение). Напротив, аргументWire.write()
представляет собойuint8_t
, который имеет только 8 бит. Этот вызов используется для помещения в буфер ровно 1 байта, не больше. Когда вы используетеWire.write(Distanceincm);
вы помещаете в буфер младший байт
Distanceincm
. Остальные будут просто выброшены (это происходит при приведении отlong
кuint8_t
). Это работает до тех пор, пока значение находится в диапазоне от 0 до 127 (что является верхней частью диапазона значенийint8_t
). С более высокими (или более низкими) значениями ваши полученные данные будут мусором. Вместо этого вам нужно записать каждый байт ваших данных в буфер:Wire.write(Distanceincm & 0x000000FF); Wire.write((Distanceincm & 0x0000FF00) >> 8); Wire.write((Distanceincm & 0x00FF0000) >> 16); Wire.write((Distanceincm & 0xFF000000) >> 24);
С помощью побитового И (
&
) шестнадцатеричного значения0x000000FF
мы удаляем все биты, кроме младшего значащего байта (аналогично0x0000FF00
). code> для второго младшего значащего байта и так далее). Затем мы сдвигаем соответствующий байт в положение младшего значащего байта (8 бит для второго младшего байта, 16 бит для третьего младшего байта и т. д.), который затем используетсяWire.write. ()
. Чтобы лучше понять это, почитайте о побитовых операторах в Интернете.Вы должны отправить оба целых числа одинаковым образом (хотя они оба имеют только 2 байта вместо 4).
Теперь, когда мы отправляем больше байтов, нам также необходимо правильно получать и читать все эти байты. Сначала нам нужно запросить правильное количество байтов. 4 байта для значения
long
и 2 байта для обоих значенийint
--> 8 байт. Итак, вам нужно запросить 8 байтов:Wire.requestFrom(4, 8);
Затем вам нужно прочитать эти байты в соответствующие переменные:
long distance = Wire.read() | (Wire.read() << 8) | (Wire.read() << 16) | (Wire.read() << 24);
Мы используем побитовое ИЛИ (
|
), чтобы соединить байты вместе, сдвигая каждый байт в правильное положение в значенииlong
. Аналогичным образом вы объединяете два значенияint
.
- Лазерный датчик ToF (VL53L0X) не работает
- Реализовать связь Visible Light с помощью Arduino
- Могу ли я соединить вместе несколько плат Arduino Nano ?
- Параллельная связь между несколькими Ардуино
- Multiple Wire.write() не работает для Arduino Nano I2C
- Как использовать I2C от Arduino Uno, когда у меня установлен GSM-shield?
- Отправка 4 аналоговых значений от одного arduino к другому с помощью I2C
- Увеличение скорости датчиков Vl6180x
Ваше событие приема должно считывать некоторые данные. Если вы не читаете никаких данных, тогда цикл while становится бесконечным., @Delta_G
Вы также должны отметить, что wire.read дает вам byte, а не long или int., @Delta_G
Что я вижу сразу, так это то, что вы назначаете адрес 4 слейву, но мастер читает с адреса 2., @Sim Son
У вас есть данные, поступающие по I2C. По сравнению со скоростью процессора эти данные поступают очень медленно. Когда поступает первый байт данных, wire. available() становится равным 1. Теперь ваш код входит в этот цикл. Он пытается прочитать кучу данных из проводной сети, но пока показывает только 1 байт. Когда вы вызываете read и ничего не доступно, он возвращает -1, который (в виде байта) выглядит как 255, когда он помещается в байт. Бьюсь об заклад, что-то подобное происходит, но у меня нет времени смотреть на Wire и смотреть, как он возвращается., @Delta_G
@Delta_G Wire.requestFrom() — это блокирующий вызов. Когда он выходит, все сообщение I2C было получено. Я думаю, он также возвращает количество полученных байтов., @chrisl
хорошо, тогда. Я не мог открыть библиотеку с того места, где находился, и я знал, что это делают другие вещи, наследующие Stream., @Delta_G