So I got the crazy idea to create a MIDI DJ Controller using a Teensy 3.6. I'll do a nice write up when I'm finished.
I am comfortable doing some MIDI stuff, writing C code, some soldering and like to tinker. I do not have much microcontroller experience though. I already have the Teensy 3.6, and I have ordered some hardware. I also have written some software and made a little design. Can you guys please check whether I am missing some big things?
For now, this is my design. The three multiplexers on the left will be connected to simple button switches. The two on the right will get all the middle pins from many rotary and slider 10k Ohm potentiometers.
Things I am unsure of:
Here is my code:
I am comfortable doing some MIDI stuff, writing C code, some soldering and like to tinker. I do not have much microcontroller experience though. I already have the Teensy 3.6, and I have ordered some hardware. I also have written some software and made a little design. Can you guys please check whether I am missing some big things?
For now, this is my design. The three multiplexers on the left will be connected to simple button switches. The two on the right will get all the middle pins from many rotary and slider 10k Ohm potentiometers.
Things I am unsure of:
- Do I need to connect some/all unused pins to GND?
- Should I use external power for potentiometers and the Teensy? If so, can I combine that with the usb-power safely?
- Can I use multiplexers to power LEDs?
- Am I using correct wire colors?
- Will multiplexing analog and digital signals like this work?
- Are there any best practises for MIDI signals/channels/messages?
Here is my code:
Code:
// analog smoothing
#include <ResponsiveAnalogRead.h>
// include the Bounce library for 'de-bouncing' switches -- removing electrical chatter as contacts settle
#include <Bounce.h>
// usbMIDI.h library is added automatically when code is compiled as a MIDI device
// Multiplexer selector pins on teensy
int SELECTOR_PINS[] = {32, 31, 30, 29};
// pins to read
const int AN = 2; // number of analog pins
const int DN = 3; // number of digital pins
const int ANALOG_PINS[AN] = {A0, A1}; // teensy mapping, pin 14,15
const int DIGITAL_PINS[DN] = {0,1,2}; // teensy mapping
const int MAX_PINS = 16; // max number of pins multiplexed per multiplexer
const int ANALOG_MUX_PINS[AN] = {16, 16}; // number of pins on multiplexer used for signals
const int DIGITAL_MUX_PINS[DN] = {16, 16, 16}; // number of pins on multiplexer used for signals
// analog[i][j] corresponds to analog pin i on Teensy, pin j on multiplexer
ResponsiveAnalogRead *analog[AN][MAX_PINS];
byte analog_data[AN][MAX_PINS];
byte analog_data_prev[AN][MAX_PINS];
// digital[i][j] corresponds to digital pin i on Teensy, pin j on multiplexer
Bounce *digital[DN][MAX_PINS];
const int BOUNCE_TIME = 5; // 5 ms is usually sufficient
const int ON_VELOCITY = 127; // pressed button "note" velocity
void setup() {
Serial.begin(9600);
setupAnalog();
setupDigital();
}
void setupAnalog() {
for (int i = 0; i < AN; i++) {
for (int j = 0; j < ANALOG_MUX_PINS[i]; j++) {
analog[i][j] = new ResponsiveAnalogRead(ANALOG_PINS[i], true); // initialize
}
}
}
void setupDigital() {
for (int i = 0; i < DN; i++) {
for (int j = 0; j < DIGITAL_MUX_PINS[i]; j++) {
digital[i][j] = new Bounce(DIGITAL_PINS[i], true); // initialize
}
pinMode(DIGITAL_PINS[i], INPUT_PULLUP); // internal pullup resistors for digital section
}
}
void loop() {
for (int j = 0; j < MAX_PINS; j++) { // loop over multiplexer
setSelectorPins(j);
readAnalogs(j);
readDigitals(j);
}
while (usbMIDI.read()) {
// read & ignore incoming messages
}
}
void readAnalogs(int j) {
for (int i = 0; i < AN; i++) { // loop over analog inputs
if (j < ANALOG_MUX_PINS[i]) { // pin j on multiplexer is actually used
analog[i][j]->update();
if (analog[i][j]->hasChanged()) {
analog_data[i][j] = analog[i][j]->getValue() >> 3;
if (analog_data[i][j] != analog_data_prev[i][j]) {
analog_data_prev[i][j] = analog_data[i][j];
usbMIDI.sendControlChange(16 + j, analog_data[i][j], i + 1);
// I use CCID 16-31 which are General Purpose Controllers or Undefined:
// https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2
// the channel is the index of current analog teensy pin index (i)
}
}
}
}
}
void readDigitals(int j) {
for (int i = 0; i < DN; i++) { // loop over digital inputs
if (j < DIGITAL_MUX_PINS[i]) { // pin j on multiplexer is actually used
digital[i][j]->update();
if (digital[i][j]->fallingEdge()) {
usbMIDI.sendNoteOn(j+i*MAX_PINS, ON_VELOCITY, AN + i + 1);
// note is 16*current digital teensy pin index (i) + current mux pin index (j)
// velocity is pressing a note (should be 65 to 127)
// channel is one above the analog + index of current digital teensy pin index (i)
}
if (digital[i][j]->risingEdge()) {
usbMIDI.sendNoteOff(i+j*MAX_PINS, 0, AN + i + 1);
}
}
}
}
void setSelectorPins(int j) {
byte s0 = (j & B0001) ? HIGH : LOW;
byte s1 = (j & B0010) ? HIGH : LOW;
byte s2 = (j & B0100) ? HIGH : LOW;
byte s3 = (j & B1000) ? HIGH : LOW;
digitalWrite(SELECTOR_PINS[0], s0);
digitalWrite(SELECTOR_PINS[1], s1);
digitalWrite(SELECTOR_PINS[2], s2);
digitalWrite(SELECTOR_PINS[3], s3);
// allow 50 us for signals to stablize
delayMicroseconds(50);
}