arduino 官網 arduino uno

Arduino 物聯網應用 - 上課教材

動畫互動網頁程式設計 > 課程內容 > 第 07 章 - 使用藍芽模組

第 07 章 - 使用藍芽模組

上次更新日期 2022/10/26

基本上,arduino 上面接感測器之後,其數據應該要發送到伺服器上面才對。而 arduino 通常會放置到某些空間, 在該空間應該不會有線路直接連線到伺服器的!所以,我們應該要給 arduino 一些無線通訊設備才對。無限通訊有非常多種, 我們這邊先使用藍芽來處理看看。

學習目標:

  1. 認識藍芽模組
  2. 使用 Android 手機測試配對藍芽
  3. 使用樹莓派派對藍芽模組
  4. 使用 SoftwareSerial 進行資料傳輸
  5. 使用樹莓派透過藍芽控制 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
例題 7.1.1: 安裝藍芽模組與使用手機掃描藍芽設備
  1. 藍芽模組連線:
    • 將藍芽模組的 4 隻腳安插到不同組的麵包板上面。
    • 將 arduino 3.3V 對應到 VCC 腳位
    • 將 arduino 接地對應到 GND 腳位
    • 將 arduino D5 對應到 TX 腳位
    • 將 arduino D4 對應到 RX 腳位
  2. 將 arduino 現在接上 USB 插孔,你會發現藍芽模組 LED 燈亮,然後閃爍相當快速!
  3. 拿出手機,點擊設定,以 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 燈了!

例題 7.3.1: 剛剛上面使用的是在 python 程式當中敲擊指令,這種情境對於網頁模式、一次性操作指令的方式,都不是太友善。 如果想要使用類似『 python led-2.py 1 』來打開燈號,使用『 python led-2.py 2 』來關閉燈號時, 就得要透過 sys 模組的支援才行。請參考底下的連結之後,寫出外帶參數來執行的腳本。 兩行重要的程式碼如下,請參考:
import sys
myinp = sys.argv[1]
# sys.argv[0] 是程式檔名本身
# sys.argv[1] 是第 1 個外帶參數
# sys.argv[2] 是第 2 個外帶參數
# ...

7.4: 參考資料