# 第14週- IoT WiFi
###### tags: `WiFi` `IoT` `遠距控制`
了解各類 IoT 無線網路、網路架構基礎知識,並練習使用 ESP32 WiFi 連網功能,下載資料、上傳控制,或充當網路伺服器管理資源。
---
## IoT 網路遠距控制的方式
### IoT 無線網路一覽表:

**1) LPWAN (NB-IoT, LTE-M)**
* ESP32 需加裝專用發射接收模組,插 SIM 卡
* 直接接入現有的 LTE 網路,重覆利用現有移動通訊基地台和設備
* 電信付費頻段,可深度休眠
* 傳輸頻寬,NB-IoT:~100kbps;LTE-M:~1Mbps
* 傳輸距離:10KM 以上, 唯 LTE-M 可漫遊
* 最貴
* 需額外月租費

**2) LoRa**
* ESP32 需加裝專用發射接收模組
* 需要設置節點、路由器
* ISM頻段 433/868/915MHz
* 傳輸頻寬為 0.3K~50kbps
* 傳輸距離:依電波穿透率 2KM ~ 15KM 以上
* 比較便宜

**3) WiFi網路**
* ESP32 已內含WiFi硬體,並具有天線
* 傳輸頻寬為 1-11Mbps(2.4GHz)/1-500Mbps(5GHz)
* 工作功率較高,使用 ISM 頻段中的 2.4G/5GHz 雙頻
* 價位界於中間
* 可以當成是工作站或熱點、或兩者同時,構成 IoT 普通採用的行動隨意網路 (MANET, Mobile ad‐hoc network) 或無線隨意網路 (WANET, Wireless ad‐hoc network)
* 可以證書方式認證並加密

---
## WiFi.h 函式庫有四種無線模式:

