irbt-cli (0.3.0)
Installation
[registries.forgejo]
index = "sparse+ " # Sparse index
# index = " " # Git
[net]
git-fetch-with-cli = truecargo add irbt-cli@0.3.0 --registry forgejoAbout this package
ir-bt-bridge
Turns any IR remote into a Bluetooth (BLE HID) remote for Google TV, using an
ESP32-S3. Mappings are configured over USB with the irbt CLI. See
SPEC.md for hardware, wiring and design.
Docs: https://selu.pages.selu.dev/ir-bt-bridge/, a user guide plus the
API docs of the irbt and irbt-proto crates.
The repo holds two separate Cargo builds:
| Path | What | Toolchain |
|---|---|---|
/ (proto/, host/) |
wire protocol, irbt library and CLI |
stable Rust |
firmware/ |
ESP32-S3 firmware (ESP-IDF, xtensa-esp32s3-espidf) |
esp (via firmware/rust-toolchain.toml) |
Dev environment setup
- Install Rust with rustup (stable is enough for the host tools).
- Install the ESP tooling and the
esptoolchain:cargo install espup espflash ldproxy espup install # installs the `esp` toolchain and writes ~/export-esp.sh - In every shell where you build firmware:
Make sure. ~/export-esp.shRUSTUP_TOOLCHAINis not set. It overridesrust-toolchain.toml, and the firmware then fails to build with "can't find crate forcore". Usecargo +esp …if you can't unset it.
ESP-IDF itself needs no manual install: the first firmware build downloads it
into firmware/.embuild/. That first build takes several minutes, as does the
first build after changing sdkconfig.defaults.
Updating the ESP tooling
Each part of the tooling updates separately:
- CLI tools (
espup,espflash,ldproxy):cargo installrebuilds only the ones that have a newer version.cargo install espup espflash ldproxy --locked espRust toolchain (Xtensa rustc/LLVM):espup update . ~/export-esp.sh # the script is rewritten; source it again in open shells- ESP-IDF: pinned by
ESP_IDF_VERSIONinfirmware/.cargo/config.toml. Pick a version that youresp-idf-syssupports and change it there, then rebuild from scratch. The next build downloads the new IDF, which takes several minutes.cd firmware cargo clean rm -rf .embuild # optional; otherwise the old IDF stays next to the new one cargo build esp-idf-*crates:firmware/Cargo.tomlpatchesesp-idf-halandesp-idf-svcto their git repos (esp-idf-syslives in theesp-idf-halrepo and comes with it). To pull their latest commits, run this infirmware/:
For a new minor version (e.g.cargo update -p esp-idf-hal -p esp-idf-svcesp-idf-svc0.53), also change the version inCargo.tomland bumpesp32-nimbleto match it (see the comment there).
After updating, build and flash a release build to check that it still works.
Build
Host tools, from the repo root:
cargo build
cargo test --workspace
cargo install --path host/irbt-cli # puts `irbt` on your PATH
Firmware:
cd firmware
cargo build # debug
cargo build --release # release (size-optimized)
Lint and format checks (same as CI, in either directory):
cargo fmt --all -- --check
cargo clippy --all-targets --all-features --workspace -- -D warnings
Flash the device
- Connect the board's USB-C port. If flashing fails to connect, hold BOOT while plugging it in to force download mode.
- Flash:
or flash an existing build without the monitor:cd firmware cargo run --release # builds, then `espflash flash --monitor`espflash flash target/xtensa-esp32s3-espidf/release/ir-bt-bridge - The device's USB output is framed binary, so
espflash monitorshows garbage. Exit it (Ctrl+C) and useirbtinstead:irbt status irbt monitor --logs
Linux: your user needs access to the serial port. Join the dialout
group (sudo usermod -aG dialout $USER, then log in again).
WSL2: attach the board to WSL with
usbipd-win
(usbipd attach --wsl --busid <id>). Re-attach it after every replug or
reset into download mode.
Releases
Prebuilt binaries are on the releases page:
| File | What |
|---|---|
ir-bt-bridge-<tag>-esp32s3.elf |
firmware, for espflash |
ir-bt-bridge-<tag>-esp32s3-merged.bin |
firmware as a single image for offset 0x0 |
irbt-<tag>-{x86_64,aarch64}-linux.tar.gz |
static irbt for Linux |
irbt-<tag>-x86_64-windows.zip |
irbt.exe |
SHA256SUMS |
checksums |
On macOS, build irbt from source (see Build), or install it
from the Cargo registry (below).
Crates
The host crates are published to the Cargo registry on code.selu.dev:
irbt-proto (wire protocol), irbt (host library), irbt-cli (the irbt
binary) and irbt-build-info (a build-script helper). Add the registry to
~/.cargo/config.toml or a project's .cargo/config.toml:
[registries.selu]
index = "sparse+https://code.selu.dev/api/packages/selu/cargo/"
then:
cargo install irbt-cli --registry selu
cargo add irbt --registry selu
To update a device, flash the ELF. This keeps the stored mappings and pairings:
espflash flash ir-bt-bridge-<tag>-esp32s3.elf
The merged image needs only esptool (or a browser-based flasher), but it
erases the mappings and pairings because it overwrites NVS. Use it for
new boards:
esptool.py --chip esp32s3 write_flash 0x0 ir-bt-bridge-<tag>-esp32s3-merged.bin
Cutting a release
.forgejo/workflows/release.yml builds and publishes a release when a v*
tag is pushed. It fails if the tag doesn't match the crate versions. The
workflows need a Forgejo runner; see
docs/forgejo-runner.md to set one up.
- Bump
versioninCargo.toml(in[workspace.package]and in the[workspace.dependencies]entries) andfirmware/Cargo.toml, and runcargo update -win the repo root and infirmware/to update the lockfiles. Commit. - Tag it. The tag message becomes the release notes. A tag with a
-(v0.3.0-rc1) makes a prerelease.git tag -a v0.3.0 git push origin master v0.3.0
The release also publishes the host crates to the Cargo registry. That needs
the repo secret CARGO_REGISTRY_TOKEN: a token with write:package. To
publish by hand instead, run cargo login --registry selu "Bearer <token>"
once, then cargo publish --workspace --registry selu.
The docs workflow builds the docs site on every push. A v* tag also pushes it
to the pages branch, which git-pages serves.
To rebuild the release of an existing tag, run the workflow by hand from the Actions tab and enter the tag.
Use
irbt learn home # press a remote button to bind it to `home`
irbt list # show mappings
irbt send-hid up # send a key over BLE without IR
irbt keys # all key names
Both the firmware and irbt carry the git commit they were built from, as
0.1.0 (c7f30e8 2026-10-27) with -dirty added for uncommitted changes.
irbt status shows the firmware's commit, its rustc, ESP-IDF and crate
versions. irbt -V shows the CLI's commit, and irbt --version adds its
toolchain and dependency versions.
To pair with the TV, go to Settings → Remotes & Accessories → Add accessory → IR Bridge.
Status LED
The onboard RGB LED shows the Bluetooth state:
| LED | Meaning |
|---|---|
| Blue, blinking (2×/s) | No Bluetooth host connected; advertising |
| Green, steady (dim) | Connected and paired to a host |
| Purple flash | A key was sent to the host |
Keys pressed while the LED is blinking blue are dropped (no flash).
License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Dependencies
| ID | Version |
|---|---|
| anyhow | ^1 |
| clap | ^4 |
| ctrlc | ^3 |
| irbt | ^0.3.0 |
| serde | ^1 |
| serde_json | ^1 |
| toml | ^1.1 |
| build-info | ^0.3.0 |