Giter VIP home page Giter VIP logo

10key_macro's Introduction

10key_macro

A DIY project building an Arduino-based macro keyboard with a 4-bit switch and rotary encoder.

Background

A number of years ago, I used to learn PIC18 microcontroller and programming using assembly language. One of the concepts is using a polling technique to get the button response. Since then I have conducted deep research into designing customized keyboards. Despite there being a huge number of existing projects on the internet, I did want to implement them myself.

Description

This project mainly consists of four parts. Circuit design, enclosure design, Arduino coding, Python coding.

  1. Circuit design

The circuit schematic was built on the EasyEDA platform and is now being ported to KiCAD.

The circuit consists of a microcontroller, 10 cherry keys with 2812 LEDs, an IC74165, an EEPROM AT24C64, a rotary encoder and a 4-bit toggle switch.

By following the schematic, the main circuit was built on a proof board and connected to keys and LEDs through wires.

  1. Enclosure design

The enclosure is designed for multiple layers of 4.5mm and 4mm acrylic. The enclosure however begins to fall apart due to aging. A 3D-printed version designed will be reproduced later.

  1. Arduino coding

The fundamental idea of the Arduino code design is framing. There are two parts of the Arduino code structure, setup and loop. I initialize all objects as well as pin I/O in setup section; and perform LED and key checks in the loop.

flowchart TD


    subgraph setup
        direction LR

        A[Create objects]-->B[`memMaster` and <br> `panelMaster` init]
        B --> C[Begin initialise <br> keyboard ]
        C --> D[Add rotary encoder <br> to interrupts]
        D --> E[Wait for Serial port]
        E --> E
        E --> F[Wellcome loop]
    end

    subgraph loop
        direction LR

        G[State switch]

        G --> H[state 0]
        G --> I[state 1-8]
        G --> J[state 13]
        G --> K[state 14]
        G --> L[state 15]

        H-->M[LEDShowTick]
        M-->S

        I-->N[monitorKeys]
        N-->O[LEDShowTick]
        O-->S

        J-->P[flush EEPROM]
        P-->S

        K-->Q[print EEPROM]
        Q-->S

        L-->R[load EEPROM <br> from python]
        R-->S

        S[check state and <br> update key config]-->G

    end
    setup --> loop

The state is determined by the toggle switch which is capable of 16 values (0 -> 15) and additional functions can be added through the addition of new cases to the switch.

swich pattern code feature description
0000 0 LED display Complete LED loop through all supported LED effects
0001 1 mode 1
0010 2 mode 2
0011 3 mode 3
0100 4 mode 4
0101 5 mode 5
0110 6 mode 6
0111 7 mode 7
1000 8 mode 8
1101 13 Flush EEPROM
(need to modify Arduino code)
Replace all values in the EEPROm by 0xFF
1110 14 Print Key map Automatically print whole EEPROM content
- represents 0xFF
1111 15 Load key map from PC Enter this mode, use Python to pass mapping json to Arduino
  1. Python coding

To configure each key output in each mode, a predefined JSON file is created and can be loaded to EEPROM. Arduino will load the specific chunk from EEPROM to update each key config.

Key in object Description Example
Mode "mode-xxxx"
where xxxx is the binary representation of the state
"mode-0001"
Key index From "0" to "9" represents the key index of the keyboard "0"
default string "str" "mx string test - y" where x is the mode from 1 to 8 and
y is the key index
"m1 string test - 1"
default key
binding list "list"
The list of keybinding that will be pressed all in once ["KEY_LEFT_GUI", "r", null]
default key mode "mode" true means print string and false means press key binding in the list true
Default configuration JSON file
{
    "mode-0001": {
        "0": {
            "str": "m1 string test - 0, intentionally make a long string for testing",
            "list": ["KEY_DELETE", null, null],
            "mode": true
        },
        "1": {
            "str": "m1 string test - 1",
            "list": ["KEY_LEFT_GUI", "r", null],
            "mode": false
        },
        "2": {
            "str": "m1 string test - 2",
            "list": ["KEY_DELETE", null, null],
            "mode": false
        },
        "3": {
            "str": "m1 string test - 3",
            "list": [null, null, null],
            "mode": true
        },
        "4": {
            "str": "m1 string test - 4",
            "list": [null, null, null],
            "mode": true
        },
        "5": {
            "str": "m1 string test - 5",
            "list": [null, null, null],
            "mode": true
        },
        "6": {
            "str": "m1 string test - 6",
            "list": [null, null, null],
            "mode": true
        },
        "7": {
            "str": "m1 string test - 7",
            "list": [null, null, null],
            "mode": true
        },
        "8": {
            "str": "m1 string test - 8",
            "list": [null, null, null],
            "mode": true
        },
        "9": {
            "str": "m1 string test - 9",
            "list": [null, null, null],
            "mode": true
        }
    },
    "mode-0010": {
        "0": {
            "str": "m2 string test - 0",
            "list": [null, null, null],
            "mode": true
        },
        "1": {
            "str": "m2 string test - 1",
            "list": [null, null, null],
            "mode": true
        },
        "2": {
            "str": "m2 string test - 2",
            "list": [null, null, null],
            "mode": true
        },
        "3": {
            "str": "m2 string test - 3",
            "list": [null, null, null],
            "mode": true
        },
        "4": {
            "str": "m2 string test - 4",
            "list": [null, null, null],
            "mode": true
        },
        "5": {
            "str": "m2 string test - 5",
            "list": [null, null, null],
            "mode": true
        },
        "6": {
            "str": "m2 string test - 6",
            "list": [null, null, null],
            "mode": true
        },
        "7": {
            "str": "m2 string test - 7",
            "list": [null, null, null],
            "mode": true
        },
        "8": {
            "str": "m2 string test - 8",
            "list": [null, null, null],
            "mode": true
        },
        "9": {
            "str": "m2 string test - 9",
            "list": [null, null, null],
            "mode": true
        }
    },
    "mode-0011": {
        "0": {
            "str": "m3 string test - 0",
            "list": [null, null, null],
            "mode": true
        },
        "1": {
            "str": "m3 string test - 1",
            "list": [null, null, null],
            "mode": true
        },
        "2": {
            "str": "m3 string test - 2",
            "list": [null, null, null],
            "mode": true
        },
        "3": {
            "str": "m3 string test - 3",
            "list": [null, null, null],
            "mode": true
        },
        "4": {
            "str": "m3 string test - 4",
            "list": [null, null, null],
            "mode": true
        },
        "5": {
            "str": "m3 string test - 5",
            "list": [null, null, null],
            "mode": true
        },
        "6": {
            "str": "m3 string test - 6",
            "list": [null, null, null],
            "mode": true
        },
        "7": {
            "str": "m3 string test - 7",
            "list": [null, null, null],
            "mode": true
        },
        "8": {
            "str": "m3 string test - 8",
            "list": [null, null, null],
            "mode": true
        },
        "9": {
            "str": "m3 string test - 9",
            "list": [null, null, null],
            "mode": true
        }
    },
    "mode-0100": {
        "0": {
            "str": "m4 string test - 0",
            "list": [null, null, null],
            "mode": true
        },
        "1": {
            "str": "m4 string test - 1",
            "list": [null, null, null],
            "mode": true
        },
        "2": {
            "str": "m4 string test - 2",
            "list": [null, null, null],
            "mode": true
        },
        "3": {
            "str": "m4 string test - 3",
            "list": [null, null, null],
            "mode": true
        },
        "4": {
            "str": "m4 string test - 4",
            "list": [null, null, null],
            "mode": true
        },
        "5": {
            "str": "m4 string test - 5",
            "list": [null, null, null],
            "mode": true
        },
        "6": {
            "str": "m4 string test - 6",
            "list": [null, null, null],
            "mode": true
        },
        "7": {
            "str": "m4 string test - 7",
            "list": [null, null, null],
            "mode": true
        },
        "8": {
            "str": "m4 string test - 8",
            "list": [null, null, null],
            "mode": true
        },
        "9": {
            "str": "m4 string test - 9",
            "list": [null, null, null],
            "mode": true
        }
    },
    "mode-0101": {
        "0": {
            "str": "m5 string test - 0",
            "list": [null, null, null],
            "mode": true
        },
        "1": {
            "str": "m5 string test - 1",
            "list": [null, null, null],
            "mode": true
        },
        "2": {
            "str": "m5 string test - 2",
            "list": [null, null, null],
            "mode": true
        },
        "3": {
            "str": "m5 string test - 3",
            "list": [null, null, null],
            "mode": true
        },
        "4": {
            "str": "m5 string test - 4",
            "list": [null, null, null],
            "mode": true
        },
        "5": {
            "str": "m5 string test - 5",
            "list": [null, null, null],
            "mode": true
        },
        "6": {
            "str": "m5 string test - 6",
            "list": [null, null, null],
            "mode": true
        },
        "7": {
            "str": "m5 string test - 7",
            "list": [null, null, null],
            "mode": true
        },
        "8": {
            "str": "m5 string test - 8",
            "list": [null, null, null],
            "mode": true
        },
        "9": {
            "str": "m5 string test - 9",
            "list": [null, null, null],
            "mode": true
        }
    },
    "mode-0110": {
        "0": {
            "str": "m6 string test - 0",
            "list": [null, null, null],
            "mode": true
        },
        "1": {
            "str": "m6 string test - 1",
            "list": [null, null, null],
            "mode": true
        },
        "2": {
            "str": "m6 string test - 2",
            "list": [null, null, null],
            "mode": true
        },
        "3": {
            "str": "m6 string test - 3",
            "list": [null, null, null],
            "mode": true
        },
        "4": {
            "str": "m6 string test - 4",
            "list": [null, null, null],
            "mode": true
        },
        "5": {
            "str": "m6 string test - 5",
            "list": [null, null, null],
            "mode": true
        },
        "6": {
            "str": "m6 string test - 6",
            "list": [null, null, null],
            "mode": true
        },
        "7": {
            "str": "m6 string test - 7",
            "list": [null, null, null],
            "mode": true
        },
        "8": {
            "str": "m6 string test - 8",
            "list": [null, null, null],
            "mode": true
        },
        "9": {
            "str": "m6 string test - 9",
            "list": [null, null, null],
            "mode": true
        }
    },
    "mode-0111": {
        "0": {
            "str": "m7 string test - 0",
            "list": [null, null, null],
            "mode": true
        },
        "1": {
            "str": "m7 string test - 1",
            "list": [null, null, null],
            "mode": true
        },
        "2": {
            "str": "m7 string test - 2",
            "list": [null, null, null],
            "mode": true
        },
        "3": {
            "str": "m7 string test - 3",
            "list": [null, null, null],
            "mode": true
        },
        "4": {
            "str": "m7 string test - 4",
            "list": [null, null, null],
            "mode": true
        },
        "5": {
            "str": "m7 string test - 5",
            "list": [null, null, null],
            "mode": true
        },
        "6": {
            "str": "m7 string test - 6",
            "list": [null, null, null],
            "mode": true
        },
        "7": {
            "str": "m7 string test - 7",
            "list": [null, null, null],
            "mode": true
        },
        "8": {
            "str": "m7 string test - 8",
            "list": [null, null, null],
            "mode": true
        },
        "9": {
            "str": "m7 string test - 9",
            "list": [null, null, null],
            "mode": true
        }
    },
    "mode-1000": {
        "0": {
            "str": "m8 string test - 0",
            "list": [null, null, null],
            "mode": true
        },
        "1": {
            "str": "m8 string test - 1",
            "list": [null, null, null],
            "mode": true
        },
        "2": {
            "str": "m8 string test - 2",
            "list": [null, null, null],
            "mode": true
        },
        "3": {
            "str": "m8 string test - 3",
            "list": [null, null, null],
            "mode": true
        },
        "4": {
            "str": "m8 string test - 4",
            "list": [null, null, null],
            "mode": true
        },
        "5": {
            "str": "m8 string test - 5",
            "list": [null, null, null],
            "mode": true
        },
        "6": {
            "str": "m8 string test - 6",
            "list": [null, null, null],
            "mode": true
        },
        "7": {
            "str": "m8 string test - 7",
            "list": [null, null, null],
            "mode": true
        },
        "8": {
            "str": "m8 string test - 8",
            "list": [null, null, null],
            "mode": true
        },
        "9": {
            "str": "m8 string test - 9",
            "list": [null, null, null],
            "mode": true
        }
    }
}

