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

19年的春节,相信大家和我一样都待在家里,利用在家的时间现学现卖,制作了一款手机控制的智能插座,网上资料很多,我在查询资料中发现,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代码

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

链接:https://pan.baidu.com/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.gz.baidubce.com"        // MQTT服务端域名/IP地址   // the IP address or domain name of your MQTT server or MQTT broker ,such as "mqtt.yourdomain.com"
#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_TYPE            AUTH_WPA2_PSK#define DEFAULT_SECURITY   NO_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_SIZE              2048        // 消息队列的缓存大小//#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 https://eclipse.org/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_REQ            TCP重连请求(等待5秒)   退出Tsak(5秒后,进入TCP_RECONNECT状态)
//--------------------------------------------------------------------------------------------
// TCP_RECONNECT                TCP重新连接             执行MQTT连接准备,并设置ESP8266状态
//--------------------------------------------------------------------------------------------
// MQTT_DELETING                MQTT正在删除            TCP断开连接
// TCP_DISCONNECTING            TCP正在断开
// TCP_RECONNECT_DISCONNECTING  TCP暂时断开(断开后会重连)
//--------------------------------------------------------------------------------------------
// TCP_DISCONNECTED             TCP成功断开             删除TCP连接,并释放pCon内存
//--------------------------------------------------------------------------------------------
// MQTT_DELETED                 MQTT已删除             删除MQTT客户端,并释放相关内存
//--------------------------------------------------------------------------------------------
// MQTT_KEEPALIVE_SEND          MQTT心跳              向服务器发送心跳报文
//--------------------------------------------------------------------------------------------
// MQTT_DATA                    MQTT数据传输            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.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客户端】结构体 = 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_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】报文的连接参数 = 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_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】
}
//====================================================================================================================/*** @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_t args, const char topic, 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_t args, const char topic, 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.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");}

烧录进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的一些美化,大概这样,大家可根据自己喜好更改完善,中文编程很简单。
右下角是按钮,这是检测为有电的时候。

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

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

  1. Esp8266 进阶之路29【高级篇】百万条消息免费之乐鑫esp8266使用TCP直连模式MQTT协议接入阿里云物联网平台,支持私家服务器对接,支持阿里云规则引擎。(附带Demo)

    本系列博客学习由非官方人员 半颗心脏 潜心所力所写,不做开发板.仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 序号 SDK版本 内容 链接 1 nonos2.0 搭建 ...

  2. micropython mqtt_MicroPython使用MQTT协议接入OneNET云平台

    MicroPython使用MQTT协议接入OneNET云平台 [复制链接] 本帖最后由 hanyeguxingwo 于 2016-11-22 11:33 编辑 之前使用Arduino+ESP8266使 ...

  3. 利用MQTT协议与阿里云数据交互的python程序

    利用MQTT协议与阿里云数据交互的python程序 设计目的 功能要求和关键问题 环境配置问题 本地程序如何连接云上设备 云上的数据交互问题 界面设计问题 阿里云相关操作 本地程序 设计目的 设计开发 ...

  4. 移远BC26模组使用MQTT协议接入阿里云(NB-IoT专栏—进阶篇4)

    目录 1.模块简介 2.阿里云物联网服务部署 3.BC26模组配置 4.数据上传 5.数据下发

  5. esp8266接入百度云,使用客户端控制灯亮灭

    esp8266接入百度云 摘要:本篇文章介绍了esp8266如何接入百度云的整个流程,最终实现一个简单的小demo,通过mqtt客户端去去控制esp8266上小灯的点亮与关闭.文章包含三个部分: 一. ...

  6. STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云

    STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云 目录 STM32+ESP8266(ESP-12F)物联网温度计-移植paho MQTT协议连接阿里云 一 ...

  7. 使用网络调试助手通过MQTT协议接入到华为云物联网平台

    一.写在之前 首先分享一篇华为云的官方指南:在线开发MQTT协议的智慧路灯_设备接入 IoTDA_最佳实践_设备接入_华为云 这篇文章很好地讲述了如何通过MQTT.fx的设备模拟器替代真实的设备,然后 ...

  8. 树莓派mqtt协议连接阿里云物联网平台,手机端获取数据并控制

    树莓派mqtt协议连接阿里云物联网平台(三) 前面树莓派的数据已经上传到云端,可是我的android手机该如何获取树莓派上传的这些数据呢,,困惑了我好几天的疑问,解开的那一刻,真的时拨开云雾见青天啊. ...

  9. 中移ML302模组通过MQTT协议接入oneNT平台

    @中移ML302模组通过MQTT协议接入oneNT平台 ML302 是中国移动最新推出的 LTE Cat.1 模块. 丰富的 Internet 协议.行业标准接口和功能,支持 Windows.Linu ...

最新文章

  1. ZOJ 3781 最短路(想法好题目)
  2. 采购定价过程字段解析
  3. Redis面试常见问题与详解
  4. ms计算机选择题,MS计算机选择题.docx
  5. 文件按m3u8顺序合并_在线视频下载之m3u8篇
  6. python网络爬虫的学习
  7. 大数据产业链结构_【数据结构 | 大整型】
  8. matlab设计激光腔,激光原理课程设计
  9. 苹果app商品定价_苹果官网闹乌龙,千元产品变百元!多人闻风薅羊毛!
  10. Drupal 曝出代码执行高危漏洞,数百万网站受影响
  11. 学习哪一款EDA软件画PCB电路板比较好?AD、PADS与Allegro的比较
  12. 新概念英语(第三册,新版)学习(原文及全文翻译)
  13. java爬虫(爬取豆瓣电影排行榜)
  14. 能力与爱好 我能熟练使用计算机,关于2020大学生简历自我评价精选5篇(心选优品)...
  15. 三、Echart图表 之 X轴(xAxis)与 Y轴(yAxis)配置项大全
  16. 中国目前的人工智能在全球处于什么水平?
  17. Verilog运算符优先级
  18. 正则表达式切掉log日志前面不需要的内容
  19. 阿里云+VMware会擦出怎样的火花?
  20. 洛谷 T6476 涂色游戏

热门文章

  1. <第6个月>shopee运营日记,2021年shopee还值不值得做?能赚钱吗
  2. 安装VS2022后报错 _CRT_SECURE_NO_WARNINGS
  3. flinkcdc实时监测oracle数据库某张表的变化
  4. 黑盒优化技术评测基准RABBO介绍
  5. 【高等数学如何学,做题方法,期末突击】
  6. Python mongodb数据库
  7. Python3.6+jieba+wordcloud 爬取豆瓣影评生成词云
  8. Linux实战教学笔记15:磁盘原理
  9. 低功耗蓝牙学习笔记-属性协议
  10. 【第五人格设计思路】囚徒“蝰”·时装设计思路