ESP 12E 算是EPS 8266 系統列很牛的晶片,也提供比較多的腳位,
找把它拿來替代Arduino取得魚缸中的資料,顯示在LCD上,並傳到ThingSpeak。
PH 2的原因是因為他爬升到穩定需要一點時間,但我20秒才讓他取一次值,
他需要取6次,去頭去尾取中間的平均值,它的取值時間不建議小於800ms,
一般來說校正後放入水中要達到穩要幾分鐘,例如海水缸,它會從0慢慢的爬到
8.x,達到穩定後,水中的PH值改變時,它就能夠快速反應。(當然你如果從8變到10,
還是要爬很久,但如果只是0.X的變化是很快的)
ESP 12E
PH meter
DS18b20 (防水版本)
1602 I2C LCD
杜邦線
麵包板(小)
麵包板電源模組
DC 9V電源供應器
USB to TTL (燒錄程式碼用)
Wi-Fi AP (讓ESP 12 可以傳資料到ThingSpeak)
ThingSpeak Account
ESP 12E 麵包板 PH meter DS18b20 LCD
VCC 3.3V
GND GND GND GND GND USB to TTL
CH_PID 3.3V
GPIO 14 Data
GPIO 4 SDA
GPIO 5 SCL
ADC (A0) Data
5V VCC VCC VCC
------------以下是燒錄時才要加的------
RX TX
TX RX
GND GND
GPIO0 GND
這邊有2個重點,
(1)DS18b20需要搭配1個4.7K電阻,詳情請參考[Arduino]DS18B20 防水溫度計
(2)ESP 12 E 的ADC,支援的類比為10bit,但電壓範為是0~1V,這點
比較特別,一開始輸入2V怎麼都輸出1024,查了很多文才發現這個重點。
When your potentiometer is near 0V it prints 0 and when it reaches 1V it should print 1024.
資料來源:
http://randomnerdtutorials.com/esp8266-adc-reading-analog-values-with-nodemcu/
但PH meter output 為0~5V analog data, 所以我只好拿起5顆10K電阻做分壓,
因為我用的不是精密電阻,10顆幾塊錢那種,所以我必須再重新校正一次PH meter,
因為電阻會有誤差,然候這樣子會導致你拿到的可能不是1/5的電壓值,所以重新依據
目前的狀況校正。(手邊沒有其他東西可用時才用這招,不是個好方法)
建立一個CHANNEL,使用2個欄位如下所示,並且把API KEY貼到程式碼中。
#include <LiquidCrystal_I2C.h> #include <Wire.h> #include <ESP8266WiFi.h> #include <OneWire.h> #define SensorPin A0 //pH meter Analog output to Arduino Analog Input 0 #define Offset 0.42 //deviation compensate #define samplingInterval 20 #define printInterval 800 #define ArrayLenth 40 //times of collection int pHArray[ArrayLenth]; //Store the average value of the sensor feedback int pHArrayIndex=0; OneWire ds(14); // on pin 14 (a 4.7K resistor is necessary) // replace with your channel’s thingspeak API key, String apiKey = "your ThingSpeak API Key"; const char* ssid = "your ssid"; const char* password = "your password"; const char* server = "api.thingspeak.com"; LiquidCrystal_I2C lcd(0x27,16,2); // Check I2C address of LCD, normally 0x27 or 0x3F static const byte degrees_glyph[] = { 0x00, 0x07, 0x05, 0x07, 0x00 }; WiFiClient client; void setup() { lcd.begin(4,5); // In ESP8266-01, SDA=0, SCL=2 lcd.backlight(); // Register the custom symbol... Serial.begin(115200); delay(10); 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.println("WiFi connected"); } void loop() { byte i; byte present = 0; byte type_s; byte data[12]; byte addr[8]; float celsius, fahrenheit; int h=20; if ( !ds.search(addr)) { Serial.println("No more addresses."); Serial.println(); ds.reset_search(); delay(250); return; } Serial.print("ROM ="); for( i = 0; i < 8; i++) { Serial.write(' '); Serial.print(addr[i], HEX); } if (OneWire::crc8(addr, 7) != addr[7]) { Serial.println("CRC is not valid!"); return; } Serial.println(); // the first ROM byte indicates which chip switch (addr[0]) { case 0x10: Serial.println(" Chip = DS18S20"); // or old DS1820 type_s = 1; break; case 0x28: Serial.println(" Chip = DS18B20"); type_s = 0; break; case 0x22: Serial.println(" Chip = DS1822"); type_s = 0; break; default: Serial.println("Device is not a DS18x20 family device."); return; } ds.reset(); ds.select(addr); ds.write(0x44, 1); // start conversion, with parasite power on at the end delay(1000); // maybe 750ms is enough, maybe not // we might do a ds.depower() here, but the reset will take care of it. present = ds.reset(); ds.select(addr); ds.write(0xBE); // Read Scratchpad Serial.print(" Data = "); Serial.print(present, HEX); Serial.print(" "); for ( i = 0; i < 9; i++) { // we need 9 bytes data[i] = ds.read(); Serial.print(data[i], HEX); Serial.print(" "); } Serial.print(" CRC="); Serial.print(OneWire::crc8(data, 8), HEX); Serial.println(); // Convert the data to actual temperature // because the result is a 16 bit signed integer, it should // be stored to an "int16_t" type, which is always 16 bits // even when compiled on a 32 bit processor. int16_t raw = (data[1] << 8) | data[0]; if (type_s) { raw = raw << 3; // 9 bit resolution default if (data[7] == 0x10) { // "count remain" gives full 12 bit resolution raw = (raw & 0xFFF0) + 12 - data[6]; } } else { byte cfg = (data[4] & 0x60); // at lower res, the low bits are undefined, so let's zero them if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms //// default is 12 bit resolution, 750 ms conversion time } celsius = (float)raw / 16.0; fahrenheit = celsius * 1.8 + 32.0; Serial.print(" Temperature = "); Serial.print(celsius); Serial.print(" Celsius, "); Serial.print(fahrenheit); Serial.println(" Fahrenheit"); static unsigned long samplingTime = millis(); static unsigned long printTime = millis(); static float pHValue,voltage; if(millis()-samplingTime > samplingInterval) { pHArray[pHArrayIndex++]=analogRead(SensorPin); if(pHArrayIndex==ArrayLenth)pHArrayIndex=0; voltage = avergearray(pHArray, ArrayLenth)*5.0/1024; pHValue = 3.5*voltage+Offset; samplingTime=millis(); } if(millis() - printTime > printInterval) //Every 800 milliseconds, print a numerical, convert the state of the LED indicator { Serial.print("Voltage:"); Serial.print(voltage,2); Serial.print(" pH value: "); Serial.println(pHValue,2); printTime=millis(); } if (client.connect(server,80)) { // "184.106.153.149" or api.thingspeak.com String postStr = apiKey; postStr +="&field1="; postStr += String(celsius); postStr +="&field2="; postStr += String(pHValue); postStr += "\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: "+apiKey+"\n"); client.print("Content-Type: application/x-www-form-urlencoded\n"); client.print("Content-Length: "); client.print(postStr.length()); client.print("\n\n"); client.print(postStr); lcd.setCursor(0, 0); lcd.print("Temperature:"); lcd.print(celsius,1); lcd.setCursor(0, 1); lcd.print("PH Value:"); lcd.print(pHValue,1); Serial.print("Temperature: "); Serial.print(celsius); Serial.print("PH: "); Serial.print(pHValue); Serial.println("send to Thingspeak"); } client.stop(); Serial.println("Waiting…"); // thingspeak needs minimum 15 sec delay between updates delay(20000); } double avergearray(int* arr, int number){ int i; int max,min; double avg; long amount=0; if(number<=0){ Serial.println("Error number for the array to avraging!/n"); return 0; } if(number<5){ //less than 5, calculated directly statistics for(i=0;i<number;i++){ amount+=arr[i]; } avg = amount/number; return avg; }else{ if(arr[0]<arr[1]){ min = arr[0];max=arr[1]; } else{ min=arr[1];max=arr[0]; } for(i=2;i<number;i++){ if(arr[i]<min){ amount+=min; //arr<min min=arr[i]; }else { if(arr[i]>max){ amount+=max; //arr>max max=arr[i]; }else{ amount+=arr[i]; //min<=arr<=max } }//if }//for avg = (double)amount/(number-2); }//if return avg; }
PH 尚在未穩定狀態,所以2.9 並不正確,到達穩定要一點時間 ,目前程式每次執行時間
間隔20秒,對PH取值而言太久,需要再做調整,PH到達穩定約10~20分鐘。
下圖是ThingSpeak上的資料(隔了一天)。
沒有留言:
張貼留言