Skip to content

A DIY hybrid piano without ADCs (purely digital sensors)

JayKominek

this depends heavily on the physical realization of the circuit.

if what is depicted is entirely on one PCB with a ground plane, then eh, it's probably okay. you've moved the mux that was designed by Very Smart People out of the ADC and onto your PCB, and made a lot of the signal loops larger, and given yourself synchronization problems to worry about. you can probably overcome all of that.

if the P0..7 nets are pieces of copper wire leaving the board, coming from some more central circuit board to sensors on each key (which seems necessary, because most actions don't have all 88 keys spaced exactly the same distance from each other, but maybe you're lucky?), then those loops are going to act as illegal radio transmitters.

it isn't the 10kHz sampling rate, or the 80kHz mux switching rate that will matter, it'll be the speed of the edge(s) generated by the 74HC238 as it switches the signals on and off that generate the noise. the '238 switches fast enough to be used in circuits of many, many MHz, for which your wires will be passable antennas.

RIP I am not sure how a constant current setup would react to this kind of multiplexing.

probably best to make the driver aware of the switching of the mux, so that it can turn off, allow the mux to switch, and then turn back on. and you're going to want a very well tuned control loop in your CC driver so that it can settle fast enough.

RIP I don't think their lifetime would be affected by the number of on/off cycles, if they are not subjected to current spikes. In fact their lifetime could be extended by such a driving mode since it's typically rated in pure time spent in the on status.

yes the LEDs would be happier and last longer.

and as you alude to, while the LED might be happy to switch extremely fast, it isn't necessarily being driven in a fashion which will allow it to do so.

the LEDs transmitting data at megahertz aren't doing it with a voltage source on one side, a resistor on the other, and a meter of copper wire inbetween.


scherbakov.​al

DavisB

That's interesting. I want to try to test a similar scheme. But I'm still forced to finish the renovation of the house. What if you use something like CD4022B instead of 74hc238?(maybe there are faster options..) Then you will need 1 channel instead of 3 channels to switch. I'll have to check it out..


DavisB

RIP
Can this constant current source driver be made such that it's powered purely from USB? (see korg microkey: plug in USB and it works)
Can a constant current source of this type be built from commodity jellybean components?

Lighting up 88/8=11 LEDs is something than can be powered directly from USB I think, and this is highly desirable.

scherbakov.​al
I have not looked at this IC yet. It does look like an interesting option...


RIP

JayKominek this depends heavily on the physical realization of the circuit.

Thanks Jay

JayKominek it isn't the 10kHz sampling rate, or the 80kHz mux switching rate that will matter, it'll be the speed of the edge(s) generated by the 74HC238 as it switches the signals on and off that generate the noise. the '238 switches fast enough to be used in circuits of many, many MHz, for which your wires will be passable antennas.

Ah, that's right, I forgot the mux won't switch with a sine wave 🤣

JayKominek probably best to make the driver aware of the switching of the mux, so that it can turn off, allow the mux to switch, and then turn back on. and you're going to want a very well tuned control loop in your CC driver so that it can settle fast enough.

I'm not sure the constant current driver can reliably switch on and off at that rate without 1) damaging itself in the process and 2) settling at the intended current in time for the measurement to happen. The datasheet of the CL220 which I am currently using says that it has a shorter than 12us turn-on delay (total, see fig 3-3 and table 1-1), so time-wise it's fine (but one might need to make the capacitor smaller, otherwise that might take longer to charge/discharge at each cyle). What I don't like is that there is an inrush current spike (of unspecified value) and current overshoot of a whopping 25mA (again see fig 3-3 and table 1-1), which I am not sure it'd be good to expose the LEDs to (and one needs to make sure to not mistake those as hammer movements, but that is just timing the measurement right in the ADC and/or software). I guess other CC regulators may be more well-behaved in that regard?

Side node: I am currently using an off-the-shelf linear power supply namely the HB48-0.5-AG and, I've got two spares because I am a hoarder and I can't resist the volume discounts, let me know if you want one or two to play around with them.

JayKominek the LEDs transmitting data at megahertz aren't doing it with a voltage source on one side, a resistor on the other, and a meter of copper wire inbetween.

The mux could be placed closed to the driven LEDs, so that meter of cable could be made 5-10cm instead which will help some, not sure if enough.

DavisB Can a constant current source of this type be built from commodity jellybean components?

I would not build the CC driver, but buy it. See the link above to the CL220. It's just a commonly available part, not jellybean, but common simple to solder and inexpensive.

DavisB Can this constant current source driver be made such that it's powered purely from USB? (see korg microkey: plug in USB and it works)

DavisB Lighting up 88/8=11 LEDs is something than can be powered directly from USB I think, and this is highly desirable.

Power-wise, for sure. If you want to power them in series (which makes sense for longer chains, but may not be ideal in this situation), 11 LED with typical forward voltage of 1.5V each means you need 16.5V. Not impossible to generate, but I would not do it that way because you need to increase voltage and that would introduce ripple (among other things). However if you have (say) 6 chains of two LEDs each, that is just 3V each. Given how inexpensive the CL220 are you can have 6 parallel chains (see fig 3-5 in the datasheet) and that will work. However the minimum operating voltage of the CL220 is just 5V which might be too close to the USB nominal one, so I'd look for another similar component instead.

At this scale (just 11 LEDs driven at any given time), I would need to think more if I like the constant current approach more, or if the current limiting resistor is adequate and may sidestep the problem with the inrush and overshoot current. But still, you'd need to do something to make sure the current is stable enough. See the problem? In this design, we want the current to be very stable (like before), but then switch it on-and-off at all times, so some typical "smoothing" approaches to remove short-term stuff (e.g. large capacitors) cannot be used.


JayKominek

DavisB Can a constant current source of this type be built from commodity jellybean components?

My design includes a simple linear constant current supply.

https://github.com/jkominek/piano-conversion/blob/master/hardware/led-power/led-power%20schematic.pdf

An op-amp capable of handling whatever the supply voltage is, a MOSFET, a resistor, and a voltage reference (which can be shared between drivers) will do. If you turn the voltage reference off to a particular op amp (driving the non-inverting input to zero) then I believe that will turn off the supply.

RIP I'm not sure the constant current driver can reliably switch on and off at that rate without 1) damaging itself in the process and 2) settling at the intended current in time for the measurement to happen.

Switching constant current drivers might not appreciate being flipped on and off like that, but simple linear ones like I include in my design can be designed such that they shouldn't have any problem. If you look at my design, that would just be a matter of flipping an op amp and a MOSFET on & off at your 8 times sample rate. The MOSFET will do that gleefully. The op amp won't mind trying, but to your second point, it might be hard to get one that can do it fast enough while satisfying all the other various requirements.

RIP The mux could be placed closed to the driven LEDs, so that meter of cable could be made 5-10cm instead which will help some, not sure if enough.

That will certainly help with the speed of things. But that length of wire without an adjacent ground is still likely to radiate.

DavisB Can this constant current source driver be made such that it's powered purely from USB?

Running something like this purely off of USB is going to make your life harder. Unless you're going to implement USB-PD, then as a practical matter that limits you to 2.5W total. Some of which you're going to lose because you're going to have to regulate that tiny trickle of 5V down to something else so that you can ensure it is stable and clean where your digital logic needs it.


DavisB

Thank you for the valuable feedback. It really got me thinking.

While the LED switching itself can be very fast, one reason this method might not work as well as I would have liked is that the phototransistors are quite slow.

ITR8307 (and CNY70 and similar) have bandwidth of around 20-40kHz depending on the load usually the 10k resistor as seen in the sample circuits) Rising/falling edge of around 20 microseconds.

If I understand correctly, when switching from one phototransistor to another (1 out of 8) up to 20microseconds are potentially necessary for the phototransistor output to settle on the correct output before even initiating any measurement or ADC conversion. This is getting too close to 10kHz-20kHz per sensor sample rate I'd like to achieve.

The difference between a photodiode and phototransistor is that phototransistor in itself functions as an amplifier of sorts, while a photodiode absolutely needs an amplifier as it outputs very little current (the transimpedence amplifier is often used in this case). Photodiodes can be very fast however (as used in fiber optics, etc)

hc238/hc138 ICs have falling rising edges of around 10ns at 5v (depends on VCC, significantly more at 3.3v).

As a rule of thumb, bandwidth of a squarewave signal is calculated using:
Bandwidth_in_GHZ = 0.35/Rise_time_in_nanoseconds.
In case of 10ns rising edge that would be ~ 35mHz.
The traces and the load will have a certain amount of capacitance and resistance - that will function as a low pass filter to reduce the 10ns rising edge to a degree.


DavisB

I'm listening to lectures by Dan Gelbart, and from my understanding, as far as high accuracy sensing is concerned -
any measurement which can be converted to time measurement can be measured to a much higher degree of accuracy.
Most things - with great care - can be measured to about 1 part per million. 1micron to 1meter, 1volt to 1microvolt, 1mg to 1kg, except time, time can be measured to orders of magnitude higher accuracy (think atomic clocks). Comparitively 1s to 1microsecond(1Mhz) is very easy and cheap to measure. Any bargain bin MCU will do.

For signals that are AC and you're measuring frequency, that becomes a measurement of zero crossing time (ie. time measurement).

This is why FM has way better noise immunity than AM, it only cares about zero crossing time (not noise on amplitude and other distortions of the signal)!

If signal is AM modulated (data is in the amplitude not carrier frequency), you can get rid of lot of the noise by building a band pass filter around the carrier frequency F.

If you amplify DC signals you get DC drift, every amplifier has an offset voltage which drifts.
DC drift is a problem if you work with low level signals in mV. AC doesn't have this problem.

Optical sensors, the ones we use, typically have peak wavelength of 940 nm. They have a built in optical band pass filter
(colored glass/colored material which filters for that wavelength).
But there are other sources of 940nm ambient light other than the IR-LED itself (room lights, outdoor lights, etc)
Working with AC also provides a way to filter out other sources if IR-LED is shone modulated with certain frequency F (10kHz-100kHz).
Then we can bandpass filter modulated frequency, thus eliminating other sources of ambient light that operate at different frequencies.

If you have an analog sensor, you want to put amplifier right next to it (if you put it on the other end, you will amplify noise, etc).

Distance sensing can be used to measure many parameters, some of them:

  1. Force (deflection of a cantilever spring, F=kX, if we can sense X we can sense F)
  2. Acceleration (deflection of a cantilever, F=mA, mass is known, F can be measured from (1))
  3. Mass (deflection of a cantilever, F=mg)
  4. Pressure (deflection of flexible diaphragm)

Many accelometer IC have silicon micro etched springs that measure distance capacitively.

For highly accurate measurements ideally you want differential sensor that eliminate drift in other parameters of the sensor.


DavisB

Vishay Semiconductors have "Application of Optical Reflex Sensors TCRT1000, TCRT5000, CNY70"
That has the following circuit that demonstrates use of such AM modulated signal:

The circuit in figure 20 is a simple example for operating the reflex sensors with chopped light. It uses a pulse generator constructed with a timer IC. This pulse generator operates with the pulse duty factor of approximately 1. The frequency is set to approximately 22 kHz. On the receiver side, a conventional LC resonance circuit (fo = 22 kHz) filters
the fundamental wave out of the received pulses and delievers it to an operational amplifier via the capacitor, Ck . The LC resonance circuit simultaneously represents the photo transistor’s load resistance. For direct current, the photo transistor’s load resistance is very low- in this case approximately 0.4, which means that the photo transistor is practically shorted for dc ambient light.


DavisB

I did some measurements for Everlight elec ITR20001 phototransistor feeding into it a 160kHz 5v pwm signal from an arduino mega:

I'm getting around 800ns fall time of photo transistor output. Which is way less than specified 20microsecond rise/fall time, am I missing something?
This is using 8kOhm resistor as load on emitter side (hooked up as seen in the initial schematic, but without the hc238).

If all sensors are sampled at 20kHz, that gives 50microseconds to switch between the 8, giving 6.25us per sensor time window, 1us of which would be taken up by the transition.

