Skip to content

Instantly share code, notes, and snippets.

@gleber
Last active September 20, 2024 12:29
Show Gist options
  • Save gleber/8d6c90d16dee45db2dd458814d3dd1fc to your computer and use it in GitHub Desktop.
Save gleber/8d6c90d16dee45db2dd458814d3dd1fc to your computer and use it in GitHub Desktop.
X.Tips X7S Keyboard (2024-04)

QMK config

I've managed to compile a working firmware with this config: https://github.com/gleber/qmk_firmware/commit/54078ec73c0ff2bd01984242f748a837c1954d03

Details

This isn't tested much, just the basics: keys and split setup. LED, bootloader magic key and any other features are not yet implemented. Tested only the Miryoku layout so far.

I've disassembled it and found a few things.

Back of one of the sides looks like this:

A more close up photo of the chip shows some STM32 chip:

If you decode the QR code it gives you a short link to what seem to be JLC PCB page about this specific PCB:

It includes product name 【X.Tips】X7S小分体键盘_-_V2.0_【热插拔】PCB-X7S-V2-R__20240726163326 which translates with Google Translate to 【X.Tips】X7S small split keyboard_-_V2.0_【Hot plug】PCB-X7S-V2-R__20240726163326

And these schematics images for the right side:

and for the left side:

Attached via-x7.json file was sent to one of the buyers by the seller. It works for my keyboard.

{
"name": "X.Tips X7 Keyboard",
"vendorId": "0x5262",
"productId": "0x4E4B",
"keycodes": ["qmk_lighting"],
"menus": [ "qmk_rgblight"],
"matrix": {"rows": 8, "cols": 5},
"layouts": {
"keymap": [
["0,0","0,1","0,2","0,3","0,4",{"x":1},"4,4","4,3","4,2","4,1","4,0"],
["1,0","1,1","1,2","1,3","1,4",{"x":1},"5,4","5,3","5,2","5,1","5,0"],
["2,0","2,1","2,2","2,3","2,4",{"x":1},"6,4","6,3","6,2","6,1","6,0"],
[{"x":2},"3,2","3,3","3,4",{"x":1},"7,4","7,3","7,2"]
]
}
}
{
"name": "X.Tips X7 Keyboard",
"vendorProductId": 1382174283,
"macros": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
"layers": [
[
"KC_Q",
"KC_W",
"KC_E",
"KC_R",
"KC_T",
"KC_A",
"KC_S",
"KC_D",
"KC_F",
"KC_G",
"KC_Z",
"KC_X",
"KC_C",
"KC_V",
"KC_B",
"KC_NO",
"KC_NO",
"MT(MOD_LCTL,KC_ESC)",
"LT(1,KC_SPC)",
"KC_LSFT",
"KC_P",
"KC_O",
"KC_I",
"KC_U",
"KC_Y",
"KC_ENT",
"KC_L",
"KC_K",
"KC_J",
"KC_H",
"KC_BSPC",
"LT(4,KC_DOT)",
"LT(3,KC_COMM)",
"KC_M",
"KC_N",
"KC_NO",
"KC_NO",
"MT(MOD_LGUI,KC_DEL)",
"LT(7,KC_SPC)",
"MT(MOD_LALT,KC_TAB)"
],
[
"KC_7",
"KC_8",
"KC_9",
"KC_PDOT",
"KC_PPLS",
"KC_4",
"KC_5",
"KC_6",
"KC_0",
"KC_PMNS",
"KC_1",
"KC_2",
"KC_3",
"KC_BSPC",
"KC_EQL",
"KC_NO",
"KC_NO",
"KC_LCTL",
"TO(0)",
"MT(MOD_LSFT,KC_PENT)",
"KC_GRV",
"S(KC_BSLS)",
"S(KC_SCLN)",
"S(KC_4)",
"KC_PAST",
"KC_SCLN",
"S(KC_RBRC)",
"S(KC_LBRC)",
"S(KC_3)",
"KC_PSLS",
"KC_QUOT",
"S(KC_DOT)",
"S(KC_COMM)",
"S(KC_7)",
"KC_BSLS",
"KC_NO",
"KC_NO",
"KC_LGUI",
"G(KC_SPC)",
"KC_LALT"
],
[
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO",
"KC_NO"
],
[
"LCAG(KC_P)",
"KC_F9",
"KC_F10",
"KC_F11",
"KC_F12",
"LCAG(KC_ENT)",
"KC_F5",
"KC_F6",
"KC_F7",
"KC_F8",
"LCAG(KC_BSPC)",
"KC_F1",
"KC_F2",
"KC_F3",
"KC_F4",
"KC_NO",
"KC_NO",
"KC_RCTL",
"TT(6)",
"KC_RSFT",
"KC_NLCK",
"KC_APP",
"KC_INS",
"KC_VOLU",
"KC_VOLD",
"KC_SLCK",
"KC_PSCR",
"KC_PAUS",
"KC_MNXT",
"KC_MPRV",
"RCS(KC_O)",
"RCS(KC_X)",
"TO(0)",
"KC_MPLY",
"KC_MUTE",
"KC_NO",
"KC_NO",
"KC_RGUI",
"MO(5)",
"KC_RALT"
],
[
"MEH(KC_Q)",
"MEH(KC_W)",
"MEH(KC_E)",
"MEH(KC_R)",
"MEH(KC_T)",
"MEH(KC_A)",
"MEH(KC_S)",
"MEH(KC_D)",
"MEH(KC_F)",
"MEH(KC_G)",
"MEH(KC_Z)",
"MEH(KC_X)",
"MEH(KC_C)",
"MEH(KC_V)",
"MEH(KC_B)",
"KC_NO",
"MEH(KC_2)",
"MEH(KC_3)",
"MEH(KC_4)",
"MEH(KC_5)",
"MEH(KC_P)",
"MEH(KC_O)",
"MEH(KC_I)",
"MEH(KC_U)",
"MEH(KC_Y)",
"MEH(KC_ENT)",
"MEH(KC_L)",
"MEH(KC_K)",
"MEH(KC_J)",
"MEH(KC_H)",
"MEH(KC_BSPC)",
"TO(0)",
"MEH(KC_COMM)",
"MEH(KC_M)",
"MEH(KC_N)",
"KC_NO",
"KC_NO",
"MEH(KC_0)",
"MEH(KC_9)",
"MEH(KC_6)"
],
[
"RGB_HUI",
"RGB_MOD",
"HYPR(KC_E)",
"HYPR(KC_R)",
"HYPR(KC_T)",
"RGB_SAI",
"RGB_SPI",
"HYPR(KC_D)",
"HYPR(KC_F)",
"HYPR(KC_G)",
"RGB_VAI",
"RGB_TOG",
"HYPR(KC_C)",
"HYPR(KC_V)",
"HYPR(KC_B)",
"HYPR(KC_1)",
"HYPR(KC_2)",
"HYPR(KC_3)",
"HYPR(KC_4)",
"HYPR(KC_5)",
"KC_MS_BTN2",
"KC_MS_UP",
"KC_MS_BTN1",
"A(KC_TAB)",
"A(KC_F4)",
"KC_MS_RIGHT",
"KC_MS_DOWN",
"KC_MS_LEFT",
"C(KC_T)",
"C(KC_W)",
"KC_MS_BTN3",
"KC_MS_BTN5",
"KC_MS_BTN4",
"C(KC_TAB)",
"RCS(KC_TAB)",
"KC_NO",
"KC_NO",
"MEH(KC_RBRC)",
"TO(0)",
"MEH(KC_LBRC)"
],
[
"LCAG(KC_Q)",
"KC_F21",
"KC_F22",
"KC_F23",
"KC_F24",
"LCAG(KC_A)",
"KC_F17",
"KC_F18",
"KC_F19",
"KC_F20",
"LCAG(KC_Z)",
"KC_F13",
"KC_F14",
"KC_F15",
"KC_F16",
"KC_NO",
"KC_NO",
"MT(MOD_LCTL,KC_ESC)",
"TO(0)",
"MT(MOD_LSFT,KC_TAB)",
"KC_PDOT",
"KC_P9",
"KC_P8",
"KC_P7",
"KC_PPLS",
"KC_P0",
"KC_P6",
"KC_P5",
"KC_P4",
"KC_PMNS",
"KC_BSPC",
"KC_P3",
"KC_P2",
"KC_P1",
"KC_PAST",
"KC_NO",
"KC_NO",
"KC_PENT",
"MT(MOD_LSFT,KC_SPC)",
"MT(MOD_LALT,KC_PSLS)"
],
[
"S(KC_GRV)",
"S(KC_5)",
"S(KC_MINS)",
"S(KC_6)",
"S(KC_EQL)",
"S(KC_2)",
"S(KC_QUOT)",
"S(KC_1)",
"S(KC_SLSH)",
"KC_MINS",
"KC_LBRC",
"KC_RBRC",
"S(KC_9)",
"S(KC_0)",
"KC_EQL",
"KC_NO",
"KC_NO",
"KC_RCTL",
"KC_CAPS",
"KC_RSFT",
"KC_PGUP",
"KC_END",
"KC_UP",
"KC_HOME",
"S(KC_8)",
"KC_PGDN",
"KC_RGHT",
"KC_DOWN",
"KC_LEFT",
"KC_SLSH",
"KC_DEL",
"KC_TAB",
"KC_ESC",
"MEH(KC_SLSH)",
"KC_BSLS",
"KC_NO",
"KC_NO",
"KC_RGUI",
"TO(0)",
"KC_RALT"
],
[
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS",
"KC_TRNS"
]
],
"encoders": []
}
@gleber
Copy link
Author

gleber commented Sep 1, 2024

@gleber
Copy link
Author

gleber commented Sep 1, 2024

The switches are JWICK

@gleber
Copy link
Author

gleber commented Sep 1, 2024

The controller on both sides is the same: STM32F103CBT6

@gleber
Copy link
Author

gleber commented Sep 1, 2024

When I've shorted RESET pins on the keyboard, qmk console showed me stm32duino: Maple 003.

@gleber
Copy link
Author

gleber commented Sep 2, 2024

Attempting to read firmware got me this:

$ dfu-util.exe -U firmware.bin -a 2
dfu-util 0.11

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2021 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

Opening DFU capable USB device...
Device ID 1eaf:0003
Device DFU version 0110
Claiming USB DFU Interface...
Setting Alternate Interface #2 ...
Determining device status...
DFU state(2) = dfuIDLE, status(0) = No error condition is present
DFU mode device DFU version 0110
Device returned transfer size 1024
Copying data from DFU device to PC
Upload  [======================== ]  99%         1024 bytes
Error during upload (LIBUSB_ERROR_PIPE)

Received a total of 122880 bytes

I can share the firmware file if someone needs it.

@gleber
Copy link
Author

gleber commented Sep 2, 2024

Also

$ dfu-util -l
dfu-util 0.11

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2021 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

Found DFU: [1eaf:0003] ver=0201, devnum=45, cfg=1, intf=0, path="1-10", alt=2, name="STM32duino bootloader v1.0  Upload to Flash 0x8002000", serial="LLM 003"
Found DFU: [1eaf:0003] ver=0201, devnum=45, cfg=1, intf=0, path="1-10", alt=1, name="STM32duino bootloader v1.0  Upload to Flash 0x8005000", serial="LLM 003"
Found DFU: [1eaf:0003] ver=0201, devnum=45, cfg=1, intf=0, path="1-10", alt=0, name="STM32duino bootloader v1.0  ERROR. Upload to RAM not supported.", serial="LLM 003"

@gleber
Copy link
Author

gleber commented Sep 2, 2024

It is possible to reset EEPROM of each side of the keyboard by pressing Q/Y and plugging it to USB

@gleber
Copy link
Author

gleber commented Sep 2, 2024

Started a thread on QMK discord about flashing it with own firmware: https://discord.com/channels/440868230475677696/473506116718952450/1280200294243762236

@gleber
Copy link
Author

gleber commented Sep 2, 2024

I was able to read and flash back firmware from one of the sides without breaking it

@gleber
Copy link
Author

gleber commented Sep 2, 2024

Left and right firmware files differ, but haven't looked exactly by how much and why

@gleber
Copy link
Author

gleber commented Sep 5, 2024

Key captioned on PCB:

@gleber
Copy link
Author

gleber commented Sep 5, 2024

Keys are connected directly to the chip. I am using this pinout of the chip:
https://robiz.net/image/cache/data/entegre/stm/stm32f103/stm32f103_07-500x500.jpg

And the left side keys are connected like this:

LEFT
Q PA8
A PB0
Z PA3
W PB15
S PB1
X PA4
E PB14
D PB2
C PA5
>< PA0
R PB13
F PB10
V PA6
END PA1
T PB12
G PB11
B PA7
" PA2

@gleber
Copy link
Author

gleber commented Sep 7, 2024

I've managed to compile a working firmware with this config:
gleber/qmk_firmware@54078ec

This isn't tested much, just the basics: keys and split setup. LED, bootloader magic key and any other features are not yet implemented. Tested only the Miryoku layout so far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment