文章程式碼顯示

2018年1月30日 星期二

NodeMCU 教學 - 07:使用 WeMos D1 mini (NodeMCU) 網路爬蟲 JSON 天氣資料 並與手機連結

本篇功能概述
  1. 實現 D1 mini 抓取中央氣象局資料
  2. 實現 D1 mini 與 BLYNK 連結

承前文

NodeMCU 教學 - 06:使用 WeMos D1 mini NodeMCU 網路爬蟲 抓取 JSON 天氣資訊

我們已經完成用 D1 mini 來從網路上取得氣溫資訊,最後就只要結合

NodeMCU 教學 - 02:WeMos D1 mini NodeMCU 與 BLYNK

就能完成最後一浬路了 !!!!

#define BLYNK_PRINT Serial
#include "ESP8266WiFi.h"
#include "ArduinoJson.h"
#include "BlynkSimpleEsp8266.h"
#include "SimpleTimer.h"

void get_Weather(); 
bool showWeather(char *json);

// ===== Wifi setup =====
const char *ssid = "WIFI帳號";
const char *pass = "wifi密碼";

// =====  Temp. setup =====
const char *host = "opendata.cwb.gov.tw";
const char *dataid = "O-A0003-001";
const char *locationName = "新竹";
const char *privateKey = "你的識別碼";
const char *elementName = "TEMP";//可用逗號隔開來取得多筆資料,詳細請見 CWB_Opendata_API.pdf
static char respBuf[4096];

// ==== BLYNK ====
char auth[] = "BLYNK 的 Auth";

// ===== Hardware setup =====
const int ledPin = D4;        // the number of the LED pin

SimpleTimer timer; 
volatile float temp1 = 0;
volatile int flag = 1;

void timer_1260000(){
  flag = 0;
  Serial.println("timerInterrupt START!");
  get_Weather();
  
  Serial.println("Send Temp. data to BLYNK !!");
  Blynk.virtualWrite(V1, temp1); //一定要寫在 showWeather 的 Function 主體以外
  
  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_Weather ====
/* =====================*/
void get_Weather()
{
  digitalWrite(ledPin, HIGH);
  Serial.print("Connecting to ");
  Serial.println(host);
  
  WiFiClient client;
  
  const int Port = 80;
  if (!client.connect(host, Port)) {
    Serial.println("Connection failed");
    return;
  }
  
  // Create a URI for the request
  String url = "/api/v1/rest/datastore/";
  url += dataid;
  url += "?Authorization=";
  url += privateKey;
  url += "&locationName=";
  url += locationName;
  url += "&elementName=";
  url += elementName;
  
  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: " + host + "\r\n" + 
               "Connection: close\r\n\r\n");
  client.flush();

  int respLen = 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 *)&respBuf[respLen], sizeof(respBuf) - respLen);
      Serial.print(F("bytesIn ")); Serial.println(bytesIn);
      if (bytesIn > 0) {
        respLen += bytesIn;
        if (respLen > sizeof(respBuf)) respLen = sizeof(respBuf);
      }
      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 (respLen >= sizeof(respBuf)) {
    Serial.print(F("respBuf overflow "));
    Serial.println(respLen);
    return;
  }
  
  respBuf[respLen++] = '\0';
  Serial.print(F("respLen "));
  Serial.println(respLen);
  Serial.println("");

  if(showWeather(respBuf)) { 
    Serial.println("Get Temp. data");     
   }  
   
  digitalWrite(ledPin, LOW);  
}

/* =====================*/
// ===== showWeather ====
/* =====================*/
bool showWeather(char *json){
  
  StaticJsonBuffer<3072> jsonBuffer;

  // Skip characters until first '{' found
  // Ignore chunked length, if present
  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;

  // Parse JSON
  JsonObject& root = jsonBuffer.parseObject(json);  
  if (!root.success()) {
    Serial.println(F("jsonBuffer.parseObject() failed"));
    return false;
  }

  // Extract weather info from parsed JSON
  int stationId = root["records"]["location"][0]["stationId"];
  Serial.print("stationId = ");
  Serial.println(stationId);
  float temp = root["records"]["location"][0]["weatherElement"][0]["elementValue"];
  Serial.print("Temp. = ");
  Serial.println(temp);    
  
  //Blynk.virtualWrite(V1, temp); 直接寫成這樣讓 temp 傳上雲端不行

  temp1 = temp;  // 用一個全域變數供 get_Weather 調用
  //Blynk.virtualWrite(V1, temp1); 寫成這樣也不行
  
  return true;
}

程式碼的部分有點長,BLYNK 部分的介面就簡單的顯示就好



最後的結果如上方兩張圖,注意在顯示元件的地方我們選擇用 PUSH ,並在程式碼中利用 timer 來定時進行推送數據到雲端的功能。

在實現的過程中也出現一些狀況如下圖所示



我本來是將 Blynk.virtualWrite(V1, temp); 寫在 showWeather 函數末端,結果微控器會一直出現上圖中的錯誤,之後是將 temp 存到全域變數 temp1 ,並且挪到 showWeather 函數之外才免除這個錯誤

網路上查詢可能的成因之後發現要根治此問題有點麻煩,暫時將此問題擱置

↓↓↓ 連結到部落格方針與索引 ↓↓↓

Blog 使用方針與索引