Inspired by the IV Works AV3 and the Rama M6-C...
This is an affordable, stacked-acrylic, gasket-mounted, 6-key macropad.
What is gasket-mounting?

Gasket-mounting is a technique that isolates the plate/pcb/switch assembly from the rest of the case using a soft material such as foam or rubber. In this case, I am using the rubber "socks" sourced from Owlab's Mr. Suit keyboard to act as the gaskets. This isolation created from this technique creates a more comfortable typing experience. 


The design process...
Measurements & Planning
CAD Drawings
CAD PARTS
Bill of Materials



Arduino Code

/*******************************************************************
 * A multi-mode Macro keyboard with Arduino Pro Micro using row column matrix-ing.

Code is sourced from Ryan Bates w/ slight modifications
  

Keyboard Control===========================================================
Keyboard.write();   Sends a keystroke to a connected computer. 
                    This is similar to pressing and releasing a key on your keyboard.
                    Will send a shift command if applicable. Example: Keyboard.write('K') will 
                    automatically do SHIFT + k. 
                    Can also accept ASCII code like this:
                    //Keyboard.write(32); // This is space bar (in decimal)
                    Helpful list of ASCII + decimal keys http://www.asciitable.com/
                    

Keyboard.press();   Best for holding down a key with multi-key commands; like copy/ paste
                    This example is [ctrl] + [shift] + [e] 
                      //Keyboard.press(KEY_LEFT_CTRL);
                      //Keyboard.press(KEY_LEFT_SHIFT);
                      //Keyboard.press('e'); 
                      //delay(100); Keyboard.releaseAll();
                

Keyboard.print();   Sends a keystroke(s)
                    Keyboard.print("stop using aim bot"); // types this in as a char or int! (not a string)!


Keyboard.println(); Sends a keystroke followed by a newline (carriage return)
                     Very practical if you want to type a password and login in one button press!
                     
SOMETIMES, applications are coded to recognize Keyboard.press() and not Keyboard.write() and vice versa.
You might have to experiment. 

=============== Mouse Control================================================================================
Mouse.move(x, y, wheel);  Moves the mouse and or scroll wheel up/down left/right.
                          Range is -128 to +127. units are pixels 
                          -number = left or down
                          +number = right or up

Mouse.press(b);         Presses the mouse button (still need to call release). Replace "b" with:
                        MOUSE_LEFT   //Left Mouse button
                        MOUSE_RIGHT  //Right Mouse button
                        MOUSE_MIDDLE //Middle mouse button
                        MOUSE_ALL    //All three mouse buttons
                        
Mouse.release(b);       Releases the mouse button.

Mouse.click(b);         A quick press and release.

*******************************************************************/
// ----------------------------
// Standard Libraries
// ----------------------------
#include <Wire.h>
#include "Keyboard.h"
#include <Mouse.h> //there are some mouse move functions for encoder_Mode 2 and 3



// Libraries - each one of these will need to be installed.
// ----------------------------
#include <Encoder.h> //Library for simple interfacing with encoders (up to two)
//low performance ender response, pins do not have interrupts
Encoder RotaryEncoderA(10, 16); //the LEFT encoder (encoder A)
Encoder RotaryEncoderB(14, 15);  //the RIGHT encoder (encoder B)

#include <Keypad.h>
// This library is for interfacing with the 4x4 Matrix
// 
// Can be installed from the library manager, search for "keypad"
// and install the one by Mark Stanley and Alexander Brevig
// https://playground.arduino.cc/Code/Keypad/

//Define the rols column matrix roots
const byte ROWS = 2; //two rows
const byte COLS = 4; //four columns


//define the physical matrix layout
char keys[ROWS][COLS] = {
  {'1', '2', '3', '4'},  //  the keyboard hardware is a 2x4 grid... 
  {'5', '6', '7', '8'},

};
// The library will return the character inside this array when the appropriate
// button is pressed then look for that case statement. This is the key assignment lookup table.
// Layout(key/button order) looks like this
//     |------------------------|
//     |   [6/5]*               |     *TRS breakout connection. Keys 5 and 6 are duplicated at the TRS jack
//     |      [1] [2] [3] [4]   |     * Encoder A location = key[1]      
//     |      [5] [6] [7] [8]   |     * Encoder B location = Key[4]
//     |------------------------|



// Variables that will change:
int modePushCounter = 0;     // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
int mouseMove;
String password;             // string for rando password generator, its a global variable because i might do something fancy with it?

long positionEncoderA  = -999; //encoderA LEFT position variable
long positionEncoderB  = -999; //encoderB RIGHT position variable

const int ModeButton = A0;    // the pin that the Modebutton is attached to
const int pot = A1;           // pot for adjusting attract mode demoTime or mouseMouse pixel value
const int Mode1= A2;
const int Mode2= A3;          //Mode status LEDs

byte rowPins[ROWS] = {4, 5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {6, 7, 8, 9 }; //connect to the column pinouts of the keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {
  lcd.init();       //initialize the 4x20 lcd
  lcd.backlight();  //turn on the backlight
  lcd.begin(LCD_NB_COLUMNS , LCD_NB_ROWS);
  randomSeed(analogRead(0));           // is this a bad idea? it's called before assigning the button as pull up... the switch is open at rest..
  pinMode(ModeButton, INPUT_PULLUP);  // initialize the button pin as a input:  
  pinMode(Mode1,OUTPUT); digitalWrite(Mode1,LOW);
  pinMode(Mode2,OUTPUT); digitalWrite(Mode2,LOW);

  lcd.setCursor(0, 0);  lcd.print("Mini Macro KB Demo");
  lcd.setCursor(0, 1);  lcd.print("(c) 2020 Ryan Bates");
  delay(800);
  lcd.clear();

  Keyboard.begin();
}


