Skip to content

Instantly share code, notes, and snippets.

@vinorodrigues
Last active February 10, 2022 06:44
Show Gist options
  • Save vinorodrigues/07fd735683856b2a06c7c52b9b3878cb to your computer and use it in GitHub Desktop.
Save vinorodrigues/07fd735683856b2a06c7c52b9b3878cb to your computer and use it in GitHub Desktop.
Determining LED Index to Physical Positions for the QMK RGB Matrix - Keycap Units

Determining LED Index to Physical Positions for the QMK RGB Matrix - Keycap Units

This is part 2 of a 2 part series. For part 1 see: Image Pixels

Introduction

In part 1 we discussed a method of attaining the values for the LED Index to Physical Position array when building a keyboard file for QMK that utilises the RGB Matrix feature set, using an image of the PCB. Now there would be several criticisms of this approach, and to be honest the opinions and assumptions that are citied in that method are, in the greater perspective, irrelevant. For example, the oval rotation (as apposed to true circular), would really not make a visual difference as the KB is not uniform - i.e. it's rectangular shape skews ones eye anyway, and even perfect circles would illusionary look oval anyway.

With that in mind - the thinking around centre is also debatable, since there are 2 axis' for centre: vertical and horizontal. For vertical, the fact that one sits at an angle to the KB would skew optically the centre anyway - so a slight variation, that be centre of the switch vs. the over or under offset of the LED - would not matter. Horizontally though, the centre could be a significant nuisance if not done correctly.

Given this, there is some leeway we can leverage to be able to use up the entire range of positional values... i.e. use the entire { 224, 64 } resolution as per QMK Docs.

The keycap units approach

Herein I'll detail an approach I use to determine the coordinates of the LED positions that deliver rotational effects that look aligned and centred horizontally, and at the same time leverage the entire effect resolution offered by the QMK Matrix library.

For this one you will need to know the keycap widths of the keyboard you wish to measure for. For existing KB's this is easily sourced from either from the VIA file (find that on the https://github.com/the-via/keyboards repo.), or in the QMK Configurator keyboard file, usually in the QMK keyboards folder under the KB name as the info.json file.

You will also need access to a spreadsheet application (Excel, Numbers, G-Docs, whatever) - this will be a mathematical exercise.

Step 1 - Map out the key and gap lengths

For this tutorial I'll use the Keychron Q1 KB, as it has an offset navigation cluster that requires some additional explanation.

image

What we will need to do is map out both the keycap and the inter-key gaps on a linear scale, using the keycap "U" widths.

For example, for the F-Row you will have (note the gaps are added):


1 2 3 .. Sum
1 0.25 1 .. 16.25

