How to enter into bootloader with serial connected MCU

Basic Information:

Printer Model: Kingroon KP3S Pro V2
MCU / Printerboard: gd32f303 stm32f103
Host / SBC Armbian/Xubuntu 22.04.5
klippy.log I hope klippy log is not needed in this case because it is not about klipper itself.

I am trying to move to katapult bootloader as it gives ability to flash via serial connection.
I tried different methods to put mcu into bootloader described here: Bootloader Entry - Klipper documentation
But not successful.

I have a small development board with stm32f103 and checked everything about bootloader on it too.
If klipper/katapult was builded with USB connection on PA11/PA12 (/dev/ttyUSB0) klipper successfully puts this mcu into bootloader at least with two first methods from the article and i can flash it with katapult tool.
In case klipper/katapult was builded with USART1 on PA10/PA9 (/dev/ttyACM0) all of this methods are not working. I am using USB TTL converter based on CH340 in this case.
The difference between builds is only connection. In both cases i can flash mcu by calling katapult with rapid reset pushes. But it is not possible in case of the printer.
Klipper firmware in both cases is working as LED pin i used in build configuration was activated and i can run klippy with minimal printer.cfg and check it with set_pin for example.
How can I force klipper to go into bootloader in this case?
The same situation in printer case. mcu i want to flash is connected as serial device but without converter (/dev/ttyS0).

See https://www.st.com/resource/en/application_note/an3155-usart-protocol-used-in-the-stm32-bootloader-stmicroelectronics.pdf

I hope this is beyond the scope of my question. I asked about how to jump into bootloader from the loaded klipper in case it is connected through USART.
I discovered that if klipper firmware was started (led is on) if I send
echo $'~ \x1c Request Serial Bootloader!! ~' >> /dev/ttyUSB0 (mcu connected with PA10/PA9 through usb ttl converter) it doesn’t work.
But if previously klippy was started it works. It doesn’t matter if it executing during this command execution or not.
The strangest thing is it still working after mcu rebooting.

Case1:

  1. Connect ttl converter + stm32 power through USB
  2. Run echo $‘~ \x1c Request Serial Bootloader!! ~’ >> /dev/ttyUSB0
  3. FAIL

Case2:

  1. Connect ttl converter + stm32 power through USB
  2. Run python3 klippy/klippy.py printer.cfg -I klippy.serial
  3. Stop klippy.py
  4. Run echo $‘~ \x1c Request Serial Bootloader!! ~’ >> /dev/ttyUSB0
  5. WIN
  6. Press reset button on stm32 board. Led is ON (means klipper was started).
  7. Run echo $‘~ \x1c Request Serial Bootloader!! ~’ >> /dev/ttyUSB0
  8. WIN
  9. RESET mcu
  10. Run echo $‘~ \x1c Request Serial Bootloader!! ~’ >> /dev/ttyUSB0
  11. WIN
  12. Remove power from mcu
  13. Run echo $‘~ \x1c Request Serial Bootloader!! ~’ >> /dev/ttyUSB0
  14. WIN

Case3:

  1. Remove power from all components.
  2. Connect ttl converter + stm32 power through USB
  3. Run echo $‘~ \x1c Request Serial Bootloader!! ~’ >> /dev/ttyUSB0
  4. FAIL

Case4:

  1. Remove power from all components.
  2. Connect ttl converter ONLY
  3. Run python3 klippy/klippy.py printer.cfg -I klippy.serial
  4. Got error serialhdl.error: mcu ‘mcu’: Serial connection closed
  5. Stop klippy
  6. Power stm32 by connecting to USB
  7. Run echo $‘~ \x1c Request Serial Bootloader!! ~’ >> /dev/ttyUSB0
  8. WIN

Can you explain this behavior?
What klippy execution does with this equipment even if stm32 is not powered on?

I have some experience with the built in bootloader with serial connection on the STM32G0B1.

First, I think you’re going to need the schematics for your main controller board. Saying it’s “gd32f303 stm32f103” doesn’t provide enough information.

Secondly, you’re going to have to study STM Applications Note AN2606. If you do, you’ll see that for the STM32F10xxx" (in Table 2, Pattern 1) that you activate the bootloader by bringing the “Boott0” pin high and the “Boot1” pin low on the MCU. Normally these are buttons on the main controller board to provide these functions.

Since I don’t know what the circuitry is in your main controller board, I can’t comment why your “Case2:” works.

Thanks for your answer. I read your post about serial connection and bootloader (issue in katapult github) at time I was trying to make it work on my printer. Now i am trying the same on development board with stm32f103 because it is hard to do on the printer.
Of course it is possible I don’t understand something but I am talking about jumping to katapult bootloader not about stm32 bootloader from klipper. And it works (led starts flashing by katapult).
I can’t use any buttons or pins on the printer, they are deep inside it’s case.
This is my development board.


