A command line tool that builds, compiles, and synchronizes project files to a MicroPython board. It maintains a content
hash manifest, creates device directories as needed, and can optionally run mpremote to drop you at a REPL after a
successful sync.
- Walks your working tree and selects only eligible files
- Creates a clean
build/tree and copies sources into it - Compiles Python files to
.mpyusingmpy-cross, skipsboot.pyandmain.py - Computes SHA-256 checksums of uploaded bytes, tracks a persistent
.sync-manifest.json - Uploads only changed files unless
--forceis used - Creates nested directories on the device before uploading files
- Cleans up files and empty directories on the device that are not in your local build
- Provides a full purge command for a clean slate
- Python 3.8+
adafruit-ampyfor interacting with the board filesystemclickfor the CLImpy-crossavailable on your PATH for.mpycompilation- A serial device path for your board
- Linux example:
/dev/ttyACM0or/dev/ttyUSB0 - macOS example:
/dev/tty.usbmodem*or/dev/tty.usbserial* - Windows example:
COM3
- Linux example:
pip install -r requirements.txtPlace mp_sync.py at the root of your project repository.
# List the files that will be considered part of the build
python mp_sync.py list-src
# Build and compile to the local build directory
python mp_sync.py build
# Sync to the board on the default port
python mp_sync.py sync
# Sync to a specific port with more logging
python mp_sync.py --port /dev/ttyACM0 -v sync
# Force upload all files, regardless of the manifest
python mp_sync.py sync --force
# Purge all files and directories on the board, then soft reset
python mp_sync.py purge
# Sync, then automatically open REPL with mpremote
python mp_sync.py sync --mpremoteUse
-vonce to enable DEBUG logs.
.
├── build
├── config.py
├── controllers
│ ├── led_controller.py
│ └── __init__.py
├── docs
├── main.py
├── boot.py
├── mp_sync.py
├── pyproject.toml
└── requirements.txtThe idea is to keep your project structure as flat as possible, with only a few top-level files. This will allow
mp_sync.py to find all files, compile them, and upload them to the board.
Global options:
--port, -pSerial port to use, default/dev/ttyACM0--verbose, -vIncrease verbosity, repeat for more detail
Commands:
list-srcPrint the local source files that will be included in a buildbuildCopy eligible files tobuild/and compile.pyto.mpysyncUpload files frombuild/to the board--forceUpload all files, ignore previous hashes--auto-build/--no-auto-buildRun build before sync, default on--mpremote/--no-mpremoteOpen REPL after sync withmpremote, default off
purgeRemove all files and empty directories from the device, then soft reset
sequenceDiagram
participant User
participant CLI as mp_sync.py CLI
participant Files as ampy.Files
participant Board as MicroPython board
User ->> CLI: run `python mp_sync.py sync`
CLI ->> CLI: make_build(), compile_build()
CLI ->> Files: ls(long_format, recursive)
Files ->> Board: enumerate filesystem
CLI ->> Files: rmdir(), rm() for stale items
loop per file in build
CLI ->> Files: mkdir() for parent directories as needed
CLI ->> Files: put(path, content)
Files ->> Board: write bytes
end
CLI ->> Board: soft reset via Pyboard
CLI -->> User: Sync complete
- Source discovery
- Recursively scan the project folder, filter by allowed extensions and exclusions.
- Build staging
- Create a clean
build/directory, copy all selected sources into it.
- Create a clean
- Compilation
- For every
.pyinbuild/, runmpy-crossand replace with.mpy, skipboot.pyandmain.py.
- For every
- Manifest accounting
- Load
.sync-manifest.jsonwhich stores size, mtime, and SHA-256 of each uploaded payload.
- Load
- Device inventory and cleanup
- List files and potential empty directories on the device, remove entries that are not in the current build.
- Directory creation on device
- Create destination directories on the device before each upload.
- Selective upload
- Upload only files whose content hash differs, update manifest accordingly.
- Finalize
- Prune manifest entries for files no longer present in
build/, write manifest, soft reset the board.
- Prune manifest entries for files no longer present in
flowchart LR
subgraph Host
A[list_src_files]
B[make_build]
C[compile_build]
D[sync]
E[purge_board]
F[manifest IO]
G[file_sha256]
H[make_dirs]
end
subgraph Device
I[Board filesystem]
end
A --> B --> C --> D
F <--> D
G --> D
H --> D
D --> I
E --> I
Included extensions: .py, .css, .html, .js, .tpl, .mpy
Special entries: boot.py, main.py are copied but not compiled.
Excluded items:
- Common virtualenv folders:
venv,.venv - Local folders and helper scripts:
local,index_tpl.py,sftp-sync.py - The current script name and the manifest file
- The
build/directory - Top level dot path is ignored
You can modify FILE_EXTENSIONS and SRC_EXCLUSIONS in the script to suit your project.
- Location:
.sync-manifest.jsonat the project root - Format: JSON mapping from a relative path to metadata
- Properties per file
sizeinteger, size of the uploaded filemtimefloat, modification time at uploadsha256string, checksum of the exact uploaded bytes
This manifest allows detection of unchanged content, which avoids unnecessary uploads.
- Ensure
mpy-crossis discoverable on PATH - Use
-vto see DEBUG logs if compilation or upload fails - If the board filesystem looks inconsistent, try
purgethensyncagain - Skip compilation for entry points, keep, and
main.pyas plain.pyfiles - On Windows, prefer a short project path to avoid path length issues
mpremote not foundInstall mpremote or remove the--mpremoteflagmpy-cross not foundInstall or buildmpy-crossfor your platform, then add it to PATHFailed to connect to boardCheck the--portvalue and your user permissionsCompilation failed for foo.pyFix syntax errors or unsupported features for your target MicroPython version, the script will skip failed files and continue
- Logging is configured at INFO by default, set
-vto increase verbosity to DEBUG - Directory creation leverages a simple cache to avoid duplicate mkdir calls
- Upload decisions are based on the hash of actual bytes that will be sent
- After successful sync, a soft reset is issued so your updated code starts immediately
MIT License
Copyright (c) 2025 Clayton Kramer
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.