- 亮度
- 風向 & 風速
- 雨量
我參考了下列書籍的範例進行測試 :
- 葉難 : Arduino 範例分析與實作設計, 博碩出版, 4-1 節
- 楊明豐 : Arduino 最佳入門與應用, 碁峰出版, 8-3-4 節
- 孫駿榮等 : Arduino 一試就上手第二版, 碁峰出版, 4-6 節
- 趙英傑, Arduino 互動設計入門超圖解第二版, 6-2 節
- Michael Margolis, Arduino Cookbook, 歐萊禮出版, 6-2 節
Vcds=Vcc*Rcds/(R+Rcds)
串聯電阻的值依光敏電阻大小而定, 若是較大顆的光敏電阻, 其電阻值較低, 則串聯電阻用 1K 即可; 若為一般較小顆的光敏電阻, 則用 4.7K 或 10K 串聯電阻, 電路圖如下所示 :
有些書的接法與上圖中的電阻上下相反 (如 "Arduino Cookbook" 與 "Arduino 範例分析與實作設計"), 即 10K 串聯電阻在下, 因此是量測串聯電阻的壓降. 這樣讀取值就會與亮度成正比, 因光度越亮, 在上的光敏電阻值越小, 壓降也越小, 接地的串聯電阻壓降則越大. 上圖則是越亮讀取值越小.
Arduino 的類比輸入 (Nano 是 A0~A7) 雖名為類比, 事實上輸入信號會被 Arduino 內建的類比至數位轉換電路轉成 0~1023 的數位值, 若 Vcc=5V, 則當輸入也是 5V 時 (全暗, 光敏電阻值最高), 用 analogRead() 讀取時將得到 1023. 由於串聯電阻多少會有一些壓降, 因此要出現 1023 的值必須光敏電阻在全暗環境下, 此時其電阻為 Mega 級, 會吃掉幾乎全部 5V 壓降.
範例 1 :
int value; //類比輸入讀取值
int LED=13;
void setup() {
pinMode(LED, OUTPUT); //設定 DIO Pin 13 為 LED 輸出
Serial.begin(9600);
}
void loop() {
value=analogRead(A0); //參數也可用 0
Serial.println(value);
delay(1000); //每秒讀取一次
}
此範例會在序列埠監視視窗顯示所讀取到的光敏電阻壓降值 (0~1023), 每秒讀取一次. 在 "Arduino" 一試就上手這本書裡提到, 由於感測器會受到環境雜訊的干擾, 經過內建 AD 轉換後的 0~1023 數值有時會突然劇烈變動, 通常使用平均值法來處理這些雜訊, 使其對最後之數值輸出影響降低, 例如使用一個 10 次的迴圈連加量測值, 再除以 10 作為輸出值.
比較簡單的方式是採用濾波方式 (積分器), 只取前一次量測值與本次量測值做比例相加後得到輸出值, 如下列範例所示 :
範例 2 :
int value; //類比輸入讀取值
int LED=13;
void setup() {
pinMode(LED, OUTPUT); //設定 DIO Pin 13 為 LED 輸出
Serial.begin(9600);
}
void loop() {
value=analogRead(A0); //參數也可用 0
float new_value=0.3*value + 0.7*analogRead(A0);
Serial.println(floor(new_value));
delay(1000); //每秒讀取一次
}
此處輸出值採前次佔 3 成, 本次佔 7 成方式處理, 這樣數值變化經過積分器濾波後變化就不會那麼劇烈了.
另外, 在 "Arduino Cookbook" 這本書中則介紹了讓光度以 PWM 方式控制 Arduino LED 的閃爍頻率 :
範例 3 :
int value; //類比輸入讀取值
int LED=13;
void setup() {
pinMode(LED, OUTPUT); //設定 DIO Pin 13 為 LED 輸出
}
void loop() {
value=analogRead(A0);
digitalWrite(LED, HIGH); //用量測值控制亮滅時間, 光度越強, 閃爍越快
delay(value);
digitalWrite(LED, LOW);
delay(value);
}
以上面的電路圖來說, 此程式光度越亮, 閃爍的頻率就越快. 如下面影片所示 :
光敏電阻最常應用在路燈自動開關上, 當白天太陽升起, 就將路燈關閉; 到了傍晚就開啟. 但是從光敏電阻讀取的數據, 其臨界值到底要用多少作為開啟與關閉的依據? 這要用 Arduino 實際做光度試驗後來決定, 例如大於 700 以上表示天色已暗, 則可用 if 來判斷 :
範例 4 :
int value; //類比輸入讀取值
int LED=13;
void setup() {
pinMode(LED, OUTPUT); //設定 DIO Pin 13 為 LED 輸出
}
void loop() {
value=analogRead(A0);
if (value > 700) {
digitalWrite(LED, HIGH); //路燈開啟
}
else {
digitalWrite(LED, LOW); //路燈關閉
}
}
這裡只是點亮板上的 LED, 只要稍作修改, 改成輸出到控制繼電器的 DIO 腳, 就能真正控制路燈了. 但上面這程式只以一個臨界值做判斷, 當天色變暗剛好在臨界值附近變動時, 路燈就會有閃爍現象, 這可以用兩個臨界值來修正, 如下範例所示 :
範例 5 :
int value; //類比輸入讀取值
int LED=13;
void setup() {
pinMode(LED, OUTPUT); //設定 DIO Pin 13 為 LED 輸出
}
void loop() {
value=analogRead(A0);
if (value > 700) {
digitalWrite(LED, HIGH); //路燈開啟
}
else if (value < 600) {
digitalWrite(LED, LOW); //路燈關閉
}
}
當量測值低於 700 時路燈仍會亮著, 直到低於 600 才會關閉; 若又彈回 600 以上仍是關閉, 直到高於 700 才會開啟. 總之, 雜訊必須每次都大於 100 才會產生閃爍情況, 機率就大大降低了. 設置寬度為 100 當緩衝區就可以消除閃爍情況了.
另外, 因為讀取到的電壓值是 0~1023 的數位化後的數值, 要如何反求光敏電阻上的壓降呢? 可以除以1023 再乘以 5 即得 :
Vcds=5*float(analogRead(A0))/1023
而光敏電阻值也可以從上面的分壓公式反求 :
Rcds=R*Vcds/(Vcc-Vcds)
另外值得一提的是, 在 "Arduino Cookbook" 與 "Arduino 最佳入門與應用" 這兩本書中介紹了一個 map() 函數, 可以把某個值域映射到另一個值域, 例如想將 0~1023 的數值映射到 0~100%, 就可以用 map(0,1023, 0, 100) 來完成.
綜合以上計算式, 我將上面範例 1 修改為下列範例 6 :
範例 6 :
int value; //類比輸入讀取值
int LED=13;
void setup() {
pinMode(LED, OUTPUT); //設定 DIO Pin 13 為 LED 輸出
Serial.begin(9600);
}
void loop() {
value=analogRead(A0); //參數也可用 0
float Vcds=5*float(value)/1023; //光敏電阻壓降
float Rcds=10000*Vcds/(5-Vcds); //光敏電阻值
int percent=map(value,0,1023,0,100);
Serial.print("value=");
Serial.print(value);
Serial.print("(");
Serial.print(percent);
Serial.print("%)");
Serial.print("\t");
Serial.print("Vcds=");
Serial.print(Vcds);
Serial.print("\t");
Serial.print("Rcds=");
Serial.println(Rcds);
delay(1000); //每秒讀取一次
}
序列埠輸出擷取如下 :
value=465(45%) Vcds=2.27 Rcds=8333.33
value=465(45%) Vcds=2.27 Rcds=8333.33
value=465(45%) Vcds=2.27 Rcds=8333.33
value=465(45%) Vcds=2.27 Rcds=8333.33
value=204(19%) Vcds=1.00 Rcds=2490.84
value=200(19%) Vcds=0.98 Rcds=2430.13
value=200(19%) Vcds=0.98 Rcds=2430.13
value=199(19%) Vcds=0.97 Rcds=2415.05
value=537(52%) Vcds=2.62 Rcds=11049.38
value=642(62%) Vcds=3.14 Rcds=16850.40
value=636(62%) Vcds=3.11 Rcds=16434.11
這裡百分比越大表示亮度越暗, 100% 就是全暗. 如果要倒過來, 就把 percent 用 100 去減即可 :
int luminance=100-map(value,0,1023,0,100);
我把這個亮度也加入 Thingspeak 物聯網氣候觀測程式, 名稱從 DHT11 改為 Atmosphere, 標題也改為氣候觀測資料, 參考前篇 :
程式則改為 :
#include <SoftwareSerial.h> //載入軟體串列埠函式庫
#include <DHT.h> //載入 DHT11 函式庫
#define DHTPIN 2 //定義 DIO 腳 2 為 DHT11 輸入
#define DHTTYPE DHT11 //定義 DHT 型態為 DHT11
DHT dht(DHTPIN, DHTTYPE); //初始化 DHT11 感測器
int LED=13; //宣告 DIO 腳 13 為 LED 輸出
String api_key="NO5N8C7T2KINFCQE"; //Thingspeak API Write Key
SoftwareSerial sSerial(10,11); //設定軟體串列埠腳位 RX, TX為 DIO 腳 10, 11
void setup() {
pinMode(LED, OUTPUT); //設定 DIO Pin 13 為 LED 輸出
Serial.begin(9600); //啟始硬體串列埠 (除錯用)
sSerial.begin(9600); //啟始軟體串列埠 (與 ESP8266 介接用)
dht.begin(); //啟始 DHT11 溫濕度感測器
sSerial.println("AT+RST"); //軟體串列埠傳送 AT 指令重啟 ESP8266
}
void loop() {
int cds=analogRead(A0); //讀取光敏電阻壓降數位化數據 0~1023
int l=100-map(cds,0,1023,0,100); //亮度轉成 %
float h=dht.readHumidity(); //讀取濕度
float c=dht.readTemperature(); //讀取攝氏溫度
float f=dht.readTemperature(true); //讀取華氏溫度
//有任何一個是 NAN 就不往下執行資料傳送
if (isnan(l) || isnan(h) || isnan(c) || isnan(f)) {
Serial.println("Failed to read from Atmosphere sensors!");
return;
}
blink_led(1000,500); //進入資料傳送程序 : LED 閃爍一次
//製作參數字串
String param="&field1=" + (String)c + "&field2=" + (String)f +
"&field3=" + (String)h + "&field4=" + (String)l;
//與 Thingspeak 主機建立 TCP 連線 (用 api.thingspeak.com 亦可)
String cmd="AT+CIPSTART=\"TCP\",\"184.106.153.149\",80";
sSerial.println(cmd); //向 ESP8266 傳送 TCP 連線之 AT 指令
//偵測 TCP 連線是否成功
if (sSerial.find("Error")) {
Serial.println("AT+CIPSTART error!");
return; //連線失敗跳出目前迴圈 (不做後續傳送作業)
}
Serial.println(cmd); //輸出 AT 指令於監控視窗
//製作 GET 字串
String GET="GET /update?api_key=" + api_key + param + "\r\n\r\n";
cmd="AT+CIPSEND=" + String(GET.length()); //傳送 GET 字串長度之 AT 指令
sSerial.println(cmd); //告知 ESP8266 即將傳送之 GET 字串長度
Serial.println(cmd); //輸出 AT 指令於監控視窗
//檢查 ESP8266 是否回應
if (sSerial.find(">")) { //若收到 ESP8266 的回應標頭結束字元
sSerial.print(GET); //向 ESP8266 傳送 GET 字串內容
Serial.println(GET); //顯示 GET 字串內容於監控視窗
}
else { //沒有收到 ESP8266 回應
sSerial.println("AT+CIPCLOSE"); //關閉 TCP 連線
Serial.println("AT+CIPCLOSE"); //顯示連線關閉訊息於監控視窗
}
delay(16000); //延遲 16 秒 (因 Thingspeak 每次更新須隔 15 秒)
}
void blink_led(int on, int off) { //LED 閃爍函式
digitalWrite(LED, HIGH);
delay(on);
digitalWrite(LED, LOW);
delay(off);
}
亮度的量測數據圖如下 :
觀察 Thingspeak 上收集的數據後, 我發現亮度在 10% 以下就算是昏暗了, 可以點亮路燈; 20% 以上大約是東方魚肚白程度, 可以關閉路燈. 公開的資料觀察連結如下 :
# https://api.thingspeak.com/channels/47985/feed.json?key=PWEAKSDX8X8QP0V5
OK, 大功告成!
#include <SoftwareSerial.h> //載入軟體串列埠函式庫
#include <DHT.h> //載入 DHT11 函式庫
#define DHTPIN 2 //定義 DIO 腳 2 為 DHT11 輸入
#define DHTTYPE DHT11 //定義 DHT 型態為 DHT11
DHT dht(DHTPIN, DHTTYPE); //初始化 DHT11 感測器
int LED=13; //宣告 DIO 腳 13 為 LED 輸出
String api_key="NO5N8C7T2KINFCQE"; //Thingspeak API Write Key
SoftwareSerial sSerial(10,11); //設定軟體串列埠腳位 RX, TX為 DIO 腳 10, 11
void setup() {
pinMode(LED, OUTPUT); //設定 DIO Pin 13 為 LED 輸出
Serial.begin(9600); //啟始硬體串列埠 (除錯用)
sSerial.begin(9600); //啟始軟體串列埠 (與 ESP8266 介接用)
dht.begin(); //啟始 DHT11 溫濕度感測器
sSerial.println("AT+RST"); //軟體串列埠傳送 AT 指令重啟 ESP8266
}
void loop() {
int cds=analogRead(A0); //讀取光敏電阻壓降數位化數據 0~1023
int l=100-map(cds,0,1023,0,100); //亮度轉成 %
float h=dht.readHumidity(); //讀取濕度
float c=dht.readTemperature(); //讀取攝氏溫度
float f=dht.readTemperature(true); //讀取華氏溫度
//有任何一個是 NAN 就不往下執行資料傳送
if (isnan(l) || isnan(h) || isnan(c) || isnan(f)) {
Serial.println("Failed to read from Atmosphere sensors!");
return;
}
blink_led(1000,500); //進入資料傳送程序 : LED 閃爍一次
//製作參數字串
String param="&field1=" + (String)c + "&field2=" + (String)f +
"&field3=" + (String)h + "&field4=" + (String)l;
//與 Thingspeak 主機建立 TCP 連線 (用 api.thingspeak.com 亦可)
String cmd="AT+CIPSTART=\"TCP\",\"184.106.153.149\",80";
sSerial.println(cmd); //向 ESP8266 傳送 TCP 連線之 AT 指令
//偵測 TCP 連線是否成功
if (sSerial.find("Error")) {
Serial.println("AT+CIPSTART error!");
return; //連線失敗跳出目前迴圈 (不做後續傳送作業)
}
Serial.println(cmd); //輸出 AT 指令於監控視窗
//製作 GET 字串
String GET="GET /update?api_key=" + api_key + param + "\r\n\r\n";
cmd="AT+CIPSEND=" + String(GET.length()); //傳送 GET 字串長度之 AT 指令
sSerial.println(cmd); //告知 ESP8266 即將傳送之 GET 字串長度
Serial.println(cmd); //輸出 AT 指令於監控視窗
//檢查 ESP8266 是否回應
if (sSerial.find(">")) { //若收到 ESP8266 的回應標頭結束字元
sSerial.print(GET); //向 ESP8266 傳送 GET 字串內容
Serial.println(GET); //顯示 GET 字串內容於監控視窗
}
else { //沒有收到 ESP8266 回應
sSerial.println("AT+CIPCLOSE"); //關閉 TCP 連線
Serial.println("AT+CIPCLOSE"); //顯示連線關閉訊息於監控視窗
}
delay(16000); //延遲 16 秒 (因 Thingspeak 每次更新須隔 15 秒)
}
void blink_led(int on, int off) { //LED 閃爍函式
digitalWrite(LED, HIGH);
delay(on);
digitalWrite(LED, LOW);
delay(off);
}
亮度的量測數據圖如下 :
觀察 Thingspeak 上收集的數據後, 我發現亮度在 10% 以下就算是昏暗了, 可以點亮路燈; 20% 以上大約是東方魚肚白程度, 可以關閉路燈. 公開的資料觀察連結如下 :
# https://api.thingspeak.com/channels/47985/feed.json?key=PWEAKSDX8X8QP0V5
OK, 大功告成!
你為什麼不把arduino uno 也丟掉
回覆刪除只用8266就好
真是見樹不見林
嗯, 之前的文章我有提到過, ESP8266 潛力很大, 確實可以取代 Arduino, 但是因為我主要是想把 Arduino 摸熟後再集中心力於 ESP8266, 畢竟 Arduino 是創客最愛的 MCU 啊! 很多範例都是用 Arduino 做的. 而且最便宜的 ESP-01 模組只有一個 GPIO2 可用, 只能連接單一感測器. ESP8266 目前只是我用來聯網的工具而已. 不過 UNO 這麼大顆, 確實已經被我冰起來囉, 我現在都只用 NANO 做實驗. 謝謝您的高見.
回覆刪除想請問老師 如果我要將三個光敏電阻也就是Arduino顯示的數據 利用Nodemcu上傳到thingspeak 因為網路上沒有相關資料 所以我利用濕度得下去改 請問哪邊有誤呢 或者這樣是不可行的 謝謝
回覆刪除#include
String apiWritekey = "IUI14WAO2THGE8XU"; // replace with your THINGSPEAK WRITEAPI key here
const char* ssid = "302"; // your wifi SSID name
const char* password = "0" ;// wifi pasword
const char* server = "api.thingspeak.com";
float resolution=3.3/1023;// 3.3 is the supply volt & 1023 is max analog read value
WiFiClient client;
void setup() {
Serial.begin(115200);
WiFi.disconnect();
delay(10);
WiFi.begin(ssid, password);
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("NodeMcu connected to wifi...");
Serial.println(ssid);
Serial.println();
}
void loop() {
float cmd = analogRead(A0) ;
if (client.connect(server,80))
{
String tsData = apiWritekey;
tsData +="&field1=";
tsData += String(cmd);
tsData += "\r\n\r\n";
client.print("POST /update HTTP/1.1\n");
client.print("Host: api.thingspeak.com\n");
client.print("Connection: close\n");
client.print("X-THINGSPEAKAPIKEY: "+apiWritekey+"\n");
client.print("Content-Type: application/x-www-form-urlencoded\n");
client.print("Content-Length: ");
client.print(tsData.length());
client.print("\n\n"); // the 2 carriage returns indicate closing of Header fields & starting of data
client.print(tsData);
Serial.print("Temperature: ");
Serial.print(cmd);
Serial.println("uploaded to Thingspeak server....");
}
client.stop();
Serial.println("Waiting to upload next reading...");
Serial.println();
// thingspeak needs minimum 15 sec delay between updates
delay(15000);
}
出現甚麼錯誤呢? 我目前無暇作測試. thingspeak 上傳三個變數是沒問題的, 就是 field1, field2, filed3 而已.
回覆刪除不好意思我想請問老師,
回覆刪除我寫了一個偵測土壤濕度想用MQTT存到thingspeak。
可是遇到一個問題
就是只要我使用到WiFi.begin 這樣我偵測到的值都會有問題會是什麼問題呢?
我是用esp32
MQTT message : field1=25&field2=62&field3=1366.00
回覆刪除MQTT message : field1=25&field2=62&field3=4095.00
前兩個是 DHT11的溫溼度 後面一個是土壤濕度檢測的值
兩段的差異只有我有沒有把WiFi.begin註解掉
抱歉, 不太清楚您的問題, 可以 POST 較完整的程式碼嗎?
回覆刪除範例6中,計算光敏電阻值的公式
回覆刪除float Rcds=10000*Vcds/(5-Vcds); //光敏電阻值
分子分母是不是反了呢?
應該是:float Rcds=10000*(5-Vcds)/Vcds;
從串聯分壓公式來看I=V1/R1=V2/R2
R1=R2*V1/V2=R2*V1/(Vt-V1)
此外,原公式計算的結果,亮反而電阻值高、暗反而電阻值低,也不符合光敏電阻的特性
從串聯分壓公式來看沒有錯
回覆刪除但為什麼亮反而電阻值高、暗反而電阻值低@@