Compare commits
14 Commits
7e1f0bef36
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c4b9d03e14 | |||
| afbf4cad61 | |||
| e8218a2ab5 | |||
| d6bc57dbab | |||
| 464160c1f7 | |||
| efca4a6b7a | |||
| 000301355f | |||
| a85732aa80 | |||
| 306243eb6a | |||
| 161e0e654c | |||
| 5347a728da | |||
| 9eddc702b6 | |||
| 2d2bd69780 | |||
| 48f64a6858 |
20
install.sh
20
install.sh
@@ -158,6 +158,22 @@ chmod +x "$INSTALL_DIR/bin/clawd.js"
|
|||||||
|
|
||||||
info "clawd symlinked to /usr/local/bin/clawd"
|
info "clawd symlinked to /usr/local/bin/clawd"
|
||||||
|
|
||||||
|
# Install RK3588S LVGL demo
|
||||||
|
DEVICE_MODEL="$(tr -d '\0' </proc/device-tree/model 2>/dev/null || true)"
|
||||||
|
if echo "$DEVICE_MODEL" | grep -qi 'RK3588S'; then
|
||||||
|
DEMO_SRC="$INSTALL_DIR/lib/resource/3588s/demo"
|
||||||
|
DEMO_DST="/usr/bin/demo"
|
||||||
|
if [ -f "$DEMO_SRC" ]; then
|
||||||
|
info "RK3588S detected, installing LVGL demo to $DEMO_DST"
|
||||||
|
if [ -f "$DEMO_DST" ] && [ ! -f "${DEMO_DST}.clawd-bak" ]; then
|
||||||
|
cp "$DEMO_DST" "${DEMO_DST}.clawd-bak"
|
||||||
|
info "Backup created: ${DEMO_DST}.clawd-bak"
|
||||||
|
fi
|
||||||
|
install -m 0755 "$DEMO_SRC" "$DEMO_DST"
|
||||||
|
else
|
||||||
|
warn "RK3588S demo binary not found: $DEMO_SRC"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Write default config files
|
# Write default config files
|
||||||
mkdir -p "$CONFIG_DIR"
|
mkdir -p "$CONFIG_DIR"
|
||||||
@@ -234,9 +250,7 @@ MemoryMax=256M
|
|||||||
CPUQuota=50%
|
CPUQuota=50%
|
||||||
TasksMax=64
|
TasksMax=64
|
||||||
|
|
||||||
# Sandbox (allow writes to config, tmp, and network files)
|
# Sandbox disabled: clawd needs to write system/config files on some devices
|
||||||
ProtectSystem=full
|
|
||||||
ReadWritePaths=$CONFIG_DIR /tmp /etc/hosts /etc/hostname
|
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const os = require('os');
|
const { execFileSync } = require('child_process');
|
||||||
const log = require('../logger');
|
const log = require('../logger');
|
||||||
|
|
||||||
// ── Constants (from reference script) ────────────────────────────────────────
|
// ── Constants (from reference script) ────────────────────────────────────────
|
||||||
@@ -45,11 +45,15 @@ function _buildClientVersion(version) {
|
|||||||
// ── State-dir helpers (mirrors reference script) ─────────────────────────────
|
// ── State-dir helpers (mirrors reference script) ─────────────────────────────
|
||||||
|
|
||||||
function _resolveStateDir() {
|
function _resolveStateDir() {
|
||||||
return (
|
return path.join('/home/sts', '.openclaw');
|
||||||
(process.env.OPENCLAW_STATE_DIR || '').trim() ||
|
}
|
||||||
(process.env.CLAWDBOT_STATE_DIR || '').trim() ||
|
|
||||||
path.join(os.homedir(), '.openclaw')
|
function _ensureStsOwnership(filePath) {
|
||||||
);
|
try {
|
||||||
|
execFileSync('chown', ['sts:sts', filePath], { stdio: 'ignore' });
|
||||||
|
} catch (err) {
|
||||||
|
log.warn('weixin', `chown sts:sts failed for ${filePath}: ${err.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _resolveWeixinStateDir() { return path.join(_resolveStateDir(), 'openclaw-weixin'); }
|
function _resolveWeixinStateDir() { return path.join(_resolveStateDir(), 'openclaw-weixin'); }
|
||||||
@@ -81,7 +85,9 @@ function _registerAccountId(accountId) {
|
|||||||
fs.mkdirSync(_resolveWeixinStateDir(), { recursive: true });
|
fs.mkdirSync(_resolveWeixinStateDir(), { recursive: true });
|
||||||
const existing = _listIndexedAccountIds();
|
const existing = _listIndexedAccountIds();
|
||||||
if (existing.includes(accountId)) return;
|
if (existing.includes(accountId)) return;
|
||||||
fs.writeFileSync(_resolveAccountIndexPath(), JSON.stringify([...existing, accountId], null, 2), 'utf8');
|
const indexPath = _resolveAccountIndexPath();
|
||||||
|
fs.writeFileSync(indexPath, JSON.stringify([...existing, accountId], null, 2), 'utf8');
|
||||||
|
_ensureStsOwnership(indexPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _loadAccount(accountId) {
|
function _loadAccount(accountId) {
|
||||||
@@ -113,6 +119,7 @@ function _saveAccount(accountId, update) {
|
|||||||
};
|
};
|
||||||
const filePath = _resolveAccountPath(accountId);
|
const filePath = _resolveAccountPath(accountId);
|
||||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
|
||||||
|
_ensureStsOwnership(filePath);
|
||||||
try { fs.chmodSync(filePath, 0o600); } catch (_) {}
|
try { fs.chmodSync(filePath, 0o600); } catch (_) {}
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
@@ -126,7 +133,9 @@ function _clearStaleAccountsForUserId(currentAccountId, userId) {
|
|||||||
log.info('weixin', `removing stale account with same userId: ${id}`);
|
log.info('weixin', `removing stale account with same userId: ${id}`);
|
||||||
try { fs.unlinkSync(_resolveAccountPath(id)); } catch (_) {}
|
try { fs.unlinkSync(_resolveAccountPath(id)); } catch (_) {}
|
||||||
const existing = _listIndexedAccountIds();
|
const existing = _listIndexedAccountIds();
|
||||||
fs.writeFileSync(_resolveAccountIndexPath(), JSON.stringify(existing.filter(x => x !== id), null, 2), 'utf8');
|
const indexPath = _resolveAccountIndexPath();
|
||||||
|
fs.writeFileSync(indexPath, JSON.stringify(existing.filter(x => x !== id), null, 2), 'utf8');
|
||||||
|
_ensureStsOwnership(indexPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1586
lib/client.js
1586
lib/client.js
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ function loadImpl() {
|
|||||||
if (forced) {
|
if (forced) {
|
||||||
name = forced;
|
name = forced;
|
||||||
} else if (isRK3588()) {
|
} else if (isRK3588()) {
|
||||||
name = 'noop';
|
name = 'rk3588-lvgl';
|
||||||
} else if (isRK3566()) {
|
} else if (isRK3566()) {
|
||||||
name = 'rk3566';
|
name = 'rk3566';
|
||||||
} else {
|
} else {
|
||||||
@@ -23,6 +23,10 @@ function loadImpl() {
|
|||||||
log.info('led', `LED/VFD backend → rk3566-openvfd (${model || 'unknown model'})`);
|
log.info('led', `LED/VFD backend → rk3566-openvfd (${model || 'unknown model'})`);
|
||||||
return require('./led/rk3566-openvfd');
|
return require('./led/rk3566-openvfd');
|
||||||
}
|
}
|
||||||
|
if (name === 'rk3588-lvgl' || name === '3588' || name === 'rk3588') {
|
||||||
|
log.info('led', `LED/VFD backend → rk3588-lvgl (${model || 'unknown model'})`);
|
||||||
|
return require('./led/rk3588-lvgl');
|
||||||
|
}
|
||||||
if (name === 'noop' || name === 'none' || name === 'off') {
|
if (name === 'noop' || name === 'none' || name === 'off') {
|
||||||
log.info('led', `LED/VFD backend → noop (${model || 'unknown model'})`);
|
log.info('led', `LED/VFD backend → noop (${model || 'unknown model'})`);
|
||||||
return require('./led/noop');
|
return require('./led/noop');
|
||||||
|
|||||||
81
lib/led/rk3588-lvgl.js
Normal file
81
lib/led/rk3588-lvgl.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const log = require('../logger');
|
||||||
|
|
||||||
|
const LVGL_CMD_FIFO = process.env.CLAWD_LVGL_CMD_FIFO || '/tmp/lvgl_cmd';
|
||||||
|
|
||||||
|
function writeLvglCommand(command) {
|
||||||
|
try {
|
||||||
|
const fd = fs.openSync(LVGL_CMD_FIFO, fs.constants.O_WRONLY | fs.constants.O_NONBLOCK);
|
||||||
|
fs.writeSync(fd, `${command}\n`);
|
||||||
|
fs.closeSync(fd);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('display', `lvgl cmd failed (${command}): ${e.message}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BasicLed {
|
||||||
|
constructor(name) {
|
||||||
|
this.name = name;
|
||||||
|
this._current = null;
|
||||||
|
}
|
||||||
|
on() { this._current = 'on'; log.debug('led', `[rk3588-lvgl] ${this.name} on`); }
|
||||||
|
off() { this._current = 'off'; log.debug('led', `[rk3588-lvgl] ${this.name} off`); }
|
||||||
|
blink() { this._current = 'blink'; log.debug('led', `[rk3588-lvgl] ${this.name} blink`); }
|
||||||
|
destroy() { this._current = 'off'; log.debug('led', `[rk3588-lvgl] ${this.name} destroy`); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatusLed {
|
||||||
|
setSetup() { log.debug('led', '[rk3588-lvgl] status setup'); }
|
||||||
|
setApps() { log.debug('led', '[rk3588-lvgl] status apps'); }
|
||||||
|
off() { log.debug('led', '[rk3588-lvgl] status off'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Display {
|
||||||
|
showAP() {
|
||||||
|
if (writeLvglCommand('show_ap')) {
|
||||||
|
log.info('display', '显示屏 → AP(闪烁)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showConn() {
|
||||||
|
if (writeLvglCommand('show_conn')) {
|
||||||
|
log.info('display', '显示屏 → Conn(闪烁)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showErr0() {
|
||||||
|
if (writeLvglCommand('show_err0')) {
|
||||||
|
log.info('display', '显示屏 → Err0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showTime() {
|
||||||
|
if (writeLvglCommand('show_time')) {
|
||||||
|
log.info('display', '显示屏 → 时间');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showPin(pin) {
|
||||||
|
const s = String(pin || '').padStart(4, '0').slice(-4);
|
||||||
|
if (writeLvglCommand(`show_pin:${s}`)) {
|
||||||
|
log.info('display', `显示屏 → PIN: ${s}(闪烁)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LanLed {
|
||||||
|
start() { log.debug('led', '[rk3588-lvgl] LAN start ignored'); }
|
||||||
|
stop() { log.debug('led', '[rk3588-lvgl] LAN stop ignored'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
const led = new BasicLed('wifi');
|
||||||
|
led.bt = new BasicLed('bt');
|
||||||
|
led.status = new StatusLed();
|
||||||
|
led.display = new Display();
|
||||||
|
led.lan = new LanLed();
|
||||||
|
|
||||||
|
module.exports = led;
|
||||||
298
lib/network.js
298
lib/network.js
@@ -10,6 +10,7 @@ const AP_IP = '10.42.0.1';
|
|||||||
const AP_PASSWORD = '12345678';
|
const AP_PASSWORD = '12345678';
|
||||||
const AP_IFACE = process.env.CLAWD_WIFI_IFACE || '';
|
const AP_IFACE = process.env.CLAWD_WIFI_IFACE || '';
|
||||||
const CON_NAME = 'clawd-hotspot';
|
const CON_NAME = 'clawd-hotspot';
|
||||||
|
const AP_RETRY_TOKEN_FILE = '/run/clawd-ap-retry.token';
|
||||||
|
|
||||||
/** 产品 RJ45 在 sysfs 中的默认名;等价于检测 `cat /sys/class/net/end0/carrier` */
|
/** 产品 RJ45 在 sysfs 中的默认名;等价于检测 `cat /sys/class/net/end0/carrier` */
|
||||||
const DEFAULT_ETH_IFACE = 'end0';
|
const DEFAULT_ETH_IFACE = 'end0';
|
||||||
@@ -92,28 +93,15 @@ function hasLanCableCarrier() {
|
|||||||
return hasWiredCarrier();
|
return hasWiredCarrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _tryPingInternet() {
|
function _tryPingDefaultInternet() {
|
||||||
try {
|
try {
|
||||||
run('ping -c 1 -W 3 8.8.8.8');
|
run('ping -c 1 -W 3 8.8.8.8');
|
||||||
return true;
|
return true;
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
// 开热点时默认路由可能走 wlan,无 -I 的 ping 会误判;指定有线口再试
|
|
||||||
const wired = getWiredIfaceWithCarrier();
|
|
||||||
if (wired) {
|
|
||||||
try {
|
|
||||||
run(`ping -c 1 -W 3 -I ${wired} 8.8.8.8`);
|
|
||||||
return true;
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function _tryPingWiredInternet() {
|
||||||
* 仅经有线口 ping 公网(不依赖默认路由)。
|
|
||||||
* AP 开启时 hasInternet() 易误判;维持 WS / 网络监视时用此兜底。
|
|
||||||
*/
|
|
||||||
function hasWiredInternetProbe() {
|
|
||||||
const wired = getWiredIfaceWithCarrier();
|
const wired = getWiredIfaceWithCarrier();
|
||||||
if (!wired) return false;
|
if (!wired) return false;
|
||||||
try {
|
try {
|
||||||
@@ -124,18 +112,31 @@ function hasWiredInternetProbe() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测是否有互联网连接(nmcli 连通性 + ping 兜底)
|
* 仅经有线口 ping 公网(不依赖默认路由)。
|
||||||
|
*/
|
||||||
|
function hasWiredInternetProbe() {
|
||||||
|
return _tryPingWiredInternet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测是否有真实互联网连接。
|
||||||
|
* 注意:NetworkManager 的 limited/local 可能只是 AP 本地网络或 captive 状态,不能当公网可用。
|
||||||
*/
|
*/
|
||||||
function hasInternet() {
|
function hasInternet() {
|
||||||
|
const wifiSta = isWifiStaConnected();
|
||||||
|
const wired = getWiredIfaceWithCarrier();
|
||||||
|
|
||||||
// 物理层快检:无 WiFi STA 且无任何有线 carrier → 立即 false(nmcli 有缓存,不可信)
|
// 物理层快检:无 WiFi STA 且无任何有线 carrier → 立即 false(nmcli 有缓存,不可信)
|
||||||
if (!isWifiStaConnected() && !hasWiredCarrier()) return false;
|
if (!wifiSta && !wired) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const out = run('nmcli networking connectivity check').trim();
|
const out = run('nmcli networking connectivity check').trim();
|
||||||
if (out === 'full' || out === 'limited') return true;
|
if (out === 'full') return true;
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
return _tryPingInternet();
|
if (wifiSta) return _tryPingDefaultInternet();
|
||||||
|
if (wired) return _tryPingWiredInternet();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,6 +266,7 @@ function nmcliAsync(args, timeoutMs = 60000) {
|
|||||||
* @returns {Promise<{ success: boolean, error?: string }>}
|
* @returns {Promise<{ success: boolean, error?: string }>}
|
||||||
*/
|
*/
|
||||||
async function connectWifi(ssid, password) {
|
async function connectWifi(ssid, password) {
|
||||||
|
cancelHotspotRadioRetry(`准备连接 WiFi: ${ssid}`);
|
||||||
const iface = getWifiIface();
|
const iface = getWifiIface();
|
||||||
log.info('network', `尝试连接 WiFi: ${ssid}(ifname=${iface})`);
|
log.info('network', `尝试连接 WiFi: ${ssid}(ifname=${iface})`);
|
||||||
try {
|
try {
|
||||||
@@ -272,14 +274,36 @@ async function connectWifi(ssid, password) {
|
|||||||
await nmcliAsync(['connection', 'delete', ssid], 15000);
|
await nmcliAsync(['connection', 'delete', ssid], 15000);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
try {
|
await _resetWifiRadioForSTA(iface);
|
||||||
await nmcliAsync(['device', 'set', iface, 'managed', 'yes'], 8000);
|
|
||||||
} catch (_) {}
|
|
||||||
|
|
||||||
const args = ['device', 'wifi', 'connect', ssid];
|
if (password) {
|
||||||
if (password) args.push('password', password);
|
// 显式创建 STA profile,并固定为 WPA2-PSK only。
|
||||||
args.push('ifname', iface);
|
// RK3588/Broadcom DHD 对 NetworkManager 默认生成的 SAE/FT/WPA-PSK-SHA256 混合参数不稳定,
|
||||||
await nmcliAsync(args, 120000);
|
// 可能表现为一直 associating -> disconnected,最后误报“需要密钥”。
|
||||||
|
await nmcliAsync([
|
||||||
|
'connection', 'add',
|
||||||
|
'type', 'wifi',
|
||||||
|
'ifname', iface,
|
||||||
|
'con-name', ssid,
|
||||||
|
'ssid', ssid,
|
||||||
|
], 15000);
|
||||||
|
|
||||||
|
await nmcliAsync([
|
||||||
|
'connection', 'modify', ssid,
|
||||||
|
// 连接成功前先禁止自动连接,避免失败恢复 AP 时 NM 又自动抢占 wlan0。
|
||||||
|
'connection.autoconnect', 'no',
|
||||||
|
'802-11-wireless-security.key-mgmt', 'wpa-psk',
|
||||||
|
'802-11-wireless-security.proto', 'rsn',
|
||||||
|
'802-11-wireless-security.pairwise', 'ccmp',
|
||||||
|
'802-11-wireless-security.group', 'ccmp',
|
||||||
|
'802-11-wireless-security.pmf', 'disable',
|
||||||
|
'802-11-wireless-security.psk', password,
|
||||||
|
], 15000);
|
||||||
|
|
||||||
|
await nmcliAsync(['connection', 'up', 'id', ssid, 'ifname', iface], 120000);
|
||||||
|
} else {
|
||||||
|
await nmcliAsync(['device', 'wifi', 'connect', ssid, 'ifname', iface], 120000);
|
||||||
|
}
|
||||||
await _ensureActiveWifiAutoconnect();
|
await _ensureActiveWifiAutoconnect();
|
||||||
|
|
||||||
const deadline = Date.now() + CONNECT_WIFI_STA_WAIT_MS;
|
const deadline = Date.now() + CONNECT_WIFI_STA_WAIT_MS;
|
||||||
@@ -299,11 +323,201 @@ async function connectWifi(ssid, password) {
|
|||||||
}
|
}
|
||||||
return { success: false, error: '超时:网卡未进入已连接状态' };
|
return { success: false, error: '超时:网卡未进入已连接状态' };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
try { await nmcliAsync(['connection', 'modify', ssid, 'connection.autoconnect', 'no'], 8000); } catch (_) {}
|
||||||
log.error('network', `WiFi 连接失败: ${e.message}`);
|
log.error('network', `WiFi 连接失败: ${e.message}`);
|
||||||
return { success: false, error: e.message };
|
return { success: false, error: e.message };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _newHotspotRetryToken() {
|
||||||
|
const token = `${process.pid}:${Date.now()}:${Math.random().toString(36).slice(2)}`;
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(AP_RETRY_TOKEN_FILE, token, { mode: 0o600 });
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('network', `写入 AP retry token 失败: ${e.message}`);
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelHotspotRadioRetry(reason = 'cancel') {
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(AP_RETRY_TOKEN_FILE);
|
||||||
|
log.info('network', `已取消后台 AP retry: ${reason}`);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _resetWifiRadioForSTA(iface, reason = '准备连接 STA 前重置 WiFi radio') {
|
||||||
|
log.warn('network', `${reason}: ${iface}`);
|
||||||
|
|
||||||
|
try { await nmcliAsync(['connection', 'down', CON_NAME], 8000); } catch (_) {}
|
||||||
|
try { await nmcliAsync(['connection', 'delete', CON_NAME], 8000); } catch (_) {}
|
||||||
|
try { await nmcliAsync(['device', 'disconnect', iface], 8000); } catch (_) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await nmcliAsync(['radio', 'wifi', 'off'], 10000);
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('network', `关闭 WiFi radio 失败: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _delay(2500);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await nmcliAsync(['radio', 'wifi', 'on'], 10000);
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('network', `开启 WiFi radio 失败: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _delay(5000);
|
||||||
|
try { await nmcliAsync(['device', 'set', iface, 'managed', 'yes'], 8000); } catch (_) {}
|
||||||
|
try { await nmcliAsync(['device', 'wifi', 'rescan', 'ifname', iface], 15000); } catch (_) {}
|
||||||
|
await _delay(1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _resetWifiRadioForAP(iface, reason = '准备 AP 前重置 WiFi radio') {
|
||||||
|
log.warn('network', `${reason}: ${iface}`);
|
||||||
|
|
||||||
|
try { nmcliSync(['connection', 'down', CON_NAME], 8000); } catch (_) {}
|
||||||
|
try { nmcliSync(['connection', 'delete', CON_NAME], 8000); } catch (_) {}
|
||||||
|
try { nmcliSync(['device', 'disconnect', iface], 8000); } catch (_) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
nmcliSync(['radio', 'wifi', 'off'], 10000);
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('network', `关闭 WiFi radio 失败: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(2500);
|
||||||
|
|
||||||
|
try {
|
||||||
|
nmcliSync(['radio', 'wifi', 'on'], 10000);
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('network', `开启 WiFi radio 失败: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(5000);
|
||||||
|
try { nmcliSync(['device', 'set', iface, 'managed', 'yes'], 8000); } catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _spawnHotspotRadioRetry(ssid, iface) {
|
||||||
|
const token = _newHotspotRetryToken();
|
||||||
|
const script = `
|
||||||
|
set -u
|
||||||
|
log() { logger -t clawd-ap-retry "$*"; }
|
||||||
|
check_token() {
|
||||||
|
if [ ! -f "$TOKEN_FILE" ] || [ "$(cat "$TOKEN_FILE" 2>/dev/null || true)" != "$TOKEN" ]; then
|
||||||
|
log "AP retry canceled"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
log "AP retry started: ssid=$SSID iface=$IFACE"
|
||||||
|
check_token
|
||||||
|
nmcli connection down "$CON_NAME" >/dev/null 2>&1 || true
|
||||||
|
nmcli connection delete "$CON_NAME" >/dev/null 2>&1 || true
|
||||||
|
nmcli device disconnect "$IFACE" >/dev/null 2>&1 || true
|
||||||
|
check_token
|
||||||
|
nmcli radio wifi off >/dev/null 2>&1 || true
|
||||||
|
sleep 2.5
|
||||||
|
# If canceled while radio is off, always turn it back on before exiting.
|
||||||
|
nmcli radio wifi on >/dev/null 2>&1 || true
|
||||||
|
sleep 5
|
||||||
|
check_token
|
||||||
|
nmcli device set "$IFACE" managed yes >/dev/null 2>&1 || true
|
||||||
|
check_token
|
||||||
|
if ! nmcli connection add type wifi ifname "$IFACE" con-name "$CON_NAME" ssid "$SSID" >/dev/null 2>&1; then
|
||||||
|
log "AP retry failed: connection add failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
args=(
|
||||||
|
connection modify "$CON_NAME"
|
||||||
|
connection.autoconnect no
|
||||||
|
802-11-wireless.mode ap
|
||||||
|
802-11-wireless.band bg
|
||||||
|
802-11-wireless.channel 1
|
||||||
|
802-11-wireless-security.key-mgmt wpa-psk
|
||||||
|
802-11-wireless-security.proto rsn
|
||||||
|
802-11-wireless-security.pairwise ccmp
|
||||||
|
802-11-wireless-security.group ccmp
|
||||||
|
802-11-wireless-security.pmf disable
|
||||||
|
ipv4.method shared
|
||||||
|
ipv4.addresses "$AP_IP/24"
|
||||||
|
ipv6.method ignore
|
||||||
|
)
|
||||||
|
if [ -n "\${AP_PASSWORD:-}" ]; then
|
||||||
|
args+=(802-11-wireless-security.psk "$AP_PASSWORD")
|
||||||
|
fi
|
||||||
|
check_token
|
||||||
|
if ! nmcli "\${args[@]}" >/dev/null 2>&1; then
|
||||||
|
log "AP retry failed: connection modify failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
check_token
|
||||||
|
if nmcli connection up "$CON_NAME" >/dev/null 2>&1; then
|
||||||
|
if [ ! -f "$TOKEN_FILE" ] || [ "$(cat "$TOKEN_FILE" 2>/dev/null || true)" != "$TOKEN" ]; then
|
||||||
|
log "AP retry canceled after connection up; tearing hotspot down"
|
||||||
|
nmcli connection down "$CON_NAME" >/dev/null 2>&1 || true
|
||||||
|
nmcli connection delete "$CON_NAME" >/dev/null 2>&1 || true
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
log "AP retry success: $SSID"
|
||||||
|
rm -f "$TOKEN_FILE"
|
||||||
|
else
|
||||||
|
log "AP retry failed: connection up failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
`;
|
||||||
|
|
||||||
|
const child = spawn('/bin/bash', ['-lc', script], {
|
||||||
|
detached: true,
|
||||||
|
stdio: 'ignore',
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
SSID: ssid,
|
||||||
|
IFACE: iface,
|
||||||
|
CON_NAME,
|
||||||
|
AP_IP,
|
||||||
|
AP_PASSWORD: AP_PASSWORD || '',
|
||||||
|
TOKEN_FILE: AP_RETRY_TOKEN_FILE,
|
||||||
|
TOKEN: token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
child.unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _createHotspotProfile(ssid, iface) {
|
||||||
|
nmcliSync([
|
||||||
|
'connection', 'add',
|
||||||
|
'type', 'wifi',
|
||||||
|
'ifname', iface,
|
||||||
|
'con-name', CON_NAME,
|
||||||
|
'ssid', ssid,
|
||||||
|
], 15000);
|
||||||
|
|
||||||
|
const modifyArgs = [
|
||||||
|
'connection', 'modify', CON_NAME,
|
||||||
|
'connection.autoconnect', 'no',
|
||||||
|
'802-11-wireless.mode', 'ap',
|
||||||
|
'802-11-wireless.band', 'bg',
|
||||||
|
'802-11-wireless.channel', '1',
|
||||||
|
'802-11-wireless-security.key-mgmt', 'wpa-psk',
|
||||||
|
'802-11-wireless-security.proto', 'rsn',
|
||||||
|
'802-11-wireless-security.pairwise', 'ccmp',
|
||||||
|
'802-11-wireless-security.group', 'ccmp',
|
||||||
|
'802-11-wireless-security.pmf', 'disable',
|
||||||
|
'ipv4.method', 'shared',
|
||||||
|
'ipv4.addresses', `${AP_IP}/24`,
|
||||||
|
'ipv6.method', 'ignore',
|
||||||
|
];
|
||||||
|
if (AP_PASSWORD) {
|
||||||
|
modifyArgs.push('802-11-wireless-security.psk', AP_PASSWORD);
|
||||||
|
}
|
||||||
|
nmcliSync(modifyArgs, 15000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _activateHotspot(ssid, iface, timeoutMs = 8000) {
|
||||||
|
_createHotspotProfile(ssid, iface);
|
||||||
|
nmcliSync(['connection', 'up', CON_NAME], timeoutMs);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动 WiFi AP 热点
|
* 启动 WiFi AP 热点
|
||||||
*/
|
*/
|
||||||
@@ -313,29 +527,24 @@ function startAP(clawId) {
|
|||||||
|
|
||||||
log.info('network', `启动 AP 热点: ${ssid} (${iface})`);
|
log.info('network', `启动 AP 热点: ${ssid} (${iface})`);
|
||||||
|
|
||||||
// 关闭已有热点
|
// 关闭已有热点,并在重新拉起 AP 前真正 power-cycle WiFi 芯片。
|
||||||
|
// RK3588/Broadcom DHD 在 LAN 断开后切 AP 时,单纯 ip link down/up 不一定清掉固件残留状态。
|
||||||
stopAP();
|
stopAP();
|
||||||
|
_resetWifiRadioForAP(iface, '准备 AP 前重置 WiFi radio');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// nmcli 创建热点(开放网络)
|
// 显式创建并激活热点,固定为 WPA2-PSK only。
|
||||||
const cmd = [
|
// 避免 NetworkManager 自动生成 WPA-PSK-SHA256/SAE 混合配置,部分 3588 Broadcom DHD 驱动重启 AP 时会拒绝该 RSN 参数。
|
||||||
'nmcli device wifi hotspot',
|
|
||||||
`ifname ${iface}`,
|
|
||||||
`con-name ${CON_NAME}`,
|
|
||||||
`ssid "${ssid}"`,
|
|
||||||
'band bg',
|
|
||||||
];
|
|
||||||
// 如果需要密码
|
|
||||||
if (AP_PASSWORD) {
|
|
||||||
cmd.push(`password "${AP_PASSWORD}"`);
|
|
||||||
}
|
|
||||||
run(cmd.join(' '));
|
|
||||||
try {
|
try {
|
||||||
nmcliSync(['connection', 'modify', CON_NAME, 'connection.autoconnect', 'no'], 8000);
|
_activateHotspot(ssid, iface, 8000);
|
||||||
} catch (_) {}
|
} catch (firstError) {
|
||||||
|
log.warn('network', `AP 启动未在短超时内完成,后台再次重置 WiFi radio 后重试;避免阻塞 watchdog: ${firstError.message}`);
|
||||||
|
_spawnHotspotRadioRetry(ssid, iface);
|
||||||
|
return { ssid, ip: AP_IP, iface, pending: true };
|
||||||
|
}
|
||||||
|
|
||||||
// 等待 AP 启动
|
// 等待 AP 启动
|
||||||
sleep(2000);
|
sleep(1000);
|
||||||
log.info('network', `AP 已启动: ${ssid}, 网关 ${AP_IP}`);
|
log.info('network', `AP 已启动: ${ssid}, 网关 ${AP_IP}`);
|
||||||
return { ssid, ip: AP_IP, iface };
|
return { ssid, ip: AP_IP, iface };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -348,6 +557,7 @@ function startAP(clawId) {
|
|||||||
* 关闭热点,恢复普通 WiFi 模式
|
* 关闭热点,恢复普通 WiFi 模式
|
||||||
*/
|
*/
|
||||||
function stopAP() {
|
function stopAP() {
|
||||||
|
cancelHotspotRadioRetry('停止 AP');
|
||||||
try {
|
try {
|
||||||
run(`nmcli connection down ${CON_NAME}`);
|
run(`nmcli connection down ${CON_NAME}`);
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@@ -454,6 +664,7 @@ async function _ensureActiveWifiAutoconnect() {
|
|||||||
* clawd 只做调度;真正的认证、DHCP、重连细节仍交给 NM。
|
* clawd 只做调度;真正的认证、DHCP、重连细节仍交给 NM。
|
||||||
*/
|
*/
|
||||||
async function connectSavedWifiConnections() {
|
async function connectSavedWifiConnections() {
|
||||||
|
cancelHotspotRadioRetry('准备连接已保存 WiFi');
|
||||||
const iface = getWifiIface();
|
const iface = getWifiIface();
|
||||||
const profiles = listSavedWifiConnections();
|
const profiles = listSavedWifiConnections();
|
||||||
if (profiles.length === 0) {
|
if (profiles.length === 0) {
|
||||||
@@ -574,6 +785,7 @@ module.exports = {
|
|||||||
connectWifi,
|
connectWifi,
|
||||||
startAP,
|
startAP,
|
||||||
stopAP,
|
stopAP,
|
||||||
|
cancelHotspotRadioRetry,
|
||||||
AP_IP,
|
AP_IP,
|
||||||
getLocalIps,
|
getLocalIps,
|
||||||
getLocalNetworks,
|
getLocalNetworks,
|
||||||
|
|||||||
@@ -1,360 +1,362 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const log = require('./logger');
|
const log = require('./logger');
|
||||||
const { hasInternet, hasSavedWifiConnection, connectSavedWifiConnections, isWifiStaConnected, scanWifi, startAP, stopAP, connectWifi, getWifiIface, AP_IP } = require('./network');
|
const { hasInternet, hasWiredInternetProbe, hasSavedWifiConnection, connectSavedWifiConnections, isWifiStaConnected, scanWifi, startAP, stopAP, connectWifi, getWifiIface, AP_IP } = require('./network');
|
||||||
const { DnsHijack } = require('./dns-hijack');
|
const { DnsHijack } = require('./dns-hijack');
|
||||||
const { CaptiveServer } = require('./captive-server');
|
const { CaptiveServer } = require('./captive-server');
|
||||||
const led = require('./led');
|
const led = require('./led');
|
||||||
|
|
||||||
const MONITOR_INTERVAL_MS = 15_000;
|
const MONITOR_INTERVAL_MS = 15_000;
|
||||||
const WIFI_RECONNECT_MAX_ROUNDS = 3;
|
const WIFI_RECONNECT_MAX_ROUNDS = 3;
|
||||||
const WIFI_RECONNECT_ROUND_DELAY_MS = 5_000;
|
const WIFI_RECONNECT_ROUND_DELAY_MS = 5_000;
|
||||||
const AP_SAVED_WIFI_RETRY_INTERVAL_MS = 180_000;
|
const AP_SAVED_WIFI_RETRY_INTERVAL_MS = 180_000;
|
||||||
const AP_MIN_UP_BEFORE_RETRY_MS = 60_000;
|
const AP_MIN_UP_BEFORE_RETRY_MS = 60_000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AP 常驻配网管理器。
|
* AP 常驻配网管理器。
|
||||||
*
|
*
|
||||||
* 规则:
|
* 规则:
|
||||||
* - 启动时:WiFi STA 优先;有已保存 WiFi 时主动让 NM 重连,最多 3 轮
|
* - 启动时:WiFi STA 优先;有已保存 WiFi 时主动让 NM 重连,最多 3 轮
|
||||||
* - 有线网络可用时:通知网络就绪,但不自动开启 AP
|
* - 有线网络可用时:通知网络就绪,但不自动开启 AP
|
||||||
* - 自动开 AP 的唯一兜底:无有线/无 WiFi,且无 saved WiFi 或 saved WiFi 3 轮失败
|
* - 自动开 AP 的唯一兜底:无有线/无 WiFi,且无 saved WiFi 或 saved WiFi 3 轮失败
|
||||||
* - 用户提交 WiFi 凭证 → 关 AP → 尝试连接 → 失败则按网络状态决定是否重新开 AP
|
* - 用户提交 WiFi 凭证 → 关 AP → 尝试连接 → 失败则按网络状态决定是否重新开 AP
|
||||||
* - AP 状态下:若仍无有线网络,低频释放 wlan0 并尝试 saved WiFi
|
* - AP 状态下:若仍无有线网络,低频释放 wlan0 并尝试 saved WiFi
|
||||||
*/
|
*/
|
||||||
class ProvisionManager extends EventEmitter {
|
class ProvisionManager extends EventEmitter {
|
||||||
constructor(clawId) {
|
constructor(clawId) {
|
||||||
super();
|
super();
|
||||||
this._clawId = clawId || 'Setup';
|
this._clawId = clawId || 'Setup';
|
||||||
this._state = 'idle'; // 'idle' | 'ap' | 'connecting' | 'sta' | 'wired'
|
this._state = 'idle'; // 'idle' | 'ap' | 'connecting' | 'sta' | 'wired'
|
||||||
this._dns = null;
|
this._dns = null;
|
||||||
this._server = null;
|
this._server = null;
|
||||||
this._monitorTimer = null;
|
this._monitorTimer = null;
|
||||||
this._monitorBusy = false;
|
this._monitorBusy = false;
|
||||||
this._apStartedAt = 0;
|
this._apStartedAt = 0;
|
||||||
this._lastApSavedWifiRetryAt = 0;
|
this._lastApSavedWifiRetryAt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 是否正处于 AP 模式(WiFi 热点广播中) */
|
/** 是否正处于 AP 模式(WiFi 热点广播中) */
|
||||||
isApMode() { return this._state === 'ap'; }
|
isApMode() { return this._state === 'ap'; }
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
led.off(); // WiFi 灯初始状态:熄灭
|
led.off(); // WiFi 灯初始状态:熄灭
|
||||||
|
|
||||||
// WiFi STA 已连接 → 直接进入 STA 模式
|
// WiFi STA 已连接 → 直接进入 STA 模式
|
||||||
if (isWifiStaConnected()) {
|
if (isWifiStaConnected()) {
|
||||||
this._state = 'sta';
|
this._state = 'sta';
|
||||||
log.info('provision', 'WiFi STA 已连接,AP 不启动');
|
log.info('provision', 'WiFi STA 已连接,AP 不启动');
|
||||||
this._emitNetworkReady();
|
this._emitNetworkReady();
|
||||||
this._startMonitor();
|
this._startMonitor();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 网络已就绪时先启动 WS;hasInternet() 可能来自 WiFi,也可能来自有线,不能直接当作 wired。
|
// 网络已就绪时先启动 WS;hasInternet() 可能来自 WiFi,也可能来自有线,不能直接当作 wired。
|
||||||
if (hasInternet()) {
|
if (hasInternet()) {
|
||||||
if (isWifiStaConnected()) {
|
if (isWifiStaConnected()) {
|
||||||
this._state = 'sta';
|
this._state = 'sta';
|
||||||
log.info('provision', 'WiFi STA 已连接,AP 不启动');
|
log.info('provision', 'WiFi STA 已连接,AP 不启动');
|
||||||
this._emitNetworkReady();
|
this._emitNetworkReady();
|
||||||
} else {
|
} else {
|
||||||
this._state = 'wired';
|
this._state = 'wired';
|
||||||
log.info('provision', '有线网络就绪,启动 WS;不自动开启 AP');
|
log.info('provision', '有线网络就绪,启动 WS;不自动开启 AP');
|
||||||
led.off();
|
led.off();
|
||||||
this._emitNetworkReady();
|
this._emitNetworkReady();
|
||||||
}
|
}
|
||||||
this._startMonitor();
|
this._startMonitor();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 无有线可用时,有 saved WiFi 才主动让 NetworkManager 重连;不要只被动等待 NM autoconnect。
|
// 无有线可用时,有 saved WiFi 才主动让 NetworkManager 重连;不要只被动等待 NM autoconnect。
|
||||||
if (hasSavedWifiConnection()) {
|
if (hasSavedWifiConnection()) {
|
||||||
log.info('provision', `发现已保存的 WiFi 配置,主动重连(最多 ${WIFI_RECONNECT_MAX_ROUNDS} 轮)...`);
|
log.info('provision', `发现已保存的 WiFi 配置,主动重连(最多 ${WIFI_RECONNECT_MAX_ROUNDS} 轮)...`);
|
||||||
this._state = 'connecting';
|
this._state = 'connecting';
|
||||||
led.blink();
|
led.blink();
|
||||||
const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS);
|
const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS);
|
||||||
if (connected) {
|
if (connected) {
|
||||||
this._state = 'sta';
|
this._state = 'sta';
|
||||||
log.info('provision', '已保存 WiFi 重连成功,AP 不启动');
|
log.info('provision', '已保存 WiFi 重连成功,AP 不启动');
|
||||||
this._emitNetworkReady();
|
this._emitNetworkReady();
|
||||||
this._startMonitor();
|
this._startMonitor();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.warn('provision', '已保存 WiFi 重连失败');
|
log.warn('provision', '已保存 WiFi 重连失败');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 无有线、无 WiFi;且无 saved WiFi 或 saved WiFi 3 轮失败 → 开 AP 兜底配网。
|
// 无有线、无 WiFi;且无 saved WiFi 或 saved WiFi 3 轮失败 → 开 AP 兜底配网。
|
||||||
this._enterAP();
|
this._enterAP();
|
||||||
this._startMonitor();
|
this._startMonitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
_emitNetworkReady() {
|
_emitNetworkReady() {
|
||||||
if (hasInternet()) {
|
if (hasInternet()) {
|
||||||
// WiFi 灯只在 STA 模式下亮(有线有网而 WiFi 在 AP 模式时不亮)
|
// WiFi 灯只在 STA 模式下亮(有线有网而 WiFi 在 AP 模式时不亮)
|
||||||
if (this._state === 'sta') led.on();
|
if (this._state === 'sta') led.on();
|
||||||
this.emit('network-ready');
|
this.emit('network-ready');
|
||||||
} else {
|
} else {
|
||||||
log.warn('provision', 'hasInternet() 返回 false,LED 保持熄灭');
|
log.warn('provision', 'hasInternet() 返回 false,LED 保持熄灭');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _trySavedWifiReconnectRounds(rounds = WIFI_RECONNECT_MAX_ROUNDS) {
|
async _trySavedWifiReconnectRounds(rounds = WIFI_RECONNECT_MAX_ROUNDS) {
|
||||||
for (let i = 1; i <= rounds; i++) {
|
for (let i = 1; i <= rounds; i++) {
|
||||||
if (isWifiStaConnected()) return true;
|
if (isWifiStaConnected()) return true;
|
||||||
log.info('provision', `尝试已保存 WiFi 重连:第 ${i}/${rounds} 轮`);
|
log.info('provision', `尝试已保存 WiFi 重连:第 ${i}/${rounds} 轮`);
|
||||||
const result = await connectSavedWifiConnections();
|
const result = await connectSavedWifiConnections();
|
||||||
if (result.success || isWifiStaConnected()) return true;
|
if (result.success || isWifiStaConnected()) return true;
|
||||||
if (i < rounds) {
|
if (i < rounds) {
|
||||||
await new Promise((r) => setTimeout(r, WIFI_RECONNECT_ROUND_DELAY_MS));
|
await new Promise((r) => setTimeout(r, WIFI_RECONNECT_ROUND_DELAY_MS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
this._stopMonitor();
|
this._stopMonitor();
|
||||||
this._stopAll();
|
this._stopAll();
|
||||||
this._state = 'idle';
|
this._state = 'idle';
|
||||||
led.destroy(); // WiFi 灯:停止时关灯、释放闪烁定时器
|
led.destroy(); // WiFi 灯:停止时关灯、释放闪烁定时器
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 进入 AP 模式 ─────────────────────────────────────────────────────────
|
// ── 进入 AP 模式 ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
_enterAP() {
|
_enterAP() {
|
||||||
if (this._state === 'ap') return;
|
if (this._state === 'ap') return;
|
||||||
|
|
||||||
led.off(); // AP 模式:WiFi 未连接,WiFi 灯熄灭
|
led.off(); // AP 模式:WiFi 未连接,WiFi 灯熄灭
|
||||||
if (!hasInternet()) led.display.showAP(); // 无网时立即显示 AP,有线时等 WS 连接后再定
|
if (!hasInternet()) led.display.showAP(); // 无网时立即显示 AP,有线时等 WS 连接后再定
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 若上次进程退出前留下 clawd-hotspot,必须先释放 wlan0;否则会在 AP 模式下扫描,列表可能只剩 2.4G/自身热点。
|
// 若上次进程退出前留下 clawd-hotspot,必须先释放 wlan0;否则会在 AP 模式下扫描,列表可能只剩 2.4G/自身热点。
|
||||||
stopAP();
|
stopAP();
|
||||||
|
|
||||||
// AP 模式下无法扫描 WiFi,必须在开 AP 之前扫描并缓存
|
// AP 模式下无法扫描 WiFi,必须在开 AP 之前扫描并缓存
|
||||||
log.info('provision', '扫描周边 WiFi...');
|
log.info('provision', '扫描周边 WiFi...');
|
||||||
this._cachedWifiList = scanWifi();
|
this._cachedWifiList = scanWifi();
|
||||||
log.info('provision', `扫描到 ${this._cachedWifiList.length} 个网络: ${this._cachedWifiList.map(w => `${w.ssid}${w.band ? `(${w.band})` : ''}`).join(', ')}`);
|
log.info('provision', `扫描到 ${this._cachedWifiList.length} 个网络: ${this._cachedWifiList.map(w => `${w.ssid}${w.band ? `(${w.band})` : ''}`).join(', ')}`);
|
||||||
|
|
||||||
// 写 DNS 劫持配置(NM 启动热点时加载);接口名与热点一致,勿写死 wlan0
|
// 写 DNS 劫持配置(NM 启动热点时加载);接口名与热点一致,勿写死 wlan0
|
||||||
this._dns = new DnsHijack();
|
this._dns = new DnsHijack();
|
||||||
this._dns.start(getWifiIface(), AP_IP);
|
this._dns.start(getWifiIface(), AP_IP);
|
||||||
|
|
||||||
const ap = startAP(this._clawId);
|
const ap = startAP(this._clawId);
|
||||||
|
|
||||||
this._server = new CaptiveServer({
|
this._server = new CaptiveServer({
|
||||||
clawId: this._clawId,
|
clawId: this._clawId,
|
||||||
cachedWifiList: this._cachedWifiList,
|
cachedWifiList: this._cachedWifiList,
|
||||||
onConnect: (ssid, password) => this._handleWifiConnect(ssid, password),
|
onConnect: (ssid, password) => this._handleWifiConnect(ssid, password),
|
||||||
});
|
});
|
||||||
this._server.startListening();
|
this._server.startListening();
|
||||||
|
|
||||||
this._state = 'ap';
|
this._state = 'ap';
|
||||||
this._apStartedAt = Date.now();
|
this._apStartedAt = Date.now();
|
||||||
this._lastApSavedWifiRetryAt = 0;
|
this._lastApSavedWifiRetryAt = 0;
|
||||||
log.info('provision', `AP 常驻模式已启动: ${ap.ssid}, 密码 12345678`);
|
log.info('provision', `AP 常驻模式已启动: ${ap.ssid}, 密码 12345678`);
|
||||||
log.info('provision', `配网地址: http://10.42.0.1`);
|
log.info('provision', `配网地址: http://10.42.0.1`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error('provision', `AP 启动失败: ${e.message}`);
|
log.error('provision', `AP 启动失败: ${e.message}`);
|
||||||
if (this._state !== 'sta') this._state = 'idle';
|
if (this._state !== 'sta') this._state = 'idle';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 用户提交 WiFi 凭证 ───────────────────────────────────────────────────
|
// ── 用户提交 WiFi 凭证 ───────────────────────────────────────────────────
|
||||||
|
|
||||||
async _handleWifiConnect(ssid, password) {
|
async _handleWifiConnect(ssid, password) {
|
||||||
if (this._state === 'connecting') return { success: false, error: '正在连接中,请稍候' };
|
if (this._state === 'connecting') return { success: false, error: '正在连接中,请稍候' };
|
||||||
|
|
||||||
this._state = 'connecting';
|
this._state = 'connecting';
|
||||||
log.info('provision', `用户请求连接 WiFi: ${ssid}`);
|
log.info('provision', `用户请求连接 WiFi: ${ssid}`);
|
||||||
led.blink(); // 正在连接 → 闪烁
|
led.blink(); // 正在连接 → 闪烁
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._stopAPServices();
|
this._stopAPServices();
|
||||||
|
|
||||||
// 关热点后射频/模式切换需要时间,立刻 connect 在部分板子上会失败
|
// 关热点后射频/模式切换需要时间,立刻 connect 在部分板子上会失败
|
||||||
await new Promise((r) => setTimeout(r, 3500));
|
await new Promise((r) => setTimeout(r, 3500));
|
||||||
|
|
||||||
const result = await connectWifi(ssid, password);
|
const result = await connectWifi(ssid, password);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this._state = 'sta';
|
this._state = 'sta';
|
||||||
log.info('provision', `WiFi 已连接: ${ssid}`);
|
log.info('provision', `WiFi 已连接: ${ssid}`);
|
||||||
led.on(); // WiFi 灯:连接成功 → 常亮
|
led.on(); // WiFi 灯:连接成功 → 常亮
|
||||||
this.emit('network-ready');
|
this.emit('network-ready');
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.warn('provision', `WiFi 连接失败: ${result.error},按当前网络状态恢复`);
|
log.warn('provision', `WiFi 连接失败: ${result.error},按当前网络状态恢复`);
|
||||||
this._recoverAfterWifiFailure();
|
this._recoverAfterWifiFailure();
|
||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error('provision', `配网过程异常: ${e.message}`);
|
log.error('provision', `配网过程异常: ${e.message}`);
|
||||||
this._recoverAfterWifiFailure();
|
this._recoverAfterWifiFailure();
|
||||||
return { success: false, error: e.message };
|
return { success: false, error: e.message };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** WiFi 连接失败后:有线可用则保持 wired;否则开 AP 兜底。 */
|
/** WiFi 连接失败后:有线可用则保持 wired;否则开 AP 兜底。 */
|
||||||
_recoverAfterWifiFailure() {
|
_recoverAfterWifiFailure() {
|
||||||
if (hasInternet()) {
|
if (hasInternet()) {
|
||||||
this._state = 'wired';
|
this._state = 'wired';
|
||||||
led.off();
|
led.off();
|
||||||
this._emitNetworkReady();
|
this._emitNetworkReady();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._safeReenterAP();
|
this._safeReenterAP();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 重新开 AP;失败时勿把 _state 永久卡在 connecting */
|
/** 重新开 AP;失败时勿把 _state 永久卡在 connecting */
|
||||||
_safeReenterAP() {
|
_safeReenterAP() {
|
||||||
try {
|
try {
|
||||||
this._enterAP();
|
this._enterAP();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error('provision', `重新启动 AP 失败: ${e.message}`);
|
log.error('provision', `重新启动 AP 失败: ${e.message}`);
|
||||||
this._state = 'idle';
|
this._state = 'idle';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── WiFi 状态监控 ─────────────────────────────────────────────────────────
|
// ── WiFi 状态监控 ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
_startMonitor() {
|
_startMonitor() {
|
||||||
this._monitorTimer = setInterval(() => {
|
this._monitorTimer = setInterval(() => {
|
||||||
if (this._monitorBusy) return;
|
if (this._monitorBusy) return;
|
||||||
this._monitorBusy = true;
|
this._monitorBusy = true;
|
||||||
this._monitorTick()
|
this._monitorTick()
|
||||||
.catch((e) => log.error('provision', `WiFi 状态监控异常: ${e.message}`))
|
.catch((e) => log.error('provision', `WiFi 状态监控异常: ${e.message}`))
|
||||||
.finally(() => { this._monitorBusy = false; });
|
.finally(() => { this._monitorBusy = false; });
|
||||||
}, MONITOR_INTERVAL_MS);
|
}, MONITOR_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _monitorTick() {
|
async _monitorTick() {
|
||||||
if (this._state === 'connecting') return;
|
if (this._state === 'connecting') return;
|
||||||
|
|
||||||
const wifiUp = isWifiStaConnected();
|
const wifiUp = isWifiStaConnected();
|
||||||
|
|
||||||
if (wifiUp && this._state !== 'sta') {
|
if (wifiUp && this._state !== 'sta') {
|
||||||
if (this._state === 'ap') {
|
if (this._state === 'ap') {
|
||||||
log.info('provision', 'WiFi 已外部连接,关闭 AP');
|
log.info('provision', 'WiFi 已外部连接,关闭 AP');
|
||||||
this._stopAPServices();
|
this._stopAPServices();
|
||||||
}
|
}
|
||||||
this._state = 'sta';
|
this._state = 'sta';
|
||||||
this._emitNetworkReady();
|
this._emitNetworkReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._state === 'sta' && !wifiUp) {
|
if (this._state === 'sta' && !wifiUp) {
|
||||||
log.warn('provision', 'WiFi 连接已断开,尝试恢复网络');
|
log.warn('provision', 'WiFi 连接已断开,尝试恢复网络');
|
||||||
await this._recoverNetworkWithoutWifi();
|
await this._recoverNetworkWithoutWifi();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._state === 'wired') {
|
if (this._state === 'wired') {
|
||||||
if (!hasInternet()) {
|
if (!hasInternet()) {
|
||||||
log.warn('provision', '有线网络不可用,尝试恢复 WiFi');
|
log.warn('provision', '有线网络不可用,尝试恢复 WiFi');
|
||||||
await this._recoverNetworkWithoutWifi();
|
await this._recoverNetworkWithoutWifi();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
led.off();
|
led.off();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._state === 'ap') {
|
if (this._state === 'ap') {
|
||||||
if (hasInternet()) {
|
// AP 模式下 hasInternet() 可能被热点本地网络 / NetworkManager limited 状态误判。
|
||||||
log.info('provision', '检测到有线网络可用,关闭 AP');
|
// 只有明确探测到有线口可访问公网时,才关闭配网 AP。
|
||||||
this._stopAPServices();
|
if (hasWiredInternetProbe()) {
|
||||||
this._state = 'wired';
|
log.info('provision', '检测到有线网络可用,关闭 AP');
|
||||||
this._emitNetworkReady();
|
this._stopAPServices();
|
||||||
return;
|
this._state = 'wired';
|
||||||
}
|
this._emitNetworkReady();
|
||||||
|
return;
|
||||||
if (hasSavedWifiConnection() && this._shouldRetrySavedWifiFromAP()) {
|
}
|
||||||
await this._retrySavedWifiFromAP();
|
|
||||||
return;
|
if (hasSavedWifiConnection() && this._shouldRetrySavedWifiFromAP()) {
|
||||||
}
|
await this._retrySavedWifiFromAP();
|
||||||
led.off();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
led.off();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
async _recoverNetworkWithoutWifi() {
|
}
|
||||||
this._state = 'connecting';
|
|
||||||
led.blink();
|
async _recoverNetworkWithoutWifi() {
|
||||||
|
this._state = 'connecting';
|
||||||
if (hasSavedWifiConnection()) {
|
led.blink();
|
||||||
const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS);
|
|
||||||
if (connected) {
|
if (hasSavedWifiConnection()) {
|
||||||
this._state = 'sta';
|
const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS);
|
||||||
this._emitNetworkReady();
|
if (connected) {
|
||||||
return;
|
this._state = 'sta';
|
||||||
}
|
this._emitNetworkReady();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
if (hasInternet()) {
|
}
|
||||||
this._state = 'wired';
|
|
||||||
led.off();
|
if (hasInternet()) {
|
||||||
this._emitNetworkReady();
|
this._state = 'wired';
|
||||||
return;
|
led.off();
|
||||||
}
|
this._emitNetworkReady();
|
||||||
|
return;
|
||||||
this._safeReenterAP();
|
}
|
||||||
}
|
|
||||||
|
this._safeReenterAP();
|
||||||
_shouldRetrySavedWifiFromAP() {
|
}
|
||||||
const now = Date.now();
|
|
||||||
if (this._apStartedAt && now - this._apStartedAt < AP_MIN_UP_BEFORE_RETRY_MS) return false;
|
_shouldRetrySavedWifiFromAP() {
|
||||||
if (this._lastApSavedWifiRetryAt && now - this._lastApSavedWifiRetryAt < AP_SAVED_WIFI_RETRY_INTERVAL_MS) return false;
|
const now = Date.now();
|
||||||
return true;
|
if (this._apStartedAt && now - this._apStartedAt < AP_MIN_UP_BEFORE_RETRY_MS) return false;
|
||||||
}
|
if (this._lastApSavedWifiRetryAt && now - this._lastApSavedWifiRetryAt < AP_SAVED_WIFI_RETRY_INTERVAL_MS) return false;
|
||||||
|
return true;
|
||||||
async _retrySavedWifiFromAP() {
|
}
|
||||||
this._lastApSavedWifiRetryAt = Date.now();
|
|
||||||
log.info('provision', 'AP 模式下定期尝试已保存 WiFi');
|
async _retrySavedWifiFromAP() {
|
||||||
this._state = 'connecting';
|
this._lastApSavedWifiRetryAt = Date.now();
|
||||||
led.blink();
|
log.info('provision', 'AP 模式下定期尝试已保存 WiFi');
|
||||||
this._stopAPServices();
|
this._state = 'connecting';
|
||||||
await new Promise((r) => setTimeout(r, 3500));
|
led.blink();
|
||||||
|
this._stopAPServices();
|
||||||
const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS);
|
await new Promise((r) => setTimeout(r, 3500));
|
||||||
if (connected) {
|
|
||||||
this._state = 'sta';
|
const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS);
|
||||||
this._emitNetworkReady();
|
if (connected) {
|
||||||
return;
|
this._state = 'sta';
|
||||||
}
|
this._emitNetworkReady();
|
||||||
|
return;
|
||||||
if (hasInternet()) {
|
}
|
||||||
this._state = 'wired';
|
|
||||||
led.off();
|
if (hasInternet()) {
|
||||||
this._emitNetworkReady();
|
this._state = 'wired';
|
||||||
return;
|
led.off();
|
||||||
}
|
this._emitNetworkReady();
|
||||||
|
return;
|
||||||
log.warn('provision', 'AP 模式下重试已保存 WiFi 失败,恢复 AP');
|
}
|
||||||
this._safeReenterAP();
|
|
||||||
}
|
log.warn('provision', 'AP 模式下重试已保存 WiFi 失败,恢复 AP');
|
||||||
|
this._safeReenterAP();
|
||||||
_stopMonitor() {
|
}
|
||||||
if (this._monitorTimer) {
|
|
||||||
clearInterval(this._monitorTimer);
|
_stopMonitor() {
|
||||||
this._monitorTimer = null;
|
if (this._monitorTimer) {
|
||||||
}
|
clearInterval(this._monitorTimer);
|
||||||
}
|
this._monitorTimer = null;
|
||||||
|
}
|
||||||
// ── 清理 ──────────────────────────────────────────────────────────────────
|
}
|
||||||
|
|
||||||
_stopAPServices() {
|
// ── 清理 ──────────────────────────────────────────────────────────────────
|
||||||
if (this._server) {
|
|
||||||
this._server.stop();
|
_stopAPServices() {
|
||||||
this._server = null;
|
if (this._server) {
|
||||||
}
|
this._server.stop();
|
||||||
if (this._dns) {
|
this._server = null;
|
||||||
this._dns.stop();
|
}
|
||||||
this._dns = null;
|
if (this._dns) {
|
||||||
}
|
this._dns.stop();
|
||||||
stopAP();
|
this._dns = null;
|
||||||
}
|
}
|
||||||
|
stopAP();
|
||||||
_stopAll() {
|
}
|
||||||
this._stopAPServices();
|
|
||||||
}
|
_stopAll() {
|
||||||
}
|
this._stopAPServices();
|
||||||
|
}
|
||||||
module.exports = { ProvisionManager };
|
}
|
||||||
|
|
||||||
|
module.exports = { ProvisionManager };
|
||||||
|
|||||||
39
lib/resource/3588s/README.md
Normal file
39
lib/resource/3588s/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# RK3588S demo resource
|
||||||
|
|
||||||
|
This directory contains the LVGL demo binary deployed on RK3588S devices by `install.sh`.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `demo` — prebuilt LVGL UI binary installed to `/usr/bin/demo` on RK3588S boards
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
Current binary source on the build machine:
|
||||||
|
|
||||||
|
- `/home/sts/share/小屏demo开发指南/lvgl源码/lv_port_linux_v2/lv_port_linux/build/bin/demo`
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This demo provides the small-screen UI used on RK3588S devices, including FIFO-based control through:
|
||||||
|
|
||||||
|
- `/tmp/lvgl_cmd`
|
||||||
|
|
||||||
|
Supported commands expected by the current clawd RK3588 LVGL backend include:
|
||||||
|
|
||||||
|
- `show_text:AP`
|
||||||
|
- `show_text:Conn`
|
||||||
|
- `show_text:Err0`
|
||||||
|
- `show_text:<PIN>`
|
||||||
|
- `show_time`
|
||||||
|
|
||||||
|
## Install behavior
|
||||||
|
|
||||||
|
During `install.sh`, if `/proc/device-tree/model` matches `RK3588S`, clawd will:
|
||||||
|
|
||||||
|
1. Back up existing `/usr/bin/demo` to `/usr/bin/demo.clawd-bak` if not already backed up
|
||||||
|
2. Install this `demo` binary to `/usr/bin/demo`
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The binary is hardware-specific and intended for RK3588S boards.
|
||||||
|
- Replacing the binary should be done together with verification of `/tmp/lvgl_cmd` behavior and screen rendering.
|
||||||
BIN
lib/resource/3588s/demo
Executable file
BIN
lib/resource/3588s/demo
Executable file
Binary file not shown.
21
lib/resource/3588s/src/LICENSE
Executable file
21
lib/resource/3588s/src/LICENSE
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Littlev Graphics Library
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
105
lib/resource/3588s/src/Makefile
Executable file
105
lib/resource/3588s/src/Makefile
Executable file
@@ -0,0 +1,105 @@
|
|||||||
|
ROOT_DIR := $(shell pwd)
|
||||||
|
|
||||||
|
LVGL_DIR_NAME ?= lvgl
|
||||||
|
|
||||||
|
CC ?= gcc
|
||||||
|
|
||||||
|
TARGET_NAME := demo
|
||||||
|
|
||||||
|
BUILD_DIR := build
|
||||||
|
OBJ_DIR := $(BUILD_DIR)/obj
|
||||||
|
BIN_DIR := $(BUILD_DIR)/bin
|
||||||
|
TARGET := $(BIN_DIR)/$(TARGET_NAME)
|
||||||
|
|
||||||
|
IMG_SRC_DIR := user/images
|
||||||
|
|
||||||
|
# 图片文件名固定为 logo,后缀可以是 png / jpg / jpeg / bmp
|
||||||
|
LOGO_NAME := logo
|
||||||
|
LOGO_IMG_EXTS := png jpg jpeg bmp
|
||||||
|
LOGO_IMG_FILES := $(foreach ext,$(LOGO_IMG_EXTS),$(IMG_SRC_DIR)/$(LOGO_NAME).$(ext))
|
||||||
|
LOGO_IMG := $(firstword $(wildcard $(LOGO_IMG_FILES)))
|
||||||
|
|
||||||
|
LOGO_C := $(LOGO_NAME).c
|
||||||
|
|
||||||
|
LVGL_IMG_CONV := lv_img_conv
|
||||||
|
LVGL_IMG_CF := CF_TRUE_COLOR_ALPHA
|
||||||
|
|
||||||
|
APP_CSRCS := $(filter-out logo.c,$(wildcard *.c))
|
||||||
|
APP_CSRCS += $(shell if [ -d src ]; then find src -type f -name "*.c"; fi)
|
||||||
|
|
||||||
|
LVGL_CSRCS := $(shell if [ -d $(LVGL_DIR_NAME)/src ]; then find $(LVGL_DIR_NAME)/src -type f -name "*.c"; fi)
|
||||||
|
|
||||||
|
LV_DRIVERS_CSRCS := $(shell if [ -d lv_drivers ]; then find lv_drivers -type f -name "*.c"; fi)
|
||||||
|
|
||||||
|
CSRCS := $(APP_CSRCS)
|
||||||
|
CSRCS += $(LVGL_CSRCS)
|
||||||
|
CSRCS += $(LV_DRIVERS_CSRCS)
|
||||||
|
CSRCS += $(LOGO_C)
|
||||||
|
|
||||||
|
OBJS := $(patsubst %.c,$(OBJ_DIR)/%.o,$(CSRCS))
|
||||||
|
|
||||||
|
CFLAGS ?= -O3 -g0
|
||||||
|
CFLAGS += -Wall
|
||||||
|
CFLAGS += -I.
|
||||||
|
CFLAGS += -I$(LVGL_DIR_NAME)
|
||||||
|
CFLAGS += -I$(LVGL_DIR_NAME)/src
|
||||||
|
CFLAGS += -Ilv_drivers
|
||||||
|
CFLAGS += -Iinclude
|
||||||
|
|
||||||
|
LDFLAGS += -lm
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(BIN_DIR):
|
||||||
|
mkdir -p $(BIN_DIR)
|
||||||
|
|
||||||
|
$(OBJ_DIR):
|
||||||
|
mkdir -p $(OBJ_DIR)
|
||||||
|
|
||||||
|
$(LOGO_C): $(LOGO_IMG)
|
||||||
|
@if [ -z "$(LOGO_IMG)" ]; then \
|
||||||
|
echo "Error: 未找到图片文件"; \
|
||||||
|
echo "请将图片命名为以下任意一种格式,并放入 $(IMG_SRC_DIR) 目录:"; \
|
||||||
|
echo " logo.png"; \
|
||||||
|
echo " logo.jpg"; \
|
||||||
|
echo " logo.jpeg"; \
|
||||||
|
echo " logo.bmp"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@echo "Use image: $(LOGO_IMG)"
|
||||||
|
rm -f $(LOGO_C)
|
||||||
|
cp $(LOGO_IMG) ./$(notdir $(LOGO_IMG))
|
||||||
|
$(LVGL_IMG_CONV) $(notdir $(LOGO_IMG)) -f -c $(LVGL_IMG_CF)
|
||||||
|
rm -f ./$(notdir $(LOGO_IMG))
|
||||||
|
|
||||||
|
$(OBJ_DIR)/%.o: %.c
|
||||||
|
mkdir -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
$(TARGET): $(LOGO_C) $(OBJS) | $(BIN_DIR)
|
||||||
|
$(CC) $(OBJS) -o $(TARGET) $(LDFLAGS)
|
||||||
|
@echo "Build success: $(TARGET)"
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -rf $(OBJ_DIR)
|
||||||
|
rm -f $(TARGET)
|
||||||
|
|
||||||
|
.PHONY: imgclean
|
||||||
|
imgclean:
|
||||||
|
rm -f $(LOGO_C)
|
||||||
|
|
||||||
|
.PHONY: distclean
|
||||||
|
distclean:
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
|
rm -f $(LOGO_C)
|
||||||
|
|
||||||
|
.PHONY: info
|
||||||
|
info:
|
||||||
|
@echo "TARGET = $(TARGET)"
|
||||||
|
@echo "IMG_SRC_DIR = $(IMG_SRC_DIR)"
|
||||||
|
@echo "LOGO_IMG = $(LOGO_IMG)"
|
||||||
|
@echo "LOGO_C = $(LOGO_C)"
|
||||||
|
@echo "Support image formats: $(LOGO_IMG_EXTS)"
|
||||||
|
@echo "CSRCS = $(CSRCS)"
|
||||||
8
lib/resource/3588s/src/README.md
Executable file
8
lib/resource/3588s/src/README.md
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
# LVGL for frame buffer device
|
||||||
|
|
||||||
|
LVGL configured to work with /dev/fb0 on Linux.
|
||||||
|
|
||||||
|
When cloning this repository, also make sure to download submodules (`git submodule update --init --recursive`) otherwise you will be missing key components.
|
||||||
|
|
||||||
|
Check out this blog post for a step by step tutorial:
|
||||||
|
https://blog.lvgl.io/2018-01-03/linux_fb
|
||||||
16
lib/resource/3588s/src/S50-lv_demo
Executable file
16
lib/resource/3588s/src/S50-lv_demo
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
start() {
|
||||||
|
demo &
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {start}"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit $?
|
||||||
BIN
lib/resource/3588s/src/build/bin/demo
Executable file
BIN
lib/resource/3588s/src/build/bin/demo
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/logo.o
Executable file
BIN
lib/resource/3588s/src/build/obj/logo.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/GC9A01.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/GC9A01.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/ILI9341.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/ILI9341.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/R61581.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/R61581.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/SHARP_MIP.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/SHARP_MIP.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/SSD1963.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/SSD1963.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/ST7565.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/ST7565.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/UC1610.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/UC1610.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/drm.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/drm.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/fbdev.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/display/fbdev.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/gtkdrv/gtkdrv.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/gtkdrv/gtkdrv.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/AD_touch.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/AD_touch.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/FT5406EE8.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/FT5406EE8.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/XPT2046.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/XPT2046.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/evdev.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/evdev.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/libinput.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/libinput.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/xkb.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/indev/xkb.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/sdl/sdl.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/sdl/sdl.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/sdl/sdl_gpu.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/sdl/sdl_gpu.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/wayland/wayland.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/wayland/wayland.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/win32drv/win32drv.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/win32drv/win32drv.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lv_drivers/win_drv.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lv_drivers/win_drv.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_disp.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_disp.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_event.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_event.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_group.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_group.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_indev.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_indev.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_indev_scroll.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_indev_scroll.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_class.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_class.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_draw.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_draw.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_pos.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_pos.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_scroll.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_scroll.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_style.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_style.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_style_gen.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_style_gen.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_tree.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_obj_tree.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_refr.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_refr.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_theme.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/core/lv_theme.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_arc.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_arc.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_img.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_img.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_label.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_label.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_line.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_line.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_mask.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_mask.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_rect.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_rect.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_triangle.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_draw_triangle.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_img_buf.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_img_buf.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_img_cache.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_img_cache.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_img_decoder.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/lv_img_decoder.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp_osa.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp_osa.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/nxp_vglite/lv_gpu_nxp_vglite.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/nxp_vglite/lv_gpu_nxp_vglite.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_arc.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_arc.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_bg.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_bg.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_composite.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_composite.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_img.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_img.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_label.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_label.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_line.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_line.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_mask.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_mask.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_polygon.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_polygon.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_rect.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_rect.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_stack_blur.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_utils.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sdl/lv_draw_sdl_utils.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_arc.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_arc.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_blend.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_blend.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_dither.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_dither.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_gradient.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_gradient.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_img.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_img.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_letter.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_letter.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_line.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_line.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_polygon.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_polygon.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_rect.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/draw/sw/lv_draw_sw_rect.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/layouts/flex/lv_flex.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/layouts/flex/lv_flex.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/layouts/grid/lv_grid.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/layouts/grid/lv_grid.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/bmp/lv_bmp.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/bmp/lv_bmp.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/freetype/lv_freetype.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/freetype/lv_freetype.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/fsdrv/lv_fs_fatfs.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/fsdrv/lv_fs_fatfs.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/fsdrv/lv_fs_posix.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/fsdrv/lv_fs_posix.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/fsdrv/lv_fs_stdio.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/fsdrv/lv_fs_stdio.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/fsdrv/lv_fs_win32.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/fsdrv/lv_fs_win32.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/gif/gifdec.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/gif/gifdec.o
Executable file
Binary file not shown.
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/gif/lv_gif.o
Executable file
BIN
lib/resource/3588s/src/build/obj/lvgl/src/extra/libs/gif/lv_gif.o
Executable file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user