Knobber - USB MIDI single knob/button controller by Echolevel

(UPDATED - see below!)



image



Well, here it is: the Echolevel Knobber! A tiny, class-compliant, USB MIDI controller with precisely ONE knob and ONE button. I always wanted something like this and could never find one on the market; fortunately, once the Teensy USB Development Board came of age, making my own became very straightforward. The Teensy is an Arduino-like dev board with two major advantages (for me) over Arduino: it’s extremely small while still having a USB (micro) connector, plus it natively acts as a class-compliant USB MIDI device allowing plug’n’play use on modern versions of OS X, Windows, iOS and - to a certain extent - Linux. Why? Well sometimes you’re working in a coffee shop or playing live on a very tiny amount of table space, and you don’t *need* the 8+ knobs that your favourite controller offers. In something like Ableton Live, where the Remote feature ‘captures’ your controller per track or device, being able to map one single knob to a parameter for the purposes of a carefully-controlled automation edit or a live performance tweak is handy. It is to me, anyway, and I primarly built this to cater for my own need :) The button is just there because a) there was enough space for it in the box and b) it’s handy for toggling that device (or another device) on/off; great for automation stutter edits with a glitchy effect, for example.



See it in use controlling an Ableton Live Auto Filter cutoff in the video above, while the push-button toggles the device on/off, and check out the pics, schematic and code to see how you can build your own. 



Dev Process - Short Version 



The first problems I ran into were in stabilising the value from the potentiometer (knob). For MIDI CC automation, you want the value to stay still and for the device to stop sending MIDI data when the knob isn’t being turned. Fortunately, my old friend and chipmusic compatriot Philip Cunningham (aka Firebrand Boy, aka unsymbol) had made a slightly more complex MIDI controller a few years ago so I was able to use some of his code (availalbe on his Github) to take a running average of the input values and smooth them out. That wasn’t quite enough, though, and a bit more digging led me to the concept of power decoupling and the fact that a small capacitor would - for reasons I don’t quite understand because I’m an electronics noob - smooth everything out nicely. Phil’s code also clued me in on the Bounce stuff, which makes it much easier to get useful data from button presses. Cheers Phil! 



I prototyped the whole thing on a breadboard (see below), testing the input with MIDI Monitor for OS X (MIDI-OX is best on Windows), and then packed it all into the smallest Hammond project box I could find in Maplin. The pot is a 100K linear (you can logarithmise the values in your code if you want to), the switch is the smallest I could find in Maplin (though Farnell/RS/etc probably have better ones; this one isn’t great, to be honest) and I used a Dremel to carve out the micro-USB slot and the knob/button holes. The £16 Teensy board sits on the case cover so that the screws are face-down when the device is in use, and I glued that down with a hot glue gun - bracing it at the back with a Dremelled-down piece of veroboard (also glued) so it can withstand having the USB cable jammed into it without moving. Hopefully the glue will keep it nicely in place, though a more hardwearing option might be to break out the micro-USB to a B-type connector mounted to the chassis. Easily done in a bigger case, but there is almost NO free space in this one once the components and wires are packed in. Finally I soldered everything in, tested it and was met with success! 



If you make one of these yourself, bear in mind that loading an update of your code onto the Teensy (which you might need to do if you want to change CC mappings, for instance) requires you to press the reset button on the Teensy itself. I can do this quite easily by unscrewing the bottom of the box, opening it slightly, then poking a finger in to hit the button. Not ideal, but then you shouldn’t have to make changes very frequently. If you did, you could break out the rest switch to another physical switch mounted on the case. In the long term, I want to implement some SysEx control whereby you can send new mapping values direct from SysEx Librarian or something, or maybe a Max/MSP patch, which are stored in the Teensy’s tiny amount of non-volatile EPROM. Just a thought. 



I’ll add other details as I think of them.



UPDATE

I’ve now got a SysEx configuration system in place, described in the code below. Basically, while some commercial controllers supply graphical config programs so you can choose which channel/CC number/button type each control should have, I’m offering a far more ghetto solution: hack the default .syx file in a hex editor (very easy) and then transmit it to the device using software like Sysex Librarian (OS X), MIDI-OX (Windows) or various DAWs. Few people will feel the need to do this, but I’ve included it in case you have a really awkward clash with some other controller in your setup. You should only need to make a change once, as the Knobber has non-volatile storage built in that should hold those values for, ooh, eternity or thereabouts.



image

image

image

image

image

image

image

image

image



Note - you’ll need to install Arduino and the Teensy stuff for it (which is all part of Teensy setup, explained on its website), and before you send any MIDI-related code to the board you should go to Tools -> USB Type and select ‘MIDI’. Once the board is running in MIDI mode, you won’t be able to see any useful output in Arduino’s built-in serial monitor, but that’s no problem - just use MIDI Monitor or MIDI-OX instead. You can also change the self-declared USB MIDI device name in a header somewhere, but I can’t remember right now…I’ll add it when I do.



CODE:

/*
'Knobber' - one knob/one button USB MIDI controller by Echolevel - 
http://echolevel.tumblr.com/post/49737964614/knobber-usb-midi-controller-by-echolevel
Feedback: http://twitter.com/echolevel

Special thanks to Philip Cunningham (aka unsymbol, aka 
Firebrand Boy - http://philipcunningham.org )

SysEx Config Message Structure:
0xF0 # SysEx message start byte
0x14 # Manufacturer ID; 0x14 is actually Fairlight, but I don't forsee too many conflicts here... 
0x01 # Knobber knob channel number
0x01 # Knobber button channel number
0x0E # Knobber knob CC number
0x0F # Knobber button CC number
0x01 # Knobber button behaviour (0 = momentary, 1 = toggle)
0xF7 # SysEx message end byte

On first run, your Teensy's EEPROM might contain values left over from a previous sketch so you 
should use Sysex Librarian, MIDI-OX or similar to transmit the default .syx file (available from
wherever you got this code). Thereafter, you can copy that default .syx and use a hex editor to 
adjust the values according to the structure above.
*/

#include <Bounce.h>
#include <EEPROM.h>

// Default settings - will be overwritten if EEPROM values are present.
int knobChan = 1; int buttonChan = 1; int knobCC = 14; int buttonCC = 15; 
int kPin = 0; int bPin = 0; int behaviour = 1;
int inputAnalog, ccValue, iAlag;
boolean toggled = false;
Bounce button0 = Bounce(0,5);

void setup() {
  //MIDI rate
  Serial.begin(31250);
  pinMode(bPin, INPUT_PULLUP);
  delay(5);
  knobChan =  EEPROM.read(1); 
  usbMIDI.sendControlChange(44, knobChan, 2);
  delay(5);
  buttonChan = EEPROM.read(2); 
  delay(5);
  knobCC = EEPROM.read(3);    
  delay(5);
  buttonCC = EEPROM.read(4);
  delay(5);
  behaviour = EEPROM.read(5);
}

void loop() {
  // Check for SysEx config message
  if(usbMIDI.read() && usbMIDI.getType() == 7) {                
     if (usbMIDI.getData1() > 1 && usbMIDI.getData1() < 9) {
        // unpack SysEx
        byte * sysbytes = usbMIDI.getSysExArray();
        if (sysbytes[0] == 0xf0 && sysbytes[7] == 0xf7) { // Good length; legit sysex.
          if(sysbytes[1] == 0x14) {  // It's either Knobber or a Fairlight CMI...
              // 2, 3, 4, 5 and 6 can now be written to EEPROM and to global vars
              EEPROM.write(1, sysbytes[2]);
              knobChan = sysbytes[2];
              EEPROM.write(2, sysbytes[3]);
              buttonChan = sysbytes[3];
              EEPROM.write(3, sysbytes[4]);
              knobCC = sysbytes[4];
              EEPROM.write(4, sysbytes[5]);
              buttonCC = sysbytes[5];
              EEPROM.write(5, sysbytes[6]);
              behaviour = sysbytes[6];

          }          
        }
     } 
  }
  
  
  if(behaviour > 0) {
      // Pushbutton - MOMENTARY behaviour
      button0.update();
      if (button0.fallingEdge()) {
          usbMIDI.sendControlChange(buttonCC, 127, buttonChan);
      }
      if (button0.risingEdge()) {
          usbMIDI.sendControlChange(buttonCC, 0, buttonChan);
      } 
  } else {      
      // Pushbutton - TOGGLE behaviour
      button0.update();
      if(button0.fallingEdge()) {
         if (toggled) {
             usbMIDI.sendControlChange(buttonCC, 0, buttonChan);
             toggled = false;
         } else {
             usbMIDI.sendControlChange(buttonCC, 127, buttonChan);
            toggled = true;
         } 
      }
  }
    
  inputAnalog = analogRead(kPin);  
  if(abs(inputAnalog - iAlag) > 7) {  
    // calc the CC value based on the raw value
    ccValue = inputAnalog/8;                                
    // Invert the pot value (because I soldered it backwards...)
    int inverted = map(ccValue, 127, 0, 0, 127);            
    // send the MIDI
    usbMIDI.sendControlChange(knobCC, inverted, knobChan);                                  
    iAlag = inputAnalog;
  }

  delay(5); // limits message frequency
}
  1. zi-phon reblogged this from echolevel
  2. reverendentity reblogged this from echolevel
  3. kexxx reblogged this from echolevel
  4. classycardigan reblogged this from echolevel
  5. toner reblogged this from echolevel
  6. thxstudio reblogged this from echolevel
  7. echolevel posted this