Open-source project
Arksine/katapult avatar
Arksine/katapult

Katapult: a Klipper bootloader that takes new firmware over CAN, USB or UART

Configurable bootloader for Klipper

681 stars142 forksCGPL-3.0

At a glance

What is it?
Katapult, formerly known as CanBoot, is a bootloader for ARM Cortex-M microcontrollers that reuses Klipper's hardware abstraction layer, trimmed to a small footprint, and lets a Klipper build be uploaded over the bus instead of over a programmer cable. It suits a 3D printer cluster with nodes on a CAN bus, and it does not suit a single board that you are happy to reflash by hand.
Who is it for?
Adopt Katapult if you run Klipper on several boards, or on a board you cannot reach with a programmer, and you want firmware updates to travel over the CAN bus or over USB and UART. Do not adopt it for a single board you can flash directly, since the bootloader occupies flash and adds an entry mechanism you will have to understand.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 22 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A resident bootloader built on Klipper's own hardware layer

Katapult, formerly known as CanBoot, is a bootloader for ARM Cortex-M microcontrollers. It was initially designed for CAN nodes to be used with Klipper, and it makes use of Klipper's hardware abstraction layer, stripped down to keep the footprint minimal. In addition to CAN, it now supports USB and UART interfaces.

The reuse of Klipper's hardware layer is the central engineering decision, and it cuts both ways. On one side, a board that already runs Klipper needs no new peripheral code, because the register level drivers and the board configuration machinery are the ones Klipper already uses. On the other side, a bootloader that has to live on the same chip alongside the application cannot afford the whole layer, which is why the documentation emphasises stripping it down and keeping the footprint minimal.

The supported processors are lpc176x, stm32, rp2040 and rp2350, and the support is not uniform. CAN support is currently limited to stm32 F-series, rp2040 and rp2350 devices, so an lpc176x board gets the bootloader but not the CAN interface.

The licence is the GNU GPL v3, which is a different position from most embedded projects, and it matches the licence of the Klipper code the bootloader is built on.

The repository also carries protocol.md, which describes the bootloader's own protocol, a scripts/ directory with the flash tool, a lib/ directory, src/ and test/ directories, and a developer-certificate-of-origin file.

menuconfig decides the clock, the bus and the entry method

Katapult uses Klipper's build system and the build is configured with menuconfig, so the whole configuration is a set of questions at compile time rather than a runtime file.

The first choice is the Microcontroller Architecture, between lpc176x, stm32 and rp2040, and the processor model options depend on that answer. The communication interface is the next decision, between CAN, USB and UART, and the documentation is careful to say to choose the interface with the appropriate pins for your hardware.

The interface specific settings follow. For CAN there is a CAN bus speed to select for your bus. For serial there is a baud rate for the serial port. For USB there is a USB ids setting that lets you define the vendor ID, product ID and, optionally, the serial number, which is what makes a board identifiable when several are on the same bus.

The entry methods are the part worth planning. There is an option to support bootloader entry on a rapid double click of the reset button, which enters the bootloader when the button is pressed twice within a 500ms window. There is an option to enter the bootloader on a button or gpio state, and the pin can be configured with a hardware pull-up by prefixing it with a caret, or a pull-down by prefixing it with a tilde. And there is a status LED option, where the pin can be inverted so the LED is on when the pin is low, with the example given that the status LED pin for a blue pill is !PC13.

Two options carry warnings. Disable SWD at startup is intended for GigaDevice STM32F103 clones, and the documentation says explicitly that it is untested for the bootloader and that those clones may not work as expected. The clock reference has to be set to match the board's crystal, since a wrong clock is the kind of mistake that shows up as an intermittent failure rather than an obvious one.

Building it and putting it on the chip

The build is Klipper's build system, and the three commands are short:

bash
git clone https://github.com/Arksine/katapult
cd katapult
make menuconfig
make

Once configured and built, you flash with a programmer such as an ST-Link. If you do not have a programmer available, the documentation says it should be possible to flash STM32F103 devices via UART and STM32F042 and STM32F072 devices over DFU, and that ST's STM32CubeProgrammer can facilitate all of these methods. There are also other tools, stm32flash for UART and dfu-util for USB DFU.

Two notes prevent the most common failure. The first is that before flashing Katapult it is recommended to do a full chip erase, because doing so allows Katapult to detect that no application is present and enter the bootloader, and that is required to enter the bootloader if you have not configured an alternative method of entry.

The second is specific to the RP parts. To flash rp2040 and rp2350 targets the MCU should be rebooted in system boot mode, usually with the BOOT button pressed, after which the make flash command can be used. You can also use the mass storage drag-and-drop method with the katapult.uf2 file from the out folder. Flashing Katapult erases the main application, so the Klipper build has to be uploaded again afterwards.

That last point is the cost of the approach in one sentence: installing the bootloader means reinstalling the application.

Getting Klipper onto the board over the bus

The upload procedure has an order, and the order matters.

First make sure the klipper service is stopped. Then build Klipper with CAN support and with a bootloader offset matching the application offset in Katapult, since the two have to agree about where the application lives in flash.

Then get into the bootloader. Katapult enters it automatically if it detects that the application area of flash is empty. When you are upgrading from a currently flashed version of Klipper, the flashtool.py script commands USB and CAN bus devices to enter the bootloader.

The exception is the interesting one. USB to CAN bridge devices and UART devices cannot be auto-detected, so if the device is not in bootloader mode you have to request the bootloader manually with the -r option, then rerun the script without -r to perform the upload. Devices running software other than Klipper need to request the bootloader with their own method.

For a CAN device, the upload is:

bash
cd ~/katapult/scripts
python3 flashtool.py -i can0 -f ~/klipper/out/klipper.bin -u <uuid>

If the device has not been flashed with Klipper before, you can query the bootloader for its UUID, which is the -q option:

bash
flashtool.py -i can0 -q

For USB and UART devices, pyserial has to be installed first, and on Debian and Ubuntu based distributions that is python3-serial, installed with sudo apt update followed by sudo apt install python3-serial. The upload command is then python3 flashtool.py -d <serial device> -b <baud_rate>, where the baud rate is only needed for UART and defaults to 250000 baud if omitted.

The query command can knock a CAN node off the bus

One warning in the documentation is serious enough to deserve its own attention, because the failure mode is not obvious.

A query should only be performed when a single CAN node is on the network. Attempting to query multiple nodes may result in transmission errors that can force a node into a bus off state, and when a node enters bus off it becomes unresponsive. The node must be reset to recover.

So the -q option, which looks like a harmless discovery command, is a tool that can take a printer off the network. On a bus with several nodes it is the wrong command, and the documentation is explicit about it. The reason is in the nature of CAN: every node on the bus transmits an acknowledgement for every frame it sees, so a broadcast style query from several devices at once is precisely the traffic pattern that provokes error frames, and a node that accumulates too many errors drops off the bus by design.

This is also why the upload path exists at all. If every node could be interrogated and programmed over the bus, a single node could be updated without touching the others. With the UUID mechanism, the tool addresses one node at a time, and the query is a way to learn which UUID belongs to which board.

For an operator, the rule is simple. Query with one node on the bus, and if a node has dropped off, power cycle it or reset it before continuing.

The flash tool flags and the optional deployment application

The flash tool has a small, readable option set, and running scripts/flashtool.py -h prints it.

There is -d or --device for a serial device and -b or --baud for the serial baud rate. For CAN there is -i or --interface for the CAN interface and -u or --uuid for the CAN device UUID. The firmware to send is -f or --firmware, pointing at the klipper.bin file. The informational options are -q to query bootloader device IDs on CANBus only, -v for verbose responses, -r to request the bootloader and exit, and -s to connect to the bootloader and print status.

That last pair, -r and -s, is worth noting. -s tells you whether a board is in the bootloader at all, and -r asks a running board to go there. Together they are the difference between a failed upload and a diagnosable one, and they are the flags you need for the devices that cannot be auto-detected.

The Makefile shows a second build artefact, the deployment application, which is what the menuconfig option Build Katapult deployment application controls. The build produces a katapult.elf and a katapult.bin for the bootloader itself, and a separate deployer elf assembled from the deployer sources, the deployer controller object and a Katapult payload object. Both are linked with garbage collection of unused sections, and the compile flags use the GNU C11 standard with link time optimisation and whole program optimisation, which is what keeps the footprint down.

The deployment application is a distinct path from the flashtool, and the documentation on it is truncated in what is available here, so treat the menuconfig entry and the section it points to as the place to read before enabling it.

One release, a busy master branch, and a name that will not go away

The release history is thin in a way that is easy to misread. There is a single tagged release, v0.0.1 with the title Initial Release, published on 2022-07-21. The repository is not archived, and the last push was on 2026-10-01, so the work has continued long after that tag.

For a bootloader that means the practical question is where your binary comes from. Building from the master branch gives you the current state of the tree, and the menuconfig driven build makes that reproducible for your board, but there is no release series to pin, and no changelog of bootloader behaviour changes to read between versions.

The naming is the other thing to understand. The project was formerly known as CanBoot, and the documentation says that references to CanBoot remain both in the source of this repository and in related projects such as Klipper, that some of them must remain indefinitely for compatibility, and that documentation and source code references which do not break compatibility will be updated over time. So you will meet both names in Klipper's own documentation and in issues, and that is not a sign of a forked project.

The licence is the GNU GPL v3, and for firmware that is a decision with consequences. A bootloader linked into a product you distribute has obligations attached to it, and the distinction between a private lab build and a shipped product matters here in a way it does not for a permissive library. Read the LICENSE file before you put a Katapult build on a board that leaves the lab.

Editorial conclusion

Adopt Katapult if you run Klipper on several boards, or on a board you cannot reach with a programmer, and you want firmware updates to travel over the CAN bus or over USB and UART. Do not adopt it for a single board you can flash directly, since the bootloader occupies flash and adds an entry mechanism you will have to understand. Verify first that your MCU is one of the supported families, lpc176x, stm32, rp2040 or rp2350, that the CAN support limit of stm32 F-series, rp2040 and rp2350 devices fits your bus, and that the Klipper build uses a bootloader offset matching the application offset in your Katapult configuration.

Frequently asked questions

What is Katapult and what is it for?

Katapult, formerly CanBoot, is a bootloader for ARM Cortex-M microcontrollers, initially designed for CAN nodes used with Klipper. It reuses Klipper's hardware abstraction layer stripped down to keep the footprint minimal, and it supports CAN, USB and UART interfaces.

How do I build Katapult?

Clone the repository, run make menuconfig to choose the microcontroller architecture, processor model, communication interface and entry options, then run make. The build uses Klipper's build system, and the output lands in the out directory.

How do I upload Klipper to a board through the Katapult bootloader?

Stop the klipper service, build Klipper with CAN support and a bootloader offset matching Katapult's application offset, then run python3 flashtool.py -i can0 -f ~/klipper/out/klipper.bin -u <uuid> for a CAN device, or python3 flashtool.py -d <serial device> -b <baud_rate> for USB and UART.

Why should I only query one CAN node at a time with Katapult?

The documentation warns that querying multiple nodes may cause transmission errors that can force a node into a bus off state, where it becomes unresponsive until it is reset. The query is a discovery step for a single board's UUID, not a broadcast.

Which microcontrollers does Katapult support?

lpc176x, stm32, rp2040 and rp2350 are supported, and CAN support is currently limited to stm32 F-series, rp2040 and rp2350 devices. The project is licensed under the GNU GPL v3, and its only tagged release is v0.0.1 from 2022-07-21 while the last push was on 2026-10-01.

Official sources

  1. Arksine/katapult on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/arksine-katapult.svg)](https://hysenlabs.com/projects/arksine-katapult)

Was this useful?