| 模式 | 語法指令 |
| -------- | -------- |
| 工作站| WiFi.mode(WIFI_STA); |
| 熱點| WiFi.mode(WIFI_AP); |
| 熱點+工作站| WiFi.mode(WIFI_AP_STA); |
| 關閉網路功能| WiFi.mode(WIFI_OFF); |
### ESP32 WiFi 實作一:
啟動 ESP32 WiFi 功能,工作站模式
>1. 需 WiFi AP,供 ESP32 掃描
>1. 依課本 p.107,上傳程式
:::warning
hint:
>1. 程式先包含 library:
>#include <WiFi.h>
>1. 宣告為網路模式為 WiFi.mode()
>1. 斷線、初始化為 WiFi.disconnect()
>1. 宣告整數 n 為掃描的網路數量 int n = WiFi.scanNetworks()
>1. 取得第 i 個網路的 SSID : WiFi.SSID(i)
>1. 取得第 i 個網路的訊號強度:WiFi.RSSI(i)
>1. 取得第 i 個網路的加密方式:WiFi.encryptionType(i)
:::spoiler
```javascript=
#include "WiFi.h"
void setup() {
Serial.begin(115200);
// 設定網路模式為STA工作站模式
WiFi.mode(WIFI_STA);
/*
括弧內可以輸入四種值,分別是:
1.WIFI_OFF (關閉WiFi)
2.WIFI_STA (無限終端)
3.WIFI_AP (wifi分享)
4.WIFI_AP_STA
*/
WiFi.disconnect();
/*
斷線,在 setup()裏為初始化的意思
*/
delay(100);
Serial.println("Setup done");
}
void loop() {
Serial.println("scan start");
//開始掃描附近網路
int n = WiFi.scanNetworks();
Serial.println("scan done");
if (n == 0) {
Serial.println("no networks found");
} else {
Serial.print(n);
Serial.println(" networks found");
//開始印出所有網路
for (int i = 0; i < n; ++i) {
// Print SSID and RSSI for each network found
Serial.print(i + 1);
Serial.print(": ");
Serial.print(WiFi.SSID(i));
Serial.print(" (");
Serial.print(WiFi.RSSI(i));
Serial.print(")");
Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*");
delay(10);
}
}
Serial.println("");
//暫停5秒
delay(5000);
}
```
:::
:::success
觀察:當開啟 Serial Monitor terminal 後,查看掃描結果。
:::
---
## ESP32 接入 WiFi 和 http 網頁流程:
### 接入 WiFi 流程:
| 動作 | 語法指令 |
| -------- | -------- |
| 模式宣告| WiFi.mode(WIFI_?); |
| 初始化| WiFi.begin(ssid,password); |
| | WiFi.begin(ssid, keyindex, key); |
| 檢查狀態| WiFi.status()==WL_CONNECTED; |
| 關閉網路功能| WiFi.mode(WIFI_OFF); |
### 接入 http 流程:
| 動作 | 語法指令 |
| -------- | -------- |
| 宣告物件| HTTPClient http; |
| 初始化| http.begin(url); |
| | url = "http://網址" |
| 檢查狀態| httpCode==HTTP_CODE_OK; |
| 取得網頁內容| String payload=http.getString();
| 關閉網頁連線| http.end(); |
### 實作二:
ESP32 連網,並讀取網頁資料
>1. 依課本 p.110,上傳程式
:::warning
hint:
>1. 啟動 WiFi 連線指令為 WiFi.begin(ssid, password),
>1. 檢查 WiFi 連線狀態為 WiFi.status()==WL_CONNECTED
>2. 網頁連線為 http.begin(URL),
>3. 讀取網頁內容為 http.getString()
:::spoiler
```javascript=
#include <WiFi.h>
#include <HTTPClient.h>
char ssid[] = "你的SSID"; //請修改為您連線的網路名稱
char password[] = "你的密碼"; //請修改為您連線的網路密碼
//申請API key 環保署: https://data.epa.gov.tw/api-term
//查看空氣品質列表:https://data.epa.gov.tw/api/v2/aqx_p_432?offset=0&format=json&api_key=你的APIkey
String APIkey = "6b143ef9-f**************557c7ffa1c"; //你的環保署網站 API Key
String Area = "橋頭"; //希望取得空氣品質的地點
String url = "https://data.epa.gov.tw/api/v2/aqx_p_432?format=json&limit=5&api_key=" + APIkey + "&filters=SiteName,EQ," + Area ; //PM2.5的網址
void setup() {
Serial.begin(115200);
Serial.print(" 開始連線到無線網路 SSID:");
Serial.println(ssid);
// 1. 設定 WiFi 模式
WiFi.mode(WIFI_STA);
// 2. 啟動 WiFi 連線
WiFi.begin(ssid, password);
//3.檢查連線狀態
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("連線完成 IP =");
Serial.println(WiFi.localIP());
}
void loop() {
//4.啟動網頁連線
HTTPClient http; // 宣告 http 物件
//http.setInsecure(); // 避免 SSL 問題
http.begin(url); // 設定網址
int httpCode = http.GET(); // 取得資料
//5.檢查網頁連線是否正常
if (httpCode == HTTP_CODE_OK) { // 如果取得資料成功
//6.取得網頁內容
String payload = http.getString(); // 取得資料內容
//7.將資料顯示在螢幕上
Serial.println(payload);
}
else { // 如果取得資料失敗
Serial.println("Error on HTTP request");
}
//8.關閉網頁連線
http.end(); // 結束 http 連線
delay(10000); // 延遲 10 秒
}
```
:::
:::info
[參考~[環保署API使用說明]~](https://data.epa.gov.tw/paradigm)
有關下載政府 epa 開放資料平台的資料,步驟如下:
1)請自行上網 API 介接服申請:
https://data.epa.gov.tw/api-term
2)從登錄的 email,下載自己的 API key
3)政府 epa 開放資料平台 空氣品質指標(AQI)的網址
https://data.epa.gov.tw/api/v2/aqx_p_432?format=json&limit=5&api_key=您的API密鑰
其中 您的API密鑰 就是你的 API key
:::
:::success
觀察:在 terminal 檢視顯示的網頁資料格式為何?哪些是需要的資料?
:::
---
### 實作三:
解析 JSON 網頁資料格式
>1. 依課本 p.113 安裝 ArduinoJson 函式庫
>1. 依課本 p.115,上傳程式
:::warning
hint:
>1. 理解 JSON object 以及 JSON array 的不同
>1. 宣告 Json 變數+大小 為 DynamicJsonDocument Variable(size)
>1. 解析 Json 文件為 deserializeJson(xx,string)
:::spoiler
```javascript=
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>//請先安裝ArduinoJson程式庫
char ssid[] = "你的SSID"; //請修改為您連線的網路名稱
char password[] = "你的密碼"; //請修改為您連線的網路密碼
//申請API key 環保署: https://data.epa.gov.tw/api-term
//查看空氣品質列表:https://data.epa.gov.tw/api/v2/aqx_p_432?offset=0&format=json&api_key=你的APIkey
String APIkey = "6b143ef9-f**************557c7ffa1c"; //你的環保署網站 API Key
String Area = "橋頭"; //希望取得空氣品質的地點
String url = "https://data.epa.gov.tw/api/v2/aqx_p_432?format=json&limit=5&api_key=" + APIkey + "&filters=SiteName,EQ," + Area ; //PM2.5的網址
void setup() {
Serial.begin(115200);
Serial.print(" 開始連線到無線網路 SSID:");
Serial.println(ssid);
// 1. 設定 WiFi 模式
WiFi.mode(WIFI_STA);
// 2. 啟動 WiFi 連線
WiFi.begin(ssid, password);
//3.檢查連線狀態
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("連線完成 IP =");
Serial.println(WiFi.localIP());
}
void loop() {
//4.啟動網頁連線
HTTPClient http; // 宣告 http 物件
//http.setInsecure(); // 避免 SSL 問題
http.begin(url); // 設定網址
int httpCode = http.GET(); // 取得資料
//5.檢查網頁連線是否正常
if (httpCode == HTTP_CODE_OK) { // 如果取得資料成功
//6.取得網頁內容
String payload = http.getString(); // 取得資料內容
//7.將資料顯示在螢幕上
Serial.println(payload);
//JSON格式解析
//payload = "[" + payload + "]"; //將資料轉為JSON 陣列格式
DynamicJsonDocument AQJarray(payload.length() * 2);
deserializeJson(AQJarray, payload);//解析payload為JSON Array格式
String AQI = AQJarray["records"][0]["aqi"];
Serial.println(Area + " AQI:" + AQI);
}
else { // 如果取得資料失敗
Serial.println("Error on HTTP request");
}
//8.關閉網頁連線
http.end(); // 結束 http 連線
delay(10000); // 延遲 10 秒
}
```
:::
:::success
:wink: 延伸:在 terminal 顯示的資料有哪些?可以試試不同地點、資料種類。
:::
---
## 多重 SSID AP 環境,選擇最強訊號連網:
> 1. 當位處於多個 WiFi AP 的環境下,可以掃描周遭 AP 訊號。
> 2. 利用 WiFiMulti 功能,選擇有預設連結帳密的最強 SSID AP 連接。
### 接入多重 WiFi 流程:
| 動作 | 語法指令 |
| -------- | -------- |
| 宣告物件 wifiMulti|WiFiMulti wifiMulti;|
| 模式宣告| WiFi.mode(WIFI_?); |
| 增加 AP 帳密清單| wifiMulti.addAP("SSID_1", "password_1");|
| 連線並檢查狀態| wifiMulti.run()==WL_CONNECTED |
| 失去連線檢查 | wifiMulti.run(connectTimeoutMs) == WL_CONNECTED |
### 實作四:
:::warning
hint:
>1. 程式先包含 library:
>#include <WiFi.h>
>#include <WiFiMulti.h>
>1. 宣告物件 WiFiMulti wifiMulti;
>1. 設定每一個 AP 的嘗試連接時間(ms)
>const uint32_t connectTimeoutMs = 10000;
>1. 宣告為網路模式為 WiFi.mode(WIFI_STA)
>1. 增加 AP 帳密清單
>wifiMulti.addAP("SSID_1", "password_1");
>1. 連線並檢查狀態為
>wifiMulti.run()==WL_CONNECTED;
>1. 宣告整數 n 為掃描的網路數量
>int n = WiFi.scanNetworks();
>1. 取得第 i 個網路的 SSID : WiFi.SSID(i);
>1. 取得第 i 個網路的訊號強度:WiFi.RSSI(i);
>1. 取得第 i 個網路的加密方式:WiFi.encryptionType(i);
>1. 失去連線檢查:
>wifiMulti.run(connectTimeoutMs) == WL_CONNECTED
:::spoiler
```javascript=
#include <WiFi.h>
#include <WiFiMulti.h>
WiFiMulti wifiMulti;
// 設定每一個 AP 的嘗試連接時間(ms)
const uint32_t connectTimeoutMs = 10000;
void setup(){
Serial.begin(115200);
delay(10);
// 設定網路模式為STA工作站模式
WiFi.mode(WIFI_STA);
// 增加 AP 帳密清單
wifiMulti.addAP("ssid_1", "password_1");
wifiMulti.addAP("ssid_2", "password_2");
wifiMulti.addAP("ssid_3", "password_3");
// //開始掃描附近網路
int n = WiFi.scanNetworks();
Serial.println("scan done");
if (n == 0) {
Serial.println("no networks found");
}
else {
Serial.print(n);
Serial.println(" networks found");
for (int i = 0; i < n; ++i) {
//開始印出所有網路
Serial.print(i + 1);
Serial.print(": ");
Serial.print(WiFi.SSID(i));
Serial.print(" (");
Serial.print(WiFi.RSSI(i));
Serial.print(")");
Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
delay(10);
}
}
// 以 wifiMulti 連訊號最強的 SSID AP
Serial.println("Connecting Wifi...");
if(wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
}
void loop(){
//如果失去最強的 SSID 連網,則連下一個 AP 清單
if (wifiMulti.run(connectTimeoutMs) == WL_CONNECTED) {
Serial.print("WiFi connected: ");
Serial.print(WiFi.SSID());
Serial.print(" ");
Serial.println(WiFi.RSSI());
}
else {
Serial.println("WiFi not connected!");
}
delay(1000);
}
```
:::
:::success
觀察:當開啟 Serial Monitor terminal 後,查看掃描以及連線結果。
:::
---
## ESP32 接入 WiFi 傳輸後,在C/S架構下,有兩種身份可以選擇:

> 1. 當 client,要求 server 提供服務
> 2. 當 server,提供 client 連線服務
| 身份 | 動作 | 語法指令 |
| -------- | -------- | -------- |
| client (客戶端) | 宣告物件 |WiFiClient client;|
| |欲連接Server 之 ip 和 port| client.connect(ip,port);|
| |client 停止連線 | client.stop();|
| server (伺服端) | 宣告物件 |WiFi.server Server(port);|
|| server 開始提供連線 | Server.begin(); |
|| 宣告客戶端連線變數,並 listen | WiFiClient client = Server.available(); |
|| 檢查 client 連線狀態 | if (client) {}; |
| |client 停止連線 | client.stop();|
|| server 停止連線服務 | Server.stop(); |
### 實作五:
ESP32 以 WiFi工作站 + Server身份,建立有 HTTP 網頁的 Web Server 連線服務

>1. 將綠 LED 連接至 GPIO 15 位置
>1. 依課本 p.173,上傳程式
>1. 在 Serial terminal 取得 WebServer 的 IP address,並在相同網域電腦的 browser 訪問此 IP
:::warning
hint:
>1. 開啟 server 的 http 網頁通訊特定埠 80: WiFiServer server(80)
>1. ESP32 (server) WiFi 上網後的 IP address 為 WiFi.localIP();
>1. 宣告連線 WiFiClient client = server.available();
>初始值為"0",無 client 在線等待
>1. 檢查客戶端 client 是否連線 if (client) {};
>是否與上週學的 while(Serial.available()>0){} 和 while(BT.availabe()>0){} 有相似之處?
>1. 在網頁列印 client.println("xxxx");
>是否與上週學的 Serial.println("xxxx"); 和 BT.print("xxxx"); 有相似之處?
>1. 在網頁讀取 client.readStringUntil('\r');
>是否與上週學的 Serial.readStringUntil('\n'); 和 BT.readString(); 有相似之處?
>1. 當網頁超聯結的「開啟綠色」字串被點擊時,client.readStringUntil('\r')會讀取字串 "GET/Gled=ON HTTP/1.1"
>1. 再以 字串變數.indexOf("蒐尋字串")>=0 去做判斷蒐尋到的起始索引[從0開始,沒有發現時為 -1]
:::spoiler
```javascript=
#include <WiFi.h>
//請修改以下參數--------------------------------------------
char ssid[] = "SSID";
char password[] = "SSIDpassword";
int Gled = 15; //宣告綠色Led在 GPIO 15
WiFiServer server(80); //宣告伺服器位在80 port
void setup()
{
Serial.begin(115200);
Serial.print("開始連線到無線網路SSID:");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.println("連線完成");
server.begin();
Serial.print("伺服器已啟動,http://");
Serial.println(WiFi.localIP());
pinMode(Gled, OUTPUT);
}
void loop()
{
//宣告一個連線
WiFiClient client = server.available();
if (client) {
//有人連入時
Serial.println("使用者連入");
//-------------網頁的html部份開始--------------
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("");
client.println("<!DOCTYPE HTML>");
client.println("<html><head><meta charset='utf-8'></head>");
client.println("<br>");
client.println("<h1>ESP32 Web Server</h1>");
//HTML超連結指令
client.println("<a href='/Gled=ON'>開啟綠色LED</a><br>");
client.println("<a href='/Gled=OFF'>關閉綠色LED</a><br>");
client.println("</html>");
//-------------網頁的html部份結束--------------
//取得使用者輸入的網址
String request = client.readStringUntil('\r');
Serial.println(request);
//判斷超連結指令
//網址內包含Gled=ON,就開啟綠燈,如果Gled=OFF,關閉綠燈
if (request.indexOf("Gled=ON") >= 0) { digitalWrite(Gled, HIGH); }
if (request.indexOf("Gled=OFF") >= 0) { digitalWrite(Gled, LOW); }
Serial.println("完成");
client.stop();//停止連線
}
}
```
:::
:::success
觀察:點選網頁燈號超聯結,查看燈號變化以及Serial Monitor的輸出
:::
:::info
## :wink:延伸補充
>1. ESP32 以 WiFi AP + Server 身份,建立有 HTTP 網頁的 Web Server 連線服務時,有何不同?

直接取得 ESP32 管理的資源(webServer背後的一顆LED燈而已),也同在一個網域內。
hint:
const char* ssidAP = "xxxx"; //Esp32s基地台名稱
const char* passwordAP = "xxxxxxxx";//Esp32s基地台密碼(至少要8碼)
WiFi.softAP(ssidAP, passwordAP);
delay(500);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
server.begin();
[參考~[Esp32s用AP_mode+LED]~](https://ithelp.ithome.com.tw/articles/10262962)
>2. 無線 AP 的差異:
>WiFi天線的發射功率最高限制在 100mW(20dBm),手機則是 200mW(23dBm),所以手機天線有指向性 。






[參考~[Wi-Fi分享器的選購要點]~](https://my-best.tw/111)
:::
:::info
## :bulb:家中的無線AP速度怎麼算?
>1. 先了解802.11協定的規格:
| 802.11協定 | 頻段GHz | 最高Mbps | MIMO支援 |
| -------- | --------|--------|-------- |
| - | 2.4 | 2 | N/A |
| a | 5 | 54 | N/A |
| b | 2.4 | 11 | N/A |
| g | 2.4 | 54 | N/A |
| n | 2.4/5 | 150 | 4, SU |
| ac | 5 | 433.3 |8, DL MU |
| ax | 2.4/5 | 600 |8, UL/DL MU|
>2. 以 ac2100 規格:
>屬於雙頻、 2x空間串流 802.11n + 4x 空間串流 802.11ac 產品,150Mx2 + 433Mx4 ≈ 2100M
[參考~[你家的无线路由器速率咋算?]~](https://zhuanlan.zhihu.com/p/432434810)
:::
### 實作六:
把實作四放大三倍,使用 3 顆 LED 燈
>1. 將綠、黃、紅 LED 分別連接至GPIO 15、2、4 位置
>1. 依課本 p.177,上傳程式
:::spoiler
```javascript=
#include <WiFi.h>
//請修改以下參數--------------------------------------------
char ssid[] = "SSID";
char password[] = "SSIDpassword";
int Gled = 15; //宣告綠色Led在 GPIO 15
int Yled = 2; //宣告黃色Led在 GPIO 2
int Rled = 4; //宣告紅色Led在 GPIO 4
WiFiServer server(80); //宣告伺服器位在80 port
void setup()
{
Serial.begin(115200);
Serial.print("開始連線到無線網路SSID:");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.println("連線完成");
server.begin();
Serial.print("伺服器已啟動,http://");
Serial.println(WiFi.localIP());
pinMode(Gled, OUTPUT);
pinMode(Yled, OUTPUT);
pinMode(Rled, OUTPUT);
}
void loop()
{
//宣告一個連線
WiFiClient client = server.available();
if (client) {
//有人連入時
Serial.println("使用者連入");
//-------------網頁的html部份開始--------------
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("");
client.println("<!DOCTYPE HTML>");
client.println("<html><head><meta charset='utf-8'></head>");
client.println("<br>");
client.println("<h1>ESP32 Web Server</h1>");
//HTML超連結指令
client.println("<a href='/Gled=ON'>開啟綠色LED</a><br>");
client.println("<a href='/Gled=OFF'>關閉綠色LED</a><br>");
client.println("<a href='/Yled=ON'>開啟黃色LED</a><br>");
client.println("<a href='/Yled=OFF'>關閉黃色LED</a><br>");
client.println("<a href='/Rled=ON'>開啟紅色LED</a><br>");
client.println("<a href='/Rled=OFF'>關閉紅色LED</a><br>");
client.println("</html>");
//-------------網頁的html部份結束--------------
//取得使用者輸入的網址
String request = client.readStringUntil('\r');
Serial.println(request);
//判斷超連結指令
//網址內包含Gled=ON,就開啟綠燈,如果Gled=OFF,關閉綠燈
if (request.indexOf("Gled=ON") >= 0) { digitalWrite(Gled, HIGH); }
if (request.indexOf("Gled=OFF") >= 0) { digitalWrite(Gled, LOW); }
if (request.indexOf("Yled=ON") >= 0) { digitalWrite(Yled, HIGH); }
if (request.indexOf("Yled=OFF") >= 0) { digitalWrite(Yled, LOW); }
if (request.indexOf("Rled=ON") >= 0) { digitalWrite(Rled, HIGH); }
if (request.indexOf("Rled=OFF") >= 0) { digitalWrite(Rled, LOW); }
Serial.println("完成");
client.stop();//停止連線
}
}
```
:::
:::success
觀察:點選網頁燈號超聯結,查看燈號變化以及Serial Monitor的輸出
:::
:::info
1. 現在可以在同一網域,上網遠距手動控制燈號了。
1. 若可以破除不同網域的限制,像不像外出時可以遠端遙控家中冷氣的雛形了呢?
:::
---
### 實作七:
請依課本 P.181,加上繼電器實作,體驗遠端控制電風扇開閉。
:::warning
:warning: 注意 110V 的用電安全!
:::
## :bulb: 挑戰整合題:
合併上週所學 BT,完成可由 BT 和 WebPage 可雙向對 ESP32 傳送指令,控制三顆 LED 明滅
>1. 將綠、黃、紅 LED 分別連接至GPIO 15、2、4 位置
>1. 上傳程式
>1. App 連接 ESP32 藍牙裝置
>1. 對手機 App 麥克風說話
:::success
觀察:當語音指令正確接受後,查看燈號是否會變化
:::spoiler
### 思考題 程式碼(一)
```javascript=
#include <BluetoothSerial.h>
#include <WiFi.h>
BluetoothSerial BT;
WiFiServer server(80); //宣告伺服器位在80 port
char ssid[] = "SSID"; //請修改為您連線的網路名稱
char password[] = "SSIDpassword"; //請修改為您連線的網路密碼
void setup() {
Serial.begin(115200);
Serial.println("");
Serial.print("Attempting to connect to WiFi AP ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("WiFi AP is connected!");
server.begin();
Serial.print("Web Server is activated. Please visit at http://");
Serial.println(WiFi.localIP());
BT.begin("Richard6854"); //請改名
pinMode(15, OUTPUT); //綠LED
pinMode(2, OUTPUT); //黃LED
pinMode(4, OUTPUT); //紅LED
}
void loop() {
//宣告一個連線
WiFiClient client = server.available();
if (client) {
Serial.println("One visitor has logged in.");
//---------網頁html開始-------
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
// client.println("Refresh: 10");
client.println("");
client.println("<!DOCTYPE HTML>");
client.println("<html><head><meta charset='utf-8'></head>");
client.println("<br>");
client.println("<h1>Web Server LED control console</h1>");
client.println("");
//HTML超連結指令
client.println("<a href='/Gled=ON'>開啟綠色LED</a><br>");
client.println("<a href='/Gled=OFF'>關閉綠色LED</a><br>");
client.println("<a href='/Yled=ON'>開啟黃色LED</a><br>");
client.println("<a href='/Yled=OFF'>關閉黃色LED</a><br>");
client.println("<a href='/Rled=ON'>開啟紅色LED</a><br>");
client.println("<a href='/Rled=OFF'>關閉紅色LED</a><br>");
client.println("</html>");
//-------------網頁的html部份結束--------------
//取得使用者輸入的網址
String request = client.readStringUntil('\r');
client.flush();
Serial.println(request);
//判斷超連結指令
//網址內包含Gled=ON,就開啟綠燈,如果Gled=OFF,關閉綠燈
if (request.indexOf("Gled=ON") >= 0) { digitalWrite(15, HIGH); }
if (request.indexOf("Gled=OFF") >= 0) { digitalWrite(15, LOW); }
if (request.indexOf("Yled=ON") >= 0) { digitalWrite(2, HIGH); }
if (request.indexOf("Yled=OFF") >= 0) { digitalWrite(2, LOW); }
if (request.indexOf("Rled=ON") >= 0) { digitalWrite(4, HIGH); }
if (request.indexOf("Rled=OFF") >= 0) { digitalWrite(4, LOW); }
client.flush();
Serial.println("Client disconnected");
client.stop();//停止連線
}
while (Serial.available()) {
//讀取序列資列
String Sdata = Serial.readStringUntil('\n');
//傳輸給藍牙
BT.println(Sdata);
//顯示在序列視窗
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);}
}
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);
}
```
:::
---
## :wink:延伸補充
除了使用 include <WiFiMulti.h> 外,另外有 WiFiManager 可讓 ESP32 Wifi 功能時,不需在程式裏,寫死SSID/Password:
[參考 ~[ESP32WifiSmartConfig]~](https://github.com/poky/ESP32WifiSmartConfig?fbclid=IwAR1ohemddDLb1BGixc_t1_4as-3zsiH-M_b1jRKqaqrg_eQk7ReErgdg-I0)
>透過 BLE 或 SoftAP 來當 Host 來接收帳密資訊
也可使用內建的 ESP-Touch 可透過 SmartConfig 來達成,把最基本的連線狀態判斷,儲存 NVR,以便重新通電後可直接連網,和如果找不到 SSID 的話會自動切換成beginSmartConfig 模式繼續接收等...
---
:::info
下週教如何破除不同網域的限制,外出時可以實現遠端遙控家中的電器啟閉。
:::
## 參考資料
>1)IOT物聯網應用第八、十二章 – 尤濬哲(夜市小霸王) 編著
>2)[LPWAN發展勢不可擋 LTE-M/NB-IoT並居主流技術2019-06-11 楊偉正](https://www.2cm.com.tw/2cm/zh-tw/tech/30D3FD9B745C4300AA2723720428DDE8)
>3)[LoRaWAN™網路架構](https://www.allion.com.tw/certification/lora/)
>4)[ESP32 WiFiMulti: Connect to the Strongest Wi-Fi Network (from a list of networks)](https://randomnerdtutorials.com/esp32-wifimulti/)
>5)[MANET Network in Internet of Things System WRITTEN BY Rasa Bruzgiene, Lina Narbutaite and Tomas Adomkus](https://www.intechopen.com/chapters/53178)
>6)[ESP32-CAM:为 Web 服务器(Arduino IDE)设置接入点(AP)](https://www.qutaojiao.com/24553.html)
>7)[參考~[Esp32s用AP_mode+LED]~](https://ithelp.ithome.com.tw/articles/10262962)
>8)[參考~[Wi-Fi分享器的選購要點]~](https://my-best.tw/111)
>9)[參考~[你家的无线路由器速率咋算?]~](https://zhuanlan.zhihu.com/p/432434810)
>10)[參考 ~[ESP32WifiSmartConfig]~](https://github.com/poky/ESP32WifiSmartConfig?fbclid=IwAR1ohemddDLb1BGixc_t1_4as-3zsiH-M_b1jRKqaqrg_eQk7ReErgdg-I0)