BMP280 là sản phẩm của Bosch Sensortec, dùng công nghệ MEMS để đo áp suất khí quyển với độ phân giải đến 0.01 hPa — tương đương phát hiện thay đổi độ cao 8.5cm. Ngoài áp suất, nó cũng đo nhiệt độ với độ chính xác ±0.5°C. Bài này đi sâu vào nguyên lý MEMS, quá trình bù nhiệt độ 20 hệ số, và cách dùng đúng trên ESP32 và Arduino Uno.
Nguyên Lý Hoạt Động
1. MEMS Piezoresistive Pressure Sensing
BMP280 dùng công nghệ MEMS (Micro-Electro-Mechanical Systems) — chế tạo cấu trúc cơ học siêu nhỏ trên silicon.
Cấu trúc sensor áp suất:
Áp suất khí quyển
↓
┌─────────────┐
│ Màng Si │ ← Membrane silicon mỏng (~µm)
│ ┌────────┐ │
│ │Piezo R │ │ ← 4 điện trở piezoresistive
│ └────────┘ │ thay đổi điện trở khi màng uốn
└─────────────┘
│ Buồng chân không │ ← Cavity bên dưới, sealed vacuum
└────────────────────┘
Khi áp suất bên ngoài tăng → màng silicon bị uốn cong → hiệu ứng piezoresistive: điện trở của silicon thay đổi theo ứng suất cơ học. 4 điện trở bố trí thành cầu Wheatstone → điện áp ra tỷ lệ với áp suất.
Tại sao cần buồng chân không: Sensor đo áp suất tuyệt đối (so với chân không), không phải áp suất tương đối. Buồng vacuum bên dưới màng đảm bảo điểm tham chiếu luôn là 0 Pa.
2. Bù Nhiệt Độ — 20 Hệ Số Hiệu Chỉnh
Vật liệu piezoresistive thay đổi theo nhiệt độ (hiệu ứng TCR — Temperature Coefficient of Resistance). Nếu không bù, áp suất đọc sẽ sai khi nhiệt độ thay đổi.
Bosch giải quyết bằng cách:
- Đo nhiệt độ đồng thời với áp suất (NTC thermistor tích hợp)
- Lưu 20 hệ số hiệu chỉnh vào OTP memory (One-Time Programmable) của mỗi con chip khi sản xuất — được đo và đặc trưng hóa cho từng chip riêng lẻ
- MCU đọc 20 hệ số này → tính toán bù nhiệt → ra giá trị áp suất và nhiệt độ chính xác
20 hệ số (trimming parameters) trong OTP:
| Nhóm | Ký hiệu | Số lượng | Chức năng |
|---|---|---|---|
| Nhiệt độ | dig_T1, T2, T3 | 3 | Bù phi tuyến nhiệt độ |
| Áp suất | digP1 … digP9 | 9 | Bù áp suất đa bậc |
| (BME280 thêm) | dig_H1 … H6 | 6 | Bù độ ẩm (BMP280 không có) |
Quá trình tính toán (theo datasheet Bosch BST-BMP280-DS001):
Bước 1: Đọc raw_T (20-bit), raw_P (20-bit) từ sensor registers
Bước 2: Đọc dig_T1, T2, T3 từ OTP → tính t_fine (intermediate)
Bước 3: Dùng t_fine + dig_P1...P9 → tính áp suất Pa
Bước 4: Kết quả = hPa = Pa / 100
Library Adafruit/Wire đã tự làm bước này — không cần lập trình thủ công.
3. Chế Độ Hoạt Động
BMP280 có 3 chế độ:
| Chế độ | Mô tả | Dùng khi |
|---|---|---|
| Sleep | Không đo, tiêu thụ 0.1µA | Tiết kiệm pin tối đa |
| Forced | Đo 1 lần rồi về Sleep | Đọc định kỳ, pin quan trọng |
| Normal | Đo liên tục theo chu kỳ standby | Giám sát liên tục |
Forced mode workflow (khuyến nghị cho IoT chạy pin):
MCU → ghi MODE=Forced → BMP280 đo → tự về Sleep → MCU đọc kết quả
↑
Tốt cho pin: chỉ active ~2ms mỗi lần đo
4. Oversampling — Đổi Tốc Độ Lấy Ứng Độ Chính Xác
BMP280 cho phép cấu hình oversampling (lấy nhiều mẫu rồi lấy trung bình) cho cả nhiệt độ và áp suất:
| Oversampling | Thời gian đo | Noise RMS áp suất |
|---|---|---|
| ×1 | 4.5ms | 2.62 Pa |
| ×2 | 6.4ms | 1.31 Pa |
| ×4 | 10.2ms | 0.66 Pa |
| ×8 | 18.1ms | 0.33 Pa |
| ×16 | 34ms | 0.16 Pa |
Chế độ “Weather Monitoring” theo datasheet: T×1, P×1, sleep 1s → tiêu thụ 3.6µA trung bình. Chế độ “Indoor Navigation”: T×2, P×16, standby 0.5ms → 3.6µA, độ phân giải 8.5cm.
Thông Số Kỹ Thuật
| Thông số | Giá trị |
|---|---|
| Điện áp (VDD) | 1.71V – 3.6V |
| Điện áp VDDIO | 1.2V – 3.6V |
| Dòng tiêu thụ (đo) | 2.7µA (1Hz, Normal mode) |
| Dòng tiêu thụ (Sleep) | 0.1µA |
| Dải áp suất | 300 hPa – 1100 hPa |
| Độ chính xác áp suất (typ) | ±1 hPa (±0°C – +65°C) |
| Độ phân giải áp suất | 0.01 hPa (tương đương 8.5cm độ cao) |
| Dải nhiệt độ (đo) | -40°C – 85°C |
| Độ chính xác nhiệt độ (typ) | ±0.5°C |
| Giao thức | I2C (400kHz) và SPI (10MHz) |
| Địa chỉ I2C | 0x76 (SDO=GND) hoặc 0x77 (SDO=VCC) |
| Package | LGA-8, 2.5 × 2.5 × 0.93mm |
| Module (có PCB) | Thường 10 × 10mm đến 14 × 14mm |
QUAN TRỌNG: BMP280 chạy tối đa 3.6V. Kết nối trực tiếp với Arduino Uno 5V → hỏng chip. Dùng module có voltage regulator, hoặc level shifter I2C.
Sơ Đồ Chân (Pinout)
BMP280 Chip (LGA-8, SMD — cực nhỏ)
Không dùng trực tiếp trong hobbyist. Thường mua dạng module.
Module BMP280 (phổ biến — 6 chân)
┌─────────────────┐
│ [BMP280 chip] │
│ │
└─────────────────┘
VCC GND SCL SDA CSB SDO
│ │ │ │ │ │
| Chân module | Tên | Chức năng |
|---|---|---|
| VCC | VCC | Nguồn 3.3V (không dùng 5V trực tiếp) |
| GND | GND | Đất |
| SCL | SCL | I2C Clock / SPI Clock |
| SDA | SDA | I2C Data / SPI MOSI |
| CSB | CS | SPI Chip Select (nối VCC để dùng I2C) |
| SDO | SDO/ADDR | SPI MISO / I2C address select (GND=0x76, VCC=0x77) |
Chọn địa chỉ I2C: SDO nối GND → địa chỉ 0x76. SDO nối VCC → 0x77. Khi có 2 BMP280 trên cùng bus: dùng cả 2 địa chỉ.
So Sánh BMP280 với Các Biến Thể
| Model | Đo được | Giao thức | Giá (tham khảo) |
|---|---|---|---|
| BMP280 | Áp suất + Nhiệt độ | I2C + SPI | Thấp |
| BME280 | Áp suất + Nhiệt độ + Độ ẩm | I2C + SPI | Cao hơn |
| BMP388 | Áp suất + Nhiệt độ (chính xác hơn) | I2C + SPI | Cao |
| BMP180 | Áp suất + Nhiệt độ (đời cũ) | I2C | Tương đương BMP280 |
| BME680 | Áp suất + Nhiệt độ + Độ ẩm + VOC gas | I2C + SPI | Rất cao |
Khi nào dùng BME280 thay BMP280: Cần đo thêm độ ẩm. Cùng kích thước module, cùng pinout, cùng địa chỉ I2C.
Phân biệt BMP280 và BME280: Nhìn marking trên chip LGA-8. BMP280: “B280”. BME280: “BE280”. Nhiều module bán ghi là BMP280 nhưng thực ra là BME280 (hoặc ngược lại) — đọc marking để chắc chắn.
Kết Nối Phần Cứng
Kết Nối với ESP32 DevKit V1 (I2C)
ESP32 DevKit V1 BMP280 Module
───────────────────── ─────────────────
3V3 ─────────────────→ VCC
GND ─────────────────→ GND
GPIO22 (SCL) ─────────→ SCL
GPIO21 (SDA) ─────────→ SDA
3V3 ─────────────────→ CSB (kéo lên VCC → chọn I2C, không phải SPI)
GND ─────────────────→ SDO (kéo xuống GND → địa chỉ 0x76)
GPIO21 và GPIO22 là I2C mặc định của ESP32 (SDA và SCL tương ứng). Không dùng GPIO6-11 (Flash SPI).
Nếu cần địa chỉ 0x77: Nối SDO lên 3V3.
Kết Nối với Arduino Uno (I2C)
BMP280 chạy 3.3V — Arduino Uno là 5V. Cần xử lý điện áp:
Cách 1: Module có onboard 3.3V regulator (nhiều module rẻ tiền đã có)
Arduino Uno BMP280 Module (có LDO)
───────────────────── ────────────────────────
5V ─────────────────→ VCC (LDO chuyển xuống 3.3V)
GND ─────────────────→ GND
A4 (SDA) ─────────────→ SDA (module có level shifter onboard)
A5 (SCL) ─────────────→ SCL
Cách 2: Chỉ cấp 3.3V từ Arduino Uno 3V3 pin
Arduino Uno 3V3 pin ──→ VCC (không dùng 5V!)
A4 (SDA) ──────────→ SDA (3.3V logic — hầu hết hoạt động được)
A5 (SCL) ──────────→ SCL
3.3V logic vào Arduino 5V INPUT: thường hoạt động (ATmega328P nhận mức HIGH từ 2.0V). Nhưng không đảm bảo về dài hạn.
Cách an toàn nhất: Dùng module có I2C level shifter tích hợp.
Arduino Uno I2C: A4 = SDA, A5 = SCL.
Code Arduino IDE
Cài Library
| Library | Tác giả | Cho |
|---|---|---|
Adafruit BMP280 Library | Adafruit | BMP280 |
Adafruit BME280 Library | Adafruit | BME280 (nếu có module đó) |
Adafruit Unified Sensor | Adafruit | Dependency chung |
Cài Adafruit BMP280 Library → khi hỏi về dependency → Install All.
Code cho Arduino Uno
/*
* BMP280 - Đọc áp suất, nhiệt độ, tính độ cao
* Board: Arduino Uno
* Library: Adafruit BMP280 Library + Adafruit Unified Sensor
* Kết nối I2C: SDA → A4, SCL → A5, VCC → 3V3 (không dùng 5V!), GND → GND
* SDO → GND (địa chỉ 0x76)
*/
#include <Wire.h> // I2C hardware driver — có sẵn trong Arduino IDE
#include <Adafruit_BMP280.h> // Library BMP280
// Khởi tạo BMP280 qua I2C
Adafruit_BMP280 bmp;
// Áp suất mực nước biển chuẩn (hPa)
// Dùng để tính độ cao tương đối so với mực nước biển
// Thay đổi theo thời tiết thực tế (từ bản tin thời tiết địa phương)
const float SEA_LEVEL_PRESSURE_HPA = 1013.25;
void setup() {
Serial.begin(9600);
Serial.println("=== BMP280 Test - Arduino Uno ===");
// Khởi động BMP280 — địa chỉ I2C mặc định 0x76
// Nếu SDO nối VCC: dùng bmp.begin(0x77)
if (!bmp.begin(0x76)) {
Serial.println("LỖI: Không tìm thấy BMP280 tại địa chỉ 0x76!");
Serial.println(" → Kiểm tra: kết nối SDA/SCL, địa chỉ (SDO=GND→0x76, SDO=VCC→0x77)");
Serial.println(" → Kiểm tra: VCC là 3.3V (không phải 5V)");
while (1) delay(1000); // Dừng tại đây
}
// Cấu hình chế độ đo
bmp.setSampling(
Adafruit_BMP280::MODE_NORMAL, // Normal mode: đo liên tục
Adafruit_BMP280::SAMPLING_X2, // Oversampling nhiệt độ ×2
Adafruit_BMP280::SAMPLING_X16, // Oversampling áp suất ×16 — chính xác nhất
Adafruit_BMP280::FILTER_X16, // IIR filter ×16 — giảm nhiễu đột biến
Adafruit_BMP280::STANDBY_MS_500 // Standby 500ms giữa 2 lần đo
);
Serial.println("BMP280 sẵn sàng.");
}
void loop() {
// Đọc nhiệt độ (°C)
float temperature = bmp.readTemperature();
// Đọc áp suất (hPa = mbar)
float pressure = bmp.readPressure() / 100.0; // Pa → hPa
// Tính độ cao dựa trên công thức khí áp chuẩn (Barometric Formula)
// Kết quả chính xác khi SEA_LEVEL_PRESSURE_HPA đúng với thực tế
float altitude = bmp.readAltitude(SEA_LEVEL_PRESSURE_HPA);
Serial.print("Nhiệt độ : ");
Serial.print(temperature, 2);
Serial.println(" °C");
Serial.print("Áp suất : ");
Serial.print(pressure, 2);
Serial.println(" hPa");
Serial.print("Độ cao : ");
Serial.print(altitude, 1);
Serial.println(" m (tham chiếu mực nước biển)");
Serial.println("---");
delay(2000); // Đọc mỗi 2 giây
}
Code cho ESP32
/*
* BMP280 - Đọc áp suất, nhiệt độ, tính độ cao
* Board: ESP32 DevKit V1
* Library: Adafruit BMP280 Library
* Kết nối: SDA → GPIO21, SCL → GPIO22, VCC → 3V3, GND → GND
* SDO → GND → địa chỉ 0x76
*/
#include <Wire.h>
#include <Adafruit_BMP280.h>
Adafruit_BMP280 bmp;
// SDA và SCL mặc định của ESP32: GPIO21 và GPIO22
// Wire.begin() tự dùng các pin này — không cần chỉ định
const float SEA_LEVEL_PRESSURE_HPA = 1013.25; // Chuẩn. Thay bằng giá trị thực địa phương.
// Lưu trữ độ cao baseline để đo độ cao tương đối
float baselineAltitude = 0.0;
void setup() {
Serial.begin(115200);
Wire.begin(); // GPIO21=SDA, GPIO22=SCL (mặc định ESP32)
Serial.println("=== BMP280 Test - ESP32 ===");
if (!bmp.begin(0x76)) {
Serial.println("LỖI: Không tìm thấy BMP280!");
while (1) delay(1000);
}
// Cấu hình cho ứng dụng indoor navigation (chính xác nhất)
bmp.setSampling(
Adafruit_BMP280::MODE_NORMAL,
Adafruit_BMP280::SAMPLING_X2, // Nhiệt độ
Adafruit_BMP280::SAMPLING_X16, // Áp suất — 16 lần lấy mẫu trung bình
Adafruit_BMP280::FILTER_X16, // IIR filter chống nhiễu
Adafruit_BMP280::STANDBY_MS_500
);
// Đọc baseline altitude — đo 5 lần lấy trung bình để ổn định
float sum = 0;
for (int i = 0; i < 5; i++) {
sum += bmp.readAltitude(SEA_LEVEL_PRESSURE_HPA);
delay(600); // Chờ đủ standby period
}
baselineAltitude = sum / 5.0;
Serial.printf("Baseline altitude: %.1f m\n", baselineAltitude);
Serial.println("BMP280 sẵn sàng.");
}
void loop() {
float temperature = bmp.readTemperature();
float pressure = bmp.readPressure() / 100.0; // Pa → hPa
float altitude = bmp.readAltitude(SEA_LEVEL_PRESSURE_HPA);
float relAltitude = altitude - baselineAltitude; // Độ cao tương đối so với điểm đặt
Serial.printf("Nhiệt độ : %.2f °C\n", temperature);
Serial.printf("Áp suất : %.2f hPa\n", pressure);
Serial.printf("Độ cao : %.1f m (tuyệt đối)\n", altitude);
Serial.printf("Thay đổi : %+.1f m (so với baseline)\n", relAltitude);
// "+" trong format: luôn in dấu + hoặc - → dễ thấy tăng/giảm
Serial.println("---");
delay(2000);
}
Kết Quả Mong Đợi
=== BMP280 Test - ESP32 ===
Baseline altitude: 12.3 m
BMP280 sẵn sàng.
Nhiệt độ : 29.50 °C
Áp suất : 1011.23 hPa
Độ cao : 17.1 m (tuyệt đối)
Thay đổi : +4.8 m (so với baseline)
---
Lưu ý: Nhiệt độ BMP280 thường cao hơn thực tế ~2–5°C vì chip gần BMP280 tỏa nhiệt. Để đo nhiệt độ môi trường → dùng DHT22 hoặc DS18B20. BMP280 tốt nhất chỉ dùng cho áp suất và độ cao.
Ứng Dụng Thực Tế
| Ứng dụng | Chi tiết |
|---|---|
| Drone / UAV — giữ độ cao | 8.5cm resolution → giữ ổn ở 1m độ cao |
| Trạm thời tiết | Áp suất + xu hướng thay đổi → dự báo mưa |
| Đồng hồ leo núi (altimeter) | Đo tổng độ cao leo |
| Phát hiện tầng nhà | 3–4m thay đổi giữa các tầng → đủ phân biệt |
| Indoor navigation | Phân biệt tầng trong tòa nhà (không thể dùng GPS) |
| Logger dữ liệu thời tiết | Kết hợp với RTC DS3231 |
Lưu Ý Khi Sử Dụng
1. Điện áp tối đa 3.6V — không dùng 5V trực tiếp
Đây là lỗi phổ biến nhất. BMP280 chip chịu tối đa 3.6V. Cấp 5V → hỏng ngay. Kiểm tra module có LDO 3.3V tích hợp hay không trước khi nối vào Arduino Uno 5V.
2. Nhiệt độ BMP280 không phản ánh nhiệt độ môi trường
BMP280 đo nhiệt độ chủ yếu để bù cho sensor áp suất, không phải để đo môi trường. Self-heating của chip làm kết quả cao hơn thực tế. Dùng cho áp suất và độ cao; dùng DHT22 cho nhiệt độ môi trường.
3. Địa chỉ I2C conflict
BMP280 dùng 0x76 hoặc 0x77. Nhiều sensor cùng dùng 0x76 (MPU-6050 dùng 0x68/0x69, SSD1306 dùng 0x3C/0x3D). Kiểm tra conflict trước khi lắp nhiều sensor I2C trên cùng bus. Dùng I2C scanner sketch để quét.
4. IIR Filter
BMP280 có bộ lọc IIR (Infinite Impulse Response) tích hợp. Bật FILTERX16 → giảm nhiễu nhưng chậm đáp ứng khi áp suất thay đổi thật (ví dụ: chuyển nhanh từ tầng 1 lên tầng 5). Với ứng dụng drone cần đáp ứng nhanh → FILTEROFF hoặc FILTER_X2.
5. Tính độ cao từ áp suất
Công thức barometric sử dụng áp suất mực nước biển chuẩn (1013.25 hPa). Thực tế áp suất mực nước biển thay đổi theo thời tiết (995–1035 hPa). Để tính độ cao tuyệt đối chính xác: cần nhập áp suất thực tế từ trạm thời tiết địa phương vào SEALEVELPRESSURE_HPA. Để đo độ cao tương đối (chênh lệch giữa 2 điểm): dùng baseline approach — không cần biết áp suất mực nước biển.


