Proyecto 23: Múltiples Funciones del Robot Extintor de Incendios

(1)Descripción:

El auto inteligente ha realizado una función individual en cada proyecto anterior.

¿Puede mostrar múltiples funciones al mismo tiempo? Sí.

En este último gran proyecto, tenemos la intención de usar un código completo para controlar el auto inteligente y mostrar todas las funciones mencionadas en proyectos anteriores. Usamos las teclas de la APP Bluetooth para cambiar automáticamente entre varias funciones, muy simple y conveniente.

(2)Diagrama de Flujo:

Por favor, consulte el Proyecto 16 para instalar y configurar la APP Bluetooth

(3)Diagrama de Conexión:

1. GND, VCC, SDA y SCL de la placa 8x16 están conectados a G (GND), + (VCC), A4 y A5 de la placa de expansión.

2. VCC, IN+, IN- y Gnd del módulo del ventilador están conectados a 5V (V), 12 (S), 13 (S) y Gnd (G).

3. El cable marrón, el cable rojo y el cable naranja del servo están conectados a Gnd (G), 5v (V) y D10.

4. RXD, TXD, GND y VCC del módulo BT están conectados a TX, RX, G (GND) y 5V (VCC). STATE y BRK no necesitan ser conectados.

5. Los pines “G”, “V” y A del sensor de llama izquierdo están conectados a G (GND), V (VCC) y A1, respectivamente; El sensor de llama derecho está conectado a G (GND), V (VCC) y A2, respectivamente.

6. Los puertos distales del sensor de seguimiento de línea son 11, 7 y 8.

(4)Código de Prueba:

(Nota: No conecte el módulo Bluetooth antes de cargar el código, porque la carga del código también usa comunicación serial, y puede haber conflictos con la comunicación serial Bluetooth, lo que puede causar que la carga falle.)

/*
  Keyestudio Mini Tank Robot V3 (Popular Edition)
  lesson 23
  Fire Extinguishing Robot Multiple Functions
  http://www.keyestudio.com
*/
#include <IRremote.h>
IRrecv irrecv(3);  //
decode_results results;
long ir_rec;  //usado para guardar el valor IR 

/***********/
#define USE_FAN_FUNCTION   1

