/* ************************************************************************************************
* NANOXYL v1.00.01 - [Begonnen am 10.02.2025]
*
* History:
* 10.02.2025: Erste Testversion (mit 2x16 I2C LCD), basierend auf Xyluino.
* Quelle: https://www.instructables.com/Xyluino-anw-Arduino-Driven-MIDI-Marimba-Xylophone-G/
*
* 15.02.2025: Zweite Testversion mit RGB Stripe und 3 Potis (über Multiplaxer).
+
*
* ToDo:
* - LCD Beleuchtung ein/aus/wenn nötig
* - Menüsystem einbauen
* - Einst. "Sound merken" speichert nach 60 Sek. den aktuellen Sound und zeigt ihn nach Kaltstart wieder an.
* - Einst. "Licht" => Immer | Aus | Kurz (bei neuer Anzeige)
* Einst.: Soundnamen ändern, Soundnummern MIDI Prog Ch. ändern
* Einst.: MIDI Kanal (1-16) pro Instrument (aus EEprom)
* Einst.: LCD Kontrast | Beleuchtung
* Einst.: Velocity => Wert (1 - 127) | Auto
* Einst.: Mindest Anschlagstärke, bis ein Pad erkannt wird (auf LCD anzeigen).
* Einst.: Pedal => Sustain | Volume | Aus
* Test: Pads, Sustain, LCD, Encoder, MIDI (Sendet C3 auf allen Kanälen)
* Test: Pad Spannnug(en)anzeigen
*
*
*
* ************************************************************************************************
/*
* Pin-Belegung für Multiplexer
* Multiplex0 D5
* Multiolex1 D4
* Multiplex2 D3
* Multiplex3 D2
*
* Analog Input der Multiplexer
* AnalogIn1 A0
* AnalogIn2 A1
* AnalogIn3 A2
*
* Taster-Belegung an den IO-Ports
* Taster1 D6
* Taster2 D7
* Taster3 D12
* Taster4 n.c.
*
* Display (Pin) - Arduino (Nano)
* GND (1) - GND
* VCC (2) - 5V
* SCK (3) - D13
* SDA (4) - D11
* RES (5) - D8
* RS (6) - D9
* CS (7) - D10
* LEDA (8) - 3.3V
*
* Evtl. Erweiterung für Sustain
* über Multiplexer 3, Kanäle 12,13,14,15
*
* Sonderzeochen LCD
* 0 = Doppelnote 4 = Pfeil rechts
* 1 = Note 5 =
* 2 = Diskette 6 =
* 3 = Pfeil links 7 = Unterstrich _
*
* 126 = -> 228 = µ
* 127 = <- 239 = ö
* 219 = ▯ 243 = ∞
* 223 = ° 244 = Ω
* 224 = Alpha 245 = Ü
* 225 = ä 247 = π
* 255 = ■
*
*
* EEprom Datenformat:
* Adresse 0 ... 9 = Name
* Adresse 10 = MIDI Kanal
* Adresse 11 = Velocity
* Adresse 12 = Threshold
* Adresse 13 =
* Adresse 14 =
* Adresse 15 =
*
*/
//*************************************************************************************************************************************************************
// Definitions *
//*************************************************************************************************************************************************************
#include <CD74HC4067.h> // 74HC4067 16 Fach Multiplexer einbinden
#include <LiquidCrystal_I2C.h> // I²C LCD Display einbinden (https://github.com/johnrickman/LiquidCrystal_I2C)
#include <SimpleRotary.h> // Rotary Encoder einbinden (https://github.com/mprograms/SimpleRotary)
#include <EEPROM.h> // EEprom einbinden
#include <Adafruit_NeoPixel.h> // RGB Stripe WS2812B einbinden
#define prgVersion "1.02.01" // Programmversion
#define prgDate "28.02.2025" // Programm Erstellungsdatum
#define multiplexMaxCount 12 // Max. Anzahl Multiplexer (1-15 * 3 Noten)
#define lcdAddress 0x27 // I²C Adresse des LCD Displays
#define lcdCols 16 // 16 Zeichen pro LCD Zeile
#define lcdRows 2 // 2 LCD Zeilen
#define ledNum 36 // Anzahl der Pads (für LED Stripe)
#define muxAdr3 2 // Multiplexer Adresse 0
#define muxAdr2 3 // Multiplexer Adresse 1
#define muxAdr1 4 // Multiplexer Adresse 2
#define muxAdr0 5 // Multiplexer Adresse 3
#define switchT1Pin 6 // Taster für Oktave -
#define switchT2Pin 7 // Taster für Oktave +
#define switchT3Pin 8 // Sustain Pedal
#define ledStripeDatPin 9 // WS2812B Datenausgang
#define encoderClkPin 10 // Encoder Pin Clock
#define encoderDatPin 11 // Encoder Pin Data
#define encoderBtnPin 12 // Taster des Encoders
#define ledMidiPin 13 // MIDI LED
#define muxA A0 // Gemeinsamer Eingang von Multiplexer A
#define muxB A1 // Gemeinsamer Eingang von Multiplexer B
#define muxC A2 // Gemeinsamer Eingang von Multiplexer C
#define SDA A4 // I²C LCD SDA
#define SCL A5 // I²C LCD SCL
#define EE_InstrumentNumber 0x00 // Letzte benutzte Instrumentennummer
#define EE_MidiChannel 0x01 // Zuletzt benutzter MIDI Kanal
#define EE_Velocity 0x02 // Anschlagstärke
#define EE_Threshhold 0x03 // Schwellwert, ab der ein Ton erkannt wird.
#define EE_BackLight 0x10 // LCD Hintergrundbeleuchtung
#define EE_Contrast 0x11 // LCD Kontrast
#define EE_Footswitch 0x13 // Was soll der Fußschalter machen? Sustain | Velotity | Instrument +| Nichts
byte instrumentNumber = 0; // Aktuelle Instrumentennummer (Wird von 0 an gezählt, Anzeige aber +1)
byte maxInstrumentNumber = 15; // Max. Instrumentennummer (Wird von 0 an gezählt)
byte midiChannel = 0; // MIDI Kanal (0 bis 15, Anzeige +1)
byte midiOctave = 2;
char* const midiOctaveName[5] = { "-2", "-1", " 0", "+1", "+2" }; // Angezeigter Oktavenname.
bool switchT1 = HIGH; //T1 = Oktave -
bool switchT1Last = LOW;
bool switchT2 = HIGH; //T2 = Oktave +
bool switchT2Last = LOW;
bool switchT3 = HIGH; //T3 = Sustainpedal
bool switchT3Last = LOW;
byte ledHue1 = 160; //Note Off Farbe (* 256)
byte ledHue2 = 230; //Note On Farbe (* 256)
byte ledSat1 = 255; //Note Off Sättigung
byte ledSat2 = 255; //Note On Sättigung
byte ledVal1 = 256; //Note Off Helligkeit
byte ledVal2 = 256; //Note On Helligkeit
unsigned long debounceTime; //Debounce Zet für alle Schalter / Buttons
unsigned long timeMidiTransmit; // Zeit zur Steuerung der MIDI LED (blau). LED wird für 100mS eingeschaltet
// Sonderzeichen für LCD Dislplay
//byte iconBell[8] = { 0x4, 0xe , 0xe , 0xe , 0x1f, 0x0 , 0x4};
byte iconNote[8] = { 0x2, 0x3 , 0x2 , 0xe , 0x1e, 0xc , 0x0};
//byte iconClock[8] = { 0x0, 0xe , 0x15, 0x17, 0x11, 0xe , 0x0};
//byte iconHeart[8] = { 0x0, 0xa , 0x1f, 0x1f, 0xe , 0x4 , 0x0};
//byte iconDuck[8] = { 0x0, 0xc , 0x1d, 0xf , 0xf , 0x6 , 0x0};
byte iconCheck[8] = { 0x0, 0x1 , 0x3 , 0x16, 0x1c, 0x8 , 0x0};
//byt iconCross[8] = { 0x0, 0x1b, 0xe , 0x4 , 0xe , 0x1b, 0x0};
byte iconReturn[8] = { 0x1, 0x1 , 0x5 , 0x9 , 0x1f, 0x8 , 0x4};
byte iconMidi[8] = { 0b00001, 0b00011, 0b00101, 0b01001, 0b01001, 0b01011, 0b11011, 0b11000 };
byte iconSave[8] = { 0b11110, 0b11111, 0b11111, 0b11111, 0b10001, 0b10001, 0b10001, 0b11111 };
//byte iconDel[8] = { 0b00000, 0b00000, 0b00111, 0b01001, 0b10001, 0b01001, 0b00111, 0b00000 };
//byte iconIns[8] = { 0b00000, 0b00000, 0b11100, 0b10010, 0b10001, 0b10010, 0b11100, 0b00000 };
byte iconKleiner[8] = { 0b00010, 0b00100, 0b01000, 0b10000, 0b10000, 0b01000, 0b00100, 0b00010 };
byte iconGroesser[8] = { 0b01000, 0b00100, 0b00010, 0b00001, 0b00001, 0b00010, 0b00100, 0b01000 };
byte iconLeft[8] = { 0b00010, 0b00110, 0b01110, 0b11110, 0b11110, 0b01110, 0b00110, 0b00010 };
byte iconRight[8] = { 0b11000, 0b11100, 0b11110, 0b11111, 0b11111, 0b11110, 0b11100, 0b11000 };
CD74HC4067 Multiplexer(muxAdr3, muxAdr2, muxAdr1, muxAdr0);
LiquidCrystal_I2C LCD(lcdAddress, lcdCols, lcdRows); // I²C adresse, LCD Zeichen, LCD Zeilen
SimpleRotary encoder(encoderClkPin, encoderDatPin, encoderBtnPin); // Clock Pin (CLK), Data Pin (DAT), Encoder Button (SW)
Adafruit_NeoPixel pixels(ledNum, ledStripeDatPin, NEO_GRB + NEO_KHZ800); // Anzahl der LEDs, Dataenpin, Typ)
/*
* Midi-Noten
* C2: 36
* C4: 60
* A4 (440Hz) : 69
* C7: 96 (nicht mehr spielbar)
*
* normal: C3, C4, C5, C6 (->H7)
* tiefer: C2, C3, C4, C5 (->H6)
*/
// Midi-Notenwerte
unsigned char PadNote[72] = { 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107 };
// Anschlagsempfindlichkeit der Pads
// Tuning-Bedarf ! Ch 10
int PadCutOff[48] = { 300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300 };
// Zeit, die jede angeschlageene Note "on" bleibt.
int MaxPlayTime[48] = { 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50 };
// Pads, die aktuell Noten spielen.
byte activePad[48] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
// Zähler, seit die Pads angeschlagen wurden, also die Noten spielen.
int PinPlayTime[48] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
int thisPlayTime = 0;
// Anschlagdynamik = true
boolean VelocityFlag = false;
int hitavg = 0;
byte pad = 0;
unsigned char status;
// Sustain-Pedal
boolean SustainStatus = false;
boolean LastSustainStatus = false;
// Instrumente
boolean InstrumentStatus = false;
boolean LastInstrumentStatus = false;
byte InstrumentCounter = 0;
String instrumentName[16] = { "Marimba", "Block", "Tube Bell", "Ping", "Glass", "Thunder", "Drums 1", "Drums 2", "Oldfield", "H. Zimmer", "Dsvinci", "Prog.12", "Prog.13", "Prog.14", "Prog.15", "Prog.16"};
byte Instrument[16] = { 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116 }; // Kann später gelöscht werden
byte instrumentMidiProg[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
byte instrumentMidiChan[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
byte instrumentThreshold[16] = { 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 44, 45, 46, 47, 48 };
int instrumentDuration[16] = { 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 131, 314, 315, 316 };
byte byte1;
byte byte2;
byte byte3;
byte count = 0; //which y pin we are selecting
// Multiplex- Array für die gelesenen Analog-Werte (Pads & Potis)
int multiplex[48];
// octave in Halbtonschritten, steuerbar evtl. über Taster
// std.: 12, bei shift 0
byte octave = 12;
byte lastoctave = 24;
boolean OctaveStatus = false;
boolean LastOctaveStatus = false;
int VThreshold = 0;
int VAmp = 0;
int longcount = 0;
// Variablen für Menüsystem
byte menuPage = 0; // 0 = Normalbetrieb | >0 = Menüseite
byte menuPageMax = 2;
byte subMenuPage = 0; // Welches Hauptmenü ist gewählt? 0 = Keins/Hauptmenü
byte subMenuPageMax = 4;
byte encoderBtn = HIGH;
byte encoderBtnLast = LOW;
unsigned long menuTimeout;
byte menuSelected = 0;
byte len;
// Menütexte
char const alphabet[] = { ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '#', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '\127' };
//char const alphabet[] = { " ", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "N", "M", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "_", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "#", "$", "%", "&", "/", "(", ")" , "=", "?", "+", "-", "*", "#", ",", ".", ";", ":", "<", "|", ">", "@", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };
char const PROGMEM settings01[] = "EINSTELLUNGEN";
char const PROGMEM settings02[] = "PROGRAMME";
char const PROGMEM settings03[] = "MIDI";
char const PROGMEM settings04[] = "RGB LED";
char const PROGMEM settings05[] = "SELBSTTEST";
char const PROGMEM settings06[] = "WERKSEINSTELL.";
char const PROGMEM settings07[] = "BEENDEN";
char* const PROGMEM menuSettings[] = { "", settings01, settings02, settings03, settings04, settings05, settings06, settings07 };
char currentMenuText[16]; // Maximale Textlänge + 1
byte alphabetLength = sizeof(alphabet) / sizeof(alphabet[0]);
byte alphabetCount = 0;
char currentChar[1];
//unsigned char menu1[] = /* Einstellungen */ { "Schwelle", "Länge", "LCD Licht", "LCD Contrast", "LCD Timeout", "", "", "" };
//unsigned char menu2[] = /* Programme */ { "Name", "MIDI Kanal", "MIDI Prg.", "Schwelle", "Dauer", "Anschlag", "Initialis.", "Tauschen" };
//unsigned char menu3[] = /* MIDI */ { "MIDI Kanal", "MIDI Thru", "", "", "", "", "", "" };
//unsigned char menu4[] = /* Fußschalter */ { "Nichts (Aus)", "Sustain", "Release", "Lautst.", "Legato"};
//unsigned char menu5[] = /* P1 / P2 / P3 */ { "Controller 1", "Controller 2", "Controller 3" };
//unsigned char menu6[] = /* Selbsttest */ { "Pad Test", "Encodertest", "Potitest", "LCD Test", "Prog. Version" };
//unsigned char menu7[] = /* Beenden */ { "SPEICHERN \1", " < ZURÜCK <" };
// Analoge Messvorgänge beschleunigen: https://forum.arduino.cc/t/faster-analog-read/6604/3
#define FASTADC 1
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
//*******************************************************************************************************************
// Setup
//*******************************************************************************************************************
void setup()
{
// Hardware Ein- und Ausgänge definieren
// Encoder
pinMode(encoderClkPin, INPUT);
pinMode(encoderDatPin, INPUT);
pinMode(encoderBtnPin, INPUT_PULLUP);
// Multiplexer Adressen
pinMode(muxAdr0, OUTPUT); // Multiplexer Adresse Bit 0
pinMode(muxAdr1, OUTPUT); // Multiplexer Adresse Bit 2
pinMode(muxAdr2, OUTPUT); // Multiplexer Adresse Bit 3
pinMode(muxAdr3, OUTPUT); // Multiplexer Adresse Bit 4
// MIDI LED (blau)
pinMode(ledMidiPin, OUTPUT);
// Ausgang für WS2812B LED Streifen
pinMode(ledStripeDatPin, OUTPUT);
// Analogeingänge für die Multiplexer
// muxA (A0) | muxB (A1) | muxC (A2)
// Eingänge für Bedientasten
pinMode(switchT1Pin, INPUT_PULLUP); // Oktave -
pinMode(switchT2Pin, INPUT_PULLUP); // Octave +
pinMode(switchT3Pin, INPUT_PULLUP); // Pedal
Serial.begin(31250); // connect to the serial port (31250) //midi standard
LCD.init();
LCD.createChar(0, iconMidi);
LCD.createChar(1, iconNote);
LCD.createChar(2, iconSave);
LCD.createChar(3, iconLeft);
LCD.createChar(4, iconRight);
//LCD.createChar(5, iconLeft2);
//LCD.createChar(6, iconRight2);
//LCD.createChar(5, iconCheck);
//LCD.createChar(6, iconCross);
//LCD.createChar(7, iconReturn);
LCD.backlight();
LCD.clear();
/*
// Mit Endoder ASCII Tabelle anzeigen
byte x = 0;
while(1)
{
byte rot = encoder.rotate();
if(rot)
{
LCD.clear();
LCD.print(x);
LCD.setCursor(0,1);
LCD.print(char(x));
if(rot == 1)
x++;
if(rot == 2)
x--;
}
}
*/
LCD.setBacklight(1);
LCD.home();
LCD.print(F("NANOXYL v"));
LCD.print(prgVersion);
for (byte x = 0; x <= ledNum / 2; x++)
{
pixels.setPixelColor( x, pixels.ColorHSV( ledHue1 * 256, 255, 127)); // Alle Tasten von links mit Farbe 1.
pixels.setPixelColor(ledNum - x, pixels.ColorHSV( ledHue2 * 256, 255, 127)); // Alle Tasten von rechts mit Farbe 2.
pixels.show();
delay(50);
}
LCD.setCursor(0, 1);
LCD.print(prgDate);
LCD.print(F(" GUCKY"));
delay(1500);
byte led = 0;
for(byte x = 0; x <= lcdCols / 2 + 1; x++)
{
for (byte y = 0; y < lcdRows; y++)
{
LCD.setCursor(x, y);
LCD.print(F(" "));
LCD.setCursor(lcdCols - x, y);
LCD.print(F(" "));
pixels.setPixelColor(ledNum / 2 + led, pixels.ColorHSV( ledHue1 * 256, 0, 0)); // Alle Tasten von links mit Farbe 1.
pixels.setPixelColor(ledNum / 2 - led, pixels.ColorHSV( ledHue2 * 256, 0, 0)); // Alle Tasten von rechts mit Farbe 2.
pixels.show();
led++;
if(digitalRead(encoderBtnPin) == LOW)
initEEprom();
}
delay(50);
}
/*
LCD.init();
LCD.begin();
LCD.clear();
LCD.home();
LCD.noDisplay();
LCD.display();
LCD.noBlink();
LCD.blink();
LCD.noCursor();
LCD.cursor();
LCD.scrollDisplayLeft(); // Ganzes Display 1 Zeichen nach links scrollen
LCD.scrollDisplayRight();// Ganzes Display 1 Zeichen nach rechts scrollen
LCD.leftToRight();
LCD.rightToLeft();
LCD.noBacklight();
LCD.backlight();
LCD.autoscroll(); // Unbekannt
LCD.noAutoscroll(); // Unbekannt
LCD.createChar();
LCD.setCursor();
LCD.print();
LCD.blink_on();
LCD.blink_off();
LCD.cursor_on();
LCD.cursor_off();
LCD.setBacklight(0|1);
LCD.load_custom_character();
LCD.printstr();
*/
/***
* ST7735-Chip initialisieren (INITR_BLACKTAB / INITR_REDTAB / INITR_GREENTAB)
* Muss bei AZ-Delivery 1.77'' 160x128px RGB TFT INITR_GREENTAB sein ansonsten Pixelfehler rechts und unten.
* Hinweis: https://github.com/adafruit/Adafruit-ST7735-Library/blob/master/examples/soft_spitftbitmap/soft_spitftbitmap.ino#L52
* Zeile 52-65
***/
// Start TFT Display
//TFTscreen.begin();
//TFTscreen.background(0, 0, 0);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.fill(0, 0, 0);
//TFTscreen.setRotation(3);
//TFTscreen.setTextSize(2);
//TFTscreen.text("Xyluino V0.75", 5, 5);
//TFTscreen.setTextSize(1);
//TFTscreen.text("50ms", 5, 25);
// TFTscreen.text("Octave: 0", 5, 35);
//TFTscreen.text("Instrument: Marimba", 5, 45);
//TFTscreen.text("Sustain active", 5, 55);
//TFTscreen.text("Octave Mid ", 5, 65);
instrumentNumber = EEPROM.read(EE_InstrumentNumber);
midiChannel = EEPROM.read(EE_MidiChannel);
updateDisplay();
// Prescale aufo 16 setzen, um schneller Analogeingänge zu messen.
sbi(ADCSRA, ADPS2) ;
cbi(ADCSRA, ADPS1) ;
cbi(ADCSRA, ADPS0) ;
timeMidiTransmit = millis();
}
//*******************************************************************************************************************
// Main Program
//*******************************************************************************************************************
void loop()
{
if(millis() >= menuTimeout + 60000 && menuPage)
{
menuPage = 0;
updateDisplay();
}
unsigned long time = micros();
LCD.setCursor(15, 1);
if(millis() < timeMidiTransmit + 250)
{
LCD.print((char)0); // NotenSymbol anzeigen
digitalWrite(ledMidiPin, HIGH);
}
else
{
LCD.print(F(" ")); // Notensymbol löschen
digitalWrite(ledMidiPin, LOW);
}
// midiLoopback();
readSensors();
checkSensors();
checkRotary(); // Fragt auch den Butto ab (Menü)
checkPedal();
//InstrumentChange();
checkOctave();
checkMenu();
//LCD.setCursor(12 , 0);
//LCD.print(micros() - time);
/*
Serial.print("P1: ");
Serial.print(multiplex[15]);
Serial.print(" | P2: ");
Serial.print(multiplex[15 + 16]);
Serial.print(" | P3: ");
Serial.print(multiplex[15 + 32]);
Serial.print(" | P4: ");
Serial.print(multiplex[0]);
Serial.print(" | T: ");
*/
//Serial.println(micros() - time);
}
void readSensors ()
{
for (int count = 0; count <= multiplexMaxCount; count++) // 11 = Multiplexer 0 ... 11 = 12 Noten
{
Multiplexer.channel(count);
//delay(10); // Für Simulator. In Schaltung später löschen.
//digitalWrite(muxAdr0, bitRead(count, 0)); // Setzt Multiplexeradresse S0 auf Bit 0 von "count".
//digitalWrite(muxAdr1, bitRead(count, 1)); // Setzt Multiplexeradresse S1 auf Bit 1 von "count".
//digitalWrite(muxAdr2, bitRead(count, 2)); // Setzt Multiplexeradresse S2 auf Bit 2 von "count".
//digitalWrite(muxAdr3, bitRead(count, 3)); // Setzt Multiplexeradresse S3 auf Bit 3 von "count".
// Analogwerte der Multiplexer lesen und in Array "multiplex[]" speichern.
// Poti P1 = multiplex[12] | Nicht belegt = multiplex[28] | Nicht belegt = multiplex[45]
// Poti P2 = multiplex[13] | Nicht belegt = multiplex[29] | Nicht belegt = multiplex[46]
// Poti P3 = multiplex[14] | Nicht belegt = multiplex[30] | Nicht belegt = multiplex[47]
// Poti P4 = multiplex[15] | Nicht belegt = multiplex[31] | Nicht belegt = multiplex[48]
multiplex[count] = analogRead(A0);
multiplex[count + 16] = analogRead(A1);
multiplex[count + 32] = analogRead(A2);
}
}
//*******************************************************************************************************************
// Check Sensors
//*******************************************************************************************************************
void checkSensors()
{
//function to get values of each piezo; only checking a single analog input pin
// Serial.println("check");
for(int sensecount=0; sensecount<=2; sensecount++)
{
//int sensecount = 0;
for(int pin = 0; pin <= 15; pin++)
{
pad = pin + (sensecount * 16);
hitavg = multiplex[pad]; //variable hitavg equals the voltage (0-1023) of the piezo
if(hitavg > PadCutOff[pad]) //if the voltage of the piezo is higher than the value of the
{ //"threshold" element in array PadCutOff, then:
if(activePad[pad] == false) //and if the pad wasn't already on or "active"
{
if(VelocityFlag == true)
//hitavg = 127 / ((1023 - PadCutOff[pin]) / (hitavg - PadCutOff[pin])); // With full range (Too sensitive ?)
hitavg = (hitavg / 8) -1; //and if you want force to correspond to volume (VelocityFlag=true),
//set voltage of piezo into volume (or "velocity") range of MIDI note (0-127)
else
hitavg = 127; //if you don't care, set velocity to max value (127)
MIDI_TX(144, PadNote[pad + octave], hitavg); //put together MIDI message; note / velocity / channel
// Serial.print(sensecount*16 + pin);
// Serial.print(" - ");
// Serial.print("Value: ");
// Serial.print(PadNote[pad]);
// Serial.print(" - ");
// int avgvolt;
// avgvolt = (hitavg / 8) -1;
// Serial.print(hitavg);
// Serial.print(" : ");
// Serial.println(avgvolt);
// Serial.println();
// Serial.println();
PinPlayTime[pad] = 0; //reset the pin play time
activePad[pad] = true; //make the pin active (was inactive)
}
else
PinPlayTime[pad] = PinPlayTime[pad] + 1; //if the pad was already active when it was hit, increment its play time
}
else if(activePad[pad] == true)
{
PinPlayTime[pad] = PinPlayTime[pad] + 1; //the pad is active, but it is not greater than cutoff, increment play time
if(PinPlayTime[pad] > MaxPlayTime[pad]) //but if it's already been on for the amount set in the MaxPlayTime array,
{
activePad[pad] = false; //turn it off
MIDI_TX(128, PadNote[pad + octave], 127); //velocity 0: OFF state
}
}
}
}
}
//*******************************************************************************************************************
// checkRotary
//*******************************************************************************************************************
void checkRotary()
{
byte rot = encoder.rotate();
//byte press = encoder.push();
//byte presslong = encoder.pushLong(1000);
//int time = encoder.pushTime(); // Gibt mS zurück
//int pushTime = encoder.pushTime(); // Gibt mS zurück
//int pushType = encoder.pushType(500); // 1 = Kurz | 2 = Lang gedrückt
if(rot)
{
if(!menuPage)
{
if(rot == 1 && instrumentNumber < maxInstrumentNumber)
instrumentNumber++;
if(rot == 2 && instrumentNumber > 0)
instrumentNumber--;
timeMidiTransmit = millis();
}
else
{
menuPageMax = sizeof(menuSettings) / sizeof(menuSettings[0]) - 1;
if(rot == 1 && alphabetCount < alphabetLength)
alphabetCount++;
//else if(alphabetCount == sizeof(alphabet) - 1)
// alphabetCount = 0;
if(rot == 2 && alphabetCount > 0) // alphabetCount = sizeof(alphabet) - 1;
alphabetCount--;
//else if(alphabetCount == 0)
// alphabetCount = sizeof(alphabet) - 1;
if(rot == 1 && menuPage < menuPageMax)
menuPage++;
if(rot == 2 && menuPage > 1)
menuPage--;
//strcpy_P(currentChar, (char*)pgm_read_word(&(alphabet[alphabetCount])));
Serial.print("Alphabet: ");
//Serial.print(currentChar);
Serial.print(alphabet[alphabetCount]);
Serial.print(" [");
Serial.print(alphabetCount);
Serial.print("/");
Serial.print(alphabetLength);
Serial.println("]");
}
updateDisplay();
LCD.setCursor(14, 1);
//Serial.print(currentChar);
LCD.print(alphabet[alphabetCount]);
menuTimeout = millis();
}
}
//*******************************************************************************************************************
// Check Pedal
//*******************************************************************************************************************
/*
// Sustain-Pedal abfragen und ControlChange senden
void checkPedal()
{
if(digitalRead(12) == LOW)
SustainStatus = true;
else
SustainStatus = false;
if ((SustainStatus == true) && (LastSustainStatus == false))
{
MIDI_CC(127);
LastSustainStatus = true;
}
if ((SustainStatus == false) && (LastSustainStatus == true))
{
MIDI_CC(0);
LastSustainStatus = false;
}
}
*/
//******************************************************************************************************************
//* CheckMenu
//******************************************************************************************************************
void checkMenu()
{
encoderBtn = digitalRead(encoderBtnPin);
// Beim Wechsel des Eingangs von LOW zu HIGH und nach der Entprellzeit:
if(encoderBtn == HIGH && encoderBtnLast == LOW && millis() - debounceTime > 5)
{
// Nichts machen
}
// Beim Wechsel des Eingangs von HIGH zu LOW und nach der Entprellzeit:
if(encoderBtn == LOW && encoderBtnLast == HIGH && millis() - debounceTime > 5)
{
if(!menuPage)
menuPage = 1;
else
menuPage = 0;
updateDisplay();
menuTimeout = millis();
}
encoderBtnLast = encoderBtn;
}
//******************************************************************************************************************
//* CheckPedal
//******************************************************************************************************************
void checkPedal()
{
switchT3 = digitalRead(switchT3Pin);
// Beim Wechsel des Eingangs von LOW zu HIGH und nach der Entprellzeit:
if (switchT3 == HIGH && switchT3Last == LOW && millis() - debounceTime > 5)
MIDI_CC(0);
// Beim Wechsel des Eingangs von HIGH zu LOW und nach der Entprellzeit:
if (switchT3 == LOW && switchT3Last == HIGH && millis() - debounceTime > 5)
MIDI_CC(127);
switchT3Last = switchT3;
}
/*
//*******************************************************************************************************************
// Check Instrument
//*******************************************************************************************************************
// Taster checken, Instrumentenwechsel GELB
void InstrumentChange()
{
if(digitalRead(A3) == LOW)
InstrumentStatus = true;
else
InstrumentStatus = false;
if ((InstrumentStatus == true) && (LastInstrumentStatus == false))
{
InstrumentCounter += 1;
if (InstrumentCounter > 7)
InstrumentCounter = 0;
MIDI_PC(Instrument[InstrumentCounter]);
if (InstrumentCounter == 0)
{
updateDisplay();
// LCD.clear();
// LCD.print(InstrumentCounter);
// LCD.print(F(":"));
// LCD.print(InstrumentName[InstrumentCounter]);
}
if (InstrumentCounter == 1)
{
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 45, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Instrument: Vibraphon ", 5, 45);
}
if (InstrumentCounter == 2)
{
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 45, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Instrument: Xylophon ", 5, 45);
}
if (InstrumentCounter == 3)
{
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 45, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Instrument: Glockenspiel ", 5, 45);
}
if (InstrumentCounter == 4)
{
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 45, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Instrument: Hackbrett ", 5, 45);
}
if (InstrumentCounter == 5)
{
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 45, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Instrument: Kalimba ", 5, 45);
}
if (InstrumentCounter == 6)
{
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 45, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Instrument: Wood Mallet ", 5, 45);
}
if (InstrumentCounter == 7)
{
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 45, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Instrument: Yellow ", 5, 45);
}
//TFTscreen.text("Instrument: " + InstrumentName[InstrumentCounter], 5, 45);
LastInstrumentStatus = true;
}
if ((InstrumentStatus == false) && (LastInstrumentStatus == true))
LastInstrumentStatus = false;
}
*/
//*******************************************************************************************************************
// Check Octave
//*******************************************************************************************************************
void checkOctave()
{
// Oktave-
switchT1 = digitalRead(switchT1Pin);
// Beim Wechsel des Eingangs von LOW zu HIGH und nach der Entprellzeit:
if (switchT1 == HIGH && switchT1Last == LOW && millis() - debounceTime > 5)
debounceTime = millis();
// Beim Wechsel des Eingangs von HIGH zu LOW und nach der Entprellzeit:
if (switchT1 == LOW && switchT1Last == HIGH && millis() - debounceTime > 5 && midiOctave > 0)
{
debounceTime = millis();
midiOctave--;
if(midiOctave == 255)
midiOctave = 0;
updateDisplay();
}
switchT1Last = switchT1;
// Oktave +
switchT2 = digitalRead(switchT2Pin);
// Beim Wechsel des Eingangs von LOW zu HIGH und nach der Entprellzeit:
if (switchT2 == HIGH && switchT2Last == LOW && millis() - debounceTime > 5)
debounceTime = millis();
// Beim Wechsel des Eingangs von HIGH zu LOW und nach der Entprellzeit:
if (switchT2 == LOW && switchT2Last == HIGH && millis() - debounceTime > 5 && midiOctave < 4)
{
debounceTime = millis();
midiOctave++;
if(midiOctave > 4)
midiOctave = 4;
updateDisplay();
}
switchT2Last = switchT2;
}
/*
void checkOctave2()
{
if(digitalRead(12) == LOW)
OctaveStatus = true;
else
OctaveStatus = false;
if ((OctaveStatus == true) && (LastOctaveStatus == false))
{
if (octave == 0)
{
octave = 12;
lastoctave = 0;
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 65, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Octave Mid ", 5, 65);
}
else if (octave == 24)
{
octave = 12;
lastoctave = 24;
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 65, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Octave Mid ", 5, 65);
}
else if ((octave == 12) && (lastoctave == 0))
{
octave = 24;
lastoctave = 12;
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 65, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Octave High ", 5, 65);
}
else if ((octave == 12) && (lastoctave == 24))
{
octave = 0;
lastoctave = 12;
//TFTscreen.stroke(0, 0, 0);
//TFTscreen.rect(5, 65, 150, 10);
//TFTscreen.stroke(255, 255, 255);
//TFTscreen.text("Octave Low ", 5, 65);
}
LastOctaveStatus = true;
}
if ((OctaveStatus == false) && (LastOctaveStatus == true))
LastOctaveStatus = false;
}
*/
//*******************************************************************************************************************
// Transmit MIDI Message
//*******************************************************************************************************************
void MIDI_TX(unsigned char MESSAGE, unsigned char PITCH, unsigned char VELOCITY)
{
status = MESSAGE + midiChannel;
Serial.write(status);
Serial.write(PITCH);
Serial.write(VELOCITY);
timeMidiTransmit = millis();
}
// MIDI Program Change
void MIDI_PC(unsigned char PROGRAMNUMBER)
{
status = 192 + midiChannel;
Serial.write(status);
Serial.write(PROGRAMNUMBER);
timeMidiTransmit = millis();
}
// MIDI Control Change
void MIDI_CC(unsigned char State)
{
status = 176 + midiChannel;
Serial.write(status);
Serial.write(64);
Serial.write(State);
timeMidiTransmit = millis();
}
//*************** MIDI LOOPBACK ******************//
void midiLoopback()
{
if(Serial.available() > 0)
{
byte1 = Serial.read();
byte2 = Serial.read();
byte3 = Serial.read();
MIDI_TX(byte1, byte2, byte3);
timeMidiTransmit = millis();
}
}
void updateDisplay()
{
//menuMaxPage = (sizeof(MeineStrings)/sizeof(MeineStrings[0]))
LCD.clear();
// Wenn menuPage = 0 ist, wird die normale Anzeige erneuert
if(!menuPage) // Wenn das Menü nicht angezeigt wird.
{
if(instrumentNumber > 254)
instrumentNumber = 0;
if(instrumentNumber > maxInstrumentNumber)
instrumentNumber = maxInstrumentNumber;
byte nr = instrumentMidiProg[instrumentNumber];
// 1. Displayzeile
if(instrumentNumber + 1 < 10) // Interne Programmnummer anzeigen
LCD.print(F(" "));
LCD.print(instrumentNumber + 1);
LCD.print(F(":"));
LCD.print(instrumentName[instrumentNumber]);
LCD.setCursor(12, 0);
LCD.print((char)126);
if(nr < 100)
LCD.print(F("0"));
if(nr < 10)
LCD.print(F("0"));
LCD.print(nr);
// 2. Displayzeile
byte chan = 12;
LCD.setCursor(0, 1);
LCD.print(F(" C")); // Channel
if(chan < 10)
LCD.print(F(" "));
LCD.print("12");
LCD.print(F(" V")); // Velocity
LCD.print(128);
LCD.print(F(" "));
LCD.print((char)1); // Oktavensymbol ( Note)
LCD.print(midiOctaveName[midiOctave]);
}
// Wenn menuPage > 0 ist, das Menü also angeezeigt werden soll
// Menüseiten in
else
{
strcpy_P(currentMenuText, (char*)pgm_read_word(&(menuSettings[menuPage])));
//Serial.print(strlen(currentMenuText));
//Serial.print(" ");
//Serial.print(currentMenuText);
//Serial.print(" [");
//Serial.print(menuPage);
//Serial.print("/");
//Serial.print(menuPageMax);
//Serial.println("]");
//Serial.println(floor(strlen(currentMenuText) / 2.0));
//Serial.println(strlen(currentMenuText) / 2.0);
if(menuPage > 1)
LCD.print((char)3);
else
LCD.print(" ");
len = floor(strlen(currentMenuText) / 2.0);
if(floor(strlen(currentMenuText) / 2.0) != strlen(currentMenuText) / 2.0)
len++;
LCD.setCursor(lcdCols / 2 - len, 0);
LCD.print(currentMenuText);
LCD.setCursor(lcdCols - 1, 0);
if(menuPage < menuPageMax)
LCD.print((char)4);
}
}
void initEEprom()
{
LCD.clear();
for(byte x = 0; x <= ledNum; x++)
pixels.setPixelColor(x, pixels.ColorHSV( 0 , 0, 0));
pixels.show();
LCD.setBacklight(1);
LCD.setCursor(2, 0);
LCD.print(F("NANOXYL WIRD"));
LCD.setCursor(0, 1);
LCD.print(F("INITIALISIERT..."));
delay(3000);
while(digitalRead(encoderBtnPin) == LOW) { }
LCD.clear();
LCD.print(F("BUTTON = ABBRUCH"));
LCD.setCursor(0, 1);
LCD.print(F("[--------------]"));
for(byte x = 1; x < 15; x++)
{
LCD.setCursor(x, 1);
LCD.print(char(255));
delay(750);
if(digitalRead(encoderBtnPin) == LOW)
{
LCD.clear();
asm volatile (" jmp 0");
}
}
EEPROM.update(EE_InstrumentNumber, 0);
EEPROM.update(EE_MidiChannel, 1);
EEPROM.update(EE_Velocity, 64);
EEPROM.update(EE_Threshhold, 30);
EEPROM.update(EE_BackLight, 1);
EEPROM.update(EE_Contrast, 255);
EEPROM.update(EE_Footswitch, 0);
String n = F("Prog. ");
for(byte adr = 1; adr < 17; adr++)
{
writeStringToEeprom(adr * 16, n + adr);
EEPROM.update(adr * 16 + 1, instrumentChannel);
//EEPROM.update(adr * 16 + 2, instrument);
//EEPROM.update(adr * 16 + 3, instrument);
//EEPROM.update(adr * 16 + 4, instrument);
//EEPROM.update(adr * 16 + 5, instrument);
//EEPROM.update(adr * 16 + 6, instrument);
//EEPROM.update(adr * 16 + 7, instrument);
instrumentName[adr] = readStringFromEeprom(adr * 16);
Serial.println(instrumentName[adr]);
}
LCD.clear();
LCD.print(F("INITIALISIERUNG"));
LCD.setCursor(4 ,1);
LCD.print(F("BEENDET"));
for(byte y = 0; y <= ledNum; y++)
pixels.setPixelColor(y, pixels.ColorHSV( 100 , 255, 128));
pixels.show();
delay(1500);
LCD.clear();
asm volatile (" jmp 0");
}
// Schreibt den Namen des aktuellen Instruments ins PEEprom.
void writeStringToEeprom(int startAddress, const String& strToWrite)
{
for (byte i = 0; i < 10; i++) // Max. Namenlänge + 1
EEPROM.update(startAddress + i, strToWrite[i]);
}
// Hier wird der Name des aktuellen Instruments aus dem EEProm gelesen
String readStringFromEeprom(int startAddress)
{
char data[10]; // Max. Namenlänge + 1
for (int i = 0; i < 9; i++)
data[i] = EEPROM.read(startAddress + i);
return String(data);
}36 x Piezo Sensor mit paralleler 5.1V ZDiode und 1MOhm an 3x Mux C0 - C11.
T1
T2
T3
Pedal
Nanoxyl v1.02.01
MIDI Out
PRG Wechsel |
Einstellungen
WS2812B - LEDs für 1. Oktave (0-11)
WS2812B - LEDs für 2. Oktave (12-23)
WS2812B - LEDs für 3. Oktave (24-35)
P1
(multiplex[12])
P3
(multiplex[14])
P4
(multiplex[15])
P2
(multiplex[13])
Mux A
Mux B
Mux C
- Oktave +
< Zeichen >