Проблемы с модулем Bluetooth(HC 05)

Я пытаюсь создать управляемого по Bluetooth робота 4WD. я следовал учебнику который нашел в Интернете (https://howtomechatronics.com/tutorials/arduino/arduino-robot-car-wireless-control-using-hc-05-bluetooth-nrf24l01-and-hc-12-transceiver-modules/). После успешного подключения моего модуля Bluetooth 2 HC 05 я использовал скрипт из учебника и немного изменил его, чтобы он работал для моей настройки. Код для робота:

//Code for Slave
#define enA 9
#define in1 8
#define in2 7
#define enB 10
#define in3 6
#define in4 5
int xAxis, yAxis;
unsigned int  x = 0;
unsigned int  y = 0;
int motorSpeedA = 0;
int motorSpeedB = 0;
void setup() {
  pinMode(enA, OUTPUT);
  pinMode(enB, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  Serial.begin(9600); // Скорость связи по умолчанию модуля Bluetooth
}
void loop() {
  // Значение по умолчанию - нет движения, когда джойстик остается в центре
  x = 510 / 4;
  y = 510 / 4;
  // Считывание входящих данных с джойстика или главного устройства Bluetooth
  while (Serial.available() >= 2) {
    x = Serial.read();
    Serial.print("X: ");
    Serial.println(x);
    delay(10);
    y = Serial.read();
    Serial.print("Y: ");
    Serial.println(y);
  }
  delay(10);
  // Преобразуйте обратно диапазон 0 - 255 в 0 - 1023, соответствующий приведенному ниже коду управления двигателем
  xAxis = x*4;
  yAxis = y*4;
  // Ось Y используется для прямого и обратного управления
  if (yAxis < 470) {
    backMov;
    // Преобразуйте уменьшающиеся показания оси Y для перехода назад от 470 до 0 в значение от 0 до 255 для ШИМ-сигнала для увеличения частоты вращения двигателя
    motorSpeedA = map(yAxis, 470, 0, 0, 255);
    motorSpeedB = map(yAxis, 470, 0, 0, 255);
  }
  else if (yAxis > 550) {
    fowardMov;
    // Преобразуйте увеличивающиеся показания оси Y для перехода вперед от 550 до 1023 в значение от 0 до 255 для ШИМ-сигнала для увеличения скорости двигателя
    motorSpeedA = map(yAxis, 550, 1023, 0, 255);
    motorSpeedB = map(yAxis, 550, 1023, 0, 255);
  }
  // Если джойстик остается посередине, двигатели не двигаются
  else {
    motorSpeedA = 0;
    motorSpeedB = 0;
  }
  // Ось X используется для левого и правого управления
  if (xAxis < 470) {
    // Преобразуйте уменьшающиеся показания оси X от 470 до 0 в увеличивающееся значение от 0 до 255
    int xMapped = map(xAxis, 470, 0, 0, 255);
    // Переместить влево - уменьшить скорость левого двигателя, увеличить скорость правого двигателя
    motorSpeedA = motorSpeedA + xMapped;
    motorSpeedB = motorSpeedB - xMapped;
    // Ограничьте диапазон от 0 до 255
    if (motorSpeedA > 255) {
      motorSpeedA = 255;
    }
    if (motorSpeedB < 0) {
      motorSpeedB = 0;
    }
  }
  if (xAxis > 550) {
    // Преобразуйте увеличивающиеся показания оси X от 550 до 1023 в значение от 0 до 255
    int xMapped = map(xAxis, 550, 1023, 0, 255);
    // Move right - уменьшить скорость правого двигателя, увеличить скорость левого двигателя
    motorSpeedA = motorSpeedA - xMapped;
    motorSpeedB = motorSpeedB + xMapped;
    // Ограничьте диапазон от 0 до 255
    if (motorSpeedA < 0) {
      motorSpeedA = 0;
    }
    if (motorSpeedB > 255) {
      motorSpeedB = 255;
    }
  }
  // Предотвратите жужжание на низких скоростях (Отрегулируйте в соответствии с вашими двигателями. Мои двигатели не могли начать движение, если значение PWM было ниже значения 70)
  if (motorSpeedA < 70) {
    motorSpeedA = 0;
  }
  if (motorSpeedB < 70) {
    motorSpeedB = 0;
  }
  analogWrite(enA, motorSpeedA); // Отправить ШИМ-сигнал на двигатель A
  analogWrite(enB, motorSpeedB); // Отправить ШИМ-сигнал на двигатель B
}
void fowardMov(){
  digitalWrite(in2,HIGH);
  digitalWrite(in4,HIGH);
  digitalWrite(in1, LOW);
  digitalWrite(in3, LOW);
}

void backMov(){
  digitalWrite(in1,HIGH);
  digitalWrite(in3,HIGH);
  digitalWrite(in2, LOW);
  digitalWrite(in4, LOW);
}

Код для контроллера:

#define Xaxis A0
#define Yaxis A1

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
 Serial.write(analogRead(Xaxis)/4);
 Serial.write(analogRead(Yaxis)/4);
 delay(20);
}

После загрузки кода я попытался переместить его, но он не работал, поэтому я поставил операторы печати, чтобы увидеть значение X и Y. изначально он не работал, поэтому я поменял местами модули Bluetooth, которые у меня были, и снова подключил их. я также изменил эту строку.

 while (Serial.available() >= 2)

К этому

 if (Serial.available() > 0)

я начал получать значения от ведущего устройства к ведомому, но я заметил, что значения были одинаковы для x и y, поэтому я искал другой учебник в Интернете и нашел это (https://create.arduino.cc/projecthub/andriy-baranov/arduino-4wd-rc-car-639953?ref=similar&ref_id=18369&offset=3) читая его код, я задумался, как работает раб. Согласно официальной документации Arduino Serial.read() не принимает никаких аргументов, но я все равно попробовал, и это не сработало. Я решил повторить код из первого учебника, который проверил, на этот раз он выглядел так, как будто работал, поэтому я попробовал переместить джойстик, и последовательный монитор сначала напечатал правильные значения для обоих, а затем поменял их местами, а затем завис. Как я могу остановить его от замораживания и замены значений? Кроме того, почему цикл while не работает вечно, разве объем данных в последовательном буфере всегда не превышает 2? Спасибо.diagram of slave

diagram of master

, 👍2


2 ответа


0

Ваша настройка слишком сложна! Упростите свою настройку до такой степени, чтобы она касалась только той части, которая вызывает у вас проблемы; в вашем случае связь через последовательный порт между ведущим и ведомым. Решите ТОЛЬКО ЭТУ проблему, а затем двигайтесь дальше

,

Я упростил свою настройку, удалив все, кроме главного и подчиненного устройств Bluetooth . я сделал простой скетч для отправки данных от ведущего устройства к ведомому. он по-прежнему делает то же самое. он зависает, а затем через некоторое время выводит все данные, отправленные основным устройством. я заметил, что когда я увеличиваю задержку для мастера до 1 секунды, это работает намного лучше, но не приведет ли задержка в 1 секунду к заметной задержке во времени при управлении роботом? Спасибо, @Ayanfe Adekanye

Рад слышать, что вы упростили ситуацию и начинаете получать некоторые обнадеживающие результаты. Если увеличение задержки мастера до 1 секунды работает намного лучше, то вы, возможно, на что-то наткнулись. Попробуйте увеличить задержку еще больше (2 секунды, 10 секунд) и посмотрите, позволит ли это ответу ведущего/ведомого устройства больше походить на "в реальном времени". Если это так, вы можете обнаружить, что скорость передачи данных ведущий/ведомый слишком низкая, и для передачи всех байтов по каналу требуется задержка в 1 секунду, @user3765883

В любом случае, не торопитесь, идите очень медленно, записывайте все и МЕНЯЙТЕ ТОЛЬКО ОДНУ ВЕЩЬ ЗА РАЗ. В какой-то момент вы посмотрите на свои результаты и свои заметки и скажете: "Конечно, почему я не подумал об этом раньше"; -)., @user3765883

Я сделал то, что ты сказал, и кое-что заметил. я запустил задержку мастеров с 2 секунд и начал постепенно уменьшать ее вдвое. На 125 миллисекундах он все еще получал последовательные данные нормально, но иногда с небольшим заметным отставанием. однако отставание длится не более секунды. Я немного изменю код для робота, чтобы ему не нужно было отправлять так много данных. в первом уроке скорость передачи данных, которую он использует, составляет 38400, но я использую 9600. Как вы думаете, это может быть решением моей проблемы? Спасибо, @Ayanfe Adekanye

Старая скорость передачи данных 9600 является древней. Попробуйте 115200 или 230400, @user3765883

Я попытался изменить скорость передачи данных, но это ничего не улучшило, @Ayanfe Adekanye

Если у вас есть "замораживание", на которое не влияет скорость передачи данных, то, возможно, ваш код настраивает, а затем каждый раз демонтирует соединение по циклу, вместо того, чтобы настраивать его один раз, а затем сохранять его открытым в течение всего срока службы программы. Должна быть причина, и тщательное экспериментирование и запись результатов ее выявят., @user3765883

Как вы думаете, проблема у меня с оборудованием? потому что я максимально упростил свой код, отправив номера 1-255 от мастера с циклом for, а затем получив и распечатав полученные данные на последовательном мониторе, но он все равно зависает. используя скорость передачи 23400 бод, я получил только то, что выглядело как случайные числа. я заметил, что если полностью удалить задержку с мастера, она не зависает, хотя некоторые данные теряются., @Ayanfe Adekanye

в общем, аппаратное обеспечение почти никогда не является проблемой, @user3765883


1

Существует проблема с тем, как передаются и принимаются данные, поскольку предполагается, что передача будет полностью синхронизирована в течение всего срока действия программы. Подумайте, что произойдет, если один байт будет удален из – за ошибки связи-данные x и y будут заменены для дальнейшей передачи.

Первоначальный Отправитель

void loop() {
  // put your main code here, to run repeatedly:
 Serial.write(analogRead(Xaxis)/4);
 Serial.write(analogRead(Yaxis)/4);
 delay(20);
}

Оригинальный Приемник

void loop() {
  // Значение по умолчанию - нет движения, когда джойстик остается в центре
  x = 510 / 4;
  y = 510 / 4;
  // Считывание входящих данных с джойстика или главного устройства Bluetooth
  while (Serial.available() >= 2) {
    x = Serial.read();
    Serial.print("X: ");
    Serial.println(x);
    delay(10);
    y = Serial.read();
    Serial.print("Y: ");
    Serial.println(y);
  }
  delay(10);
  . . .
}

Следовательно, должен быть способ синхронизации с первым байтом сообщения. Это может быть достигнуто с помощью пакетов сообщений, в которых начало и конец сообщения разделены уникальными символами, например { и}, аналогично формату JSON.

В качестве другого примера, стандарт GPS NMEA использует $ и \n для разделения сообщений и для разделения полей. В конце также есть контрольная сумма.

Вот неблокирующий алгоритм, который основан на примере readline() Майенко и примере мигания без задержки для чтения пакета. Функция ReadPacket() возвращает значение true, когда входящие данные были собраны в допустимый пакет. Светодиод мигает, указывая на то, что микроконтроллер не замерз.

Новый Отправитель

void loop()
{
    Serial.print("{");                  // Разделитель первого символа.
    Serial.print(analogRead(Xaxis));    // Полное разрешение. Не нужно делить на 4.
    Serial.print(",");                  // Разделитель полей.
    Serial.print(analogRead(Yaxis));    // Полное разрешение. Не нужно делить на 4.
    Serial.print("}");                  // Разделитель последних символов.
    delay(20);
}

Новый Приемник

void loop()
{
    //
    // ЗАДАЧА 1: Мигайте без задержки, чтобы указать, что микроконтроллер не завис.
    //
    const unsigned int INTERVAL = 250;
    unsigned long current_timestamp = millis();
    static unsigned long previous_timestamp = current_timestamp;
    static bool led_state = false;
    if (current_timestamp - previous_timestamp >= INTERVAL)
    {
        led_state = !led_state;
        digitalWrite(LED_BUILTIN, led_state);
        previous_timestamp += INTERVAL;
    }

    //
    // ЗАДАЧА 2: Чтение и анализ пакета данных.
    //
    static char packet[100];
    const byte NUM_FIELDS = 2;
    unsigned int fields[NUM_FIELDS];
    static unsigned int x = 512;   // Полное разрешение. Не нужно делить на 4.
    static unsigned int y = 512;   // Полное разрешение. Не нужно делить на 4.
    bool new_x = false;
    bool new_y = false;
    if (ReadPacket(Serial, packet, sizeof(packet)))
    {
        Serial.print(F("Received packet >"));
        Serial.print(packet);
        Serial.println(F("<"));

        // Проанализируйте пакет для значений x и y в полном разрешении.
        const char delimiters[] = "{,}";
        char* field;
        int i = 0;
        field = strtok(packet, delimiters);
        while (field != NULL && i < NUM_FIELDS)
        {
            Serial.print("field = ");
            Serial.println(field);
            fields[i] = atoi(field);
            Serial.print("fields[");
            Serial.print(i);
            Serial.print("] = ");
            Serial.println(fields[i]);
            field = strtok(NULL, delimiters);
            i++;
        }
        if (i == NUM_FIELDS)
        {
            if (x != fields[0])
            {
                new_x = true;
                x = fields[0];  // Полное разрешение. Нет необходимости умножать на 4.
                Serial.print("New x = ");
                Serial.println(x);
            }
            if (y != fields[1])
            {
                new_y = true;
                y = fields[1];  // Полное разрешение. Нет необходимости умножать на 4.
                Serial.print("New y = ");
                Serial.println(y);
            }
        }
    }

    Serial.print("x = ");
    Serial.println(x);
    Serial.print("y = ");
    Serial.println(y);

    //
    // ЗАДАЧА 3: Обработайте значения x и y.
    //
    if (new_x || new_y)
    {
        Serial.println("Processing new values.");
        
        . . .
    }
}

bool ReadPacket(Stream& stream, char *const packet, const unsigned int SIZE)
{
    const char FIRST = '{';
    const char LAST = '}';
    static bool read_until_last = false;
    static unsigned int i = 0;
    char ch;

    if (stream.available())
    {
        ch = stream.read();

        if (ch == FIRST)
        {
            Serial.println(F("Received FIRST."));
            i = 0;
            packet[0] = ch;
            read_until_last = true;
            return false;
        }
        if (read_until_last)
        {
            i++;
            if (i > SIZE - 2)
            {
                Serial.println(F("Buffer overrun. Resetting to look for next packet."));
                i = 0;
                read_until_last = false;
                return false;
            }
            if (ch == LAST)
            {
                Serial.println(F("Received LAST."));
                packet[i++] = ch;
                packet[i] = 0;
                read_until_last = false;
                return true;
            }
            else
            {
                Serial.print(F("Received char >"));
                Serial.print(ch);
                Serial.println("<");
                packet[i] = ch;
                return false;
            }
        }
    }
    return false;
}
,