0

I am new to the site and this is my first question. I'm going to try to describe my issue the best I can.

I got the following code for my project which consists basically of getting a car to have 2 driving mode, automatic and manual.

As far as I can tell, I got it all working except it's not. I got an HC-05 connected to my L293D motor shield (attached to an Arduino UNO), as well as 2 dc motors, an ultrasonic sensor HC-SR04 and a LED. The circuit part is not the problem, I’ve managed to connect it all and it's working (I tested the obstacle avoiding part by itself, before adding the mode selection code, so I know the motors and sensor do their job). I should also mention that I’m sending the information through the BT from an app I made in APP Inventor. The app is working fine, and I do receive the correct data in the Serial Monitor.

What’s puzzling me is, why can’t I get to assign the Serial.read(); value to my response variable (resp). I’m going to try to explain this a bit further. In my serial monitor I get the intended chars from the app buttons, however, my “resp” variable doesn’t seem to be getting any value from Serial.read(); for some reason, because when it gets to the switch statement, even though the char is printed on the monitor the cases don’t start, for instance, and I might be completely off here, once I get ‘1’ on the monitor I should also start getting the distance measurement printed in the monitor and it’s not.

For what I gather, I believe the issue is between the Serial.write() and the Serial.println()/.read(); since the serial.write() seems to be taking priority over all others, since setting this lines as comments don’t affect the writing in the monitor:

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

I’m out of ideas so, any help or guidance towards the right direction will be greatly appreciated.

//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);
  } 


Here is an image of what I get on my serial monitor. Serial Monitor with BT readings.

UPDATE:

As suggested by @chrisl, I'm now looking at it from a different approach.

I was sending the BT information to the serial monitor when the idea in the first place is having the car run around without being connected to the pc at all.

So instead of sending the information to serial monitor and trying to take it from there, I simply asigned the value from BTSerial to my variable and it kinda works as intended now.

"Automatic mode" is working, my switch statement recognises the resp value and starts measuring and prints the distance, however, once I push '2', the first command activates and the motors stop but all the other instructions don't work. I even changed my second switch statemate to if's but that didnt help either.

Here's my new Serial output (9600 / Newline / 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

And my updated code:

//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);
  } 

1 Answer 1

0

I guess the main problem is, that you write your commands from the bluetooth app, but the HC-05 module is connected to BTSerial. Here you are reading from it and forwarding the data to Serial (serial monitor on your PC):

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

You are not doing anything else with the bluetooth serial data, so you cannot change the resp variable over bluetooth. Instead you are doing this with Serial (not BTSerial). But as long, as you don't type the commands into the serial monitor, that code will never run. Thus the distance code will never be executed.

If you want to control the program from the bluetoooth app, you need to actually work with the data from the right serial interface.


Btw: Your serial monitor is configured for a newline as line ending character. Your Serial code does not discard these, so you might get undesired behavior there, when resp becomes \n.


About your new problem: This problem actually existed also with the old code, but you couldn't see it, because the you were reading from the wrong serial interface. Try to read this (compressed) snipped of your (old) code:

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

With the first switch statement you are checking the value of resp. If it is '2', you will enter the second case. In there you are again checking the value of resp for the values 'A', ... . But when you already know, that resp is equal to '2', how can it ever be equal to 'A' or one of the other subcommands, since you don't change resp in between. Thus the code inside the second switch statement is unreachable.

What to do now? I guess you want the first switch statement to handle the modes and the second one to handle the move commands, while in second mode. For that you need to restructure your code, in the best case to an FSM (Finite State Machine). I already once wrote a good descriptions of how an FSM works in my answer to this question.

Introduce a state variable, which gets used in the first switch statement, and a command variable, that gets used in the second one. I would suggest a structure like this:

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;
    }
}

Here the serial input will be put into the state or command variable, depending, on what exactly was send. The part with the main code comes after that. Depending on the state, the corresponding code is executed. When in manual mode, the code will look at the command variable to check, which function to execute.

3
  • I see, perhaps I got it all wrong then, because the intention is to have the car run around without being connected to the pc, and therefore not conected to the serial monitor in the first place. So, should I simply asign the recieved information to my resp variable without writing it to the serial monitor at all? Apr 20, 2020 at 23:01
  • With Serial (UART) writing and reading is independent. You can see the bluetooth data on the serial monitor, if you write it to Serial, but that doesn't mean, that you can read it back from there. You can only read from Serial, what is actively send by the serial monitor. You read the important command data from BTSerial, so you should use that data for assigning to resp. You can still additionally send the data to the serial monitor for debugging purpose.
    – chrisl
    Apr 21, 2020 at 7:39
  • @GenaroBasoria I added a part to my answer about the problem with the manual mode
    – chrisl
    Apr 21, 2020 at 8:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.