Search models, users, collections, and posts

Macro Pad 3x2 Macro Keyboard | multiple layers

IP Report

Print Profile(1)

All
A1 mini
X1E
A1
X2D
H2S
P2S
P1S
X1
H2C
H2D Pro
P1P
X1 Carbon
H2D
A2L

0.2mm layer, 2 walls, 100% infill
0.2mm layer, 2 walls, 100% infill
Designer
1.9 h
2 plates

Open in Bambu Studio
Boost
76
218
15
3
46
21
Released 

Bill of Materials

List other parts
  • solid copper wire x 1: I used soldering wire but I would not recomment it. See step 1.1
  • keycaps for mx x 6: printed mine
  • esp32-s3 super mini x 1: s3 not c3!! c3 does not support hid
  • WS2812 LED strip 5v x 4:
  • some wire x 1:
  • m3x6mm heat inserts x 4:
  • m3x6mm screws x 4:
  • mechanical switches mx x 6:

Description

This is my macro pad. It runs on CircuitPython and is powered by an ESP32-S3 Super Mini. You can have multiple layers, switching between them is as easy as pressing the button in the upper left corner. You can edit the code that each layer has its own light.

 

Changing the buttons is verry easy but if you need help with any of those steps feel free to send me a message or a comment.

 

Assembly Instructions:

 

1.0 Build everything:

 

1.1

Print out every part and melt the heat insterts into the plastic body.

 

Insert the MX switches into the 3D-printed housing and secure them with hot glue if necessary.

 

Now connect one big solid copper wire to each pin of the button.

(I used normal soldering wire but I would not recomment it, solid coper wire is the best option.)

 

 

1.2

Solder a cable to each switch.

 

 

 

1.3

Use a drop of hot glue to attach the ESP and the base. Cut 4 LEDs from a WS2812 LED strip and glue them to the base as shown in the picture.

 

Now solder everything like in the diagram.

 

1.4

I highly recommend to put some anti sliding pads under the Macro Pad because the screws might scrub against your table and the overall feel will improve.

 

 

1.5

If you printed it out of white filament you might want to add some electrical tape on the inner walls to prevent light leaking out.

 

 

The hardest part is now complete!!

 

 

2.0 Flash the ESP32-s3 super mini with the official CircuitPython firmware:

 

2.1

Pick up your ESP32, press the BOOTSEL button, and connect the ESP32 to your PC while continuing to hold down the button. Wait at least 2 seconds, and only then can you release the BOOTSEL button. This makes shure you can flash the firmware onto your ESP32.

 

2.2

Open the official CircuitPython website, and flash your ESP. The website has very good step by step instructions so I wont go deeper in here.

 

https://code.circuitpython.org/

 

Troubleshooting:

If the ESP32 disappears and reappears every second, repeat step 2.1

 

Now you should see a new drive called "CIRCUITPY"

 

 

 

3.0 Custom Buttons

 

3.1

Download the “MacroPad.zip” file and extract it.

 

I had to upload it to my Google Drive here is the link: 

https://drive.google.com/file/d/16J8FaDnoWh3g7HPIsaC9tBNlD_BnWS1A/view?usp=sharing

(If there are any problems with the link let me konw)

 

 

You will finde a folder called "CIRCUITPY".

Copy and overwrite all the internals onto your CIRCUITPY drive that shows up earlier. Unplug the the MacroPad and plug it back in.

-------------------------------------------------------------------------------

If you just want to build it you dont care about this:

 

The Code:

If you don't trust people with there google drive scroll all the way down you will find the code there.

 

For the libraries I  used the 10.x:

  • adafruit_hid
  • adafruit_pixelbuf
  • neopixel

https://circuitpython.org/libraries

-------------------------------------------------------------------------------

 

 

3.2

You can open up the "code.py" in any editor. I use VS Code.

 

If you made any changes save and overwrite the code.py and disconnect the MacroPad and plug it back in.

This makes shure alle the code can run properly.

 

 

--- Disable Layers ---

you can disable layers by changing the line L[1]_AKTIV to "False"

to anable it just write "True"

 

--- Change Color ---

you can change the color by edeting the L[2]_COLOR Line to your favourite color.

it is normal RGB it goes from 0-255

 

--- Change buttons ---

you can change buttons by edeting the Lines with the L[1]_B[1] Lines.

 

 

Some Keycodes:

KeycodeDescription
Keycode.WINDOWS / Keycode.GUIWindows Key / Command Key (Mac)
Keycode.LEFT_CONTROLLeft Control (Ctrl)
Keycode.RIGHT_CONTROLRight Control (Ctrl)
Keycode.LEFT_ALTLeft Alt / Option (Mac)
Keycode.RIGHT_ALTRight Alt / Alt Gr
Keycode.LEFT_SHIFTLeft Shift
Keycode.APPLICATIONMenu / App Key
Keycode.ENTEREnter / Return
Keycode.ESCAPEEscape (Esc)
Keycode.SPACESpacebar
Keycode.BACKSPACEBackspace
Keycode.DELETEDelete
Keycode.TABTab
Keycode.UP_ARROWUp Arrow
Keycode.DOWN_ARROWDown Arrow
Keycode.LEFT_ARROWLeft Arrow
Keycode.RIGHT_ARROWRight Arrow
Keycode.HOMEHome
Keycode.ENDEnd
Keycode.PAGE_UPPage Up
Keycode.PAGE_DOWNPage Down
Keycode.F1 to Keycode.F12Function Keys F1 to F12
Keycode.PRINT_SCREENPrint Screen
Keycode.SCROLL_LOCKScroll Lock
Keycode.PAUSEPause / Break
Keycode.A to Keycode.ZLetters A through Z
Keycode.ONE to Keycode.ZERONumbers 1 through 0

[Not Recommented]

Just download the files and the code from my google drive but if you dont want to do that or it dont work here is the Code.

(Feel free to edit the code to your liking and share it in the comments!)

Code: 

import board

import digitalio

import usb_hid

import neopixel

import time

import supervisor

from adafruit_hid.keyboard import Keyboard

from adafruit_hid.keycode import Keycode


 

# =========================================================

# 1. HARDWARE-SETUP

# =========================================================

# Onboard-LED Setup um das blaue Blinken zu stoppen

onboard_pixel = neopixel.NeoPixel(board.IO48, 1)

onboard_pixel.fill((0, 0, 0))


 

EXT_PIXEL_PIN = board.IO7  

NUM_PIXELS = 10            

HELLIGKEIT = 0.5            


 

LAYER_PIN = board.IO1      

B1_PIN = board.IO2

B2_PIN = board.IO3

B3_PIN = board.IO4

B4_PIN = board.IO5

B5_PIN = board.IO6  


 

# =========================================================

# 2. LAYER-KONFIG

# =========================================================


 

# --- Disable Layers ---

# you can disable layers by changing the line L[1]_AKTIV to "False"

# to anable it just write "True"


 

# --- Change Color ---

# you can change the color by edeting the L[2]_COLOR Line to your favourite color.

# it is normal RGB it goes from 0-255


 

# --- Change buttons ---

# you can change buttons by edeting the Lines with the L[1]_B[1] Lines.

# If you want to look at some keycodes look in the MakerWorld description.


 

# --- Standart ---

L1_AKTIV = True

L1_COLOR = (80, 200, 255)

L1_B1 = (Keycode.LEFT_CONTROL, Keycode.LEFT_SHIFT, Keycode.M)

L1_B2 = (Keycode.LEFT_CONTROL, Keycode.LEFT_SHIFT, Keycode.D)

L1_B3 = (Keycode.LEFT_CONTROL, Keycode.C)

L1_B4 = (Keycode.LEFT_CONTROL, Keycode.V)

L1_B5 = (Keycode.LEFT_CONTROL, Keycode.A)


 

# --- Fusion 360 ---

L2_AKTIV = True

L2_COLOR = (255, 107, 0)

L2_B1 = (Keycode.LEFT_CONTROL, Keycode.Y)

L2_B2 = (Keycode.LEFT_CONTROL, Keycode.Z)

L2_B3 = (Keycode.LEFT_SHIFT, Keycode.D)

L2_B4 = Keycode.E

L2_B5 = Keycode.ESCAPE


 

# --- LAYER 3 ---

L3_AKTIV = False

L3_COLOR = (0, 0, 255)

L3_B1 = Keycode.F1

L3_B2 = Keycode.F2

L3_B3 = Keycode.F3

L3_B4 = Keycode.F4

L3_B5 = Keycode.F5


 

# --- LAYER 4 ---

L4_AKTIV = False

L4_COLOR = (255, 255, 0)

L4_B1 = Keycode.F6

L4_B2 = Keycode.F7

L4_B3 = Keycode.F8

L4_B4 = Keycode.F9

L4_B5 = Keycode.F10


 

# --- LAYER 5 ---

L5_AKTIV = False

L5_COLOR = (255, 0, 255)

L5_B1 = Keycode.Q

L5_B2 = Keycode.W

L5_B3 = Keycode.E

L5_B4 = Keycode.R

L5_B5 = Keycode.T


 

# --- LAYER 6 ---

L6_AKTIV = False

L6_COLOR = (0, 255, 255)

L6_B1 = Keycode.Y

L6_B2 = Keycode.U

L6_B3 = Keycode.I

L6_B4 = Keycode.O

L6_B5 = Keycode.P


 

# --- LAYER 7 ---

L7_AKTIV = False

L7_COLOR = (255, 127, 0)

L7_B1 = Keycode.Z

L7_B2 = Keycode.X

L7_B3 = Keycode.C

L7_B4 = Keycode.V

L7_B5 = Keycode.B


 

# --- LAYER 8 ---

L8_AKTIV = False

L8_COLOR = (127, 0, 255)

L8_B1 = Keycode.H

L8_B2 = Keycode.J

L8_B3 = Keycode.K

L8_B4 = Keycode.L

L8_B5 = Keycode.M


 

# --- LAYER 9 ---

L9_AKTIV = False

L9_COLOR = (0, 255, 127)

L9_B1 = Keycode.N

L9_B2 = Keycode.M

L9_B3 = Keycode.COMMA

L9_B4 = Keycode.PERIOD

L9_B5 = Keycode.FORWARD_SLASH


 

# --- LAYER 10 ---

L10_AKTIV = False

L10_COLOR = (255, 255, 255)

L10_B1 = Keycode.SPACE

L10_B2 = Keycode.ENTER

L10_B3 = Keycode.BACKSPACE

L10_B4 = Keycode.ESCAPE

L10_B5 = Keycode.TAB


 

# =========================================================

# 3. LOGIK

# =========================================================


 

ALL_LAYERS = [

    {"active": L1_AKTIV, "color": L1_COLOR, "keys": [L1_B1, L1_B2, L1_B3, L1_B4, L1_B5]},

    {"active": L2_AKTIV, "color": L2_COLOR, "keys": [L2_B1, L2_B2, L2_B3, L2_B4, L2_B5]},

    {"active": L3_AKTIV, "color": L3_COLOR, "keys": [L3_B1, L3_B2, L3_B3, L3_B4, L3_B5]},

    {"active": L4_AKTIV, "color": L4_COLOR, "keys": [L4_B1, L4_B2, L4_B3, L4_B4, L4_B5]},

    {"active": L5_AKTIV, "color": L5_COLOR, "keys": [L5_B1, L5_B2, L5_B3, L5_B4, L5_B5]},

    {"active": L6_AKTIV, "color": L6_COLOR, "keys": [L6_B1, L6_B2, L6_B3, L6_B4, L6_B5]},

    {"active": L7_AKTIV, "color": L7_COLOR, "keys": [L7_B1, L7_B2, L7_B3, L7_B4, L7_B5]},

    {"active": L8_AKTIV, "color": L8_COLOR, "keys": [L8_B1, L8_B2, L8_B3, L8_B4, L8_B5]},

    {"active": L9_AKTIV, "color": L9_COLOR, "keys": [L9_B1, L9_B2, L9_B3, L9_B4, L9_B5]},

    {"active": L10_AKTIV, "color": L10_COLOR, "keys": [L10_B1, L10_B2, L10_B3, L10_B4, L10_B5]},

]


 

ACTIVE_LAYERS = [l for l in ALL_LAYERS if l["active"]]


 

def make_pin(p):

    io = digitalio.DigitalInOut(p)

    io.direction = digitalio.Direction.INPUT

    io.pull = digitalio.Pull.UP

    return io


 

switch_btn = make_pin(LAYER_PIN)

btns = [make_pin(B1_PIN), make_pin(B2_PIN), make_pin(B3_PIN), make_pin(B4_PIN), make_pin(B5_PIN)]


 

kbd = Keyboard(usb_hid.devices)

pixels = neopixel.NeoPixel(EXT_PIXEL_PIN, NUM_PIXELS, brightness=HELLIGKEIT)


 

curr_idx = 0

last_switch = True

last_states = [True] * 5

led_manuell_an = True

layer_press_time = 0

layer_hold_triggered = False


 

while True:

    # Sleep Mode / PC Off

    if not supervisor.runtime.usb_connected:

        pixels.fill((0, 0, 0))

        onboard_pixel.fill((0, 0, 0))

        time.sleep(1)

        continue


 

    # Onboard-Pixel Off

    onboard_pixel.fill((0, 0, 0))


 

    if led_manuell_an:

        pixels.fill(ACTIVE_LAYERS[curr_idx]["color"])

    else:

        pixels.fill((0, 0, 0))


 

    # Layer button logik

    if not switch_btn.value:

        if last_switch:

            layer_press_time = time.monotonic()

            layer_hold_triggered = False

            last_switch = False

        if not layer_hold_triggered and (time.monotonic() - layer_press_time) >= 3.0:

            led_manuell_an = not led_manuell_an

            layer_hold_triggered = True

    elif switch_btn.value and not last_switch:

        if not layer_hold_triggered:

            curr_idx = (curr_idx + 1) % len(ACTIVE_LAYERS)

        last_switch = True


 

    # Makro button

    current_keys = ACTIVE_LAYERS[curr_idx]["keys"]

    for i, btn in enumerate(btns):

        if not btn.value and last_states[i]:

            time.sleep(0.02)

            if not btn.value:

                k = current_keys[i]

                if k is not None:

                    if isinstance(k, (list, tuple)):

                        for key in k: kbd.press(key)

                    else:

                        kbd.press(k)

                last_states[i] = False

        elif btn.value and not last_states[i]:

            k = current_keys[i]

            if k is not None:

                if isinstance(k, (list, tuple)):

                    for key in k: kbd.release(key)

                else:

                    kbd.release(k)

            last_states[i] = True

           

    time.sleep(0.01)

[Code end]

 

 

If there are any question or mistakes I made let me know in the comments or sende me a message.

 

Hope you like it!!

Boost Me (for free)

Thanks!!

If you have any questions fell free to send my a massage.

 

@AgentTropenfish

 

Comment & Rating (15)

(0/1000)

License

This user content is licensed under the MakerWorld Exclusive License.

You may create derivative works based on this object, provided that all such derivative works are published exclusively on the MakerWorld platform and include proper attribution to the original creator. You may not share, upload, host, distribute, or publish this object—or any derivative work of this object—on any other digital platform, marketplace, or distribution channel. Commercial use of this object and any derivative works is strictly prohibited. This includes, but is not limited to, selling, renting, sublicensing, or using the object in any context in which you receive monetary compensation or other financial benefits.