How to convert PlatformIO firmware with build offset to firmware without

Some firmwares are build with a build offset to accomodate flash space (like 32k – 0x8000) for a bootloader that is started before the firmware. One of the downsides of using such a firmware is that it always depends on a compatible bootloader to be present (compatibility is mostly defined by how much flash is allocated for the bootloader)

If you want to convert a firmware to a “normal” non-bootloader firmware, this is what you’ll have to do:

  • Set board_build.offset to 0x0 – look not only in your build configuration in platformio.ini and all included files, but also look in the configuration that your config extends, if any.
  • Set board_upload.offset_address to 0x0. This mostly affects uploading with a debugger, but you should always keep this information consistent because debugging that is a huge nightmare.
  • Edit the specific linker script your firmware is using and set the flash offset appropriately:

Look for the MEMORY section like this:

FLASH (rx)      : ORIGIN = 0x8008000, LENGTH = 512K
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K

As you can see in

FLASH (rx) : ORIGIN = 0x8008000, LENGTH = 512K

the flash for this STM32 (example) is not at the MCU’s flash address 0x8000000, but at 0x8008000 – basically, the linker is told to skip the 0x8000 (32k) the bootloader occupies (else, it would overwrite the bootloader.

Change this to your MCU’s flash adress. For ARMs like the STM32, this is typically 0x8000000.

Now recompile and your firmware should work without the bootloader.

Posted by Uli Köhler in Embedded, PlatformIO, STM32

How to hide all boot text & blinking cursor on Raspberry Pi

In order to hide all the boot messages (including the terminal) and the blinking cursor on the Raspberry Pi, edit /boot/cmdline.txt and first change




After that, add the following to the end of the line:

 loglevel=3 quiet logo.nologo vt.global_cursor_default=0

Now you can reboot and enjoy the clutter-free experience.

Posted by Uli Köhler in Raspberry Pi

How to install OctoScreen plugin for OctoPrint

OctoScreen provides a plugin that you can use within OctoPrint to configure the UI. You can find it on GitHub here, but you can simply install it using the OctoPrint plugin manager using the following URL:


Posted by Uli Köhler in 3D printing

How to change OctoScreen minimum extrusion temperature


When trying to extrude low-temperature filaments using OctoScreen, you will see this error message:

The temperature of the hotend is too low to extrude. Please increase the temperature and try again

By default, OctoScreen has a minimum extrusion temperature of 150°C which is fine for PLA, but  for some low temperature materials you need to change the hardcoded value.

Note that setting you minimum extrusion temperature too low might damage or even destroy your extruder, if the material is not properly melted. So take caution in setting the correct value here.


Open utils/tools.go and edit this line:


Just set your desired temperature and then recompile (see the OctoScreen README on how to do that)

Note that often the firmware (like Marlin) has a separate cold extrusion prevention limit, so you need to change the limit in both OctoScreen and the firmware. For Marlin, see How to disable Marlin cold extrusion prevention via G-Code.


Posted by Uli Köhler in 3D printing, Embedded, Raspberry Pi

How to auto-reset Marlin after Kill

Under certain critical circumstances like thermal runaway protection, Marlin entered a killed state. You typically need to hard-reset the mainboard after entering that state. However, in environments where physical access to the machine, hard-resetting Marlin is not possible, so you might need to implement.

Note that diagnosing & fixing the underlying issue that caused the reset is extremely important and ignoring it might lead to further issues, up to a fire hazard or the destruction of the printer. So be sure to know what you are doing before just.

The following guide was tested with Marlin It should work with most Marlin 2.x versions with minor adjustments.

First, note that as an alternative to hard-resetting the mainboard there is the SOFT_RESET_ON_KILL option which allows you to trigger a soft reset out of kill mode using a button connected to a pin. We won’t delve into more detail on this, since it doesn’t really change the requirement for physical access to the printer.

Open Marlin/src/MarlinCore.cpp and find the minkill(bool) function.

At the end, this function contains a preprocessor-controlled branch of the following structure:

  // Branch 1 ...
  // Branch 2 ...

We will replace this entire branch by these lines:

// Wait for 5 seconds for controller to catch up
for (int i = 5000; i--;) { DELAY_US(1000); watchdog_refresh(); } // 5000*1ms = 3s
// Auto-reset after kill

I prefer to insert the 5-second delay into the reboot logic so no superordinate controller (like a Raspberry Pi running OctoPrint) can miss that the Marlin board has been killed. Although most controllers should be able to detect the killed state from the G-Code, this 5-second delay will simulate the default behaviour of not resetting at all after the killed state has been reached. However note that in case you absolutely need to reset ASAP after the kill state has been reached, it’s typically safe to omit the wait loop.

Posted by Uli Köhler in 3D printing, Embedded

Implementing STM32 DFU bootloader firmware upgrade in Marlin using M997

Marlin implements the M997 command which is intended to switch the mainboard into a firmware upgrade mode.

All STM32 variants have an integrated (hard-coded – so no need to flash it yourself) bootloader that is notoriously difficult to active.

However, Marlin implements M997 on the STM32 as just a reboot:

void flashFirmware(const int16_t) { HAL_reboot(); }

This only works if you are using a custom bootloader on your board – however, it does not use the STM32 integrated bootloader.

The following code was tested on the STM32F446 (BigTreeTech Octopus V1) but should work on any STM32 variant. It is based on previous work by Dave Hyland on StackOverflow. Replace the default flashFirmware() function in Marlin/src/HAL/STM32/HAL.cpp

void flashFirmware(const int16_t) {


    // arm-none-eabi-gcc 4.9.0 does not correctly inline this
    // MSP function, so we write it out explicitly here.
    //__set_MSP(*((uint32_t*) 0x00000000));
    __ASM volatile ("movs r3, #0\nldr r3, [r3, #0]\nMSR msp, r3\n" : : : "r3", "sp");

    ((void (*)(void)) *((uint32_t*) 0x00000004))();

    // This will never be executed

Note that we left a HAL_reboot() call as a safeguard at the end, just in case the previous calls fail.

On my BigTreeTech Octopus V1 (STM32F446), by using this code, you can successfully enter the integrated DFU bootloader.

Also see our previous posts on how to use the STM32 in DFU bootloader mode:

Posted by Uli Köhler in 3D printing, C/C++, STM32

How to allow ALL filetypes for Media upload in WordPress

On your own blog or website, you often don’t care about the WordPress upload restriction and just want to be able to upload all filetypes, to just upload any filetype.

In order to do that, append this line to wp-config.php:


and then, log out from WordPress and log back in again.

After that, you should be able to upload any file, no matter its file type.

Posted by Uli Köhler in Wordpress

How to run System() in dialplan in the background on Asterisk/FreePBX

In Asterisk, a System() command like

exten => 123456,n,System(mycommand)

will generally run in the foreground, i.e. Asterisk will wait until the command is finished before continuing with the Dialplan.

However, just like in any normal Linux shell, you can add a & to the end to run the command in the background:

exten => 123456,n,System(mycommand &)


Posted by Uli Köhler in FreePBX

How to restart Asterisk from Dialplan in FreePBX

In our previous post How to add action on call to custom extension in FreePBX we showed how to get. Moreover, in our post on How to restart Asterisk in FreePBX we explored the three different options of restarting Asterisk, e.g. core restart gracefully.

In this post, we’ll add a dialplan extension (i.e. a number that you can dial) that restarts Asterisk. This will not restart FreePBX, but completely restart (and not only reload) Asterisk itself, loading any new configuration options and re-intializing all SIP trunks, for example. In this case we’re using core restart gracefully, i.e. we’ll be waiting for any currently active calls to finish, but not admit new calls.

Add this block to /etc/asterisk/extensions_custom.conf (see How to add action on call to custom extension in FreePBX):

exten => 999999,1,Answer()
exten => 999999,n,Wait(0.5)
exten => 999999,n,SayAlpha(OK)
exten => 999999,n,System(bash -c "sleep 1 && asterisk -rx 'core restart gracefully'" &)
exten => 999999,n,Hangup()

Reload Asterisk and now you can call 999999 to restart Asterisk

How it works

Basically, we’re reading OK to the user and then running

System(bash -c "sleep 1 && asterisk -rx 'core restart gracefully'" &)

Basically, you could thing we’ll get away with just running

System(asterisk -rx 'core restart gracefully')

However, this will fail (or at least delay the restart for approximately one minute) because the call initiating the restart is still ongoing. Also, we can’t run System() after Hangup() because Hangup() will terminate the dialplan, hence System() will never run. Hence, we run it with a delay ( sleep 1) in the background (& at the end of the command), causing Asterisk to first cleanly hang up on the call and then the command will be run.

Note that the background & at the end is absolutely essential, omitting it will cause Asterisk to just wait for the sleep 1 && asterisk ... command to finish, not Hangup() in between.

Using bash -c to run the command is just a workaround so we can stuff the multiple chained commands of sleep 1 && asterisk ... into a single command which we can run in the background using the trailing &.

Posted by Uli Köhler in FreePBX

What does “BRep” mean in OpenCASCADE?

BRep in class and function names like BRepGProp means Boundary Representation. That means representing a geometrical object by its boundary, i.e. its outer shell. For example, a cylinder in boundary representation would be represented by two circles (its end faces) and a cylindrical shell.

BRep ist the way STEP files represent their geometry data – in contrast to, for example, STL files, representing the geometry by a buch of triangles connected together. Boundary reprensentation allows exact representation of geometries whereas representing objects with triangles or other methods often involves a loss of precision, i.e. the original geometry will be slightly different to the geometry as stored in a STL file.

Posted by Uli Köhler in OpenCASCADE

M206 Set home offset: Positive or negative coordinates? A simple rule for Marlin

When adjusting the home offset (most often the Z offset) of a 3D printer using the M206 command, many users wonder which sign to use. In other words, in order to move the nozzle 0.1mm closer to the bed, do you use M206Z0.1 or M206Z-0.1. However, there’s a simple rule:

To move the nozzle closer to the bed, use positive coordinates, e.g. M206Z0.1 to move the nozzle 0.1mm closer to the print bed.

To move the nozzle further away from the bed, use negative coordinates, e.g. M206Z-0.2 to move the nozzle 0.2mm further away from the print bed.


Posted by Uli Köhler in 3D printing

How to flash STM32 PlatformIO firmware using dfu-util

First, you need to find the correct firmware file. dfu-util will flash firmware.bin, not firmware.elf. You can find firmware.bin in


inside your project folder. [PROFILE_NAME] is the name of the build profile you’re using, i.e. the name of the section in platformio.ini. For example:


Now flash using dfu-util:

dfu-util -a 0 -D .pio/build/PROFILE_NAME/firmware.bin -s 0x08000000


  • -a 0. The STM32 appears as four different devices in dfu-util (see dfu-util --list): The flash, option bytes, RAM etc each appear as a separate device. We only care about the flash device, which is always the first (index 0) of those devices, at least in every board I have seen so far
  • -D [filename]Download the firmware to the device
  • -s 0x08000000: Flash at address 0x08000000 which is the address of the STM32 flash.
Posted by Uli Köhler in Embedded, PlatformIO, STM32

How to exit/reset STM32 DFU bootloader

Both dfu-util and dfu-tool can flash firmware, but their current versions can’t reset the STM32 from DFU mode to run the application.

I found that using MicroPython’s is able to reset the. Note that currently I use only for resetting, not for flashing (but that might change in the future).

Download using


Install the PyUSB dependency using

sudo pip3 install pyusb

And then reset the STM32 in DFU mode:

python3 -x


Posted by Uli Köhler in STM32

How to fix ‘command not found: pio’ even though PlatformIO is installed


You have installed PlatformIO on your computer using Visual Studio code, however when you try to run it in your terminal/shell, you see:

$ pio
zsh: command not found: pio


PlatformIO is installed in $HOME/.platformio, but not added to the PATH environment variable, so your terminal can’t find it. Add it using

echo "export PATH=\$PATH:/home/${USER}/.platformio/penv/bin" >> ~/.profile

and then logout from your current session and log back in again (or reboot) in order for the changes to take effect. After that, you can run pio from any shell.

Posted by Uli Köhler in Linux, PlatformIO

How to run single command on 3D printer using picocom

In our previous post How to connect to your 3D printer using picocom we showed how to open an interactive serial session on the command line using picocom. But you can also use picocom to run just a single command such as the M997 firmware update command:

echo "M997" | picocom -b 115200 /dev/ttyACM0 --imap lfcrlf --echo

If your USB serial device is named /dev/ttyUSB0 instead of /dev/ttyACM0 you can use this alternative version:

echo "M997" | picocom -b 115200 /dev/ttyACM0 --imap lfcrlf --echo



Posted by Uli Köhler in 3D printing, Linux

How to fix pio remote agent start: You are not authorized! Please log in to PlatformIO Account.


When running

pio remote agent start

you see an error message like

2021-10-05 17:37:23 [info] Name: Raspberry Pi
2021-10-05 17:37:23 [info] Connecting to PlatformIO Remote Development Cloud                                                                    
2021-10-05 17:37:23 [info] Successfully connected
2021-10-05 17:37:23 [info] Authenticating
2021-10-05 17:37:23 [error] You are not authorized! Please log in to PlatformIO Account.                                                        
2021-10-05 17:37:23 [info] Successfully disconnected


You need to login to your PlatformIO account. Run

pio account login

which will prompt you for your username & password. If you don’t have an account, register using

pio account register
Posted by Uli Köhler in PlatformIO

How to fix pio remote agent start: error: can’t find Rust compiler


When running pio remote agent start, while PlatformIO installs the Python packages, you see an error log like

  generating cffi module 'build/temp.linux-armv7l-3.7/_openssl.c'                                                                                                           
  creating build/temp.linux-armv7l-3.7                                                                                                                                      
  running build_rust                                                                                                                                                        
      =============================DEBUG ASSISTANCE=============================                                                                                            
      If you are seeing a compilation error please try the following steps to                                                                                               
      successfully install cryptography:                                                                                                                                    
      1) Upgrade to the latest pip and try again. This will fix errors for most                                                                                             
         users. See:                                                                                                
      2) Read for specific                                                                                                  
         instructions for your platform.                                                                                                                                    
      3) Check our frequently asked questions for more information:                                                                                                                                                                                                                                      
      4) Ensure you have a recent Rust toolchain installed:                                                                                                                                                                                                                                
      Python: 3.7.3                                                                                                                                                         
      platform: Linux-5.10.60-v7l+-armv7l-with-debian-10.10                                                                                                                 
      pip: n/a                                                                                                                                                              
      setuptools: 58.2.0                                                                                                                                                    
      setuptools_rust: 0.12.1                                                                                                                                               
      =============================DEBUG ASSISTANCE=============================                                                                                            
  error: can't find Rust compiler                                                                                                                                           
  If you are using an outdated pip version, it is possible a prebuilt wheel is available for this package but pip is not able to install from it. Installing from the wheel would avoid the need for a Rust compiler.                                                                                                                                   
  To update pip, run:                                                                                                                                                       
      pip install --upgrade pip                                                                                                                                             
  and then retry package installation.                                                                                                                                      
  If you did intend to build this package from source, try installing a Rust compiler from your system package manager and ensure it is on the PATH during installation. Alternatively, rustup (available at is the recommended way to download and update the Rust compiler toolchain.                                              
  This package requires Rust >=1.41.0.                                                                                                                                      
  ERROR: Failed building wheel for cryptography
Successfully built pyopenssl
Failed to build cryptography
ERROR: Could not build wheels for cryptography which use PEP 517 and cannot be installed directly
Error: Traceback (most recent call last):
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/platformio/package/manager/", line 127, in inject_contrib_pysite                                       
    from OpenSSL import SSL                                                                                                                                                 
  File "/home/pi/.platformio/packages/contrib-pysite/OpenSSL/", line 8, in <module>                                                                              
  File "/home/pi/.platformio/packages/contrib-pysite/OpenSSL/", line 12, in <module>                                                                               
  File "/home/pi/.platformio/packages/contrib-pysite/cryptography/x509/", line 8, in <module>                                                                    
  File "/home/pi/.platformio/packages/contrib-pysite/cryptography/x509/", line 18, in <module>                                                                       
  File "/home/pi/.platformio/packages/contrib-pysite/cryptography/x509/", line 20, in <module>                                                                 
  File "/home/pi/.platformio/packages/contrib-pysite/cryptography/hazmat/primitives/", line 11, in <module>                                                 
ImportError: /home/pi/.platformio/packages/contrib-pysite/cryptography/hazmat/bindings/ cannot open shared object file: No such file or directory    

During handling of the above exception, another exception occurred:                                                                                                         

Traceback (most recent call last):                                                                                                                                          
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/platformio/", line 115, in main                                                                    
    cli()  # pylint: disable=no-value-for-parameter                                                                                                                         
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/click/", line 1137, in __call__                                                                        
    return self.main(*args, **kwargs)                                                                                                                                       
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/click/", line 1062, in main                                                                            
    rv = self.invoke(ctx)                                                                                                                                                   
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/platformio/commands/", line 44, in invoke                                                          
    return super(PlatformioCLI, self).invoke(ctx)                                                                                                                           
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/click/", line 1668, in invoke                                                                          
    return _process_result(sub_ctx.command.invoke(sub_ctx))                                                                                                                 
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/click/", line 1665, in invoke                                                                          
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/click/", line 1404, in invoke                                                                          
    return ctx.invoke(self.callback, **ctx.params)                                                                                                                          
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/click/", line 763, in invoke                                                                           
    return __callback(*args, **kwargs)                                                                                                                                      
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/click/", line 26, in new_func                                                                    
    return f(get_current_context(), *args, **kwargs)                                                                                                                        
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/platformio/commands/remote/", line 40, in cli                                                       
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/platformio/package/manager/", line 129, in inject_contrib_pysite                                       
  File "/home/pi/.platformio/penv/lib/python3.7/site-packages/platformio/package/manager/", line 156, in build_contrib_pysite_package
    subprocess.check_call(args + [dep])
  File "/usr/lib/python3.7/", line 347, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['/home/pi/.platformio/penv/bin/python', '-m', 'pip', 'install', '--no-compile', '-t', '/home/pi/.platformio/packages/contrib-pysite', '--no-binary', ':all:', 'pyopenssl >= 16.0.0']' returned non-zero exit status 1.


An unexpected error occurred. Further steps:

* Verify that you have the latest version of PlatformIO using
  `pip install -U platformio` command

* Try to find answer in FAQ Troubleshooting section

* Report this problem to the developers



You need to install rust using

sudo apt -y install rustc

and then run the original command again:

pio remote agent start


Posted by Uli Köhler in PlatformIO

How to home only ONE axis in Marlin

In the Marlin 3D printer firmware, you can home one axis without homing the other axes using G28[Axis].

In order to home the X axis:


In order to home the Y axis:


In order to home the Z axis:


Homing X & Y without homing Z

You can also home multiple axes using G28[Axis 1][Axis 2]. So in order to home the X and the Y axis (but not home the Z axis), run



Posted by Uli Köhler in 3D printing

What is the correct supply voltage for DALI?

DALI operates at a supply voltage of 16V, current limited at 250mA.

Since the specified voltage range for a 1 bit is between 9.5V and 22.5V, the supply voltage can also be in this range (with 16V being right in the middle of this range. But even if you technically can use a 12V supply, this might not work for setups with longer cable lengths and/or setups with many DALI devices connected. If you have a 12V or 5V power supply, I recommend to use a boost converter to generate 16V.

Note that you always need to current-limit the DALI supply. Just connecting a power supply to the DALI line could destroy your equipment.

Posted by Uli Köhler in Electronics