第10篇 ESP32 WiFi網路連線抓取即時空氣品質資訊

本章介紹如何使用ESP32內建的WiFi模組連上網路,並上網抓取環保署提供的即時空氣品質等資料


實做說明

本章介紹如何使用ESP32內建的WiFi模組連上網路,並上網抓取即時空氣品質資料顯示在序列埠上,ESP32使用網路模組非常簡單,利用WiFi函式庫連上網路,並用HTTPClient模擬一個瀏覽器,就可以上網抓取網頁資料,不過為了讓讀者了解ESP32 WiFi的運作原理,我們會先從WiFiscan這個範例開始上,WiFiscan就是掃描網路的意思,就像我們手機開啟WiFi功能時,會先列表附近掃描到的網路名稱及訊號強度,WiFiscan就是這樣的功能,我們掃描到網路之後,再來選擇要使用哪個網路上網。

一、WiFiscan網路掃描

在ESP32中使用無線網路要使用到WiFi.h函式庫內的WiFi物件,另外ESP32啟動WiFi之後,可以選擇四種模式WiFi.mode,列表如下。

名稱說明語法
WIFI_STA以工作站(Station)模式啟動,ESP32用來上網讀取資料,此為預設模式WiFi.mode(WIFI_STA);
WIFI_AP以熱點(Access Point)模式啟動,讓其他裝置連入ESP32WiFi.mode(AP);
WIFI_AP_STA混合模式,同時當熱點也當作工作站WiFi.mode(WIFI_AP_STA);
WIFI_OFF關閉網路,可用於網路不正常時,重啟網路WiFi.mode(WIFI_OFF);

大部分的狀況下,比較常用的是WIFI_STA,讓ESP32就像是一台手機,將資料傳到某個資料庫,或者讀取網路的資料,設定好模式後,就啟動WiFi.scanNetworks()掃描附近的無線網路,除了顯示無線網路的名稱SSID之外,也會顯示訊號強度RSSI,RSSI是負數表示,越接近0代表訊號越強,另外就是有設定密碼的則會標示「*」。

WiFiScan網路掃描是內建的範例程式,我們透過功能表/檔案/範例/WiFi/WiFiScan可以找到。

#include "WiFi.h"
void setup(){
    Serial.begin(115200);
    WiFi.mode(WIFI_STA);//設定為STA工作站模式
    WiFi.disconnect();//斷線(初始化的意思)
    delay(100);
    Serial.println("Setup done");
}

void loop(){
    Serial.println("scan start");
    int n = WiFi.scanNetworks();//掃描網路,並將掃描到的網路數量存入n
    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) {
            //顯示無線網路SSID, RSSI, 加密
            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("");
    //等候五秒後,重新掃描
    delay(5000);
}

關鍵語法包括

  1. WiFi.mode(WIFI_STA);//設定為STA工作站模式
  2. WiFi.disconnect();//斷線(初始化的意思)
  3. int n = WiFi.scanNetworks();//掃描網路,並將掃描到的網路數量存入n

掃描結果如下圖,一共掃描到附近的3個網路,其中訊號以1:You(-47)最強、Asus(-82)最弱,而2:You2F後面沒有「*」代表該網路沒有連線密碼,而WiFi的名稱一般稱為SSID(Service Set Identifier:服務識別碼),且有分大寫小,後續連線時要注意大小寫是否輸入正確。

WiFiScan網路掃描結果

二、HTTPClient抓取空氣品質資訊

本節我們將使用ESP32的無線網路讀取公開資訊的PM2.5空氣品質為範例,說明如何讓ESP32抓取網路資料。

上一節掃描網路之後,我們就可以選擇要連上哪一個網路,等連上網路後,就可以使用最簡單的HTTPClient物件讀取網頁資料,所謂的HTTPClient是一個網路物件,可以想像他是一個模擬的瀏覽器,可以讀取網頁的資料,在ESP32中讀
取網頁除了HTTPClient外,還有WiFiClient及WiFiClientSecure,功能稍有差異,後續我們用到時會再詳述。

