Schleifbremse manuell einstellbar


Google-Suche auf MEINE-SCHALTUNG.de :





Online Rechner

Chronik

Dauerkalender


Kugelwerfen

(Serielle Kommunikation zwischen Arduino und ESP32)

LCD Infobild


Am Beispiel eines Spieles, das wir hier einfach „Kugelwerfen“ nennen, wollen wir ein wenig mit der seriellen Kommunikation zwischen zwei verschiedenen Mikrocontrollern experimentieren. Neben einigen wenigen Komponenten besteht die Schaltung aus den Mikrocontrollern Arduino (Nano) und ESP32. Die beiden Mikrocontroller stammen von zwei verschiedenen Herstellern, man kann sie beide jedoch mit der Software „Arduiono IDE“ programmieren.
Das Spiel selbst findet auf einer 8x8 RGB-Matrix statt. Die beiden Spieler stehen sich gegenüber und „werfen“ sich gegenseitig Kugeln zu. Der erste Spieler, hier durch Arduino Nano imitiert, steht links auf dem Spielfeld und spielt mit grünen Bällen. Der andere, ESP32, steht auf der rechten Seite des Spielfeldes und spielt mit roten Kugeln. Jede Spielrunde besteht aus acht Würfen. Jeder Wurf wird mit Pluspunkten belohnt. Wird zum Beispiel eine Kugel auf das Feld (Spalte) vier geworfen, erhält der Spieler vier Punkte. Es gewinnt der Spieler, der mehr Punkte angesammelt hat. Die Besonderheit des Spiels sind die Minuspunkte. Wenn eine Kugel die Kugel des Gegners trifft, wird sie vom Spielfeld verbannt und dem Gegenspieler werden die davor gutgeschriebene Punkte wieder abgezogen.
Die Spielzüge (Kugelwürfe) werden via Zufall bestimmt.

Spielfeld

Das Spielfeld

Arduino Nano

Arduino Nano

Arduino Nano spielt von links nach rechts. Seine Kugeln sind grün. Der Mikrocontroller verfügt über eine serielle Schnittstelle. Die Pins werden mit RX0 und TX1 bezeichnet. Einige andere Modelle von Arduino sind mit mehreren seriellen Schnittstellen ausgestattet.

Arduino

ESP32

ESP32

Der zweite Spieler, der auf der rechten Seite spielt, ist der Mikrocontroller ESP32 (ESP32 Dev KitC V4). Er spielt von rechts nach links mit roten Kugeln. Er kann bei Bedarf gleich drei seriellen Schnittstellen zur Verfügung stellen. In der Schaltung nutzen wie die Pins mit Bezeichnung TX und RX. Dabei handelt es sich um die GPIO1 und GPIO3.

ESP32

LCD Display

LCD Display


I2C Modul

Mit einem 4-Zeilen und 20-Zeichen LCDisplay wird signalisiert, welcher der beiden Spieler mit dem nächsten Wurf an der Reihe ist. Zusätzlich werden die aktuellen Koordinaten des Wurfs und die Punkte der Spieler angezeigt. Das Display kommuniziert mit dem Mikrocontroller über den I2C Bus. Dies ermöglicht das FC-113 Modul, das an das Display hinten angelötet ist. Auf dem Modul befindet sich ein Potentiometer, mit dem die Displayhelligkeit eingestellt werden kann.

Display

Spielfeld

Spielfeld

Als Spielfeld kommt eine RGB-Matrix zum Einsatz. Auf dem so definierten Spielfeld werden die Kugel angezeigt, sodass man durchgehend die aktuelle Situation erblicken kann. Wie bereits erwähnt spielt Arduino mit Grün von links nach rechts, ESP32 mit Rot von rechts nach links.

RGB Matrix

Levelkonverter

Level/Pegelkonverter

Die beiden Mikrocontroller-Module arbeiten mit unterschiedlichen Spannungen. Arduino Betriebsspannung beträgt 5VDC, der ESP32 arbeitet mit 3,3VDC. An dieser Stelle soll man aufpassen. Wenn auf einen Pin von ESP32 eine Spannung von 5VDC gelangt, kann er beschädigt werden. Als Abhilfe werden in solchen Fällen Levelkonverter eingesetzt. Sie passen die Spannungen entsprechend an.

Spannungsregler

Spannungsregler

Der Arduino wird mit einer externen Spannung von 9VDC eingespeist. Mit einem Spannungsregler wird die hohe Eingangsspannung auf 5VDC heruntergeregelt. Mit ihr werden dann das ESP32-Modul, die RGB-Matrix und das Display eingespeist. Der Hauptbestandteil des Moduls ist der Regler LM2596.

Spannungsregler

Der Schaltplan

Schaltplan

Schaltplan

Auf dem Schaltplan findet sich neben diversen Komponenten auch ein Kondensator (C1). Die Aufgabe des Kondensators ist dafür zu sorgen, dass bei jedem Spiel (vor allem beim Einschalten) die Spieler jeweils mit anderen Zufallszahlen agieren. Die Zufallsgeneratoren beider Mikrocontroller arbeiten zwar gut, starten jedoch nach Anlegen der Spannung immer mit den gleichen Zufallszahlen. Mit der Funktion randomSeed(Wert) kann man das Problem umgehen. Die Funktion benötigt allerdings einen Startwert, der möglichst bei jedem Einschalten einen anderen Wert hat. Diese Aufgabe übernimmt der Kondensator. Er lädt sich über den Widerstand R3 auf. Die Spannung am Kondensator kann maximal 3,3V erreichen. Bei Betätigen des Tasters S1 erfolgt eine Entladung des Kondensators über den Widerstand R4. Da man nicht exakt abschätzen kann, wie lange ein Mensch den Taster betätigt, baut sich auf dem Kondensator eine Spannung von unbekanntem Wert auf. Sie wird den analogen Eingängen der beiden Mikrocontroller zugeführt. Die Werte werden im Programm ausgelesen und der Funktion randomSeed() zugeführt.

Die Testschaltung

Testschaltung

Testschaltung

Das Programm (ESP32)

// **********************************************************************************************
// Kugelwerfen
// Serielle Komminikation zwischen Arduino und ESP32
// ESP32 Sketch
// Arduino IDE 1.8.19
// **********************************************************************************************
int LED_PIN = 18;                                               // Leuchtdiode
int Reihe, Spalte;                                              // Mein Wurf via Zufalsszahlen
String Mein_Wurf;                                               // Mein Wurf als String
String Sende_String, Antwort;                                   // Kommunikation - Variablen
unsigned long Wartezeit = 500;                                  // Antwortzeit
unsigned long Zeit_Start;                                       // Zeitpunkt festhalten
int Spielfeld_Reihen [8];                                       // Reihen 

void setup() {

    Serial.begin (9600);                                        // Serielle Schnittstelle
    pinMode (LED_PIN, OUTPUT);                                  // Status Leuchtdiode
}
// **********************************************************************************************

void loop() {                                                   // Hauptprogramm

    if (Serial.available() > 0) {                               // Daten vom Partner vorhanden?
        Sende_String = Serial.readStringUntil('*');             // Ja. Auslesen bis "*"
        digitalWrite (LED_PIN, HIGH);                           // Leuchtdiode Ein
        if (Sende_String == "Start") {                          // Neues Spiel, alte Variablen 
            for (int i=0; i<8; i++) {                           // 8 Reihen
                Spielfeld_Reihen [i] = 0;                       // Neues Spiel = alles nullen
            }
            Sende_String = "Neuer_Wurf";                        // Mein Wurf vorbereiten
            randomSeed(analogRead(32));                         // immer andere Zufallszahlen
        }
    }

    if (Sende_String == "Neuer_Wurf") {                         // Mein Wurf
        bool Mein_Wurf_OK = false;
        Spalte = random(1,7);                                   // Spalte via Zufall
        while (Mein_Wurf_OK != true) {
            Reihe = random(0,8);                                // Reihe via Zufall
            if (Spielfeld_Reihen [Reihe] == 0) {                // Reihen dürfen sich nicht
                Spielfeld_Reihen [Reihe] = 1;                   // wiederholen
                Mein_Wurf_OK = true;
            }
        }
        Mein_Wurf = String(Reihe) + String(Spalte) + "*";       // Mein Wurf als String
        Antwort = "";
        while (Antwort != "OK") {                               // Senden bis Bestätigung
            Serial.print (Mein_Wurf);                           // Meinen Wurf senden
            delay (500);                                        // Wartezeit
            if (Serial.available() > 0) {                       // Bestätigung angekommen?
                Zeit_Start = millis();
                while ((Antwort != "OK") or ((Zeit_Start + Wartezeit) > millis())) {
                    Sende_String = Serial.readStringUntil('*'); 
                    if (Sende_String == "OK") {                 // Bestätigung erhalten
                        Antwort = "OK";                         // Alles wiederholen
                        digitalWrite (LED_PIN, LOW);            // Leuchtdiode Aus
                    }
                }
            }
        }        
    }
} 
// **********************************************************************************************      
  

