Progetto 23: Robot Antincendio con Funzioni Multiple

(1)Descrizione:

Il robot ha eseguito una singola funzione in ogni progetto precedente.

Può visualizzare più funzioni contemporaneamente? Sì.

In questo ultimo grande progetto, intendiamo utilizzare un codice completo per controllare il robot e mostrare tutte le funzioni menzionate nei progetti precedenti. Usiamo i tasti sull’APP Bluetooth per passare automaticamente tra le varie funzioni, molto semplice e conveniente.

(2)Diagramma di Flusso:

Si prega di fare riferimento al Progetto 16 per installare e configurare l’APP Bluetooth

(3)Diagramma di Collegamento:

1. GND, VCC, SDA e SCL della scheda 8x16 sono collegati a G (GND), + (VCC), A4 e A5 della scheda di espansione.

2. VCC, IN+, IN- e Gnd del modulo ventola sono collegati a 5V (V), 12 (S), 13 (S) e Gnd (G).

3. Il filo marrone, il filo rosso e il filo arancione del servo sono collegati a Gnd (G), 5v (V) e D10.

4. RXD, TXD, GND e VCC del modulo BT sono collegati a TX, RX, G (GND) e 5V (VCC). STATE e BRK non devono essere collegati.

5. I pin “G”, “V” e A del sensore di fiamma sinistro sono collegati rispettivamente a G (GND), V (VCC) e A1; Il sensore di fiamma destro è collegato rispettivamente a G (GND), V (VCC) e A2.

6. Le porte distali del sensore di inseguimento linea sono 11, 7 e 8.

(4)Codice di Test:

(Nota: Non collegare il modulo Bluetooth prima di caricare il codice, perché il caricamento del codice utilizza anche la comunicazione seriale, e potrebbero verificarsi conflitti con la comunicazione seriale Bluetooth, che possono causare il fallimento del caricamento.)

/*
  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;  // utilizzato per salvare il valore IR

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

// Array, utilizzato per salvare i dati delle immagini, può essere calcolato da soli o ottenuto dallo strumento di modulo
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  // imposta il pin del clock su A5
#define SDA_Pin  A4  // imposta il pin dei dati su A4

#define ML_Ctrl 4  // definisce il pin di controllo della direzione del motore sinistro come 4
#define ML_PWM 6   // definisce il pin di controllo PWM del motore sinistro
#define MR_Ctrl 2  // definisce il pin di controllo della direzione del sensore destro
#define MR_PWM 5   // definisce il pin di controllo PWM del motore destro
char ble_val;      // utilizzato per salvare il valore Bluetooth
byte speeds_L = 200; // la velocità iniziale del motore sinistro è 200
byte speeds_R = 200; // la velocità iniziale del motore destro è 200
String speeds_l, speeds_r; // riceve i caratteri PWM e li converte in valore PWM

// collegamento del sensore di inseguimento linea
#define L_pin  11  // sinistra
#define M_pin  7  // centro
#define R_pin  8  // destra
int L_val, M_val, R_val;

#if USE_FAN_FUNCTION  /****usa la ventola*******/
int flame_L = A1; // definisce la porta analogica del sensore di fiamma sinistro su A1
int flame_R = A2; // definisce la porta analogica del sensore di fiamma destro su A2
int flame_valL, flame_valR;

// il pin del motore 130
int INA = 12;
int INB = 13;

#else /****usa il sensore a ultrasuoni*******/
#define servoPin    10  // pin del servo
#define light_L_Pin A1   // definisce il pin del fotoresistore sinistro
#define light_R_Pin A2   // definisce il pin del fotoresistore destro
int left_light;
int right_light;

#define Trig 12
#define Echo 13
float distance;// Memorizza i valori di distanza rilevati dagli ultrasuoni per il seguimento

// Memorizza i valori di distanza rilevati dagli ultrasuoni per l'evitamento ostacoli
int a;
int a1;
int a2;

#endif

bool flag;  // variabile flag, utilizzata per entrare e uscire da una modalità

void setup() 
{
  Serial.begin(9600);
  irrecv.enableIRIn();  // Inizializza la libreria del telecomando 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); // definisce i pin dei sensori come INPUT
  pinMode(M_pin, INPUT);
  pinMode(R_pin, INPUT);

  matrix_display(clear);    // cancella lo schermo
  matrix_display(start01);  // mostra avvio

#if USE_FAN_FUNCTION/****usa la ventola*******/
  pinMode(INA, OUTPUT);// imposta INA come OUTPUT
  pinMode(INB, OUTPUT);// imposta INB come OUTPUT

  // definisce gli ingressi del sensore di fiamma
  pinMode(flame_L, INPUT);
  pinMode(flame_R, INPUT);
#else/****usa il sensore a ultrasuoni*******/
  pinMode(servoPin, OUTPUT);
  pinMode(light_L_Pin, INPUT);
  pinMode(light_R_Pin, INPUT);

  pinMode(Trig, OUTPUT);
  pinMode(Echo, INPUT);
  procedure(90); // imposta l'angolo del servo a 90°
#endif
}

