331 lines
14 KiB
Markdown
331 lines
14 KiB
Markdown
# WiFiSense — ESP32-S3 + OLED Edition
|
||
|
||
> Phát hiện chuyển động thụ động qua biến động tín hiệu RSSI WiFi, hiển thị trực tiếp trên màn hình OLED 0.96", tự động cấu hình WiFi qua Captive Portal.
|
||
|
||

|
||

|
||

|
||
|
||
---
|
||
|
||
## Mục lục
|
||
|
||
- [Nguyên lý hoạt động](#nguyên-lý-hoạt-động)
|
||
- [Sơ đồ luồng hệ thống](#sơ-đồ-luồng-hệ-thống)
|
||
- [Phần cứng](#phần-cứng)
|
||
- [Cài đặt môi trường](#cài-đặt-môi-trường)
|
||
- [Nạp code](#nạp-code)
|
||
- [Cách sử dụng](#cách-sử-dụng)
|
||
- [Màn hình OLED](#màn-hình-oled)
|
||
- [Troubleshooting](#troubleshooting)
|
||
|
||
---
|
||
|
||
## Nguyên lý hoạt động
|
||
|
||
WiFiSense **không dùng camera, microphone hay cảm biến vật lý nào**. Thay vào đó, nó khai thác một hiện tượng vật lý: khi có người/vật di chuyển trong phòng, sóng WiFi bị phản xạ và nhiễu loạn, khiến **chỉ số RSSI** (cường độ tín hiệu) dao động theo những mẫu đặc trưng.
|
||
|
||
### Pipeline xử lý tín hiệu
|
||
|
||
```
|
||
WiFi RSSI thô
|
||
│
|
||
▼
|
||
┌─────────────────┐
|
||
│ Kalman Filter │ ← Loại bỏ nhiễu ngẫu nhiên ngắn hạn
|
||
└────────┬────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────┐
|
||
│ Exponential Smoothing │ ← Làm mượt tín hiệu (α = 0.15)
|
||
└──────────┬───────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────────────────────────────┐
|
||
│ Cửa sổ trượt (40 mẫu ngắn + 100 mẫu dài) │
|
||
│ • Độ lệch chuẩn (Variance) → biên độ dao động │
|
||
│ • Rate of Change → tốc độ thay đổi │
|
||
│ • Peak Detection → đỉnh đột biến │
|
||
│ • Z-Score → bất thường thống │
|
||
└──────────────────────┬───────────────────────────┘
|
||
│
|
||
▼
|
||
┌────────────────────────────────┐
|
||
│ Tính Confidence Score (0–100) │
|
||
└──────────────┬─────────────────┘
|
||
│
|
||
▼
|
||
┌──────────────────────────────────────────────────┐
|
||
│ Persistence Counter (≥ 3 mẫu liên tiếp) │
|
||
│ → Kết luận: MOTION / CLEAR │
|
||
│ → Bật LED + hiển thị OLED │
|
||
└──────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Adaptive Baseline
|
||
|
||
Baseline (đường cơ sở khi không có chuyển động) **tự thích nghi** theo thời gian thực với tốc độ rất chậm (α = 0.008), giúp hệ thống tự điều chỉnh khi môi trường thay đổi dần (người rời phòng, thay đổi vị trí router…).
|
||
|
||
---
|
||
|
||
## Sơ đồ luồng hệ thống
|
||
|
||
```
|
||
┌──────────────┐
|
||
│ Khởi động │
|
||
└──────┬───────┘
|
||
│
|
||
┌──────▼───────┐
|
||
│ Đọc NVS │ credentials đã lưu?
|
||
└──────┬───────┘
|
||
Có ▼ ▼ Không
|
||
┌──────────────┐ ┌──────────────────────┐
|
||
│ Kết nối WiFi │ │ Mở AP WiFiSense-Setup│
|
||
└──────┬───────┘ │ DNS + Captive Portal │
|
||
OK ▼ ▼ Fail │ Người dùng chọn mạng │
|
||
┌────────┐ ┌──────────┐ │ → Lưu NVS → Restart │
|
||
│Calibrate│ │Thử lại x3│ └──────────────────────┘
|
||
└────┬───┘ └────┬─────┘
|
||
│ Fail│
|
||
│ ┌────▼──────────────┐
|
||
│ │ Xóa NVS + Restart │
|
||
│ └───────────────────┘
|
||
┌────▼──────────────────┐
|
||
│ Loop chính │
|
||
│ • Lấy RSSI mỗi 500ms │
|
||
│ • Kalman + Smooth │
|
||
│ • Tính Variance/Z │
|
||
│ • Cập nhật OLED │
|
||
│ • Bật/tắt LED │
|
||
└───────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## Phần cứng
|
||
|
||
### Linh kiện cần thiết
|
||
|
||
| Linh kiện | Thông số | Ghi chú |
|
||
|---|---|---|
|
||
| Vi điều khiển | ESP32-S3 (bất kỳ board) | Đã test trên ESP32-S3-DevKitC-1 |
|
||
| Màn hình OLED | 0.96" SSD1306 128×64 I2C | Địa chỉ I2C: `0x3C` |
|
||
| LED | 3mm hoặc 5mm, bất kỳ màu | Kèm điện trở 220Ω–330Ω |
|
||
| Dây jumper | Male-to-Male hoặc Male-to-Female | — |
|
||
|
||
### Sơ đồ đấu dây
|
||
|
||
```
|
||
ESP32-S3 SSD1306 OLED
|
||
───────── ────────────
|
||
GPIO 6 ───────── SDA
|
||
GPIO 7 ───────── SCL
|
||
3.3V ───────── VCC
|
||
GND ───────── GND
|
||
|
||
ESP32-S3 LED
|
||
───────── ────────────
|
||
GPIO 2 ──[330Ω]── Anode (+)
|
||
GND ───────── Cathode (-)
|
||
```
|
||
|
||
> **Lưu ý:** Một số board ESP32-S3 (ví dụ Waveshare) có thể có sẵn LED trên GPIO 2 — kiểm tra datasheet của board bạn đang dùng.
|
||
|
||
---
|
||
|
||
## Cài đặt môi trường
|
||
|
||
### 1. Arduino IDE
|
||
|
||
Tải về tại [arduino.cc/en/software](https://www.arduino.cc/en/software) (phiên bản 2.x khuyến nghị).
|
||
|
||
### 2. Thêm ESP32 vào Board Manager
|
||
|
||
Vào **File → Preferences → Additional boards manager URLs**, thêm:
|
||
|
||
```
|
||
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
|
||
```
|
||
|
||
Sau đó vào **Tools → Board → Boards Manager**, tìm **esp32** của Espressif và cài đặt.
|
||
|
||
### 3. Cài thư viện
|
||
|
||
Vào **Tools → Manage Libraries** và cài đặt 2 thư viện sau:
|
||
|
||
| Thư viện | Tác giả | Ghi chú |
|
||
|---|---|---|
|
||
| **Adafruit SSD1306** | Adafruit | Tìm đúng tên này |
|
||
| **Adafruit GFX Library** | Adafruit | Dependency của SSD1306 |
|
||
|
||
> Các thư viện còn lại (`WiFi`, `WebServer`, `DNSServer`, `Preferences`) đã có sẵn trong ESP32 Arduino core, **không cần cài thêm**.
|
||
|
||
### 4. Chọn board
|
||
|
||
**Tools → Board → ESP32 Arduino → ESP32S3 Dev Module**
|
||
|
||
Các thông số quan trọng:
|
||
|
||
| Tùy chọn | Giá trị |
|
||
|---|---|
|
||
| USB Mode | Hardware CDC and JTAG |
|
||
| Flash Size | 4MB (hoặc theo board của bạn) |
|
||
| Partition Scheme | Default 4MB with spiffs |
|
||
| PSRAM | Disabled (nếu board không có) |
|
||
|
||
---
|
||
|
||
## Nạp code
|
||
|
||
1. Kết nối ESP32-S3 với máy tính qua USB.
|
||
2. Mở file `WiFiSense.ino` trong Arduino IDE.
|
||
3. Chọn đúng **Port** tại **Tools → Port**.
|
||
4. Nhấn nút **Upload** (→) hoặc `Ctrl+U`.
|
||
5. Chờ quá trình biên dịch và nạp hoàn tất (~30–60 giây).
|
||
|
||
> Nếu board không tự vào chế độ nạp: giữ nút **BOOT**, nhấn **RESET**, thả **RESET**, rồi thả **BOOT**.
|
||
|
||
---
|
||
|
||
## Cách sử dụng
|
||
|
||
### Lần đầu tiên (chưa có WiFi lưu)
|
||
|
||
Khi khởi động và chưa có credentials trong NVS, ESP32 sẽ tự động mở **Captive Portal**:
|
||
|
||
```
|
||
OLED hiển thị:
|
||
┌──────────────────────────────┐
|
||
│ ** SETUP MODE ** │
|
||
│──────────────────────────────│
|
||
│ 1. Ket noi WiFi: │
|
||
│ WiFiSense-Setup │
|
||
│ 2. Mo trinh duyet: │
|
||
│ 192.168.4.1 │
|
||
└──────────────────────────────┘
|
||
```
|
||
|
||
**Các bước cấu hình:**
|
||
|
||
1. Dùng điện thoại hoặc máy tính, kết nối vào WiFi **`WiFiSense-Setup`** (không mật khẩu).
|
||
2. Trình duyệt sẽ tự động mở trang cấu hình (Captive Portal). Nếu không tự mở, truy cập thủ công vào `http://192.168.4.1`.
|
||
3. Chọn mạng WiFi nhà bạn từ danh sách quét được.
|
||
4. Nhập mật khẩu WiFi → nhấn **Kết nối & Lưu**.
|
||
5. ESP32 lưu thông tin vào bộ nhớ NVS và **tự khởi động lại**.
|
||
6. Lần này ESP32 kết nối thẳng vào WiFi đã chọn và bắt đầu calibrate.
|
||
|
||
### Các lần sau (đã có WiFi lưu)
|
||
|
||
ESP32 tự kết nối WiFi khi khởi động, không cần cấu hình lại.
|
||
|
||
### Đổi WiFi
|
||
|
||
Nếu muốn kết nối sang mạng khác: xóa credentials bằng cách mất điện trong khi đang ở chế độ portal, hoặc thêm một nút bấm vật lý để gọi `clearCredentials()` + `ESP.restart()`.
|
||
|
||
### Calibration
|
||
|
||
Sau khi kết nối WiFi thành công, ESP32 sẽ **calibrate trong ~6 giây** (200 mẫu × 30ms). Trong thời gian này:
|
||
|
||
- **Giữ phòng trống** (không có người di chuyển).
|
||
- OLED hiển thị tiến trình calibration.
|
||
- Baseline được thiết lập dựa trên RSSI trung bình.
|
||
|
||
---
|
||
|
||
## Màn hình OLED
|
||
|
||
Sau calibration, OLED hiển thị **1 trang duy nhất** cập nhật mỗi 500ms:
|
||
|
||
```
|
||
┌──────────────────────────────┐ ← 128px
|
||
│ WiFiSense [ MOTION ] │ dòng 1: tiêu đề + badge trạng thái
|
||
│──────────────────────────────│ đường kẻ
|
||
│ RSSI: -67dBm C: 82% │ dòng 2: RSSI thô + Confidence số
|
||
│ Conf [████████████░░░░░░] │ dòng 3: thanh Confidence
|
||
│ Qual [█████████░░░░░░░░░] │ dòng 4: thanh Signal Quality
|
||
│──────────────────────────────│ đường kẻ
|
||
│ Base:-66.1 Var:2.3 │ dòng 5: Baseline + Variance
|
||
│ #15 WALKING │ dòng 6: tổng detections + cường độ
|
||
└──────────────────────────────┘
|
||
```
|
||
|
||
### Ý nghĩa các chỉ số
|
||
|
||
| Chỉ số | Mô tả |
|
||
|---|---|
|
||
| **RSSI** | Cường độ tín hiệu WiFi thô (dBm). Giá trị điển hình: -40 (gần) đến -90 (xa). |
|
||
| **C (Confidence)** | Độ tin cậy phát hiện chuyển động, 0–100%. Trên ~50% là có người. |
|
||
| **Conf bar** | Thanh trực quan hóa Confidence. |
|
||
| **Qual bar** | Chất lượng tín hiệu tổng thể (dựa trên độ ổn định dài hạn). |
|
||
| **Base** | Baseline RSSI hiện tại (tự thích nghi). |
|
||
| **Var** | Variance của cửa sổ 40 mẫu gần nhất — số càng cao = dao động càng mạnh. |
|
||
| **#N** | Tổng số lần phát hiện chuyển động kể từ khi khởi động. |
|
||
| **Badge** | `MOTION` (đảo màu trắng) khi phát hiện, `CLEAR` khi yên tĩnh. |
|
||
| **Cường độ** | `CALM` / `SLOW` / `WALKING` / `FAST` / `SPRINT` — ước lượng mức độ di chuyển. |
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### OLED không hiển thị gì
|
||
|
||
| Nguyên nhân | Giải pháp |
|
||
|---|---|
|
||
| Sai địa chỉ I2C | Chạy sketch `I2C Scanner` để tìm địa chỉ thực. Nếu khác `0x3C`, sửa `#define OLED_ADDRESS` thành `0x3D`. |
|
||
| Sai chân SDA/SCL | Kiểm tra lại: SDA → GPIO **6**, SCL → GPIO **7**. |
|
||
| Nguồn không đủ | Đảm bảo OLED lấy nguồn 3.3V (không phải 5V đối với các module 3.3V). |
|
||
| Thư viện chưa cài | Kiểm tra lại Library Manager: `Adafruit SSD1306` và `Adafruit GFX Library`. |
|
||
|
||
### Không thấy WiFi "WiFiSense-Setup"
|
||
|
||
| Nguyên nhân | Giải pháp |
|
||
|---|---|
|
||
| ESP32 đang kết nối WiFi cũ | Chờ 30 giây timeout, ESP32 sẽ tự chuyển sang AP mode. |
|
||
| Credentials cũ còn trong NVS | Upload lại firmware có `clearCredentials()` trong `setup()` (tạm thời), sau đó upload lại bản gốc. |
|
||
|
||
### Trang cấu hình không tự mở trên điện thoại
|
||
|
||
M��t số thiết bị Android/iOS không tự kích hoạt captive portal với DNS redirect. Mở trình duyệt và truy cập thủ công: **`http://192.168.4.1`**
|
||
|
||
### Phát hiện chuyển động không chính xác
|
||
|
||
| Vấn đề | Giải pháp |
|
||
|---|---|
|
||
| Phát hiện khi không có người | Giảm `FAST_MOVEMENT_THRESHOLD` và `SLOW_MOVEMENT_THRESHOLD`, hoặc tăng `PERSISTENCE_REQUIRED`. |
|
||
| Không phát hiện khi có người | Đặt ESP32 gần router hơn; tăng `CALIBRATION_SAMPLES`; kiểm tra không có thiết bị khác gây nhiễu. |
|
||
| Baseline trôi quá nhanh | Giảm `ADAPTIVE_ALPHA` (ví dụ từ `0.008` xuống `0.003`). |
|
||
|
||
### Mất kết nối WiFi liên tục
|
||
|
||
- Kiểm tra khoảng cách tới router. RSSI nên ở mức > -75 dBm để ổn định.
|
||
- Sau 3 lần mất kết nối liên tiếp (`MAX_RECONNECT_ATTEMPTS = 3`), ESP32 tự xóa NVS và mở lại Captive Portal — đây là hành vi thiết kế để người dùng có thể đổi mạng.
|
||
- Tăng `MAX_RECONNECT_ATTEMPTS` nếu mạng nhà bạn hay bị ngắt tạm thời.
|
||
|
||
### Upload thất bại (Port not found / No device)
|
||
|
||
1. Kiểm tra cáp USB (phải là cáp data, không phải cáp sạc).
|
||
2. Giữ nút **BOOT** trên board, nhấn **RESET**, thả **RESET**, thả **BOOT**, rồi Upload ngay.
|
||
3. Thử port COM khác trong Arduino IDE.
|
||
|
||
---
|
||
|
||
## Cấu trúc project
|
||
|
||
```
|
||
WiFiSense/
|
||
└── WiFiSense.ino ← Toàn bộ code trong 1 file
|
||
```
|
||
|
||
---
|
||
|
||
## Tác giả
|
||
|
||
**Tony Trần** — [github.com/thanhtantran](https://github.com/thanhtantran)
|
||
**Orange Pi Việt Nam** — [orangepi.vn](https://orangepi.vn)
|
||
|
||
---
|
||
|
||
## License
|
||
|
||
MIT License — xem file `LICENSE` để biết thêm chi tiết.
|