Metronom Arduino Mini V3 + OLED 128x64

Guten Abend liebe Community,

ich habe vor einigen Jahren das ein oder andere mit Arduinos gemacht, eher was einfaches doch dann aufgrund von Job, Familie und dem damit verbundenen Aufgaben alles bei Seite gelegt und nie wieder damit angefangen.

Als ich kürzlich die Kiste mit dem ganzen Zeugs ausgepackt habe, hat mich gleich wieder das Fieber gepackt und ich habe direkt mit einem Projekt gestartet. :slight_smile: Wobei ich gleich sagen muss, dass ich kein erfahrener Programmierer bin und wenig ELektro Know-How habe…ich bin eigentlich aus dem Maschinenbau. :slight_smile: So viel zu mir…

…und nun zu meinem Projekt und den aktuellen Problemen. Ich hoffe das mir einer etwas helfen kann bei meiner Fragestellung und zu diesem Projekt.

Zunächst einmal: Was versuche ich umzusetzen?

Ich spiele gerne Gitarre (Flamenco). Der Flamenco hat viele Taktarten und Formen, nicht ganz einfach. Um mir den Takt nicht nur akustisch sondern auch visuel deutlich zu machen, habe ich mit enschieden ein Metronom zu bauen, das ich an die Gitarre klemmen kann. In Form eines monochrome OLED (128x64).

Was habe ich bisher programmiert an diesem Projekt?

Der “Grundaufbau” des Display, also das Layout wie alles aussehen soll ich getan. Vermutlich ist es lachhaft wie es programmiert wurde, aber das werdet Ihr sicher besser beantworten können. :slight_smile:

Die BPM Anzeige auf dem Display ist ebenfalls i.O. und kann mit einem Poti reguliert werden. Passt (vermute ich jetzt einfach mal).

Piezo Summer und/oder LED leuchten bzw. piepsen auch immer dann wenn es sein muss.

Was fehlt dann noch?

Kleiner weisser Kreis:

display.fillCircle(3, 45, 2, WHITE);

Der von mir im Code gezeichnete “Kreis” der sich überhalb der Rechtecke (die gefüllt oder ungefüllt sind, spielt aber hierfür bzw. die Funktion keine Rolle…die haben eine rein visuelle Funktion).

So, dieser “Kreis” muss sich jetzt bei jedem “LED HIGH” oder ben jedem piepsen des Piezo um ein “rechteck” weiter bewegen…kontinuierlich bis ich den strom weg nehme. Die Geschwindigkeit ergibt sich aus der BPM die ich mit dem Poti regle, klar.

Bedeutet bei jedem “Takt” muss sich der kleine weisse “Kreis” immer um ein Rechteck verschieben bzw. springen, und genau da fängt bei mir das Problem an…eigentlich muss ich sagen das Problem 1 von 2.

Ich habe leider im Netz gesucht und gesucht, habe mich versucht an den LED Blink Tutorial das es gibt zu orientieren, keine Chance…ich komme nicht dahinter wie der Code aussehen muss. Wie kann ich das umsetzen, was fehlt mir hierzu?

Was ist das Problem 2 von 2?

Das zweite Problem ist, dass im oberen linken Bereich (unter dem angezeigten Text “Tiempo”) ein Zähler mitlaufen muss. Ist der kleine weisse “Kreis” über dem ersten Rechteckt, muss dort oben dann eine “1” stehen, ist der kleine weisse “Kreis” über dem zweiten Rechteck muss dort dann eine “2” stehen und so weiter…auch hier habe ich versucht im Netz und in den Beispielsketches was zu finden um mich etwas orientieren zu können, aber leider steige ich nicht durch. :frowning:

Der aktuelle Code (es darf auch gerne über den Code gelacht werden, Freude schadet nie):

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2

#define icone_HEIGHT 16
#define icone_WIDTH  16

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

int led = 13;          // LED and Piezo
int sensorPin = 1;    // select the input pin for the potentiometer
int sensorValue = 0;
int metronomeBPM = 0;
int metronomePulse = 50;
int staticBPM = 60000;
int divisor  = 0;


void setup()   {
  pinMode(led, OUTPUT);                 
  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)

  display.display();
  delay(2000);
}

