Projects

Mix It Up

A DIY 3D Printed Arduino-Controlled MIDI CDJ

Victor Casado

Issue 25, August 2019

This article includes additional downloadable resources.
Please log in to access.

Log in

Build your own Arduino MEGA based USB MIDI DJ Controller with 3D Printed enclosure. - by Victor Casado & Johann Wyss

BUILD TIME: 5 Hours + 3d PRINTING TIME
DIFFICULTY RATING: Intermediate

One of our avid readers, Victor Casado, recently shared his DIY MIDI CDJ project to us. Instead of simply featuring his wonderful build in our magazine, we thought we would get Victor to describe how to build one for yourself. We ended up going one step further and building one for ourselves, with a few modifications along the way.

As a matter of introduction, Victor has just finished the first year of Electronic Engineering and has developed this project in his free time for the last three months.

This is a full DIY project that you can build at home with a relatively low budget, a 3D printer, and not much Electronic/Programming knowledge. You can download the print and code files from the Resources section of our website. There will be a few components, such as the rotary encoders, that you may need to source from online retailers, however, most of the components are available from local retailers. You can also modify the project to suit other switches and potentiometers, as you see fit.

THE BROAD OVERVIEW

Victor has always been keen on music and instruments. A CDJ MIDI controller had always amazed him, however, due to their high cost he could not afford one being a student.

By using affordable and commonly available electronic components, such as the Arduino, open source software, and access to a 3D printer, Victor was able to build his own “commercial looking” product at home.

WHAT IS MIDI?

MIDI is a technical standard that describes a communications protocol, digital interface, and electrical connectors that connect a wide variety of electronic musical instruments, computers, and related audio devices for playing, editing and recording music.

A single MIDI link through a MIDI cable can carry up to sixteen channels of information, each of which can be routed to a separate device or instrument. The standard MIDI connector is a DIN-5 plug. but modern devices use USB, just like the project we describe here.

MIDI over USB has become increasingly common as other interfaces that had been used for MIDI connections (serial, joystick, etc.) disappeared from personal computers. Linux, Microsoft Windows, Macintosh OS X, and Apple iOS operating systems include standard class drivers to support devices that use the “Universal Serial Bus Device Class Definition for MIDI Devices”. Some manufacturers choose to implement a MIDI interface over USB that is designed to operate differently from the class specification, using custom drivers.

HOW IT WORKS

Basically, the Arduino MEGA is the “brains”, which reads the data from the Analogue Inputs (Potentiometers), the Digital Inputs (Pushbuttons), and the Interrupt Inputs (Encoders). It then maps that data from the reading value range to the MIDI protocol range. For example, due to the 10-bit resolution of the Arduino ADCs, the potentiometer reading will be between 0 and 1023 values, but the MIDI expects data from 0 to 127. You do this with a mapping process. In this case, we have used the Binary Shift technique rather than the map() function because it’s faster, more efficient and more professional. From there, it sends the data over the Serial Port.

The project requires you to use the Arduino IDE to flash the ATmega16U2 chip that makes the function of “bridge” between the MCU (what you program) and your computer. This chip is what will be the HID device for your computer, as a commercial product would be. Then your favourite MIDI capable software will be controlled by the CDJ.

ENCODERS

Two rotary encoders are required for the build. These are both 24PPR (24 Pulses per 360° Rotation); one with detents and the other without detents for the Jog Wheel.

SWITCHES

If you intend to use this as a DJ mixer in dark environments then we recommend you use switches with built-in LEDs. If this is the case, you will need to put 100Ω resistors in series with each LED. If you are using PCB mounted switches, as Victor did in his project, you will need some prototyping board to solder these onto.

JOG WHEEL

The jog wheel needs to be 3D printed, which we detail further within the build. In Victor's build, he installed a bearing with 8mm internal diameter and 22mm external diameter for his jog wheel to spin without problems. Our final build does not use this, and the jog wheel spins fine.

Victor's Build:

Our build has been based on Victor's build shown here.

You can learn more about Victor's build and watch a video to see it in action by going to this website: https://create.arduino.cc/projecthub/victorcasado/

We have retained most of Victor's design, but have made improvements where we saw the need.

The Build:

3D Printed Arduino MIDI CDJ

For our build, we made some modifications to Victor's design to make it easier to assemble and wire up, while still maintaining the look-and-feel of Victor's design. The building process is very easy but will take a few hours, depending on your experience with soldering, programming, and building.

We suggest you start with the 3D printed parts so you can work on the electronics while the parts are printing. The electronics is mostly soldering the switches and LEDs onto power bus lines, soldering resistors to the associated LEDs, preparing your Arduino MEGA, and finally wiring it all up.

Note: For simplicity, the schematic is shown without the jog wheel and switch LEDs. The 22 LEDs (12 for jog wheel and 10 tactile switches) each require a 100Ω resistor in series (The three metal switches have resistors built-in).
Electronic Parts Required:JaycarAltronicsCore Electronics
1 × Arduino Compatible MEGAXC4420Z6281CE05630
3 × 10kΩ Linear Single Gang 9mm PotentiometersRP8510--
3 × 18mm Brushed Aluminium 1/4" Shaft Knobs-H6305-
1 × 16mm Aluminium Black 1/4" Shaft KnobHK7009H6211-
1 × 10kΩ Linear Slide Pot - Medium--COM-11621
2 × SPST Illuminated Red Pushbutton Switches-S0952-
1 × DPDT Blue LED Solder Tail Pushbutton SwitchSP0793S0937-
10 × SPST Momentary Red LED PCB Mount Tactile Switches-S1101-
22 × 100Ω 1/4W Resistors*RR0548R7534COM-10969
5 × No.6 × 15mm Screws* (to mount MEGA)HP0620--
4 × No.4 × 6mm Screws* (to secure base)HP0558H1145-
1 × Pack of 100mm Cable TiesHP1196H4031AFIT0343
1 × 1m Length of Tinned Copper WireWW4032--
1 × Mixed Jumper WiresWC6027P1017PRT-14284
1m × Ribbon CableWM4516W2516CAB-10649
1 × 1m Length of 2.5-3mm HeatshrinkHP5531W0912AADA1649

Electronic Parts Required:

OPTIONAL: JaycarAltronicsCore Electronics
1 × Slide Pot Knob (3D print file also included)--COM-09120
1 × Small Tube of Liquid Electrical TapeNM2836T3135-

OPTIONAL:

Also required:RS Components
1 × Rotary Encoder - 24 Quadrature without Detent (PEC11R-4015F-S0024) 737-7739
1 × Rotary Encoder - 24 Quadrature Detent (PEC12R-4220F-S0024)737-7773
10 × Illuminated Tactile Switches^ (FSMIJM63APG04)** 124-6913
10 × Red Tactile Switch Cover for use with Illuminated Tactile Switch (2311403-3)** 138-0528

* Quantity shown, may be sold in packs.

^ The number of buttons depends on what software you plan to use and how many controls you want to map. The project was designed for 15, but NI Traktor only supports 9.

** Victor's design used tactile switches from RS components, which need to be soldered onto perfboard. We found throughhole versions from Altronics instead.

CONSTRUCTION

There is a significant amount of wiring to be done to complete this project, so please give yourself plenty of time to avoid any wiring errors.

We have used a different approach to Victor's wiring by using solid copper wire for the main power and ground circuit. This helps avoid a birds nest of wires, and makes wiring and troubleshooting much more straightforward. Three separate lengths will form the 5V and GND rails, which all other components will connect to.

We used the copper wire from a metre length of multi-core mains power cable and separated a few individual strands. You could also use tinned copper wire, which we have listed in the parts list.

Note: Bare copper wire, and even the tinned copper wire should be clear of oxidation and ready for clean and effective soldering. On the workbench, we commonly pull wire, and even resistor and other component legs, through steel wool, a scotch bright pad, or even an ink eraser slit with a sharp knife.

Note: The switch and encoder descriptions in this Fritzing describe Victor’s setup using Traktor software. Readers can map the components differently depending on the program they use. The LEDs are not shown so that the main wiring is easier to follow.

LEDs

Insert the twelve LEDs into the holes in the 3D printed enclosure. We recommend you position them in the following fashion to make wiring easier; the inner circle has the anode facing the centre and cathode facing out, while the outer ring is reversed with the cathode facing in and the anode out. In this orientation, you will only need three wire loops for the power and GND rails, as the cathodes will share the one ring. Once you are happy with the orientation and position of the LEDs, secure them with a little hot glue to prevent them from moving during the soldering process. You now want to make a loop of wire that will connect each of the anodes and cathodes, being sure to add a 100Ω resistor to one side to limit the current to the LED.

