Hi all.
I’ve been trying to learn to program my Pro Micro board to use as a music-controller/volume button, IE.:
single click for play/pause, double-click for next, hold for previous and rotation for volume. I’ve found some various code snippets, which I’ve tried mashing together. Unfortunately, I can’t get the rotation to register:
I’m using serial.print and keyboard.press to troubleshoot and figure out what’s happening - but so far I can only get it to register clicks on the button, and once I start rotating the encoder it stops serial.printing, but still sends the keycode on button-clicks.
Any help in figuring out what I’m missing would be appreciated!
Thanks in advance.
/* 4-Way Button: Click, Double-Click, Press+Hold, and Press+Long-Hold Test Sketch
By Jeff Saltzman
Oct. 13, 2009
To keep a physical interface as simple as possible, this sketch demonstrates generating four output events from a single push-button.
1) Click: rapid press and release
2) Double-Click: two clicks in quick succession
3) Press and Hold: holding the button down
4) Long Press and Hold: holding the button for a long time
*/
/*
Connect button between pin 9 and ground
Connect the encoder to pin 6, 7 and ground on the common pin
*/
#define ledPin1 17 // digital output pin for LED 1
#define ledPin2 16 // digital output pin for LED 2
#define ledPin3 15 // digital output pin for LED 3
#define ledPin4 14 // digital output pin for LED 4
// LED variables
boolean ledVal1 = false; // state of LED 1
boolean ledVal2 = false; // state of LED 2
boolean ledVal3 = false; // state of LED 3
boolean ledVal4 = false; // state of LED 4
// constants won't change. They're used here to
// set pin numbers:
const int buttonPin = 19; // the number of the pushbutton pin
const int encoder0PinA = 6;
const int encoder0PinB = 7;
// Variables will change:
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;
//=================================================
/*
Include Keyboard to enable sending keycodes
*/
#include <Keyboard.h>
void setup() {
Serial.begin(9600); // open the serial port at 9600 bps:
// Set button input pin
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH );
// Rotary
pinMode (encoder0PinA, INPUT);
pinMode (encoder0PinB, INPUT);
// Set LED output pins
pinMode(ledPin1, OUTPUT);
digitalWrite(ledPin1, ledVal1);
pinMode(ledPin2, OUTPUT);
digitalWrite(ledPin2, ledVal2);
pinMode(ledPin3, OUTPUT);
digitalWrite(ledPin3, ledVal3);
pinMode(ledPin4, OUTPUT);
digitalWrite(ledPin4, ledVal4);
Keyboard.begin();
}
void loop() {
// Get button event and act accordingly
int b = checkButton();
if (b == 1) clickEvent();
if (b == 2) doubleClickEvent();
if (b == 3) holdEvent();
if (b == 4) longHoldEvent();
if (b == 5) volumeUp();
if (b == 6) volumeDown();
}
//=================================================
// Events to trigger
void clickEvent() {
ledVal1 = !ledVal1;
digitalWrite(ledPin1, ledVal1);
//Keyboard.press(KEY_LEFT_CTRL);
//Keyboard.press(KEY_LEFT_ALT);
Keyboard.press('S');
delay(100);
Keyboard.releaseAll();
Serial.println ("ClickEvent");
}
void doubleClickEvent() {
ledVal2 = !ledVal2;
digitalWrite(ledPin2, ledVal2);
//Keyboard.press(KEY_LEFT_CTRL);
//Keyboard.press(KEY_LEFT_ALT);
Keyboard.press('A');
delay(100);
Keyboard.releaseAll();
Serial.println ("DoubleClickEvent");
}
void holdEvent() {
ledVal3 = !ledVal3;
digitalWrite(ledPin3, ledVal3);
//Keyboard.press(KEY_LEFT_CTRL);
//Keyboard.press(KEY_LEFT_ALT);
Keyboard.press('D');
delay(100);
Keyboard.releaseAll();
Serial.println ("HoldEvent");
}
void longHoldEvent() {
ledVal4 = !ledVal4;
digitalWrite(ledPin4, ledVal4);
}
void volumeUp() {
Keyboard.press('Q');
delay(100);
Keyboard.releaseAll();
Serial.println ("Rotate R2 Event");
}
void volumeDown() {
Keyboard.press('Z');
delay(100);
Keyboard.releaseAll();
Serial.println ("Rotate R1 Event");
}
//=================================================
// MULTI-CLICK: One Button, Multiple Events
// Button timing variables
int debounce = 20; // ms debounce p<eriod to prevent flickering when pressing or releasing the button
int DCgap = 250; // max ms between clicks for a double click event
int holdTime = 1000; // ms hold period: how long to wait for press+hold event
int longHoldTime = 3000; // ms long hold period: how long to wait for press+hold event
// Button variables
boolean buttonVal = HIGH; // value read from button
boolean buttonLast = HIGH; // buffered value of the button's previous state
boolean DCwaiting = false; // whether we're waiting for a double click (down)
boolean DConUp = false; // whether to register a double click on next release, or whether to wait and click
boolean singleOK = true; // whether it's OK to do a single click
long downTime = -1; // time the button was pressed down
long upTime = -1; // time the button was released
boolean ignoreUp = false; // whether to ignore the button release because the click+hold was triggered
boolean waitForUp = false; // when held, whether to wait for the up event
boolean holdEventPast = false; // whether or not the hold event happened already
boolean longHoldEventPast = false;// whether or not the long hold event happened already
int checkButton() {
int event = 0;
buttonVal = digitalRead(buttonPin);
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) {
encoder0Pos--;
}
lastDirection = 0;
DCwaiting = false;
singleOK = false;
DConUp = false;
} else {
if (lastDirection == 1) {
encoder0Pos++;
DCwaiting = false;
singleOK = false;
DConUp = false;
}
lastDirection = 1;
}
}
encoder0PinALast = n;
// Button pressed down
if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce)
{
downTime = millis();
ignoreUp = false;
waitForUp = false;
singleOK = true;
holdEventPast = false;
longHoldEventPast = false;
if ((millis() - upTime) < DCgap && DConUp == false && DCwaiting == true) DConUp = true;
else DConUp = false;
DCwaiting = false;
}
// Button released
else if (buttonVal == HIGH && buttonLast == LOW && (millis() - downTime) > debounce)
{
if (not ignoreUp)
{
upTime = millis();
if (DConUp == false) DCwaiting = true;
else
{
event = 2;
DConUp = false;
DCwaiting = false;
singleOK = false;
}
}
}
// Test for normal click event: DCgap expired
if ( buttonVal == HIGH && (millis() - upTime) >= DCgap && DCwaiting == true && DConUp == false && singleOK == true && event != 2)
{
event = 1;
DCwaiting = false;
}
// Test for hold
if (buttonVal == LOW && (millis() - downTime) >= holdTime) {
// Trigger "normal" hold
if (not holdEventPast)
{
event = 3;
waitForUp = true;
ignoreUp = true;
DConUp = false;
DCwaiting = false;
//downTime = millis();
holdEventPast = true;
}
// Trigger "long" hold
if ((millis() - downTime) >= longHoldTime)
{
if (not longHoldEventPast)
{
event = 4;
longHoldEventPast = true;
}
}
}
if ( buttonVal == HIGH && encoderLastValue != encoder0Pos) {
//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
event = 5;
DCwaiting = false;
singleOK = false;
DConUp = false;
//Serial.println ("Volume down");
}
else if (encoderLastValue < encoder0Pos) {
//volume up
event = 6;
DCwaiting = false;
singleOK = false;
DConUp = false;
//Serial.println ("Volume up");
}
}
buttonLast = buttonVal;
encoderLastValue=encoder0Pos;
return event;
}