配网:指的是外部向WiFi模块提供SSID和密码,以便Wi-Fi模块可以连接指定的热点
常见的配网方式有:-键配网smart config、SoftAP配网、蓝牙配网、屏幕配网。
1.0 一键配网
2.0 蓝牙配网
一键配网的模式对应的厂加模式
3.0 状态机WIFI模组物联网
4.0 创建枚举结构体
typedef enum
{
// 正在处理AT指令
WIFI_COMM_WAIT,
// AT 指令接收成功
WIFI_COMM_OK,
// AT 指令接收失败
WIFI_COMM_FALL,
}WifiCommState_t;
5.0 AT指令任务句柄
static WifiCommState_t AtCmdHandle(char *cmd, char *rsp, uint32_t timeoutMs)
{
static WifiCommState_t s_commState = WIFI_COMM_OK;
static uint64_t s_sendCmdTime;
char *recvStrBuf;
if (s_commState != WIFI_COMM_WAIT)
{
if (cmd != NULL)
{
SendWifiModuleStr(cmd);
}
s_commState = WIFI_COMM_WAIT;
s_sendCmdTime = GetSysRunTime();
}
else
{
if ((GetSysRunTime() - s_sendCmdTime) < timeoutMs)
{
recvStrBuf = RecvWifiModuleStr();
if (strstr(recvStrBuf, rsp) != NULL)
{
s_commState = WIFI_COMM_OK;
ClearRecvWifiStr();
}
}
else
{
s_commState = WIFI_COMM_FAIL;
}
}
return s_commState;
}
注:这个函数的含义是,首先使用if 语句判断是否有命令在处理,在使用if 判断命令是否为空如果命令不为空的话,发送AT指令,使用延时等待函数,同时获取系统当前运行时间。否则获取当前系统时间减去第一次记录时间如果小于规定的时间,发送WIFI指令等...
6.0 AT指令结构体和指令集
typedef struct {
/* 要发送的AT命令 */
char *cmd;
/* 期望的应答数据,默认处理匹配到该字符串认为命令执行成功 */
char *rsp;
/* 得到应答的超时时间,达到超时时间为执行失败,单位ms*/
uint32_t timeoutMs;
} AtCmdInfo_t;
/*模组初始化命令集*/
static AtCmdInfo_t g_checkModuleCmdTable[] = {
{
.cmd = "AT+RST\r\n", // 软复位
.rsp = "ready",
.timeoutMs = 3000,
},
{
.cmd = NULL, // 只为等待
.rsp = "XXXXX",
.timeoutMs = 1000,
},
{
.cmd = "ATE0\r\n", // 关闭回显
.rsp = "OK",
.timeoutMs = 500,
},
{
.cmd = "AT+CWMODE=1\r\n",
.rsp = "OK",
.timeoutMs = 500,
},
};
7.0 枚举检查AT指令命令类型
typedef enum
{
// AT指令硬件复位
AT_RST,
// AT指令延时
AT_RST_DELAY,
// AT指令回显
AT_E0,
// AT指令模式
AT_CWMODE_1,
} AtCheckModuleCmdType;
8.0 检查WIFI模组工作
WifiCommState_t CheckWifiModuleWork(void)
{
WifiCommState_t commState;
static uint8_t retryCount = 0;
static AtCheckModuleCmdType cmdType = AT_RST;
switch (cmdType)
{
case AT_RST:
commState = AtCmdHandle(g_checkModuleCmdTable[AT_RST].cmd, g_checkModuleCmdTable[AT_RST].rsp,
g_checkModuleCmdTable[AT_RST].timeoutMs);
if (commState == WIFI_COMM_OK)
{
retryCount = 0;
cmdType = AT_RST_DELAY;
}
else if (commState == WIFI_COMM_FAIL)
{
retryCount++;
if (retryCount == 3)
{
retryCount = 0;
return WIFI_COMM_FAIL;
}
}
break;
case AT_RST_DELAY:
commState = AtCmdHandle(g_checkModuleCmdTable[AT_RST_DELAY].cmd, g_checkModuleCmdTable[AT_RST_DELAY].rsp,
g_checkModuleCmdTable[AT_RST_DELAY].timeoutMs);
if (commState == WIFI_COMM_OK)
{
cmdType = AT_E0;
}
else if (commState == WIFI_COMM_FAIL)
{
cmdType = AT_E0;
}
break;
case AT_E0:
commState = AtCmdHandle(g_checkModuleCmdTable[AT_E0].cmd, g_checkModuleCmdTable[AT_E0].rsp,
g_checkModuleCmdTable[AT_E0].timeoutMs);
if (commState == WIFI_COMM_OK)
{
retryCount = 0;
cmdType = AT_CWMODE_1;
}
else if (commState == WIFI_COMM_FAIL)
{
retryCount++;
if (retryCount == 3)
{
retryCount = 0;
return WIFI_COMM_FAIL;
}
}
break;
case AT_CWMODE_1:
commState = AtCmdHandle(g_checkModuleCmdTable[AT_CWMODE_1].cmd, g_checkModuleCmdTable[AT_CWMODE_1].rsp,
g_checkModuleCmdTable[AT_CWMODE_1].timeoutMs);
if (commState == WIFI_COMM_OK)
{
cmdType = AT_RST;
return WIFI_COMM_OK;
}
else if (commState == WIFI_COMM_FAIL)
{
return WIFI_COMM_FAIL;
}
break;
}
return WIFI_COMM_WAIT;
}
9.0 检查WIFI状态和WIFI信息
// 检查WIFI连接状态和连接信息
static AtCmdInfo_t g_checkConnectCmdTable[] =
{
{
.cmd = "AT+CWSTATE?\r\n",
.rsp = "CWSTATE:2",
.timeoutMs = 1000,
},
};
10.0 创建枚举类型
// 创建枚举类型
typedef enum
{
AT_CWSTATE = 0,
}AtcheckConnectCmdType;
11.0 检查WIFI连接
WifiCommState_t CheckWifiConnect(void)
{
// 结构体的参数包括WAITE,
WifiCommState_t commState;
static AtcheckConnectCmdType cmdType = AT_CWSTATE;
switch(cmdType)
{
case AT_CWSTATE:
commState = AtCmdHandle(g_checkConnectCmdTable[AT_CWSTATE].cmd, g_checkConnectCmdTable[AT_CWSTATE].rsp,
g_checkConnectCmdTable[AT_CWSTATE].timeoutMs);
if (commState == WIFI_COMM_OK)
{
cmdType = AT_CWSTATE;
return WIFI_COMM_OK;
}
else if (commState == WIFI_COMM_FALL)
{
return WIFI_COMM_FALL;
}
break;
}
return WIFI_COMM_WAIT;
}
注:检查WIFI是否连接成功,信息查询 ESP32-C2 设备的 Wi-Fi 状态和 Wi-Fi 信息
12.0 阿里云服务器AT指令信息
const static char g_mqttClientId[] = "k0tp7kBek08.board1|securemode=2\\,signmethod=hmacsha256\\,timestamp=1708571611329|";
/*
字符串里\\,第二个\是给at指令用的,第一个\是给编译器用的,不然识别不了第二个\
*/
const static char g_mqttUserName[] = "board1&k0tp7kBek08";
const static char g_mqttPwd[] = "4f777d27c5a22d090d53e47ae8f8b814d129ce64c1bf8bb07529948523e7bf16";
const static char g_mqttUrl[] = "iot-06z00c3kqsjgy5d.mqtt.iothub.aliyuncs.com";
static AtCmdInfo_t g_connectMqttCmdTable[] = {
{
.cmd = "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n",
.rsp = "OK",
.timeoutMs = 500,
},
{
.cmd = "AT+MQTTCONN=0,\"%s\",1883,1\r\n",
.rsp = "OK",
.timeoutMs = 2000,
},
};
注:以下的这条AT指令用于设置MQTT的用户属性具体参数和格式如下所示:
具体的AT指令如下所示:
AT+MQTTUSERCFG=<LinkID>,<scheme>,<"client_id">,<"username">,<"password">,<cert_key_ID>,<CA_ID>,<"path">
参数按照以下给出的格式进行填写:
-
LinkID>:当前仅支持 link ID 0。
-
<scheme>:
-
1: MQTT over TCP;
-
2: MQTT over TLS(不校验证书);
-
3: MQTT over TLS(校验 server 证书);
-
4: MQTT over TLS(提供 client 证书);
-
5: MQTT over TLS(校验 server 证书并且提供 client 证书);
-
6: MQTT over WebSocket(基于 TCP);
-
7: MQTT over WebSocket Secure(基于 TLS,不校验证书);
-
8: MQTT over WebSocket Secure(基于 TLS,校验 server 证书);
-
9: MQTT over WebSocket Secure(基于 TLS,提供 client 证书);
-
10: MQTT over WebSocket Secure(基于 TLS,校验 server 证书并且提供 client 证书)。
-
-
<client_id>:MQTT 客户端 ID,最大长度:256 字节。
-
<username>:用户名,用于登陆 MQTT broker,最大长度:64 字节。
-
<password>:密码,用于登陆 MQTT broker,最大长度:64 字节。
-
<cert_key_ID>:证书 ID,目前 ESP-AT 仅支持一套 cert 证书,参数为 0。
-
<CA_ID>:CA ID,目前 ESP-AT 仅支持一套 CA 证书,参数为 0。
-
<path>:资源路径,最大长度:32 字节。
13.0 创建枚举类型
注:内部的成员变量包括检查用户信息和连接MQTT服务器设备
typedef enum
{
AT_MQTTUSERCFG = 0,
AT_MQTTCONN,
} AtConnectMqttCmdType;
14.0 连接MQTT服务
WifiCommState_t ConnectMqttServer(void)
{
WifiCommState_t commState;
static uint8_t retryCount = 0;
static AtConnectMqttCmdType cmdType = AT_MQTTUSERCFG;
char cmdStrBuf[256] = {0};
switch (cmdType)
{
case AT_MQTTUSERCFG:
sprintf(cmdStrBuf, g_connectMqttCmdTable[AT_MQTTUSERCFG].cmd, g_mqttClientId, g_mqttUserName, g_mqttPwd);
commState = AtCmdHandle(cmdStrBuf, g_connectMqttCmdTable[AT_MQTTUSERCFG].rsp,
g_connectMqttCmdTable[AT_MQTTUSERCFG].timeoutMs);
if (commState == WIFI_COMM_OK)
{
cmdType = AT_MQTTCONN;
}
else if (commState == WIFI_COMM_FAIL)
{
return WIFI_COMM_FAIL;
}
break;
case AT_MQTTCONN:
sprintf(cmdStrBuf, g_connectMqttCmdTable[AT_MQTTCONN].cmd, g_mqttUrl);
commState = AtCmdHandle(cmdStrBuf, g_connectMqttCmdTable[AT_MQTTCONN].rsp,
g_connectMqttCmdTable[AT_MQTTCONN].timeoutMs);
if (commState == WIFI_COMM_OK)
{
retryCount = 0;
cmdType = AT_MQTTUSERCFG;
return WIFI_COMM_OK;
}
else if (commState == WIFI_COMM_FAIL)
{
retryCount++;
if (retryCount == 3)
{
cmdType = AT_MQTTUSERCFG;
retryCount = 0;
return WIFI_COMM_FAIL;
}
}
break;
default:
break;
}
return WIFI_COMM_WAIT;
}
15.0 发布MQTT消息
const static char g_mqttTopic[] = "/sys/k0tp7kBek08/board1/thing/event/property/post"; // board1对应deviceName,设备名称
static AtCmdInfo_t g_commMqttCmdTable[] = {
{
.cmd = "AT+MQTTPUB=0,\"%s\",\"{\\\"params\\\": {\\\"temp\\\": %.1f}}\",0,0\r\n",
/* 特别注意:转义字符\的数量
发送的AT命令:
AT+MQTTPUB=0,"/sys/k0tp7kBek08/board1/thing/event/property/post","{\"params\": {\"temp\": 10.5}}",0,0\r\n
*/
.rsp = "OK",
.timeoutMs = 500,
},
};
注:以下是AT模组参考资料说明
注:注意AT指令格式中 \ 的数量
.cmd = "AT+MQTTPUB=0,\"%s\",\"{\\\"params\\\": {\\\"temp\\\": %.1f}}\",0,0\r\n",
注意第一个\ 是给" 使用的第二个是给\使用的,第三个是给编译器使用的如果贸然删除其中的一个会导致指令报错,编译无法通过。
16.0 发布MQTT指令
typedef enum
{
AT_MQTTPUB_SENSOR = 0,
} AtCommMqttCmdType;
#define MQTT_PUB_PERIOD 3000UL // 上传数据周期,单位ms,不需要太频繁,减少服务器的负担
注:还有一个参数是MQTT指令的时间类型
17.0 传感器数据发布处理
WifiCommState_t CommMqttServer(void)
{
static WifiCommState_t commState = WIFI_COMM_OK;
static uint8_t retryCount = 0;
static AtCommMqttCmdType cmdType = AT_MQTTPUB_SENSOR;
static uint64_t lastSysTime = 0;
char cmdStrBuf[256] = {0};
SensorData_t sensorData;
switch (cmdType)
{
case AT_MQTTPUB_SENSOR:
if (commState != WIFI_COMM_WAIT)
{
if ((GetSysRunTime() - lastSysTime) < MQTT_PUB_PERIOD)
{
break;
}
else
{
lastSysTime = GetSysRunTime();
}
}
GetSensorData(&sensorData);
sprintf(cmdStrBuf, g_commMqttCmdTable[AT_MQTTPUB_SENSOR].cmd, g_mqttTopic, sensorData.temp);
commState = AtCmdHandle(cmdStrBuf, g_commMqttCmdTable[AT_MQTTPUB_SENSOR].rsp,
g_commMqttCmdTable[AT_MQTTPUB_SENSOR].timeoutMs);
if (commState == WIFI_COMM_OK)
{
retryCount = 0;
return WIFI_COMM_OK;
}
else if (commState == WIFI_COMM_FAIL)
{
retryCount++;
if (retryCount == 3)
{
cmdType = AT_MQTTPUB_SENSOR;
retryCount = 0;
return WIFI_COMM_FAIL;
}
}
break;
default:
break;
}
return WIFI_COMM_WAIT;
}
18.0 创建开启SmartConfig结构体数组
static AtCmdInfo_t g_smartCfgCmdTable[] = {
{
.cmd = "AT+CWSTARTSMART\r\n",
.rsp = "ssid", // 因为AT+CWSTARTSMART响应有多个包,驱动使用空闲中断,第一包数据包含热点账号密码
.timeoutMs = SMART_CONFIG_RECV_SSID_TIMEOUT,
},
{
.cmd = NULL, // 因为AT+CWSTARTSMART响应有多个包,驱动使用空闲中断,第三包数据包含"GOT IP",不发送具体命令,只为解析响应
.rsp = "GOT IP",
.timeoutMs = SMART_CONFIG_RECV_GOTIP_TIMEOUT,
},
{
.cmd = NULL, // 接收到第三包数据包含"GOT IP",等待一会再去发送STOP命令,不然手机侧提示失败,只为等待
.rsp = "XXXXX",
.timeoutMs = SMART_CONFIG_DELAY_TIMEOUT,
},
{
.cmd = "AT+CWSTOPSMART\r\n",
.rsp = "OK",
.timeoutMs = 300,
},
};
注:结构体数组的第一个参数是发送smartConfig命令,第二个参数是接收GOP IP也就是发送命令之后会返回几个数据包这个是其中的一个数据包,第三个参数没有具体的意义是用于实现一个非延时死等的延时方式,第四个参数是结束smartConfig命令的指令。
19.0 创建枚举类型
typedef enum
{
// 这个参数表示发送smartConfig
AT_CWSTARTSMART,
// 第二个参数表示返回值
AT_CWSTARTSMART_RECV,
// 第三个参数表示延时
AT_CWSTARTSMART_DELAY,
// 第四个参数表示停止
AT_CWSTOPSMART,
} AtSmartCfgCmdType;
20.0 创建smartConfig任务处理函数
static WifiCommState_t SmartConfigHandle(void)
{
WifiCommState_t commState;
static AtSmartCfgCmdType cmdType = AT_CWSTARTSMART;
char *recvStrBuf;
static uint8_t retryCount = 0;
switch (cmdType)
{
case AT_CWSTARTSMART:
commState = AtCmdHandle(g_smartCfgCmdTable[AT_CWSTARTSMART].cmd, g_smartCfgCmdTable[AT_CWSTARTSMART].rsp,
g_smartCfgCmdTable[AT_CWSTARTSMART].timeoutMs);
if (commState == WIFI_COMM_OK)
{
recvStrBuf = RecvWifiModuleStr();
cmdType = AT_CWSTARTSMART_RECV;
}
else if (commState == WIFI_COMM_FAIL)
{
cmdType = AT_CWSTARTSMART_DELAY;
}
break;
case AT_CWSTARTSMART_RECV:
commState = AtCmdHandle(g_smartCfgCmdTable[AT_CWSTARTSMART_RECV].cmd, g_smartCfgCmdTable[AT_CWSTARTSMART_RECV].rsp,
g_smartCfgCmdTable[AT_CWSTARTSMART_RECV].timeoutMs);
if (commState == WIFI_COMM_OK)
{
cmdType = AT_CWSTARTSMART_DELAY;
}
else if (commState == WIFI_COMM_FAIL)
{
cmdType = AT_CWSTARTSMART_DELAY;
}
break;
case AT_CWSTARTSMART_DELAY:
commState = AtCmdHandle(g_smartCfgCmdTable[AT_CWSTARTSMART_DELAY].cmd, g_smartCfgCmdTable[AT_CWSTARTSMART_DELAY].rsp,
g_smartCfgCmdTable[AT_CWSTARTSMART_DELAY].timeoutMs);
if (commState == WIFI_COMM_OK)
{
cmdType = AT_CWSTOPSMART;
}
else if (commState == WIFI_COMM_FAIL)
{
cmdType = AT_CWSTOPSMART;
}
break;
case AT_CWSTOPSMART:
commState = AtCmdHandle(g_smartCfgCmdTable[AT_CWSTOPSMART].cmd, g_smartCfgCmdTable[AT_CWSTOPSMART].rsp,
g_smartCfgCmdTable[AT_CWSTOPSMART].timeoutMs);
if (commState == WIFI_COMM_OK)
{
retryCount = 0;
cmdType = AT_CWSTARTSMART;
return WIFI_COMM_OK;
}
else if (commState == WIFI_COMM_FAIL)
{
retryCount++;
if (retryCount == 3)
{
cmdType = AT_CWSTARTSMART;
retryCount = 0;
return WIFI_COMM_FAIL;
}
}
break;
}
return WIFI_COMM_WAIT;
}
注:以上程序代码使用的也是状态机的方式
21.0 创建枚举类型表示WIFI连接的状态
static bool g_needSmartCfg = false;
void StartSmartCfgWifi(void)
{
g_needSmartCfg = true;
}
static WifiConnectState_t g_wifiConnectState = WIFI_CONNECT_NWK_ING;
WifiConnectState_t GetWifiConnectState(void)
{
return g_wifiConnectState;
}
typedef enum
{
CHECK_WIFI_MODULE,
CHECK_WIFI_CONNECT,
CONNECT_MQTT_SERVER,
COMM_MQTT_SERVER,
SMARTCONFIG_WIFI,
HWRESET_WIFI_MODULE,
WIWI_MODULE_ERROR,
} WifiWorkState_t;
22.0 WIFI网络任务函数
void WifiNetworkTask(void)
{
WifiCommState_t commState;
static WifiWorkState_t workState = CHECK_WIFI_MODULE;
static uint8_t hwresetCnt = 0;
switch (workState)
{
case CHECK_WIFI_MODULE:
g_wifiConnectState = WIFI_CONNECT_NWK_ING;
commState = CheckWifiModuleWork();
if (commState == WIFI_COMM_OK)
{
workState = CHECK_WIFI_CONNECT;
}
else if (commState == WIFI_COMM_FAIL)
{
workState = HWRESET_WIFI_MODULE;
}
break;
case CHECK_WIFI_CONNECT:
commState = CheckWifiConnect();
if (commState != WIFI_COMM_WAIT && g_needSmartCfg)
{
workState = SMARTCONFIG_WIFI;
break;
}
if (commState == WIFI_COMM_OK)
{
workState = CONNECT_MQTT_SERVER;
}
else if (commState == WIFI_COMM_FAIL)
{
workState = CHECK_WIFI_CONNECT;
}
break;
case CONNECT_MQTT_SERVER:
commState = ConnectMqttServer();
if (commState == WIFI_COMM_OK)
{
workState = COMM_MQTT_SERVER;
}
else if (commState == WIFI_COMM_FAIL)
{
workState = CHECK_WIFI_MODULE;
}
break;
case COMM_MQTT_SERVER:
commState = CommMqttServer();
if (commState != WIFI_COMM_WAIT && g_needSmartCfg)
{
workState = SMARTCONFIG_WIFI;
break;
}
if (commState == WIFI_COMM_OK)
{
g_wifiConnectState = WIFI_CONNECT_MQTT_SUCESS;
workState = COMM_MQTT_SERVER;
}
else if (commState == WIFI_COMM_FAIL)
{
g_wifiConnectState = WIFI_CONNECT_MQTT_FAIL;
workState = CHECK_WIFI_MODULE;
}
break;
case SMARTCONFIG_WIFI:
g_needSmartCfg = false;
g_wifiConnectState = WIFI_SMART_CONFIGING;
commState = SmartConfigHandle();
if (commState == WIFI_COMM_OK)
{
workState = CONNECT_MQTT_SERVER;
}
else if (commState == WIFI_COMM_FAIL)
{
workState = CHECK_WIFI_MODULE;
}
break;
case HWRESET_WIFI_MODULE:
if (hwresetCnt < 1) // 如果AT命令不通,硬件复位1次
{
HwresetWifiModule();
DelayNms(1000);
workState = CHECK_WIFI_MODULE;
hwresetCnt++;
}
else
{
printf("wifi module error!\n");
workState = WIWI_MODULE_ERROR; // 如果硬件复位1次,AT命令还是不通,就不再执行WIFI任务的业务逻辑,直接退出,避免影响其他任务
}
break;
default:
break;
}
}
注:以上程序使用的也是状态机的思想
23.0 头文件
#ifndef _WIFI_APP_H_
#define _WIFI_APP_H_
#define SMART_CONFIG_RECV_SSID_TIMEOUT 30000
#define SMART_CONFIG_RECV_GOTIP_TIMEOUT 5000
#define SMART_CONFIG_DELAY_TIMEOUT 5000
#define SMART_CONFIG_ENTIRE_TIMEOUT (SMART_CONFIG_RECV_SSID_TIMEOUT + SMART_CONFIG_RECV_GOTIP_TIMEOUT + SMART_CONFIG_DELAY_TIMEOUT + 5)
typedef enum
{
WIFI_CONNECT_NWK_ING,
WIFI_CONNECT_MQTT_FAIL,
WIFI_CONNECT_MQTT_SUCESS,
WIFI_SMART_CONFIGING,
} WifiConnectState_t;
void WifiNetworkTask(void);
void StartSmartCfgWifi(void);
WifiConnectState_t GetWifiConnectState(void);
#endif
...