walkingpad is an asynchronous Rust library and stateless command-line tool
for KingSmith WalkingPad treadmills. It connects, performs one operation, and
disconnects; no background daemon or persistent interactive shell is needed.
The protocol was tested with a WalkingPad A1. Other models that expose the same FE00/FE01/FE02 GATT service and characteristics may work, but should be tested carefully at low speed.
cargo install walkingpadBluetooth access must be allowed for your terminal application. The original phone app must be disconnected because the belt accepts only one client at a time.
Discover once and save the platform-specific device identifier:
walkingpad scanSubsequent commands reopen that known peripheral without scanning, connect, perform the operation, and disconnect:
walkingpad start
walkingpad speed 2.0
walkingpad status
walkingpad status --json
walkingpad mode automatic
walkingpad stopPlain walkingpad stop requests zero speed and leaves the belt ready for a
later command. walkingpad stop --standby also powers down into standby;
depending on the model, physical wake-up may then be required before another
Bluetooth command can connect.
If the saved identifier is no longer known to the operating system, a command
automatically scans for the proprietary WalkingPad service and refreshes the
saved selection. Use walkingpad device to inspect it or walkingpad forget
to remove it.
The saved identifier lives in the platform-native per-user application data directory. Only one CLI process can control the belt at a time; a process lock prevents overlapping commands.
The library keeps Bluetooth transport and packet parsing behind a small async API suitable for adding controls to a desktop or mobile application:
use std::time::Duration;
use walkingpad::WalkingPad;
# async fn run(id: walkingpad::DeviceId) -> walkingpad::Result<()> {
let timeout = Duration::from_secs(5);
let mut pad = WalkingPad::open(&id, timeout).await?;
pad.start().await?;
let status = pad.status(timeout).await?;
println!("{} steps", status.steps);
pad.disconnect().await?;
# Ok(())
# }For first-run setup, call WalkingPad::discover, persist
DiscoveredPad::id, and call DiscoveredPad::connect. DeviceStore provides
the same atomic JSON persistence and command lock used by the CLI, but apps
may manage identifiers themselves.
The protocol module exposes packet UUIDs and parsers for integrations that
need lower-level access.
Every connection enables the notification characteristic once and keeps it
active until disconnect. This lets long-lived applications receive and inspect
notifications emitted outside an explicit status request.
The connection retains the newest 128 transmitted commands and received notifications in memory. The history is bounded, starts empty for every new connection, and can be copied into an application's incident record before it reconnects:
use std::time::Duration;
use walkingpad::WalkingPad;
async fn poll(pad: &mut WalkingPad) {
if let Err(error) = pad.status(Duration::from_secs(2)).await {
eprintln!("status failed: {error}");
for packet in pad.packet_history() {
eprintln!("{:?} {}", packet.direction, packet.hex());
}
}
}With a tracing subscriber installed, malformed or unrecognized notifications
are warnings containing their complete raw hexadecimal packet. Request and
disconnect failures include the retained packet history. Valid status packets
produce an info event for the initial operational state and whenever the belt
state, mode, current speed, target speed, controller button, or either currently
unclassified status byte changes; counter-only updates remain quiet.
WalkingPad::disconnect waits at most three seconds for the Bluetooth stack.
Use disconnect_with_timeout to choose a different limit. Dropping a connection
also stops its notification task, but applications should still disconnect
explicitly when possible.
Applications that manage device identifiers themselves can avoid compiling the CLI and persistence dependencies:
[dependencies]
walkingpad = { version = "0.3", default-features = false }bluest::Adapter::open_device is the fast path. On macOS, it maps to
CoreBluetooth's retrievePeripherals(withIdentifiers:), so a saved device can
be reopened without waiting for an advertisement scan. Linux and Windows use
their corresponding persistent platform identifiers through the same API.
The belt must still be powered on and reachable for the new process to create a BLE connection. Stateless commands trade a short connection setup delay for simple automation and no continuously running process.
This software controls moving equipment. The start command lets the belt choose
its retained or configured starting target, which may be faster than 0.5
km/h. Keep the belt clear, remain ready to step off, and keep the hardware remote
or emergency stop within reach. A CLI command cannot stop a belt if Bluetooth is
unavailable.
The MSRV is Rust 1.85.
cargo fmt --all -- --check
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo test --locked --all-targets --all-features
cargo package --lockedMIT. The packet protocol was derived from the MIT-licensed
ph4r05/ph4-walkingpad; see
THIRD_PARTY_NOTICES.md.