chessnut-move-rs
A typed, transport-independent Rust SDK for Chessnut Move boards.
chessnut-move turns the board's byte protocol into Rust values such as
Square, Position, Command, and BoardEvent. Applications can use the
included btleplug and Tokio integrations, provide another Bluetooth backend,
or use the protocol in no_std environments without Bluetooth support in the
crate at all.
This is an independent community project and is not affiliated with or endorsed by Chessnut.
[!NOTE] This library is specifically made for the Chessnut Move boards.
I'm willing to adapt the library for other Chessnut boards, but I would need someone else with the boards available for testing purposes.
Highlights
- Typed commands for LEDs, automatic movement, battery status, tracked-piece status, and real-time position updates.
- Chess-specific values for squares, pieces, positions, and LED patterns.
- A runtime-neutral async session that is available with the default features.
- An allocation-free blocking session for embedded and synchronous programs.
- An optional Tokio actor with cloneable handles, request helpers, lifecycle reporting, and event streams.
- An optional
btleplugadapter for using common desktop Bluetooth backends. - Public transport traits and GATT metadata for integrating other Bluetooth libraries.
- Optional structured diagnostics through
tracing. - Protocol and core transport support without
stdoralloc.
Table of Contents
Installation
The most complete application setup uses btleplug for Bluetooth I/O, Tokio
for concurrent access, and tracing for diagnostics:
Or add it directly to Cargo.toml:
[]
= "0.12"
= {
version = "1",
= ["btleplug", "tokio", "tracing"],
}
= { = "1", = ["macros", "rt"] }
= { = "0.3", = ["env-filter"] }
The default features are std and async. They provide the runtime-neutral
async API but intentionally do not select a Bluetooth implementation or async
runtime:
[]
= "1"
Quick start
The Tokio actor is the convenient path when multiple parts of an application need to send commands or observe the board.
Given a connected btleplug peripheral:
use Error;
use Peripheral;
use BoardEvent;
use BtleplugTransport;
use ;
type AnyError = ;
async
async
Discovery, connection timeouts, reconnection, and peripheral selection remain application policy.
See examples/basic.rs for a complete program that
scans for the board, connects, queries its status, waits for a position
update, shuts down the actor, and disconnects.
API documentation for all public types is available on docs.rs.
Architecture
The crate separates the Chessnut Move protocol from Bluetooth and runtime choices:
| Layer | Purpose |
|---|---|
protocol |
Commands, decoded events, positions, squares, pieces, LED patterns, and protocol errors |
transport |
Notification decoding, runtime-neutral sessions, and traits for Bluetooth adapters |
transport::btleplug |
Optional adapter from a connected btleplug peripheral to the crate's transport traits |
transport::tokio |
Optional actor task, cloneable handles, request helpers, lifecycle state, and event streams |
This separation allows protocol tests and embedded integrations to avoid specific Bluetooth dependencies. It also keeps scan duration, device selection, pairing, reconnection, and application shutdown behavior in the application where those policies can be chosen deliberately.
Runtime-neutral async session
The default Board alias owns any AsyncTransport implementation and performs
the board-session initialization sequence:
use ;
use ;
async
Use this API when one task owns the board or when the executor does not require
Send futures. Use the Tokio actor when concurrent callers need cloneable
handles and independently subscribed event streams.
Common operations
Commands are transport-independent values. They can be created before a board is connected and sent through async, blocking, or Tokio-backed sessions.
Light board squares
use ;
let mut leds = default;
leds.set_color;
leds.set_color;
let command = set_leds;
Request board information
The Tokio actor correlates status responses with these request helpers:
use ;
let battery = board.battery_status.await?;
let pieces = board.piece_status.await?;
println!;
for tracked in pieces.pieces
Runtime-neutral sessions can send Command::read_battery_level() or
Command::read_piece_status() and consume the corresponding BoardEvent.
Auto move
Command::auto_move accepts the complete target Position, not only a source
and destination square:
use ;
let stop = stop_auto_move;
In a real application, derive the target from the board's latest reported
position. Always send Command::stop_auto_move() during cancellation or
shutdown.
Feature flags
| Feature | Default | Enables |
|---|---|---|
std |
Yes | Standard-library error integration |
async |
Yes | AsyncTransport, AsyncBoard, and the Board alias |
blocking |
No | Allocation-free BlockingTransport and BlockingBoard |
btleplug |
No | BtleplugTransport; also enables std and async |
tokio |
No | Actor task, cloneable handles, request helpers, lifecycle state, and event streams; also enables std, alloc, and async |
tracing |
No | Structured spans and events; also enables alloc |
alloc |
No | Allocation-dependent integrations without otherwise requiring std |
Custom transports
Bluetooth libraries integrate through one of three public traits:
- Implement
AsyncTransportfor runtime-neutral async programs. - Implement
BlockingTransportfor synchronous or embedded programs. - Implement
TokioTransportwhen the transport futures areSendand will run inside the Tokio actor.
Each trait exposes the same essential operations:
- Subscribe to a
NotificationSource. - Write a typed
Command. - Copy the next notification into the supplied buffer and return a borrowed
Notification.
unsubscribe and close hooks have no-op defaults for transports that do not
need explicit cleanup. The board sessions own fixed-size notification buffers,
so a transport does not need to allocate or expose its Bluetooth library's
channel and notification-session types.
The UUIDs and characteristic mapping are public in transport::gatt.
Implementations use Command::bytes() and Command::write_kind() and do not
need access to the actual (private) wire-format types.
no_std and blocking use
Disable the default features to use protocol types and notification decoding
without std, allocation, async, or a Bluetooth dependency:
[]
= {
version = "1",
= false,
}
Add the allocation-free blocking session when required:
[]
= {
version = "1",
= false,
= ["blocking"],
}
Then implement BlockingTransport and use BlockingBoard. Enabling tracing
in a no_std application also enables alloc; the crate still does not
install or select a tracing subscriber.
Tracing
Enable the tracing feature to receive structured diagnostics for protocol
decoding, transport I/O, lifecycle transitions, actor queries, timeouts, and
recoverable failures.
The library never installs a global subscriber. Applications decide how events are formatted, filtered, and collected:
fmt
.with_env_filter
.init;
Add tracing-subscriber to the application when using this setup:
[]
= { = "0.3", = ["env-filter"] }
Use chessnut_move=trace to include routine command and notification traffic.
Raw command and notification payloads are not recorded.
Examples
You can find example implementations under the /examples directory.
Additional notes
- Wake the board before scanning and keep it near the Bluetooth adapter.
- Disconnect the official Chessnut application and other nearby clients before connecting; the board may only accept one active connection.
- Bluetooth discovery and connection behavior varies by operating system and
adapter. The
btleplugadapter inherits the platform behavior ofbtleplug. - Ensure cancellation and shutdown paths send
Command::stop_auto_move().
The protocol implementation is based on Chessnut's published Move API, and some manual testing.
Support
Use the repository's issue tracker for reproducible bugs and feature request.
Contributing
Contributions are welcome. Read
CONTRIBUTING.md
for the development commands, formatting requirements, licensing process, and
pull request conventions. Contributors using AI-assisted tools must also
follow
AI_POLICY.md.
The usual local checks are:
License
Licensed under the Apache License, Version 2.0. See
LICENSE for
details.