• Models
  • Contests
  • Slicer
  • Login
  • Start Here
    thingiverse-iconprintables-iconcults3d-iconmakerworld-iconmyminifactory-icon

    3D GO

    3D ModelsContestsCollectionsSaved ModelsOn a mobile device?

3D GO

Privacy Policy
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Image 1
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Image 2
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Image 3
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Image 4
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Image 5
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Image 6
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Image 7
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Image 8
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Image 9
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Image 10
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Thumbnail 1
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Thumbnail 2
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Thumbnail 3
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Thumbnail 4
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Thumbnail 5
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Thumbnail 6
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Thumbnail 7
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Thumbnail 8
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Thumbnail 9
MacroPad - Seeed Xiao RP2040 - 11 keys 3D Printer File Thumbnail 10

MacroPad - Seeed Xiao RP2040 - 11 keys

netpaq avatarnetpaq

March 9, 2023

printables-icon
DescriptionCommentsTags

Description

Features:
  • Designed for Cherry MX compatible switches.
  • LED Window to leverage the built-in RP2040 neopixel RGB LED.
  • No screws!
    • Mechanical peg holders for the RP2040.
    • Snap cover.
  • USB port access hole on the back of the macropad.
  • 11 keys (using all the digital IOs on the RP2040).

 

Little brother:

Check this one key macropad that I also designed!

 

Keycaps
ProductivityComing soon!
Various (48+)https://www.printables.com/model/414597-cherry-mx-keycap-various-icons-50 
Diceshttps://www.printables.com/model/414603-cherry-mx-keycap-dices 
Playing cardshttps://www.printables.com/model/414613-cherry-mx-keycap-playing-cards 
Blankhttps://www.printables.com/model/397305-cherry-mx-keycap-blank 

 

Overview

Microcontroller:

I built the macropad using a Seeed Studio Xiao RP2040 microcontroller (MCU) board.  It is a 10$ (Canadian) board available on Amazon.  I won’t go into the detailed specifications of the board but it is built around a dual-core RP2040 Raspberry PI processor.  It can run Arduino, MicroPython, and CircuitPython.  For this project, I opted to use CircuitPython (my first time!) and I am using all the 11 digital IO pins, the built-in neopixel RGB led, and the USB C port.

Wiring:

Keyboards and macropads are typically wired in a matrix of rows and columns interconnected using diodes.  Each of the rows and columns are then connected to a digital IO on the MCU which allows to have more keys than digital IOs.  As an example, if you wire a matrix of 5 rows and 6 columns, you can have 30 keys with just 11 digital IOs.  I did not want to use diodes so I connected the switches directly to the MCU, which limited me to 11 keys.  QMK (not a typo, this is another popular keyboard firmware) has a very good hand wiring guide here.

In my case, one pin from each switch is directly connected to a dedicated IO pin (D0-D10) on the MCU.  I am using internal pull up resistors so no need for external resistors either.  The other switches pins are all connected together to the ground pin on the MCU.  In other words, the only hardware needed is the RP2040 MCU, Cherry MX compatible switches, and wire to solder them to the MCU.

Key to digital IO pin mapping:

In retrospect, for the ground connection, I should have stripped and soldered one common wire while running it along the switches pins instead of soldering individual wires to each switch, bundle them, solder them together, and then connect them to the ground pin on the MCU.  I'll do better next time and maybe design a PCB.  I would have avoided this ugly solder blob:

 

 

Macropad firmware (the code):

For the keyboard code, rather than reinventing the wheel, I used KMK, a keyboard focused layer that runs on top of CircuitPython.  It is pretty well documented for matrix wiring with diodes but not so much for direct connections like I did so I'll share some code to help you get started if you go that route too.

When you connect the board, it starts by running boot.py and then code.py.  boot.py is optional and only needed if you want to hide the USB drive (CIRCUITPY) and serial interface (COM port).  I put code in boot.py to mount the USB drive and serial interface when the key connected to D10 is pushed and held while booting but otherwise only the keyboard interface is enabled. 

