Button box project - Questions about wiring and code

Hello!
yes...another thread about button box.
First, I read some discussions on this forum about the same topic, but I still have some doubts and I hope to have them all clarified with your help.
I don't have any serious electrical or programming background, but I consider myself able to understand basic things related to this matter.

The project is related to a button box (switches with leds, toggles and rotary encoders with an arduino pro micro board).

The button box is made of:
a) 1x latching toggle switch (on-off positions), named SW1 in the diagram below.
This has a built-in led running at 12V, but I don't plan to connect the led after reading other topics on the matter

b) 4x momentary toggle switches (on-off-on positions), named SW2-3, SW4-5, SW6-7, SW8-9 in the diagram below.

c) 9x momentary push buttons, each with an integrated led (running at 5V), named SW10, SW11, SW12, SW13, SW14, SW15, SW16, SW17, SW18 (switches), L1, L2, L3, L4, L5, L6, L7, L8, L9 in the diagram below.
The leds can be wired independently of the switches (each button have 4 pins, 2 of these for each led).

d) 4x rotary encoders, named SW19, SW20, SW21, SW22 in the diagram below.

e) 5x 10k ohm resistors, named R1 --> R5 in the diagram below.

f) 22x diodes, type 1N4148, named D1--> D22 in the diagram below.

All the switches, encoders and toggles are organized in a matrix, 5x5.

Since I read several discussions about this, I'm planning to use diodes for all the switches to prevent ghosting.

Here you can find the diagram I did (I hope I used the correct symbols):

Question is very simple, do you see any issue with this diagram?

The other question is related to software.
I'm planning to use this code (modified, hopefully correctly, for my needs), found in internet, freely available:

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

#define ENABLE_PULLUPS
#define NUMROTARIES 4
#define NUMBUTTONS 22
#define NUMROWS 5
#define NUMCOLS 5


byte buttons[NUMROWS][NUMCOLS] = {
  {0,1,2,3,4},
  {5,6,7,8,9},
  {10,11,12,13,14},
  {15,16,17,18,19},
  {20,21,22,23,24}
};

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

rotariesdef rotaries[NUMROTARIES] {
  {0,1,24,25,0},
  {2,3,26,27,0},
  {4,5,28,29,0},
  {6,7,30,31,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

byte rowPins[NUMROWS] = {21,20,19,18,15}; 
byte colPins[NUMCOLS] = {14,16,10,9,8}; 

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

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();}

void loop() { 
  CheckAllEncoders();
  CheckAllButtons();
}

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) {
   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) {
  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);
    };
  }
}

If I understood correctly, I'm using 5 pull-up resistors (external resistors), so I should not need defining ENABLE_PULLUPS in the software, is it right?
If this is true, is it right to delete this line?:

#define ENABLE_PULLUPS

and what should I do with this block of code?

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
  }
}

Should I delete at all the following lines?

    #ifdef ENABLE_PULLUPS
      digitalWrite(rotaries[i].pin1, HIGH);
      digitalWrite(rotaries[i].pin2, HIGH);
    #endif

Or should I put the 2 digitalWrite() instructions outside of the if block like this?

void rotary_init() {
  for (int i=0;i<NUMROTARIES;i++) {
    pinMode(rotaries[i].pin1, INPUT);
    pinMode(rotaries[i].pin2, INPUT);
    digitalWrite(rotaries[i].pin1, HIGH);
    digitalWrite(rotaries[i].pin2, HIGH);
  }
}

Thank you for any feedback, and please be patient... as I'm a total noob on this matter.

current limiting resistor needed

i think row of diodes is not correct
compare

1 Like

Thank you for the feedback.
About resistors for leds I think they should be included inside the push button, I will open it once I have one in my hands, and if there aren't I will add them, but I agree that they are missing in the diagram, my fault.
As far as diodes I followed the same image you posted to connect them, but I will double check them.

Diodes and switches are definitely wired wrong there.

In the reference schematic of #2 you see the switches and diodes in series, and connect to the signal line. In your schematic they just connect to another switch.

1 Like

i see no one pull-up resistor, R1-R5 are current limiting one.

1 Like

Hopefully, you meant Pro Micro? Pro Mini cannot be used for button box.

1 Like

Yes, it's a pro micro, editing first post, thanks.

joystick button #24 is present on both button matrix and encoder output

1 Like

you can save pins and connect many more devices if you use this principle


one "SparkFun Pro Micro" board can have 81 buttons matrix.

1 Like

Should be fixed.
I found also that I connected leds in series and I think it wont work at all, since the voltage drop off all the leds will be more than the supplied 5V, so I connected them in parallel and calculated the resistor to be 150 ohm for each led (or other value if I will measure a different voltage drop and absorbed current once I have one in my hands), unless it's already included in the push button.

