MIDI Foot Controller
Contents |
Overview |
^ 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 www.parallax.com 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 www.parallax.com 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
'=====================================
' PINS
'=====================================
' 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
'=====================================
' USER CONFIG
'=====================================
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
'=====================================
' CONSTANTS
'=====================================
#SELECT $STAMP 'set to 31.25 kBaud, open
#CASE BS1
#ERROR "BS1 is not supported"
#CASE BS2, BS2E, BS2PE
MidiBaud CON $8000 + 12
#CASE BS2SX, BS2P
MidiBaud CON $8000 + 60
#CASE BS2PX
MidiBaud CON $8000 + 108
#ENDSELECT
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
'=====================================
' VARIABLES
'=====================================
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
'=====================================
' INITIALISATION
'=====================================
'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)
'=====================================
' MAIN PROGRAM
'=====================================
DO
GOSUB GetSwitches
GOSUB SendMidi
GOSUB GetPedal
LOOP
'=====================================
' SUBS
'=====================================
GetSwitches:
'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 for...next
ENDIF
NEXT
nFswNow = nFswNow ^ nFswChange 'current fsw state
ENDIF
RETURN
'=====================================================================================================
SendMidi:
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]
ENDIF
'------------------------------------------------------------------
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
ENDSELECT
'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
ENDIF
'------------------------------------------------------------------
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
ENDSELECT
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]
ENDIF
'------------------------------------------------------------------
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
ENDSELECT
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]
ENDIF
ENDIF
ENDIF
ENDIF
RETURN
'=====================================================================================================
GetPedal:
'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
ELSE
wPedalValueNow = yPedalPotMin
ENDIF
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
ELSE
wPedalValueNow = wPedalPotMax
ENDIF
ENDIF
'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]
ENDIF
RETURN
'=====================================================================================================
'end of code
|