Skip to content

Repository files navigation

Akita-Zmodem-MeshCore

This repository contains a Python implementation of a lightweight ZMODEM-like file transfer protocol that is tightly integrated with the MeshCore companion client (meshcore 2.3.7). The protocol is self-contained. The only runtime dependency besides the optional tqdm progress bar is the MeshCore Python library.

Development notes

  • The project follows PEP 8 style; run black/autopep8 or flake8 in the virtualenv before committing. A requirements-dev.txt is provided for the linter and test runner.
  • Use pytest (with pytest-asyncio) to execute the tests. The test harness used in CI also relies on pytest-timeout to prevent hanging tests; it's included in requirements-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 -v

If 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-place is handy for bulk fixes.

License: GPL v3

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.

Key Features

  • 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: Talks to companion radios through meshcore 2.3.7 (create_serial/create_tcp, subscribe, send_msg(dst, msg), auto-fetch, auto-reconnect). File bytes are sent as AZM1: + base64 because companion send_msg is a UTF-8 text API, not a raw radio payload API.
  • 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: Protocol frames are split into mesh_packet_chunk_size binary fragments (including a 2-byte app-port header), then encoded into a MeshCore text body. The default and maximum is 129 bytes so the encoded message fits a 179-character TXT_MSG.
  • Configurable Timeouts: Automatic cancellation of stalled transfers.
  • JSON Configuration: Tweak ports, chunk sizes, MeshCore connection params, and more via akita_zmodem_meshcore_config.json or 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 auto-saves inbound files into incoming_dir.
  • 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.

Important Note on Dependencies

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: tqdm for 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.

Installation

  1. Python 3.10+ is required.
  2. Clone the repository (or download the script).
  3. Create and activate a virtual environment (recommended):
    python -m venv venv
    source venv/bin/activate  # On Windows: venv\Scripts\activate
  4. Install the required Python libraries:
    pip install -r requirements.txt
    This installs meshcore plus the optional tqdm helper listed in requirements.txt. No external zmodem package is required.

Alternatively, from a checkout: pip install -e . (exposes the akita-zmodem-meshcore console script and python akita_zmodem_meshcore.py --version).

Configuration

  1. Generate Configuration File: The configuration file akita_zmodem_meshcore_config.json will be created with default settings in the same directory as the script if it doesn't exist when you run a command that initializes the application.

    python akita_zmodem_meshcore.py status
  2. Edit Configuration: Modify akita_zmodem_meshcore_config.json to 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: Binary size of each mesh text message, including the 2-byte app-port header, before AZM1/base64 encoding. Valid range is 18 to 129. Larger legacy values are clamped.
    • timeout: Transfer timeout in seconds.
    • tx_delay_ms, min_tx_delay_ms: Packet pacing controls that help prevent radio saturation.
    • max_consecutive_send_failures, max_inbound_queue, max_file_size_bytes: Safety limits for failed sends, receive backlogs, and oversized transfers (enforced on send and receive; default 1 MiB).
    • auto_receive, incoming_dir, allowed_senders: Daemon drop-box behaviour. Empty allowed_senders accepts every peer.
    • mesh_connection_type: How to connect to your MeshCore device (serial or tcp).
    • mesh_serial_port, mesh_serial_baud: Settings for serial connection.
    • mesh_tcp_host, mesh_tcp_port: Settings for TCP connection.
    • control_host, control_port: Local control socket used by status and cancel.

    These MeshCore connection settings can also be overridden via CLI arguments. See docs/CONFIGURATION.md for more details.

Usage

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>
--control-host <HOST_IP_OR_NAME>
--control-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]
  • --directory forces 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 active transfer status from the running daemon/process:

python akita_zmodem_meshcore.py status [transfer_id]

Cancel a transfer in the running daemon/process:

python akita_zmodem_meshcore.py cancel <transfer_id>

See docs/USAGE.md for detailed command explanations and examples.

Example

# 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 - daemon auto-receive):
# Incoming files land in incoming/ using the advertised filename.
python akita_zmodem_meshcore.py

# Alternatively, pre-declare an exact save path and wait for that transfer:
# python akita_zmodem_meshcore.py receive received_files/my_document.txt [--overwrite]

Contributing

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.

License

This project is licensed under the GNU General Public License v3.0. See the LICENSE file for details.

About

This repository contains a Python implementation of a lightweight ZMODEM-like file transfer protocol that is tightly integrated with the MeshCore companion client. The protocol is self-contained. The only runtime dependency besides the optional tqdm progress bar is the MeshCore Python library.

Topics

Resources

Contributing

Stars

27 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages