2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 手机控制的esp8266利用mqtt协议接入百度云智能插座

手机控制的esp8266利用mqtt协议接入百度云智能插座

时间:2019-10-04 07:20:14

相关推荐

手机控制的esp8266利用mqtt协议接入百度云智能插座

手机控制的esp8266利用mqtt协议接入百度云智能插座

的春节,相信大家和我一样都待在家里,利用在家的时间现学现卖,制作了一款手机控制的智能插座,网上资料很多,我在查询资料中发现,esp8266接入百度云的教程已经过时,今天,我就分享我如何制作智能插座的思路以及学习过程中出现的问题,欢迎大家一起讨论学习,如有不足之处欢迎指正。

学习思路

对于esp8266的使用我则是参考了开发板的视频教程,大家也可以 直接买个开发板先进行学习,某宝有售,五六十就够了,在了解了esp8266这款wifi芯片的工作方式以及控制代码的书写之后便可以进行实践,将理论融入现实。

这是我买的开发板。

所需元器件

首先,考虑到需要用到的元器件。

esp8266-12s(芯片直接上电即可工作,方便使用,体积较小方便放入插座内,我之前就因为没好好了解芯片的使用问题,买回来还得自己接外围电路,在家实属不方便)。

220V转3.3VAC转DC模块(esp8266用3.3V供电,io口检测输入电压也最好3.3V)。这里这个是5V和3.3V双路输出的。这是220V转3.3V的比上面的稍大,但是寿命长。

继电器模块(3.3V供电,我用的是高电平吸合型,注意吸合电流最好10ma一下,因为esp8266io口最大输出电流为16ma,我就憨憨过)。我这是5V供电的。

普通插座(我用的是某牛,六插孔的,,注意螺丝为专业螺丝,需要专业螺丝刀,为了这个我又网上买了一把,疫情严重快递更是…,唉),电烙铁啥的都是基本的就不一一赘述了。

最后这是成品内部图,第一次做,有些凌乱,都已打胶固定,需注意220v的电,用电安全,用粗点的线避免烧毁。

配置百度云端

首先打开百度云官网进行注册,只有实名认证之后才能进行使用,完成实名注册之后打开

产品服务/物联网服务/物接入

点击创建设备(如果需要利用百度云的数据可视化只能创建设备型,而且设备型只能创建一个,具体可自行在物接入里的概览进行帮助查询,里面也有官方教程),这里我们创建一个数据型就够用了。

这里我已经创建好了,可以看见已经分配了给我们连接的地址将

连接地址复制到文本文档

后面接入时方便直接使用。

点击设备进入设备内配置,先配置策略(具体策略是什么请自行参考物接入的帮助文档自行理解),我们利用的是mqtt协议,这里发布与订阅都勾选,主题则是发送和接受消息的一个“过滤器”,如果手机订阅了switch主题那么它将收到设备发布到主题switch的消息。

再配置身份,创建身份绑定刚配置的策略,生成的

密钥先复制到文本文档

里,接入百度云时需要用到。

再进行用户的创建,直接绑定之前的策略以及身份就行,这里的cellphone用户是手机端,我们还需在创建个esp8266接入的用户,我创建的是first_floor,这里的

用户名需复制到文本文档

,接入时会用上。

这里,我们的百度云配置已经完成。

esp8266代码

这是部分代码,只需改几个参数即可接入,完整代码可自行下载。

链接:/s/1BPnpXsJhk6NQLFOC3nsKhw 提取码:yb9g

mqtt_config.h里的MQTT_HOST地址改成自己的,端口号默认1883不用改,ID不改,MQTT_USER用户名改成自己的,MQTT_PASS密码就是密钥,自己改好,以及wifi名称及密码自己改好。

#ifndef __MQTT_CONFIG_H__#define __MQTT_CONFIG_H__typedef enum{NO_TLS = 0, // 0: disable SSL/TLS, there must be no certificate verify between MQTT server and ESP8266TLS_WITHOUT_AUTHENTICATION = 1, // 1: enable SSL/TLS, but there is no a certificate verifyONE_WAY_ANTHENTICATION = 2, // 2: enable SSL/TLS, ESP8266 would verify the SSL server certificate at the same timeTWO_WAY_ANTHENTICATION = 3, // 3: enable SSL/TLS, ESP8266 would verify the SSL server certificate and SSL server would verify ESP8266 certificate}TLS_LEVEL;/*IMPORTANT: the following configuration maybe need modified*//***********************************************************************************************************************************************************************************************************************************************************/#define CFG_HOLDER 0x66666666// 持有人标识(只有更新此数值,系统参数才会更新)/* Change this value to load default configurations *//*DEFAULT CONFIGURATIONS*/// 注:【MQTT协议规定:连接服务端的每个客户端都必须有唯一的客户端标识符(ClientId)】。如果两相同ID的客户端不断重连,就会进入互踢死循环//--------------------------------------------------------------------------------------------------------------------------------------#define MQTT_HOST"eakpedg.mqtt.iot." // MQTT服务端域名/IP地址// the IP address or domain name of your MQTT server or MQTT broker ,such as ""#define MQTT_PORT 1883 // 网络连接端口号// the listening port of your MQTT server or MQTT broker#define MQTT_CLIENT_ID "ESP8266ID0x%x"// 官方例程中是"Device_ID"// 客户端标识符// the ID of yourself, any string is OK,client would use this ID register itself to MQTT server#define MQTT_USER "eakpedg/first_floor" // MQTT用户名// your MQTT login name, if MQTT server allow anonymous login,any string is OK, otherwise, please input valid login name which you had registered#define MQTT_PASS "***" // MQTT密码// you MQTT login password, same as above#define STA_SSID "CMCC_10050_B2674C" // WIFI名称// your AP/router SSID to config your device networking#define STA_PASS "***" // WIFI密码// your AP/router password#define STA_TYPEAUTH_WPA2_PSK#define DEFAULT_SECURITYNO_TLS// 加密传输类型【默认不加密】// very important: you must config DEFAULT_SECURITY for SSL/TLS#define CA_CERT_FLASH_ADDRESS 0x77 // 【CA证书】烧录地址// CA certificate address in flash to read, 0x77 means address 0x77000#define CLIENT_CERT_FLASH_ADDRESS 0x78 // 【设备证书】烧录地址// client certificate and private key address in flash to read, 0x78 means address 0x78000/*********************************************************************************************************************************************************************************************************************************************************************************//*Please Keep the following configuration if you have no very deep understanding of ESP SSL/TLS*/#define CFG_LOCATION 0x79// 系统参数的起始扇区/* Please don't change or if you know what you doing */#define MQTT_BUF_SIZE 1024// MQTT缓存大小#define MQTT_KEEPALIVE 120// 保持连接时长/*second*/#define MQTT_RECONNECT_TIMEOUT 5 // 重连超时时长/*second*/#define MQTT_SSL_ENABLE// SSL使能//* Please don't change or if you know what you doing */#define QUEUE_BUFFER_SIZE2048// 消息队列的缓存大小//#define PROTOCOL_NAMEv31 // 使用MQTT协议【v31】版本/*MQTT version 3.1 compatible with Mosquitto v0.15*/#define PROTOCOL_NAMEv311// 使用MQTT协议【v311】版本/*MQTT version 3.11 compatible with /paho/clients/testing/*/#endif // __MQTT_CONFIG_H__

