Files
Mira_Firmware_Loader/README.md
2026-03-06 22:19:58 +07:00

256 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ⚡ IoT Firmware Loader
Công cụ desktop dùng để **scan, phát hiện và flash firmware hàng loạt** cho các thiết bị IoT (ESP32/ESP8266) trong mạng LAN.
> **Tech stack:** Python 3.9+ · PyQt6 · Scapy · Requests
---
## 📁 Cấu trúc dự án
```
iot_fw_loader/
├── main.py # UI chính (PyQt6) + điều phối toàn bộ luồng
├── scanner.py # Quét thiết bị trong mạng LAN
├── device_filter.py # Lọc thiết bị IoT theo MAC vendor
├── flasher.py # Upload firmware qua HTTP
├── run.sh # Script khởi chạy (macOS/Linux)
├── run.bat # Script khởi chạy (Windows)
└── venv/ # Python virtual environment
```
---
## 🔧 Cài đặt & Chạy
### Yêu cầu
- Python 3.9+
- Các thư viện: `PyQt6`, `scapy`, `requests`
### Khởi chạy nhanh
**macOS / Linux:**
```bash
./run.sh
```
**Windows:**
```bat
run.bat
```
> Script tự tạo `venv` và cài dependencies nếu chưa có.
**Hoặc chạy thủ công:**
```bash
source venv/bin/activate
python main.py
```
---
## 🏗 Kiến trúc hệ thống
```
┌─────────────────────────────────────────────────────┐
│ main.py (UI) │
│ ┌───────────┐ ┌───────────┐ ┌────────────────────┐ │
│ │ Machine │ │ Firmware │ │ Network Scan │ │
│ │ Info │ │ Selector │ │ (QThread) │ │
│ └───────────┘ └───────────┘ └────────┬───────────┘ │
│ ┌────────────────────────────────────┼───────────┐ │
│ │ Device Table │ │ │
│ │ IP │ MAC │ Type │ Status │ │ │
│ └────────────────────────────────────┼───────────┘ │
│ ┌────────────────────────────────────┼───────────┐ │
│ │ Flash Controls + Progress Bar │ │ │
│ │ (ThreadPoolExecutor × 5) │ │ │
│ └────────────────────────────────────┼───────────┘ │
└───────────────────────────────────────┼─────────────┘
┌───────────────────┼───────────────────┐
│ │ │
┌─────▼─────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ scanner.py │ │device_filter│ │ flasher.py │
│ │ │ .py │ │ │
│ Scapy ARP │ │ MAC vendor │ │ HTTP POST │
│ ↓ fallback│ │ matching │ │ /update │
│ arp -a │ │ │ │ │
└────────────┘ └─────────────┘ └─────────────┘
```
---
## 🔄 Luồng hoạt động chi tiết
### Tổng quan
```
Khởi động App ──▶ Hiển thị Machine Info
Chọn Firmware (.bin/.hex/.uf2)
Nhập dải mạng ──▶ Nhấn "Scan LAN"
┌─────────────────────────────────┐
│ ScanThread (background) │
│ 1. Thử Scapy ARP broadcast │
│ 2. Nếu lỗi → fallback arp -a │
└──────────────┬──────────────────┘
Lọc IoT device theo MAC vendor
Hiển thị bảng: tất cả thiết bị
(IoT = 🟢 xanh lá, Other = ⚪)
Nhấn "Flash All IoT Devices"
┌──────────────────────────────────┐
│ ThreadPoolExecutor (5 workers) │
│ POST firmware → mỗi thiết bị │
│ Cập nhật status + progress bar │
└──────────────────────────────────┘
Hoàn tất ✅
```
---
## 📋 Phân tích từng module
### 1. `main.py` — Giao diện & Điều phối
| Thành phần | Mô tả |
| ------------------------- | --------------------------------------------- |
| `get_local_ip()` | Lấy IP máy tính qua UDP socket tới `8.8.8.8` |
| `get_default_network(ip)` | Tự tính dải mạng `/24` từ IP máy |
| `get_machine_info()` | Thu thập hostname, IP, OS, MAC |
| `ScanThread` | `QThread` chạy scan ở background, tránh đơ UI |
| `App` | Widget chính chứa toàn bộ UI |
**Các phần UI:**
| Group Box | Chức năng |
| ---------------- | -------------------------------------- |
| 🖥 Machine Info | Hiển thị Hostname, IP, OS, MAC của máy |
| 📦 Firmware | Chọn file firmware |
| 📡 Network Scan | Nhập dải mạng + nút Scan |
| 📋 Devices Found | Bảng thiết bị (IP, MAC, Type, Status) |
| 🚀 Flash | Progress bar + nút Flash All |
**Xử lý sự kiện chính:**
| Method | Trigger | Hành động |
| ------------------ | ------------------ | ------------------------------------------ |
| `select_fw()` | Nhấn "Select File" | Mở dialog chọn firmware |
| `scan()` | Nhấn "Scan LAN" | Tạo `ScanThread` → chạy background |
| `_on_scan_done()` | Scan hoàn tất | Phân loại IoT/Other → hiển thị bảng |
| `_on_scan_error()` | Scan lỗi | Hiện `QMessageBox` lỗi |
| `flash_all()` | Nhấn "Flash All" | Tạo `ThreadPoolExecutor` → flash song song |
| `flash_worker()` | Mỗi thread | POST firmware → cập nhật status |
---
### 2. `scanner.py` — Quét mạng
**Chiến lược 2 lớp (dual-layer scan):**
| Phương pháp | Hàm | Yêu cầu | Độ chính xác |
| ----------------------- | ------------------------ | -------------- | ------------------------------------------- |
| **Primary:** Scapy ARP | `_scan_with_scapy()` | Root/sudo | Cao — scan active |
| **Fallback:** ARP table | `_scan_with_arp_table()` | Không cần root | Trung bình — chỉ thấy thiết bị đã giao tiếp |
**Luồng trong `scan_network()`:**
```python
try:
return _scan_with_scapy(network) # Cần root
except:
return _scan_with_arp_table(network) # Fallback, parse "arp -a"
```
**Scapy ARP scan:**
- Tạo ARP request → broadcast `ff:ff:ff:ff:ff:ff`
- Thu thập response → trích IP + MAC
- Timeout: 2 giây
**ARP table fallback:**
- Chạy lệnh `arp -a` của hệ thống
- Parse output bằng regex: `\(IP\) at MAC`
- Lọc theo dải mạng đầu vào
---
### 3. `device_filter.py` — Phân loại thiết bị
Lọc thiết bị IoT dựa trên **3 byte đầu của MAC address** (OUI — Organizationally Unique Identifier):
| MAC Prefix | Vendor |
| ---------- | ------------------------- |
| `24:6F:28` | Espressif (ESP32/ESP8266) |
| `84:F3:EB` | Espressif |
| `DC:4F:22` | Espressif |
> **Lưu ý:** Chỉ thiết bị có MAC bắt đầu bằng các prefix trên mới được đánh dấu là IoT. Tất cả thiết bị khác vẫn hiển thị trong bảng với label "Other".
---
### 4. `flasher.py` — Nạp firmware
| Bước | Chi tiết |
| ------------- | --------------------------------------------------- |
| 1. Mở file | `open(firmware_path, "rb")` |
| 2. Upload | `POST http://{ip}/update` với `multipart/form-data` |
| 3. Chờ reboot | `time.sleep(3)` — đợi thiết bị khởi động lại |
| 4. Kết quả | Trả `"DONE"` hoặc `"FAIL"` |
> **Giả định:** Thiết bị IoT chạy HTTP server với endpoint `/update` hỗ trợ OTA firmware update (chuẩn ESP32 Arduino OTA).
---
## ⚙️ Cấu hình
| Tham số | Giá trị | Vị trí |
| ----------------- | ----------------------- | ----------------------------------------------- |
| Network range | Tự tính `/24` từ IP máy | `main.py``get_default_network()` |
| Scan timeout | 2 giây | `scanner.py``srp(..., timeout=2)` |
| Flash timeout | 10 giây | `flasher.py``requests.post(..., timeout=10)` |
| Reboot wait | 3 giây | `flasher.py``time.sleep(3)` |
| Max flash workers | 5 | `main.py``ThreadPoolExecutor(max_workers=5)` |
| Firmware filter | `.bin .hex .uf2` | `main.py``QFileDialog` filter |
---
## 🔒 Lưu ý bảo mật & Quyền
- **Scapy cần root/sudo** trên macOS/Linux để gửi raw ARP packets
- Nếu không có quyền root → tự động dùng `arp -a` (không cần root nhưng chỉ thấy thiết bị đã giao tiếp gần đây)
- Firmware upload qua **HTTP không mã hóa** — chỉ nên dùng trong mạng LAN nội bộ
---
## 📊 Trạng thái thiết bị (State Machine)
```
READY ──────▶ FLASHING
│ │
│ ┌─────┴──────┐
│ ▼ ▼
│ ✅ DONE ❌ FAIL
└── (thiết bị Other: trạng thái "—", không flash)
```