Introduction

The Arduino has pull-up resistors connected to each input. This allows the Arduino to sense when a circuit is open or closed - when a button is pressed or not pressed. This information can be accessed programmatically and can be used to process information.

Programming the Arduino is based on the C programming language. This is useful as almost every tool can carry over and syntax remains the same. The one difference are the functions: setup and loop.

The functions are Arduino built-in and provide the main control structure. When the Arduino is powered on, it executes everything in the setup function then proceeds to execute loop. Loop executes continuously until it is stopped programmatically or the Arduino loses power. This is where the main processing will take place.

Inputs and Outputs

Each button or encoder will act as an input. When a button changes states (pressed or not pressed) the Arduino will process this information and send an output to the computer as a keyboard or mouse command. It may also turn on and off LEDs based on the button state.

In the main loop, for each button, the Arduino checks the state of the button. If the button is pressed, then send a signal to the computer as pressed keyboard key. It should also turn on the LED for the corresponding button.

When the button is not pressed, the LED should be turned off and the keyboard key should be released.

Firmware

The controller firmware is a modular Arduino sketch. Open the code/sdvx sketch folder in the Arduino IDE. Do not compile the legacy sdvx.ino.ino file.

The sketch is organized into these files:

  • sdvx.ino initializes the USB HID interfaces and coordinates the input subsystems.
  • config.h contains pin assignments, key mappings, timing, and encoder tuning parameters.
  • buttons.cpp debounces the seven switches and sends keyboard events only when a switch changes state.
  • encoders.cpp reads both quadrature encoders, applies filtering, and maps movement to the mouse axes.
  • leds.cpp controls button lighting and the non-blocking idle animation.

The firmware requires these libraries:

  • Mouse.h
  • Keyboard.h
  • Encoder.h
  • SoftPWM.h

Download the source

Download the refactored sketch

Encoder configuration

The original design used 600 pulses-per-revolution incremental encoders. With the Arduino Encoder library using 4x quadrature decoding, a full revolution produces approximately 2,400 counts:

600 pulses/revolution * 4 state changes/pulse = 2,400 counts/revolution

The default settings in config.h are:

#define ENC_PULSE_DIVIDER    4
#define ENC_HYSTERESIS_TICKS 2
#define ENC_DEADZONE_TICKS   1

ENC_PULSE_DIVIDER groups four raw counts into one logical movement step. This reduces the effect of small shaft movements caused by knob weight, settling, or tilting the controller. The hysteresis setting rejects rapid direction chatter at an optical boundary, while the deadzone setting suppresses isolated low-level movement.

The filtering is designed for rhythm-game response time: intentional knob movement is accumulated and sent immediately, without a smoothing delay. If your controller is still too sensitive, increase ENC_PULSE_DIVIDER to 5 or 6. If a knob moves in the wrong direction, change its ENC_DIR_L or ENC_DIR_R setting from 1 to -1.

Button behavior

The START button is a safety latch. After connecting the controller to the computer, press START once to enable keyboard and mouse output. The other buttons are debounced and send keyboard press/release events on state transitions. LEDs follow the button state, and the START LED pulses while the controller is waiting to be enabled.

Programming the Arduino

To program the Arduino:

(1) Open the Arduino IDE

(2) Select your Arduino

(3) Open the file for the source code.

(4) Compile and upload

Using Your Controller

Each button is mapped to a different keyboard input. The knobs are mapped to mouse X and Y movement. This allows the controller to be used on virtually any computer without having to find drivers.

Press the start button to allow the controller to begin sending information to your computer. This acts as a failsafe should wiring or programming be incorrect. If the Arduino were to send key presses immediately, it could potentially send inputs continuously, blocking the ability to work on your computer and reprogram the Arduino.

Troubleshooting

Key presses aren’t received

Double check all wiring and inputs in the code.

It caught on fire

Don’t cross bare wires.

End

At this point, the controller should be functionally complete. For some, this might be all that’s needed. For others, the controller working might mean more features are still needed. The firmware includes configurable encoder filtering, debounced buttons, reactive LEDs, and a non-blocking idle animation. Further optional features include artwork, physical encoder dampeners, and addressable LED strips.

Make the controller your own, and enjoy your work. Don’t sweat the small mistakes. Don’t forget, the process was fun too!

Thanks for reading!