// Arduino Nano ATmega328P (Old Bootloader) COM10
// Arduino Pro Micro ATmega32U4 COM8
/*
C:\Program Files (x86)\Arduino\hardware\arduino\avr\boards.txt
micro.build.vid=0x2341 0x2343
micro.build.pid=0x8037 0x8039
micro.build.usb_product="Arduino Micro" "MIDI-eDrum"
*/
// programme : 68%
// mémoire : 40%
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
//#include "MIDIUSB.h"
#include <EncButton.h>
#include <EEPROM.h>
// Definition des couleurs de base au format RGB
#define CoulFond 0x0000 // RGB_COLOR16(0, 0, 0) ST77XX_BLACK
#define CoulDef 0xF800 // RGB_COLOR16(255,0,0) ST77XX_RED
#define CoulCadre 0xF81F // RGB_COLOR16(255,0,255) ST77XX_MAGENTA
#define CoulSel 0xFFFF // RGB_COLOR16(255,255,255) ST77XX_WHITE
//#define BLEU 0x001F // RGB_COLOR16(0, 0, 255) ST77XX_BLUE
//#define VERT 0x07E0 // RGB_COLOR16(0,255,0)
//#define CYAN 0x07FF // RGB_COLOR16(0,255,255)
//#define JAUNE 0xFFE0 // RGB_COLOR16(255,255,0)
#define TFT_MOSI 11 // Data out
#define TFT_SCLK 13 // Clock out
#define TFT_CS 14 // CS
#define TFT_RST 12 // Reset
#define TFT_DC 16 // D/C
#define TFT_BACKLIGHT 15 // Display backlight pin
// Encodeur rotatif
#define ENCODEUR_SW 03 // SW
#define ENCODEUR_CLK 05 // CLK
#define ENCODEUR_DT 04 // DT
//Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
EncButton encodeur(ENCODEUR_CLK, ENCODEUR_DT, ENCODEUR_SW);
/*
#define ANA_INPUT 13 // nb entrées analoqique
#define NUM_INPUT 0 // nb entrées numérique
#define NUM_OUTPUT 9 // nb sorties numérique
#define NOT 1
#define VOL 2
const byte anaPin[ANA_INPUT][3] =
{ // { Port, Note, Volume(%), Menu} // Element
{ A2, 35, 100 }, // Kick 3
//{ A, 35, 100 }, // Kick Double
{ A3, 38, 100 }, // Snare Hit 4
{ A5, 37, 100 }, // Snare SideStick 5
{A10, 47 ,100 }, // Tom 1 Hit ou Cymbal 3 (57) 6
//{ A, 01 ,100 }, // Tom 1 Rimshot
{ A1, 45 ,100 }, // Tom 2 Hit 7
//{ A, 01 ,100 }, // Tom 2 Rimshot
{ A0, 43 ,100 }, // Tom 3 Hit 8
//{ A, 01 ,100 }, // Tom 3 Rimshot
{ A4, 49 ,100 }, // Cymbal 1 9
{A11, 57 ,100 }, // Cymbal 2 10
//{ A0, 55 ,100 }, // Cymbal 3
{ A6, 00, 100 }, // Hi-Hat Tip 11
//{ A, 01, 100 }, // Hi-Hat Rimshot
//{ A, 01 ,100 }, // Hi-Hat Shaht
//{ A, 01 ,100 }, // Hi-Hat Bell
{ A7, 02 ,100 }, // Hi-Hat Pedal 18
{ A9, 51 ,100 }, // Ride Tip 19
{ A8, 52 ,100 } // Ride Shaht 20
//{ 5, 53 ,100 } // Ride Bell 21
};
/*
const byte numPin[NUM_OUTPUT] =
{
00, // D0 / Tx // Encodeur DT
01, // D1 / Rx // Encodeur CLK
02, // D2 / SDA // Ecran R/W
03, // D3 / SCL // Ecran RS
//04, // D4 / A6
//05, // D5 // Ride Bell
//06, // D6 / A7
07, // D7 // Encodeur SW
//08, // D8 / A8
//09, // D9 / A9
//10, // D10 / A10
11, // (D11) // Ecran E
//12, // (D12 / A11)
//13, // (D13)
14, // D14 / MISO // Ecran MISO
15, // D15 / SCLK // Ecran SCK
16, // D16 / MOSI // Ecran MOSI
//17, // RxLed
//18, // D18 / A0
//19, // D19 / A1
//20, // D20 / A2
//21, // D21 / A3
//30 // TxLed
};
*/
const byte LED = 7;
//const byte LedRx = 17/14; // Led Rx pinMode(LedRx, OUTPUT)
//const byte LedTx = 30; // Led Tx pinMode(LedTx, OUTPUT)
//bool InputState[ANA_INPUT]; // Etat des entrées
//byte canValue[ANA_INPUT]; // Valeur entrées analogiques
//byte valueMin, valueMax; // Valeur pédale d'expression
#define nbMenu 9
#define nbPage 4
#define nbIndex 22
//char* Menu[nbMenu] =
String Menu[nbMenu] =
{
"Volume ",
"Program",
"Channel",
"Kick ",
"Snare ",
"Tom ",
"Cymbal ",
"Hi-Hat ",
"Ride "
};
//char* TabMenu[nbIndex] =
String TabMenu[nbIndex] =
{
"Volume ",
"Program",
"Channel",
"Kick ",
"Hit ", // Snare
"RimShot", // Snare
"1 ", // Tom
"2 ", // Tom
"3 ", // Tom
"1 ", // Cymbal
"2 ", // Cymbal
"Tip ", // Hi-Hat
"1 ", // Hi-Hat
"2 ", // Hi-Hat
"A ", // Hi-Hat
"B ", // Hi-Hat
"C ", // Hi-Hat
"D ", // Hi-Hat
"Calib. ", // Hi-Hat
"Tip ", // Ride
"Shaht ", // Ride
"Bell " // Ride
};
//**** à gérer dans l'EEPROM ***************************
byte TabVal[nbIndex][2]= // {Note, Vol.}
{//{ Note, Volume(%)} // Element
{ 67, 00 }, // Volume
{ 00, 00 }, // Programm
{ 10, 00 }, // Channel
{ 35, 100 }, // Kick 3
//{ 35, 100 }, // Kick Double
{ 38, 100 }, // Snare Hit 4
{ 37, 100 }, // Snare SideStick 5
{ 47 ,100 }, // Tom 1 Hit ou Cymbal 3 (57) 6
//{ 00 ,100 }, // Tom 1 Rimshot
{ 45 ,100 }, // Tom 2 Hit 7
//{ 00 ,100 }, // Tom 2 Rimshot
{ 43 ,100 }, // Tom 3 Hit 8
//{ 00 ,100 }, // Tom 3 Rimshot
{ 49 ,100 }, // Cymbal 1 9
{ 57 ,100 }, // Cymbal 2 10
//{ 55 ,100 }, // Cymbal 3
{ 00, 100 }, // Hi-Hat Tip 11
//{ 00, 100 }, // Hi-Hat Rimshot
//{ 00 ,100 }, // Hi-Hat Shaht
//{ 00 ,100 }, // Hi-Hat Bell
{ 00, 100 }, // Hi-Hat 1 12
{ 00, 100 }, // Hi-Hat 2 13
{ 00, 100 }, // Hi-Hat A 14
{ 00, 100 }, // Hi-Hat B 15
{ 00, 100 }, // Hi-Hat C 16
{ 00, 100 }, // Hi-Hat D 17
{ 02 , 99 }, // Hi-Hat Pedal 18
{ 51 ,100 }, // Ride Tip 19
{ 52 ,100 }, // Ride Shaht 20
{ 53 ,100 } // Ride Bell 21
};
byte index; // [0 - 21]
byte indexMenu; // menu page 0 [0 - 8]
byte indexPage; // page en cours [0 - 3]
byte indexPage1; // Index temporaire page 1
byte indexPage2; // Index page 2 [0 - 1]
byte valeur; // valeur temporaire de l'Encodeur rotatif
byte valMax;
unsigned long time; // durée depuis l'activation de l'écran
bool TFTon;
void setup()
{
//**** charger depuis l'EEPROM si différent de 0 ****
for (byte i = 0; i < nbIndex; i++)
{
//TabVal[i][0] = 127;
//TabVal[i][1] = 100;
}
//Serial.begin(9600);
//Serial.print(F("Hello! ST77xx TFT Test"));
pinMode(2, OUTPUT);
digitalWrite(2, HIGH);
pinMode(TFT_BACKLIGHT, OUTPUT);
digitalWrite(TFT_BACKLIGHT, HIGH); // Backlight on
tft.initR(INITR_BLACKTAB); // Init ST7735S chip, black tab
tft.setTextWrap(false);
tft.setRotation(1);
tft.fillScreen(CoulCadre);
tft.setTextColor(CoulSel);
tft.setTextSize(4);
tft.setCursor(25, 30);
tft.print("Midi");
tft.setCursor(25, 70);
tft.print("eDrum");
delay(500);
tft.fillRect(4, 4 , 152, 37, CoulFond);
tft.drawFastHLine(0, 41, 160, CoulCadre);
tft.drawFastHLine(0, 42, 160, CoulCadre);
tft.drawFastHLine(0, 43, 160, CoulCadre);
tft.drawFastHLine(0, 44, 160, CoulCadre);
tft.fillRect(4, 45 , 152, 37, CoulFond);
tft.drawFastHLine(0, 82, 160, CoulCadre);
tft.drawFastHLine(0, 83, 160, CoulCadre);
tft.drawFastHLine(0, 84, 160, CoulCadre);
tft.drawFastHLine(0, 85, 160, CoulCadre);
tft.fillRect(4, 86 , 152, 37, CoulFond);
tft.setTextSize(3);
TextLigne(Menu[0], 1, CoulSel);
TextLigne(Menu[1], 2, CoulDef);
TextLigne(Menu[2], 3, CoulDef);
//tft.write(0x01)
Triangle(1, CoulSel);
/*
for (byte i = 0; i < ANA_INPUT; i++)
{
pinMode(anaPin[i][0], INPUT);
//canValue[i] = readana(anaPin[i]);
}
for (byte i = 0; i < NUM_INPUT; i++)
{
pinMode(numPin[i][0], INPUT);
}
*/
// initialize digital pin LED_BUILTIN as an output.
//pinMode(LED_BUILTIN, OUTPUT);
// Set MIDI baud rate:
//Serial.begin(115200); // 115200 (Serial MIDI USB), 31250 (MIDI Connector Application)
// initialisation des LEDs
//pinMode(LED, OUTPUT); // LEDs
//digitalWrite(LED, LOW); // Vert ON
time = millis();
TFTon = true;
}
void loop()
{
encodeur.tick();
//anaPin[ANA_INPUT][NOT]
//anaPin[ANA_INPUT][VOL]
/*
for (byte i = 0; i < ANA_INPUT; i++)
{
// Lecture pédale d'expression
byte value = readana(anaPin[0][i]);
if (canValue[i] != value) // Send CC only if th has changed.
{
canValue[i] = value;
//intensity = (uint8_t) (map(val, 0, 1023, 0, 127));
//(uint8_t) (map(val, 0, 1023, 0, 127));
// adapter en fonction de valueMin & valueMax
noteOn(0, anaPin[i][NOT], (value - valueMin) * 127 / (valueMax - valueMin) * anaPin[ANA_INPUT][VOL] / 100);
MidiUSB.flush();
long map(long x, long in_min, long in_max, long out_min, long out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
delay(50);
noteOff(0, anaPin[i][NOT], (value - valueMin) * 127 / (valueMax - valueMin) * anaPin[ANA_INPUT][VOL] / 100);
MidiUSB.flush();
}
}
*/
if (millis() - time > 30000) //tempo pour éteindre l'écran au bout de 30s
{
digitalWrite(TFT_BACKLIGHT, LOW); // Backlight on
TFTon = false;
}
switch (indexPage)
{
case 0: // page 0 (Catégorie)
EncodeurPage0();
break;
case 1: // page 1 (Type de frappe)
EncodeurPage1();
break;
case 2: // page 2 (Note / Volume)
EncodeurPage2();
break;
case 3: // page 3 (Valeur)
EncodeurValeur();
}
}
void EncodeurPage0()
{
bool modif = false;
if (encodeur.release() && TFTon)
{
Triangle(1, CoulFond);
Triangle(2, CoulSel);
Triangle(3, CoulFond);
TextLigne(Menu[indexMenu] , 1, CoulCadre);
if (indexMenu < 4)
{
index = indexMenu;
if (indexMenu < 3) // Volume, Program, Channel
{
indexPage = 3;
valeur = TabVal[index][indexPage2];
Triangle(2, CoulFond);
tft.fillRect(4, 86 , 152, 37, CoulFond);
Triangle(3, CoulSel);
TextLigne("Valeur " , 2, CoulDef);
TextLigne(String(TabVal[index][0]), 3, CoulSel);
if (index == 0) valMax = 100;
if (index == 1) valMax = 10;
if (index == 2) valMax = 16;
}
else if(indexMenu == 3) // Kick
{
indexPage = 2;
indexPage2 = 0;
TextLigne("Note ", 2, CoulSel);
TextLigne("Volume ", 3, CoulDef);
}
}
else
{
if(indexMenu == 4) // Snare
index = 4;
if(indexMenu == 5) // Tom
index = 6;
if(indexMenu == 6) // Cymbal
index = 9;
if(indexMenu == 7) // Hi-Hat
index = 11;
if(indexMenu == 8) // Ride
index = 19;
indexPage = 1;
indexPage1 = 0;
TextLigne(TabMenu[index] , 2, CoulSel);
TextLigne(TabMenu[index + 1], 3, CoulDef);
}
}
if (encodeur.left() && indexMenu < nbMenu -1 && TFTon)
{
indexMenu++;
modif = true;
}
if (encodeur.right() && indexMenu > 0 && TFTon)
{
indexMenu--;
modif = true;
}
if (modif)
{
switch (indexMenu)
{
case 0:
Triangle(1, CoulSel);
Triangle(2, CoulFond);
TextLigne(Menu[indexMenu ], 1, CoulSel);
TextLigne(Menu[indexMenu + 1], 2, CoulDef);
TextLigne(Menu[indexMenu + 2], 3, CoulDef);
break;
case nbMenu - 1:
Triangle(2, CoulFond);
Triangle(3, CoulSel);
TextLigne(Menu[indexMenu - 2], 1, CoulDef);
TextLigne(Menu[indexMenu - 1], 2, CoulDef);
TextLigne(Menu[indexMenu ], 3, CoulSel);
break;
default:
Triangle(1, CoulFond);
Triangle(2, CoulSel);
Triangle(3, CoulFond);
TextLigne(Menu[indexMenu - 1], 1, CoulDef);
TextLigne(Menu[indexMenu ], 2, CoulSel);
TextLigne(Menu[indexMenu + 1], 3, CoulDef);
}
modif = false;
}
if (encodeur.turn() || encodeur.release())
{
time = millis();
digitalWrite(TFT_BACKLIGHT, HIGH); // Backlight on
TFTon = true;
}
}
void EncodeurPage1()
{
bool modif = false;
const byte TabIndex[5][2] = {{4,1}, {5,2}, {6,1}, {7,7}, {8,2}};
byte i;
String MenuTemp;
String TabTemp;
for (byte j = 0; j < nbIndex; j++)
if (Menu[j][0] == indexMenu)
i = j;
if (encodeur.release() && TFTon)
{
index = index + indexPage1;
indexPage = 2;
//indexPage1 = 0;
indexPage2 = 0;
MenuTemp = Menu[indexMenu];
TabTemp = TabMenu[index];
if (MenuTemp == "Snare ") MenuTemp = "Sn. ";
if (MenuTemp == "Tom ") MenuTemp = "Tom ";
if (MenuTemp == "Cymbal ") MenuTemp = "Cymb. ";
if (MenuTemp == "Hi-Hat ") MenuTemp = "H-H. ";
if (MenuTemp == "Ride ") MenuTemp = "Ri. ";
if (TabTemp == "Hit ") TabTemp = "Hit";
if (TabTemp == "RimShot") TabTemp = "Rim";
if (TabTemp == "1 ") TabTemp = "1";
if (TabTemp == "2 ") TabTemp = "2";
if (TabTemp == "3 ") TabTemp = "3";
if (TabTemp == "A ") TabTemp = "A";
if (TabTemp == "B ") TabTemp = "B";
if (TabTemp == "C ") TabTemp = "C";
if (TabTemp == "D ") TabTemp = "D";
if (TabTemp == "Calib. ") TabTemp = "Ca";
if (TabTemp == "Tip ") TabTemp = "Tip";
if (TabTemp == "Shaht ") TabTemp = "Sha";
if (TabTemp == "Bell ") TabTemp = "Bel";
TextLigne(MenuTemp + TabTemp, 1, CoulCadre);
if (index == 18)
{
TextLigne("Min. ", 2, CoulSel);
TextLigne("Max. ", 3, CoulDef);
if (index == 0) valMax = 100;
}
else
{
TextLigne("Note ", 2, CoulSel);
TextLigne("Volume ", 3, CoulDef);
}
//Triangle(1, CoulFond);
Triangle(2, CoulSel);
Triangle(3, CoulFond);
}
if (encodeur.left() && indexPage1 < TabIndex[i][1] && TFTon)
{
indexPage1++;
//index++;
modif = true;
}
if (encodeur.right() && indexPage1 > 0 && TFTon)
{
indexPage1--;
//index--;
modif = true;
}
if (modif)
{
if ( indexPage1 < TabIndex[i][1] )
{
Triangle(2, CoulSel);
Triangle(3, CoulFond);
TextLigne(TabMenu[index + indexPage1] , 2, CoulSel);
TextLigne(TabMenu[index + indexPage1 + 1], 3, CoulDef);
}
else
{
Triangle(2, CoulFond);
Triangle(3, CoulSel);
TextLigne(TabMenu[index + indexPage1 -1], 2, CoulDef);
TextLigne(TabMenu[index + indexPage1] , 3, CoulSel);
}
modif = false;
}
if (encodeur.turn() || encodeur.release())
{
time = millis();
digitalWrite(TFT_BACKLIGHT, HIGH); // Backlight on
TFTon = true;
}
}
void EncodeurPage2()
{
bool modif = false;
if (encodeur.release() && TFTon)
{
indexPage = 3;
valeur = TabVal[index][indexPage2];
Triangle(2, CoulFond);
tft.fillRect(4, 86 , 152, 37, CoulFond);
Triangle(3, CoulSel);
if (index == 18)
TextLigne(indexPage2 ? "Max. " : "Min. ", 2, CoulDef);
else
TextLigne(indexPage2 ? "Volume " : "Note ", 2, CoulDef);
TextLigne(String(TabVal[index][indexPage2]), 3, CoulSel);
valMax = indexPage2 ? 100 : 127;
}
if (encodeur.left() && indexPage2 < 1 && TFTon)
{
indexPage2++;
modif = true;
}
if (encodeur.right() && indexPage2 > 0 && TFTon)
{
indexPage2--;
modif = true;
}
if (modif)
{
switch (indexPage2)
{
case 0: // Note
Triangle(2, CoulSel);
Triangle(3, CoulFond);
if (index == 18)
{
TextLigne("Min. ", 2, CoulSel);
TextLigne("Max. ", 3, CoulDef);
}
else
{
TextLigne("Note ", 2, CoulSel);
TextLigne("Volume ", 3, CoulDef);
}
break;
case 1: // Volume
Triangle(2, CoulFond);
Triangle(3, CoulSel);
if (index == 18)
{
TextLigne("Min. ", 2, CoulDef);
TextLigne("Max. ", 3, CoulSel);
}
else
{
TextLigne("Note ", 2, CoulDef);
TextLigne("Volume ", 3, CoulSel);
}
}
modif = false;
}
if (encodeur.turn() || encodeur.release())
{
time = millis();
digitalWrite(TFT_BACKLIGHT, HIGH); // Backlight on
TFTon = true;
}
}
void EncodeurValeur()
{
bool modif = false;
if (encodeur.release() && TFTon)
{
if (encodeur.pressFor() < 500)
{
indexPage = 0;
Triangle(1, CoulSel);
Triangle(2, CoulFond);
Triangle(3, CoulFond);
TextLigne(Menu[indexMenu ], 1, CoulSel);
TextLigne(Menu[indexMenu + 1], 2, CoulDef);
TextLigne(Menu[indexMenu + 2], 3, CoulDef);
//*** Sauvegarder la valeur en mémoire ***
TabVal[index][indexPage2] = valeur;
}
}
if (encodeur.left() && TFTon)
{
if (valeur < valMax)
{
valeur++;
modif = true;
}
}
if (encodeur.right() && TFTon)
{
if (valeur > 0)
{
valeur--;
modif = true;
if (valeur == 99 || valeur == 9)
{
tft.setCursor(19, 93);
tft.print(" ");
}
}
}
if (encodeur.leftH() && TFTon)
{
if (valeur < valMax - 10)
{
modif = true;
if(valeur < valMax - 11)
valeur += 10;
else if(valeur < valMax - 1)
valeur = valMax;
}
else if (valeur < valMax)
{
modif = true;
valeur = valMax;
}
}
if (encodeur.rightH() && TFTon)
{
modif = true;
if(valeur >= 10)
{
modif = true;
valeur -= 10;
if ((valeur >= 90 && valeur < 100) || valeur < 10)
{
tft.setCursor(19, 93);
tft.print(" ");
}
}
else if(valeur > 0)
{
modif = true;
valeur = 0;
}
}
if(modif)
{
tft.setCursor(19, 93);
tft.print(valeur);
modif = false;
}
if (encodeur.turn() || encodeur.release())
{
time = millis();
digitalWrite(TFT_BACKLIGHT, HIGH); // Backlight on
TFTon = true;
}
}
void TextLigne(String Texte, byte nLigne, unsigned int couleur)
{
byte x;
switch (nLigne)
{
case 1:
x = 11;
break;
case 2:
x = 52;
break;
case 3:
x = 93;
break;
}
tft.setTextColor(couleur, CoulFond);
tft.setCursor(19, x);
tft.print(Texte);
}
void Triangle(byte nLigne, unsigned int couleur)
{
switch (nLigne)
{
case 1:
tft.fillTriangle( 7, 16, 15, 24, 7, 32, couleur);
tft.fillTriangle(153, 16, 145, 24, 153, 32, couleur);
break;
case 2:
tft.fillTriangle( 7, 57, 15, 65, 7, 73, couleur);
tft.fillTriangle(153, 57, 145, 65, 153, 73, couleur);
break;
case 3:
tft.fillTriangle( 7, 98, 15, 106, 7, 114, couleur);
tft.fillTriangle(153, 98, 145, 106, 153, 114, couleur);
}
}
/*
// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).
void noteOn(byte channel, byte pitch, byte velocity)
{
midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOn);
}
void noteOff(byte channel, byte pitch, byte velocity)
{
midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOff);
}
byte readana(byte pin)
{
return analogRead(pin) >> 3; // Convert from 10bit value to 7bit.
}
*/