By default, the Arduino keyboard library accepts a one-byte character that represents the key on a modern keyboard. The Python program can convert the special character into that one-byte character.

Arduino Keyboard library key code JSON file
{
    "KEY_LEFT_CTRL": "0x80",
    "KEY_LEFT_SHIFT": "0x81",
    "KEY_LEFT_ALT": "0x82",
    "KEY_LEFT_GUI": "0x83",
    "KEY_RIGHT_CTRL": "0x84",
    "KEY_RIGHT_SHIFT": "0x85",
    "KEY_RIGHT_ALT": "0x86",
    "KEY_RIGHT_GUI": "0x87",

    "KEY_UP_ARROW": "0xDA",
    "KEY_DOWN_ARROW": "0xD9",
    "KEY_LEFT_ARROW": "0xD8",
    "KEY_RIGHT_ARROW": "0xD7",
    "KEY_BACKSPACE": "0xB2",
    "KEY_TAB": "0xB3",
    "KEY_RETURN": "0xB0",
    "KEY_MENU": "0xED",
    "KEY_ESC": "0xB1",
    "KEY_INSERT": "0xD1",
    "KEY_DELETE": "0xD4",
    "KEY_PAGE_UP": "0xD3",
    "KEY_PAGE_DOWN": "0xD6",
    "KEY_HOME": "0xD2",
    "KEY_END": "0xD5",
    "KEY_CAPS_LOCK": "0xC1",
    "KEY_PRINT_SCREEN": "0xCE",
    "KEY_SCROLL_LOCK": "0xCF",
    "KEY_PAUSE": "0xD0",

    "KEY_NUM_LOCK": "0xDB",
    "KEY_KP_SLASH": "0xDC",
    "KEY_KP_ASTERISK": "0xDD",
    "KEY_KP_MINUS": "0xDE",
    "KEY_KP_PLUS": "0xDF",
    "KEY_KP_ENTER": "0xE0",
    "KEY_KP_1": "0xE1",
    "KEY_KP_2": "0xE2",
    "KEY_KP_3": "0xE3",
    "KEY_KP_4": "0xE4",
    "KEY_KP_5": "0xE5",
    "KEY_KP_6": "0xE6",
    "KEY_KP_7": "0xE7",
    "KEY_KP_8": "0xE8",
    "KEY_KP_9": "0xE9",
    "KEY_KP_0": "0xEA",
    "KEY_KP_DOT": "0xEB",

    "KEY_F1": "0xC2",
    "KEY_F2": "0xC3",
    "KEY_F3": "0xC4",
    "KEY_F4": "0xC5",
    "KEY_F5": "0xC6",
    "KEY_F6": "0xC7",
    "KEY_F7": "0xC8",
    "KEY_F8": "0xC9",
    "KEY_F9": "0xCA",
    "KEY_F10": "0xCB",
    "KEY_F11": "0xCC",
    "KEY_F12": "0xCD",
    "KEY_F13": "0xF0",
    "KEY_F14": "0xF1",
    "KEY_F15": "0xF2",
    "KEY_F16": "0xF3",
    "KEY_F17": "0xF4",
    "KEY_F18": "0xF5",
    "KEY_F19": "0xF6",
    "KEY_F20": "0xF7",
    "KEY_F21": "0xF8",
    "KEY_F22": "0xF9",
    "KEY_F23": "0xFA",
    "KEY_F24": "0xFB"
}

Use of this project

This project is a user-friendly design and is highly customizable. By modifying the configuration JSON file, users can now have 80 individually defined shortcut keys.

Build the keyboard

To build the keyboard, you will need the following electronic components.

Index Item Quantity
1 2812 LEDs and capacitor (100nF) 10
2 Cherry keys 10
3 IC74165 1
4 AT24C64 1
5 Rotary encoder 1
6 4-bit toggle switch 1
7 Arduino Pro Micro 1
8 10k resistor 9
9 Micro usb female and male header 1 each
10 Suitable capacitor at Arduino's 5V output and GND 1
11 3D printed or acrylic enclosure,
10 key acrylic testing frame
1
12 M3 screws long (4 cm) and short (0.5 cm) 4 each
13 Jumper wires, soldering wire and proof board As needed

Load JSON to EEPROM

Once finish building the board, you can load the Arduino code to the board and get ready to write the JSON file to the EEPROM.

Install the required library for Python code.

pip install pyserial
pip install inputimeout
  1. Load the Arduino code to the board.
  2. Toggle the switch to 1111 position and unplug the board from the PC.
  3. Turn off all terminals on the PC.
  4. Plug in the board.
  5. Execute the Python code and select the correct port.

The Python code will automatically detect the Arduino is looking for the JSON and dump the data through the Serial port.

Upcoming updates

Some minor improvements to this project may or may not be updated.

  • Add manual LED effect sequence that allows different LED patterns for different state
  • Update 3D model

Used libraries

This project consisted of several open-source projects. This section aims to acknowledge them to make this project possible.

10key_macro's People

Contributors

siraisisatoru avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.