前言
嵌入式系統和MCU之間的連結,主要是透過有線通訊或無線通訊的方式進行,有線通訊的方式最常見的是採用USB的UART的方式進行,有線通訊是一種簡單方便的連線方式,傳輸效率甚高,但有線通訊在使用上會受到訊號線長度的限制,不適合用來進行較長距離或是行動裝置的應用情境中,為了解決有線通訊的限制,就必須考慮無線通訊的方式來處理嵌入式系統與MCU之間的環境限制的問題,由於無線通訊技術範疇十分廣泛,若以涵蓋範圍來分類可分為「廣域型」與「區域型」,本篇教學將會以「區域型」無線網路通訊應用為主,您將可看到 Tinker 2S 支援兩種藍牙通訊方式,為此將以不同的開發板來示範:
- 傳統藍牙 – Raspberry Pi Pico + HC06 藍牙模組
- 藍牙低功耗(BLE) – LinkIt 7697
目前在嵌入式系統或MCU常見的區域型無線通訊方式,如下表大致可以區分成5大類:
通訊技術 |
適用距離 |
Bits / Sec |
常見應用 |
NFC |
數公分~數公尺 |
約數百K |
門禁裝置 |
藍牙 |
約 <50公尺
(5.X版可達300公尺) |
約數十M |
辦公室自動化、居家自動化 |
Wi-Fi |
約 < 50公尺
(透過強波可到數百公尺) |
約 <數G |
辦公室自動化、居家自動化、工業自動化 |
Zigbee |
約數百公尺 |
約數百K |
辦公室自動化、居家自動化、工業自動化 |
LoRa |
約數公里 |
約數十K |
工業自動化、農業自動化 |
本篇教學將會以常見的藍牙通訊為主,主要是因為藍牙通訊廣泛支援許多行動裝置(如:手機、平板電腦、筆記型電腦等)的操作,此外藍牙通訊模組或晶片大量整合到各種常見MCU開發平台上,藍牙通訊也是各種無線通訊應用中最為廣泛的一種。
目前MCU常見的藍牙通訊模組電路或設計大致上是以「HC05系列的UART傳輸」或是「BLE系列的GATT傳輸」兩大類,本篇教學也將會以這兩種傳輸方式進行說明,以下是兩種做法的列表:
MCU種類 |
藍牙模組 |
傳輸方式 |
Raspberry Pi Pico |
外接HC05或HC06系列 |
UART |
Linkit-7697 |
內建BLE藍牙 |
GATT |
Tinker Board 2S針對上述兩種傳輸方式,也必須採用不同的Python程式以及安裝不同的Python API套件程式,主要分別如下表:
HC05系列UART傳輸API套件 |
BLE系列GATT傳輸API套件 |
Pyserial 套件 |
PyBluez 套件
Gattlib 套件
|
一、Tinker Board 2S安裝相關套件程式
Tinker 開機之後,請確認有網路連線,開啟終端機之後輸入以下指令來安裝相關套件:
pip3 install pyserial
sudo apt-get install python-dev
sudo apt-get install libbluetooth-dev
pip3 install pybluez --user
sudo apt-get install pkg-config
sudo apt-get install libboost-python-dev
sudo apt-get install libboost-thread-dev
sudo apt-get install libglib2.0-dev
pip3 install pybluez[ble]==0.22 --user
sudo pip3 install gattlib
二、HC05系列UART傳輸連線設定與程式設計
1.檢視並設定藍牙配對
接下來要在 Tinker 的桌面作業系統來操作
2. 檢視UART在Tinker Board 2S上的硬體資源占用狀況
在Tinker Board 2S作業系統對於藍牙UART的通訊應用,在系統資源名稱都是以rfcomm的方式進行資源的存取,這些相關存取的硬體資源訊息,通常都會放置在 /dev
目錄當中,只要檢索該目錄就可以尋找到對應MCU的通訊資源編號與名稱,本篇教學的HC-06 UART存取,是以/dev/rfcomm1
命名。
3. MCU的UART存取程式設計流程(簡易輸入/輸出資料傳輸測試)
- 設定通訊埠Baud Rate
- 接收來自Tinker Board 2S的UART串列資料
- 檢查序列資料並進行RGB-LED的操作
- 輸出序列資料回應Tinker Board 2S
4. Raspberry Pi Pico 連接 HC-06模組測試程式
請將 HC06 模組接在 Raspberry Pi Pico 之後,使用 Arduino IDE 燒錄以下程式:
#define LED_G 4
#define LED_B 3
#define LED_R 2
String str;
String SA[2];
int A[2];
UART Serial2(8, 9, 0, 0);
void setup(void)
{
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
pinMode(LED_R, OUTPUT);
digitalWrite(LED_G, LOW);
digitalWrite(LED_B, LOW);
digitalWrite(LED_R, LOW);
Serial2.begin(9600);
}
void loop(void)
{
str = "";
if (Serial2.available()) {
// 讀取傳入的字串直到"#"結尾
str = Serial2.readStringUntil('#');
String_to_Int(str, 2);
}
if (str.length() != 0) {
if (SA[0] == "G") {
if (A[1] == 1) {
digitalWrite(LED_G, HIGH);
Serial2.println("Green ON");
}
if (A[1] == 0) {
digitalWrite(LED_G, LOW);
Serial2.println("Green OFF");
}
}
if (SA[0] == "B") {
if (A[1] == 1) {
digitalWrite(LED_B, HIGH);
Serial2.println("Blue ON");
}
if (A[1] == 0) {
digitalWrite(LED_B, LOW);
Serial2.println("Blue OFF");
}
}
if (SA[0] == "R") {
if (A[1] == 1) {
digitalWrite(LED_R, HIGH);
Serial2.println("Red ON");
}
if (A[1] == 0) {
digitalWrite(LED_R, LOW);
Serial2.println("Red OFF");
}
}
}
}
void String_to_Int(String temp, int count)
{
int index;
index = temp.indexOf(',');
SA[0] = temp.substring(0, index);
for (int i = 1; i < count; i++) {
temp = temp.substring(index + 1, temp.length());
index = temp.indexOf(',');
SA[i] = temp.substring(0, index);
A[i] = SA[i].toInt();
}
}
Pi pico HC06
這個測試程式主要可以「接收」使用者的命令進行RGB-LED電路模組的控制,並且「發送」訊息回應使用者。
接收命令 |
RGB-LED動作 |
發送回應訊息 |
R,1 |
亮起紅色光 |
Red ON |
R,0 |
熄滅紅色光 |
Red OFF |
G,1 |
亮起綠色光 |
Green ON |
G,0 |
熄滅綠色光 |
Green OFF |
B,1 |
亮起藍色光 |
Blue ON |
B,0 |
熄滅藍色光 |
Blue OFF |
5. Tinker Board 2S Python測試流程:
- 輸入控制命令
- 發送控制命令至MCU
- 等候來自MCU的回應訊息
- 接收MCU的回應訊息
- 顯示來自MCU的回應訊息
6. Tinker Board 2S Python測試程式:
在 Tinker 上,請用您喜歡的文字編輯器新增一個 .py 檔,例如:
nano SERIAL_DATA_IO.py
create .py
SERIAL_DATA_IO.py
內容如下:
import serial
import argparse
import io
import time
import numpy as np
import cv2
from PIL import Image
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument(
'--com', help='Number of UART prot.', required=True)
args = parser.parse_args()
COM_PORT = args.com
BAUD_RATES = 9600
ser = serial.Serial(COM_PORT, BAUD_RATES,timeout=0, parity=serial.PARITY_EVEN, stopbits=1)
while(True):
COMMAND = input('請輸入控制命令=')
Data_flag = 0
while(Data_flag == 0):
Send_UART_command = COMMAND.encode(encoding='utf-8')
ser.write(Send_UART_command) # 訊息必須是位元組類型
time.sleep(0.5)
while ser.in_waiting:
mcu_feedback = ser.readline().decode() # 接收回應訊息並解碼
Data = mcu_feedback.strip('\r\n')
if 'ON' in Data:
Data_flag = 1
print('MCU 回應:', Data)
elif 'OFF' in Data:
Data_flag = 1
print('MCU 回應:', Data)
else:
Data_flag = 0
tinker bluetooth communication
這個程式可以讓使用者可以把控制命令從Tinker Board 2S發送至MCU,並且等待MCU的回應訊息,接收回應訊息後必須檢查序列資料的內容是否正確,最後在螢幕顯示MCU的回傳訊息。
使用以下指令來對指令 com port 進行通訊:
python3 SERIAL_DATA_IO.py --com /dev/rfccomm1
run .py to communicate with MCU via BT
Raspberry Pi pico 實際執行照片
二、Tinker Board 2S與Linkit-7697以BLE藍牙通訊方式進行連結
透過BLE藍牙通訊協定進行連結最大的特點,便是Tinker Board 2S與藍牙裝置彼此之間不需要進行「事前配對」,只要Tinker Board 2S開啟藍牙存取功能,在系統中所運作的程式中便可以進行配對,並且可以在程式結束之後立刻將系統資源進行釋放,這樣的運作方式,除了可以更加有效率進行各種藍牙周邊裝置的資源分配,還可以讓Tinker Board 2S在程式運作的過程中迅速完成各類裝置的資料存取。
本篇教學所採用的Linkit-7697 MCU內建了BLE藍牙通訊功能,不需要再額外連接藍牙通訊模組,而且也擁有可以用於進行各種電路周邊模組連接的數位類比控制接腳,非常適合進行各種藍牙通訊控制應用,開發環境亦支援搭配Arduino IDE進行開發。
1. 測試電路
2. Linkit-7697 BLE藍牙通訊存取程式設計流程(簡易輸入與輸出資料傳輸測試)
設定通訊埠存取規範,如:存取名稱、GATT存取方式等。
接收來自Tinker Board 2S以GATT傳輸的串列資料
檢查串列資料並進行RGB-LED的操作
透過GATT方式將輸出串列資料回應給Tinker Board 2S
3. Linkit-7697 BLE藍牙通訊Arduino IDE測試程式片段:
請用 Arduino IDE (需先安裝 LinkIt 7697 相關套件)燒錄以下程式,可參考 [LinkIt 7697 BlocklyDuino 使用指南] (已移交 CAVEDU管理)
#include <LBLE.h>
#include <LBLEPeriphral.h>
#define LED_G 10
#define LED_B 11
#define LED_R 12
String Send_Data;
String SA[2];
int A[2];
// Define a simple GATT service with only 1 characteristic
LBLEService ledService("19B10010-E8F2-537E-4F6C-D104768A1214");
//LBLECharacteristicInt switchCharacteristic("19B10011-E8F2-537E-4F6C-D104768A1214", LBLE_READ | LBLE_WRITE);
LBLECharacteristicString switchCharacteristic("19B10011-E8F2-537E-4F6C-D104768A1214", LBLE_READ | LBLE_WRITE);
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Serial.begin(9600);
//初始化BLE
Serial.println("BLE begin");
LBLE.begin();
while (!LBLE.ready()) {
delay(100);
}
Serial.println("BLE ready");
Serial.print("device address is:");
Serial.println(LBLE.getDeviceAddress()); //顯示7697 BLE addr
//設定廣播資料
// In this case, we simply create an advertisement that represents an
// connectable device with a device name
LBLEAdvertisementData advertisement;
advertisement.configAsConnectableDevice("BLE Jack");
// Configure our device's Generic Access Profile's device name
// Ususally this is the same as the name in the advertisement data.
LBLEPeripheral.setName("BLE Jack");
// Add characteristics into ledService
ledService.addAttribute(switchCharacteristic);
// Add service to GATT server (peripheral)
LBLEPeripheral.addService(ledService);
//啟動GATT伺服器,此時已可被連線
LBLEPeripheral.begin();
//開始廣播
LBLEPeripheral.advertise(advertisement);
}
void loop()
{
if (switchCharacteristic.isWritten()) {
String value = switchCharacteristic.getValue();
String_to_Int(value, 2);
Serial.print(SA[0]);
Serial.print(",");
Serial.println(A[1]);
if (SA[0] == "G") {
if (A[1] == 1) {
digitalWrite(LED_G, HIGH);
Send_Data = "Green ON";
switchCharacteristic.setValue(Send_Data);
}
if (A[1] == 0) {
digitalWrite(LED_G, LOW);
Send_Data = "Green OFF";
switchCharacteristic.setValue(Send_Data);
}
value = "";
}
if (SA[0] == "B") {
if (A[1] == 1) {
digitalWrite(LED_B, HIGH);
Send_Data = "Blue ON";
switchCharacteristic.setValue(Send_Data);
}
if (A[1] == 0) {
digitalWrite(LED_B, LOW);
Send_Data = "Blue OFF";
switchCharacteristic.setValue(Send_Data);
}
value = "";
}
if (SA[0] == "R") {
if (A[1] == 1) {
digitalWrite(LED_R, HIGH);
Send_Data = "Red ON";
switchCharacteristic.setValue(Send_Data);
}
if (A[1] == 0) {
digitalWrite(LED_R, LOW);
Send_Data = "Red OFF";
switchCharacteristic.setValue(Send_Data);
}
value = "";
}
}
delay(100);
}
void String_to_Int(String temp, int count)
{
int index;
index = temp.indexOf(',');
SA[0] = temp.substring(0, index);
for (int i = 1; i < count; i++) {
temp = temp.substring(index + 1, temp.length());
index = temp.indexOf(',');
SA[i] = temp.substring(0, index);
A[i] = SA[i].toInt();
}
}
linkit 7697 ble communication
這個測試程式主要可以「接收」使用者的命令進行RGB-LED電路模組的控制,並且「發送」訊息回應使用者。
接收命令 |
RGB-LED動作 |
發送回應訊息 |
R,1 |
亮起紅色光 |
Red ON |
R,0 |
熄滅紅色光 |
Red OFF |
G,1 |
亮起綠色光 |
Green ON |
G,0 |
熄滅綠色光 |
Green OFF |
B,1 |
亮起藍色光 |
Blue ON |
B,0 |
熄滅藍色光 |
Blue OFF |
4. Tinker Board 2S Python測試流程:
- 輸入控制命令
發送控制命令至MCU
等候來自MCU的回應訊息
接收MCU的回應訊息
顯示來自MCU的回應訊息
5. Tinker Board 2S檢查Linkit-7697 BLE 藍牙相關資訊
主要是針對Linkit-7697 BLE藍牙名稱與藍牙MAC資訊,無需事先配對,但因為Python程式需要針對特定的藍牙MAC進行存取,因此必須先行檢查,檢查的方式可由Tinker Board 2S作業系統的Bluetooth Device程式進行MAC的查詢。
6. Tinker Board 2S Python測試程式
同樣,請新增一個 .py 之後輸入以下內容
from gattlib import GATTRequester
import io
import time
import os
import sys
MAC_BT = 'F5:7C:00:2B:88:8C'
requester = GATTRequester(MAC_BT,False)
data_list = []
def BLE_connect():
print("Connecting...", end='')
sys.stdout.flush()
requester.connect(True)
print("OK!")
def send_data(Data_String):
requester.write_by_handle(0xa2,Data_String)
def request_data():
data = requester.read_by_handle(0xa2)[0]
data_string = data.decode()
# data_list = data_string.split(',')
# return data_list
return data_string
def main():
BLE_connect()
while (True):
COMMAND = input('請輸入控制命令=')
send_data(COMMAND)
recive_data = request_data()
print(recive_data)
if __name__ == '__main__':
main()
tinker BLE communication
這個程式可以讓使用者可以把控制命令從Tinker Board 2S發送至MCU,並且等待MCU的回應訊息,接收回應訊息後必須檢查序列資料的內容是否正確,最後在螢幕顯示MCU的回傳訊息。
使用以下指令來對指令 com port 進行通訊:
python3 BLE_DATA_IO.py
run BLE communication
本篇教學是以藍牙通訊為主進行Tinker Board 2S與MCU的連結控制,藍牙通訊大都是應用在短距離傳輸且資料傳輸量不高的應用情境中,搭配上各種行動裝置大都也將藍牙通訊列為標準配備,使得藍牙通訊成為用途極廣的物聯網應用技術,但若是要進行長距離的遠端遙控,藍牙通訊便會有所限制,因此,我們會在另外一篇「ASUS Tinker Board 2S 與MCU的聯結應用—<WiFi無線通訊篇>」,針對TCP/IP通訊應用進行詳細的介紹,希望透過TCP/IP的相關協定應用設計,將整個物聯網相關應用範疇拓展到更加寬廣的應用情境當中。
相關文章