feat: init
This commit is contained in:
412
src/wifi_manager.cpp
Normal file
412
src/wifi_manager.cpp
Normal file
@@ -0,0 +1,412 @@
|
||||
/**
|
||||
* @file wifi_manager.cpp
|
||||
* @brief WiFi 配网管理器实现
|
||||
*/
|
||||
|
||||
#include "wifi_manager.h"
|
||||
|
||||
// 单例指针
|
||||
WiFiMgmt* WiFiMgmt::_instance = nullptr;
|
||||
|
||||
// ============================================================
|
||||
// 构造函数 & 析构函数
|
||||
// ============================================================
|
||||
|
||||
WiFiMgmt::WiFiMgmt()
|
||||
: _connected(false)
|
||||
, _lastCheckTime(0)
|
||||
, _buttonPressTime(0)
|
||||
, _buttonPressed(false)
|
||||
, _ledLastToggle(0)
|
||||
, _ledState(false)
|
||||
, _param_mqtt_server(nullptr)
|
||||
, _param_mqtt_port(nullptr)
|
||||
, _param_mqtt_user(nullptr)
|
||||
, _param_mqtt_pass(nullptr)
|
||||
, _param_device_id(nullptr)
|
||||
, _param_env_note(nullptr)
|
||||
{
|
||||
memset(&_config, 0, sizeof(_config));
|
||||
_instance = this; // 设置单例
|
||||
}
|
||||
|
||||
WiFiMgmt::~WiFiMgmt() {
|
||||
_instance = nullptr;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 初始化
|
||||
// ============================================================
|
||||
|
||||
bool WiFiMgmt::begin() {
|
||||
Serial.println(F("\n========================================"));
|
||||
Serial.println(F(" WiFi 配网管理器 初始化"));
|
||||
Serial.println(F("========================================"));
|
||||
|
||||
// 初始化引脚
|
||||
pinMode(CONFIG_BUTTON_PIN, INPUT_PULLUP);
|
||||
pinMode(LED_WIFI_PIN, OUTPUT);
|
||||
digitalWrite(LED_WIFI_PIN, LOW);
|
||||
|
||||
// 初始化 LittleFS
|
||||
if (!LittleFS.begin(true)) {
|
||||
Serial.println(F("[WiFi] ❌ LittleFS 挂载失败!"));
|
||||
return false;
|
||||
}
|
||||
Serial.println(F("[WiFi] ✅ LittleFS 已挂载"));
|
||||
|
||||
// 加载配置
|
||||
if (!_loadConfig()) {
|
||||
Serial.println(F("[WiFi] 使用默认配置"));
|
||||
// 设置默认值
|
||||
strncpy(_config.mqtt_server, MQTT_DEFAULT_SERVER, sizeof(_config.mqtt_server) - 1);
|
||||
strncpy(_config.mqtt_port, MQTT_DEFAULT_PORT_STR, sizeof(_config.mqtt_port) - 1);
|
||||
strncpy(_config.mqtt_user, MQTT_DEFAULT_USER, sizeof(_config.mqtt_user) - 1);
|
||||
strncpy(_config.mqtt_pass, MQTT_DEFAULT_PASS, sizeof(_config.mqtt_pass) - 1);
|
||||
strncpy(_config.device_id, MQTT_DEFAULT_DEVICE, sizeof(_config.device_id) - 1);
|
||||
strncpy(_config.env_note, MQTT_DEFAULT_ENV, sizeof(_config.env_note) - 1);
|
||||
}
|
||||
|
||||
// 配置 WiFiManager
|
||||
_wm.setDebugOutput(false); // 关闭调试输出 (避免干扰)
|
||||
_wm.setConfigPortalTimeout(WIFI_CONFIG_TIMEOUT);
|
||||
_wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
|
||||
_wm.setTitle("🏥 五劳无感康养监测 - 设备配网"); // 配网页面的中文标题
|
||||
_wm.setCustomHeadElement(
|
||||
"<style>"
|
||||
"body { font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif; }"
|
||||
".msg .info { font-size: 14px; }"
|
||||
"</style>"
|
||||
);
|
||||
WiFi.setAutoReconnect(true); // ESP32 自动重连
|
||||
|
||||
// 注册保存回调 (必须使用静态函数, 不能使用 lambda)
|
||||
_wm.setSaveConfigCallback(_saveParamsCallback);
|
||||
|
||||
// 创建自定义参数
|
||||
_param_mqtt_server = new WiFiManagerParameter(
|
||||
"mqtt_server", "MQTT 服务器地址",
|
||||
_config.mqtt_server, 40);
|
||||
_param_mqtt_port = new WiFiManagerParameter(
|
||||
"mqtt_port", "MQTT 端口",
|
||||
_config.mqtt_port, 6);
|
||||
_param_mqtt_user = new WiFiManagerParameter(
|
||||
"mqtt_user", "MQTT 用户名",
|
||||
_config.mqtt_user, 20);
|
||||
_param_mqtt_pass = new WiFiManagerParameter(
|
||||
"mqtt_pass", "MQTT 密码",
|
||||
_config.mqtt_pass, 20);
|
||||
_param_device_id = new WiFiManagerParameter(
|
||||
"device_id", "设备名称",
|
||||
_config.device_id, 20);
|
||||
_param_env_note = new WiFiManagerParameter(
|
||||
"env_note", "🏠 环境备注 (如: 主卧/客厅/老人房)",
|
||||
_config.env_note, 20);
|
||||
|
||||
// 注册自定义参数
|
||||
_wm.addParameter(_param_mqtt_server);
|
||||
_wm.addParameter(_param_mqtt_port);
|
||||
_wm.addParameter(_param_mqtt_user);
|
||||
_wm.addParameter(_param_mqtt_pass);
|
||||
_wm.addParameter(_param_device_id);
|
||||
_wm.addParameter(_param_env_note);
|
||||
|
||||
// 设置 AP 模式回调 (静态函数)
|
||||
_wm.setAPCallback(_apCallback);
|
||||
|
||||
// 尝试连接 WiFi
|
||||
_connected = _connectWiFi();
|
||||
|
||||
if (!_connected) {
|
||||
// 如果自动连接失败, 启动配网页面
|
||||
Serial.println(F("[WiFi] 自动连接失败, 启动配网模式..."));
|
||||
_setLEDMode(1); // 快闪 = 配网模式
|
||||
_connected = _connectWiFi(); // 内部会启动配网页面
|
||||
}
|
||||
|
||||
if (_connected) {
|
||||
_setLEDMode(2); // 常亮 = 已连接
|
||||
}
|
||||
|
||||
_lastCheckTime = millis();
|
||||
return _connected;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 主循环
|
||||
// ============================================================
|
||||
|
||||
bool WiFiMgmt::loop() {
|
||||
// 检测按钮长按
|
||||
if (_checkButtonLongPress()) {
|
||||
Serial.println(F("[WiFi] ⚠️ 检测到长按, 清除配置并重启..."));
|
||||
resetSettings();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查 WiFi 状态
|
||||
if (millis() - _lastCheckTime > WIFI_RETRY_INTERVAL) {
|
||||
_lastCheckTime = millis();
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
_connected = false;
|
||||
_setLEDMode(0); // 慢闪 = 未连接
|
||||
Serial.println(F("[WiFi] ⚠️ WiFi 断开, 尝试重连..."));
|
||||
_connectWiFi();
|
||||
} else {
|
||||
_connected = true;
|
||||
_setLEDMode(2); // 常亮
|
||||
}
|
||||
}
|
||||
|
||||
// LED 闪烁处理
|
||||
if (!_connected) {
|
||||
uint32_t interval = (_wm.getConfigPortalActive()) ? 300 : 1000;
|
||||
if (millis() - _ledLastToggle > interval) {
|
||||
_ledLastToggle = millis();
|
||||
_ledState = !_ledState;
|
||||
digitalWrite(LED_WIFI_PIN, _ledState ? HIGH : LOW);
|
||||
}
|
||||
}
|
||||
|
||||
return _connected;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// WiFi 连接
|
||||
// ============================================================
|
||||
|
||||
bool WiFiMgmt::_connectWiFi() {
|
||||
// 检查是否有已保存的配置
|
||||
// autoConnect 会先尝试连接, 失败后启动 AP 配网页面
|
||||
_connected = _wm.autoConnect(WIFI_AP_SSID, WIFI_AP_PASSWORD);
|
||||
|
||||
if (_connected) {
|
||||
Serial.println(F("[WiFi] ✅ 已连接!"));
|
||||
Serial.printf(" WiFi名称: %s\n", WiFi.SSID().c_str());
|
||||
Serial.printf(" IP地址: %s\n", WiFi.localIP().toString().c_str());
|
||||
Serial.printf(" MAC地址: %s\n", WiFi.macAddress().c_str());
|
||||
|
||||
// 重新加载配置 (确保获取最新值)
|
||||
// autoConnect 过程中如果有保存, 回调已写入 LittleFS
|
||||
_loadConfig();
|
||||
|
||||
// 同步自定义参数 (如果配置是从 LittleFS 加载的)
|
||||
_param_mqtt_server->setValue(_config.mqtt_server, sizeof(_config.mqtt_server));
|
||||
_param_mqtt_port->setValue(_config.mqtt_port, sizeof(_config.mqtt_port));
|
||||
_param_mqtt_user->setValue(_config.mqtt_user, sizeof(_config.mqtt_user));
|
||||
_param_mqtt_pass->setValue(_config.mqtt_pass, sizeof(_config.mqtt_pass));
|
||||
_param_device_id->setValue(_config.device_id, sizeof(_config.device_id));
|
||||
_param_env_note->setValue(_config.env_note, sizeof(_config.env_note));
|
||||
|
||||
Serial.printf(" 设备名: %s\n", _config.device_id);
|
||||
Serial.printf(" 环境: %s\n", _config.env_note);
|
||||
Serial.printf(" MQTT服务器: %s:%s\n", _config.mqtt_server, _config.mqtt_port);
|
||||
}
|
||||
|
||||
return _connected;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 配网页面
|
||||
// ============================================================
|
||||
|
||||
void WiFiMgmt::startConfigPortal() {
|
||||
Serial.println(F("[WiFi] 🔧 手动启动配网页面..."));
|
||||
_setLEDMode(1);
|
||||
_wm.startConfigPortal(WIFI_AP_SSID, WIFI_AP_PASSWORD);
|
||||
_connected = (WiFi.status() == WL_CONNECTED);
|
||||
if (_connected) {
|
||||
_setLEDMode(2);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 重置设置
|
||||
// ============================================================
|
||||
|
||||
void WiFiMgmt::resetSettings() {
|
||||
Serial.println(F("[WiFi] 清除所有配置..."));
|
||||
|
||||
// 删除 LittleFS 中的配置文件
|
||||
if (LittleFS.exists(CONFIG_FILE_WIFI)) LittleFS.remove(CONFIG_FILE_WIFI);
|
||||
if (LittleFS.exists(CONFIG_FILE_MQTT)) LittleFS.remove(CONFIG_FILE_MQTT);
|
||||
if (LittleFS.exists(CONFIG_FILE_DEVICE)) LittleFS.remove(CONFIG_FILE_DEVICE);
|
||||
|
||||
// 清除 WiFiManager 的配置
|
||||
_wm.resetSettings();
|
||||
|
||||
// LED 快闪提示
|
||||
for (int i = 0; i < 10; i++) {
|
||||
digitalWrite(LED_WIFI_PIN, !digitalRead(LED_WIFI_PIN));
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println(F("[WiFi] 🔄 正在重启..."));
|
||||
delay(500);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 按钮长按检测
|
||||
// ============================================================
|
||||
|
||||
bool WiFiMgmt::_checkButtonLongPress() {
|
||||
bool pressed = (digitalRead(CONFIG_BUTTON_PIN) == LOW);
|
||||
|
||||
if (pressed && !_buttonPressed) {
|
||||
// 按下
|
||||
_buttonPressed = true;
|
||||
_buttonPressTime = millis();
|
||||
} else if (!pressed && _buttonPressed) {
|
||||
// 松开
|
||||
_buttonPressed = false;
|
||||
if (millis() - _buttonPressTime >= BUTTON_LONG_PRESS_MS) {
|
||||
return true; // 长按确认
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// LittleFS 配置持久化
|
||||
// ============================================================
|
||||
|
||||
bool WiFiMgmt::_loadConfig() {
|
||||
if (!LittleFS.exists(CONFIG_FILE_DEVICE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File file = LittleFS.open(CONFIG_FILE_DEVICE, "r");
|
||||
if (!file) return false;
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError err = deserializeJson(doc, file);
|
||||
file.close();
|
||||
|
||||
if (err) {
|
||||
Serial.printf("[WiFi] JSON 解析失败: %s\n", err.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
strncpy(_config.mqtt_server, doc["mqtt_server"] | MQTT_DEFAULT_SERVER, sizeof(_config.mqtt_server) - 1);
|
||||
strncpy(_config.mqtt_port, doc["mqtt_port"] | MQTT_DEFAULT_PORT_STR, sizeof(_config.mqtt_port) - 1);
|
||||
strncpy(_config.mqtt_user, doc["mqtt_user"] | MQTT_DEFAULT_USER, sizeof(_config.mqtt_user) - 1);
|
||||
strncpy(_config.mqtt_pass, doc["mqtt_pass"] | MQTT_DEFAULT_PASS, sizeof(_config.mqtt_pass) - 1);
|
||||
strncpy(_config.device_id, doc["device_id"] | MQTT_DEFAULT_DEVICE, sizeof(_config.device_id) - 1);
|
||||
strncpy(_config.env_note, doc["env_note"] | MQTT_DEFAULT_ENV, sizeof(_config.env_note) - 1);
|
||||
|
||||
Serial.println(F("[WiFi] ✅ 设备配置已加载"));
|
||||
Serial.printf(" 设备名: %s, 环境: %s\n", _config.device_id, _config.env_note);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiMgmt::_saveConfig() {
|
||||
JsonDocument doc;
|
||||
doc["mqtt_server"] = _config.mqtt_server;
|
||||
doc["mqtt_port"] = _config.mqtt_port;
|
||||
doc["mqtt_user"] = _config.mqtt_user;
|
||||
doc["mqtt_pass"] = _config.mqtt_pass;
|
||||
doc["device_id"] = _config.device_id;
|
||||
doc["env_note"] = _config.env_note;
|
||||
|
||||
File file = LittleFS.open(CONFIG_FILE_DEVICE, "w");
|
||||
if (!file) {
|
||||
Serial.println(F("[WiFi] ❌ 无法创建配置文件"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (serializeJson(doc, file) == 0) {
|
||||
Serial.println(F("[WiFi] ❌ 配置写入失败"));
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
file.close();
|
||||
Serial.println(F("[WiFi] ✅ 设备配置已保存"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// WiFiManager 保存参数回调
|
||||
// ============================================================
|
||||
|
||||
// ============================================================
|
||||
// 静态回调函数 (WiFiManager 要求 C 函数指针)
|
||||
// ============================================================
|
||||
|
||||
void WiFiMgmt::_apCallback(WiFiManager* wm) {
|
||||
Serial.println(F("[WiFi] 🔧 进入配网模式"));
|
||||
Serial.printf(" AP WiFi名称: %s\n", WIFI_AP_SSID);
|
||||
Serial.printf(" AP 密码: %s\n", WIFI_AP_PASSWORD);
|
||||
Serial.println(F(" 手机连接此 WiFi 即可打开配网页面 (自动弹出)"));
|
||||
}
|
||||
|
||||
void WiFiMgmt::_saveParamsCallback() {
|
||||
if (!_instance) return;
|
||||
|
||||
Serial.println(F("[WiFi] 💾 参数已保存"));
|
||||
|
||||
// 从 WiFiManager 参数中读取值
|
||||
strncpy(_instance->_config.mqtt_server, _instance->_param_mqtt_server->getValue(),
|
||||
sizeof(_instance->_config.mqtt_server) - 1);
|
||||
strncpy(_instance->_config.mqtt_port, _instance->_param_mqtt_port->getValue(),
|
||||
sizeof(_instance->_config.mqtt_port) - 1);
|
||||
strncpy(_instance->_config.mqtt_user, _instance->_param_mqtt_user->getValue(),
|
||||
sizeof(_instance->_config.mqtt_user) - 1);
|
||||
strncpy(_instance->_config.mqtt_pass, _instance->_param_mqtt_pass->getValue(),
|
||||
sizeof(_instance->_config.mqtt_pass) - 1);
|
||||
strncpy(_instance->_config.device_id, _instance->_param_device_id->getValue(),
|
||||
sizeof(_instance->_config.device_id) - 1);
|
||||
strncpy(_instance->_config.env_note, _instance->_param_env_note->getValue(),
|
||||
sizeof(_instance->_config.env_note) - 1);
|
||||
|
||||
_instance->_saveConfig();
|
||||
|
||||
Serial.println(F("[WiFi] 📋 新配置:"));
|
||||
Serial.printf(" MQTT: %s:%s\n",
|
||||
_instance->_config.mqtt_server,
|
||||
_instance->_config.mqtt_port);
|
||||
Serial.printf(" 设备: %s\n", _instance->_config.device_id);
|
||||
Serial.printf(" 环境: %s\n", _instance->_config.env_note);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Getter 方法
|
||||
// ============================================================
|
||||
|
||||
bool WiFiMgmt::isConnected() const {
|
||||
return _connected && (WiFi.status() == WL_CONNECTED);
|
||||
}
|
||||
|
||||
DeviceConfig WiFiMgmt::getConfig() const {
|
||||
return _config;
|
||||
}
|
||||
|
||||
const char* WiFiMgmt::getDeviceId() const {
|
||||
return _config.device_id;
|
||||
}
|
||||
|
||||
const char* WiFiMgmt::getEnvNote() const {
|
||||
return _config.env_note;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// LED 模式控制
|
||||
// ============================================================
|
||||
|
||||
void WiFiMgmt::_setLEDMode(uint8_t mode) {
|
||||
// mode 由 loop() 中的闪烁逻辑处理
|
||||
// 0=慢闪 1=快闪 2=常亮
|
||||
switch (mode) {
|
||||
case 0:
|
||||
case 1:
|
||||
// 闪烁由 loop 处理
|
||||
break;
|
||||
case 2:
|
||||
digitalWrite(LED_WIFI_PIN, HIGH);
|
||||
_ledState = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user