USB MIDI Controller
Tuesday, 2014-12-09, 18:54I recently started learning to play the electric guitar. To be able to spend more money on the actual guitar, I forwent to buy an amp and chose instead to use Guitarix, an open source program that can do all sorts of nifty guitar effects. It also has MIDI support, meaning that you can use hardware that speaks MIDI to control it, e.g. a slider or foot pedal. The cheapest pedal I could find went for 45 EUR on eBay, so I decided to build my own.
Setup
- ATmega32u4 (for testing an Arduino Leonardo will do) as host device
- Uses LUFA to send MIDI messages to the computer over the USB
- Uses I2C to talk to one or more clients
- ATmega8 as client device
- ADC to read the controller value from a potentiometer or similar
- It’s possible to read up to 4 analog values and 15 digital value with one ATmega8. Two of the analog input pins are used up by I2C, and one more pin is used to enable the next client in the chain.
- Uses I2C to communicate with the master
- Clients are daisy-chainable, see Communication and Wiring
- ADC to read the controller value from a potentiometer or similar
Communication
The host device acts as a slave and has address 1, all clients are I2C masters.
- Initialization
- When a client powers on, it repeatedly (until it receives an
answer) sends a message to the host, consisting of
- A request code
- The number
nc
of controls it needs allocated to itself
- The host then allocates a continuous block of
nc
controls for the device and responds with- The response code corresponding to the request
- The 7-bit address of the first control
- The device then enables the next device, either by pulling its RESET line high or enabling VCC
- When a client powers on, it repeatedly (until it receives an
answer) sends a message to the host, consisting of
- MIDI messages are constructed by the clients and sent as-is. The host recognizes them through the set MSB of the first byte and will buffer them until it has received the STOP, then forward them over USB.
- On collision, the device waits until a STOP condition has been detected and tries again.
A preliminary command table, with each length including the command byte. Generally, response codes are the same as the request code, but with the most significant bit set to 1.
Request | Length | Response | Length | Description |
---|---|---|---|---|
0x3F | 2 | 0xBF | 2 | Initialization |
0b1xxxxxxx | According to MIDI spec | None | 0 | MIDI message |
In theory the same system can be used in reverse to act as a MIDI OUT device, by having the clients become I2C slaves and the host act as a master. You would only get 127 slave IDs, but there are ways around that by using the general call.
Wiring
- GND, SDA, SCL are common to all devices
- Each device switches a FET or similar to enable VCC for the next
device in the chain after it has triggered
- This can also be used to trigger the RESET pin instead, if a device has its own power supply
- A simple method to hook these up to each other is to use TRRS connectors. These are commonly used for smartphone earphones with a microphone.
Code
On Github. :)
Notes
At first I wanted to use an ATtiny24 as client device, since in theory it had everything I need - an ADC, I2C support, and one extra I/O pin to enable the next device. This plan failed due to my inability to make the USI work properly. It would initialize its addresses properly, but then hang during the transmission of the first control change message. I could bitbang I2C, but that would occupy the bus unnecessarily long. In the end it’s no big loss, as the ATmega8 is cheaper anyway, but it does feel like using a sledgehammer to crack a nut :)