Homemade finger drumkit, ATMega-based, MIDI-through-serial output

Similar projects worth following
I'd like to play drums but the price of an electronic drumset is preposterous. I'll just make one, then! Lots of people have already done that, so there's no shortage of inspiration. And it shall be called DrumkitOne because One is much classier than 1, and that opens the door to DrumkitTwo, an even more awesome homemade drumkit.

I'm going to write a full description of the project when it is acceptably finished. In the meantime, the updates are posted as logs. Files are available here.

My goal for the project is not only to build a drumkit, but also to make everything as clean and documented as possible. This way, I can assess what I can already do well (e.g. AVR programming), what I need to improve (e.g. analog electronics) and what I need to learn (e.g. automated software documentation).

Disclaimer: I consider myself quite good at digital electronics and microcontroller programming, but somewhat inexperienced in analog electronics. So anything I say about the analog circuits is to be taken with a grain of salt, and corrections are welcome.

Hardware, toolchain and external software

Here's the development environment for this project:

  • MCU: ATMega168PA with a 10 MHz crystal mounted on a Crumb168 breakout board by chip45. I really like those boards because they're inexpensive, they include only the bare minimum for the MCU to work BUT also a USART-to-USB bridge, and they can be plugged on a breadboard. I'm not affiliated with chip45 :p
  • Programmer: Atmel-ICE (the one in the white case) in ISP mode
  • OS: Linux Mint 17.3
  • IDE/toolchain: Eclipse 3.8 with AVR plugin, avr-gcc v4.8.2, avrdude v6.1
  • Serial-to-MIDI: the Hairless MIDI to Serial Bridge v0.4
  • MIDI sequencer: Hydrogen v0.9.6
  • Helper scripts are written in Python 3.4

  • Pad stroke detection

    Peter Dell12/23/2016 at 16:28 0 comments

    In the last log, I presented some signals from the drumpads. The measurements led to several remarks:

    • When a pad is struck, a bouncy signal is generated. Logically, the first peak is caused by the stroke itself, so it's the most important here.
    • The peak amplitude and rising speed seem greater when hitting the pad hard.
    • The signal goes back to zero in about 10 ms, and the fastest I can strike a pad is approximately by 60 ms interval.

    Since the peak height corresponds to the stroke strength, I thought it would be interesting to detect the peak itself (in contrast to just detecting any activity above a threshold). This way, I could generate louder sounds (i.e. MIDI messages with a higher volume) for harder strikes. I set up the ADC of the microcontroller to sample the pad output, and I implemented a naive peak detection:

    • Make a shift register of three values.
    • Every time the ADC finishes a conversion, push the new value in the shift register.
    • If the middle value is higher than both its neighbors, it's a peak.

    Usually the peak is detected quickly enough. On the screenshot below, channel 1 (yellow) is the pad output and channel 2 (blue) is a digital pin triggered by the peak detection. Here the triggering happens almost instantly.

    But sometimes (about 10 % of the time), the detection is really late, as shown below.

    There's almost 12 ms of delay, if the first peak is even the one that was detected! I think this is too slow for reactive playback. But the main issue is that it's unreliable. Sometimes there's no triggering at all.

    The peak detection is too simple, in my opinion. There are certainly better algorithms out there, but a simple solution is to trigger as soon as the signal rises above a threshold. Of course in that case, we lose the information of peak amplitude, so we don't know how strong the hit was. (Actually, I already made some tests and I don't think that it's a great loss. The dynamic range of the piezos don't allow precise hit intensity differences anyway.)

    I tried that simple thresholding plus "freezing" the pad for 50 ms after triggering (i.e. a basic debouncing). This seems to be much more reliable. Below is an example where I "drumroll" the pad as fast as I can with two fingers. The yellow signal is the piezo's and the blue is the trigger. The trigger says high as long as the pad is frozen, for illustration purposes.

    The freezing time and the height of the threshold are completely empirical, but the method works decently, so I'll keep it for the moment.

    Another observation: I have to hit the pads pretty hard to get a trigger. This comes from the pad itself and not from my signal processing, since even the oscilloscope (which I assume uses mode reliable methods than mine) doesn't trigger in some cases. I wonder if there's a way to make the pads mechanically more sensitive. I saw that some people put a layer of metal in their pads, i.e. a sandwich with rubber-metal-piezo-rubber. Maybe that could help.

  • Finger pads and oscillo measurements

    Peter Dell12/22/2016 at 14:19 0 comments

    It's been a while since I updated this project, coz life got in the way and I didn't have access to an oscilloscope to actually know what I was trying to measure. Fortunately, now I do. Before I write about those measurements, let's talk about the pads.

    Finger drums

    Realistically, I'm not going to build a whole drumset (for several reasons, including no time and no room for that). Since the whole thing will just be a toy for me anyway, I decided to make it a finger drumset instead. Much more manageable.

    Following that decision, I noticed that the piezos seem to react quite differently whether they're enclosed in some kind of pad or not. Since I'm not going to use them bare, I'd better experiment with pads right away. I built simple pads by sandwiching the piezos between round pieces of cheap Amazon mousepads. The pads have a diameter of about 6 cm, the piezos are 35 mm diameter, and I glued the whole thing together with spray glue. It's ugly but I don't care :p


    Thanks to the oscilloscope, I can stop hypothesizing about the signals and make clean measurements. The questions I want to answer at the moment are the following:

    • When I strike a pad with a finger, how does a typical piezo signal look like? How much does it bounce? How long does it take to get back to zero volts? What's the maximum voltage reached?
    • Is there any significant difference across piezos?
    • What's the influence of striking hard or normally?
    • How fast can I strike a pad?
    • What is the influence of the resistor value at the pad's output?

    To answer that, I hit the pads and looked at their outputs. I did some measurements with no resistor at the output (i.e. only with a 3.3 V Zener diode) and some with the resistor.

    Pad stroke, no output resistor

    On the picture below, the rows are three independent measurements. The left column shows the whole signal, including the decay to zero, and the right column is a close-up of the initial peaks.

    The same measurements with other pads are not significantly different (which is a relief). A typical signal has two major bounces at the beginning (max about 2 V, the Zener doesn't even clip), a series of lower bounces, and then decays to zero in as long a 500 ms. It's interesting that the second signal is quite different, at least I know that it won't be all the time the exact same shape.

    Pad stroke, with output resistor

    I used to put a 1 MΩ resistor at the output, but a lower value allows the piezo to discharge faster. With 100 kΩ, the voltage returns to zero about 40 ms after striking. With 10 kΩ in 10 ms. At 1 kΩ, the piezo stops outputting anything, so I settled for 10 kΩ.

    The screenshots below show six independent measurements (top row: hard strokes, bottom row: normal strokes). This time, there's the 10 kΩ resistor at the output.

    Unsurprisingly, the normal strokes generate a smaller voltage than the strong ones. And the main difference with the earlier screenshot is that the signals go back to zero much quicker. Apart from that, the general shape is similar.

    Stroke speed

    Using a single finger, I can strike a pad twice within a 100 ms interval. With two fingers, 60 ms. Of course I wouldn't be able to sustain a drum roll with my fingers at that speed, a 60 ms period makes 1000 bpm... But that gives an idea of a worst-case scenario. I probably won't generate successive strokes faster than 60 ms, and the signal returns to zero in about 10 ms. So there isn't much chance that the signal would still be bouncing from a first stroke when a second one happens. That's good because it will make debouncing in software easier. I'm thinking of just detecting the first peak, and then discarding any other peak that occurs within the next 50 ms or so.

  • Signals from a piezo sensor

    Peter Dell07/23/2016 at 21:44 0 comments

    The point of the whole system is to play a sound sample when a drumpad is hit. But how can we recognize that event? What kind of signal is generated by a piezo when struck, and how to process this signal to a) detect the strike and b) measure its strength?

    Short of having an oscilloscope, I can still get an idea of the voltage generated by the piezos thanks to the MCU's ADC. As explained in the first log, the voltage across the piezo is clipped by a 3.3 V Zener diode. I fed this signal to one of the ATMega's ADC channel and made the microcontroller stream the conversion result to the serial port. (Actually, not all conversion results: only when the signal is larger than 0 V. This way, the interesting part of the signal -- when the piezo is struck -- is the only thing that is sent.) Then I wrote a small Python script to receive this data and plot it.

    Before looking at the results, some info on the ADC settings. I chose a sampling rate of about 10 kHz because after experimenting a bit, it seems to be an appropriate value. The ADC can give results on 10 bits but we don't need that kind of precision. Remember that in a MIDI message, the velocity (how loud the sound should be played) is on 7 bits only. Sometime in the future, the velocity will be linked to the ADC reading (how hard the pad was struck). So we really don't need a 10-bit conversion. I set the ADC to work on 8 bits (which has the added advantage of being faster to read).

    Long story short, I made a kind-of-oscilloscope :) I put a piezo on my desk and hit it with my finger softly, normally and hard. I did it three times for each strength to be sure that the signals looked the same all the time (they did). Here are the signals, starting with a light touch...

    Edit: The timings are all wrong because they represent how fast the data was received by the computer. They're baudrate-dependent instead of being sampling-rate-dependent. See the following log for proper measurements.

    ... a somewhat harder one...

    ... and a hard one.

    Note that I didn't convert the result of the conversion to volts, because it doesn't matter here. So the Y axis is between 0 and 255 (the conversion being on 8 bits... I hope you're following).

    First remark on those signals: they all bounce! There's always a clear first strike and then something like a smaller secondary strike. I'm guessing that's because the piezo bends several times when hit: first strongly in one direction, then goes back in the original shape, then again a bit in the first direction... Until it stabilizes. We don't see the negative part of the signal but I expect a negative peak to appear between the two positive ones that we see here.

    Hitting harder seems to have at least four effects:

    1. the signal's maximum is larger
    2. this maximum is reached quicker
    3. the signal is noisier
    4. the voltage takes longer to fall back to 0 V

    1. and 2. are really nice, because there are two obvious ways to find out the strength of a hit: the maximum value of the first lobe, or some kind of derivative of the first few samples. I think that point 3. is for mechanical reasons. It's possible that when striking hard, the piezo membrane is shaking more or less randomly additionally to the large back-and-forth bending.

    As for the remark 4., it's interesting because the decay time will limit how often we can detect hits. Let's assume that we just detected a strike by some method, and that we want the signal to completely go back to zero before being able to detect a second one. Let's also round it up and say that in the worst case, a hard strike takes 100 ms to decay. That limits the frequency of detectable successive strikes to 10 Hz = 600 bpm. Which is about half the typical speed of the world's fastest drummers. I probably won't reach that in my lifetime, so the speed limitation isn't too worrying.

    By the way, realistically, the last strike isn't really "hard". It's nice here because the signal doesn't go over 255 (i.e. 3.3 V), so there's no clipping. But I think that a hit from...

    Read more »

  • MIDI messages

    Peter Dell07/23/2016 at 05:33 0 comments

    Like I said in the first log, the MIDI protocol is super easy to set up. The messages are generally composed of one status byte + one or more data bytes. There is no error checking and no flow control. Most of the info I found about MIDI comes from here and here.

    For the specific case of drums, the protocol becomes even simpler. So far I implemented three possible MIDI messages: note on, note off and program change.

    Note on

    This message has one status byte and two data bytes. It tells the MIDI sequencer (for this project, Hydrogen) to start playing a note. That's the structure of the message: 0x9N - 0xPP - 0xVV.

    • 0x9N: status byte 0x90 for "note on" + channel number from 0x00 to 0x0F. By default, the MIDI specification uses channel 10 for drums. If I get it correctly, it's possible to choose any instrument on any channel, but keeping channel 10 avoids having to select an instrument altogether. Since channels 1 to 16 are numbered 0x00 to 0x0F, channel 10 is 0x09.
    • 0xPP: first data byte, pitch from 0x00 to 0xEF. For drums, instead of the pitch, this byte selects a drum or percussion to play. For example, acoustic bass drum is 0x23, acoustic snare 0x26 and closed hi-hat 0x2A. (The whole list is here again.)
    • 0xVV: second data byte, velocity from 0x00 to 0xEF. This corresponds to how loud the sound is going to be played. A default value that I used for tests is 0x40.

    For example, to play a bass drum at a medium volume, the message to send is 0x99 - 0x23 - 0x40.

    Note that the pitch/instrument and velocity are on 7 bits instead of 8. This is because in MIDI, only status bytes start with a MSB set (i.e. status bytes go from 0x80 to 0xFF). For the pitch, it's not too important because in the case of drums, the values are fixed anyway. But for the velocity, we have to keep that in mind. This will be important when we map a reading from a piezo sensor (how hard the drumpad was struck) to a velocity (how loud the sound should be).

    Note off

    When interfacing a piano for instance, you want to be able to tell the sequencer when to stop playing a note. With drums, it might or might not be useful. The structure of the "note off" message is the same as "note on", except that the status byte is 0x8N and the velocity is generally set to 0.

    Program change

    This message sets one MIDI channel to play one particular instrument: 0xCN - 0xJJ.

    • 0xCN: status byte 0xC0 for "program change" + channel number from 0x00 to 0x0F.
    • 0xJJ: instrument from 0x00 to 0xEF.

    Normally, the drums are selected by default on channel 10. But if they weren't, this message should be sent as 0xC9 - 0x00 before starting to play.

    In the firmware, I implemented these three messages but concretely, I've been using only "note on". It seems that Hydrogen has the drum channel properly set up (it's a drum sequencer after all, héhé) and doesn't care about "note off". If I end up trying another sequencer that needs the "note off" and "program change" messages, at least I already have the functions.

  • The story so far...

    Peter Dell07/20/2016 at 16:28 0 comments

    I started this project at the beginning of July. Since then I adjusted the ideas I had at first and got some promising results. In the following, I'll summarize the status of the project and explain the choices I made for the general concept, the system's input and its output. The ideas that didn't work are also included so that maybe someone will learn from my mistakes. This is probably overdetailed, but I want to include everything I can for future reference.

    Original concept

    Originally, I wanted the first version to look like the block diagram above. The drumpad is a simple trigger that launches a sample playback from the MCU. The samples are directly played on a loudspeaker. S1 and S2 are signal conditionning blocks. We'll see in the following that the concept has changed since the beginning of the project, as I gave up the ideas that didn't work.


    The drumpads should detect the strike of a drumstick and send that info to the MCU. To do that, most people use piezoelectric sensors and some other use accelerometers. I decided to try with piezos first because they're cheap, then upgrade to accelerometers if needed (maybe for DrumkitTwo!). There's one disadvantage I could foresee with piezos: the voltage generated can reach tens of volts when hit hard enough, but I want to use only 3.3 V everywhere for my circuit. The basic method is to limit that voltage with a Zener diode, but it means that the piezo output is clipped when exceeding 3.3 V.

    The piezos I used so far are EPZ-20MS64 (20 mm diameter). At first, I thought that it would be easier to simply detect a strike and not care about the strength of said strike (turns out I was wrong). Just to test the knock detection, I built the following circuit around an opamp in comparator setup:

    The roles of the components are the following:

    • R1 = 1 MΩ discharges the piezo once a charge has been generated by hitting
    • Dz is a 3.3 V Zener diode that prevents the input of the opamp to go off rails
    • R2 is a potentiometer (100 kΩ) that sets the comparator threshold
    • The opamp is a TLC272 with VDD = 3.3 V
    • R3 = 330 Ω and D1 is… an LED! (Blue of course.)

    Knocking on the piezo turns on the LED, and the sensitivity is set by R2. Since the opamp is a comparator, the output is either 0 or VDD and can be used as a digital input for the MCU.

    Let's stop and think for a moment. Indeed, the LED turns on. But if it turns on long enough for me to see, even though the knock on the piezo lasts only fractions of a second, it means that something somewhere is discharging very slowly. Or the piezo output bounces. Unfortunately, I don't have an oscilloscope at home so I can't measure the piezo voltage directly. But by googling around, it seems that a typical voltage is some kind of damped sine wave, i.e. lots of bounces.

    In the above circuit, I tried to connect the opamp's output to a digital MCU pin instead of the resistor and LED. The MCU was programmed to send one byte of test data through serial port on the rising edge of that digital pin. And sure enough, when I hit the piezo, bursts of data were sent instead of just one. Meaning, the signal was really bouncing.

    How to debounce? In hardware, it's somewhat more elegant and it simplifies the firmware. In firmware, it's more flexible and it decreases the component count. Let's try hardware first! I attempted that with 1) an RC low-pass filter and 2) with a Schmitt trigger instead of the comparator. Since I STILL didn't have an oscilloscope at that time, tuning the component values and measuring the results was kinda hard. Neither circuit gave good results when plugged with the MCU, as the triggering was still bouncy or not responsive enough. Debouncing in firmware seemed clumsy and I didn't like it.

    At that point I decided to try what virtually everyone else does with homemade drumpads, i.e. directly connect the output of the piezo to an analog input of the MCU and use the internal ADC to detect strikes. The circuit looks like that:

    … and contrary to what...

    Read more »

View all 5 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates