2013年3月9日 星期六

讓 Respberry Pi 告訴你溫濕度(DHT11)

本篇以 Arduino uno 控制版連接 DHT11 溫濕感應元件,讓 Raspberry Pi (RPi)透過 pySerial 與 Arduino 進行通訊,並且利用網路上豐富的 Arduino 與 RPi 資源,使用一些已經寫好的程式範例,讓溫濕度的資訊顯示在 RPi 上,所使用的材料如下;
  • Arduino uno  
  • Raspberry Pi 
  • DHT11 溫濕度感測器
  • 麵包版
  • 數條連接線

PART I  Arduino 連接溫濕度感測元件(DHT11)
  1. 線路圖參考 "Connecting to aDHTxx Sensor | adafruit learning system",很容易,注意一下腳極跟電阻應該不會有問題。
  2. 程式是使用 "Digital output temperature and humidity sensor DHT11",當中包涵攝氏華氏的轉換,與露點的計算。
    • 關於 Arduino Library 的概念與安裝,在「Arduino Library介紹與安裝 | I'm a C noob」當中敘述的相當清楚,需注意 library 最好是使用這個網頁下面所附的 "DHT11.zip",就筆者本身的經驗,似乎換了其他板本 DHT11 library 會出現問題。
    • 只要把上一步的檔案放到正確的路徑中,然後重開 sketch,就可以引用 DHT11 函式庫,接著寫入以下指令並燒錄到 Arduino中。
      /* 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 <dht11.h>
      
      /*-----( Declare objects )-----*/
      dht11 DHT11;
      
      /*-----( Declare Constants, Pin Numbers )-----*/
      #define DHT11PIN 2
      
      void setup()   /*----( SETUP: RUNS ONCE )----*/
      {
        Serial.begin(9600);
        Serial.println("DHT11 TEST PROGRAM ");
        Serial.print("LIBRARY VERSION: ");
        Serial.println(DHT11LIB_VERSION);
        Serial.println();
      }/*--(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));
      
        delay(2000);
      }/* --(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 ) */
      
  3. 打開 serial monitor,可以見到濕度(Humidity),溫度(Temperature ),露點等資訊。

PART II 使用 pySerial 監聽溫濕度值

當 Arduino 獲得感測器數據時,Raspberry Pi 需要藉著 pySerial 與 Arduino 建立通訊介面,才能提取所需要的數據,而「利用Arduino和Raspberry Pi 学习电子编程 」一文不僅詳細記錄 pySerial 安裝過程,還提到些需注意的地方。所以這邊就盡量精簡地把步驟記錄下來。
  1. 本篇安裝的版本為 pySerial 2.6,進入網頁後將檔案的位址複製下來
  2. 使用 ssh 連線到 RPi,鍵入以下指令,將剛剛的複製網址貼在 wget 後方,輸入之後開始下載 pySerial 2.6
    pi@raspberrypi /etc/workspace $ sudo wget https://pypi.python.org/packages/source/p/pyserial/pyserial-2.6.tar.gz#md5=cde799970b7c1ce1f7d6e9ceebe64c98
  3. 解壓縮檔案,進行安裝。
    pi@raspberrypi /etc/workspace $ sudo gunzip pyserial-2.6.tar.gz
    pi@raspberrypi /etc/workspace $ tar -xvf pyserial-2.6.tar
    pi@raspberrypi /etc/workspace $ cd pyserial-2.6
    pi@raspberrypi /etc/workspace/pyserial-2.6 $ sudo python setup.py install
  1. 確認連接端口。在 USB 沒有接上 Arduino 的狀況下,輸入以下指令,會列出所有接口名稱,接著馬上插上 USB 連接線,再輸入一次指令,多出來的那個接口即是 Arduino 所使用,記下它,下個步驟會使用到。
    pi@raspberrypi /etc/workspace $ ls /dev/tty*
  2. 參考 "Raspberry Pi and Arduino | Dr. Monk's DIY Electronics Blog",建立 python 的文件,並寫入以下代碼,存為 helloworld.py。之後同步到 RPi 上的資料夾內。(同步方法可以參考這篇「與 Raspberry Pi 同步開發環境」)
    import serial
    ser = serial.Serial('/dev/ttyACM0', 9600)
    while 1 :
        data = ser.readline()
        print(data) 
  3. 打開 ssh 執行 helloworld.py,順利的話,終端機畫面會跟 Arduino serial monitor 顯示的結果一樣。
    pi@raspberrypi /etc/workspace $ python helloworld.py

如果有問題的話,可以參考 "Arduino + Raspberry Pi – Measuring Temperature and Humidity" 的作法,試著修改權限,如果是端口問題的話,可以修改 .rule 檔案。
pi@raspberrypi ~ $ sudo usermod -a -G dialout pi 
再者若跟作者一樣,沒有顯示 ttyACM0 的 USB 端口,作者解決的方式是新增一檔案 55-odd.rules 到目錄 /etc/udev/rules.d/ 底下,並寫入以下代碼;
DERNEL == "ttyACM0",
SYMLINK += "ttyS7"

事實上 RPi 也具備輸入輸出接口(GPIO, General Purpose I/O),可以提供程式進行控制,目前有許多這方面的開發正在進行,不過目前比起 Arduino 背後的資源,還是稍嫌不足,而且擴充版價格也較高,但話說回來 Raspberry Pi 運算速度快、擴充性優異,兩者似乎有著不同的應用範圍。


沒有留言:

張貼留言