
在本教程中,我们将使用一个多功能模块,协助Raspberry Pi同时测量温度、湿度和气压三种参数。
该项目需要焊接!如上图所示,该部件带有一个单独的排针,因此我们需要将其焊接到面包板上。
AE-BME280
图1:AE-BME280 / © Akizuki Denshi
使用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连至主板、嵌入式系统和移动电话等设备。
图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 Pi
Raspberry 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源代码:
单击右上角的“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上的引脚非常小,彼此非常靠近,我当时还担心可能会把多个引脚焊接在一起。最后,成品的引脚焊接得很好,我很高兴。需要焊接的项目可能比较困难,但是值得!