Note: Some people will suggest that you can use a single resistor to limit the current to multiple LEDs in parallel. This is fundamentally incorrect and will certainly result in a dramatically reduced LED lifespan. Each LED has a slightly different forward voltage. If you don’t add a current limiting resistor to each LED, the LED with the lowest Vf will sink more current than the others, this results in that LED failing prematurely in which case the next LED with the lowest Vf sinks more until all the LEDs have failed. Whilst it is tempting to just use one resistor, we strongly advise you to heed this warning.

You should now have twelve LEDs firmly in place and wired to three rings with the centre ring being 5V, the middle ring as GND, and the outer ring as 5V.

ROTARY ENCODERS

You can now begin to add the control components, starting with the jog wheel rotary encoder (This is the encoder without the detent).

Connect the centre pin of the encoder to the GND ring you just created by using a small length of wire (We used a single wire of ribbon cable). The outer two pins of the encoder are the data signals. We simply cut the end off a male to male/female jumper wire, stripped a small amount of the insulation back and soldered it directly to the pins of the rotary encoder. The male side will connect to the input pins of the Arduino MEGA.

You will notice that the opposite side of the encoder has two pins. These will be left unconnected, however, you can use these as a push-button if you desire for your application.

Repeat the same process for the second rotary encoder that has the detents.

It’s a good idea to insulate all the connections to protect against short circuits. We have used a combination of liquid electrical tape and heatshrink.

POTENTIOMETERS

Connect the three rotary and the slide potentiometers to the 5V and GND rails. The 5V and GND connections of the potentiometers will be the outer connectors (1 and 3, with 1 being 5V and 3 being GND). The centre pin (pin 2) is the wiper, which we solder a jumper wire onto. This lead needs to be long enough for the male connector to reach the Arduino MEGA.

SMALL PUSH BUTTONS

Wire in all of the thirteen buttons. To do this, we followed the same point-to-point method. Use a single strand of copper wire connect all of the Cathodes of the LEDs and negative switch connections.

The LED of each switch needs a 100Ω resistor soldered to them. The resistors are then soldered together in parallel. Both of these main power bus wires were then connected in correct polarity to the 5V and GND rings.

You then want to solder a male ended jumper wire to the opposite side of each switch so that we can connect it to the Arduino MEGA.

LARGE PUSH BUTTONS

Install the three larger push buttons.

These switches have inbuilt current limiting, so we don't need to add a resistor.

Connect the positive side of each switch and LED together using the point-to-point method, and secure the bus wire to the buttons using the screw terminals. Solder these to the 5V and GND rings we made earlier.

For the two larger switches, we left the pin header attached as it was easier to secure to the switch screw terminals. For the smaller latching switch, we soldered the signal wire directly to the tab.

All of the LEDs, buttons, potentiometers and rotary encoders should now be installed, wired and secured with either their mounting hardware or glue. It's now time to mount the MEGA.

MEGA BOARD

We designed standoffs into the 3D printed enclosure to hold the MEGA board, ensuring there is sufficient space to prevent shorts with the wiring underneath.

The standoffs have 2.5mm holes designed into them to allow M3 or 4G screws to secure the Microcontroller firmly to the board. You may find that one of the screws is not able to be inserted due to its proximity to the pin headers. If so, don’t force it as doing so could damage the microcontroller. Five screws should be more than adequate to hold the board securely.

Follow the provided wiring diagram to connect each of the signal wires to the Arduino MEGA. You also need to solder a male ended jumper wire to from both the GND rail and 5V rails, which can be plugged directly into the 5V and GND pins of the MEGA. This will power all of the hardware.

We have also provided the ability to add a power switch, which can be used to isolate the 5V from the bus rings. This will allow you to turn off all of the LEDs without unplugging the device. Note that with the switch in the off position there will still be power via the USB to the MEGA and some functions will still work, however, the LEDs and potentiometers will be disabled as they will not have 5V available.

If you want to use the switch, all you need to do is solder a wire from the 5V bus ring to the switch, and the remaining contact is wired to the 5V rail of the MEGA.

THE CODE

The code is straightforward and uses two open source libraries; the MIDI.h and the RotaryEncoder.h. These can be downloaded from Github: https://github.com/FortySevenEffects/arduino_midi_library/blob/master/src/MIDI.h and https://github.com/mathertel/RotaryEncoder

You will find that the code works fast, which is exactly what we need. It reads every input and has interrupt algorithms that only send data to the computer when some input has changed its value. This avoids your system saturating the buffer.

