2016年7月2日 星期六

Scicore製作多功能sensor (三)韌體篇

我一條程式都不會寫,只會複製貼上人家的東西,所以這東西搞的我超級辛苦的。以下就記錄一下我這個菜鳥的心路歷程。這個我超不懂,整個隔行如隔山的感覺,所以只能憑自己的經驗寫下不專業的心得。

首先先學會驅動每一個sensor,例如
壓力: http://jjpaid.blogspot.tw/2016/06/mpx4115.html
溫度:http://jjpaid.blogspot.tw/2015/12/arduino.html

要做到每一個單一sensor都可以運作不難,就是搜尋那個 sensor的名稱,通常就可以找到別人接線的方式和寫好的程式。照著接,然後程式複製貼上,該下載的library下載安裝一下,大概就贏了。

每一個sensor都會動之後,就可以保證每一個需要的library都有了,接著就是要組合了。首先看一下每一個程式大概就是分成幾個部分

1.呼叫一些library和定義腳位

2.設定

3.反覆執行的部份

以DHT11為例程式碼如下(來源

  1. #include <dht.h>   
  2.   
  3. #define dht_dpin 2 //定義訊號要從Pin A0 進來  
  4.   
  5. dht DHT;   
  6.   
  7. void setup(){   
  8. Serial.begin(9600);   
  9. delay(300);             //Let system settle   
  10. Serial.println("Humidity and temperature\n\n");   
  11. delay(700);             //Wait rest of 1000ms recommended delay before   
  12.                         //accessing sensor   
  13. }
  14.   
  15. void loop(){   
  16. DHT.read11(dht_dpin);   //去library裡面找DHT.read11  
  17. Serial.print("Humidity = ");   
  18. Serial.print(DHT.humidity);   
  19. Serial.print("% ");   
  20. Serial.print("temperature = ");   
  21. Serial.print(DHT.temperature);   
  22. Serial.println("C ");   
  23. delay(1000);            //每1000ms更新一次   
  24. }  

從01~05 就是在呼叫library, 定義腳位等等的。

從void setup後面到void loop() 也就是07~12這一段,就是一些設定

從15~24就是反覆執行的部份,例如每隔多少時間,就要輸出一次什麼樣的數據之類的。

各種sensor的寫法大概都差不多。所以我要做的事情很簡單,把每一個sensor的1, 2 3三個部分分別抓出來,所有sensor的1就集中在一起,2集中在一起,3也集中在一起。


這樣的話,就變成一個程式碼可以驅動所有的sensor啦。然後一次就把所有sensor的訊息全部都顯示出來。當然要注意一下腳位的問題,每個地方抓到的程式碼,腳位都不一定,如果遇到重複的部份,就要避開換另一個腳位。

這樣雖然可以驅動全部的sensor,但是顯然跑出來的訊息太多了,所以就要想辦法用電腦或平板告訴arduino,我想要看什麼訊息,讓arduino顯示我想要的。(參考資料

這部份就要先用一個serial.avalible() 去判定有沒有資料輸入,然後把輸入的資料用Serial.read()丟給一個預先設定好的變數。然後再用if判斷變數的數值,就能用這個條件判斷來告訴arduino我現在想看什麼。不過這個數值進位方式不同,假設你輸入1, 其實它讀到的是49, 輸入2,讀到的是50,依此類推,所以判斷的時候要注意這個事情。雖然概念是這樣,但是我中間卡關卡了好多次。大多出於對於程式碼的概念不熟悉。像是 =和 ==傻傻分不清楚; 各種() {} 要放哪邊這類的事,就搞掉我好多時間。

所以這邊的寫法就變成,setup loop() 裡面,共同重複的部份,例如要提取什麼變數,那就放到if判斷式的外面。 if判斷式只要負責 serial.monitor和lcd上要顯示的數值就好。我一開始是這樣想的,不過後來吃了大苦頭...


這些程式碼都解決了之後,基本上sensor都可以運作了,可是又出了一個怪問題,就是明顯每個sensor都delay太久才收數據。我原本想,環境數據不需要太快重複收,一秒收一次已經很快了。運動學部分,0.1秒收一個數據是差不多的;單擺的實驗,就得要更快,我直接設定1毫秒要收一個數據。

但是執行的結果,每個都大約一秒才收一個數據。找了非常非常久,把寫好的程式碼又拆了重組了好幾次才發現,DS18b20溫度計的讀取要經過requestTemperature和getTempByIndex這些指令,本身就會延遲時間。

因為我在兩個地方都有用到DS18b20來讀溫度數據,所以我本來把這些指令放到if判斷式外面,也就是不管我輸入什麼數字,這些事情都會一直loop重複做。結果就變成不管我輸入什麼數字,每一次這些指令的執行都造成延遲。

所以解決方法很簡單,就把這些東西通通搬進if裡面就好。



以下是完整程式碼
  1. #include <Wire.h>  // Arduino IDE 內建
  2. // LCD I2C Library,從這裡可以下載:
  3. // https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads
  4. #include <LiquidCrystal_I2C.h>
  5. // Set the pins on the I2C chip used for LCD connections:
  6. //                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
  7. LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // 設定 LCD I2C 位址
  8. //以上是驅動LCD的部分

  9. int va1 = 1; //用來設定選單的變數,預設為1 讓最開始就直接顯示環境變數

  10. #include <dht.h>   //抓溫溼度計的資料庫
  11. #define dht_dpin 3 //定義訊號要從Pin D3 進來
  12. dht DHT;

  13. // 匯入程式庫標頭檔
  14. //from http://playground.arduino.cc/Learning/OneWire
  15. #include <OneWire.h>

  16. //from https://github.com/milesburton/Arduino-Temperature-Control-Library
  17. #include <DallasTemperature.h>
  18. #include <Wire.h>

  19. // from http://arduino-info.wikispaces.com/file/view/LiquidCrystal_I2C1602V1.zip

  20. // Arduino數位腳位2接到1-Wire裝置
  21. #define ONE_WIRE_BUS 4

  22. // 運用程式庫建立物件
  23. OneWire oneWire(ONE_WIRE_BUS);
  24. DallasTemperature sensors(&oneWire);

  25. // HCSR04Ultrasonic/examples/UltrasonicDemo/UltrasonicDemo.pde
  26. #include <Ultrasonic.h>
  27. #define TRIGGER_PIN  12
  28. #define ECHO_PIN     13
  29. Ultrasonic ultrasonic(TRIGGER_PIN, ECHO_PIN);

  30. //運動學

  31. int x0=0;//一開始的位置
  32. int v0=0;//初速
  33. float temp=20.0;//溫度
  34. float c=(331.5+0.6*temp)/10000;//單位是每微秒幾公分
  35. float intervaltime=100;  //每幾毫秒測量一次
  36. float duration, distance,velocity,acceleration;

  37. //pH meter
  38. #define SensorPin A2            //pH meter Analog output to Arduino Analog Input 2
  39. #define Offset 0.00            //deviation compensate
  40. #define LED 11                  //不知道幹嘛用的腳位
  41. #define samplingInterval 20
  42. #define printInterval 800
  43. #define ArrayLenth  40    //times of collection
  44. int pHArray[ArrayLenth];   //Store the average value of the sensor feedback
  45. int pHArrayIndex=0;  


  46. void setup(){
  47. Serial.begin(9600);
  48. Serial.println("1.enviroment data");              //弄個選單出來
  49. Serial.println("2.temperature and pressure");
  50. Serial.println("3.Super sonic reflection time and distance");
  51. Serial.println("4.dynamic experiment");
  52. Serial.println("5.pendulum");
  53. Serial.println("6.pH meter");
  54. pinMode(TRIGGER_PIN, OUTPUT);      //超音波的輸出腳位
  55. pinMode(ECHO_PIN, INPUT);         //超音波的輸入腳位
  56. pinMode(LED,OUTPUT);               //這是給pH meter用的,但我不知道功能
  57. lcd.begin(16, 2);                  //起始lcd
  58. sensors.begin();                    //起始ds18b20溫度計
  59. }

  60. void loop(){
  61.      
  62.     DHT.read11(dht_dpin);   //去library裡面找DHT.read11
  63.  
  64.     float pressure = readPressure(A1);
  65.     float millibars = pressure/100;
  66.  
  67.  
  68. if (Serial.available() >0) {
  69.   va1 = Serial.read();
  70. }
  71. if (va1 == 49){
  72.     Serial.print("temperature = ");
  73.     Serial.print(DHT.temperature);
  74.     Serial.println("C ");
  75.     Serial.print("Humidity = ");
  76.     Serial.print(DHT.humidity);
  77.     Serial.print("% ");
  78.     Serial.print("  Pressure = ");
  79.     Serial.print(pressure/101300);
  80.     Serial.println(" atm");

  81.     lcd.clear();
  82.     lcd.setCursor(0, 0);
  83.     lcd.print("T=");
  84.     lcd.print(DHT.temperature);
  85.     lcd.print("C ");
  86.     lcd.print("H=");
  87.     lcd.print(DHT.humidity);
  88.     lcd.print("% ");
  89.     lcd.setCursor(0, 1);
  90.     lcd.print("P=");
  91.     lcd.print(pressure/101300);
  92.     lcd.print(" atm");
  93.     delay(1000);
  94. }
  95. if (va1 == 50){
  96.     sensors.requestTemperatures();               //這東西會吃很多時間,所以要擺進if裡面
  97.     float T1=sensors.getTempCByIndex(            //這東西會吃很多時間,所以要擺進if裡面
  98.     float T2=sensors.getTempCByIndex(1);         //這東西會吃很多時間,所以要擺進if裡面
  99.     Serial.print("  Pressure = ");
  100.     Serial.print(pressure);
  101.     Serial.println(" pascals");
  102.     Serial.print("T1:");
  103.     lcd.clear();
  104.     lcd.setCursor(0, 1);
  105.     lcd.print("P= ");
  106.     lcd.print(pressure);
  107.     lcd.print("pa");
  108.     lcd.setCursor(0, 0);
  109.     lcd.print("T1:");
  110.     if (T1 == -127)
  111.  
  112.     {Serial.print ("N/A");
  113.     lcd.print("N/A");
  114.     }
  115.     else
  116.     {
  117.       Serial.print(T1);
  118.       Serial.print(" C");
  119.       lcd.print(T1);
  120.       lcd.print("C");
  121.     }
  122.     Serial.print(" T2:");
  123.     lcd.print(" T2:");
  124.     if (T2 == -127)
  125.     {Serial.print ("N/A");
  126.     lcd.print ("N/A");
  127.     }
  128.     else
  129.     {
  130.       Serial.print(T2);
  131.       Serial.print(" C");
  132.       lcd.print(T2);
  133.       lcd.print("C");
  134.     }
  135. }

  136. if (va1 == 51){
  137.     sensors.requestTemperatures();               //這東西會吃很多時間,所以要擺進if裡面
  138.     float T1=sensors.getTempCByIndex(0);         //這東西會吃很多時間,所以要擺進if裡面
  139.     float T2=sensors.getTempCByIndex(1);         //這東西會吃很多時間,所以要擺進if裡面
  140.    float cmMsec;
  141.    long microsec = ultrasonic.timing();
  142.    cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM); // 計算距離,單位: 公分
  143.   Serial.print("t=");
  144.   Serial.print(microsec);
  145.   Serial.print("ms   ");
  146.   Serial.print("distance=");
  147.   Serial.print(cmMsec);
  148.   Serial.print("cm  ");
  149.   lcd.clear();
  150.   lcd.setCursor(0 ,0);
  151.   lcd.print("t=");
  152.   lcd.print(microsec);
  153.   lcd.print("ms ");
  154.   lcd.print("D=");
  155.   lcd.print(cmMsec);
  156.   lcd.print("cm");
  157.   lcd.setCursor(0, 1);
  158.   lcd.print("T:");
  159.   Serial.print("T:");
  160.   if (T1 == -127)
  161.     {Serial.println ("N/A");
  162.     lcd.print("N/A");
  163.     }
  164.     else
  165.     {
  166.       Serial.print(T1);
  167.       Serial.println(" C");
  168.       lcd.print(T1);
  169.       lcd.print("C");
  170.     }

  171. }
  172. if (va1 == 52){
  173.   digitalWrite(TRIGGER_PIN, HIGH);
  174.     delayMicroseconds(1000);
  175.     digitalWrite(TRIGGER_PIN, LOW);
  176.     duration = pulseIn(ECHO_PIN, HIGH);//超音波來回經過的時間,單位是微秒
  177.     distance = (duration/2) *c;//單位是公分
  178.     velocity=(distance-x0)/(intervaltime/1000);//每秒速度
  179.     acceleration=(velocity-v0)/(intervaltime/1000);//每秒加速度
  180.     x0 = distance;
  181.     v0 = velocity;
  182.   Serial.print(distance);
  183.   Serial.print(",");
  184.   Serial.print(velocity);
  185.   Serial.print(",");
  186.   Serial.println(acceleration);
  187.   lcd.clear();
  188.   lcd.print("D=");
  189.   lcd.print(distance);
  190.   lcd.print(" cm");
  191.   delay(intervaltime);
  192. }

  193. if (va1 == 53){
  194.   unsigned long time;
  195.   time=millis();
  196.   float cmMsec, inMsec;
  197.   long microsec = ultrasonic.timing();
  198.   cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM); // 計算距離,單位: 公分
  199.   if (cmMsec < 5)                          //只要距離小於五公分就把累計的秒數ㄒ出來
  200.   {
  201.     Serial.println(time);
  202.     lcd.clear();
  203.     lcd.setCursor(0,0);
  204.     lcd.print(time);
  205. }}

  206. if(va1 ==54){
  207.   static unsigned long samplingTime = millis();
  208.   static unsigned long printTime = millis();
  209.   static float pHValue,voltage;
  210.   if(millis()-samplingTime > samplingInterval)
  211.   {
  212.       pHArray[pHArrayIndex++]=analogRead(SensorPin);
  213.       if(pHArrayIndex==ArrayLenth)pHArrayIndex=0;
  214.       voltage = avergearray(pHArray, ArrayLenth)*5.0/1024;
  215.       pHValue = 3.5*voltage+Offset;
  216.       samplingTime=millis();
  217.   }
  218.   if(millis() - printTime > printInterval)   //Every 800 milliseconds, print a numerical, convert the state of the LED indicator
  219.   {
  220.   Serial.print("E");
  221.   Serial.print(" pH=");
  222.   Serial.println(pHValue,2);
  223.   lcd.clear();
  224.   lcd.print("pH= ");
  225.   lcd.print(pHValue,2);
  226.   digitalWrite(LED,digitalRead(LED)^1);
  227.   printTime=millis();
  228.   }
  229. }
  230. }
  231. //以下是pH meter的程式碼,要放在if外面,我也不知道是啥東西
  232. double avergearray(int* arr, int number){
  233.   int i;
  234.   int max,min;
  235.   double avg;
  236.   long amount=0;
  237.   if(number<=0){
  238.     Serial.println("Error number for the array to avraging!/n");
  239.     return 0;
  240.   }
  241.   if(number<5){   //less than 5, calculated directly statistics
  242.     for(i=0;i<number;i++){
  243.       amount+=arr[i];
  244.     }
  245.     avg = amount/number;
  246.     return avg;
  247.   }else{
  248.     if(arr[0]<arr[1]){
  249.       min = arr[0];max=arr[1];
  250.     }
  251.     else{
  252.       min=arr[1];max=arr[0];
  253.     }
  254.     for(i=2;i<number;i++){
  255.       if(arr[i]<min){
  256.         amount+=min;        //arr<min
  257.         min=arr[i];
  258.       }else {
  259.         if(arr[i]>max){
  260.           amount+=max;    //arr>max
  261.           max=arr[i];
  262.         }else{
  263.           amount+=arr[i]; //min<=arr<=max
  264.         }
  265.       }//if
  266.     }//for
  267.     avg = (double)amount/(number-2);
  268.   }//if
  269.   return avg;
  270. }
  271. //以下是MPX4115的程式碼,要放在if外面,我也不知道是啥東西
  272. /* Reads pressure from the given pin.
  273. * Returns a value in Pascals
  274. */
  275. float readPressure(int pin){
  276.     int pressureValue = analogRead(pin);
  277.     float pressure=((pressureValue/1024.0)+0.095)/0.000009;
  278.     return pressure;
  279. }
  280.