(c) 2012-2014 Chris Andreae
An extensively programmable USB keyboard firmware for AVR microcontrollers, easily adaptable to new keyboard hardware.
- Kinesis Contoured (Advantage, Professional, Model 110) with drop-in microcontroller replacement (see schematic/ directory)
- Ergodox (on native Teensy hardware)
Can support most ATMega series AVR microcontrollers, using LUFA if native USB is available and VUSB otherwise. The macro and program-interpreter features require an external AT24C164 or compatible I2C EEPROM. (This EEPROM is already built in to Kinesis Advantage/Professional hardware.)
- Dynamically reprogrammable keyboard layout, including 'keypad layer' support (two independent key layouts toggled by a 'keypad' key).
- Onboard layout profiles to save/restore up to ten separate programmed keyboard layouts.
- Enhanced text macros that fully support modifier keys and can be triggered by any combination of up to four keys.
- Programming, macro recording and layout backup save/load can be performed entirely on-keyboard with no additional software.
- Also appears as USB mouse, mouse functions can be be bound to keys.
- Built-in virtual machine interpreter for running up to six concurrent independent tasks.
- Buzzer audio support (included in Kinesis hardware)
- USB API for configuring, remapping and uploading programs to the keyboard. (C++ GUI client and Ruby library included.)
To build for a non-USB-capable AVR using the V-USB library:
make -f Makefile.vusb HARDWARE_VARIANT=<variant>
Currently supported hardware variants for non-USB AVRs are:
KINESIS
(Kinesis Advantage/Professional, ATMega32, board design in schematic/Kinesis{Advantage/Professional})KINESIS110
(Kinesis Model 110, ATMega32, board design in schematic/KinesisModel110)
To build for a USB-capable AVR using the LUFA library:
make -f Makefile.lufa HARDWARE_VARIANT=<variant> HAS_EXTERNAL_STORAGE={1,0}
Currently supported hardware variants for USB AVRs are:
ERGODOX
(Ergodox, ATMega32u4 (Teensy))
The default key layout for each hardware type can be found in the subdirectory layouts/
Kinesis: Program + F12
Ergodox: Program + P
Press the above key combination to enter programming mode. In programming mode, first press a source key to select the action of that key in the default layout. Then, press a destination key to assign the selected action to. Repeat source and destination keys as desired to continue remapping. The keypad key may be pressed at any time to switch layers, in order to allow copying keys between layers. The keypad and program keys may not be themselves remapped. When done, press the above programming-mode key combination to leave programming mode.
- Save current layout to backup slot =
Progrm + S + [1 - 0]
- Restore layout from backup slot =
Progrm + L + [1 - 0]
- Delete saved layout from backup slot =
Progrm + D + [1 - 0]
In addition to the current layout, you can create up to 10 independent backup layouts.
Backup layouts allow you to rapidly switch between different keyboard mapping configurations.
Each backup layout slot is associated with a number key from 1
to 0
. Layouts are
stored as their difference from the factory-default layout: the more keys that are
remapped from their default positions, the more space that a backup layout will consume.
In total, there is space for 256 key remappings shared between all backup layouts.
Kinesis: Program + F7
Ergodox: Program + Z
Resets the current layout to the default. Saved layouts are not affected.
Kinesis: Program + LShift + F7
Ergodox: Program + LShift + Z
Resets all keyboard customizations (current layout, saved layouts, macros, programs).
Kinesis: Program + F11
Ergodox: Program + M
Press the above key combination to enter macro recording mode. Then, press and release a combination of up to four keys as a trigger for the macro. Then, type the contents of the macro. Macros are dynamically sized: you can define up to 50 macros, whose size in total must be under 1022 bytes (each key press or release in the macro consumes one byte). To finish recording the macro, press the above macro recording key combination again.
To delete a macro, enter macro recording mode, press the macro's trigger combination, and then immediately exit macro mode by pressing the above macro recording key combination.
Program + \
When key-click is enabled, a noise will be each time the keyboard registers a keypress. This can be useful to learn to type without 'bottoming-out' the keys.
The keyboard includes a built in virtual machine interpreter which can run up to six concurrent programs. A program is bound to a trigger combination like a macro. Once the combination is pressed, the program starts executing and continues until it finishes. Programs can inspect the state of the keyboard, cause keys to be pressed and released, and perform other functions as documented in the section Compiler below. Programs must be uploaded to the keyboard using the client software as described in the section GUI Client below.
The keyboard also supports a GUI client which uses USB vendor commands to reprogram the keyboard. The client provides:
- Layout viewing, remapping and resetting
- Macro trigger editing and macro removal (macro body editing is yet to come)
- Program uploading
The GUI client is written in C++/QT and requires libusb. Run qmake
then
make
in the qtclient/
subdirectory to build. A Ruby library is
also provided to communicate with the keyboard, in the ruby-client
subdirectory.
The keyboard can run small compiled programs written in a C-like language. To
build the compiler, run cabal build
in the compiler/
subdirectory. The
GHC Haskell compiler is required to build.
To compile a program:
keyc -oprogram.k program.kc
See compiler/examples/
for some example programs.
A program is a set of global variable and function declarations. A function
named main
must be present. Control structures are C-like (if, while,
for, return), however pointers, arrays and goto are not present. Additionally,
the exit
keyword may be used to terminate the program at any time.
Local variables are lexically scoped, and declarations may hide global variables or local variables in enclosing scopes.
The language features two signed types, byte
(8-bit) and short
(16-bit). Bytes may be automatically promoted (with sign extension) to shorts,
but to truncate a short to a byte requres a cast. As in Java, unsigned values
may be provided using hexadecimal literals, but will be treated by arithmetic as
signed. Certain library functions expect unsigned values. Bare literals are
interpreted as bytes: to specify a short literal, append a s
.
Programs by default are run in very small stacks (48 bytes), so unbounded
recursion is not recommended. This can be increased on larger memory devices by
changing STACK_SIZE
in interpreter.h.
The system library includes the following functions:
-
void pressKey(byte h_keycode)
Causes the argument (unsigned byte) HID (not physical) key to be pressed. Does not return until a report has been sent. -
void releaseKey(byte h_keycode)
Causes the argument (unsigned byte) HID key to be released, if it was being pressed by this program. -
byte checkKey(byte h_keycode)
Checks the current state for any key mapped to the argument (unsigned byte) HID keycode, returns 1 or 0. -
byte checkPhysKey(byte p_keycode)
Checks the current state for the argument (unsigned byte) physical key. An argument of 0 means the the key that triggered the program. Returns 1 or 0. -
byte waitKey(byte key, short timeout)
Causes execution to be stopped until a key mapped to the argument (unsigned byte) HID keycode (or 0 for any key) has been pressed, or the argument time has elapsed (0 for indefinite). Returns the mapped HID keycode of the first pressed key (not necessarily the argument) or 0 if timed out. -
byte waitPhysKey(byte key, short timeout)
Like waitKey(), but takes an argument physical keycode or 0 for the key that triggered the program. Returns 1 if pressed or 0 if timed out. -
void delay(short ms)
Causes execution to be stopped until argument milliseconds have elapsed -
short getUptimeMS()
Returns keyboard uptime in ms truncated to a signed short int -
short getUptime()
Returns keyboard uptime in seconds truncated to signed short int -
void buzz(short time)
Runs the buzzer for the next 'time' ms -
void buzzAt(short time, unsigned byte freq)
Runs the buzzer for the next 'time' ms at frequency (1/(4e-6 * freq)) Hz -
void moveMouse(byte x, byte y)
Moves the mouse by the requested offset next time the mouse report is sent. Does not return until report has been sent. -
void pressMouseButtons(byte buttonMask)
Presses the mouse buttons specified by buttonMask (bits 1-5 = buttons 1-5). Does not return until report has been sent. -
void releaseMouseButtons(byte buttonMask)
Releases the mouse buttons specified by buttonMask (bits 1-5 = buttons 1-5) if they are pressed by this program. Does not return until report has been sent.