Einsteigercode für DIY Selbstbau Flipper / Pinball

Hallo Forum
Ich bin Elektroniker der alten Schule und möchte einen Flipperautomaten mit / für meinen Enkel bauen. Die Bedienelemente und Kugelsensoren sind für mich kein Problem. Ich werde mich Mikroschaltern und Näherungssensoren bedienen.
Nun geht es aber um das zählen der Punkte im Spiel.
Dies ,denke ich, könnte ein Arduino und eine Dot-Matrix bestens erledigen.
Ich habe mir also einen Arduino-UNO und mehrere 4x64-LED-Matrix-Screen-Module
bestellt.
Mit Hilfe von E-Books und Recherche im Internet konnte ich diese Komponenten soweit zum laufen bringen.
Über IDE die Library MAX72XX und MD_Parola eingebunden und mit einem Beispiel-Sketch konnte ich eine Laufschrift erzeugen.
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW

Nun meine Frage um Unterstützung, da ich im Programmieren Anfänger bin.

Ich benötige ein Code um über die Eingänge des Arduino bei "Aktiv-Signal" einen Wert auf die Anzeige zu bringen. zB. Eingang 1 gibt immer 10 Punkte, die sich dann aufaddieren. So könnte ich eine Summe der Punkte zählen, die im Spiel erreicht werden.
Hat zufällig jemand schon damit Erfahrungen und kann mir etwas zur Verfügung stellen oder Tipps geben, wo ich nachsehen kann?

Vielen Dank vorab.
Liebe Grüße
Norbert

Behandeln Sie jeden „Treffer“ wie einen Knopfdruck. Konfigurieren Sie eine Reihe von Knopfstiften und eine Reihe von Punktzahlen (ein Ziel erhält „1“ Punkt pro Treffer, ein anderes Ziel erhält „2“ Punkte pro Treffer und so weiter). Als Nächstes wird die Punktzahl angezeigt. Nehmen Sie „37“ Punkte an. Nehmen Sie ein „modulo 10“ der Punktzahl, um die „Einheiten“ zu erhalten … 37 % 10 = 7. Wenn Sie 48 zu 7 addieren, erhalten Sie ASCII „7“ … drucken Sie das. Subtrahieren Sie das von der Punktzahl, sodass „30“ übrig bleibt … dividieren Sie durch 10 … 30 / 10 = 3. Addieren Sie 48, um 51 ASCII „3“ zu erhalten … drucken Sie Nummer 1 und Nummer 2.

Hallo xfpd
Ja, jeder Treffer ist ein Tastendruck und die Summen werden addiert.
Den Rest habe ich leider nicht verstanden.
Ich bräuchte ein Beispiel für ein Programm.
Beste Grüße
Norbert

Sehr viele Beispiele findest du auch schon in der IDE.
Einfach mal ansehen und testen.

Du brauchst ne Variable um den Zählerstand zu halten.
Die Taster=Sensoren fragst Du ab, wenn jeder z.B. an einem Pin hängt kannst Du dem Pin eime Wertigkeit zuordnen und beim Auslösen die Wertigkeit der Variable hinzuaddieren und neu anzeigen.
Pseudocode als Beispiel:
Zählerstand = 0
Wenn Sensor1=true dann Zählerstand +10
Wenn Sensor2=true dann Zählerstand +20

Nach jedem Update des Zählerstands die Anzeige aktualisieren.

Könnte so aussehen:

#include <Button_SL.hpp>   // https://github.com/DoImant/Button_SL

using namespace Btn;

constexpr uint8_t NUM_COUNTER {4};

// KLasse zum speichern und addieren der Punktzahl, mit der sie initialisiert wird
class ScoreCounter {
public:
  ScoreCounter(uint16_t points, uint32_t sum = 0) : points {points}, sum {sum} {}
  uint16_t operator()() {   // Addiere pro kontakt die initialisierte Punktzahl
    sum += points;
    return points;
  }
  uint32_t getSum() const { return sum; }   // Gebe Summe für die Kontakte * initialisierte Punktzahl zurück

private:
  const uint16_t points;
  uint32_t sum;
};

//
// Abfrage der Kontaktpins. Diese sind Active-Low konfiguriert (INPUT_PULLUP)
// Kann über die Buttonklasse aber auch auf Active High umkonfiguriert werden.
// Rückgabewert ist der Index des Kontaktpins im Array. (-1) = kein Kontakt festgestellt.
//
template <size_t N> int checkButtons(ButtonSL (&btn)[N]) {
  for (size_t i = 0; i < N; ++i) {
    if (btn[i].tick() != ButtonState::notPressed) {
      return i;
    }
  }
  return -1;
}

// Array für die Pinabfrage anlegen. Das Lesen der Pins erfolgt entprellt
// Standard Entprellzeit ist 30ms
ButtonSL buttons[NUM_COUNTER] {{4}, {5}, {6}, {7}};

// Für jede gewünschte Punktzahl (pro Kontakt) ein Objekt anlegen
// Die Array-Reihenfolge (Index) muss mit dem Button Array identisch sein
ScoreCounter score[NUM_COUNTER] {10, 100, 1000, 10000};

void setup() {
  Serial.begin(115200);
  for (auto& button : buttons) {
    button.begin();      // Initialisieren der Buttonobjekte
  }  
}

void loop() {
  static uint32_t scoreSum {0};           // Speicher für die Punktzahlsumme
  int8_t index = checkButtons(buttons);   // Abfrage der Kontaktpins
  if (index > (-1)) {                     // Wenn ein Kontakt festgestellt wird, die jeweilige Punktzahl addieren
    scoreSum += score[index]();
    Serial.print("Score: ");
    Serial.println(scoreSum);
  }
}

Zum Ausprobieren:

Als "Auslöser" wurden Taster verwendet um den Kontakt zu simulieren.
Ein eventuelles Problem sehe ich darin, dass möglicherweise ein Kontakt zu kurz aktiv ist weil die Pins nacheinander abgefragt werden und deshalb nicht gezählt wird. Müsste man halt ausprobieren.

1 Like

Vielen Dank für Eure Rückmeldungen. Ich werde hier viel lernen.
Ein besonderer Dank gilt Kai-R. " DAS IST JA DER HAMMER."
Genau so etwas hatte ich gesucht. Vielen Dank, dass Du dir hier diese Mühe
gemacht hast und genau zu meiner Hardware ein Beispiel geschrieben hast.
Auch der Simulator ist super. Ich werde das Script am Wochenende an meiner Hardware mal ausprobieren und versuche es zu verstehen.
Dann melde ich mich anschließend.
Schönes Wochenende
Gruß
Norbert

Hallo Kai-R

Das funktioniert jetzt.
Ich musste für das Display noch den HARDWARE_TYPE von PAROLA_HW auf FC16_HW
ändern. weil sonst die Anzeige kryptisch ist.
Dann würde ich gern noch die Kontakte auf Active-high haben.
Das konnte ich leider nicht. Kannst du mir die Änderung noch schreiben?
Wäre super.
Danke für die Unterstützung.
LG
Norbert

Dann brauchst Du aber an jedem Kontakt einen externen Pull-Down-Widerstand - siehe hier.
Da das zusätzlichen Aufwand bedeutet, machen das die meisten nur ungern und verwenden stattdessen - wie Kai - "low active" Taster.
Wichtig ist, in beiden Zuständen einen stabilen Pegel zu haben.

Wenn es HIGH sein muss, muss auch die Initialisierung der Buttons geändert werden:

ButtonSL signals[NUM_COUNTER] {{4,1000,HIGH}, {5,1000,HIGH}, {6,1000,HIGH}, {7,1000,HIGH}};

Wokwi unter Verwendung der Basis von @Kai-R

Code für die nicht Wokwi-Nutzer

Es ist guter Brauch, den Code auch hier im Forum zu haben - für alle, die Wokwi nicht nutzen können (Telefon) oder wollen.

#include <MD_MAX72xx.h>    // https://github.com/MajicDesigns/MD_MAX72XX
#include <MD_Parola.h>     // https://github.com/MajicDesigns/MD_Parola
#include <Button_SL.hpp>   // https://github.com/DoImant/Button_SL
#include <SPI.h>

using namespace Btn;

constexpr uint8_t NUM_COUNTER {4};

constexpr uint8_t MAX_DEVICES {5};
constexpr uint8_t CLK_PIN {13};
constexpr uint8_t DATA_PIN {11};
constexpr uint8_t CS_PIN {10};

// KLasse zum speichern und addieren der Punktzahl, mit der sie initialisiert wird
class ScoreCounter {
public:
  ScoreCounter(uint16_t points, uint32_t sum = 0) : points {points}, sum {sum} {}
  uint16_t operator()() {   // Addiere pro Kontakt die initialisierte Punktzahl
    sum += points;
    return points;          // Gib initialisierte Punktzahl zurück. Z.b. für eine andere Addition
  }
  uint32_t getSum() const { return sum; }   // Gebe Summe für die Kontakte * initialisierte Punktzahl zurück

private:
  const uint16_t points;
  uint32_t sum;
};

//
// Abfrage der Kontaktpins. Diese sind Active-Low konfiguriert (INPUT_PULLUP)
// Kann über die Buttonklasse aber auch auf Active High umkonfigurierte werden.
// Rückgabewert ist der Index des Kontaktpins im Array. (-1) = kein Kontakt festgestellt.
//
template <size_t N> int checkSignals(ButtonSL (&btn)[N]) {
  for (size_t i = 0; i < N; ++i) {
    if (btn[i].tick() != ButtonState::notPressed) {
      return i;
    }
  }
  return -1;
}

// Array für die Pinabfrage anlegen. Das Lesen der Pins erfolgt entprellt
// Standard Entprellzeit ist 30ms
ButtonSL signals[NUM_COUNTER] {{4,1000,HIGH}, {5,1000,HIGH}, {6,1000,HIGH}, {7,1000,HIGH}};

// Für jede gewünschte Punktzahl (pro Kontakt) ein Objekt anlegen
// Der Array-Reihenfolge (Index) muss mit dem Button Array identisch sein
ScoreCounter score[NUM_COUNTER] {10, 100, 1000, 10000};

// Display
MD_Parola mx = MD_Parola(MD_MAX72XX::PAROLA_HW, CS_PIN, MAX_DEVICES);

void setup() {
  Serial.begin(115200);
  // Initialisieren der Buttonobjekte
  for (auto& signal : signals) {
    signal.begin();
  }   

  mx.begin();
  mx.displayText("", PA_RIGHT, 0, 0, PA_PRINT, PA_NO_EFFECT);
  mx.print(0);
}

void loop() {
  static uint32_t scoreSum {0};   // Speicher für die Punktzahlsumme

  int8_t index = checkSignals(signals);   // Abfrage der Kontaktpins
  if (index > (-1)) {                     // Wenn ein Kontakt festgestellt wird die jeweilige Punktzahl addieren
    scoreSum += score[index]();
    mx.print(scoreSum);                   // Kein Test auf Überlauf Stellenzahl!
    Serial.print("Score: ");
    Serial.println(scoreSum);
  }
}
2 Likes

Dem kann man nur zustimmen!

Ok. Das kann ich gut nachvollziehen. Ich werde nun mit der Hardware weiterbauen
und kann dann entscheiden, ob ich zwingend die Änderung benötige oder nach
Kais Methode arbeite.
Danke für den Link zu den Arduino Grundlagen
und die Konfigurationsänderung.
Das hilft mir sehr.

In dem Sketch steht bei jeder verwendeten Bibliothek ein Link. Wenn man da drauf klickt, gelangt man u.a. zu den „Gebrauchsanweisungen“ zu diesen Bibliotheken.
Wenn man sich diese anschaut, hilft das auch weiter.

1 Like

...und auf genau die Art bin ich zu geänderten Initialisierung gekommen - hatte vorher noch nie mit dieser Lib zu tun.
Also Danke fürs Verlinken im Sketch!