Velocity-sensitive, contactless MIDI keyboard with polyphonic aftertouch for Teensy synth

Yes, the purpose of those transistors is to allow to implement power saving. Since there are lots of Hall sensors their total power consumption might be >300mA. The idea for those was to enter power-saving mode when keyboard is not used for extended periods of time. There is also 0.5V voltage drop on those transistors that is actually good so I limit output voltage of Hall sensors.
 
Last edited:
Yes I thought I needed extra protection, but then I checked 4067 analog multiplexer data sheet. https://www.ti.com/lit/ds/symlink/cd4067b.pdf
Every input is protected internally (see below) and since I am using Vdd=3.3V supply for the analog multiplexer, I seem to have that covered, especially considering the fact that I am supplying SS49E with something like 4.5V only (note bipolar transistor keys in the schematics) and at full swing SS49E outputs not more than 3.8V, so I am within 4067 specs.

View attachment 35192
That only protects the digital signal inputs, not the analogue signals. Note the asterisk that is on each of the digital control pins.
 
As I wrote: spec say max voltage on any pin Vdd + 0.5V. I am within specs even under most extreme situation (wrong assembly, reverse orientation of magnets, keys with wrong orientation pressed to max).
Under normal operation I am always below 2.5V.

I guess that if I ever made this "commercially" I would put those extra diodes just for the "peace of mind", but with my prototype build, I am not worried.
 
Last edited:
As I wrote: spec say max voltage on any pin Vdd + 0.5V. I am within specs even under most extreme situation (wrong assembly, reverse orientation of magnets, keys with wrong orientation pressed to max).
Under normal operation I am always below 2.5V.

I guess that if I ever made this "commercially" I would put those extra diodes just for the "peace of mind", but with my prototype build, I am not worried.
You want to protect the Teensy from over voltage. It didn't want more than 3.3V on its ADC pins. The AH49E can send +5V and the CD4067 can pass it through without the clamping that you had previously assumed so you can get 5V at the Teensy. You can test this by disconnecting from the Teensy then applying a reverse magnetic field to a sensor then measuring the voltage at the output of the CD4067.

Of course you may remain lucky that the sensors don't come near to a reverse polarity magnet but someone may put a loudspeaker near the keyboard at sometime and kill the microprocessor.

I considered some ideas to mitigate this risk, including:

- Driving the AH49E from 3.3V. This reduces the range and hence resolution. I am testing this now.
- Controlling the CD4067 enable pin from it's output. This may cause it to oscillate.
- Clamping the output of the CD4067. Probably the _right_ thing to do.
- Adding 5V to 3.3V converter at output of CD4067. Probably not linear.

Anyway, I have some magnets and sensors so will play with them to see what works. Thanks for the ideas and inspiration.
 
Apparently you did not notice what I wrote before.
Teensy never sees voltage above 2.5V in real life at the analog input.
I would need to get the assembly completely wrong to get anything above 2.5V.
Typically you don't protect circuits against wrong assembly.

And you are overestimating those "loudspeaker fields" as they fall off with at least (distance)^3 factor.
To get anything above 3V I would need to put Hall sensor INSIDE loudspeaker.
That would never happen.

The schematic I have sent in the first post did not show the microcontroller part. In fact I do have Schottky diodes
between analog input of microcontroller and +3.3V, but even then, Schottky diodes have still something like 0.3-0.4V dropoff voltage
and would only clamp voltages above 3.3V+0.4V = 3.7V.
In the circuit I have shown such voltages simply don't exist, since SS49E output swing is Vdd-1V and Vdd for Hall sensors in my circuit is NOT 5V. It is 4.5V due to collector-emiter drop on Q1/Q2 transistors used in power circuit. So in practice, even in worst case the output at SS49E would be 3.5V. Which is below what those protection diodes would even react.

Anyway my project works fine already, I have fully working keyboard, playability is excellent, much much better than mechanical solution.
 
Last edited:
Apparently you did not notice what I wrote before.
Teensy never sees voltage above 2.5V in real life at the analog input.
I would need to get the assembly completely wrong to get anything above 2.5V.
Typically you don't protect circuits against wrong assembly.

And you are overestimating those "loudspeaker fields" as they fall off with at least (distance)^3 factor.
To get anything above 3V I would need to put Hall sensor INSIDE loudspeaker.
That would never happen.

