Kristian Blåsol
Published © GPL3+

Rotary Encoder as a Multimedia Controller

With a simple rotary encoder and an Arduino Pro-Micro you can control most media players with play, pause, next, prev, volup, voldown.

BeginnerWork in progress4 hours4,981
Rotary Encoder as a Multimedia Controller

Things used in this project

Hardware components

Pro Micro - 5V/16MHz
SparkFun Pro Micro - 5V/16MHz
As many knockoffs claim to be Arduino Pro Micro, you need to make sure that it is an Arduino with an Atmel AtMega32U4
Generic Rotary Encoder


Read more


Simple schematics of how to attach the Rotary Encoder to the Pro-Micro

Follow this simple schematics or even simpler, watch the video of it being done. ;)


Arduino Code for Rotary Encoder as a Multimedia Controller

The code to upload to the Arduino Pro-Micro to make it into a Multimedia Keyboard USB-client
This code uses the example Debounce code, 
and the Example code for Rotary Encoders from the Arduino Playground
and also the Arduino Core changes done in Anything Arduino episode 5:
You can watch the build video here:

Connect button between pin 9 and ground
Connect the encoder to pin 6, 7 and ground on the common pin

// constants won't change. They're used here to 
// set pin numbers:
const int buttonPin = 9;    // the number of the pushbutton pin
const int ledPin = 13;      // the number of the LED pin
const int encoder0PinA = 6;
const int encoder0PinB = 7;

// Variables will change:
int bstate = 0; //0 is paused, 1 is playing
int ledState = HIGH;         // the current state of the output pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers
long longpressDelay = 500;    // If we hold it longer than 500ms then it is a long press.
int val;
int encoder0Pos = 0;
int encoder0PinALast = LOW;
long encoderLastValue=0;
//int lastCommand; //0=volup 1=voldown 2=next, 3=prev
int lastDirection; //0=--, 1=++
int n = LOW;
int reading;

void setup() {
  pinMode (encoder0PinA, INPUT_PULLUP);
  pinMode (encoder0PinB, INPUT_PULLUP);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, ledState);
  //Serial.begin (9600);

void loop() {
  n = digitalRead(encoder0PinA);
  if ((encoder0PinALast == LOW) && (n == HIGH)) {
    if (digitalRead(encoder0PinB) == LOW) { 
      //to make a double check we are going the right direction.
      if (lastDirection == 0) {
      lastDirection = 0;
    } else {
      if (lastDirection == 1) {
      lastDirection = 1;
  encoder0PinALast = n;
  //We make the vol up/down descision with the lastButttonState.
  if (lastButtonState == HIGH) { //((millis() - lastDebounceTime) < longpressDelay) 
    //nothing happens with the button so if the rotary encoder moves now, it is volume. 
    //Well actually this code runs also if the button isnt released, so we need to make sure it isnt a longpress aswell... which we do two rows up.
    if (encoderLastValue > encoder0Pos) {
      //volume down
      //Serial.println ("Volume down");
    else if (encoderLastValue < encoder0Pos) {
      //volume up
      //Serial.println ("Volume up");
  //We must make the next/prev descisions before the button press if.
  if (((millis() - lastDebounceTime) > longpressDelay) && (lastButtonState == LOW)) {
    //So if the button has been held longer than 500ms, then if the rotary encoder is turned it is next/prev
    if (encoderLastValue > encoder0Pos) {
      //Serial.println ("Previous");
    else if (encoderLastValue < encoder0Pos) {
      //Serial.println ("Next");
  reading = digitalRead(buttonPin);
  if (reading != lastButtonState) {
    //The button is (probalby) pressed or released
    if (reading==LOW) {
      //if it indeed is pressed then:
      //Set the start time
      lastDebounceTime = millis();
    } else {
       //if it is high the button was released, check how long time it was pressed...
       if ((millis() - lastDebounceTime) > longpressDelay) {
         //the button was pressed 500ms or longer
         //we cant handle next/prev here because here the button is already released again, se code above for next/prev...
         //Serial.println ("Long press");
       else if ((millis() - lastDebounceTime) > debounceDelay) {
          //it was only pressed a short while, but more than 50ms so it is a play/pause press
          //Serial.println ("Play/Pause");
          if (bstate == 0) {
             bstate = 1;
           } else {
             bstate = 0;
       } else {
         //Serial.println (millis()-lastDebounceTime);
  // save the reading.  Next time through the loop,
  // it'll be the lastButtonState:
  lastButtonState = reading;



Kristian Blåsol

Kristian Blåsol

5 projects • 27 followers
Maker, Dad, Geek, Musician, System Engineer, Android Tinkerer... (Not in that specific order)