---
tags: 單晶片助教
---
# **[10] TCP/IP**
## **課程大綱**
[ToC]
---
# **1. TCP/IP**
## 簡介
TCP/IP 是一種網際網路的基礎通訊架構,其內容主要分為 TCP (Transmission Control Protocol/傳輸控制協定) 還有 IP (Internet Protocol/網際網路協定)。
TCP/IP架構主要由四個層所組成,分別是:
1. Application Layer (應用層)
2. Transport Layer (傳輸層)
3. Internet Layer (網際網路層、網路層)
4. Network Access Layer (網路存取層)
通常我們在 messenger、Line 等傳送訊息,是在應用層輸入訊息,而這個訊息會經由傳輸層、網際網路層、網路存取層,一層層的將訊息打包成封包,並將這個封包發送出去。就好比在寫信時,必須寫好信件內容,並放入信封,寫上地址、收件者等資訊,才有辦法寄送出去。而 TCP/IP 四層架構,也是在做相同的事。

| 層級 | 協定種類 | 功能 |
| -------- | --------- | -------- |
| Application Layer (應用層) | HTTP、FTP、MQTT|定義應用專屬資料格式|
| Transport Layer (傳輸層) | TCP、UDP |定義傳輸方式|
| Internet Layer (網際網路層) | IP |定義收發位置|
| Network Access Layer (網路存取層) | MAC|提供定址及媒體存取的控制方式|
其中,我們會較著眼在 Transport Layer (傳輸層),以及 Internet Layer (網際網路層)。
**傳輸層 Transport Layer**
在傳輸層中有兩種比較常用的協定,分別為UDP (User Datagram Protocol)、TCP (Transmission Control Protocol)。其中以 TCP 為大宗。
大部分的網路協定都是建立在 TCP 協定之上,因為 TCP 有著高度可靠性。而 TCP 的高度可靠是建立在TCP的三次握手 (three-way handshake)規則上。當 TCP 在建立連接通道的過程,資料會互相來回傳送三次,以確保對方是否有接收到。
**網際網路層 Internet Layer**
在此層裡面,是將 IP 資料 (Internet Protocol) 再包裝到訊息封包內,而 IP 資料就是將訊息上標記,該訊息是從誰傳送出來,還有要將訊息傳給誰。
常見的IP協定有兩種,一是 IPv4,二則是 IPv6,兩者最大的差別在於 IPv4 是 32bits,IPv6 則是 64bits。
此協定內有一段資料是 IP Address,IP Address 就是說明該訊息是從哪個位置傳送出來的。其中 IP 有分為以下幾種類型:
固定 IP:固定的 IP,企業、學校等都是使用固定 IP,這樣方能確保使用者可以連上伺服器。
浮動 IP:在每次連網的時的 IP 位置都不一樣,一般的使用者跟家用電腦大部分都是浮動 IP。
虛擬 IP:僅能使用於內部網路 (或是區域網路),外網是連不上的。
# **2. Wi-Fi**
WiFi.h 為 Arduino IDE 內建程式庫,提供 4 種 WiFi 模式可用:
| 語法 | WI-FI模式 | 功能 |
| -------- | -------- | -------- |
| WiFi.mode(WIFI_AP); | Access Point (AP) | ESP32 可以讓其他設備透過 Wi-Fi 接入(就像家裡的 Wi-Fi 基地台,可供手機連線)。 |
| WiFi.mode(WIFI_STA); | Station(STA) | 無線終端模式,也就是讓 ESP32 可以連接上其他的熱點(就像手機一樣,可以連上家裡 Wi-Fi)。 |
| WiFi.mode(WIFI_AP_STA); | AP+STA | 將 ESP32 設置成兩個模式並存。 |
| WiFi.mode(WIFI_OFF); | OFF | 關閉 Wi-Fi。 |
## 將 ESP32 連接至 Wi-Fi
* 程式碼:
```c++=
#include <WiFi.h> //Wi-Fi 程式庫
const char* ssid = "Wi-Fi 網路名稱"; //Wi-Fi 網路名稱
const char* password = "Wi-Fi 密碼"; //Wi-Fi 密碼
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA); //設置 Wi-Fi 模式
WiFi.begin(ssid, password);
Serial.print("Wi-Fi Connecting");
//當 Wi-Fi 連線時會回傳 WL_CONNECTED,因此跳出迴圈時代表已成功連線
while(WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.print("Wi-Fi Connected.");
}
void loop() {
}
```
## Lab 1-1:
### 獲取網路時間
* 實驗目的:學習於 ESP32 上使用 Wi-Fi。
* 實驗目標:透過 Wi-Fi 取得網路時間,並顯示於 LCD 螢幕上。
* 範例程式碼:
```c++=
#include <WiFi.h> //Wi-Fi 程式庫
#include <Wire.h> //I2C 程式庫
#include <LiquidCrystal_I2C.h> //LCD_I2C 模組程式庫
LiquidCrystal_I2C lcd(0x27, 16, 2);
const char* ssid = "Wi-Fi 網路名稱"; //Wi-Fi 網路名稱
const char* password = "Wi-Fi 密碼"; //Wi-Fi 密碼
const char *ntpServer = "pool.ntp.org"; //校時伺服器
const long gmtOffset_sec = 8*3600; //格林威治時間, GMT+8 就是 8*3600
const int daylightOffset_sec = 0; //日光調節時間, 有為 3600, 沒有則為 0
void setup(){
Serial.begin(115200);
//初始化LCD
//To do.
//Connect to Wi-Fi
//To do.
//取得網路時間
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
printLocalTime();
//連上拿到時間資料後就切斷連線
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
void loop(){
delay(1000);
printLocalTime();
}
void printLocalTime(){
struct tm timeinfo;
if(!getLocalTime(&timeinfo)){
Serial.println("Failed to obtain time");
return;
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
//顯示於 LCD 螢幕
//To do.
}
```
* 結果:
<iframe width="560" height="315" src="https://www.youtube.com/embed/eKa7FWkbXng""YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
## LINE Notify

