2016年7月31日 星期日
[名詞定義]DFU Mode
Device Firmware Upgrade Mode (DFU Mode),
也就是裝置韌體更新模式。
一般的情況下,裝置在電腦看起來就是一個USB裝置,
但如果需要更新軔體時,就必須進入DFU Mode。
2016年7月30日 星期六
「MAC」Painter 2016 在10.11.6 上無法開啟解法
Painter 2016 在Mac OS 10.11.6上一開啟立刻Crash,查詢解法.
(1)安裝更新包 Update 1
http://www.wacom.com/en-us/support/product-support/drivers
Painter 2016 Update 1 will update the initial release of Painter 2016 to the latest version.
Support for OS X El Capitan
(1)安裝更新包 Update 1
http://www.wacom.com/en-us/support/product-support/drivers
Painter 2016 Update 1 will update the initial release of Painter 2016 to the latest version.
Support for OS X El Capitan
- Lag and performance issues have been fixed.
- Monitor screen no longer goes black when you exit Split View mode from a Painter document.
Painter 2016 安裝Update 1後的版本為15.1.0.740
(2) Wacom繪圖板用戶,可安裝最新版的Wacom手寫板軟體
(3)如果像我一樣做了以上2者都無效的話,可以嘗試檢查一個地方
系統偏好設定--》使用者與群組
確認使用者帳號與家目錄是否相同名稱,如果不同,請把帳號名稱改成跟
家目錄的名字一樣,我修改後重開機,Paiter 2016可正常使用。
參考連結:
[EPS8266] ESP 12E 傳送PH以及溫度資料到ThingSpeak
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上的資料(隔了一天)。
2016年7月27日 星期三
[Arduino] PH meter
最近在學習養海水魚,所以動了敗PH計的需求,在Y拍與沒有屋頂看了幾天後,
想說我沒有在淘寶過東西,上去一看,奶奶個熊,一樣的東西怎麼差了五百塊,
好吧,在大陸買一次看看,最後算一算,含運+跨國手續費,1K有找。
我買的這款是沒有溫度補償的,建議如果要更加精準的,可以考慮買有溫度補償的,
它會多一支防水溫度計,連接在主板上面,有興趣知道溫度對PH值影響的,可以
GOOGLE一下。
主板-用來連接PH探頭的BNC接頭,以及增益電路,並與Arduino連接的功用。
PH電極探頭(不可填充式,消耗品無誤),最左邊那個其實是保養液瓶,可以取下來
拿掉保養液瓶的樣子,如果長時間不用,要把頭沖乾淨後,放回保養液中。
主板與Arduino連接的線,紅: VCC ,黑:GND ,黃:Data (類比)
探頭不能直接使用,必須經過至少2階段校正。
把探頭接上主板
主板與Arduino連接後,會出現藍燈
拿出另外買的PH 7.0校正液(再強調一次PH與溫度有相關性,本篇暫時忽略,另篇說明)
校正液口太小,找另一個乾淨容器來裝
執行程式,記得把黃色線接在A2
/* # This sample code is used to test the pH meter V1.1. # Editor : YouYou # Date : 2014.06.23 # Ver : 1.1 # Product: analog pH meter # SKU : SEN0161 */ #define SensorPin A2 //pH meter Analog output to Arduino Analog Input 0 #define Offset 0.00 //deviation compensate #define LED 13 #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; void setup(void) { pinMode(LED,OUTPUT); Serial.begin(9600); Serial.println("pH meter experiment!"); //Test the serial monitor } void loop(void) { 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); digitalWrite(LED,digitalRead(LED)^1); printTime=millis(); } } 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; }
插入PH7.0校正液
等待數值穩定,約需要30~60秒
穩定後,將目前的PH與與7相減,以本例來說是7.04-7.00=0.04
將0.04填入Offset ,再次將程式上傳到Arduino。
接下來拿出另一瓶PH4.0 校正液。
因為電路有增益的問題,所以需要做2點校正,我沒有買到PH10校正液,
所以只能做2個點的校正,沒辦法校正大於PH7 那邊的,但理論上結果不會有太大誤差,
我們假設電路輸出是線性的,接下來我們把探頭清洗一下,再插入PH4.01這瓶。
看一下目前的數值,差蠻多的,必須要調整一下增益。
用一個可以轉到旋鈕的東西,例如尺,來轉到它一下,
它的變化很細微,所以可能要轉不少圈。
數據開始下降
快接近了,減慢旋鈕調整速度
再往下調一點
到達指定PH值,等候一下,確認有穩定下來,此時PH探頭拿去清洗一下後,
就可以開始用了,但記得凡事PH探頭要拿來量不同液體時,一定要先清洗,不要
汙染了校正液以及保養液。每個月建議重新校正一次PH探頭。
實際運作畫面
2016年7月24日 星期日
[EPS8266] 用ESP 01 搭配LCD做NTP Client
ESP 01 是較長見的EPS8266 的其中一種封裝,共有八支腳
圖片來源:
麵包板電源供應模組(支援3.3V以及5V)
EPS 01
I2C LCD
杜邦線
USB 轉TTL
1.燒錄圖。
2.實際運作腳位圖
注意一下,這裡的#include <LiquidCrystal_I2C.h> 載入的並不是內建的LiquidCrystal,必須使用這個函式庫
https://github.com/agnunez/ESP8266-I2C-LCD1602
/* Udp NTP Client Get the time from a Network Time Protocol (NTP) time server Demonstrates use of UDP sendPacket and ReceivePacket For more on NTP time servers and the messages needed to communicate with them, see http://en.wikipedia.org/wiki/Network_Time_Protocol created 4 Sep 2010 by Michael Margolis modified 9 Apr 2012 by Tom Igoe updated for the ESP8266 12 Apr 2015 by Ivan Grokhotkov This code is in the public domain. */ #include <Wire.h> #include <LiquidCrystal_I2C.h> #include <ESP8266WiFi.h> #include <WiFiUdp.h> char ssid[] = "GFI-5F-ID"; // your network SSID (name) char pass[] = "gfi8158456"; // your network password char data[8]; char buffer[2]; unsigned int localPort = 2390; // local port to listen for UDP packets /* Don't hardwire the IP address or we won't get the benefits of the pool. * Lookup the IP address for the host name instead */ //IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server IPAddress timeServerIP; // time.nist.gov NTP server address const char* ntpServerName = "time.nist.gov"; const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets // A UDP instance to let us send and receive packets over UDP WiFiUDP udp; LiquidCrystal_I2C lcd(0x27, 16, 2); void setup() { Serial.begin(115200); Serial.println(); Serial.println(); // initialize the LCD lcd.begin(0,2); // sda=0, scl=2 // Turn on the blacklight and print a message. lcd.backlight(); lcd.print("NTP Client"); // We start by connecting to a WiFi network Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); Serial.println("Starting UDP"); udp.begin(localPort); Serial.print("Local port: "); Serial.println(udp.localPort()); } void loop() { //get a random server from the pool WiFi.hostByName(ntpServerName, timeServerIP); sendNTPpacket(timeServerIP); // send an NTP packet to a time server // wait to see if a reply is available delay(1000); int cb = udp.parsePacket(); if (!cb) { Serial.println("no packet yet"); } else { Serial.print("packet received, length="); Serial.println(cb); // We've received a packet, read the data from it udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer //the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, esxtract the two words: unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // combine the four bytes (two words) into a long integer // this is NTP time (seconds since Jan 1 1900): unsigned long secsSince1900 = highWord << 16 | lowWord; Serial.print("Seconds since Jan 1 1900 = " ); Serial.println(secsSince1900); // now convert NTP time into everyday time: Serial.print("Unix time = "); // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: const unsigned long seventyYears = 2208988800UL; // subtract seventy years: unsigned long epoch = secsSince1900 - seventyYears; // print Unix time: Serial.println(epoch); //clear lcd lcd.clear(); // print the hour, minute and second: Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT) lcd.print("The UTC time is "); lcd.setCursor(0,1); Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) if ( ((epoch % 3600) / 60) < 10 ) { lcd.print('0'); } lcd.print(((epoch % 86400L) / 3600)+8); Serial.print(':'); lcd.print(':'); if ( ((epoch % 3600) / 60) < 10 ) { // In the first 10 minutes of each hour, we'll want a leading '0' Serial.print('0'); lcd.print('0'); } Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) Serial.print(':'); lcd.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) lcd.print(':'); if ( (epoch % 60) < 10 ) { // In the first 10 seconds of each minute, we'll want a leading '0' Serial.print('0'); lcd.print('0'); } Serial.println(epoch % 60); // print the second lcd.print(epoch % 60); } // wait ten seconds before asking for the time again delay(10000); } // send an NTP request to the time server at the given address unsigned long sendNTPpacket(IPAddress& address) { Serial.println("sending NTP packet..."); // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: udp.beginPacket(address, 123); //NTP requests are to port 123 udp.write(packetBuffer, NTP_PACKET_SIZE); udp.endPacket(); }
訂閱:
文章 (Atom)