Projects Research & Design Hub

Repurposing Old Disk Drives For Rotary Encoders

Figure 1 Front and back views of two BLDC platter motors used in 3.5” hard drives.
Written by Brian Millier

Using a Teensy LC Microcontroller

In most modern computers, 3.5” hard drives have been replaced with faster, higher capacity, solid-state drives. In this article, Brian tells us how he repurposed an old 3.5” drive to make a rotary encoder for his music studio, using a Teensy LC MCU and an LM399 quad comparator.

  • How can I make a rotary encoder?
  • What can I do with old disk drives?
  • What projects can I do with a Teensy LC MCU?

  • Teensy LC MCU
  • LM399 quad computer

For many years, the capacity of hard disk drives has been increasing exponentially, with little increase in cost. Personal computer users were constantly swapping out hard drives for faster, higher-capacity models. When solid-state drives became economical, they displaced existing hard drives in ever-increasing numbers.

Although I don’t work in a computer repair facility, I’ve worked with enough personal computers to have a whole box of surplus 3.5” hard drives taking up space in my basement. I decided to take the covers off some of the drives to salvage the extremely strong magnets that are a part of the head actuator assembly. Once inside, I marveled at the extremely polished surfaces of the disk platters, and the highly machined parts that make up the moving head assembly.

Earlier in my career, I had worked on large disk storage units used in data centers, and was familiar with the large “disk-packs” that were installed in those disk drives. But those units cost tens of thousands of dollars, and it was amazing to see similar precision in the $100 hard disk drives used in the last 15 years.

All 3.5” drives use brushless DC (BLDC) motors to rotate the platter(s). These are three-phase motors, generally wired up in a “Y” configuration. They are easy to mount and, in place of a shaft, they have basically a 25mm hub to fit the standard hole in the platters, themselves. Figure 1 shows a few such BLDC hard-drive motors. As you can see they are not a lot bigger than the 25mm shaft/hub, itself.

Figure 1
Front and back views of two BLDC platter motors used in 3.5” hard drives.
Figure 1
Front and back views of two BLDC platter motors used in 3.5” hard drives.

For my music studio, I had been looking for a rotary encoder to use as a “scrub wheel” (as it’s known in the business). This is a large knob that can be turned gently to move the time marker in a sound recording in very small increments, thus allowing you to define the exact moment in time at which to do some editing or perform other functions. You often want to move quickly to some other part of the recording that is many seconds or minutes separated in time. In this case, it would be nice if you could give the scrub wheel a flick and let it spin for a little while under its own momentum. As you approached the target time, you could just grasp the scroll wheel lightly to decelerate it to a stop.

I didn’t think it would be practical to try to couple a heavy knob to the common rotary encoders that are available, because they don’t contain bearings to handle a knob with much mass. Also, they exhibit a fair amount of resistance, because they incorporate detents.

When I first looked at the BLDC motors used in 3.5” hard drives, I thought that they could be an ideal candidate for use as a scrub wheel. They have excellent bearings; with the platters attached, you can give them a flick, and they rotate quite nicely for a few seconds. The platters, themselves, are thin, but the two 3.5” platters normally used would approximate the moment of inertia of the 2.5” diameter aluminum knob that I planned to use.


Advertise Here

The BLDC motor used in the hard drive that I had disassembled was three-phase. Although I couldn’t take the motor apart without destroying it, when I powered up each phase in sequence, it moved 30 degrees per step. So it looked like I could resolve 12 discrete index points or detents per rotation. This is somewhat less than the 24 index points per revolution of a common rotary encoder, but would allow for easy “fine tuning,” while the spin momentum could handle large changes quickly.

In the next section, I’ll describe the method I used to turn this BLDC motor into a rotary encoder. But first, I should mention that the vintage of hard disk that is usable for this project is limited. Back when hard disks were around 80-160GB in capacity, they actually used a discrete BLDC motor
(Figure 1). As technology advanced, and the pressure to decrease the cost of parts increased, manufacturers started to integrate the platter BLDC motor into the cast aluminum body of the drive. That is, the aluminum casting that made up the drive body contained a cavity in which the magnetic coils were attached, along with the flex PCB that connects the motor windings to a header. For these disk drives there is no removable platter motor—you would have to use the drive’s body, itself, if you wanted to use the BLDC motor. This was not practical for my proposed rotary encoder use (though I do have a DIY CNC milling machine, which I could have used to cut away just the motor section of the disk’s case).

If your hard drive is greater than 120GB in capacity, it will likely look something like the one shown in Figure 2, where you can see that the platter motor is integrated into the case. But if you’re like me, you will likely have some smaller-capacity drives lying around with the discrete BLDC platter motors. All the BLDC motors used in the disk drives that I’ve examined are wired in a Y configuration—three phase legs plus one common. That configuration is needed to work with the circuit that I designed. The three-wire delta configuration won’t work.

Figure 2
Newer hard drives, with capacities generally beyond 160GB, have BLDC motors integrated with the hard drive's case.
Figure 2
Newer hard drives, with capacities generally beyond 160GB, have BLDC motors integrated with the hard drive’s case.

From Figure 1, you can see that there are four terminals in a row. The Common terminal will be at one end or the other. If you take three readings between adjacent terminals, you will measure two equal resistances in the 1-5Ω range, and another one that measures half the value of the others. The end terminal at which this smaller resistance reading occurs is the one connected to the Common phase connection.


To work as a rotary encoder, the idea is to use the motor as a three-phase generator. At normal turning speeds, if you connect any phase plus the Common terminal to an oscilloscope, you’ll see a sine wave as shown in Figure 3. Its peak-to-peak value is 860mV, and it decreases in amplitude as it slows down from the spin I gave it. My oscilloscope is only two-channel, but if you could measure all three phases simultaneously, it would look like Figure 4. If you count up the zero-crossings for all three waves, you get six in total. Although I couldn’t take the BLDC motor apart to see the winding configuration, I found that I could measure a total of 12 zero-crossings among the three windings, per revolution.


Advertise Here

Figure 3 
An oscilloscope trace of the signal from one phase of the BLDC motor, as it slows down from being spun by hand.
Figure 3
An oscilloscope trace of the signal from one phase of the BLDC motor, as it slows down from being spun by hand.

Figure 4
The relationship between the three signals coming from the BLDC motor's three phase windings, when it is turned manually.
Figure 4
The relationship between the three signals coming from the BLDC motor’s three phase windings, when it is turned manually.

For scrub-wheel use, 12 index points per revolution plus the ability to give the wheel a flick and have it spin quickly on its own for a few seconds is still not quite satisfactory. Additionally, you need to measure velocity, and when the velocity increases, you need to send more motion increment commands per index point. If you have used instruments with rotary encoders, often the MCU’s software will accelerate the rate of change of the variable you are adjusting, as you increase the speed at which you are turning the encoder.

Given the fact that the BLDC motor, acting as a generator, produces a larger amplitude sine wave the faster you turn it, this seemed to be a great way to implement this rate-of-change acceleration when the knob was turned more quickly. Initially, I thought I’d use the fast, multiplexed, multichannel ADC present in a Teensy MCU, to measure the waveforms of all three BLDC motor phases. I’d use the peak amplitude of the sine wave as an indicator of velocity, and use the phase relationship of each phase’s zero-crossing to determine when an index point was hit, and in what direction.

In a word, this method was a “Fail.” The big stumbling block was that when I was turning the motor slowly (for fine adjustments), the amplitude of the sine wave was too low for the ADC to measure accurately. So I would get a lot of extraneous motion readings. The software to do any of the above measurements would have been challenging, but the low-amplitude sine wave problem was a deal-breaker.


Advertise Here

I quickly changed gears, and decided to use an LM339 quad comparator to monitor each of the three-phase signals. Figure 5 shows a schematic diagram of a project I built using the BLDC motor as a rotary encoder. The Common terminal of the BLDC motor is connected to a 1.25V reference supply, provided by a ZXRE125 voltage reference chip. Therefore, each of the three-phase windings will produce a sine wave referenced to this 1.25V “virtual ground.” Capacitors C1-C3 are 0.1µF, and they filter out any extraneous RF noise that may be present.

Figure 5 
Schematic of the project I designed using the BLDC motor as a rotary encoder. This project also included six capacitive-touch sensors for other functions.
Figure 5
Schematic of the project I designed using the BLDC motor as a rotary encoder. This project also included six capacitive-touch sensors for other functions.

Sections 1-3 of the LM339 are configured as comparators. The inverting input of each LM339 comparator is connected to the same 1.25V virtual ground as the BLDC’s Common terminal. The combination of R1 and pot R4 provide positive feedback and introduce an amount of hysteresis proportional to the setting of pot R4. The same applies to the other two phases—using R2, R3 and pots R5, R6. This hysteresis is necessary to produce a clean transition at the comparator’s output, when the zero-crossing of the BLDC’s waveform occurs. Lacking this hysteresis, when the motor is turned slowly, the resulting low-level sine waves produced by the three motor windings would produce a lot of noise at the comparator’s output, resulting in extraneous motion readings. The LM339 has an open-collector output, so it needs pull-up resistors to the Vcc supply (R9-R11).

The three LM339 comparator outputs are fed to GPIO3, 4, 5 of a Teensy LC MCU. The program sets up an interrupt on pin-change for all three of these GPIO pins. When a zero-crossing is indicated by any of the three comparator outputs, the appropriate pin-change ISR will be executed. This routine will look at the state of the other two phases, and can determine the direction from this information.

Figure 6 is a screen capture of my oscilloscope, with the purple channel connected to one of the motor windings (using AC coupling), and the yellow channel to the associated comparator output. You can see that the comparator goes positive when the motor’s sine wave output is positive. The zero-crossing of this waveform is at the voltage level marked by the purple arrow on the Y axis at the left. If you look closely, you’ll see that the comparator actually goes high at a point somewhat after the motor winding’s upward zero-crossing, and it drops low slightly after the downward zero-crossing. This is due to the hysteresis present in the comparators. I have configured the three pin-change ISRs to respond to a CHANGE in the pin, not just a RISING or FALLING EDGE. By monitoring both the leading and trailing edges of the zero-crossings (plus some extra code in the ISRs themselves), I get 12 encoder “counts” per revolution. This resolution makes it easy to fine-tune a parameter with a slow movement of the motor, but it’s also easy to give the large, heavy knob a spin, and get large adjustments to the variable quickly.

Figure 6 
The relationship between one phase of the BLDC motor's output signal (Purple) and the resulting LM339 quad comparator output (Yellow).
Figure 6
The relationship between one phase of the BLDC motor’s output signal (Purple) and the resulting LM339 quad comparator output (Yellow).

The value of pots R4, R5, and R6 should be set identically. The actual value needed will depend on the actual BLDC motor used, and on your criteria for how slow a motion you want to be detected. I believe I set my pots for about 100kΩ and got a satisfactory response.


I used this BLDC-motor-based rotary encoder in a project for my music studio. This unit performs two basic functions:

  • The BLDC-based rotary encoder is used to move the “now-time” cursor to different places in the sound recording that I am working on (scrub-wheel).
  • Several buttons are used for what are called “Transport Controls”—Stop, Play, Rewind, Record , and Undo.

I chose a Teensy LC MCU for this project. This low-cost module contains the least-sophisticated ARM MCU in the Teensy family, but it has three capabilities that are essential to the project:

  • It can act as a USB-MIDI device, which is what I use to interface it to a PC running my Digital Audio Workstation (DAW) software.
  • It can handle up to 11 capacitive-touch sensors.
  • It has 27 GPIO pins, all of which can be configured for the Interrupt-on-Change mode. Three of these GPIOs are used to monitor the outputs of the LM339 comparator that are connected to the BLDC motor.

To set up the three GPIO pins for Interrupt on change, you use the following statements:




I am using GPIO pins 2, 3, 4 for the rotary encoder. By specifying CHANGE, I get an interrupt whenever the LM339’s output goes either Low or High, thus capturing every zero-crossing.

Please refer to Listing 1 for one of the three interrupt service routines (ISRs) that I have implemented (one per BLDC motor phase).

The first statement determines whether this interrupt was a rising or falling edge. Depending on that determination, I have two discrete routines that check the state of the LM339’s output for the other two phases. Depending on which of the two phases is High at the time, I can tell in what direction the motor (rotary encoder) is being turned. I then either increment or decrement the variable “count.” This count variable is monitored in the Loop section of the code, and various MIDI messages are sent out to indicate that the scrub wheel has been turned and in what direction. The other function of this ISR is to keep track of the time at which each of the zero-crossings occurred, and store the interval between consecutive crossings. The variable ph1Interval is used for one of these phases and is derived as follows:

ph1Interval = millis()-ph1Last;

ph1Last= millis();

In the Loop section of the program, the average time between zero-crossings among all three phases, is calculated as:

intervalAvg= (ph1Interval+ ph2Interval+ph3Interval)/3;

The variable intervalAvg is basically the reciprocal of the rotation speed of the encoder. I define the variable mult as 1 + 300/intervalAvg. For each zero-crossing of the rotary encoder, I send out “mult” copies of an appropriate MIDI message, which tells the DAW software to move the “now time” cursor. This method gives an “accelerated” action of the rotary encoder, as it is spun more quickly.

The other two ISRs are similar, apart from monitoring the applicable LM339 outputs for each case.

Even with the accelerated action of the rotary encoder, as well as its ability to be spun quickly or fine-tune the position of the “now cursor,” routine editing functions of the DAW software often require an even higher dynamic range of adjustment. Therefore, the DAW software recognizes two discrete MIDI commands, with respect to moving the “now time” cursor. Each of these MIDI commands can be configured to move the “now time” cursor in increments of ticks, beats, or measures. Ticks are 1ms, whereas beats and measures depend on the tempo of the song, but are in the seconds range. Using Ticks for one command and Beats for the other makes for a quite responsive control over the “now time” cursor.

I have a Jog button on the unit, which toggles between these two time adjustment MIDI commands, plus an LED that indicates which is active.

As mentioned earlier, I decided to implement all five of the Transport Control buttons, plus the Jog button (mentioned above), using the capacitive-touch capabilities of the Teensy LC MCU. I had never used capacitive-touch sensors in place of push buttons, but was aware that many MCUs contain this capability. However, I had also read that capacitive touch sensors need some configuration or “tuning” to work properly. Luckily, I found that the capacitive touch scheme used in the Teensy LC MCU was quite well-implemented. To read a capacitive touch sensor on a given GPIO pin, you just do the following:

int touch1 = touchRead(GPIOpin);

For the physical sensor that I used, the readings that are returned are in the range of 500 to 1,000, depending on what pin is read and the length of the interconnecting wire. However, any reading for a given GPIO pin will remain constant, until you bring your finger nearby. Therefore, at power-up, I take 16 readings of each of the six GPIO pins used as touch sensors, and store the average as one element of the touchThreshold[6] array. In operation, when I call the touchRead(GPIOpin) function, if the returned value is 50 counts greater than the threshold stored in this array, I know that this particular touch sensor has been touched and act accordingly.

An internal view of the project is shown in Figure 7. You can see that I’ve embedded pennies into two pieces of white plastic to act as the six touch sensors. I milled the holes to fit the pennies, while allowing about a 2.5mm thickness of plastic to remain, to hide the pennies from view from the front panel.

Figure 7 
The inside of the project, showing the small circuit board containing the Teensy LC MCU and the LM339 quad comparator. The capacitive touchpads are pennies embedded in two white plastic sheets.
Figure 7
The inside of the project, showing the small circuit board containing the Teensy LC MCU and the LM339 quad comparator. The capacitive touchpads are pennies embedded in two white plastic sheets.

Sending MIDI commands via the Teensy LC’s built-in USB port is easy. The most important thing to note is that you must set the Tools>USB Type to “MIDI,” to tell the compiler to include the USB-MIDI functions. I use the “Serial+ MIDI” setting, since this also allows me to send diagnostic messages out the Serial port during development.

The actual MIDI commands needed for this project are sent using the following two commands:



The only other consideration is that in the Loop section of the program, you must include the statements:

while ( {

// MUST read & ignore incoming messages


This is needed to read (and ignore) any incoming MIDI messages. If you don’t do this, the input MIDI buffer will fill up and flag an error.


The completed unit is shown in Figure 8. I have a DIY CNC milling machine at home, but fabricating the knob for the BLDC-motor rotary encoder was a job suited to a metal-working lathe. My former co-worker, Mike Boutilier, a machinist at Dahousie University, fabricated this knob for me.

While this is certainly a “niche” project, it was satisfying to salvage at least one useful component from the pile of surplus 3.5” hard drives I had lying around. 

Figure 8 
 Front panel view of the completed project. The pennies are just visible; if I had used thicker plastic to obscure them, the capacitive sensing would have been degraded.
Figure 8
Front panel view of the completed project. The pennies are just visible; if I had used thicker plastic to obscure them, the capacitive sensing would have been degraded.

Teensy LC Module |
STMicroelectronics |

Code and Supporting Files


Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.

Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article
+ posts

Brian Millier runs Computer Interface Consultants. He was an instrumentation engineer in the Department of Chemistry at Dalhousie University (Halifax, NS, Canada) for 29 years.

Supporting Companies

Upcoming Events

Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

Repurposing Old Disk Drives For Rotary Encoders

by Brian Millier time to read: 14 min