Ergonomic One Handed Keyboard and Joystick Using Artsey

by idan_eldar in Workshop > 3D Printing

761 Views, 18 Favorites, 0 Comments

Ergonomic One Handed Keyboard and Joystick Using Artsey

P1180071-Enhanced-NR.jpg

In this project, we will create a one-handed ergonomic keyboard with a joystick, based on the open-source Artsey project. We identified the need for such a device when we discovered that existing one-handed keyboards for people with disabilities are often too expensive. Our final design is inspired by the natural form of the human hand, ensuring comfort and ease of use. As an open-source project, the device can also be adapted into a macro pad or customized for other functions.

Supplies

Supplies:

  1. KB2040
  2. KB2040 Pins
  3. 1N4148 Diodes
  4. Hook Up Wire
  5. Analog Joystick
  6. Jumper Wires
  7. Breadboard
  8. Cherry MX Style Switches
  9. Cherry MX Style Keycaps
  10. Switch Push Button
  11. Rubber Feet
  12. M3x Screws
  13. M3 Inserts
  14. PLA Filament


Tools:

  1. 3D Printer
  2. Soldering Iron
  3. Soldering Wire
  4. Wire Cutter
  5. Hex Key
  6. USB-C Cable

3D Print the Shell

IMG_4006.jpg
IMG_4788.jpg

This is the first step in the process of building this device.

First, download the files we attached.

Then, using the soldering iron, heat up the M3 inserts in order to melt them into place. Sand the parts if necessary.

Solder the Pins to the KB2040 Board

IMG_4951.jpg
IMG_4954.jpg

Now, get ready for soldering. Grab your soldering iron and some soldering wire. Begin by soldering the pins onto the KB2040 board. After that, connect all the components onto a breadboard.

The Matrix

IMG_6512.PNG
IMG_4809.jpg
IMG_4794.jpg
IMG_4797.jpg
IMG_4803.jpg
IMG_4807.jpg

Now we create the matrix.

Insert the keyboard switches into the shell and start by soldering the diodes onto the switches. We’ve included an image to guide you. After the diodes are in place, use a rigid wire to complete the matrix. Cut the wire to the correct lengths using a wire cutter, then solder it to the switches. Ensure the diodes and the wire do not touch each other.


Cut one end of the jumper wire and use the soldering iron to connect all the wires. Next, solder the jumper wires to the ends of the diodes and the cables.

Joystick

IMG_6511.PNG
IMG_4947.jpg
IMG_4811.jpg

This project is currently in progress, and unfortunately, we haven't been able to get the joystick to work yet. We encourage you to continue developing it and share your final results with us.

The joystick we used is the analog joystick. Attach the joystick to the shell using M3x screws. Connect it to the breadboard with jumper wires as shown.

Ensure the joystick moves freely. If it doesn't, sand the hole.

Code

IMG_4961.jpg

Now for the fun part: the code.

To flash the keyboard, you will need to download QMK MSYS and VS Code, which will help us flash the KB2040 with our code.

First, open QMK MSYS and type:

QMK new-keyboard


Then, you'll need to name your keyboard, and type enter.

Keyboard name? [insert-name-here]


Now select:

RP2040


Open VS Code

In your keyboard folder, create "keymap.c", and paste the following code:

// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later


#include QMK_KEYBOARD_H


//joystick config
joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = {
  JOYSTICK_AXIS_IN(GP26, 0, 512, 1023),
  JOYSTICK_AXIS_IN(GP27, 0, 512, 1023)
};
Now for the fun part: the code.

To flash the keyboard, you will need to download QMK MSYS and VS Code, which will help us flash the KB2040 with our code.

First, open QMK MSYS and type:

QMK new-keyboard

Then, you'll need to name your keyboard, and type enter.

Keyboard name? [insert-name-here]

Now select:

RP2040

Open VS Code

In your keyboard folder, create "keymap.c", and paste the following code:

// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later


#include QMK_KEYBOARD_H


//joystick config
joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = {
  JOYSTICK_AXIS_IN(GP26, 0, 512, 1023),
  JOYSTICK_AXIS_IN(GP27, 0, 512, 1023)
};


bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    // Custom keycode handling
    return true;
}

