MIDI Foot Controller


^ top

Here an overview of some features of my current MIDI foot controller (MFC). Also, read further below for ideas on how to build your own MFC!

(the surface has since been blackened and labelled)

About PCs, CCs and IAs
^ top

PCs and CCs are MIDI messages. IA is terminology used by some manufacturers to refer to a particular type of toggled CC message (see below).

PC = Program Change
Recalls a patch with its saved settings. PCs have a number from 0 to 127, which commonly recalls patches labelled one higher, from 1 to 128.

CC = Continuous Controller
CC messages are capable of setting parameter values and can also be used to toggle effects on and off. CC messages have a number and a value. This is the equivalent of selecting a parameter with the number and setting that parameter to a value. For example, CC number 7 is typically used for volume and sending values 0, 1, 2, 3, ... up to 127 would sweep the volume level from off to maximum.

CCs are numbered from 0 to 127 and most equipment manufacturers follow MIDI conventions where possible. The Fractal Axe-FX, however, has an open structure where nearly any CC number can be used for any available purpose. Note that CC numbers 0 and 32 are reserved for Bank changes sent before a PC message to allow access to more than 128 different patches. Never use these CC numbers as controllers, because following patch changes will be unpredictable.

CC values also range from 0 to 127 and are used in several different ways with MIDI foot controllers:

IA = Instant Access (or Immediate Access)
There is no such thing as an IA MIDI message. IA is a term used by some manufacturers to refer to toggled CCs that switch effects on and off within a patch. Turning effects off and on within a patch is often instantaneous, however, patch changes usually take some time to load, causing a brief sound dropout when sustaining notes while switching patches.

A Basic Project
^ top

Here's a schematic and code for a basic MIDI foot-controller. They have not been tested, no guarantees are given, no support is offered, and no responsibility is taken by GM Arts. Visit for PBasic documentation and their editor, as well as help with Basic Stamp schematics. This is a cut-down version of my own design, and gives you:

Here's a list of what you'll need:

The schematic uses only 3 IC chips plus the stamp module. I used a BS2sx module, however, you're probably better off with the BS2p which is faster, pin compatible and has some additional commands and flexibility (eg you can can split code and data into separate memory areas). There's also a BS2p40 module which has 32 I/O pins instead of 16 - this could be handy if you want to control a large number of LEDs or relays, or perhaps to simplify input switching.

You could use an extra chip if you want to replace the diode footswitch encoder array with a 74C922 chip. I decided not to because the chip is more expensive and needs an extra stamp module pin for "a switch is pressed". The advantages of the chip are: you can detect 16 switches instead of 15, and the switch debouncing is done in hardware, not PBasic code.

A much cheaper and more powerful alternative to Basic Stamp is PicAxe. The extra power brings a little more complex coding, but there's not much in it. Porting Bacic Stamp to PicAxe is also quite easy.

Power is intended to be supplied via a 7-pin cable that also conforms to MIDI on pins 4 and 5. Expect to lose about 1V over a long cable, so taking this into account, you'll need about 10VDC @ 300mA, or 8VAC @ 400mA. The regulator should be placed on a heatsink.

PBasic code is shown below. This can be copied and pasted directly into the PBasic editor. It demonstrates how to program a Basic Stamp module to:

This example is coded for clarity, however, there are much better ways to do this that result in faster processing and room for more functionality. My own code, with all of the functionality described above and configuration data, uses about 25% of code space and 80% of RAM.

' {$STAMP BS2p}
' {$PBASIC 2.5}

' This is sample Basic Stamp code for a MIDI footcontroller
' It has not been tested, no guarantees are given, no support is offered and no responsibility is accepted
' It demonstrates how to use a Basic Stamp module to:
'  - use MIDI configuration data
'  - read & debounce footswitch presses
'  - send PC MIDI mesasages
'  - send CC toggle, momentary and tap messages
'  - display footswitch status LEDs
'  - read and autocalibrate a pedal controller and send continuous CC messages
' Adapted from a MIDI footcontroller built by GM Arts

' PBasic is similar to other versions of Basic, with some additional commands to support I/O
' on Basic Stamp modules.  The BS2p has enough code space, user RAM, pins and speed to support
' a fairly sophisticated foot controller.  The controller only sends MIDI data, it cannot receive
' MIDI data for 2-way communication with external devices.
' This program uses less than one third of EPROM code space in one of the 8 available slots,
' less than half of user RAM, no SP-RAM, and 13 of the 16 I/O pins (the BS2p40 offers 32 I/O pins!)
' see for PBasic documentation, a PBasic editor, module information and schematics
' Some important gotchas:
' Never connect an output pin directly to +5V, earth or to the output of another device.  This can
' cause a short and destroy the module.  This project doesn't need any high current output pins,
' so for beginners, connect a 220 ohm resistor in series with every I/O pin to fully protect the chip.
' Even if pins start as inputs, several commands can automatically change a pin to an output.
' Take great care when using binary logic with IF statements.  PBasic allows several data types, but
' IF statements convert all variables to 16 bit.  eg if you have a bit variable IsPressed = 1 (true)
' you would expect "IF NOT IsPressed" to be false, but it actually returns a true result because the
' 1-bit variable is converted to 16 bits (0000000000000001) and "not IsPressed" = 1111111111111110
' which is also true.  One way to avoid this is to use XOR instead on NOT, so:  IF (tIsPressed ^ 1)
' is the same as 0000000000000001 XOR 0000000000000001 which = 0 (false), the desired result.

' PBasic calculates left to right within parenthesis first, then on the whole line, as integers
' eg 12 + 3 * 2 / 4   calculates as:   12+3 = 15,  15*2 = 30,   30/4 = 7 (integer math)


' This sample program has:
'   8 x PC footswitches (switches 1 to 8), each with a red LED
'   4 x CC toggle footswitches (switches 9 to 12), each with a green LED
'   2 x CC momentary footswitches (switches 13 & 14)
'   1 x CC tap footswitch (switch 15)
'   1 x CC pedal


' The BS2p has 16 pins which can individually act as inputs or outputs

' fsw inputs are 1-based BCD on pins 0, 1, 2, 3 (high when pressed)

' 0-based PC data (0 to 7) is output as BCD on pins 4, 5, 6

' pin 7 is not used

' CC toggle LED states are output on these pins:
pinC9      PIN 8
pinC10     PIN 9
pinC11     PIN 10
pinC12     PIN 11

' pins 12 & 13 are not used

' pin 14 has associated circuitry to interface to a linear 10K pedal pot
pinPedalPot PIN 14 'used with RCTIME command

' pin 15 has current limiting resistor to connect to MIDI output
pinMidiOut  PIN 15

' I/O

' define inputs, outputs and startup states

  DIRA = %0000     'pins 0 to 3 are inputs for the 15 footswitches

  DIRB = %0111     'pins 4 to 6 are outputs (BCD for P fsw LED)
  OUTB = %0000     'all pins low (so P1 LED will be lit at startup)

  DIRH = %11001111 'pins 8 to 11, 14 & 15 are outputs (see pins above)
  OUTH = %01000000 'start pinPedalPot high in preparation for next RCTIME command


MidiChan    CON 0     ' 0 to 15 for MIDI channel 1 to 16

' PC numbers for footswitches P1 to P8
P1num       CON 0
P2num       CON 1
P3num       CON 2
P4num       CON 3
P5num       CON 4
P6num       CON 5
P7num       CON 6
P8num       CON 7

' CC numbers below can be 1 to 31 or 33 to 127

