Koppelrelais


Google-Suche auf MEINE-SCHALTUNG.de :





Online Rechner

Chronik

Dauerkalender


Sanduhr mit RGB P3-64x64 Matrix und Arduino Mega


Sanduhr

Mit Arduino Mega und einer RGB-64x64-Matrix konstruieren wir jetzt eine Sanduhr. Das wohlbekannte Zeitmessgerät wird bei uns auf der Matrix grafisch dargestellt, die nach unten fallenden Sandkörner werden durch einzelnen Pixel der Matrix imitiert. Ihre Bewegung steuert der Mikrocontroller. Die Schaltung ergänzen wir noch mit einem Potentiometer, mit dem die Geschwindigkeit der fallenden Sandkörner verstellt werden kann. Ein aktiver Buzzer signalisiert mit einem Piepton, dass die Zeit abgelaufen ist. Mit zwei Taster wird die Sanduhr dann zurückgesetzt oder gestartet.

Matrix mit 4096 RGB-Leuchtdioden

Matrix

Die Matrix besteht aus 64x64 = 4096 RGB-Leuchtdioden. Die Versorgungsspannung der Matrix beträgt 5 VDC. Damit ist jedoch noch nicht alles gesagt. Das Netzteil, der die Matrix mit stromversorgt, soll in der Lage sein, bis zu 4A am Strom zu liefern.

Mehr über die Matrix 64x64

Arduino Mega

Mikrocontroller

Für die Steuerung der Matrix kann nicht jedes Arduino-Modell eingesetzt werden. Neben der Spannungsversorgung müssen insgesamt 16 Anschlüsse der Matrix bedient werden.

Arduino

Summer

Summer

Für die Erzeugung des akustischen Signals kommt ein aktiver Buzzer zum Einsatz. Der kleine Lautsprecher mit eingebauter Elektronik arbeitet mit 5 VDC. Um ein Signal zu erzeugen, reicht es, das Gerät mit Spannung zu versorgen.

Summer (aktiv)

Schaltplan

Schaltplan


Testschaltung

Testschaltung


Programm (Sketch)

Das Programm besteht grundsätzlich aus zwei Unterprogrammen „Sand_fliesst_ab()“ und „Sand_fliesst_rein()“. Alle weiteren Unterprogramme sind diesen beiden Hauptsequenzen untergeordnet.
Das Unterprogramm „Sand_fliesst_ab()“ stützt sich bei seinen Berechnungen auf die Tabelle (Array) „Sandkorn_oben[204]“. Die Elemente in der Tabelle werden beim Start mit festen Werten vorbelegt. Die Felder, wo anschließend Sandkörner positioniert werden, werden mit dem Wert „1“ vorbelegt. Alle anderen Felder haben den Wert „5“. Die Tabelle könnte man sich wie folgt vorstellen:

Tabelle oben

In bestimmten Zeitabständen, die in ms mit „ZeitLang„ festgelegt sind, durchsucht das Programm die Tabelle und sucht die „Sandkörner“ (Felder mit 1, in der Grafik blaue Felder), die gelöscht werden können, aus. Sie werden in der Liste „Obere_Reihe [Anzahl]“ festgehalten. Gibt es mehrere Sandkörner, die gelöscht werden können, entscheidet der Zufall, welches Sandkorn dran ist. Das Löschen der Sandkörner imitiert den wegfließenden Sand. Sobald die Entscheidung gefallen ist, welches Sandkorn das Feld räumen muss, wird in die Liste „fallendes_Sandkorn[x][y]“ ein neuer Eintrag eingefügt. Diese Liste stellt die einzige Schnittstelle zwischen den beiden Haupt-Unterprogrammen dar. Die Liste enthält die Sandkörner, die sich im freien Fall befinden. Das erste Unterprogramm fügt hier neue Sandkörner ein, das zweite, sobald ein Sandkorn nach dem Fall seine endgültige Position erreicht hat, löscht sie wieder.

Das zweite Unterprogramm „Sand_fliesst_rein()“ stützt sich auf der Tabelle „Platz_unten[15][24]“. Die Felder der Tabelle (zweidimensionales Array) bleiben zunächst unbelegt und haben den Wert „0“. Die Tabelle kann wie folgt grafisch dargestellt werden:

Tabelle unten

