// MIDI RGB Keyboard v4.01.01 - 05.02.2025
// Begonnen am: 01.06.2021
// v1.00.01: Erste unveröfffentlichte Testversion. WS2801B LED Stripe (zu großer Abstand). Optokoppler = CNY 17-3
// v2.00.01: Zweite unveröffentlichte Testversion. WS2812B LED Stripe (Abstand im Bereich der Tasten). Display 128x32 OLED SSD1306.
// v3.00.01: Dritte unveröffente Testversion. Testversion mit Encoder und Menü. Testweise SH1101 Display getestet (größer).
// v4.00.01: Vierte unveröffentlichte Version. Dispay wieder zu Display SSD1306 (128x32 oder 128x64) geändert (über Jumper änderbar). Optokoppler auf 6N-138 geändert.
// v4.00.02: Erste veröffentlichte Version mit KY-040 Encoder, SSD1306 (nur 128x64 wegen Speicher), Encoder Library auf "SimpleRotary" geändert wegen Wokwi Simulator.
//
// Version ohne Menü
// https://www.ascii-codes.com/
// https://cdn-learn.adafruit.com/downloads/pdf/adafruit-gfx-graphics-library.pdf
// display.cp437(true);
// display.write(167);
// display.ssd1306_command(SSD1306_DISPLAYOFF);
// display.ssd1306_command(SSD1306_DISPLAYON);
// display.fillScreen(SSD1306_BLACK);
// display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Text invertieren
// display.setTextColor(SSD1306_WHITE); // Text normal
// display.ssd1306_command(SSD1306_SETCONTRAST)
// display.ssd1306_command(0 - 255);
// Simple Rotary Library:
// SimpleRotary rotary(EncoderPin A, EncderPinB, EncoderButton);
// i = rotary.rotate(); // 1 = CW | 2 = CCW
// i = rotary.push();
// i = rotary.pushLong(1000);
// i = rotary.pushTime();
// i = rotary.pushTime(); if ( i > 1000 ) { ... }
// rotary.resetPush();
// i = rotary.pushType(1000); // 1 = Short | 2 = Long
/*
* Todo:
* Akkordanzeige einbauen (https://github.com/k-a-r-g/Chords)
* Menü einbauen:
* Farben ändern (Off / On)
* Helligkeit ändern (Off / On)
* Sättigung ändern (Off / On)
* MIDI Kanal ändern / Transponierung ändern / MIDI Ein-/ oder Ausgang abfragen
? Akkuanzeige einbauen (Speicher Batterie)
? Farbmuster statt MIDI Anzeige?
? 3 Patches für Farben einbauen?
? MIDI in statt MIDI Out anzeigen?
? LEDs Nach Anschlag zu Farbe 1 faden?
? LED Stripe beschleunigen (möglich?)
Umlaute & Sonderzeichen
\132 - ä
\148 - ö
\129 - ü
\224 - ß
\142 - Ä
\153 - Ö
\154 - Ü
Port 7 in C ein/Ausschalten
PORTD |= (1<<PD7);
PORTD &= ~(1<<PD7);
Pin 5 in C abfragen (schneller als "digitalread":
pinStatus = (PIND & (1<<PD5));
Akkordtabelle: https://tomplay.com/de/piano-chords/b-major?notation=am
*/
//#define DEBUG // Debug Ausgaben
//#define DEBUG_MEM // Freien Speicher ausgeben
#define RESET_EEPROM // EEprom Inhalt initialisieren
#include <Adafruit_GFX.h> // Grafiklibrary einbinden
#include <Adafruit_SSD1306.h> // Adafriut SSD1306 Library einbinden
#include <Adafruit_NeoPixel.h> // LED Stripes (WS2812B) ansteuern
#include <EEPROM.h> // EEprom Zugriff ermöglichen
//#include <Rotary.h> // Rotary Encoder einbinden
#include <SimpleRotary.h> // Rotary Encoder einbinden
#define ver "4.00.02" // Programmversion
#define verDate "08.02.2025" // Erstellungsdatum
#define numLed 61 // Anzahl der LEDs/Tasten
#define screenWidth 128 // OLED Breite in Pixeln
#define screenHeight 64 // OLED Höhe in Pixeln (32 oder 64 jenach Display)
#define OLED_WHITE WHITE
#define OLED_BLACK BLACK
#define OLED_INVERSE SSD1306_INVERSE
#define displayAddress 0x3C // I²C Adresse des OLED Displays
#define encButton 2 // Encoder Taste D2 [2]
#define encDat 3 // Encoder Clock D3 [3]
#define encClk 4 // Encoder Data D4 [4]
#define ledStripe 5 // LED Steuerpin D5 [5]
#define enableUSB 6 // Update Steuerpin D6 [6] USB = MIDI Daten kommen über CD4066 an den Serial Port | LOW = MIDI Daten
#define intLed 13 // Interne LED für Fehleranzeige
byte keysOn[numLed + 1] = { }; // Array zum Speichern der gedrückten Keyboard Tasten
byte midiChannel; // MIDI Kanal (0 = Omni Mode/Alle Kanäle)
byte keysPressed = 0; // Anzahl gedürckter Tasten (!= 0 blendet Notensymbol ein)
byte unsigned displayTimeout; // Nach n Minuten Display abschalten
long unsigned displayTime; // DiaplayZeit (millis)
byte unsigned ledTimeout; // Nach n Minuten LEDs abschalten
long unsigned ledTime; // LED Zeit (millis)
byte unsigned displayContrast; // Displaykontrast (0-255)
byte unsigned displayDim; // Display dimmen?
byte unsigned displaySleep; // Display nach timeout ausschalten (Sleep) statt zu dimmen
bool ledStripeOn = true; // Sind die LEDs eingeschaltet?
byte midiTranspose; // MIDI Transpose (Notenversatz)
int unsigned midiChord; // Summe aller Notenwerte zur Akkordbestimmung
byte H1, S1, V1; // LED Werte für nicht gedrükte Tasten
byte H2, S2, V2; // LED Werte für gedrückte Tasten
byte currentMenuPage = 0; // 0 = Kein Menü, >0 = aktueller Menüpunkt
byte maxMenuPage; // Größte Menüseitenzahl
byte menuLevel = 0; // Wird > 0, wenn Menü angezeigt wird. 0 = Normaler Modus
byte menuItemSelected = 0;
long unsigned menuTimeout; // Wird gebraucht, um die Einstellungen mach 1 Minute ohne Änderung zu verlassen
// Texte für Notenanzeige
const char PROGMEM note01[] = "C";
const char PROGMEM note02[] = "C#";
const char PROGMEM note03[] = "D";
const char PROGMEM note04[] = "D#";
const char PROGMEM note05[] = "E";
const char PROGMEM note06[] = "F";
const char PROGMEM note07[] = "F#";
const char PROGMEM note08[] = "G";
const char PROGMEM note09[] = "G#";
const char PROGMEM note10[] = "A";
const char PROGMEM note11[] = "A#";
const char PROGMEM note12[] = "B";
const char* const notes[] PROGMEM = { note01, note02, note03, note04, note05, note06, note07, note08, note09, note10, note11, note12 };
//const char PROGMEM chord00[] = "?";
//const char PROGMEM chord01[] = "C";
//const char* const chordNames[] PROGMEM = { chord00, chord01 };
// Menütexte
const char mPage00[] PROGMEM = ""; const char PROGMEM mText00[] = "";
const char mPage01[] PROGMEM = " FARBE 1"; const char PROGMEM mText01[] = "Farbton\n'NoteOff'";
const char mPage02[] PROGMEM = " FARBE 1"; const char PROGMEM mText02[] = "Helligkeit\n'NoteOff'";
const char mPage03[] PROGMEM = " FARBE 1"; const char PROGMEM mText03[] = "S\65ttigung\n'NoteOff'";
const char mPage04[] PROGMEM = " FARBE 2"; const char PROGMEM mText04[] = "Farbton\n'NoteOn'";
const char mPage05[] PROGMEM = " FARBE 2"; const char PROGMEM mText05[] = "Helligkeit\n'NoteOn'";
const char mPage06[] PROGMEM = " FARBE 2"; const char PROGMEM mText06[] = "Sättigung\n'NoteOn'";
const char mPage07[] PROGMEM = " LED"; const char PROGMEM mText07[] = "Automat.\nabschalten";
const char mPage08[] PROGMEM = " DISPLAY"; const char PROGMEM mText08[] = "Display\ndimmen";
const char mPage09[] PROGMEM = " MIDI"; const char PROGMEM mText09[] = "Kanal 1-16\n(0=OMNI)";
const char mPage10[] PROGMEM = " MIDI"; const char PROGMEM mText10[] = "Notenvers.\n-12 bis +12";
const char mPage11[] PROGMEM = "SPEICHERN?"; const char PROGMEM mText11[] = "Einstell.\nspeichern";
const char mPage12[] PROGMEM = " INFO"; const char PROGMEM mText12[] = "Batterie-\nspannung";
const char mPage13[] PROGMEM = " INFO"; const char PROGMEM mText13[] = "Programm\nInformat.";
const char mPage14[] PROGMEM = " INFO"; const char PROGMEM mText14[] = "Programm-\nversion";
const char mPage15[] PROGMEM = " INFO"; const char PROGMEM mText15[] = "Zurück zum\nHauptmrnü";
// Grafiken
const unsigned char iconLogo1[] PROGMEM = /* 128 x 32 */ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x07, 0xe0, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x00, 0x07, 0xf0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x0f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x00, 0x0f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x00, 0x1f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x00, 0x1f, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xf8, 0x00, 0x3e, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0xf8, 0x00, 0x3e, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x78, 0x00, 0x3c, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf8, 0x00, 0x3e, 0x7c, 0x00, 0x7c, 0xf8, 0x3f, 0xf8, 0x7e, 0x1f, 0xfe, 0x1f, 0xff, 0x03, 0xff, 0xf8, 0x00, 0x3e, 0x7c, 0x00, 0x7c, 0xf8, 0x3f, 0xf8, 0x7e, 0x3f, 0xff, 0x1f, 0xff, 0x87, 0xff, 0xf8, 0x00, 0x3e, 0x3c, 0x00, 0x78, 0xf8, 0x3f, 0xf8, 0xfc, 0x7f, 0xff, 0x9f, 0xff, 0xc7, 0xff, 0xf8, 0x00, 0x3e, 0x3e, 0x00, 0xf8, 0xf8, 0x3f, 0xf8, 0xfc, 0x7f, 0xff, 0x9f, 0xff, 0xc7, 0xff, 0xf8, 0x00, 0x3e, 0x3e, 0x00, 0xf8, 0xf8, 0x3f, 0xf8, 0xf8, 0x7f, 0x7f, 0x9f, 0x8f, 0xc7, 0xf8, 0x00, 0x00, 0x3e, 0x1e, 0x00, 0xf0, 0xf8, 0x3f, 0xf9, 0xf8, 0x7e, 0x3f, 0x9f, 0x8f, 0xc7, 0xf0, 0x00, 0x00, 0x3e, 0x1f, 0x01, 0xf0, 0xf8, 0x3f, 0xf9, 0xf8, 0x7e, 0x3f, 0x9f, 0x8f, 0xc7, 0xf0, 0x00, 0x00, 0x3e, 0x1f, 0x01, 0xf0, 0xf8, 0x3f, 0xf9, 0xf0, 0x7e, 0x3f, 0x9f, 0x8f, 0xc7, 0xf0, 0x00, 0x00, 0x3e, 0x0f, 0x01, 0xe0, 0xf8, 0x3f, 0xff, 0xf0, 0x7e, 0x3f, 0x9f, 0xff, 0x87, 0xf1, 0xfc, 0x00, 0x3e, 0x0f, 0x83, 0xe0, 0xf8, 0x3f, 0xff, 0xe0, 0x7e, 0x3f, 0x9f, 0xff, 0x07, 0xf1, 0xfc, 0x00, 0x3e, 0x0f, 0x83, 0xe0, 0xf8, 0x3f, 0xff, 0xe0, 0x7e, 0x3f, 0x9f, 0xfe, 0x07, 0xf1, 0xfc, 0x00, 0x3e, 0x07, 0x83, 0xc0, 0xf8, 0x3f, 0xff, 0xf0, 0x7e, 0x3f, 0x9f, 0xff, 0x07, 0xf1, 0xf8, 0x00, 0x3e, 0x07, 0xc7, 0xc0, 0xf8, 0x3f, 0xf9, 0xf0, 0x7e, 0x3f, 0x9f, 0x9f, 0x87, 0xf1, 0xf8, 0x00, 0x3e, 0x07, 0xc7, 0xc0, 0xf8, 0x3f, 0xf9, 0xf8, 0x7e, 0x3f, 0x9f, 0x9f, 0xc7, 0xf1, 0xf8, 0x00, 0x3e, 0x03, 0xc7, 0x80, 0xf8, 0x3f, 0xf9, 0xf8, 0x7e, 0x3f, 0x9f, 0x9f, 0xc7, 0xf1, 0xf8, 0x00, 0x3e, 0x03, 0xef, 0x80, 0xf8, 0x3f, 0xf9, 0xf8, 0x7e, 0x3f, 0x9f, 0x8f, 0xc7, 0xf1, 0xf8, 0x00, 0x3e, 0x03, 0xef, 0x80, 0xf8, 0x3f, 0xf8, 0xfc, 0x7f, 0x7f, 0x9f, 0x8f, 0xc7, 0xfb, 0xf8, 0x00, 0x3e, 0x01, 0xff, 0x00, 0xf8, 0x3f, 0xf8, 0xfc, 0x7f, 0xff, 0x9f, 0x8f, 0xe7, 0xff, 0xf8, 0x00, 0x3e, 0x01, 0xff, 0x00, 0xf8, 0x3f, 0xf8, 0xfc, 0x7f, 0xff, 0x9f, 0x8f, 0xe7, 0xff, 0xf8, 0x00, 0x3e, 0x00, 0xfe, 0x00, 0xf8, 0x3f, 0xf8, 0x7e, 0x3f, 0xff, 0x1f, 0x87, 0xe3, 0xff, 0xf8, 0x00, 0x3e, 0x00, 0xfe, 0x00, 0xf8, 0x3f, 0xf8, 0x7e, 0x1f, 0xfe, 0x1f, 0x87, 0xe1, 0xff, 0xf8, 0x00, 0x3e, 0x00, 0x7c, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const unsigned char iconLogo2[] PROGMEM = /* 128 x 32 */ { 0x00, 0x07, 0xf0, 0x00, 0x01, 0xf0, 0x07, 0xcf, 0x3f, 0xff, 0xe1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x01, 0xf8, 0x0f, 0xcf, 0x3f, 0xff, 0xf1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x80, 0x01, 0xf8, 0x0f, 0xcf, 0x3f, 0xff, 0xf9, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xe0, 0x01, 0xfc, 0x1f, 0xcf, 0x3f, 0xff, 0xf9, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc7, 0xf1, 0xf0, 0x00, 0x3c, 0x1f, 0xc0, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x87, 0xf0, 0xf8, 0x01, 0xfe, 0x3f, 0xcf, 0x3c, 0x00, 0x79, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x07, 0xf0, 0x7c, 0x01, 0xfe, 0x3f, 0xcf, 0x3c, 0x00, 0x79, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x3c, 0x01, 0xef, 0x7b, 0xcf, 0x3c, 0x00, 0x79, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x1e, 0x01, 0xef, 0x7b, 0xcf, 0x3c, 0x00, 0x79, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x0e, 0x01, 0xe7, 0xf3, 0xcf, 0x3c, 0x00, 0xf9, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x0f, 0x01, 0xe7, 0xf3, 0xcf, 0x3f, 0xff, 0xf9, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x07, 0x01, 0xe3, 0xe3, 0xcf, 0x3f, 0xff, 0xf9, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x07, 0x01, 0xe3, 0xe3, 0xcf, 0x3f, 0xff, 0xf1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x03, 0x81, 0xe1, 0xc3, 0xcf, 0x3f, 0xff, 0xe1, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xe1, 0xc0, 0x01, 0xc3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe0, 0x03, 0xe3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe0, 0x03, 0xe3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0xc0, 0x01, 0xc3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x03, 0x81, 0xe0, 0x00, 0x0f, 0x0f, 0xff, 0xf9, 0xe0, 0x03, 0xcf, 0xff, 0xff, 0x70, 0x00, 0x00, 0x07, 0x01, 0xe0, 0x00, 0x0f, 0x1f, 0xff, 0xf9, 0xe0, 0x03, 0xcf, 0xff, 0xff, 0x70, 0x70, 0x07, 0x07, 0x01, 0xe0, 0x00, 0x0f, 0x3f, 0xff, 0xf9, 0xe0, 0x03, 0xcf, 0xff, 0xff, 0x78, 0xf8, 0x0f, 0x8f, 0x01, 0xe0, 0x00, 0x0f, 0x3f, 0xff, 0xf9, 0xe0, 0x03, 0xcf, 0xff, 0xff, 0x38, 0xf9, 0xcf, 0x8e, 0x01, 0xe0, 0x00, 0x0f, 0x3e, 0x00, 0x01, 0xe0, 0x03, 0xc0, 0x0f, 0x00, 0x3c, 0x73, 0xe7, 0x1e, 0x01, 0xe0, 0x00, 0x0f, 0x3c, 0xff, 0xf9, 0xe0, 0x03, 0xc0, 0x0f, 0x00, 0x1e, 0x03, 0xe0, 0x3c, 0x01, 0xe0, 0x00, 0x0f, 0x3c, 0xff, 0xf9, 0xff, 0xff, 0xc0, 0x0f, 0x00, 0x1f, 0x01, 0xc0, 0x7c, 0x01, 0xe0, 0x00, 0x0f, 0x3c, 0xff, 0xf9, 0xff, 0xff, 0xc0, 0x0f, 0x00, 0x0f, 0x80, 0x00, 0xf8, 0x01, 0xe0, 0x00, 0x0f, 0x3c, 0x00, 0x79, 0xff, 0xff, 0xc0, 0x0f, 0x00, 0x07, 0xe0, 0x01, 0xf0, 0x01, 0xf0, 0x00, 0x0f, 0x3e, 0x00, 0xf9, 0xe0, 0x03, 0xc0, 0x0f, 0x00, 0x03, 0xf8, 0x0f, 0xe0, 0x01, 0xff, 0xff, 0xcf, 0x3f, 0xff, 0xf9, 0xe0, 0x03, 0xc0, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xcf, 0x3f, 0xff, 0xf9, 0xe0, 0x03, 0xc0, 0x0f, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x1f, 0xff, 0xf9, 0xe0, 0x03, 0xc0, 0x0f, 0x00, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x7f, 0xff, 0xcf, 0x0f, 0xff, 0xe1, 0xe0, 0x03, 0xc0, 0x0f, 0x00 };
const unsigned char iconNote[] PROGMEM = /* 14 x 16 */ { 0x00, 0x70, 0x00, 0x78, 0x18, 0x6c, 0x1c, 0x64, 0x1e, 0x64, 0x1b, 0x60, 0x19, 0x60, 0x19, 0x60, 0x18, 0x60, 0x18, 0x60, 0x19, 0xe0, 0x1b, 0xe0, 0x7b, 0xe0, 0xf9, 0xc0, 0xf8, 0x00, 0x70, 0x00 };
const unsigned char iconSettings[] PROGMEM = /* 128 x 32 */ { 0x00, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xff, 0xf9, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf0, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x07, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x07, 0xe3, 0xc0, 0x3f, 0x98, 0xe0, 0x61, 0xe7, 0xf9, 0xfc, 0xc0, 0x60, 0x0f, 0xc0, 0x7e, 0x03, 0xf3, 0xf0, 0x3f, 0x98, 0xf0, 0x63, 0xf7, 0xf9, 0xfc, 0xc0, 0x60, 0x3f, 0xe0, 0xfc, 0x01, 0xf9, 0xf8, 0x30, 0x18, 0xf0, 0x66, 0x10, 0xc1, 0x80, 0xc0, 0x60, 0x30, 0x20, 0xfc, 0x00, 0xf9, 0xf8, 0x30, 0x18, 0xd8, 0x66, 0x00, 0xc1, 0x80, 0xc0, 0x60, 0x60, 0x00, 0x7c, 0x00, 0xf8, 0xf0, 0x30, 0x18, 0xdc, 0x67, 0x00, 0xc1, 0x80, 0xc0, 0x60, 0x60, 0x00, 0x38, 0xc0, 0xfc, 0xe0, 0x3f, 0x18, 0xcc, 0x63, 0xc0, 0xc1, 0xf8, 0xc0, 0x60, 0x63, 0xe0, 0x38, 0xe1, 0xfc, 0xe0, 0x3f, 0x18, 0xc6, 0x61, 0xe0, 0xc1, 0xf8, 0xc0, 0x60, 0x63, 0xe0, 0x7c, 0xf3, 0xfc, 0xf0, 0x30, 0x18, 0xc7, 0x60, 0x70, 0xc1, 0x80, 0xc0, 0x60, 0x60, 0x60, 0xfc, 0xff, 0xfc, 0xf8, 0x30, 0x18, 0xc3, 0x60, 0x30, 0xc1, 0x80, 0xc0, 0x60, 0x60, 0x60, 0xfc, 0xff, 0xfe, 0x78, 0x30, 0x18, 0xc1, 0xe4, 0x30, 0xc1, 0x80, 0xc0, 0x60, 0x30, 0x60, 0x7e, 0x7f, 0xff, 0x38, 0x3f, 0x98, 0xc1, 0xe7, 0xe0, 0xc1, 0xfc, 0xfe, 0x7f, 0x3f, 0xe6, 0x1e, 0x3f, 0xff, 0x80, 0x3f, 0x98, 0xc0, 0xe3, 0xc0, 0xc1, 0xfc, 0xfe, 0x7f, 0x0f, 0xc6, 0x0f, 0x0f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf0, 0x1f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xcf, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xff, 0xe7, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xf3, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xf1, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const unsigned char iconMidi[] PROGMEM = /* 16 x 16 */ { 0x03, 0xc0, 0x0f, 0xf0, 0x1b, 0xd8, 0x33, 0xcc, 0x60, 0x06, 0x40, 0x02, 0xc0, 0x03, 0xd8, 0x1b, 0xd8, 0x1b, 0xc0, 0x03, 0x4c, 0x32, 0x6d, 0xb6, 0x31, 0x8c, 0x18, 0x18, 0x0f, 0xf0, 0x03, 0xc0 };
const unsigned char iconLight[] PROGMEM = /* 16 x 16 */ { 0x03, 0xc0, 0x03, 0xc0, 0x07, 0xe0, 0x07, 0xe0, 0x0c, 0x30, 0x08, 0x10, 0x18, 0x18, 0x10, 0x08, 0xd0, 0x0b, 0x10, 0x08, 0x18, 0x18, 0x0c, 0x30, 0x07, 0xe0, 0x20, 0x04, 0x40, 0x82, 0x00, 0x80};
const unsigned char iconClock[] PROGMEM = /* 16 x 16 */ { 0x03, 0xc0, 0x21, 0x84, 0x2f, 0xf4, 0x3c, 0x3c, 0x30, 0xcc, 0x60, 0xe6, 0x60, 0xf6, 0xc0, 0xfb, 0xc0, 0x03, 0xc0, 0x03, 0x60, 0x06, 0x60, 0x06, 0x30, 0x0c, 0x1c, 0x38, 0x0f, 0xf0, 0x03, 0xc0 };
const unsigned char iconDim[] PROGMEM = /* 16 x 16 */ { 0x03, 0xc0, 0x0f, 0xf0, 0x18, 0x18, 0x31, 0xcc, 0x67, 0xe6, 0x46, 0x72, 0xc8, 0x3b, 0xd0, 0x1b, 0xc0, 0x03, 0xc0, 0x03, 0x40, 0x02, 0x60, 0x06, 0x30, 0x0c, 0x18, 0x18, 0x0f, 0xf0, 0x03, 0xc0 };
const unsigned char iconInfo[] PROGMEM = /* 16 x 16 */ { 0x07, 0xe0, 0x1f, 0xf8, 0x3f, 0x3c, 0x7f, 0x3e, 0x7f, 0xfe, 0xfc, 0x3f, 0xfe, 0x3f, 0xfe, 0x3f, 0xfe, 0x7f, 0xfc, 0x7f, 0xfc, 0xff, 0x7c, 0xfe, 0x78, 0xfe, 0x38, 0x3c, 0x1f, 0xf8, 0x07, 0xe0 };
const unsigned char iconHue[] PROGMEM = /* 16 x 16 */ { 0x03, 0xc1, 0x0f, 0xf3, 0x1c, 0xff, 0x3c, 0xcf, 0x7e, 0xcd, 0x67, 0xd9, 0xe7, 0xfe, 0xff, 0xfa, 0xff, 0xf2, 0xef, 0xf4, 0xc7, 0xa4, 0x6f, 0x28, 0x7f, 0x50, 0x3f, 0x60, 0x1e, 0x40, 0x0c, 0x00 };
const unsigned char iconSat[] PROGMEM = /* 16 x 16 */ { 0x07, 0xc0, 0x18, 0xf8, 0x20, 0xfc, 0x40, 0xfe, 0x40, 0xfe, 0xc0, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0x80, 0xff, 0xc0, 0xff, 0x40, 0xfe, 0x40, 0xfe, 0x20, 0xfc, 0x18, 0xf8, 0x07, 0xe0 };
const unsigned char iconVal[] PROGMEM = /* 16 x 16 */ { 0x01, 0x80, 0x41, 0x82, 0x21, 0x84, 0x10, 0x08, 0x01, 0x80, 0x07, 0xe0, 0x07, 0xe0, 0xef, 0xf7, 0xef, 0xf7, 0x07, 0xe0, 0x07, 0xe0, 0x01, 0x80, 0x10, 0x08, 0x21, 0x84, 0x41, 0x82, 0x01, 0x80 };
const unsigned char iconSave[] PROGMEM = /* 16 x 16 */ { 0x3f, 0xf0, 0x60, 0x18, 0xe1, 0x9c, 0xe1, 0x9e, 0xe0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x07, 0xe0, 0x07, 0xef, 0xf7, 0xe0, 0x07, 0xef, 0xf7, 0xe0, 0x07, 0xff, 0xff, 0x7f, 0xfe, 0x3f, 0xfc };
const unsigned char iconUnChecked[] PROGMEM = /* 16 x 16 */ { 0x07, 0xe0, 0x1f, 0xf8, 0x38, 0x1c, 0x70, 0x0e, 0x60, 0x06, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0x60, 0x06, 0x70, 0x0e, 0x38, 0x1c, 0x1f, 0xf8, 0x07, 0xe0 };
const unsigned char iconChecked[] PROGMEM = /* 16 x 16 */ { 0x07, 0xe0, 0x1f, 0xf1, 0x38, 0x03, 0x70, 0x07, 0x60, 0x0e, 0xc0, 0x1c, 0xd8, 0x38, 0xdc, 0x71, 0xce, 0xe3, 0xc7, 0xc3, 0xc3, 0x83, 0x61, 0x06, 0x70, 0x0e, 0x38, 0x1c, 0x1f, 0xf8, 0x07, 0xe0 };
//const char* const menuIcon[] PROGMEM = { iconSettings, iconMidi, iconLight };
const char* const menuPage[] PROGMEM = { mPage00, mPage01, mPage02, mPage03, mPage04, mPage05, mPage06, mPage07, mPage08, mPage09, mPage10, mPage11, mPage12, mPage13, mPage14, mPage15 };
const char* const menuText[] PROGMEM = { mText00, mText01, mText02, mText03, mText04, mText05, mText06, mText07, mText08, mText09, mText10, mText11, mText12, mText13, mText14, mText15 };
Adafruit_SSD1306 display(screenWidth, screenHeight, &Wire, -1, 1000000UL, 1000000UL); // Display initialisieren
Adafruit_NeoPixel pixels(numLed, ledStripe, NEO_GRB + NEO_KHZ800); // LED Stripe initialiseren
// Rotary Enc = Rotary(encClk, encDat); // Encoder einbinden
SimpleRotary Encoder(encDat, encClk, encButton); // Encoder eininden (ersetzt Rotary, da nicht in Wokwi enthalten)
// ################################################## SETUP ##################################################
void setup()
{
maxMenuPage = sizeof(menuPage) / sizeof(menuPage[0]) - 1; // Größte Menüseitenzahl errechnen
// EEprom Speicher vorbelegen
#ifdef RESET_EEPROM
EEPROM.update(0x01, 160); // Farbe #1 (* 256)
EEPROM.update(0x02, 240); // Sättigung #1
EEPROM.update(0x03, 64); // Helligkeit #1
EEPROM.update(0x11, 230); // Farbe #2
EEPROM.update(0x12, 255); // Sättigung #2
EEPROM.update(0x13, 64); // Helligkeit #2
EEPROM.update(0x21, 1); // MIDI Kanal
EEPROM.update(0x22, 0); // MIDI Transpose
EEPROM.update(0x31, 1); // LED Timeout (* 60000)
EEPROM.update(0x32, 1); // Display Timeout (* 60000)
EEPROM.update(0x33, 0); // Displaykontrast (0 - 255)
EEPROM.update(0x34, 1); // Display dimmen?
EEPROM.update(0x35, 0); // Display nach timeoout ausschalten (Sleep), statt zu dimmen
#endif
// Einstellungen aus EEprom lesen
H1 = EEPROM.read(0x01); // Farbe #1 (* 256)
S1 = EEPROM.read(0x02); // Sättigung #1
V1 = EEPROM.read(0x03); // Helligkeit #1
H2 = EEPROM.read(0x11); // Farbe #2
S2 = EEPROM.read(0x12); // Sättigung #2
V2 = EEPROM.read(0x13); // Helligkeit #2
midiChannel = EEPROM.read(0x21); // MIDI Kanal (0 = Omni Mode / Alle Kanäle)
midiTranspose = EEPROM.read(0x22); // MIDI Transpose
ledTimeout = EEPROM.read(0x31); // LED Timeout in Minuten (0 = aus)
displayTimeout = EEPROM.read(0x32); // Display Timeout (0 = 255)
displayContrast = EEPROM.read(0x33); // Displaykontrast
displayDim = EEPROM.read(0x34); // Display dimmen?
displaySleep = EEPROM.read(0x35); // Display nach Timeout ausschalten (Sleep) statt zu dimmen
Wire.setClock(3400000L); // 100000 (Standard 100kHz), 400000 (Fast Mode, 400kHz). Einige CPUs unterstützen auch: 10000 (Low Speed, 10kHz), 1000000 (Fast Mode Plus, 1MHz) und 3400000 (High Speed Mode, 3.4Mhz)
pinMode(intLed, OUTPUT); // Pin Interne LED
pinMode(enableUSB, OUTPUT); // Seriellen Port aktivieren (KEIN Serieller Betrieb mehr möglich!)
pinMode(ledStripe, OUTPUT); // Datenpin des LED Stripes
pinMode(encClk, INPUT); // Rotary Encoder CLK
pinMode(encDat, INPUT); // Rotary Encoder DAT
pinMode(encButton, INPUT_PULLUP); // Rotary Encoder BUTTON
// pinMode(vccBattery, INPUT); // Analogeingang A0 aktivieren
digitalWrite(intLed, LOW); // Interne LED abschalten
digitalWrite(enableUSB, LOW); // Seriellen Port für MIDI deaktivieren (über CD4066), damit Updates möglich sind. HIGH=USB | LOW=MIDI
Serial.begin(31250); //RS-232 initialisieren (311250 Baud)
// Display initialisieren
while(!display.begin(SSD1306_SWITCHCAPVCC, displayAddress)) // Displayinstanz starten
{
Serial.println(F("SSD1306 Displayfehler!"));
digitalWrite(intLed, HIGH);
delay(50);
digitalWrite(intLed, LOW);
delay(400);
}
display.setTextColor(OLED_WHITE);
//display.setRotation(0); // Display Rotation setzen
//display.setTextWrap(false); // Zeilenumbruch deaktivieren
display.setTextSize(2);
//display.ssd1306_command(SSD1306_SETCONTRAST); // Displaykontrast
//display.ssd1306_command(displayContrast);
//display.ssd1306_command(displayDim); // Display dimmen?
display.clearDisplay();
display.display(); // Display aktualisieren
// Einschaltmeldung und LED Selbsttest (1 x beim Einschalten) und Anzeige der Versionsnummer
for(byte y = 0; y < 129; y += 2)
{
display.clearDisplay();
display.drawBitmap(128 - y , 0, iconLogo1, screenWidth, 32, OLED_WHITE); // Logo 1 von rechts einblenden
display.drawBitmap(y - 128, 32, iconLogo2, screenWidth, 32, OLED_WHITE); // Logo 2 von links einblenden
display.display();
pixels.setPixelColor(0 + y / 2, pixels.ColorHSV( H1 * 256, 255, 64)); // Alle Tasten von links mit Farbe 1...
pixels.setPixelColor(61 - y / 2, pixels.ColorHSV( H2 * 256, 255, 64)); // und von rechts mit Farbe 2 füllen.
pixels.show();
}
delay(2000);
display.setTextColor(OLED_WHITE, OLED_BLACK);
for(byte y = 0; y < 33; y++)
{
//display.clearDisplay();
display.setTextSize(2);
display.fillRect(0, 64 - y , screenWidth, y * 2, OLED_BLACK); // Screen von Mitte nach oben und unten löschen
display.drawBitmap(0, -y, iconLogo1, screenWidth, 32, OLED_WHITE, OLED_BLACK); // Logo 1 (obere Hälfte)
display.drawBitmap(0, 32 + y, iconLogo2, screenWidth, 32, OLED_WHITE, OLED_BLACK); // Logo 2 (untere Hälfte)
//display.fillRect(0, 32 - y, screenWidth, y, OLED_WHITE);
//display.fillRect(0, 32 , screenWidth, y, OLED_WHITE);
display.fillRect(0, 32 - y, screenWidth, y, OLED_BLACK);
display.fillRect(0, 32 , screenWidth, y, OLED_BLACK);
display.drawRoundRect(0, 32 - y, screenWidth , y * 2, 7, OLED_WHITE); // Rahmen mit wachsender Höhe zeichnen
display.display();
pixels.setBrightness(map(y, 0, 32, 255, 0)); // Helligeit aller LED von 64 bis 0 dimmen.
//pixels.setPixelColor(32 - y, pixels.ColorHSV( H2 * 256, 0, 64)); // Alle Tasten in weiß von der ...
//pixels.setPixelColor(32 + y, pixels.ColorHSV( H2 * 256, 0, 64)); // Mitte nach außen einschalten.
pixels.show();
}
display.setCursor(18, 6);
display.print(F("v"));
display.print(F(ver));
display.setCursor(5, 27);
display.print(F(verDate));
display.setTextSize(1);
display.setCursor(10, 50);
display.print(F("Copyright by Gucky"));
display.display();
delay(2000);
pixels.setBrightness(64);
for(byte x = 0; x < 62; x++) // Softwareversion und LEDs...
{ // von rechts und links außen zur Mitte ausblenden.
display.drawFastVLine(x + 64, 3, screenHeight - 6, OLED_WHITE);
display.drawFastVLine(x + 63, 3, screenHeight - 6, OLED_BLACK);
display.drawFastVLine(64 - x, 3, screenHeight - 6, OLED_BLACK);
display.drawFastVLine(63 - x, 3, screenHeight - 6, OLED_WHITE);
display.drawRoundRect(0, 0, screenWidth , screenHeight, 7, OLED_WHITE);
display.display();
pixels.setPixelColor(30 - x / 2, pixels.ColorHSV( H1 * 256, S1, V1)); // Alle Tasten von links und rechts...
pixels.setPixelColor(30 + x / 2, pixels.ColorHSV( H1 * 256, S1, V1)); // mit Farbe 1 füllen.
pixels.show();
//byte y = map(x, 0, 62, 0, V1);
//pixels.setPixelColor( map(x, 0, 128, 0, numLed / 2), pixels.ColorHSV( H1 * 256 ,S1, y));
//pixels.setPixelColor(numLed - map(x, 0, 128, 0, numLed / 2), pixels.ColorHSV( H1 * 256 ,S1, y));
//pixels.show();
}
digitalWrite(intLed, LOW);
initScreen(); // Hauptbildschirm aufbauen (Grundlegende Elemente)
ledStripeOn = true;
digitalWrite(enableUSB, HIGH); // Ab jetzt ist MIDI auf den seriellen Port gelegt.
attachInterrupt(digitalPinToInterrupt(encButton), int1, FALLING); // Interrupt 1 für Taste initialisieren.
ledTime = millis();
displayTime = millis();
}
// ################################################## LOOP ##################################################
void loop()
{
menuLevel = !digitalRead(enableUSB);
//display.invertDisplay(!menuLevel);
//display.dim(displayDim);
//display.ssd1306_command(SSD1306_SETCONTRAST);
//display.ssd1306_command(!menuLevel * 255);
if(Serial.available() > 2) // Sind 3 Bytes oder mehr verfügbar?
{
byte midiCommand = Serial.read(); // Ja, dann das 1 Byte einlesen...
byte midiReceiveChannel = (midiCommand & B00001111) + 1; // MIDI Kanal errechnen
if(midiReceiveChannel != midiChannel && midiChannel) // Richtiger MIDI Kanal oder OMNI Mode (Kanal 0)?
{
Serial.flush(); // Seriellen Speicher löschen...
return; // ... und nicht reagieren
}
byte command = midiCommand >> 4; // MIDI Kommando errechnen
byte midiNoteNumber = Serial.read(); // Ja, dann Byte für Notennummer lesen...
byte midiVelocity = Serial.read(); // ... und Byte für Velocity lesen
if(!menuLevel)
initScreen();
if(command == 9) // Ist command = NoteOn?
{
if(!ledStripeOn)
powerOnLed();
keysPressed++; // Zähler für gedrückte Tasten incrementieren (für Noten Symbolanzeige)
keysOn[midiNoteNumber] = true; // Array für die gedrückten Tasten aktualisieren
showDisplay(midiChannel, midiNoteNumber, midiVelocity); // Display aktualisieren
pixels.setPixelColor(midiNoteNumber-36+midiTranspose, pixels.ColorHSV(H2 * 256, S2, V2)); // Leds aktualisieren
}
else if(command == 8) // Ist Command = NoteOff?
{
keysOn[midiNoteNumber] = false; // Array für gedrückte Testen aktualisieren
if(keysPressed) // Ist die Anzahl gedrückter Tasten > 0?
keysPressed--; // Ja, dann 1 abziehen
else
{
initScreen();
showDisplay(midiChannel, midiNoteNumber + midiTranspose, 255); // Display aktualisieren (velo 0 nicht ausgeben, wenn 255)
}
pixels.setPixelColor(midiNoteNumber-36+midiTranspose, pixels.ColorHSV(H1 * 256, S1, V1)); // LEDs aktualisieren
}
if(ledStripeOn)
pixels.show(); // Leds mit neuen Werten anzeigen
//showMidiChord();
// ########################################################################################################################################
// unsigned char encoderAction = Enc.process(); // Rotray Encoder abfragen
if(menuLevel)
{
display.clearDisplay();
display.drawBitmap(3, 2, iconSettings, 119, 28, OLED_WHITE);
display.drawRoundRect(0, 0, screenWidth , screenHeight, 7, OLED_WHITE);
display.display();
}
//else
// initScreen();
}
//unsigned char encoderAction = Enc.process(); // Rotray Encoder abfragen
//if(encoderAction == DIR_CW) // Drehung im Uhrzeigersinn?
//{
// currentMenuPage++; // Ja, dann Menüseitenzähler + 1
// if(currentMenuPage > maxMenuPage) // Zähler >
// currentMenuPage = 1;
// doMenu();
//}
//else if(encoderAction == DIR_CCW)
//{
// currentMenuPage--;
// if(currentMenuPage < 2)
// currentMenuPage = maxMenuPage;
// doMenu();
//}
byte encoderData;
encoderData = Encoder.rotate(); // 0 = Nichts, 1 = CW, 2 = CCW
if(encoderData) // Wird der Encoder gedreht?
{
if (encoderData == 1) // Im Uhrzeigersinn ...
{
currentMenuPage++; // ... dann Menüseitenzähler + 1
if(currentMenuPage > maxMenuPage) // Seite vorwärts
currentMenuPage = 1; // Wenn höchste Seite, dann zu Seite 1
}
else if (encoderData == 2) // Gegen den Uhrzeigersinn ...
{
currentMenuPage--; // ..., dann Menüseitenzähler - 1
if(currentMenuPage < 1) // Seite rückwärts
currentMenuPage = maxMenuPage; // Wenn kleonste Seite, dann letzte Seite
}
doMenu();
}
// ########################################################################################################################################
//if(millis() > (displayTimeout * 60000) + displayTime && !keysPressed) // Soll die Version noch angezeigt werden?
// screenSaver();
if(!menuLevel && millis() > menuTimeout + 60000) // Nach 60 Sekunden wird das Einstellungmenü ohne Speicherung verlasseen
{
digitalWrite(enableUSB, HIGH);
initScreen();
menuTimeout = millis();
}
if(millis() > (ledTimeout * 10000) + ledTime && ledTimeout && ledStripeOn) // LEDs nach n Minuten abschalten
powerOffLed();
}
// ########## Interrupt zur Abfrage des Buttons (Menü anzeigen) ##########
void int1()
{
while(digitalRead(encButton) == LOW) { }
digitalWrite(enableUSB, !digitalRead(enableUSB));
menuLevel = digitalRead(enableUSB);
}
// Display Grundaufbau (damit nur das nötigtse neu gezeichnet werden muss) für 128 x 32
void initScreen()
{
display.clearDisplay(); // Disply löschen
display.setTextColor(OLED_WHITE); // Schriftfarbe weiß
display.setTextSize(1);
display.setCursor(4, 3); // Midi Kanal anzeigen
display.print(F("CHAN"));
display.setTextSize(2);
display.setCursor(4, 13);
if(midiChannel < 10)
display.print(F(" "));
display.print(midiChannel);
display.drawFastVLine(29, 0, 32, OLED_WHITE); // Vertikale Linie neben Channel
display.fillRect(31, 14, 14, 16, OLED_BLACK); // Notensymbol löschen
//display.drawBitmap(31, 14, iconNote, 14, 16, OLED_WHITE); // Notensymbol zeichnen
display.setCursor(47, 6); // Notenname ausgeben
display.setTextSize(3); // Textgroße 3
display.print(F("--"));
display.drawFastVLine(82, 0, 32, OLED_WHITE); // Vertikale Linie nach Notenname
display.setTextSize(1);
display.setCursor(85, 9);
display.print(F("N"));
//display.fillRect(112, 12, 34, 16, OLED_BLACK); // Notenummer löschen
display.setTextSize(1);
display.setCursor(85, 23);
display.print(F("V"));
//display.fillRect(90, 16, 34, 16, OLED_BLACK); // Velocity löschen
/* Textaussgaben für 128 x 64
display.setTextSize(2); // Textgröße setzen
//display.setTextColor(OLED_WHITE); // Schriftfarbe weiß
//display.setContrast(displayContrast); // Displaykontrast
display.setCursor(3, 3); // Midi Kanal anzeigen
display.print(F("C"));
display.drawFastVLine(40, 0, 19, OLED_WHITE); // Vertikale Linie neben Channel
display.setCursor(43, 3); // Notennummer anzeigen
display.print(F("N"));
display.drawFastVLine(91, 0, 19, OLED_WHITE); // Vertikale Linie nach Notennummer
display.setCursor(95, 3); // MIDI Oktave aussgeben
display.print(F("O"));
display.drawFastHLine(0, 19, screenWidth, OLED_WHITE); // Obere horizontale Linie Zeichnen
display.setCursor(80, 47); // Velocity ausgeben
display.print(F("V"));
display.drawFastHLine(76, 42, 52, OLED_WHITE); // Horizontale Linie zwischen Transpose und Velocity zeichnen
display.setCursor(80, 24); // Transpose ausgeben
display.print(F("T"));
display.drawFastVLine(76, 20, 48, OLED_WHITE); // Vertikale Linie links von Tanspose und Velocity zeichnen
*/
display.drawRoundRect(0, 0, screenWidth, screenHeight, 7, OLED_WHITE); // Rahmen um alles zeichnen
display.display();
}
void showMidiChord()
{
int chord = 0;
for(byte x = 0; x < numLed; x++)
chord += x * keysOn[x];
display.fillRect(2, 32, 70, 30, OLED_BLACK);
display.setCursor(2, 32);
display.setTextSize(3);
display.print(chord);
display.display();
//Serial.println(chord);
}
// Displayinhalt aktualisieren
void showDisplay(byte receiveChannel, byte noteNumber, byte velo)
{
char noteName[12];
noteNumber -= 36;
byte midiOctave = noteNumber / 12; // Oktave berechnen
display.setTextSize(2);
display.fillRect(103, 2, 22, 14, OLED_BLACK); // Notenumber löschen
display.setCursor(103, 2);
if(noteNumber + 1 < 10) // Notenumber rechtsbündig anzeigen
display.print(F(" "));
display.print(noteNumber + 1);
if(velo && velo != 255) // Velocity nur ausgeben, wenn nicht 0 oder 255
{
display.fillRect(90, 16, 35, 14, OLED_BLACK); // Velocity löschen
display.setCursor(91, 16);
//if(velo == 0)
// display.print(F(" "));
//if(velo < 10) // Veocity rechtsbündig anzeigen.
// display.print(F(" "));
if(velo < 100)
display.print(F(" "));
display.print(velo);
}
if(keysPressed)
display.drawBitmap(31, 14, iconNote, 14, 16, OLED_WHITE); // Notensymbol zeichnen
else
{
display.fillRect(31, 14, 15, 16, OLED_BLACK); // Notensymbol löschen
display.fillRect(47, 6, 33, 21, OLED_BLACK); // Notenname löschen
}
display.fillRect(47, 6, 33, 21, OLED_BLACK); // Notenname löschen
display.setCursor(47, 6); // Notenname ausgeben
display.setTextSize(3); // Textgroße 3
strcpy_P(noteName, (char*)pgm_read_word(&(notes[noteNumber - (midiOctave * 12)])));
display.print(noteName);
/*
if(switchDisplay == 0)
{
switchDisplay = 2;
noteNumber -= 36;
initScreen();
}
byte midiOctave = noteNumber / 12; // Oktave berechnen
if(keysPressed) // Ist mindestens eine Taste gedrückt?
{ // Ja, dann Display aufbauen
display.fillRect( 18, 3, 21, 14, OLED_BLACK); // MIDI Kanal ausblenden
//if(midiChannel < 10) // Wenn Kanal einstellig...
// display.setCursor(28, 3);
//else
display.setCursor(16, 3);
display.print(midiChannel);
display.fillRect( 55, 3, 34, 14, OLED_BLACK); // Notennummer ausblenden
//if(note >= 0 && note < 10) // Notennummer 1... 10?
// display.setCursor(72, 3); // Ja, dann Cursor positionieren...
//else if(note > 9 && note < 100) // Notennummer 10 ... 99?
// display.setCursor(67, 3); // Ja, dann Cursor positionieren...
//else if(note > 99) // Notannummer > 100?
display.setCursor(55, 3); // Ja, dann Cursor positionieren...
display.print(noteNumber); // ... und Notennummer ausgeben
display.fillRect(114, 3, 12, 14, OLED_BLACK); // Oktave ausblenden
display.setCursor(114, 3);
display.print(midiOctave);
display.fillRect(28, 27, 45, 28, OLED_BLACK); // Notenname löschen
display.setTextSize(4); // Zeichengröße auf groß setzen für Notenausgabe
display.setCursor(28, 27); // Cursor positionieren
strcpy_P(noteName, (char*)pgm_read_word(&(notes[noteNumber - (midiOctave * 12)])));
display.print(noteName);
display.setTextSize(2); // Zeichengröße zurücksetzen
display.fillRect( 90, 24, 35, 14, OLED_BLACK);
display.setCursor(109, 24);
//if(midiTranspose - 36 == 0) // Transpose = 0 (Note Off)?
// display.setCursor(114, 24); // Ja, dann Cursor positionieren
//else if(midiTranspose - 36 > -10 && midiTranspose - 36 < 0) // Transpose -9 ... -1?
// display.setCursor(102, 24); // Ja, dann Cursor positionieren
//else if(midiTranspose - 36 < - 9 || midiTranspose - 36 > 9) // Transpose < -10 oder > 9?
display.setCursor(90, 24); // Cursor positionieren
display.print(midiTranspose); // Transpose ausgeben
display.fillRect( 92, 47, 33, 14, OLED_BLACK);
//if(velo < 10) // Velocity 1 ... 9?
// display.setCursor(114, 47); // Ja, dann Cursor positionieren
//else if(velo < 100) // Velocity 10 ... 99?
// display.setCursor(102, 47); // Ja, dann Cursor positionieren
//else // Velocity > 99?
display.setCursor(92, 47); // Ja, dann Cursor positionieren
display.print(velo); // Velocitywert ausgeben
display.drawBitmap(5, 44, iconNote, 18, 16, OLED_WHITE); // Notensymbol anzeigen
}
else
display.fillRect(5, 44, 18, 16, OLED_BLACK); // Notensymbol ausblenden
*/
display.display(); // Displayinhalt aktualisieren
displayTimeout = millis(); // Display Timeout setzen
ledTimeout = millis(); // LED Timeout zurücksetzen
}
void doMenu()
{
char mPage[maxMenuPage + 1];
char mText[22];
display.setTextSize(1);
display.fillRect(4, 12, 7, 9, OLED_WHITE);
display.fillRect(4, 21, 14, 9, OLED_WHITE);
display.setTextColor(OLED_BLACK);
display.setCursor(5, 13);
display.print(menuLevel);
display.print(currentMenuPage);
display.fillRect(3, 3, 122, 60, OLED_WHITE);
display.setTextSize(2);
display.setCursor(4, 4);
strcpy_P(mPage, (char*)pgm_read_word(&(menuPage[currentMenuPage])));
display.print(mPage);
display.setCursor(4, 17);
strcpy_P(mText, (char*)pgm_read_word(&(menuText[currentMenuPage])));
display.print(mText);
Serial.println(currentMenuPage);
Serial.println(mPage);
Serial.println(mText);
display.setTextColor(OLED_BLACK);
display.display();
/*
// Menu anzeigen
char mPage [maxMenuPage];
char mText [21];
display.clearDisplay();
display.setTextSize(2); // Textgröße 0, weil externer Font benutzt wird
display.drawFastHLine(0, 19, screenWidth, OLED_WHITE);
display.fillRoundRect(0, 0, screenWidth, 64, 7, OLED_HITE);
display.fillRect(0, 19, screenWidth, screenHeight - 19, OLED_BLACK);
display.drawRoundRect(0, 0, screenWidth, screenHeight, 7, OLED_WHITE);
display.setTextColor(SH110X_BLACK);
display.setCursor(5, 3);
strcpy_P(mPage, (char*)pgm_read_word(&(menuPage[currentMenuPage])));
display.print(mPage);
display.setTextColor(SH110X_WHITE);
display.setTextSize(1);
display.setCursor(4, 22);
strcpy_P(mText, (char*)pgm_read_word(&(menuText[currentMenuPage])));
//mText[] = mText[].replace("#VER#", ver);
display.print(mText);
//display.setTextColor(OLED_WHITE);
if(currentMenuPage > 1)
display.fillTriangle(4, 54, 14, 49, 14, 59, OLED_INVERSE);
if(currentMenuPage < maxMenuPage)
display.fillTriangle(124, 54, 114, 49, 114, 59, OLED_INVERSE);
// SSD1306_INVERSE
if(!menuItemSelected)
{
switch(currentMenuPage)
{
case 1: // Farbe 1 - Farbton
drawBar(H1, false);
break;
case 2: // Farbe 1 - Sättigung
drawBar(S1, true);
break;
case 3: // Farbe 1 - Helligkeit
drawBar(V1, true);
break;
case 4: // Farbe 2 - Farbton
drawBar(H2, false);
break;
case 5: // Farbe 2 - Sättigung
drawBar(S2, true);
break;
case 6: // Farbe 2 - Helligkeit
drawBar(V2, true);
break;
case 7: // LED Abschaltung nach n Minuten
break;
case 8: // Display kontrast
break;
case 9: // MIDI Kanal (1 ... 16 | 0 = OMNI)
break;
case 10: // Transpose
break;
// case 11: // Einstellungen speichern
// if(menuItemSelected)
// {
// EEPROM.update(0x01, H1); // Farbe #1 (* 256)
// EEPROM.update(0x02, S1); // Sättigung #1
// EEPROM.update(0x03, V1); // Helligkeit #1
// EEPROM.update(0x11, H2); // Farbe #2
// EEPROM.update(0x12, S2); // Sättigung #2
// EEPROM.update(0x13, V2); // Helligkeit #2
// EEPROM.update(0x21, midiChannel); // MIDI Kanal
// EEPROM.update(0x22, midiTranspose); // MIDI Transpose
// EEPROM.update(0x31, ledTimeout); // ledTimeout (* 60000)
// EEPROM.update(0x32, displayContrast; // Displaykontrast
// display.fillRect(2, 21, screenWidth - 2, 16, OLED_BLACK);
// display.setTextSize(1);
// display.setCursor(4, 22);
// display.setTextColor(SH110X_WHITE);
// display.print(F("Einstellungen\ngespeichert."));
// display.display();
// delay(1000);
// currentMenuPage = 0;
// initScreen();
// menuItemSelected = false;
// }
// break;
case 12: // Einstellungen nicht speichern
currentMenuPage = 0;
initScreen();
break;
case 13: // Batteriespannung anzeigen
break;
case 14: // Version anzeigen
break;
}
}
else
{
switch(menuItemSelected)
{
case 1: // Farbe 1 - Farbton
drawBar(H1, false);
break;
case 2: // Farbe 1 - Sättigung
drawBar(S1, true);
break;
case 3: // Farbe 1 - Helligkeit
drawBar(V1, true);
break;
case 4: // Farbe 2 - Farbton
drawBar(H2, false);
break;
case 5: // Farbe 2 - Sättigung
drawBar(S2, true);
break;
case 6: // Farbe 2 - Helligkeit
drawBar(V2, true);
break;
case 7: // LED Abschaltung nach n Minuten
break;
case 8: // Displaykontrast
break;
case 9: // MIDI Kanal (1 ... 16 | 0 = OMNI)
break;
case 10: // Transpose
break;
case 11: // Einstellungen speichern
break;
case 12: // Einstellungen nicht speichern
currentMenuPage = 0;
menuItemSelected = false;
initScreen();
break;
case 13: // Batteriespannung anzeigen
currentMenuPage = 0;
menuItemSelected = false;
initScreen();
break;
case 14: // Version anzeigen
break;
}
}
*/
display.display();
displayTime = millis(); // Displaytimeout setzen
ledTime = millis(); // LED Timeout setzen
}
void drawBar(byte value, byte filled) // Routine zum Zeichnen der Einstellbalken (Helligkeit, etc.)
{
byte x = map(value, 0, 255, 0, 118);
display.drawRoundRect(4, 32, screenWidth - 8, 15, 5, OLED_WHITE);
if(filled == false)
display.fillRect(5 + x, 21, 2, 7, OLED_WHITE);
}
void powerOnLed()
{
for(byte x = 0; x <= round(numLed / 2); x++)
{
pixels.setPixelColor((numLed / 2) + x, pixels.ColorHSV( H1 * 256, S1, V1));
pixels.setPixelColor((numLed / 2) - x, pixels.ColorHSV( H1 * 256, S1, V1));
pixels.show();
delay(10);
}
ledStripeOn = true;
ledTime = millis();
}
void powerOffLed()
{
//ledStripeOn = false; // LEDs sind aus
//for(byte x = 0; x <= round(numLed / 2); x++)
//{
// pixels.setPixelColor(x , pixels.ColorHSV( H1 * 256, S1, 0)); // ... LED Leiste von links ...
// pixels.setPixelColor(numLed - x, pixels.ColorHSV( H1 * 256, S1, 0)); // ... und rechts zur Mitte hin abschalten.
// pixels.show();
// delay(75); // Kurz warten, um den "Laufeffekt" zu simulieren
//}
//keysPressed = 0;
}
void screenSaver()
{
//display.ssd1306_command(SSD1306_SETCONTRAST);
//display.ssd1306_command(0);
//display.drawBitmap(0, 0, Logo2, screenWidth, screenHeight, OLED_WHITE);
//display.display();
//display.invertDisplay(false);
//display.startscrolldiagleft(0x00, 0x0F);
//while(!Serial.available())
//{
// byte x, y;
// display.clearDisplay();
// display.drawBitmap(x, 32 - y, Logo1, screenWidth, 32, OLED_WHITE);
// display.drawBitmap(x, 64 - y, Logo2, screenWidth, 32, OLED_WHITE);
// display.display();
// y++;
// if(y >= 96)
// {
// y = 0;
// x++;
// }
// delay(50);
//}
}
/*
void prettyPrint(const __FlashStringHelper* str, int width, bool rightadjust)
{
//usage: prettyPrint(F("012345"), 16, true);
if (!rightadjust)
Serial.print(str);
int length = strlen_P((PGM_P)str);
for (int i = 0; i < width - length; i++)
Serial.print(' ');
if (rightadjust)
Serial.print(str);
}
*/
// Freien Speicher ermitteln
//int freeRam()
//{
// extern int __heap_start, *__brkval;
// int v;
// return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
//}
61 x WS2812B
1
31
61
30
10
20
40
50