void loop() {
char key = keypad.getKey();
mouseMove = (analogRead(pot)); //reading the analog input, pot = pin A1
mouseMove = map(mouseMove, 0,1023, 1,124); //remap the analog pot values fron 1 to 124
checkModeButton();

  switch (modePushCounter) { // switch between keyboard configurations:
    case 0:    //  Application Alpha or MODE 0 ========================================================================
      encoderA_Mode0();
      encoderB_Mode0();
      LCD_update_0();
      digitalWrite(Mode1,LOW); digitalWrite(Mode2,LOW);   //indicate what mode is loaded
       if (key) {
    //Serial.println(key);
    switch (key) {
      case '1': Keyboard.println("Alpha key1"); break;    //don't forget the break statement!
      case '2': RandoPasswordGenerator(); break;           //a not-so-sophisticated password generator
      
      case '3': Keyboard.press(KEY_LEFT_CTRL);   
                Keyboard.print('z');                       //undo
                break;
      case '4': lcd.setCursor(0,3); lcd.print("                    "); //overwrite the bottom LCD row
                lcd.setCursor(0,3); 
                break; 
      case '5': Keyboard.press(KEY_LEFT_CTRL);   
                Keyboard.print('a');                        //select all    
                break;
      case '6': Keyboard.press(KEY_LEFT_CTRL); 
                Keyboard.print('c');                         //copy
                break;
                
      case '7': Keyboard.press(KEY_LEFT_CTRL);  
                Keyboard.print('v');                         // paste
                break;
                
      case '8': Keyboard.press(KEY_DELETE); 
                 break; }
    delay(50); Keyboard.releaseAll(); // this releases the buttons 
                }
      break;
      
    case 1:    //  Arduino shortcuts or MODE 1 ========================================================================
      encoderA_Mode1();
      encoderB_Mode1();
      LCD_update_1();
      digitalWrite(Mode1,HIGH); digitalWrite(Mode2,LOW);
      if (key) {
    //Serial.println(key);
    switch (key) {
      case '1': Keyboard.write(32);                          //Space Bar
                break;    
      case '2': Keyboard.press(KEY_LEFT_CTRL);  
                Keyboard.print('r');                          //Verify
                break;    
      case '3': Keyboard.press(KEY_LEFT_CTRL);  
                Keyboard.print('u');                          //Upload
                break;    
      case '4': Keyboard.press(KEY_LEFT_CTRL);  
                Keyboard.press(KEY_LEFT_SHIFT);
                Keyboard.print('m');                          //Serial Monitor
                break;    
      case '5': Keyboard.press(KEY_LEFT_CTRL);  
                Keyboard.print('c');                          //Copy
                break;
      case '6': Keyboard.press(KEY_LEFT_CTRL);  
                Keyboard.print('v');                          //Paste
                break;
      case '7': Keyboard.press(KEY_LEFT_CTRL);  
                Keyboard.print('/');                          //Comment / Uncomment
                break;
      case '8': Keyboard.press(KEY_LEFT_CTRL);  
                Keyboard.print('f');                          //Find
                break; }
    delay(50); Keyboard.releaseAll(); // this releases the buttons 
               }
      break;
      
    case 2:
// Application Delta or MODE 2 ========================================================================
               //this mode mimics a password manager. Types ['login ID'] + [tab] + ['password'] + [enter]
               //This is not a really secure way manage your passwords, but it's an idea...
      encoderA_Mode2();
      encoderB_Mode2();
     
      digitalWrite(Mode1,LOW); digitalWrite(Mode2,HIGH);
    if (key) {
    switch (key) {
      case '1': Keyboard.println("Delta key1"); break;
      case '2': Keyboard.println("Delta key2"); break;
      case '3': Keyboard.println("Delta key3"); break;
      case '4': Keyboard.println("Delta key4"); break;
      
      case '5': Keyboard.print("admin_1");      Keyboard.write(9); //tab key
                Keyboard.println("password_1"); break;
      case '6': Keyboard.print("admin_2");      Keyboard.write(9); //tab key
                Keyboard.println("password_2"); break;
      case '7': Keyboard.print("admin_3");      Keyboard.write(9); //tab key
                Keyboard.println("password_3"); break;
      case '8': Keyboard.print("admin_4");      Keyboard.write(9); //tab key
                Keyboard.println("password_4"); break;
      
 }
    delay(50); Keyboard.releaseAll(); // this releases the buttons 
              }
      break;
      
    case 3:    // Application Delta or MODE 3 ========================================================================
     digitalWrite(Mode1,HIGH); digitalWrite(Mode2,HIGH);
      encoderA_Mode3(); //etch-a-sketch with the mouse up/down
      encoderB_Mode3(); //etch-a-sketch with the mouse left/right
      LCD_update_3();
   if (key) {
    //Serial.println(key);
    switch (key) {
      case '1': Keyboard.println("Hi.   Hopefully this example code makes sense."); break;
      
      case '2': //macro that opens chrome and a random wiki page for learning.
        Keyboard.press(KEY_LEFT_GUI); 
        Keyboard.press('r'); 
        Keyboard.release(KEY_LEFT_GUI); 
        Keyboard.release('r'); 
        delay(50); //give your system time to catch up with these android-speed keyboard presses
        Keyboard.println("chrome"); delay(500);
        Keyboard.println("https://en.wikipedia.org/wiki/Special:Random"); break; 

        
      case '3': Keyboard.press(KEY_LEFT_GUI); delay(50);
                Keyboard.release(KEY_LEFT_GUI);delay(400);
                Keyboard.println("mag"); delay(200);
           
      break;
      
      case '4': Keyboard.write(32); break; //smashing that space bar
      case '5': //macro that opens Chrome & Rick Rolls you like a chump
        Keyboard.press(KEY_LEFT_GUI); 
        Keyboard.press('r'); 
        Keyboard.release(KEY_LEFT_GUI); 
        Keyboard.release('r'); 
        delay(50); //give your system time to catch up with these android-speed keyboard presses
        Keyboard.println("chrome"); delay(500);
        Keyboard.println("https://www.youtube.com/watch?v=dQw4w9WgXcQ"); 
        break;

      
      case '6':  //macro that opens Chrome and goes to my youtube channel!
        Keyboard.press(KEY_LEFT_GUI); 
        Keyboard.press('r'); 
        Keyboard.release(KEY_LEFT_GUI); 
        Keyboard.release('r'); 
        delay(50); //give your system time to catch up with these android-speed keyboard presses
        Keyboard.println("chrome"); delay(500);
        Keyboard.println("https://www.youtube.com/c/ryanbatesrbg"); 
        break;
      
      case '7': //macro example!!! Windows_Key+R = Run then type "mspaint" and press enter. Opens MS Paint
        Keyboard.press(KEY_LEFT_GUI); 
        Keyboard.press('r'); 
        Keyboard.release(KEY_LEFT_GUI); 
        Keyboard.release('r'); 
        delay(50); //give your system time to catch up with these android-speed keyboard presses
        Keyboard.println("mspaint"); 
        break;
      
      
      case '8': //macro example!!! Windows_Key+R = Run then type "calc" and press enter. Opens MS Calculator
        Keyboard.press(KEY_LEFT_GUI); 
        Keyboard.press('r'); 
        Keyboard.release(KEY_LEFT_GUI); 
        Keyboard.release('r'); 
        delay(50); //give your system time to catch up with these android-speed keyboard presses
        Keyboard.println("calc"); 
        break;
                  }
  
  delay(50); Keyboard.releaseAll(); // this releases the buttons 

}}
  }
