Close

[E1][R][X] AirBerries: Learning a custom keyboard layout

A project log for Tetent [gd0090]

An input device for quickly entering text and equations, gaming, drawing and cursor/6DoF movement.

kelvinakelvinA 07/21/2023 at 02:350 Comments

So I've recently got a fully electrically and mechanically complete CAD model of Tetrinsic and was, once again, doing some research for Tetent, mainly because I don't like how this featureless flat face looks:

It turns out that the eteeController actually exists. I heard about them a while ago when I was researching VR input devices and I was thinking that it would be one of those crowdfunded ventures that never sees the light at the end of that tunnel.

It turns out that it uses some futuristic sounding material and algorithms to detect the pressure and touch across the 3D form, which makes Tetrinsic look like a Victorian-era solution in comparison.

Anyway, I eventually came across this video:

This reinactment of what someone like me could think when buying (or in my case, building) an ergonomic keyboard was rather relatable. But the real part that hit me was 4:16, when "You go back to your original keyboard...". For my journey, that's where it ended and I've been typing on my laptop for 2 years and this not-so-temporary "Temorary Keyboard" for 2 years instead of using the Lets Split mechanical keyboard I built 4 years ago.


Seeing to the end of the video unexpectedly gave me the potent motivation to actually take out my AirBerries. I call them that because the Gateron Clears I wanted to try were ULTRA light but WAY deeper in actuation than the laptop keyboards I've mainly typed on, meaning it was easy for me to accidentally hit another key, and because the whole thing looks like it was themed from some kind of berry burst smoothie.

So, in 1 hour sand 45 minutes, I: 

and I was live!


Something I found out in MonkeyType is that every specific time / word count gets treated as it's own seperate entity. Thus, this is the strategy I've done:

This means that, if you look on my profile and saw any test that ends in 3 in July 2023 or later, I specifically typed it on the AirBerries . I think I was using some other but similar strat before then, because there's no way I'm getting 50wpm on my smarphone.

Anyway, as you can see, I only got 4 and a half wpm. Immediately, I was like "Well if I've got to relearn from 0 anyway, that means that this layout is probably sufficiently different enough that I can learn a completely new layout and still be able to type on QWERTY devices." I also thought it would be a good idea, so that I can prime my mind for learning something new and find out new ways of learning a new skill that I could apply to Tetent when the time came.

First order of business, taken from Tetent layout research / printed trails with #Tetent TestCut [gd0139] , I decided to move Finger5 down a row.

Then I went on Reddit to look for some insights. I already knew layouts like DVORAK and Colemak-DH, but there are other options that I learned about in this video:

A nice idea was to put the punctuation in the center. As well as reducing the workload on my painfully overworked Finger2's, I could make the keyboard look symmetrical (more on that later). The last suggestion was actually a website that generates optimized layouts, called Carpalx. I actually forgot to look into it because I wanted to quickly see what's currently around on Reddit first.

Almost immediately, I found this, which was great!

To me, the first and 3rd bullet points make a lot of sense. For a staggered QWERTY, I wouldn't've considered it, but an ortholinear layout (as with the AirBerries) could really take advantage of being able to almost slide from the top row to the home row when using the same finger. Obviously, Tetent doesn't even need to worry about any of this because the entire characterset is just duplicated to every finger.

He then linked to an analyzer website and I could enter my own text, so I put in 3 recent logs I've written. These are ideal because, unlike something like Microsoft Word, there's no autocorrect when typing Hackaday logs and I have to type everything. This is the result:

I can see why Colemak-DH has been recommended over Colemak recently.

The above were also useful insights. He also seemed to have a massive-looking swath of data to back himself up:

In the comments, he linked to an importable layout that would more-or-less be representive. Thus, I brought it in and added even more past logs.

This heatmap helped understand what was happening. Thus, I started on the layout in the QMK Configurator.

I was, coincidentally, able to get all the high-value keyboard shortcuts in the same general area. 

I created the rest of the keymap and I wasn't liking the idea of O and F being on the same key, thinking of words like "of" and "for". Thus, I swapped it for W.

With that starting to take shape, I went back to the reddit post to see any more insights.

But I was like "Wait a second!! E getting a dedicated finger on Finger5's like the spacebar/backspace getting a thumb?! The ones that aren't doing much anything else and can free up other fingers?"

I knew the letter T was going to be right behind, so I put that on the right Finger5. This strat also meant that I could do CRTL+A with reduced finger movement.


For my other layers, I wanted to be able to actually type symbols like ±, and it turns out you need an ALT code for that. Did some reasearch and it turns out a QMK fork called Vial is the easiest solution. It seems that keymap.c stores the default keymap and then Vial uses EEPROM to store and load a custom layout on the fly so that one doesn't have to recompile on every keymap change.

I created a fork and started work. It was some tough stuff, but the toughest part was actually the keymap in the vial.json file. You see, it's the thing that allows Vial's desktop software to properly set the layout. First, I thought it was optional, then I thought that I just had to have blank keys, then I thought the layout was this:

I had to comment the {x:1} part for it to work:

In the config, I increased the default max limit of layers because I thought "I've only got half the keys of a normal 104 layout, so I should be able to get some more". Then it came to flashing:

Well going back didn't do anything. Reading the docs, the amount of layers is only really going to affect RAM / EEPROM useage. Convinently, there's a section that goes over reducing the firmware size.

ANGUISH!!!

I have no idea what these settings do, I can't find any documentation that says what these do and I never hit any issue turning it off, and I got a 25,XXX byte file.


The good news is that I could get the 6 layers I hoped for. The bad news is that the map was all over the place!

I did some tweaking and it all looked like it aligned:

However, when I tried the matrix tester, it was all wrong. After a while of experimentation and digging, I found this file:

This implied that the matrix was actually a 6 x 8 not a 4x12 as I originally thought. 

Before, those 2 keys were showing up starting from (1, 2) instead of (0,0) seen here.

Finally, I could actually start getting those alt codes:

I also tried out those OSM modifiers and they're cool. They work kind of like a touchscreen keyboard.

Well it was actually time to try it:

It seemed either like the backspace key wasn't registering correctly (I've got 3 o-rings on most of the keys to reduce the bottom-out distance), registering multiple times or QMK doesn't like OSM LCTRL + Backspace.  Turned off the modifiers and I got much further.

I thought it would now be a good time to use #Interval Provisional [gd0097] to set up a practice timer:

I was supposed to set a 6minute, 3 second time on Monkeytype but I accidentally set a 603 timer instead, which is 10 minutes. Regardless, since I want to keep that perfect no-DNFs streak, I went through anyway:

It was nice because the keys were actually in a position that I could reason with and my fingers were actually right were they needed to be. Right now, I'm in "training wheels" mode and so I'm looking at the keyboard / reference of it. I did feel that it was a bit of an annoying stretch to go for the G key though.

The next test was Ngram which is a site that teaches common bigrams. 

I got to this when I found out that I was getting double presses on my N key. I increased my debounce time from 6 to 12ms, as I hear that the new Apple Vision Pro has a 12ms latency and the industry is saying that the target is under 20ms. Increasing the debounce time really helped out.


Next, I tweaked the layout in the Analyzer to see how it compared.

I'm suprised how far down my version has fallen from the template. The good news though is that my layout is an equilizer:

I was also able to get an additional 1% by swapping the positions of Q and G. But then, I found out how to set up my own custom layout that is more representative:

AltGr is the layer for my numbers, where the left side is the normal numbers and the right are the numberpad numbers. I didn't bother adding things like the arrows. Well, how did it perform?

Still though, in 29.3 thousand characters of my past logs, I didn't see the SHE layout layout drop before 72%. 


In other news, I had to wrip up my number and function layers because it seems that they didn't activate in my Fusion 360 layer. It turns out that they have to be in the topmost layers so that they work on lower ones.

AirBerries: Base layer. Can you see that it looks like a yoghurt or smoothie drink? Also, those 4 transluscent keys are the punctuation symbols and were the reason why my layout didn't look symmetrical before.
Fusion 360 Layer (currently unpopulated)
Number layer, locked though first pressing the Fn key. In real life, this looks like #00BCD4.

This is where I got to until writing this log (on my Z-88 Retro, by the way) and now my fingers hurt aftr these 4 hours of typing. Well, 2 actually: my left and right Finger2s. I was thinking that learning a new layout, especially when Tetent could exist Soom(TM) would be a waste of time, but I'm starting to change my mind.

I was also thinking that swapping Q and K would improve my score, but it actually got slightly worse.

The layout:

The layout for Keyboard Layout Analyser:

{
    "label": "IRN",
    "fingerStart": {
        "1": 37,
        "2": 26,
        "3": 27,
        "4": 28,
        "5": 53,
        "6": 54,
        "7": 31,
        "8": 32,
        "9": 33,
        "10": 46,
        "11": -1,
        "false": -1
    },
    "keyboardType": "matrix",
    "author": "Glodigit",
    "authorUrl": "https://github.com/Glodigit",
    "moreInfoUrl": "https://hackaday.io/project/184181-tetent-gd0090/log/221386-rx-airberries-a-lets-split-keyboard",
    "moreInfoText": "AirBerries",
    "keys": [
        {
            "primary": -1,
            "shift": -1,
            "finger": 1,
            "id": 0,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 1,
            "id": 1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 2,
            "id": 2,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 3,
            "id": 3,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 4,
            "id": 4,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 4,
            "id": 5,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 7,
            "id": 6,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 7,
            "id": 7,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 8,
            "id": 8,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 9,
            "id": 9,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 10,
            "id": 10,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 10,
            "id": 11,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 1,
            "id": 12,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 9,
            "shift": -1,
            "finger": 1,
            "id": 13,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 99,
            "shift": 67,
            "finger": 2,
            "id": 14,
            "altGr": 55,
            "shiftAltGr": 38
        },
        {
            "primary": 111,
            "shift": 79,
            "finger": 3,
            "id": 15,
            "altGr": 56,
            "shiftAltGr": 42
        },
        {
            "primary": 108,
            "shift": 76,
            "finger": 4,
            "id": 16,
            "altGr": 57,
            "shiftAltGr": 40
        },
        {
            "primary": 107,
            "shift": 75,
            "finger": 4,
            "id": 17,
            "altGr": 43,
            "shiftAltGr": -1
        },
        {
            "primary": 113,
            "shift": 81,
            "finger": 7,
            "id": 18,
            "altGr": 47,
            "shiftAltGr": 63
        },
        {
            "primary": 117,
            "shift": 85,
            "finger": 7,
            "id": 19,
            "altGr": 55,
            "shiftAltGr": -1
        },
        {
            "primary": 100,
            "shift": 68,
            "finger": 8,
            "id": 20,
            "altGr": 56,
            "shiftAltGr": -1
        },
        {
            "primary": 112,
            "shift": 80,
            "finger": 9,
            "id": 21,
            "altGr": 57,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 10,
            "id": 22,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 10,
            "id": 23,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 1,
            "id": 24,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 120,
            "shift": 88,
            "finger": 1,
            "id": 25,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 115,
            "shift": 83,
            "finger": 2,
            "id": 26,
            "altGr": 52,
            "shiftAltGr": 36
        },
        {
            "primary": 97,
            "shift": 65,
            "finger": 3,
            "id": 27,
            "altGr": 53,
            "shiftAltGr": 37
        },
        {
            "primary": 104,
            "shift": 72,
            "finger": 4,
            "id": 28,
            "altGr": 54,
            "shiftAltGr": 94
        },
        {
            "primary": 44,
            "shift": 60,
            "finger": 4,
            "id": 29,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 46,
            "shift": 62,
            "finger": 7,
            "id": 30,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 105,
            "shift": 73,
            "finger": 7,
            "id": 31,
            "altGr": 52,
            "shiftAltGr": -1
        },
        {
            "primary": 114,
            "shift": 82,
            "finger": 8,
            "id": 32,
            "altGr": 53,
            "shiftAltGr": -1
        },
        {
            "primary": 110,
            "shift": 78,
            "finger": 9,
            "id": 33,
            "altGr": 54,
            "shiftAltGr": -1
        },
        {
            "primary": 106,
            "shift": 74,
            "finger": 10,
            "id": 34,
            "altGr": 59,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "shift": -1,
            "finger": 10,
            "id": 35,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 16,
            "finger": 1,
            "id": 36,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 101,
            "finger": 1,
            "id": 37,
            "shift": 69,
            "altGr": 48,
            "shiftAltGr": 41
        },
        {
            "primary": 118,
            "finger": 2,
            "id": 38,
            "shift": 86,
            "altGr": 49,
            "shiftAltGr": 33
        },
        {
            "primary": 119,
            "finger": 3,
            "id": 39,
            "shift": 87,
            "altGr": 50,
            "shiftAltGr": 34
        },
        {
            "primary": 102,
            "finger": 4,
            "id": 40,
            "shift": 70,
            "altGr": 51,
            "shiftAltGr": 163
        },
        {
            "primary": 63,
            "finger": 4,
            "id": 41,
            "shift": -1,
            "altGr": 45,
            "shiftAltGr": 95
        },
        {
            "primary": 33,
            "finger": 7,
            "id": 42,
            "shift": -1,
            "altGr": 42,
            "shiftAltGr": -1
        },
        {
            "primary": 121,
            "finger": 7,
            "id": 43,
            "shift": 89,
            "altGr": 49,
            "shiftAltGr": -1
        },
        {
            "primary": 109,
            "shift": 77,
            "finger": 8,
            "id": 44,
            "altGr": 50,
            "shiftAltGr": -1
        },
        {
            "primary": 98,
            "finger": 9,
            "id": 45,
            "shift": 66,
            "altGr": 51,
            "shiftAltGr": -1
        },
        {
            "primary": 116,
            "finger": 10,
            "id": 46,
            "shift": 84,
            "altGr": 48,
            "shiftAltGr": -1
        },
        {
            "primary": 39,
            "finger": 10,
            "id": 47,
            "shift": 34,
            "altGr": 58,
            "shiftAltGr": -1
        },
        {
            "primary": 17,
            "finger": 1,
            "id": 48,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 122,
            "finger": 1,
            "id": 49,
            "shift": 90,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -91,
            "finger": 5,
            "id": 50,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 18,
            "finger": 5,
            "id": 51,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "finger": 5,
            "id": 52,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 32,
            "finger": 5,
            "id": 53,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 8,
            "finger": 6,
            "id": 54,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -18,
            "finger": 6,
            "id": 55,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "finger": 6,
            "id": 56,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": -1,
            "finger": 6,
            "id": 57,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 103,
            "finger": 10,
            "id": 58,
            "shift": 71,
            "altGr": -1,
            "shiftAltGr": -1
        },
        {
            "primary": 13,
            "finger": 10,
            "id": 59,
            "shift": -1,
            "altGr": -1,
            "shiftAltGr": -1
        }
    ]
}

My forked repository: https://github.com/Glodigit/vial-qmk


[23 July: Edit 1] 

I've moved a few keys around, namely swapping the H and R columns, adding a right shift key and moving the CTRL so that I can reach it with my thumb. My score on the analyzer is now 68.88 for the ZEV-XS layout:

I've also populated my Fusion 360 layer, which is only on the left side because I intend to use it one handed. To allow this, I've had to move some keys on the number layer so that I could access everything I think I'd need:

In terms of learning, my plan so far is 

  1. Ngram Type (72wpm threshold)
  2. keybr.com
  3. monkeytype (363 seconds, and my last test a few hours ago, I got 8wpm)

The main strat I'm trying out on Ngram is to not actualy look at the output text on the screen and instead focusing on the sensory input of my fingers. This is allowing me to simulate typing bigrams / trigrams without physically using the keyboard, and motivates me to strike the keys with a sufficient amount of confidence.

I've also programmed in LEDs for the various Lock LEDs, and I'm very glad on how the CAPS LOCK version came out. Now, it's so much more obvious that I'm in that mode.

Discussions