Главный считыватель/ведомый передатчик с датчиком расстояния

В настоящее время я работаю над проектом и хочу передать информацию с датчика расстояния от одного 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);
}

На последовательном мониторе я получаю следующее:

, 👍1

Обсуждение

Ваше событие приема должно считывать некоторые данные. Если вы не читаете никаких данных, тогда цикл 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


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.

,