Raspberry Pi 是一款手掌大小的計(jì)算機(jī),具有內(nèi)置藍(lán)牙、Wi-Fi、以太網(wǎng)端口、攝像頭端口等,使其最適合基于 IoT 的嵌入式應(yīng)用程序的微控制器。它還用于制作多種服務(wù)器,如打印服務(wù)器、媒體服務(wù)器、Web 服務(wù)器等。今天我們將學(xué)習(xí)樹(shù)莓派如何將具有 3.5 毫米插孔的普通揚(yáng)聲器轉(zhuǎn)換為無(wú)線藍(lán)牙揚(yáng)聲器。
在這篇文章中,我們將通過(guò)融合 A2DP、Linux 和音頻編解碼器的強(qiáng)大功能來(lái)構(gòu)建基于 Raspberry Pi 的藍(lán)牙揚(yáng)聲器,以將數(shù)據(jù)包從音頻源無(wú)線傳輸?shù)揭纛l接收器。為此,我們將破解一點(diǎn) Linux 系統(tǒng)并用 bash 和 python 編寫(xiě)一段代碼,然后我們就可以開(kāi)展業(yè)務(wù)了。
A2DP
A2DP 是Advanced Audio Distribution Profile的首字母縮寫(xiě)。這是幾乎所有支持藍(lán)牙的設(shè)備中都存在的協(xié)議。它為從一個(gè)設(shè)備到另一個(gè)設(shè)備的聲音數(shù)據(jù)傳輸鋪平了道路,前提是它們都通過(guò)藍(lán)牙相互連接。A2dp 使用無(wú)損壓縮算法在傳輸前壓縮音頻數(shù)據(jù)包以減少延遲,但由于這種壓縮導(dǎo)致的損失幾乎無(wú)法被人耳感知。
為無(wú)頭設(shè)置準(zhǔn)備 Raspberry Pi
要將 Raspberry Pi 轉(zhuǎn)換為無(wú)線揚(yáng)聲器,首先將 OS(Raspbian Stretch)安裝到 Raspberry PI SD 卡中,如果您是 Raspberry Pi 新手,請(qǐng)按照本文開(kāi)始使用 Raspberry Pi。
我們大多數(shù)人都擁有 Raspberry Pi 和筆記本電腦,但沒(méi)有顯示器。但是為了通過(guò) SSH 連接到 Raspberry Pi,我們希望將其連接到與我們的計(jì)算機(jī)連接的同一網(wǎng)絡(luò)中。我們需要連接到 Pi 的顯示器,通過(guò)它我們可以選擇 Wi-Fi 并連接?
其實(shí)我們沒(méi)有。Raspberry Pi 可以通過(guò)向名為wpa_supplicant.conf的文件添加條目來(lái)連接到 Wi-Fi
為此,請(qǐng)將 SD 卡連接到計(jì)算機(jī)并打開(kāi)文件rootfs/etc/wpa_supplicant/wpa_supplicant.conf并將以下條目添加到其中。不要忘記以管理員(root)權(quán)限打開(kāi)文件。
網(wǎng)絡(luò)={
?
ssid="wifi_ssid" psk="wifi_passkey" key_mgmt=WPA-PSK }
?
條目應(yīng)與此類(lèi)似。
上面的條目應(yīng)該讓我們連接到 Wi-Fi,但這還不足以在 Raspberry Pi 和計(jì)算機(jī)之間創(chuàng)建和維護(hù) SSH 連接。默認(rèn)情況下 SSH 在 Raspberry Pi 中是禁用的,因此要啟用它,請(qǐng)?jiān)谝龑?dǎo)目錄中創(chuàng)建一個(gè)名為ssh的空文件。
現(xiàn)在 Raspberry Pi 在技術(shù)上可以遠(yuǎn)程訪問(wèn)。將樹(shù)莓派連接到電源?,F(xiàn)在 pi 會(huì)自動(dòng)連接到 Wi-Fi,但需要它的 IP 地址才能通過(guò) SSH 連接到它。有多種方法可以找出相同的。我使用nmap命令
nmap -sn [網(wǎng)關(guān)ip地址]/24
該命令將為我們提供網(wǎng)絡(luò)中連接的所有設(shè)備的 IP 地址。例如,
其中之一是樹(shù)莓派的?,F(xiàn)在我們知道了 pi 的 IP 地址讓我們連接到它
ssh pi@pi_ip_address
在 Raspberry Pi 中安裝的先決條件
藍(lán)Z
BlueZ 是 Raspbian 發(fā)行版附帶的默認(rèn)應(yīng)用程序。它用于訪問(wèn)系統(tǒng)的藍(lán)牙控件。如果您的 pi 中沒(méi)有它,也可以安裝它,原因只有您可能知道。
下面的命令獲取安裝在我們的 pi 中的藍(lán)牙接口應(yīng)用程序。
apt-get 安裝 bluez
脈沖音頻
Pulse Audio 是一種將計(jì)算機(jī)數(shù)據(jù)字節(jié)轉(zhuǎn)換為人類(lèi)感知的應(yīng)用程序。它也被稱(chēng)為音樂(lè)播放器。A2DP 協(xié)議在 PulseAudio 應(yīng)用程序插件中可用。因此,讓我們使用以下命令安裝所有與脈沖音頻相關(guān)的應(yīng)用程序:
apt-get 安裝 pulseaudio-*.
將藍(lán)牙設(shè)備與 Raspberry Pi 配對(duì)
使用命令打開(kāi) BlueZ 應(yīng)用程序
藍(lán)牙控制
藍(lán)牙代理是在兩個(gè)啟用藍(lán)牙的設(shè)備之間進(jìn)行通信并初始化它們之間的連接的代理。有不同類(lèi)型的藍(lán)牙代理。我們將使用NoInputNoOutput代理,因?yàn)樗屛覀儫o(wú)需用戶(hù)干預(yù)即可連接。因此,讓我們通過(guò)運(yùn)行以下命令來(lái)初始化代理。
代理 NoInputNoOutput
您應(yīng)該得到消息“代理注冊(cè)”作為響應(yīng)?,F(xiàn)在我們已經(jīng)注冊(cè)了我們的代理,讓我們將其設(shè)為默認(rèn)代理。
默認(rèn)代理
響應(yīng)應(yīng)該是“默認(rèn)代理請(qǐng)求成功”
現(xiàn)在讓我們讓我們的設(shè)備可被發(fā)現(xiàn)
可在
其響應(yīng)應(yīng)為“成功更改可發(fā)現(xiàn)”
現(xiàn)在嘗試將您的手機(jī)或計(jì)算機(jī)連接到樹(shù)莓派
應(yīng)用程序會(huì)提示我們授權(quán)服務(wù),我們不需要這樣做。相反,我們將只信任設(shè)備并連接它。信任設(shè)備非常重要,因?yàn)楫?dāng)受信任的設(shè)備嘗試與 pi 連接時(shí),它允許相同的操作而無(wú)需用戶(hù)干預(yù)。
trust [設(shè)備mac地址]
connect [設(shè)備mac地址]
完成所有這些操作后,您的終端應(yīng)該與此類(lèi)似。
耶!我們的手機(jī)通過(guò)藍(lán)牙與 Raspberry Pi 連接。但這足夠了嗎?顯然不,我們希望我們的聲音數(shù)據(jù)包從手機(jī)傳輸?shù)?pi,然后從 pi 傳輸?shù)竭B接到 pi 音頻端口的揚(yáng)聲器。
讓我們通過(guò)運(yùn)行以下命令確保我們的手機(jī)在PulseAudio應(yīng)用程序的音頻源中列出:
契約清單簡(jiǎn)短
它將列出所有加載的聲音模塊、音頻接收器和音頻源
對(duì)照序列號(hào) 30 查看值。Bluez_source表示通過(guò)藍(lán)牙的 BlueZ 應(yīng)用程序的音頻源。交叉檢查bluez_source 和 a2dp_source 之間的設(shè)備 MAC 地址以及您在 BlueZ 應(yīng)用程序中的地址。在我的情況下,它是bluez_source.3C_28_6D_FD_65_3D.a2dp_source,它與 BlueZ 應(yīng)用程序中的相同?,F(xiàn)在,如果您從連接到 pi 的設(shè)備播放歌曲,它應(yīng)該被路由到連接到樹(shù)莓派音頻端口的揚(yáng)聲器。
尤里卡!我們已經(jīng)成功打造了一款藍(lán)牙音箱。我們已經(jīng)路由了聲音,但這還不是全部。我們無(wú)法手動(dòng)完成上述所有步驟,因此讓我們使用expect 腳本和接口 pi 與一個(gè)開(kāi)關(guān)來(lái)自動(dòng)化它們,按下該開(kāi)關(guān)時(shí),將 Pi 與 devices 配對(duì)。
涼爽的?現(xiàn)在讓我們開(kāi)始談?wù)隆?/p>
使用 Python 腳本自動(dòng)化藍(lán)牙配對(duì)過(guò)程
Expect 腳本類(lèi)似于 bash 腳本,但是是自動(dòng)化的。它在終端中查找給定的單詞,當(dāng)相同的單詞到達(dá)時(shí),它根據(jù)腳本發(fā)送命令。讓我們自動(dòng)化配對(duì)過(guò)程。創(chuàng)建一個(gè)名為pair_bluetooth_device.expect的文件
?
設(shè)置超時(shí) 30 生成藍(lán)牙ctl 預(yù)計(jì) ”# ” 發(fā)送“代理關(guān)閉\r” 期待“?gistered” 發(fā)送“\r” 預(yù)計(jì) ”# ” 發(fā)送“代理 NoInputNoOutput\r” 期待“代理注冊(cè)” 發(fā)送“\r” 預(yù)計(jì) ”# ” 發(fā)送“默認(rèn)代理\r” 期望“默認(rèn)代理請(qǐng)求成功” 發(fā)送“\r” 預(yù)計(jì) ”# ” 發(fā)送“可發(fā)現(xiàn)于\r” 期待“授權(quán)” 發(fā)送“是\r” 發(fā)送“退出\r”
?
復(fù)制代碼并將其粘貼到文件中。它只是自動(dòng)執(zhí)行,我們?cè)趯⑹謾C(jī)與樹(shù)莓派配對(duì)時(shí)執(zhí)行的操作。它只是讓設(shè)備連接但不信任它。要信任設(shè)備,我們需要它的 mac 地址。所以我們將把這個(gè)expect腳本的輸出打印到一個(gè)可以獲取mac地址的日志文件中。
?
grep -Pom 1 "(?<=Device ).*(?= Connected)"
?
上面的命令打印出字符串“Device”和“Connected”之間的值。在我們的例子中(Device 3C:28:6D:FD:65:3D Connected: no)它是設(shè)備的 MAC 地址。
讓我們編寫(xiě)一個(gè)期望腳本,它將 mac 地址作為第一個(gè)參數(shù)并信任并連接到該設(shè)備。
創(chuàng)建一個(gè)名為trust_and_connect.expect的文件
?
設(shè)置超時(shí) 30 spawn bluetoothctl expect "#" send "agent off\r" expect "?egistered" send "\r" 期望“#” 發(fā)送“代理在\r” 期望“代理注冊(cè)” 發(fā)送“\r” 期望“#” 發(fā)送“默認(rèn)代理\r” 期望“默認(rèn)代理請(qǐng)求成功” 發(fā)送“\r” 期望“#” 發(fā)送“信任[lindex $argv 0]\r” 期望“變化” 發(fā)送“連接 [lindex $argv 0]\r” 期望“連接成功” 發(fā)送“退出\r”
?
將上面的代碼復(fù)制到該文件中。它自動(dòng)完成信任和連接部分。
現(xiàn)在讓我們將所有這些都放在一個(gè) Python 腳本文件中,以便整個(gè)配對(duì)過(guò)程可以自動(dòng)化。
讓我們創(chuàng)建一個(gè)文件pair_and_trust_bluetooth_device.sh
?
cd $(dirname $0) echo "Pairing..." expect pair_bluetooth_device.expect > expect_script.log chmod 777 expect_script.log sleep 2 echo "信任和連接.." device_mac_address=$(cat expect_script.log | grep -Pom 1 "(?<=Device ).*(?= Connected)") echo mac address is $device_mac_address if [[ ! -z $device_mac_address ]] ; 然后 期望 trust_and_connect.expect $device_mac_address else echo "No device connected" fi rm expect_script.log
?
所以bash腳本,
調(diào)用一個(gè)期望腳本(其輸出將被打印到一個(gè)名為 expect_script.log 的文件中),其中,
啟動(dòng)NoInputNoOutput代理
使其成為默認(rèn)代理
打開(kāi) pi 的可發(fā)現(xiàn)性
等待某人連接并在有人連接或超時(shí)時(shí)退出
睡眠 2 秒
獲取設(shè)備mac地址的expect_script.log文件
如果 mac_address 為空,則信任并連接設(shè)備
刪除殘留文件expect_script.log
使用按鈕觸發(fā)藍(lán)牙配對(duì)腳本
現(xiàn)在我們有了自動(dòng)化配對(duì)過(guò)程的腳本。但是這個(gè)腳本必須在用戶(hù)需要的時(shí)候方便地運(yùn)行。因此,讓我們將此腳本與一個(gè)物理按鈕掛鉤,以便每次按下按鈕時(shí)都會(huì)調(diào)用此腳本。中斷是嵌入式編程的重要組成部分之一。對(duì)于初學(xué)者來(lái)說(shuō),當(dāng)感知到中斷時(shí),會(huì)執(zhí)行程序的常規(guī)例程并運(yùn)行一個(gè)預(yù)定義的 ISR,稱(chēng)為中斷服務(wù)例程。
因此,讓我們將按鈕連接到 gpio 引腳 11 并為其分配 ISR。在 ISR 內(nèi)部,我們將調(diào)用腳本。
讓我們創(chuàng)建一個(gè)名為?Bluetooth-speaker-main.py的 python 文件,并將下面的代碼添加到其中。我已經(jīng)在程序中添加了注釋?zhuān)匀绻闶褂眠@個(gè)代碼,你仍然有它們
?
#import 需要的包 import subprocess import RPi.GPIO as gpio import time import os import logging pair_pin=11 #獲取運(yùn)行python腳本的文件目錄 fileDirectory = os.path.dirname(os.path.realpath(__file__)) #設(shè)置日志文件位置和python腳本位置一樣 logFile=fileDirectory+"/bluetoothSpeaker.log" logging.basicConfig(filename=logFile, filemode='w', format='%(name)s - %(levelname)s - %(message)s', level=logging.INFO) def pairNewDevice(channel): #ISR for pin 11 print("Waiting to pair") logging.info("Waiting to pair") output = subprocess.call(["/bin/bash",fileDirectory+"/pair_and_trust_bluetooth_device.sh" , ">>", 文件目錄+"/bluetoothSpeaker.log"]) gpio.setmode(gpio.BOARD) gpio.setup(pair_pin, gpio.IN, pull_up_down=gpio.PUD_UP) try: #將pair_pin設(shè)置為檢測(cè)下降沿的中斷引腳,當(dāng)檢測(cè)到下降沿時(shí),調(diào)用pairNewDevice函數(shù) gpio.add_event_detect(pair_pin, gpio.FALLING, callback=pairNewDevice,bouncetime=1000) print("Bluetooth program has started") logging.info("Bluetooth program has started") while True: time.sleep (5) 除了 KeyboardInterrupt: gpio.cleanup()
?
電路原理圖
下面是連接一個(gè)按鈕與樹(shù)莓派的 GPIO11 以觸發(fā)藍(lán)牙配對(duì)過(guò)程以通過(guò)藍(lán)牙傳輸音頻的電路圖。
設(shè)置 Cron 作業(yè)以在啟動(dòng)時(shí)啟動(dòng)藍(lán)牙揚(yáng)聲器 Python 程序
現(xiàn)在最后讓我們?cè)O(shè)置一個(gè) cron 作業(yè),它將在每次 pi 啟動(dòng)時(shí)啟動(dòng)這個(gè) python 程序。
crontab -e
選擇您喜歡的編輯器并在文件末尾添加以下行
@reboot python3 /home/pi/blueooth-speaker/Bluetooth-speaker-main.py
這將在每次 pi 啟動(dòng)時(shí)調(diào)用我們的 python 程序。
就是這樣。鷹已著陸。你已經(jīng)制作了一個(gè)無(wú)頭樹(shù)莓派藍(lán)牙音箱。
重新啟動(dòng)您的 Pi,配對(duì)您的手機(jī)并流式傳輸音頻。:)
評(píng)論
查看更多