Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
11 changes: 11 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"configurations": [
{
"name": "Test Mode",
"defines": [
"TEST_MODE"
]
}
],
"version": 4
}
65 changes: 51 additions & 14 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,39 +1,76 @@
# Moura's Keyboard Scanner
Turn your broken (or unused) keyboard in a MIDI controller (with pedal and veloticy)

Turn your broken (or unused) keyboard in a MIDI controller

This Arduino sketch was the one that I used to make the project demonstrated
in [this](https://www.youtube.com/watch?v=z840N9P-T2k) video.
It is about a keyboard controller that I've made using an old Alesis QS6 Keyboard
directly connected to an Arduino Mega rev3 acting as keyboard scanner with
directly connected to an Arduino Mega rev3 acting as keyboard scanner with
velocity reading and sustain pedal support.

In 2017 I did the same with another keyboard (an old Casio from a friend).
The code was refactored and a great library called [DIO2](https://github.com/FryDay/DIO2)
was used to speed up the scanning and clean up the old code.

In 2020, thanks to Leandro Meucchi, from Argentina, the code is simpler to be used with any keyboard.
He made the PDF showing the keyboard wiring for Yamaha PSR530 keyboard, that helps a lot do understand what needs to be done.
In 2020, thanks to Leandro Meucchi, from Argentina, the code improved to be used with any keyboard.
He made the PDF showing the keyboard wiring for Yamaha PSR530 keyboard, that helps a lot to understand what needs to be done.

In 2025 Emerson Seiler did a great job adding Kurzweil SP76II configuration + schematics, velocity sensitivity curves and potentiomers support, as he shows [here](https://www.youtube.com/watch?v=GndR5BkHnv0). He also suggested to explode the code into small pieces.

I started 2026 working on top Emerson Seiler's job as an opportunity to do a refactoring, creating the concept of "models" and merging his code into the main line.

Warning: this sketch is for keyboard with velocity support only.
## Features

![keyboardscanner](https://raw.githubusercontent.com/oxesoft/keyboardscanner/master/keyboardscanner.jpg)
- Sustain pedal
- Velocity sensitivity with curves support
- Potentiometers

![keyboardscanner](assets/keyboardscanner.jpg)

## How velocity works

Normally it is a ribbon rubber with two contacts for each key that touch the board in two diffent moments:
since the key was pressed until it slopes the board completly. The code measure the difference, varying between
2 and 120 ms, depending on the keyboard. It is transformed in a MIDI value from 0 to 127.

## Diagram of one key

This scheme makes clear how to identify input and output pins. This has been the main question of guys on Youtube.
I hope it helps:

![key](https://raw.githubusercontent.com/oxesoft/keyboardscanner/master/key_scheme.png)
![key](assets/key_scheme.png)

## How to make your own MIDI controller
1) Disassemble the keyboard to have access to the flat cables (one, two or even three, depending on the number of keys and manufacturer);
2) Using a multimeter with the diode testing function selected, find out and understand the matrix, starting from the first key. Some keyboards have a logical pattern, some doesn't;
3) Connect the ribbon pins **directly** to the Arduino Mega (because it has enough pins to connect any keyboard with velocity). You **dont't** need to change anything in the keyboard circuit board;
4) Change the pins in the code (output_pins + input_pins), uncomment DEBUG_MIDI_MESSAGE and see the console output;
5) If the MIDI messages looks OK, comment DEBUG_MIDI_MESSAGE back and use some Serial<->MIDI Bridge like the excelent [Hairless](https://projectgus.github.io/hairless-midiserial/);
6) If everything goes well, consider turn you Arduino in a MIDI interface using [HIDUINO](https://github.com/ddiakopoulos/hiduino) or similar firmware.
7) Enjoy!

1. Disassemble the keyboard to have access to the flat cables (one, two or even three, depending on the number of keys and manufacturer);
2. Using a multimeter with the diode testing function selected, find out and understand the matrix, starting from the first key. Some keyboards have a logical pattern, some doesn't;
3. Connect the ribbon pins **directly** to the Arduino Mega (because it has enough pins to connect any keyboard with velocity). You **dont't** need to change anything in the keyboard circuit board;
4. Duplicate one of the existing models and change the pins in the model.h (output_pins + input_pins), uncomment DEBUG_MIDI_MESSAGE in globals.h and see the console output;
5. If the MIDI messages looks OK, comment DEBUG_MIDI_MESSAGE back and use some Serial<->MIDI Bridge like the excelent [Hairless](https://projectgus.github.io/hairless-midiserial/);
6. If everything goes well, consider turn you Arduino in a MIDI interface using [HIDUINO](https://github.com/ddiakopoulos/hiduino) or similar firmware.
7. Enjoy!

## Converting an Arduino Mega into MIDI device

To convert the Arduino Mega into a MIDI device, I used the ability to modify the bootloader of the 16u2 chip.
NOTE: only Arduino Megas/Unos with this chip will work with this procedure.

- Using Homebrew I installed dfu-programmer

`brew install dfu-programmer`

- Then download the dualMoco.hex file, which is a custom firmware that makes the 16u2 chip recognized as midi. It can be found in this project and also in [developers' github.](https://github.com/kuwatay/mocolufa/tree/master/HEX)
- It is important that serial communications are set to 31250 in the code to work.
- Connect the arduino mega and quickly short the first 2 pins with a jumper, then remove the jumper.
![jumper mega](assets/mega.jpg)
- Now in the terminal run the bootloader wipe
`dfu-programmer atmega16u2 erase`
- Finally I installed dualMoco.hex
`dfu-programmer atmega16u2 flash /Users/emerson/Downloads/dualMoco.hex`
- If you want to return to the original boot, repeat the previous steps but flash the original Mega/Uno bootloader.
`dfu-programmer atmega16u2 flash /Users/emerson/Downloads/Arduino-usbserial-mega.hex`
- Perform a reset and remove and insert the ubs.
`dfu-programmer atmega16u2 reset`
- Now the device will be recognized as a MIDI device.
- The files are available in the /bootloader folder.
- Make sure that SERIAL_SPEED in config.h is set to 31250.
File renamed without changes
File renamed without changes
Binary file added assets/mega.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 54 additions & 0 deletions config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "models/kurzweil_sp76ii/model.h" // change here your keyboard model

// Uncomment the next line to inspect the number of scans per seconds
// #define DEBUG_SCANS_PER_SECOND
/*
For reference, I've got these results at the time that I tested it:
426 cyles per second (2,35ms per cycle) using standard digitalWrite/digitalRead
896 cyles per second (1,11ms per cycle) using DIO2 digitalWrite2/digitalRead2
*/

// Uncoment the next line to get text midi message at output
// #define DEBUG_MIDI_MESSAGE

// Enables debug mode to check MIN_TIME_MS and MAX_TIME_MS values
// #define DEBUG_VELOCITY_TIMES

// Uncomment for using one of the available velocity curves
// #define VELOCITY_CURVE VELOCITY_CURVE_LINEAR
// #define VELOCITY_CURVE VELOCITY_CURVE_CONVEX
// #define VELOCITY_CURVE VELOCITY_CURVE_SATURATED
// #define VELOCITY_CURVE VELOCITY_CURVE_CONCAVE

// Cheap keyboards often has the black keys softer or harder than the white ones
// Uncomment the two next lines to allow a soft correction
// #define BLACK_KEYS_CORRECTION
// #define BLACK_KEYS_MULTIPLIER 192 // 127 is the central value (corresponding to 1.0)
// Copy the following code snippet to your model.h and configure it
// #ifdef DEFINE_BLACK_KEYS_MAP
// byte black_keys[KEYS_NUMBER] = { // each index corresponds to the white (zero) and black (one) keys
// 0,1,0,1,0,0,1,0,1,0,1,0,
// 0,1,0,1,0,0,1,0,1,0,1,0,
// 0,1,0,1,0,0,1,0,1,0,1,0,
// 0,1,0,1,0,0,1,0,1,0,1,0,
// 0,1,0,1,0,0,1,0,1,0,1,0,
// 0
// };
// #endif

// Uncoment the following lines to use potentiometers (pitch bend, modulation wheel, knobs and sliders)
// #define ENABLE_POTENTIOMETER_SUPPORT
// #define POTS_RESOLUTION_MILISECONDS 5
// #define POTS_THREASHOLD_VALUE 8 // 1024 divided by 128
// #define POTS_PB_CENTER_DEADZONE 4
// #define POTS_NUMBER 2
// const int POTS_ANALOG_PINS[POTS_NUMBER] = {
// A0,
// A1
// };
// const int POTS_TYPES[POTS_NUMBER] = {
// POT_TYPE_PITCHBEND,
// POT_TYPE_MODWHEEL
// };

#define SERIAL_SPEED 31250 // 115200 for hairless; 31250 for MOCO lufa
38 changes: 38 additions & 0 deletions debug.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Moura's Keyboard Scanner: turn you broken (or unused) keyboard in a MIDI controller
Copyright (C) 2017 Daniel Moura <oxesoft@gmail.com>

This code is originally hosted at https://github.com/oxesoft/keyboardscanner

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "globals.h"

#ifdef DEBUG_SCANS_PER_SECOND
void countCycles()
{
static unsigned long cycles = 0;
static unsigned long start = 0;
static unsigned long current = 0;
cycles++;
current = millis();
if (current - start >= 1000)
{
Serial.println(cycles);
cycles = 0;
start = current;
}
}
#endif
53 changes: 53 additions & 0 deletions globals.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

#ifndef GLOBALS_H
#define GLOBALS_H

#if TEST_MODE
#include "tests/mocks.h"
#else
#include <Arduino.h>
#include <DIO2.h> // install the library DIO2
#endif

#define VELOCITY_CURVE_LINEAR 0
#define VELOCITY_CURVE_CONVEX 1
#define VELOCITY_CURVE_SATURATED 2
#define VELOCITY_CURVE_CONCAVE 3

#define POT_TYPE_PITCHBEND 0xE000
#define POT_TYPE_MODWHEEL 0xB001
#define POT_TYPE_VOLUME 0xB007
#define POT_TYPE_PAN 0xB00A
#define POT_TYPE_EXPRESSION 0xB00B
#define POT_TYPE_RESONANCE 0xB047
#define POT_TYPE_FILTER 0xB04A
#define POT_TYPE_REVERB 0xB05B
#define POT_TYPE_CHORUS 0xB05D

#include "config.h"

#define KEY_OFF 0
#define KEY_START 1
#define KEY_ON 2
#define KEY_RELEASED 3
#define KEY_SUSTAINED 4
#define KEY_SUSTAINED_RESTART 5

void initStates();
void initIOPins();
#ifdef DEBUG_SCANS_PER_SECOND
void countCycles();
#endif
void scanMatrix();
void updateStates();
void sendKeyEvent(byte status_byte, byte key_index, unsigned long time);
void sendMidiEvent(byte status_byte, byte data1, byte data2);
#ifdef ENABLE_POTENTIOMETER_SUPPORT
void initPotentiometers();
void readPotentiometers();
#endif

extern boolean matrix_signals[KEYS_NUMBER * 2];
extern byte sustain_pedal_signal;

#endif
Loading