The sample below has macros to open Google, notepad, cmd, PowerShell, your password (not secure, don't do it!), etc.  It can be customized for your own needs.  I did not do much yet but KMK allows you to create layers, change the color of the LED based on the layer and tons of other fun stuff.  This is a work in progress so I will add more features later.  If you come up with something nice, please share it in the comments!

I haven't seen other examples using the keypad KeysScanner (needed for direct switch connections) module so I hope this helps you.

 

code.py contains the main keyboard code:
#
# <code.py>
# Import modules
# KeysScanner is needed because we use direct switch connections instead
# of a matrix with diodes.
# simple_key_sequence and send_string are used in macros.
# RGB enables using the built-in RP2040 neopixel RGB LED.
# Mediakeys enables assigning things like increasing the volume to keys.
# Layers enables alternate key mapping by layer.
#
import board
from kmk.kmk_keyboard import KMKKeyboard
from kmk.scanners.keypad import KeysScanner
from kmk.keys import KC
from kmk.handlers.sequences import simple_key_sequence
from kmk.handlers.sequences import send_string
from kmk.extensions.RGB import RGB
from kmk.extensions.RGB import AnimationModes
from kmk.extensions.media_keys import MediaKeys
from kmk.modules.layers import Layers as _Layers

#
# Print a string to the serial console
#
print("NPQ Macropad")


#
# GPIO to key mapping - We are using all the board digital IOs
#
# Seed Xiao RP2040 pins:
# 'A0', 'A1', 'A2', 'A3'
# 'D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10'
# 'I2C', 'MISO', 'MOSI', 'RX', 'SCK', 'SCL', 'SDA', 'SPI', 'TX', 'UART', 'board_id'
# 'LED', 'LED_BLUE', 'LED_GREEN', 'LED_RED', 'NEOPIXEL', 'NEOPIXEL_POWER'
#
_KEY_CFG = [
#   0 - D0    1 - D1    2 - D2
    board.D0, board.D1, board.D2,
#   3 - D3    4 - D4    5 - D5    6 - D6
    board.D3, board.D4, board.D5, board.D10,
#   7 - D6    8  - D7   9 - D8    10 - D9
    board.D6, board.D7, board.D8, board.D9,
]


#
# Our own Keyboard class.  We need to do this because we have direct wiring of the switches
# to the MCU instead of the "standard" matrix of columns and rows interconnected with diodes.
#
class MyKeyboard(KMKKeyboard):
    def __init__(self):
        # create and register the scanner
        self.matrix = KeysScanner(
            # require argument:
            pins=_KEY_CFG,
            # optional arguments with defaults:
            value_when_pressed=False,
            pull=True,
            interval=0.02,  # Debounce time in floating point seconds
            max_events=64,
        )


# Our keyboard object!
keyboard = MyKeyboard()

#
# Neopxiel RGB LED extension - http://kmkfw.io/docs/rgb
#
neopix = RGB(
    pixel_pin=board.NEOPIXEL,
    num_pixels=1,
    hue_default=170,  # Blue = 170
    sat_default=255,  # Blue = 255
    val_default=255,  # Blue = 255
    animation_mode=AnimationModes.BREATHING,
    animation_speed=6,
)

#
# Layers
#

# Layer colors
LYR_BLUE, LYR_WHITE, LYR_RED, LYR_GREEN, LYR_PINK, LYR_YELLOW = 0, 1, 2, 3, 4, 5
LAYOR_COLOR = ["BLUE", "WHITE", "RED", "GREEN", "PINK", "YELLOW"]

# Replacing the Layers class with our own to implement
# a LED layer color indicator    
class Layers(_Layers):
    last_top_layer = 0

    # HSV values for each layer
    #           BLUE   WHITE  RED    GREEN  PINK   YELLOW
    lyr_hue = [ 170,   0,     0,     85,    213,   42,       ]
    lyr_sat = [ 255,   0,     255,   255,   255,   255,      ]
    lyr_val = [ 255,   255,   255,   255,   255,   255,      ]

    # Handles the LED color change when a layer is changed
    def after_hid_send(self, keyboard):
        if keyboard.active_layers[0] != self.last_top_layer:
            
            self.last_top_layer = keyboard.active_layers[0]
            
            neopix.hue = self.lyr_hue[self.last_top_layer]
            neopix.sat = self.lyr_sat[self.last_top_layer]
            neopix.val = self.lyr_val[self.last_top_layer]
            
            # I used the serial console for debugging
            # print(LAYOR_COLOR[self.last_top_layer])
            # print(keyboard.active_layers)

#
# Append extensions
#
keyboard.extensions.append(neopix)
keyboard.extensions.append(MediaKeys())
keyboard.modules.append(Layers())

#
# Macros - Can be assigned to keys
#
# My keyboard layout is Canadian French so some keys did not work well for me.
# The backslash and slash were problematic.  I resolved the issue of the slash
# (gives me é) by using the keycode from the numeric pad.  For the backslash,
# I haven't found a KC that will work on all layouts so this will need to be 
# adjusted in the macros...
#
# On Canadian French keyboard:
# \ = KC.RALT(KC.GRAVE)    # KC.BSLASH give "é" on a Canadian French keyboard
# / = KC.LSFT(KC.N3)       # KC.SLASH gives "<" on a Canadian French keyboard
#
# On a US keyboard:
# \ = KC.BSLASH
# / = KC.SLASH
#
# This one works on both:
# / = KC.KP_SLASH
#
# This is a sample of what you can do but there is more...  Look at the KMK
# documentation to see what is possible.
#

# Notepad
NOTEPAD = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("notepad.exe"),
        KC.ENTER,
    )
)