void loop() {

  sensorValue = analogRead(sensorPin);
  sensorValue = constrain(sensorValue, 250, 1023);
  digitalWrite(led, HIGH);   
  delay(metronomePulse);   // Use a 50 ms pulse for Piezo
  digitalWrite(led, LOW);    
  delay(sensorValue);      // Take potentiometer as the metronome interval (offset 50ms).

  divisor = sensorValue + metronomePulse;
  metronomeBPM = 60000 / divisor;
//  Serial.println(metronomeBPM);
  
   // Clear the buffer.
  display.clearDisplay();

// Grunddesign wird hier festgelegt (Text, Aufteilung und der 12er Compas
display.drawLine(0, 36, 128, 36, WHITE);//Grundlinie horizontal
display.drawLine(64, 0, 64, 36, WHITE); //Grundlinie vertikal
display.setTextSize(1);              // Definition Textgröße & Farbe
display.setTextColor(WHITE);
display.setCursor(0,0);           // Positionsangabe für "Tiempo"
display.println("Tiempo");          // Ausgabetext "Tiempo"
display.setCursor(73,0);         // Positionsangabe für "BPM"
display.println("BPM");          // Ausgabetext "BPM"  
display.setTextSize(2);             // Definition Textgröße Variable Werte wie BPM und "Tiempo"
display.setCursor(20,15);           // Positionsangabe variabler Wert "Tiempo"
display.println("12");             // Aktuell nur Platzhalter, Wert muss variabel sein.
display.setCursor(73,15);    // Positionsangabe variabler Wert "BPM"
display.println(metronomeBPM);            // Aktuell nur Platzhalter, Wert muss variabel sein.
display.drawRect(1, 53, 5, 11, WHITE);
display.drawRect(12, 53, 5, 11, WHITE);
display.fillRect(23, 53, 5, 11, WHITE);
display.drawRect(34, 53, 5, 11, WHITE);
display.drawRect(45, 53, 5, 11, WHITE);
display.fillRect(56, 53, 5, 11, WHITE);
display.drawRect(67, 53, 5, 11, WHITE);
display.fillRect(78, 53, 5, 11, WHITE);
display.drawRect(89, 53, 5, 11, WHITE);
display.fillRect(100, 53, 5, 11, WHITE);
display.drawRect(111, 53, 5, 11, WHITE);
display.fillRect(122, 53, 5, 11, WHITE);
display.fillCircle(3, 45, 2, WHITE);
      
  display.display();
  display.clearDisplay();
}

Ich bin über jede Hilfe, Anregung und auch Kritik dankbar.

Viele liebe Grüße und einen schönen restlichen Sonntag.

Am Ende vom loop gibst Du die Daten ans Display aus und löschst es gleich darauf wieder. Ist das so gewollt? Am Anfang löschst Du es wieder.

Auf Grafikdisplays löscht man eigentlich nicht immer das ganze Display, sondern nur den Teil, der sich verändert. Am schnellsten geht das, wenn man das Objekt (z.B. den gefüllten Kreis) mit der Hintergrundfarbe (schwarz) zeichnet.
Das geht schneller und flackert nicht so.

Die Erklärung mit den Rechtecken habe ich nicht verstanden. Irgendwann ist doch das Display zu Ende.

Gruß Tommy

Hallo Tommy56,

wie gesagt, gewollt ist an dem Code vielen nicht aber es ist auch nicht so, dass es mir aufgefallen wäre.
Leider habe ich vorher nie etwas mit OLED gemacht, daher kenne ich viele Befehle, Funktionen und Arbeitsweisen überhaupt nicht. :confused:

Muss ich den Code dann umstrukturieren um eben nur die Variablem bzw. sich die zu verändernden Bereiche zu “löschen” ?

Ja, wenn der weisse Kreis beim 12. Rechteck angelangt ist, dann fängt er beim 1. wieder an. Verstehst du wie ich das meine?

Viele Grüße

Hi

Bin nicht Tommy, aber: Nein :slight_smile:
Da Du das Display so laufen hast, kannst Du ein Bild davon machen?
Wenn ich Dein Problem recht verstehe, brauchst Du 'nur' einen Zähler, Der von 0-11 zählt und je nach Zahlenwert andere Koordinaten als Kreismittelpunkt raus rückt.
Kurz vor dem Umsetzen entweder den Kreis in Hintergrundfarbe neu zeichnen (und den Kreis damit löschen) oder ein Rechteck in Hintergrundfarbe um den Kreis - könnte schneller gehen, da keine Trigonometrie gebraucht wird (wobei hier ein Ansatz war, wo man auch ohne SIN/COS zu einer Sinus-Form kommen konnte, wäre also möglich, daß der Kreis auch nur einfache Berechnungen benötigt).

MfG

Ja, Rechteck könnte flinker sein, wenn man da nichts anderes mit erwischt.
Das andere ist ein Modulo-12-Zähler.

Gruß Tommy

Hallo,

du hast es richtig verstanden, einmal einen Zähler von 1-12 und eben dazu der springende Kreis von Rechteck zu Rechteck.

Also wenn ein kleines weisses Rechteck besser und einfach als ein kleiner weisser Kreis ist, dann kann ich auch damit verdammt gut leben. :slight_smile:

// display.fillCircle(3, 45, 2, WHITE);
display.fillRect(1, 43, 5, 5,WHITE);

Anbei das Bild, ich hoffe man kann es erkennen (noch mit Kreis). Habe mal ein weisses Quadrat statt einem weissen Kreis.

Grüße

Ich sehe kein Bild.

Gruß Tommy

Hier das aktuelle Bild.

Der "gefüllte Kreis" ist ja schon fast ein Rechteck. Dann sollte man da eins zum Löschen nehmen.

Gruß Tommy

Hi

Da musst Du doch nur den X-Wert verändern.
Wie weit stehen die druter liegenden Rechtecke auseinander?
Diesen Abstand musst Du 'pro Zähler' nur zum X-Wert des Anzeige-Kreis dazu zählen.

Per Modulo 12 (Ergebnis 0-11) - Du kannst also hier einfach den Modulo x Abstand der Rechtecke zu Dem Mittelpunkt des Kreises addieren, fertig.

Ob Kreis oder Rechteck, wenn es hier nicht auf eine einzuhaltende Geschwindigkeit ankommt - egal: Geschmackssache.

MfG

Hi,

ich verstehe was du meinst, kenne diese Funktion so nicht, werde es aber morgen probieren. Ich befürchte das dies meine aktuell vorhanden Fähigkeiten übersteigt....probieren geht über studieren. :slight_smile:

Der Abstand von Rechteck zu Rechteck beträgt 11 pixel.

Hi

Modulo (%), Arduino-Referenz

MfG

Danke dir, bringt etwas Licht ins Dunkle.

Ich steige mit dem “%“ nicht durch, bzw. erkenne den Sinn dieses nicht. Theoretisch ist es doch auch ohne “%“ umsetzbar?

Damit kannst Du sicher stellen, dass Dein Wert immer von 0 bis 11 geht.
Modulo ist der Rest der Division - wie ganz früher in der Schule

22 durch 12 ist 1 Rest 10
wert = (wert +1)%12;
oder kürzer
wert = (++wert)%12;
oder noch kürzer
wert %= ++wert;

Am verständlichsten dürfte die 1. Version sein.

Gruß Tommy

Tommy56:

wert = (wert +1)%12;

oder kürzer
wert = (++wert)%12;
oder noch kürzer
wert %= ++wert;

Wo bleibt bei der dritten Version die "12"?

JeBaFe:
Der Abstand von Rechteck zu Rechteck beträgt 11 pixel.

Ich habe es mal so probiert: Die Variable tiempo zählt von 0 bis 11, angezeigt wird eins mehr. Die Positionsberechnung von kreispos ist dann auch nicht so geheimnisvoll:

byte tiempo, kreispos;
...
  display.setCursor(20, 15);          // Positionsangabe variabler Wert "Tiempo"
  display.println(tiempo + 1);        // tiempo = 0 bis 11, Anzeige 1 bis 12
...
  kreispos = (tiempo * 11) + 3;
  display.fillCircle(kreispos, 45, 2, WHITE);
  tiempo = (tiempo + 1) % 12;

Bei irgendeiner Animation wärst Du jetzt fertig, als Musiker solltest Du aber im Takt bleiben, was Dir wegen der Programmierung mittels delay() wohl niemals gelingen wird. Ein Vergleich mit einem kalibrierten Metronom oder einer guten Flamenco-Tänzerin sollte das bestätigen.

Mit millis() kannst Du zwar auch nur die ungenaue Frequenz des Resonators als Grundlage nutzen, diese sollte aber eine einigermaßen gleichmäßige Abweichung haben.

Das Beispiel blinkWithoutDelay der IDE ist Deine Grundlage.

Ok, 3. Streichen, das geht nicht mit dem Präinkrement.

Danke für die Korrektur.

Gruß Tommy

Tommy56:
Danke für die Korrektur.

Bitte gerne :slight_smile:

Bei 2. bekomme ich mit IDE 1.6.5 die Warnung “operation may be undefined [-Wsequence-point]”. Was soll mir das denn sagen?

Die bekomme ich auch. Es tut aber das, was es soll (ESP8266 Version 2.4.0 IDE 1.8.5).
Also sollte man klassisch bei Version 1 bleiben.

Gruß Tommy

Hallo und guten Abend zusammen,

viel Input für mich...ich werd versuchen es einzubauen und melde mich...habe aktuell noch keine Ahnung wie, muss das erst verstehe bzw. versuchen es zu verstehen.

Das mit der Abweichung kann ich bestätigen, allerdings muss ich zugeben, dass ich soweit noch nicht gedacht hatte bzw. es vll. sogar verdrängt habe. :confused:

Wenn ich die Animation zum laufen gebracht habe, können wir uns um dieses Problem kümmern. :slight_smile:

Viele Grüße

Darf ich Dir ein kleines Motivationsbildchen anbieten?

Tiempo.png

Tiempo.png