Chapter Audio Configuration
kita tutup dengan kasus klasik: headset yang sudah pair tetapi menolak tersambung. Bluetooth di Linux memang punya reputasi itu, padahal tumpukannya cukup rapi: semua perangkat Bluetooth ditangani satu stack bernama BlueZ, dan antarmuka paling andalnya adalah bluetoothctl di terminal. Chapter ini membedah alur klasik power → scan → pair → trust → connect, membedakan tiga status yang sering dikira sama, lalu menyiapkan kita menghadapi masalah pairing yang paling sering muncul.
BlueZ dan bluetoothctl #
BlueZ adalah stack Bluetooth resmi Linux. Inti berjalannya adalah daemon bluetoothd, yang mengelola adapter (pengontrol radio, biasa muncul sebagai hci0) dan mengekspos semua perangkat lewat API D-Bus. Baik panel Bluetooth di GNOME/KDE maupun bluetoothctl sebenarnya hanyalah klien yang berbicara ke daemon yang sama — jadi ilmunya tidak hangus ketika kita pindah desktop environment.
bluetoothctl adalah klien CLI tersebut, dan layak dikuasai karena tiga alasan: berjalan di atas SSH untuk mesin headless, perilakunya sama di semua distro, dan pesan kesalahannya jujur. Perintahnya bisa dijalankan satu-satu (bluetoothctl power on) maupun dari dalam sesi interaktif — rincian seluruh perintahnya ada di bluetoothctl(1)
dan ikhtisar ekosistemnya di wiki Bluetooth
.
Alur Klasik: power, agent, scan, pair, trust, connect #
Menghubungkan perangkat baru selalu mengikuti urutan yang sama. Berikut contoh tampilan sesi bluetoothctl interaktif untuk sebuah headset — baris [bluetooth]# adalah prompt yang menandakan kita berada di dalam sesi:
$ bluetoothctl
Agent registered
[bluetooth]# power on
Changing power on succeeded
[bluetooth]# agent on
Agent is already registered
[bluetooth]# default-agent
Default agent request successful
[bluetooth]# scan on
Discovery started
[NEW] Device 00:1A:7D:DA:71:13 Logitech G435
[bluetooth]# pair 00:1A:7D:DA:71:13
Attempting to pair with 00:1A:7D:DA:71:13
[CHG] Device 00:1A:7D:DA:71:13 Paired: yes
Pairing successful
[bluetooth]# trust 00:1A:7D:DA:71:13
[CHG] Device 00:1A:7D:DA:71:13 Trusted: yes
[bluetooth]# connect 00:1A:7D:DA:71:13
Attempting to connect to 00:1A:7D:DA:71:13
[CHG] Device 00:1A:7D:DA:71:13 Connected: yes
Connection successful
[bluetooth]# exit
Tiap tahap punya tugasnya sendiri. power on menyalakan radio adapter; tanpa langkah ini, scan pun tidak akan menemukan apa pun. agent on dan default-agent menyiapkan pairing agent — pihak yang menjawab konfirmasi angka atau PIN saat pairing, tampil sebagai dialog konfirmasi di layar. scan on memulai pencarian dan mencetak tiap perangkat yang ditemukan lewat baris [NEW]; setelah cukup, scan off menghentikannya. Tiga perintah terakhir — pair, trust, connect — sering dianggap satu paket padahal statusnya berbeda.
Paired, Trusted, Connected: Tiga Status Berbeda #
Kebingungan terbesar pemakai Bluetooth Linux datang dari tiga status yang saling bertumpuk:
| Status | Yang tersimpan | Efek praktis |
|---|---|---|
| Paired | link key di kedua pihak | boleh connect kapan pun tanpa proses pairing ulang |
| Trusted | penanda pada daemon | koneksi dari perangkat itu diterima otomatis, tanpa konfirmasi |
| Connected | tautan radio yang sedang aktif | perangkat benar-benar siap dipakai |
Pairing adalah pertukaran kunci: kedua perangkat menyimpan link key sehingga percakapan berikutnya tidak perlu kenalan ulang. Trust adalah keputusan kebijakan di sisi komputer: perangkat tepercaya boleh membangunkan tautan tanpa kita konfirmasi satu per satu — inilah yang membuat headset tersambung sendiri begitu dinyalakan. Connect baru menegakkan tautan radio sungguhan. Headset yang sudah pair tetapi tidak kunjung connect adalah masalah yang sama sekali berbeda dari headset yang belum dikenal.
Urutan dan syarat antar status itu bisa kita amati tanpa perangkat sungguhan. Skrip simulasi-pairing.sh di bundle chapter ini meniru state machine bluetoothctl dengan menyimpan status tiap perangkat ke berkas kecil — kolomnya Name, Paired, Trusted, Connected:
export BT_STATE="$PWD/state-bt"
rm -rf "$BT_STATE"
bash simulasi-pairing.sh connect 00:1A:7D:DA:71:13
bash simulasi-pairing.sh pair 00:1A:7D:DA:71:13
bash simulasi-pairing.sh trust 00:1A:7D:DA:71:13
bash simulasi-pairing.sh connect 00:1A:7D:DA:71:13
bash simulasi-pairing.sh status 00:1A:7D:DA:71:13Output yang Dihasilkan:
Failed to connect: device 00:1A:7D:DA:71:13 belum di-pair
[CHG] Device 00:1A:7D:DA:71:13 Paired: yes
Pairing successful
[CHG] Device 00:1A:7D:DA:71:13 Trusted: yes
[CHG] Device 00:1A:7D:DA:71:13 Connected: yes
Connection successful
Device 00:1A:7D:DA:71:13 (Logitech G435)
Name: Logitech G435
Paired: yes
Trusted: yes
Connected: yesBaris pertama gagal justru disengaja: connect sebelum pair ditolak karena perangkatnya belum dikenal. Setelah pair, status berubah dan connect pun lolos. Bagian penting skripnya:
- Baris
state=${BT_STATE:-$(mktemp -d)}: direktori penyimpanan status diambil dari variabel lingkunganBT_STATE, atau direktori sementara segar bila tidak diatur — sehingga skrip aman dijalankan dari folder mana pun. - Fungsi
nama_perangkat(): peta MAC ke nama ramah pembaca, pengganti basis data nama asli BlueZ. - Fungsi
set_flag(): mengubah satu baris status (Paired,Trusted,Connected) lewatsed -ipada berkas perangkat — state machine sungguhan menyimpan kunci kriptografi, simulasi ini cukup menyimpan bendera. - Blok
connect): memeriksa berkas perangkat lebih dulu; perangkat tak dikenal ditolak dengan pesan alabluetoothctl, perangkat yang sudah tersambung juga ditolak. - Blok
demo): dipakai saat skrip berjalan tanpa argumen — menjalankan urutan pair-trust-connect di direktori sementara, lalu keluar dengan status sukses.
Kebijakan Daemon di main.conf #
Perilaku bluetoothd diatur oleh /etc/bluetooth/main.conf — berkas INI bersisi kebijakan global. Salah satu keluhan paling klasik, “adapter selalu mati setelah reboot”, diselesaikan lewat berkas ini. Kita bedah bentuknya lewat salinan latihan:
mkdir -p bluetooth
cat > bluetooth/main.conf <<'EOF'
[General]
ControllerMode = dual
FastConnectable = true
[Policy]
AutoEnable = true
ReconnectAttempts = 7
ReconnectIntervals = 1,2,4,8,16
EOF
cat bluetooth/main.confOutput yang Dihasilkan:
[General]
ControllerMode = dual
FastConnectable = true
[Policy]
AutoEnable = true
ReconnectAttempts = 7
ReconnectIntervals = 1,2,4,8,16Mari kita bedah tiap pasangan kunci-nilainya:
- Bagian
[General]: pengaturan perilaku adapter secara umum. ControllerMode = dual: adapter bekerja dua mode sekaligus, BR/EDR klasik (headset, keyboard) dan LE modern (sensor) — modebredrataulemembatasi salah satunya.FastConnectable = true: adapter memancar lebih sering agar siap disambung, dengan konsekuensi daya — berguna di desktop, boros di laptop.- Bagian
[Policy]: kebijakan otomatis daemon, bukan kemampuan adapter. AutoEnable = true: adapter dinyalakan begitu daemon mulai — inilah obat klasik untuk Bluetooth yang mati sendiri setiap reboot; setelah mengubah berkas ini, muat ulang dengansudo systemctl restart bluetooth.ReconnectAttemptsdanReconnectIntervals: daemon mencoba menyambung ulang perangkat tepercaya yang terputus, sampai tujuh kali dengan jeda yang membesar mengikuti daftar interval — menjelaskan mengapa headset kadang tersambung sendiri beberapa detik setelah dinyalakan.
Radio yang Terkunci: rfkill #
“Bluetooth saya hilang” sering kali bukan hilang, melainkan diblokir. Kernel menyimpan status radio lewat rfkill: soft block dipasang perangkat lunak (mode pesawat, tombol fn), sedangkan hard block dipasang saklar fisik. Berikut contoh tampilan rfkill list pada laptop yang radionya diblokir mode pesawat:
0: hci0: Bluetooth
Soft blocked: yes
Hard blocked: no
1: phy0: Wireless LAN
Soft blocked: no
Hard blocked: noAdapter hci0 dalam keadaan soft blocked tidak akan muncul sebagai perangkat yang bisa dipakai walau daemon sehat. Membukanya satu perintah: rfkill unblock bluetooth (halaman rinciannya di rfkill(8)
). Bila barisnya menulis Hard blocked: yes, tidak ada perintah yang membantu — cari saklar atau tombol fisiknya dulu.
Data Pairing di /var/lib/bluetooth #
bluetoothd menyimpan identitas adapter dan kunci tiap perangkat di /var/lib/bluetooth/: satu direktori per adapter (dinamai MAC adapter), berisi subdirektori per perangkat yang di dalamnya ada berkas info dengan link key hasil pairing. Struktur ini penting dipahami karena dua hal. Pertama, kunci yang basi di kedua sisi membuat pairing berulang kali gagal — obatnya bluetoothctl remove 00:1A:7D:DA:71:13 yang menghapus subdirektori tersebut, lalu pair ulang dari nol. Kedua, dua komputer tidak boleh memakai perangkat dengan kunci yang sama-sama tersimpan basi; hapus di satu sisi agar perangkat tidak bingung.
Masalah Umum dan Solusinya #
Sisanya adalah pola yang berulang. Empat keluhan paling sering beserta jalur keluarnya:
| Gejala | Penyebab khas | Solusi |
|---|---|---|
| Pairing berhasil, tidak mau connect | perangkat tidur atau profil audio belum siap | nyalakan perangkat, connect ulang, cek profil A2DP/HFP lewat pactl seperti di Chapter Audio Configuration |
Tidak muncul saat scan on | perangkat tidak dalam mode discoverable | aktifkan mode pairing di perangkat, dekati jaraknya |
| Pairing gagal berkali-kali | link key lama basi di salah satu sisi | remove perangkat lalu pair ulang |
| Adapter tidak terdeteksi sama sekali | soft block, atau pengontrol USB kehilangan akal | rfkill unblock bluetooth; bila tetap, reset modul dengan sudo modprobe -r btusb && sudo modprobe btusb |
Dua pola pertama selesai dalam hitungan detik; pola keempat adalah satu-satunya yang kadang butuh reboot. Setelah perangkat nirkabel beres, satu ranah kustomisasi desktop yang nyaris selalu membuat kaget pendatang baru dari Windows masih tersisa: printer — dan ternyata di Linux semuanya berporos pada satu layanan bernama CUPS. Kita bongkar pada Chapter Printer Management .
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
simulasi-pairing.sh
Unduh berkas#!/usr/bin/env bash
# simulasi-pairing.sh -- simulasi state machine pairing ala bluetoothctl
# Pemakaian: BT_STATE=<direktori> bash simulasi-pairing.sh <perintah> [MAC]
# Perintah : pair | trust | connect | disconnect | status
# Tanpa argumen: menjalankan demo singkat di direktori sementara.
state=${BT_STATE:-$(mktemp -d)}
mkdir -p "$state"
nama_perangkat() {
case "$1" in
00:1A:7D:DA:71:13) echo "Logitech G435" ;;
88:C9:D0:F5:12:34) echo "Keyboard Kantor" ;;
*) echo "Perangkat Tanpa Nama" ;;
esac
}
berkas_perangkat() {
echo "$state/$(echo "$1" | tr ':' '_')"
}
set_flag() { # $1=MAC $2=Paired|Trusted|Connected $3=yes|no
sed -i "s/^$2: .*/$2: $3/" "$(berkas_perangkat "$1")"
}
perintah=${1:-demo}
mac=${2:-00:1A:7D:DA:71:13}
f=$(berkas_perangkat "$mac")
case "$perintah" in
pair)
cat > "$f" <<EOF2
Name: $(nama_perangkat "$mac")
Paired: yes
Trusted: no
Connected: no
EOF2
echo "[CHG] Device $mac Paired: yes"
echo "Pairing successful"
;;
trust)
if [ ! -f "$f" ]; then
echo "Device $mac not available"
else
set_flag "$mac" Trusted yes
echo "[CHG] Device $mac Trusted: yes"
fi
;;
connect)
if [ ! -f "$f" ]; then
echo "Failed to connect: device $mac belum di-pair"
elif grep -q '^Connected: yes' "$f"; then
echo "Failed to connect: perangkat sudah tersambung"
else
set_flag "$mac" Connected yes
echo "[CHG] Device $mac Connected: yes"
echo "Connection successful"
fi
;;
disconnect)
if [ ! -f "$f" ] || ! grep -q '^Connected: yes' "$f"; then
echo "Device $mac not connected"
else
set_flag "$mac" Connected no
echo "[CHG] Device $mac Connected: no"
fi
;;
status)
if [ ! -f "$f" ]; then
echo "Device $mac not available"
else
echo "Device $mac ($(nama_perangkat "$mac"))"
sed 's/^/ /' "$f"
fi
;;
demo)
echo "== demo di direktori sementara =="
BT_STATE=$state bash "$0" pair "$mac"
BT_STATE=$state bash "$0" trust "$mac"
BT_STATE=$state bash "$0" connect "$mac"
BT_STATE=$state bash "$0" status "$mac"
;;
*)
echo "Perintah tidak dikenal: $perintah"
echo "Pilihan: pair | trust | connect | disconnect | status"
;;
esac