Der Spieler mit roten Kugeln (ESP32) hat nicht allzu viel zu tun. Das Programm überwacht die Schnittstelle und wartet auf eine Anforderung von Arduino. Zwei Varianten sind hier möglich. Er muss einen neuen Kugelwurf generieren, wenn er die Nachricht „Start“ oder „Neuer_Wurf“ erhält. Beide Anforderungen führen zu einem neuen Wurf. Bei der Nachricht „Start“ werden zusätzlich die Spielfeldvariablen genullt. Nachdem via Zufall neue Zufallszahlen für Reihe und Spalte generiert und verschickt wurden, wartet ESP32 auf eine Bestätigung mit „OK“ von Arduino. Sobald diese eintrifft, verlässt das Programm die interne Warteschleife und wartet auf eine weitere Anforderung zum neuen Wurf.


Das Programm (Arduino Nano)

// **********************************************************************************************
// Kugelwerfen
// Serielle Komminikation zwischen Arduino und ESP32
// Arduino Sketch
// Arduino IDE 1.8.19
// **********************************************************************************************

#define LED_PIN   6                                               // DIN RGB Matrix
#define LED_COUNT 64                                              // Pixel Anzahl

#include <Adafruit_NeoPixel.h>                                    // Bibliothek Matrix
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

#include <LiquidCrystal_I2C.h>                                    // Bibliothek Display I2C-Bus
LiquidCrystal_I2C lcd(0x27,20,4);                                 // Display definieren

int Start_Taster = 2;                                             // Taster Spiel Start
int Status_LED = 9;                                               // Status Leuchtdiode (mein Wurf)
int Spiel;                                                        // 0=kein Spiel / 1=Spiel
String Sende_String, Antwort;                                     // Sende/Empf-Variablen
int Reihe, Spalte;                                                // Spielfeld-Koordinaten
unsigned long Wartezeit = 500;                                    // Antwortzeit
unsigned long Zeit_Start;                                         // Zeitpunkt festhalten
int Wurf_Nr;                                                      // 8 Würfe pro Spiel
int Spieler;                                                      // 1-ESP32 / 2-Arduino
int Punkte_ESP32 [8], Punkte_Arduino [8];                         // Punkte pro Reihe
int Gesamtpunkte_ESP32, Gesamtpunkte_Arduino;

void setup() {
  
    strip.begin();                                                // Matrix
    strip.show();            
    strip.setBrightness(40);                                       // Matrix Helligkeit
    lcd.init();                                                   // Display initialisieren 
    lcd.backlight();                                              // LCD Hintergrundbeleuchtung
    pinMode (Start_Taster, INPUT_PULLUP);                         // Start Taster als Pullup
    pinMode(Status_LED, OUTPUT);                                  // Status LED             
    Serial.begin(9600);                                           // Serielle Kommunikation
    lcd.setCursor(4,0);   lcd.print("Kugelwerfen");
}
// **********************************************************************************************

void loop() {

    // SPIEL BEGINN ----------------------------------------------
    Sende_String ="Neuer_Wurf*";
    if (digitalRead(Start_Taster) == LOW) {                       // Spiel Start
        Spiel = 1;                                                // Spiel läuft
        Wurf_Nr = 0;
        Gesamtpunkte_ESP32 = Gesamtpunkte_Arduino = 0;
        lcd.clear();
        Sende_String = "Start*";                                  // Start an den Partner
        for (int i=0; i<8; i++) {   
            Punkte_ESP32 [i] = Punkte_Arduino [i] = 0;            // Neues Spiel = alles nullen  
        }
        randomSeed(analogRead(A0));                               // Startwert Zufallsgenerator
        Spielfeld_Start ();
    }

    // ESP32 IST AM ZUG ------------------------------------------
    if (Spiel == 1) {                                             // Spiel gestartet?
        Spieler = 1;                                              // ESP32 ist dran
        Antwort = "";
        Reihe = Spalte = -1;
        LCD_Infotafel ();                                         // Anzeige aktualisieren
        while (Antwort != "OK") {
            Serial.print (Sende_String);                          // Partner ist dran
            delay (500);
            if (Serial.available() > 0) {                         // Partner sendet seinen Wurf
                Zeit_Start = millis();           
                while ((Antwort != "OK") or ((Zeit_Start + Wartezeit) > millis())) {
                    Sende_String = Serial.readStringUntil('*');
                    if (Sende_String.length() == 2) {                        
                        String Ziffer = Sende_String.substring(0,1);
                        Reihe = Ziffer.toInt();
                        Ziffer = Sende_String.substring(1,2);
                        Spalte = Ziffer.toInt();
                        Antwort = "OK";
                        Serial.print ("OK*");                     // Bestätigung
                        Punkte_berechnen();
                        LCD_Infotafel ();                         // Anzeige aktualisieren
                        Wurf_auf_Matrix_Zeigen ();
                    }
                }
            }
        }
    }

    // ARDUINO WURF ----------------------------------------------
    if (Spiel == 1) {
        Spieler = 2;
        Reihe = Spalte = -1;
        LCD_Infotafel ();
        digitalWrite (Status_LED, HIGH);                          // Status LED an
        delay(1000);                                              // für LED
        digitalWrite (Status_LED, LOW);

        bool Mein_Wurf_OK = false;
        Spalte = random(1,7);                                     // Spalte via Zufall
        while (Mein_Wurf_OK != true) {
            Reihe = random(0,8);                                  // Reihe via Zufall
            if (Punkte_Arduino [Reihe] == 0) {                    // Reihen dürfen sich nicht
                Mein_Wurf_OK = true;                              // wiederholen
            }
        }
        Punkte_berechnen();
        LCD_Infotafel ();
        Wurf_auf_Matrix_Zeigen ();
        Wurf_Nr++;
    }

    if (Wurf_Nr == 8) {                                            // Spiel zu Ende
      Spiel = 0;
      lcd.setCursor(0,1);   lcd.print("--------------------");
      lcd.setCursor(0,2);   lcd.print("Endstand:           ");
    }
}
// **********************************************************************************************

void LCD_Infotafel () {                                           // Unterprogramm LCD-Anzeige
  
    lcd.setCursor(4,0);   lcd.print("Kugelwerfen");               // Titel
    lcd.setCursor(1,1);
    if (Spieler == 1) {                                           // Spieler Anzeige
        lcd.print("ESP32 ist dran  ");                        
    } else {
        lcd.print("Arduino ist dran");
    }
    lcd.setCursor(1,2);   lcd.print("Reihe: ");   lcd.print(Reihe+1);         // Wurf aktuell
    lcd.setCursor(10,2);  lcd.print("Spalte: ");  lcd.print(Spalte+1);

    lcd.setCursor(0,3);   lcd.print("Arduino:");                  // Aktueller Punktestand
    lcd.setCursor(8,3);   lcd.print("  ");        
    lcd.setCursor(8,3);   lcd.print(Gesamtpunkte_Arduino);       
    lcd.setCursor(12,3);  lcd.print("ESP32:");    
    lcd.setCursor(18,8);  lcd.print("  ");        
    lcd.setCursor(18,8);  lcd.print(Gesamtpunkte_ESP32);
    delay (1000);
}
// **********************************************************************************************

void Spielfeld_Start () {                                         // ESP32 spielt rechts
    strip.clear();                                                // Arduino spielt links
    for (int i=0; i<8; i++) {
        strip.setPixelColor(i*8, 0, 255, 0);                      // GRÜN für Arduino
        strip.setPixelColor((i*8)+7, 255, 0, 0);                  // BLAU für ESP32
    }
    strip.show();
}
// **********************************************************************************************

