</>CoderDIY
← Quay lại danh sách
Bài viết

Cảm biến hiện diện Bluetooth cho nhà thông minh với ESP32

Tác giả: CoderDIY··Đọc bằng tiếng Anh
📶

Tự động hóa nhà thông minh dựa trên GPS điện thoại tốn pin và cần internet ổn định; dựa trên cảm biến chuyển động thì không phân biệt được ai đang ở nhà. Một cách rẻ và hiệu quả hơn: hầu hết điện thoại đều phát tín hiệu Bluetooth Low Energy (BLE) liên tục dù không kết nối với thiết bị nào — ESP32 có thể quét các tín hiệu này trong phạm vi vài mét và suy ra “có người ở nhà” hay “đã rời đi” mà không cần app hay cấu hình gì trên điện thoại.

Nguyên lý hoạt động

  1. ESP32 định kỳ quét (BLE scan) các thiết bị Bluetooth phát sóng xung quanh, thu thập địa chỉ MAC và cường độ tín hiệu (RSSI) của từng thiết bị.
  2. So sánh địa chỉ MAC quét được với danh sách MAC đã biết trước (điện thoại của các thành viên trong nhà) — MAC ngẫu nhiên thay đổi theo thời gian trên iOS/Android hiện đại là hạn chế cần lưu ý (xem phần lỗi thường gặp).
  3. Nếu một MAC quen thuộc xuất hiện liên tục qua vài lần quét, ESP32 coi như thiết bị đó “có mặt”; nếu vắng mặt trong nhiều lần quét liên tiếp (quá một khoảng thời gian chờ), coi như đã rời đi.
  4. Trạng thái hiện diện được publish qua MQTT tới Home Assistant hoặc broker tự dựng, từ đó kích hoạt automation: bật đèn/điều hòa khi về nhà, tắt hết thiết bị khi không còn ai.
<rect x="10" y="20" width="140" height="60" rx="10" class="box" />
<text x="80" y="45" text-anchor="middle" class="label">Quét BLE</text>
<text x="80" y="62" text-anchor="middle" class="label">(mỗi 10-15s)</text>

<rect x="170" y="20" width="140" height="60" rx="10" class="box" />
<text x="240" y="45" text-anchor="middle" class="label">So MAC</text>
<text x="240" y="62" text-anchor="middle" class="label">với danh sách biết</text>

<rect x="330" y="20" width="140" height="60" rx="10" class="box" />
<text x="400" y="45" text-anchor="middle" class="label">Cập nhật trạng thái</text>
<text x="400" y="62" text-anchor="middle" class="label">(có mặt/vắng mặt)</text>

<rect x="490" y="20" width="140" height="60" rx="10" class="box" />
<text x="560" y="45" text-anchor="middle" class="label">Publish MQTT</text>
<text x="560" y="62" text-anchor="middle" class="label">→ Home Assistant</text>

<line x1="150" y1="50" x2="170" y2="50" class="arrow" />
<line x1="310" y1="50" x2="330" y2="50" class="arrow" />
<line x1="470" y1="50" x2="490" y2="50" class="arrow" />

<path d="M 560 80 L 560 190 L 80 190 L 80 80" class="arrow" fill="none" />
<text x="320" y="210" text-anchor="middle" class="sublabel">Lặp lại liên tục để cập nhật trạng thái theo thời gian thực</text>
Vòng lặp phát hiện hiện diện: quét BLE, đối chiếu MAC, cập nhật trạng thái, publish MQTT tới hệ thống nhà thông minh.

Linh kiện

  • ESP32 (bất kỳ dòng nào có BLE, kể cả ESP32-C3/S3)
  • Nguồn 5V ổn định (adapter USB) vì thiết bị chạy liên tục 24/7
  • Broker MQTT (Mosquitto tự host hoặc tích hợp sẵn trong Home Assistant)
  • Điện thoại của các thành viên trong nhà để lấy địa chỉ MAC BLE (dùng app dò BLE như nRF Connect để xem MAC khi Bluetooth đang bật)
<rect x="20" y="80" width="150" height="70" rx="10" class="box" />
<text x="95" y="120" text-anchor="middle" class="label">Điện thoại (BLE)</text>

<rect x="245" y="80" width="150" height="70" rx="10" class="box" />
<text x="320" y="120" text-anchor="middle" class="label">ESP32 (quét)</text>

<rect x="470" y="80" width="150" height="70" rx="10" class="box" />
<text x="545" y="110" text-anchor="middle" class="label">MQTT Broker /</text>
<text x="545" y="128" text-anchor="middle" class="label">Home Assistant</text>

<line x1="170" y1="115" x2="245" y2="115" class="wire" />
<text x="207" y="105" text-anchor="middle" class="sublabel">BLE</text>

<line x1="395" y1="115" x2="470" y2="115" class="wire" />
<text x="432" y="105" text-anchor="middle" class="sublabel">WiFi/MQTT</text>
ESP32 quét thụ động sóng BLE quanh nó, rồi publish trạng thái hiện diện qua WiFi tới broker MQTT.

Code mẫu

#include <WiFi.h>
#include <PubSubClient.h>
#include <BLEDevice.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

const char* WIFI_SSID = "ten-wifi";
const char* WIFI_PASS = "mat-khau-wifi";
const char* MQTT_HOST = "192.168.1.10";

WiFiClient espClient;
PubSubClient mqtt(espClient);
BLEScan* bleScan;

// Dia chi MAC dien thoai can theo doi, thay bang MAC thuc te
const char* KNOWN_MAC = "aa:bb:cc:dd:ee:ff";
bool present = false;
unsigned long lastSeen = 0;
const unsigned long TIMEOUT_MS = 60000; // 1 phut khong thay -> coi nhu vang mat

void connectWifiMqtt() {
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  mqtt.setServer(MQTT_HOST, 1883);
  while (!mqtt.connected()) {
    mqtt.connect("esp32-presence");
    delay(500);
  }
}

void setup() {
  Serial.begin(115200);
  connectWifiMqtt();

  BLEDevice::init("");
  bleScan = BLEDevice::getScan();
  bleScan->setActiveScan(false); // quet thu dong, tiet kiem pin dien thoai hon
}

void loop() {
  if (!mqtt.connected()) connectWifiMqtt();
  mqtt.loop();

  BLEScanResults* results = bleScan->start(5, false); // quet 5 giay moi vong
  bool foundNow = false;

  for (int i = 0; i < results->getCount(); i++) {
    BLEAdvertisedDevice device = results->getDevice(i);
    if (device.getAddress().toString() == KNOWN_MAC) {
      foundNow = true;
      lastSeen = millis();
      break;
    }
  }

  bleScan->clearResults();

  bool nowPresent = (millis() - lastSeen) < TIMEOUT_MS;
  if (nowPresent != present) {
    present = nowPresent;
    mqtt.publish("home/presence/phone1", present ? "home" : "away", true);
    Serial.println(present ? "Da ve nha" : "Da roi di");
  }

  delay(10000); // nghi giua cac lan quet
}

Những lỗi thường gặp

  • Địa chỉ MAC ngẫu nhiên (MAC randomization): iOS và Android hiện đại thường xuyên đổi MAC quảng bá để bảo vệ quyền riêng tư, khiến việc theo dõi bằng MAC cố định không ổn định. Giải pháp thực tế: dùng một thiết bị BLE chuyên dụng phát MAC cố định (vòng đeo tay, tag BLE như Nordic Thingy hoặc beacon iBeacon) thay vì trực tiếp điện thoại, hoặc chấp nhận độ chính xác không tuyệt đối.
  • Ngưỡng timeout quá ngắn khiến hệ thống báo “vắng mặt” giả khi điện thoại tạm thời không quảng bá BLE (màn hình khóa, tiết kiệm pin) — nên để timeout tối thiểu 1-2 phút.
  • Quét quá thường xuyên làm nóng ESP32 và tốn điện không cần thiết — quét 5 giây, nghỉ 5-10 giây là đủ cho hầu hết nhu cầu nhà ở.
  • Không xử lý mất kết nối WiFi/MQTT khiến thiết bị “treo” âm thầm — luôn kiểm tra và tự kết nối lại như trong code mẫu.

Mở rộng

Theo dõi nhiều thiết bị cùng lúc (mỗi thành viên trong nhà một MAC) để phân biệt “có người” và “ai đang ở nhà”, kết hợp với cảm biến chuyển động PIR để tăng độ tin cậy, hoặc tích hợp trực tiếp vào Home Assistant qua ESPHome thay vì tự viết firmware MQTT thủ công.

Thẻ:#esp32#bluetooth#home-automation

Bình luận