mqtt.c中void ICACHE_FLASH_ATTR mqtt_timer(void *arg)函数为一秒心跳函数,我进行的是是否有电的检测,里面的主题参数等自己可以照着修改,如果只是控制的话不需要设置,一秒检测一次,因为esp8266进行的是内核回调和普通单片机while循环不一样。

// mqtt.c#include "user_interface.h"#include "osapi.h"#include "espconn.h"#include "os_type.h"#include "mem.h"#include "mqtt_msg.h"#include "debug.h"#include "user_config.h"#include "mqtt.h"#include "queue.h"#include "ets_sys.h"#include "driver/uart.h"#include "osapi.h"#include "mqtt.h"#include "wifi.h"#include "config.h"#include "debug.h"#include "gpio.h"#include "user_interface.h"#include "mem.h"#include "sntp.h"#include "eagle_soc.h"// GPIO函数、宏定义#define MQTT_TASK_PRIO 2// MQTT任务优先级#define MQTT_TASK_QUEUE_SIZE 1// MQTT任务消息深度#define MQTT_SEND_TIMOUT 5// MQTT发送超时#ifndef QUEUE_BUFFER_SIZE#define QUEUE_BUFFER_SIZE 2048// 队列缓存 = 2048#endifunsigned char *default_certificate;// 默认证书指针unsigned int default_certificate_len = 0;// 默认证书长度unsigned char *default_private_key;// 默认密钥指针unsigned int default_private_key_len = 0;// 默认密钥长度int last_Power;os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE];// MQTT任务结构体(定义为结构体数组的形式)// 域名解析成功_回调//=============================================================================================LOCAL void ICACHE_FLASH_ATTR mqtt_dns_found(const char *name, ip_addr_t *ipaddr, void *arg){struct espconn *pConn = (struct espconn *)arg; // 获取TCP连接指针MQTT_Client* client = (MQTT_Client *)pConn->reverse; // 获取mqttClient指针if (ipaddr == NULL)// 域名解析失败{INFO("DNS: Found, but got no ip, try to reconnect\r\n");client->connState = TCP_RECONNECT_REQ;// TCP重连请求(等待5秒)return;}INFO("DNS: found ip %d.%d.%d.%d\n",// 打印域名对应的IP地址*((uint8 *) &ipaddr->addr),*((uint8 *) &ipaddr->addr + 1),*((uint8 *) &ipaddr->addr + 2),*((uint8 *) &ipaddr->addr + 3));// 判断IP地址是否正确(?=0)//----------------------------------------------------------------------------------------if (client->ip.addr == 0 && ipaddr->addr != 0)// 未保存IP地址:mqttClient->ip.addr == 0{os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4);// IP赋值// 根据安全类型,调用不同的TCP连接方式//-------------------------------------------------------------------------------------------------if (client->security)// 安全类型 != 0{#ifdef MQTT_SSL_ENABLEif(DEFAULT_SECURITY >= ONE_WAY_ANTHENTICATION )// 单向认证【ONE_WAY_ANTHENTICATION = 2】{espconn_secure_ca_enable(ESPCONN_CLIENT,CA_CERT_FLASH_ADDRESS);}if(DEFAULT_SECURITY >= TWO_WAY_ANTHENTICATION)// 双向认证【TWO_WAY_ANTHENTICATION = 3】{espconn_secure_cert_req_enable(ESPCONN_CLIENT,CLIENT_CERT_FLASH_ADDRESS);}espconn_secure_connect(client->pCon);// 不认证【TLS_WITHOUT_AUTHENTICATION = 1】#elseINFO("TCP: Do not support SSL\r\n");#endif}else// 安全类型 = 0 = NO_TLS{espconn_connect(client->pCon);// TCP连接(作为Client连接Server)}client->connState = TCP_CONNECTING;// TCP正在连接INFO("TCP: connecting...\r\n");}system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Task}//=============================================================================================// ESP8266获取服务端【PUBLISH】报文的【主题】、【有效载荷】//===================================================================================================================================LOCAL void ICACHE_FLASH_ATTR deliver_publish(MQTT_Client* client, uint8_t* message, int length){mqtt_event_data_t event_data;event_data.topic_length = length;// 主题名长度初始化event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length);// 获取【PUBLISH】报文的主题名(指针)、主题名长度event_data.data_length = length;// 有效载荷长度初始化event_data.data = mqtt_get_publish_data(message, &event_data.data_length);// 获取【PUBLISH】报文的载荷(指针)、载荷长度// 进入【接收MQTT的[PUBLISH]数据】函数//-------------------------------------------------------------------------------------------------------------------------if (client->dataCb)client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length);}//===================================================================================================================================// 向MQTT服务器发送【心跳】报文(报文不写入队列,TCP直接发送)//===================================================================================================================================void ICACHE_FLASH_ATTR mqtt_send_keepalive(MQTT_Client *client){INFO("\r\nMQTT: Send keepalive packet to %s:%d!\r\n", client->host, client->port);client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection);// 设置【PINGREQ】报文client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ;client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data);// 获取报文类型client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);// TCP发送成功/5秒计时结束 => 报文发送结束(sendTimeout=0)//-------------------------------------------------------------------------client->sendTimeout = MQTT_SEND_TIMOUT;// 发送MQTT报文时,sendTimeout=5INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id);err_t result = ESPCONN_OK;if (client->security)// 安全类型 != 0{#ifdef MQTT_SSL_ENABLEresult = espconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);#elseINFO("TCP: Do not support SSL\r\n");#endif}else // 安全类型 = 0 = NO_TLS{result = espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);}client->mqtt_state.outbound_message = NULL;// 报文发送完后,清除出站报文指针if(ESPCONN_OK == result)// 网络数据发送成功{client->keepAliveTick = 0;// 心跳计数 = 0client->connState = MQTT_DATA;// ESP8266当前状态 = MQTT传输数据system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Task}else // 【心跳请求】发送失败{client->connState = TCP_RECONNECT_DISCONNECTING;// TCP暂时断开(断开后会重连)system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Task}}//===================================================================================================================================/*** @brief Delete tcp client and free all memory* @param mqttClient: The mqtt client which contain TCP client* @retval None*/// 删除TCP连接、释放pCon内存、清除TCP连接指针//=======================================================================void ICACHE_FLASH_ATTR mqtt_tcpclient_delete(MQTT_Client *mqttClient){if (mqttClient->pCon != NULL){INFO("Free memory\r\n");espconn_delete(mqttClient->pCon);// 删除传输连接if (mqttClient->pCon->proto.tcp)// 如果是TCP连接os_free(mqttClient->pCon->proto.tcp);// 释放TCP内存os_free(mqttClient->pCon);// 释放pCon内存mqttClient->pCon = NULL;// 清除mqttClient->pCon指针}}//=======================================================================/*** @brief Delete MQTT client and free all memory* @param mqttClient: The mqtt client* @retval None*/// 删除MQTT客户端,并释放所有相关内存//=====================================================================================void ICACHE_FLASH_ATTR mqtt_client_delete(MQTT_Client *mqttClient){mqtt_tcpclient_delete(mqttClient);// 删除TCP连接、释放pCon内存、清除TCP连接指针if (mqttClient->host != NULL){os_free(mqttClient->host);// 释放主机(域名/IP)内存mqttClient->host = NULL;}if (mqttClient->user_data != NULL){os_free(mqttClient->user_data);// 释放用户数据内存mqttClient->user_data = NULL;}if(mqttClient->connect_info.client_id != NULL){os_free(mqttClient->connect_info.client_id);// 释放client_id内存mqttClient->connect_info.client_id = NULL;}if(mqttClient->connect_info.username != NULL){os_free(mqttClient->connect_info.username);// 释放username内存mqttClient->connect_info.username = NULL;}if(mqttClient->connect_info.password != NULL){os_free(mqttClient->connect_info.password);// 释放password内存mqttClient->connect_info.password = NULL;}if(mqttClient->connect_info.will_topic != NULL){os_free(mqttClient->connect_info.will_topic);// 释放will_topic内存mqttClient->connect_info.will_topic = NULL;}if(mqttClient->connect_info.will_message != NULL){os_free(mqttClient->connect_info.will_message);// 释放will_message内存mqttClient->connect_info.will_message = NULL;}if(mqttClient->mqtt_state.in_buffer != NULL){os_free(mqttClient->mqtt_state.in_buffer);// 释放in_buffer内存mqttClient->mqtt_state.in_buffer = NULL;}if(mqttClient->mqtt_state.out_buffer != NULL){os_free(mqttClient->mqtt_state.out_buffer);// 释放out_buffer内存mqttClient->mqtt_state.out_buffer = NULL;}}//=============================================================================/*** @brief Client received callback function.* @param arg: contain the ip link information* @param pdata: received data* @param len: the lenght of received data* @retval None*/// TCP成功接收网络数据:【服务端->客户端ESP8266】//==================================================================================================================================================void ICACHE_FLASH_ATTR mqtt_tcpclient_recv(void *arg, char *pdata, unsigned short len){uint8_t msg_type;// 消息类型uint8_t msg_qos;// 服务质量uint16_t msg_id;// 消息IDstruct espconn *pCon = (struct espconn*)arg;// 获取TCP连接指针MQTT_Client *client = (MQTT_Client *)pCon->reverse;// 获取mqttClient指针client->keepAliveTick = 0;// 只要收到网络数据,客户端(ESP8266)心跳计数 = 0// 读取数据包//----------------------------------------------------------------------------------------------------------------------------READPACKET:INFO("TCP: data received %d bytes\r\n",len);//###############################################################################################################################INFO("TCP: data received %d,%d,%d,%d \r\n", *pdata,*(pdata+1),*(pdata+2),*(pdata+3));// 查看【确认连接请求】报文的具体值#//###############################################################################################################################if (len<MQTT_BUF_SIZE && len>0)// 接收到的数据长度在允许范围内{os_memcpy(client->mqtt_state.in_buffer, pdata, len);// 获取接收数据,存入【入站报文缓存区】msg_type = mqtt_get_type(client->mqtt_state.in_buffer);// 获取【报文类型】msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer);// 获取【消息质量】msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length);// 获取报文中的【报文标识符】// 根据ESP8266运行状态,执行相应操作//-------------------------------------------------------------------------switch (client->connState){case MQTT_CONNECT_SENDING:// 【MQTT_CONNECT_SENDING】if (msg_type == MQTT_MSG_TYPE_CONNACK) // 判断消息类型!=【CONNACK】{// ESP8266发送 != 【CONNECT】报文//--------------------------------------------------------------if (client->mqtt_state.pending_msg_type!=MQTT_MSG_TYPE_CONNECT){INFO("MQTT: Invalid packet\r\n");// 网络数据出错if (client->security)// 安全类型 != 0{#ifdef MQTT_SSL_ENABLEespconn_secure_disconnect(client->pCon);// 断开TCP连接#elseINFO("TCP: Do not support SSL\r\n");#endif}else // 安全类型 = 0 = NO_TLS{espconn_disconnect(client->pCon);// 断开TCP连接}}// ESP8266发送 == 【CONNECT】报文//---------------------------------------------------------------------------------else{INFO("MQTT: Connected to %s:%d\r\n", client->host, client->port);client->connState = MQTT_DATA;// ESP8266状态改变:【MQTT_DATA】if (client->connectedCb)// 执行[mqttConnectedCb]函数(MQTT连接成功函数)client->connectedCb((uint32_t*)client);// 参数 = mqttClient}}break;// 当前ESP8266状态 == MQTT_DATA || MQTT_KEEPALIVE_SEND//-----------------------------------------------------case MQTT_DATA:case MQTT_KEEPALIVE_SEND:client->mqtt_state.message_length_read = len;// 获取接收网络数据的长度// 计算接收到的网络数据中,报文的实际长度(通过【剩余长度】得到)//------------------------------------------------------------------------------------------------------------------------------client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read);// 当前ESP8266状态 ==【MQTT_DATA || MQTT_KEEPALIVE_SEND】,根据接收的报文类型,决定ESP8266接下来的动作//-----------------------------------------------------------------------------------------------------------------------switch (msg_type){// ESP8266接收到【SUBACK】报文:订阅确认//-----------------------------------------------------------------------------------------------------------------------case MQTT_MSG_TYPE_SUBACK:// 判断ESP8266之前发送的报文是否是【订阅主题】//-------------------------------------------------------------------------------------------------------------if (client->mqtt_state.pending_msg_type==MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id==msg_id)INFO("MQTT: Subscribe successful\r\n");//break;//-----------------------------------------------------------------------------------------------------------------------// ESP8266接收到【UNSUBACK】报文:取消订阅确认//-----------------------------------------------------------------------------------------------------------------------case MQTT_MSG_TYPE_UNSUBACK:// 判断ESP8266之前发送的报文是否是【订阅主题】//------------------------------------------------------------------------------------------------------------------if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id)INFO("MQTT: UnSubscribe successful\r\n");break;//-----------------------------------------------------------------------------------------------------------------------// ESP8266接收到【PUBLISH】报文:发布消息//--------------------------------------------------------------------------------------------------------------------------------case MQTT_MSG_TYPE_PUBLISH:if (msg_qos == 1)// 【服务端->客户端】发布消息 Qos=1client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id);// 配置【PUBACK】报文else if (msg_qos == 2)// 【服务端->客户端】发布消息 Qos=2client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id);// 配置【PUBREC】报文if (msg_qos == 1 || msg_qos == 2){INFO("MQTT: Queue response QoS: %d\r\n", msg_qos);// 将ESP8266应答报文(【PUBACK】或【PUBREC】),写入队列//-------------------------------------------------------------------------------------------------------------------------------if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){INFO("MQTT: Queue full\r\n");}}/// 获取服务端【PUBLISH】报文的【主题】、【有效载荷】//---------------------------------------------------------------------------------------------deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read);break;//-----------------------------------------------------------------------------------------------------------------------// ESP8266接收到【PUBACK】报文:发布消息应答//-------------------------------------------------------------------------------------------------------------------case MQTT_MSG_TYPE_PUBACK:if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id){INFO("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\r\n");}break;//-------------------------------------------------------------------------------------------------------------------// Qos == 2//-------------------------------------------------------------------------------------------------------------------------------------case MQTT_MSG_TYPE_PUBREC:client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id);if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {INFO("MQTT: Queue full\r\n");}break;case MQTT_MSG_TYPE_PUBREL:client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id);if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {INFO("MQTT: Queue full\r\n");}break;case MQTT_MSG_TYPE_PUBCOMP:if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) {INFO("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\r\n");}break;//-------------------------------------------------------------------------------------------------------------------------------------// ESP8266接收到【PINGREQ】报文:【这个是服务端发送给客户端的,正常情况下,ESP8266不会收到此报文】//-------------------------------------------------------------------------------------------------------------------------------------case MQTT_MSG_TYPE_PINGREQ:client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection);if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) {INFO("MQTT: Queue full\r\n");}break;//-------------------------------------------------------------------------------------------------------------------------------------// ESP8266接收到【PINGRESP】报文:心跳应答//-----------------------------------------case MQTT_MSG_TYPE_PINGRESP:// Ignore// 心跳应答报文则忽略break;//-----------------------------------------}// NOTE: this is done down here and not in the switch case above// because the PSOCK_READBUF_LEN() won't work inside a switch// statement due to the way protothreads resume.// ESP8266接收到【PUBLISH】报文:发布消息//-------------------------------------------------------------------------------------if (msg_type == MQTT_MSG_TYPE_PUBLISH){len = client->mqtt_state.message_length_read;// 报文实际长度 < 网络数据长度//------------------------------------------------------------------------------if (client->mqtt_state.message_length < client->mqtt_state.message_length_read){//client->connState = MQTT_PUBLISH_RECV;//Not Implement yetlen -= client->mqtt_state.message_length;// 跳过之前解析过的报文pdata += client->mqtt_state.message_length;// 指向之后为解析的网络数据INFO("Get another published message\r\n");goto READPACKET;// 重新解析网络数据}}break;// case MQTT_DATA:// case MQTT_KEEPALIVE_SEND://-------------------------------------------------------------------------------------}}else // 接收到的报文出错{INFO("ERROR: Message too long\r\n");}system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Task}//==================================================================================================================================================/*** @brief Client send over callback function.* @param arg: contain the ip link information* @retval None*/// TCP发送成功_回调函数//================================================================================void ICACHE_FLASH_ATTR mqtt_tcpclient_sent_cb(void *arg){struct espconn *pCon = (struct espconn *)arg;// 获取TCP连接指针MQTT_Client* client = (MQTT_Client *)pCon->reverse;// 获取mqttClient指针INFO("TCP: Sent\r\n");client->sendTimeout = 0;// TCP发送成功/报文发送5秒计时结束 => 报文发送结束(sendTimeout=0)client->keepAliveTick =0;// 只要TCP发送成功,客户端(ESP8266)心跳计数 = 0// 当前是MQTT客户端 发布消息/发送心跳 状态//--------------------------------------------------------------------------if ((client->connState==MQTT_DATA || client->connState==MQTT_KEEPALIVE_SEND)&& client->mqtt_state.pending_msg_type==MQTT_MSG_TYPE_PUBLISH){if (client->publishedCb)// 执行[MQTT发布成功]函数client->publishedCb((uint32_t*)client);}system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Task}//================================================================================// MQTT定时(1秒)【功能:心跳计时、重连计时、TCP发送计时】//================================================================================void ICACHE_FLASH_ATTR mqtt_timer(void *arg){MQTT_Client* client = (MQTT_Client*)arg;last_Power = Power;if( GPIO_INPUT_GET(GPIO_ID_PIN(15)) == 0 )// 读取GPIO_0电平{Power = OFF;}else{Power = ON;}if(last_Power != Power){if(Power == ON)// ESP8266向主题发布消息:【参数2:主题名 / 参数3:发布消息的有效载荷 / 参数4:有效载荷长度 / 参数5:发布Qos / 参数6:Retain】{MQTT_Publish(client, "first_floor_switch", "Power_ON", strlen("Power_ON"), 0, 0);// 向主题"first_floor_switch"发布"Power_ON",Qos=0、retain=0INFO("\r\n Power ON ...\r\n");//GPIO_OUTPUT_SET(GPIO_ID_PIN(4),0);// LED亮}else{MQTT_Publish(client, "first_floor_switch", "Power_OFF", strlen("Power_OFF"), 0, 0);// 向主题"first_floor_switch"发布"Power_OFF",Qos=0、retain=0INFO("\r\n Power OFF ...\r\n");//GPIO_OUTPUT_SET(GPIO_ID_PIN(4),1);// LED灭}}// 如果当前是[MQTT_DATA]状态,则进行存活计时操作//--------------------------------------------------------------------------if (client->connState == MQTT_DATA)// MQTT_DATA{client->keepAliveTick ++;// 客户端(ESP8266)心跳计数++// 判断客户端(ESP8266)心跳计数 ?> 保持连接的1/2时间//--------------------------------------------------------------------------------------------------------------------------if (client->keepAliveTick>(client->mqtt_state.connect_info->keepalive/2))//【注:官方例程中是:判断是否超过保持连接时长】{client->connState = MQTT_KEEPALIVE_SEND;// MQTT_KEEPALIVE_SENDsystem_os_post(MQTT_TASK_PRIO,0,(os_param_t)client);// 安排任务}}// 重连等待计时:当进入重连请求状态后,需等待5秒,之后进行重新连接//--------------------------------------------------------------------------else if (client->connState == TCP_RECONNECT_REQ)// TCP重连请求(等待5秒){client->reconnectTick ++;// 重连计时++if (client->reconnectTick > MQTT_RECONNECT_TIMEOUT)// 重连请求超过5秒{client->reconnectTick = 0;// 重连计时 = 0client->connState = TCP_RECONNECT;// TCP重新连接system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务// 重连等待计时结束//-----------------------------------------------------------------if (client->timeoutCb)// 未创建回调函数client->timeoutCb((uint32_t*)client);}}// TCP发送成功/报文发送5秒计时结束 => 报文发送结束(sendTimeout=0)//----------------------------------------------------------------if (client->sendTimeout>0)// 发送MQTT报文时,sendTimeout=5client->sendTimeout --;// sendTimeout每秒递减(直到=0)}//================================================================================// TCP断开成功_回调函数//================================================================================void ICACHE_FLASH_ATTR mqtt_tcpclient_discon_cb(void *arg){struct espconn *pespconn = (struct espconn *)arg; // 获取TCP连接指针MQTT_Client*client = (MQTT_Client*)pespconn->reverse; // 获取mqttClient指针INFO("TCP: Disconnected callback\r\n");// 不执行重连操作//……………………………………………………………………………………………………if(TCP_DISCONNECTING == client->connState)// 当前状态如果是:TCP正在断开{client->connState = TCP_DISCONNECTED;// ESP8266状态改变:TCP成功断开}else if(MQTT_DELETING == client->connState)// 当前状态如果是:MQTT正在删除{client->connState = MQTT_DELETED;// ESP8266状态改变:MQTT删除}//……………………………………………………………………………………………………// 只要不是上面两种状态,那么当TCP断开连接后,进入【TCP重连请求(等待5秒)】//……………………………………………………………………………………………………else{client->connState = TCP_RECONNECT_REQ;// TCP重连请求(等待5秒)}if (client->disconnectedCb)// 调用[MQTT成功断开连接]函数client->disconnectedCb((uint32_t*)client);//……………………………………………………………………………………………………system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Task}//================================================================================/*** @brief Tcp client connect success callback function.* @param arg: contain the ip link information* @retval None*/// TCP连接成功的回调函数//============================================================================================================================================void ICACHE_FLASH_ATTR mqtt_tcpclient_connect_cb(void *arg){struct espconn *pCon = (struct espconn *)arg; // 获取TCP连接指针MQTT_Client* client = (MQTT_Client *)(pCon->reverse);// 获取mqttClient指针// 注册回调函数//--------------------------------------------------------------------------------------espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb);// TCP断开成功_回调espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);// TCP接收成功_回调espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);// TCP发送成功_回调INFO("MQTT: Connected to broker %s:%d\r\n", client->host, client->port);// 【CONNECT】报文发送准备//……………………………………………………………………………………………………………………………………………………………………………………// 初始化MQTT报文缓存区//-----------------------------------------------------------------------------------------------------------------------mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length);// 配置【CONNECT】控制报文,并获取【CONNECT】报文[指针]、[长度]//----------------------------------------------------------------------------------------------------------------------------client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info);// 获取待发送的报文类型(此处是【CONNECT】报文)//----------------------------------------------------------------------------------------------client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data);// 获取待发送报文中的【报文标识符】(【CONNECT】报文中没有)//-------------------------------------------------------------------------------------------------------------------------------------client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data,client->mqtt_state.outbound_message->length);// TCP发送成功/报文发送5秒计时结束 => 报文发送结束(sendTimeout=0)//-------------------------------------------------------------------------client->sendTimeout = MQTT_SEND_TIMOUT;// 发送MQTT报文时,sendTimeout=5INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id);//……………………………………………………………………………………………………………………………………………………………………………………// TCP:发送【CONNECT】报文//-----------------------------------------------------------------------------------------------------------------------------if (client->security)// 安全类型 != 0{#ifdef MQTT_SSL_ENABLEespconn_secure_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);#elseINFO("TCP: Do not support SSL\r\n");#endif}else// 安全类型 = 0 = NO_TLS{// TCP发送:数据=[client->mqtt_state.outbound_message->data]、长度=[client->mqtt_state.outbound_message->length]//-----------------------------------------------------------------------------------------------------------------espconn_send(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length);}//-----------------------------------------------------------------------------------------------------------------------------client->mqtt_state.outbound_message = NULL;// 报文发送完后,清除出站报文指针client->connState = MQTT_CONNECT_SENDING;// 状态设为:MQTT【CONNECT】报文发送中【MQTT_CONNECT_SENDING】system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Task}//============================================================================================================================================/*** @brief Tcp client connect repeat callback function.* @param arg: contain the ip link information* @retval None*/// TCP异常中断_回调//==============================================================================void ICACHE_FLASH_ATTR mqtt_tcpclient_recon_cb(void *arg, sint8 errType){struct espconn *pCon = (struct espconn *)arg;// 获取TCP连接指针MQTT_Client* client = (MQTT_Client *)pCon->reverse;// 获取mqttClient指针INFO("TCP: Reconnect to %s:%d\r\n", client->host, client->port);client->connState = TCP_RECONNECT_REQ;// TCP重连请求(等待5秒)system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Task}//==============================================================================/*** @brief MQTT publish function.* @param client:MQTT_Client reference* @param topic: string topic will publish to* @param data: buffer data send point to* @param data_length: length of data* @param qos: qos* @param retain: retain* @retval TRUE if success queue*/// ESP8266向主题发布消息:【参数2:主题名 / 参数3:发布消息的有效载荷 / 参数4:有效载荷长度 / 参数5:发布Qos / 参数6:Retain】//============================================================================================================================================BOOL ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain){uint8_t dataBuffer[MQTT_BUF_SIZE];// 解析后报文缓存(1204字节)uint16_t dataLen;// 解析后报文长度// 配置【PUBLISH】报文,并获取【PUBLISH】报文[指针]、[长度]//------------------------------------------------------------------------------------------client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection,topic, data, data_length,qos, retain,&client->mqtt_state.pending_msg_id);if (client->mqtt_state.outbound_message->length == 0)// 判断报文是否正确{INFO("MQTT: Queuing publish failed\r\n");return FALSE;}// 串口打印:【PUBLISH】报文长度,(队列装填数量/队列大小)//--------------------------------------------------------------------------------------------------------------------------------------------------------------------INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size);// 将报文写入队列,并返回写入字节数(包括特殊码)//----------------------------------------------------------------------------------------------------------------------------------while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){INFO("MQTT: Queue full\r\n");// 队列已满// 解析队列中的数据包//-----------------------------------------------------------------------------------------------if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1)// 解析失败 = -1{INFO("MQTT: Serious buffer error\r\n");return FALSE;}}system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务return TRUE;}//============================================================================================================================================/*** @brief MQTT subscibe function.* @param client:MQTT_Client reference* @param topic:string topic will subscribe* @param qos: qos* @retval TRUE if success queue*/// ESP8266订阅主题【参数2:主题过滤器 / 参数3:订阅Qos】//============================================================================================================================================BOOL ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client *client, char* topic, uint8_t qos){uint8_t dataBuffer[MQTT_BUF_SIZE];// 解析后报文缓存(1204字节)uint16_t dataLen;// 解析后报文长度// 配置【SUBSCRIBE】报文,并获取【SUBSCRIBE】报文[指针]、[长度]//----------------------------------------------------------------------------------------------------------------------------------------client->mqtt_state.outbound_message=mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection,topic, qos,&client->mqtt_state.pending_msg_id);INFO("MQTT: queue subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id);// 将报文写入队列,并返回写入字节数(包括特殊码)//----------------------------------------------------------------------------------------------------------------------------------while(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){INFO("MQTT: Queue full\r\n");// 解析队列中的报文//-----------------------------------------------------------------------------------------------if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1)// 解析失败 = -1{INFO("MQTT: Serious buffer error\r\n");return FALSE;}}system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Taskreturn TRUE;}//============================================================================================================================================/*** @brief MQTT un-subscibe function.* @param client:MQTT_Client reference* @param topic: String topic will un-subscribe* @retval TRUE if success queue*/// ESP8266取消订阅主题//============================================================================================================================================BOOL ICACHE_FLASH_ATTR MQTT_UnSubscribe(MQTT_Client *client, char* topic){uint8_t dataBuffer[MQTT_BUF_SIZE];// 解析后报文缓存(1204字节)uint16_t dataLen;// 解析后报文长度// 配置【UNSUBSCRIBE】报文【参数2:取消订阅主题 / 参数3:报文标识符】//----------------------------------------------------------------------------------------------------------------------------------------client->mqtt_state.outbound_message = mqtt_msg_unsubscribe(&client->mqtt_state.mqtt_connection,topic,&client->mqtt_state.pending_msg_id);INFO("MQTT: queue un-subscribe, topic\"%s\", id: %d\r\n", topic, client->mqtt_state.pending_msg_id);// 将报文写入队列,并返回写入字节数(包括特殊码)//----------------------------------------------------------------------------------------------------------------------------------while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){INFO("MQTT: Queue full\r\n");// 解析队列中的报文//-----------------------------------------------------------------------------------------------if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1)// 解析失败 = -1{INFO("MQTT: Serious buffer error\r\n");return FALSE;}}system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Taskreturn TRUE;}//============================================================================================================================================/*** @brief MQTT ping function.* @param client:MQTT_Client reference* @retval TRUE if success queue*/// 向MQTT服务器发送【心跳】报文(将报文写入队列,任务函数中发送)//============================================================================================================================================BOOL ICACHE_FLASH_ATTR MQTT_Ping(MQTT_Client *client){uint8_t dataBuffer[MQTT_BUF_SIZE];uint16_t dataLen;client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection);// 配置【PINGREQ】报文if(client->mqtt_state.outbound_message->length == 0)// 报文错误{INFO("MQTT: Queuing publish failed\r\n");return FALSE;}INFO("MQTT: queuing publish, length: %d, queue size(%d/%d)\r\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size);// 将报文写入队列,并返回写入字节数(包括特殊码)//----------------------------------------------------------------------------------------------------------------------------------while(QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1){INFO("MQTT: Queue full\r\n");// 解析队列中的报文//-----------------------------------------------------------------------------------------------if(QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1)// 解析失败 = -1{INFO("MQTT: Serious buffer error\r\n");return FALSE;}}system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client);// 安排任务MQTT_Taskreturn TRUE;}//============================================================================================================================================// MQTT任务函数【任务:根据ESP8266运行状态,执行相应操作】//--------------------------------------------------------------------------------------------// TCP_RECONNECT_REQTCP重连请求(等待5秒)退出Tsak(5秒后,进入TCP_RECONNECT状态)//--------------------------------------------------------------------------------------------// TCP_RECONNECTTCP重新连接执行MQTT连接准备,并设置ESP8266状态//--------------------------------------------------------------------------------------------// MQTT_DELETINGMQTT正在删除TCP断开连接// TCP_DISCONNECTINGTCP正在断开// TCP_RECONNECT_DISCONNECTINGTCP暂时断开(断开后会重连)//--------------------------------------------------------------------------------------------// TCP_DISCONNECTEDTCP成功断开删除TCP连接,并释放pCon内存//--------------------------------------------------------------------------------------------// MQTT_DELETEDMQTT已删除删除MQTT客户端,并释放相关内存//--------------------------------------------------------------------------------------------// MQTT_KEEPALIVE_SENDMQTT心跳向服务器发送心跳报文//--------------------------------------------------------------------------------------------// MQTT_DATAMQTT数据传输TCP发送队列中的报文//====================================================================================================================================void ICACHE_FLASH_ATTR MQTT_Task(os_event_t *e)// 不判断消息类型{INFO("\r\n------------- MQTT_Task -------------\r\n");MQTT_Client* client = (MQTT_Client*)e->par;// 【e->par】 == 【mqttClient指针的值】,所以需类型转换uint8_t dataBuffer[MQTT_BUF_SIZE];// 数据缓存区(1204字节)uint16_t dataLen;// 数据长度if (e->par == 0)// 没有mqttClient指针,错误return;// 根据ESP8266运行状态,执行相应操作//………………………………………………………………………………………………………………………………………………………………………switch (client->connState){// TCP重连请求(等待5秒),退出Tsak//---------------------------------case TCP_RECONNECT_REQ:break;//---------------------------------// TCP重新连接:执行MQTT连接准备,并设置ESP8266状态//--------------------------------------------------------------------------------case TCP_RECONNECT:mqtt_tcpclient_delete(client);// 删除TCP连接、释放pCon内存、清除TCP连接指针MQTT_Connect(client);// MQTT连接准备:TCP连接、域名解析等INFO("TCP: Reconnect to: %s:%d\r\n", client->host, client->port);client->connState = TCP_CONNECTING;// TCP正在连接break;//--------------------------------------------------------------------------------// MQTT正在删除、TCP正在断开、【心跳请求】报文发送失败:TCP断开连接//------------------------------------------------------------------case MQTT_DELETING:case TCP_DISCONNECTING:case TCP_RECONNECT_DISCONNECTING:if (client->security)// 安全类型 != 0{#ifdef MQTT_SSL_ENABLEespconn_secure_disconnect(client->pCon);#elseINFO("TCP: Do not support SSL\r\n");#endif}else // 安全类型 = 0 = NO_TLS{espconn_disconnect(client->pCon);// TCP断开连接}break;//------------------------------------------------------------------// TCP成功断开//--------------------------------------------------------------------------------case TCP_DISCONNECTED:INFO("MQTT: Disconnected\r\n");mqtt_tcpclient_delete(client);// 删除TCP连接、释放pCon内存、清除TCP连接指针break;//--------------------------------------------------------------------------------// MQTT已删除:ESP8266的状态为[MQTT已删除]后,将MQTT相关内存释放//--------------------------------------------------------------------case MQTT_DELETED:INFO("MQTT: Deleted client\r\n");mqtt_client_delete(client);// 删除MQTT客户端,并释放相关内存break;// MQTT客户端存活报告//--------------------------------------------------------------------case MQTT_KEEPALIVE_SEND:mqtt_send_keepalive(client);// 向MQTT服务器发送【心跳】报文break;// MQTT传输数据状态//-------------------------------------------------------------------------------------------------------------------------------case MQTT_DATA:if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0){break;// 【队列为空 || 发送未结束】,不执行操作}// 【队列非空 && 发送结束】:解析并发送 队列中的报文//--------------------------------------------------------------------------------------------------------if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0)// 解析成功 = 0{client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer);// 获取报文中的【报文类型】client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen);// 获取报文中的【报文标识符】client->sendTimeout = MQTT_SEND_TIMOUT;// 发送MQTT报文时,sendTimeout=5INFO("MQTT: Sending, type: %d, id: %04X\r\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id);// 发送报文//-----------------------------------------------------------------------if (client->security)// 安全类型 != 0{#ifdef MQTT_SSL_ENABLEespconn_secure_send(client->pCon, dataBuffer, dataLen);#elseINFO("TCP: Do not support SSL\r\n");#endif}else// 安全类型 = 0 = NO_TLS{espconn_send(client->pCon, dataBuffer, dataLen);// TCP发送数据包}client->mqtt_state.outbound_message = NULL;// 报文发送完后,清除出站报文指针break;}break;}//………………………………………………………………………………………………………………………………………………………………………}// 函数【MQTT_Task】结束//====================================================================================================================================/*** @brief MQTT initialization connection function* @param client:MQTT_Client reference* @param host:Domain or IP string* @param port:Port to connect* @param security: 1 for ssl, 0 for none* @retval None*/// 网络连接参数赋值:服务端域名【mqtt_test_jx.mqtt.iot.】、网络连接端口【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客户端】结构体 = 0temp = 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;// 网络端口号 = 1883mqttClient->security = security;// 安全类型 = 0 = NO_TLS}//====================================================================================================================/*** @brief MQTT initialization mqtt client function* @param client:MQTT_Client reference* @param clientid: MQTT client id* @param client_user:MQTT client user* @param client_pass:MQTT client password* @param client_pass:MQTT keep alive timer, in second* @retval None*/// MQTT连接参数赋值:客户端标识符【..】、MQTT用户名【..】、MQTT密钥【..】、保持连接时长【120s】、清除会话【1:clean_session】//======================================================================================================================================================void ICACHE_FLASH_ATTRMQTT_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】报文的连接参数 = 0temp = 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;// 保持连接 = 120smqttClient->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】型}//======================================================================================================================================================// 设置遗嘱:遗嘱主题【...】、遗嘱消息【...】、遗嘱质量【Will_Qos=0】、遗嘱保持【Will_Retain=0】//====================================================================================================================void ICACHE_FLASH_ATTRMQTT_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】}//====================================================================================================================/*** @brief Begin connect to MQTT broker* @param client: MQTT_Client reference* @retval None*/// WIFI连接、SNTP成功后 => MQTT连接准备(设置TCP连接、解析域名)//============================================================================================================================================void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client *mqttClient){//espconn_secure_set_size(0x01,6*1024); // SSL双向认证时才需使用// try to modify memory size 6*1024 if ssl/tls handshake failed// 开始MQTT连接前,判断是否存在MQTT的TCP连接。如果有,则清除之前的TCP连接//------------------------------------------------------------------------------------if (mqttClient->pCon){// Clean up the old connection forcefully - using MQTT_Disconnect// does not actually release the old connection until the// disconnection callback is invoked.mqtt_tcpclient_delete(mqttClient);// 删除TCP连接、释放pCon内存、清除TCP连接指针}// TCP连接设置//------------------------------------------------------------------------------------------------------mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn));// 申请pCon内存mqttClient->pCon->type = ESPCONN_TCP;// 设为TCP连接mqttClient->pCon->state = ESPCONN_NONE;mqttClient->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));// 申请esp_tcp内存mqttClient->pCon->proto.tcp->local_port = espconn_port();// 获取ESP8266可用端口mqttClient->pCon->proto.tcp->remote_port = mqttClient->port;// 设置端口号mqttClient->pCon->reverse = mqttClient;// mqttClient->pCon->reverse 缓存 mqttClient指针espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb);// 注册TCP连接成功的回调函数espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb);// 注册TCP异常中断的回调函数//---------------------------------------------------------------------------------------------------mqttClient->keepAliveTick = 0;// MQTT客户端(ESP8266)心跳计数mqttClient->reconnectTick = 0;// 重连等待计时:当进入重连请求状态后,需等待5秒,之后进行重新连接// 设置MQTT定时(1秒)【功能:心跳计时、重连计时、TCP发送计时】//---------------------------------------------------------------------------------------------------os_timer_disarm(&mqttClient->mqttTimer);os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient);// mqtt_timeros_timer_arm(&mqttClient->mqttTimer, 1000, 1);// 1秒定时(重复)// 打印SSL配置:安全类型[NO_TLS == 0]//--------------------------------------------------------------------------------------------------------------------------------------------------------------os_printf("your ESP SSL/TLS configuration is %d.[0:NO_TLS\t1:TLS_WITHOUT_AUTHENTICATION\t2ONE_WAY_ANTHENTICATION\t3TWO_WAY_ANTHENTICATION]\n",DEFAULT_SECURITY);// 解析点分十进制形式的IP地址//------------------------------------------------------------------------------------------------------------------if (UTILS_StrToIP(mqttClient->host, &mqttClient->pCon->proto.tcp->remote_ip))// 解析IP地址(点分十进制字符串形式){INFO("TCP: Connect to ip %s:%d\r\n", mqttClient->host, mqttClient->port);// 打印IP地址// 根据安全类型,调用不同的TCP连接方式//-------------------------------------------------------------------------------------------------if (mqttClient->security)// 安全类型 != 0{#ifdef MQTT_SSL_ENABLEif(DEFAULT_SECURITY >= ONE_WAY_ANTHENTICATION )// 单向认证【ONE_WAY_ANTHENTICATION = 2】{espconn_secure_ca_enable(ESPCONN_CLIENT,CA_CERT_FLASH_ADDRESS);}if(DEFAULT_SECURITY >= TWO_WAY_ANTHENTICATION)// 双向认证【TWO_WAY_ANTHENTICATION = 3】{espconn_secure_cert_req_enable(ESPCONN_CLIENT,CLIENT_CERT_FLASH_ADDRESS);}espconn_secure_connect(mqttClient->pCon);// 不认证【TLS_WITHOUT_AUTHENTICATION = 1】#elseINFO("TCP: Do not support SSL\r\n");#endif}else// 安全类型 = 0 = NO_TLS{espconn_connect(mqttClient->pCon);// TCP连接(作为Client连接Server)}}// 解析域名//----------------------------------------------------------------------------------------------else{INFO("TCP: Connect to domain %s:%d\r\n", mqttClient->host, mqttClient->port);espconn_gethostbyname(mqttClient->pCon, mqttClient->host, &mqttClient->ip, mqtt_dns_found);}mqttClient->connState = TCP_CONNECTING;// TCP正在连接}//============================================================================================================================================// TCP断开连接(Clean up the old connection forcefully)//==============================================================================void ICACHE_FLASH_ATTR MQTT_Disconnect(MQTT_Client *mqttClient){mqttClient->connState = TCP_DISCONNECTING;// ESP8266状态改变:TCP正在断开system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient);// 安排任务os_timer_disarm(&mqttClient->mqttTimer);// 取消MQTT定时}//==============================================================================// 删除MQTT客户端//==============================================================================void ICACHE_FLASH_ATTR MQTT_DeleteClient(MQTT_Client *mqttClient){mqttClient->connState = MQTT_DELETING;// ESP8266状态改变:MQTT正在删除system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient);// 安排任务os_timer_disarm(&mqttClient->mqttTimer);// 取消MQTT定时}//==============================================================================// 函数调用重定义//………………………………………………………………………………………………………………………………………// 执行 mqttClient->connectedCb(...) => mqttConnectedCb(...)//------------------------------------------------------------------------------------------void ICACHE_FLASH_ATTR MQTT_OnConnected(MQTT_Client*mqttClient, MqttCallback connectedCb){mqttClient->connectedCb = connectedCb;// 函数名【mqttConnectedCb】}// 执行 mqttClient->disconnectedCb(...) => mqttDisconnectedCb(...)//-------------------------------------------------------------------------------------------------void ICACHE_FLASH_ATTR MQTT_OnDisconnected(MQTT_Client *mqttClient, MqttCallback disconnectedCb){mqttClient->disconnectedCb = disconnectedCb;// 函数名【mqttDisconnectedCb】}// 执行 mqttClient->dataCb(...) => mqttDataCb(...)//------------------------------------------------------------------------------------void ICACHE_FLASH_ATTR MQTT_OnData(MQTT_Client *mqttClient, MqttDataCallback dataCb){mqttClient->dataCb = dataCb;// 函数名【mqttDataCb】}// 执行 mqttClient->publishedCb(...) => mqttPublishedCb(...)//-------------------------------------------------------------------------------------------void ICACHE_FLASH_ATTR MQTT_OnPublished(MQTT_Client *mqttClient, MqttCallback publishedCb){mqttClient->publishedCb = publishedCb;// 函数名【mqttPublishedCb】}// 执行 mqttClient->timeoutCb(...) => 【...】未定义函数//--------------------------------------------------------------------------------------void ICACHE_FLASH_ATTR MQTT_OnTimeout(MQTT_Client *mqttClient, MqttCallback timeoutCb){mqttClient->timeoutCb = timeoutCb;}//………………………………………………………………………………………………………………………………………

user_main.c函数,相当于单片机的主函数,

void mqttConnectedCb(uint32_t *args)这个函数进行主题的订阅,自行照着修改即可,

void mqttDataCb(uint32_targs, const chartopic, uint32_t topic_len, const char *data, uint32_t data_len)这个函数是收到主题发布的消息后的回调函数,比如手机向switch主题发布“ON”,在esp8266订阅switch主题的前提下收到“ON”之后执行的函数,例如

if( os_strcmp(topicBuf,"first_floor_switch") == 0 )// 主题 == "frist_floor_switch"{if( os_strcmp(dataBuf,"SWITCH_ON") == 0 )// 有效载荷 == "SWITCH_ON"{GPIO_OUTPUT_SET(GPIO_ID_PIN(0),0);// switch开}}

这段代码是在void mqttDataCb(uint32_targs, const chartopic, uint32_t topic_len, const char *data, uint32_t data_len)这个函数里的判断语句,如果收到主题为 "frist_floor_switch"的"SWITCH_ON"消息就使GPIO0输出低电平,从而控制继电器开电操作。

void user_init(void)这个函数相当于单片机的主函数,这段代码首先是进行串口的初始化,以及GPIO的初始化,我初始化的是GPIO0为输出控制继电器的IO口,GPIO15为检测是否有电的IO口。