void loop() 
{
  if (Serial.available()) // se ci sono dati nel buffer seriale
  {
    ble_val = Serial.read();
    Serial.println(ble_val);
    switch (ble_val) 
    {
      case 'F': Car_front(); break; // il comando per andare avanti

      case 'B': Car_back(); break;  // il comando per andare indietro

      case 'L': Car_left(); break;  // il comando per girare a sinistra

      case 'R': Car_right(); break; // il comando per girare a destra

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

      case 'e': Tracking();  break; // entra nella modalità di inseguimento linea

      case 'f': Confinement(); break;  // entra nella modalità di confinamento

#if USE_FAN_FUNCTION/****usa la ventola*******/
      case 'j': Fire(); break;  // abilita la modalità di estinzione incendio

      case 'c': fan_begin(); break;  // abilita la ventola

      case 'd': fan_stop();  break;  // spegni la ventola

#else/****usa il sensore a ultrasuoni*******/
      case 'g': Avoid(); break;  // entra nella modalità di evitamento ostacoli

      case 'h': Follow(); break;  // entra nella modalità di seguimento

      case 'i': Light_following();  break;  // entra nella modalità di seguimento della luce
#endif
      case 'u': 
        speeds_l = Serial.readStringUntil('#'); 
        speeds_L = String(speeds_l).toInt(); 
        break; // inizia ricevendo u, termina ricevendo il carattere # e converte in intero

      case 'v': 
        speeds_r = Serial.readStringUntil('#');
        speeds_R = String(speeds_r).toInt(); 
        break; // inizia ricevendo u, termina ricevendo il carattere # e converte in intero

      case 'k': matrix_display(Smile);    break;  // mostra la faccia "sorridente"
      case 'l': matrix_display(Disgust);  break;  // mostra la faccia "disgustata"
      case 'm': matrix_display(Happy);    break;  // mostra la faccia "felice"
      case 'n': matrix_display(Squint);   break;  // mostra la faccia "triste"
      case 'o': matrix_display(Despise);  break;  // mostra la faccia "sprezzante"
      case 'p': matrix_display(Heart);    break;  // mostra l'immagine del battito cardiaco
      case 'z': matrix_display(clear);    break;  // cancella le immagini

      default: break;
    }
  }

#if (USE_FAN_FUNCTION != 1)/****la funzione per non utilizzare la ventola*******/
  // I seguenti tre segnali sono principalmente utilizzati per la stampa ciclica
  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))  // Riceve il valore del telecomando infrarossi
  {
    ir_rec = results.value;
    Serial.println(ir_rec, HEX);
    switch (ir_rec) 
    {
      case 0xFF629D: Car_front();   break;   // vai avanti
      case 0xFFA857: Car_back();    break;   // vai indietro
      case 0xFF22DD: Car_left();    break;   // ruota a sinistra
      case 0xFFC23D: Car_right();   break;   // ruota a destra
      case 0xFF02FD: Car_Stop();    break;   // ferma
      default: break;
    }
    irrecv.resume();
  }
}

#if (USE_FAN_FUNCTION != 1)/****usa il sensore a ultrasuoni*******/

// Controlla il sensore a ultrasuoni
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 funzione per controllare il servo
void procedure(int myangle) 
{
  int pulsewidth;
  pulsewidth = map(myangle, 0, 180, 500, 2000);  // Calcola il valore della larghezza dell'impulso, che dovrebbe essere il valore di mappatura da 500 a 2500. Considerando l'influenza della libreria infrarossi, viene utilizzato 500~2000.
  for (int i = 0; i < 5; i++) 
  {
    digitalWrite(servoPin, HIGH);
    delayMicroseconds(pulsewidth);   // La durata del livello alto è la larghezza dell'impulso
    digitalWrite(servoPin, LOW);
    delay((20 - pulsewidth / 1000));  // Il periodo è 20ms, quindi il livello basso dura il resto del tempo
  }
}

/*****************evitamento ostacoli******************/
void Avoid()
{
  flag = 0;
  while (flag == 0)
  {
    a = checkdistance();  // la distanza frontale è impostata su a
    if (a < 20) // Quando la distanza davanti è inferiore a 20cm
    {
      Car_Stop();  // ferma
      delay(500); // ritardo di 500ms
      procedure(180);  // il servo gira a sinistra
      delay(500); // ritardo di 500ms
      a1 = checkdistance();  // la distanza sinistra è impostata su a1
      delay(100); // leggi il valore

      procedure(0); // il servo gira a destra
      delay(500); // ritardo di 500ms
      a2 = checkdistance(); // la distanza destra è impostata su a2
      delay(100); // leggi il valore

      procedure(90);  // torna a 90°
      delay(500);
      if (a1 > a2)  // Quando la distanza a sinistra è maggiore della distanza a destra
      {
        Car_left();  // il robot gira a sinistra
        delay(700);  // gira a sinistra per 700ms
      } 
      else 
      {
        Car_right(); // gira a destra
        delay(700);
      }
    }
    else  // se la distanza frontale è ≥20cm, il robot va avanti
    {
      Car_front(); // vai avanti
    }
    // riceve il valore Bluetooth per uscire dal ciclo
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S')  // riceve S
      {
        flag = 1;  // imposta flag a 1 per uscire dal ciclo
        Car_Stop();
      }
    }
  }
}

/*******************seguimento***************/
void Follow() 
{
  flag = 0;
  while (flag == 0) 
  {
    distance = checkdistance();  // imposta il valore della distanza su distance
    if (distance >= 20 && distance <= 60) // vai avanti
    {
      Car_front();
    }
    else if (distance > 10 && distance < 20)  // ferma
    {
      Car_Stop();
    }
    else if (distance <= 10)  // vai indietro
    {
      Car_back();
    }
    else  // ferma
    {
      Car_Stop();
    }
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S')
      {
        flag = 1;  // esci dal ciclo
        Car_Stop();
      }
    }
  }
}

/****************seguimento della luce******************/
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) // vai avanti
    {
      Car_front();
    }
    else if (left_light > 650 && right_light <= 650)  // gira a sinistra
    {
      Car_left();
    }
    else if (left_light <= 650 && right_light > 650) // gira a destra
    {
      Car_right();
    }
    else  // altrimenti, ferma
    {
      Car_Stop();
    }
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S') 
      {
        flag = 1;
        Car_Stop();
      }
    }
  }
}

#else/****usa la ventola*******/
/***************abilita la ventola*****************/
void fan_begin() 
{
  digitalWrite(INA, LOW);
  digitalWrite(INB, HIGH);
}

/***************ferma la ventola*****************/
void fan_stop() 
{
  digitalWrite(INA, LOW);
  digitalWrite(INB, LOW);
}

/***************estingui l'incendio****************/
void Fire() 
{
  flag = 0;
  while (flag == 0) 
  {
    // Leggi il valore analogico del sensore di fiamma
    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); // Leggi il valore del sensore sinistro
      M_val = digitalRead(M_pin); // Leggi il valore del sensore sinistro
      R_val = digitalRead(R_pin); // Leggi il valore del sensore destro
     if (M_val == 1)  //quello centrale rileva linee nere
      {
        if (L_val == 1 && R_val == 0)  //Se viene rilevata una linea nera a sinistra, ma non a destra, gira a sinistra
        {
          Car_left();
        }
        else if (L_val == 0 && R_val == 1)  //Se viene rilevata una linea nera a destra, non a sinistra, gira a destra
        {
          Car_right();
        }
        else //vai avanti
        { 
          Car_front();
        }
      }
      else //quello centrale rileva linee nere
      { 
        if (L_val == 1 && R_val == 0) //Se viene rilevata una linea nera a sinistra, ma non a destra, gira a sinistra
        { 
          Car_left();
        }
        else if (L_val == 0 && R_val == 1) //Se viene rilevata una linea nera a destra, non a sinistra, gira a destra
        { 
          Car_right();
        }
        else //altrimenti fermati
        { 
          Car_Stop();
        }
      }
    }
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S') 
      {
        flag = 1;
        Car_Stop();
      }
    }
  }
}

#endif

/***************inseguimento linea*****************/
void Tracking() 
{
  flag = 0;
  while (flag == 0) 
  {
    L_val = digitalRead(L_pin); //Leggi il valore del sensore sinistro
    M_val = digitalRead(M_pin); //Leggi il valore del sensore centrale
    R_val = digitalRead(R_pin); //Leggi il valore del sensore destro
    if (M_val == 1)  //quello centrale rileva linee nere
    {
      if (L_val == 1 && R_val == 0) //Se viene rilevata una linea nera a sinistra, ma non a destra, gira a sinistra
      {
        Car_left();
      }
      else if (L_val == 0 && R_val == 1) //Se viene rilevata una linea nera a destra, non a sinistra, gira a destra
      { 
        Car_right();
      }
      else //vai avanti
      { 
        Car_front();
      }
    }
    else //il sensore centrale non rileva linee nere
    { 
      if (L_val == 1 && R_val == 0) //Se viene rilevata una linea nera a sinistra, ma non a destra, gira a sinistra
      { 
        Car_left();
      }
      else if (L_val == 0 && R_val == 1) //Se viene rilevata una linea nera a destra, non a sinistra, gira a destra
      { 
        Car_right();
      }
      else //altrimenti fermati
      { 
        Car_Stop();
      }
    }
    if (Serial.available())
    {
      ble_val = Serial.read();
      if (ble_val == 'S') 
      {
        flag = 1;
        Car_Stop();
      }
    }
  }
}

/***************Confinamento*****************/
void Confinement() 
{
  flag = 0;
  while (flag == 0) 
  {
    L_val = digitalRead(L_pin); //Leggi il valore del sensore sinistro
    M_val = digitalRead(M_pin); //Leggi il valore del sensore centrale
    R_val = digitalRead(R_pin); //Leggi il valore del sensore destro
    if ( L_val == 0 && M_val == 0 && R_val == 0 ) //Vai avanti quando non vengono rilevate linee nere   
    {    
        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();
      }
    }
  }
}


/***************matrice di punti******************/
//questa funzione viene utilizzata per la visualizzazione della matrice di punti 
void matrix_display(unsigned char matrix_value[])
{
  IIC_start();  //usa la funzione per iniziare la trasmissione dei dati
  IIC_send(0xc0);  //seleziona un indirizzo
  for (int i = 0; i < 16; i++) //i dati dell'immagine hanno 16 caratteri
  {
    IIC_send(matrix_value[i]); //dati per trasmettere le immagini
  }
  IIC_end();   //termina la trasmissione dei dati delle immagini
  IIC_start();
  IIC_send(0x8A);  //mostra il controllo e seleziona la larghezza dell'impulso 4/16
  IIC_end();
}

//la condizione in cui i dati iniziano la trasmissione
void IIC_start()
{
  digitalWrite(SDA_Pin, HIGH);
  digitalWrite(SCL_Pin, HIGH);
  delayMicroseconds(3);
  digitalWrite(SDA_Pin, LOW);
  delayMicroseconds(3);
  digitalWrite(SCL_Pin, LOW);
}

//trasmetti dati
void IIC_send(unsigned char send_data)
{
  for (byte mask = 0x01; mask != 0; mask <<= 1) //ogni carattere ha 8 cifre, che vengono rilevate una per una
  {
    if (send_data & mask)  //imposta livelli alti o bassi in base a ciascun bit (0 o 1)
    {
      digitalWrite(SDA_Pin, HIGH);
    } 
    else 
    {
      digitalWrite(SDA_Pin, LOW);
    }
    delayMicroseconds(3);
    digitalWrite(SCL_Pin, HIGH); //alza il pin del clock SCL_Pin per terminare la trasmissione dei dati
    delayMicroseconds(3);
    digitalWrite(SCL_Pin, LOW); //abbassa il pin del clock SCL_Pin per cambiare i segnali di SDA 
  }
}

//il segnale che la trasmissione dei dati è terminata
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);
}

/***************funzionamento motore****************/
void Car_back() 
{
  digitalWrite(MR_Ctrl, LOW);
  analogWrite(MR_PWM, speeds_R);
  digitalWrite(ML_Ctrl, LOW);
  analogWrite(ML_PWM, speeds_L);
  matrix_display(back);  //mostra l'immagine di andare indietro
}

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);  //mostra l'immagine di andare avanti
}

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);  //mostra l'immagine di girare a sinistra
}

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);  //mostra l'immagine di girare a destra
}

void Car_Stop() 
{
  digitalWrite(MR_Ctrl, LOW);
  analogWrite(MR_PWM, 0);
  digitalWrite(ML_Ctrl, LOW);
  analogWrite(ML_PWM, 0);
  matrix_display(STOP01);  //mostra l'immagine di stop
}

(5)Risultato del Test

Prima di caricare il codice del programma, è necessario rimuovere il modulo Bluetooth; altrimenti il caricamento del codice fallirà.

Dopo aver caricato il codice con successo, attiva i servizi di localizzazione sul tuo dispositivo, quindi connetti il modulo Bluetooth.

Una volta che il modulo Bluetooth è collegato e alimentato, e l’APP mobile è connessa con successo al Bluetooth, possiamo utilizzare l’APP mobile per controllare il robot tank.

Puoi anche controllare il robot con il telecomando.