Firmware Design
Arduino Program Dependencies
| Name | Version | Purpose | Link | Configuration |
| platformIO | >= 6.1.12 | compiler toolchain | https://platformio.org | platform = atmelavr board = uno framework = Arduino |
The Arduino program can be built from the following project folder: https://github.com/daniel-sudz/pie/blob/main/arduino/src/main.cpp. It is compiled using the PlatformIO integrated toolchain (https://platformio.org/).
The Arduino is responsible for polling the keypad, potentiometers, reset button, and sustain pedal, and reporting their status to the software over serial.
As explained in Electrical Design, the keypad is multiplexed to save input pins on the Arduino. This means reading it is slightly more complicated than a simple button. To read the keypad, we turn on one output pin at a time, and for each pin, we see which inputs are high. When an input is high, this means the key at that output and input pair is pressed. We record this information for all keys, and then output the result over serial.
Keyboard State
The current states of all the keyboard keys are tracked in a bit field. This makes it very easy to output the information to serial, as the bit field can simply be printed.
/* Marks a note as being pressed using a bitfield approach */
void set_note_field(uint32_t& note, uint32_t mux_input, uint32_t mux_output) {
uint32_t field = (uint32_t(1) << uint32_t(((mux_input * mux_num_output) + mux_output)));
note |= field;
}By storing the state of the keyboard in a single uint32_t, we greatly reduce the overhead of wasted Arduino CPU cycles spent on serial printing. Furthermore, we also increase the performance of the Visualizer program (documented in the software design subpage) since it only takes one machine instruction to check if a note is being pressed and we don’t need any complex data structure to store the state of the keyboard.
for (uint32_t note = 0; note < 30; note++) {
if(uint32_t(1) << note) {
// note is being pressed proceed
}
}Example of how to read the bitfield using simple bit-shift operations.
Erase Button
The erase button is much simpler. It is polled on every loop, and if it is found to be pressed when it was previously not pressed, an erase message is sent over serial. This means only one erase message is sent, regardless of how long the button is held.
Sustain Pedal
The sustain pedal has a slight complication. Not all sustain pedals from all synthesizer manufacturers are the same. Some are normally closed (nc) and some normally open (no). Nc means the switch is closed (circuit completed) unless it’s being pressed. In order to deal with this, the firmware checks the state of the switch on startup.
While the sustain pedal is pressed, the keypad reader will not register key releases. This means, if a key is pressed when the sustain pedal is pressed, it will not be released until the pedal is released.
Debouncing
Finally, all of this is complicated somewhat by the need to de-bounce all switches. Most switches do not cleanly make or break contact only one time, and an Arduino can be fast enough to register the micro-bounces that the contacts make as they close or open. To prevent this being registered as multiple key-presses, the firmware keeps track of the time when each key or button was last pressed and last released, and uses these values to ignore any events that happen in too-rapid succession. See this tutorial to learn more about debouncing on Arduino.
Potentiometers
The potentiometers (knobs) are very simple. Each potentiometer’s wiper pin is analog read, and this value is sent over serial along with a text flag indicating which pot value is being sent.