---
# System prepended metadata

title: 第13週- IoT 無線遠距控制
tags: [BLE, 遠距控制, Bluetooth, IoT]

---

# 第13週- IoT 無線遠距控制
###### tags: `Bluetooth` `BLE` `IoT` `遠距控制`

終於到了遙控 ESP32 IoT 的課程，可以開始遠距讀取並控制 ESP32 的數值和動作。
## 遠距控制的方式：
**1) 紅外線** [（第12週課程）](https://hackmd.io/@smile2307/H1AzS7he3)
* ESP32 至少需加裝 1838B 紅外線發射接收器
* 傳輸速度與距離成反比，最快 16Mbps
* 工作功率極低，但容易被阻擋
* 較便宜
* 需額外的紅外線遙控器
![](https://hackmd.io/_uploads/BJ6kpFnrh.png)

**2) 藍牙**（第13週課程）
* ESP32 已內含BLE硬體，並具有天線
* 傳輸頻寬為 1Mbps
* 工作功率低，但ISM頻段 2.4GHz 容易受干擾
* 較貴
* 需藍牙連線的控制設備，如手機當 host 並配對
![](https://hackmd.io/_uploads/BJmKaFhr2.png)

**3) WiFi網路**（第14、15週課程）
* ESP32 已內含WiFi硬體，並具有天線
* 傳輸頻寬為 1-11Mbps(2.4GHz)/1-500Mbps(5GHz)
* 工作功率較高，使用 ISM 頻段中的 2.4G/5GHz 雙頻
* 較貴
* 可以當成是工作站或熱點、或兩者同時，構成 IoT 普通採用的行動隨意網路 (MANET, Mobile ad‐hoc network) 或無線隨意網路 (WANET, Wireless ad‐hoc network)
* 可以證書方式認證並加密
![](https://hackmd.io/_uploads/BkCGAY2r2.png)



## ESP32 藍牙實作:
### 實作一：
啟動 ESP32 藍牙功能
>1. 依課本 p.86 的硬體需求
>1. 依課本 p.87，上傳程式
>1. 需一部 Android 手機，並下戴安裝 "Arduino Bluetooth Control"
>1. Android 手機需先開藍牙設定、開啟、配對新裝置
>1. 依 p.89 所示，連接 ESP32 藍牙裝置

:::warning
hint: BluetoothSerial 宣告為 BT，BT.begin() 和 BT.println()
:::spoiler
```javascript=
#include <BluetoothSerial.h>
BluetoothSerial BT;//宣告藍芽物件，名稱為BT

void setup() {
  Serial.begin(115200);
  BT.begin("");//請改名
}

void loop() {
  BT.println("Hello World!");
  delay(1000);
}
```
:::
:::success
觀察：當打開 App terminal 後，連續收到 "Hello World"。
:::

### 實作二：:wink:延伸整合題
ESP32 透過藍牙將溫濕度數值，向手機傳送
>1. 增加 DHT11 硬體，訊號線接 GPIO 4
>1. 依課本 p.91，上傳程式
>1. App 連接 ESP32 藍牙裝置

:::warning
hint: 將 DHT11 練習的 Serial.println() 改為 BT.println()
:::spoiler
```javascript=
#include <SimpleDHT.h>
#include <BluetoothSerial.h>
BluetoothSerial BT;
int pinDHT11 = 4; //本例請將溫度計S腳接在GPIO 4
SimpleDHT11 dht11(pinDHT11);

void setup() {
  Serial.begin(115200);
  BT.begin("");//請改名
}

void loop() {
  // start working...
  Serial.println("=================================");
  Serial.println("Sample DHT11...");
  // read without samples.
  byte temperature = 0;
  byte humidity = 0;
  int err = SimpleDHTErrSuccess;
  if ((err = dht11.read(&temperature, &humidity, NULL)) != SimpleDHTErrSuccess) {
    Serial.print("Read DHT11 failed, err="); Serial.println(err); delay(1000);
    return;
  }
  //將溫濕度傳輸到藍芽裝置
  BT.print((int)temperature);
  BT.print(" *C,");
  BT.print((int)humidity);
  BT.println(" H");
  delay(1000); //休息1秒
}
```
:::

:::success
觀察：當打開 App terminal 後，連續收到溫濕度數值。
:::

:::info
已經擺脫 LCD 液晶顯示幕，可以遠距觀察環境即時變化。
:::

### 實作三：
手機透過藍牙，反向對 ESP32 遠距傳輸
>1. 依課本 p.93，上傳程式
>1. App 連接 ESP32 藍牙裝置

:::warning
hint: BT.readString()
:::spoiler
```javascript=
#include <BluetoothSerial.h>
BluetoothSerial BT;

void setup() {
  Serial.begin(115200);
  BT.begin("");//請改名
}
void loop() {
  //檢查藍芽內是否有資料
  while (BT.available()) {
    //讀取藍芽資料
    String BTdata=BT.readString();
    //顯示在序列視窗
    Serial.println(BTdata);
  }
  delay(1);
}
```
:::
:::success
觀察：當在 App terminal 輸入後，電腦連續收到手機的訊息。
:::

### 實作四：
透過藍牙，手機和 ESP32 雙向溝通
>1. 依課本 p.94，上傳程式
>1. App 連接 ESP32 藍牙裝置
:::warning
hint: Serial.readString() 和 BT.readString()
:::spoiler
```javascript=
#include <BluetoothSerial.h>
BluetoothSerial BT;

void setup() {
  Serial.begin(115200);
  BT.begin("");//請改名
}

void loop() {
  //檢查序列監控視窗是否有輸入資料
  while (Serial.available()) {
    //讀取序列資料
    String Sdata = Serial.readString();
    //傳輸給藍芽
    BT.println(Sdata);
    //顯示在序列視窗
    Serial.println(Sdata);
  }

  //檢查藍芽內是否有資料
  while (BT.available()) {
    //讀取藍芽資料
    String BTdata = BT.readString();
    //顯示在序列視窗
    Serial.println(BTdata);
  }
  delay(1);
}
```
:::
:::success
觀察：同時在 App terminal 和手機App看到雙向訊息。
:::

### 實作五：
透過手機藍牙向 ESP32 傳送控制指令
>1. 將綠、黃、紅 LED 分別連接至GPIO 15、2、4 位置
>1. 依課本 p.95，上傳程式
>1. App 連接 ESP32 藍牙裝置
>1. 在 App terminal 輸入數字
:::warning
:::spoiler
```javascript=
#include <BluetoothSerial.h>
BluetoothSerial BT;

void setup() {
  Serial.begin(115200);
  BT.begin("");//請改名
  pinMode(15, OUTPUT); //綠色LED
  pinMode(2, OUTPUT); //黃色LED
  pinMode(4, OUTPUT); //紅色LED
}

void loop() {
  //檢查序列內是否有資料
  while (Serial.available()) {
    //讀取序列資料
    String Sdata = Serial.readString();
    //傳輸給藍芽
    BT.print(Sdata);
  }

  //檢查藍芽內是否有資料
  while (BT.available()) {
    //讀取藍芽資料
    String BTdata = BT.readString();
    //顯示在序列視窗
    Serial.println(BTdata);
    //檢查藍芽資料及相對的指令
    //接收到"1"：開綠燈
    if (BTdata == "1") { digitalWrite(15, HIGH); }
    //接收到"2"：關綠燈
    if (BTdata == "2") { digitalWrite(15, LOW); }
    //接收到"3"：開黃燈
    if (BTdata == "3") { digitalWrite(2, HIGH); }
    //接收到"4"：關黃燈
    if (BTdata == "4") { digitalWrite(2, LOW); }
    //接收到"5"：開紅燈
    if (BTdata == "5") { digitalWrite(4, HIGH); }
    //接收到"6"：關紅燈
    if (BTdata == "6") { digitalWrite(4, LOW); }
  }
  delay(1);
}

```
:::
:::success
觀察：輸入數字1~6後，查看燈號變化
現在可以手機藍牙遠距手動控制燈號了。
:::

### 實作六：
透過設定語音指令，以語音向 ESP32 傳送控制指令
>1. 依課本 p.100，輸入語音指令
>1. App 連接 ESP32 藍牙裝置
>1. 對手機 App 麥克風說話
:::success
觀察：當語音指令正確接受後，查看燈號變化
:::



## :bulb:程式語法思考題
如何同時使用手機 App 和 電腦序列視窗，雙向輸入控制指令？

:::warning
建議：是否該去理解C/C++的程式語言？
:::

### 思考題 參考程式 
:::success
hint: 以 char 宣告 Serial 讀取字串, char 讀 'x'， String 讀 "xx"
[參考 ~[C語言_01]字元陣列，字串傻傻分不清楚？by_Andy_Cheng~](https://medium.com/andy的趣味程式練功坊/c語言-01-字元陣列-字串傻傻分不清楚-45089f69f6be)
:::spoiler
### 思考題 參考程式(一)
```javascript=
#include <BluetoothSerial.h>
BluetoothSerial BT;

char Sdata;

void setup() {
  Serial.begin(115200);
  BT.begin("");//請改名
  pinMode(15, OUTPUT);    //綠LED, pin#15
  pinMode(2, OUTPUT);     //黃LED, pin#2
  pinMode(4, OUTPUT);     //紅LED, pin#4
}

void loop() {
  while (Serial.available()>0) {

    //讀取序列資列
    Sdata = Serial.read();
    Serial.print(Sdata);
    if (Sdata == '1') {digitalWrite(15,HIGH);}
    if (Sdata == '2') {digitalWrite(15,LOW);}
    if (Sdata == '3') {digitalWrite(2,HIGH);}
    if (Sdata == '4') {digitalWrite(2,LOW);}
    if (Sdata == '5') {digitalWrite(4,HIGH);}
    if (Sdata == '6') {digitalWrite(4,LOW);}

    //傳輸給藍牙
    BT.print(Sdata);
  }
  delay(1);
  
  //檢查藍牙內是否有資料
  while (BT.available()) {
    //讀取藍牙資料
    String BTdata=BT.readString();
    //顯示在序列視窗
    Serial.println(BTdata);
    //檢查藍牙資料及相對的指示
    if (BTdata == "1") {digitalWrite(15,HIGH);}
    if (BTdata == "2") {digitalWrite(15,LOW);}
    if (BTdata == "3") {digitalWrite(2,HIGH);}
    if (BTdata == "4") {digitalWrite(2,LOW);}
    if (BTdata == "5") {digitalWrite(4,HIGH);}
    if (BTdata == "6") {digitalWrite(4,LOW);}
  }
  delay(1);
}
```
:::

:::success
hint: 讀取序列字串 Serial.readStringUntil()，Terminal 輸出以 '\n' 結尾
:::spoiler
### 思考題 參考程式(二)
```javascript=
#include <BluetoothSerial.h>
BluetoothSerial BT;

void setup() {
  Serial.begin(115200);
  BT.begin("");//請改名
  pinMode(15, OUTPUT);    //綠LED, pin#15
  pinMode(2, OUTPUT);     //黃LED, pin#2
  pinMode(4, OUTPUT);     //紅LED, pin#4
}

void loop() {
  while (Serial.available()) {

    //讀取序列資列
    String Sdata = Serial.readStringUntil('\n');
    Serial.println(Sdata);
    if (Sdata == "1") {digitalWrite(15,HIGH);}
    if (Sdata == "2") {digitalWrite(15,LOW);}
    if (Sdata == "3") {digitalWrite(2,HIGH);}
    if (Sdata == "4") {digitalWrite(2,LOW);}
    if (Sdata == "5") {digitalWrite(4,HIGH);}
    if (Sdata == "6") {digitalWrite(4,LOW);}

    //傳輸給藍牙
    BT.print(Sdata);
  }
  delay(1);
  
  //檢查藍牙內是否有資料
  while (BT.available()) {
    //讀取藍牙資料
    String BTdata=BT.readString();
    //顯示在序列視窗
    Serial.println(BTdata);
    //檢查藍牙資料及相對的指示
    if (BTdata == "1") {digitalWrite(15,HIGH);}
    if (BTdata == "2") {digitalWrite(15,LOW);}
    if (BTdata == "3") {digitalWrite(2,HIGH);}
    if (BTdata == "4") {digitalWrite(2,LOW);}
    if (BTdata == "5") {digitalWrite(4,HIGH);}
    if (BTdata == "6") {digitalWrite(4,LOW);}
  }
  delay(1);
}
```
:::

:::success
hint: 以自建函式庫 function
:::spoiler
### 思考題 參考程式(三)
```javascript=
#include <BluetoothSerial.h>
BluetoothSerial BT;

void setup() {
  Serial.begin(115200);
  BT.begin("");//請改名
  pinMode(15, OUTPUT);    //綠LED, pin#15
  pinMode(2, OUTPUT);     //黃LED, pin#2
  pinMode(4, OUTPUT);     //紅LED, pin#4
}

void loop() {
  while (Serial.available()) {

    //讀取序列資列
    String Sdata = Serial.readStringUntil('\n');
    Serial.println(Sdata);
    //檢查藍牙資料及相對的指示
    judgeled(Sdata);
    //傳輸給藍牙
    BT.print(Sdata);
  }
  delay(1);
  
  //檢查藍芽內是否有資料
  while (BT.available()) {
    //讀取藍芽資料
    String BTdata=BT.readString();
    //顯示在序列視窗
    Serial.println(BTdata);
    //檢查藍牙資料及相對的指示
    judgeled(BTdata);
  }
  delay(1);
}


void judgeled(String input) {
    if (input == "1") {digitalWrite(15,HIGH);}
    if (input == "2") {digitalWrite(15,LOW);}
    if (input == "3") {digitalWrite(2,HIGH);}
    if (input == "4") {digitalWrite(2,LOW);}
    if (input == "5") {digitalWrite(4,HIGH);}
    if (input == "6") {digitalWrite(4,LOW);}
}
```
:::

## :wink:自主練習題
如何以語音啟動三種不同亮燈模式的 WS2812?
>閃綠圈
>閃黃圈
>閃紅圈
>

## 參考資料
>1）IOT物聯網應用第七章 – 尤濬哲(夜市小霸王) 編著
>2）[Infrared Remote & Receiver Module](https://www.jsumo.com/infrared-remote-receiver-module)
>3）[ESP32 Bluetooth Low Energy By Fernando Koyanagi in Circuits](https://www.instructables.com/ESP32-Bluetooth-Low-Energy/)
>4）[MANET Network in Internet of Things System WRITTEN BY Rasa Bruzgiene, Lina Narbutaite and Tomas Adomkus](https://www.intechopen.com/chapters/53178)
>5) [[C語言_01] 字元陣列，字串傻傻分不清楚？by Andy Cheng](https://medium.com/andy的趣味程式練功坊/c語言-01-字元陣列-字串傻傻分不清楚-45089f69f6be)