void Wurf_auf_Matrix_Zeigen () {                      
              
    if (Spieler == 1) {                                           // Wurfanzeige ESP32
        int i;
        for (i=0; i<(Spalte+1); i++) {
            for (int j=1; j<5; j++) {
                strip.setPixelColor(((Reihe+1)*8-(i+1)), 255, 0, 0);
                strip.show();
                delay (100);
                strip.setPixelColor(((Reihe+1)*8-(i+1)), 0, 0, 0);
                strip.show();
                delay (100);
            }
        }
        strip.setPixelColor(((Reihe+1)*8-i), 255, 0, 0);
        strip.show();
    }

    if (Spieler == 2) {                                           // Wurfanzeige Arduino
        int i;
        for (i=0; i<(Spalte+1); i++) {
            for (int j=1; j<5; j++) {
                strip.setPixelColor((Reihe*8+i), 0, 255, 0);
                strip.show();
                delay (100);
                strip.setPixelColor((Reihe*8+i), 0, 0, 0);
                strip.show();
                delay (100);
            }
        }
        strip.setPixelColor((Reihe*8+i-1), 0, 255, 0);
        strip.show();
    }                                
}
// **********************************************************************************************

void Punkte_berechnen() {

    if (Spieler == 1) {                                           // Punkte ESP32   
        Punkte_ESP32 [Reihe] = Spalte + 1;
        if (Punkte_Arduino [Reihe] != 0) {
            if ((8-Spalte) <= Punkte_Arduino [Reihe]) {
                Gesamtpunkte_Arduino = Gesamtpunkte_Arduino - Punkte_Arduino [Reihe];               
            }
        }
        Gesamtpunkte_ESP32 = Gesamtpunkte_ESP32 + Punkte_ESP32 [Reihe];
    }

    if (Spieler == 2) {                                           // Punkte Arduino  
        Punkte_Arduino [Reihe] = Spalte + 1;
        if (Punkte_ESP32 [Reihe] != 0) {
            if ((8-Punkte_ESP32 [Reihe]) <= Spalte) {
                Gesamtpunkte_ESP32 = Gesamtpunkte_ESP32 - Punkte_ESP32 [Reihe];
            }
        }
        Gesamtpunkte_Arduino = Gesamtpunkte_Arduino + Punkte_Arduino [Reihe];
    }
}
// **********************************************************************************************       
  

Der gesamte Spielverlauf ist nicht kompliziert. Arduino fordert sein Gegenüber mit „Start“ (beim Spielanfang) oder „Neuer_Wurf“ (beim laufenden Spiel) dazu auf, einen neuen Wurf zu absolvieren. Sobald er die Koordinaten für den Wurf erhalten hat, bestätigt er dies mit „OK“. Anschließend, nachdem der Wurf seines Gegners auf der Matrix angezeigt wurde, generiert er seine eigenen Werte für Reihe und Spalte und führt auf der Matrix die Bewegung durch. Das Spiel endet, nachdem beide Spieler alle ihre Kugel geworfen haben.
Für die serielle Übertragung werden in beiden Programme nur wenige Funktionen verwendet:
- Serial.begin(9600) – die Funktion initialisiert die serielle Schnittstelle mit Baudrate von 9600.
- Serial.print (Sende_String) : Mit Serial.print() werden Daten verschickt
- Serial.available() : Mit der Funktion wird die Schnittstelle abgefragt, ob neue Daten vorhanden sind. Serial.available() > 0 liefert den Hinweis, dass neue Nachrichten angekommen sind und gelsen werden können.
- Serial.readStringUntil() : Mit der Funktion wird die Schnittstelle ausgelesen und zwar bis zu dem Zeichen, das in Klammern angegeben wird. Serial.readStringUntil('*') liest die Daten bis zum Zeichen „*“. Das Zeichen selbst wird nicht berücksichtigt.


Kurzvideo

Kurzvideo


Weitere Themen:



Google-Suche auf MEINE-SCHALTUNG.de :


Home Impressum Datenschutz