IoTLabs

Nghiên cứu, Sáng tạo và Thử nghiệm

Series 37 Module Cảm Biến – Nguyên Lý Hoạt Động BMP280: Cảm Biến Áp Suất & Nhiệt Độ Chính Xác Của Bosch

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:

  1. Đo nhiệt độ đồng thời với áp suất (NTC thermistor tích hợp)
  2. 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ẻ
  3. 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ómKý hiệuSố lượngChức năng
Nhiệt độdig_T1, T2, T33Bù phi tuyến nhiệt độ
Áp suấtdigP1 … digP99Bù áp suất đa bậc
(BME280 thêm)dig_H1 … H66Bù độ ẩ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
SleepKhông đo, tiêu thụ 0.1µATiế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ỳ standbyGiá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:

OversamplingThời gian đoNoise RMS áp suất
×14.5ms2.62 Pa
×26.4ms1.31 Pa
×410.2ms0.66 Pa
×818.1ms0.33 Pa
×1634ms0.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 VDDIO1.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ất300 hPa – 1100 hPa
Độ chính xác áp suất (typ)±1 hPa (±0°C – +65°C)
Độ phân giải áp suất0.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ứcI2C (400kHz) và SPI (10MHz)
Địa chỉ I2C0x76 (SDO=GND) hoặc 0x77 (SDO=VCC)
PackageLGA-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 moduleTênChức năng
VCCVCCNguồn 3.3V (không dùng 5V trực tiếp)
GNDGNDĐất
SCLSCLI2C Clock / SPI Clock
SDASDAI2C Data / SPI MOSI
CSBCSSPI Chip Select (nối VCC để dùng I2C)
SDOSDO/ADDRSPI 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 đượcGiao thứcGiá (tham khảo)
BMP280Áp suất + Nhiệt độI2C + SPIThấp
BME280Áp suất + Nhiệt độ + Độ ẩmI2C + SPICao hơn
BMP388Áp suất + Nhiệt độ (chính xác hơn)I2C + SPICao
BMP180Áp suất + Nhiệt độ (đời cũ)I2CTương đương BMP280
BME680Áp suất + Nhiệt độ + Độ ẩm + VOC gasI2C + SPIRấ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

LibraryTác giảCho
Adafruit BMP280 LibraryAdafruitBMP280
Adafruit BME280 LibraryAdafruitBME280 (nếu có module đó)
Adafruit Unified SensorAdafruitDependency 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ụngChi tiết
Drone / UAV — giữ độ cao8.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 navigationPhân biệt tầng trong tòa nhà (không thể dùng GPS)
Logger dữ liệu thời tiếtKế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.

Bài tiếp theo: