各位好

由於環保署已經停用AQI 1.0的網址,AQI 2.0的網址則需要申請API KEY,相關流程請參考本人所撰寫的新文章

https://www.nmking.io/index.php/2022/11/21/612/


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

1.WiFiscan網路掃描
2.HTTPClient抓取PM2.5資訊
3.JSON資料解析
4.1602LCD顯示資訊

一、WiFiscan網路掃描

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

名稱

說明

語法

WIFI_STA

以工作站(Station)模式啟動,ESP32用來上網讀取資料,此為預設模式

WiFi.mode(WIFI_STA);

WIFI_AP

以熱點(Access Point)模式啟動,讓其他裝置連入ESP32

WiFi.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:服務識別碼),且有分大寫小,後續連線時要注意大小寫是否輸入正確。
image

二、HTTPClient抓取PM2.5資訊
本節我們將使用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://PM2.5網址");
5.    檢查網頁連線是否正常: httpCode == HTTP_CODE_OK
6.    如果網頁連線正常,則取得網頁內容:String payload = http.getString();
7.    將資料顯示在序列監控視窗上。
上述完整流程圖如下。
image


#include <WiFi.h>
#include <HTTPClient.h>

char ssid[] = "SSID"; //請修改
char password[] = "SSIDpassword"; //請修改
char url[] = "http://opendata2.epa.gov.tw/AQI.json"; //PM2.5的網址

void setup() {
  Serial.begin(115200);
  delay(1000);
  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.啟動網頁連線
  Serial.print("啟動網頁連線");
  HTTPClient http;
  http.begin(url); 
  int httpCode = http.GET();
  Serial.print("httpCode=");
  Serial.println(httpCode);
  //5.檢查網頁連線是否正常
  if (httpCode == HTTP_CODE_OK) {
    //6.取得網頁內容
    String payload = http.getString();
    Serial.print("payload=");
    //7.將資料顯示在螢幕上
    Serial.println(payload);
  }
  http.end();
  delay(10000);
}


image

讀者可以發現其中可能會有多次讀取失敗,而此時回覆的httpcode為-1,推測可能的原因是網路延遲造成。若讀取成功,則會將該網頁所有內容取回,如下圖的啟動網頁連線httpCode=200,代表讀取成功,訊息擷取一段如下:


 payload=[{"SiteName":"基隆", "County":"基隆市", "AQI":"70", "Pollutant":"細懸浮微粒", "Status":"普通", "SO2":"2.6", "CO":"0.35", "CO_8hr":"0.6", "O3":"34", "O3_8hr":"17", "PM10":"52", "PM2.5":"19", "NO2":"16", "NOx":"20", "NO":"4.7", "WindSpeed":"0.8", "WindDirec":"11", "PublishTime":"2020-03-21 10:00", "PM2.5_AVG":"23", "PM10_AVG":"39", "SO2_AVG":"2", "Longitude":"121.760056", "Latitude":"25.129167", "SiteId":"1"},
...略…
{"SiteName":"桃園(觀音工業區)", "County":"桃園市", "AQI":"79", "Pollutant":"細懸浮微粒", "Status":"普通", "SO2":"4.3", "CO":"", "CO_8hr":"", "O3":"27", "O3_8hr":"24", "PM10":"66", "PM2.5":"31", "NO2":"14", "NOx":"19", "NO":"4.6", "WindSpeed":"1.6", "WindDirec":"13", "PublishTime":"2020-03-21 10:00", "PM2.5_AVG":"27", "PM10_AVG":"58", "SO2_AVG":"1", "Longitude":"121.128044", "Latitude":"25.063039", "SiteId":"312"}] 


此格式為JSON(JavaScript Object Notation,JavaScript物件表示法),是目前網路最流行的資料交換格式,不過要如何讓ESP32自動找到需要地區的PM2.5的資訊,則必須要透過JSON解析工具。

三、JSON資料解析
JSON格式是一種資料結構,主要為{"標題1":"內容1", "標題2":"內容2"}這樣的方式組成,如以資料庫的觀點來說明,每一個大括弧{ }代表一個紀錄,而內容則依序寫在{ }內部,以分號「:」區隔欄位名稱及內容。舉例來說,以上表取得的資料第一筆「基隆」來說,若要獲得PM2.5的資料,則是尋找"PM2.5":"19"這個欄位,可以獲得目前基隆的PM2.5為19,同理也可以從上表得到桃園市PM2.5為31。
JSON可分成以下兩大種類JSON物件「{ }」與JSON陣列「[ ]」,兩者又可以互相組合,因此結構比資料庫欄位形式較為複雜,先說明兩者的差異。

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陣列來表示。

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的這一個函式庫。
image

 

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


    //架設payload是空氣品質JSON陣列
    //要一個payload大小x2的JSON暫存
    DynamicJsonDocument AQJarray(payload.length()*2);
    deserializeJson(AQJarray, payload);//解析payload為JSON Array格式
    String KeelungPM25=AQJarray[0]["PM2.5"];//第0個是基隆的PM2.5
    String XizhiPM25=AQJarray[1]["PM2.5"];//第1個是汐止的PM2.5
    Serial.println("基隆PM2.5:" + KeelungPM25);
    Serial.println("汐止PM2.5:" + XizhiPM25);


image

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

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

String KeelungPM25=AQJarray[0]["PM2.5"];
此時需要任何資料,就可以透過Json變數[序號]["欄位名"]取得相關的空氣品質數值。

不過呢,用陣列序號的方式來找PM2.5資訊,前面幾筆還比較好算,如果您的地區剛好中間或尾巴,那不知道要算多久?當然我們可以用For或While迴圈來協助我們比較,例如您的地區是高雄市橋頭,而已知SiteId站台編號為48,此時注意我們用For迴圈來找SiteId=48即可,讀者也許會問,為何不直接比較站台名SiteName=橋頭呢?因為網路上中文的比對比較容易出問題,使用編號的比對是比較正式作法的。


image


    for (int i = 0; i <= AQJarray.size(); i++) {
      if (AQJarray[i]["SiteId"] == "48") {
        //橋頭站SiteId為"48" 
        String QiaotouPM25 = AQJarray[i]["PM2.5"];
        Serial.println("橋頭PM2.5:" + QiaotouPM25);
        break;//退出for迴圈
      }
    }


for (int i = 0 ; i <= AQJarray.size()-1 ; i++)
是比對迴圈的初始設定,從第0個開始,比對到最後一個,如何知道最後一個呢?AQJarray.size()代表這個這列的種數量,但因為程式的陣列編號都是從0開始的,所以最後一個JSON元素編號為AQJarray.size()-1
if (AQJarray[i]["SiteId"] == "48")
比較第i個元素的SiteId是否為橋頭站的編號"48",如果是的話,就取得該站的PM2.5資訊,顯示在序列監控視窗中。
而最後的break;則是退出For迴圈,因為我們要的橋頭站資訊已經取得,不需要再比對到最後一站。

image

 

arrow
arrow

    夜市 小霸王 發表在 痞客邦 留言(2) 人氣()