在物联网开发中,bmp280是一款高精度的数字气压和温度传感器,常用来采集环境气压并计算对应的海拔值。php作为服务端脚本语言,可以通过和硬件层的通信接口对接,完成bmp280传感器数据的读取和解析工作。

bmp280传感器基础说明
bmp280支持I2C和SPI两种通信协议,默认I2C地址为0x76或者0x77,传感器会返回校准参数、原始温度数据、原始气压数据三类信息。我们需要先读取校准参数,再获取原始数据,经过校准计算后得到真实的气压值,最后通过气压和海拔的换算公式得到海拔值。
php读取bmp280数据的实现方式
方式一:通过I2C接口读取
如果php运行在支持I2C的嵌入式设备(比如树莓派)上,可以使用shell_exec调用系统I2C工具完成数据读取,以下是完整实现代码:
<?php
class BMP280 {
private $i2cAddress;
private $i2cPath;
private $calibParams = [];
public function __construct($address = 0x76, $i2cBus = 1) {
$this->i2cAddress = $address;
$this->i2cPath = "/dev/i2c-{$i2cBus}";
}
// 读取单个寄存器数据
private function readRegister($reg) {
$cmd = sprintf("i2cget -y 1 0x%02x 0x%02x", $this->i2cAddress, $reg);
$res = shell_exec($cmd);
return hexdec(trim($res));
}
// 读取多个连续寄存器数据
private function readRegisters($startReg, $len) {
$data = [];
for ($i = 0; $i < $len; $i++) {
$data[] = $this->readRegister($startReg + $i);
}
return $data;
}
// 读取校准参数
public function loadCalibParams() {
$regs = $this->readRegisters(0x88, 24);
// 解析校准参数,具体对应关系参考bmp280数据手册
$this->calibParams['dig_T1'] = $regs[0] | ($regs[1] << 8);
$this->calibParams['dig_T2'] = $regs[2] | ($regs[3] << 8);
if ($this->calibParams['dig_T2'] > 32767) $this->calibParams['dig_T2'] -= 65536;
$this->calibParams['dig_T3'] = $regs[4] | ($regs[5] << 8);
if ($this->calibParams['dig_T3'] > 32767) $this->calibParams['dig_T3'] -= 65536;
// 气压校准参数解析
$this->calibParams['dig_P1'] = $regs[6] | ($regs[7] << 8);
$this->calibParams['dig_P2'] = $regs[8] | ($regs[9] << 8);
if ($this->calibParams['dig_P2'] > 32767) $this->calibParams['dig_P2'] -= 65536;
$this->calibParams['dig_P3'] = $regs[10] | ($regs[11] << 8);
if ($this->calibParams['dig_P3'] > 32767) $this->calibParams['dig_P3'] -= 65536;
$this->calibParams['dig_P4'] = $regs[12] | ($regs[13] << 8);
if ($this->calibParams['dig_P4'] > 32767) $this->calibParams['dig_P4'] -= 65536;
$this->calibParams['dig_P5'] = $regs[14] | ($regs[15] << 8);
if ($this->calibParams['dig_P5'] > 32767) $this->calibParams['dig_P5'] -= 65536;
$this->calibParams['dig_P6'] = $regs[16] | ($regs[17] << 8);
if ($this->calibParams['dig_P6'] > 32767) $this->calibParams['dig_P6'] -= 65536;
$this->calibParams['dig_P7'] = $regs[18] | ($regs[19] << 8);
if ($this->calibParams['dig_P7'] > 32767) $this->calibParams['dig_P7'] -= 65536;
$this->calibParams['dig_P8'] = $regs[20] | ($regs[21] << 8);
if ($this->calibParams['dig_P8'] > 32767) $this->calibParams['dig_P8'] -= 65536;
$this->calibParams['dig_P9'] = $regs[22] | ($regs[23] << 8);
if ($this->calibParams['dig_P9'] > 32767) $this->calibParams['dig_P9'] -= 65536;
}
// 读取原始温度数据
private function readRawTemp() {
$this->writeRegister(0xF4, 0x2E); // 触发温度测量
usleep(5000);
$data = $this->readRegisters(0xFA, 3);
return ($data[0] << 12) | ($data[1] << 4) | ($data[2] >> 4);
}
// 读取原始气压数据
private function readRawPress() {
$this->writeRegister(0xF4, 0x34 | (0x03 << 2)); // 触发气压测量,过采样率x4
usleep(8000);
$data = $this->readRegisters(0xF7, 3);
return ($data[0] << 12) | ($data[1] << 4) | ($data[2] >> 4);
}
// 写入寄存器
private function writeRegister($reg, $val) {
$cmd = sprintf("i2cset -y 1 0x%02x 0x%02x 0x%02x", $this->i2cAddress, $reg, $val);
shell_exec($cmd);
}
// 计算真实温度
public function getTemperature() {
$rawTemp = $this->readRawTemp();
$var1 = ($rawTemp / 16384.0 - $this->calibParams['dig_T1'] / 1024.0) * $this->calibParams['dig_T2'];
$var2 = (($rawTemp / 131072.0 - $this->calibParams['dig_T1'] / 8192.0) * ($rawTemp / 131072.0 - $this->calibParams['dig_T1'] / 8192.0)) * $this->calibParams['dig_T3'];
$tFine = $var1 + $var2;
$temp = $tFine / 5120.0;
return $temp;
}
// 计算真实气压
public function getPressure() {
$rawPress = $this->readRawPress();
$temp = $this->getTemperature();
$var1 = ($tFine / 2.0) - 64000.0;
$var2 = $var1 * $var1 * $this->calibParams['dig_P6'] / 32768.0;
$var2 = $var2 + $var1 * $this->calibParams['dig_P5'] * 2.0;
$var2 = ($var2 / 4.0) + ($this->calibParams['dig_P4'] * 65536.0);
$var1 = ($this->calibParams['dig_P3'] * $var1 * $var1 / 524288.0 + $this->calibParams['dig_P2'] * $var1) / 524288.0;
$var1 = (1.0 + $var1 / 32768.0) * $this->calibParams['dig_P1'];
if ($var1 == 0) return 0;
$press = 1048576.0 - $rawPress;
$press = ($press - $var2 / 4096.0) * 6250.0 / $var1;
$var1 = $this->calibParams['dig_P9'] * $press * $press / 2147483648.0;
$var2 = $press * $this->calibParams['dig_P8'] / 32768.0;
$press = $press + ($var1 + $var2 + $this->calibParams['dig_P7']) / 16.0;
return $press / 100.0; // 转换为hPa
}
// 气压转海拔,海平面气压默认1013.25hPa
public function pressureToAltitude($pressure, $seaLevelPressure = 1013.25) {
return 44330.0 * (1.0 - pow($pressure / $seaLevelPressure, 1.0 / 5.255));
}
}
// 使用示例
$sensor = new BMP280(0x76);
$sensor->loadCalibParams();
$pressure = $sensor->getPressure();
$altitude = $sensor->pressureToAltitude($pressure);
echo "当前气压:{$pressure} hPan";
echo "当前海拔:{$altitude} 米n";
?>
方式二:通过串口接收传感器数据
如果bmp280的数据已经通过单片机处理并通过串口发送,php可以通过serial扩展或者调用系统串口工具读取数据,以下是读取串口数据的示例:
<?php
// 假设单片机通过串口发送格式为 "press:1012.5,alt:100.2" 的字符串
$serialPort = '/dev/ttyUSB0';
$baudRate = 9600;
// 打开串口
$fd = dio_open($serialPort, O_RDWR | O_NOCTTY | O_NONBLOCK);
dio_fcntl($fd, F_SETFL, O_SYNC);
// 设置波特率
exec("stty -F {$serialPort} {$baudRate} raw -echo");
// 读取数据
$data = dio_read($fd, 1024);
dio_close($fd);
// 解析数据
if (preg_match('/press:([0-9.]+),alt:([0-9.]+)/', $data, $matches)) {
$pressure = $matches[1];
$altitude = $matches[2];
echo "当前气压:{$pressure} hPan";
echo "当前海拔:{$altitude} 米n";
}
?>
注意事项
- 运行I2C读取代码需要php有权限访问/dev/i2c-1设备,需要给运行用户添加对应权限
- bmp280的校准参数解析需要严格按照数据手册的对应关系处理,否则会得到错误的气压值
- 海拔计算结果受当地海平面气压影响,若需要更高精度可以手动传入当地实时海平面气压值
- 串口读取方式需要确保单片机发送的数据格式和解析逻辑匹配,避免数据解析错误