2016年10月15日 星期六

SciCore多功能sensor說明文件

這篇文章是關於把SciCore製作成國高中一些實驗課程可以用的sensor的說明文件。如果你有一台SciCore,或者有相關的零件想要自己組裝,或者你很厲害,會用eagle畫出電路圖的話,這篇文章你可能可以參考,也做一台。



這個多功能sensor設計的想法,基本上是把韌體整合在一起,不用每次想測不同東西,就要重灌一次韌體。學生使用的時候,只要電腦上有arduino,可以看到serial monitor就可以進行實驗收集數據。



雖然我會盡量把東西整理包起來,不過如果要自己弄一台的話,基本arduino的知識是需要知道一些的。像是library的安裝,一些接點、插孔大致上的意義等等。我想大概就是需要自己玩過三四種不同的sensor,然後有要用到library,這樣就夠了。

所有的sensor都做成USB的頭,可以隨時插拔,不同功能的切換,則是利用3pin滑動開關達成。實驗時一些取樣方式的調控,或者輸出的模式,則由可變電阻VR來調整。(主要是取樣的時間間隔)

一、元件
(一)SciCore本體

1.Arduino UNO

2.LCD IIC 1602

3.USB母頭三個

4. 5k可變電阻三個

5. 3pin的滑動開關三個

Scicore基本設計的想法請看阿簡的影片 https://www.youtube.com/watch?v=dAjlLz8IFAo

不過從影片可以看到USB母頭其實會有點晃,即使有壓條固定,USB插拔的時候還是會不太好操作。另外USB底座接點也比較弱,所以我後來用另外的元件重新處理了。


我買了直的USB母頭,和左邊那個接腳,焊起來就行。(焊接這件事情,大概花了我十幾個小時有!)

這樣焊好之後,把前面金屬部分和後面塑膠部分,都上一點熱融膠,就可以黏在壓克力上,很緊,插拔的時候很穩定。不過要小心,熱融膠如果從USB洞進去一些,就很難插拔了。


(二)接在麵包板上的sensor

1. DHT11 溫濕度感測:參考資料。這東西理論上很容易處理,不過我之前去光華商場要買的時候,發現兩個版本。一個是後面有紅色電路板的,一個沒有。價格一樣都是 NT80。那當然要買有電路板的啊!結果買回來測到的數據都有問題,下雨天記錄到的濕度是18%。五顆都一樣,於是就拿回去換了沒有電路板的,就運作正常了。

(三)外接的sensors與其他

1.氣壓 MPX4115:參考資料

2.酸鹼pH計:參考資料

3.超音波模組:參考資料

4.光敏電阻:參考資料

5.DS18b20溫度計:參考資料

6.RGB燈:我用的是四個接點的,RGB接點是正極,第四個接點是共用負極的。有些RGB燈會設計成共用正極,就不適用了。參考資料

7. USB轉3.5mm 耳麥公頭:這本來江老師是設計可以做成縮時攝影等功能,可以做成photoduino。不過我暫時用不到,就只拿這個東西作為頻率產生器的輸出。要做縮時攝影的話看這裡

二、接線方式

