Keyboardio Model100 custom-firmware notes

I am typing this on my new Keyboardio Model100!  Setup was a bit rocky; hopefully these notes will help you!

What worked (try 3)

Setup prerequisites

  1. Add udev rules for the Model100 per .
    • chown root:root the new rules file or it won’t be used (ask me how I know! 😉 )
  2. Install the latest dfu-util from source
  3. Confirm that the keyboard is accessible:
    1. Hold prog while plugging in the keyboard
    2. Run ./src/dfu-util -lv and make sure Model 100 (bootloader) appears
  4. Install the latest firmware using Chrysalis per

Setup Chrysalis-Firmware-Bundle

  1. Clone and cd into the clone dir.
  2. Run ./tools/bootstrap.  This will create .arduino/ and .kaleidoscope/ and download lots of files into them.
  3. export KALEIDOSCOPE_DIR="$(realpath .kaleidoscope/)"
  4. export ARDUINO_DIRECTORIES_USER="$(realpath .kaleidoscope/.arduino/user)"
  5. make -C .kaleidoscope setup

Build the firmware

  1. make Keyboardio/Model100 VERBOSE=1.  This should leave output in output/Keyboardio/Model100/default.bin.
  2. Unplug the Model100, hold Prog, and plug the Model100 back in.  The prog key should light steady red.
  3. Run dfu-util -d 3496:0005 -D ./output/Keyboardio/Model100/default.bin -R — use your locally-built dfu-util.  You should see something like the below “Sample dfu-util output”.  The first column of keys will cycle green while this is happening.
  4. When the programming is done, the keyboard should reset.  If it doesn’t, unplug and replug it.

Sample dfu-util output

dfu-util 0.11-dev
dfu-util: Warning: Invalid DFU suffix signature
dfu-util: A valid DFU suffix will be required in a future dfu-util release
Opening DFU capable USB device...
Device ID 3496:0005
Copying data from PC to DFU device
Download [=========================] 100% 167104 bytes
Download done.
DFU state(7) = dfuMANIFEST, status(0) = No error condition is present
DFU state(2) = dfuIDLE, status(0) = No error condition is present
Resetting USB to switch back to Run-Time mode
dfu-util: error resetting after download: LIBUSB_ERROR_OTHER

Customizing the firmware

  1. Edit Keyboardio/Model100/Model100.inoExample.
  2. Run the “Build the firmware” steps from above.

Try 1 (didn’t work)

  1. Add udev rules for the Model100 per .
    • chown root:root the new rules file or it won’t be used (ask me how I know! 😉 )
  2. Install the latest dfu-util from source
  3. Confirm that the keyboard is accessible:
    1. Hold prog while plugging in the keyboard
    2. Run ./src/dfu-util -lv and make sure Model 100 (bootloader) appears
  4. Install the latest firmware using Chrysalis per
  5. Update the Arduino keyboard defs:
    1. Tools | Boards | Board manager
    2. Select Type: Updateable in the upper-left
    3. click on Keyboardio, then Update
    4. In the top-right box, search for Model 100 (with a space)
    5. Click on the result and select Install
  6. Fork .  Clone and create a dev branch.
  7. In the dev branch, `git checkout 50a7fbb`.  This is to prevent a compile error (reported here).
  8. In the Arduino IDE, open the Model100 firmware from your clone of Chrysalis-Firmware-Bundle/Keyboardio/Model100/Model100.ino
  9. Build:
    1. Tools | Board | Kaleidoscope Keyboards (2021+) | Keyboardio Model 100
    2. Sketch | Verify/Compile
  10. Upload: When I try to upload, I get an error because the bundled dfu-util needs glibc 2.34, which my dev machine does not currently have.  Therefore:
    1. Sketch | Upload
    2. Copy the dfu-util command line from the error window
    3. Unplug the keyboard, then plug it back in while holding prog
    4. Run the dfu-util command using my built-from-source dfu-util

Unfortunately, the result is a nonfunctional keyboard!  The led key’s LED breathes green briefly but the keyboard is otherwise inert (although does show as USB-connected).  I am able to use Chrysalis to get back to factory firmware, however.

Try 2 (didn’t work)

  1. Remove my old ~/.arduino15/
  2. Download Arduino 1.8.19 tarball from
  3. Untar it
  4. Run cd arduino-1.8.19 && ./
  5. Run the IDE
  6. File | Preferences, and add as an additional board manager URL (ref.)
  7. Tools | Board | Board manager
  8. Search for Keyboardio
  9. Install the Kaleidoscope Keyboards (GD32) bundle (takes a while)
  10. Fix a warning: cd ~/.arduino15/packages/keyboardio/hardware/gd32/1.99.7/libraries/Kaleidoscope-LEDControl && ln -s ../Kaleidoscope/src/Kaleidoscope-LEDControl.h .
  11. Add the built-from-src dfu-util: cp ~/src/dfu-util/src/dfu-{prefix,suffix,util} ~/.arduino15/packages/keyboardio/tools/dfu-util/0.11.0-arduino3/
  12. In the IDE, Tools | Board | Kaleidoscope Keyboards (2021+) | Keyboardio Model 100
  13. In the IDE, File | Examples | Kaleidoscope | Devices | Keyboardio | Model 100
  14. In the IDE, Sketch | Verify/Compile
  15. Unplug the Model100, then hold Prog and plug it back in.  Prog should light steady red.
  16. In the IDE, Sketch | Upload


Trying out Perl packages locally

I’ve really enjoyed Perl since I started using it in late 2016. I invite you to experiment with it yourself! For trying out packages from CPAN (Perl’s package archive) on your local system, I suggest:

  1. Install local::lib per
  2. Install cpanminus. If it’s not a system package on your distro, follow,-plenv-etc.) .
  3. say cpanm App::cpmcpm is faster than cpanminus 😀
  4. To install a module, say cpm -gv Whatever::Module

Yes, this is more tedious than I would prefer! I’m sure there are shorter ways that are distro-specific, but the above should work just about anywhere.

Some sites that can help you learn:

Dragging tabs off a Chromium window (solved!)

For several months, I have not been able to drag tabs off a Chromium window. When I let go of the mouse button to release the tab, Chromium didn’t respond. That is finally fixed!

First, thanks to jangxx for discovering that turning off Chromium’s “Use system titlebar and borders” option fixes the problem. Unfortunately, my Chromium doesn’t have that option! I had to do it by hand:

  1. In the browser, go to chrome:settings
  2. Under Appearance, make sure “Theme” is “GTK+”. If it’s not, hit “Use GTK+”.
  3. In the browser, go to chrome:version
  4. Make a note of the “profile path”.
  5. Exit Chromium entirely.
  6. In a shell, cd to the profile path
  7. cp Preferences Preferences.bak
  8. Edit Preferences and change "custom_chrome_frame":false to "custom_chrome_frame":true
  9. Restart Chromium. You should be able to drag tabs!

Let me know if this helps you, or if you need more information about these steps. Happy browsing!

autom4te and m4sh for the impatient

autom4te is the engine beneath autoconf, but you can use it to generate your own portable shell scripts for other purposes.  For example, create this test.m4sh:

# test.m4sh
m4_errprintn([before init])
m4_errprintn([before copyright])

# draws a box on stdout
AS_BOX([Test foo])

# Puts a box in the output as a comment
m4_text_box([testing foo])

AS_IF([test "x$foo" = 'x1'],[
echo Foo is 1
m4_errprintn([at end])

Run autom4te:

$ autom4te -l M4sh -Wall -o test.m4sh 
before init
before copyright
at end

(Note that the m4_errprintn calls print while autom4te is running.)

Now you have

$ ./ WARNING: warning
## -------- ##
## Test foo ##
## -------- ##
Foo is 1

Stunning, right? 😉

The number one gotcha

If you forget AS_INIT, you will get no output and no error message.  You have been warned.

Commands used in this example

  • AS_INIT: required.  At the top of the input file.
  • AS_COPYRIGHT: puts the text you give it into the output as a comment at the top of the generated script file.
  • AS_WARN: prints a warning when the generated script runs
  • AS_ECHO: prints to stdout when the generated script runs
  • m4_errprintn: prints to stderr when autom4te runs.  No effect on the generated script.
  • AS_BOX: prints the “Test foo” box in the example output, when the generated script runs
  • m4_text_box: puts into the generated script file a comment like this one:
## ----------- ##
## testing foo ##
## ----------- ##
  • AS_IF: a shell conditional, but more portable.
  • test: the clunky, old-school (but very portable) way of checking conditionals.  For maximum portability, don’t let any argument to test be the empty string (hence the xs in the test command above)

Happy hacking!

Repeating the last ‘:’ (Ex) command in Vim

Sometimes I run a complicated search-and-replace (:s/../../) in Vim, and I then want to repeat it somewhere else. In the past, I have always hit :, up-arrow to retrieve the command from the history. It should be no surprise by now that there is an easier way!

@: in normal mode (at-sign, colon) will repeat the last : command. Once you have done @:, you can hit @@ to re-run the command. Quick and easy!

This works because the : register, ":, holds the last Ex command. @ runs a macro, and knows that using the : register means you want to repeat an Ex command. @@ runs the last macro, so works for @: just as it does for @a or any other normal-mode macro.

I found ": in :help registers when I was looking for a register that held the filename of the file open in another window, as opposed to the alternate filename in the current window. Details of ": are at :help ": (believe it or not! 😉 ).

Generating Roman numerals in Vala

I have recently been learning and enjoying the Vala programming language.  I am writing a lightweight markdown-to-PDF converter and wanted to be able to automatically number list items in Roman numerals.  Here, in case anyone wants it, is Knuth’s algorithm for producing the Roman numeral for a number.  I converted this to Vala from the original WEB source, part of TeX, as quoted by Hans Wennborg.  Enjoy!

string roman(uint num)
    // Knuth's algorithm for Roman numerals, from TeX.  Quoted by
    // Hans Wennborg at
    // Converted to Vala by Chris White (  CC-BY 4.0 Intl.

    var sb = new StringBuilder();

    string control = "m2d5c2l5x2v5i";
    int j, k;   // mysterious indices into `control`
    uint u, v;  // mysterious numbers
    j = 0;
    v = 1000;

    while(true) {
        while(num >= v) {
            num -= v;
        if(num <= 0) {  // nonpositive input produces no output

        k = j+2;
        u = v / control[k-1].digit_value();
        if(control[k-1] == '2') {
            k += 2;
            u /= control[k-1].digit_value();

        if(num+u >= v) {
            num += u;
        } else {
            j += 2;
            v /= control[j-1].digit_value();

    return sb.str;
} // roman()

(not extensively tested — use at your own risk. No warranty. License details here.)

Git internals for fun and profit

Just ran across this very enjoyable article about how GitHub sped up cloning by several orders of magnitude –

If you haven’t seen it, check out the git commit graph.  In the Linux kernel tree, the time for me to show the commit graph of the five most recent commits went from five seconds to 20ms!  Requires Git 2.18+, which you can install on Ubuntu LTS from a PPA per this.  Short version:

git config --global core.commitGraph true

and then, in each repo:

git show-ref -s | git commit-graph write --stdin-commits

Vim tips: curly quotes and visual selection

Curly quotes: because reasons.  😉  In Insert mode:

  • Ctl+K '6 will give you an open curly single quote, and Ctl+K '9 will give you the closing curly single quote.
  • Similarly, Ctl+K "6 will give you an open curly single quote, and Ctl+K "9 will give you the closing curly single quote.

Visual mode: turns out you can just hit o when in Visual mode to move the cursor to the other end of the selection.  Handy!

Installing Elementary Files on Ubuntu 18.04 LTS

I learned about elementary OS from Slashdot.  I currently use Nautilus and xfe, but am not 100% happy with either.  I thought I would give Elementary Files (the elementary OS file manager) a try.

  • sudo apt-get install -y ninja-build gobject-introspection libgirepository1.0-dev
  • Uninstall meson if you have it installed (e.g., sudo apt remove meson)
  • pip3 install meson – you need at least v0.50 (I think).
  • Add ~/.local/bin to your PATH if you haven’t yet.
  • sudo apt-get install -y valac libcanberra-dev libdbus-glib-1-dev libgail-3-dev libgee-0.8-dev libglib2.0-dev libgtk-3-dev libnotify-dev libpango1.0-dev libplank-dev libsqlite3-dev libunity-dev libzeitgeist-2.0-dev
  • libcloudproviders-dev (bionic’s package isn’t new enough): git clone ; cd libcloudproviders ; git checkout 0.3.0 ; meson build ; cd build ; ninja && sudo ninja install
  • libgranite-dev (bionic’s package isn’t new enough): git clone elementary-granite ; cd elementary-granite/ ; git checkout 5.2.5 ; meson build ; cd build ; ninja && sudo ninja install
  • Files (at last!): git clone elementary-files ; cd elementary-files ; git checkout 4.2.0 ; meson build ; cd build ; ninja && sudo ninja install

Whew!  This is reminding me why we have package managers 🙂 .

Let me know if this doesn’t work for you — I may have missed something by accident.

Word VBA: for speed, avoid Document.Paragraphs(idx)

In Word VBA, calling doc.Paragraphs(idx) to get a paragraph by its index is very slow.  Instead, once you have a Paragraph object, use para.Next and para.Previous to navigate, and your code will be much faster!  I just refactored an old routine this way and received a dramatic speedup.

I think doc.Paragraphs() must be iterating through the document starting from the beginning, but I don’t know that for sure.  I do know that adding paragraphs before the region my code was working with made the code run perceptibly slower, even though the code was never touching the added paragraphs.

Here are some simple helpers, in case you want to manually check for errors rather than throwing when you run off the end of the document:

Private Function NextParaOrNothing_(p As Paragraph) As Paragraph
    Set NextParaOrNothing_ = Nothing
    On Error Resume Next
    Set NextParaOrNothing_ = p.Next
End Function 'NextParaOrNothing_

Private Function PrevParaOrNothing_(p As Paragraph) As Paragraph
    Set PrevParaOrNothing_ = Nothing
    On Error Resume Next
    Set PrevParaOrNothing_ = p.Previous
End Function 'PrevParaOrNothing_