目录
mqttConnectedCb--------MQTT_Subscribe
mqttConnectedCb-----MQTT_Publish
首先先注意下这个结构体变量
// 全局变量
//============================================================================
MQTT_Client mqttClient; // MQTT客户端_结构体【此变量非常重要】
他是8266之后将要使用的TCP连接,MQTT连接,MQTT收发报文等等所有参数集合,需要特别留意一下
// MQTT客户端
//-----------------------------------------------------------------------------
typedef struct
{
struct espconn *pCon; // TCP连接结构体指针
uint8_t security; // 安全类型
uint8_t* host; // 服务端域名/地址
uint32_t port; // 网络连接端口号
ip_addr_t ip; // 32位IP地址
mqtt_state_t mqtt_state; // MQTT状态
mqtt_connect_info_t connect_info; // MQTT【CONNECT】报文的连接参数
MqttCallback connectedCb; // MQTT连接成功_回调
MqttCallback disconnectedCb; // MQTT断开连接_回调
MqttCallback publishedCb; // MQTT发布成功_回调
MqttCallback timeoutCb; // MQTT超时_回调
MqttDataCallback dataCb; // MQTT接收数据_回调
ETSTimer mqttTimer; // MQTT定时器
uint32_t keepAliveTick; // MQTT客户端(ESP8266)心跳计数
uint32_t reconnectTick; // 重连等待计时
uint32_t sendTimeout; // 报文发送超时时间
tConnState connState; // ESP8266运行状态
QUEUE msgQueue; // 消息队列
void* user_data; // 用户数据(预留给用户的指针)
} MQTT_Client;
user-init
void user_init(void)
{
uart_init(BIT_RATE_115200, BIT_RATE_115200); // 串口波特率设为115200
os_delay_us(60000);
//【技小新】添加
//###########################################################################
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4); // GPIO4输出高 #
GPIO_OUTPUT_SET(GPIO_ID_PIN(4),1); // LED初始化 #
//###########################################################################
CFG_Load(); // 加载/更新系统参数【WIFI参数、MQTT参数】
// 网络连接参数赋值:服务端域名【mqtt_test_jx.mqtt.iot.gz.baidubce.com】、网络连接端口【1883】、安全类型【0:NO_TLS】
//-------------------------------------------------------------------------------------------------------------------
MQTT_InitConnection(&mqttClient, sysCfg.mqtt_host, sysCfg.mqtt_port, sysCfg.security);
// MQTT连接参数赋值:客户端标识符【..】、MQTT用户名【..】、MQTT密钥【..】、保持连接时长【120s】、清除会话【1:clean_session】
//----------------------------------------------------------------------------------------------------------------------------
MQTT_InitClient(&mqttClient, sysCfg.device_id, sysCfg.mqtt_user, sysCfg.mqtt_pass, sysCfg.mqtt_keepalive, 1);
// 设置遗嘱参数(如果云端没有对应的遗嘱主题,则MQTT连接会被拒绝)
//--------------------------------------------------------------
// MQTT_InitLWT(&mqttClient, "Will", "ESP8266_offline", 0, 0);
// 设置MQTT相关函数
//--------------------------------------------------------------------------------------------------
MQTT_OnConnected(&mqttClient, mqttConnectedCb); // 设置【MQTT成功连接】函数的另一种调用方式
MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); // 设置【MQTT成功断开】函数的另一种调用方式
MQTT_OnPublished(&mqttClient, mqttPublishedCb); // 设置【MQTT成功发布】函数的另一种调用方式
MQTT_OnData(&mqttClient, mqttDataCb); // 设置【接收MQTT数据】函数的另一种调用方式
// 连接WIFI:SSID[..]、PASSWORD[..]、WIFI连接成功函数[wifiConnectCb]
//--------------------------------------------------------------------------
WIFI_Connect(sysCfg.sta_ssid, sysCfg.sta_pwd, wifiConnectCb);
INFO("\r\nSystem started ...\r\n");
}
在user_init函数中首先执行串口初始化,之后调用这个函数 CFG_Load();来加载/更新系统参数
CFG_Load
// 加载/更新系统参数【WIFI参数、MQTT参数】(由持有人标识决定)
//===================================================================================================================================
void ICACHE_FLASH_ATTR CFG_Load()
{
INFO("\r\nload ...\r\n");
// 读Flash【0x7C】扇区,存放到【saveFlag】(读出之前的持有人标识)
//----------------------------------------------------------------------------------------------
spi_flash_read((CFG_LOCATION+3)*SPI_FLASH_SEC_SIZE,(uint32 *)&saveFlag, sizeof(SAVE_FLAG));
//根据【参数扇区标志】,读取对应扇区的系统参数【0:系统参数在0x79扇区 !0:系统参数在0x7A扇区】
//-------------------------------------------------------------------------------------------------------------------------
if (saveFlag.flag == 0)
{
spi_flash_read((CFG_LOCATION+0)*SPI_FLASH_SEC_SIZE, (uint32 *)&sysCfg, sizeof(SYSCFG)); // 读出系统参数(1区:0x79)
}
else //saveFlag.flag != 0
{
spi_flash_read((CFG_LOCATION+1)*SPI_FLASH_SEC_SIZE, (uint32 *)&sysCfg, sizeof(SYSCFG)); // 读出系统参数(2区:0x7A)
}
// 只有在【持有人标识和之前不同】的情况下,才会更新系统参数(修改系统参数时,一定要记得修改持有人标识的值)
//------------------------------------------------------------------------------------------------------------------------
if(sysCfg.cfg_holder != CFG_HOLDER) // 持有人标识不同
{
os_memset(&sysCfg, 0x00, sizeof sysCfg); // 参数扇区=0
sysCfg.cfg_holder = CFG_HOLDER; // 更新持有人标识
os_sprintf(sysCfg.device_id, MQTT_CLIENT_ID, system_get_chip_id()); // 【MQTT_CLIENT_ID】MQTT客户端标识符
sysCfg.device_id[sizeof(sysCfg.device_id) - 1] = '\0'; // 最后添'\0'(防止字符串填满数组,指针溢出)
os_strncpy(sysCfg.sta_ssid, STA_SSID, sizeof(sysCfg.sta_ssid)-1); // 【STA_SSID】WIFI名称
os_strncpy(sysCfg.sta_pwd, STA_PASS, sizeof(sysCfg.sta_pwd)-1); // 【STA_PASS】WIFI密码
sysCfg.sta_type = STA_TYPE; // 【STA_TYPE】WIFI类型
os_strncpy(sysCfg.mqtt_host, MQTT_HOST, sizeof(sysCfg.mqtt_host)-1); // 【MQTT_HOST】MQTT服务端域名/IP地址
sysCfg.mqtt_port = MQTT_PORT; // 【MQTT_PORT】网络连接端口号
os_strncpy(sysCfg.mqtt_user, MQTT_USER, sizeof(sysCfg.mqtt_user)-1); // 【MQTT_USER】MQTT用户名
os_strncpy(sysCfg.mqtt_pass, MQTT_PASS, sizeof(sysCfg.mqtt_pass)-1); // 【MQTT_PASS】MQTT密码
sysCfg.security = DEFAULT_SECURITY; /* default non ssl */ // 【DEFAULT_SECURITY】默认安全等级(默认=0,不加密)
sysCfg.mqtt_keepalive = MQTT_KEEPALIVE; // 【MQTT_KEEPALIVE】保持连接时长(宏定义==120)
INFO(" default configuration\r\n");
CFG_Save(); // 将更新后的系统参数烧录到Flash中
}
}
这也就是为什么我们之前在设置MQTT连接WIFI相关参数的时候,需要把持有人标志更新一下
// 只有在【持有人标识和之前不同】的情况下,才会更新系统参数(修改系统参数时,一定要记得修改持有人标识的值)
//------------------------------------------------------------------------------------------------------------------------
if(sysCfg.cfg_holder != CFG_HOLDER) // 持有人标识不同
#define CFG_HOLDER 0x66666663 // 持有人标识(只有更新此数值,系统参数才会更新) /* Change this value to load default configurations */
如果觉得麻烦的话,可以把if(sysCfg.cfg_holder != CFG_HOLDER) 这句话注释掉,那么8266每次上电执行的时候,都会将参数更新到flash中
MQTT_InitConnection
接下来调用MQTT_InitConnection这个函数来将网络连接参数赋值
// 网络连接参数赋值:服务端域名【mqtt_test_jx.mqtt.iot.gz.baidubce.com】、网络连接端口【1883】、安全类型【0:NO_TLS】
//====================================================================================================================
void ICACHE_FLASH_ATTR MQTT_InitConnection(MQTT_Client *mqttClient, uint8_t* host, uint32_t port, uint8_t security)
{
uint32_t temp;
INFO("MQTT_InitConnection\r\n");
os_memset(mqttClient, 0, sizeof(MQTT_Client)); // 【MQTT客户端】结构体 = 0
temp = os_strlen(host); // 服务端域名/IP的字符串长度
mqttClient->host = (uint8_t*)os_zalloc(temp+1); // 申请空间,存放服务端域名/IP地址字符串
os_strcpy(mqttClient->host, host); // 字符串拷贝
mqttClient->host[temp] = 0; // 最后'\0'
mqttClient->port = port; // 网络端口号 = 1883
mqttClient->security = security; // 安全类型 = 0 = NO_TLS
}
MQTT_InitClient
之后调用MQTT_InitClient这个函数来将MQTT连接参数赋值
// MQTT连接参数赋值:客户端标识符【..】、MQTT用户名【..】、MQTT密钥【..】、保持连接时长【120s】、清除会话【1:clean_session】
//======================================================================================================================================================
void ICACHE_FLASH_ATTR
MQTT_InitClient(MQTT_Client *mqttClient, uint8_t* client_id, uint8_t* client_user, uint8_t* client_pass, uint32_t keepAliveTime, uint8_t cleanSession)
{
uint32_t temp;
INFO("MQTT_InitClient\r\n");
// MQTT【CONNECT】报文的连接参数 赋值
//---------------------------------------------------------------------------------------------------------------
os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); // MQTT【CONNECT】报文的连接参数 = 0
temp = os_strlen(client_id);
mqttClient->connect_info.client_id = (uint8_t*)os_zalloc(temp + 1); // 申请【客户端标识符】的存放内存
os_strcpy(mqttClient->connect_info.client_id, client_id); // 赋值【客户端标识符】
mqttClient->connect_info.client_id[temp] = 0; // 最后'\0'
if (client_user) // 判断是否有【MQTT用户名】
{
temp = os_strlen(client_user);
mqttClient->connect_info.username = (uint8_t*)os_zalloc(temp + 1);
os_strcpy(mqttClient->connect_info.username, client_user); // 赋值【MQTT用户名】
mqttClient->connect_info.username[temp] = 0;
}
if (client_pass) // 判断是否有【MQTT密码】
{
temp = os_strlen(client_pass);
mqttClient->connect_info.password = (uint8_t*)os_zalloc(temp + 1);
os_strcpy(mqttClient->connect_info.password, client_pass); // 赋值【MQTT密码】
mqttClient->connect_info.password[temp] = 0;
}
mqttClient->connect_info.keepalive = keepAliveTime; // 保持连接 = 120s
mqttClient->connect_info.clean_session = cleanSession; // 清除会话 = 1 = clean_session
//--------------------------------------------------------------------------------------------------------------
// 设置mqtt_state部分参数
//------------------------------------------------------------------------------------------------------------------------------------------------------------
mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); // 申请in_buffer内存【入站报文缓存区】
mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; // 设置in_buffer大小
mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); // 申请out_buffer内存【出站报文缓存区】
mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; // 设置out_buffer大小
mqttClient->mqtt_state.connect_info = &(mqttClient->connect_info); // MQTT【CONNECT】报文的连接参数(指针),赋值给mqttClient->mqtt_state.connect_info
// 初始化MQTT出站报文缓存区
//----------------------------------------------------------------------------------------------------------------------------------
mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length);
QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); // 消息队列初始化【队列可以存放一个/多个MQTT报文】
// 创建任务:任务函数【MQTT_Task】、优先级【2】、任务指针【mqtt_procTaskQueue】、消息深度【1】
//---------------------------------------------------------------------------------------------
system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE);
// 安排任务:参数1=任务等级 / 参数2=消息类型 / 参数3=消息参数
//-----------------------------------------------------------------------------------------------
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); // 参数3的类型必须为【os_param_t】型
}
创建了一个任务,MQTT例程就是基于任务来处理不同状态下所要做的操作
// 创建任务:任务函数【MQTT_Task】、优先级【2】、任务指针【mqtt_procTaskQueue】、消息深度【1】
//---------------------------------------------------------------------------------------------
system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE);
MQTT_InitLWT
设置遗嘱参数(如果云端没有对应的遗嘱主题,则MQTT连接会被拒绝)-------这里不用遗嘱
// 设置遗嘱:遗嘱主题【...】、遗嘱消息【...】、遗嘱质量【Will_Qos=0】、遗嘱保持【Will_Retain=0】
//====================================================================================================================
void ICACHE_FLASH_ATTR
MQTT_InitLWT(MQTT_Client *mqttClient, uint8_t* will_topic, uint8_t* will_msg, uint8_t will_qos, uint8_t will_retain)
{
uint32_t temp;
temp = os_strlen(will_topic);
mqttClient->connect_info.will_topic = (uint8_t*)os_zalloc(temp + 1); // 申请【遗嘱主题】的存放内存
os_strcpy(mqttClient->connect_info.will_topic, will_topic); // 赋值【遗嘱主题】
mqttClient->connect_info.will_topic[temp] = 0; // 最后'\0'
temp = os_strlen(will_msg);
mqttClient->connect_info.will_message = (uint8_t*)os_zalloc(temp + 1);
os_strcpy(mqttClient->connect_info.will_message, will_msg); // 赋值【遗嘱消息】
mqttClient->connect_info.will_message[temp] = 0;
mqttClient->connect_info.will_qos = will_qos; // 遗嘱质量【Will_Qos=0】
mqttClient->connect_info.will_retain = will_retain; // 遗嘱保持【Will_Retain=0】
}
函数的另一种调用方式
MQTT_OnConnected(&mqttClient, mqttConnectedCb); // 设置【MQTT成功连接】函数的另一种调用方式
// 函数调用重定义
//………………………………………………………………………………………………………………………………………
// 执行 mqttClient->connectedCb(...) => mqttConnectedCb(...)
//------------------------------------------------------------------------------------------
void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client*mqttClient, MqttCallback connectedCb)
{
mqttClient->connectedCb = connectedCb; // 函数名【mqttConnectedCb】
}
执行 mqttClient->connectedCb(...) => mqttConnectedCb(...)这条语句就相当于调用了mqttConnectedCb这个函数
// MQTT已成功连接:ESP8266发送【CONNECT】,并接收到【CONNACK】
//============================================================================
void mqttConnectedCb(uint32_t *args)
{
MQTT_Client* client = (MQTT_Client*)args; // 获取mqttClient指针
INFO("MQTT: Connected\r\n");
// 【参数2:主题过滤器 / 参数3:订阅Qos】
//-----------------------------------------------------------------
MQTT_Subscribe(client, "SW_LED", 0); // 订阅主题"SW_LED",QoS=0
// MQTT_Subscribe(client, "SW_LED", 1);
// MQTT_Subscribe(client, "SW_LED", 2);
// 【参数2:主题名 / 参数3:发布消息的有效载荷 / 参数4:有效载荷长度 / 参数5:发布Qos / 参数6:Retain】
//-----------------------------------------------------------------------------------------------------------------------------------------
MQTT_Publish(client, "SW_LED", "ESP8266_Online", strlen("ESP8266_Online"), 0, 0); // 向主题"SW_LED"发布"ESP8266_Online",Qos=0、retain=0
// MQTT_Publish(client, "SW_LED", "ESP8266_Online", strlen("ESP8266_Online"), 1, 0);
// MQTT_Publish(client, "SW_LED", "ESP8266_Online", strlen("ESP8266_Online"), 2, 0);
}
WIFI_Connect
设置WiFi连接的参数
// 连接WIFI:SSID【...】、PASSWORD【...】、WIFI连接成功回调函数【wifiConnectCb】
//====================================================================================================================
void ICACHE_FLASH_ATTR WIFI_Connect(uint8_t* ssid, uint8_t* pass, WifiCallback cb)
{
struct station_config stationConf;
INFO("WIFI_INIT\r\n");
wifi_set_opmode_current(STATION_MODE); // 设置ESP8266为STA模式
//wifi_station_set_auto_connect(FALSE); // 上电不自动连接已记录的AP(已注释,即:上电自动连接已记录的AP(默认))
//--------------------------------------------------------------------------------------
wifiCb = cb; // 函数名赋值:wifiCb可以作为函数名使用,wifiCb(..) == wifiConnectCb(..)
// 设置STA参数
//--------------------------------------------------------------------------
os_memset(&stationConf, 0, sizeof(struct station_config)); // STA信息 = 0
os_sprintf(stationConf.ssid, "%s", ssid); // SSID赋值
os_sprintf(stationConf.password, "%s", pass); // 密码赋值
wifi_station_set_config_current(&stationConf); // 设置STA参数
// 设置WiFiLinker定时器
//-------------------------------------------------------------------------------------------------------
os_timer_disarm(&WiFiLinker); // 定时器:WIFI连接
os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); // wifi_check_ip:检查IP获取情况
os_timer_arm(&WiFiLinker, 1000, 0); // 1秒定时(1次)
//wifi_station_set_auto_connect(TRUE); // 上电自动连接已记录AP
wifi_station_connect(); // ESP8266接入WIFI
}
wifi_check_ip
在这里他定时检查ip地址的获取情况,如果没有成功获取到ip地址,他会再次启用定时器,如果WIFI状态改变,则调用[wifiConnectCb]函数
// 定时函数:检查IP获取情况
//==============================================================================
static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg)
{
struct ip_info ipConfig;
os_timer_disarm(&WiFiLinker); // 关闭WiFiLinker定时器
wifi_get_ip_info(STATION_IF, &ipConfig); // 获取IP地址
wifiStatus = wifi_station_get_connect_status(); // 获取接入状态
// 获取到IP地址
//-------------------------------------------------------------------------
if (wifiStatus == STATION_GOT_IP && ipConfig.ip.addr != 0)
{
// 获取IP后,每2秒检查一次WIFI连接的正确性【防止WIFI掉线等情况】
//------------------------------------------------------------------
os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL);
os_timer_arm(&WiFiLinker, 2000, 0);
}
//-------------------------------------------------------------------------
// 未获取到IP地址
//-------------------------------------------------------------------------
else
{
if(wifi_station_get_connect_status() == STATION_WRONG_PASSWORD)
{
INFO("STATION_WRONG_PASSWORD\r\n"); // 密码错误
wifi_station_connect();
}
else if(wifi_station_get_connect_status() == STATION_NO_AP_FOUND)
{
INFO("STATION_NO_AP_FOUND\r\n"); // 未发现对应AP
wifi_station_connect();
}
else if(wifi_station_get_connect_status() == STATION_CONNECT_FAIL)
{
INFO("STATION_CONNECT_FAIL\r\n"); // 连接失败
wifi_station_connect();
}
else
{
INFO("STATION_IDLE\r\n");
}
// 再次开启定时器
//------------------------------------------------------------------
os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL);
os_timer_arm(&WiFiLinker, 500, 0); // 500Ms定时
}
//-------------------------------------------------------------------------
// 如果WIFI状态改变,则调用[wifiConnectCb]函数
//-------------------------------------------------------------------------
if(wifiStatus != lastWifiStatus)
{
lastWifiStatus = wifiStatus; // WIFI状态更新
if(wifiCb) // 判断是否设置了[wifiConnectCb]函数
wifiCb(wifiStatus); // wifiCb(wifiStatus)=wifiConnectCb(wifiStatus)
}
}
wifiConnectCb
在这里先判断是否获取到ip地址,如果成功获取的话,进行SNTP初始化,并设置SNTP定时器
// WIFI连接状态改变:参数 =