Blog Archive

Sunday 14 September 2014

Arduino Leonardo (ATMega32U4) based USB Volume control with mute - using a rotary encoder





This is a quick project I did with a clone Arduino Pro Micro (which I bought off eBay for peanuts). 


What you will need:


  • 1 x Arduino Leonardo or Pro Micro (any Arduino with a ATMega32U4 should work)
  • 1 x 10K resistor
  • 1 x rotary encoder with push button (I got mine from Adafruit  - http://www.adafruit.com/products/377 )
  • A couple of wires and a breadboard


Software libraries required:

You will need two libraries, one for the rotary encoder and one to produce the multimedia Volume Up/Down/Mute keys via USB

http://www.pjrc.com/teensy/td_libs_Encoder.html

https://github.com/NicoHood/HID

You will need to figure out how to install libraries within the Arduino IDE if you haven't done this before. Google is your friend.


Wiring:

I used pins digital pins 2 and 3 on the Arduino Pro Micro for the rotary encoder as they offer the best performance (interrupt driven). Try others if you like.

I used digital pin 10 for the push button part of the rotary encoder.


The rotary encoder has 5 pins. Three on one side and two on the other side. The group of 3 pins is used for the encoder itself. The two pins on the other side are for the push button switch inside the encoder. Wire the middle pin of the three (Pin 4 in diagram) to GND. Wire pins 3 and 5 of the encoder to digital pins 2 and 3 of the Arduino. If you like, you can swap pins 2 and 3 to make the encoder work in reverse (anti clockwise).

Connect pin 1 of the encoder to VCC on the Arduino. Wire pin 2 of the encoder to digital pin 10.

The 10K resistor is then connected from digital pin 10 to GND. This pulls the pin low.  Google more about pull-up/down resistors if you want to know more.












Wiring Summary:

Arduino VCC - Rotary Encoder Pin 1
Arduino Pin 10 - Rotary Encoder Pin 2
Arduino Pin 2 - Rotary Encoder Pin3
Arduino Pin 3 - Rotary Encoder Pin 5
Arduino GND - Rotary Encoder Pin 4

10K Resistor between Arduino Pin 10 and GND




The Code:

Just copy and paste the code below into an Arduino Sketch. It should work provided you have the Encoder and HID libraries installed.






// A small Arduino Leonardo based program which uses a rotary encoder to control the volume of a PC.
// Assumes the use of a rotary encoder with a push button, to allow muting.
// I used a clone Arduino Pro Micro (ATMega32U4), which are available for peanuts on eBay. 
//
// Use the code as you please - loiphin :)



#include <Encoder.h>
// The rotary encoder library http://www.pjrc.com/teensy/td_libs_Encoder.html

#include<HID.h>
// The HID library which produces the multimedia keys (Volume UP,DOWN and MUTE)  https://github.com/NicoHood/HID

int accel = 2;
// This is an acceleration factor. Use between 1 and 8 to suit how quickly the volume goes up or down.

int mutePin = 10;
// The digital pin to which the rotary encoder push button is connected too. Assumes that the pin has a pull-down resistor.

Encoder myEnc(2, 3);
// These are the pins to which the rotary encoder is connected.
// Pins 2,3 are the interrupt pins on a Leonardo/Uno, which give best performance with a rotary encoder. 
// Use other pins if you wish, but performance may suffer.
// Avoid using pins that have LED's attached.

long oldPosition  = -999;
int buttonState;
int lastButtonState = LOW;
long lastDebounceTime = 0;
long debounceDelay = 80;  // Button debounce time in millseconds. Increase if mute button doesnt work properly.



void setup() {
  pinMode(mutePin, INPUT);
  
  Serial.begin(9600);
  Serial.println("Debug Output:");
  
  Media.begin();
  
}



void loop() {
  long newPosition = myEnc.read() / accel;
  if (newPosition != oldPosition) {
    
    if(newPosition > oldPosition) {
      Media.write(MEDIA_VOLUME_UP);
    }
    
    if(oldPosition > newPosition) {
     Media.write(MEDIA_VOLUME_DOWN);
    }
    
    oldPosition = newPosition;
    Serial.println(newPosition);
    
  }
  
  // read the state of the switch into a local variable:
  int reading = digitalRead(mutePin);

  // check to see if you just pressed the button 
  // (i.e. the input went from LOW to HIGH),  and you've waited 
  // long enough since the last press to ignore any noise:  

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  } 
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // Toggle MUTE if digital pin reads HIGH
      if (buttonState == HIGH) {
        Media.write(MEDIA_VOLUME_MUTE);
      }
    }
  }
  
  // save the reading.  Next time through the loop,
  // it'll be the lastButtonState:
  lastButtonState = reading;
}



Enjoy! - Leave a comment if you are stuck and I will try to help





8 comments:

ThisIsMyMuse said...

The adruino IDE is telling me "'media' was not declared in this scope".

Any idea how to fix that?

Crispip said...

I'm getting fatal error: HID.h: No such file or directory.

NicHood's pack does not have HID.h included, only .cpp. What am I doing wrong?

Otherwise instructions are clear, really helpful for my first Arduino project.

Jani V. said...

ThisIsMyMuse: The problem is that due the new version of HID library, they have changed the name of object.
Try Consumer.begin();

Another solution is download older version and use that.

Crispip: Probaply your files are at wrong directory... Check out the manual where HID library files has to be located.

Osmi said...

It is working :)
I had to use 1.8 The Hoodloader update + easy installation version of HID and Arduino IDE 1.5.7 to make it work!
Thank you very much :D

ak33 said...

when i compile I get this mesaages
sketch_nov18a:41: error: 'Media' was not declared in this scope

Media.begin();

^

C:\Users\ADICON~1\AppData\Local\Temp\arduino_modified_sketch_345316\sketch_nov18a.ino: In function 'void loop()':

sketch_nov18a:52: error: 'Media' was not declared in this scope

Media.write(MEDIA_VOLUME_UP);

^

sketch_nov18a:52: error: 'MEDIA_VOLUME_UP' was not declared in this scope

Media.write(MEDIA_VOLUME_UP);

^

sketch_nov18a:56: error: 'Media' was not declared in this scope

Media.write(MEDIA_VOLUME_DOWN);

^

sketch_nov18a:56: error: 'MEDIA_VOLUME_DOWN' was not declared in this scope

Media.write(MEDIA_VOLUME_DOWN);

^

sketch_nov18a:87: error: 'Media' was not declared in this scope

Media.write(MEDIA_VOLUME_MUTE);

^

sketch_nov18a:87: error: 'MEDIA_VOLUME_MUTE' was not declared in this scope

Media.write(MEDIA_VOLUME_MUTE);


exit status 1
'Media' was not declared in this scope



Marchi said...
This comment has been removed by the author.
Anonymous said...

For those who have the same problem: You need to include "HID-Project.h" instead of "HID.h", and to replace "media" with "consumer". It should compile just fine.

Unknown said...

Thanks for the info it will work for me..