boot.py

boot.py lives in the root of your keyboard when mounted as a storage device. There is a more detailed explanation in the CircuitPython Documentation, however there are some common use cases for your keyboard listed on this page.

KMK’s builtin boot configurator

KMK ships with a handy boot configuration function that does all the hard work for you. The interface may change in the future, but there is a safety mechanism in place: if anything goes wrong, it’ll boot into a mountable and debuggable configuration.

Signature

from kmk.bootcfg import bootcfg

bootcfg(
    # required:
    sense: [microcontroller.Pin, digitalio.DigitalInOut],
    # optional:
    source: Optional[microcontroller.Pin, digitalio.DigitalInOut] = None,
    autoreload: bool = True,
    boot_device: int = 0,
    cdc_console: bool = True,
    cdc_data: bool = False,
    consumer_control: bool = True,
    keyboard: bool = True,
    midi: bool = True,
    mouse: bool = True,
    nkro: bool = False,
    pan: bool = False,
    storage: bool = True,
    usb_id: Optional[tuple[str, str]] = None,
    **kwargs,
) -> bool

All optional parameters are set to reflect common CircuitPython defaults, however they may differ from board specific defaults.

sense

sense accepts either uninitialized Pins or DigitalInOut instances for maximum flexibility. The boot configuration is only applied if sense reads True or “high”, and skipped if it reads False or “low”. If sense is an uninitialized Pin, it’ll be configured as pulled-up input; it wont be further configured if it is a DigitalInOut.

source

source accepts either uninitialized Pins or DigitalInOut instances for maximum flexibility. It’s the “source” of the test voltage to be read by the sense pin. If source is an uninitialized Pin, it’ll be configured as a “low” output; it wont be further configured if it is a DigitalInOut.

Common matrix and direct pin configurations (see also the examples below):

diode_orientationsense pinsource pin
COL2ROWcolumnrow
ROW2COLrowcolumn
direct pindirect pinNone

autoreload

CircuitPython will automatically reload when any data or meta data is written to the CircuitPython drive. Certain operating systems tend to do that without asking; in that case autoreload can be disabled as a workaround.

boot_device

Boot HID device configuration for usb_hid, see the usb_hid documentation for details.

cdc_console

This will enable or disable the USB endpoint for the serial console with REPL.

cdc_data

This will enable or disable the USB endpoint for the data serial.

consumer_control

Enable the HID endpoint for consumer control reports. Those are extra keys for things like multimedia control and browser shortcuts.

keyboard

Enable the keyboard HID endpoint. Why would you disable that? For a split half that isn’t connected to USB and needs extra memory for a massive display maybe?

midi

It’s MIDI over USB. Enabled by default in CircuitPython, but most keyboards don’t use it.

mouse

Enable the HID endpoint for a pointing device. A pointing device, or mouse, is like a keyboard, but with continuous instead of binary keys… which also go sideways.

nkro

Enable n-key rollover support. If the default keyboard is enabled, this option will replace the standard 6-key rollover endpoint with an n-key rollover one. This is technically not a standard HID endpoint, but if you want this, you probably know what you’re doing.

pan

Enable panning, aka horizontal scrolling, for the pointing device, aka mouse, hid endpoint.

storage

Disable storage if you don’t want your computer to go “there’s a new thumb drive I have to mount!” every time you plug in your keyboard.

usb_id

A recent addition to CircuitPython 8 is the ability to give your keyboard an identity other than “MCU board manufacturer” - “CircuitPython device”.

return value

bootcfg returns true if boot configuration applied successfully and false if it was skipped, in case you want to use the sense pin mechanism for other custom boot configurations. Any unexpected errors are intentionally not handled, in order to be recorded to the boot_out.txt file for easier debugging.

Example 1

  • diode direction from columns to rows,
  • disabled storage
  • disabled midi
  • disabled mouse
  • custom vendor and device names
import board

from kmk.bootcfg import bootcfg

bootcfg(
    sense=board.GP0,  # column
    source=board.GP8, # row
    midi=False,
    mouse=False,
    storage=False,
    usb_id=('KMK Keyboards', 'Custom 60% Ergo'),
)

Tip: for a diode direction from rows to columns, switch row and column GPIOs when assigning them to sense and source.

Example 2

Dedicated switch to disable boot configuration, connected to ground:

import board

from kmk.bootcfg import bootcfg

bootcfg(sense=board.GP22, ...)

Example 3

Shut-in mode: Always apply boot configuration and disable any contact to the outside world. Caution: this an example for a DigitalInOut sense pin, and probably an unwise thing to do in actuality.

import board
import digitalio

from kmk.bootcfg import bootcfg

sense = digitalio.DigitalInOut(board.GP42)
sense.direction = digitalio.Direction.OUTPUT
sense = True

bootcfg(
    sense=sense,
    cdc_console=False,
    consumer_control=False,
    keyboard=False,
    midi=False,
    mouse=False,
    storage=False,
)

We generally advise against importing your keyboard definition and using rows/columns to define sense and source pins, because that essentially loads the firmware twice, almost doubling boot times.