Последовательная связь с HC-05 - Arduino UNO

Я новичок на сайте, и это мой первый вопрос. Я постараюсь описать свою проблему как можно лучше.

Я получил следующий код для своего проекта, который в основном состоит в том, чтобы заставить автомобиль иметь 2 режима вождения, автоматический и ручной.

Насколько я могу судить, у меня все работает, за исключением того, что это не так. Я получил HC-05, подключенный к моему двигателю L293D (подключенному к Arduino UNO), а также 2 двигателя постоянного тока, ультразвуковой датчик HC-SR04 и светодиод. Часть схемы не является проблемой, мне удалось подключить все это, и оно работает (я протестировал часть, избегающую препятствий, сам по себе, прежде чем добавить код выбора режима, поэтому я знаю, что двигатели и датчик выполняют свою работу). Я также должен упомянуть, что я отправляю информацию через BT из приложения, которое я создал в APP Inventor. Приложение работает нормально, и я действительно получаю правильные данные на последовательном мониторе.

Что меня озадачивает, так это то, почему я не могу присвоить значение Serial.read(); моей переменной ответа (соотв.). Я попытаюсь объяснить это немного подробнее. В моем последовательном мониторе я получаю нужные символы с кнопок приложения, однако моя переменная “resp”, похоже, не получает никакого значения от Serial.read(); по какой-то причине, потому что, когда она переходит к оператору switch, даже если символ напечатан на мониторе, например, дела не начинаются, и я могу полностью отключиться, как только я получу " 1 " на мониторе, я также должен начать печатать измерение расстояния на мониторе, а это не так.

Насколько я понимаю, я полагаю, что проблема заключается между Serial.write() и Serial.println()/.read(); поскольку serial.write (), похоже, имеет приоритет над всеми остальными, поскольку установка этих строк в качестве комментариев не влияет на запись на мониторе:

