Inzwischen hat sich ein reger Informationsaustausch via Email mit unserem Kunden Herrn Küster, dem wir die Elektronische Sanduhr verdanken, entwickelt. Dieser hat verletzungsbedingt viel Zeit zum Programmieren seines Micro Controllers und diesmal hat er mir das Spiel „Vier gewinnt“ für zwei Spieler auf dem U 64 LED Matrix Panel CJMCU-8 * 8 Modul mit RGBLEDs geschickt. Für den Versuchsaufbau hatte ich alle Teile zuhause. Also gleich ausprobieren.
Weiter unten finden Sie ein aktuelles Update des Projektes
Verwendete Hardware
Anzahl | Bauteil |
---|---|
1 |
Nano V3.0 mit FT232RL Chip und ATmega328 Oder: Nano V3.0 mit Atmega328 CH340 Alternativ: Micro Controller mit ATMEGA 328P |
1 | U 64 LED Matrix Panel CJMCU-8 * 8 Modul |
Steckbrett (Breadboard), 3 Taster und Kabel (Jumper Wire) |
Für das abgebildete fertige Set benötigen Sie noch:
1 | Gehäuse (Pollin) |
---|---|
1 | Batterie-Pack |
1 | LM2596S DC-DC Netzteil Adapter Step down Modul |
Hier der Schaltplan:
Wie so häufig haben sich die Programmierer der New Yorker Firma Adafruit die Mühe gemacht, uns Anwendern den schwierigsten Teil bei der Hardware-nahen Programmierung abzunehmen und eine Bibliothek für das LED-Display entwickelt und auf github bzw. über den Bibliotheksverwalter bereitgestellt. Deshalb wird diese zu Beginn des Sketches mit
#include <Adafruit_NeoPixel.h> eingefügt. Sofern noch nicht geschehen, bitte im Suchfenster des Bibliotheksverwalters NeoPixel eingeben (bei mir bereits installiert).
NeoPixel ist wie so häufig eine Bibliothek für eine Vielzahl von RGBLED-Displays, die trotz unterschiedlichster Bauformen eines gemeinsam haben: Außer der Spannungsversorgung gibt es eine einzige Datenleitung, die die hintereinander liegenden RGBLEDs ansteuert. Mit den Zeilen
int anzahl = 64;
Adafruit_NeoPixel strip(anzahl, 6, NEO_GRB + NEO_KHZ800);
instanziieren wir deshalb unser 8x8 Modul.
Für das Spielfeld stehen acht Spalten und sieben Zeilen zur Verfügung, denn die oberste Zeile wird jeweils für die Positionierung des nächsten „Spielsteins“ benötigt. Rot beginnt. Die oberste, linke LED leuchtet rot und kann mit den zwei Tastern für links (an GPIO9) und rechts (an GPIO10) entsprechend bewegt werden. Der Zug wird mit dem mittleren Taster (an GPIO8) abgeschlossen. Der Leuchtpunkt wandert so weit nach unten, wie freie Felder verfügbar sind.
Nach der Prüfroutine, ob ein Spieler bereits vier gleichfarbige Felder waagerecht, senkrecht oder diagonal erreicht hat, erscheint oben links eine grüne LED und der zweite Spieler macht seinen Zug in gleicher Weise. Dann sehen wir wieder eine rote LED oben links usw.
Wenn die Prüfroutine zu einem positiven Ergebnis kommt, blinken zunächst die vier RGBLEDs in Reihe, dann das ganze Display in den Farben des Gewinners. Sodann ist der Micro Controller für die nächste Runde bereit. Um das Spiel vorzeitig zu beenden, beide Richtungstaster gleichzeitig drücken.
Hier der Sketch im Ganzen und als Download:
int anzahl = 64;
Adafruit_NeoPixel strip(anzahl, 6, NEO_GRB + NEO_KHZ800);
long rot = strip.Color(5, 0, 0); long gruen = strip.Color(0, 5, 0); long farbe = 0;
byte zw1 = 0; int zw2 = 0; // werden in "Ausgabe Spielfeld" benutzt
byte zw3 = 0; // wird in Absenken der Spielsteine benutzt
byte zw4 = 0; // Startfeld beim Testen
byte zw5 = 0; // Zaelwert innere Schleife, testet 4 Felder
byte zw6 = 0; // Zaelwert mittlere Schleife, testet eine Spalte
byte zw7 = 0; // Zaelwert außere Schleife, testet alle Spalten
byte zw8 = 0; // Gefallene Steine
byte zw9 = 0; // Ausgabe Siegerfarbe
byte zw10 = 0; //Loeschen Anzeige und Spielfeld
byte zw11 = 0; //Blinken Siegersteine
long farbe_1 = 0; long farbe_2 = 0; // Siegerfarbe
byte position_1 = 0; byte position_2 = 0; byte position_3 = 0; byte position_4 = 0; //Position Gewinnersteine
// 0 -> nicht belegt; 1 -> rot; 2 -> gruen
byte spielfeld [64] = {
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
byte rechts = 10; byte links = 9; byte runter = 8; // Nummer der benoetigten Ports
byte richtung = 0 ; // 1 -> rechts; 2 -> links; 3 -> runter
byte status_start = 1; // 1 -> rot; 2 -> gruen
byte status_spiel = 1 ; // 1 -> rot; 2 -> gruen
byte position_0 = 0; // Zeiger fuer Spielfeld
byte test_rot = 0; byte test_gruen = 0; byte sieger = 0; // Benoetigt zum Ermitteln des Siegers
void setup() {
Serial.begin(9600);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
pinMode(rechts, INPUT); digitalWrite (rechts, HIGH);
pinMode(links, INPUT); digitalWrite (links, HIGH);
pinMode(runter, INPUT); digitalWrite (runter, HIGH);
ausgabe_spielfeld ();
}
void loop() {
// Abfrage Tastatur und Sprung in entsprechendes Programm
do {
if (digitalRead(rechts) == LOW && digitalRead(links) == LOW) {
zw8 = 56;
test_spielende ();
};
if ( digitalRead(rechts) == LOW) {
richtung = 1; rechts_0 ();
}
if ( digitalRead(links) == LOW) {
richtung = 2; links_0 ();
}
if ( digitalRead(runter) == LOW) {
if (spielfeld [position_0 + 8] == 0) {
richtung = 3;
runter_0 (); zw8 ++; // Anzahl Durchlaeufe + 1
test_spielende ();
};
}
}
while ( richtung == 0);
richtung = 0; // Loeschender Richtung
ausgabe_spielfeld ();
delay(300);
}
// ------------- Test ob es einen Sieger gibt ------------------
// ---------- Programm wird nach Absenken eines Steines aufgerufen
void test_spielende () {
// ----------- Test diagonal oben ------------
// ----------3 verschaltelte Zyklen ----------
zw4 = 32; // Startfeld beim Testen
sieger = 0; zw7 = 0;
do {
zw6 = 0;
do {
zw5 = 0; // Zaelschleife
test_rot = 0; test_gruen = 0;
// Testen 4 Zellen diegonal oben
do {
if ( spielfeld [zw4] == 1) {
test_rot ++;
};
if ( spielfeld [zw4] == 2) {
test_gruen ++;
};
// Merken der Position beim Sieg
if (sieger == 0) {
if (zw5 == 0) {
position_1 = zw4;
};
if (zw5 == 1) {
position_2 = zw4;
};
if (zw5 == 2) {
position_3 = zw4;
};
if (zw5 == 3) {
position_4 = zw4;
};
};
zw4 = zw4 - 7; zw5++;
}
while (zw5 != 4);
if (test_rot == 4) {
sieger = 1;
} else {
test_rot = 0;
};
if (test_gruen == 4) {
sieger = 2;
} else {
test_gruen = 0;
};
zw4 = zw4 + 36; zw6++;
if (sieger != 0) {
zw6 = 4;
};
}
while ( zw6 != 4);
zw4 = zw4 - 31; zw7++;
if (sieger != 0) {
zw7 = 5;
};
}
while (zw7 != 5);
// ----------- Test diagonal unten -----------
// ----------3 verschachtelte Zyklen ----------
zw4 = 8; // Startfeld beim Testen
zw7 = 0;
do {
zw6 = 0;
do {
zw5 = 0; //zaehlschleife
test_rot = 0; test_gruen = 0;
// Testen 4 Zellen diagonal unten
do {
if ( spielfeld [zw4] == 1) {
test_rot ++;
};
if ( spielfeld [zw4] == 2) {
test_gruen ++;
};
// Merken der Position beim Sieg
if (sieger == 0) {
if (zw5 == 0) {
position_1 = zw4;
};
if (zw5 == 1) {
position_2 = zw4;
};
if (zw5 == 2) {
position_3 = zw4;
};
if (zw5 == 3) {
position_4 = zw4;
};
};
zw4 = zw4 + 9; zw5++;
}
while (zw5 != 4);
if (test_rot == 4) {
sieger = 1;
} else {
test_rot = 0;
};
if (test_gruen == 4) {
sieger = 2;
} else {
test_gruen = 0;
};
zw4 = zw4 - 28; zw6++;
if (sieger != 0) {
zw6 = 4;
};
}
while ( zw6 != 4);
zw4 = zw4 - 31; zw7++;
if (sieger != 0) {
zw7 = 5;
};
}
while (zw7 != 5);
// ------------ Test senkrecht ---------------
// ----------3 verschaltelte Zyklen ----------
zw4 = 8; // Startfeld beim Testen
zw7 = 0;
do {
zw6 = 0;
do {
zw5 = 0; //zaelschleife
test_rot = 0; test_gruen = 0;
// Testen 4 Zellen senkrecht
do {
if ( spielfeld [zw4] == 1) {
test_rot ++;
};
if ( spielfeld [zw4] == 2) {
test_gruen ++;
};
// Merken der Position beim Sieg
if (sieger == 0) {
if (zw5 == 0) {
position_1 = zw4;
};
if (zw5 == 1) {
position_2 = zw4;
};
if (zw5 == 2) {
position_3 = zw4;
};
if (zw5 == 3) {
position_4 = zw4;
};
};
zw4 = zw4 + 8; zw5++;
}
while (zw5 != 4);
if (test_rot == 4) {
sieger = 1;
} else {
test_rot = 0;
};
if (test_gruen == 4) {
sieger = 2;
} else {
test_gruen = 0;
};
zw4 = zw4 - 24; zw6++;
if (sieger != 0) {
zw6 = 4;
};
}
while ( zw6 != 4);
zw4 = zw4 - 31; zw7++;
if (sieger != 0) {
zw7 = 8;
};
}
while (zw7 != 8);
// ------------- Test waagerecht --------------
// ----------3 verschachtelte Zyklen ----------
zw4 = 8; // Startfeld beim Testen
zw7 = 0;
do {
zw6 = 0;
do {
zw5 = 0;
test_rot = 0; test_gruen = 0;
// Testen 4 Zellen waagerecht
do {
if ( spielfeld [zw4] == 1) {
test_rot ++;
};
if ( spielfeld [zw4] == 2) {
test_gruen ++;
};
// Merken der Position beim Sieg
if (sieger == 0) {
if (zw5 == 0) {
position_1 = zw4;
};
if (zw5 == 1) {
position_2 = zw4;
};
if (zw5 == 2) {
position_3 = zw4;
};
if (zw5 == 3) {
position_4 = zw4;
};
};
zw4++; zw5++;
}
while (zw5 != 4);
if (test_rot == 4) {
sieger = 1;
} else {
test_rot = 0;
};
if (test_gruen == 4) {
sieger = 2;
} else {
test_gruen = 0;
};
zw4 = zw4 + 4; zw6++;
if (sieger != 0) {
zw6 = 7;
};
}
while ( zw6 != 7);
zw4 = zw4 - 55; zw7++;
if (sieger != 0) {
zw7 = 5;
};
}
while (zw7 != 5);
// Festlegen Siegerfarben
if (sieger == 1) {
farbe_1 = rot;
farbe_2 = rot;
ende (); // Ausgabe Siegerfarbe Rot
};
if (sieger == 2) {
farbe_1 = gruen;
farbe_2 = gruen;
ende (); // Ausgabe Siegerfarbe Gruen
};
if (zw8 == 56 ) {
farbe_1 = gruen;
farbe_2 = rot;
ende (); // Ausgabe Unentschieden
};
position_1 = 0; position_2 = 0; position_3 = 0; position_4 = 0;
test_rot = 0; test_gruen = 0;
}
// ---------- Ausgabe Siegerfarbe ----------
void ende () {
zw11 = 0;
if (zw8 != 56 ) {
do {
strip.setPixelColor ( position_1, 0, 0, 0);
strip.setPixelColor ( position_2, 0, 0, 0);
strip.setPixelColor ( position_3, 0, 0, 0);
strip.setPixelColor ( position_4, 0, 0, 0);
strip.show(); delay(200);
strip.setPixelColor ( position_1, farbe_1);
strip.setPixelColor ( position_2, farbe_1);
strip.setPixelColor ( position_3, farbe_1);
strip.setPixelColor ( position_4, farbe_1);
strip.show(); delay(200);
zw11++;
}
while ( zw11 != 5);
}
zw9 = 0;
do {
strip.setPixelColor ( zw9, farbe_1); zw9++;
strip.setPixelColor ( zw9, farbe_2); zw9++;
}
while (zw9 != 64);
strip.show();
delay(2000);
// ---------- Vorbereitung neuer Start -----------
zw10 = 0;
do {
strip.setPixelColor ( zw10, 0, 0, 0); spielfeld [zw10] = 0;
zw10++;
}
while ( zw10 != 64);
strip.show();
spielfeld [0] = 1; ausgabe_spielfeld ();
zw1 = 0; zw2 = 0; zw3 = 0; zw4 = 0; zw5 = 0; zw6 = 0; zw7 = 0; zw8 = 0; zw9 = 0; zw10 = 0;
position_1 = 0; position_2 = 0; position_3 = 0; position_4 = 0;
position_0 = 0;
// Wechsel Start-Spielfarbe
farbe_1 = 0; farbe_2 = 0; sieger = 0;
if (status_start == 1) {
farbe = gruen; status_spiel = 2;status_start=2;
spielfeld [0] = 2;
} else {
farbe = rot;
spielfeld [0] = 1; status_spiel = 1;status_start=1;
};
ausgabe_spielfeld ();
}
// ---------- Ein Schritt nach Links ----------
void links_0 () {
if (position_0 != 0) {
spielfeld [position_0] = 0;
position_0--;
spielfeld [ position_0 ] = status_spiel;
};
}
// ---------- Ein Schritt nach Rechts ----------
void rechts_0 () {
if (position_0 != 7) {
spielfeld [position_0] = 0;
position_0++;
spielfeld [ position_0 ] = status_spiel;
};
}
// ---------- Absenken des Spielsteines ----------
void runter_0 () {
zw3 = 0;
do {
if (spielfeld [position_0 + 8] == 0 ) {
spielfeld [position_0] = 0; position_0 = position_0 + 8; spielfeld [position_0] = status_spiel;
zw3++;
ausgabe_spielfeld (); delay(200);
}
else {
zw3 = 7;
}
}
while (zw3 != 7) ;
richtung = 0; position_0 = 0;
if (status_spiel == 1 ) {
status_spiel = 2;
spielfeld [position_0] = 2;
} else {
status_spiel = 1;
spielfeld [position_0] = 1;
};
ausgabe_spielfeld ();
}
// ---------- Ausgabe Spielfeld ----------
void ausgabe_spielfeld () {
zw1 = 0;
do {
zw2 = spielfeld [zw1];
if (zw2 == 0) {
strip.setPixelColor ( zw1, 0, 0, 0);
};
if (zw2 == 1) {
strip.setPixelColor ( zw1, rot);
};
if (zw2 == 2) {
strip.setPixelColor ( zw1, gruen);
};
zw1++;
}
while (zw1 != 64);
strip.show();
}
// HUK Vier_gewinnt_4.4 27.07.2021
Alternativ können Sie Vier gewinnt auch auf dem Wandgehäuse AZ-Touch spielen. Diese Möglichkeit hatte Jörn Weise in seinem Blog am 3. Juli 2021 vorgestellt.
Beide vorgestellte Versionen sind für zwei Spieler ausgelegt und bieten außer der schönen Anzeige die Prüfroutine, ob bereits ein Spieler gewonnen hat. Wer gegen einen spielstarken Computer spielen möchte, dem sei die Version von Al Sweigart auf dem Raspberry Pi empfohlen. Ich muss zu meiner Schande gestehen, dass ich hin und wieder verliere. Also lieber mit dem Sketch von Herrn Küster auf dem U 64 LED Matrix Panel CJMCU-8 * 8 Modul gegen meine Enkeltochter spielen?
UPDATE
Es hat unserem Kunden Herrn Küster keine Ruhe gelassen, bis er auch die Option Spieler gegen Mikrcontroller programmiert hat. Man hat die Wahlmöglichkeit zwischen zwei Spielern (linke Richtungstaste) oder Spieler gegen MCU (rechte Richtungstaste).
- Bei Spieler gegen Spieler wird die Anfangsfarbe bei jedem neuen Spiel gewechselt
- Bei Spieler gegen Rechner hat der Spieler immer rot und beginnt das erste Spiel. Danach beginnt immer der Verlierer.
Hier der Link zum Download
Viel Spaß beim Nachbauen und Spielen.
9 comments
Andreas Wolter
@mircemk: you have to change the order in the array “byte spielfeld 64”. But it’s difficult to change that. The odd rows (if start with 1) should be the same and the even rows are mirrored. Sorry but you hav to search the parts by yourself step by step.
If you find a solution please tell us.
Best regards,
Andreas Wolter
AZ-Delivery Blog
mircemk
Hello,
My matrix is slightly different from yours ie. the diodes light up one by one in a zig zag pattern, and with yours each new row starts on the left. What code modification do I need to do. Do you have an idea?
Thank you
Andreas Wolter
@René: Wenn auch mit den Testsketches die LED nicht eingeschaltet wird, gehe ich davon aus, dass in dem Modul etwas nicht korrekt verlötet ist. SIe könnten versuchen, einen Testsketch so anzupassen, dass genau diese LED eingeschaltet wird. Wenn diese nicht leuchtet, müssen Sie den Support kontaktieren und das Modul austauschen lassen.
Grüße,
Andreas Wolter
AZ-Delivery Blog
René
Hi,
also auch mit einem Test-Sketch/Beispiel Sketch aus der Neopixel Bibliothek bleibt die erste LED dunkel. Keine Ahnung warum das so ist.
Wenn man das 4-Gewinnt Programm startet, dann ist es ja auch so, dass die erste LED dunkel ist und man eben direkt bei der zweiten startet. Also entweder ist das Modul defekt oder irgendein anderer Fehler ist vorhanden.
Habe beides, Board und Matrix bei euch bezogen.
Andreas Wolter
@Renè: haben Sie am Programm änderungen vorgenommen?
Wenn nicht, empfehle ich, ein Testprogramm auf den MC zu laden, um zu sehen, ob die LEDs alle ordnungsgemäß angesprochen werden.
Unser Blogautor Bernd Albrecht hat den Sketch nochmal getestet und er funktioniert so wie er soll.
Ich denke aber, dass wir den Fehler finden werden.
Grüße,
Andreas Wolter
AZ-Delivery Blog
René
Ich habe jetzt einen Fehler im Programm:
Beim Start des Arduinos wird nicht das ganz oben, ganz links befindliche LED rot aktiviert sondert immer ganz oben und eins nach rechts.
Hab versucht das zu finden, aber kann es sein, dass die LED Matrix falsch initialisiert wird?
René
Super, vielen Dank.
Andreas Wolter
Wir haben den Link zum Gehäuse ergänzt. Es ist kein 3D-Modell für den Drucker.
Außerdem hat uns Herr Küster ein Update zur Verfügung gestellt, das Sie jetzt ebenfalls im Beitrag finden.
Mit freundlichen Grüßen,
Andreas Wolter
AZ-Delivery Blog
René
Cooles Projekt, würde ich gern nachbauen aber den Hinweis zum Gehäuse finde ich sehr lustig ;)
Könntet ihr vielleicht erwähnen welches ihr dazu genutzt habt?
Oder noch besser wäre, wenn das eine stl für einen 3D-Druck wäre ;)