(一)LCD SDA, SCL部分,通常會接到A4, A5。不過板子有另一個地方可以接。下面圖的左上角,另外有SDA和SCL(圖片來源:https://forum.arduino.cc/index.php?topic=84190.0)
我會習慣插在左上角的位置,把Analog的腳位都空下來用。但其實A4, A5和左上角的SDA SCL根本是連通的。所以A4, A5就不是所有的sensor都可以接了,有些東西接上去,會影響LCD。

元件
元件腳位
連接腳位
LCD IIC1602
GND
VCC
SDA
SCL
GND
VCC
SDA
SCL



(二)USB們,USB的腳位定義,請參考。這四個腳位要看清楚相對位置,最好都按照原本的定義去接線,之後要找線比較容易。要轉接成PS/2的時候,也比較知道怎麼對應。

基本上,1號腳位都是正極,4號腳位都接地。主要訊號都是從2號腳位進去的。3號腳位的話,就是如果元件還需要多一個接點,就用下去。

像超音波模組,需要有一個輸出聲音的和一個輸入聲音的,就分別用3號腳位輸出,2號腳位就固定做輸入。這件事情由USB1擔任。

元件
元件腳位
連接腳位
USB1
1
2
3
4
VCC
D3
D4
GND

USB2 主要是接上VCC, GND,然後只有2號腳位把資料給A0接收。3號腳位接上A4, 目前沒用到。不過如果需要的話,就直接可以擴充來用了。

元件
元件腳位
連接腳位
USB2
1
2
3
4
VCC
A0
A4
GND

USB3是打算給RGB燈用的,所以1,2,3都接到digital了,4號仍然是GND。不過因為arduino數位腳位,可以變身成正極,所以當接上DS18b20溫度計的時候,就把1號腳位接上的D9變成正極,2號腳位接上的D10變成資料輸入,3號不理,4號仍然接地。就可以轉變功能了...

其實USB1也可以這樣做,變化就會多一點,不過我懶得改了...

元件
元件腳位
連接腳位
USB3
1
2
3
4
D9
D10
D11
GND

(三)可變電阻VR。可變電阻都三隻腳,兩邊分別接上正負極,調整旋鈕就等於控制了中間腳位的電位(基礎電學用上囉~)三個可變電阻中間的腳,分別就輸出給A1, A2, A3三個類比輸入。三個旋鈕設計成可以控制RGB的燈泡亮度。如果不是在用RGB的時候,R的旋鈕基本上都是控制取樣頻率,越順時針轉,取樣頻率就越高。不同實驗取樣頻率不同。像超音波實驗的話,最快是50ms取一個數據,最長是1秒;環境溫濕度壓力的話,最短是一秒,最長是五秒等等...

元件
元件腳位
連接腳位
VR1

中間
VCC
A1
GND

元件
元件腳位
連接腳位
VR2

中間
VCC
A2
GND

元件
元件腳位
連接腳位
VR3

中間
VCC
A3
GND

(四)3p滑動開關們。SciCore上面設計了四個3p滑動開關。可以作為功能調控使用。四個開關可以有十六種組合。我用不到那麼多,實際運用的只有3個。

本來三個接點的接法,是分別接上 正極,digital接腳,負極的。不過因為arduino有一個PULL UP指令,於是可以少接一條線。少接一條是一條啊!參考資料參考資料2

元件
元件腳位
連接腳位
3pin 滑動開關
S1


x
D7
GND

元件
元件腳位
連接腳位
3pin 滑動開關
S2


x
D8
GND

元件
元件腳位
連接腳位
3pin 滑動開關
S3


x
D12
GND

元件
元件腳位
連接腳位
3pin 滑動開關
S4


x
D13
GND

以上的線,可以通通用一頭母一頭公的杜邦端子線連接。但是線其實不少,看起來會長這樣。


 我後來把正負極焊起來,也花了不少時間,不過接線部分就單純一些




(四)USB轉PS/2腳位

這是外接sensor接線時需要知道的。
幾乎所有的sensor,都是三隻腳,分別是正極、負極還有資料輸出。所以基本上只要認得這三個孔就行。

再看一下PS/2的腳位意義。


4號孔是正極,3號是負極,就是連結到剛剛USB的1, 4號。不過不用管數字,反正這兩個孔就是正負極,都固定的。

最後一個孔,是資料輸入的地方,就是1號孔了,在負極的下方。這個孔連著USB的2號,是我們前面USB設定要輸入資料的地方。

如果還有第四隻腳怎辦?那就接到5號孔,就在負極上方。

總之,正負極反正一定要接的。然後訊號就從1號孔進去,如果還有一條線,就接到5號。


(五)韌體。有些基本設計的想法已經寫在前面了。如果library都準備好,下面程式碼理論上複製貼上,上傳就能運作了。我基本上不會寫程式,下面的程式碼都是複製貼上後,再做修改的。可能很亂或笨笨的,如果有神人願意再把程式碼修的更好,就太棒啦。

//SciCore 1.0

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

//以下呼叫library們

#include <dht.h>   //抓溫溼度計的資料庫
// 匯入程式庫標頭檔 
//from http://playground.arduino.cc/Learning/OneWire
#include <OneWire.h>

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

// HCSR04Ultrasonic/examples/UltrasonicDemo/UltrasonicDemo.pde
#include <Ultrasonic.h>


//數位接腳定義

#define dht_dpin 2     //定義DHT11的訊號要從Pin D2 進來 
#define DigitalIN 3    //第一個USB的二號腳位連接到D3,作為數位訊號輸入
#define DigitalOUT 4    //第一個USB的三號腳位連接到D4,作為數位訊號輸出

#define Switch1 7   //D5, D6, D7作為switch的切換腳位,是digital input_pullup
#define Switch2 8
#define Switch3 12
#define Switch4 13

#define LEDR 9      //D9, 10, 11為PWM,作為三個可變電阻數值的輸入腳位

#define LEDG 10
#define LEDB 11

#define SensorPin A0            //定義SensorPin是0號類比腳位

dht DHT; 

// DS18B20溫度
OneWire oneWire(LEDG);
DallasTemperature sensors(&oneWire);


Ultrasonic ultrasonic(DigitalOUT, DigitalIN);

//運動學

unsigned long time;
float intervaltime;  //測量的時間間隔(由控制紅光的電阻決定)
float x0, v0,cmMsec, duration, distance,velocity,acceleration; //定變數
int showtime; //要在LCD上呈現取樣時間間隔,要整數

//pH meter

#define Offset 0.00            //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;    


int potPinR = 1;
int potPinG = 2;
int potPinB = 3;
  
void setup(){   
Serial.begin(9600);
pinMode(DigitalOUT, OUTPUT);      //數位輸出腳位
pinMode(DigitalIN, INPUT);         //數位輸入腳位

  pinMode(Switch1, INPUT_PULLUP); //檔位輸入的三個腳位
  pinMode(Switch2, INPUT_PULLUP);
  pinMode(Switch3, INPUT_PULLUP);
  
lcd.begin(16, 2);                  //起始lcd

sensors.begin();                    //起始ds18b20溫度計

delay(5000);

//RGB setup
}
int r = 0;
int g = 0;
int b = 0;

void loop(){

int S1 = digitalRead(Switch1);
int S2 = digitalRead(Switch2);
int S3 = digitalRead(Switch3);

      
   DHT.read11(dht_dpin);   //去library裡面找DHT.read11  
   
    float pressure = readPressure(A0);   //壓力數值從A0讀取
    float millibars = pressure/100;
//switch 說明 000=0, 100=1, 010=2, 001=3, 110=4, 101=5, 011=6, 111=7
if  ((S1 == 0)&&(S2 ==0) && (S3==0)){
intervaltime = analogRead(potPinR);
intervaltime = map (intervaltime , 900, 0, 500, 4000);
  Serial.println ("000 = menu");
  Serial.println ("100 = enviroment data");
  Serial.println ("010 = temperature and pressure");
  Serial.println ("001 = supersonic");
  Serial.println ("110 = RGB light and LUX");
  Serial.println ("101 = Frequency output");
  Serial.println ("011 = pH meter");
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print ("000=menu");
  lcd.setCursor(0,1);
  lcd.print("100=enviroment data");
  delay (intervaltime);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print ("010=temp & press");
  lcd.setCursor(0,1);
  lcd.print("001=supersonic");
  intervaltime = analogRead(potPinR);
  intervaltime = map (intervaltime , 900, 0, 500, 4000);
  delay (intervaltime);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print ("110=RGB 011=pH");
  lcd.setCursor(0,1);
  lcd.print("101=Freq generator");
  intervaltime = analogRead(potPinR);
  intervaltime = map (intervaltime , 900, 0, 500, 4000);
  delay (intervaltime);
}


//1.環境溫溼度與壓力,溫溼度來自於DHT, 壓力來自於MPX4115
if ((S1 == 1)&&(S2 ==0) && (S3==0)){
intervaltime = analogRead(potPinR);
intervaltime = map (intervaltime , 900, 0, 1000, 5000);
    Serial.print(DHT.temperature);   
    Serial.print(","); 
    Serial.print(DHT.humidity);
    Serial.print(",");
    Serial.println(pressure/101300);

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("T=");   
    lcd.print(DHT.temperature);   
    lcd.print("C "); 
    lcd.print("H=");
    lcd.print(DHT.humidity);
    lcd.print("% ");
    lcd.setCursor(0, 1);
    lcd.print("P=");
    lcd.print(pressure/101300);
    lcd.print(" atm");
    delay(5000);
}

//2兩個溫度和壓力,溫度用外接的溫度計,壓力還是MPX4115

if ((S1==0)&&(S2==1)&&(S3==0)){
  pinMode (LEDG, INPUT);
  pinMode (LEDR, OUTPUT);
  digitalWrite(LEDR, HIGH);


    intervaltime = analogRead(potPinR);
    intervaltime = map (intervaltime , 900, 0, 100, 10000);
    sensors.requestTemperatures();                                  //這東西會吃很多時間,所以要擺進if裡面
    float T1=sensors.getTempCByIndex(0);                            //這東西會吃很多時間,所以要擺進if裡面
    float T2=sensors.getTempCByIndex(1);                            //這東西會吃很多時間,所以要擺進if裡面
    time=millis();

    Serial.print(time);
    Serial.print(pressure/1000);
    Serial.print(",");
    Serial.print(T1);
    Serial.print(",");
    Serial.println(T2);
    
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print("P= ");
    lcd.print(pressure);
    lcd.print("pa");
    lcd.setCursor(0, 0);
    lcd.print("T1:");
    if (T1 == -127)
    
    {
    lcd.print("N/A");
    }
    else
    {
      lcd.print(T1);
      lcd.print("C");
    }
    lcd.print(" T2:");
    if (T2 == -127)
    {
    lcd.print ("N/A");
    }
    else
    {
      lcd.print(T2);
      lcd.print("C");
    }
    delay(intervaltime);
}
//3.超音波測距
if ((S1==0)&&(S2==0)&&(S3==1)){
pinMode (LEDG, INPUT);
pinMode (LEDR, OUTPUT);
digitalWrite(LEDR, HIGH);

intervaltime = analogRead(potPinR);
intervaltime = map (intervaltime , 1000, 0, 50, 1000);
showtime = intervaltime;
sensors.requestTemperatures();                             //這東西會吃很多時間,所以要擺進if裡面
float T1=sensors.getTempCByIndex(0);                  //這東西會吃很多時間,所以要擺進if裡面
float cmMsec;
long microsec = ultrasonic.timing();
DHT.read11(dht_dpin);   //去library裡面找DHT.read11

float c;

if (T1==-127){

 c=(331.5+0.6*DHT.temperature)*0.0001;
}
else
{
 c=(331.5+0.6*T1)*0.0001;
}
digitalWrite(DigitalOUT, HIGH);
delayMicroseconds(1000);
digitalWrite(DigitalOUT, LOW);

time=millis();
distance = (microsec/2)*c;
distance +=(microsec/2)*c;
distance +=(microsec/2)*c;
distance +=(microsec/2)*c;
distance +=(microsec/2)*c;
distance =distance/5;


  Serial.print("Time=");
  Serial.print(time);
  Serial.print(", ");
  Serial.print("dt =");
  Serial.print(microsec); 
  Serial.print ("distance= ");
  Serial.print(distance);
  Serial.print(", Temp=");
  if (T1==-127){
Serial.println(DHT.temperature);
}
else
{
Serial.println(T1);
}

  lcd.clear();
  lcd.setCursor(0 ,0);
  lcd.print("t=");
  lcd.print(microsec);
  lcd.print("ms ");
  lcd.setCursor(8,0);
  lcd.print("D=");
  lcd.print(distance);
  lcd.print("cm");
  lcd.setCursor(0, 1);
  lcd.print("T:");
  if (T1 == -127)
    {
    lcd.print(DHT.temperature);
lcd.print("C");
    }
    else
    {
      lcd.print(T1);
      lcd.print("C");
    }
    lcd.print (" dt");
    lcd.print (intervaltime);
  delay(intervaltime);
}
//4.RGB光源輸出極光敏電阻測光實驗
if ((S1==1)&&(S2==1)&&(S3==0)){     
  int pr = analogRead(A0);
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);

  r = analogRead(potPinR);
  r = map(r, 0, 900, 0, 200);
  analogWrite(LEDR, r);

  g = analogRead(potPinG);
  g = map(g, 0, 900, 0, 200);
  analogWrite(LEDG, g);
  
  b = analogRead(potPinB);
  b = map(b, 0, 900, 0, 200);
  analogWrite(LEDB, b);

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("R= ");
  lcd.print(r);
  lcd.print(" G= ");
  lcd.print(g);
  lcd.setCursor(0,1);
  lcd.print("B= "); 
  lcd.print(b); 
lcd.print(" LUX =");
lcd.print(pr);
  
  Serial.print("R= ");
  Serial.print(r);
  Serial.print("  G= ");
  Serial.print(g);
  Serial.print("  B= "); 
  Serial.print(b); 
Serial.print(" LUX =");
Serial.println(pr);
delay(1000);
}
//5.頻率產生器
if ((S1 == 1)&&(S2 ==0) && (S3==1)){
pinMode (LEDG, OUTPUT);
pinMode (LEDR, OUTPUT);
digitalWrite(LEDR, HIGH);

int frequency;
  r = analogRead(potPinR);                      
  r +=analogRead(potPinR);
  r +=analogRead(potPinR);                      
  r +=analogRead(potPinR);                      
  r +=analogRead(potPinR);
  r +=analogRead(potPinR);
  r +=analogRead(potPinR);
  r +=analogRead(potPinR);
  r +=analogRead(potPinR);
  r +=analogRead(potPinR);
  r=r/10;
  r = map(r, 0, 1023, 0, 20000);

  g = analogRead(potPinG);
  g +=analogRead(potPinG);
  g +=analogRead(potPinG);
  g +=analogRead(potPinG);
  g +=analogRead(potPinG);
  g +=analogRead(potPinG);
  g +=analogRead(potPinG);
  g +=analogRead(potPinG);
  g +=analogRead(potPinG);
  g +=analogRead(potPinG);
  g=g/10;
  g = map(g, 0, 1023, 0, 200);
  
  b = analogRead(potPinB);
  b +=analogRead(potPinB);
  b +=analogRead(potPinB);
  b +=analogRead(potPinB);
  b +=analogRead(potPinB);
  b +=analogRead(potPinB);
  b +=analogRead(potPinB);
  b +=analogRead(potPinB);
  b +=analogRead(potPinB);
  b +=analogRead(potPinB);
  b +=analogRead(potPinB);
  b=b/10;
  b = map(b, 0, 1023, 0, 20);
  
  Serial.print("R= ");
  Serial.print(r);
  Serial.print("  G= ");
  Serial.print(g);
  Serial.print("  B= "); 
  Serial.println(b);  
  
frequency = r + g +b;
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("F= ");
  lcd.print(frequency);
  lcd.setCursor(8,0);
lcd.print (" HZ");
  tone (LEDG, frequency, 500); 
}
//6.pH meter
if((S1==0)&&(S2==1)&&(S3==1)){    

pinMode(LEDR,OUTPUT);

  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(" pH=");
  Serial.println(pHValue,2);
  lcd.clear();
  lcd.print("pH= ");
  lcd.print(pHValue,2);
  digitalWrite(LEDR,digitalRead(LEDR)^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;

//7.導電度

}

/* Reads pressure from the given pin.
* Returns a value in Pascals
*/
float readPressure(int pin){
    int pressureValue = analogRead(pin);
    float pressure=((pressureValue/1024.0)+0.095)/0.000009;
    return pressure;
}


(六)後續及延伸:上面只有設計7種功能,用到3個Switch的話,其實可以有八種功能。接下來或許會想再把縮時攝影和碼錶計時的功能加進去。還有高斯計、加速度計也是想放進去的東西,不過可能會還要好一陣子才能做出來吧。

感謝江宏仁老師設計的SciCore本體,感謝阿簡一直被我煩著問問題,感謝網路上諸多神人願意把他們的知識心血放在網路上,讓我可以自學。

張貼留言