2016年7月31日 星期日

「Camera」美食照片


































美食照一張

拍攝資訊如下:

光圈 F3.2

快門 60ms

相機 Panasonic Lumix DMC-GF2

鏡頭 f/1.4 25mm panasonic leica

閃光: 無

[名詞定義]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
  • 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的電壓值,所以重新依據

目前的狀況校正。(手邊沒有其他東西可用時才用這招,不是個好方法)


ThingSpeak


建立一個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 channels 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.實際運作腳位圖



實際運作照片






Code


注意一下,這裡的#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();
}