2013年3月17日 星期日

樹莓派 & 試算表 = Free Data Logger

上一篇「讓 Respberry Pi 告訴你溫濕度(DHT11) 」已經可以成功取得溫濕度的資訊,但是要如何利用免費的資源去記錄下這些資訊?

而本篇將實作這個功能,只要稍稍擴充前一篇中的監聽程式,加上 "DHT Humidity Sensing on Raspberry Pi with GDocs Logging" 裡的程式 "Adafruit_DHT_googledocs.ex. py",讓 Raspberry Pi (RPi)可以自動連結 google 雲端試算表,並且記錄溫濕度、露點等等於試算表中。

等同於擁有一個個人而且免費的雲端 Data Logger,只要連接一些適當的感應器,再加上低功耗的 RPi,就可以記錄周遭微氣候的變化。


  • Arduino
    Arduino程式跟上一篇 PART I 幾乎相同,只有多了一個 Serial.flush 指令,用來清空緩衝區的數值,以及延長等待時間至 20 秒。
    燒錄程式之前,記得先把 "DHT11-Humidity-TempSensor" 網頁底下的 DHT11.zip 加到 Sketch 的函式庫(library)中。

    Sketch code
    /* YourDuino.com Example Software Sketch
       DHT11 Humidity and Temperature Sensor test
       Credits: Rob Tillaart
       http://arduino-direct.com/sunshop/index.php?l=product_detail&p=162
       terry@yourduino.com */
       
    /*-----( Import needed libraries )-----*/
    #include 
    
    /*-----( Declare objects )-----*/
    dht11 DHT11;
    
    /*-----( Declare Constants, Pin Numbers )-----*/
    #define DHT11PIN 2
    
    void setup()   /*----( SETUP: RUNS ONCE )----*/
    {
      Serial.begin(9600);
    }/*--(end setup )---*/
    
    void loop()   /*----( LOOP: RUNS CONSTANTLY )----*/
    {
       Serial.println("\n");
    
      int chk = DHT11.read(DHT11PIN);
    
      Serial.print("Read sensor: ");
      switch (chk)
      {
        case 0: Serial.println("OK"); break;
        case -1: Serial.println("Checksum error"); break;
        case -2: Serial.println("Time out error"); break;
        default: Serial.println("Unknown error"); break;
      }
    
      Serial.print("Humidity (%): ");
      Serial.println((float)DHT11.humidity, 2);
    
      Serial.print("Temperature (oC): ");
      Serial.println((float)DHT11.temperature, 2);
    
      Serial.print("Temperature (oF): ");
      Serial.println(Fahrenheit(DHT11.temperature), 2);
    
      Serial.print("Temperature (K): ");
      Serial.println(Kelvin(DHT11.temperature), 2);
    
      Serial.print("Dew Point (oC): ");
      Serial.println(dewPoint(DHT11.temperature, DHT11.humidity));
    
      Serial.print("Dew PointFast (oC): ");
      Serial.println(dewPointFast(DHT11.temperature, DHT11.humidity));
    
      Serial.flush();
      delay(20000);
    }/* --(end main loop )-- */
    
    /*-----( Declare User-written Functions )-----*/
    //
    //Celsius to Fahrenheit conversion
    double Fahrenheit(double celsius)
    {
            return 1.8 * celsius + 32;
    }
    
    //Celsius to Kelvin conversion
    double Kelvin(double celsius)
    {
            return celsius + 273.15;
    }
    
    // dewPoint function NOAA
    // reference: http://wahiduddin.net/calc/density_algorithms.htm 
    double dewPoint(double celsius, double humidity)
    {
            double A0= 373.15/(273.15 + celsius);
            double SUM = -7.90298 * (A0-1);
            SUM += 5.02808 * log10(A0);
            SUM += -1.3816e-7 * (pow(10, (11.344*(1-1/A0)))-1) ;
            SUM += 8.1328e-3 * (pow(10,(-3.49149*(A0-1)))-1) ;
            SUM += log10(1013.246);
            double VP = pow(10, SUM-3) * humidity;
            double T = log(VP/0.61078);   // temp var
            return (241.88 * T) / (17.558-T);
    }
    
    // delta max = 0.6544 wrt dewPoint()
    // 5x faster than dewPoint()
    // reference: http://en.wikipedia.org/wiki/Dew_point
    double dewPointFast(double celsius, double humidity)
    {
            double a = 17.271;
            double b = 237.7;
            double temp = (a * celsius) / (b + celsius) + log(humidity/100);
            double Td = (b * temp) / (a - temp);
            return Td;
    }
    
    /* ( THE END ) */

  • Raspberry Pi
    從 Arduino 送出溫濕度以及相關的數據到 google 試算表這中間,需要由 RPi 擔任閘道(gateway)的角色,由 "Connecting to Google Docs" 一文,可以知道這部份的工作,大略分為三個部分:

    1. Gspread
      首先必須先安裝 Gspread 函式庫,就可以輕鬆的以寫好的函式登入 google 試算表,自動把數據寫到裡面。
      pi@raspberrypi ~ $ wget http://pypi.python.org/packages/source/g/gspread/gspread-0.0.13.tar.gz#md5=d413ad08805f3f0a1e9d5f9bebe5d35b
      pi@raspberrypi ~ $ tar -zxvf gspread-0.0.13.tar.gz
      pi@raspberrypi ~ $ cd gspread-0.0.13
      pi@raspberrypi ~ $ sudo python setup.py install
    2. python 程式
      程式很簡短也不難,首先需先填入 google 帳戶資料以登入連結雲端文件。使用第一個 while 迴圈清除掉 Arduino 丟回來的第一筆不規則資料,第二個 while 迴圈讀取數據並使用 re.search 擷取所需要的數據資料,最後這裡把 Sensor 狀態、溫濕度(攝氏、華氏)、露點丟到試算表上頭。
    1. google 試算表
      建立試算表,填入抬頭,並刪掉以下所有得儲存格,程式丟出數據後會自動新增儲存格,而且要注意兩次輸入間隔不要小於兩秒,否則會被擋掉。

     python code

    import serial
    import time
    import re
    import subprocess
    import sys
    import datetime
    import gspread
    # ===========================================================================
    # Google Account Details
    # ===========================================================================
    
    # Account details for google docs
    email       = 'google帳號'
    password    = ''
    spreadsheet = '試算表名稱'
    
    # ===========================================================================
    # Example Code
    # ===========================================================================
    
    
    # Login with your Google account
    try:
      gc = gspread.login(email, password)
    except:
      print "Unable to log in.  Check your email address/password"
      sys.exit()
    
    # Open a worksheet from your spreadsheet using the filename
    try:
      worksheet = gc.open(spreadsheet).sheet1
      # Alternatively, open a spreadsheet using the spreadsheet's key
      # worksheet = gc.open_by_key('0BmgG6nO_6dprdS1MN3d3MkdPa142WFRrdnRRUWl1UFE')
    except:
      print "Unable to open the spreadsheet.  Check your filename: %s" % spreadsheet
      sys.exit()
    
    
    # Set Channel 
    ser = serial.Serial('/dev/ttyACM0', 9600)
    
    # buffer clear
    start_time = time.time()
    while 1 :
        output = ser.readline()
        print output
        
        if time.time() - start_time > 15:
         print time.time() - start_time
         break
    
    # Reading sensor data
    while 1 :
        output = ser.readline()
        match = re.search("sensor\:\s(\w+)", output)
        if match:
            status = match.group(1)
    #        print match.group(1)
            continue
            
        match = re.search("Humidity\s\(\%\)\:\s(\d+)", output)
        if match:
            Humidity = float(match.group(1))
    #        print "Humidity:",Humidity
            continue
        
        match = re.search("Temperature\s\(oC\)\:\s(\d+)", output)
        if match:
            TempC = float(match.group(1))
    #       print "Temp:", float(match.group(1)),"oC"
            continue
    
        match = re.search("Temperature\s\(oF\)\:\s(\d+)", output)    
        if match:
            TempF = float(match.group(1))
    #        print "Temp:", float(match.group(1)),"oF"
            continue   
        
        match = re.search("Temperature\s\(K\)\:\s(\d+)", output)    
        if match:
            TempK = float(match.group(1))
    #        print "Temp:", float(match.group(1)),"K"
            continue  
        
        match = re.search("Dew\sPoint\s\(oC\)\:\s(\d+)", output)    
        if match:
            Dew = float(match.group(1))
    #        print "Dew:", float(match.group(1)),"oC"
            continue  
        
        match = re.search("Dew\sPointFast\s\(oC\)\:\s(\d+)", output)    
        if match:
            DewFast = float(match.group(1))
    #        print "Dew PointFast:", float(match.group(1)),"oC"
        else:
            continue
    # Insert data    
        try:
            values = [datetime.datetime.now(), status, Humidity, TempC, TempF, TempK, Dew, DewFast]
            worksheet.append_row(values)
        except:
            print "Unable to append data.  Check your connection?"
            sys.exit()
        
    #    print status, Humidity, TempC, TempF, TempK, Dew, DewFast
        time.sleep(0.5)
不過本篇的程式做法並不符合效率,如果需要承接的感測值太多,實際上會盡量讓前端 Arduino 輸出的資料格式更為精簡,使 RPi 以最少的循環次數讀完所有值,一方面可以加速執行效率,另一方面則降低能量的耗損。

最後下圖就是以試算表內製作圖表的功能,所繪製的溫濕度圖,不訪試著變動解析度,來觀察這兩天(3/16 ~ 3/17)房間內溫濕度的變化。



沒有留言:

張貼留言