enum layers {
  _ART_BASE,
  _ART_NUM,
  _ART_CUS,
  _ART_PUNC,
  _ART_MOU,
  _ART_NAV,
  _ART_SYM,
};


#include "aliases.c"
#include "combos.c"


const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
   
    /*
     * ┌───┬───┬───┬───┐
     * │ A │ B │ C │ D │
     * ├───┼───┼───┼───┤
     * │ E │ F │ G │ H │
     * └───┴───┴───┴───┘
     */


    [_ART_BASE] = LAYOUT_ortho_2x4(
        LT(_ART_SYM,KC_A),KC_R,KC_T,LT(_ART_NUM,KC_S),
        LT(_ART_PUNC,KC_E),KC_Y,KC_I,LT(_ART_CUS,KC_O)
    ),


    [_ART_NUM] = LAYOUT_ortho_2x4(
        KC_1,KC_2,KC_3,KC_TRNS,
        KC_4,KC_5,KC_6,KC_TRNS
    ),

    [_ART_PUNC] = LAYOUT_ortho_2x4(
        KC_EXLM,KC_BSLS,KC_SCLN,KC_GRV,
        KC_TRNS,KC_QUES,KC_MINS,KC_EQL
    ),


    [_ART_CUS] = LAYOUT_ortho_2x4(
        KC_MPLY,KC_MUTE,KC_KB_VOLUME_UP,KC_TRNS,
        KC_MPRV,KC_MNXT,KC_KB_VOLUME_DOWN,KC_TRNS
    ),


    [_ART_SYM] = LAYOUT_ortho_2x4(
        KC_TRNS,KC_LPRN,KC_RPRN,KC_LCBR,
        KC_TRNS,KC_LBRC,KC_RBRC,KC_RCBR
    ),


    [_ART_MOU] = LAYOUT_ortho_2x4(
        KC_BTN1,KC_MS_U,KC_BTN2,KC_WH_U,
        KC_MS_L,KC_MS_D,KC_MS_R,KC_WH_D
    ),


    [_ART_NAV] = LAYOUT_ortho_2x4(
        KC_HOME,KC_UP,KC_END,KC_PGUP,
        KC_LEFT,KC_DOWN,KC_RIGHT,KC_PGDN
    ),
};


Create "info.json", and paste the following code:

{
    "manufacturer": "Yuval",
    "keyboard_name": "artsyiotest",
    "maintainer": "Yuval",
    "bootloader": "rp2040",
    "diode_direction": "ROW2COL",
    "features": {
        "bootmagic": true,
        "command": false,
        "console": false,
        "extrakey": true,
        "mousekey": true,
        "nkro": true
    },
    "matrix_pins": {
        "cols": ["GP4", "GP5", "GP6", "GP7"],
        "rows": ["GP2", "GP3"]
    },
    "processor": "RP2040",
    "url": "",
    "usb": {
        "device_version": "1.0.0",
        "pid": "0x0000",
        "vid": "0xFEED"
    },
    "layouts": {
        "LAYOUT_ortho_2x4": {
            "layout": [
                {"matrix": [0, 0], "x": 0, "y": 0},
                {"matrix": [0, 1], "x": 1, "y": 0},
                {"matrix": [0, 2], "x": 2, "y": 0},
                {"matrix": [0, 3], "x": 3, "y": 0},
                {"matrix": [1, 0], "x": 0, "y": 1},
                {"matrix": [1, 1], "x": 1, "y": 1},
                {"matrix": [1, 2], "x": 2, "y": 1},
                {"matrix": [1, 3], "x": 3, "y": 1}
            ]
        }
    }
}


Create "aliases.c", and paste the following code:

#define BASE_1_1 LT(_ART_SYM,KC_A)
#define BASE_1_2 KC_R
#define BASE_1_3 KC_T
#define BASE_1_4 LT(_ART_NUM,KC_S)
#define BASE_2_1 LT(_ART_PUNC,KC_E)
#define BASE_2_2 KC_Y
#define BASE_2_3 KC_I
#define BASE_2_4 LT(_ART_CUS,KC_O)
#define NUM_1_1 KC_1
#define NUM_1_2 KC_2
#define NUM_1_3 KC_3
#define NUM_1_4 KC_TRNS
#define NUM_2_1 KC_4
#define NUM_2_2 KC_5
#define NUM_2_3 KC_6
#define NUM_2_4 KC_TRNS
#define PUNC_1_1 KC_HASH
#define PUNC_1_2 KC_GRV
#define PUNC_1_3 KC_SCLN
#define PUNC_1_4 KC_BSLS
#define PUNC_2_1 KC_TRNS
#define PUNC_2_2 KC_AT
#define PUNC_2_3 KC_MINS
#define PUNC_2_4 KC_EQL
#define CUS_1_1 KC_MPLY
#define CUS_1_2 KC_MUTE
#define CUS_1_3 KC_VOLU
#define CUS_1_4 KC_TRNS
#define CUS_2_1 KC_MPRV
#define CUS_2_2 KC_MNXT
#define CUS_2_3 KC_VOLD
#define CUS_2_4 KC_TRNS
#define SYM_1_1 KC_TRNS
#define SYM_1_2 KC_LPRN
#define SYM_1_3 KC_LBRC
#define SYM_1_4 KC_LCBR
#define SYM_2_1 KC_TRNS
#define SYM_2_2 KC_RPRN
#define SYM_2_3 KC_RBRC
#define SYM_2_4 KC_RCBR
#define MOU_1_1 KC_BTN1
#define MOU_1_2 KC_MS_U
#define MOU_1_3 KC_BTN2
#define MOU_1_4 KC_WH_U
#define MOU_2_1 KC_MS_L
#define MOU_2_2 KC_MS_D
#define MOU_2_3 KC_MS_R
#define MOU_2_4 KC_WH_D
#define NAV_1_1 KC_HOME
#define NAV_1_2 KC_UP
#define NAV_1_3 KC_END
#define NAV_1_4 KC_PGUP
#define NAV_2_1 KC_LEFT
#define NAV_2_2 KC_DOWN
#define NAV_2_3 KC_RIGHT
#define NAV_2_4 KC_PGDN


Create "combos.c", and paste the following code:

enum combo_events{
ARTSEY_H,
ARTSEY_Q,
ARTSEY_U,
ARTSEY_C,
ARTSEY_K,
ARTSEY_B,
ARTSEY_W,
ARTSEY_N,
ARTSEY_F,
ARTSEY_X,
ARTSEY_J,
ARTSEY_M,
ARTSEY_P,
ARTSEY_V,
ARTSEY_L,
ARTSEY_Z,
ARTSEY_D,
ARTSEY_G,
ARTSEY_SPACE,
ARTSEY_SPACE_2,
ARTSEY_BACKSPACE,
ARTSEY_BACKSPACE_2,
ARTSEY_DEL,
ARTSEY_DEL_2,
ARTSEY_ENTER,
ARTSEY_ENTER_2,
ARTSEY_ESCAPE,
ARTSEY_ESCAPE_2,
ARTSEY_QUOTE,
ARTSEY_BANG,
ARTSEY_QUEST,
ARTSEY_PERIOD,
ARTSEY_COMMA,
ARTSEY_SLASH,
ARTSEY_TAB,
ARTSEY_OS_SHIFT,
ARTSEY_CTRL,
ARTSEY_CTRL_2,
ARTSEY_CTRL_3,
ARTSEY_GUI,
ARTSEY_GUI_2,
ARTSEY_GUI_3,
ARTSEY_ALT,
ARTSEY_ALT_2,
ARTSEY_ALT_3,
ARTSEY_SHIFT,
ARTSEY_SHIFT2,
ARSEY_PANIC,
ARSEY_PANIC_2,
ARSEY_PANIC_3,
ARTSEY_NAV,
ARTSEY_MAIN1,
ARTSEY_MOU,
ARTSEY_MAIN2,
ARTSEY_7,
ARTSEY_8,
ARTSEY_9,
ARTSEY_0,
COMBO_LENGTH
};
uint16_t COMBO_LEN = COMBO_LENGTH;
const uint16_t PROGMEM artsey_h[] = {BASE_2_1,BASE_2_3,COMBO_END};
const uint16_t PROGMEM artsey_q[] = {BASE_1_1,BASE_1_3,BASE_1_4,COMBO_END};
const uint16_t PROGMEM artsey_u[] = {BASE_2_2,BASE_2_3,COMBO_END};
const uint16_t PROGMEM artsey_c[] = {BASE_2_1,BASE_2_2,COMBO_END};
const uint16_t PROGMEM artsey_k[] = {BASE_2_2,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_b[] = {BASE_2_1,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_w[] = {BASE_1_1,BASE_1_4,COMBO_END};
const uint16_t PROGMEM artsey_n[] = {BASE_2_3,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_f[] = {BASE_1_1,BASE_1_2,COMBO_END};
const uint16_t PROGMEM artsey_x[] = {BASE_1_2,BASE_1_3,BASE_1_4,COMBO_END};
const uint16_t PROGMEM artsey_j[] = {BASE_1_3,BASE_1_4,COMBO_END};
const uint16_t PROGMEM artsey_m[] = {BASE_2_2,BASE_2_3,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_p[] = {BASE_2_1,BASE_2_3,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_v[] = {BASE_1_2,BASE_1_4,COMBO_END};
const uint16_t PROGMEM artsey_l[] = {BASE_2_1,BASE_2_2,BASE_2_3,COMBO_END};
const uint16_t PROGMEM artsey_z[] = {BASE_1_1,BASE_1_2,BASE_1_3,BASE_1_4,COMBO_END};
const uint16_t PROGMEM artsey_d[] = {BASE_1_1,BASE_1_2,BASE_1_3,COMBO_END};
const uint16_t PROGMEM artsey_g[] = {BASE_1_2,BASE_1_3,COMBO_END};
const uint16_t PROGMEM artsey_space[] = {BASE_2_1,BASE_2_2,BASE_2_3,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_space_2[] = {NAV_2_1,NAV_2_2,NAV_2_3,NAV_2_4,COMBO_END};
const uint16_t PROGMEM artsey_backspace[] = {BASE_2_1,BASE_1_2,COMBO_END};
const uint16_t PROGMEM artsey_backspace_2[] = {NAV_2_1,NAV_1_2,COMBO_END};
const uint16_t PROGMEM artsey_del[] = {BASE_1_2,BASE_2_3,COMBO_END};
const uint16_t PROGMEM artsey_del_2[] = {NAV_1_2,NAV_2_3,COMBO_END};
const uint16_t PROGMEM artsey_enter[] = {BASE_1_1,BASE_2_1,COMBO_END};
const uint16_t PROGMEM artsey_enter_2[] = {NAV_1_1,NAV_2_1,COMBO_END};
const uint16_t PROGMEM artsey_escape[] = {BASE_1_1,BASE_1_2,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_escape_2[] = {NAV_1_1,NAV_1_2,NAV_2_4,COMBO_END};
const uint16_t PROGMEM artsey_quote[] = {BASE_1_2,BASE_2_2,COMBO_END};
const uint16_t PROGMEM artsey_bang[] = {BASE_1_3,BASE_2_3,COMBO_END};
const uint16_t PROGMEM artsey_quest[] = {BASE_1_4,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_period[] = {BASE_1_1,BASE_2_3,COMBO_END};
const uint16_t PROGMEM artsey_comma[] = {BASE_1_1,BASE_2_2,COMBO_END};
const uint16_t PROGMEM artsey_slash[] = {BASE_1_1,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_tab[] = {BASE_1_1,BASE_1_2,BASE_1_3,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_os_shift[] = {BASE_2_1,BASE_1_2,BASE_1_3,BASE_1_4,COMBO_END};
const uint16_t PROGMEM artsey_ctrl[] = {BASE_1_4,BASE_2_1,COMBO_END};
const uint16_t PROGMEM artsey_ctrl_2[] = {NAV_1_4,NAV_2_1,COMBO_END};
const uint16_t PROGMEM artsey_ctrl_3[] = {MOU_1_4,MOU_2_1,COMBO_END};
const uint16_t PROGMEM artsey_gui[] = {BASE_1_4,BASE_2_2,COMBO_END};
const uint16_t PROGMEM artsey_gui_2[] = {NAV_1_4,NAV_2_2,COMBO_END};
const uint16_t PROGMEM artsey_gui_3[] = {MOU_1_4,MOU_2_2,COMBO_END};
const uint16_t PROGMEM artsey_alt[] = {BASE_1_4,BASE_2_3,COMBO_END};
const uint16_t PROGMEM artsey_alt_2[] = {NAV_1_4,NAV_2_3,COMBO_END};
const uint16_t PROGMEM artsey_alt_3[] = {MOU_1_4,MOU_2_3,COMBO_END};
const uint16_t PROGMEM artsey_shift[] = {BASE_1_1,BASE_2_2,BASE_2_3,BASE_2_4,COMBO_END};
const uint16_t PROGMEM artsey_shift2[] = {NAV_1_1,NAV_2_2,NAV_2_3,NAV_2_4,COMBO_END};
const uint16_t PROGMEM arsey_panic[] = {BASE_1_1,BASE_1_2,BASE_1_3,BASE_1_4,BASE_2_1,BASE_2_2,BASE_2_3,BASE_2_4,COMBO_END};
const uint16_t PROGMEM arsey_panic_2[] = {NAV_1_1,NAV_1_2,NAV_1_3,NAV_1_4,NAV_2_1,NAV_2_2,NAV_2_3,NAV_2_4,COMBO_END};
const uint16_t PROGMEM arsey_panic_3[] = {MOU_1_1,MOU_1_2,MOU_1_3,MOU_1_4,MOU_2_1,MOU_2_2,MOU_2_3,MOU_2_4,COMBO_END};
const uint16_t PROGMEM artsey_nav[] = {BASE_1_2,BASE_2_1,BASE_2_3,COMBO_END};
const uint16_t PROGMEM artsey_main1[] = {NAV_1_2,NAV_2_1,NAV_2_3,COMBO_END};
const uint16_t PROGMEM artsey_mou[] = {BASE_1_1,BASE_1_3,BASE_2_2,COMBO_END};
const uint16_t PROGMEM artsey_main2[] = {MOU_1_1,MOU_1_3,MOU_2_2,COMBO_END};
const uint16_t PROGMEM artsey_7[] = {NUM_1_1,NUM_1_2,COMBO_END};
const uint16_t PROGMEM artsey_8[] = {NUM_1_2,NUM_1_3,COMBO_END};
const uint16_t PROGMEM artsey_9[] = {NUM_2_1,NUM_2_2,COMBO_END};
const uint16_t PROGMEM artsey_0[] = {NUM_2_2,NUM_2_3,COMBO_END};
combo_t key_combos[] = {
[ARTSEY_H] = COMBO_ACTION(artsey_h),
[ARTSEY_Q] = COMBO_ACTION(artsey_q),
[ARTSEY_U] = COMBO_ACTION(artsey_u),
[ARTSEY_C] = COMBO_ACTION(artsey_c),
[ARTSEY_K] = COMBO_ACTION(artsey_k),
[ARTSEY_B] = COMBO_ACTION(artsey_b),
[ARTSEY_W] = COMBO_ACTION(artsey_w),
[ARTSEY_N] = COMBO_ACTION(artsey_n),
[ARTSEY_F] = COMBO_ACTION(artsey_f),
[ARTSEY_X] = COMBO_ACTION(artsey_x),
[ARTSEY_J] = COMBO_ACTION(artsey_j),
[ARTSEY_M] = COMBO_ACTION(artsey_m),
[ARTSEY_P] = COMBO_ACTION(artsey_p),
[ARTSEY_V] = COMBO_ACTION(artsey_v),
[ARTSEY_L] = COMBO_ACTION(artsey_l),
[ARTSEY_Z] = COMBO_ACTION(artsey_z),
[ARTSEY_D] = COMBO_ACTION(artsey_d),
[ARTSEY_G] = COMBO_ACTION(artsey_g),
[ARTSEY_SPACE] = COMBO_ACTION(artsey_space),
[ARTSEY_SPACE_2] = COMBO_ACTION(artsey_space_2),
[ARTSEY_BACKSPACE] = COMBO_ACTION(artsey_backspace),
[ARTSEY_BACKSPACE_2] = COMBO_ACTION(artsey_backspace_2),
[ARTSEY_DEL] = COMBO_ACTION(artsey_del),
[ARTSEY_DEL_2] = COMBO_ACTION(artsey_del_2),
[ARTSEY_ENTER] = COMBO_ACTION(artsey_enter),
[ARTSEY_ENTER_2] = COMBO_ACTION(artsey_enter_2),
[ARTSEY_ESCAPE] = COMBO_ACTION(artsey_escape),
[ARTSEY_ESCAPE_2] = COMBO_ACTION(artsey_escape_2),
[ARTSEY_QUOTE] = COMBO_ACTION(artsey_quote),
[ARTSEY_BANG] = COMBO_ACTION(artsey_bang),
[ARTSEY_QUEST] = COMBO_ACTION(artsey_quest),
[ARTSEY_PERIOD] = COMBO_ACTION(artsey_period),
[ARTSEY_COMMA] = COMBO_ACTION(artsey_comma),
[ARTSEY_SLASH] = COMBO_ACTION(artsey_slash),
[ARTSEY_TAB] = COMBO_ACTION(artsey_tab),
[ARTSEY_OS_SHIFT] = COMBO_ACTION(artsey_os_shift),
[ARTSEY_CTRL] = COMBO_ACTION(artsey_ctrl),
[ARTSEY_CTRL_2] = COMBO_ACTION(artsey_ctrl_2),
[ARTSEY_CTRL_3] = COMBO_ACTION(artsey_ctrl_3),
[ARTSEY_GUI] = COMBO_ACTION(artsey_gui),
[ARTSEY_GUI_2] = COMBO_ACTION(artsey_gui_2),
[ARTSEY_GUI_3] = COMBO_ACTION(artsey_gui_3),
[ARTSEY_ALT] = COMBO_ACTION(artsey_alt),
[ARTSEY_ALT_2] = COMBO_ACTION(artsey_alt_2),
[ARTSEY_ALT_3] = COMBO_ACTION(artsey_alt_3),
[ARTSEY_SHIFT] = COMBO_ACTION(artsey_shift),
[ARTSEY_SHIFT2] = COMBO_ACTION(artsey_shift2),
[ARSEY_PANIC] = COMBO_ACTION(arsey_panic),
[ARSEY_PANIC_2] = COMBO_ACTION(arsey_panic_2),
[ARSEY_PANIC_3] = COMBO_ACTION(arsey_panic_3),
[ARTSEY_NAV] = COMBO_ACTION(artsey_nav),
[ARTSEY_MAIN1] = COMBO_ACTION(artsey_main1),
[ARTSEY_MOU] = COMBO_ACTION(artsey_mou),
[ARTSEY_MAIN2] = COMBO_ACTION(artsey_main2),
[ARTSEY_7] = COMBO_ACTION(artsey_7),
[ARTSEY_8] = COMBO_ACTION(artsey_8),
[ARTSEY_9] = COMBO_ACTION(artsey_9),
[ARTSEY_0] = COMBO_ACTION(artsey_0),
};
void process_combo_event(uint16_t combo_index, bool pressed) {
switch(combo_index) {
case ARTSEY_H:
if (pressed) {SEND_STRING("h");}
break;
case ARTSEY_Q:
if (pressed) {SEND_STRING("q");}
break;
case ARTSEY_U:
if (pressed) {SEND_STRING("u");}
break;
case ARTSEY_C:
if (pressed) {SEND_STRING("c");}
break;
case ARTSEY_K:
if (pressed) {SEND_STRING("k");}
break;
case ARTSEY_B:
if (pressed) {SEND_STRING("b");}
break;
case ARTSEY_W:
if (pressed) {SEND_STRING("w");}
break;
case ARTSEY_N:
if (pressed) {SEND_STRING("n");}
break;
case ARTSEY_F:
if (pressed) {SEND_STRING("f");}
break;
case ARTSEY_X:
if (pressed) {SEND_STRING("x");}
break;
case ARTSEY_J:
if (pressed) {SEND_STRING("j");}
break;
case ARTSEY_M:
if (pressed) {SEND_STRING("m");}
break;
case ARTSEY_P:
if (pressed) {SEND_STRING("p");}
break;
case ARTSEY_V:
if (pressed) {SEND_STRING("v");}
break;
case ARTSEY_L:
if (pressed) {SEND_STRING("l");}
break;
case ARTSEY_Z:
if (pressed) {SEND_STRING("z");}
break;
case ARTSEY_D:
if (pressed) {SEND_STRING("d");}
break;
case ARTSEY_G:
if (pressed) {SEND_STRING("g");}
break;
case ARTSEY_SPACE:
if (pressed) {SEND_STRING(" ");}
break;
case ARTSEY_SPACE_2:
if (pressed) {SEND_STRING(" ");}
break;
case ARTSEY_BACKSPACE:
if (pressed) {SEND_STRING(SS_TAP(X_BSPC));}
break;
case ARTSEY_BACKSPACE_2:
if (pressed) {SEND_STRING(SS_TAP(X_BSPC));}
break;
case ARTSEY_DEL:
if (pressed) {SEND_STRING(SS_TAP(X_DEL));}
break;
case ARTSEY_DEL_2:
if (pressed) {SEND_STRING(SS_TAP(X_DEL));}
break;
case ARTSEY_ENTER:
if (pressed) {SEND_STRING(SS_TAP(X_ENTER));}
break;
case ARTSEY_ENTER_2:
if (pressed) {SEND_STRING(SS_TAP(X_ENTER));}
break;
case ARTSEY_ESCAPE:
if (pressed) {SEND_STRING(SS_TAP(X_ESC));}
break;
case ARTSEY_ESCAPE_2:
if (pressed) {SEND_STRING(SS_TAP(X_ESC));}
break;
case ARTSEY_QUOTE:
if (pressed) {SEND_STRING("'");}
break;
case ARTSEY_BANG:
if (pressed) {SEND_STRING("!");}
break;
case ARTSEY_QUEST:
if (pressed) {SEND_STRING("?");}
break;
case ARTSEY_PERIOD:
if (pressed) {SEND_STRING(".");}
break;
case ARTSEY_COMMA:
if (pressed) {SEND_STRING(",");}
break;
case ARTSEY_SLASH:
if (pressed) {SEND_STRING("/");}
break;
case ARTSEY_TAB:
if (pressed) {SEND_STRING(SS_TAP(X_TAB));}
break;
case ARTSEY_OS_SHIFT:
if (pressed) {add_oneshot_mods(MOD_BIT(KC_LSFT));}
break;
case ARTSEY_CTRL:
if (pressed) {
 if (get_mods() & MOD_MASK_CTRL){
 del_mods(MOD_MASK_CTRL);;
 } else {
 add_mods(MOD_MASK_CTRL); } ;}
break;
case ARTSEY_CTRL_2:
if (pressed) {
 if (get_mods() & MOD_MASK_CTRL){
 del_mods(MOD_MASK_CTRL);;
 } else {
 add_mods(MOD_MASK_CTRL); } ;}
break;
case ARTSEY_CTRL_3:
if (pressed) {
 if (get_mods() & MOD_MASK_CTRL){
 del_mods(MOD_MASK_CTRL);;
 } else {
 add_mods(MOD_MASK_CTRL); } ;}
break;
case ARTSEY_GUI:
if (pressed) {
 if (get_mods() & MOD_MASK_GUI){
 del_mods(MOD_MASK_GUI);;
 } else {
 add_mods(MOD_MASK_GUI); } ;}
break;
case ARTSEY_GUI_2:
if (pressed) {
 if (get_mods() & MOD_MASK_GUI){
 del_mods(MOD_MASK_GUI);;
 } else {
 add_mods(MOD_MASK_GUI); } ;}
break;
case ARTSEY_GUI_3:
if (pressed) {
 if (get_mods() & MOD_MASK_GUI){
 del_mods(MOD_MASK_GUI);;
 } else {
 add_mods(MOD_MASK_GUI); } ;}
break;
case ARTSEY_ALT:
if (pressed) {
 if (get_mods() & MOD_MASK_ALT){
 del_mods(MOD_MASK_ALT);;
 } else {
 add_mods(MOD_MASK_ALT); } ;}
break;
case ARTSEY_ALT_2:
if (pressed) {
 if (get_mods() & MOD_MASK_ALT){
 del_mods(MOD_MASK_ALT);;
 } else {
 add_mods(MOD_MASK_ALT); } ;}
break;
case ARTSEY_ALT_3:
if (pressed) {
 if (get_mods() & MOD_MASK_ALT){
 del_mods(MOD_MASK_ALT);;
 } else {
 add_mods(MOD_MASK_ALT); } ;}
break;
case ARTSEY_SHIFT:
if (pressed) {
 if (get_mods() & MOD_MASK_SHIFT){
 del_mods(MOD_MASK_SHIFT);;
 } else {
 add_mods(MOD_MASK_SHIFT); } ;}
break;
case ARTSEY_SHIFT2:
if (pressed) {
 if (get_mods() & MOD_MASK_SHIFT){
 del_mods(MOD_MASK_SHIFT);;
 } else {
 add_mods(MOD_MASK_SHIFT); } ;}
break;
case ARSEY_PANIC:
if (pressed) {clear_mods();
 layer_move(_ART_BASE);;}
break;
case ARSEY_PANIC_2:
if (pressed) {clear_mods();
 layer_move(_ART_BASE);;}
break;
case ARSEY_PANIC_3:
if (pressed) {clear_mods();
 layer_move(_ART_BASE);;}
break;
case ARTSEY_NAV:
if (pressed) {layer_move(_ART_NAV);}
break;
case ARTSEY_MAIN1:
if (pressed) {layer_move(_ART_BASE);}
break;
case ARTSEY_MOU:
if (pressed) {layer_move(_ART_MOU);}
break;
case ARTSEY_MAIN2:
if (pressed) {layer_move(_ART_BASE);}
break;
case ARTSEY_7:
if (pressed) {SEND_STRING("7");}
break;
case ARTSEY_8:
if (pressed) {SEND_STRING("8");}
break;
case ARTSEY_9:
if (pressed) {SEND_STRING("9");}
break;
case ARTSEY_0:
if (pressed) {SEND_STRING("0");}
break;
}
}


Create "config.c", and paste the following code:

// Copyright 2024 Yuval (@Yuval)
// SPDX-License-Identifier: GPL-2.0-or-later


#pragma once
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 250U
#define JOYSTICK_BUTTON_COUNT 0
#define JOYSTICK_AXES_COUNT 2


Create "rules.mk", and paste the following code:

# This file intentionally left blank
COMBO_ENABLE = yes
BOOTMAGIC_ENABLE = yes      # Enable Bootmagic Lite
MOUSEKEY_ENABLE = yes       # Mouse keys
EXTRAKEY_ENABLE = yes       # Audio control and System control
CONSOLE_ENABLE = yes         # Console for debug
COMMAND_ENABLE = no         # Commands for debug and configuration
NKRO_ENABLE = no            # Enable N-Key Rollover
JOYSTICK_ENABLE = yes
JOYSTICK_DRIVER = analog


Now save everything and go back to QMK MSYS.


In QMK MSYS, type:

qmk compile -kb your_keyboard_name -km defult

Replace 'your_keyboard_name' with your keyboard's name.

Finishing Touches

P1180068-Enhanced-NR.jpg
P1180084.jpg
P1180086-Enhanced-NR.jpg
P1180091.jpg

Now, after everything is soldered and connected, you can test the keyboard.

Attach the bottom cover to the shell using M3x screws. We recommend routing the cables neatly and ensuring they are not bent excessively.

We added rubber feet to the bottom to prevent the device from moving on the desk.

Insert any MX-style keycaps into the switches, and your keyboard is ready to use.

Connect it with a USB-C cable, and you are done. Enjoy! 😊

Documentation

IMG_4047-EditE.jpg
IMG_4020.jpg
IMG_4026.jpg
IMG_4071.jpg
IMG_4081.jpg
IMG_4867.jpg

Creating this project involved a lot of experimentation. We tested various angles and button heights, tried different codes, and explored multiple shapes.

Here, you can see the process we went through. Remember, this is an open-source project, and you are welcome to continue developing and improving it.

We want to thank everyone who helped us with this project:

Enrico Bassi, @kapyuval, @electronegative


For more info on the Artsey project click here.


The project was made by Alexander Savelyev, Idan Eldar, and Mariya Levytska.