> Note: this diagram and explanation are both simplified, so [click here](http://www.openmusiclabs.com/learning/digital/input-matrix-scanning/) for a more detailled explanation.
> In practice, diodes are used to ensure power doesn't flow the wrong way.
So that's how a digital piano works, theoretically.
What does that look like, under the hood?
As it turns out, the matrix is accessible through ribbon cables (or [_flat flexible cable_](https://en.m.wikipedia.org/wiki/Flexible_flat_cable), or FFC).
This is in contrast to actually generating the sound in my circuit and also playing it through a speaker,
like the original board did.
I personally think that this architecture is the fastest way to get to a working product.
After all, convincingly synthesizing a piano sound is difficult,
so reinventing this wheel would be unwise.
## hardware
Now, physically, what does that `[geode-piano]` box in the architecture diagram above look like?
The answer is that it looks like a mess.
![My circuit, on a breadboard with many jumper wires](/public/img/piano/doodad2.jpg)
### microcontroller
First of all, the heart of geode-piano is the Raspberry Pi Pico microcontroller,
which is the green chip in the image above.
I had a few laying around, so it was the obvious choice for me to use.
This part actually runs the firmware, does all the processing, and also connects back to a computer via a micro-USB port.
### sockets
Then, there are the sockets above.
Those are actually FFC sockets, which the ribbon cables can be plugged into.
This is definitely one of the cursed parts of this project,
because these sockets are designed to be soldered, and not to be used with jumper cables.
In fact, I had to slice off the tips of a bunch of female-to-male jumper cables to get them to connect to the pins.
I am still quite surprised that the pins snap perfectly in the female ends.
This arrangement of many jumper cables in parallel going up to the sockets was also a bad idea,
as it caused crosstalk.
In tests, it showed up as ghost signals being detected with no visible source.
Twisting some wires together and attempting to space them out fixed this issue.
As an aside, I originally bought the wrong size of socket due to carelessness.
I put up a ruler to the contacts and eyeballed the pin pitch (distance between each contacts' centers),
and decided it was 1.0mm.
This was a big mistake on my part, as I found out later that it was 1.25mm.
After this, I discovered that the socket specsheets had measurements of the distance between the first and last contacts,
which is easier and less error-prone to measure with a typical ruler.
Actually reading these documents should help me avoid these kinds of mistakes.
### pin extenders
The astute among you might have noticed that a Pico microcontroller does not have enough input pins for this project.
To remedy this issue, I used two [MCP23017](https://www.microchip.com/en-us/product/mcp23017) chips, which are pin extenders.
Each has 16 GPIO pins, and they communicate over [I²C](https://en.m.wikipedia.org/wiki/I%C2%B2C) to the Pico,
which requires only 2 pins on that end.
For these 14 extra pins we get, we sacrifice a bit of convenience and efficiency.
One of the features of these chips is their capacity for both input and output.
This is important because I don't actually know which contact on the ribbon cable corresponds to which row and column.
Instead of reverse-engineering the circuitry with a multimeter,
I made a [scanner](https://github.com/dogeystamp/geode-piano/blob/main/src/bin/pin_scanner.rs) that will try every row/column combination possible for each key until it finds a valid one.
With this information, we can reconstruct the key matrix pinout.
> A few important tips I would tell past me about this chip:
>
> - You need [pull-up resistors](https://www.joshmcguigan.com/blog/internal-pull-up-resistor-i2c/)
> for I²C. I won't go into detail about it because the linked blog post sums up my experience with this.
> - Multiple I²C peripherals can live on the same bus.
> - Plug the `RESET` pin into the positive power rail. I was stuck for an entire afternoon because no documentation said this clearly.
> In the datasheet, "must be externally biased" means "do not leave this pin floating under any circumstances".
> Also, the overbar on the pin name in the datasheet means that pulling the pin low will cause a reset.
> - MCP23017 chips are known to have weird behaviour on pins GPA7 and GPB7. (Look at the most recent [datasheet](https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP23017-Data-Sheet-DS20001952.pdf),
> not the old one!)
## firmware
If you've used microcontrollers before,
you probably know that they're programmed using C++, C, or MicroPython,
or some similar language.
The Raspberry Pi Pico is no different,
as the most common ways to write firmware for it are the [Pico C SDK](https://www.raspberrypi.com/documentation/microcontrollers/c_sdk.html),
and MicroPython.
I had tried C before, but the tooling was painful to deal with.
My language server [clangd](https://clangd.llvm.org/) would display unfixable errors
about missing imports and unknown functions.
This was fine, but it was really annoying.
MicroPython does seem quite user-friendly,
but for scanning the key matrix, it could be problematic due to performance concerns.
In the end, I settled on using Rust.
This option seems relatively obscure and less well documented,
however it ended up working well for me.
The main advantage of Rust for me is that it is a modern, yet quite performant language.
Even in a `no_std` embedded environment, you have a full package manager to easily install libraries.
The [MCP23017 library](https://docs.rs/mcp23017/latest/mcp23017/), for example,
let me develop that part of the code faster.
Also, [rust-analyzer](https://rust-analyzer.github.io/) works perfectly well, and
gives the most detailled and helpful messages out of all language servers I've used before.
Specifically for this project, I used the [embassy-rs](https://embassy.dev/) framework.
This library makes embedded development in Rust really easy.
It offers drivers for a bunch of useful features,
like [USB MIDI](https://docs.embassy.dev/embassy-usb/git/default/index.html),
The code for handling this is not quite different from handling regular keys.
However, connecting the pedal to the microcontroller is more difficult.
Typically, the pedals are connected to the piano via a [TRS jack](https://en.m.wikipedia.org/wiki/Phone_connector_(audio)) (not dissimilar to a headphone jack).
However, I had no socket component for this type of plug.
Therefore, I made the most cursed part of the circuit: