|
Smart Knob Controller
|
The Smart Knob Controller is an exploration of how brushless DC motors can breathe new life into one of the most ubiquitous input devices—the humble knob—by enabling dynamic, software-configurable feedback. The result is a clean, tactile, dual-degree-of-freedom controller that feels like multiple knobs in one: snappy detents, elastic spring force, or a completely free-spinning dial. The only limit is your creativity.

This project was inspired by Scott Bezek's Smart Knob, an open-source design that garnered a strong following online. While many have used Bezek’s design as a computer input device, this project set out to explore how that same concept could enhance hardware control—specifically as a unique yet intuitive interface for my partner’s custom differential drive robot.
Serving as a pseudo-capstone to my undergraduate mechatronics studies at Cal Poly SLO, the Smart Knob Controller blends mechanical design, electrical hardware, embedded firmware, and control system theory—all custom designed from the ground up around an STM32 microcontroller.
The Smart-Knob is centered—literally and figuratively—around a custom PCB designed in KiCAD 9.0 and manufactured by JLCPCB, featuring the STM32F411CEU6 microcontroller. Code is flashed to the microcontroller through SWD using an ST-Link.

At the heart of the board is a mounting surface for the GM3506 brushless gimbal motor, surrounded by cutouts that allow the PCB to flex, enabling the second degree-of-freedom. The PCB is focused on two centrally located sensors that capture each axis of input.
On the top center, the AS5047P magnetic rotary encoder captures the absolute angular position of the motor through a diametric encoder magnet on the center shaft of the motor. The position is read using SPI and offers 14-bit resolution. On the underside, directly opposite, the A1304 linear hall-effect sensor outputs an analog signal based on its distance away from an axial magnet fixed in the case.
The interaction between these two neodymium magnets was of chief concern when designing the PCB. Through clever software, detailed in the Firmware section, each was able to operate largely unaffected by the other.
The largest IC on the board is the DRV8313 motor driver, which receives three PWM and internally handles amplification to 12 volts as well as the MOSFET switching required to deliver properly timed signals to the three phases of the BLDC motor. The driver was selected to align with the custom commutation method devised to control the motor. Motor commutation is discussed at length in the subsequent Firmware section.
Powering the device through USB-C was made a priority to ensure user-friendly operation and reflect a polished product that could be envisioned in a consumer’s home. To achieve this, the CYPD3177 USB-C Power Delivery (PD) chip was used to negotiate 12V power at 1A for the motor driver. A TLV761-series LDO regulator. Power calculations confirmed the LDO would operate without excessive temperature rise.
The only component not integrated into the PCB is an HC-05 Bluetooth module, which is connected through UART and adhered to the bottom of the PCB. A future improvement to the PCB would certainly involve an MCU with integrated BLE.
The device is housed in a custom 3D-printed case that cleanly conceals all electronics and mounting hardware.

The case exposes the USB-C port for power and features a small hole for a power indication LED. The user button is hidden to preserve the clean appearance of the device but can still be actuated by lightly pressing anywhere in a large region around the LED.
The design of the case is only disrupted by features on the bottom necessary for prototyping. A small hole in the center allows the press-fit magnet for the hall-effect sensor to be removed, and another cutout provides access to an 8-pin header for flashing and debugging through the ST-link. These features can be easily omitted from the final case design if the product were to be fully developed.
Inside, a small spacer gives the motor clearance to mount above the encoder and leaves a passthrough for the motor’s JST connector, which routes through the board to the connector underneath.
The firmware for the Smart Knob Controller was developed using STM32CubeIDE in C++, organized around a finite state machine (FSM) architecture that runs at 100 Hz. Motor commutation is handled via timer interrupts firing at 20 kHz, enabling smooth and responsive control of the BLDC motor.
The codebase is built on STM32 HAL libraries, wrapped in custom C++ classes for readability and modularity. The FSM uses the user button to switch between modes that coordinate sensor readings, control logic, haptic feedback, and sending robot control commands.
At demo time, the controller supported two primary modes:
Other configurations were tested during development, but these two offered the cleanest and most intuitive user experience. With more time, an additional mode was planned where the knob’s angle in free-spinning mode would correspond directly to the wheel angle—essentially turning the device into a high-fidelity, ball-mouse-style input.
At the heart of the Smart Knob Controller lies a custom implementation of closed-loop sinusoidal commutation—a stripped-down but highly effective take on Field-Oriented Control (FOC).
A 3-phase BLDC motor requires three voltage signals offset by 120°, applied in a coordinated sequence to produce continuous torque. Conventional block commutation steps through these phases abruptly, but FOC allows for much finer control by generating sinusoidal voltages aligned with the rotor’s magnetic field. This results in quieter, smoother, and more precise motion—ideal for the tactile haptics demanded by this project.
While traditional FOC uses current sensors for precise torque control, this implementation forgoes current feedback in favor of simplicity and speed. It’s technically "open-loop" in that sense but remains closed-loop from a position control standpoint, using real-time feedback from the encoder to drive the motor with smooth, continuous sinusoidal signals.
After a deep dive into academic papers and open-source implementations, I filtered the core principles of FOC into a custom control scheme that suited this hardware. The structure is outlined in the block diagram below.