# Calculator
CALC = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("calc.exe"),
        KC.ENTER,
    )
)

# Command prompt
CMD = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("cmd.exe"),
        KC.ENTER,
    )
)

# Windows PowerShell
WPOWERSHELL = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("powershell.exe"),
        KC.ENTER,
    )
)

# Windows PowerShell ISE
WPOWERSHELLISE = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("powershell_ise.exe"),
        KC.ENTER,
    )
)

# PowerShell 7
POWERSHELL7 = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("pwsh.exe"),
        KC.ENTER,
    )
)

# Certificate manager - Local Machine
CERTLM = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("mmc.exe CERTLM.MSC"),
        KC.ENTER,
    )
)

# Certificate manager - Current User
CERTMGR = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("mmc.exe CERTMGR.MSC"),
        KC.ENTER,
    )
)

# Opens Google in a new tab in the default browser
GOOGLE = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("https:"),
        #KC.LSFT(KC.N3),                # / on a Canadian french keyboard
        #KC.LSFT(KC.N3),                # / on a Canadian french keyboard
        KC.KP_SLASH,                    # / on Canadian French and US layout!
        KC.KP_SLASH,                    # / on Canadian French and US layout!
        send_string("www.google.com"),
        KC.ENTER,
    )
)

# MS Word (32bit)
# C:\Program Files (x86)\Microsoft Office\root\Office16\WINWORD.EXE
MSWORD32 = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("C:"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Program Files (x86)"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Microsoft Office"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("root"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Office16"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("WINWORD.EXE"),
        KC.ENTER,
    )
)

# MS Excel (32bit)
# C:\Program Files (x86)\Microsoft Office\root\Office16\EXCEL.EXE
EXCEL32 = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("C:"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Program Files (x86)"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Microsoft Office"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("root"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Office16"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("EXCEL.EXE"),
        KC.ENTER,
    )
)

# MS Outlook (32bit)
# C:\Program Files (x86)\Microsoft Office\root\Office16\OUTLOOK.EXE
OUTLOOK32 = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("C:"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Program Files (x86)"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Microsoft Office"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("root"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Office16"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("OUTLOOK.EXE"),
        KC.ENTER,
    )
)

