Flashing UPDI devices with pymcuprog

If neither avrdude nor zflasher are viable choices, but the system is capable of running Python, then pymcyprog may be a good alternative. The main limitation of pymcuprog is that it only supports UPDI programmers (e.g. CH340).


Install Python

To run pymcyprog, first install Python. Use Python 3.x as older versions are no longer supported.


On Windows, there are two main ways to get Python:

  1. Use the Windows installer from Python

  2. Install from the Microsoft Store

Either method will result in a working Python installation.


Many Linux distributions come with Python installed as a part of the base OS, but those that do not should also have it available by their packaging system (e.g. apt, dnf, etc.).

In nearly all cases python should be on the path already so it should be ready to use as-is. It may be called python3 or versioned in a more specific way.


Download the macOS installer from Python and run it.

The installer adds it to the system path automatically, though it may be necessary to restart a terminal session to pick up the change.

Install pymcuprog

After installing python, open a command prompt and install pymcuprog:

$ pip3 install pymcuprog


Depending on the system, the pip command may be OK or it may point to a different version of Python. It’s safer to use pip3 to ensure it runs the Python 3.x version.


In some cases the install may trigger prompts to install other required dependencies, e.g. macOS may require development tools, Xcode or similar.

Getting pymcuprog on the Path

For Linux and macOS, pymcuprog should be in the command path automatically. This is not always the case for Windows, however.

There may be some quirks getting the paths right for pymcuprog if Python was installed from the Microsoft Store, since the location for the packages is not in the path by default.

Try running pymcuprog from a terminal, PowerShell, or command prompt, if it prints its usage output, then it’s OK to proceed. Otherwise, check and correct the path or python issues.

In Explorer, first locate the correct folder. It will be something similar to C:\Users\<name>\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_<id>\LocalCache\local-packages\Python39\Scripts – Copy that full path.

The correct path will also be found in the output of:

PS> python -c "import sys; print('\n'.join(sys.path))"

The correct item will be the entry similar to the path mentioned previously.

  • Right click on the Start/Windows button

  • Click System

  • Click Advanced System Settings

  • Click Environment Variables

  • Find the entry for Path in the User variables section and select it

  • Click Edit

  • Add the path to the list (paste it in or type it out)

  • Click OK, then OK again to close the Environment Variables window

  • Click OK one more time to close System Properties

pymcuprog Syntax

If pymcuprog is on the path properly, then invoking the command on its own at a terminal command prompt (e.g. PowerShell, Konsole, Terminal, etc.) will print some basic information on the syntax.

The pymcuprog command takes numerous parameters but only a handful are important for the purposes of working with flashlights:


Perform a communication test between pymcuprog, the programmer, and the controller chip. This does not make any changes to the controller or local system.


Read the chosen area from the chip and write it to a local file. This is used when making a backup of the current firmware on a controller chip.


Write the chosen area on the chip using content from a local file. This is used when updating the firmware on the controller chip to a new version using a local file.


Erase the chosen area on the controller chip.

-d <device>

The type of controller chip (MCU) on the light. Currently this is most likely to be attiny1616. In most cases the MCU for a given light is listed in the Anduril MODELS file.

-t <tool>

The type of connection to the programmer. When using a programmer such as the CH340 with a serial interface, this will be uart.

-u <uart>

The serial port through which pymcuprog can reach the programmer. For example, com7 or /dev/ttyUSB0.

See also

To locate the correct port, see CH340 Drivers and Serial Port.

-m <memory>

The type of memory to operate on. Only two areas are relevant for most controller chips found on flashlights, flash and eeprom (Controller Chip Storage Areas). The default is flash and this parameter can be omitted when working with that area.

-f <filename>

The firmware filename to read or write. If the file is not in the current directory, include the full path.

When reading, the file will be created or overwritten with the content of the selected area from the controller chip. When writing, the file will be read and used to reprogram the selected area of the controller chip.


Erase the selected memory area of the controller chip before writing. This is not performed by default, but is a beneficial step.


Verify the selected memory area of the controller chip after writing. This is not performed by default, but is a beneficial step.

See also

For more details on syntax, see the pymcuprog documentation on PiPI.

Ping the Device

The best first test of connectivity to the light is to perform a “ping” which makes sure that pymcuprog can communicate with both the programmer and the controller chip.

For this to work the programmer must be connected to the computer and the probe must be connected to both the programmer and the light. For example, by touching the probe to the flashing pads.

This example runs pymcuprog as if it is on the path in Windows communicating with an ATtiny1616 controller through a serial UPDI programmer on COM7, and attempts communication with the chip without making any changes:

PS> pymcuprog ping -d attiny1616 -t uart -u com7

The output should look like the following if it’s successful:

Connecting to SerialUPDI
Pinging device...
Ping response: 1E9421

If there is an error, most likely the probe was not correctly contacting all of the correct pins. Check and correct the probe placement and try again.

Backing Up

An important part of the process is making a backup of the firmware in case the new firmware does not work as expected. The old firmware can be written back so the light can be returned to its prior state.

When performing a read operation, pymcuprog reads the contents of the chosen area on the controller chip and then stores the contents in a local file.

Change to the directory where the firmware file was downloaded, for example:

PS> cd ~\Downloads

This example reads the firmware from a light (ATtiny1616 controller, serial UPDI programmer on COM7) and stores the old firmware in a file named old-firmware.hex in the current directory.

PS> pymcuprog read -d attiny1616 -t uart -u com7 -f old-firmware.hex


Change to the directory where the firmware file was downloaded, for example:

PS> cd ~\Downloads

Now write the firmware file. Replace the filename with the correct firmware filename that matches the light.

PS> pymcuprog write -d attiny1616 -t uart -u com7 --erase --verify -f new-firmware.hex