Project: VCV Rack Cable Control


A virtual cable controller for VCVRack which requires nothing more than a Raspberry Pi Pico (or other CircuitPython compatible Microcontroller). Written in CircuitPython. Provides 26x physical sockets which can be distributed as inputs or outputs via entries at lines 23 & 27 of, which are then remapped to MIDI CC gates (1-indexed sequentially, outputs then inputs). MIDI Output channel is set at end of line 17 (0-indexed in code, becomes 1-indexed in reality)

Use with Stoermelder’s (experimental) VCVRack-PackTau T7 modules which maps MIDI CC Gates to module i/o ports in VCVRack:

  • GPIO2-14 are (potentially stackable) outputs mapped to MIDI CC1-13 Ch1
  • GPIO15-27 are (non-stackable) inputs mapped to MIDI CC14-26 Ch1
  • When outputs are shorted to inputs, the matching pair of MIDI CC transmit level 127.
  • When disconnected, the matching pair transmit level 0
  • Pack-Tau T7-CTRL/T7-MIDI translate the pairs of MIDI CC Gates into a cable link between ports mapped to those CCs via a JSON file loaded into T7-CTRL module (see examples/ folder)

To make a physical patch bay: Attach tip connector of 3.5mm socket to each GPIO pin. Short with mono 3.5mm patch leads. Done!

GPIO0 & 1 are skipped as I intend to use these for i2c expansion (see further down this file)

What’s happening on the Pico: ALL GPIO PINS are set as input with pull-up, defaulting high in the code. GPIO Pins 2 thru 14 are switched to outputs one at a time and pulled low and then GPIO Pins 15 thru 27 are sequentially scanned for any now pulled low (linked). Their state is compared against a statematrix dictionary object – if the state differs, the relevant MIDI CC codes are sent to reflect the change – then the statematrix updated to store the latest current (inverted) state. The current GPIO Output is then switched back to a pulled-up input before moving on to the next output pin.

This prevents any pair of pins being set as an Output simultaneously, as a short between two pins set as Outputs would damage the Pico without additional hardware protection. This scanning process runs in a continuous loop. A full sweep of the matrix takes around 80ms iirc so that’s your rough max latency.

PiPico GPIO Pins

If running multiple Picos as the code stands, change MIDI channel in code at line 17 so each Pico outputs on it’s own channel (or rewrite the code to remap the ccs from a higher start point to not conflict with an existing Pico on same MIDI Channel), but don’t link across Picos because that isn’t handled and will make a mess (input-bearing Pico will think an input is linked to EVERY output on itself), especially as you now have a situation where an output per Pico may be active simultaneously, which could cause Physical Damage if linked (so definitely don’t link outputs of one Pico to outputs of another Pico or you’ll risk popping them both)

  • Grab Pack-Tau ( and manually install.
  • Copy & lib/ folder to Pi Pico running CircuitPython
  • Open examples/t7-ctrl_example.vcv in VCVRack (requires Pack-Tau, BogAudio, Instruo & VCV Fundamentals) – this file maps 24 of the available sockets to MIDI CCs…
  • Short any of GPIO2-13 to GPIO15-26. Be careful NOT to inadvertently short to RUN or GND, which will crash the Pico (no damage) – unplug / replug to restart it, but statematrix will now have reset to 0 everywhere, so your links will be out of sync – unplug/replug one end of each link to get state back in sync. MIDI CC13 & 26 (GPIO14 & 27) aren’t mapped in the example json file. 12x output, 12x input. Add them for 13x of each (26 sockets).

To load a custom map, click ports on your modules and watch the T7-Assistant module. Note the moduleId and portId (you can highlight and copy them from the T7-Assistant display)


Open examples/t7-ctrl_blank.json in a text editor. Add/paste the relevant moduleId and PortId (pay attention to the portType in the JSON file – output portIds for output port types, input portIds for input port types) to the CC to be mapped to that port. This maps to the equivalent GPIO pin [CC + 1].

completed codeblock

Once you’ve got that done for all the ports in the file (or removed unmapped entries), select all > copy. Right click T7-Ctrl module > Paste JSON Mapping. (This gets stored in the VCVRack file on save)

Context Menu of T7-CTRL

NOTE: T7-MIDI module MUST be adjacent to the right of the T7-CTRL module for MIDI commands to be transmitted from T7-MIDI to T7-CTRL. Select the PiPico midi device in T7-MIDI. For multiple Picos, just add more T7-MIDI modules to the right. All the mapping goes into the single T7-CTRL instance

Now start shorting your Pins (or if you’ve added sockets etc, patching your cables) and watch cables appear onscreen between the mapped ports, and disappear when you unshort.


MCP23017 modules over i2c provide additional banks of 16 digital GPIO at a time, so can be used raw or in HAT format (and just link the i2c / power / gnd pins up). Up to 8 can be added to get a total 156 physical sockets (or combination of sockets, buttons & switches) on one Pico. Adafruit provide a library for CircuitPython, which would make this the easiest way to expand the code also. Same applies for banks of ADS1115/1015 modules to add 4x channels of ADC for sliders & potentiometers per module. BUT, time to loop over the matrix increases so latency may creep up.

I’ll look into these at somepoint when I’m not so broke… one step at a time. But that should be enough to shove folk in the right direction to investigate for yourselves (plus I have no shortage of knobs & sliders in my setup already – BCR2000 & BCF2000 fulfil that remit for now).