fix wifi 2 pkg issue
This commit is contained in:
@@ -24,6 +24,14 @@ WIFI_QUALITY_RTT_WARN_MS = 350.0 # 与 RSSI 联合:超过此值且信号弱
|
|||||||
WIFI_QUALITY_RSSI_BAD_DBM = -80.0 # 低于此 dBm(更负更差)视为信号弱
|
WIFI_QUALITY_RSSI_BAD_DBM = -80.0 # 低于此 dBm(更负更差)视为信号弱
|
||||||
WIFI_QUALITY_USE_RSSI = True # 是否把 RSSI 纳入综合判定(False 则仅看 RTT)
|
WIFI_QUALITY_USE_RSSI = True # 是否把 RSSI 纳入综合判定(False 则仅看 RTT)
|
||||||
|
|
||||||
|
# WiFi 热点配网(手机连设备 AP,浏览器提交路由器 SSID/密码;仅 GET/POST,标准库 socket)
|
||||||
|
WIFI_CONFIG_AP_ENABLED = False # True=启动时开热点并起迷你 HTTP 配网服务
|
||||||
|
WIFI_CONFIG_AP_SSID = "ArcherySetup" # 设备发出的热点名称
|
||||||
|
WIFI_CONFIG_AP_PASSWORD = "12345678" # 热点密码(WPA2 通常至少 8 位)
|
||||||
|
WIFI_CONFIG_HTTP_HOST = "0.0.0.0" # HTTP 监听地址
|
||||||
|
WIFI_CONFIG_HTTP_PORT = 8080 # 默认 8080,避免占用 80 需 root
|
||||||
|
WIFI_CONFIG_AP_IP = "192.168.66.1" # 与 MaixPy Wifi.start_ap 默认一致,手机访问 http://192.168.66.1:8080/
|
||||||
|
|
||||||
# ===== TCP over SSL(TLS) 配置 =====
|
# ===== TCP over SSL(TLS) 配置 =====
|
||||||
USE_TCP_SSL = False # True=按手册走 MSSLCFG/MIPCFG 绑定 SSL
|
USE_TCP_SSL = False # True=按手册走 MSSLCFG/MIPCFG 绑定 SSL
|
||||||
TCP_LINK_ID = 2 #
|
TCP_LINK_ID = 2 #
|
||||||
|
|||||||
9
main.py
9
main.py
@@ -115,6 +115,15 @@ def cmd_str():
|
|||||||
# 2. 从4G模块同步系统时间(需要 at_client 已初始化)
|
# 2. 从4G模块同步系统时间(需要 at_client 已初始化)
|
||||||
sync_system_time_from_4g()
|
sync_system_time_from_4g()
|
||||||
|
|
||||||
|
# 2.1 WiFi 热点配网兜底:仅当 STA 与 4G 均不可用时起 AP + HTTP;提交后删 /boot/wifi.ap、建 wifi.sta 并 reboot
|
||||||
|
try:
|
||||||
|
from wifi_config_httpd import maybe_start_wifi_ap_fallback
|
||||||
|
|
||||||
|
maybe_start_wifi_ap_fallback(logger)
|
||||||
|
except Exception as e:
|
||||||
|
if logger:
|
||||||
|
logger.error(f"[WIFI-AP] 兜底配网检测/启动失败: {e}")
|
||||||
|
|
||||||
# 2.5. 启动存图 worker 线程(队列 + worker,避免主循环阻塞)
|
# 2.5. 启动存图 worker 线程(队列 + worker,避免主循环阻塞)
|
||||||
start_save_shot_worker()
|
start_save_shot_worker()
|
||||||
|
|
||||||
|
|||||||
41
network.py
41
network.py
@@ -1291,42 +1291,38 @@ class NetworkManager:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# 接收数据(根据网络类型选择接收方式)
|
# 接收数据(根据网络类型选择接收方式)
|
||||||
item = None
|
# WiFi 粘包:一次 recv 可能含多条完整包;也可能缓冲里已有完整包但本轮 recv 超时为空
|
||||||
|
rx_items = []
|
||||||
if self._network_type == "wifi":
|
if self._network_type == "wifi":
|
||||||
# WiFi接收数据
|
data = self.receive_tcp_data_via_wifi(timeout_ms=5)
|
||||||
data = self.receive_tcp_data_via_wifi(timeout_ms=50)
|
|
||||||
if data:
|
if data:
|
||||||
# 将数据添加到缓冲区
|
|
||||||
self.logger.info(f"[NET] 接收WiFi数据, {time.time()}")
|
self.logger.info(f"[NET] 接收WiFi数据, {time.time()}")
|
||||||
wifi_manager.recv_buffer += data
|
wifi_manager.recv_buffer += data
|
||||||
|
while len(wifi_manager.recv_buffer) >= 12:
|
||||||
# 尝试从缓冲区解析完整的数据包
|
|
||||||
while len(wifi_manager.recv_buffer) >= 12: # 至少需要12字节的头部
|
|
||||||
# 解析头部
|
|
||||||
try:
|
try:
|
||||||
body_len, msg_type, checksum = struct.unpack(">III", wifi_manager.recv_buffer[:12])
|
body_len, msg_type, checksum = struct.unpack(">III", wifi_manager.recv_buffer[:12])
|
||||||
total_len = 12 + body_len
|
total_len = 12 + body_len
|
||||||
|
|
||||||
if len(wifi_manager.recv_buffer) >= total_len:
|
if len(wifi_manager.recv_buffer) >= total_len:
|
||||||
# 有完整的数据包
|
|
||||||
payload = wifi_manager.recv_buffer[:total_len]
|
payload = wifi_manager.recv_buffer[:total_len]
|
||||||
wifi_manager.recv_buffer = wifi_manager.recv_buffer[total_len:]
|
wifi_manager.recv_buffer = wifi_manager.recv_buffer[total_len:]
|
||||||
item = (0, payload) # link_id=0 for WiFi
|
rx_items.append((0, payload))
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
# 数据包不完整,等待更多数据
|
|
||||||
self.logger.info(f"[NET] 接收WiFi数据不完整, {time.time()}")
|
self.logger.info(f"[NET] 接收WiFi数据不完整, {time.time()}")
|
||||||
break
|
break
|
||||||
except:
|
except Exception:
|
||||||
# 解析失败,清空缓冲区
|
|
||||||
wifi_manager.recv_buffer = b""
|
wifi_manager.recv_buffer = b""
|
||||||
self.logger.info(f"[NET] 接收WiFi数据解析失败, {time.time()}")
|
self.logger.info(f"[NET] 接收WiFi数据解析失败, {time.time()}")
|
||||||
break
|
break
|
||||||
elif self._network_type == "4g":
|
elif self._network_type == "4g":
|
||||||
# 4G接收数据
|
|
||||||
item = hardware_manager.at_client.pop_tcp_payload()
|
item = hardware_manager.at_client.pop_tcp_payload()
|
||||||
|
|
||||||
if item:
|
if item:
|
||||||
|
rx_items.append(item)
|
||||||
|
|
||||||
|
_rx_login_fail = False
|
||||||
|
_rx_skip_tcp_iteration = False
|
||||||
|
if rx_items:
|
||||||
|
self.logger.info(f"total {len(rx_items)} items")
|
||||||
|
for item in rx_items:
|
||||||
if isinstance(item, tuple) and len(item) == 2:
|
if isinstance(item, tuple) and len(item) == 2:
|
||||||
link_id, payload = item
|
link_id, payload = item
|
||||||
else:
|
else:
|
||||||
@@ -1362,6 +1358,7 @@ class NetworkManager:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f"[OTA] ota_ok 上报失败: {e}")
|
self.logger.error(f"[OTA] ota_ok 上报失败: {e}")
|
||||||
else:
|
else:
|
||||||
|
_rx_login_fail = True
|
||||||
break
|
break
|
||||||
|
|
||||||
# 处理心跳 ACK
|
# 处理心跳 ACK
|
||||||
@@ -1435,12 +1432,14 @@ class NetworkManager:
|
|||||||
if not ota_url:
|
if not ota_url:
|
||||||
self.logger.error("ota missing_url")
|
self.logger.error("ota missing_url")
|
||||||
self.safe_enqueue({"result": "missing_url"}, 2)
|
self.safe_enqueue({"result": "missing_url"}, 2)
|
||||||
continue
|
_rx_skip_tcp_iteration = True
|
||||||
|
break
|
||||||
|
|
||||||
from ota_manager import ota_manager
|
from ota_manager import ota_manager
|
||||||
if ota_manager.update_thread_started:
|
if ota_manager.update_thread_started:
|
||||||
self.safe_enqueue({"result": "update_already_started"}, 2)
|
self.safe_enqueue({"result": "update_already_started"}, 2)
|
||||||
continue
|
_rx_skip_tcp_iteration = True
|
||||||
|
break
|
||||||
|
|
||||||
# 自动判断模式:如果没有明确指定,根据WiFi连接状态和凭证决定
|
# 自动判断模式:如果没有明确指定,根据WiFi连接状态和凭证决定
|
||||||
if mode not in ("4g", "wifi"):
|
if mode not in ("4g", "wifi"):
|
||||||
@@ -1550,6 +1549,10 @@ class NetworkManager:
|
|||||||
self.logger.info(f"[NET] body={body}, {time.time()}")
|
self.logger.info(f"[NET] body={body}, {time.time()}")
|
||||||
else:
|
else:
|
||||||
self.logger.info(f"[NET] 未知数据 {body}, {time.time()}")
|
self.logger.info(f"[NET] 未知数据 {body}, {time.time()}")
|
||||||
|
if _rx_login_fail:
|
||||||
|
break
|
||||||
|
if _rx_skip_tcp_iteration:
|
||||||
|
continue
|
||||||
else:
|
else:
|
||||||
time.sleep_ms(5)
|
time.sleep_ms(5)
|
||||||
|
|
||||||
|
|||||||
74
wifi.py
74
wifi.py
@@ -116,6 +116,20 @@ class WiFiManager:
|
|||||||
|
|
||||||
# ==================== WiFi 连接方法 ====================
|
# ==================== WiFi 连接方法 ====================
|
||||||
|
|
||||||
|
def is_sta_associated(self):
|
||||||
|
"""
|
||||||
|
是否作为 STA 已关联到上游 AP(用于与 AP 模式区分:AP 模式下 wlan0 可能有 IP 但 iw link 为 Not connected)。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
out = os.popen("iw dev wlan0 link 2>/dev/null").read()
|
||||||
|
if not out.strip():
|
||||||
|
return False
|
||||||
|
if "Not connected" in out:
|
||||||
|
return False
|
||||||
|
return "Connected to" in out
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
def is_wifi_connected(self):
|
def is_wifi_connected(self):
|
||||||
"""检查WiFi是否已连接"""
|
"""检查WiFi是否已连接"""
|
||||||
# 优先用 MaixPy network(如果可用)
|
# 优先用 MaixPy network(如果可用)
|
||||||
@@ -272,6 +286,66 @@ class WiFiManager:
|
|||||||
self.logger.error(f"[WIFI] 连接/验证失败,已回滚: {e}")
|
self.logger.error(f"[WIFI] 连接/验证失败,已回滚: {e}")
|
||||||
return None, str(e)
|
return None, str(e)
|
||||||
|
|
||||||
|
def persist_sta_credentials(self, ssid: str, password: str, restart_service: bool = True):
|
||||||
|
"""
|
||||||
|
仅写入 STA 凭证(/etc/wpa_supplicant.conf + /boot/wifi.ssid|pass),
|
||||||
|
可选是否立即 /etc/init.d/S30wifi restart。
|
||||||
|
不做可达性验证。用于热点配网页提交后切换到连接指定路由器。
|
||||||
|
password 为空时按开放网络(key_mgmt=NONE)写入。
|
||||||
|
Returns:
|
||||||
|
(ok: bool, err_msg: str)
|
||||||
|
"""
|
||||||
|
ssid = (ssid or "").strip()
|
||||||
|
password = (password or "").strip()
|
||||||
|
if not ssid:
|
||||||
|
return False, "SSID 为空"
|
||||||
|
|
||||||
|
conf_path = "/etc/wpa_supplicant.conf"
|
||||||
|
ssid_file = "/boot/wifi.ssid"
|
||||||
|
pass_file = "/boot/wifi.pass"
|
||||||
|
|
||||||
|
def _write_text(path: str, content: str):
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if password:
|
||||||
|
net_conf = os.popen(f'wpa_passphrase "{ssid}" "{password}"').read()
|
||||||
|
if "network={" not in net_conf:
|
||||||
|
return False, "wpa_passphrase 失败"
|
||||||
|
else:
|
||||||
|
esc = ssid.replace("\\", "\\\\").replace('"', '\\"')
|
||||||
|
net_conf = (
|
||||||
|
"network={\n"
|
||||||
|
f' ssid="{esc}"\n'
|
||||||
|
" key_mgmt=NONE\n"
|
||||||
|
"}\n"
|
||||||
|
)
|
||||||
|
_write_text(
|
||||||
|
conf_path,
|
||||||
|
"ctrl_interface=/var/run/wpa_supplicant\n"
|
||||||
|
"update_config=1\n\n"
|
||||||
|
+ net_conf,
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return False, str(e)
|
||||||
|
|
||||||
|
try:
|
||||||
|
_write_text(ssid_file, ssid)
|
||||||
|
_write_text(pass_file, password)
|
||||||
|
except Exception as e:
|
||||||
|
return False, str(e)
|
||||||
|
|
||||||
|
if restart_service:
|
||||||
|
try:
|
||||||
|
os.system("/etc/init.d/S30wifi restart")
|
||||||
|
except Exception as e:
|
||||||
|
return False, str(e)
|
||||||
|
self.logger.info(f"[WIFI] persist_sta_credentials: 已写入并重启 S30wifi, ssid={ssid!r}")
|
||||||
|
else:
|
||||||
|
self.logger.info(f"[WIFI] persist_sta_credentials: 已写入凭证(未重启 S30wifi), ssid={ssid!r}")
|
||||||
|
return True, ""
|
||||||
|
|
||||||
def disconnect_wifi(self):
|
def disconnect_wifi(self):
|
||||||
"""断开WiFi连接并清理资源"""
|
"""断开WiFi连接并清理资源"""
|
||||||
if self._wifi_socket:
|
if self._wifi_socket:
|
||||||
|
|||||||
Reference in New Issue
Block a user