Projekt 19: Ultraschall-Panzerroboter Mehrfachfunktionen
(1)Beschreibung:
Der Smart Car hat in jedem vorherigen Projekt eine einzelne Funktion ausgeführt.
Kann er mehrere Funktionen gleichzeitig anzeigen? Ja.
In diesem letzten großen Projekt beabsichtigen wir, einen vollständigen Code zu verwenden, um den Smart Car zu steuern und alle in früheren Projekten erwähnten Funktionen zu zeigen. Wir verwenden die Tasten auf der Bluetooth-APP, um automatisch zwischen verschiedenen Funktionen zu wechseln, was sehr einfach und bequem ist.
(2)Ablaufdiagramm:

(3)Verbindungsdiagramm:

1. GND, VCC, SDA und SCL des 8x16-Boards sind mit G (GND), + (VCC), A4 und A5 des Erweiterungsboards verbunden.
2. VCC, Trig, Echo und Gnd des Ultraschallsensors sind mit 5V (V), 12 (S), 13 (S) und Gnd (G) verbunden.
3. Das braune Kabel, das rote Kabel und das orangefarbene Kabel des Servos sind mit Gnd (G), 5v (V) und D10 verbunden.
4. RXD, TXD, GND und VCC des BT-Moduls sind mit TX, RX, G (GND) und 5V (VCC) verbunden. STATE und BRK müssen nicht angeschlossen werden.
5. Die Pins „G“, „V“ und S des linken Fotowiderstandsmoduls sind jeweils mit G (GND), V (VCC) und A1 verbunden; Das rechte Fotowiderstandsmodul ist jeweils mit G (GND), V (VCC) und A2 verbunden.
6. Die distalen Anschlüsse des Linienverfolgungssensors sind 11, 7 und 8.
(4)Testcode:
(Hinweis: Schließen Sie das Bluetooth-Modul nicht an, bevor Sie den Code hochladen, da das Hochladen des Codes ebenfalls die serielle Kommunikation verwendet und es zu Konflikten mit der Bluetooth-Serielkommunikation kommen kann, was dazu führen kann, dass der Upload fehlschlägt.)
/*
Keyestudio Mini Tank Robot V3 (Popular Edition)
lesson 19
Ultrasonic Tank Robot Multiple Functions
http://www.keyestudio.com
*/
#include <IRremote.h>
IRrecv irrecv(3); //
decode_results results;
long ir_rec; // wird verwendet, um den IR-Wert zu speichern
/***********/
#define USE_FAN_FUNCTION 0
// Array, wird verwendet, um Bilddaten zu speichern, kann selbst berechnet oder mit dem Modulus-Tool ermittelt werden
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 // den Takt-Pin auf A5 setzen
#define SDA_Pin A4 // den Daten-Pin auf A4 setzen
#define ML_Ctrl 4 // den Richtungssteuerungspin des linken Motors als 4 definieren
#define ML_PWM 6 // den PWM-Steuerungspin des linken Motors definieren
#define MR_Ctrl 2 // den Richtungssteuerungspin des rechten Sensors definieren
#define MR_PWM 5 // den PWM-Steuerungspin des rechten Motors definieren
char ble_val; // wird verwendet, um den Bluetooth-Wert zu speichern
byte speeds_L = 200; // die Anfangsgeschwindigkeit des linken Motors beträgt 200
byte speeds_R = 200; // die Anfangsgeschwindigkeit des rechten Motors beträgt 200
String speeds_l, speeds_r; // PWM-Zeichen empfangen und in PWM-Wert umwandeln
// Linienverfolgungssensor anschließen
#define L_pin 11 // links
#define M_pin 7 // mitte
#define R_pin 8 // rechts
int L_val, M_val, R_val;
#if USE_FAN_FUNCTION /****Lüfter verwenden*******/
int flame_L = A1; // den analogen Anschluss des linken Flammensensors auf A1 definieren
int flame_R = A2; // den analogen Anschluss des rechten Flammensensors auf A2 definieren
int flame_valL, flame_valR;
// der Pin des 130-Motors
int INA = 12;
int INB = 13;
#else /****Ultraschallsensor verwenden*******/
#define servoPin 10 // Servo-Pin
#define light_L_Pin A1 // den Pin des linken Fotowiderstands definieren
#define light_R_Pin A2 // den Pin des rechten Fotowiderstands definieren
int left_light;
int right_light;
#define Trig 12
#define Echo 13
float distance;// Speichert die vom Ultraschall erkannten Distanzwerte für die Verfolgung
// Speichert die vom Ultraschall erkannten Distanzwerte zur Hindernisumgehung
int a;
int a1;
int a2;
#endif
bool flag; // Flag-Variable, wird verwendet, um einen Modus zu betreten und zu verlassen
void setup()
{
Serial.begin(9600);
irrecv.enableIRIn(); // die Bibliothek der IR-Fernbedienung initialisieren
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); // die Pins der Sensoren als INPUT definieren
pinMode(M_pin, INPUT);
pinMode(R_pin, INPUT);
matrix_display(clear); // Bildschirm löschen
matrix_display(start01); // Start anzeigen
#if USE_FAN_FUNCTION/****den Lüfter verwenden*******/
pinMode(INA, OUTPUT);// INA auf OUTPUT setzen
pinMode(INB, OUTPUT);// INB auf OUTPUT setzen
// Eingänge des Flammensensors definieren
pinMode(flame_L, INPUT);
pinMode(flame_R, INPUT);
#else/****den Ultraschallsensor verwenden*******/
pinMode(servoPin, OUTPUT);
pinMode(light_L_Pin, INPUT);
pinMode(light_R_Pin, INPUT);
pinMode(Trig, OUTPUT);
pinMode(Echo, INPUT);
procedure(90); // den Winkel des Servos auf 90° setzen
#endif
}
void loop()
{
if (Serial.available()) // wenn Daten im seriellen Puffer vorhanden sind
{
ble_val = Serial.read();
Serial.println(ble_val);
switch (ble_val)
{
case 'F': Car_front(); break; // der Befehl zum Vorwärtsfahren
case 'B': Car_back(); break; // der Befehl zum Rückwärtsfahren
case 'L': Car_left(); break; // der Befehl zum Linksdrehen
case 'R': Car_right(); break; // der Befehl zum Rechtsdrehen
case 'S': Car_Stop(); break; // stoppen
case 'e': Tracking(); break; // den Linienverfolgungsmodus starten
case 'f': Confinement(); break; // den Begrenzungsmodus starten
#if USE_FAN_FUNCTION/****Lüfter verwenden*******/
case 'j': Fire(); break; // den Feuerlöschmodus aktivieren
case 'c': fan_begin(); break; // den Lüfter einschalten
case 'd': fan_stop(); break; // den Lüfter ausschalten
#else/****den Ultraschallsensor verwenden*******/
case 'g': Avoid(); break; // den Hindernisumgehungsmodus starten
case 'h': Follow(); break; // den Ultraschall-Folgemodus starten
case 'i': Light_following(); break; // den Lichtfolgemodus starten
#endif
case 'u':
speeds_l = Serial.readStringUntil('#');
speeds_L = String(speeds_l).toInt();
break; // beginnt mit dem Empfang von u, endet mit dem Empfang von # und wird in eine ganze Zahl umgewandelt
case 'v':
speeds_r = Serial.readStringUntil('#');
speeds_R = String(speeds_r).toInt();
break; // beginnt mit dem Empfang von u, endet mit dem Empfang von # und wird in eine ganze Zahl umgewandelt
case 'k': matrix_display(Smile); break; // "Lächeln"-Gesicht anzeigen
case 'l': matrix_display(Disgust); break; // "Ekel"-Gesicht anzeigen
case 'm': matrix_display(Happy); break; // "Glücklich"-Gesicht anzeigen
case 'n': matrix_display(Squint); break; // "Traurig"-Gesicht anzeigen
case 'o': matrix_display(Despise); break; // "Verachtung"-Gesicht anzeigen
case 'p': matrix_display(Heart); break; // das Herzbild anzeigen
case 'z': matrix_display(clear); break; // Bilder löschen
default: break;
}
}
#if (USE_FAN_FUNCTION != 1)/****die Funktion ohne Lüfter verwenden*******/
// Die folgenden drei Signale werden hauptsächlich für den zyklischen Druck verwendet
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)) // Infrarot-Fernbedienungswert empfangen
{
ir_rec = results.value;
Serial.println(ir_rec, HEX);
switch (ir_rec)
{
case 0xFF629D: Car_front(); break; // vorwärts fahren
case 0xFFA857: Car_back(); break; // rückwärts fahren
case 0xFF22DD: Car_left(); break; // nach links drehen
case 0xFFC23D: Car_right(); break; // nach rechts drehen
case 0xFF02FD: Car_Stop(); break; // stoppen
default: break;
}
irrecv.resume();
}
}
#if (USE_FAN_FUNCTION != 1)/****den Ultraschallsensor verwenden*******/
// Den Ultraschallsensor steuern
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;
}
// die Funktion zur Steuerung des Servos
void procedure(int myangle)
{
int pulsewidth;
pulsewidth = map(myangle, 0, 180, 500, 2000); // Den Impulsbreitenwert berechnen, der der Zuordnungswert von 500 bis 2500 sein sollte. Angesichts des Einflusses der Infrarotbibliothek wird hier 500~2000 verwendet.
for (int i = 0; i < 5; i++)
{
digitalWrite(servoPin, HIGH);
delayMicroseconds(pulsewidth); // Die Dauer des High-Pegels ist die Impulsbreite
digitalWrite(servoPin, LOW);
delay((20 - pulsewidth / 1000)); // Die Periode beträgt 20ms, daher dauert der Low-Pegel die restliche Zeit
}
}
/*****************Hindernisumgehung******************/
void Avoid()
{
flag = 0;
while (flag == 0)
{
a = checkdistance(); // der vordere Abstand wird auf a gesetzt
if (a < 20) // Wenn der Abstand nach vorne weniger als 20 cm beträgt
{
Car_Stop(); // stoppen
delay(500); // 500ms verzögern
procedure(180); // Servo dreht nach links
delay(500); // 500ms verzögern
a1 = checkdistance(); // der linke Abstand wird auf a1 gesetzt
delay(100); // Wert lesen
procedure(0); // Servo dreht nach rechts
delay(500); // 500ms verzögern
a2 = checkdistance(); // der rechte Abstand wird auf a2 gesetzt
delay(100); // Wert lesen
procedure(90); // zurück auf 90°
delay(500);
if (a1 > a2) // Wenn der Abstand links größer als der Abstand rechts ist
{
Car_left(); // der Roboter dreht nach links
delay(700); // 700ms nach links drehen
}
else
{
Car_right(); // nach rechts drehen
delay(700);
}
}
else // wenn der vordere Abstand ≥20cm ist, fährt der Roboter vorwärts
{
Car_front(); // vorwärts fahren
}
// den Bluetooth-Wert empfangen, um die Schleife zu beenden
if (Serial.available())
{
ble_val = Serial.read();
if (ble_val == 'S') // S empfangen
{
flag = 1; // Flag auf 1 setzen, um die Schleife zu beenden
Car_Stop();
}
}
}
}
/*******************Folgen***************/
void Follow()
{
flag = 0;
while (flag == 0)
{
distance = checkdistance(); // den Distanzwert auf distance setzen
if (distance >= 20 && distance <= 60) // vorwärts fahren
{
Car_front();
}
else if (distance > 10 && distance < 20) // stoppen
{
Car_Stop();
}
else if (distance <= 10) // rückwärts fahren
{
Car_back();
}
else // stoppen
{
Car_Stop();
}
if (Serial.available())
{
ble_val = Serial.read();
if (ble_val == 'S')
{
flag = 1; // die Schleife beenden
Car_Stop();
}
}
}
}
/****************Lichtfolgen******************/
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) // vorwärts fahren
{
Car_front();
}
else if (left_light > 650 && right_light <= 650) // nach links drehen
{
Car_left();
}
else if (left_light <= 650 && right_light > 650) // nach rechts drehen
{
Car_right();
}
else // sonst stoppen
{
Car_Stop();
}
if (Serial.available())
{
ble_val = Serial.read();
if (ble_val == 'S')
{
flag = 1;
Car_Stop();
}
}
}
}
#else/****den Lüfter verwenden*******/
/***************den Lüfter einschalten*****************/
void fan_begin()
{
digitalWrite(INA, LOW);
digitalWrite(INB, HIGH);
}
/***************den Lüfter stoppen*****************/
void fan_stop()
{
digitalWrite(INA, LOW);
digitalWrite(INB, LOW);
}
/***************Feuer löschen****************/
void Fire()
{
flag = 0;
while (flag == 0)
{
// Den Analogwert des Flammensensors lesen
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); // den Wert des linken Sensors lesen
M_val = digitalRead(M_pin); // den Wert des mittleren Sensors lesen
R_val = digitalRead(R_pin); // den Wert des rechten Sensors lesen
if (M_val == 1) //Die mittlere Einheit erkennt schwarze Linien
{
if (L_val == 1 && R_val == 0) //Wenn links eine schwarze Linie erkannt wird, aber rechts nicht, nach links abbiegen
{
Car_left();
}
else if (L_val == 0 && R_val == 1) //Wenn rechts eine schwarze Linie erkannt wird, aber links nicht, nach rechts abbiegen
{
Car_right();
}
else //vorwärts fahren
{
Car_front();
}
}
else //Die mittlere Einheit erkennt schwarze Linien
{
if (L_val == 1 && R_val == 0) //Wenn links eine schwarze Linie erkannt wird, aber rechts nicht, nach links abbiegen
{
Car_left();
}
else if (L_val == 0 && R_val == 1) //Wenn rechts eine schwarze Linie erkannt wird, aber links nicht, nach rechts abbiegen
{
Car_right();
}
else //sonst anhalten
{
Car_Stop();
}
}
}
if (Serial.available())
{
ble_val = Serial.read();
if (ble_val == 'S')
{
flag = 1;
Car_Stop();
}
}
}
}
#endif
/***************Linienverfolgung*****************/
void Tracking()
{
flag = 0;
while (flag == 0)
{
L_val = digitalRead(L_pin); //Wert des linken Sensors lesen
M_val = digitalRead(M_pin); //Wert des mittleren Sensors lesen
R_val = digitalRead(R_pin); //Wert des rechten Sensors lesen
if (M_val == 1) //Die mittlere Einheit erkennt schwarze Linien
{
if (L_val == 1 && R_val == 0) //Wenn links eine schwarze Linie erkannt wird, aber rechts nicht, nach links abbiegen
{
Car_left();
}
else if (L_val == 0 && R_val == 1) //Wenn rechts eine schwarze Linie erkannt wird, aber links nicht, nach rechts abbiegen
{
Car_right();
}
else //vorwärts fahren
{
Car_front();
}
}
else //Der mittlere Sensor erkennt keine schwarzen Linien
{
if (L_val == 1 && R_val == 0) //Wenn links eine schwarze Linie erkannt wird, aber rechts nicht, nach links abbiegen
{
Car_left();
}
else if (L_val == 0 && R_val == 1) //Wenn rechts eine schwarze Linie erkannt wird, aber links nicht, nach rechts abbiegen
{
Car_right();
}
else //sonst anhalten
{
Car_Stop();
}
}
if (Serial.available())
{
ble_val = Serial.read();
if (ble_val == 'S')
{
flag = 1;
Car_Stop();
}
}
}
}
/***************Eingrenzung*****************/
void Confinement()
{
flag = 0;
while (flag == 0)
{
L_val = digitalRead(L_pin); //Wert des linken Sensors lesen
M_val = digitalRead(M_pin); //Wert des mittleren Sensors lesen
R_val = digitalRead(R_pin); //Wert des rechten Sensors lesen
if ( L_val == 0 && M_val == 0 && R_val == 0 ) //Vorwärts fahren, wenn keine schwarzen Linien erkannt werden
{
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();
}
}
}
}
/***************Punktmatrix******************/
//Diese Funktion wird für die Anzeige der Punktmatrix verwendet
void matrix_display(unsigned char matrix_value[])
{
IIC_start(); //Funktion zum Starten der Datenübertragung verwenden
IIC_send(0xc0); //Eine Adresse auswählen
for (int i = 0; i < 16; i++) //Bilddaten haben 16 Zeichen
{
IIC_send(matrix_value[i]); //Daten zur Übertragung von Bildern
}
IIC_end(); //Datenübertragung der Bilder beenden
IIC_start();
IIC_send(0x8A); //Anzeigesteuerung und Impulsbreite 4/16 auswählen
IIC_end();
}
//Bedingung, unter der die Datenübertragung beginnt
void IIC_start()
{
digitalWrite(SDA_Pin, HIGH);
digitalWrite(SCL_Pin, HIGH);
delayMicroseconds(3);
digitalWrite(SDA_Pin, LOW);
delayMicroseconds(3);
digitalWrite(SCL_Pin, LOW);
}
//Daten übertragen
void IIC_send(unsigned char send_data)
{
for (byte mask = 0x01; mask != 0; mask <<= 1) //Jedes Zeichen hat 8 Stellen, die einzeln geprüft werden
{
if (send_data & mask) //Hohes oder niedriges Niveau je nach jedem Bit (0 oder 1) setzen
{
digitalWrite(SDA_Pin, HIGH);
}
else
{
digitalWrite(SDA_Pin, LOW);
}
delayMicroseconds(3);
digitalWrite(SCL_Pin, HIGH); //Taktpin SCL_Pin hochziehen, um die Datenübertragung zu beenden
delayMicroseconds(3);
digitalWrite(SCL_Pin, LOW); //Taktpin SCL_Pin herunterziehen, um SDA-Signale zu ändern
}
}
//Zeichen, dass die Datenübertragung endet
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);
}
/***************Motor läuft****************/
void Car_back()
{
digitalWrite(MR_Ctrl, LOW);
analogWrite(MR_PWM, speeds_R);
digitalWrite(ML_Ctrl, LOW);
analogWrite(ML_PWM, speeds_L);
matrix_display(back); //Bild der Rückwärtsfahrt anzeigen
}
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); //Bild der Vorwärtsfahrt anzeigen
}
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); //Bild der Linkskurve anzeigen
}
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); //Bild der Rechtskurve anzeigen
}
void Car_Stop()
{
digitalWrite(MR_Ctrl, LOW);
analogWrite(MR_PWM, 0);
digitalWrite(ML_Ctrl, LOW);
analogWrite(ML_PWM, 0);
matrix_display(STOP01); //Haltebild anzeigen
}
(5)Testergebnis:
Vor dem Hochladen des Programmcodes muss das Bluetooth-Modul entfernt werden, da sonst das Hochladen des Codes fehlschlägt.
Nachdem der Code erfolgreich hochgeladen wurde, aktivieren Sie den Standortdienst auf Ihrem Gerät und verbinden Sie dann das Bluetooth-Modul.
Sobald das Bluetooth-Modul eingesteckt und eingeschaltet ist und die mobile APP erfolgreich mit dem Bluetooth verbunden ist, können wir die mobile APP verwenden, um den Panzerroboter zu steuern.
Sie können den Roboter auch mit der Fernbedienung steuern.