在本課程中,我們將使用一個多功能模組,協助Raspberry Pi同時測量溫度、濕度和氣壓三種參數。
該專案需要焊接!如上圖所示,該零件帶有一個單獨的排針,因此我們需要將其焊接到麵包板上。
使用BME280的溫度/濕度/壓力感測器模組套件BME280 (日語)
憑藉搭載Bosch Sensortec BMP280的感測器模組,您可以同時測量溫度、濕度和氣壓。該模組還可以透過I2C或SPI與微控制器通信。
我們將在本課程中使用這款超精巧型AE-BME280壓力感測器(尺寸:16x10mm)。該模組採購於日本零件供應商Akizuki Denshi,您也可以使用具有相同晶片的Adafruit BME280模組。請注意,Adafruit的引腳佈局略有不同,因此連線時請務必參考其技術規格書。
BME280——一塊位於元件中心的微小銀晶片——承擔了大部分工作。該模組表面上還有一個非常小的開口,其作用是讀取數值,因此切勿遮蓋這個開口。
AE-BME280板和排針買來就是分開的。連接Raspberry Pi的最簡單方法就是將兩者組裝在一起,如圖1所示。這需要焊接。我購買的排針有10個引腳,但是連接AE-BME280只需6個引腳,所以 應剪掉第6個引腳後面的排針。
技術規格請參見AE-BME280的日語手冊。
您可以用I2C或SPI進行通訊。由於我之前已經用過SPI,所以這次我將嘗試使用I2C。
I2C – 維基百科
I2C (積體電路匯流排)是一種由Philips Semiconductor(現為NXP Semiconductors)發明的序列匯流排。I2C代表I-平方-C。由於純文字環境中的字元限制,我們將其稱為I2C或IIC。該協定通常用於將低速週邊IC連至主機板、嵌入式系統和手機等裝置。
連接I2C或SPI是不同的,所以我們必須注意引腳接法。請參閱AE-BME280 技術規格書 (日語) 或 Adafruit BME280 技術規格書 (英語)。
如圖2所示,使用I2C時,我們需要焊接 J3 跳線。我們必須在此跳線處填充焊料。請注意,這僅適用於AE-BME280晶片。Adafruit晶片不需要設定此跳線。
現在,是時候焊接了!首先應加熱烙鐵。
焊料 – 維基百科
焊料是一種主要由鉛和錫組成的合金,與電烙鐵配合使用。主要用於連接金屬元件,以及將電氣元件焊接到電路板上。根據其成分,焊料會在4-10度時變成超導體。
我在這個項目中用的是這種焊料。這種焊料非常柔韌,容易洩漏,因此只能一點一點地送錫。我們還應準備一個吸錫器,以防萬一。如果送錫過多,可以用吸錫器吸掉多餘的焊料,所以要小心!
首先,我們應焊接圖4所示的J3跳線。J3與相鄰引腳靠的很近,所以要小心不要焊接在一起。送錫時要小心仔細!
下一步是連接排針。我從電路板背面焊接排針。引腳之間的間隙非常小,因此很難將電烙鐵塞入。電路板兩端引腳的焊料結塊範例如圖5所示。
焊接技巧是先用烙鐵頭稍稍加熱引腳,然後再送錫。如果烙鐵尖過熱,焊料會燃燒並形成結塊。因此,我建議焊完一個引腳後,從電源插座上拔下電烙鐵並冷卻後再焊接下一個引腳。不必著急。小心不要將引腳焊接在一起。慢慢來!
完成!排針焊接完畢,現在電路板垂直插在麵包板上。
焊接時,我不小心碰到了排針末端並稍微燙了一下,但幸運的是這並沒有影響讀數。
現在,讓我們將Raspberry Pi連接到AE-BME280。連接示意圖如圖7所示,因為我們使用的是I2C通信。
在Raspberry Pi上,“SDA”連至GPIO2(引腳3),“SCL”連到GPIO3(引腳5)。VDD連接到引腳1,這樣前3個GPIO引腳按順序排列(參見圖8)。接線時容易記憶。
我將AE-BME280上的引腳5(SDO)連至GND,您也可以將其連至VDD。(請注意,如果這樣做,資料收集位址會發生變化)。
Raspberry Pi默認禁用I2C。啟用I2C的方法與啟用SPI的方法相同,請參閱上一個課程:Raspberry Pi WebIOPi 物聯網,類比輸入程式設計。在功能表中選擇[Preferences] – [Raspberry Pi Configuration],然後打開“Settings ”螢幕。
必須重新開機系統該設定才能生效,因此請在快顯視窗中按一下“Yes”。重啟後,I2C傳輸已啟用。
(OS: 2015年11月21日發佈的Raspbian Jessie版本)
接下來,我們安裝使用I2C所需的套裝軟體。我們需要在命令列中使用[i2c-tools],在Python中使用[python-smbus]。
安裝命令
sudo apt-get install i2c-tools
sudo apt-get python-smbus
如果運行命令“i2cdetect”,系統會檢測透過I2C方式連接的所有裝置。
sudo i2cdetect -y 1
注:選擇I2C位址時,如果電路板(SDO)上的引腳5連至GND,那麼默認為[0x76];如果連至VDD,則默認為[0x77]。
我將SDO連接到GND,因此顯示0x76,但如果連至VDD,則會顯示0x77。
此外,i2Cdetect命令中的最後一個參數取決於Raspberry Pi的版本。Revision 1(2012年10月14日之前發貨的Raspberry Pi Model B)使用0,而Revision 2指定使用1。我用的是Raspberry Pi 2 Model B,因此參數值為1。
我運行程式時發生了以下錯誤:
發生錯誤後,我查看了dev目錄,看到有一個名為[i2c-1]而不是[i2c-0]的檔。我無法打開這個檔,但我認為該檔包含記錄的測量值。
sudo i2cdump -y 1 0x76
上圖是我用i2Cdump命令輸出暫存器值的結果。看起來裝置讀取了很多數值,但是我無法分辨哪些值來自哪裡以及為什麼是這樣子的。
我很難對這些值進行轉換/計算,所以我從Switch Science程式館中借用了一些Python原始程式碼:
按一下右上角的“Download ZIP”連結,將Python27資料夾中的“bme_280_sample.py”文件放在Raspberry Pi的相應位置。注意:此程式需要“python-smbus”套裝軟體才能運行。
python /home/pi/bme280_sample.py
準備工作完成後,運行程式!成功運行之後,您會看到三行輸出:溫度、壓力和濕度。
注意:您必須具有root許可權才能運行smbus套裝軟體。和往常一樣,我嘗試用PHP運行,但是出現錯誤,所以我放棄了。bme_280_sample.py 原始程式碼中有很多複雜的計算,雖然我Python經驗很少,但是我仍然設法修改了輸出部分。我的部分客製化代碼如下:
/home/pi/bme280_custom.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
#coding: utf-8 import smbus import time bus_number = 1 i2c_address = 0x76 bus = smbus.SMBus(bus_number) digT = [] digP = [] digH = [] t_fine = 0.0 def writeReg(reg_address, data): bus.write_byte_data(i2c_address,reg_address,data) def get_calib_param(): calib = [] for i in range (0x88,0x88+24): calib.append(bus.read_byte_data(i2c_address,i)) calib.append(bus.read_byte_data(i2c_address,0xA1)) for i in range (0xE1,0xE1+7): calib.append(bus.read_byte_data(i2c_address,i)) digT.append((calib[1] << 8) | calib[0]) digT.append((calib[3] << 8) | calib[2]) digT.append((calib[5] << 8) | calib[4]) digP.append((calib[7] << 8) | calib[6]) digP.append((calib[9] << 8) | calib[8]) digP.append((calib[11]<< 8) | calib[10]) digP.append((calib[13]<< 8) | calib[12]) digP.append((calib[15]<< 8) | calib[14]) digP.append((calib[17]<< 8) | calib[16]) digP.append((calib[19]<< 8) | calib[18]) digP.append((calib[21]<< 8) | calib[20]) digP.append((calib[23]<< 8) | calib[22]) digH.append( calib[24] ) digH.append((calib[26]<< 8) | calib[25]) digH.append( calib[27] ) digH.append((calib[28]<< 4) | (0x0F & calib[29])) digH.append((calib[30]<< 4) | ((calib[29] >> 4) & 0x0F)) digH.append( calib[31] ) for i in range(1,2): if digT[i] & 0x8000: digT[i] = (-digT[i] ^ 0xFFFF) + 1 for i in range(1,8): if digP[i] & 0x8000: digP[i] = (-digP[i] ^ 0xFFFF) + 1 for i in range(0,6): if digH[i] & 0x8000: digH[i] = (-digH[i] ^ 0xFFFF) + 1 def readData(): data = [] for i in range (0xF7, 0xF7+8): data.append(bus.read_byte_data(i2c_address,i)) pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4) temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4) hum_raw = (data[6] << 8) | data[7] #compensate_T(temp_raw) #compensate_P(pres_raw) #compensate_H(hum_raw) t = compensate_T(temp_raw) p = compensate_P(pres_raw) h = compensate_H(hum_raw) return p + "," + t + "," + h def compensate_P(adc_P): global t_fine pressure = 0.0 v1 = (t_fine / 2.0) - 64000.0 v2 = (((v1 / 4.0) * (v1 / 4.0)) / 2048) * digP[5] v2 = v2 + ((v1 * digP[4]) * 2.0) v2 = (v2 / 4.0) + (digP[3] * 65536.0) v1 = (((digP[2] * (((v1 / 4.0) * (v1 / 4.0)) / 8192)) / 8) + ((digP[1] * v1) / 2.0)) / 262144 v1 = ((32768 + v1) * digP[0]) / 32768 if v1 == 0: return 0 pressure = ((1048576 - adc_P) - (v2 / 4096)) * 3125 if pressure < 0x80000000: pressure = (pressure * 2.0) / v1 else: pressure = (pressure / v1) * 2 v1 = (digP[8] * (((pressure / 8.0) * (pressure / 8.0)) / 8192.0)) / 4096 v2 = ((pressure / 4.0) * digP[7]) / 8192.0 pressure = pressure + ((v1 + v2 + digP[6]) / 16.0) #print "pressure : %7.2f hPa" % (pressure/100) return "%7.2f" % (pressure/100) def compensate_T(adc_T): global t_fine v1 = (adc_T / 16384.0 - digT[0] / 1024.0) * digT[1] v2 = (adc_T / 131072.0 - digT[0] / 8192.0) * (adc_T / 131072.0 - digT[0] / 8192.0) * digT[2] t_fine = v1 + v2 temperature = t_fine / 5120.0 #print "temp : %-6.2f ℃" % (temperature) return "%.2f" % (temperature) def compensate_H(adc_H): global t_fine var_h = t_fine - 76800.0 if var_h != 0: var_h = (adc_H - (digH[3] * 64.0 + digH[4]/16384.0 * var_h)) * (digH[1] / 65536.0 * (1.0 + digH[5] / 67108864.0 * var_h * (1.0 + digH[2] / 67108864.0 * var_h))) else: return 0 var_h = var_h * (1.0 - digH[0] * var_h / 524288.0) if var_h > 100.0: var_h = 100.0 elif var_h < 0.0: var_h = 0.0 #print "hum : %6.2f %" % (var_h) return "%.2f" % (var_h) def setup(): osrs_t = 1 #Temperature oversampling x 1 osrs_p = 1 #Pressure oversampling x 1 osrs_h = 1 #Humidity oversampling x 1 mode = 3 #Normal mode t_sb = 5 #Tstandby 1000ms filter = 0 #Filter off spi3w_en = 0 #3-wire SPI Disable ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode config_reg = (t_sb << 5) | (filter << 2) | spi3w_en ctrl_hum_reg = osrs_h writeReg(0xF2,ctrl_hum_reg) writeReg(0xF4,ctrl_meas_reg) writeReg(0xF5,config_reg) setup() get_calib_param() if __name__ == '__main__': try: readData() except KeyboardInterrupt: pass |
我並沒有修改太多代碼,只是對主要的“處理”部分進行了一些小的調整。我將“print”改為“return”並進行了相關編輯,使得程式以CSV格式返回數值(帶逗號)。
/home/pi/bme280.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#coding: utf-8 import bme280_custom import datetime import os dir_path = '/home/pi/bme280-data' now = datetime.datetime.now() filename = now.strftime('%Y%m%d') label = now.strftime('%H:%M') csv = bme280_custom.readData() if not os.path.exists('/home/pi/bme280-data'): os.makedirs('/home/pi/bme280-data') f = open('/home/pi/bme280-data/'+filename+'.csv','a') f.write("'"+label+"',"+csv+"\n") f.close() |
我創建了另一個py,從之前修改過的“bme280_custom.py”中調用readData()函數。這會將讀取的數值保存在CSV檔中。文件的設定如下:記錄一整天的資料,以日期作為檔案名保存。
我用cron登記了這個程式並進行了設定,讓它定期運行。現在完成了!
sudo crontab -e
0-59/10 * * * * /home/pi/bme280.py
注:我將其設定為每10分鐘運行一次。
接下來,我將創建一個PHP檔來顯示資料。我用 DS18B20 溫度感測器製作溫度計時做過類似事情。
/var/www/html/bme280.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
<?php $today = date("Ymd"); $csv_dir = '/home/pi/bme280-data/'; $csv_file = $today.'.csv'; $grapgh = ''; if (($handle = fopen($csv_dir.$csv_file, "r")) !== false) { while (($line = fgets($handle)) !== false) { $grapgh .= '['.rtrim($line).'],'.PHP_EOL; } fclose($handle); }else{ echo 'no data'; } ?> <html> <head> <script type="text/javascript" src="https://www.google.com/jsapi"></script> <script type="text/javascript"> google.load("visualization", "1", {packages:["table"]}); google.setOnLoadCallback(drawTable); function drawTable() { var data = new google.visualization.DataTable(); data.addColumn('string', 'Time'); data.addColumn('number', 'Pressure'); data.addColumn('number', 'Temperature'); data.addColumn('number', 'Humidity'); data.addRows([ <?php echo $grapgh; ?> ]); var table = new google.visualization.Table(document.getElementById('table_div')); table.draw(data, {showRowNumber: true}); } </script> </head> <body> <div id="table_div"></div> </body> </html> |
我創建了一個簡單的PHP檔,在表中顯示相關資料(您必須安裝“php5”套裝軟體才能使用PHP)。
透過這種方式,我可以用瀏覽器——導航至 http://localhost/bme280.php —— 查看CSV檔的內容。即使每隔10分鐘,壓力也會發生巨大變化!
Raspbian最新版本預裝了一個名為“LibreOffice”的辦公套件。如果您只是想查看資料,那麼可以使用“LibreOffice Calc”(按兩下CSV檔),然後您會看到如下內容:
總結
今天,我們用AE-BME280感測器構建了一個簡單模組來測量多個數值(壓力、濕度和溫度)。這麼小的感測器可以測量多達三個不同的參數,真是太神奇了。感測器非常微小,但非常強大!
這個項目還讓我提高了焊接技巧。AE-BME280上的引腳非常小,彼此非常靠近,我當時還擔心可能會把多個引腳焊接在一起。最後,成品的引腳焊接得很好,我很高興。需要焊接的專案可能比較困難,但是值得!