Skip to content

shikihane/lobster

Repository files navigation

Lobster WiFi Portal (大龙虾配网助手)

Radxa 开发板的常驻 WiFi 配网服务。开机自动检测网络,无 WiFi 时开热点供用户配网,有 WiFi 时提供管理页面。

系统要求

  • Debian 11+ (aarch64)
  • Python 3.9+ (仅用 stdlib,无需 pip 安装任何包)
  • NetworkManager (nmcli 1.30+)
  • nginx (已安装且 sites-available/default 可写)
  • avahi-daemon (可选,提供 mDNS/.local 域名发现)

文件清单与部署目标

源文件                    →  板卡目标路径
─────────────────────────────────────────────────────────────
lobster.py                →  /opt/lobster/lobster.py
index.html                →  /var/www/html/index.html        ← nginx web root,重要!
lobster.service           →  /etc/systemd/system/lobster.service
lobster-web.service       →  /etc/avahi/services/lobster-web.service
nginx-default             →  /etc/nginx/sites-available/default
captive-portal.conf       →  /etc/NetworkManager/dnsmasq-shared.d/captive-portal.conf

注意:index.html 由 nginx 直接提供,必须放到 /var/www/html/,不是 /opt/lobster/

部署前:需要适配的硬编码

index.html 中有一处需要根据实际板卡 hostname 修改:

// index.html 第 365 行附近
const PROBE_HOSTS = ["clawbox.local", "clawbox", "radxa-cubie-a7a.local", "radxa-cubie-a7a"];

改成实际的板卡 hostname。此值用于 AP→STA 切换后,前端自动探测板卡新地址。

lobster.py 顶部常量按需修改:

常量 默认值 说明
WIFI_IFACE wlan0 WiFi 网卡名
AP_PASSWORD 88888888 热点密码
WIFI_CHECK_TIMEOUT 60 开机等待 WiFi 的秒数
API_PORT 8899 HTTP API 端口(同时需改 nginx-default 里的 proxy_pass)
OPENCLAW_PORT 18790 OpenClaw 控制台端口(前端跳转用)

全新部署步骤

以下所有命令在板卡上以 root 执行。假设源文件已上传到板卡的 /tmp/lobster/

步骤 1:检查依赖

python3 --version       # 需要 3.9+
nmcli --version         # 需要 NetworkManager
nginx -v                # 需要 nginx
avahi-daemon --check    # 可选,返回 0 表示正常

步骤 2:复制服务文件

mkdir -p /opt/lobster
cp /tmp/lobster/lobster.py /opt/lobster/lobster.py
chmod +x /opt/lobster/lobster.py

步骤 3:复制前端页面到 nginx web root

cp /tmp/lobster/index.html /var/www/html/index.html

为什么是 /var/www/html/ 因为 nginx 配置中 root /var/www/html;,浏览器访问 http://板卡IP/ 时 nginx 直接从这里读文件。lobster.py 本身不提供静态文件,它只提供 /api/* 接口。

步骤 4:nginx 配置

# 备份原配置
cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak

# 部署新配置
cp /tmp/lobster/nginx-default /etc/nginx/sites-available/default

# 验证语法
nginx -t

# 重载(不要用 restart,reload 不断开现有连接)
systemctl reload nginx

nginx-default 做了两件事:

  • location /api/ → 反向代理到 127.0.0.1:8899(lobster.py)
  • location /try_files $uri $uri/ /index.html(任意路径 fallback 到配网页面,captive portal 需要此行为)

步骤 5:Captive Portal DNS

# 确认目录存在
ls -d /etc/NetworkManager/dnsmasq-shared.d/

# 部署
cp /tmp/lobster/captive-portal.conf /etc/NetworkManager/dnsmasq-shared.d/

作用:AP 热点模式下,dnsmasq 将所有 DNS 查询解析到 10.42.0.1(网关),手机连上热点后自动弹出"登录此网络"页面。此配置仅在 NetworkManager shared 模式(即 AP 热点)下生效,不影响正常 WiFi。

如果目录不存在,跳过此步。影响:手机连上热点后不会自动弹出页面,需要用户手动打开浏览器访问 10.42.0.1

步骤 6:Avahi 服务发现(可选)

cp /tmp/lobster/lobster-web.service /etc/avahi/services/
systemctl restart avahi-daemon

作用:在局域网广播 _http._tcp 服务,名称"大龙虾控制台"。Windows/Mac/iOS 可通过 http://<hostname>.local/ 直接访问。Android 用户可安装 BonjourBrowser (wellenvogel, Google Play, 开源免费) 扫描发现。

步骤 7:systemd 服务

cp /tmp/lobster/lobster.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable lobster.service
systemctl start lobster.service

步骤 8:验证

# 1. 服务是否在运行
systemctl status lobster.service
# 预期:active (running)

# 2. API 直连(lobster.py 本体)
curl http://127.0.0.1:8899/api/status
# 预期:{"mode":"sta","ssid":"...","ip":"...","hostname":"...","ap_ssid":"大龙虾_xxxx",...}

# 3. API 经过 nginx 代理
curl http://127.0.0.1/api/status
# 预期:同上

# 4. 前端页面
curl -s http://127.0.0.1/ | head -3
# 预期:<!DOCTYPE html> ... 大龙虾控制台

# 5. 实时日志
journalctl -u lobster.service -f

升级/重新部署

如果板卡已经部署过,需要更新文件时:

# 1. 停服务
systemctl stop lobster.service

# 2. 覆盖文件(按需)
cp /tmp/lobster/lobster.py /opt/lobster/lobster.py
cp /tmp/lobster/index.html /var/www/html/index.html
# 如果改了 nginx 配置:
cp /tmp/lobster/nginx-default /etc/nginx/sites-available/default
nginx -t && systemctl reload nginx

# 3. 重启
systemctl start lobster.service

工作原理

开机
 │
 ├─ Phase 0: 清理残留
 │    ├─ 检测 8899 端口是否被占用,是则 kill
 │    ├─ 删除残留的 lobster-ap 连接(上次崩溃留下的)
 │    └─ 写 PID 文件到 /tmp/lobster.pid
 │
 ├─ Phase 1: 检测 WiFi(每 5 秒检查一次,共 60 秒)
 │    ├─ 已连接 → 进入 STA 模式,记录当前连接名
 │    └─ 60 秒仍未连接 → Phase 2
 │
 ├─ Phase 2: 开启 AP 热点
 │    ├─ nmcli device wifi hotspot ...
 │    ├─ 禁用 IPv6(加速客户端 DHCP)
 │    ├─ SSID: 大龙虾_<machine-id末4位>
 │    ├─ 密码: 88888888
 │    └─ 网关: 10.42.0.1
 │
 └─ Phase 3: HTTP API 常驻运行 (127.0.0.1:8899)
      无论 AP 还是 STA 模式都运行
      用户通过 nginx :80 代理访问

API 接口

所有接口通过 nginx http://板卡地址/api/ 访问。

方法 路径 请求体 说明
GET /api/status - 当前模式、SSID、IP、watchdog 剩余秒数
GET /api/scan - 扫描附近 WiFi 列表(AP 模式下可能为空)
POST /api/connect {"ssid":"名称","password":"密码"} 连接指定 WiFi,失败自动回滚
POST /api/mode {"mode":"ap","timeout":300} 手动切 AP,必须带 timeout(秒),超时自动恢复
POST /api/mode {"mode":"sta"} 手动切回之前的 WiFi

安全机制

核心原则:任何网络操作失败都不能让板卡失联。

  • POST /api/connect 失败 → 回滚到之前的连接(AP 模式下恢复 AP,STA 模式下恢复旧 WiFi)
  • POST /api/mode 切 AP → 强制带 watchdog 定时器,超时无人访问则自动恢复 WiFi
  • AP 启动失败 → 尝试连回之前的 WiFi
  • WiFi 恢复失败 → 重新开 AP
  • 以上全部失败 → 遍历板卡所有已保存的 WiFi 连接逐个尝试,最后兜底开 AP

用户访问方式

场景 访问地址
STA 模式(已联网) http://<hostname>.local/http://<IP>/
AP 模式(配网热点) 手机连热点后自动弹出页面;或手动访问 http://10.42.0.1/

架构图

手机/电脑浏览器
     │
     │ http://<板卡>:80
     ▼
  nginx (:80)
     │
     ├── GET /           → /var/www/html/index.html (配网页面)
     ├── GET /api/*      → proxy_pass 127.0.0.1:8899 (lobster.py)
     └── POST /api/*     → proxy_pass 127.0.0.1:8899 (lobster.py)

  lobster.py (127.0.0.1:8899)
     │
     ├── HTTP API 处理
     ├── nmcli 控制 WiFi/AP
     └── watchdog 线程(AP 超时自动恢复)

  avahi-daemon
     └── 广播 _http._tcp "大龙虾控制台"

  dnsmasq (NetworkManager shared mode)
     └── AP 模式下:所有 DNS → 10.42.0.1 (captive portal)

卸载

systemctl stop lobster.service
systemctl disable lobster.service
rm /etc/systemd/system/lobster.service
systemctl daemon-reload

rm -rf /opt/lobster
rm -f /var/www/html/index.html
rm -f /etc/NetworkManager/dnsmasq-shared.d/captive-portal.conf
rm -f /etc/avahi/services/lobster-web.service

# 恢复 nginx 备份
cp /etc/nginx/sites-available/default.bak /etc/nginx/sites-available/default
nginx -t && systemctl reload nginx

故障排查

# 服务状态和最近日志
systemctl status lobster.service
journalctl -u lobster.service --no-pager -n 50

# 端口是否被占用
ss -tlnp | grep 8899

# 杀残留进程
fuser -k 8899/tcp

# 手动启动看完整输出
systemctl stop lobster.service
python3 /opt/lobster/lobster.py

# 检查 nginx 配置语法
nginx -t

# 检查 AP 连接残留
nmcli connection show | grep lobster
# 清理:
nmcli connection delete lobster-ap

# 紧急恢复网络(接屏幕键盘操作)
nmcli connection delete lobster-ap
nmcli device wifi connect "WiFi名称" password "密码" ifname wlan0
systemctl restart lobster.service

About

大龙虾配网助手 - Radxa 开发板 WiFi 配网服务

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors