MIDI-fying an old Casio Digital Keyboard

Marwan Ghamloush
14 min readMay 19, 2020

--

Hello!
With the current down-time at home, alot of us turned to developing skills we always wanted to boast. For me, I wanted to make music (or just cool sounds really). So I got my laptop ready with a digital audio workstation (DAW) and started messing around, but it soon became apparent that playing virtual music instruments with a keyboard and mouse is less than intuitive and downright painful on the wrists.
Taking inspiration from my YouTube professors, I noticed most of them have a MIDI-keyboard connected to their workstations for the sole purpose of playing virtual instruments.

That’s where I remembered this piece of beauty:

Casio PT-480 Keyboard Synth

We’ve had this keyboard for my older sibling before I was born, and though it’s quite dusty it still worked well and could play crisp tunes. Being outdated, the keyboard had only three ports : 7.5V power, audio OUT (a headphone jack), and audio IN (for a mic). More than enough for a stand-alone instrument, but lacking for the digital workflow.

Just to keep the goals clear, what I intended to do is find some way to translate the key presses into signals I can send to my computer.

Being the curious human I am, I decided to crack it open and see what I can do to upgrade this keyboard.

The juicy insides of the keyboard

Right away we can see the keyboard keys are isolated to their own (lengthy) PCB at the bottom, with only a 12 wire-ribbon cable connecting it to the rest of the system. Just 12 wires to rule them all…

Close up of the 12 wires connecting the keyboard keys to the main PCB

At this point we’d want to figure out what each of those wires does or reacts to key presses, and so a handy tool would be to use a poor man’s oscilloscope to measure the voltage on each wire ( = analogRead on the Arduino). Through trial and error we can figure out what wires react to what key presses. I soldered wires right on top of the ribbon cable’s connections to reduce noise, and to have a permanent fixture later on.

Operating Principle

After trying each key while measuring the signal of each wire. We find a pattern emerging. The first 4 wires have periodic square signals across them regardless of the keys pressed(or not pressed), while the remaining 8 wires correspond to every 8th key pressed. For instance, the 1st wire of the remaining 8 would turn on for the 1st, 9th, 17th and 25th key, while the 4th wire of the remaining 8 would turn on for the 4st, 12th, 20th and 28th keys. (see where this is going?)

Every 8th key has the same color. Each of the 8 wires corresponds to a color.

As this is a 32-key keyboard, using the remaining 8 wires (remember the first 4 wires hold periodic signals - unresponsive to key presses) I can identify the note pressed but I can not tell which of the 4 sections(octaves) of the keyboard this note is on.

The 4 sections of our keyboard highlighted in different colors

Small note, before opening the keyboard I had an idea of what to expect to detect key presses, in particular how the circuit would look like and how the signals operate. In this particular case my idea was verified as the technique used here to detect key presses is called multiplexing.

Here’s a useful link to know more, but in short, we have control signals and data signals, the control signals dictate which section of your circuit is active and the data signals receive(sometimes send) data from the active section.
So a controller would activate a section of the circuit and listen for data, then activate the next section and listen for data and so on. If this cycle of activating circuits and listening to data is fast enough, the user can’t notice any chopped input.

Trying to connect the dots, it seems the first 4 wires with the periodic signals are our control signals that activate each of the 4 sections, one after the other.
While the remaining 8 are our data signals and tell us which of the 8 keys was pressed in a section.

So to uniquely identify each key, we need to know what section is active and which of the other 8 wires is active as well (We could have more than one active data signal at a time).

Hardware Setup

Now that we understand how to identify key presses, we can connect all 12 wires to a micro-controller and decide what key is pressed from these 12 signals.

Arduino UNO connected to the keyboard’s ribbon cable

In my setup, I’ve connected the wires in the same order as the ribbon cable, starting with the first control wire to pin 13 and the last data wire to pin 2. The wires were soldered onto 90° male headers.
It is very important NOT TO USE DIGITAL PINS 0 AND 1 as they are reserved for hardware serial communication. In case you want to have the Arduino be a MIDI device digital pins 0 and 1 must be unused. If you absolutely need a serial interface you could use Software Serial on the analog pins. (I did that for debugging)

Also be sure that the both, the keyboard and the Arduino share the same ground connection. I screwed a wire to the ground plate inside the keyboard and connected my Arduino’s GND pin. This will help reduce alot of noise and false signals.

This should be it for our hardware connections now. We now have a look at the code.

Code Code Code Code Code Code Code Code

The code’s goal is quite simple, detect which control and data signals are simultaneously HIGH and use that to identify the right key pressed. So you might be tempted to write something like this for each combination :

if (digitalRead(Section1) && digitalRead(Data1))
{
//key number one of section one has been pressed
Serial.println("key1 section1 pressed!")
}

This would work if we had fewer keys and slower multiplexing, however in our case (or really just mine), this code is too slow and will miss alot of key presses (and later we’ll see that we need to detect key releases for MIDI!) and this is mainly due to the overhead in the digitalRead() function, so instead we’ll rely on reading the chip registers directly.
You can read about chip registers here, but the gist is that all the inputs and outputs of any micro-controller are stored in what are called registers. Registers are most often 8bits with each bit holding a meaning. For our case that meaning is either the pin is HIGH or LOW if the bit is set to 1 or to 0 respectively.

