# MyBeacon Native Нативная реализация BLE/WiFi сканера для Luckfox Pico Ultra W на Alpine Linux. ## Hardware - **SoC:** RK1106 (ARM Cortex-A7 @ 1.2 GHz) - **RAM:** 256 MB - **Storage:** 8 GB eMMC - **WiFi/BT:** AIC8800DC (combo chip) - 2.4 GHz WiFi (802.11 b/g/n) - Bluetooth 5.0 + BLE - **ВАЖНО:** Один радиомодуль - BLE и WiFi в некоторых случаях конфликтуют! ### AIC8800 Combo Chip Ограничения ``` Один физический радиомодуль для WiFi + BLE: ✓ РАБОТАЕТ: - BLE scan + WiFi client одновременно - BLE scan + WiFi monitor одновременно - BLE scan + WiFi AP одновременно ✗ НЕ РАБОТАЕТ: - WiFi client + monitor одновременно (data transfer blocked) РЕШЕНИЕ В КОДЕ: - Меняем режимы WiFi ТОЛЬКО когда BLE остановлен - При подключении WiFi client - BLE временно останавливается - После WiFi подключения - BLE автоматически перезапускается - С WiFi monitor тоже самое. ``` ### USB Ограничения (для модемов) ``` USB SWITCH (U10) между Type-C и USB-A: - SEL подтянут к VBUS через R70 (62K) Type-C питание → USB-A НЕ РАБОТАЕТ (data на Type-C) POE питание → USB-A РАБОТАЕТ 5V на гребёнку → USB-A РАБОТАЕТ ДЛЯ USB-МОДЕМА: требуется POE или внешний 5V! ``` ## Компоненты - **ble-scanner** — BLE сканирование через BlueZ D-Bus API - **wifi-scanner** — WiFi probe request capture через pcap/AF_PACKET - **beacon-daemon** — центральный демон: - Управление сканерами (запуск/остановка) - Управление сетью (eth0, wlan0 client, AP fallback, NTP) - Агрегация событий через ZMQ - Batching + gzip + HTTP POST на сервер - Spooler (при отсутствии сети) - HTTP API + WebSocket для dashboard (вкл/выкл с сервера) - SSH туннель (reverse, для терминала) - Dashboard туннель (reverse, для веб-доступа) - Config polling (cloud mode) ## Device Modes ``` ┌──────────────┐ ┌──────────────┐ │ CLOUD MODE │ │ LAN MODE │ │ (default) │ │ │ ├──────────────┤ ├──────────────┤ │ • Config from│ │ • Config │ │ server │ │ local only │ │ • Polling ON │ │ • Polling OFF│ │ (30 sec) │ │ │ └──────────────┘ └──────────────┘ Первая регистрация ВСЕГДА через интернет (для получения device_password) ``` ## Network Priority & AP Fallback ``` Priority 1: eth0 (carrier detect) │ ├── Link UP → DHCP/Static → ONLINE │ └── Link DOWN │ Priority 2: wlan0 client (if configured, НЕЗАВИСИМО от eth0!) │ ├── WiFi.ClientEnabled = true → Connect │ └── Success → ONLINE (eth0 + wlan0 одновременно) │ └── WiFi.ClientEnabled = false OR connection failed │ WiFi Scanner: если wlan0 free AND MonitorEnabled │ └── BLE останавливается (AIC8800 conflict) │ Fallback: wlan0 AP (120 сек без сети) │ └── AP UP (SSID: mybeacon_XXXX, пароль: device_password) └── Продолжаем пробовать wlan0 client каждые 30 сек! AP Settings: SSID: "mybeacon_<4 последних символа MAC>" (например: mybeacon_1bac) Password: device_password из /opt/mybeacon/etc/device.json (8 цифр с регистрации) fallback: "mybeacon" (если device_password пустой) IP: 192.168.4.1 DHCP: 192.168.4.100-200 (dnsmasq) ``` ## BLE Types (3 типа) 1. **iBeacon** (Apple 0x004C) - UUID + Major + Minor + TxPower 2. **my-beacon_acc** (Nordic 0x0059) - Format: "acc" + X/Y/Z (accel) + battery + temp 3. **rt_mybeacon** (Custom 0xFFFF) - Relay beacon: "rt" + orig_mac + orig_rssi + payload ## Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ beacon-daemon (Go) │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ Network Manager │ │ │ │ - eth0 (DHCP/Static) │ │ │ │ - wlan0 client (WPA2-PSK) │ │ │ │ - wlan0 AP fallback (120s timeout) │ │ │ │ - NTP sync (auto-start on network up) │ │ │ │ - WiFi/BLE scanner coordination (AIC8800 workaround) │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ Scanner Manager │ │ │ │ - Starts/stops ble-scanner and wifi-scanner │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ ZMQ SUB │ │ ZMQ SUB │ │ HTTP API + WS │ │ │ │ (BLE) │ │ (WiFi) │ │ (Dashboard) │ │ │ │ :5555 │ │ :5556 │ │ :80 │ │ │ └──────┬──────┘ └──────┬──────┘ └─────────────────────┘ │ │ │ │ │ │ └───────┬────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ Event Batcher → gzip → HTTP POST → Server │ │ │ │ ↓ (on failure) │ │ │ │ Spooler (max 100MB) │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ Config │ │ Registration│ │ SSH Tunnel │ │ │ │ Poller │ │ Handler │ │ Manager │ │ │ │ (30s) │ │ │ │ - SSH (22→50xxx) │ │ │ │ │ │ │ │ - Dashboard (80→60xxx) │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ │ │ spawns │ spawns ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ ble-scanner │ │ wifi-scanner │ │ (Go) │ │ (Go) │ │ │ │ │ │ BlueZ D-Bus API │ │ pcap + nl80211 │ │ ZMQ PUB :5555 │ │ ZMQ PUB :5556 │ └─────────────────┘ └─────────────────┘ ``` ## ZMQ Протокол Сканеры публикуют события в формате: `topic JSON` Топики: - `ble.ibeacon` — iBeacon - `ble.acc` — my-beacon accelerometer - `ble.relay` — relay beacon - `wifi.probe` — WiFi probe request Пример: ``` ble.ibeacon {"type":"ble.ibeacon","mac":"AA:BB:CC:DD:EE:FF","rssi":-65,"ts_ms":1703001234567,...} ``` ## HTTP API ``` GET /api/status - статус (сеть, режим, counters, uptime) GET /api/metrics - метрики системы (CPU, mem, temp) GET /api/ble/recent - последние BLE события (15 sec TTL) GET /api/wifi/recent - последние WiFi события GET /api/config - конфиг (без секретов) POST /api/settings - применить настройки (требует пароль) POST /api/unlock - проверить device_password GET /api/logs - daemon logs (last 100 lines) WS /api/ws - live updates (BLE/WiFi events) Static: / - Vue.js dashboard ``` ## Dashboard ``` ┌─────────────────────────────────────────────────────────────┐ │ 🟢 MyBeacon Device: 38:54:39:4b:1b:ac │ ├─────────────────────────────────────────────────────────────┤ │ [Status] [Daemon Log] [Settings] │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Status Tab: │ │ - System info (uptime, CPU, memory, temp) │ │ - Server connection status │ │ - NTP server │ │ - Ethernet (IP, gateway, DNS, RX/TX) │ │ - Wireless (IP, SSID, gateway, channel, RSSI, RX/TX) │ │ - Live BLE devices (last 15s with TTL countdown) │ │ │ │ Settings Tab (device_password protected): │ │ - Mode (Cloud/LAN) │ │ - eth0 config (DHCP/Static IP/Gateway/DNS) │ │ - WiFi client (Enable/SSID/PSK) │ │ - WiFi monitor mode (Enable/Disable) │ │ - NTP servers │ │ - API endpoints (для LAN mode) │ │ - SSH Tunnel config │ │ │ │ Daemon Log Tab: │ │ - Real-time logs через WebSocket │ │ │ └─────────────────────────────────────────────────────────────┘ Доступ: http://192.168.4.1 (AP mode) или http:// ``` ## Recovery Procedure (AP Fallback) 1. Выдернуть Ethernet кабель 2. Отключить WiFi на стороне сервера (если device подключен как client) 3. Подождать **120 секунд** 4. Устройство автоматически поднимает AP: **mybeacon_XXXX** (где XXXX - последние 4 символа MAC) 5. Пароль AP: **device_password** из `/opt/mybeacon/etc/device.json` 6. Подключиться к AP с телефона/ноутбука 7. Зайти на **http://192.168.4.1** 8. Ввести device_password в Settings 9. Исправить настройки (WiFi SSID/PSK или eth0) 10. Воткнуть кабель обратно ИЛИ включить WiFi на сервере **Пароль забыт?** → Посмотреть в личном кабинете на сервере или в `/opt/mybeacon/etc/device.json` (требуется SSH доступ) ## File Layout ``` /opt/mybeacon/ ├── bin/ │ ├── ble-scanner # BLE сканер │ ├── wifi-scanner # WiFi сканер │ ├── beacon-daemon # Главный демон │ ├── wifi-connect.sh # WiFi client подключение │ ├── ap-start.sh # Запуск AP fallback │ └── ap-stop.sh # Остановка AP fallback ├── etc/ │ ├── config.json # Конфигурация (user-editable) │ ├── device.json # Device state (device_id, device_token, device_password) │ └── tunnel_key # SSH private key для туннеля └── www/ # Vue.js dashboard (dist) ├── index.html └── assets/ /var/spool/mybeacon/ # Очередь событий (при отсутствии сети) ├── ble/ └── wifi/ /var/log/ # tmpfs (10MB, не убивает eMMC) ├── mybeacon.log # Daemon logs ├── wifi-connect.log # WiFi connection logs └── ap.log # AP fallback logs /etc/init.d/ ├── S01tmpfs-log # Mount /var/log as tmpfs ├── S10udev ├── S10usbhost ├── S15wireless # Load AIC8800 modules ├── S20dbus # D-Bus (required for BlueZ) ├── S30network # Базовая сеть (deprecated, управляет Network Manager) ├── S30usbmodem # USB modem support ├── S36bluetooth # BlueZ (required for BLE) ├── S50sshd # SSH server └── S98mybeacon # MyBeacon daemon (управляет сетью и NTP!) ``` ## Сборка ```bash # Prerequisites sudo apt install golang libzmq3-dev gcc-arm-linux-gnueabihf # Clone cd /home/user/work/luckfox/alpine/mybeacon # Download Go dependencies go mod download # ARM cross-compile (для Luckfox) make arm # Результат в bin/arm/: # - beacon-daemon # - ble-scanner # - wifi-scanner ``` ## Конфигурация ### /opt/mybeacon/etc/config.json ```json { "mode": "cloud", "api_base": "http://server:5000/api/v1", "zmq_addr_ble": "tcp://127.0.0.1:5555", "zmq_addr_wifi": "tcp://127.0.0.1:5556", "spool_dir": "/var/spool/mybeacon", "wifi_iface": "wlan0", "debug": false, "ble": { "enabled": true, "batch_interval_ms": 2500 }, "wifi": { "monitor_enabled": true, "client_enabled": false, "ssid": "", "psk": "", "batch_interval_ms": 10000 }, "network": { "ntp_servers": ["pool.ntp.org"], "eth0": { "static": false, "address": "", "gateway": "", "dns": "" } }, "ap_fallback": { "password": "mybeacon" }, "dashboard": { "enabled": true }, "ssh_tunnel": { "enabled": false, "server": "tunnel.example.com", "port": 22, "user": "tunnel", "remote_port": 0, "keepalive_interval": 30, "reconnect_delay": 5 }, "dashboard_tunnel": { "enabled": false, "server": "tunnel.example.com", "port": 22, "user": "tunnel", "remote_port": 0, "keepalive_interval": 30, "reconnect_delay": 5 } } ``` ### /opt/mybeacon/etc/device.json (generated on first registration) ```json { "device_id": "38:54:39:4b:1b:ac", "device_token": "VL9tUGZrxGR7G4KmSDYjhrfZT7Por7C/ghvH9HdwMjQ=", "device_password": "62358673" } ``` ### Server Config (from GET /api/v1/config) Конфигурация, которую устройство получает с сервера каждые 30 секунд в cloud mode: ```json { "force_cloud": false, "ble": { "enabled": true, "batch_interval_ms": 2500, "uuid_filter_hex": "" }, "wifi": { "client_enabled": false, "ssid": "AP_name", "psk": "123456789", "monitor_enabled": true, "batch_interval_ms": 10000 }, "ssh_tunnel": { "enabled": false, "server": "tunnel.example.com", "port": 22, "user": "tunnel", "remote_port": 0, "keepalive_interval": 30 }, "dashboard_tunnel": { "enabled": false, "server": "tunnel.example.com", "port": 22, "user": "tunnel", "remote_port": 0, "keepalive_interval": 30 }, "dashboard": { "enabled": true }, "net": { "ntp": { "servers": ["pool.ntp.org", "time.google.com"] } }, "debug": false } ``` **Приоритет настроек:** - **Cloud mode:** серверные настройки имеют приоритет (BLE, WiFi, NTP) - **LAN mode:** локальные настройки имеют приоритет - **SSH tunnel:** ВСЕГДА с сервера (для удалённой поддержки) - **Dashboard:** ВСЕГДА с сервера (для удалённого управления) - **eth0:** ВСЕГДА локальные (никогда с сервера) ## Server API Endpoints Устройство взаимодействует с сервером через следующие endpoints: ### POST /api/v1/registration Регистрация нового устройства. Отправляется один раз при первом запуске. **Request:** ```json { "device_id": "38:54:39:4b:1b:ac", "eth_ip": "192.168.1.100", "wlan_ip": "192.168.1.101" } ``` **Response:** ```json { "device_token": "secure-token-here", "device_password": "generated-password" } ``` ### GET /api/v1/config Получение конфигурации от сервера (polling каждые 30 секунд в Cloud Mode). **Headers:** ``` Authorization: Bearer {device_token} ``` **Response:** см. выше (Server Config) ### POST /api/v1/ble Загрузка BLE событий. **Headers:** ``` Authorization: Bearer {device_token} Content-Type: application/json Content-Encoding: gzip ``` **Request (gzipped):** ```json { "device_id": "38:54:39:4b:1b:ac", "events": [ { "timestamp": 1234567890, "mac": "AA:BB:CC:DD:EE:FF", "rssi": -65, "name": "Device Name" } ] } ``` ### POST /api/v1/wifi Загрузка WiFi событий. **Headers:** ``` Authorization: Bearer {device_token} Content-Type: application/json Content-Encoding: gzip ``` **Request (gzipped):** ```json { "device_id": "38:54:39:4b:1b:ac", "events": [ { "timestamp": 1234567890, "mac": "AA:BB:CC:DD:EE:FF", "rssi": -65, "ssid": "Network Name" } ] } ``` ### POST /api/v1/wifi-credentials Обновление WiFi credentials пользователем через Dashboard (только в Cloud Mode). **Headers:** ``` Authorization: Bearer {device_token} Content-Type: application/json ``` **Request:** ```json { "ssid": "NewNetwork", "psk": "password123" } ``` **Response:** ```json { "success": true } ``` **Назначение:** Когда пользователь в Cloud Mode изменяет WiFi Client credentials через Dashboard, устройство: 1. Сохраняет новые credentials локально 2. Применяет их для подключения к WiFi 3. Отправляет их на сервер для централизованного управления Сервер может использовать эти данные для: - Отображения текущей WiFi конфигурации в личном кабинете - Удалённого управления WiFi подключением (через GET /api/v1/config) - Автоматической синхронизации настроек между устройствами ## Деплой ### Через образ Alpine Linux ```bash cd /home/user/work/luckfox/alpine # 1. Собрать бинарники cd mybeacon && make arm && cd .. # 2. Скопировать в overlay cp mybeacon/bin/arm/* overlay/opt/mybeacon/bin/ # 3. Собрать dashboard cd mybeacon/dashboard && npm run build && cd ../.. cp -r mybeacon/dashboard/dist/* overlay/opt/mybeacon/www/ # 4. Собрать образ cd scripts && sudo ./build_update_img.sh # 5. Прошить (device в maskrom mode) sudo /home/user/work/luckfox/upgrade_tool_v2.17_for_linux/upgrade_tool uf output/update.img ``` ### Обновление только демона (через SSH) ```bash # Собрать cd /home/user/work/luckfox/alpine/mybeacon && make arm # Залить scp bin/arm/beacon-daemon root@:/opt/mybeacon/bin/ # Перезапустить ssh root@ '/etc/init.d/S98mybeacon restart' ``` ## Текущий статус - [x] BLE Scanner (BlueZ D-Bus API) - работает - [x] WiFi Scanner (pcap + nl80211) - работает (с AIC8800 workaround) - [x] Daemon core (ZMQ, batching, gzip, upload) - работает - [x] Scanner Manager - работает - [x] Network Manager (eth0/wlan0 client/AP fallback/NTP) - работает - [x] Device Registration - работает - [x] Config Polling (cloud mode, 30s) - работает, сразу после регистрации - [x] HTTP API - работает - [x] Vue.js Dashboard - работает - [x] Dashboard remote control (enable/disable from server) - работает - [x] NTP management (auto-start on network up) - работает - [x] Spooler (offline queue) - работает - [x] SSH Tunnel (reverse, для терминала) - работает ✓ - [x] Dashboard Tunnel (reverse, для веб-доступа) - работает ✓ - [x] Logs optimization (tmpfs, rotation, spam reduction) - работает ## Known Issues 1. **AIC8800 BLE + WiFi monitor conflict** - решено через координацию в Network Manager 2. **WiFi scanner spam "bad file descriptor" при остановке** - решено (graceful exit) 3. **Upload failed spam при отсутствии сети** - решено (логирование раз в минуту) 4. **ap0 interface не удалялся через `ip link del`** - решено (используется `iw dev ap0 del`) 5. **Dashboard обрезался снизу на мобильных** - решено (padding-bottom: 6rem) ## Performance - **BLE events:** ~2-5 events/sec → batching 2.5s → ~5-12 events/request - **WiFi events:** ~1-20 probes/sec → batching 10s → ~10-200 events/request - **CPU usage:** ~5-10% (idle), ~15-20% (active scanning) - **Memory:** ~30-40 MB (daemon + scanners) - **Network:** ~1-5 KB/sec upload (compressed) ## License Proprietary - All Rights Reserved