2016年11月8日 星期二

20W 太陽能板安裝測試 (二)

昨天週日傍晚終於完成 20W 小型太陽能板安裝, 但已夕陽西下, 要準備晚餐, 等到吃過晚飯, 整理打掃妥當後才將這幾天測試好的程式趕緊修改, 測試完成已八點矣, 趕緊將蓄電池, 充電控制器, 以及Arduino 控制板接好線, 同時將樓上的小米路由器 (設定為 Repeater) 打開以延長 WiFi 涵蓋範圍, 測試結果正常. 前篇文章參考 :

# 20W 太陽能板安裝測試 (一)

在上一篇文章的測試 4 中, 傳送資料到 ThingSpeak 的指令是放在 loop() 迴圈中, 由於 ThingSpeak 有每次傳送間隔不可低於 15 秒限制, 使得偵測光度變化的光敏電阻或偵測物體移動的 PIR 模組會被 15 秒限制住而無法快速做出反應. 為了解決此問題, 我改為使用 TimeAlarm 函式庫以便設定計時器固定每 60 秒將 DHT 模組, PIR 模組, 以及光敏電阻所偵測到的溫溼度, 光度, 以及移動捕捉次數傳送到 ThingSpeak 記錄起來, 而需要即時反應變化的光敏電阻偵測與 PIR 移動偵測就放在 loop() 迴圈裡, 關於 TimeAlarm 參考 :

利用 NTP 伺服器來同步 Arduino 系統時鐘 (三)

程式如下 :

測試 5  :

#include <SoftwareSerial.h>
#include <TimeAlarms.h>
#include "DHT.h"
#define DHTTYPE DHT11
#define DEBUG true

const byte pirPin=2; //PIR ouput connected to Arduino D2 (int 0)
const byte ledPin=13;
const byte relayPin=6;
const byte dhtPin=5;
const byte onLevel=20; //0(dark)~100(bright)
const byte offLevel=30; //0(dark)~100(bright)
const String ssid="EDIMAX-tony";
const String pwd="1234567890";
String api_key="NO5N8C7T2KINFCQE";  //Thingspeak API Write Key
int triggerCount=0; //interrupt counter
int triggerLimit=5; //cieling of trigger Counter

SoftwareSerial esp8266(7,8); //(RX,TX)
DHT dht(dhtPin, DHTTYPE); // Initialize DHT sensor

void(* resetFunc) (void)=0; //declare reset function at address 0

float c; //celsius
float f; //fahrenheit
float h; //humidity
int l; //luminance

void setup() {
  Serial.begin(9600);
  esp8266.begin(9600);
  /*sendData(F("AT+RST\r\n"),2000,DEBUG); // reset ESP8266
  while (!connectWifi(ssid, pwd)) {
    Serial.println(F("Connecting WiFi ... failed"));
    delay(2000);
    }
  sendData(F("AT+GMR\r\n"),1000,DEBUG);
  delay(3000); //wait for wifi connection to get local ip
  sendData(F("AT+CIFSR\r\n"),1000,DEBUG); //get ip address  */
  pinMode(ledPin, OUTPUT);
  pinMode(relayPin, OUTPUT);
  attachInterrupt(0, int0, RISING); //assign int0
  Alarm.timerRepeat(60, toThingSpeak);
  }

void loop() {
  c=dht.readTemperature();
  f=dht.readTemperature(true);
  h=dht.readHumidity();
  int cds=analogRead(A0); //get cds voltage (0~5V to 0~1023 dark)
  l=100-map(cds,0,1023,0,100); //map 0~1023(dark) to 0(dark)~100%
  Serial.print("C=");
  Serial.print(c);
  Serial.print(" ");
  Serial.print("F=");
  Serial.print(f);
  Serial.print(" ");
  Serial.print("H=");
  Serial.print(h);
  Serial.print(" ");  
  Serial.print("L=");
  Serial.print(l);
  Serial.print(" ");  
  Serial.print("T=");
  Serial.println(triggerCount);
  if (l < onLevel) {
    digitalWrite(relayPin, HIGH);  //turn on
    digitalWrite(ledPin, HIGH);
    }
  else if  (l > offLevel) {
    digitalWrite(relayPin, LOW);   //turn off
    digitalWrite(ledPin, LOW);
    }
  Alarm.delay(1000);
  }