//Array, usado para guardar datos de imágenes, puede calcularse por uno mismo u obtenerse de la herramienta de módulo
unsigned char start01[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
unsigned char STOP01[] = {0x2E, 0x2A, 0x3A, 0x00, 0x02, 0x3E, 0x02, 0x00, 0x3E, 0x22, 0x3E, 0x00, 0x3E, 0x0A, 0x0E, 0x00};
unsigned char front[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x12, 0x09, 0x12, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char back[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x48, 0x90, 0x48, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char left[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x28, 0x10, 0x44, 0x28, 0x10, 0x44, 0x28, 0x10, 0x00};
unsigned char right[] = {0x00, 0x10, 0x28, 0x44, 0x10, 0x28, 0x44, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

unsigned char Smile[] = {0x00, 0x00, 0x1c, 0x02, 0x02, 0x02, 0x5c, 0x40, 0x40, 0x5c, 0x02, 0x02, 0x02, 0x1c, 0x00, 0x00};
unsigned char Disgust[] = {0x00, 0x00, 0x02, 0x02, 0x02, 0x12, 0x08, 0x04, 0x08, 0x12, 0x22, 0x02, 0x02, 0x00, 0x00, 0x00};
unsigned char Happy[] = {0x02, 0x02, 0x02, 0x02, 0x08, 0x18, 0x28, 0x48, 0x28, 0x18, 0x08, 0x02, 0x02, 0x02, 0x02, 0x00};
unsigned char Squint[] = {0x00, 0x00, 0x00, 0x41, 0x22, 0x14, 0x48, 0x40, 0x40, 0x48, 0x14, 0x22, 0x41, 0x00, 0x00, 0x00};
unsigned char Despise[] = {0x00, 0x00, 0x06, 0x04, 0x04, 0x04, 0x24, 0x20, 0x20, 0x26, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00};
unsigned char Heart[] = {0x00, 0x00, 0x0C, 0x1E, 0x3F, 0x7F, 0xFE, 0xFC, 0xFE, 0x7F, 0x3F, 0x1E, 0x0C, 0x00, 0x00, 0x00};

unsigned char clear[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

#define SCL_Pin  A5  //establecer el pin del reloj en A5
#define SDA_Pin  A4  //establecer el pin de datos en A4

#define ML_Ctrl 4  //definir el pin de control de dirección del motor izquierdo como 4
#define ML_PWM 6   //definir el pin de control PWM del motor izquierdo
#define MR_Ctrl 2  //definir el pin de control de dirección del sensor derecho
#define MR_PWM 5   //definir el pin de control PWM del motor derecho
char ble_val;      //usado para guardar el valor Bluetooth
byte speeds_L = 200; //la velocidad inicial del motor izquierdo es 200
byte speeds_R = 200; //la velocidad inicial del motor derecho es 200
String speeds_l, speeds_r; //recibir caracteres PWM y convertirlos en valor PWM

//conectar el sensor de seguimiento de línea
#define L_pin  11  //izquierda
#define M_pin  7  //centro
#define R_pin  8  //derecha
int L_val, M_val, R_val;

#if USE_FAN_FUNCTION  /****usar ventilador*******/
int flame_L = A1; //definir el puerto analógico del sensor de llama izquierdo en A1
int flame_R = A2; //definir el puerto analógico del sensor de llama derecho en A2
int flame_valL, flame_valR;

//el pin del motor 130
int INA = 12;
int INB = 13;

#else /****usar el sensor ultrasónico*******/
#define servoPin    10  //pin del servo
#define light_L_Pin A1   //definir el pin de la fotoresistencia izquierda
#define light_R_Pin A2   //definir el pin de la fotoresistencia derecha
int left_light;
int right_light;

#define Trig 12
#define Echo 13
float distance;//Almacenar los valores de distancia detectados por el ultrasónico para seguimiento

//Almacenar los valores de distancia detectados por el ultrasónico para evitar obstáculos
int a;
int a1;
int a2;

#endif

bool flag;  //variable de bandera, usada para entrar y salir de un modo

void setup() 
{
  Serial.begin(9600);
  irrecv.enableIRIn();  //Inicializar la librería del control remoto IR

  pinMode(SCL_Pin, OUTPUT);
  pinMode(SDA_Pin, OUTPUT);
  
  pinMode(ML_Ctrl, OUTPUT);
  pinMode(ML_PWM, OUTPUT);
  pinMode(MR_Ctrl, OUTPUT);
  pinMode(MR_PWM, OUTPUT);

  pinMode(L_pin, INPUT); //definir los pines de los sensores como ENTRADA
  pinMode(M_pin, INPUT);
  pinMode(R_pin, INPUT);

  matrix_display(clear);    //limpiar pantalla
  matrix_display(start01);  //mostrar inicio

#if USE_FAN_FUNCTION/****usar el ventilador*******/
  pinMode(INA, OUTPUT);//establecer INA como SALIDA
  pinMode(INB, OUTPUT);//establecer INB como SALIDA

  //definir entradas del sensor de llama
  pinMode(flame_L, INPUT);
  pinMode(flame_R, INPUT);
#else/****usar el sensor ultrasónico*******/
  pinMode(servoPin, OUTPUT);
  pinMode(light_L_Pin, INPUT);
  pinMode(light_R_Pin, INPUT);

  pinMode(Trig, OUTPUT);
  pinMode(Echo, INPUT);
  procedure(90); //establecer el ángulo del servo en 90°
#endif
}

void loop() 
{
  if (Serial.available()) //si hay datos en el buffer serial
  {
    ble_val = Serial.read();
    Serial.println(ble_val);
    switch (ble_val) 
    {
      case 'F': Car_front(); break; //el comando para ir hacia adelante

      case 'B': Car_back(); break;  //el comando para ir hacia atrás

      case 'L': Car_left(); break;  //el comando para girar a la izquierda

      case 'R': Car_right(); break; //el comando para girar a la derecha

      case 'S': Car_Stop();  break; //detener

      case 'e': Tracking();  break; //entrar al modo de seguimiento de línea

      case 'f': Confinement(); break;  //entrar al modo de confinamiento

#if USE_FAN_FUNCTION/****usar ventilador*******/
      case 'j': Fire(); break;  //activar el modo de extinción de incendios

      case 'c': fan_begin(); break;  //activar el ventilador

      case 'd': fan_stop();  break;  //apagar el ventilador

#else/****usar el sensor ultrasónico*******/
      case 'g': Avoid(); break;  //entrar al modo de evitación de obstáculos

      case 'h': Follow(); break;  //entrar al modo de seguimiento

      case 'i': Light_following();  break;  //entrar al modo de seguimiento de luz
#endif
      case 'u': 
        speeds_l = Serial.readStringUntil('#'); 
        speeds_L = String(speeds_l).toInt(); 
        break; //comenzar recibiendo u, terminar recibiendo el carácter # y convertir en entero

      case 'v': 
        speeds_r = Serial.readStringUntil('#');
        speeds_R = String(speeds_r).toInt(); 
        break; //comenzar recibiendo u, terminar recibiendo el carácter # y convertir en entero

      case 'k': matrix_display(Smile);    break;  //mostrar cara "sonriente"
      case 'l': matrix_display(Disgust);  break;  //mostrar cara "disgustada"
      case 'm': matrix_display(Happy);    break;  //mostrar cara "feliz"
      case 'n': matrix_display(Squint);   break;  //mostrar cara "triste"
      case 'o': matrix_display(Despise);  break;  //mostrar cara "despreciativa"
      case 'p': matrix_display(Heart);    break;  //mostrar la imagen del corazón
      case 'z': matrix_display(clear);    break;  //limpiar imágenes

      default: break;
    }
  }

#if (USE_FAN_FUNCTION != 1)/****la función para no usar el ventilador*******/
  //Las siguientes tres señales se usan principalmente para impresión cíclica
  if (ble_val == 'x') 
  {
    distance = checkdistance(); Serial.println(distance);
    delay(50);
  } 
  else if (ble_val == 'w') 
  {
    left_light = analogRead(light_L_Pin);
    Serial.println(left_light);
    delay(50);
  } 
  else if (ble_val == 'y') 
  {
    right_light = analogRead(light_R_Pin);
    Serial.println(right_light);
    delay(50);
  }
#endif

  if (irrecv.decode(&results))  //Recibir valor del control remoto infrarrojo
  {
    ir_rec = results.value;
    Serial.println(ir_rec, HEX);
    switch (ir_rec) 
    {
      case 0xFF629D: Car_front();   break;   //ir hacia adelante
      case 0xFFA857: Car_back();    break;   //ir hacia atrás
      case 0xFF22DD: Car_left();    break;   //girar a la izquierda
      case 0xFFC23D: Car_right();   break;   //girar a la derecha
      case 0xFF02FD: Car_Stop();    break;   //detener
      default: break;
    }
    irrecv.resume();
  }
}

#if (USE_FAN_FUNCTION != 1)/****usar el sensor ultrasónico*******/

//Controlar el sensor ultrasónico
float checkdistance() 
{
  float distance;
  digitalWrite(Trig, LOW);
  delayMicroseconds(2);
  digitalWrite(Trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(Trig, LOW);
  distance = pulseIn(Echo, HIGH) / 58.20;  //
  delay(10);
  return distance;
}


//la función para controlar el servo
void procedure(int myangle) 
{
  int pulsewidth;
  pulsewidth = map(myangle, 0, 180, 500, 2000);  //Calcular el valor del ancho de pulso, que debe ser el valor de mapeo de 500 a 2500. Considerando la influencia de la librería infrarroja, se usa 500~2000 aquí.
  for (int i = 0; i < 5; i++) 
  {
    digitalWrite(servoPin, HIGH);
    delayMicroseconds(pulsewidth);   //La duración del nivel alto es el ancho de pulso
    digitalWrite(servoPin, LOW);
    delay((20 - pulsewidth / 1000));  //El período es 20ms, por lo que el nivel bajo dura el resto del tiempo
  }
}

/*****************evitación de obstáculos******************/
void Avoid()
{
  flag = 0;
  while (flag == 0)
  {
    a = checkdistance();  //la distancia frontal se establece en a
    if (a < 20) //Cuando la distancia al frente es menor a 20cm
    {
      Car_Stop();  //detener
      delay(500); //retardo de 500ms
      procedure(180);  //el servo gira a la izquierda
      delay(500); //retardo de 500ms
      a1 = checkdistance();  //la distancia izquierda se establece en a1
      delay(100); //leer valor

      procedure(0); //el servo gira a la derecha
      delay(500); //retardo de 500ms
      a2 = checkdistance(); ///la distancia derecha se establece en a2
      delay(100); //leer valor

      procedure(90);  //volver a 90°
      delay(500);
      if (a1 > a2)  //Cuando la distancia a la izquierda es mayor que la distancia a la derecha
      {
        Car_left();  //el robot gira a la izquierda
        delay(700);  //girar a la izquierda 700ms
      } 
      else 
      {
        Car_right(); //girar a la derecha
        delay(700);
      }
    }
    else  //si la distancia frontal ≥20cm, el robot avanza
    {
      Car_front(); //avanzar
    }
    //recibir el valor Bluetooth para salir del bucle
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S')  //recibir S
      {
        flag = 1;  //establecer flag en 1 para salir del bucle
        Car_Stop();
      }
    }
  }
}

/*******************seguimiento***************/
void Follow() 
{
  flag = 0;
  while (flag == 0) 
  {
    distance = checkdistance();  //establecer el valor de distancia en distance
    if (distance >= 20 && distance <= 60) //avanzar
    {
      Car_front();
    }
    else if (distance > 10 && distance < 20)  // detener
    {
      Car_Stop();
    }
    else if (distance <= 10)  //retroceder
    {
      Car_back();
    }
    else  //detener
    {
      Car_Stop();
    }
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S')
      {
        flag = 1;  //salir del bucle
        Car_Stop();
      }
    }
  }
}

/****************seguimiento de luz******************/
void Light_following() 
{
  flag = 0;
  while (flag == 0) 
  {
    left_light = analogRead(light_L_Pin);
    right_light = analogRead(light_R_Pin);
    if (left_light > 650 && right_light > 650) //avanzar
    {
      Car_front();
    }
    else if (left_light > 650 && right_light <= 650)  //girar a la izquierda
    {
      Car_left();
    }
    else if (left_light <= 650 && right_light > 650) //girar a la derecha
    {
      Car_right();
    }
    else  //de lo contrario, detener
    {
      Car_Stop();
    }
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S') 
      {
        flag = 1;
        Car_Stop();
      }
    }
  }
}

#else/****usar el ventilador*******/
/***************activar el ventilador*****************/
void fan_begin() 
{
  digitalWrite(INA, LOW);
  digitalWrite(INB, HIGH);
}

/***************detener el ventilador*****************/
void fan_stop() 
{
  digitalWrite(INA, LOW);
  digitalWrite(INB, LOW);
}

/***************extinguir incendio****************/
void Fire() 
{
  flag = 0;
  while (flag == 0) 
  {
    //Leer el valor analógico del sensor de llama
    flame_valL = analogRead(flame_L);
    flame_valR = analogRead(flame_R);
    if (flame_valL <= 700 || flame_valR <= 700) 
    {
      Car_Stop();
      fan_begin();
    } 
    else 
    {
      fan_stop();
      L_val = digitalRead(L_pin); //Leer el valor del sensor izquierdo
      M_val = digitalRead(M_pin); //Leer el valor del sensor central
      R_val = digitalRead(R_pin); //Leer el valor del sensor derecho
     if (M_val == 1)  //el del medio detecta líneas negras
      {
        if (L_val == 1 && R_val == 0)  //Si se detecta una línea negra a la izquierda, pero no a la derecha, girar a la izquierda
        {
          Car_left();
        }
        else if (L_val == 0 && R_val == 1)  //Si se detecta una línea negra a la derecha, no a la izquierda, girar a la derecha
        {
          Car_right();
        }
        else //avanzar
        { 
          Car_front();
        }
      }
      else //el del medio detecta líneas negras
      { 
        if (L_val == 1 && R_val == 0) //Si se detecta una línea negra a la izquierda, pero no a la derecha, girar a la izquierda
        { 
          Car_left();
        }
        else if (L_val == 0 && R_val == 1) //Si se detecta una línea negra a la derecha, no a la izquierda, girar a la derecha
        { 
          Car_right();
        }
        else //de lo contrario detener
        { 
          Car_Stop();
        }
      }
    }
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S') 
      {
        flag = 1;
        Car_Stop();
      }
    }
  }
}

#endif

/***************seguimiento de línea*****************/
void Tracking() 
{
  flag = 0;
  while (flag == 0) 
  {
    L_val = digitalRead(L_pin); //Leer el valor del sensor izquierdo
    M_val = digitalRead(M_pin); //Leer el valor del sensor intermedio
    R_val = digitalRead(R_pin); //Leer el valor del sensor derecho
    if (M_val == 1)  //el del medio detecta líneas negras
    {
      if (L_val == 1 && R_val == 0) //Si se detecta una línea negra a la izquierda, pero no a la derecha, girar a la izquierda
      {
        Car_left();
      }
      else if (L_val == 0 && R_val == 1) //Si se detecta una línea negra a la derecha, no a la izquierda, girar a la derecha
      { 
        Car_right();
      }
      else //avanzar
      { 
        Car_front();
      }
    }
    else //el sensor del medio no detecta líneas negras
    { 
      if (L_val == 1 && R_val == 0) //Si se detecta una línea negra a la izquierda, pero no a la derecha, girar a la izquierda
      { 
        Car_left();
      }
      else if (L_val == 0 && R_val == 1) //Si se detecta una línea negra a la derecha, no a la izquierda, girar a la derecha
      { 
        Car_right();
      }
      else //de lo contrario detener
      { 
        Car_Stop();
      }
    }
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S') 
      {
        flag = 1;
        Car_Stop();
      }
    }
  }
}

/***************Confinamiento*****************/
void Confinement() 
{
  flag = 0;
  while (flag == 0) 
  {
    L_val = digitalRead(L_pin); //Leer el valor del sensor izquierdo
    M_val = digitalRead(M_pin); //Leer el valor del sensor intermedio
    R_val = digitalRead(R_pin); //Leer el valor del sensor derecho
    if ( L_val == 0 && M_val == 0 && R_val == 0 ) //Avanzar cuando no se detectan líneas negras   
    {    
        Car_front();
    }
    else 
    { 
      Car_back();
      delay(700);
      Car_left();
      delay(800);
    }
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S') 
      {
        flag = 1;
        Car_Stop();
      }
    }
  }
}


/***************matriz de puntos******************/
//esta función se usa para la visualización de la matriz de puntos
void matrix_display(unsigned char matrix_value[])
{
  IIC_start();  //usar la función para comenzar a transmitir datos
  IIC_send(0xc0);  //seleccionar una dirección
  for (int i = 0; i < 16; i++) //los datos de imagen tienen 16 caracteres
  {
    IIC_send(matrix_value[i]); //datos para transmitir imágenes
  }
  IIC_end();   //finalizar la transmisión de datos de imágenes
  IIC_start();
  IIC_send(0x8A);  //mostrar control y seleccionar ancho de pulso 4/16
  IIC_end();
}

//la condición para que los datos comiencen a transmitirse
void IIC_start()
{
  digitalWrite(SDA_Pin, HIGH);
  digitalWrite(SCL_Pin, HIGH);
  delayMicroseconds(3);
  digitalWrite(SDA_Pin, LOW);
  delayMicroseconds(3);
  digitalWrite(SCL_Pin, LOW);
}

//transmitir datos
void IIC_send(unsigned char send_data)
{
  for (byte mask = 0x01; mask != 0; mask <<= 1) //cada carácter tiene 8 dígitos, que se detectan uno a uno
  {
    if (send_data & mask)  //establecer niveles altos o bajos según cada bit (0 o 1)
    {
      digitalWrite(SDA_Pin, HIGH);
    } 
    else 
    {
      digitalWrite(SDA_Pin, LOW);
    }
    delayMicroseconds(3);
    digitalWrite(SCL_Pin, HIGH); //elevar el pin de reloj SCL_Pin para finalizar la transmisión de datos
    delayMicroseconds(3);
    digitalWrite(SCL_Pin, LOW); //bajar el pin de reloj SCL_Pin para cambiar las señales de SDA 
  }
}

//la señal de que la transmisión de datos ha terminado
void IIC_end()
{
  digitalWrite(SCL_Pin, LOW);
  digitalWrite(SDA_Pin, LOW);
  delayMicroseconds(3);
  digitalWrite(SCL_Pin, HIGH);
  delayMicroseconds(3);
  digitalWrite(SDA_Pin, HIGH);
  delayMicroseconds(3);
}

/***************funcionamiento del motor****************/
void Car_back() 
{
  digitalWrite(MR_Ctrl, LOW);
  analogWrite(MR_PWM, speeds_R);
  digitalWrite(ML_Ctrl, LOW);
  analogWrite(ML_PWM, speeds_L);
  matrix_display(back);  //mostrar la imagen de retroceso
}

void Car_front() 
{
  digitalWrite(MR_Ctrl, HIGH);
  analogWrite(MR_PWM, 255 - speeds_R);
  digitalWrite(ML_Ctrl, HIGH);
  analogWrite(ML_PWM, 255 - speeds_L);
  matrix_display(front);  //mostrar la imagen de avance
}

void Car_left() 
{
  digitalWrite(MR_Ctrl, HIGH);
  analogWrite(MR_PWM, 255 - speeds_R);
  digitalWrite(ML_Ctrl, LOW);
  analogWrite(ML_PWM, speeds_L);
  matrix_display(left);  //mostrar la imagen de giro a la izquierda
}

void Car_right() 
{
  digitalWrite(MR_Ctrl, LOW);
  analogWrite(MR_PWM, speeds_R);
  digitalWrite(ML_Ctrl, HIGH);
  analogWrite(ML_PWM, 255 - speeds_L);
  matrix_display(right);  //mostrar la imagen de giro a la derecha
}

void Car_Stop() 
{
  digitalWrite(MR_Ctrl, LOW);
  analogWrite(MR_PWM, 0);
  digitalWrite(ML_Ctrl, LOW);
  analogWrite(ML_PWM, 0);
  matrix_display(STOP01);  //mostrar la imagen de parada
}

(5)Resultado de la prueba

Antes de cargar el código del programa, es necesario retirar el módulo Bluetooth; de lo contrario, la carga del código fallará.

Después de cargar el código correctamente, active los servicios de ubicación en su dispositivo y luego conecte el módulo Bluetooth.

Una vez que el módulo Bluetooth esté conectado y encendido, y la APP móvil se haya conectado correctamente al Bluetooth, podemos usar la APP móvil para controlar el robot tanque.

También puede controlar el robot con el control remoto.