void user_init(void){uart_init(BIT_RATE_115200, BIT_RATE_115200);// 串口波特率设为115200os_delay_us(60000);// 初始化(注意【PIN_NAME】、【FUNC】、【gpio_no】不要混淆)//-------------------------------------------------------------------------PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U,FUNC_GPIO0);// GPIO0输出高#GPIO_OUTPUT_SET(GPIO_ID_PIN(0),1);// GPIO_0 = 1PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U,FUNC_GPIO15);// GPIO_15设为IO口GPIO_DIS_OUTPUT(GPIO_ID_PIN(15));// GPIO_15失能输出(默认)PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U);// GPIO_15失能内部上拉(默认)//PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDI_U);//PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO0_U);// GPIO_0使能内部上拉CFG_Load();// 加载/更新系统参数【WIFI参数、MQTT参数】// 网络连接参数赋值:服务端域名【mqtt_test_jx.mqtt.iot.】、网络连接端口【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");}

烧录进esp8266后进行复位重启,可利用串口调试助手查看esp8266是否接入百度云。

手机app代码

手机的APP我则是利用简单易懂的易安卓e4a语言开发的一款小app,不得不说易安卓简单,易上手,适合轻量级开发,中文编程支持一下,当然新语言还有很不足,不建议大家细学。

大家在安装e4a软件之后,直接打开中级例程里的mqtt通信,

这是例程源码,发送主题以及内容自行修改即可。

事件 按钮1.被单击()mqtt通讯1.连接服务器(编辑框1.内容,编辑框3.内容,编辑框4.内容,mqtt通讯1.取IMEI码(),真,假,5)结束 事件事件 按钮3.被单击()mqtt通讯1.发送消息("first_floor_switch",文本到字节("msg","UTF-8"),0,真)结束 事件事件 按钮4.被单击()mqtt通讯1.订阅消息("first_floor_switch",0)结束 事件事件 按钮5.被单击()mqtt通讯1.断开连接()结束 事件事件 mqtt通讯1.连接成功()弹出提示("连接成功")结束 事件事件 mqtt通讯1.连接失败()弹出提示("连接失败")结束 事件事件 mqtt通讯1.连接断开()弹出提示("连接断开")结束 事件事件 mqtt通讯1.订阅成功()弹出提示("订阅成功")结束 事件事件 mqtt通讯1.订阅失败()弹出提示("订阅失败")结束 事件事件 mqtt通讯1.发送完毕()弹出提示("发送完毕")结束 事件事件 mqtt通讯1.收到消息(消息主题 为 文本型, 消息内容 为 字节型(), 消息策略 为 整数型)编辑框2.内容 = "主题:" & 消息主题 & "\n内容:" & 字节到文本(消息内容,"UTF-8") & "\n策略:" & 消息策略结束 事件

例程的app界面

进行编译之后可运行在模拟器上安装,界面如图,第一行填地址,第二行填用户名,第三行填密钥(我也不知道为什么,一开始我就连接不上,后面就一直能连接了)。

输入完后点连接,连接成功的话会有消息提醒。

再进行订阅,最下面会显示订阅的主题以及发送内容,点击发送消息成功后就会弹出提醒,而esp8266则根据主题的消息内容进行判断进而控制继电器控制断电开电。

将所有模块放入插板后,就完成了手机控制的智能插座。

这里我进行了对APP的一些美化,大概这样,大家可根据自己喜好更改完善,中文编程很简单。

右下角是按钮,这是检测为有电的时候。

按下右下角按钮后即可断电,手机拿来拍照,手机上也一样,这里用的是模拟器。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。