ATMega328 chip pinout on the Arduino UNO board

For the Arduino UNO which uses the ATMega328(p) chip, we can see from the diagram that digital pin 0 till 7 are on register port D , while the digital pins 8 till 13 are on port B.

As such, these are the two ports we’ll need to read to detect any key presses (or key releases!).

So to read the signal for the first key for the first section, the code should look something like this :

//adding B before a number indicates its in binary format
if ( ((PINB & B00111100) == B00100000) && ((PINB & B00000010) == B00000010))
{
//key number one of section one has been pressed
Serial.println("key1 section1 pressed!")
}

Over here PINB is our port B register and it contains the 4 wires that dictate the active section. And then (PINB & B00111100)takes the upper 4 bits excluding the two unused ones. These 4 bits tell us the states of the 4 control wires. Now that we have the state of these 4 wires we check and see if only the last of these bits is on as that is the control wire activating section 1. Meanwhile we also check port B for the bits representing the first key of a section. If both conditions are valid (the active state is the first section and the key pressed is the first key in a section) then we can be sure this is the first key of the first section.

The same process of reading follows for the rest of the keys.

MIDI

So far so good. We can detect what key is pressed and take action on that. However we can take this project a step further and have the Arduino natively output MIDI signals that an audio program can easily interpret and play virtual instruments for you (our whatever shenanigans you decide to do). If this is not the path you desire check out this project and the function below.

MIDI is capable of holding a list of data but the four bits of information we’re concerned with are the 1) command 2) channel 3) note and 4) velocity (loudness). These are the bits of information we need to tell the computer what pitch to play (or to stop playing) and how loud it is.

I followed this Arduino resource to get up and running, and I recommend you have a look if you too. Here’s the bit of code we’ll be using:

void noteOn(int cmd, int pitch, int velocity) {
Serial.write(cmd);//Serial.write() to send byte without formatting
Serial.write(pitch);
Serial.write(velocity);
}

This function will send three bytes of data over the serial port. The cmd will contain our command to turn on a pitch and specify the channel. While each of pitch will specify the note, and velocity how loud the note is.

Note that per the MIDI convention, pitch and velocity both have to be below the value of 128 while the command has to be above 127.

Our code now looks something like this:

//adding B before a number indicates its in binary format
//adding 0x before a number indicates its in hex format
if ( ((PINB & B00111100) == B00100000) && ((PINB & B00000010) == B00000010))
{
//key number one of section one has been pressed
noteOn(0x90,53,0x45)
}

The parameter cmd sends an ON signal on channel 0 (the lower 4 bits determine the channel 0x9[ChannelNumber]). The second parameter pitch specifies the note to play - to determine this value you need some trial and error and can use this tool to map the right physical keys to the right virtual ones quite quickly. Lastly we send the velocity (loudness) of the note and you can adjust this to your liking — just be sure all notes have the same velocity. Don’t forget you can also adjust the volume of the notes within your audio program if you feel the need to.

MIDI also requires that you send message when a key is released else the note will play indefinitely; a simple else statement will help:

//adding B before a number indicates its in binary format
//adding 0x before a number indicates its in hex format
if ( ((PINB & B00111100) == B00100000) && ((PINB & B00000010) == B00000010))
{
//key number one of section one has been pressed
noteOn(0x90,53,0x45)
}
else
{
//key number one of section one has been pressed
noteOn(0x90,53,0x00) //we send a velocity of zero
}

Note that we still send an ON message on a key release but we just set the velocity to 0. We can replicate this code for all our keys and put it in our main loop.

A quick side note : MIDI communicates at 31,250 bits per second so don’t forget to set the correct baud rate in your setup code!

void setup(){
// put your setup code here, to run once:
Serial.begin(31250); //Set baud rate to meet the MIDI standard
}

So far we can detect a key press and key release and send the appropriate message, however the Arduino is still acting a typical serial device and not a MIDI device. This is sufficient if you use a tool like hairless to do the serial to MIDI conversion on your computer. If you wish to have the Arduino UNO become a MIDI device read the next section.

Improving our code

If you’re reading this post for a simple project with only a few inputs you can skip this section and start with updating the Arduino firmware to act as a MIDI device! However if your project is getting complex and you don’t want to constantly spam your computer with absent key presses (sending notes with zero velocity), and only send a message when a key is pressed or released I encourage you to read this section!

As you might’ve noticed, the code we wrote above will constantly send a message to your computer telling it all the keys that aren’t pressed and the one that are. Ideally we’d only send a message when the state of a key is changed.
You could go around this multiple ways, but the way we’re going to do it is to have an array that holds the state of each key. The size of the array has to be atleast as much as the number of keys you’re detecting.

int state [50] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //array to hold our button states

We initialize our array to all zeros meaning no key is pressed yet. Every element of our array represents a key’s state. Besides just holding the state of the key, the array will also count how long (or how many iterations) a key has been pressed. This is important for debouncing!! As such, we will only consider a key as pressed if it has been pressed for atleast a few iterations in a row (I found 2–4 iterations to be good enough, your values may vary).