Should be fixed in the following new diagram:

Thank you very much, I didn't notice! Should be fixed.
I also shifted the last 2 rows on the left, so to be able to optimize the number of buttons, so new matrices are:

byte buttons[NUMROWS][NUMCOLS] = {
  {0,1,2,3,4},
  {5,6,7,8,9},
  {10,11,12,13,14},
  {15,16,17,18},
  {19,20,21,22}
};

and

rotariesdef rotaries[NUMROTARIES] {
  {0,1,24,25,0},
  {2,3,26,27,0},
  {4,5,28,29,0},
  {6,7,30,31,0}
};

Thank you for the advice, I didn't apply this, number of switches, toggles, etc. are more than enough for this project, but this is a very good advice.

So, as far as the "ENABLE_PULLUPS" to enable internal resistors I understand that I have to preserve the original code without deleting nor modifying lines of code.
However I read in a discussion that a user wrote:

It's not 100% clear what he meant. Can someone explain the "..you would not connect USB 5V to anything if this is done.."?I have the doubt I can't supply voltage for leds by the VCC pin if I enable pullup in software?

Code:

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

#define ENABLE_PULLUPS
#define NUMROTARIES 4
#define NUMBUTTONS 22
#define NUMROWS 5
#define NUMCOLS 5


byte buttons[NUMROWS][NUMCOLS] = {
  {0,1,2,3,4},
  {5,6,7,8,9},
  {10,11,12,13,14},
  {15,16,17,18},
  {19,20,21,22}
};

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

rotariesdef rotaries[NUMROTARIES] {
  {0,1,24,25,0},
  {2,3,26,27,0},
  {4,5,28,29,0},
  {6,7,30,31,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

byte rowPins[NUMROWS] = {21,20,19,18,15}; 
byte colPins[NUMCOLS] = {14,16,10,9,8}; 

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

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();}

void loop() { 
  CheckAllEncoders();
  CheckAllButtons();
}

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) {
   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) {
  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);
    };
  }
}

no, they was parallel connected. now with additional CLR

1 Like

it is about encoder wiring (not momentary button function). you leave internal pull-up activated and wiring is complete.
similar with keypad lib, it handle by itself a input/output/high/low, so you not need to connect additional pull-up resistors like in my post#2.

upd: i see... you set current limiting resistors R1-R5 again...

i am not sure it will work

probably this will work

const byte buttons[NUMROWS][NUMCOLS] = {
  {0,1,2,3,4},
  {0,5,6,7,8},
  {9,10,11,12,13},
  {14,15,16,17,0},
  {18,19,20,21,0}
};
1 Like

When switches are pressed/activated they take the row pins high (when the corresponding column pin is high), but there is nothing to ensure that the row pins are low when no switches are operated - they are left "floating".

You need a PULLDOWN resistor on each row input to ensure the row is not read as being high.
These pulldown resistors will form a potential divider with the 10kΞ© resistors in the columns, so make them at least 10 times as large as the existing resistors to ensure correct operation.

1 Like

unless you use keypad.h library

void Keypad::scanKeys() {
	// Re-intialize the row pins. Allows sharing these pins with other hardware.
	for (byte r=0; r<sizeKpd.rows; r++) {
		pin_mode(rowPins[r],INPUT_PULLUP);
	}

	// bitMap stores ALL the keys that are being pressed.
	for (byte c=0; c<sizeKpd.columns; c++) {
		pin_mode(columnPins[c],OUTPUT);
		pin_write(columnPins[c], LOW);	// Begin column pulse output.
		for (byte r=0; r<sizeKpd.rows; r++) {
			bitWrite(bitMap[r], c, !pin_read(rowPins[r]));  // keypress is active low so invert to high.
		}
		// Set pin to high impedance input. Effectively ends column pulse.
		pin_write(columnPins[c],HIGH);
		pin_mode(columnPins[c],INPUT);
	}
}

1 Like

I agree, I don't know why I saw them in series..They already were in parallel.

Sorry kolaha, please be patient..should I remove the 5x 10k resistors with the enable pullups in the software?

So, no need for pull down resistors in rows.

Thank, with your updated code and drawing, now it's clear, I was in doubt too that the button matrix should be symmetric.

My apologies, I didn't look at any libraries being used.

But doesn't that mean that all the diodes need reversing, so that when columns are taken low they can pull the row inputs low?

1 Like

From what I know, in this case columns are inputs and rows are outputs, so current needs to flow from columns to rows, so orientation should be right, isn't it?

The code that kolaha showed in post #15 contradicts that statement - columns are set to outputs, and rows to input pullup.

1 Like

yes, you right. another solution is to swap pin numbers

1 Like