基于Chappie-II的二次开发日志-3
期末和考研一堆事情,开发日志虽然很久没写了但是代码是一直有在更新的。接着上次的工作,为WiFi连接新增了保存上次WiFi的功能。
本文主要包括:
- 使用nvs存储WiFi信息
- 修正WIFI连接逻辑
使用NVS存储WiFi信息
参考ESP32存储配网信息+LED显示配网状态+按键清除配网信息(附源码) - 汉塘阿德 - 博客园。
NVS 是 ESP32 提供的一个持久化存储机制,用于存储小量的配置和状态数据。使用 NVS 需要先进行初始化,打开存储空间,然后进行读写操作,最后提交更改并关闭存储。
因此向ChappieUI.cpp中添加代码,使得NVS能随之初始化。
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// 如果 NVS 空间不足或存在新版本的 NVS 数据,需要格式化 NVS
ESP_ERROR_CHECK(nvs_flash_erase()); // 格式化 NVS
err = nvs_flash_init(); // 再次初始化
}
并编写读/写nvs和标志位重置的函数。
首先添加部分前置条件
//是否配置的标志位
typedef enum{
wifi_unconfiged = 0,
wifi_configed = 0xAA,
}wifi_info_str_t;
//esp32的wifi配置结构体
wifi_config_t wifi_config;
//其结构体中前32+64是ssid和密码
#define ID_AND_PWD_LEN (32+64)
读取
static esp_err_t readWifiConfig(wifi_config_t *sta_config)
{
nvs_handle nvs;
unsigned char u8WifiConfigVal;
// 0.打开
nvs_open("WIFI_CONFIG", NVS_READWRITE, &nvs);
// 1.读取标志位,并判断
nvs_get_u8(nvs, "WifiConfigFlag", &u8WifiConfigVal);
if(u8WifiConfigVal != wifi_configed){
// 1.1 没有配过网,关闭nvs,返回错误码
ESP_LOGE("WIFI", "no wifi config,read fail!");
nvs_close(nvs);
return ESP_FAIL;
}else{
// 1.2 进入下个步骤
ESP_LOGI("WIFI", "wifi configed,read ok!");
}
// 2.读取上一次配网的ID,password
uint32_t len = ID_AND_PWD_LEN;
esp_err_t err = nvs_get_blob(nvs, "wifi_config", sta_config, &len);
ESP_LOGI("WIFI", "readout SSID:%s", sta_config->sta.ssid);
ESP_LOGI("WIFI", "readout PASSWORD:%s", sta_config->sta.password);
// 3.关闭nvs退出
nvs_close(nvs);
return err;
}
写入
/**
* @brief 保存wifi配置参数结构体变量wifi_config到nvs
* @param wifi_config wifi配置参数
*/
static void saveWifiConfig(wifi_config_t *wifi_config)
{
nvs_handle nvs;
// 0.打开
nvs_open("WIFI_CONFIG", NVS_READWRITE, &nvs);
// 1.写入标记 0xaa,表示已经配过网
nvs_set_u8(nvs, "WifiConfigFlag", wifi_configed);
// 2.写入AP ID和AP password
ESP_ERROR_CHECK(nvs_set_blob(nvs, "wifi_config", wifi_config, ID_AND_PWD_LEN));
// 3.提交 并保存表的内容
ESP_ERROR_CHECK(nvs_commit(nvs));
// 4.关闭nvs退出
nvs_close(nvs);
}
标志位重置
void clearWiFiConfigFlag(void){
nvs_handle nvs;
// 0.打开
nvs_open("WIFI_CONFIG", NVS_READWRITE, &nvs);
// 1.写入标记 0x00,清除配网标记
nvs_set_u8(nvs, "WifiConfigFlag", wifi_unconfiged);
// 2.提交 并保存表的内容
ESP_ERROR_CHECK(nvs_commit(nvs));
// 3.关闭nvs退出
nvs_close(nvs);
}
这样,当我们通过ESP-TOUCH配网以后,就能读出wifi的ssid和密码,并写入nvs,下一次启动wifi连接时,自动尝试对应的wifi能否成功连接,若不能连接则等待手动配网。
修正WIFI连接逻辑
先前编写的WiFi连接在后续的测试里不仅简陋而且各种BUG,干脆直接配合-1屏的WiFi按钮和nvs功能重新整理。
首先为WiFi、蓝牙以及时间更新添加标志位。
/* Structure to hold device status */
struct DeviceStatus_t {
bool updated = false;
bool autoScreenOff = false;
bool WifiOn = false;
bool timeupdated = false;
bool BleOn = false;
uint8_t brightness = 127;
uint32_t autoScreenOffTime = 20000;
};
static DeviceStatus_t _device_status;
然后找到void App_Launcher::panel_control_pad_event_cb(lv_event_t * e)
在其中添加对应的事件响应
/* If enable wifi */
else if (obj == ui_ButtonWifi) {
if (lv_obj_get_state(obj) == (LV_STATE_CHECKED | LV_STATE_FOCUSED)) {
_device_status.WifiOn = true;
}
else {
_device_status.WifiOn = false;
WiFi.disconnect();
ESP_LOGI("WIFI","WiFi Disconnect");
}
}
/* If enable ble */
else if (obj == ui_ButtonBle) {
if (lv_obj_get_state(obj) == (LV_STATE_CHECKED | LV_STATE_FOCUSED)) {
_device_status.BleOn = true;
}
else {
_device_status.BleOn = false;
}
}
为保证UI创建时,wifi按钮能处于默认状态,并在wifi启动后,-1屏刷新的情况下wifi按钮能保持被按下的状态。
首先在void App_Launcher::onCreate()中添加代码
if (_device_status.WifiOn) {
lv_obj_add_state(ui_ButtonWifi, (LV_STATE_CHECKED | LV_STATE_FOCUSED));
}
if (_device_status.timeupdated) {
_device_status.timeupdated = false;
}
if (_device_status.BleOn) {
lv_obj_add_state(ui_ButtonBle, (LV_STATE_CHECKED | LV_STATE_FOCUSED));
}
然后在void App_Launcher::updateDeviceStatus()中添加
/**
* @brief wifi按钮按下启动配网
*/
if (_device_status.WifiOn && !_device_status.timeupdated && (WiFi.status() != WL_CONNECTED)){
WiFi_config();
_device_status.timeupdated = true;
if(WiFi.status() != WL_CONNECTED){
WiFi.disconnect();
_device_status.WifiOn = false;
_device_status.timeupdated = false;
}
}
这其中的WiFi_config()只是通过xTaskCreatePinnedToCore(task_WiFiConnect, "TaskWiFiConnect", 6*1024, NULL, 1, NULL, 0);创建task,真正的功能代码集中在static void task_WiFiConnect(void *xTask1)中,代码本身阅读性还可以,不用多做说明。
static void task_WiFiConnect(void *xTask1){
if(readWifiConfig(&wifi_config) == ESP_OK){
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
// 1.已经配过网,直接连AP
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_LOGI("WIFI","WiFi init.");
/* 设置WiFi的工作模式为 STA */
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_LOGI("WIFI","Mode: STA");
/* 设置WiFi连接的参数,主要是ssid和password */
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_LOGI("WIFI","Set config");
/* 启动WIFI 驱动程序*/
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI("WIFI","WiFi Start");
/* 启动WiFi连接到 AP*/
//ESP_ERROR_CHECK(esp_wifi_connect());
while(esp_wifi_connect()!=ESP_OK){ vTaskDelay(200); }
vTaskDelay(100);
ESP_LOGD("WIFI_CFG","SSID: %s PWD: %s",wifi_config.sta.ssid,wifi_config.sta.password);
ESP_LOGI("WIFI","Status: Connected IP: %s",WiFi.localIP().toString().c_str());
ESP_LOGD("WIFI","SSID: %s PWD: %s",WiFi.SSID(),WiFi.psk());
vTaskDelete(NULL);
}
else{
WiFi.mode(WIFI_STA);
ESP_LOGI("WIFI", "WiFi mode : STA");
ESP_LOGI("WIFI", "Try to connect");
WiFi.begin();
WiFi.beginSmartConfig();
clearWiFiConfigFlag();
//ESP_ERROR_CHECK(WiFi.begin());
//ESP_ERROR_CHECK(WiFi.beginSmartConfig());
ESP_LOGI("WIFI", "Waiting for SmartConfig...");
//UI_LOG("[WiFi] Waiting for SmartConfig...\n");
while (!WiFi.smartConfigDone()) { vTaskDelay(200); }
ESP_LOGI("WIFI","SmartConfig received, connecting WiFi...");
//UI_LOG("[WiFi] SmartConfig received, connecting WiFi...\n");
while (WiFi.status() != WL_CONNECTED) { vTaskDelay(200); }
if(WiFi.status() == WL_CONNECTED){
memset(&wifi_config,0,sizeof(wifi_config));
memcpy(wifi_config.sta.ssid,WiFi.SSID().c_str(),sizeof(wifi_config.sta.ssid));
memcpy(wifi_config.sta.password,WiFi.psk().c_str(),sizeof(wifi_config.sta.password));
ESP_LOGD("WIFI_CFG","SSID: %s PWD: %s",wifi_config.sta.ssid,wifi_config.sta.password);
ESP_LOGD("WIFI","SSID: %s PWD: %s",WiFi.SSID(),WiFi.psk());
saveWifiConfig(&wifi_config);
}
ESP_LOGI("WIFI","Status: Connected IP: %s",WiFi.localIP().toString().c_str());
/*补上存配置的代码*/
ESP_LOGD("WIFI","Task close.");
//UI_LOG("[WiFi] Connected. IP: %s\n", WiFi.localIP().toString().c_str());
vTaskDelete(NULL);
}
}
这样就实现了按下-1屏的WiFi按钮后启动WiFi连接,并且能保存最新一次WiFi信息的功能。
总结
在写文章的时候发现好像还有bug,有空了可能需要重新修正。日志中的代码并不一定是最新的,但是Github上一般是我测试过最新的代码大概也有BUG。
我正在大量将原代码中的UI_LOG替换为ESP_LOG,开发和测试时能看到更多的信息。在写这篇文章的时候碰撞检测的功能已经开发的差不多了,就是界面还很丑,还在调整参数来确保检测精度,等差不多了会继续更新日志的。

浙公网安备 33010602011771号