Das Programm, ähnlich wie der Vorgänger, arbeitet ebenfalls in bestimmten Zeitabständen. Sie werden mit der Variable „ZeitKurz“ festgelegt. In jedem Durchlauf untersucht das Programm die Liste der fallenden Sandkörner „fallendes_Sandkorn[x][y]“. Wird hier ein Sandkorn gefunden, wird seine Position um einen Platz nach unten verschoben. Erreicht ein Sandkorn die Spitze der Sandpyramide, entscheidet wieder der Zufall, ob seine Bewegung nach links oder nach rechts fortgesetzt wird. Wenn das Sandkorn schließlich eine Position erreicht hat, wo es nicht weiter geht, wird diese Stelle in der Tabelle „Platz_unten[15][24]“ mit „1“ markiert. Das Sandkorn bleibt hier endgültig stehen und sein Eintrag wird aus der Liste „fallendes_Sandkorn[x][y]“ gestrichen. Die Durchläufe des Programms werden so oft wiederholt, bis alle Sandkörner „zu Hause“ sind.

// ***************************************************************************************************
// Sanduhr mit Arduino und RGB 64x64 Matrix
// Eine Schaltung mit Arduino Mega
// Arduino IDE 2.1.0 (2023)
// ***************************************************************************************************

#include "RGBmatrixPanel.h"

#define CLK 11
#define OE 9
#define LAT 10
#define A A0
#define B A1
#define C A2
#define D A3
#define E A4

RGBmatrixPanel matrix(A, B, C, D, E, CLK, LAT, OE, false, 64);

                                                              // Sanduhr Konturen (Linien-Koordinaten)
int Sanduhr[12][4] = { { 33, 8, 33, 20 }, { 34, 21, 40, 27 }, { 51, 8, 51, 20 }, 
                       { 50, 21, 44, 27 }, { 40, 28, 40, 29 }, { 44, 28, 44, 29 }, 
                       { 40, 30, 33, 37 }, { 44, 30, 51, 37 }, { 33, 38, 33, 50 }, 
                       { 51, 38, 51, 50 }, { 33, 7, 51, 7 }, { 33, 51, 51, 51 } };

                                                              // Platzbelegung oberer Sanduhrbehälter
int Sandkorn_oben[204] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
                           5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5,
                           5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5,
                           5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5,
                           5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5,
                           5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5,
                           5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5,
                           5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5,
                           5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5,
                           5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5,
                           5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5,
                           5, 5, 5, 5, 5, 5, 5, 5, 1, 5, 5, 5, 5, 5, 5, 5, 5 };

bool Platz_unten[15][24];                                     // Platzzellen Sanduhrbehälter unten
int fallendes_Sandkorn[10][3];                                // Hilfstabelle mit X, Y - Koordinaten

unsigned long MillisAktuell = 0;                              // Hilfsvariable Milisekunden
unsigned long ZeitLang = 200;                                 // Takt für Abfluss oberer Sanduhrbehälter
unsigned long ZeitLang_Start;                                 // Start Zeitmessung oben
unsigned long ZeitKurz = 30;                                  // Takt für fallende Sandkörner
unsigned long ZeitKurz_Start;                                 // Start Zeitmessung unten
int ZufallStart;                                              // Wert für randomSeed
int Taster_Start = 2;                                         // Taster Sanduhr Start
int SpeedPin = A8;                                            // Analoger Eingang
int Piepser = 3;                                              // Signalgeber
boolean Sanduhr_laeuft = false;                               // Hilfsvariable Uhr läuft
int Digital_Zaehler = 110;                                    // 109 Sandkörner



// ***************************************************************************************************
void setup() {
  matrix.begin();                                             // Matrix initialisieren
  delay(500);                                                 // Wartezeit 500 ms
  pinMode(Taster_Start, INPUT_PULLUP);                        // Eingang Start-Taster
  pinMode(Piepser, OUTPUT);                                   // Piepser-Ausgang
  Sanduhr_zeichnen();                                         // Sanduhrkonturen zeichnen
  Sanduhr_fuellen();                                          // Sand einfpgen
  matrix.setTextSize(1);                                      // textgröße festlegen
  zaehler();                                                  // Zählerstand anzeigen;
}

// ***************************************************************************************************
void loop() {                                                 // Hauptprogramm

  int AnalogWert = analogRead(SpeedPin);                      // Bestimmung der Geschwindigkeit
  ZeitLang = (AnalogWert / 4) * 3 + 250;                      // Einfache Formeln für Geschwindigkeiten
  ZeitKurz = (AnalogWert / 20) * 7 + 50;

  MillisAktuell = millis();                                   // Milisekunden aktuell auslesen
  ZufallStart++;                                              // randomSeed-Startvariable inkrementieren
  if (ZufallStart > 100) {                                    // randomSeed-Startvariable abfragen
    ZufallStart = 20;                                         // ggf. zurücksetzen
  }

  if (digitalRead(Taster_Start) == LOW) {                     // Mit Starttaster wird die Uhr gestartet
    delay(200);                                               // Prellen abwarten
    Sanduhr_laeuft = true;                                    // Hilfsvariable auf TRUE
    randomSeed(ZufallStart);                                  // Zufalssgenerator vorbelegen
    ZeitLang_Start = ZeitKurz_Start = MillisAktuell;
  }
                                                              // Taktüberwachung Behälter oben
  if (Sanduhr_laeuft && (ZeitLang_Start + ZeitLang) < MillisAktuell) {
    digitalWrite(Piepser, LOW);
    Sand_fliesst_ab();
    ZeitLang_Start = MillisAktuell;
  }
                                                              // Taktüberwachung Behälter unten
  if (Sanduhr_laeuft && (ZeitKurz_Start + ZeitKurz) < MillisAktuell) {
    Sand_fliesst_rein();
    ZeitKurz_Start = MillisAktuell;
  }

}

// Sanduhr Kontur zeichnen ---------------------------------------------------------------------------
void Sanduhr_zeichnen() {

  screen_clear();                                             // Matrix löschen
  for (int i = 0; i < 12; i++) {                              // Linien der Sanduhr zeichnen
      matrix.drawLine(Sanduhr[i][0], Sanduhr[i][1], Sanduhr[i][2], 
                    Sanduhr[i][3], matrix.Color333(0, 7, 0));
  }
}

// Sanduhr füllen  -----------------------------------------------------------------------------------
void Sanduhr_fuellen() {

  for (int i = 0; i < 204; i++) {                             // Sanduhr mit "Sand" füllen
    if (Sandkorn_oben[i] == 1) {                              // Nur Felder mit dem Wert 1
      int X = i - ((i / 17) * 17) + 34;                       // X, Y - Koordinaten für Ausgabe
      int Y = (i / 17) + 15;                                  // berechnen
      matrix.drawPixel(X, Y, matrix.Color333(0, 0, 7));       // Pixel sichtbar machen
    }
  }
}

// Sanduhr oben leeren  ------------------------------------------------------------------------------
void Sand_fliesst_ab() {
  
  if (Sandkorn_oben[195] == 1) {                              // Noch mindestens 1 Sandkorn vorhanden?
    int Anzahl = 0;                                           // In Frage kommenden Sandkörner suchen
    int Obere_Reihe [15];                                     // Maximal 15 Sandkörner
    for (int i=18; i<196; i++) {                              // Tabelle durchsuchen
      if (Sandkorn_oben[i] == 1) {                            // Sandkorn (irgendein) gefunden
                                                              // Nur bestimmte Sandkörner können 
                                                              // gelöscht werden:
        int Wertung = Sandkorn_oben[i-18] + Sandkorn_oben[i-17] + Sandkorn_oben[i-16];
        if ((Wertung == 0) or (Wertung == 5) or (Wertung == 15)) {
          Anzahl++;                                           // Anzahl gefundenen Sandkörner
          Obere_Reihe [Anzahl] = i;                           // Nummer aus der Haupttabelle festhalten
        }
      }
    }
    if (Anzahl > 0) {                                         // Sandkörner, die gelöscht werden können
      int Reihe_Nr = random(1, Anzahl);                       // Sandkorn via Zufall bestimmen
        int Nummer = Obere_Reihe [Reihe_Nr];                  // Nummer in der Haupttabelle
        int X = Nummer - ((Nummer / 17) * 17) + 34;           // Matrix-Koordinaten
        int Y = (Nummer / 17) + 15;
        matrix.drawPixel(X, Y, matrix.Color333(0, 0, 0));     // Sandkorn löschen
        Sandkorn_oben[Nummer] = 0;                            // Platz freimachen

        for (int i = 10; i > 0 ; i--) {                       // Tabelle der fallenden Sandkörner sort.
          fallendes_Sandkorn[i][0] = fallendes_Sandkorn[i-1][0];
          fallendes_Sandkorn[i][1] = fallendes_Sandkorn[i-1][1];
          fallendes_Sandkorn[i][2] = fallendes_Sandkorn[i-1][2];
        }
        fallendes_Sandkorn[0][0] = 1;                         // Neues Sandkorn auf Platz 0
        fallendes_Sandkorn[0][1] = 7;                         // X Startkoordinate
        fallendes_Sandkorn[0][2] = 1;                         // Y Startkoordinate
        matrix.drawPixel(42, 27, matrix.Color333(0, 0, 7));   // Sandkorn sichtbar machen
        zaehler();                                            // Zählerstand aktualisieren und anzeigen
      }
  }
}