Another way to look at is - if 50us are the time window to sample all 88 sensors, then 8x11 mux using 238 would eat up 8us of that budget in transitions. So this approach might as well work if that is taken into account.


DavisB

I've upgraded slot-through slit-sensor PCB design with hc238 muxing too.
Now it effectively draw less than 200mA, which means it can easily be directly powered from USB alone.
And requires only 3 resistors instead of 32 previously, only one hc238 IC per 8keys (instead of two shift registers per 8keys), 1 decoupling cap. This reduces part count and simplifies assembly (matters alot if hand placed).

A single Raspberry Pi Pico is wholly sufficient as main USB-MIDI device too, with tinyusb library. RP2040 is 70cents @ 100qty, compatible QPSI flash around 25cents. Ready made original Raspberry Pico boards are 4.2-5€, WeActStudio rp2040 is 3-4€, YD-RP2040 modules go for as low as 1.5-2.5€ a piece.

Total effective electronics BOM of this slot through design ends up being roughly 50-60€, including PCBs, but excluding assembly. No external power supplies necessary either. This is as simple as it gets, but it does require a mechanical element to move through the slit, the only downside of this approach.

I suspect a wholly analog version can also be built in roughly same electronics BOM budget, but it is highly dependent on built in ADCs of stm32 mcus to be "good enough".

My first experiences with programming stm32 via SWD has been thoroughly positive, debugger and breakpoints work great, while also being able to see peripheral registers and their state as machine is running. This is great for troubleshooting. (not using cubeIDE)

stm32 has two vendor specific APIs, stm32 HAL (high level) and stm32 LL (low level). STM32 LL are just thin wrapper over peripheral registers that are memory mapped into flat address space. Interfacing with peripherals is essentially taking 32bit addresses (as pointers) and reading/writing to values to them. Reference manuals have descriptions in detail where each peripheral is in memory space. I find stm32 LL API particularly pleasant and easy to understand.

Other than vendor specific peripherals, both rp2040, stm32g431, stm32h7, teensy4.1, they all have arm cortex m0/m4/m7 cores . RP2040 is a cortex m0 (no floating point unit), teensy4.1 and stm32h7 are cortex m7 (have floating point units), just clocked at 600mhz(teensy) vs 480mhz (stm32h750). They are programmed and debugged in the same way via SWD/JTAG.

I have weact stm32g431 (2x 12bit 0.25us adcs) devboard on hand with weact stlink probe. DMA DAC works well (very useful as signal generator!), sending ADC readings via UART using DMA works well too. ADC conversions thus far seem to work great via DMA too (ADC conversions automatically get written in buffers).

I wrote a small app for streaming and displaying ADC values over UART, works easily up to 1mbit baud rate (haven't tested higher)

(ignore ENOB readings, those are rough estimates if the signal is not "moving")

This is capable of also generating MIDI signals to be routed to Piano VIs. Which means that I can develop and test velocity calculation algorithms fully on PC before porting it over to a MCU.

The "tricky part" is setting up the peripherals/timers/DMA and buffers such that almost nothing is blocking (or calling interrupts as interupts also take cycles to switch in and out of).

For h7 480mhz / 88keys * 20khz sampling = 272 cpu cycles per key. This is only slightly less than teensy at 340 cpu cyles per key.


scherbakov.​al

DavisB ….. 480mhz (stm32h750)

There are stm32h723vgt6 boards with 550mhz processor and built-in full color display for only 10 dollars. I myself switched from stm32h750 to stm32h723. They work great and are inexpensive.


scherbakov.​al


RIP

Great to hear that the STM32 are working for you (in fact they are working fantastically well for me too -- but at work where we have a team of specialized engineers each of which can take care of one small but essential aspect of the design and where I never touch them directly).

For a hobbyist like me, almost-alone in this project, the STM32 was putting myself squarely in the "project does not get completed" camp. Like this guy puts it better than I can.

https://www.youtube.com/watch?v=4jgTCayWlwc


DavisB

Got tinyUSB MIDI working on stm32g4 as well!

Now that I've gotten tinyusb MIDI work with both RP2040 and STM32G4, i'm pretty confident that I'll be able to get it working for any of the supported MCUs on the list.
https://github.com/hathach/tinyusb/blob/master/docs/reference/supported.rst

Of which rp2040 and stm32h750 offer the best value! Honorable mention WCH CH32V203C8T6, RISCV 144mhz, LQFP-48(7x7), 50cents a piece - great MCU for basic MIDI devices with sliders and knobs, etc!

Now I can send MIDI directly from MCU via USB, or send sensor data via UART and generate MIDI on PC via virtual MIDI port!

The first algorithm for analog velocity calculation is very simple - store last N samples, and once current sample crosses some threshold, take current sample and one sample N samples ago (N = 32, 64 or 128, ....) and then take the average of that. This doesn't even require floating point numbers, or division and can be implemented in few cycles with just fixed point math, etc. Then I feel that calculated velocity has to be fitted onto some curve before being translated into MIDI.

(taking current sample and one sample N samples ago is eerily similar to effectively reducing sample rate by N)

Perhaps there's some smart things that can be done with those samples (filtering, rejecting obviously noisy samples, etc), but nothing pops up at the moment! Any ideas?


RIP

DavisB Got tinyUSB MIDI working on stm32g4 as well!

Did you got it working on its own, were you able to integrate it with another project (e.g. the ADC example, or your own fully fledged algorithm)? In my experience the former is quite easy, the hard part is the latter. If that is what you have done, big kudos to you!

DavisB Any ideas?

Plenty. Most of the discussion on this topic happened in the thread linked below (and links therein), but many of my multi-part "Build your own DIY hybrid piano - step xxx" have some. And to be clear most of those are not mine, the contributions are from the whole community here!

https://pianoclack.com/forum/d/289-scanning-speed-and-velocity-mapping-of-cybrid


DavisB

I've built the circuit as seen in this image:

And it doesn't work works only to extremely slow switching speeds, ie. switching only couple of hundred of times a second. After that it works, but only allows to sense one-sensor at a time (at speeds switching sensors 160khz times a second) - if any of the sensors is active, it just reports low 0V for all the other sensors, regardless of their state.

And It's not obvious why is it so... and seems like the only way it could happen is if the transistor starts conducing in reverse for some time after being switched off? (hc238 can both source and sink current).

PCB looks like this, double sided.


scherbakov.​al

Hello! I'm not sure, but it seems to me that the problem may be that the transistor in the sensor takes about 20us to open and close. 20us = 50 kHz period. As stated in the datasheet: Oh yes, it was mentioned above..

I am very impressed that you managed to run the tinyusb library on the stm32! I once tried to make this library work, but apparently I didn't spend enough time studying it and it didn't work out. Nevertheless, it would be extremely interesting to look at the stm32 project with tinyusb running.


CyberGene

scherbakov.​al sorry, I misread your post, never mind 😃(edited my comment)


scherbakov.​al

A couple of years ago, a friend proposed a sensor circuit that used ambient light filtering. It's a cool scheme, I couldn't have come up with such a thing. The LED would turn off, the transistor would be measured, and then the LED would light up. The value of the previous measurement would be subtracted from the current measurement on the transistor. This process seemed to work well, but we ran into a problem when we tried to adjust the frequency of the sensor's stable operation. The frequency was about max 10 kHz when we used CNY70 components.

https://github.com/brrastak/midi_sensor/blob/main/midi_sensor.pdf

I do not know how it works) There's also a need for a control driver for such a sensor.

CyberGene sorry Nothing) I've got a double post above (2 weeks ago. I was writing from the subway and lost contact for a moment).. I tried to delete it, but I couldn't find how. It can be deleted


DavisB

The odd thing is that the sensors work just fine easily switching up to 20khz as long as they are alone, and don't share the same time-multiplexed analog output signal line at the same time. (or when they operate at very slow switching speeds, then the circuit works as intended)

I've also measured response time of these phototransistors via an oscilloscope individually, which makes me think there's some weird interaction between the sensors themselves as they are switching between each other while sharing the same output line.

The only solution to this issue that I can imagine is putting a signal switching diode on each transistor output...


« Previous Page Next Page »