void RandoPasswordGenerator(){
      long random_alpha;
      long random_ALPHA;
      long random_numeric;
      long random_special;
      
      char alpha;         // lower case letters
      char ALPHA;         // uppercase letters
      char number;        // numbers!
      char specialChar;   // !@#$%^&* and so on
      char randoPassword; // array to combine these all together
       
      char alphabet[]="qwertyuiopasdfghjklzxcvbnm"; // arrary for lower case letters. alphabet[0]; returns "q"...... alphabet[25]; returns "m"  alphabet[26]; will break something by accessing the null character
      char ALPHABET[]="QWERTYUIOPASDFGHJKLZXCVBNM"; //when declaring an array of type char, one more element than your initialization is required, to hold the required null character
      char numeric[] ="1234567890";
      char special[] ="!@#$%^&*()";
      
      random_alpha = random(sizeof(alphabet));    //random(max) sets the limit of the random number being pulled
      random_ALPHA = random(sizeof(ALPHABET));    //sets the random number upper limit to the size of char array
      random_numeric = random(sizeof(numeric));
      random_special = random(sizeof(special));   
      
      alpha = alphabet[random_alpha]; //picks a random character in the respective array. 
      ALPHA = ALPHABET[random_ALPHA];
      number = numeric[random_numeric];
      specialChar = special[random_special];
      
      String alpha_String = String(alpha); //convert all the single chars to strings...so we can concatenate them...
      String ALPHA_String = String(ALPHA);
      String number_String = String(number);
      String specialChar_String = String(specialChar);
      
      password = (alpha_String + ALPHA_String + number_String + specialChar_String); //concatenate the random alpha and numerics to format: xX$!
      //this is great and all, but it will always generate a pattern, which isnt secure with machine learning (or any basic pattern recognition)
           
      password.toCharArray(randoPassword,sizeof(randoPassword)); //convert the xX# string to char datatype
      Keyboard.print(password);  
      lcd.print(password);
      }
  
void checkModeButton(){
  buttonState = digitalRead(ModeButton);
  if (buttonState != lastButtonState) { // compare the buttonState to its previous state
    if (buttonState == LOW) { // if the state has changed, increment the counter
      // if the current state is LOW then the button cycled:
      modePushCounter++;
    } 
    delay(50); // Delay a little bit to avoid bouncing
  }
  lastButtonState = buttonState; // save the current state as the last state, for next time through the loop
   if (modePushCounter >3){ //reset counter after 4 presses (remember we start counting at 0) change this number to add more modes
      modePushCounter = 0;}
}
//=============== encoder definitions/assignments ===========================================
//this section allows a unique encoder function for each mode (profile). Four total in this case or modes 0 through 3.

//=============Encoder A & B Function ====== Set 0 =========================================================
void encoderA_Mode0(){
  long newPos = RotaryEncoderA.read()/4; //When the encoder lands on a valley, this is an increment of 4.
                                          // your encoder might be different (divide by 2) i dunno. 
  if (newPos != positionEncoderA && newPos > positionEncoderA) {
    positionEncoderA = newPos;
    Keyboard.press(KEY_RIGHT_ARROW);
    Keyboard.release(KEY_RIGHT_ARROW);                      }

  if (newPos != positionEncoderA && newPos < positionEncoderA) {
    positionEncoderA = newPos;
    Keyboard.press(KEY_LEFT_ARROW);
    Keyboard.release(KEY_LEFT_ARROW);                      }
}

void encoderB_Mode0(){
  long newPos = RotaryEncoderB.read()/4; //When the encoder lands on a valley, most encoders use an increment of 4.
  if (newPos != positionEncoderB && newPos > positionEncoderB) {
    positionEncoderB = newPos;
     Keyboard.press(KEY_DOWN_ARROW);
    Keyboard.release(KEY_DOWN_ARROW);                      }

  if (newPos != positionEncoderB && newPos < positionEncoderB) {
    positionEncoderB = newPos;

    Keyboard.press(KEY_UP_ARROW);
    Keyboard.release(KEY_UP_ARROW);                         }

You may also like

Back to Top