Arduino MIDI Rotary Encoder Controller – Part 2

Having just received back my Arduino MIDI Proto Shield PCBs I was thinking of a project to use them on and decided that a rotary encoder patch changer would be ideal, so this is a rebuild of my Arduino MIDI Rotary Encoder Controller using my MIDI proto shield.

Warning! I strongly recommend using an old or second hand keyboard for your MIDI experiments.  I am not responsible for any damage to expensive instruments!

These are the key Arduino tutorials for the main concepts used in this project:

If you are new to Arduino, see the Getting Started pages.

Parts list

The Circuit

ArduinoMIDIProto7SegmentRE_bb

As I was attempting to build this circuit onto my MIDI Proto Shield, I wanted to use a small, 0.28″ 3-digit 7-segment display.  All of the I2C-driven units (like those I’ve used before) are larger and 4-digits.  I have a smaller 3-digit display, but I will have to drive it directly from the Arduino.  This means it will need current limiting resistors for the LEDs (on the “digit” common cathode connections in this case) and the “matrix” of segments and digits will have to be connected directly to IO pins as I described here: Arduino MIDI 7 Segment Controller.

Also, rather than using one of those common KY040 rotary encoder modules, I’m just using a “raw” switched encoder here. I have included debouncing capacitors, but I’m not using external pull-up resistors – I’ll be relying on the code configuring the IO in INPUT_PULLUP mode.

In the Fritzing diagram above, I’ve reproduced the circuit on some protoboard but this will actually be built on the prototyping area on the MIDI proto shield.  The main difference is that there are 3V3, 5V and GND “rails” in the bottom left (I’m using the GND), and D0-13 and A0-A5 are connected to the top and bottom rows of the prototyping area respectively.

Apart from that this should be reproducible on standard protoboards, proto-shields or even solderless breadboards if required.

Here are some photos of my finished build.

IMPORTANT: As I’ve used standard pin-headers rather than extended headers, this can’t be used with an Arduino Uno with a USB B connector.  This will only fit on an Uno with a mini or micro USB connector.

The Code

I’m using a mixture of code from the following two previous projects:

I won’t go over details again, but the main points are detailed below.  Note that I’m using the following libraries:

And of course, FortySevenEffect’s ubiquitous Arduino MIDI library: https://github.com/FortySevenEffects/arduino_midi_library

The configuration for the SevSeg library is as follows (for details of how it works, see the above-mentioned previous project):

// Pin definitions for the display
//
//  = A =
// F     B
//  = G =
// E     C
//  = D =
//
// Pattern repeated for three digits (1,2,3) each with a common cathode.
// Note: Cathode should be connected to the "digit" pins via a resistor (1k).
//
// Digit Order: D1-D2-D3
//
// Pinout of my 10-pin display.
//
// D1--B-D2--A-D3
// |           |
// F--C--D--E--G
//
#define NUM_DIGITS 3
#define NUM_SEGMENTS 7
byte digitPins[NUM_DIGITS] = {4,3,2}; // Order: D1,D2,D3
byte segmentPins[NUM_SEGMENTS] = {5,6,7,8,9,10,11}; // Order: A,B,C,D,E,F,G
#define RESISTORS_ON_SEGMENTS false // Resistors on segments (true) or digits (false)
#define HARDWARE_CONFIG COMMON_CATHODE // COMMON_CATHODE or COMMON_ANODE
#define UPDATE_WITH_DELAYS false // false recommended apparently
#define LEADING_ZEROS true // Set true to show leading zeros
#define NO_DECIMAL_POINT true // Set true if no DP or not connected

The configuration for the rotary encoder is as follows:

#define RE_A A1 // "CLK"
#define RE_B A0 // "DT"
#define RE_SW A2 // "SW"
RotaryEncoder encoder(RE_A, RE_B, RotaryEncoder::LatchMode::FOUR3);

Note the use of “FOUR3” mode for the encoder.  This is the one that works best for me, other encoders might need to use a different mode (see the encoder library for details) and it is possible that the A and B pins could be the other way round.

The main logic is something like this:

loop:
  Call MIDI.Read() to handle any MIDI THRU functionality
  Refresh the display
  Read the rotary encoder

  IF the rotary encoder has changed position THEN
    Increase or decrease the stored Program Number (range 1 to 128)
    Update the display with the new number
    IF instant updates are required THEN
      Send the new MIDI Program Change message

  IF the rotary encoder switch was pressed THEN
    Send the MIDI Program Change message

There are two modes, configurable in the code: instant or non-instant mode.  In instant mode, the MIDI Program Change message is sent as soon as the rotary encoder is turned to change the program number. In non-instant mode, it is only sent when the rotary encoder’s switch is pressed.

Note that automatic software MIDI THRU functionality is supported, so this means that a MIDI keyboard can be connected to the MIDI IN port, whilst a MIDI sound module can be connected to the MIDI OUT port and both the keyboard and Arduino can send messages to the sound module.

Here is a connection diagram showing what I mean (it is the same as my previous project):

ArduinoMIDIRE7Segment

Update: The code now also includes the option to switch to “bank select” mode.  This can be achieved either using the rotary encoder switch, if in “instant mode”, or using an additional external button if in “non-instant mode” which will need the encoder switch to send the messages.

The various compilation definitions that allow this are as follows:

// Comment this out if you don't want the program change messages to
// be instant, but would prefer them to require a button press.
#define INSTANT_PC 1

// Comment this out if you don't want a "bank select" mode.
// By default this will use the rotary encoder switch
// to switch modes.
#define BANK_SEL 1

// Optional: Use an additional switch as the bank select mode switch.
// If not defined but BANK_SEL above is, then it is assumed the
// rotary encoder switch changes modes (this only really makes sense
// if the code is running in INSTANT_PC mode though!)
//#define BANK_SEL_PIN A5

To actually enable Bank Selection over MIDI requires the sending of two MIDI CC values – a least significant byte (LSB) and a most significant byte (MSB).  These are defined upfront in a structure as follows:

#define NUM_BANKS 8
int ccs[NUM_BANKS][2] = {
// MSB, LSB
  { 0, 0},
  { 0, 1},
  { 0, 2},
  { 0, 3},
  { 0, 4},
  { 0, 5},
  { 0, 6},
  { 0, 7},
};

Unfortunately these will be specific to the synth being controlled!  The display shows which entry in this table is being used from 1 to “NUM_BANKS”.  It adds a minus to indicate the system is in “bank select” mode.  So to cycle through the above, it will go from -01 through to -08 and then wrap around back to -01 again.

Find it on GitHub here.

Closing Thoughts

The video shows this being used to control my Korg O5R/W in general MIDI mode. I’ve added a small, mirrored surface to show the voice changing on the module (in reverse of course), just to prove it is really changing it!

I did manage at one point to “crash” the display – it got stuck showing a single segment on each of the digits.  Having fiddled about with it a little, I wondered if it was a power supply issue… with a different power supply for the Arduino it all seemed ok.  Even with the display stuck, the MIDI side of things was all working fine though, so it is a bit of a mystery.  I’ll have to see how it goes!

One enhancement I’ve already thought of: it might be useful to have a variant that could use A3-A4 rather than D9-11 as three of the LED pins.  That would allow the use of add-on shields that use SPI.  Of course, actually, what could be really useful would be to have ether a shift-register or an SPI/I2C driver chip to drive the 7-segment display itself… but then the circuit is getting more complex.  An I2C OLED display could be another option.

But the main aim of the project was to use one of my MIDI proto shields, and I think this is a pretty good use.  This is a much neater unit that would be possible with a standard proto shield and external MIDI module.

Kevin

Leave a comment