Anatomía de un teclado mecánico: PCB, switches, keycaps, placa, carcasa, controlador, firmware
Texto Anton
Lectura 5 min
03 May 2026
ZMK: firmware wireless. Devicetree, GitHub Actions, ZMK Studio, keymap-editor. Errores comunes (prefijo &kp, puntos y comas)
QMK y Vial son el mundo del cable. Cuando quieres un teclado inalámbrico por Bluetooth, con batería y bajo consumo, el firmware es ZMK; resuelve justo el inalámbrico. Está pensado desde cero para BLE (Bluetooth Low Energy) y para microcontroladores como el nRF52840 (la placa nice!nano, la Seeed XIAO BLE), que llevan radio Bluetooth y gestión de batería integradas. ZMK no se configura en C: usa ficheros devicetree, una sintaxis distinta con sus propias trampas. En este artículo entiendes su flujo de trabajo, escribes un .keymap real y aprendes a evitar los errores que pillan a todo el mundo el primer día.
Conviene avisar de un cambio de terreno respecto a QMK. Allí el keymap era código C que compilabas en tu propia máquina. ZMK introduce dos cosas nuevas a la vez: la compilación ocurre en un servidor remoto (GitHub Actions, lo veremos abajo) y el keymap se escribe en devicetree, no en C. Son dos saltos independientes; este artículo los presenta por separado y, antes del ejemplo, define la sintaxis de devicetree que vas a leer.
QMK soporta cable y poco más; su soporte de BLE es marginal. ZMK nació para resolver justo eso:
Si tu cliente quiere un Corne sin cables que conectar, ZMK + nice!nano es el estándar.
Aquí ZMK hace algo muy distinto a QMK. No compilas en tu máquina: usas tu propio repositorio de configuración (zmk-config) en GitHub, y GitHub Actions compila el firmware en la nube cada vez que haces push. GitHub Actions es el sistema de automatización de GitHub: ejecuta tareas (en este caso, compilar el firmware) en sus propios servidores cuando ocurre un evento en el repositorio, como subir un cambio. El flujo:
zmk-config a partir de la plantilla oficial..keymap y tu .conf.El resultado de la compilación son ficheros .uf2, uno por cada lado del split (corne_left.uf2, corne_right.uf2). Para flashear el nice!nano: conéctalo por USB, pulsa dos veces el botón de reset; el chip arranca en modo bootloader y aparece una unidad de almacenamiento USB llamada NICENANO; arrastras el .uf2 a esa unidad y se reinicia ya con el firmware. Flasheas cada mitad con su .uf2 correspondiente.
Tu repositorio contiene básicamente:
zmk-config/
├── config/
│ ├── corne.keymap # las capas y bindings (devicetree)
│ ├── corne.conf # opciones (BT, deep sleep, etc.)
│ └── west.yml # de dónde sale ZMK
└── build.yaml # qué shields/boards compilar
El .conf es donde activas cosas como el ahorro de energía:
CONFIG_ZMK_SLEEP=y
CONFIG_ZMK_IDLE_SLEEP_TIMEOUT=900000
Devicetree es un lenguaje para describir hardware y su configuración. No es C: no tiene funciones ni variables al uso, sino una jerarquía de nodos entre llaves. Para leer el keymap basta con cuatro reglas de sintaxis:
default_layer { ... };. Dentro van sus propiedades.= y termina siempre en punto y coma: propiedad = valor;. El punto y coma no es opcional.< y >: bindings = < ... >;. El bloque entero cierra con >;.& delante de un nombre es una referencia a un comportamiento (un behavior) ya definido. &kp no es texto: apunta al comportamiento “pulsar tecla”. Sin el &, devicetree no sabe que estás invocando un comportamiento.Los comportamientos que aparecen en el ejemplo son tres:
&kp (key press): envía una tecla. &kp A envía la “a”.&mo (momentary layer): activa una capa mientras mantienes la tecla. &mo 1 activa la capa 1. Es el equivalente al MO() de QMK.&trans (transparent): tecla transparente, deja pasar lo que haya en la capa de debajo.Con esas reglas, el fichero siguiente se lee sin sorpresas.
El keymap es devicetree, no C. Mira un ejemplo de tres capas para el Corne:
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>
/ {
keymap {
compatible = "zmk,keymap";
default_layer {
bindings = <
&kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSPC
&kp LCTRL &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT
&kp LSHFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp ESC
&kp LGUI &mo 1 &kp SPACE &kp RET &mo 2 &kp RALT
>;
};
lower_layer {
bindings = <
&kp TAB &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp BSPC
&kp LCTRL &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
&kp LSHFT &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
&kp LGUI &trans &kp SPACE &kp RET &mo 2 &kp RALT
>;
};
raise_layer {
bindings = <
&kp TAB &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp BSPC
&kp LCTRL &trans &trans &trans &trans &trans &kp LEFT &kp DOWN &kp UP &kp RIGHT &trans &trans
&kp LSHFT &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
&kp LGUI &mo 1 &kp SPACE &kp RET &trans &kp RALT
>;
};
};
};
Cosas a entender:
&kp es el behavior “key press”. &kp A envía la “a”. El & indica que referencias un comportamiento.&mo 1 activa la capa 1 mientras la mantienes (equivale al MO() de QMK).&trans es transparente (deja pasar la capa de abajo).Para Bluetooth añades bindings como &bt BT_SEL 0 (selecciona el primer perfil) o &bt BT_CLR (borra emparejamientos), normalmente en una capa de ajustes.
Como devicetree no es C, hay tres errores clásicos que generan fallos de compilación crípticos:
&. Es &kp A, no kp A. Sin el &, el compilador no reconoce el comportamiento y la build falla.bindings = < ... >; termina en >;. Olvidar el ; rompe toda la compilación con un error que no apunta claramente al sitio.N1…N0, la comilla es SQT, la barra FSLH. No asumas los nombres de QMK; consulta la lista de keycodes de ZMK.Cuando una build de GitHub Actions falla, abre el log del job: ZMK muestra el devicetree procesado y el error de compilación. Casi siempre es un & o un ; que falta.
ZMK también tiene su respuesta al “no quiero recompilar para cada cambio”: ZMK Studio. Es una aplicación (de escritorio y web) que permite cambiar las capas del keymap en tiempo real sobre un teclado ya flasheado, sin volver a pasar por GitHub Actions. Para usarla, tu keymap tiene que incluir un binding &studio_unlock que desbloquea la edición (igual que el unlock de Vial, por seguridad física). Para el aprendizaje inicial también existen editores visuales como keymap-editor, que te dibujan el teclado y generan el devicetree por ti, útil para no pelearte con la sintaxis al principio.
Para un Corne inalámbrico, monta nice!nano (nRF52840), usa un zmk-config propio en GitHub y deja que Actions compile. Empieza con un keymap sencillo, verifica que cada mitad empareja por BT y añade complejidad poco a poco. Si quieres que el cliente ajuste capas sin tocar GitHub, habilita ZMK Studio. Y memoriza la regla de oro de la sintaxis: siempre & delante del behavior, siempre ; al cerrar el bloque. Esos dos detalles te ahorrarán el 90% de los fallos de compilación.
Del blog al libro Este post forma parte del temario de Construir teclados split. El libro completo incluye las dos rutas de ensamblaje (v3 y v4) completas y los keymaps del repo complementario.
Ver el libroAnatomía de un teclado mecánico: PCB, switches, keycaps, placa, carcasa, controlador, firmware
Texto Anton
Lectura 5 min
Estamos preparando algo. Vuelve pronto.
Novedades y montajes.
Directo a tu correo.
Sin spam.
Sin anuncios.
Al suscribirte aceptas recibir correos del taller. Puedes darte de baja cuando quieras.