This is how i connected it to laptop

The most interesting case is Case4.
I can jump to katapult by pressing two times reset button (default behavior of katapult) and it works. Led starts flashing.
I can jump to katapult by sending ~ \x1c Request Serial Bootloader!! ~’ but only if klippy was previously started even if stm32 is not powered on.
I don’t have any issues with flashing klipper through usart if katapult is loaded, or starting/running klipper.

klippy made something with my system.
After running

stty 115200 < /dev/ttyUSB0

It stops working. But if klippy was started it starts working again.

This happens because of a non-standard baud rate which can’t be adjusted via stty for example.

stty -F /dev/ttyUSB0 250000
stty: invalid argument ‘250000’
Try 'stty --help' for more information.

stty 115200 < /dev/ttyUSB0 broke success command sending
after execution of this python code it starts working again.

import serial

ser = serial.Serial('/dev/ttyUSB0', baudrate=250000)
print(f"Port opened with baud rate: {ser.baudrate}")
ser.close()

It works for all cases. I think it should be described in Bootloader Entry - Klipper documentation

2 Likes

Interesting. Thanks for sharing. I wonder what the real root cause to this behavior actually is.

To wit, I’m currently fighting against a stubborn Octopus board with flashed katapult, that would enter the BL on first attempt but refuses to flash Klipper. After resetting the board it will stay in katapult and flashing works regularly as expected.

When TTL-USB converter plugged in it’s default baud rate as we can see is 9600 and bootloader command is not working.

stty -F /dev/ttyUSB0
speed 9600 baud; line = 0;
-brkint -imaxbel

After klippy execution:

stty -F /dev/ttyUSB0
speed 0 baud; line = 0;
min = 0; time = 0;
-brkint -icrnl -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe -echok -echoctl -echoke

stty can be used to set speed 0 baud but it throws an error and bootloader command is not working but looks like it was adjusted

stty -F /dev/ttyUSB0 115200
stty -F /dev/ttyUSB0
speed 115200 baud; line = 0;
min = 0; time = 0;
-brkint -icrnl -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe -echok -echoctl -echoke
stty -F /dev/ttyUSB0 0
stty: /dev/ttyUSB0: unable to perform all requested operations
stty -F /dev/ttyUSB0
speed 0 baud; line = 0;
min = 0; time = 0;
-brkint -icrnl -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe -echok -echoctl -echoke

Sorry, how did you get Katapult on your main controller board? That wasn’t clear at all in the original post.

That’s known as a “Blue Pill development board”:

What do you have connected to the USB connector at the end of the board? Is it just power or are you also running data?

You’ve put a number of posts following this one that I’m responding to. I’m not sure which boards you are working with and what they are loaded with. Is there some way in which you could organize the data to indicate which boards are being used and what firmware was loaded onto them and how?

Finally, I’m not sure that you’re using Katapult in a manner for which it is designed. As far as I can tell, the Klipper installation that you’re using does not communicate with another controller using CAN (just looking at the GitHub page it indicates that it also supports USB and Serial connected controllers).

Is @arksine out there to comment?

I used a katapult deployer with 8 kb offset. katapult was deployed as i expected but i think it starts something like a garbage of previous bootloader. Then i flash it with stlink on previously mass_erased flash and after that i flash klipper with katapult flashtool. The main problem is i can’t jump to katapult from the klipper by sending a command. I don’t have a button outside of the printer case.
Here is the detailed description Get to katapult bootloader mode via serial UART1 ¡ Issue #135 ¡ Arksine/katapult ¡ GitHub

Yeah, it is a kind of it. I am using it because GD32F303 clone of stm32f103.

In this case it is just a power. stm32f103 with katapult or klipper can’t use both at once (usart & usb) as there is no suck option in make menuconfig.

It doesn’t matter because this topic is only about why the command works if this board connected as USB and doens’t work when it is connected as USART. This is only one difference.

I am trying to use katapult in a manner for which it was designed. I just want to flash klipper WITHOUT connecting pins, pressing buttons and other. I have only one problem - request bootloader mode through the klipper.
Katapult can requests bootloader but CAN only
help="Requests the bootloader and exits (CAN only)"
USB and UART communication for flashing is working too but there is no way to put it into bootloader mode by sending command. So this command should be sent by other tools. There is 3 method described in article above. But any of them are not successful.
And now we are knew why.
I will make a PR to Katapult repo with changes which allows to request bootloader mode for serial devices.

This little program works for serial connected mcu:


#!/usr/bin/python3

import serial

# Connection parameters
DEVICE = "/dev/ttyUSB0"
BAUDRATE = 250000  # Specify the port speed

# Data to send
COMMAND = b"~ \x1c Request Serial Bootloader!! ~\n"

def main():
    try:
        # Open the port
        with serial.Serial(DEVICE, BAUDRATE, timeout=1) as ser:
            print(f"Port {DEVICE} opened at {BAUDRATE} baud")

            # Send the command
            ser.write(COMMAND)
            print(f"Command sent: {COMMAND.decode(errors='replace')}")

            # Read possible response
            response = ser.read(100)  # Read up to 100 bytes
            if response:
                print(f"Response received: {response.decode(errors='replace')}")
            else:
                print("No response from the device.")

    except serial.SerialException as e:
        print(f"Serial port error: {e}")
    except Exception as e:
        print(f"General error: {e}")

if __name__ == "__main__":
    main()

Tried the same on printer and failed. In printer system there is a /dev/ttyS0 device.
I launched picocom and monitored the arrival of data from mcu. Sent this command, but the data continued to arrive. On the development board, after sending the command, they stopped. Is it possible this is related to bootloader which is not katapult right now?

I want to use the Katapult deployer. However, by default, it doesn’t have an application offset of 32 KiB, which is how the Klipper firmware is currently built. With an 8 KiB offset, it likely runs what is already stored in the flash memory. As a result, the Klipper firmware doesn’t start and the bootloader is not accessible. Will it work if I configure the Katapult deployer with a 32 KiB application offset? Will it run the Klipper firmware that is already in the flash memory, starting at 32 KiB?

I checked. It works. I flashed the Klipper with a 32 KiB offset. I flashed the Catapult with an 8 KiB offset. Klipper doesn’t start, but the bootloader is accessible. I then built Catapult with a 32 KiB offset, flashed it, and the Klipper started.
So the plan is as follows:

  1. Build the Katapult with a 32 KiB offset and use the same offset for the Catapult deployer (since this offset is used in the bootloader installed on the printer).
  2. Write it to the SD card and power on the printer with the SD card inserted. If everything goes as expected, Katapult will launch the already installed Klipper on the next start, and I won’t lose control over the MCU.

@mykepredko @Sineos
Don’t you know how to catch the bootloader after a reboot without using buttons? I managed to do it once somehow. The bootloader didn’t start the Klipper because the offset did not match the one from which Klipper started, and at the same time, the bootloader was no longer available. But I reset the microcontroller via ST-Link while simultaneously running flashtool.py from the Кatapult. I managed to flash it once this way. My printer has supercapacitors, and the system remains accessible for about 15 seconds, but the microcontrollers turn off immediately after power is cut. So I can reset the microcontroller without rebooting the operating system.

Just to be clear, when you say “Bootloader”, you mean Katapult - correct?

As far as I know, there is no way to engage Katapult on the main controller board without pressing reset twice quickly. There is no communications string or process. This is by design to avoid complicating things on the host running Klipper/Moonraker.

Most likely your ST-Link toggled the reset line a couple of times to put the

I’m not exactly sure what you’re describing (and I don’t know what you consider “supercapacitors” or how they’re wired) but it sounds like you’re flipping power on and off, causing the MCU to reset and the capacitors maintaining power to your host computer.

That’s pretty sketchy and hard on your power supply (as well as main board electronics).

On my printers, I run a line from the nRESET and GND lines on the SWD connector to a momentary on push button mounted on (the rear of) my printers. Maybe not completely elegant but discrete and works well. Maybe this is a solution that will be acceptable to you.

Katapult or another bootloader which is currently installed on my printer. But not embedded stm32 bootloader.

I thought that perhaps the command from the flashtool.py script was sent at exactly the moment when the bootloader hadn’t yet launched Klipper. Similar to a delay in U-Boot: if you press any key during this time window, you will enter U-Boot.

Absolutely right.


This part is named BIT WELL.
The manufacturer has provided a way to correctly shut down the operating system. When the power is turned off, the state of one of the GPIO pins of the SoC transitions to 1, and a systemd service executes the shutdown command.

This is better than having to disassemble half of the printer again. :slight_smile:
Of course, as long as it provides a way to access the bootloader.

Thanks for the suggested option, I’ll think about it. Now it’s possible to reliably update the firmware via SD card without disassembling. But I want a more convenient option.

There are 4 conditions that can trigger Katapult to enter the bootloader:

  1. Jump to it from an application such as Klipper
  2. Configure double reset
  3. Configure a button (this could be a jumper as well)
  4. Katapult detects that the application area is empty

In addition, after the deployer writes Katapult to flash memory it will set the magic key that enters the bootloader after reset (this effectively the same thing as jumping to the bootloader from an application).

I found this picture on aliexpress, it “might” help. Good luck.

Have a look at the lower right corner, there are the markings “MCU-RST” and “MCU-BOOT”.