### 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** ![](media/image-20230427102547633.png) #### **(3)Diagramma di Collegamento:** ![](media/e7ac834ba04aa2e8862995d2d33ce9356.jpg) 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. ![](media/13656cfee75dc5acbeba18a90a084e159.jpg)