# MS Word (64bit)
# C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE
MSWORD64 = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("C:"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Program Files"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Microsoft Office"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("root"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Office16"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("WINWORD.EXE"),
        KC.ENTER,
    )
)

# MS Excel (64bit)
# C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE
EXCEL64 = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("C:"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Program Files"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Microsoft Office"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("root"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Office16"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("EXCEL.EXE"),
        KC.ENTER,
    )
)

# MS Outlook (64bit)
# C:\Program Files\Microsoft Office\root\Office16\OUTLOOK.EXE
OUTLOOK64 = simple_key_sequence(
    (
        KC.LWIN(KC.R),
        KC.MACRO_SLEEP_MS(250),
        send_string("C:"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Program Files"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Microsoft Office"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("root"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("Office16"),
        # KC.RALT(KC.GRAVE),                # \ on a Canadian french keyboard
        KC.BSLASH,                          # \ on a US keyboard
        send_string("OUTLOOK.EXE"),
        KC.ENTER,
    )
)

# I doubt that you will actually use these "color" macros in real life but this
# is what i used for debugging and clarity

# Types: BLUE<Enter>
BLUE = simple_key_sequence(
    (
        send_string("BLUE"),
        KC.ENTER,
    )
)

# Types: WHITE<Enter>
WHITE = simple_key_sequence(
    (
        send_string("WHITE"),
        KC.ENTER,
    )
)

# Types: RED<Enter>
RED = simple_key_sequence(
    (
        send_string("RED"),
        KC.ENTER,
    )
)

# Types: GREEN<Enter>
GREEN = simple_key_sequence(
    (
        send_string("GREEN"),
        KC.ENTER,
    )
)

# Types: PINK<Enter>
PINK = simple_key_sequence(
    (
        send_string("PINK"),
        KC.ENTER,
    )
)

# Types: YELLOW<Enter>
YELLOW = simple_key_sequence(
    (
        send_string("YELLOW"),
        KC.ENTER,
    )
)


# Aliases - looks better in the keymap
_______ = KC.TRANSPARENT     # Transparent
XXXXXXX = KC.NO              # No key


#
# Keymap!
#
# You will need to customize this for your own needs.  The sample
# below will give you a good idea of what you can do.
#
# Pressing the D10 key will cycle between layer 0-4 and then go
# back to layer 0.
#
# Layer 5 is only active when HOLDING the D10 key.
#
# To cycle between layers, we use KC.TG to go to higher level
# layers and KC.TO to go back to the lowest (first) layer when
# we reach the last layer. I initially used KC.TO everywhere 
# which resulted in the current layer being the only active layer
# amd transparent keys not working as expected.  A big thumbs up
# to "regicidal plutophage" for helping me understand that on the
# zulip support chat https://kmkfw.zulipchat.com/
#
# Cycle between layers:
# Layer 0 (Blue LED)   - Active layers [0]
# Layer 1 (White LED)  - Active layers [1,0]
# Layer 2 (Red LED)    - Active layers [2,1,0]
# Layer 3 (Green LED)  - Active layers [3,2,1,0]
# Layer 4 (Pink LED)   - Active layers [4,3,2,1,0]
# 
# Momentary layer (hold D10):
# Layer 5 (Yellow LED) - Active layers [5,<active layers of the calling layer>]
#                        ex1: calling yellow from pink [5,4,3,2,1,0]
#                        ex2: calling yellow from white [5,1,0]
#                        ex3: calling yellow from blue [5,0]
#
keyboard.keymap = [
    # BLUE_LAYER (Base/Default) - KC.LT(LYR_YELLOW, GOTO_LYR_WHITE)
    [
    #   D0                D1                D2
        KC.N0,            KC.N1,            KC.N2,
    #   D3                D4                D5                D10 - Enable and go to layer white
        KC.N3,            KC.N4,            KC.N5,            KC.LT(LYR_YELLOW, KC.TG(LYR_WHITE)),
    #   D6                D7                D8                D9 - type the layor color
        KC.N6,            KC.N7,            KC.N8,            BLUE,
    ],
    # WHITE_LAYER - KC.LT(LYR_YELLOW, GOTO_LYR_RED)
    [
    #   D0                D1                D2
        NOTEPAD,          CALC,             GOOGLE,
    #   D3                D4                D5                D10 - Enable and go to layer red
        WPOWERSHELLISE,   WPOWERSHELL,      CMD,              KC.LT(LYR_YELLOW, KC.TG(LYR_RED)),
    #   D6                D7                D8                D9 - type the layor color
        CERTLM,           CERTMGR,          _______,          WHITE,
    ],
    # RED_LAYER - KC.LT(LYR_YELLOW, GOTO_LYR_GREEN)
    [
    #   D0                D1                D2
        MSWORD32,         EXCEL32,          OUTLOOK32,
    #   D3                D4                D5                D10 - Enable and go to layer green
        _______,          _______,          _______,          KC.LT(LYR_YELLOW, KC.TG(LYR_GREEN)),
    #   D6                D7                D8                D9 - type the layor color
        _______,          _______,          _______,          RED,
    ],
    # GREEN_LAYER - KC.LT(LYR_YELLOW, GOTO_LYR_BLUE)
    [
    #   D0                D1                D2
        MSWORD64,         EXCEL64,          OUTLOOK64,
    #   D3                D4                D5                D10 - Enable and go to layer pink
        _______,          _______,          _______,          KC.LT(LYR_YELLOW, KC.TG(LYR_PINK)),
    #   D6                D7                D8                D9 - type the layor color
        _______,          _______,          _______,          GREEN,
    ],
    # PINK_LAYER
    [
    #   D0                D1                D2
        _______,          _______,          _______,
    #   D3                D4                D5                D10 - Wrap to layer blue disabling other layers
        _______,          _______,          _______,          KC.LT(LYR_YELLOW, KC.TO(LYR_BLUE)),
    #   D6                D7                D8                D9 - type the layor color
        _______,          _______,          _______,          PINK,
    ],
    # YELLOW_LAYER - Temporary/Momentary layer activated when HOLDING D10
    [
    #   D0                D1                D2
        _______,          _______,          _______,
    #   D3                D4                D5                D10 - Do Nothing
        _______,          _______,          _______,          XXXXXXX,
    #   D6                D7                D8                D9 - type the layor color
        _______,          _______,          _______,          YELLOW,
    ],
]

if __name__ == "__main__":
    keyboard.go()

 

boot.py (optional) contains the code to hide the CIRCUITPY USB drive and serial COM port (REPL) unless you press and hold a specific key while booting, in this case this is the key connected to D10.  For me, it is underneath the LED window:
# <boot.py>
# Include this file at the root of the CIRCUITPY drive if
# you want to disable the USB drive and serial interface
# unless you press and hold key 10 while booting
import board
import digitalio
import storage
import usb_cdc

# Need this to make the keyboard visible in the BIOS
# This is a macropad so unlikely that we'll need it
# import usb_hid
# usb_hid.enable(boot_device=1)

# The USB drive (CIRCUITPY), and serial interface (REPL)
# are enabled by default
#
# If the key on pin D10 is not held during boot,
# execute the code to disable the USB drive and serial interface.
# (Hold the key to mount the drive and serial interface)
#
# https://learn.adafruit.com/customizing-usb-devices-in-circuitpython/circuitpy-midi-serial

bypass_key = digitalio.DigitalInOut(board.D10)
bypass_key.pull = digitalio.Pull.UP

if bypass_key.value:
    # Disable the CIRCUITPY USB drive
    storage.disable_usb_drive()

    # Disable both serial devices
    # Equivalent to usb_cdc.enable(console=False, data=False)
    usb_cdc.disable()

bypass_key.deinit()

 

 

Installation steps
Install CircuitPython

There is a detailed tutorial available for Mac OSX here but beware, the download link provided is for an outdated version of CircuitPython; I suggest getting the latest stable version from the CircuitPython download page.

 

Start the RP2040 in bootloader mode: 

Press and hold the “BOOT” button (see diagram in the overview section for reference) on the RP2040 board and then connect it to USB or hit and release the “reset” button (while still holding “BOOT”) if the board is already connected to USB.

Release the “BOOT” button after the board has started and you see the RP1-RPI2 USB drive.

 

Download and copy CircuitPython for the Seeed Xiao RP2040 board:

I downloaded the current stable version (8.0.3) from here.

Copy the UF2 file to the root of RP1-RPI2 USB drive.

The board will restart and you will see a CIRCUITPY USB drive instead of RP1-RPI2.

Congrats, you just installed CircuitPython!

Install KMK and the neopixel library

Download KMK from here.

Open the zip file and copy the KMK folder and its content to the root of the CIRCUITPY drive.

Download the neopixel library from here.

Open the zip file and copy the neopixel.py file to the root of the CIRCUITPY drive.

Setup KMK 

Create a code.py file at the root of the CIRCUITPY drive.  The file can contain your own code or the sample that I have provided.

Optionally, create a boot.py file at the root of the CIRCUITPY drive.  This file is only needed if you prefer to hide the USB drive and serial port.  You need to unplug and reconnect the USB cable to apply changes to the boot.py file (a simple reset is not sufficient).

 
 

Print Settings:

If you are willing to sacrifice the LED window, you can print with a single color but I recommend printing the top layer with transparent filament.

 

Color changes instructions: https://help.prusa3d.com/article/color-change_1687

Recommended settings
Layer Height0.2 mm0.15 mm0.1 mm
MaterialPETG
Infill - Bottom fill patternHilbert Curve
Layers and perimeters - Solid Layers - Bottom152029

Color changes

Color change instructions for Prusa slicer.

1.8 mm

3.2 mm

1.7 mm

3.2 mm

1.6 mm

3.1 mm

I printed the top with a 0.6 nozzle because that's what I had installed / 0.2 mm layer height but I would probably print with a 0.4 nozzle. 

All color changes are optional if you are willing to sacrifice the LED window.  The first layer should be printed with transparent filament with a change of color at the height specified in the table above.  The first color change is highly recommended.  The second  is optional but the color change gives a nice look.

Hilbert Curve gives a wonderful finish on the transparent layer (much better than diagonal lines!).

PLA will probably work but I am not sure how the RP2040 pegs will handle being bent when inserting the microcontroller (PLA is more brittle than PETG).  I also don’t remember ever seeing transparent PLA so you would need to sacrifice the LED window.  If you print it with PLA, please let me know how it worked for you!

 

If you want to learn more about CircuitPython you can check this tutorial on the Adafruit website.

The KMK github page is here.

The KMK web site is here.

License:

Creative Commons — Attribution

Related Models

bakercube preview image

bakercube

iomaa profile image

iomaa

44,394

Book Page Holder V3 preview image

Book Page Holder V3

fifindr profile image

fifindr

3,440

Customizable Spotify Keychain / Tag preview image

Customizable Spotify Keychain / Tag

ewt profile image

ewt

2,867

Bottle Opener and Cap GUN! preview image

Bottle Opener and Cap GUN!

3Deddy profile image

3Deddy

43,930

Funnel Tray preview image

Funnel Tray

fifindr profile image

fifindr

3,493

Aldi Cart keychain preview image

Aldi Cart keychain

Nexus profile image

Nexus

1,054

Mini Tape Gun - Tape Dispenser preview image

Mini Tape Gun - Tape Dispenser

brycelowe profile image

brycelowe

23,736

Mini Whistle preview image

Mini Whistle

fifindr profile image

fifindr

1,889