The horizontal sum should be the total "U" length of the KB. Do this for each row... the lengths should all match (except when the keys don't align on both sides, as in row Shift-Z on the Q1). Don't worry too much if the column count is different, as some rows have more keys that others - the sanity check is that the total lengths are bound to the the same (in this case 16.25U).

You should also do a vertical row for the heights. With the Q1 the navigation arrows are offset slightly lower, so you'd also create a second column for that clusters heights (as depicted in the image below).

Once you have these you'll also need the grid resolution to U-width ratios. The formula for the X and Y multipliers are:

X = 224 / ( {total width} - 1 )
Y = 64 / ( {total height} - 1 )

For our Q1 X = 14.689, and Y = 11.636. (I've rounded to 3 decimals.)

Your sheet should look something like this:

image

Step 2 - Work out the progressive length

Next we need to work towards the positional coordinates of the keys (think of it as "U"-coordinates). To do this one creates another table and then, for the column just copy the 1st key width. For the second column, use the prior column value, and add the width of the current column. Column 3 is a duplication for column 2, with the appropriate column shift.

Formula for the cells are:

1 2 ..
=C3 =(D3)+C16 ..

Giving you a progressive length (i.e. "U"-length from the left edge to the right edge of each key, for each row.

(For the sake of brevity, I'll not explain the Y-axis, but it's the same theory, except applied top-down, instead of right-to-left.)

Your sheet should now have a second table that looks like this:

image

Step 3 - Shift the "U"-coordinates to the centre of each key

The "U"-coordinates established in step two only give you the coordinates for the right edge of each key. We'd need to bring back the position the the centre of the individual keys (we're making an assumption that the LED is in the middle, this may not be true always, e.g. a stepped Caps-Lock).

Yup - you guessed it, another table. First column is the same-key "U"=width (from tbl.1) divided by 2.

The second (and subsequent) columns are: same-key "U"=width (from tbl.1) divided by 2, then add prior key progressive length (from tbl.2).

Formulas are:

1 2 ..
=C3/2 =(D3/2)+C16 ..

You should get this:

image

Step 4 - Apply the grid resolution ratio

We're almost done... last step is getting the LED Index to Physical Position. This one is simple... just take the centred "U"-coordinates and multiply it with the grid resolution ratio. Two caveats:

  1. you also need to bring the point back by 1/2 "U" so that the middle of a 1U key starts at 0.
  2. Apply rounding to the result, since the coordinates are code "byte" types.

Formulas for table 4 are:

1 2 ..
=(C26-0.5) * $W$12 =(D26-0.5) * $W$12 ..

Your final table should look like this:

image

Then you have all the data to build the coordinate array, with the top-left coordinate at { 0, 0 } and the bottom-right coordinate at { 224, 64 } - a edge-to-edge use of the RGB Matrix ... matrix.

(Remember to ignore the gaps.)

Keychron Q1 LED Index to Physical Position array of the g_led_config variable should look like this:

}, {
    // LED Index to Physical Position
    {0, 0},  {18, 0},  {33, 0},  {48, 0},  {62, 0},  {81, 0},  {95, 0},  {110, 0},  {125, 0},  {143, 0},  {158, 0},  {173, 0},  {187, 0},  {206, 0},             {224, 0},
    {0, 15}, {15, 15}, {29, 15}, {44, 15}, {59, 15}, {73, 15}, {88, 15}, {103, 15}, {118, 15}, {132, 15}, {147, 15}, {162, 15}, {176, 15}, {198, 15},            {224, 15},
    {4, 26}, {22, 26}, {37, 26}, {51, 26}, {66, 26}, {81, 26}, {95, 26}, {110, 26}, {125, 26}, {140, 26}, {154, 26}, {169, 26}, {184, 26}, {202, 26},            {224, 26},
    {6, 38}, {26, 38}, {40, 38}, {55, 38}, {70, 38}, {84, 38}, {99, 38}, {114, 38}, {129, 38}, {143, 38}, {158, 38}, {173, 38},            {196, 38},            {224, 38},
    {9, 49},           {33, 49}, {48, 49}, {62, 49}, {77, 49}, {92, 49}, {106, 49}, {121, 49}, {136, 49}, {151, 49}, {165, 49},            {185, 49}, {209, 49},
    {2, 57}, {20, 57}, {39, 57},                               {94, 57},                                  {147, 57}, {162, 57}, {176, 57}, {195, 64}, {209, 64}, {224, 64}
}, {

Before (top) and after (bottom) (with center guidelines drawn in) for comparison:

image

--made with ♡, Vino Rodrigues

@lokher
Copy link

lokher commented Feb 10, 2022

Great job, and I found a easier way which is using the pick place data exported from EDA to acheive accurate key layout.

  1. Export the pick place as .csv file from EDA.
  2. Open the .csv by Excel and remove other components except the switches.
  3. Sort the content by x value column, then sort the content by y value column, now we should get the switches postion like:
Ref X Y
SW1 x1 y1
SW2 x2 y1
... ... ...
SW14 x14 y2
SW15 x15 y2
... ... ...
SW83 x83 y6
SW84 x84 y6
  1. The x1 and y1 value may not be zero, we offset the position to X0/Y0 colomn via minus X/Y column value by x1/y1
Ref X Y X0 Y0
SW1 x1 y1 ... ...
SW2 x2 y1 ... ...
... ... ... ... ...
SW14 x14 y2 ... ...
SW15 x15 y2 ... ...
... ... ... ... ...
SW83 x83 y6 ... ...
SW84 x84 y6 ... ...
  1. Find maximun X0/Y0 value as x_max and y_max, apply the scaling to from X0 to X1 column : X1 = X0 * 224/x_max, also apply the scaling from Y0 to Y1 column : Y1 = Y0 * 224/y_max
Ref X Y X0 Y0 X1 Y1
SW1 x1 y1 ... ... ... ...
SW2 x2 y1 ... ... ... ...
... ... ... ... ... ... ...
SW14 x14 y2 ... ... ... ...
SW15 x15 y2 ... ... ... ...
... ... ... ... ... ... ...
SW83 x83 y6 ... ... ... ...
SW84 x84 y6 ... ... ... ...

Here we get the final accrutae position date.

PS: Maybe we can write an app/script to extract the data from the .csv and generate C code and json automaticly, I will try when I get time.

@vinorodrigues
Copy link
Author

This is actually a really good idea. I was thinking on the same lines - but for me the starting point would have been the source JSON from Keyboard Layout Editor (easier to process than stripping EDA xml) ... but alas, my time is not friendly to me too.

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