void toThingSpeak() {
  String data="GET /update?api_key=" + api_key + "&field1=" + (String)c +
              "&field2=" + (String)f + "&field3=" + (String)h + "&field4=" +
              (String)l + "&field5=" + (String)triggerCount + "\r\n";
  String cmd="AT+CIPSTART=\"TCP\",\"184.106.153.149\",80\r\n";
  sendData(cmd, 1000, DEBUG);
  cmd="AT+CIPSEND=" + (String)data.length() + "\r\n";
  sendData(cmd,1000,DEBUG);
  String res=sendData(data, 2000, DEBUG);
  res.replace("\r\n",""); //remove all line terminator
  //Serial.println("===" + res + "===");
  if (res.indexOf("Unlink") == -1) { //if no auto unlink
    sendData("AT+CIPCLOSE\r\n",2000,DEBUG); //close session
    }
  if (res.indexOf("busy") != -1 || res.indexOf("ERROR") != -1) {
    //Serial.println("*****busy/ERROR*****");
    resetFunc();
    }
  //Serial.println(res.indexOf("busy"));
  //Serial.println(res.indexOf("ERROR"));
  }

boolean connectWifi(String ssid, String pwd) {
  String res=sendData("AT+CWJAP=\"" + ssid + F("\",\"") + pwd + F("\"\r\n"),10000,DEBUG);
  res.replace("\r\n",""); //remove all line terminator
  if (res.indexOf("OK") != -1) {return true;}
  else {return false;}
  }

void int0() { //interrupt handler
  if (triggerCount <= triggerLimit) {++triggerCount;}
  }

String sendData(String command, const int timeout, boolean debug) {
  String response="";
  esp8266.print(command); // send the read character to the esp8266
  long int time=millis();
  while ((time+timeout) > millis()) {
    while(esp8266.available()) {
      // The esp has data so display its output to the serial window
      char c=esp8266.read(); // read the next character.
      response += c;
      }
    }
  if (debug) {Serial.print(response);}
  return response;
  }

這裡要傳送的參數如溫溼度, 光度, 以及移動次數因為傳送給 ThingSpeak 的函數要擷取的關係, 在此都設為全域變數. PIR 輸出接 Arduino 的 D2 (int 0), 當偵測到移動時才觸發執行 int0() 函數, 而傳送資料到 ThingSpeak 則由 TimeAlarm 函式庫的 Alarm.timerRepeat() 函數每 60 秒觸發執行一次 (計時器中斷), 這樣便不會被 ThingSpeak 的 15 秒限制拖住了.

我看程式有順利執行資料傳送後才關掉電腦返回高雄. 今天觀察 ThingSpeak 所記錄的資料一切都正常, 發現一天之中氣溫從早上的 25 度逐漸上升, 濕度則反向逐步下降, 到中午 14:30~:15:30 左右到達最高溫 31 反轉向下, 濕度則從午後低點的 36% 回升, 如下圖所示 :


這裡的 TriggerCount 就是 PIR 感測器所偵測到的移動次數累計值, 從時間判斷, 那兩個突波應該是爸早上要出去時觸發的. 到這裡這次的實驗就告一個段落了, 接下來有時間要製作一個木製的機箱來放電池, 充控, 以及 Arduino+ESP8266 IOT 模組與感測器了. 或者, 也可以將上面程式改為使用 Blynk 以便在手機 App 上觀看結果. 

另外, 還有一個感測數據尚待紀錄, 那就是蓄電池的電壓! 這可能需要用一個電阻分壓器接到 Arduino 來換算. 如果以 15V 當最高電壓, 轉換成 5V 就需要除以 3, 這可以用 2K+1K 或 20K+10K 分壓器來做, 10K 壓降接到 Arduino 的 A1, 經內部 A/D 轉換成 0~1023 數值, 再反推為 0~15V 即可, 當然這需要取平均數並且經過校正才行. 


沒有留言 :