Arduino button box code help

Hello hive mind.

I have built a button box for my sim racing rig, it has a button matrix, rotary encoders and rotary switches. Controlled via a Arduino Pro Micro.

I have the code sorted for most of it. I have everything working except the rotary switches.

I can’t seem to figure out the code for them. I have 3 of them, the pins are 5v, signal and ground. I have the signal wires soldered to pins 4,5,6 on the Arduino.

Can someone help me out here please?

The rotary switches are these Gravity: Analog Rotary Switch Module For Arduino - DFRobot

Below is the code I have in Arduino IDE

#include <Keypad.h>
#include <Joystick.h>

//DEFINITIONS
#define ENABLE_PULLUPS
#define NUMROTARIES 2 //replace "?" with number of rotary encoders you are using
#define NUMBUTTONS 15 //replace "?"with number of buttong you are using
#define NUMROWS 3 //replace "?" with number of rows you have
#define NUMCOLS 5 //replace "?" with number of columns you have

//BUTTON MATRIX
//first change number of rows and columns to match your button matrix, 
//then replace all "?" with numbers (starting from 0)
byte buttons[NUMROWS][NUMCOLS] = {
  {0,1,2,3,4},
  {5,6,7,8,9},
  {10,11,12,13,14}

 
 
};

struct rotariesdef {
  byte pin1;
  byte pin2;
  int ccwchar;
  int cwchar;
  volatile unsigned char state;
};

//ROTARY ENCODERS
//each line controls a different rotary encoder
//the first two numbers refer to the pins the encoder is connected to 
//the second two are the buttons each click of the encoder wil press 
//do NOT exceed 31 for the final button number
rotariesdef rotaries[NUMROTARIES] {
  {0,1,22,23,0}, //rotary 1
  {2,3,24,25,0} //rotary 2



};

#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

//BUTTON MATRIX PART 2
byte rowPins[NUMROWS] = {A3,A2,10}; //change "?" to the pins the rows of your button matrix are connected to
byte colPins[NUMCOLS] = {A1,A0,15,14,16}; //change "?" to the pins the rows of your button matrix are connected to

Keypad buttbx = Keypad( makeKeymap(buttons), rowPins, colPins, NUMROWS, NUMCOLS);

//JOYSTICK SETTINGS
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
  JOYSTICK_TYPE_JOYSTICK,
  32, //number of buttons
  0, //number of hat switches
  //Set as many axis to "true" as you have potentiometers for
  false, // y axis
  false, // x axis
  false, // z axis
  false, // rx axis
  false, // ry axis
  false, // rz axis
  false, // rudder
  false, // throttle
  false, // accelerator
  false, // brake
  false); // steering wheel

const int numReadings = 20;
 
int readings[numReadings];      // the readings from the analog input
int index = 0;              // the index of the current reading
int total = 0;                  // the running total
int currentOutputLevel = 0;

//POTENTIOMETERS PART 1
//add all the axis' which are enabled above
int zAxis_ = 0;
int RxAxis_ = 0; 
int RyAxis_ = 0;  

               
//POTENTIOMETERS  PART 2
//Which pins are your potentiometers connected to?
int potentiometerPin1 = 4; //Change "?" to the pin your potentiometer is connected to
int potentiometerPin2 = 5;
int potentiometerPin3 = 6;

const bool initAutoSendState = true;


void setup() {
  Joystick.begin();
  rotary_init();
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}

void loop() {

  CheckAllEncoders();
  CheckAllButtons();
  CheckAllPotentiometers();
 
}

//POTENTIOMETERS PART 3
//change the details to match teh details above for each potentiometer you are using
void CheckAllPotentiometers(){
                           
  //potentiometer 1
  currentOutputLevel = getAverageOutput(potentiometerPin1);
  zAxis_ = map(currentOutputLevel,0,1023,0,255);
  Joystick.setZAxis(zAxis_); 

  //potentiometer 2
  currentOutputLevel = getAverageOutput(potentiometerPin2);
  RxAxis_ = map(currentOutputLevel,0,1023,0,255);
  Joystick.setRxAxis(RxAxis_);

  //potentiometer 3
  currentOutputLevel = getAverageOutput(potentiometerPin2);
  RyAxis_ = map(currentOutputLevel,0,1023,0,255);
  Joystick.setRxAxis(RyAxis_);

}

int getAverageOutput(int pinToRead){
  index = 0;
  total = 0; 
 
  while (index < numReadings){
    readings[index] = analogRead(pinToRead);
    total = total + readings[index];
    index = index + 1;
    //delay (1);
  }
  return total / numReadings;
}


void CheckAllButtons(void) {
      if (buttbx.getKeys())
    {
       for (int i=0; i<LIST_MAX; i++)   
        {
           if ( buttbx.key[i].stateChanged )   
            {
            switch (buttbx.key[i].kstate) { 
                    case PRESSED:
                    case HOLD:
                              Joystick.setButton(buttbx.key[i].kchar, 1);
                              break;
                    case RELEASED:
                    case IDLE:
                              Joystick.setButton(buttbx.key[i].kchar, 0);
                              break;
            }
           }   
         }
     }
}


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) {
  //Serial.print("Processing rotary: ");
  //Serial.println(_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 CheckAllEncoders(void) {
  Serial.println("Checking rotaries");
  for (int i=0;i<NUMROTARIES;i++) {
    unsigned char result = rotary_process(i);
    if (result == DIR_CCW) {
      Serial.print("Rotary ");
      Serial.print(i);
      Serial.println(" <<< Going CCW");
      Joystick.setButton(rotaries[i].ccwchar, 1); delay(50); Joystick.setButton(rotaries[i].ccwchar, 0);
    };
    if (result == DIR_CW) {
      Serial.print("Rotary ");
      Serial.print(i);
      Serial.println(" >>> Going CW");
      Joystick.setButton(rotaries[i].cwchar, 1); delay(50); Joystick.setButton(rotaries[i].cwchar, 0);
    };
  }
  Serial.println("Done checking");
1 Like

You are dealing with two differnt types of rotary switches.

The library you are using is designed for a device that uses two pins to provide a signal that can be interpreted as indicating whether the knob has been turrned to the left or right. It can also count how many positions it turns, and (with some extra code) how fast. It uses switches, but it is an encoder, not a switch.

The device you have is a multi-position switch. It ‘encodes’ a position of the rotary switch as an analog signal, presumably, as a voltage of 12 distinct levels (although the documentation is a bit confused).

Try this: connect the signal pin to an analog input of the Arduino. Read the voltage using analogRead(). DIsplay the voltage and move the switch to a new position. The numbers you see won’t be precise, but each posiition should be within a small range of values. By testing to see what range the value falls into you can tell what position the switch is at. By comparing it to a previous value you can tell which way it has been moved. And by timing the changes you can tell what speed it was moved at.

2 Likes

thank you so much for your response. I am brend new to Audrino so I will try and figure out how t do what you suggest.

I do have 2 x normal rotary encoders on the bo which are represented in the code.

I figured the other ones were different as they had positions but could not figure out how to code them in. unless I am wrong it seems like they are potentiometers that have specific positions.

i will do more research based on your response and I will see what I come up with.

Thanks again.

2 Likes

Hi Nathan

That is pretty much what they are. The schematic for that device is on DfRobot web site.
Jeff sums it up

Although I don’t see what use the LEDs would be after initial debugging. Once mounted on a panel they would be out of sight.

On another subject. I don’t claim to be any sort of expert on programming but I am a bit puzzled by a semicolon “;” immediately after some of the closing curly brackets. I had a quick browse of your sketch as I often do to see if I can pick up any little schemes or not very well documented syntax other people are using but I have never seen this before. Can you enlighten me please.
Cheers Bob

Edit: Re rotary switches. It would be a bit difficult to calculate or predict just what voltage is represented by each position as the voltage drop across the LEDs comes into play here. Firstly there does not seem to be a value published for this and there is no guarantee they are all the same. This makes it a bit difficult sensing an absolute ADC value to represent a switch position so you would have to select a range to allow for variations.

2 Likes

Your existing code might work if you change the definition for the pot pins to the Arduino analog pins and adjust the connections accordingly However it is very complex code and would be difficult to debug, or to see exactly how those devices work. A simple example using console output to show your results would be the way to start. If you are using the Arduino IDE there is an analog input example at File\Examples\03.Analog\Analoginput

1 Like

Yes they are definately hidden. If I make another, maybe I will use some frosted clear plastic or something so that the light can come through.

This is a arcade gamepad template that I modified. Unfortunately I am not experienced enough to explain what this does.

I am today starting the process of figuring out how to read the analog return signal and implement the code somehow. Wish me luck haha.

2 Likes

Hi Nathan

A better idea might be to use solid rod type “light pipe”. Drill a circle of holes around the switch of a diameter to suit the pipe allowing for a firm push in fit. I have used this for good effect in the past. They come in a bracket of about 60mm lengths and are about 3mm or 0.125" in diameter. Cut to length so one end sits just above the LED and the other is flush with the outside of the panel. Would work better if the panel was 3mm or more thick like the lid of some plastic boxes, then the pipe should be a snug fit or if the panel is thin the pipe might have to be glued in place with a bit of silicon or similar. When you look at this product it just looks like some small plastic rods but is pretty effective. In fact small pieces of clear plastic rod might do the job.

I can’t remember where the material I was using some years ago was purchased.
Cheers Bob

1 Like