Guitar Pickguard Wireless MIDI Controller
Add a wireless MIDI controller to your guitar's pickguard that detects note presses via capacitive touch sensing.
Components and supplies
1
SparkFun Snappable Protoboard
1
Feather 32u4 Bluefruit LE
3
Tactile Switch, Top Actuated
1
Toggle Switch, SPDT
1
Capacitive Touch Sensor Breakout - MPR121
2
Rotary potentiometer (generic)
Tools and machines
1
Solder Wire, Lead Free
1
Soldering iron (generic)
1
Tape, PVC (Polyvinyl Chloride)
1
Tape, Hook and Loop
Project description
Code
Arduino-MIDI-Scratchplate-Controller
c_cpp
You will also need to download the Bluefruit LE Config.h file. See here for instructions: https://learn.adafruit.com/bluefruit-le-connect/library-and-config
1/* Scratchplate Midi Controller 2 * Version 1.0.0 - ALPHA 3 * 4 * This midi controller should work with any MIDI compatible synth. 5 * I have been using it with Audio Synth One on iOS with good results. 6 * 7 * The capacitive sensor allows 12 chromatic notes to be played. 8 * 9 * Octave can be changed via two push buttons. The default range is 4 octaves but this can easily be changed. 10 * 11 * A third push button allows you to change the device from normal mode to sustain mode. In sustain mode only one note can be played at a time. 12 * This is equivalent to setting 'MONO' and 'HOLD' on a synth. 13 * 14 * Finally two potentiometers control MIDI CC 1 and MIDI CC 7. 15 * These are traditionally mod wheel and volume knobs, but through MIDI learn can usually be set to anything you like. 16 * 17 * For full instructions please see: https://create.arduino.cc/projecthub/projects/238dd5 18 * 19 * Acknowledgements: This sketch is adapted from ble_neopixel_mpr121.ino, by Todd Treece, copyright of Adafruit 20 21 (C) MIT Kzra 2021 22 */ 23 24// libraries 25#include "Wire.h" 26#include "Adafruit_MPR121.h" 27#include "Adafruit_BLE.h" 28#include "Adafruit_BluefruitLE_SPI.h" 29#include "Adafruit_BLEMIDI.h" 30#include "BluefruitConfig.h" 31 32// defined constants 33#define FACTORYRESET_ENABLE 1 34#define MINIMUM_FIRMWARE_VERSION "0.7.0" 35#define IRQ_PIN A4 36#define MOD_POT A0 37#define VOL_POT A1 38#define OCTAVE_UP 13 39#define OCTAVE_DOWN 12 40#define SUSTAIN 11 41 42//set up capacitive sensor and bluetooth LE 43Adafruit_MPR121 cap = Adafruit_MPR121(); 44Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST); 45Adafruit_BLEMIDI blemidi(ble); 46 47// prime dynamic values 48int channel = 0; 49int octave = 4; 50int potVal; 51int modVal; 52int lastModVal; 53int volVal; 54int lastVolVal; 55bool sustainMode = false; 56int prevNote; 57uint16_t lasttouched = 0; 58uint16_t currtouched = 0; 59bool isConnected = false; 60 61// constant values 62const int pitch[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; 63const int PADS = 12; 64const int vel = 127; 65 66// A small helper 67void error(const __FlashStringHelper*err) { 68 Serial.println(err); 69 while (1); 70} 71void connected(void) 72{ 73 isConnected = true; 74 Serial.println(F(" CONNECTED!")); 75} 76 77void disconnected(void) 78{ 79 Serial.println("disconnected"); 80 isConnected = false; 81} 82 83 84void setup() { 85 Serial.print(F("Initialising the Bluefruit LE module: ")); 86 87 if ( !ble.begin(VERBOSE_MODE) ) 88 { 89 error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?")); 90 } 91 Serial.println( F("OK!") ); 92 93 if ( FACTORYRESET_ENABLE ) 94 { 95 /* Perform a factory reset to make sure everything is in a known state */ 96 Serial.println(F("Performing a factory reset: ")); 97 if ( ! ble.factoryReset() ) { 98 error(F("Couldn't factory reset")); 99 } 100 } 101 102 //ble.sendCommandCheckOK(F("AT+uartflow=off")); 103 ble.echo(false); 104 105 Serial.println("Requesting Bluefruit info:"); 106 /* Print Bluefruit information */ 107 ble.info(); 108 109 /* Set BLE callbacks */ 110 ble.setConnectCallback(connected); 111 ble.setDisconnectCallback(disconnected); 112 113 Serial.println(F("Enable MIDI: ")); 114 if ( ! blemidi.begin(true) ) 115 { 116 error(F("Could not enable MIDI")); 117 } 118 119 ble.verbose(false); 120 Serial.print(F("Waiting for a connection...")); 121 122 // set mpr121 IRQ pin to input 123 pinMode(IRQ_PIN, INPUT); 124 125 // bail if the mpr121 init fails 126 if (! cap.begin(0x5A)) 127 while (1); 128 129} 130 131void loop() { 132 // check for sustain 133 if(digitalRead(SUSTAIN) == HIGH){ 134 if(sustainMode == false){ 135 sustainMode = true; 136 }else { 137 sustainMode = false; 138 } 139 // send a note off event on every pitch in current octave range 140 for (uint8_t i=0; i < PADS; i++) { 141 midi(channel, 0x8, pitch[i] + (12 * octave), 0x0); 142 } 143 Serial.println(sustainMode); 144 delay(500); 145 } 146 // if mpr121 irq goes low, there have been 147 // changes to the button states, so read the values 148 if(digitalRead(IRQ_PIN) == LOW){ 149 // read current values 150 currtouched = cap.touched(); 151 // normal mode 152 if(sustainMode == false){ 153 cap_midi_normal(); 154 // sustain mode 155 } else { 156 cap_midi_sustain(); 157 } 158 lasttouched = currtouched; 159 } 160 if(digitalRead(OCTAVE_UP) == HIGH){ 161 // send a note off event on every pitch in prev octave range 162 for (uint8_t i=0; i < PADS; i++) { 163 midi(channel, 0x8, pitch[i] + (12 * octave), 0x0); 164 } 165 octave++; 166 if(octave == 7){ 167 octave = 3; 168 } 169 Serial.println(octave); 170 delay(500); 171 } 172 if(digitalRead(OCTAVE_DOWN) == HIGH){ 173 // send a note off event on every pitch in prev octave range 174 for (uint8_t i=0; i < PADS; i++) { 175 midi(channel, 0x8, pitch[i] + (12 * octave), 0x0); 176 } 177 octave--; 178 if(octave == 2){ 179 octave = 6; 180 } 181 Serial.println(octave); 182 delay(500); 183 } 184 // read the volume pot 185 potVal = analogRead(VOL_POT); 186 volVal= map(potVal, 0, 1023, 0, 127); 187 if (lastVolVal != volVal) { 188 Serial.print("volWheel = "); 189 Serial.println(volVal); 190 // It's a nibble for a CC change 191 midi(channel, 0xB, 7, volVal); 192 lastVolVal = volVal; 193 } 194 // finally read the modulation pot 195 potVal = analogRead(MOD_POT); 196 modVal = map(potVal, 0, 1023, 0, 127); 197 //send new mod value if it has changed 198 if (lastModVal != modVal) { 199 Serial.print("modWheel = "); 200 Serial.println(modVal); 201 // It's a nibble for a CC change 202 midi(channel, 0xB, 1, modVal); 203 lastModVal = modVal; 204 } 205 206} 207 208/* standard mode 209 * (i) notes on only while touched 210 * (ii) can play multiple notes at once 211 */ 212void cap_midi_normal(){ 213 for (uint8_t i=0; i < PADS; i++) { 214 // note on check 215 if ((currtouched & _BV(i)) && !(lasttouched & _BV(i))) { 216 // play pressed note 217 midi(channel, 0x9, pitch[i] + (12 * octave), vel); 218 } 219 // note off check 220 if ( !(currtouched & _BV(i)) && (lasttouched & _BV(i)) ) { 221 // play note off 222 midi(channel, 0x8, pitch[i] + (12 * octave), 0x0); 223 } 224 } 225} 226 227/* in sustain mode you can only play one note at a time, and it will stay on until you 228 * (i) press the note again, or (ii) press another note 229 */ 230void cap_midi_sustain(){ 231 for (uint8_t i=0; i < PADS; i++) { 232 // note on check 233 if ((currtouched & _BV(i))) { 234 // play pressed note 235 midi(channel, 0x9, pitch[i] + (12 * octave), vel); 236 // turn off last pressed note 237 midi(channel, 0x9, prevNote + (12 * octave), 0x0); 238 // reset the index 239 prevNote = i; 240 // delay to allow user to remove hand 241 delay(500); 242 } 243 } 244} 245 246// the callback that will be called by the sequencer when it needs 247// to send midi commands over BLE. 248void midi(byte channel, byte command, byte arg1, byte arg2) { 249 // init combined byte 250 byte combined = command; 251 // shift if necessary and add MIDI channel 252 if(combined < 128) { 253 combined <<= 4; 254 combined |= channel; 255 } 256 blemidi.send(combined, arg1, arg2); 257} 258
Downloadable files
Circuit schematic
Circuit schematic

Circuit schematic
Circuit schematic

Comments
Only logged in users can leave comments