以本例而言,我們將以連上最強的網路訊號上圖第一個SSID:You,而密碼則是假設已知為ABCD1234,此時連線整個範例的步驟則為

  1. 設定WiFi模式:WiFi.mode(WIFI_STA);
  2. 啟動WiFi連線:WiFi.begin(ssid, password);
  3. 檢查是否連線成功:WiFi.status() != WL_CONNECTED
  4. 如果已經連上網路,則啟動網頁連線:http.begin(“http://環保署空氣品質網址”);
  5. 檢查網頁連線是否正常: httpCode == HTTP_CODE_OK
  6. 如果網頁連線正常,則取得網頁內容:String payload = http.getString();
  7. 將資料顯示在序列監控視窗上。
完整流程圖

不過由於環保署的空氣品質網站已經升級為2.0版本,原本1.0的版本已經關閉,因此如果要取得數據,必須先向環保署註冊,取得API KEY才能讓ESP32讀取資訊。

什麼是API KEY呢?簡單的說就是通關密碼,那為什麼在這裡需要一組密碼呢?由於網路資源有限,如果讓很多人無限制取用資訊的話可能導致伺服器記憶體不足或者網路塞車,以至於真正需要資訊的人無法使用,因此現今一般網路服務都會讓使用者透過申請密碼來管控使用狀況,例如說這次環保署的空氣品質網站就規定一天最多5000次取用,超過次數就會暫時關閉這組讀取權利,避免有心人士藉機濫用。接下來我們來介紹如何申請密碼:

請點選網址:申請API key 環保署 https://data.epa.gov.tw/api-term

1. 進入後拉到最下方,勾選我已閱讀,並點選下一步

2. 輸入個人相關資訊後,點選建立帳號

3. 接著到上述註冊時填寫的信箱內就可以看到API KEY了,下面我們就利用這個API KEY來取得空氣品質資訊。

接下來就可以來撰寫程式,程式部份請參考下面內容,其中我這個範例是挑選取得高雄的「橋頭」站所為範例,讀者若需要使用其他地方的城市資訊,可以自行修改。

#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.println("連線完成");
}

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 秒
}

若程式執行沒有問題,就可以在序列埠看到以下的內容:

~中間省略一部分~

最上面的…….是連線WIFI網路過程,如果這部份一直…..,而沒有出現任何的訊息,可以參考本文來除錯:ESP32網路連線問題

如果連線正常,就會出現一連串的訊息,不過這個訊息好像沒有編排,看起來雜亂無章非常難以閱讀,此格式為JSON(JavaScript Object Notation,JavaScript物件表示法),是目前網路最流行的資料交換格式,不過要如何讓ESP32自動找到空氣品質的資訊,則必須要再透過JSON解析工具,來分析得到的內容數據。

三、JSON資料解析

JSON格式是一種資料結構,主要為{“標題1″:”內容1”, “標題2″:”內容2”}這樣的方式組成,如以資料庫的觀點來說明,每一個大括弧{ }代表一個紀錄,而內容則依序寫在{ }內部,以分號「:」區隔欄位名稱及內容。

舉例來說,以上表取得的資料第一筆「基隆」來說,若要獲得PM2.5的資料,則是尋找”PM2.5″:”19″這個欄位,可以獲得目前基隆的PM2.5為19,同理也可以從上表得到桃園市PM2.5為31。

JSON可分成以下兩大種類JSON物件「{ }」與JSON陣列「[ ]」,兩者又可以互相組合,因此結構比資料庫欄位形式較為複雜,先說明兩者的差異。

JSON物件結構說明

1. JSON物件(object):以大括號{}起訖

每一個欄位都會先標示欄位名稱,在用「:」標示該欄位內容,不同欄位之間用「,」做分隔。

JSON物件是JSON基本形式:JObject={“欄位1″:”內容1”, “欄位2″:”內容2”}

例如我們要表示同學Jake的數學65分,歷史85分
可以這樣寫

{"name":" Jake ", "Math":"65","History":"85"}

而多個JSON物件再組成一個JSON物件或陣列,例如我們可以把上面的內容改成下面這樣

JObject={"name":" Jake ", "grade":{"Math":"65","History":"85"}}

也就是說,同學Jake成績單裡面有數學65分,及歷史85分,其意義是相同的,我們只是把兩個成績欄位塞入一個成績單物件中,在成績單物件中分成兩個欄位是數學與歷史。

此時要拿到Jake的數學成績時,我們用JObject[“grade”][“math”]就可以取得數學成績。

如果我們需要再把Mary的成績一起顯示,則必須再多一個JSON物件,因此我們可以改用下面的JSON陣列來表示。

2. JSON陣列(array):以中括號[]起訖

此種就是C語言裡的陣列表示法,因此稱為JSON陣列。其格式大致如下
[{Json物件1}, { Json物件2}, { Json物件3}, { Json物件4}]


一個JSON陣列可以包含多個JSON物件,每個物件之間以「,」區隔,以上一個例子來說
假設班上只有兩位學生,則可用這樣的方式來表示

JArray= [{"name":" Jake ", "grade":{"Math":"65","History":"85"} , {"name":" Mary ", "grade":{"Math":"84","History":"32"}}]

此時要拿到Jake的數學成績時,就用JArray [0][“math”]

我們可以發現Array與Object取值時Array用的是[數字],代表第幾個元素,而Object則是用[欄位]。此外我們也可以發現PM2.5網站提供的內容為中括號[]開頭及結尾,因此我們將用會Array的方式進行解析。

了解格式後,我們在Arduino IDE中安裝AduinoJson函式庫以解析資料內容,首先點選功能表/草稿碼/匯入函式庫/管理函式庫,在跳出的程式庫管理元中,輸入關鍵字JSON即會出現我們需要的函式庫AduinoJson,請注意要認清作者為Benoit Blanchon的這一個函式庫。

安裝AduinoJson函式庫

完成安裝後,我們可以將程式上方引用ArdunioJson函式庫,並對取得的網頁內容Payload進行JSON解析,其解析的語法為:

    //JSON格式解析
    DynamicJsonDocument AQJarray(payload.length()*2); 
    deserializeJson(AQJarray, payload);//解析payload為JSON Array格式
    String AQI = AQJarray[0]["records"][0]["aqi"]; //獲得橋頭AQI資訊
    Serial.println(Area + " AQI:" + AQI); //在序列埠列印出來

ArdunioJson函式庫說明

1. DynamicJsonDocument Json變數(size);

是向系統要一個名為「AQJarray」的空間存放JSON,至於空間要多大,比較保險的作法是字串長度x2或x4,本例選擇payload.length()*2。

2. deserializeJson(Json變數,待解析之JSON字串);

是將「待解析之JSON字串」完成解析後,將結果放在「AQJarray」,本例「待解析之JSON字串」就是自網路取得的空氣品質字串payload,完成解析後的JSON變數為AQJarray。

3. String AQI = AQJarray[“records”][0][“aqi”]; //獲得橋頭AQI資訊

此時需要任何資料,就可以透過Json變數[“records”][0][“aqi”],來取得空氣品質數值。以下詳細說明:

1. [“records”]:此為我們要取得這筆JSON內的records欄位內的所有資料

2. [0]:由於本資料只有一筆,因此橋頭屬於第0筆,這裡選擇使用[0]取得所有橋頭的資料

3. [“aqi”]:我們再從所有橋頭的資料內,取得aqi這個欄位的數值,因此輸入[“aqi”]

最後完成的成果如下:

得到的AQI結果

以下為完整的程式碼

#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.println("連線完成");
}

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 秒
}

延伸閱讀

???? 第十一篇 ESP32 資料庫存取MySQL連線

2 Comments

  1. 去年暑假時尤老師有釋出一個版本是採用WiFiClientSecure的連線,所以採用HTTPClient,也可以連線成功?

Leave a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *