这篇文章来源于DevicePlus.com英语网站的翻译稿。
在本教程中,我们将使用一个多功能模块,协助Raspberry Pi同时测量温度、湿度和气压三种参数。
该项目需要焊接!如上图所示,该部件带有一个单独的排针,因此我们需要将其焊接到面包板上。
AE-BME280图1:AE-BME280 / © Akizuki Denshi
使用BME280的温度/湿度/压力传感器模块套件BME280(日语)
凭借搭载Bosch Sensortec BMP280的传感器模块,您可以同时测量温度、湿度和气压。该模块还可以通过I2C或SPI与微控制器通信。
我们将在本教程中使用这款超紧凑型AE-BME280压力传感器(尺寸:16x10mm)。该模块采购于日本零件供应商Akizuki Denshi,您也可以使用具有相同芯片的Adafruit BME280模块。请注意,Adafruit的引脚布局略有不同,因此连线时请务必参考其技术规格书。
Adafruit BME280 技术规格书
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连至主板、嵌入式系统和移动电话等设备。
图2:引脚编号和功能–摘自AE-BME280技术规格书
连接I2C或SPI是不同的,所以我们必须注意引脚接法。请参阅AE-BME280 技术规格书 (日语)或Adafruit BME280 技术规格书(英语)。
如图2所示,使用I2C时,我们需要焊接 J3 跳线。我们必须在此跳线处填充焊锡。请注意,这仅适用于AE-BME280芯片。Adafruit芯片不需要设置此跳线。
焊接!图3:焊接设备
现在,是时候焊接了!首先应加热烙铁。
焊锡 – 维基百科
焊锡是一种主要由铅和锡组成的合金,与电烙铁配合使用。主要用于连接金属元件,以及将电气元件焊接到电路板上。根据其成分,焊锡会在4-10度时变成超导体。
我在这个项目中用的是这种焊锡。这种焊锡非常柔韧,容易泄漏,因此只能一点一点地送锡。我们还应准备一个吸锡器,以防万一。如果送锡过多,可以用吸锡器吸掉多余的焊锡,所以要小心!
图4:焊接J3
首先,我们应焊接图4所示的J3跳线。J3与相邻引脚靠的很近,所以要小心不要焊接在一起。送锡时要小心仔细!
图5:焊接排针
下一步是连接排针。我从电路板背面焊接排针。引脚之间的间隙非常小,因此很难将电烙铁塞入。电路板两端引脚的焊锡结块示例如图5所示。
焊接技巧是先用烙铁头稍稍加热引脚,然后再送锡。如果烙铁尖过热,焊锡会燃烧并形成结块。因此,我建议焊完一个引脚后,从电源插座上拔下电烙铁并冷却后再焊接下一个引脚。不必着急。小心不要将引脚焊接在一起。慢慢来!
图6:连至面包板的AE-BME280
完成!排针焊接完毕,现在电路板垂直插在面包板上。
焊接时,我不小心碰到了排针末端并稍微烫了一下,但幸运的是这并没有影响读数。
接线现在,让我们将Raspberry Pi连接到AE-BME280。连接示意图如图7所示,因为我们使用的是I2C通信。
图7:I2C连接方法–摘自AE-BME280技术规格书
在Raspberry Pi上,“SDA”连至GPIO2(引脚3),“SCL”连到GPIO3(引脚5)。VDD连接到引脚1,这样前3个GPIO引脚按顺序排列(参见图8)。接线时容易记忆。
图8:接线示意图
我将AE-BME280上的引脚5(SDO)连至GND,您也可以将其连至VDD。(请注意,如果这样做,数据收集地址会发生变化)。
设置 Raspberry PiRaspberry Pi默认禁用I2C。启用I2C的方法与启用SPI的方法相同,请参阅上一个教程:Raspberry Pi WebIOPi物联网,模拟输入编程。在菜单中选择[Preferences] – [Raspberry Pi Configuration],然后打开“Settings ”屏幕。
点击[Interfaces]选项卡,将[I2C]设为[Enable]
必须重新启动系统该设置才能生效,因此请在弹出窗口中单击“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
76是十六进制数;代表0x76。
注:选择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源代码:
SWITCHSCIENCE/BME280
单击右上角的“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
#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
#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
<?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)。
通过这种方式,我可以用浏览器——导航至 https://localhost/bme280.php ——查看CSV文件的内容。即使每隔10分钟,压力也会发生巨大变化!
Raspbian最新版本预装了一个名为“LibreOffice”的办公套件。如果您只是想查看数据,那么可以使用“LibreOffice Calc”(双击CSV文件),然后您会看到如下内容:
总结
今天,我们用AE-BME280传感器构建了一个简单模块来测量多个数值(压力、湿度和温度)。这么小的传感器可以测量多达三个不同的参数,真是太神奇了。传感器非常微小,但非常强大!
这个项目还让我提高了焊接技巧。AE-BME280上的引脚非常小,彼此非常靠近,我当时还担心可能会把多个引脚焊接在一起。最后,成品的引脚焊接得很好,我很高兴。需要焊接的项目可能比较困难,但是值得!