Ich hatte mal den funduino Sketch mit einem 1,3" OLED Display getestet und ein Gehäuse gezeichnet.

Lektion 10 - Arrays / würfeln von 1-6
-
-
Zumindest ist mein untenstehendes Beispiel dahingehend schlecht designed, dass die Würfelklasse die Ausgabe über LEDs fest verdrahtet hat. Eigentlich müsste es noch ein Interface geben, welches das Würfeln und die Ausgabe des Würfelergebnisses trennt und eine Schnittstelle zur Ausgabe (LEDs, OLED usw.) darstellt.
-
Kai, es ist mir doch klar, dass es Dir nicht um Spielereien ging, sondern um spezielle Lösungsansätze bei der Programmierung.
Deshalb auch mein Hinweis, wenn man den Würfel als Spielwürfel nutzen möchte.
Bei fast allen Programmierbeispielen werden als Hardware LED's und Taster eingesetzt. Ich dachte, bei diesem Beispiel könnte man auch mal abweichen.
Den funduino Würfel wollte ich mal mit Display und Gehäuse für meine Enkel basteln und habe mich deshalb gefreut, dass hier schon das Thema würfeln behandelt wurde.
-
Zumindest ist der „funduino“ Würfel wesentlich simpler. Und die Programmierung desselben, würde ich mir nicht als Vorbild nehmen
Es geht hier auch gar nicht um originell oder nicht originell. Es geht um Lösungsansätze und ums lernen.
Sich mit bestimmten Problematiken beschäftigen und verschiedene Varianten durchspielen. So lerne ich…
andere ja möglicherweise such.
-
Hallo Kai, wie immer sehr schön.
Ich persönlich, finde den Würfel wie in diesem .PDF beschrieben mit OLED Display viel origineller. Wenn der Würfel auch als Spielwürfel benutzt werden soll.
Wuerfeln_OLED.pdf (funduino.de)
Grüße, Franz
-
Hallo zusammen,
ich habe auch mal mit René's Aufgabe herumgespielt und versucht elektronische Würfel zu "bauen" die ohne die delay() Anweisung auskommen. So kann LED animiert, nicht blockierend gewürfelt werden. Der Ansatz ist C++ OOP mäßig.
Das Programm besteht im Wesentlichen aus drei Klassen. Zuerst einer Buttonklasse, welche das entprellte Abfragen eines Tasters erledigt. Eine Timerklasse die den Umgang mit millis() erleichtert. Diese ist notwendig um ein "nicht blockierndes" Würfeln (ohne delay) zu ermöglichen. Zu Guter Letzt gibt es eine Würfelklasse, welche den Würfelvorgang abbildet.
Durch das nicht blockierende Würfeln ist es möglich, dass mehrere LED Würfel quasi gleichzeitig animiert dargestellt werden können. In einer For-Schleife mit der delay() Anweisung wäre das unmöglich. Bei einem Würfel ist das egal. Wenn man aber z.B. ein elektronisches Kniffel Spiel erstellen möchte, welches fünf Würfeln benötigt, wäre es doch ziemlich störend, wenn nur ein Würfel nach dem anderen seine Würfelsequenz "abfahren" würde.
Durch den (notwendigen) Verzicht auf eine For-Schleife mit delay(), ist es bei dieser Implementierung erforderlich, nach jedem Würfelvorgang den/die Würfel mit der Methode <objektname>.rollReset(); zurückzusetzen. Dabei wird ein interner Zähler der für die LED Animation benötigt wird, wieder auf Null zurück gesetzt.
Das Beispiel ist für zwei Würfel ausgelegt. Die Würfelobjekte sind in einem Array abgelegt. Durch den OOP Ansatz ist das Beispiel im Prinzip (genug Pins vorausgesetzt) mit wenig Programmieraufwand um Würfel erweiterbar. Im Prinzip muss nur das Array um weitere Würfelobjekte erweitert werden.
Es ist auch gut vorstellbar, das Ganze mit Portexpandern zu betreiben. Dafür ist aber etwas Umbau notwendig.
Für Vorschläge, wie man das Würfelobjekt optimaler aufbauen kann bin ich offen und würde mich darüber freuen...
C- #include <digitalWriteFast.h>
- ////////////////////////////////////////////////////
- // Global Definitions
- ////////////////////////////////////////////////////
- //
- // Class for a debounced push button input.
- //
- class Button {
- public:
- Button(const uint8_t pin, bool as = LOW) : pin {pin}, activeState {as} {}
- void begin() {
- buttonState = !activeState;
- lastButtonState = activeState;
- pinMode(pin, (activeState) ? INPUT : INPUT_PULLUP);
- }
- bool tick() {
- bool reading = digitalReadFast(pin);
- if (reading != lastButtonState) { lastDebounceTime_ms = millis(); }
- if ((millis() - lastDebounceTime_ms) > debounceDelay_ms) {
- if (reading != buttonState) {
- buttonState = reading;
- if (buttonState == HIGH) { return true; } // Button is pressed even after the debounce time has expired
- }
- }
- lastButtonState = reading;
- return false;
- }
- private:
- const uint8_t pin;
- bool activeState;
- bool buttonState {HIGH};
- bool lastButtonState {LOW};
- uint32_t lastDebounceTime_ms {0};
- uint32_t debounceDelay_ms {50};
- };
- //
- // Timer for non-blocking delays
- //
- class Timer {
- public:
- void start() { timeStamp = millis(); }
- bool operator()(const unsigned long duration) const { return (millis() - timeStamp >= duration) ? true : false; }
- private:
- unsigned long timeStamp {0};
- };
- //
- // LED Pin Data
- //
- struct Led {
- const uint8_t pinNr;
- };
- //
- // A dice class.
- //
- class Dice {
- public:
- template <size_t MAX> Dice(const Led (&pLeds)[MAX]) : pLeds {pLeds}, ledCount {MAX} {}
- void begin() {
- for (uint8_t i = 0; i < ledCount; ++i) { pinMode(pLeds[i].pinNr, OUTPUT); }
- }
- bool roll();
- void rollReset() { rollCounter = 0; }
- void lightLeds(uint8_t pips) {
- for (uint8_t i = 0; i < NIBBLE; ++i) { digitalWriteFast(pLeds[i].pinNr, ((BITPATTERN[pips] >> i) & 0x01)); }
- }
- uint8_t getRollPips() const { return pips; }
- uint8_t getBitPattern() const { return BITPATTERN[pips]; }
- private:
- uint8_t getRandom(uint8_t, uint8_t);
- const Led *pLeds;
- const uint8_t ledCount;
- uint8_t pips;
- Timer delay_ms;
- uint8_t rollCounter {0};
- uint8_t rollIterations {20};
- uint16_t wait_ms;
- bool isWaiting {true};
- static constexpr uint8_t NIBBLE {4};
- static constexpr uint8_t MIN_PIPS {1};
- static constexpr uint8_t MAX_PIPS {6};
- static const uint8_t BITPATTERN[7];
- // static constexpr uint8_t BITPATTERN[7] {0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x0E}; // Version > C++11 necessary
- };
- bool Dice::roll() {
- if (rollCounter > rollIterations) { rollCounter = rollIterations; }
- if (isWaiting) {
- if (delay_ms(wait_ms)) {
- pips = random(MIN_PIPS, MAX_PIPS + 1);
- lightLeds(pips);
- isWaiting = false;
- rollCounter++;
- }
- } else {
- delay_ms.start();
- wait_ms = random(1, 200);
- isWaiting = true;
- }
- return (rollCounter == rollIterations) ? true : false;
- }
- //
- // Create a random number.
- //
- uint8_t Dice::getRandom(uint8_t from, uint8_t to) {
- uint16_t seed = analogRead(7);
- seed += analogRead(1);
- seed *= analogRead(2);
- seed *= analogRead(3);
- seed *= analogRead(4);
- randomSeed(seed);
- return random(from, to);
- }
- // Bit sequences - sequence in which the pins must be controlled
- // in order to display the corresponding number of pips via LED.
- // 0 7 L 6 L 5 L 4 L 0000 All off
- // 1 = 7 L 6 L 5 L 4 H 0001
- // 2 = 7 L 6 L 5 H 4 L 0010
- // 3 = 7 L 6 L 5 H 4 H 0011
- // 4 = 7 L 6 H 5 H 4 L 0110
- // 5 = 7 L 6 H 5 H 4 H 0111
- // 6 = 7 H 6 H 5 H 4 L 1110
- // 0 1 2 3 4 5 6
- const uint8_t Dice::BITPATTERN[7] {0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x0E};
- ////////////////////////////////////////////////////
- // Global literals / variables
- ////////////////////////////////////////////////////
- // The order with which the pin numbers are initialized in LedArray
- // must be the reverse of the order of the bit sequences.
- constexpr Led ledArray_d0[] {{4}, {5}, {6}, {7}}; // Pin numbers to which the LEDs are connected.
- constexpr Led ledArray_d1[] {{8}, {9}, {10}, {11}}; // Pin numbers to which the LEDs are connected.
- Button btn {2};
- Dice dices[] {{ledArray_d0}, {ledArray_d1}};
- ////////////////////////////////////////////////////
- // Functions
- ////////////////////////////////////////////////////
- //
- // Auxiliary function for displaying bit sequences.
- //
- void printBits(uint8_t value) {
- for (uint8_t i = 0; i < 4; ++i) { Serial.print(((value << i) & 0x08) ? 1 : 0); }
- }
- //
- // Does the dice roll.
- //
- template <size_t MAX> void rollAllDices(Dice (&dice)[MAX]) {
- bool isFinished {false};
- while (!isFinished) {
- for (uint8_t i = 0; i < MAX; i++) { isFinished = dice[i].roll(); }
- }
- for (uint8_t i = 0; i < MAX; i++) {
- // Serial.print("Dice pips: ");
- // printBits(dice[i].getBitPattern()); Serial.print(" ");
- // Serial.println(dice[i].getRollPips());
- dice[i].rollReset();
- }
- }
- ////////////////////////////////////////////////////
- // Main program
- ////////////////////////////////////////////////////
- void setup() {
- Serial.begin(115200);
- btn.begin();
- for (auto &dice : dices) { dice.begin(); }
- // Do animation
- rollAllDices(dices);
- // light off leds
- for (auto &dice : dices) { dice.lightLeds(0); }
- }
- void loop() {
- if (btn.tick()) { rollAllDices(dices); }
- }
Hier gibt es auch eine WokWi Simulation der zwei Würfel: https://wokwi.com/projects/359113191790579713
Nicht wundern, das "random" funktioniert in der Simulation nicht wirklich. Bei einem echten Aufbau sollten die gewürfelten Werte wesentlich zufälliger sein als in der Simulation (dort sind sie es überhaupt nicht bzw. allenfalls der erste Wert). Das Programm startet mit einer Würfelanimation. Die Leds werden aber wieder gelöscht wenn sie beendet ist. Im Anschluss löst ein Tasterdruck einen Würfelvorgang aus und die Augenzahl wird am Ende durch die LEDs angezeigt.
-
Ok, unter Zuhilfenahme von Arrays ist das Programm um einiges kürzer.
Was mich etwas gestört hat, war die sehr langsame Ausgabe der Würfelaugen, über die "zeige-Funktion", beim würfeln. Das war gefühlt 1 Sekunde und man hätte das Ergebnis beeinflussen können. Die Stelle am Code hab ich etwas angepasst. Es gibt jetzt eine eigene "wuerfeln-Funktion". Dort lasse ich die LEDs wahllos blinken und der "random-delay" greift auch wieder.
Code- /*
- * elektronischer Würfel
- *
- * Version 2.0
- *
- * Rayko Schmidt
- * www.dreid-gfx.de
- * Der Code ist Public Domain und darf frei verwendet werden.
- *
- */
- const int wuerfelnPIN = 11;
- const int Anzahl = 7;
- const int LEDPin[Anzahl] = {2,3,4,5,6,7,8};
- int Wert = 0; //der gewuerfelte Wert
- // Matrix Wuerfelaugen
- int anzeige[][Anzahl] = {
- {0,0,0,0,0,0,0},
- {0,0,0,1,0,0,0},
- {1,0,0,0,0,0,1},
- {0,1,0,1,0,1,0},
- {1,1,0,0,0,1,1},
- {1,1,0,1,0,1,1},
- {1,1,1,0,1,1,1},
- };
- int neueZeile = 0; // zum reseten der seriellen Ausgabe
- // Funktion zum Darstellen der Wuerfelaugen
- void zeige(int Wert) {
- for ( int i=0; i < Anzahl; i++ ) {
- digitalWrite(LEDPin[i],anzeige[Wert][i]);
- }
- }
- // Funktion zum wuerfeln, beim wuerfeln werden zufaellig LEDs ein und ausgeschaltet
- void wuerfeln() {
- Wert = (random(1,7));
- digitalWrite(LEDPin[(random(0,8))], HIGH);
- delay(random(1,30)); // ein zufaelliger Delay beim wuerfeln
- digitalWrite(LEDPin[(random(0,8))], LOW);
- Serial.print(Wert);
- }
- // Startroutine alle LEDs blinken lassen
- void Start() {
- zeige(0);
- delay(300);
- for ( int i=0; i < Anzahl; i++ ) {
- digitalWrite(LEDPin[i],HIGH);
- }
- delay(300);
- zeige(0);
- delay(300);
- for ( int i=0; i < Anzahl; i++ ) {
- digitalWrite(LEDPin[i],HIGH);
- }
- delay(300);
- zeige(0);
- }
- void setup() {
- Serial.begin(9600);
- Serial.println();
- Serial.println("bitte wuerfeln");
- for ( int i=0; i < Anzahl; i++ ) {
- pinMode(LEDPin[i], OUTPUT);
- }
- pinMode(wuerfelnPIN, INPUT_PULLUP);
- Start();
- randomSeed(analogRead(A0)); // damit der Zufallswert auch zufaellig ist
- }
- void loop() {
- if ( digitalRead(wuerfelnPIN) == LOW ) {
- neueZeile = 1;
- wuerfeln();
- } else {
- if ( neueZeile ==1 ) {
- Serial.println();
- neueZeile = 0;
- zeige(Wert);
- }
- }
- }
Video:
-
for each Konstrukte gibt es in C nicht. Man müsste das etwas mühsam nachbilden.
In deinem einfachen Fall wäre das kein Problem. Ich verwende hier der Bequemlichkeit halber das String - Objekt. Reine C - Programmierer würden hier eher mit CHAR - Arrays arbeiten. Dann ist aber die kombinierte Ausgabe innerhalb der Schleife wesentlich komplizierter.
CodeGruss
René
-
Danke für eure Antworten. Das hab ich mir soweit auch schon gedacht.
Wenn ich auf Arbeit mit der Powershell, von Windows,
Scripte programmiere, gibt es die Möglichkeit Variablen in einer Schleife zu kombinieren.
Gibt es sowas auf dem Arduino auch?
Grüße
Rayko
-
Zitat
Beim initialisieren der Eingänge „pinMode(LED1, OUTPUT);“ gibt es da eine Möglichkeit mit einer Schleife zu arbeiten und die Ziffer hinter LED durch eine Variable zu ersetzen (auch in der Start-Funktion). Sodass beim durchlaufen der Schleife die Ports LED1, LED2, LED3... angesprochen werden? Oder könnte man das über ein Array lösen? Wie verbinde ich dann aber die Variable mit dem entsprechenden Port?
LED1..LED7 sind Variablennamen bzw. Konstanten, die letztendlich auch nur Zahlen enthalten. Von daher kann pinMode ohne weiteres per Schleife aufgerufen werden. LED1..LED7 sind dazu nicht notwendig, sondern es wird der Zählerwert einer Schleife verwendet, der bei jedem Schleifendurchlauf, eine um eins erhöhte Pinnummer repräsentiert. Es ist nicht notwendig die Ziffer hinter LED durch eine Variable zu ersetzen. Es geht ja nicht darum einen Textstring zu bearbeiten. Die Konstanten LEDx werden u.a. der besseren Lesbarkeit des Quellcodes wegen angelegt.
Natürlich kann man die Pinnummern auch in einem Array abspeichern und damit die Pins schalten. Der Zählerwert der Schleife ist dann der Index auf die Arrayposition, in der die Pinnummer gespeichert ist. Im Falle eines Arrays können auch Pins berücksichtigt werden, die nicht aufeinanderfolgend sind.
Gruß Kai
-
Hallo Rayko,
dein Weg ist korrekt, er erfordert einfach etwas unnötige Tipparbeit.
Da die LEDs an aufeinanderfolgenen Pins angeschlossen sind, könnte mit einer Schleife gearbeitet werden. Damit liessen sich setup() und start() vereinfachen.
Sobald die Pins nicht mehr lückenlos nacheinender kommen, geht das mit der Schleife nicht mehr direkt. In diesem Fall muss eine Umsetzung von LED-Nummer zu Pin-Nummer mit Hilfe eines Arrays gemacht werden. Ein einfaches Beispiel mit 5 LEDs findest du im Lösungsvideo. Eine Stufe weiter geht dann Lektion 11.
Gruss
René
-
Hallo,
ein paar kurze Infos zu mir. Mein Name ist Rayko, 46 Jahre alt, gelernter Schlosser und danach Umschulung zum Büroinformationselektroniker. Grundkenntnisse im Programmieren und Elektronik sind vorhanden, müssen aber immer mal wieder aufgefrischt werden, da ich nur selten zum Anwenden komme.
Meine Hobbys sind 3D-Design, 3D-Druck, Fotografieren, digitale Bildbearbeitung und etwas programmieren. Im Bereich 3D-Druck verwende ich Arduinos um kleine Projekte umzusetzen.
Für die Projekte werde ich jetzt öfters mal hier vorbeischauen. Der Youtube-Kanal und das Forum vom Hobbyelektroniker find ich super. Beide haben mir schon sehr geholfen. Danke dafür!
Ich habe mich heute mal an die Aufgabe mit dem elektronischen Würfel gesetzt. Da die Aufgabe am Ende der Array-Lektion kam, denke ich mal, es sollten Arrays verwendet werden. Bei mir hab ich aber keine verwendet.
Soweit bin ich mit der Funktion der Schaltung und meinem Code zufrieden. Die vorherigen Lektionen haben mir dabei auf jeden Fall geholfen. Ich würde euch trotzdem bitten, mal über den Code zu schauen. Ihr werdet bestimmt noch ein paar Verbesserungsvorschläge haben.
Ein paar Fragen habe ich auch.
Beim initialisieren der Eingänge „pinMode(LED1, OUTPUT);“ gibt es da eine Möglichkeit mit einer Schleife zu arbeiten und die Ziffer hinter LED durch eine Variable zu ersetzen (auch in der Start-Funktion). Sodass beim durchlaufen der Schleife die Ports LED1, LED2, LED3... angesprochen werden? Oder könnte man das über ein Array lösen? Wie verbinde ich dann aber die Variable mit dem entsprechenden Port?
Im seriellen Fenster lasse ich die Zufallswerte beim würfeln ausgeben. Dort habe ich auch bemerkt, dass „randomseed()“ zwingend notwendig ist.
2020-07-18 20.35.34-1.jpg2020-07-18 20.35.39-1.jpg
Video der Schaltung:
http://www.dropbox.com/s/s2qdcke4dib39xr/2020-07-18%2019.35.16.mov?dl=0
Video der seriellen Ausgabe:
http://www.dropbox.com/s/f7mzi…07-18%2019.39.01.mov?dl=0
Code- /*
- * elektronischer Würfel
- *
- * Version 1.0
- *
- * Rayko Schmidt
- * www.dreid-gfx.de
- * Der Code ist Public Domain und darf frei verwendet werden.
- *
- */
- const int wuerfelnPIN = 11;
- const int LED1 = 2;
- const int LED2 = 3;
- const int LED3 = 4;
- const int LED4 = 5;
- const int LED5 = 6;
- const int LED6 = 7;
- const int LED7 = 8;
- int Wert = 0; // der gewürfelte Wert
- int neueZeile = 0; // zum reseten der seriellen Ausgabe
- // Funktion zum Darstellen der Würfelaugen
- void Ziffer(int Zahl) {
- if (Zahl == 1) {
- digitalWrite(LED1, LOW);
- digitalWrite(LED2, LOW);
- digitalWrite(LED3, LOW);
- digitalWrite(LED4, HIGH);
- digitalWrite(LED5, LOW);
- digitalWrite(LED6, LOW);
- digitalWrite(LED7, LOW);
- }
- if (Zahl == 2) {
- digitalWrite(LED1, HIGH);
- digitalWrite(LED2, LOW);
- digitalWrite(LED3, LOW);
- digitalWrite(LED4, LOW);
- digitalWrite(LED5, LOW);
- digitalWrite(LED6, LOW);
- digitalWrite(LED7, HIGH);
- }
- if (Zahl == 3) {
- digitalWrite(LED1, LOW);
- digitalWrite(LED2, HIGH);
- digitalWrite(LED3, LOW);
- digitalWrite(LED4, HIGH);
- digitalWrite(LED5, LOW);
- digitalWrite(LED6, HIGH);
- digitalWrite(LED7, LOW);
- }
- if (Zahl == 4) {
- digitalWrite(LED1, HIGH);
- digitalWrite(LED2, HIGH);
- digitalWrite(LED3, LOW);
- digitalWrite(LED4, LOW);
- digitalWrite(LED5, LOW);
- digitalWrite(LED6, HIGH);
- digitalWrite(LED7, HIGH);
- }
- if (Zahl == 5) {
- digitalWrite(LED1, HIGH);
- digitalWrite(LED2, HIGH);
- digitalWrite(LED3, LOW);
- digitalWrite(LED4, HIGH);
- digitalWrite(LED5, LOW);
- digitalWrite(LED6, HIGH);
- digitalWrite(LED7, HIGH);
- }
- if (Zahl == 6) {
- digitalWrite(LED1, HIGH);
- digitalWrite(LED2, HIGH);
- digitalWrite(LED3, HIGH);
- digitalWrite(LED4, LOW);
- digitalWrite(LED5, HIGH);
- digitalWrite(LED6, HIGH);
- digitalWrite(LED7, HIGH);
- }
- }
- // Startroutine alle LEDs 2 Sekunden an
- void Start() {
- digitalWrite(LED1, HIGH);
- digitalWrite(LED2, HIGH);
- digitalWrite(LED3, HIGH);
- digitalWrite(LED4, HIGH);
- digitalWrite(LED5, HIGH);
- digitalWrite(LED6, HIGH);
- digitalWrite(LED7, HIGH);
- delay(2000);
- digitalWrite(LED1, LOW);
- digitalWrite(LED2, LOW);
- digitalWrite(LED3, LOW);
- digitalWrite(LED4, LOW);
- digitalWrite(LED5, LOW);
- digitalWrite(LED6, LOW);
- digitalWrite(LED7, LOW);
- }
- void setup() {
- Serial.begin(9600);
- Serial.println();
- Serial.println("bitte würfeln");
- pinMode(LED1, OUTPUT);
- pinMode(LED2, OUTPUT);
- pinMode(LED3, OUTPUT);
- pinMode(LED4, OUTPUT);
- pinMode(LED5, OUTPUT);
- pinMode(LED6, OUTPUT);
- pinMode(LED7, OUTPUT);
- pinMode(wuerfelnPIN, INPUT_PULLUP);
- Start();
- Serial.print(Wert);
- randomSeed(analogRead(A0)); // damit der Zufallswert auch zufällig ist
- }
- void loop() {
- if ( digitalRead(wuerfelnPIN) == LOW ) {
- Wert = random(1,7);
- Serial.print(Wert); // serielle Ausgabe der Zufallswerte zur Kontrolle, dabei hab ich bemerkt, was das fehlende randomSeed bewirkt. ;-)
- Ziffer(Wert);
- delay(random(1,30)); // ein zufälliger Delay beim würfeln
- neueZeile = 1; // neue Zeile für die Zufallswerte
- } else {
- if ( neueZeile == 1 ) {
- neueZeile = 0;
- Serial.println(); // neue Zeile für die Zufallswerte
- }
- Ziffer(Wert);
- }
- }