' There are no real CC number standards for guitar effects, but here's what Line 6 use:
' CC1 Pedal 1/Tweak, CC2 Pedal 2, CC4 Wah Pedal, CC7 Volume Pedal, CC25 Stomp #1,
' CC26 Compressor/Booster, CC28 Delay, CC36 Reverb, CC43 Effect Pedal On*, CC50 Mod, CC63 EQ,
' CC64 Tap, CC69 Tuner, CC72 Pitch Shift, CC73 Double Tracker, CC105 Effect Pedal Off*,
' CC107 FX Loop, CC109 Stomp #2, CC110 Stomp #3, CC111 Amp #1, CC112 Amp #2, CC113 Tremolo
' * CC43 and CC105 work opposite when implemented (ie when CC43 is turned on,
' CC105 is turned off & vice versa) CC105 is typically used for a volume pedal

' CC numbers for toggle footswitches C9 to C12
C9num      CON 25   ' Stomp
C10num     CON 50   ' Mod
C11num     CON 28   ' Delay
C12num     CON 26   ' Boost

' Momentary footswitch CC numbers
M13num     CON 72   ' Pitch
M14num     CON 36   ' Reverb

' Tap footswitch CC number
T15num     CON 64   ' Tap

' Pedal CC number
PedCCnum   CON 7    ' Volume


#SELECT $STAMP                    'set to 31.25 kBaud, open
    #ERROR "BS1 is not supported"
    MidiBaud CON $8000 + 12
    MidiBaud CON $8000 + 60
    MidiBaud CON $8000 + 108

MidiPC        CON $C0 + MidiChan  'MIDI patch change command
MidiCC        CON $B0 + MidiChan  'MIDI continuous controller command

'switch debounce
fswTests      CON 3               'how many tests for the same fsw press result
fswPause      CON 2               'mSec to wait between footswitch tests

AbsoluteMax   CON 2000            'absolute max PedalValue to prevent overflows


nFswNow      VAR Nib   'fsw now result
nFswChange   VAR Nib   'which footswitches have changed

' RCTIME values - IMPORTANT - check results from your own schematic,
' and set Min and Max values to suit
' then check no calculations in the program overflow their data types !!!
wPedalValueNow  VAR Word 'starts as RCTIME value read from pedal
yLastPedalValue VAR Byte 'last pedal CC value sent

yPedalPotMin    VAR Byte
wPedalPotMax    VAR Word

'temp vars, initialised JIT before use
nTemp     VAR Nib
yData     VAR Byte


  'RAM is set to zeros at powerup, so we can REM zero initialisations
  'nFswNow = 0
  'nFswChange = 0
  'wPedalValueNow = 0
  'yLastPedalValue = 0

  'RCTIME returns values of 70 - 1001 for 10K pot for RCTIME - series 1K & 0.1 cap - see schematic
  yPedalPotMin = 200                   'start with a conservative min (auto calibrates)
  wPedalPotMax = 500                   'start with a conservative max (auto calibrates)


    GOSUB GetSwitches
    GOSUB SendMidi
    GOSUB GetPedal



  'Footswitches P1 - P8, C9 - C12, M13 - M14 and T15 are high when pressed

  nFswChange = nFswNow ^ INA           'what's changed from last fsw press/release?
  IF nFswChange THEN                   'there is a change
    FOR nTemp = 2 TO fswTests          'check a few times
      PAUSE fswPause                   'wait a while
      IF nFswChange <> nFswNow ^ INA THEN
        nFswChange = 0                 'debounce test failed, so no change
        EXIT                           'exit
    nFswNow = nFswNow ^ nFswChange     'current fsw state




  IF nFswChange   THEN  ' process switches if something's changed

    'handle tap data first (this is time-critical)
    IF nFswChange = 15 THEN       'Tap fsw changed

      IF nFswNow = 15 THEN    'Tap fsw pressed, send MIDI
        SEROUT pinMidiOut, MidiBaud, [MidiCC, T15num, $7F]

    ELSEIF nFswChange <= 8 THEN 'this is a program change (fsw P1 to P8)

      yData = 255      'default invalid PC number

      SELECT nFswNow   'get pressed fsw (released P fsw's are ignored)
        CASE 1
          yData = P1num
        CASE 2
          yData = P2num
        CASE 3
          yData = P3num
        CASE 4
          yData = P4num
        CASE 5
          yData = P5num
        CASE 6
          yData = P6num
        CASE 7
          yData = P7num
        CASE 8
          yData = P8num

      'send PC
      IF yData.BIT7 = 0 THEN  'a valid PC number has been set

        SEROUT pinMidiOut, MidiBaud, [MidiPC, yData]
        OUTB = nFswNow - 1   'display 0-based P footswitch output via pins 4, 5 & 6

        'you would include CC reset messages here

        'we'll just reset all C toggle LEDs to off
        '(so next C fsw press will turn a CC on)
        OUTC = %0000  'pins 8, 9, 10 & 11 set low


    ELSEIF nFswChange >= 13 THEN 'this is a momentary switch M13 or M14 (T15 already handled above)

      SELECT nFswChange
        CASE 13
          yData = M13num
        CASE 14
          yData = M14num

      IF nFswNow = nFswChange THEN  'momentary switch was pressed
          SEROUT pinMidiOut, MidiBaud, [MidiCC, yData, $7F]
      ELSE                          'momentary switch was released
          SEROUT pinMidiOut, MidiBaud, [MidiCC, yData, $00]

    ELSE 'a CC toggle footswitch has changed C9 to C12

      yData = 255      'default invalid CC number

      SELECT nFswNow   'this ensures we're looking at a C fsw press, not a release

        CASE 9
          TOGGLE pinC9
          yData = C9num

        CASE 10
          TOGGLE pinC10
          yData = C10num

        CASE 11
          TOGGLE pinC11
          yData = C11num

        CASE 12
          TOGGLE pinC12
          yData = C12num


      IF yData.BIT7 = 0 THEN  'a valid CC number has been set
        IF INS & (DCD (nFswChange - 1)) THEN  ' toggle LED is on
          SEROUT PINMidiOut, MidiBaud, [MidiCC, yData, $7F]
        ELSE 'toggle LED is off
          SEROUT pinMidiOut, MidiBaud, [MidiCC, yData, $00]






  'read pedal value, auto-calibrate min and max as required

  'read pedal pot
  RCTIME pinPedalPot, 1, wPedalValueNow
  HIGH pinPedalPot  'for next time

  'recalibrate pedal range?
  IF wPedalValueNow < yPedalPotMin THEN
    'PedalValue = 0 if RCTIME reaches max time (136mS on BS2?)
    'this happens if pedal is open circuit, so ignore this case
    'PedalValue = 1 if some fault prevents timed measurement
    IF wPedalValueNow > 1 THEN
      yPedalPotMin = wPedalValueNow
      wPedalValueNow = yPedalPotMin

  ELSEIF wPedalValueNow > wPedalPotMax THEN
    'must limit max to prevent calc overflow below
    'ignore really high values (pedal is probably high resistance)
    IF wPedalValueNow <= AbsoluteMax THEN
      wPedalPotMax = wPedalValueNow
      wPedalValueNow = wPedalPotMax

  'adjust to range
  wPedalValueNow = wPedalValueNow - yPedalPotMin    'zero based, 0 to (max - min)

  'scale to MIDI data range (0 to 127)
  'in this calc, AbsoluteMax value = 2000, / 4 = 500, * 127 = 63500 (ok, doesn't exceed 65535) ... etc
  wPedalValueNow = ((wPedalValueNow >> 2) * 127) / ((wPedalPotMax - yPedalPotMin) >> 2)

  'don't re-send if same as last value
  IF wPedalValueNow <> yLastPedalValue THEN
    yLastPedalValue = wPedalValueNow.BYTE0
    SEROUT pinMidiOut, MidiBaud, [MidiCC, PedCCnum, yLastPedalValue]


'end of code

Back to GM Arts Home page