marlin-binary-transfer 0.1.2

Host-side implementation of Marlin's Binary File Transfer Mark II protocol for SD-card upload to 3D printers.
Documentation
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.2] - 2026-05-13

### Added

- `adapters::{blocking,tokio}::UploadOptions.progress` — optional
  `Box<dyn FnMut(Progress) + Send>` callback fired once after each
  acknowledged WRITE. The `Progress` payload exposes cumulative
  `bytes_sent`, `chunks_sent` and `source_bytes` so callers can drive
  their own UI / rate calculations.

### Changed

- `UploadOptions` no longer derives `Clone` (the new `progress` field
  holds a `FnMut` closure). `Debug` is preserved via a manual impl, so
  the existing `..UploadOptions::default()` patterns keep working.

### Yanked

- 0.1.1 was published with experimental `Progress::percent()` /
  `fraction()` helpers and immediately yanked. 0.1.2 ships the same
  progress callback without those helpers.

## [0.1.0] - 2026-05-11

Initial public release.

### Fixed

- `file_transfer`: state-gate `pending_ascii` setters for every `PFT:*`
  variant. Previously a stray `PFT:busy`/`fail`/`ioerror`/`invalid`/`version`
  line arriving while `AwaitingWriteAck` would corrupt that slot and cause
  the legitimate `ok<n>` to be silently swallowed; the next `write()` would
  then panic with state still `AwaitingWriteAck`. Now out-of-context PFT
  lines are cleanly ignored.
- `adapters::tokio`: every `transport.read(..).await` is now wrapped in
  `tokio::time::timeout(session.response_timeout(), ..)`. Without this,
  a quiet transport meant the loop never reached `session.tick()`, so the
  retransmit and total-budget timeouts were effectively dead code.
- `adapters` (both blocking and tokio): `drive_session_until_synced` now
  surfaces `FileError::SessionFatalError` / `SessionTimeout` /
  `SessionOutOfSync` instead of always returning the generic
  `UploadError::HandshakeFailed` after exhausting the retry budget.
- `adapters` (both blocking and tokio): send the control-plane CLOSE
  (proto=0, type=2) after a successful upload so the device exits binary
  mode and resumes accepting ASCII g-code on the same serial session.
  Previously the device remained in binary mode after `upload()` returned
  `Ok`.

### Added

- `Session::response_timeout()` / `Session::total_timeout()` and
  `FileTransfer::response_timeout()` accessors so adapters and external
  callers can bound their own I/O against the session's timing
  configuration.
- Tokio's `time` feature is now enabled by the `tokio` feature flag.
- New integration test `tests/blocking_adapter_e2e.rs` driving the
  blocking adapter against an in-memory `Read+Write` transport,
  asserting the control CLOSE lands on the device.
- Initial repository scaffold: dual MIT/Apache-2.0 license, README, CI
  scaffolding, module placeholders.
- Codec layer (`codec`) — packet encode/decode, Fletcher-16 mod-255,
  full error variants, validated against the Python reference fixtures.
- Sans-I/O session state machine (`session`) — sync handshake, ack
  matching, retransmit on response timeout, total-budget timeout,
  resend-request and fatal-error pass-through.
- File-transfer state machine (`file_transfer`) — QUERY / OPEN / WRITE
  / CLOSE / ABORT, with compression negotiation (none, heatshrink, auto).
- Optional heatshrink wrapper (`compression`, behind `heatshrink`
  feature) using `embedded-heatshrink`.
- Adapters (`adapters::blocking`, `adapters::tokio`,
  `adapters::serialport`) wrapping the sans-I/O core for the common
  transport choices.
- Examples: `examples/upload.rs` (CLI mirror of Python's `transfer.py`)
  and `examples/inspect.rs` (decoder for captured byte streams).
- Criterion benches for codec encode/decode and Fletcher-16.
- `cargo-fuzz` target for the decoder.

[Unreleased]: https://github.com/J040M/marlin-binary-transfer/compare/v0.1.2...HEAD
[0.1.2]: https://github.com/J040M/marlin-binary-transfer/releases/tag/v0.1.2
[0.1.0]: https://github.com/J040M/marlin-binary-transfer/releases/tag/v0.1.0

## Acknowledgements

The wire format and reference behaviour are taken from Marlin's
[Binary File Transfer Mark II PR (#14817)](https://github.com/MarlinFirmware/Marlin/pull/14817)
and the Python reference implementation
[`marlin-binary-protocol`](https://github.com/trippwill/marlin-binary-protocol)
(MIT) by Chris Pepper (`@p3p`). This is an independent Rust port of the
documented protocol.