This repository contains a Python implementation of a lightweight ZMODEM-like file transfer protocol that is tightly integrated with the MeshCore mesh networking client. The code is intended to be self‑contained, dependency‑free (other than MeshCore when used), and covered by an extensive unit test suite.
- The project follows PEP 8 style; run
black/autopep8orflake8in the virtualenv before committing. Arequirements-dev.txtis provided for the linter and test runner. - Use
pytest(withpytest-asyncio) to execute the tests. The test harness used in CI also relies onpytest-timeoutto prevent hanging tests; it's included inrequirements-dev.txt.
Running tests (recommended workflow):
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements-dev.txt
pytest -vIf tests hang locally, pytest-timeout enforces per-test time limits; you can
adjust timeouts via --timeout=<seconds> when invoking pytest.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
pytest -q- All lint warnings have been cleared; contributions should remain free of
E722/E701/unused-imports etc.autopep8 --in-placeis handy for bulk fixes.
Akita-Zmodem-MeshCore is a robust file transfer utility developed by Akita Engineering (www.akitaengineering.com). It enables reliable file and directory transfers over low-bandwidth, high-latency MeshCore networks (based on ripplebiz/MeshCore) using the venerable Zmodem protocol.
This utility is designed for asynchronous operation, making it suitable for environments where network responsiveness can vary.
- Built‑in Zmodem Protocol: A fully self‑contained, Z‑modem‑like implementation lives in
zmodem.py, so there is no requirement to install an external Zmodem library. The code handles handshakes, CRC32 framing, resumable transfers, per‑chunk port headers, and stale duplicate ACK/RESUME suppression for reliable operation over the mesh. - MeshCore Integration: Uses Python bindings for MeshCore networks (tested with
fdlamotte/meshcore_pybut compatible with any API providingMeshCore.create_*,mesh.subscribe, andmesh.commands.send_msg). - Async I/O with asyncio: Non‑blocking operations keep transfers responsive even on poor links.
- File & Directory Support: Send files directly or zip directories on‑the‑fly; received zips are automatically extracted.
- Chunk Header Handling: Data sent over the mesh is split into
mesh_packet_chunk_sizefragments; each fragment begins with an application port header so the receiver can reassemble correctly. The default is184bytes, matching the current MeshCore payload ceiling. - Configurable Timeouts: Automatic cancellation of stalled transfers.
- JSON Configuration: Tweak ports, chunk sizes, MeshCore connection params, and more via
akita_zmodem_meshcore_config.jsonor CLI overrides. - Robust CLI: Commands (
send,receive,status,cancel) either run as a one‑off or the script can operate as a long‑running daemon. - Daemon Mode & Status: Run as a listener and query active transfers.
- Cancellation & Safety: Cancel mid‑transfer and protect against unwanted overwrites.
- Detailed Logging: Built‑in logging at INFO/DEBUG levels aids debugging and monitoring.
- Operational Context: See USE_CASES.md for concrete deployment scenarios across UAS, agriculture, SAR, infrastructure, research, and remote maintenance.
Because the Zmodem protocol is implemented internally, the only mandatory third‑party dependency is the meshcore Python library (to communicate with a MeshCore radio). You do not need to install any separate zmodem package; if one is present it will be ignored in favor of the built‑in implementation.
- MeshCore Python Library – must expose the basic API used here (
MeshCore.create_serial/create_tcp,mesh.subscribe,mesh.commands.send_msg, etc.). This library handles the low‑level radio/TCP transport. - Optional:
tqdmfor progress bars in CLI sessions (not required). If absent, the program falls back to plain logging.
All other functionality is self‑contained within the repository.
- Python 3.8+ is recommended.
- Clone the repository (or download the script).
- Create and activate a virtual environment (recommended):
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
- Install the required Python libraries:
This installs
pip install -r requirements.txt
meshcoreplus the optionaltqdmhelper listed inrequirements.txt. No externalzmodempackage is required.
-
Generate Configuration File: The configuration file
akita_zmodem_meshcore_config.jsonwill be created with default settings in the same directory as the script if it doesn't exist on the first run.python akita_zmodem_meshcore.py --help # Running with --help or any command will generate it -
Edit Configuration: Modify
akita_zmodem_meshcore_config.jsonto suit your needs. Key settings include:zmodem_app_port: An application-level port number used to distinguish Zmodem traffic over MeshCore.mesh_packet_chunk_size: Maximum size of data chunks sent over the mesh network, including the prepended app-port header. The valid MeshCore-safe range is greater than the 2-byte header size and at most184bytes.timeout: Transfer timeout in seconds.mesh_connection_type: How to connect to your MeshCore device (serialortcp).mesh_serial_port,mesh_serial_baud: Settings for serial connection.mesh_tcp_host,mesh_tcp_port: Settings for TCP connection.
These MeshCore connection settings can also be overridden via CLI arguments. See
docs/CONFIGURATION.mdfor more details.
The utility is controlled via command-line arguments.
python akita_zmodem_meshcore.py [OPTIONS] [COMMAND] [COMMAND_ARGS]Global Options (Optional, override config file):
--config <path> Path to JSON configuration file (created if missing)
--mesh-type [serial|tcp] Override connection type
--serial-port <PORT_PATH> (e.g., /dev/ttyUSB0, COM3)
--serial-baud <BAUDRATE>
--tcp-host <HOST_IP_OR_NAME>
--tcp-port <PORT_NUMBER>
Commands:
Run as a daemon (listener mode):
python akita_zmodem_meshcore.py
# (add connection args if not in config or to override)Send a file or directory:
python akita_zmodem_meshcore.py send <destination_node_id> <path/to/file_or_dir><destination_node_id>: String identifier for the target MeshCore node (e.g., !aabbccdd or a name your MeshCore setup recognizes).
Receive a file or directory:
python akita_zmodem_meshcore.py receive <path/to/save_location> [--overwrite] [--directory]--directoryforces the path to be treated as a directory even if it does not yet exist or lacks a recognisable extension. The utility will also infer directory mode automatically when the target already exists and is a directory.
Note: For directory reception, provide the path where the directory's contents should be extracted.
Get transfer status:
python akita_zmodem_meshcore.py status <transfer_id>Cancel a transfer:
python akita_zmodem_meshcore.py cancel <transfer_id>See docs/USAGE.md for detailed command explanations and examples.
# On Machine A (Sender):
# Ensure your config file is set up or use CLI overrides for connection:
# e.g., python akita_zmodem_meshcore.py --mesh-type serial --serial-port /dev/ttyUSB0
# Send a file to node with ID "!TargetNodeHexID"
python akita_zmodem_meshcore.py send "!TargetNodeHexID" my_document.txt
# On Machine B (Receiver - running in daemon mode):
# Start the listener (it will use its config for connection)
python akita_zmodem_meshcore.py
# Alternatively, to pre-designate where a file should go on Machine B:
# python akita_zmodem_meshcore.py receive received_files/my_document.txt [--overwrite]
# This command will wait for the specific transfer to complete.Contributions are welcome! Please feel free to submit pull requests or open issues for bug reports and feature requests.
See CONTRIBUTING.md for more details.
This project is licensed under the GNU General Public License v3.0. See the LICENSE file for details.