The control system accepts an input called Effort—a percentage of the motor’s maximum speed, ranging from -100% to 100%. This effort value is converted into a voltage in the direct–quadrature (DQ) reference frame, where voltage in the q-axis produces maximum torque, and voltage in the d-axis produces none. As such, the input voltage vector is defined as:
\[\begin{bmatrix} V_d \\ V_q \end{bmatrix} = \begin{bmatrix} 0 \\ \frac{V_{max}}{100} \end{bmatrix} \times \text{Effort} \]
This DQ voltage is then transformed into the motor’s native abc reference frame, corresponding to the three physical phases. This transformation is a modified combination of the well-known Inverse Park and Inverse Clarke transforms—the core of sinusoidal commutation. The resulting equation is:
\[\begin{bmatrix} v_a \\ v_b \\ v_c \end{bmatrix} = \begin{bmatrix} \cos(\theta_e) & -\sin(\theta_e) \\ \cos(\theta_e - 120^\circ) & -\sin(\theta_e - 120^\circ) \\ \cos(\theta_e + 120^\circ) & -\sin(\theta_e + 120^\circ) \end{bmatrix} \begin{bmatrix} v_d \\ v_q \end{bmatrix} \]
Here, the electrical angle is calculated by scaling the encoder’s mechanical angle by the number of magnetic pole pairs in the motor:
\[\theta_e = \text{# pole pairs} \times \theta_m \]
This scaling ensures that the generated voltage vectors remain properly aligned with the rotor’s position, even as it rotates through multiple magnetic cycles.
Finally, the computed phase voltages are converted to PWM duty cycles. Since the STM32 can only output digital pulses, each phase is modulated with high-frequency PWM whose duty cycle matches the desired voltage. The motor’s inductance naturally smooths these signals, producing an average voltage that closely follows the target sine wave.
Observing the PWM outputs on an oscilloscope reveals their mesmerizing behavior:

When averaged over time, the signals form clean, 120°-offset sinusoidal waveforms—the hallmark of smooth, precise BLDC control:

To ensure this system ran fast enough on the STM32F411, a precomputed lookup table (LUT) was used for the cosine and sine values, indexed by the rotor angle. This eliminated the need for expensive real-time floating point operations, allowing commutation to run comfortably within a 20 kHz interrupt corresponding to the PWM timer.
The Smart Knob Controller supports two distinct haptic behaviors: an elastic mode, where the knob feels like it’s springing back to center, and a free-spinning mode, where it rotates effortlessly with virtually no resistance.
In elastic mode, the controller generates a restoring torque based on a non-wrapping angular position, allowing the spring-like behavior to persist over multiple full revolutions. This non-wrapping reference makes it feel like the knob has a continuous torsion spring anchored at a moving center point. A proportional controller computes the restoring torque, which is then applied using the sinusoidal commutation system, making the response smooth and natural.
In free-spinning mode, the system continuously measures the angular velocity of the motor and applies a voltage based on a linear equation derived from motor characterization. This feed-forward voltage matches the back-EMF profile of the motor, effectively canceling out drag and allowing the knob to spin with even less resistance than it would when unpowered. The result is uncannily smooth, a feeling that mimics the sensation of a bearing.
While the two magnets for the different sensors were able to coexist on the same axis, they didn’t operate entirely independently. During testing, the Hall sensor output showed a position-dependent offset caused by the rotating field of the encoder magnet. To correct for this, the ADC value was recorded across a full rotation of the motor with no axial force applied. The result was a clean sinusoidal trend.

This data was fit with a sinusoidal regression, which produced an equation used to define a correction baseline for the Hall-effect signal. This correction curve was stored in a lookup table to avoid runtime trigonometric calculations. Every time an ADC reading was taken, the system referenced the current motor angle and subtracted the corresponding offset from the raw signal—yielding a consistent reading of the axial displacement regardless of the knobs angular position.
That said, the resolution wasn’t ideal. The PCB’s cutouts failed to provide as much flexibility as expected, meaning small axial movements didn’t translate to large changes in ADC values. As a result, the second degree-of-freedom control was not as high-fidelity as desired. Future revisions could benefit from a more flexible mechanical design leading to higher-resolution ADC sampling, and possibly more advanced filtering techniques to better extract the Hall-effect signal from noise.
You can find the detailed class documentation in the Class List.
Refer to the Firmware Overview to see a description of Demo functionality.