第 07 章 - 使用藍芽模組
上次更新日期 2022/10/26
基本上,arduino 上面接感測器之後,其數據應該要發送到伺服器上面才對。而 arduino 通常會放置到某些空間, 在該空間應該不會有線路直接連線到伺服器的!所以,我們應該要給 arduino 一些無線通訊設備才對。無限通訊有非常多種, 我們這邊先使用藍芽來處理看看。
學習目標:
- 認識藍芽模組
- 使用 Android 手機測試配對藍芽
- 使用樹莓派派對藍芽模組
- 使用 SoftwareSerial 進行資料傳輸
- 使用樹莓派透過藍芽控制 arduino
7.1: 藍芽模組連接與測試
Arduino 的藍芽模組,主要有 HC-05 與 HC-06 兩者,其中 HC-05 可以當作主導者,也可以是接收者,而 HC-06 只能當接收者。 我們這裡的練習當中,arduino 主要是作為環境感測資料的收集者,因此,只要被連結即可,不用主動去連結別人。 所以,你手上拿到的藍芽就會是 HC-06 這樣。
- 藍芽模組
藍芽可以分好幾世代,在無限傳輸的領域中,藍芽可以在同一個空間裡面,不受物體隔離的限制來進行連線。 例如,座位兩端有隔板時,在隔板兩端的藍芽傳輸是可以運作的!這跟遙控器的紅外線傳輸不太一樣!前面講到,arduino 常用的藍芽模組, 主要有 HC-05 與 HC-06,藍芽模組上面主要有六支腳,可以透過某些腳位來對藍芽模組直接控制。但是,如果你的藍芽, 只是單純的作為一個傳接器,你沒有要控制藍芽時,那只要有供電、接地、傳輸與接收這四隻腳就可以了! 本章提供的 HC-06 也就只有這四隻腳而已!
- VCC:使用 3.3V 的供電
- GND:就是接地
- TX:傳送出資料的腳位
- RX:接收資料的腳位
- 關於 RX/TX 針腳
如果你仔細看一下你的 arduino 面板接腳,會發現有兩個比較特別的腳位,一個是 RX(receiver) 一個是 TX(transfer),這個是接收與傳送的腳位之意。 這個是 arduino 的預設值喔!不過,比較傷腦筋的是,當你有使用 USB 連接到電腦時,這個 RX 與 TX 預設會被 USB 的的序列埠所佔用, 所以,如果有接上電腦,基本上,你就不能使用這兩個針腳了。如果你的 arduino 沒有連上電腦,USB 只是拿來供電, 那就沒問題!你可以直接將藍芽安插到 RX/TX 上面。
但是你要注意喔,arduino 的 TX 主要是傳輸,而 HC-06 的 RX 則是在接收,因此,在接線的時候,你得要這樣接, 不然資料是無法傳遞的!注意注意!
- arduino TX <==> HC-06 RX
- arduino RX <==> HC-06 TX
- 藍芽模組連線:
- 將藍芽模組的 4 隻腳安插到不同組的麵包板上面。
- 將 arduino 3.3V 對應到 VCC 腳位
- 將 arduino 接地對應到 GND 腳位
- 將 arduino D5 對應到 TX 腳位
- 將 arduino D4 對應到 RX 腳位
- 將 arduino 現在接上 USB 插孔,你會發現藍芽模組 LED 燈亮,然後閃爍相當快速!
- 拿出手機,點擊設定,以 android 手機為例,選擇『已連結的裝置』之後,點選『配對新裝置』, 就會看到有個 HC-06 新裝置出現了!點擊 HC-06 準備配對,這時手機上面會出現需要輸入 PIN 碼的密碼欄位, 預設 HC-06 密碼是『 1234 』,輸入這串文字,應該就可以配對成功。之後在『已配對』的畫面中,就會看到 HC-06 了! 請點擊 HC-06,應該就可以看到這個裝置的藍芽位址。確定可以成功之後,按下『清除』,取消配對吧!
目前我們知道這個藍芽模組已經可以實際運作,也大略知道這個藍芽模組的藍芽位址,但是,我們沒有手機程式可以控制這個藍芽! 也就是說,目前我們還沒有辦法經過這個藍芽來達到通訊的目的。那怎辦呢?這時,就得要透過擁有藍芽功能的樹莓派支援了!
7.2: 以樹莓派配對藍芽與傳輸測試
我們在第二章的 2.1 小節,就已經使用過 Serial 序列埠模組進行終端機與 arduino 的資料傳遞,當時大家只知道可以透過 COM4 來進行資料傳輸, 還不曉得 RX/TX 腳位的功能呢!現在,我們要使用的,就是透過藍芽的傳輸,取代 Serial 在終端界面的輸出/輸入而已! 所以,我們必須要在樹莓派上面進行藍芽配對,然後同樣透過藍芽序列埠的功能,從藍芽去傳輸資料!這樣有稍微理解藍芽的功能與目的嘛?
- 登入樹莓派
假設你已經知道怎麼使用樹莓派,如果不知道的話,可以參考底下的連結來稍微熟悉一下:
假設你已經架設好樹莓派,同時登入到你的樹莓派系統中,我這裡使用的是樹莓派 pi 3B 的樹莓派硬體版本,使用了樹莓派原生的 Linux 系統, 系統相關的版本可以這樣查閱:
$ cat /etc/os-release PRETTY_NAME="Debian GNU/Linux 11 (bullseye)" NAME="Debian GNU/Linux" VERSION_ID="11" VERSION="11 (bullseye)" VERSION_CODENAME=bullseye ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/" $ uname -a Linux raspberrypi 5.15.61-v8+ #1579 SMP PREEMPT Fri Aug 26 11:16:44 BST 2022 aarch64..
- 應用樹莓派的藍芽裝置 - 掃描用
在樹莓派上面有個名為 hcitool 的工具,我們可以拿它來了解一下,樹莓派本身的藍芽是否正常運作中! 同時也可以掃描附近的藍芽裝置喔!一般來說,樹莓派預設應該是安裝了 bluez 軟體的,如果沒有的話,請自行安裝這套軟體! 安裝妥當之後,我們先來看看樹莓派藍芽自己的狀態如何:
# 先看一下自己藍芽的狀態 $ hciconfig -a hci0: Type: Primary Bus: UART BD Address: B8:27:EB:27:51:76 ACL MTU: 1021:8 SCO MTU: 64:1 UP RUNNING RX bytes:2544 acl:1 sco:0 events:122 errors:0 TX bytes:3657 acl:1 sco:0 commands:106 errors:0 Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87 Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 Link policy: RSWITCH SNIFF Link mode: SLAVE ACCEPT Name: 'raspberrypi' Class: 0x2c0000 Service Classes: Rendering, Capturing, Audio Device Class: Miscellaneous, HCI Version: 4.1 (0x7) Revision: 0x1fc LMP Version: 4.1 (0x7) Subversion: 0x2209 Manufacturer: Broadcom Corporation (15)
上面我們可以看到我們樹莓派上面的藍芽裝置名稱為 hci0 ,然後目前的狀態是正在運行中 (UP RUNNING)。如果你發現藍芽沒開啟, 或者是想要關閉藍芽的功能,就可以這樣做看看:
# 關閉藍芽 $ sudo hciconfig hci0 down # 啟動藍芽 $ sudo hciconfig hci0 up
另外,也能使用 hcitool 這個指令來查看一下樹莓派上面的藍芽裝置的位址與裝置名稱喔:
$ hcitool dev
Devices:
hci0 B8:27:EB:27:51:76
現在,讓我們來掃描一下環境當中的可用藍芽裝置有多少!
$ hcitool scan
Scanning ...
20:17:03:02:53:55 HC-06
經過一小段時間的掃描後,樹莓派就會告訴你,目前有多少藍芽裝置了!如上,我們會有一個名為 HC-06 的藍芽裝置可以連接! 上面比較重要的其實是藍芽位址,也就是 arduino 上面的 HC-06 藍芽模組的位址 20:17:03:02:53:55 那串資料囉! 現在,讓我們來查看一下這個藍芽模組跟我們的樹莓派之間的連線,是否可以連接成功?用 ping 的方式來查詢看看:
$ sudo l2ping -c 3 20:17:03:02:53:55
Ping: 20:17:03:02:53:55 from B8:27:EB:27:51:76 (data size 44) ...
4 bytes from 20:17:03:02:53:55 id 0 time 9.73ms
4 bytes from 20:17:03:02:53:55 id 1 time 13.40ms
4 bytes from 20:17:03:02:53:55 id 2 time 7.26ms
3 sent, 3 received, 0% loss
繼續讓我們來查看一下這個 HC-06 的藍芽內容是什麼,很簡單的,同樣透過 hcitool 的指令來查詢即可,如下所示: (要注意,查詢對方的藍芽模組內容,就得要使用 root 權限才行了!)
$ sudo hcitool info 20:17:03:02:53:55
Requesting information ...
BD Address: 20:17:03:02:53:55
Device Name: HC-06
LMP Version: 2.0 (0x3) LMP Subversion: 0x103b
Manufacturer: Cambridge Silicon Radio (10)
Features: 0xff 0xff 0x8f 0x78 0x18 0x18 0x00 0x80
<3-slot packets> <5-slot packets> <encryption> <slot offset>
<timing accuracy> <role switch> <hold mode> <sniff mode>
<park state> <RSSI> <channel quality> <SCO link> <HV2 packets>
<HV3 packets> <u-law log> <A-law log> <CVSD> <paging scheme>
<power control> <transparent SCO> <broadcast encrypt>
<enhanced iscan> <interlaced iscan> <interlaced pscan>
<inquiry with RSSI> <AFH cap. slave> <AFH class. slave>
<AFH cap. master> <AFH class. master> <extended features>
確定是可以連接上的囉!那就準備來配對吧!
- 樹莓派的藍芽配對
開始來配對吧!要注意的是,配對的時候,最好取得藍芽的位址喔~這樣綁定比較不會失誤! 在教室的場合中,很可能會有多個不同的藍芽模組設備,這時最好找到正確的那個才好!可能得要一個一個打開供電才行。 假設找到你的藍芽位址之後,就可以使用 bluetoothctl 指令這樣做:
$ bluetoothctl Agent registered [bluetooth]# agent on Agent is already registered [bluetooth]# default-agent Default agent request successful [bluetooth]# scan on Discovery started [CHG] Controller B8:27:EB:27:51:76 Discovering: yes [NEW] Device 6E:39:7E:FF:19:EE 6E-39-7E-FF-19-EE [NEW] Device 20:17:03:02:53:55 HC-06 [bluetooth]# pair 20:17:03:02:53:55 Attempting to pair with 20:17:03:02:53:55 [CHG] Device 20:17:03:02:53:55 Connected: yes Request PIN code [agent] Enter PIN code: 1234 [CHG] Device 20:17:03:02:53:55 UUIDs: 00001101-0000-1000-8000-00805f9b34fb [CHG] Device 20:17:03:02:53:55 ServicesResolved: yes [CHG] Device 20:17:03:02:53:55 Paired: yes Pairing successful [CHG] Device 20:17:03:02:34:51 ServicesResolved: no [CHG] Device 20:17:03:02:34:51 Connected: no [NEW] Device 51:FB:DA:8F:77:DA 51-FB-DA-8F-77-DA [bluetooth]# paired-devices Device 20:17:03:02:53:55 HC-06 [bluetooth]# exit
我們只是用這個指令做配對,那麼未來這台樹莓派重新開機之類的,再次偵測到這部 HC-06 之後,就會自動配對 (pair), 因此,上面的步驟在同一台已經紀錄配對的樹莓派上面,不需要重新操作指令!只需要進行底下的任務即可。
- 建立連線通道:每次樹莓派重新開機都要實作一次
接下來,我們建立給藍芽使用的通道裝置!一般來說,都是建置在 /dev/rfcommNN 這個名稱上面,NN 是數值,從 0 到 99 這樣。 至於頻道,好像都使用 1 就可以了,如果有衝突再修改即可。
$ sudo rfcomm bind rfcommXX 藍芽位址 $ sudo rfcomm bind rfcomm0 20:17:03:02:53:55 $ rfcomm -a rfcomm0: 20:17:03:02:53:55 channel 1 clean $ ll /dev/rfc* crw-rw---- 1 root dialout 216, 0 10月 24 09:27 /dev/rfcomm0
要注意,裝置檔名不可以重複!這樣,你的樹莓派,就可以使用多個不同的藍芽模組裝置了! 如果要卸載這個藍芽模組,不要刪除裝置檔案喔~而是使用移除的方式來處理:
$ sudo rfcomm release /dev/rfcomm0
在這裡特別強調,如果你曾經綁定過某個藍芽模組,再次綁定時,可能會有互相衝突的情況發生。因此,最好的方法是, 先使用 rfcomm release 的方式卸載,之後將 arduino 拔除,讓 HC-06 供電完全停止,然後再給予供電,這樣就可以再次綁定了!
- 關於 arduino 的 SoftwareSerial 模組
前面說到,既然 RX/TX 可能被 USB 序列埠佔用,那麼,如何進行藍芽模組的資料接傳呢?我們可以透過 SoftwareSerial (軟體序列埠) 這個模組來模擬, 也就是說,你可以載入 SoftwareSerial 模組,然後自定義 RX/TX 的腳位是那一隻呢!另外要注意,這個 RX/TX 使用的是數位腳位, 也就是 D1~D13 的位置上喔!不要搞混了。
以上面 7.1.1 的例題為例的話,那麼 arduino 的 D5 會是本身的 RX 腳位,而 D4 則是 TX 腳位!要跟藍芽模組的腳位剛好相反才行! 使用 SoftwareSerial 模組,就會有點像這樣:
#include <SoftwareSerial.h> SoftwareSerial BT(5,4); // 函數名(接收腳, 傳送腳)
這時你就會自定義一個名為 BT 的函數,這個函數的使用跟一般序列埠,也就是 Serial 序列埠的使用,幾乎相同! 例如讀取 USB 與讀取藍芽資料的方法,對照如下:
Serial.read(); BT.read(); Serial.readString(); BT.readString();
那,就讓我們寫一隻簡單的收、發資訊用的腳本吧!回到你的 arduino IDE 環境下,請記得,目前我們藍芽模組的兩端, 都是透過 9600 鮑率來傳輸資料,從藍芽抓到的資訊,就透過 USB 的終端界面寫回終端機~那就這樣做 (檔名設為 code-bt-1):
#include <SoftwareSerial.h> SoftwareSerial BT(5,4); // RX, TX void setup() { // 這是 USB 界面終端機的設定 Serial.begin(9600); Serial.println("Setup Bluetooth!"); // 這是藍芽傳輸的界面設定 BT.begin(9600); BT.println("Hello, world!"); } void loop() { // run over and over // 藍芽有資訊傳來時,就輸出到 USB 終端界面 if (BT.available()) { Serial.write(BT.read()); } // 終端機有資料傳來,就藉由藍芽界面傳輸出去 if (Serial.available()) { BT.write(Serial.read()); } }
若沒有問題,就請編譯、上傳到 arduino 上面看看!並且打開 USB 終端界面,等著傳輸資料囉!另外要注意的是, BT.println 可以傳輸字串, 而 BT.write 則是傳輸字元 (char),兩者的資料型別不太一樣!我們從 USB 終端界面抓到的是字元型態,所以使用 BT.write 來輸出! 在樹莓派上面抓到的資料,除了第一個 "Hello, world!" 之外,大致上就是字元格式喔!
- 由樹莓派上使用 python 擷取資料
接著下來,我們得要透過樹莓派上面的 python 來處理讀取/傳輸訊號的方式!使用的腳本有點像這樣:
$ cd ~ $ mkdir bt $ cd bt $ vim conn.py import serial from time import sleep BT = serial.Serial( "/dev/rfcomm0", 9600) BT.flushInput() while True: print("get data....") getstr = BT.readline() print("recv from arduino : " + str(getstr)) print("transfer to arduino...\n") BT.write(b'3') sleep(1) $ python conn.py
接下來,你嘗試到 arduino 的 USB 終端機,輸入一些簡單的字串看看,就可以發現,這些資料應該可以在 python 裡面出現了! 相當的有趣喔!另外,若確定程式碼沒問題,就可以將 sleep 拿掉了!互動傳輸速度比較快!
- 問題排除
其實 HC-06 藍芽模組配合樹莓派,藍芽的連線好像不是很順暢,老是容易掛掉的感覺。如果掛掉的話,或許嘗試將整個 arduino 拔除, 過幾秒鐘之後,再次連接到 USB 看看,有時候會自動連上線。如果還是有問題,可以進入到樹莓派,使用 bluetoothctl 的指令, 透過裡面的 remove 的功能,將你的藍芽裝置移除,再次配對 (pair) 之後離開 bluetoothctl 程式,再次處理 arduino 看看。 很神奇的,有時候就變成 OK 的模樣了....很怪!
另外,當樹莓派的 python 程式連上藍芽 HC-06 時,HC-06 的燈號會恆亮,不會閃爍!這也是判斷是否連線成功的參考方向喔!
7.3: 以樹莓派 python 程式及藍芽控制 arduino 的 LED 燈號
目前,我們已經可以透過藍芽在樹莓派上面發送、取得 arduino 的訊息。樹莓派使用 python 取得 arduino 的資訊之後, 進一步處理數據是比較單純的!但是,我們能不能透過藍芽,對 arduino 下達指令呢?記得喔,預設的情況下, 從樹莓派使用藍芽下達指令時,其資料主要是以字元 (byte 或 char) 的資料型態存在喔!在處理上面要特別留意! 我們舉個很簡單的案例來處理。當連接上 LED 燈號在 arduino 上面時,我們使用樹莓派對這個 LED 下達開關的指令處理好了! 應該不是很難~直接來進行指令處理:
- arduino 上面的 IDE 程式碼
目前鳥哥這邊將 LED 接在 D9 上面,相關的接法請自行找一下之前的課程內容。接好之後,直接使用底下的程式碼燒錄看看: (檔名先設定為 code-bt-02 好了)
#include <SoftwareSerial.h> SoftwareSerial BT(5,4); // RX, TX int led = 9; void setup() { Serial.begin(9600); Serial.println("Setup Bluetooth!"); BT.begin(9600); pinMode(led, OUTPUT); } void loop() { if (BT.available()) { char myinp = BT.read(); Serial.write(myinp); if ( myinp == '1' ) { digitalWrite(led,HIGH); } if ( myinp == '2' ) { digitalWrite(led,LOW); } } }
上面的意思是,當發現有藍芽傳來的訊息 (BT.available) 時,就將傳來的資訊抓下來~捉下來的資料判斷一下,如果是字元 1 時, 就打開 LED 燈,如果是字元 2 ,那就關閉 LED 燈,這樣就處理妥當!其他不用理會!
- 使用樹莓派進行指令傳輸
其實用的是 python 啦!透過 python 的 input() 函數,就可以讓使用者以鍵盤跟程式互動,整體的程式寫入到名為 led-1.py 的檔案中, 該檔案內容有點像這樣:
$ vim ~/bt/led-1.py
import serial
from time import sleep
BT = serial.Serial( "/dev/rfcomm0", 9600)
BT.flushInput()
while True:
print("1) light up LED")
print("2) light off LED")
myinp = input("your LED (1|2): ")
print("transfer to arduino...")
BT.write(bytes(myinp, 'utf-8'))
#BT.write('GoGo'.encode('ascii'))
#BT.write(bytes("GoGo", 'utf-8'))
#BT.print("gogo")
sleep(1)
接下來,只要『 python led-1.py 』,你就可以點亮/關閉 LED 燈了!
import sys myinp = sys.argv[1] # sys.argv[0] 是程式檔名本身 # sys.argv[1] 是第 1 個外帶參數 # sys.argv[2] 是第 2 個外帶參數 # ...
7.4: 參考資料
- 與藍芽模組相關性較高的幾篇文章:
- 無線連結兩個Arduino控制板:https://swf.com.tw/?p=750
- 使用藍芽操控:https://tutorials.webduino.io/zh-tw/docs/basic/setting/bluetooth.html
- 樹莓派的藍芽 bluetoothctl 相關性較高的幾篇:
- 台部落:樹莓和Arduino之間的藍牙通訊https://www.twblogs.net/a/5c8c3674bd9eee35cd6ad1a7
- Connecting To Bluetooth Devices Via CLI: https://www.geeksforgeeks.org/connecting-to-bluetooth-devices-via-cli/
- bluetooth command:https://raspberry-projects.com/pi/pi-operating-systems/raspbian/bluetooth/bluetooth-commands
- 與樹莓派 bluez 及藍芽 BLE 技術(低耗電) 較相關的幾篇資料:
- 一篇有幫助的碩士論文:https://www.airitilibrary.com/Publication/alDetailedMesh1?DocID=U0002-2502201912135300&PublishTypeID=P003
- 雙樹莓探測rssi並通過藍芽互傳獲取的rssi訊號強度: https://iter01.com/568207.html
- Radius Network:https://developer.radiusnetworks.com/2014/12/04/fundamentals-of-beacon-ranging.html
- 阿舍:https://www.arthurtoday.com/2014/12/raspberry-pi-install-bluez-5-with-ble-support.html
- http://cheng-min-i-taiwan.blogspot.com/2015/03/raspberry-pi-40ibeacon.html
- ESP8266: https://randomnerdtutorials.com/esp8266-nodemcu-static-fixed-ip-address-arduino/
- python argv: https://www.tutorialspoint.com/python/python_command_line_arguments.htm