蓝牙功放连接与音量异常排查记录

分享

环境

笔记本运行 Ubuntu,桌面为 KDE Plasma Wayland,蓝牙栈为 BlueZ 加 PipeWire 1.6.2 加 WirePlumber,音频设备为一台 TVCTO 蓝牙功放,通过 A2DP 播放。

现象

三个问题同时存在。

第一,功放与电脑连接不稳定,表现为 Connection refused 111Connection timed out 110 交替出现,重试若干次后可以连上。

第二,蓝牙日志中持续出现 Hands-Free Voice gateway 相关报错,A2DP 媒体流频繁断开重连。

第三,连接恢复后,系统音量调节异常。音量键只在 14 到 16 的小区间内变化,托盘滑条拖动后显示跳回 15,但实际音量已经改变。

排查过程

连接拒绝

排查入口是 BlueZ 守护进程日志:

journalctl -u bluetooth -f

拒连时段的关键日志:

9月 13 23:33:55 bluetoothd[6105]: src/profile.c:ext_connect() Hands-Free Voice gateway failed connect to 41:42:46:3C:D4:31: Connection refused (111)
9月 13 23:33:55 bluetoothd[6105]: profiles/audio/avdtp.c:avdtp_connect_cb() connect to 41:42:46:3C:D4:31: Connection refused (111)
9月 13 23:37:55 bluetoothd[6105]: src/profile.c:record_cb() Unable to get Hands-Free Voice gateway SDP record: Operation already in progress
9月 13 23:38:20 bluetoothd[6105]: profiles/audio/avdtp.c:avdtp_connect_cb() connect to 41:42:46:3C:D4:31: Connection timed out (110)
9月 13 23:38:47 bluetoothd[6105]: src/profile.c:record_cb() Unable to get Hands-Free Voice gateway SDP record: Connection timed out
9月 13 23:39:52 bluetoothd[6105]: /org/bluez/hci1/dev_41_42_46_3C_D4_31/sep1/fd0: fd(40) ready

Connection refused 是 L2CAP 层主动拒绝,说明协议栈与设备均存活,是设备策略层面的拒连。紧随其后的 SDP 查询超时是同一原因派生的次生现象。设备端并未与其他主机建立真实连接,但固件维护的链路状态机认为旧连接仍然存活,形成僵尸连接,新连接进入时主动拒绝。

这类行为无法从主机侧根治。有效触发手段有两个。其一,按下功放配对键使其重新进入配对模式,固件会清理链路状态。其二,确保旧主机侧先执行显式断开:

bluetoothctl disconnect 41:42:46:3C:D4:31

断电重启是保底手段,原理相同但成本更高。日志中 23:38 到 23:39:52 约 90 秒后才自动恢复,说明该设备链路释放周期长,不宜依赖其自愈。

HFP 协商异常

日志中 ext_connect Hands-Free Voice gateway failed 表明 BlueZ 作为 Audio Gateway 角色持续尝试与设备建立 HFP 链路。功放放音乐只依赖 A2DP,HFP 与 HSP 属于多余协商面,协商失败会直接干扰 A2DP 会话稳定性。

BlueZ 不提供环境变量方式关闭 profile,正确做法是通过 --noplugin 禁用对应插件。修改 systemd 覆盖文件:

sudo mkdir -p /etc/systemd/system/bluetooth.service.d
sudo tee /etc/systemd/system/bluetooth.service.d/override.conf <<'EOF'
[Service]
ExecStart=
ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=hfp_ag,hsp_ag
EOF
sudo systemctl daemon-reload
sudo systemctl restart bluetooth

注意三点。其一,ExecStart= 留空行用于清除原有启动命令,不可省略。其二,bluetoothd 路径以 systemctl cat bluetooth 实际输出为准,不要凭发行版惯性推断。旧版本 Ubuntu 与 Debian 位于 /usr/lib/bluetooth/bluetoothd,新版本 Ubuntu 的 bluez 打包已迁移到 /usr/libexec/bluetooth/bluetoothd。其三,验证覆盖是否挂载:

systemctl cat bluetooth

输出末尾应同时出现空 ExecStart= 与带参数的新 ExecStart。进一步验证 HFP 已禁用:

sudo sdptool records 41:42:46:3C:D4:31 | grep -i handsfree

无输出即生效。

音量显示异常

蓝牙音量控制走 AVRCP 协议的控制信道,与传输音频的 A2DP 媒体信道相互独立。绝对音量模式下,电脑将目标音量通过 SetAbsoluteVolume 命令下发设备,设备执行后通过 Changed 通知上报当前值。协议规定上报范围为 0 到 127,但该功放固件将其映射为 16 级粗刻度,并且上报值与实际执行值不一致,导致 GUI 显示锁死在 15,按键步进被设备刻度钳制。

处理方式为禁用绝对音量,让音量回归 PipeWire 内部的软件控制。先确认设备 sink 状态:

wpctl status
pactl list sinks short

修复前 TVCTO sink 的音量显示为 16 级刻度:

Sinks:
     272. TVCTO                               [vol: 0.16]

新增 WirePlumber 规则:

sudo tee /etc/wireplumber/wireplumber.conf.d/51-bluez-no-absvol.conf <<'EOF'
monitor.bluez.rules = [
  {
    matches = [
      { device.name = "~bluez_card.*" }
    ]
    actions = {
      update-props = {
        bluez5.absolute-volume = false
      }
    }
  }
]
EOF

重启 WirePlumber 并断开重连设备:

systemctl --user restart wireplumber
bluetoothctl disconnect 41:42:46:3C:D4:31
sleep 3
bluetoothctl connect 41:42:46:3C:D4:31

断开后立即回连可能返回:

Failed to connect: org.bluez.Error.Failed br-connection-unknown

原因是控制器与设备均在清理旧链路状态,间隔数秒后重试即可。

验证标准为 wpctl status 中设备 sink 音量显示为浮点值:

Sinks:
     135. TVCTO                               [vol: 0.32]

按键与托盘滑条在 0 到 100 区间连续可调,显示值与实际音量一致。音量事件可用 pactl subscribe 实时监听。

结论

三个问题的根源均在设备固件侧。主机侧的应对策略一致,即识别有缺陷的协议环节并逐个绕开。禁 HFP 砍掉多余协商面,关绝对音量绕开错误上报,连接异常则通过配对键触发设备自清。

配置变更存档三处:bluetooth.service 覆盖文件,WirePlumber 规则文件,以及操作习惯变更,即连接前先确认旧链路已释放。

工具备忘

工具 用途
journalctl -u bluetooth -f BlueZ 守护进程日志
journalctl --user -f -u pipewire -u wireplumber 音频栈日志
sudo btmon HCI 层原始报文,断连归因看 reason code
bluetoothctl 设备连接管理
wpctl status sink 与音量状态
pactl list sinks short sink 列表与运行状态
pactl subscribe 音量事件实时监听
sdptool records <MAC> 设备 SDP 记录检查

阅读更多

躺姿笔记本看视频的硬件与系统调优

问题背景 床头挡板与墙之间存在约三指宽的缝隙,笔记本竖直卡入其中,依靠开合角支撑。躺于床尾观看视频时,屏幕法线朝向下前方,与躺姿视线方向不一致。开合角已达机械上限,微调垫片只能改变数度,属于机构层面的约束,无法通过摆放优化解决。 一、视角问题的方案评估 方案:整机倒挂配合屏幕旋转。若开合角支持一百八十度,将键盘朝上放置,屏幕反向展开,屏幕平面朝向天花板,与躺姿视线一致。系统内将内屏旋转一百八十度即可正常显示。零硬件成本,前置条件为开合角达到一百八十度,且需接受键盘与触控板镜像。 二、KWin Wayland 下的屏幕旋转 图形界面路径为系统设置,显示与监视器,选中内屏,方向选翻转。配置由 KScreen 持久化。 命令行使用 kscreen-doctor: kscreen-doctor -o kscreen-doctor output.eDP-1.rotation.inverted kscreen-doctor

By bakamake

ros 工具怪癖

rqt 启动之后要关闭D符号debug,才能看到数据 2d pose estimate not working head frame 需要是map 才能接收2d pose estimate ,但是这部分是amcl处理数据来收敛的, ros2 wtf ,刚刚发现这个东西还挺好用的 要看到蓝→红渐变,必须订阅带 inflation layer 的 costmap topic,例如: * /local_costmap/costmap * /global_costmap/costmap /map 节点切换到 costmap 也只能看到洋红色的障碍物,因为/map里100是障碍物,以及0和-1,costmap接受的范围是0~255

By bakamake

iwd 提高 Wi-Fi 断连容错能力

iwd(iNet wireless daemon)负责 Linux 系统中的 Wi-Fi 连接管理。对于偶发断连、连接恢复较慢或漫游异常等问题,可以通过调整扫描、漫游和网络配置相关选项提高连接的容错能力。 配置 配置文件: /etc/iwd/main.conf 示例配置: [General] EnableNetworkConfiguration=true [Scan] DisablePeriodicScan=false [Network] EnableRoaming=true 修改配置后重启 iwd: sudo systemctl restart iwd 日志排查 发生断连时,可以实时查看 iwd 日志: journalctl -u iwd -f 重点关注以下错误: 4-Way Handshake failed

By bakamake