#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Bounce2.h>
// Definições do display OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define BUTTON1_PIN 2
#define BUTTON2_PIN 3
#define BUTTON3_PIN 4
#define POT_PIN A0 // Pino analógico para o potenciômetro
// Tempo em milissegundos para considerar um pressionamento longo
#define LONG_PRESS_TIME 1000
// Intervalo máximo em milissegundos entre cliques para considerar duplo ou triplo clique
#define CLICK_INTERVAL 500
// Estrutura para armazenar type, val e channel
struct ButtonAction {
int type;
int val;
int channel;
};
const int numButtons = 3;
const int numClicks = 3;
Bounce debouncers[numButtons];
unsigned long pressStartTime[numButtons] = {0};
int clickCount[numButtons] = {0};
bool longPressDetected[numButtons] = {false};
bool simultaneousPressDetected[2] = {false};
// Variáveis de estado para o toggle
bool toggleStateButtonActions[numButtons][numClicks] = {false};
bool toggleStateSimultaneousActions[2] = {false};
bool toggleStateLongpressActions[numButtons] = {false};
// Atualiza o valor de val para toggle
int getToggleValue(bool &toggleState) {
toggleState = !toggleState;
return toggleState ? 127 : 0;
}
// Variável para armazenar o banco atual
int currentBank = 1;
const int maxBank = 1;
int lastStatePot = 0; // Último valor lido do potenciômetro
int statePot = 0; // Último valor lido do potenciômetro
// Variáveis de estado para o modo de configuração
bool configMode = false;
int selectedButton = -1;
int selectedClickType = -1;
int selectedPotValue = 0;
// Matriz para armazenar as ações para cada botão e banco
ButtonAction buttonActions[numButtons][numClicks] = {
{{70, 127, 1}, {71, 127, 1}, {72, 127, 1}}, // Botão 1, Banco 1 (simples, duplo, triplo)
{{73, 127, 1}, {74, 127, 1}, {75, 127, 1}}, // Botão 2, Banco 1
{{76, 127, 1}, {77, 127, 1}, {78, 127, 1}} // Botão 3, Banco 1
};
// Matriz para armazenar as ações simultâneas
ButtonAction simultaneousActions[2] = {
{79, 127, 1}, // Botões 1 e 2
{80, 127, 1} // Botões 2 e 3
};
// Matriz para armazenar as ações de pressionamento longo
ButtonAction longpressActions[numButtons] = {
{81, 127, 1}, // Botão 1
{82, 127, 1}, // Botão 2
{83, 127, 1} // Botão 3
};
ButtonAction getActionForButtonAndBank(int buttonIndex, int clickCount) {
if (buttonIndex >= 0 && buttonIndex < numButtons && clickCount >= 1 && clickCount <= numClicks) {
buttonActions[buttonIndex][clickCount - 1].val = getToggleValue(toggleStateButtonActions[buttonIndex][clickCount - 1]);
return buttonActions[buttonIndex][clickCount - 1];
}
return {-1, -1, -1}; // Ação inválida
}
ButtonAction getSimultaneousAction(int index) {
if (index >= 0 && index < 2) {
simultaneousActions[index].val = getToggleValue(toggleStateSimultaneousActions[index]);
return simultaneousActions[index];
}
return {-1, -1, -1}; // Ação inválida
}
ButtonAction getLongpressAction(int buttonIndex) {
if (buttonIndex >= 0 && buttonIndex < numButtons) {
longpressActions[buttonIndex].val = getToggleValue(toggleStateLongpressActions[buttonIndex]);
return longpressActions[buttonIndex];
}
return {-1, -1, -1}; // Ação inválida
}
void processButtonClicks(int buttonIndex, int clickNumber) {
if (configMode) {
if (clickNumber == 4 && buttonIndex == 0) {
exitConfigMode();
} else {
handleConfigMode(buttonIndex, clickNumber, -1);
}
} else {
if (buttonIndex == 0 && clickNumber == 4) {
enterConfigMode();
} else {
ButtonAction action = getActionForButtonAndBank(buttonIndex, clickNumber);
sendControlChange(action);
}
}
}
void handleConfigMode(int buttonIndex, int clickNumber, int potValue) {
static unsigned long lastMessageTime = 0; // Controla o tempo das mensagens temporárias
// Seleção inicial do botão e do tipo de clique
if (selectedButton == -1 && selectedClickType == -1) {
selectedButton = buttonIndex + 1; // Ajuste para valores de 1 a 3
// Define o tipo de clique
selectedClickType = clickNumber;
// Atualiza o display
updateDisplay(selectedButton, selectedClickType, potValue);
} else if (potValue != -1) {
// Ajuste de valor pelo potenciômetro e atualização contínua
selectedPotValue = potValue;
updateDisplay(selectedButton, selectedClickType, selectedPotValue);
}
if (selectedButton != -1 && selectedClickType != -1 && selectedPotValue != -1) {
// Salvar ou cancelar
if (buttonIndex == 1) { // Salva com BUTTON2_PIN
saveConfigParams();
} else if (buttonIndex == 2) { // Cancela com BUTTON3_PIN
resetConfigParams(); // Reseta seleção para próxima configuração
// displayMessage2("OPERACAO \nCANCELADA", 2);
// display.clearDisplay();
// display.setTextSize(1);
// display.setTextColor(SSD1306_WHITE);
// display.setCursor(20, 24);
// display.println("CANCELADO");
// display.setCursor(10, 40);
// display.println("Nenhuma alteracao salva");
enterConfigMode();
}
}
}
// Função para atualizar o display de forma otimizada
void updateDisplay(int button, int clickType, int potValue) {
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
// Exibe as informações usando display.print e display.println
display.println("SELECIONADO:");
display.print("BOTAO: ");
display.println(button);
display.print("TIPO: ");
display.println(clickType);
display.print("VALOR: ");
if (potValue != -1) {
display.println(potValue);
} else {
display.println("---");
}
display.display(); // Atualiza o display
}
// Função para exibir mensagens temporárias
void displayMessage2(const char* message, int textSize) {
display.clearDisplay();
display.setTextSize(textSize);
display.setCursor(0, 0);
display.println(message);
display.display();
delay(1000); // Exibe a mensagem por 1 segundo
}
// Função para exibir uma mensagem no display
void displayMessage(const String &message) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.print(message);
display.display();
}
// Função para entrar no modo de configuração
void enterConfigMode() {
configMode = true;
resetConfigParams(); // Reseta seleção para próxima configuração
display.clearDisplay();
// display.setCursor(2, 0);
// display.setTextSize(2);
// Exibe as informações usando display.print e display.println
// display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
// display.println("CONFIG");
// display.println("SELECIONE");
// display.println("O BOTAO");
// Tela 1: Config, selecione o botão
// display.setTextSize(1); // Tamanho normal do texto
display.setCursor(40, 0);
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
display.setTextSize(1);
display.println("CONFIG");
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(10, 10);
display.println("Pressione o botao");
display.setTextSize(2);
display.setCursor(0, 30);
display.println("A");
display.setCursor(58, 22);
display.println("B");
display.setCursor(110, 30);
display.println("C");
display.setTextSize(1);
display.setCursor(10, 55);
display.println("No tipo esc");
display.display(); // Atualiza o display
}
// Função para sair do modo de configuração
void exitConfigMode() {
configMode = false;
displayMessage2("MODO \nNORMAL", 2);
}
// Função para resetar o modo de configuração
void resetConfigParams() {
selectedButton = -1;
selectedClickType = -1;
selectedPotValue = -1;
}
// Função para salvar as configurações
void saveConfigParams() {
//salvar aqui
//...
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(20, 24);
display.println("BOTAO SALVO");
display.setCursor(10, 40);
display.println("salva");
display.display(); // Atualiza o display
delay(1000); // Exibe a mensagem por 1 segundo
// displayMessage2("Valor \nSalvo!", 2);
// após salvar
resetConfigParams();
enterConfigMode();
}
void handleSimultaneousPress() {
if (debouncers[1].fell() && debouncers[0].read() == LOW) {
simultaneousPressDetected[0] = true;
ButtonAction action = getSimultaneousAction(0);
sendControlChange(action);
clickCount[0] = 0;
clickCount[1] = 0;
}
if (debouncers[1].fell() && debouncers[2].read() == LOW) {
simultaneousPressDetected[1] = true;
ButtonAction action = getSimultaneousAction(1);
sendControlChange(action);
clickCount[1] = 0;
clickCount[2] = 0;
}
}
void handleButton(int buttonIndex) {
if (debouncers[buttonIndex].fell()) {
pressStartTime[buttonIndex] = millis();
clickCount[buttonIndex]++;
longPressDetected[buttonIndex] = false;
simultaneousPressDetected[0] = false;
simultaneousPressDetected[1] = false;
}
if (debouncers[buttonIndex].read() == LOW) {
unsigned long pressDuration = millis() - pressStartTime[buttonIndex];
// pressionamento longo
if (pressDuration >= LONG_PRESS_TIME && !longPressDetected[buttonIndex]) {
longPressDetected[buttonIndex] = true;
if (configMode) {
handleConfigMode(buttonIndex, 4, -1);
} else {
ButtonAction action = getLongpressAction(buttonIndex);
sendControlChange(action);
}
clickCount[buttonIndex] = 0;
}
} else {
if (millis() - pressStartTime[buttonIndex] > CLICK_INTERVAL && clickCount[buttonIndex] > 0 && !longPressDetected[buttonIndex] && !simultaneousPressDetected[0] && !simultaneousPressDetected[1]) {
processButtonClicks(buttonIndex, clickCount[buttonIndex]);
clickCount[buttonIndex] = 0;
}
}
}
void sendControlChange(const ButtonAction &action) {
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(2);
display.print(action.type);
display.display();
//MIDI.sendControlChange(action.type, action.val, action.channel);
}
//MIDI_CREATE_DEFAULT_INSTANCE();
void setup() {
Serial.begin(9600);
// Inicializa o display
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("Falha ao inicializar o display OLED"));
for(;;);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
int buttonPins[numButtons] = {BUTTON1_PIN, BUTTON2_PIN, BUTTON3_PIN};
for (int i = 0; i < numButtons; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
debouncers[i].attach(buttonPins[i]);
debouncers[i].interval(25);
}
}
void loop() {
for (int i = 0; i < numButtons; i++) {
debouncers[i].update();
handleButton(i);
}
// handleSimultaneousPress();
// Leitura e mapeamento do potenciômetro
statePot = constrain(map(analogRead(POT_PIN), 0, 1023, 0, 127), 0, 127);
if (abs(statePot - lastStatePot) > 1) {
if (configMode) {
handleConfigMode(-1, -1, statePot);
} else {
// display.clearDisplay();
// display.setCursor(0,0);
// display.setTextSize(3);
// display.println(lastStatePot);
// display.print(statePot);
// display.display();
//MIDI.sendControlChange(7, statePot, 1);
}
}
lastStatePot = statePot;
delay(10);
}