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

11 KiB
Raw Blame History

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:

./run.sh

Windows:

run.bat

Script tự tạo venv và cài dependencies nếu chưa có.

Hoặc chạy thủ công:

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():

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.pyget_default_network()
Scan timeout 2 giây scanner.pysrp(..., timeout=2)
Flash timeout 10 giây flasher.pyrequests.post(..., timeout=10)
Reboot wait 3 giây flasher.pytime.sleep(3)
Max flash workers 5 main.pyThreadPoolExecutor(max_workers=5)
Firmware filter .bin .hex .uf2 main.pyQFileDialog 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)