// Sanduhr unten füllen  -----------------------------------------------------------------------------
void Sand_fliesst_rein() {

  for (int i = 0; i < 10 ; i++) {
    if (fallendes_Sandkorn[i][0] == 1) {                      // Noch fallende Sandkörner unterwegs?
      int Richtung = 0;                                       // 0-Pos,1-Links,2-Rechts,3-runter
      int X = fallendes_Sandkorn[i][1];                       // Aktuelle Koordinaten
      int Y = fallendes_Sandkorn[i][2];
      if ((X - 1) > -1 && (Y + 1) < 24 && (X + 1) < 15) {     // Weitere Bewegung möglich?
        if (X == 7 && Platz_unten[X][Y + 1] == 1) {           // senkrecht nach unten geht nicht
          if (Platz_unten[X - 1][Y + 1] == 0 && Platz_unten[X + 1][Y + 1] == 0) {
            int Zufall = random(2);
            Richtung = Zufall + 1;                            // Zufall entscheidet ob Links/Rechts
          }
        }
        if (Richtung == 0 && Platz_unten[X][Y + 1] == 0 && Y < 23) { 
          Richtung = 3;                                       // Senkrecht runter
        }
        if (Richtung == 0 && Platz_unten[X - 1][Y + 1] == 0) { 
          Richtung = 1;                                       // Links runter
        }
        if (Richtung == 0 && Platz_unten[X + 1][Y + 1] == 0) { 
          Richtung = 2;                                       // Rechts runter
        }
      }
        
      if (Richtung > 0) {
        matrix.drawPixel(X + 35, Y + 26, matrix.Color333(0, 0, 0));  // löschen
      }

      switch (Richtung) {
        case 0: {                                             // Endposition erreicht
          fallendes_Sandkorn[i][0] = 0;                       // Raus aus der Fallliste
          Platz_unten[X][Y] = 1;                              // Platz markieren
          break; }
        case 1: {                                             // Runter Links
          fallendes_Sandkorn[i][1] = X - 1;                   // Neue X Startkoordinate
          fallendes_Sandkorn[i][2] = Y + 1;                   // Neue Y Startkoordinate
          matrix.drawPixel(X - 1 + 35, Y + 1 + 26, matrix.Color333(0, 0, 7)); // Zeichnen
          break; }
        case 2: {                                             // Runter Rechts
          fallendes_Sandkorn[i][1] = X + 1;                   // Neue X Startkoordinate
          fallendes_Sandkorn[i][2] = Y + 1;                   // Neue Y Startkoordinate
          matrix.drawPixel(X + 1 + 35, Y + 1 + 26, matrix.Color333(0, 0, 7)); // Zeichnen
          break; }
        case 3: {                                             // Senkrecht Runter
          fallendes_Sandkorn[i][1] = X;                       // Neue X Startkoordinate
          fallendes_Sandkorn[i][2] = Y + 1;                   // Neue Y Startkoordinate
          matrix.drawPixel(X + 35, Y + 1 + 26, matrix.Color333(0, 0, 7)); // Zeichnen
          break; }
      }
    }
  }
}

// Matrix Löschen ------------------------------------------------------------------------------------
void screen_clear() {

    matrix.fillRect(0, 0, matrix.width(), matrix.height(), matrix.Color333(0, 0, 0));
}

// Digital Zähler anzeigen ---------------------------------------------------------------------------
void zaehler() {

  matrix.setTextColor(matrix.Color333(0,0,0));                // Letzte Ausgabe zunächst löschen
  matrix.setCursor(8, 45);                                    // Cursor positionieren
  matrix.print(Digital_Zaehler);                              // Alten Wert schreiben
  Digital_Zaehler--;                                          // Zähler dekrementieren
  matrix.setTextColor(matrix.Color333(7,7,0));                // Textfarbe festlegen
  matrix.setCursor(8, 45);                                    // Wieder auf Startposition
  matrix.print(Digital_Zaehler);                              // Neuen Wert schreiben
  if (Digital_Zaehler == 0) {
    digitalWrite(Piepser, HIGH);                              // Piepser einschalten
  }
}
// ***************************************************************************************************        
        



Kurzvideo

Kurzvideo


Weitere Themen:


Google-Suche auf MEINE-SCHALTUNG.de :


Home Impressum Datenschutz