The MIDI messages are sent by the MIDI library class functions sendNoteOn() for the push buttons, and sendControlChange() for the potentiometers and encoders.

Instead of the standard Arduino bootloader, we want to change the firmware of the ATmega16U2 to the open source MOCO-LUFA bootloaders. We have compiled one for those who don’t know how to compile with GCC or Make, but we have included the code if you want to recompile the bootloader with your name.

IMPORTANT NOTE: The VID and PID used for this project have been sublicensed by Microchip (the manufacturer of the MCUs that original Arduinos run). They MUST NOT be used for commercial purposes, but can be used on a project for your OWN personal purposes.

PROGRAMMING

PROGRAMMING THE MEGA

The code to program the MEGA is available to download from the Resources section (below) of the DIYODE website.

Firstly, upload the code to your Arduino with the Arduino IDE. Refer to ‘Setting Up The Arduino IDE’ article in Issue 017 if this is your first time doing this.

ATMEGA16U2 PROGRAMMING

We will separate programming the ATmega16U2 in two parts. First, we'll explain how to flash it with the precompiled bootloader, and then explain how to modify the code and recompile it.

Note: These instructions are written assuming you are using a Windows machine. Linux/Mac may need to be done differently.

FLASHING WITH THE PRECOMPILED BOOTLOADER

To flash the 16U2 you need to use Atmel FLIP, which you can download for free from the Microchip page. https://www.microchip.com/DevelopmentTools/ProductDetails/PartNO/FLIP

Windows may require the Java Runtime Environment installed, which can be downloaded from Oracle if you don’t have that installed.

Select the ATmega16U2 from the Device Selection menu.

Choose File then Load HEX File from the Flip menu.

Briefly connect the two jumpers on the MEGA with a cable (as you see below). Your computer should sound like a device was disconnected.

Remove that cable from the two jumpers and your computer should sound like a new device was connected. (Mac/Linux users should find a port close and open again in '/dev/').

The Arduino is now in DFU mode, ready for flashing.

Note: for the next steps, if Flip doesn’t allow you to open the USB port, it is probably because you don’t have the DFU drivers for the 16U2 installed. To install them, go to the Device Manager. Find the ATmega16U2 shown with a warning, open the device properties, select install driver, and install the drivers at C:Program Files (x86)AtmelFlip 3.4.7usb

Choose USB and Open the USB Port Connection.

Match the settings in the following screenshot. Select ‘Run’, followed by ‘Start Application’.

Disconnect your Arduino, reconnect it and your Device Administrator should show a new Audio Device called “CandyX”.

Select CandyX in your preferred software and enjoy!

RECOMPILING THE BOOTLOADER

Now we will show you how to modify the bootloader code and compile. This is the MOCO-LUFA open source bootloader, which can only be used and modified under no commercial purposes.

Open the bootloaders file folder attached.

With the help of a text editor (We recommend Notepad++) open Descriptors.c (you must go to bootloadershiduino-masterLUFA-140928Projectsarduino_midi)

You can modify the Vendor ID and Product ID but we recommend you leave them to match our setup.

IMPORTANT NOTE: The VID and PID are sublicensed to Victor - he lets you use them ONLY for your personal builds. It is COMPLETELY FORBIDDEN to use them for COMMERCIAL PURPOSES.

You can edit the ManufacturerString and ProductString with a custom developer name and a custom device name, which will be the name that your computer will show when the device is connected. It will also be the name that you will select in your software.

Before you create the .HEX file, you first want to place the arduino_midi folder containing the newly modified discriptor.c file into a location that is easy to navigate to. We recommend placing the folder directly into the drive, which will save the effort of navigating through many folders.

We found the easiest directory to place the folder was the D:, therefore, the location of the makefile is D:hiduino-masterLUFA-140928Projectsarduino_midi

To create the HEX file, you simply open the windows start menu and type: cmd. This will open up the command prompt console. Next, type the following:

Press enter to compile; to do that, you should install the avr-gcc toolchain.

When it has successfully compiled, you should see new objects in the arduino_midi folder. You only want the arduino_midi.hex, which is the file you should upload to the 16U2, as explained in the "Flashing With The Precompiled Bootloader" section.

TESTING

Now you have to see if it works correctly. For that, we have included the mapping file for the NI Traktor software. If you don’t have Traktor, there is free software that you can download called MIDI-OX that monitors the incoming MIDI data so you can see if everything works properly. For that, you will have to set up your device as MIDI Input in the preferences of the MIDI-OX.

Make sure that you have reflashed the 16U2 MCU with the bootloader we have built, because if you don’t, then the Arduino will work as a COM Serial device, not a MIDI HID device. You won’t see anything in the MIDI-OX monitor.

3D PRINTED ENCLOSURE & JOG WHEEL

We made a few adjustments to Victor's original design to make it easier to print and give it a little more structural rigidity. You can download the .stl files from the resources section on our website.

CASE

Firstly, we redesigned the case to print as one piece. This will require a printer that can support a build area of 200mm x 200mm. Printers, such as the Prusa i3, Lulzbot Taz, Flashforge Guider II and the CoreI3 from Altronics can print this size, for example.

We also removed the need for a bearing under the jog wheel, in favor of mounting the jog wheel directly to the rotary encoder. We found that the bearing didn't offer any support, and added unnecessary complexity to the design and build. However, we did add a slider ring to the case design to support the jog wheel as the user puts weight on the jog wheel. This reduces pressure on the encoder's shaft.

Our case was printed on a Cocoon Create I3 at a 300-micron layer height with supports enabled. It took about 14 hours to print at this layer height using black Flashforge branded filament.

Note: You may notice that we have made a small modification to the main base. In our images, the USB cable comes out of the rear of the unit. In the final revision, we moved the hole to align with the Arduino USB socket. You can easily modify the design in tinker CAD, for example, to change the position to suit your needs. We have also included a cover.stl file if you want to cover the hole as you see in our photos.

JOG WHEEL

We printed the jog wheel at a 200-micron layer height on our Flashforge Creator Pro. We used the Jaycar glow-in-the-dark filament for effect. At this layer height, it takes about 5 hours to print without support material.

SLIDER KNOB

We also printed a slider knob in the glow-in-the-dark filament to match the jog wheel. This was printed at a 100-micron layer height with support material and a raft. This took an hour using our Flashforge Creator Pro.

REAR COVER

The final part to print is the rear cover. This took 4 hours at a 300-micron layer height on our Cocoon Create I3, using black Flashforge branded filament.

CASE ASSEMBLY

The jog wheel is designed to slot directly over the metal shaft of the rotary encoder and is best secured to it using a small dab of hot glue. The slider knob should just press fit over the shaft of the slider potentiometer. The rear cover is designed to simply fit over the rear of the base unit and is secured using 4 × M3x15mm or No.6 × 15mm screws.

WHERE TO FROM HERE?

There are a few improvements that Victor has thought of since completing this build. For example, if you build it with the LED pushbuttons, you could make them light up when something in your software happens. For example, you could have the PLAY button with the LED ON if there is nothing playing, and the LED blinking if the music is playing. If you have a large 3D printer, you could print the enclosure with enough space to fit a 3.5” inch TFT to show some data from the software.

In general, to make this, you will have to modify your code. The first thing you will need to do is set up the Arduino to handle an Input function. For this, in the setup() function, you should add MIDI.handleControlChange(YOUR_FUNCTION); and then add to your code the function with the next prototype: void YOUR_FUNCTION(byte channel, byte control, byte value);

void YOUR_FUNCTION(byte channel, byte control, 
byte value){  
//This receives the data from MIDI channel 1,
value 1
  if(channel == 1){
    if(control == 1 && value == 0){
      digitalWrite(PIN_WHERE_THE_LED_IS_CONNECTED, LOW);
    }
    if(control == 1 && value == 127){
      digitalWrite(PIN_WHERE_THE_LED_IS_CONNECTED, HIGH);
    }
  }
}

Here you can make a simple program that turns on a LED when the play button is pressed and turn off the LED if it isn’t. Or you could consider making a VU Meter with the WS2812B RGB addressable strip:

void YOUR_FUNCTION(byte channel, byte number, 
byte value) {  
  // For this you will need to use the 
  // Adafruit_NeoPixel.h library
  int n_led = 0;
  if (number == 20) {
  for (int i = 0; i <= value >> 3; i++) {  
  // This will draw a vumeter that starts
  // from BLUE and finishes at PURPLE colour.
    for (int j = 0; j < i; j++) {
    strip.setPixelColor(i, (j * 16) , 0, (255 - (j * 16)));
    }
  }
  for (int i = ((value >> 3) + 1); i < 16; i++) {
    strip.setPixelColor(i, 0);
  }
  strip.show();
  }
}
Victor Casado

Victor Casado

First year Electronic Engineering Student, Madrid, Spain