if(Serial.available()>0){
//resp = Serial.read();
//Serial.println(resp);

У меня нет идей, так что любая помощь или руководство в правильном направлении будут высоко оценены.

//Incluir librerias para controlar motor shield y sensor ultrasonico.
#include <AFMotor.h>
#include <NewPing.h>
#include <SoftwareSerial.h>

//Definir constantes y variables.
#define TRIG_P A4 
#define ECHO_P A5
#define MAX_DIST 300
#define MAX_Vel 200
int led = A0;
int speedSet;
int iteraciones = 5;
float distancia, duracion;
char resp;

//Inicializar objetos del sensor y motores.
NewPing sonar(TRIG_P, ECHO_P, MAX_DIST); 
AF_DCMotor motorD(2); 
AF_DCMotor motorI(1);
SoftwareSerial BTSerial(A1, A2); // RX | TX

void setup() {
  Serial.begin(9600);
  pinMode(led, OUTPUT);
  BTSerial.begin(9600);
}

void loop() {

  //Lee información del BT y la escribe en el monitor serial.
  if (BTSerial.available()){
    Serial.write(BTSerial.read());
    Serial.write('\n');
  }  

  delay(24); //Tiempo maximo que tarda en recibir una medición.

      /*Calcular la distancia frente al sensor utilizando la duración de la señal, 
      la cual mide ida y vuelva por lo cual se divide entre 2 
      y se multiplica por la velocidad del sonido (343 m/s).
      */

      duracion = sonar.ping_median(iteraciones);
      distancia = (duracion/2)*0.0343;

  if(Serial.available()>0){  
    resp = Serial.read();
    Serial.println(resp);

    switch(resp){

      case '1':

      delay(24); //Tiempo maximo que tarda en recibir una medición.

      /*Calcular la distancia frente al sensor utilizando la duración de la señal, 
      la cual mide ida y vuelva por lo cual se divide entre 2 
      y se multiplica por la velocidad del sonido (343 m/s).
      */

      duracion = sonar.ping_median(iteraciones);
      distancia = (duracion/2)*0.0343;

      Serial.print("Distancia: ");
      Serial.print(distancia);
      Serial.println(" cm"); 

      if(distancia!=0 && distancia<=20){
        digitalWrite(led,HIGH);
        ALTO();
        delay(100);
        REVERSA();
        delay(400);
        IZQUIERDA();
        delay(100);
      }else 
        digitalWrite(led,LOW);
        ADELANTE();
      break;

      case '2':
      ALTO();

        switch(resp){

          case 'A':
            ADELANTE();
          break;
          case 'R':
            REVERSA();
          break;
          case 'I':
            IZQUIERDA();
          break;
          case 'D':
            DERECHA();
          break;
          case 'O':
            ALTO();
          break;
        }
        break;
       }       
    }
 }


  // Inician metodos que definen las acciones del carro.

void ADELANTE(){
   motorD.run(FORWARD);      
   motorI.run(FORWARD);
    for (speedSet = 0; speedSet < MAX_Vel; speedSet +=2){
      motorD.setSpeed(speedSet + 20);
      motorI.setSpeed(speedSet - 20);
    }
  }

void REVERSA(){
   motorD.run(BACKWARD);      
   motorI.run(BACKWARD);  
    for (speedSet = 0; speedSet < MAX_Vel; speedSet +=2){
      motorD.setSpeed(speedSet + 20);
      motorI.setSpeed(speedSet - 20);
    }
  }  

void DERECHA() {
   motorD.run(RELEASE);
   motorI.run(FORWARD);     
   delay(300);
   motorD.run(FORWARD);      
   motorI.run(FORWARD);      
  } 

void IZQUIERDA() {
   motorD.run(FORWARD);     
   motorI.run(RELEASE);     
   delay(300);
   motorD.run(FORWARD);     
   motorI.run(FORWARD);
  }  

void ALTO(){
   motorD.run(RELEASE); 
   motorI.run(RELEASE);
  } 


Вот изображение того, что я получаю на своем последовательном мониторе. Serial Monitor with BT readings.

ОБНОВЛЕНИЕ:

Как предложил @chrisl, теперь я смотрю на это с другой точки зрения.

Я отправлял информацию BT на последовательный монитор, когда идея, в первую очередь, заключалась в том, чтобы автомобиль ездил без подключения к компьютеру вообще.

Поэтому вместо того, чтобы отправлять информацию на последовательный монитор и пытаться извлечь ее оттуда, я просто перенес значение из BTSerial в свою переменную, и теперь это работает так, как задумывалось.

"Автоматический режим" работает, моя инструкция switch распознает значение resp и начинает измерять и печатать расстояние, однако, как только я нажимаю "2", активируется первая команда и двигатели останавливаются, но все остальные инструкции не работают. Я даже изменил свой второй статус переключения на "если", но это тоже не помогло.

Вот мой новый последовательный вывод (9600 / Новая строка / COM5):

1
Distancia: 46.84 cm
Distancia: 154.74 cm
Distancia: 154.33 cm
Distancia: 154.68 cm
Distancia: 154.33 cm
Distancia: 155.70 cm
Distancia: 13.09 cm
Distancia: 12.74 cm
Distancia: 155.16 cm
Distancia: 155.77 cm
Distancia: 155.22 cm
Distancia: 154.81 cm
Distancia: 155.29 cm
Distancia: 155.22 cm
Distancia: 155.22 cm
2
A
R
I
@
D
O
1
Distancia: 155.43 cm
Distancia: 155.50 cm
Distancia: 155.57 cm
Distancia: 155.50 cm
Distancia: 155.09 cm
Distancia: 155.50 cm
Distancia: 155.09 cm
Distancia: 156.32 cm
Distancia: 155.57 cm
Distancia: 155.16 cm
Distancia: 155.09 cm
Distancia: 155.09 cm
Distancia: 155.50 cm
2

И мой обновленный код:

//Incluir librerias para controlar motor shield y sensor ultrasonico.
#include <AFMotor.h>
#include <NewPing.h>
#include <SoftwareSerial.h>

//Definir constantes y variables.
#define TRIG_P A4 
#define ECHO_P A5
#define MAX_DIST 300
#define MAX_Vel 200
int led = A0;
int speedSet;
int iteraciones = 5;
float distancia, duracion;
char resp;

//Inicializar objetos del sensor y motores.
NewPing sonar(TRIG_P, ECHO_P, MAX_DIST); 
AF_DCMotor motorD(2); 
AF_DCMotor motorI(1);
SoftwareSerial BTSerial(A1, A2); // RX | TX

void setup() {
  Serial.begin(9600);
  pinMode(led, OUTPUT);
  BTSerial.begin(9600);
}

void loop() {

  //Lee información del BT y la escribe en el monitor serial.
  if (BTSerial.available()){
    resp = BTSerial.read();
    Serial.println(resp);
  }  

//  if(Serial.available()>0){      
//    resp = Serial.read();
//    Serial.println(resp);

    switch(resp){

      case '1':

      delay(24); //Tiempo maximo que tarda en recibir una medición.

      /*Calcular la distancia frente al sensor utilizando la duración de la señal, 
      la cual mide ida y vuelva por lo cual se divide entre 2 
      y se multiplica por la velocidad del sonido (343 m/s).
      */

      duracion = sonar.ping_median(iteraciones);
      distancia = (duracion/2)*0.0343;

      Serial.print("Distancia: ");
      Serial.print(distancia);
      Serial.println(" cm"); 

      if(distancia!=0 && distancia<=20){
        digitalWrite(led,HIGH);
        ALTO();
        delay(100);
        REVERSA();
        delay(400);
        IZQUIERDA();
        delay(100);
      }else 
        digitalWrite(led,LOW);
        ADELANTE();
      break;

      case '2':
      ALTO();

       if(resp == 'A'){
         ADELANTE();
       } else if (resp == 'R'){
         REVERSA();
       } else if (resp == 'I'){
         IZQUIERDA();
       } else if (resp == 'D'){
         DERECHA();
       } else if (resp == 'O'){
         ALTO();
       }
      break;
       }       
    }
// }


  // Inician metodos que definen las acciones del carro.

void ADELANTE(){
   motorD.run(FORWARD);      
   motorI.run(FORWARD);
    for (speedSet = 0; speedSet < MAX_Vel; speedSet +=2){
      motorD.setSpeed(speedSet + 20);
      motorI.setSpeed(speedSet - 20);
    }
  }

void REVERSA(){
   motorD.run(BACKWARD);      
   motorI.run(BACKWARD);  
    for (speedSet = 0; speedSet < MAX_Vel; speedSet +=2){
      motorD.setSpeed(speedSet + 20);
      motorI.setSpeed(speedSet - 20);
    }
  }  

void DERECHA() {
   motorD.run(RELEASE);
   motorI.run(FORWARD);     
   delay(300);
   motorD.run(FORWARD);      
   motorI.run(FORWARD);      
  } 

void IZQUIERDA() {
   motorD.run(FORWARD);     
   motorI.run(RELEASE);     
   delay(300);
   motorD.run(FORWARD);     
   motorI.run(FORWARD);
  }  

void ALTO(){
   motorD.run(RELEASE); 
   motorI.run(RELEASE);
  } 

, 👍0


1 ответ


1

Я предполагаю, что основная проблема в том, что вы пишете свои команды из приложения Bluetooth, но модуль HC-05 подключен к BTSerial. Здесь вы считываете с него и пересылаете данные на последовательный (последовательный монитор на вашем ПК):

if (BTSerial.available()){
    Serial.write(BTSerial.read());
    Serial.write('\n');
}

Вы больше ничего не делаете с последовательными данными bluetooth, поэтому вы не можете изменить переменную resp по Bluetooth. Вместо этого вы делаете это с помощью Serial (не BTSerial). Но до тех пор, пока вы не введете команды в последовательный монитор, этот код никогда не будет запущен. Таким образом, код расстояния никогда не будет выполнен.

Если вы хотите управлять программой из приложения bluetoooth, вам нужно фактически работать с данными из правильного последовательного интерфейса.


Кстати: Ваш последовательный монитор настроен для новой строки в качестве символа окончания строки. Ваш серийный код не отбрасывает их, поэтому вы можете получить нежелательное поведение там, когда resp станет \n.


О вашей новой проблеме: Эта проблема действительно существовала и со старым кодом, но вы не могли ее увидеть, потому что вы читали с неправильного последовательного интерфейса. Попробуйте прочитать этот (сжатый) фрагмент вашего (старого) кода:

switch(resp){
   case '1':
       ...
       break;
   case '2':
       switch(resp){
           case 'A':
               ...

С помощью первого оператора switch вы проверяете значение resp. Если это "2", вы войдете во второй случай. Там вы снова проверяете значение resp для значений "A",.... Но когда вы уже знаете, что значение resp равно "2", как оно может быть равно " A " или одной из других подкоманд, поскольку вы не меняете соотношение между ними. Таким образом, код внутри второго оператора switch недоступен.

Что теперь делать? Я предполагаю, что вы хотите, чтобы первый оператор switch обрабатывал режимы, а второй-команды перемещения, находясь во втором режиме. Для этого вам нужно перестроить свой код, в лучшем случае на FSM (конечный автомат). Я уже однажды написал хорошее описание того, как работает FSM, в своем ответе на этот вопрос.

Введите переменную состояния, которая используется в первом операторе switch, и переменную команды, которая используется во втором. Я бы предложил такую структуру:

enum State {AUTOMATIC_MODE, MANUAL_MODE};

State state = AUTOMATIC_MODE;
char command = 0;

void loop(){
    if(BTSerial.available()){
        resp = BTSerial.read();
        Serial.println(resp);
        switch(resp){
            case '1':
                state = AUTOMATIC_MODE;
                break;
            case '2':
                state = MANUAL_MODE;
                break;
            case 'A':
            case 'R':
            case 'I':
            case 'D':
            case 'O':
                command = resp;
                break;
        }
    }

    switch(state){
        case AUTOMATIC_MODE:
            ...
            break;
        case MANUAL_MODE:
            switch(command){
                case 'A':
                    ...
            }
            break;
    }
}

Здесь последовательный ввод будет введен в переменную состояния или команды, в зависимости от того, что именно было отправлено. После этого следует часть с основным кодом. В зависимости от состояния выполняется соответствующий код. В ручном режиме код будет просматривать переменную команды, чтобы проверить, какую функцию следует выполнить.

,

Я понимаю, что, возможно, тогда я все неправильно понял, потому что намерение состоит в том, чтобы машина работала без подключения к ПК и, следовательно, не была подключена к последовательному монитору в первую очередь. Итак, должен ли я просто записать полученную информацию в свою переменную resp, не записывая ее на последовательный монитор вообще?, @Genaro Basoria

При последовательном (UART) запись и чтение независимы. Вы можете увидеть данные Bluetooth на последовательном мониторе, если запишете их в "Serial", но это не значит, что вы можете прочитать их оттуда. Вы можете читать только из Последовательного, что активно посылается последовательным монитором. Вы читаете важные командные данные из BTSerial, поэтому вы должны использовать эти данные для назначения resp. Вы все еще можете дополнительно отправить данные на последовательный монитор для отладки., @chrisl

@GenaroBasoria Я добавил часть к своему ответу о проблеме с ручным режимом, @chrisl