Hi. I've built this button box:
I'm using a knock off board that although it has the layout of a Pro Micro, it's detected by the computer as a Leonardo. It does have a 32U4 chip, so it's properly detected as a game device by windows.
I couldn't think of a better way to wire it up than using an analog pin for the encoder pushbuttons, with different resistors, and the same for the bottom pushbuttons.
The rotary encoder function, switches and leds are independently wired up to the rest of the available pins (including TX and RX).
This is the schematic (sorry for being so shabby):
And this is the code:
#include <Joystick.h>
#define ENABLE_PULLUPS
#define NUMROTARIES 4
int led1 = 15;
int led2 = 14;
int led3 = 16;
int led4 = 10;
int switch1 = A0;
int switch2 = A1;
int switch3 = 9;
int switch4 = 8;
int encoders = A2;
int buttons = A3;
struct rotariesdef {
byte pin1;
byte pin2;
int ccwchar;
int cwchar;
volatile unsigned char state;
};
rotariesdef rotaries[NUMROTARIES] {
{0, 1, 0, 1, 0},
{3, 2, 3, 4, 0},
{5, 4, 6, 7, 0},
{7, 6, 9, 10, 0},
};
#define DIR_CCW 0x10
#define DIR_CW 0x20
#define R_START 0x0
#ifdef HALF_STEP
#define R_CCW_BEGIN 0x1
#define R_CW_BEGIN 0x2
#define R_START_M 0x3
#define R_CW_BEGIN_M 0x4
#define R_CCW_BEGIN_M 0x5
const unsigned char ttable[6][4] = {
// R_START (00)
{R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START},
// R_CCW_BEGIN
{R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START},
// R_CW_BEGIN
{R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START},
// R_START_M (11)
{R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START},
// R_CW_BEGIN_M
{R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW},
// R_CCW_BEGIN_M
{R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW},
};
#else
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6
const unsigned char ttable[7][4] = {
// R_START
{R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START},
// R_CW_FINAL
{R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW},
// R_CW_BEGIN
{R_CW_NEXT, R_CW_BEGIN, R_START, R_START},
// R_CW_NEXT
{R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START},
// R_CCW_BEGIN
{R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START},
// R_CCW_FINAL
{R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW},
// R_CCW_NEXT
{R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};
#endif
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
JOYSTICK_TYPE_JOYSTICK, 32, 0,
false, false, false, false, false, false,
false, false, false, false, false);
void setup() {
Joystick.begin();
rotary_init();
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
pinMode(led4, OUTPUT);
pinMode(switch1, INPUT_PULLUP);
pinMode(switch2, INPUT_PULLUP);
pinMode(switch3, INPUT_PULLUP);
pinMode(switch4, INPUT_PULLUP);
}
void loop() {
CheckAllEncoders();
CheckAllSwitches();
CheckAllEncoderButtons();
CheckAllButtons();
}
void rotary_init() {
for (int i = 0; i < NUMROTARIES; i++) {
pinMode(rotaries[i].pin1, INPUT);
pinMode(rotaries[i].pin2, INPUT);
#ifdef ENABLE_PULLUPS
digitalWrite(rotaries[i].pin1, HIGH);
digitalWrite(rotaries[i].pin2, HIGH);
#endif
}
}
unsigned char rotary_process(int _i) {
unsigned char pinstate = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1);
rotaries[_i].state = ttable[rotaries[_i].state & 0xf][pinstate];
return (rotaries[_i].state & 0x30);
}
void CheckAllEncoderButtons(void) {
if ((analogRead(encoders) < 950) && (analogRead(encoders) > 900)) {
Joystick.setButton(2, 1);
}
else {
Joystick.setButton(2, 0);
}
if ((analogRead(encoders) < 870) && (analogRead(encoders) > 830)) {
Joystick.setButton(5, 1);
}
else {
Joystick.setButton(5, 0);
}
if ((analogRead(encoders) < 700) && (analogRead(encoders) > 650)) {
Joystick.setButton(8, 1);
}
else {
Joystick.setButton(8, 0);
}
if ((analogRead(encoders) < 530) && (analogRead(encoders) > 490)) {
Joystick.setButton(11, 1);
}
else {
Joystick.setButton(11, 0);
}
}
void CheckAllButtons(void) {
if ((analogRead(buttons) < 600) && (analogRead(buttons) > 450)) {
Joystick.setButton(16, 1);
}
else {
Joystick.setButton(16, 0);
}
if ((analogRead(buttons) < 700) && (analogRead(buttons) > 600)) {
Joystick.setButton(17, 1);
}
else {
Joystick.setButton(17, 0);
}
if ((analogRead(buttons) < 900) && (analogRead(buttons) > 800)) {
Joystick.setButton(18, 1);
}
else {
Joystick.setButton(18, 0);
}
if ((analogRead(buttons) < 950) && (analogRead(buttons) > 900)) {
Joystick.setButton(19, 1);
}
else {
Joystick.setButton(19, 0);
}
if ((analogRead(buttons) < 1000) && (analogRead(buttons) > 950)) {
Joystick.setButton(20, 1);
}
else {
Joystick.setButton(20, 0);
}
if (analogRead(buttons) > 1000) {
Joystick.setButton(21, 1);
}
else {
Joystick.setButton(21, 0);
}
}
void CheckAllSwitches(void) {
if (digitalRead(switch1) == 0) {
digitalWrite(led1, 1);
Joystick.setButton(12, 1);
} else {
digitalWrite(led1, 0);
Joystick.setButton(12, 0);
}
if (digitalRead(switch2) == 0) {
digitalWrite(led2, 1);
Joystick.setButton(13, 1);
} else {
digitalWrite(led2, 0);
Joystick.setButton(13, 0);
}
if (digitalRead(switch3) == 0) {
digitalWrite(led3, 1);
Joystick.setButton(14, 1);
} else {
digitalWrite(led3, 0);
Joystick.setButton(14, 0);
}
if (digitalRead(switch4) == 0) {
digitalWrite(led4, 1);
Joystick.setButton(15, 1);
} else {
digitalWrite(led4, 0);
Joystick.setButton(15, 0);
}
}
void CheckAllEncoders(void) {
for (int i = 0; i < NUMROTARIES; i++) {
unsigned char result = rotary_process(i);
if (result == DIR_CCW) {
Joystick.setButton(rotaries[i].ccwchar, 1); delay(50); Joystick.setButton(rotaries[i].ccwchar, 0);
};
if (result == DIR_CW) {
Joystick.setButton(rotaries[i].cwchar, 1); delay(50); Joystick.setButton(rotaries[i].cwchar, 0);
};
}
}
The functions in the loop control each "section", and work as expected independently. However, if I leave all the functions on, the rotary pulses are not detected. If then comment one of the functions (obviously not the rotary one), then it detects the pulses, but not very smoothly. If I comment two of the functions, they work pretty much ok.
I think the issue might be in the way the rotary encoders are read (I copied this function from another project), or the processing power of the chip, one of the two.
Can you think of another/better way to read input from the rotary encoders?
Thank you