Skip to content

Instantly share code, notes, and snippets.

@NickCulbertson
Created January 4, 2024 15:07
Show Gist options
  • Save NickCulbertson/9bb6122f4fb12c04d71e156c788c3985 to your computer and use it in GitHub Desktop.
Save NickCulbertson/9bb6122f4fb12c04d71e156c788c3985 to your computer and use it in GitHub Desktop.
This is an Arduino Sketch for a simple arcade button MIDI Controller using ESP32
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#define SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700"
#define CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3"
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
unsigned long debounceDuration = 10; // millis
unsigned long lastTimeButton1StateChanged = 0;
unsigned long lastTimeButton2StateChanged = 0;
unsigned long lastTimeButton3StateChanged = 0;
unsigned long lastTimeButton4StateChanged = 0;
unsigned long lastTimeButton5StateChanged = 0;
unsigned long lastTimeButton6StateChanged = 0;
unsigned long lastTimeButton7StateChanged = 0;
unsigned long lastTimeButton8StateChanged = 0;
unsigned long lastTimeButton9StateChanged = 0;
unsigned long lastTimeButton10StateChanged = 0;
unsigned long lastTimeButton11StateChanged = 0;
unsigned long lastTimeButton12StateChanged = 0;
unsigned long lastTimeButton13StateChanged = 0;
unsigned long lastTimeButton14StateChanged = 0;
unsigned long lastTimeButton15StateChanged = 0;
unsigned long lastTimeButton16StateChanged = 0;
byte lastState1 = LOW;
byte lastState2 = LOW;
byte lastState3 = LOW;
byte lastState4 = LOW;
byte lastState5 = LOW;
byte lastState6 = LOW;
byte lastState7 = LOW;
byte lastState8 = LOW;
byte lastState9 = LOW;
byte lastState10 = LOW;
byte lastState11 = LOW;
byte lastState12 = LOW;
byte lastState13 = LOW;
byte lastState14 = LOW;
byte lastState15 = LOW;
byte lastState16 = LOW;
byte note1 = 0x33;
byte note2 = 0x32;
byte note3 = 0x31;
byte note4 = 0x30;
byte note5 = 0x2f;
byte note6 = 0x2e;
byte note7 = 0x2d;
byte note8 = 0x2c;
byte note9 = 0x2b;
byte note10 = 0x2a;
byte note11 = 0x29;
byte note12 = 0x28;
byte note13 = 0x27;
byte note14 = 0x26;
byte note15 = 0x25;
byte note16 = 0x24;
uint8_t midiPacket[] = {
0x80, // header
0x80, // timestamp, not implemented
0x00, // status
0x3c, // 0x3c == 60 == middle c
0x00 // velocity
};
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
void setup() {
Serial.begin(115200);
pinMode( 5, INPUT_PULLUP);
pinMode(18, INPUT_PULLUP);
pinMode(19, INPUT_PULLUP);
pinMode(21, INPUT_PULLUP);
pinMode( 3, INPUT_PULLUP);
pinMode( 1, INPUT_PULLUP);
pinMode(22, INPUT_PULLUP);
pinMode(23, INPUT_PULLUP);
pinMode(13, INPUT_PULLUP);
pinMode(12, INPUT_PULLUP);
pinMode(14, INPUT_PULLUP);
pinMode(27, INPUT_PULLUP);
pinMode(26, INPUT_PULLUP);
pinMode(25, INPUT_PULLUP);
pinMode(33, INPUT_PULLUP);
pinMode(32, INPUT_PULLUP);
BLEDevice::init("Blue ESP32");
// Create the BLE Server
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(BLEUUID(SERVICE_UUID));
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
BLEUUID(CHARACTERISTIC_UUID),
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_WRITE_NR
);
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Create a BLE Descriptor
pCharacteristic->addDescriptor(new BLE2902());
// Start the service
pService->start();
// Start advertising
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->addServiceUUID(pService->getUUID());
pAdvertising->start();
}
void loop() {
if (deviceConnected) {
byte button1 = digitalRead(5);
byte button2 = digitalRead(18);
byte button3 = digitalRead(19);
byte button4 = digitalRead(21);
byte button5 = digitalRead(3);
byte button6 = digitalRead(1);
byte button7 = digitalRead(22);
byte button8 = digitalRead(23);
byte button9 = digitalRead(13);
byte button10 = digitalRead(12);
byte button11 = digitalRead(14);
byte button12 = digitalRead(27);
byte button13 = digitalRead(26);
byte button14 = digitalRead(25);
byte button15 = digitalRead(33);
byte button16 = digitalRead(32);
if (millis() - lastTimeButton1StateChanged >= debounceDuration) {
if (button1 != lastState1) {
lastTimeButton1StateChanged = millis();
lastState1 = button1;
if (button1 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note1; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note1; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton2StateChanged >= debounceDuration) {
if (button2 != lastState2) {
lastTimeButton2StateChanged = millis();
lastState2 = button2;
if (button2 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note2; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note2; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton3StateChanged >= debounceDuration) {
if (button3 != lastState3) {
lastTimeButton3StateChanged = millis();
lastState3 = button3;
if (button3 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note3; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note3; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton4StateChanged >= debounceDuration) {
if (button4 != lastState4) {
lastTimeButton4StateChanged = millis();
lastState4 = button4;
if (button4 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note4; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note4; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton5StateChanged >= debounceDuration) {
if (button5 != lastState5) {
lastTimeButton5StateChanged = millis();
lastState5 = button5;
if (button5 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note5; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note5; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton6StateChanged >= debounceDuration) {
if (button6 != lastState6) {
lastTimeButton6StateChanged = millis();
lastState6 = button6;
if (button6 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note6; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note6; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton7StateChanged >= debounceDuration) {
if (button7 != lastState7) {
lastTimeButton7StateChanged = millis();
lastState7 = button7;
if (button7 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note7; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note7; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton8StateChanged >= debounceDuration) {
if (button8 != lastState8) {
lastTimeButton8StateChanged = millis();
lastState8 = button8;
if (button8 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note8; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note8; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton9StateChanged >= debounceDuration) {
if (button9 != lastState9) {
lastTimeButton9StateChanged = millis();
lastState9 = button9;
if (button9 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note9; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note9; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton10StateChanged >= debounceDuration) {
if (button10 != lastState10) {
lastTimeButton10StateChanged = millis();
lastState10 = button10;
if (button10 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note10; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note10; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton11StateChanged >= debounceDuration) {
if (button11 != lastState11) {
lastTimeButton11StateChanged = millis();
lastState11 = button11;
if (button11 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note11; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note11; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton12StateChanged >= debounceDuration) {
if (button12 != lastState12) {
lastTimeButton12StateChanged = millis();
lastState12 = button12;
if (button12 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note12; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note12; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton13StateChanged >= debounceDuration) {
if (button13 != lastState13) {
lastTimeButton13StateChanged = millis();
lastState13 = button13;
if (button13 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note13; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note13; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton14StateChanged >= debounceDuration) {
if (button14 != lastState14) {
lastTimeButton14StateChanged = millis();
lastState14 = button14;
if (button14 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note14; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note14; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton15StateChanged >= debounceDuration) {
if (button15 != lastState15) {
lastTimeButton15StateChanged = millis();
lastState15 = button15;
if (button15 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note15; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note15; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
if (millis() - lastTimeButton16StateChanged >= debounceDuration) {
if (button16 != lastState16) {
lastTimeButton16StateChanged = millis();
lastState16 = button16;
if (button16 == LOW) {
//Button Pressed
midiPacket[2] = 0x90; // note down, channel 0
midiPacket[3] = note16; // key
midiPacket[4] = 127; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
pCharacteristic->notify();
}else{
//Button Released
midiPacket[2] = 0x80; // note up, channel 0
midiPacket[3] = note16; // key
midiPacket[4] = 0; // velocity
pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
pCharacteristic->notify();
}
}
}
}
}
@DRCRecoveryData
Copy link

I suggest wirring midi 4*4 by matrix method

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>

#define SERVICE_UUID        "03b80e5a-ede8-4b33-a751-6ce34ec4c700"
#define CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3"

BLECharacteristic *pCharacteristic;
bool deviceConnected = false;

unsigned long debounceDuration = 10; // millis

const int BUTTON_PINS[] = {5, 18, 19, 21, 3, 1, 22, 23, 13, 12, 14, 27, 26, 25, 33, 32};
const byte NOTE_VALUES[] = {0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 
                             0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24};
const int NUM_BUTTONS = sizeof(BUTTON_PINS) / sizeof(BUTTON_PINS[0]);

uint8_t midiPacket[] = {
   0x80,  // header
   0x80,  // timestamp, not implemented 
   0x00,  // status
   0x3c,  // 0x3c == 60 == middle c
   0x00   // velocity
};

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

void setup() {
  Serial.begin(115200);
  
  // Configure button pins
  for (int i = 0; i < NUM_BUTTONS; i++) {
    pinMode(BUTTON_PINS[i], INPUT_PULLUP);
  }

  BLEDevice::init("Blue ESP32");
    
  // Create the BLE Server
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyServerCallbacks());

  // Create the BLE Service
  BLEService *pService = pServer->createService(BLEUUID(SERVICE_UUID));

  // Create a BLE Characteristic
  pCharacteristic = pService->createCharacteristic(
    BLEUUID(CHARACTERISTIC_UUID),
    BLECharacteristic::PROPERTY_READ   |
    BLECharacteristic::PROPERTY_WRITE  |
    BLECharacteristic::PROPERTY_NOTIFY |
    BLECharacteristic::PROPERTY_WRITE_NR
  );

  // Create a BLE Descriptor
  pCharacteristic->addDescriptor(new BLE2902());

  // Start the service
  pService->start();

  // Start advertising
  BLEAdvertising *pAdvertising = pServer->getAdvertising();
  pAdvertising->addServiceUUID(pService->getUUID());
  pAdvertising->start();
}

void loop() {
  if (deviceConnected) {
    for (int i = 0; i < NUM_BUTTONS; i++) {
      handleButton(i, BUTTON_PINS[i], NOTE_VALUES[i]);
    }
  }
}

void handleButton(int index, int pin, byte note) {
  static unsigned long lastTimeButtonStateChanged = 0;
  static byte lastStates[NUM_BUTTONS];

  byte buttonState = digitalRead(pin);

  if (millis() - lastTimeButtonStateChanged >= debounceDuration) {
    if (buttonState != lastStates[index]) {
      lastTimeButtonStateChanged = millis();
      lastStates[index] = buttonState;
      if (buttonState == LOW) {
        // Button Pressed
        midiPacket[2] = 0x90;  // note down, channel 0
        midiPacket[3] = note;  // key
        midiPacket[4] = 127;   // velocity
        pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
        pCharacteristic->notify();
      } else {
        // Button Released
        midiPacket[2] = 0x80;  // note up, channel 0
        midiPacket[3] = note;  // key
        midiPacket[4] = 0;     // velocity
        pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
        pCharacteristic->notify();
      }
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment