### 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.)
```C
/*
Keyestudio Mini Tank Robot V3 (Popular Edition)
lesson 23
Fire Extinguishing Robot Multiple Functions
http://www.keyestudio.com
*/
#include
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.