When a key is pressed is detected to be pressed, we will increment its state. Only when the key’s state reaches the value 4(or any value you set) we send an ON message to play the note.
When a key is detected to not be pressed, we will check if we sent an ON message for it (state is equal to or greater than 4). If we did, we send an OFF message to stop playing that note, if we didn’t play it before we just reset its state to zero (eliminated false presses/noise).

Our code should now look as follows to detect the press or release of the first key of the first section:

if ( ((PINB & B00111100) ==  B00100000) && ((PINB & B00000010) ==  B00000010))
{
//key number one of section one has been pressed
state[0]++; //increment its state
if (state[0] == 4) //the key has been pressed for four iterations in a row
{
noteOn(0x090,note,0x45); //send a message to play this note
}
}
else if (state[0] != 0) //the key is not pressed now but was pressed previously
{
if (state[0] >= 4) //the key has been considered ON previously and now it isn't
{
noteOn(0x090,note,0x00); //send a message to stop this note
}
state[0] = 0; //reset its state to zero
}

And that’s it. We now turn this into a function to reduce code size and improve readability:

void checkState1(int state[] ,int i, byte section, byte pin, byte note)
{
if ((PINB & B00111100) == section ) {
if ((PINB & pin) == pin) //if our section is active and our pin is active
{
//button i is pressed
state[i]++;
if (state[i] == 4) //the button has been pressed four iterations in a row
{
noteOn(0x090,note,0x45);
}
}
else if (state[i] != 0) //not pressed now and was pressed previously
{
if (state[i] >= 4) //the button has been considered ON
{
noteOn(0x090,note,0x00);
}
state[i] = 0;
}
}
}

And a function call for the first key of the first section of the keyboard would be:

checkState(state,0,B00100000,B00000010, 60);

The final code has a few more details to deal with all the inputs in my case (i.e. check for port D as well). You can find the complete code on GitHub.

Arduino as a MIDI Device

So we can now detect the right key presses and construct and send the right MIDI message over the serial interface. Yet your computer still recognizes your Arduino as a serial device and not a MIDI device. To fix this we need to update the firmware of that little cute ATmega8U2 (or more modern Atmega16U2 ) chip that handles USB communication on the Arduino board itself. If you’re using a variant of the Arduino that has a different chip this might not work!

Arduino UNO

The highlighted red chip is responsible for communicating with your computer and dealing with USB communication. We’re going to update its firmware to behave as a MIDI device. The ICSP pin headers highlighted in green are connected to that chip.

The firmware we’ll update to is MocoLUFA. This firmware has two modes : USB-MIDI and Arduino-Serial (the regular mode). After installing the firmware, by default it will boot to the USB-MIDI mode that will convert our serial messages to MIDI messages on-the-fly as we send messages to our computer. You can read more about how it works here.

For more details on updating the firmware you can follow these two tutorials here and here, but I’ll cover the basic steps here as well for Linux. (Windows wouldn’t work for me :/)

First connect the Arduino through USB to your computer as you usually would. Then download the mocoLUFA firmware from GitHub, and locate the .hex file in the HEX folder. The .hex file is what we’ll flash the chip with.

We then install dfu-porgrammer , a tool to flash all sorts of chips :

sudo apt-get install dfu-programmer

then navigate to the folder containing the .hex file and run the following commands one after the other:

sudo dfu-programmer atmega16u2 erase
sudo dfu-programmer atmega16u2 flash dualMoco.hex
sudo dfu-programmer atmega16u2 reset

The commands above are for the atmega16u2 chip, replace with atmega8u2 if that’s the chip on your board (I’ve only tested atmega16u2 but the firmware should support both).

That’s it. Your Arduino should now be detected as a MIDI device and you can test this with hairless if it considers the Arduino as MIDI-IN (not Serial!) or if whatever audio program you’re using now detects a new MIDI device (I’m using StudioOne and and can now receive input from the Arduino).

We can switch between back to the Arduino-Serial mode by connecting PIN4 (MOSI PB2) and PIN6 (GND) on ICSP connector for 16U2, after a reset. You can also change the firmware back to the original using the same process but using this firmware .hex file instead.

That’s a wrap

At this stage all our electronics should be working fine. We can detect key presses and relay messages as MIDI message to our computer over USB. All that is left is fixating the Arduino in the keyboard and exposing the USB port. I tried different spaces in the keyboard and found the bottom middle to give the most room and least strain on the connecting wires.

As a bonus feature, it turns out the Vin pin could also power the keyboard itself and so I soldered Vin to the keyboard’s positive rail (red wire) and joined the GND of each device together (via the silver ground plate).

The keyboard now has an additional USB port for power and MIDI:

The keyboard with its newly added USB port.

Because we didn’t break any of the circuity and instead just monitor the existing signals, this keyboard can still function as it used to if we decide to use its power jack rather than USB.

--

--

Marwan Ghamloush

Extremely curious Maker of Things. Geek. Love Pizza & open-source.