- 實現 D1 mini 抓取環保署空氣品質資料
- 實現 D1 mini 與 BLYNK 連結
NodeMCU 教學 - 06:使用 WeMos D1 mini NodeMCU 網路爬蟲 抓取 JSON 天氣資訊
有抓過中央氣象局的天氣資訊(如氣溫),而此篇是要從環保署去抓現今最夯的空氣品質資料
連結在此 行政院環境保護署。環境資源資料開放平臺
我們可以抓到空汙品質的資訊,這是在前面的氣象局開放資料裡面沒有的
空氣品質指標(AQI)
取得資料的方式以及範例連結在此
在"權限登記"可以申請到專屬自己的 KEY
我們用空氣品質指標(AQI)的範例來得到 JSON 格式的空氣品質資料。可以發現得到的資料還是很多,這個部分我目前還不曉得要怎麼修改 URL 以取得某一個地區的資料(例如前一章可以用 locatioName 針對新竹地區去抓)
在使用範例的地方是教我們用 "skip"來忽略某些資料,後來我發現好像沒有用處,我們把 offset 替代 skip 並且加上 limit 後就可以成功進行資料篩選(得到新竹地區的空氣品質資訊)
因為我們只想針對 "新竹" 地區的空氣品質來進行資料擷取,並不想要一次得到這麼多筆資料,所以我們必須想辦法減少無效的資料量
東搞西弄後就抓出新竹部分的空氣品質了
https://opendata.epa.gov.tw/webapi/api/rest/datastore/355000000I-000259/?format=json&limit=3&offset=49&sort=County&token=你的key
#define BLYNK_PRINT Serial
#include "ESP8266WiFi.h"
#include "ArduinoJson.h"
#include "BlynkSimpleEsp8266.h"
#include "SimpleTimer.h"
void get_AQI();
bool showAQI(char *json);
// ===== Wifi setup =====
const char *ssid = "WIFI名稱";
const char *pass = "WIFI密碼";
// ===== AQI. setup =====
const char *AQIhost = "opendata.epa.gov.tw";
const char *sourceid = "355000000I-000259";
const char *_limit = "3";
const char *_offset = "49";
const char *token = "環保署得到的KEY";
static char AQIrespBuf[4096];
// ==== BLYNK ====
char auth[] = "BLYNK的KEY";
// ===== Hardware setup =====
const int ledPin = D4; // the number of the LED pin
SimpleTimer timer;
volatile float AQI = 0;
volatile float PM2_5 = 0;
volatile float PM10 = 0;
volatile int flag = 1;
void timer_1260000(){
flag = 0;
Serial.println("timerInterrupt START!");
get_AQI();
Serial.println("Send API. data to BLYNK !!");
Blynk.virtualWrite(V2, AQI);
Blynk.virtualWrite(V3, PM2_5);
Blynk.virtualWrite(V4, PM10);
Serial.println("timerInterrupt END!\n");
flag = 1;
}
void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(115200);
delay(10);
digitalWrite(ledPin,LOW);
Blynk.begin(auth, ssid, pass);
timer.setInterval(1260000L, timer_1260000);
digitalWrite(ledPin,HIGH);
}
void loop()
{
if(flag) {
Blynk.run();
timer.run();
}
delay(20);
}
/* =====================*/
// ===== get_AQI ====
/* =====================*/
void get_AQI()
{
digitalWrite(ledPin, HIGH);
Serial.print("Connecting to ");
Serial.println(AQIhost);
WiFiClient client;
const int Port = 80;
if (!client.connect(AQIhost, Port)) {
Serial.println("Connection failed");
return;
}
// ====== Create a URI for the request =====
String url = "/webapi/api/rest/datastore/";
url += sourceid;
url += "/?format=json&limit=";
url += _limit;
url += "&offset=";
url += _offset;
url += "&sort=County&token=";
url += token;
Serial.print("Requesting URL: ");
Serial.println(url);
// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + AQIhost + "\r\n" +
"Connection: close\r\n\r\n");
client.flush();
int AQIrespLen = 0;
bool skip_headers = true;
while (client.connected() || client.available()) {
if (skip_headers) {
String aLine = client.readStringUntil('\n');
if (aLine.length() <= 1) {
skip_headers = false;
}
}
else {
int bytesIn;
bytesIn = client.read((uint8_t *)&AQIrespBuf[AQIrespLen], sizeof(AQIrespBuf) - AQIrespLen);
Serial.print(F("bytesIn ")); Serial.println(bytesIn);
if (bytesIn > 0) {
AQIrespLen += bytesIn;
if (AQIrespLen > sizeof(AQIrespBuf)) AQIrespLen = sizeof(AQIrespBuf);
}
else if (bytesIn < 0) {
Serial.print(F("read error "));
Serial.println(bytesIn);
}
}
delay(1);
}
Serial.println();
Serial.println("closing connection");
Serial.println();
client.stop();
if (AQIrespLen >= sizeof(AQIrespBuf)) {
Serial.print(F("AQIrespBuf overflow "));
Serial.println(AQIrespLen);
return;
}
AQIrespBuf[AQIrespLen++] = '\0';
Serial.print(F("AQIrespLen "));
Serial.println(AQIrespLen);
Serial.println("");
if(showAQI(AQIrespBuf)) {
Serial.println("Get AQI. data");
}
digitalWrite(ledPin, LOW);
}
/* =====================*/
// ===== showAQI ====
/* =====================*/
bool showAQI(char *json){
StaticJsonBuffer<3072> jsonBuffer;
char *jsonstart = strchr(json, '{');
Serial.println("jsonstart");
Serial.println(jsonstart);
Serial.println("");
if (jsonstart == NULL) {
Serial.println(F("JSON data missing"));
return false;
}
json = jsonstart;
JsonObject& root = jsonBuffer.parseObject(json);
if (!root.success()) {
Serial.println(F("jsonBuffer.parseObject() failed"));
return false;
}
float _AQI = root["records"][0]["AQI"];
float _PM2_5 = root["records"][0]["PM2.5"];
float _PM10 = root["records"][0]["PM10"];
Serial.print("AQI = ");
Serial.println(_AQI);
Serial.print("APM2.5 = ");
Serial.println(_PM2_5);
Serial.print("PM10 = ");
Serial.println(_PM10);
AQI = _AQI;
PM2_5 = _PM2_5;
PM10 = _PM10;
return true;
}
3072>
收了好幾天之後的結果如下圖
補充:
上述那個空氣品質來源十分不穩定,常常會有連不上的問題,所以這裡提供另外一個資料來源,無須申請識別碼
Opendata2
這來源穩定很多且回應很迅速,但缺點是沒辦法在 URL 進行資料的篩選,只能把所有資料都抓了,目前我還沒有成功完成這個來源的實作,因為一次要收取全台灣所有測站資料的數據,過於龐大的 data 造成解析的困難以及版子的不穩定。