* **LINE Notify 是一項非常方便的服務,用戶可以透過 LINE 接收GitHub、IFTTT 及 Mackerel 等網站服務的提醒。與網站服務連動完成後,LINE 所提供的官方帳號「LINE Notify」將會傳送通知。**
### 登入 Line Notify 取得 Token
* 請先打開瀏覽器,並搜尋Line Notify,進入頁面後點擊登入。

* 登入後,點擊相同地方,選擇個人頁面。

* 點擊發行權杖。

* 當出現這畫面時,就是開始申請權杖 (Token),所以請先輸入名稱,及選擇透過 1 對 1 聊天接收 Line Notify 的通知,都選擇好後點擊發行。

* 發行後,會看到一條序號,這序號就是你的權杖(Token),請先把這權杖複製起來,之後會使用到。

**<font color="Red">注意,該權杖只會顯示這次,所以請先把這權杖記錄起來,盡量複製起來貼上記事本上</font>**
### Arduino IDE 取得 TridentTD_LineNotify 程式庫
* 至程式庫管理員下載 "TridentTD_LineNotify",版本為 3.0.3 **<font color="Red">其餘版本會導致無法編譯。</font>**

### Arduino IDE 取得 DHT11 程式庫
* 至程式庫管理員下載 "DHT sensor library",使用版本為 1.4.4。

* 範例程式碼:
```c++=
#include <WiFi.h> //Wi-Fi 程式庫
#include <TridentTD_LineNotify.h> //Line 程式庫
#define LINE_TOKEN "xxxxxxxxxxx" //修改成上述的 Token 號碼
const char* ssid = "Wi-Fi 網路名稱"; //Wi-Fi 網路名稱
const char* password = "Wi-Fi 密碼"; //Wi-Fi 密碼
void setup(void)
{
Serial.begin(115200);
WiFi.mode(WIFI_STA); //設置 Wi-Fi 模式
WiFi.begin(ssid, password);
Serial.print("Wi-Fi Connecting");
//當 Wi-Fi 連線時會回傳 WL_CONNECTED,因此跳出迴圈時代表已成功連線
while(WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.print("Wi-Fi Connected.");
Serial.println(LINE.getVersion()); //顯示 Line 版本
LINE.setToken(LINE_TOKEN); //設定要傳送至的聊天室 Token
}
void loop(void)
{
//每一分鐘 (60000ms),傳送 "Hello" 至 Token 指定的聊天室
LINE.notify("Hello");
delay(60000);
}
```
## Lab 1-2:
### IOT 物聯網
* 實驗目的:學習於 ESP32 上使用 Wi-Fi,並透過 Line 傳送訊息。
* 實驗目標:使用 DHT11 讀取環境溫濕度,並透過 Wi-Fi 將訊息傳送至 Line。
* 接線圖:

* **DHT11 與其他感測器相比較為脆弱,若 VCC、GND 接反很快就會燒毀,在接線上必須特別注意**
* 程式碼:
<font color="Red">下方僅給予溫度的程式碼,需自行加入濕度的程式碼</font>
```c++=
#include <WiFiClient.h> //WiFi 程式庫
#include <Wire.h> // I2C 程式庫
#include <TridentTD_LineNotify.h> //Line 程式庫
#include "DHT.h" // DHT11 程式庫
#define LINE_TOKEN "xxxxxxxxxxx" //修改成上述的 Token 號碼
#define DHTTYPE DHT11
#define DHTPIN 12
const char* ssid = "Wi-Fi 網路名稱"; //Wi-Fi 網路名稱
const char* password = "Wi-Fi 密碼"; //Wi-Fi 密碼
float humidity, temp_c; //從 DHT11 讀取的值
DHT dht(DHTPIN, DHTTYPE); //啟動 DHT 程式庫
void setup(){
Serial.begin(115200);
dht.begin();
// Connect to Wi-Fi
// To do.
Serial.println(LINE.getVersion()); //顯示 Line 版本
LINE.setToken(LINE_TOKEN); //設定要傳送至的聊天室 Token
}
void loop(){
//讀取環境溫濕度
temp_c = dht.readTemperature(); //讀取溫度(攝氏)
//To do.
//傳送至 Token 的聊天室
//To do.
}
```
* 結果:

# **3. ESP32 網頁應用**
## Http 介紹
超文字傳輸通訊協定 (HTTP) 是針對在網路上傳送內容而設計的通訊協定。 HTTP 是簡單的通訊協定,利用可靠的傳輸控制通訊協定 (TCP) 服務來執行其內容傳輸功能。 因此,HTTP 是高度可靠的內容傳輸通訊協定。 HTTP 是其中一個最常使用的應用程式協定。 Web 上的所有作業都會使用 HTTP 通訊協定。

使用者會發出 HTTP Request 跟伺服器索取資料,伺服器會回傳 HTTP Response 告知使用者網頁讀取的狀態
* HTTP Resuest Headers 儲存每個 HTTP Request 的核心資訊,例如:版本、語言、網址、編碼方式
* HTTP Resuest Method 告知伺服器要執行的動作,有 8 種動作可執行:
* OPTIONS
* GET:期望返傳回資訊(通常以網站的形式)
* HEAD
* POST:表示用戶端正在向伺服器提交資訊(例如表單資訊,如提交的使用者名稱和密碼)。
* PUT
* DELETE
* TRACE
* CONNECT
最常使用的是 GET、POST
* HTTP Response Status 告知 HTTP Resuest 是否完成的狀態代碼
* 1xx 資訊內容
* 2xx 成功
* 3xx 重新導向
* 4xx 用戶端錯誤
* 5xx 伺服器錯誤
## Lab 2-1:
### ESP32 Wi-Fi Server
* 實驗目的:學習使用 ESP32 建立 Wi-Fi server
* 實驗目標:使用 DHT11 讀取環境溫濕度,並使用 ESP32 建立 server,使其他內網裝置的瀏覽器可觀看溫濕度資訊。
* 接線圖:

* **DHT11 與其他感測器相比較為脆弱,若 VCC、GND 接反很快就會燒毀,在接線上必須特別注意**
* 使用 Library
* WiFi.h
* ESPAsyncWebServer.h
* 不需要更新整個網頁就對局部數值更新
* 避免網頁收發延遲造成卡死
* 可以同時處理多個連線的收發
* 因為是非同步伺服器,連線部分不寫在 loop 裡,而是獨立運作的執行緒
* 不可使用讓整個程式停止運作的函數,例如:delay(),若要間隔一段時間要使用 millis()
* 一個request只會有一個response
* [程式庫連結](https://github.com/me-no-dev/ESPAsyncWebServer)
* AsyncTCP
* 配合 ESPAsyncWebServer.h 使用
* [程式庫連結](https://github.com/me-no-dev/AsyncTCP)
* DHT.h
* 程式碼:
<font color="Red">下方僅給予溫度的程式碼,需自行加入濕度的程式碼</font>
```c++=
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <DHT.h>
// 定義 DHT 資料傳輸的腳位
#define DHTPIN 23
// 選擇使用的 DHT 溫溼度計種類
#define DHTTYPE DHT11 // DHT 11
//#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
//#define DHTTYPE DHT21 // DHT 21 (AM2301)
DHT dht(DHTPIN, DHTTYPE); // 啟動 DHT 程式庫
const char* ssid = "Wi-Fi 網路名稱"; // Wi-Fi 網路名稱
const char* password = "Wi-Fi 密碼"; // Wi-Fi 密碼
// 初始化溫度與濕度的變數
float t = 0.0;
float h = 0.0;
// 建立一個非同步伺服器在通訊埠 80
AsyncWebServer server(80);
// 紀錄量測間隔時間,因為數值較大,用 unsigned long
unsigned long previousMillis = 0;
// 每 n 秒量測,1000->1秒,2000->2秒
const long interval = 2000;
// 建立網頁
// 網頁大小相對大但是架構不會變,一般存變數的動態記憶體不足
// 要存在儲存程式程序的flash記憶體中,所以呼叫PROGMEM
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--設定字體-->
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h2 { font-size: 3.0rem; }
p { font-size: 3.0rem; }
.units { font-size: 1.2rem; }
.dht-labels{
font-size: 1.5rem;
vertical-align:middle;
padding-bottom: 15px;
}
</style>
</head>
<!--建立網頁內容-->
<body>
<h2>ESP32 DHT11 Server</h2>
<p>
<span class="dht-labels">Temperature</span>
<span id="temperature">%TEMPERATURE%</span>
<sup class="units">°C</sup>
</p>
</body>
<script>
setInterval(function ( ) { <!--將溫度資料傳入伺服器顯示在瀏覽器上-->
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("temperature").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/temperature", true);
xhttp.send();
}, 1000 ) ;
</script>
</html>)rawliteral";
// 抓取最新的溫溼度資料
String processor(const String& var) {
//Serial.println(var);
if (var == "TEMPERATURE") {
return String(t);
}
return String();
}
void setup() {
// Serial port for debugging purposes
Serial.begin(115200);
// Connect to Wi-Fi
// To do.
// 查詢 ESP32 的 local IP 並印出,在瀏覽器輸入此 IP 即可看到畫面
// To do.
server.begin();
// 在伺服器上建立網頁
server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send_P(200, "text/html", index_html, processor);
}); //建立主網頁
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest * request) {
request->send_P(200, "text/plain", String(t).c_str());
}); //建立溫度顯示的網頁
server.begin(); // 啟動伺服器
dht.begin(); // 啟動 DHT 感測器
}
void loop() {
// 建立量測溫溼度的迴圈
// 因為網頁伺服器不能使用 delay 函數,所以用 millis
// millis 用運算的次數來計算時間,更為準確,也不會阻擋程式運作
// delay 函數運作時會完全停止程式運作,使網頁伺服器出錯
//使用 DHT11 讀取環境溫濕度
//使用 millis 做間隔,每 2 秒讀一次
//To do.
t = temperature; // t 為要給 server 的溫度變數,temperature 來自 DHT11 的讀值
Serial.print(F("Temperature: "));
Serial.println(t);
}
}
```
## Lab 2-2:
### ESP32 外網連接
在 Lab2-1 中使用 ESP32 建立的 server 僅能用於內網 (同一個路由器底下的網路),為了使其他不同網域的裝置也能使用到 ESP32 作為處理器的資料,我們這邊會使用到另一種網路協定:MQTT
#### MQTT (Message Queuing Telemetry Transport)
* 以 TCP/IP 架構傳輸,專門用在效能低與網路不佳的環境
* MQTT Broker 作為伺服器,其他裝置作為 client 發送與訂閱訊息
* 資訊的傳輸是通過 topic 管理的。發布者發送資料給 broker 時會寫下資料的 topic,broker 則向訂閱此 topic 的客戶端分發此資料
* 發布者與訂閱者不需知道彼此是誰
* 可跨網域遠端運作

#### QoS (Quality of Service)
* 決定資料傳輸的驗證方式,會影響傳輸效能
| 服務品質 | 傳輸方式 | 說明 |
| -------- | -------- | -------- |
| QoS0 | 最多一次傳輸 | 只負責發送,送後不理 |
| QoS1 | 至少一次傳輸 | 確認資料發送,確認前不停發送 |
| QoS2 | 正好一次傳輸 | 保證資料交付成功,且只發送一次 |
#### Retained Message
* Topic 會保存最新的訊息,確保新連上或重連的訂閱者都能收到最新的訊息
* 使用Library
* WiFi.h
* PubSubClient.h
* 建立 MQTT 連線
* DHT.h
* 程式碼:
<font color="Red">下方僅給予溫度的程式碼,需自行加入濕度的程式碼</font>
```C++=
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#define LED 2 //使用 ESP32 內建 LED
#define DHTPIN 23 // 定義 DHT 資料傳輸的腳位
// 選擇使用的 DHT 溫溼度計種類
#define DHTTYPE DHT11 // DHT 11
//#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
//#define DHTTYPE DHT21 // DHT 21 (AM2301)
DHT dht(DHTPIN, DHTTYPE); // 啟動 DHT 程式庫
// 紀錄量測間隔時間,因為數值較大,用 unsigned long
unsigned long previousMillis = 0;
// 每 n 秒量測,1000->1秒,2000->2秒
const long interval = 2000;
const char* ssid = "Wi-Fi 網路名稱"; // Wi-Fi 網路名稱
const char* password = "Wi-Fi 密碼"; // Wi-Fi 密碼
// 建立 MQTT 連線所需的 client ID,可使用隨機生成
String mqtt_ClientID = "mqttClientID";
// 定義 MQTT 連線時,用來識別 publish/subscribe 資料類別的 topic
// 請依照組別更改groupX
const char* sub_topic = "groupX/esp32mqtt";
const char* pub_led_topic = "groupX/esp32s_led_state"; // 控制 ESP32 上的 LED
const char* pub_init_topic = "groupX/esp32s_is_back"; // 確認 ESP32 與 MQTT broker 已連線
const char* pub_temp_topic = "groupX/esp32s_temp"; // 傳輸溫度
// EMQX MQTT broker 的連線資訊
const char *mqtt_server = "broker.emqx.io"; // broker 所在的網址
const char *mqtt_userName = "emqx"; // EMQX broker 的公用帳號
const char *mqtt_password = "public"; // EMQX broker 的公用密碼
// 初始 Wi-Fi client 與 MQTT client 連線
WiFiClient espClient;
PubSubClient client(espClient);
// 初始化儲存溫度與濕度的變數
#define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];
void setup_wifi() { // 設定 Wi-Fi 連線
delay(10);
// Connect to Wi-Fi
// To do.
randomSeed(micros());
// 連線成功後將連線的 IP 資訊印出
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
//接收 MQTT broker subscribe 來的資訊 (LED 亮暗)
void callback(char *topic, byte *payload, unsigned int length) { // 資訊包含 topic 與 topic 內的資料
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
payload[length] = '\0';
String message = (char *)payload; // 儲存 topic 內的資料 payload 到 message 裡
// 驗證傳來的 topic 是我定義中要求的 topic
// strcmp 會比較()內兩個字串,相同的話回傳 0,不同的話會輸出差幾個字母數,並用正負表示哪邊比較大
if (strcmp(topic, sub_topic) == 0) {
if (message == "off") {
digitalWrite(LED, LOW); // 收到 off 關掉 ESP32 的 LED
client.publish(pub_led_topic, "off");
}
if (message == "on") {
digitalWrite(LED, HIGH); // 收到 on 開啟 ESP32 的 LED
client.publish(pub_led_topic, "on");
}
}
}
void reconnect() {
// 對 MQTT 進行連線建立,若未建立成功會持續在此循環
while (!client.connected()) {
Serial.println("Attempting EMQX MQTT connection...");
// 用亂數確保建立唯一的 ClientID
mqtt_ClientID += String(random(0xffff), HEX);
// 嘗試建立連線
if (client.connect((mqtt_ClientID, mqtt_userName, mqtt_password)))
{
Serial.print(" connected with Client ID: ");
Serial.println(mqtt_ClientID);
// 若建立成功,可在其他裝置訂閱 pub_init_topic 收到 "Hi, I'm online!" 的訊息
client.publish(pub_init_topic, "Hi, I'm online!");
// 重新接收資料
client.subscribe(sub_topic);
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
// 可於此查詢連線失敗原因編碼
// https://github.com/knolleary/pubsubclient/blob/master/src/PubSubClient.h
Serial.println(" try again in 5 seconds");
// 若 MQTT 連線失敗,每 5 秒重試
delay(5000);
}
}
}
void setup() {
dht.begin(); //啟動 DHT 感測器
pinMode(LED, OUTPUT); // 將 LED Pin 腳位設為輸出
digitalWrite(LED, LOW); // 將 LED Pin 腳位設為數位輸出(0V/5V),並預設為低(0V)
Serial.begin(115200);
setup_wifi(); // 進入設定 Wi-Fi 連線的 void function
client.setServer(mqtt_server, 1883); // 設定 MQTT broker 的連線
client.setCallback(callback); // 接收從其他 MQTT client 傳來的資料
}
void loop() {
if (!client.connected()) { // 若 MQTT 連線建立失敗就重新連線
reconnect();
}
client.loop();
//使用 DHT11 讀取環境溫濕度
//使用 millis 做間隔,每 2 秒讀一次
//To do.
if (......) {
// 如果上次時間點到這次時間點已經大於間隔時間,就開始量測
// 將上次的時間記錄更新成這次的時間點儲存
// 使用 DHT11 讀取環境溫濕度
if (......) { // 如果讀取到的溫溼度為空,表示 DHT11 有問題
Serial.println(F("Failed to read from DHT sensor!"));
return;
}
else { // 如果溫溼度正常就publish給MQTT broker
snprintf(msg, MSG_BUFFER_SIZE, "%.1lf°", temperature); //印出溫度
Serial.print("Publish message: ");
Serial.print(msg);
client.publish(pub_temp_topic, msg); // 綁定 topic,發送溫度
}
}
}
```
* 於電腦上開啟 MQTT 方式
Chrome 搜尋 MQTT Lens 並安裝

點選新增 "+"

輸入 MQTT Broker 連線資訊

訂閱存有溫溼度資訊的 topic

* 於 Android 手機上開啟 MQTT 方式
Play 商店搜尋 MQTT Dash 安裝

訂閱存有溫溼度資訊的 topic

訂閱開關 LED 的 topic

## Lab3

* 實驗目的:將 ESP32 與 Hololens MR 眼鏡透過 TCP/IP 連接,並在 MR 眼鏡上玩彈跳球小遊戲。
* 實驗目標:將 ESP32 作為 TCP/IP server,接兩顆按鈕,代表方向鍵左與右,向 client 端輸出 "LXRX" 字串,以 0/1 代表左右按鈕是否被按下,如 "L1R0","L0R1"。
* 遊戲畫面:{%youtube liImljSbvM4%}
```C++=
#include <WiFi.h>
int port = 8888; //Port number
WiFiServer server(port);
//Server connect to WiFi Network
const char *ssid = ""; //Enter your wifi SSID
const char *password = ""; //Enter your wifi Password
void setup()
{
Serial.begin(115200);
Serial.println();
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password); //Connect to wifi
// Wait for connection
Serial.println("Connecting to Wifi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
Serial.print("Open Telnet and connect to IP:");
Serial.print(WiFi.localIP());
Serial.print(" on port ");
Serial.println(port);
}
void loop()
{
/*do something*/
WiFiClient client = server.available();
//Serial.println(sendData.c_str());
if (client) {
if(client.connected())
{
Serial.println("Client Connected");
}
while(client.connected()){
/*do something*/
}
client.stop();
Serial.println("Client disconnected");
}
}
```
# 課後問題 (3 題)
:::info
* **Q1. 請簡述什麼是 TCP/IP?**
* **Q2. 請簡述 HTTP 與 HTTPS 的差別。**
* **Q3. 請簡述什麼是 MQTT?**
**回答請勿直接複製上述講義內容!**
:::
:::success
## 作業繳交格式
**W10結報_第XX組.zip**
壓縮檔裡包含:
1.W(週數)結報.pdf
2.資料夾:W(週數)
Lab1.ino
Lab2.ino
...
:::