This commit is contained in:
legop3
2026-07-16 22:35:07 -04:00
parent 8a9205b5b7
commit 35561495b4
4 changed files with 94 additions and 13 deletions
+3
View File
@@ -178,6 +178,9 @@ kinect:
captureCooldownMs: 10000
balanceBoard:
# After enabling this feature, rerun install_server.sh. The installer then
# configures BlueZ's Wii-compatible HID mode, persistent hid-wiimote loading,
# and the restricted evdev permission automatically.
enabled: false
# A load must reach this weight before stability timing begins. Keeping the
# threshold above sensor drift prevents an empty board from capturing itself.
+57 -4
View File
@@ -17,6 +17,8 @@ SNAPSHOT_DIR="/var/lib/rover-snapshots"
REPLAY_SEGMENT_DIR="/var/lib/replay-segments"
KINECT_UDEV_RULE="/etc/udev/rules.d/99-kinect-world.rules"
BALANCE_BOARD_UDEV_RULE="/etc/udev/rules.d/99-multirover-balance-board.rules"
BALANCE_BOARD_MODULES_LOAD="/etc/modules-load.d/multirover-balance-board.conf"
BLUEZ_INPUT_CONFIG="/etc/bluetooth/input.conf"
if [[ $EUID -ne 0 ]]; then
echo "This installer must be run with sudo/root." >&2
@@ -139,6 +141,7 @@ dnf install -y \
libfreenect-devel \
libusb1-devel \
bluez \
crudini \
libcap >/dev/null
NODE_BIN="$(command -v node)"
@@ -201,6 +204,52 @@ if [[ ! -f "$CONFIG_PATH" ]]; then
echo "Copied config.example.yaml to config.yaml; edit it before exposing the service."
fi
# Use the same YAML library as the server instead of approximating nested YAML
# with grep. This makes system configuration follow the exact explicit boolean
# feature flag that the running Node service will use.
BALANCE_BOARD_ENABLED=$(SERVER_DIR="$SERVER_DIR" CONFIG_PATH="$CONFIG_PATH" "$NODE_BIN" <<'NODE'
const fs = require('fs');
const path = require('path');
const yaml = require(path.join(process.env.SERVER_DIR, 'node_modules', 'js-yaml'));
const config = yaml.load(fs.readFileSync(process.env.CONFIG_PATH, 'utf8')) || {};
process.stdout.write(config.balanceBoard?.enabled === true ? 'true' : 'false');
NODE
)
if [[ "$BALANCE_BOARD_ENABLED" == "true" ]]; then
echo " Configuring BlueZ for the Wii Balance Board"
if ! modinfo hid-wiimote >/dev/null 2>&1; then
echo "The running kernel does not provide hid-wiimote; install a Fedora kernel with that module before enabling balanceBoard." >&2
exit 1
fi
install -d -m 0755 /etc/bluetooth /etc/modules-load.d
touch "$BLUEZ_INPUT_CONFIG"
chmod 0644 "$BLUEZ_INPUT_CONFIG"
# Modern BlueZ defaults Classic HID devices to userspace UHID and enforces a
# security mode that breaks the Wii family's unusual legacy HID handshake.
# crudini changes only these two keys, preserving every unrelated Bluetooth
# input option an operator may already have configured on the server.
crudini --set "$BLUEZ_INPUT_CONFIG" General UserspaceHID false
crudini --set "$BLUEZ_INPUT_CONFIG" General ClassicBondedOnly false
# The service consumes the calibrated evdev axes created specifically by the
# kernel hid-wiimote driver. Load it now and on every future boot so the brief
# board wake window is never lost waiting for manual module setup.
cat > "$BALANCE_BOARD_MODULES_LOAD" <<'EOF'
# MultiRoombaRover Wii Balance Board support.
hid-wiimote
EOF
chmod 0644 "$BALANCE_BOARD_MODULES_LOAD"
modprobe hid-wiimote
# BlueZ reads input.conf only at daemon startup. Restart it during the
# installer, before multirover is restarted below, so the new HID mode is
# guaranteed to be active without requiring a reboot or another command.
systemctl enable --now bluetooth.service
systemctl restart bluetooth.service
fi
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
@@ -295,8 +344,8 @@ EOF
cat > "$MULTIROVER_SERVICE" <<EOF
[Unit]
Description=Multi-Roomba Rover control server
After=network-online.target mediamtx.service
Wants=network-online.target
After=network-online.target mediamtx.service bluetooth.service
Wants=network-online.target bluetooth.service
[Service]
User=$TARGET_USER
@@ -333,5 +382,9 @@ echo
echo "Update $CONFIG_PATH to set admins, lockdown settings, and media parameters."
echo "Kinect/libfreenect packages and udev permissions were installed."
echo "If a Kinect is already plugged in, unplug/replug its USB/power before testing so the new udev rule applies."
echo "Wii Balance Board Bluetooth support and the restricted input rule were installed."
echo "Enable balanceBoard in config.yaml, then press the red Sync button once to commission it."
if [[ "$BALANCE_BOARD_ENABLED" == "true" ]]; then
echo "Wii Balance Board BlueZ compatibility, kernel driver, bridge, and restricted input rule were installed."
echo "Press the red Sync button once to commission it; later wakes use the front power button."
else
echo "Wii Balance Board bridge and restricted input rule were installed but system Bluetooth compatibility was left unchanged because balanceBoard is disabled."
fi
@@ -7,19 +7,28 @@ Balance Board input device.
## Commissioning on the real server
1. Run `sudo ./install_server.sh` from the `server` directory.
2. Set `balanceBoard.enabled: true` in `server/config.yaml` and restart
`multirover.service`.
3. Open the Activities tab. While it says **Pairing setup**, press the red Sync
button under the board's battery cover.
4. Wait for the card to change to **Sleeping** or **Ready**. BlueZ retains the
bond and the service also stores the selected board address in
1. Set `balanceBoard.enabled: true` in `server/config.yaml`.
2. Run `sudo ./install_server.sh` from the `server` directory. The installer
configures BlueZ's Wii-compatible kernel HID mode, loads `hid-wiimote` now
and at boot, and restarts Bluetooth and the rover server automatically.
3. Open the Activities tab. When it says **Waiting for red Sync**, press the red
Sync button under the board's battery cover.
4. Wait for the card to report a paired bond and then **Ready**. BlueZ retains
the bond and the service also stores the selected board address in
`server/data/balance-board.json`.
5. For ordinary use, press only the front power button. The board should connect
to the server without another Sync operation.
The bridge intentionally uses short Bluetooth Classic discovery windows only
while no board is commissioned. It stops scanning after a successful bond.
The bridge keeps Bluetooth Classic discovery active while no board is
commissioned and stops scanning after a successful bond. Once commissioned, it
actively retries the saved address while disconnected so a front-button wake is
caught even when the adapter does not accept the board's incoming reconnect.
Wii-family HID compatibility requires `UserspaceHID=false` and
`ClassicBondedOnly=false` in BlueZ's `input.conf`. The installer applies only
those two keys with an INI-aware tool and preserves unrelated Bluetooth input
settings. The latter relaxes BlueZ's global Classic HID bonding restriction;
this is limited to servers where Balance Board support is explicitly enabled.
## Physical station
@@ -418,7 +418,23 @@ std::string command_error_summary(const std::string& raw, const std::string& fal
std::string summary;
summary.reserve(std::min<std::size_t>(raw.size(), 400));
bool previous_was_space = false;
int ansi_state = 0;
for (unsigned char ch : raw) {
// bluetoothctl emits terminal color CSI sequences even when its output is
// captured by a pipe. Drop the entire ESC ... final-byte sequence so the UI
// never exposes fragments such as `[[0;93mCHG[0m]` as hardware diagnostics.
if (ch == 0x1b) {
ansi_state = 1;
continue;
}
if (ansi_state == 1) {
ansi_state = ch == '[' ? 2 : 0;
continue;
}
if (ansi_state == 2) {
if (ch >= 0x40 && ch <= 0x7e) ansi_state = 0;
continue;
}
const bool is_space = ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
if (is_space) {
if (!summary.empty() && !previous_was_space) summary.push_back(' ');