The schematic I have sent in the first post did not show the microcontroller part. In fact I do have Schottky diodes
between analog input of microcontroller and +3.3V, but even then, Schottky diodes have still something like 0.3-0.4V dropoff voltage
and would only clamp voltages above 3.3V+0.4V = 3.7V.
In the circuit I have shown such voltages simply don't exist, since SS49E output swing is Vdd-1V and Vdd for Hall sensors in my circuit is NOT 5V. It is 4.5V due to collector-emiter drop on Q1/Q2 transistors used in power circuit. So in practice, even in worst case the output at SS49E would be 3.5V. Which is below what those protection diodes would even react.

Anyway my project works fine already, I have fully working keyboard, playability is excellent, much much better than mechanical solution.
Very happy to read about the outcome and steps here. Curious if you have any sort of full write-up that would let people follow in your footsteps? Also curious of any video or audio of its use. Cheers!
 
I am sorry, I wish I had more time to publish more stuff and work more on hobby side but life is busy and you gotta do the things you don't like first and fun things need to wait :-(
Anyway, I am now trying to build some real enclosure and add some extra controls (lots of pots and ribbon controller :)) to get that CS-80 vibe (ribbon + aftertouch). I am not really good with mechanical stuff so the progress is sloooow.

I have really crazy idea to use this keyboard with a "monster" synth project that I am working on that would have eight Teensy 4 as synth engines, plus ESP32-driven 1024x600 IPS display for user interface and couple of STM32F103/RP Pico for pot/encoder handling. I am working on multiple things at once, so unfortunately everything is work-in-progress and nothing is complete (yet).

Thank you for showing interest! I truly do hope to work more on this.
 
Last edited:
Hello there I actually had imagined a keyboard like this in my dream with a million dollar idea with Hall effect sensors to zero the latency of any midi device in any given moment. Then while searching if it already existed I have stumbled upon this forum. If you manage to create this keyboard . You could create a software to trigger midi messages as soon as the magnetic field change before you actually reaching the end of the keys where you hit the electronic 0-1 trigger which would create negative latency to compensate for whatever latency you might have on your system. So even on a bluetooth midi keyboard you would actually have an actual zero latency. Then I have realized yes it is possible to create this but only problem then would be what happens to the unstable latency which is created by the analog sensors in different velocities. Because if you hit the keys slower the travel time will be longer and you would hear the sound before you even press the keys the whole way. 🤣 If someone can crack this problem they would be as rich as Bill Gates. But this system can be used for a non velocity keyboard the create zero latency effect.
 
I will post some videos soon of working keyboard. So yes I have done it already and it works perfectly fine.
Currently position of each key is measured 1000 times per second and temporal velocity can be measured as difference between positions in this small delta time.

As for MIDI triggering you are free to choose any position as the trigger, you don't need to touch keyboard bed but practically the only sensible solution is to have different areas for key-on and key-off events (hysteresis) as it prevents false repeated triggering. These areas can be anywhere. I find it comfortable for those areas to be in 1/3..2/3 zone of whole key travel.
 
OK, after many days of work, I have finally achieved the point where I have my keyboard with polyphonic aftertouch connected with fancy LED lights, ribbon controller, Teensy based synth and 800x480 LCD display all working together. And I am having lots of fun playing it :)

Synth1.jpg


Synth2.jpg


I promised you a video, so here it comes very short test of Polyphonic Aftertouch


As you can see in the video each key has totally independent polyphonic aftertouch.

It was very tricky to get the amount of precision and control I have now because the distance that key travels in the "aftertouch zone" was very small (like 1-2 mm) and the readings from Hall sensor where quite noisy and inprecise, but thanks to DSP I managed to solve all issues :)
The readings from Hall sensor using 12 bit ADC in the "touch zone" represented only 64 discrete levels and they were super noisy. At one point I thought that I would not be able to use it at all, but then I realized that noise can be useful too, because when I oversample readings 16x, with filtering and decimation I can get 4 bits extra resolution and noise doesn't hurt because it provides natural "dithering". As it turned out oversampling and filtering provided quite stable readings. Now I only had to address nonlinearity (as magnetic field is not linear vs distance / pressure). But with a little bit of math (polynomial approximation) I managed to linearize the output.

I am very satisfied with the result, the polyphonic aftertouch offers absolutely great smooth control and playability. Now I understand why Vangelis loved Yamaha CS-80. It is so cool and fun to play with such expressiveness.
 
Here it comes more demonstration of my Polyphonic Aftertouch sensitive keyboard and Teensy-based synth. This time recreating opening sequence of Chariots of Fire by Vangelis. Except for the piano, all sounds are coming from my DIY Teensy-based synth (multitracked). Appologies for my sloppy playing :) Most funny thing is that sounds are so close to original that Universal Music immediately claimed copyright on that :) , even though this music was recreated from scratch not using original instruments or sounds/samples. Pure mathematics + Teensy computational power.
Thanks to PJRC for creating such a powerful board.

 
Last edited:
I am still looking for perfect material for the keyboard's key bed. The felt that I have now is a bit too hard (tough) I bought another piece and it is hard too. I ordered some "EVA foam" seal tape and it is softer (good) but is not "springy" ("dead") so it does not return to the original shape quickly. I need something in between.
I am curious if anyone of you can offer any suggestions regarding key bed material?
 
Last edited:
At a hardware store, in garden department, look for hose washers. They're available in different rubber hardness as well as a foam sponge. You'd need to experiment.

Piano key guide-pin felt? It's used to control key-down stop height. Looks like a little donut.

How about a blob of silicon caulking?
Or, lay down a bead for consistent diameter.

And then there's all sorts of weather stripping: foam, rubber.

I used a piece of rubber stripping for my truck-bed box cap. Look in auto-motive department.

Bicycle inner tube rubber? Car inner tube might be thicker. Rubber floor mat?

I think the contact area under the key (usually pretty thin) will play a major roll in the material you use.
 
Thanks for all the ideas. I will certainly try some of them. I am also thinking about going "two layer" (bottom layer using hard felt and upper layer with something on the softer side (like EVA, but maybe more springy).
 
I thought about the 2-layer approach too. You could try the soft/hard layers top/bottom as well as bottom/top. Should get different dynamic feel for both configurations.

I also just noticed my rectangular-soft-white-rubbery pencil eraser as a possibility. Difficult to know if it would break down over time from repeated strikes. Maybe a layer of felt over top of it to protect it?

Sponge mouse pad with cloth top?
 
Last edited:
Since my goal is to be able to play live as much music as I can, I implemented advanced arpeggiator and split keyboard feature and multi-timbrality.
I also worked on some recreation of famous Solina String Ensemble sound.

A little demo is here.

 
As for after touch, closed cell double sided window glazing tape might be useful. Amazon has it at https://www.amazon.com/dp/B08131X821 . There are various widths and thicknesses available. You could double the thickness by overlaying two tapes. You would keep the top layer covered with the plastic protection film. It is closed cell cross-linked polyethylene foam.
 
@bach911 - thank you for your interest. What programming would you be interested in (I mean audio part, keyboard, part, analog part) ?

Currently the project has become quite a monster. I am using eight Teensy 4 for main audio processing, ESP32S3 for nice 800x480 touch GUI interface and STM32F103 for analog pot readouts and interfacing and even small ATmega for foot controller. It is multi-microcontroller monster that is constantly evolving and I am not yet sure what will be the destination.
The more I use it, the more I like "performance" aspect of it, so it is now 8-may multi-timbral synth capable of 64+ voice polyphony and many features targeted towards allowing real-time musical performance (layering, multi-way keyboard split, seamless "scene" switching and more). I am having lots of fun and time just playing it, and lots of ideas that are far from being complete. Even though I am doing C++ for living for 30+ years now, this project is a hobby at the moment and I am doing it only in my spare time, so the code is pretty much in messy shape and I wouldn't feel confortable showing this mess :) . I may change my mind once I finally arrive to the point when I consider it more or less "complete" but now it is just mess-in-progress.
 
Last edited:
I am interested how to program the Teensy to read and convert your multiplexer board signals into key information.

I am building a three 61 key manual 32 note pedal board organ. That will require 12 multiplexer boards not counting the pedal. I already have the three 61 note manual wood keyboards.

Thank you very much for the detailed design of the multiplexer boards. That alone makes my project much easier and elegant.

The sound comes from program called Hauptwerk. Hauptwerk reads MIDI input and plays back sounds from actual pipe organs the user selects (buys). Every note of every pipe in a selected organ is recorded sometime with church acoustics sometime without so the user can provide their own reverberation.

I definitely would like key stroke on and velocity for piano. After touch would be nice but not required. I am currently relearning C for Arduino with two Teensy 4.1s. I could try to slog through mulitplexing, coding key on, velocity and after touch myself. However, it would be very helpful if you would show only a part of your code that shows multiplexing and one key (note).
 
Things have changed since I wrote original post because in addition to the keyboard (48 analog Hall sensors) I have expended the multiplexer infrastructure to read 48 analog pots. So I have 96 analog inputs right now. Pots are more troublesome than Hall sensors because Pots have much higher impedance (10kohm). Hall sensor output impedance is low because it has emitter follower output stage and something like <500 ohm so it is easier to drive capactive load from Hall sensor than pot.
Why I am mentioning this? Because higher impedance means more settling time required after switching analog multiplexer and that makes reading pots is more problematic and bumps up timing requirements (you need to specifically add extra "wait" time after analog multiplexer channel switch to make sure that voltage stabilised at new level).
Data sheet of 4067 16-to-1 analog switch says that it's "ON" resistance is 70ohm, so it is way less than pots 5k (10k/2) impedance, input capacitance is 10pF, OFF-ON propagation delay is about 60 ns. Now assuming that wire connecting pot to multiplexer is less than 1meter (worst case for me) it would add another 21pF (https://www.emisoftware.com/calculator/wire-pair-capacitance/)

So overall I have 5kohm source charging 30pF capacitance. Time constant for this RC circuit would be R * C (5 * 10^3 * 30 * 10^-12 ) = 150 * 10^-12 = 150 ns. Settling time to 99.8% is 6 time constants = 900ns. I don't need more than that for pots because I would encode pots to 7 bit MIDI anyway.

The above calculations say that to get correct measurement I need to wait at least 1 μs after changing analog switch address lines for output voltage to stabilize within 99.8% of actual value.

With 16-input multiplexer it means that 16 μs per one read cycle (all 16 inputs per mux) is just spent **waiting** for mux output to stabilize. This also means that proper timing is crucial.

My goal was to read the analog values 1000 times per second. With 96 analog inputs it means that I need to read at about 100kHZ sampling rate, which means 10μs total sampling time, including time required to wait for analog mux, so effectively it is below 9 μs.
With twice as many analog inputs as you are planning, it would mean half of that (4.5μs), unless you dropped scanning frequency below 1000 times per second.

General workflow would be (in Arduino speak):

C++:
void setup()
{
    // D0..D3 to the mux address pins
    pinMode( PIN_D0, OUTPUT ); // configure pings
    pinMode( PIN_D1, OUTPUT );
    pinMode( PIN_D2, OUTPUT );
    pinMode( PIN_D3, OUTPUT );
 
 
    // we want 12 bit resolution
    analogReadResolution(12);
   // and NO averaging because it is VERY slow with averaging
   // this setting allows to go down to 6usec per analogRead()
    analogReadAveraging(1);
 
}

void setMux( int addr )
{
    digitalWriteFast( PIN_D0, ( addr & 1 ) ? HIGH : LOW );
    digitalWriteFast( PIN_D1, ( addr & 2 ) ? HIGH : LOW );
    digitalWriteFast( PIN_D2, ( addr & 4 ) ? HIGH : LOW );
    digitalWriteFast( PIN_D3, ( addr & 8 ) ? HIGH : LOW );

}

#define HOW_MANY_MUXES 6

#define MUX_CHANNELS 16

int anReadData[ HOW_MANY_MUXES * MUX_CHANNELS ];

// the code below assumes that you have muxes connected to
// consecutive analog inputs PIN_A0, A1, A2 and so on.

void loop()
{
    // this reads ALL data from ALL muxes
    for( int i = 0; i < MUX_CHANNELS; i++ )
    {
        setMux( i );
        delayMicroseconds( 1 ); // settling time required for mux output to stabilize
 
        for( int j = 0; j < HOW_MANY_MUXES; j++ )
        {
            anReadData[ i + j * MUX_CHANNELS ] = analogRead( PIN_A0 + j );
        }
    }
}



Problem is that Teensy's 4.0 analogRead is too slow to handle that as by default (out of the box, without tweaking) it takes 20μs.

There are tweaks to make it faster (down to approx 6μs ) by switching OFF averaging and thus decreasing signal-to-noise ratio

You could also use some third party libs like this: https://github.com/pedvide/ADC
that promise to get extra speed but at the expense of lowering resolution. As it turned out Teensy 4 ADC subsystem is not as good as all the other parts of this microcontroller.

After doing some more research, I came to conclusion that I would need to sacrifice entire Teensy 4 to do nothing but data acquisition and whenever I could achieve desired speed would be questionable, so I decided to move data acquisition to separate microcontroller, so Teensy is free to do what it is good at (ie, heavy number crunching). Since I was familiar with STM32 family (from previous projects) I decided to use cheapest thing available (so called "Blue Pill", STM32F103) that has support for hardware based DMA scan mode reading so instead of wasting Teensy just to wait for the hardware, I would use dirt cheap micro that has hardware that does most of the work by itself (STM32 has DMA scan mode that automatically does inner loop completely in hardware -> i.e. read ADC from analog input pin, store to RAM, switch analog channel to the next pin and repeat the whose scan, without using CPU at all). And now I have Teensy 4 free to do actual audio processing.

In your use case, with twice as many analog inputs, if you wanted to continue to use Teensy 4 for that task, you would either use above code and lower scan rate to 500-700 per second (which I believe would be perfectly fine), or dig into iMXRT 1060 Manual https://www.pjrc.com/teensy/IMXRT1060RM_rev3_annotations.pdf and figure out how to talk directly to hardware to make the most out of it .

On paper Teensy 4 processor can reach 1M sps (1μs) conversion time with effective number of bits = 10.

If you wanted to go hardware route (that frees your CPU to do other work) on Teensy 4 (as opposed to software-driven data acqustition), you would need to program timer (PIT), then use ADC_ETC (ADC external trigger control) to switch channels and do the AD conversion and use eDMA controller to transfer AD data to RAM. Unfortunately Arduino library won't do that for you and you need to talk to hardware directly. I did not choose that route because doing the same on BluePill was just cheaper with slightly less noise, as Teensy is high-frequency part and AD converters don't like HF around them.
 
Last edited:
Thank you for the time and effort you pit into your answer. You are a bit over my head, but I will try. Is it correct that you are trying to show me how to read the SS49E Hall sensors directly into a Teensy analog input without the "Blue Pill", STM32F103) indicating the issues going to such an approach. If I do that, I would need 12 Teensy's to read all 183 keys on three manuals? I can duplicate the schematic and picture you have at the beginning of the post. However, without a schematic, I would not know how to utilize the STM32F103. Sounds like the STM32F103 would have to be programmed. The Ardino C program you showed is for directly reading the Hall Sensor not the output from the Sparkfun chip?

Why can't you use a lower resistance pots to reduce your timing?
 
No, no, you don't need 12 Teensy's. You need just ONE Teensy.

The code that I have provided above is for ONE Teensy reading ALL 183 keys.
You would need 12 HC4067 Muxes. Each mux has 16 inputs, so you would have upto 12*16 = 192 inputs (for Hall sensors). The output of each mux will be connected to Teensy pins PIN_A0, ..., PIN_A11

The code that I have provided was for 6 muxes, but you can just change the single line to handle 12:

C++:
#define HOW_MANY_MUXES 12

Look at the original schematics. You need to treat this as a single "module" with 16 hall sensors. You would need 12 of those. Each Mux has S0, S1, S2, S3 select lines. You connect them in parallel to PIN_D0, PIN_D1, PIN_D2, and PIN_D3 of your single Teensy.

So Teensy's PIN_D0 connects to S0 line of EACH mux module. S1 to PIN_P1, S2 to PIN_D2 and S3 to PIN_D3.
This lines select channel in ALL muxes in parallel.

Now the SIGNAL line output from each mux (SIG) is connected to consecutive analog inputs of SINGLE Teensy. So SIG line of first mux goes to PIN_A0, SIG line of second mux goes to PIN_A1, SIG line of third mux goes to PIN_A2, and so on the last module SIG line goes to PIN_A11

Now the code that I have shown, outputs the number 0..15 to PIN_D0...D3 effectively choosing channel 0..15 in each mux. It waits 1us. Then it reads analog inputs from each of the mux (12 of them). After reading it switches the channel. This way it reads 12 * 16 analog inputs in ONE sweep.
1779048526385.png

The "MUX" module

PS. As to your question "why can't I use lower resistance for pots?" Yes I could but keep in mind that lowering resistance increases the current flowing thru pot constantly. With 10kOhm and 3.3V supply you get 3.3/10000 = 0.33mA per pot. With 48 pots you have 0.33*48 = 15mA consumed by pots alone, constantly. If I used 1kOhm pots, 48 of them would consume 150mA (more than Teensy). The whole Teensy consumes 100mA.
 
Last edited:
Back
Top