/mankoffskey

Custom mankoff keyboard

MIT LicenseMIT

Table of contents

Layout

kb.py

import board

from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
from kmk.scanners import DiodeOrientation

# print(dir(board))
class KMKKeyboard(_KMKKeyboard):
    col_pins = (board.D27, board.D26, board.D22, board.D20, board.D23, board.D21)
    row_pins = (board.D5, board.D6, board.D7, board.D8, board.D9)
    diode_orientation = DiodeOrientation.COLUMNS
    uart_pin = board.RX
    rgb_pixel_pin = board.TX
    data_pin = board.RX
    i2c = board.I2C
    SCL=board.SCL
    SDA=board.SDA

    coord_mapping = [
    0,  1,  2,  3,  4,  5,           35, 34, 33, 32, 31, 30,
    6,  7,  8,  9, 10, 11,           41, 40, 39, 38, 37, 36,
    12, 13, 14, 15, 16, 17,          47, 46, 45, 44, 43, 42,
    18, 19, 20, 21, 22, 23, 29,  59, 53, 52, 51, 50, 49, 48,
              25, 26, 27, 28,       58, 57, 56, 55,
    ]

main.py

from kb import KMKKeyboard
from kmk.keys import KC
from kmk.modules.layers import Layers
from kmk.modules.modtap import ModTap
from kmk.hid import HIDModes
from kmk.handlers.sequences import send_string
import supervisor
from kmk.modules.split import Split, SplitSide, SplitType
keyboard = KMKKeyboard()
modtap = ModTap()
layers_ext = Layers()
keyboard.modules.append(layers_ext)
keyboard.modules.append(modtap)

from kmk.extensions.media_keys import MediaKeys
keyboard.extensions.append(MediaKeys())

from kmk.modules.mouse_keys import MouseKeys
keyboard.modules.append(MouseKeys())

from kmk.modules.oneshot import OneShot
oneshot = OneShot()
# optional: set a custom tap timeout in ms (default: 1000ms)
# oneshot.tap_time = 1500
keyboard.modules.append(oneshot)

# TODO Comment one of these on each side
split_side = SplitSide.LEFT
#split_side = SplitSide.RIGHT
split = Split(split_side = split_side,use_pio=True)
keyboard.modules.append(split)

# Cleaner key names
_______ = KC.TRNS
XXXXXXX = KC.NO

keyboard.debug_enabled = True


class DV:
    # Build DV map for when host OS is in Dvorak mode
    # https://kmkfw.zulipchat.com/user_uploads/49575/-m7O9LXFqS9ilkgMYVT5D2Iz/dvorkeys-4110265058.png
    QUOT=KC.Q; COMM=KC.W; DOT=KC.E
    P=KC.R; Y=KC.T; F=KC.Y; G=KC.U; C=KC.I; R=KC.O; L=KC.P
    A=KC.A; O=KC.S; E=KC.D; U=KC.F; I=KC.G; D=KC.H; H=KC.J; T=KC.K; N=KC.L; S=KC.SCLN
    SCLN=KC.Z; Q=KC.X; J=KC.C; K=KC.V; X=KC.B; B=KC.N; M=KC.M
    W=KC.COMM; V=KC.DOT; Z=KC.SLSH
    GRV=KC.GRV

    LBRC=KC.MINS; RBRC=KC.EQL
    LCBR=KC.UNDS; RCBR=KC.PLUS
    SLSH=KC.LBRC; EQL=KC.RBRC
    PLUS=KC.LSFT(KC.RBRC); MINS=KC.QUOT
    COLN=KC.LSFT(KC.Z)

# class DV:
#     # Build DV map for when host OS is in QWERTY mode
#     QUOT=KC.QUOT; COMM=KC.COMM; DOT=KC.DOT
#     P=KC.P; Y=KC.Y; F=KC.F; G=KC.G; C=KC.C; R=KC.R; L=KC.L
#     A=KC.A; O=KC.O; E=KC.E; U=KC.U; I=KC.I; D=KC.D; H=KC.H; T=KC.T; N=KC.N; S=KC.S
#     SCLN=KC.SCLN; Q=KC.Q; J=KC.J; K=KC.K; X=KC.X; B=KC.B; M=KC.M
#     W=KC.W; V=KC.V; Z=KC.Z
#     GRV=KC.GRV

#     LBRC=KC.LBRC; RBRC=KC.RBRC
#     LCBR=KC.LCBR; RCBR=KC.RCBR
#     SLSH=KC.SLSH; EQL=KC.EQL
#     PLUS=KC.PLUS; MINS=KC.MINS
#     COLN=KC.COLN

class PV:
    # Paraview. See Edit (menu) > Settings... > Camera > 3D Interaction Options
    ROT = KC.MB_LMB; PAN = KC.MB_MMB; ZOOM = KC.MB_RMB
    ROLL = KC.LSFT(KC.MB_LMB); MULTIROT = KC.LSFT(KC.MB_MMB); SKYBOXROT = KC.LCTL(KC.MB_LMB)
    ZOOMMOUSE = KC.LECTL(KC.MB_RMB)

# http://kmkfw.io/docs/keycodes

keyboard.keymap = [

[ # 0: Dvorak
    DV.GRV,  KC.N1,   KC.N2,   KC.N3,  KC.N4,    KC.N5,                     KC.N6, KC.N7, KC.N8, KC.N9, KC.N0, KC.BSPACE, \
    KC.TAB,  DV.QUOT, DV.COMM, DV.DOT,  DV.P,    DV.Y,                      DV.F,  DV.G,  DV.C,  DV.R,  DV.L,  DV.SLSH, \
    KC.LCTL, DV.A,    DV.O,    DV.E,    DV.U,    DV.I,                      DV.D,  DV.H,  DV.T,  DV.N,  DV.S,  DV.MINS, \
    KC.LSFT, DV.SCLN, DV.Q,    DV.J,    DV.K,    DV.X, KC.LT(1,_______), KC.LT(4, KC.ESC), DV.B,  DV.M,  DV.W,  DV.V,  DV.Z,  XXXXXXX,  \
                        KC.LT(4,_______), KC.LGUI,  KC.LALT,  KC.ENT,          KC.LSFT, KC.LT(2, KC.SPACE), KC.LT(3, KC.BSPACE), KC.DEL,
],

[ # 1: Nav
    KC.TO(5), _______, _______, _______, _______, _______,                      _______, _______, _______, _______, _______, _______, \
    _______, _______, _______, _______, _______, _______,                      KC.MW_UP, _______, KC.UP,   _______, _______, _______, \
    _______, _______, _______, _______, KC.MW_DN, KC.PGUP,                      KC.MW_DN, KC.LEFT, KC.DOWN, KC.RGHT, KC.MS_UP, _______, \
    _______, _______, KC.PGUP, KC.PGDN, KC.MW_UP, KC.PGDN, _______,    KC.TO(0), _______, _______, _______, KC.MS_LT, KC.MS_DN, KC.MS_RT, \
                           _______, _______, _______, _______,             _______, KC.MB_MMB, KC.MB_LMB, KC.MB_RMB,
],
    
[ # 2: Num
    _______, _______, _______, _______, _______, _______,                         _______, _______, _______, _______, _______, _______, \
    _______, _______, DV.COMM, KC.LPRN, KC.RPRN, DV.SLSH,                         DV.PLUS, KC.N7, KC.N8, KC.N9, KC.N0, DV.PLUS, \
    _______, _______, DV.DOT,  DV.LBRC, DV.RBRC, KC.ASTR,                         DV.MINS, KC.N4, KC.N5, KC.N6, KC.N0, DV.MINS, \
    DV.COLN, _______, _______, DV.LCBR, DV.RCBR, KC.BSLS, XXXXXXX,       XXXXXXX, DV.EQL,  KC.N1, KC.N2, KC.N3, KC.N0, DV.COLN, \
                           XXXXXXX, KC.SPC,  KC.BSPC, KC.LALT,             _______, _______, _______, XXXXXXX,

],

[ # 3: Sym
    _______, _______, _______, _______, _______, _______,                         _______, _______, _______, _______, _______, _______, \
    _______, _______, _______, _______, KC.PERC, _______,                         _______, KC.CIRC, _______, _______, _______, _______, \
    _______, KC.EXLM, KC.AMPR, KC.AT,   KC.HASH, _______,                          _______, KC.TILD, DV.SLSH, KC.PIPE, KC.BSLS, _______, \
    _______, _______, _______, _______, KC.DLR,  _______, KC.OS(KC.RALT), XXXXXXX, _______, _______, KC.BSLS, _______, _______, _______, \
                            XXXXXXX, _______, _______, _______,               _______, _______, _______, XXXXXXX,
],
    
[ # 4: Fn
    _______, _______, _______, _______, _______, _______,                         _______, _______, _______, _______, _______, _______, \
    _______, _______, _______, KC.VOLU, _______, _______,                         KC.F12, KC.F7, KC.F8, KC.F9, _______, _______, \
    _______, _______, _______, KC.VOLD, _______, _______,                         KC.F11, KC.F4, KC.F5, KC.F6, _______, _______, \
    _______, _______, _______, KC.MUTE, _______, _______,XXXXXXX,       XXXXXXX,  KC.F10, KC.F1, KC.F2, KC.F3, _______, _______, \
    XXXXXXX, _______, _______, _______,             _______, _______, _______, XXXXXXX,
],


[ # 5: Paraview
    # ROT = KC.MB_LMB; PAN = KC.MB_MMB; ZOOM = KC.MB_RMB
    # ROLL = KC.LSFT(KC.MB_LMB); MULTIROT = KC.LSFT(KC.MB_MMB); SKYBOXROT = KC.LCTL(KC.MB_LMB)
    # ZOOMMOUSE = KC.LECTL(KC.MB_RMB)
    _______, _______, _______, _______, _______, _______,                      _______, _______, _______, _______, _______, _______, \
    _______, _______, _______, PV.ZOOMMOUSE, PV.ZOOM, _______,                KC.MW_UP, _______, KC.UP,   _______, _______, _______, \
    _______, _______, PV.SKYBOXROT, PV.PAN,  PV.ROT,  _______,                KC.MW_DN, KC.LEFT, KC.DOWN, KC.RGHT, KC.MS_UP, _______, \
    _______, _______, _______, PV.MULTIROT, PV.ROLL, _______, _______,    KC.TO(0), _______, _______, _______, KC.MS_LT, KC.MS_DN, KC.MS_RT, \
                           _______, _______, _______, _______,             _______, KC.MB_MMB, KC.MB_LMB, KC.MB_RMB,
],

# [ # n: desc
#     _______, _______, _______, _______, _______, _______,                      _______, _______, _______, _______, _______, _______, \
#     _______, _______, _______, _______, _______, _______,                      _______, _______, _______, _______, _______, _______, \
#     _______, _______, _______, _______, _______, _______,                      _______, _______, _______, _______, _______, _______, \
#     _______, _______, _______, _______, _______, _______, _______,    _______, _______, _______, _______, _______, _______, _______, \
#                            _______, _______, _______, _______,             _______, _______, _______, _______,
# ],

]

if __name__ == '__main__':
    keyboard.go(hid_type=HIDModes.USB)

Graphical layout

This PNG can be edited on https://drawio.png

./lily58.drawio.png

Heatmap

Capture

# turn on debugging:
# keyboard.debug_enabled = True # in main.py

# capture everything
cat < /dev/ttyACM0 |grep pressed >> logfile 

Process

grep -Eo 'pressed={[0-9].*|key_number\ [0-9].*' ~/logfile|cut -d":" -f1|grep -o "[[:digit:]]*"|sort|uniq -c | sort -n > ~/logfile.sort
head ~/logfile.sort
 5 35
12 48
14 5
14 55
21 21
28 34
34 4
36 3
37 49
40 59
import numpy as np
import pandas as pd

# coord_mapping = [
# 0,  1,  2,  3,  4,  5,           35, 34, 33, 32, 31, 30,
# 6,  7,  8,  9, 10, 11,           41, 40, 39, 38, 37, 36,
# 12, 13, 14, 15, 16, 17,          47, 46, 45, 44, 43, 42,
# 18, 19, 20, 21, 22, 23, 29,  59, 53, 52, 51, 50, 49, 48,
#           25, 26, 27, 28,       58, 57, 56, 55,
# ]

df = pd.read_csv('~/logfile.sort', sep='\s+', header=None, index_col=1)
# df.head()

left_dict = {0:(0,0),1:(0,1),2:(0,2),3:(0,3),4:(0,4),5:(0,5),
             6:(1,0),7:(1,1),8:(1,2),9:(1,3),10:(1,4),11:(1,5),
             12:(2,0),13:(2,1),14:(2,2),15:(2,3),16:(2,4),17:(2,5),
             18:(3,0),19:(3,1),20:(3,2),21:(3,3),22:(3,4),23:(3,5),29:(3,6),
                                      25:(4,3),26:(4,4),27:(4,5),28:(4,6)}

right_dict = {35:(0,1),34:(0,2),33:(0,3),32:(0,4),31:(0,5),30:(0,6),
              41:(1,1),40:(1,2),39:(1,3),38:(1,4),37:(1,5),36:(1,6),
              47:(2,1),46:(2,2),45:(2,3),44:(2,4),43:(2,5),42:(2,6),
     59:(3,0),53:(3,1),52:(3,2),51:(3,3),50:(3,4),49:(3,5),48:(3,6),
      58:(4,0),57:(4,1),56:(4,2),55:(4,3)}

left = np.zeros((7,5)).T
right = np.zeros_like(left)

for key in df.index:
     if key <= 29:
          x,y = left_dict[key]
          left[x,y] = df.loc[key].values[0]
     else:
          x,y = right_dict[key]
          right[x,y] = df.loc[key].values[0]

left[left == 0] = np.nan          
right[right == 0] = np.nan          

merge = np.hstack((left,right))
import matplotlib.pyplot as plt
plt.imshow(np.log10(merge))
plt.colorbar(fraction=0.017, pad=0.02, label='log$_{10}$ press count [#]')

for y in range(merge.shape[0]):
     for x in range(merge.shape[1]):
          val = merge[y,x]
          if ~np.isnan(val):
               plt.text(x-0.33,y,str(int(val)), color='w', horizontalalignment='left')

plt.savefig('/home/kdm/projects/KB/mankoffskey/heatmap.png')

./figs_tmp/ca93a925160bc526a351365f460c5306df2479d6.png

Background

Is it attached?

ls /media/kdm/
echo ""
ls /media/kdm/CIRCUITPY
CIRCUITPY
Kindle

boot_out.txt
kb.py
kmk
main.py

Auto-mount when plugged in

cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
/dev/mapper/vgkubuntu-root /               ext4    errors=remount-ro 0       1
# /boot was on /dev/nvme0n1p2 during installation
UUID=3550e9b4-85b0-4996-9ca3-740c0ef22e78 /boot           ext4    defaults        0       2
# /boot/efi was on /dev/nvme0n1p1 during installation
UUID=B298-D0CC  /boot/efi       vfat    umask=0077      0       1
/dev/mapper/vgkubuntu-swap_1 none            swap    sw              0       0

/dev/mapper/sda1_crypt /home ext4 defaults 0 2

# sudo blkid
UUID=F6AB-6D5A	/media/kdm/CIRCUITPY	vfat	nofail,user	0	0