1.技术原理及流程

1) MQTT数据通讯原理

 2).网关协议运行状态机

 3). 主程序流程

2.关键程序代码实现

MDK集成开发环境的搭建,大家可以百度搜索,或者参考感知层的软件设计部分。

1)主程序

2)网络状态机的处理程序

详细的处理程序如下:

void    Mqtt_Net_Init ( void )     //网络初始化,COM2  ,AT
{ Net_DefPara();
g_SystemStus = STA_WIFI_STARTCONNECT;
} //----------------------------------------
void fun_Wifi_StartConnect ( void )
{
if ( ESP8266_Init() == 0 )
{
g_SystemStus = STA_WIFI_CONNECTED;
}
else//等会重新连接
{
g_SystemStus = STA_WIFI_WAITCONNECT;
gg_oldtime = millis();//获取滴答时钟
} } void fun_Wifi_WaitConnect ( void )
{
if ( millis() - gg_oldtime > 5000 ) //5秒超时,等会重新连接
{
g_SystemStus = STA_WIFI_STARTCONNECT;
} } void fun_Wifi_Connected ( void )
{
//连接云服务
if ( ESP8266_ConnectCloud ( ) == 0 )
{
g_SystemStus = STA_MQTT_STARTCONNECT;
}
else
{
//原地踏步
} } void fun_Mqtt_StartConnect ( void )
{
if ( OneNet_DevLink ( ) == 0 )
{
g_SystemStus = STA_MQTT_CONNECTED;//连接成功,
}
else
{
g_SystemStus = STA_MQTT_WAITCONNECT;//超时重新连接
gg_oldtime = millis();//获取滴答时钟
}
} void fun_Mqtt_WaitConnect ( void )
{
if ( millis() - gg_oldtime > 5000 ) //5秒超时,等会重新连接
{
g_SystemStus = STA_WIFI_STARTCONNECT;
} } void fun_Mqtt_Subscribe ( void )
{
//订阅主题
if ( OneNet_Subscribe ( g_SysPara.mqtt_subtopic1 ) == 0 )
{
if ( OneNet_Subscribe ( g_SysPara.mqtt_subtopic2 ) == 0 )
{
g_SystemStus = STA_SYSTEM_WORK;
gg_oldtime = millis();//获取滴答时钟
return;
}
} //订阅失败
g_SystemStus = STA_MQTT_WAITSUBCRIBE;
gg_oldtime = millis();//获取滴答时钟 } void fun_Mqtt_WaitSubcribe ( void )
{
if ( millis() - gg_oldtime > 5000 ) //5秒超时,等会重新连接
{
g_SystemStus = STA_MQTT_CONNECTED;
}
} void fun_System_Work ( void )
{
unsigned char *cmd = NULL; //心跳包,正常数据传输,每3秒
if ( millis() - gg_oldtime > 120000L ) //120秒超时,等会重新连接, 实际后台是keep alive 256s
{
OneNet_HeartBeat();
gg_oldtime = millis();
} //3.接收到网络数据的处理 :订阅的数据
if ( ( cmd = ESP8266_GetIPD_main() ) != NULL ) //5秒判断接收的数据
{ OneNet_RevPro ( cmd ); //接收到数据,解码处理,转发串口
//超过多少次,如果还收不到信息,可能是网络断了,需要复位
} }

3)串口接收处理程序

串口接收数据终端服务程序:

4)循环数据缓冲池(循环队列):用于串口接收

void Que_Init(STRUCT_QUEUE *dd)
{
dd->begin=0;
dd->end=0;
dd->count=0; } unsigned long Que_GetCount(STRUCT_QUEUE *dd)
{ return dd->count;
} void Que_AddDataOne(STRUCT_QUEUE *dd,unsigned char ch)
{ if(dd->count>QUE_SIZE)return; dd->buffer[dd->end]=ch; dd->end=(dd->end+1)%QUE_SIZE;
dd->count++;
} //读取1个数值
unsigned char Que_ReadOne(STRUCT_QUEUE *dd)
{
unsigned char ch=dd->buffer[dd->begin]; dd->begin=(dd->begin+1)%QUE_SIZE;
dd->count--;
return ch;
}

5)MQTT开发包头文件说明

#ifndef _MQTTKIT_H_
#define _MQTTKIT_H_ #include "Common.h" //=============================配置==============================
//===========可以提供RTOS的内存管理方案,也可以使用C库的=========
//RTOS
#include <stdlib.h> #define MQTT_MallocBuffer malloc #define MQTT_FreeBuffer free
//========================================================== #define MOSQ_MSB(A) (uint8)((A & 0xFF00) >> 8)
#define MOSQ_LSB(A) (uint8)(A & 0x00FF) /*--------------------------------内存分配方案标志--------------------------------*/
#define MEM_FLAG_NULL 0
#define MEM_FLAG_ALLOC 1
#define MEM_FLAG_STATIC 2 typedef struct Buffer
{ uint8 *_data; //协议数据 uint32 _len; //写入的数据长度 uint32 _size; //缓存总大小 uint8 _memFlag; //内存使用的方案:0-未分配 1-使用的动态分配 2-使用的固定内存 } MQTT_PACKET_STRUCTURE; /*--------------------------------固定头部消息类型--------------------------------*/
enum MqttPacketType
{ MQTT_PKT_CONNECT = 1, /**< 连接请求数据包 */
MQTT_PKT_CONNACK, /**< 连接确认数据包 */
MQTT_PKT_PUBLISH, /**< 发布数据数据包 */
MQTT_PKT_PUBACK, /**< 发布确认数据包 */
MQTT_PKT_PUBREC, /**< 发布数据已接收数据包,Qos 2时,回复MQTT_PKT_PUBLISH */
MQTT_PKT_PUBREL, /**< 发布数据释放数据包, Qos 2时,回复MQTT_PKT_PUBREC */
MQTT_PKT_PUBCOMP, /**< 发布完成数据包, Qos 2时,回复MQTT_PKT_PUBREL */
MQTT_PKT_SUBSCRIBE, /**< 订阅数据包 */
MQTT_PKT_SUBACK, /**< 订阅确认数据包 */
MQTT_PKT_UNSUBSCRIBE, /**< 取消订阅数据包 */
MQTT_PKT_UNSUBACK, /**< 取消订阅确认数据包 */
MQTT_PKT_PINGREQ, /**< ping 数据包 */
MQTT_PKT_PINGRESP, /**< ping 响应数据包 */
MQTT_PKT_DISCONNECT, /**< 断开连接数据包 */ //新增 MQTT_PKT_CMD /**< 命令下发数据包 */ }; /*--------------------------------MQTT QOS等级--------------------------------*/
enum MqttQosLevel
{ MQTT_QOS_LEVEL0, /**< 最多发送一次 */
MQTT_QOS_LEVEL1, /**< 最少发送一次 */
MQTT_QOS_LEVEL2 /**< 只发送一次 */ }; /*--------------------------------MQTT 连接请求标志位,内部使用--------------------------------*/
enum MqttConnectFlag
{ MQTT_CONNECT_CLEAN_SESSION = 0x02,
MQTT_CONNECT_WILL_FLAG = 0x04,
MQTT_CONNECT_WILL_QOS0 = 0x00,
MQTT_CONNECT_WILL_QOS1 = 0x08,
MQTT_CONNECT_WILL_QOS2 = 0x10,
MQTT_CONNECT_WILL_RETAIN = 0x20,
MQTT_CONNECT_PASSORD = 0x40,
MQTT_CONNECT_USER_NAME = 0x80 }; /*--------------------------------消息的packet ID,可自定义--------------------------------*/
#define MQTT_PUBLISH_ID 10 #define MQTT_SUBSCRIBE_ID 20 #define MQTT_UNSUBSCRIBE_ID 30 /*--------------------------------删包--------------------------------*/
void MQTT_DeleteBuffer ( MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------解包--------------------------------*/
uint8 MQTT_UnPacketRecv ( uint8 *dataPtr ); /*--------------------------------登录组包--------------------------------*/
uint8 MQTT_PacketConnect ( const int8 *user, const int8 *password, const int8 *devid,
uint16 cTime, uint1 clean_session, uint1 qos,
const int8 *will_topic, const int8 *will_msg, int32 will_retain,
MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------断开连接组包--------------------------------*/
uint1 MQTT_PacketDisConnect ( MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------连接响应解包--------------------------------*/
uint8 MQTT_UnPacketConnectAck ( uint8 *rev_data ); /*--------------------------------命令下发解包--------------------------------*/
uint8 MQTT_UnPacketCmd ( uint8 *rev_data, int8 **cmdid, int8 **req, uint16 *req_len ); /*--------------------------------命令回复组包--------------------------------*/
uint1 MQTT_PacketCmdResp ( const int8 *cmdid, const int8 *req, MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------订阅主题组包--------------------------------*/
uint8 MQTT_PacketSubscribe ( uint16 pkt_id, enum MqttQosLevel qos, const char *topic, MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------订阅主题回复解包--------------------------------*/
uint8 MQTT_UnPacketSubscribe ( uint8 *rev_data ); /*--------------------------------取消订阅组包--------------------------------*/
uint8 MQTT_PacketUnSubscribe ( uint16 pkt_id, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------取消订阅回复解包--------------------------------*/
uint1 MQTT_UnPacketUnSubscribe ( uint8 *rev_data ); /*--------------------------------发布主题组包--------------------------------*/
uint8 MQTT_PacketPublish ( uint16 pkt_id, const int8 *topic,
const int8 *payload, uint32 payload_len,
enum MqttQosLevel qos, int32 retain, int32 own,
MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------发布消息回复解包--------------------------------*/
uint8 MQTT_UnPacketPublish ( uint8 *rev_data, int8 **topic, uint16 *topic_len, int8 **payload, uint16 *payload_len, uint8 *qos, uint16 *pkt_id ); /*--------------------------------发布消息的Ack组包--------------------------------*/
uint1 MQTT_PacketPublishAck ( uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------发布消息的Ack解包--------------------------------*/
uint1 MQTT_UnPacketPublishAck ( uint8 *rev_data ); /*--------------------------------发布消息的Rec组包--------------------------------*/
uint1 MQTT_PacketPublishRec ( uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------发布消息的Rec解包--------------------------------*/
uint1 MQTT_UnPacketPublishRec ( uint8 *rev_data ); /*--------------------------------发布消息的Rel组包--------------------------------*/
uint1 MQTT_PacketPublishRel ( uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------发布消息的Rel解包--------------------------------*/
uint1 MQTT_UnPacketPublishRel ( uint8 *rev_data, uint16 pkt_id ); /*--------------------------------发布消息的Comp组包--------------------------------*/
uint1 MQTT_PacketPublishComp ( uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket ); /*--------------------------------发布消息的Comp解包--------------------------------*/
uint1 MQTT_UnPacketPublishComp ( uint8 *rev_data ); /*--------------------------------心跳请求组包--------------------------------*/
uint1 MQTT_PacketPing ( MQTT_PACKET_STRUCTURE *mqttPacket ); #endif

6)Esp8266AT指令使用程序代码

//单片机头文件
#include "main.h" extern volatile STRUCT_QUEUE g_Queue_Esp; void ISR_USART3_Recv_Fun ( void );
//==========================================================
// 函数名称: ESP8266_Clear
//
// 函数功能: 清空缓存
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_Clear ( void )
{
Que_Init ( ( STRUCT_QUEUE * ) &g_Queue_Esp ); }
u8 MYstrstr ( unsigned char *strSrc, u16 num, unsigned char *str )
{
u16 i;
u16 count = strlen ( ( char * ) str ); for ( i = 0; i < num; i++ )
{ if ( memcmp ( ( strSrc + i ), str, count ) == 0 )
{
return 0;
}
} return 1;
} //==========================================================
// 函数名称: ESP8266_SendCmd
//
// 函数功能: 发送命令
//
// 入口参数: cmd:命令
// res:需要检查的返回指令
//
// 返回参数: 0-成功 1-失败
//
// 说明:
//==========================================================
_Bool ESP8266_SendCmd ( char *cmd, char *res, u16 dly )
{ unsigned int timeOut = dly;
u16 num = 0; ESP8266_Clear(); printf ( "%s\n", cmd );
Usart2_SendString ( ( unsigned char * ) cmd, strlen ( ( const char * ) cmd ) ); while ( timeOut-- )
{
num = Que_GetCount ( ( STRUCT_QUEUE * ) &g_Queue_Esp );
if ( num ) //如果收到数据
{
if ( MYstrstr ( ( unsigned char * ) g_Queue_Esp.buffer, num, ( unsigned char * ) res ) == 0 ) //如果检索到关键词
{
//等待回车换行接收完成
delay_ms ( 10 );
num = Que_GetCount ( ( STRUCT_QUEUE * ) &g_Queue_Esp );
Debug_Print ( ( unsigned char * ) g_Queue_Esp.buffer, num ); ESP8266_Clear();
return 0;
}
}
delay_ms ( 1 );
} delay_ms ( 10 );
Debug_Print ( ( unsigned char * ) g_Queue_Esp.buffer, num ); ESP8266_Clear(); return 1; } //==========================================================
// 函数名称: ESP8266_SendData
//
// 函数功能: 发送数据
//
// 入口参数: data:数据
// len:长度
//
// 返回参数: 无
//
// 说明:
//========================================================== void ESP8266_SendData ( unsigned char *data, unsigned short len )
{ char cmdBuf[32]; ESP8266_Clear(); //清空接收缓存
sprintf ( cmdBuf, "AT+CIPSEND=%d\r\n", len ); //发送命令
if ( !ESP8266_SendCmd ( cmdBuf, ">", 400 ) ) //收到‘>’时可以发送数据
{
delay_ms ( 10 );//非常重要,否则手机模块传送不回来。!!!!
Usart2_SendString ( data, len ); //发送设备连接请求数据
}
else
{
//if(strstr ( ( char * ) g_Queue_Esp.buffer, "link is not valid" ))//断网检测
//网络不在线,重头开始连接网络
g_SystemStus = STA_WIFI_STARTCONNECT; } } //==========================================================
// 函数名称: ESP8266_GetIPD_link
//
// 函数功能: 获取平台返回的数据
//
// 入口参数: 等待的时间(乘以10ms)
//
// 返回参数: 平台返回的原始数据
//
// 说明: 不同网络设备返回的格式不同,需要去调试
// 如ESP8266的返回格式为 "+IPD,x:yyy" x代表数据长度,yyy是数据内容
//==========================================================
unsigned char *ESP8266_GetIPD_link ( unsigned short timeOut )
{ unsigned long num = 0; char *ptrIPD = NULL; do
{
num = Que_GetCount ( ( STRUCT_QUEUE * ) &g_Queue_Esp );
if ( num > 0 )
{
delay_ms ( 100 ); ptrIPD = strstr ( ( char * ) g_Queue_Esp.buffer, "IPD," );
if ( ptrIPD != NULL )
{
ptrIPD = strchr ( ptrIPD, ':' ); //找到':'
if ( ptrIPD != NULL )
{
ptrIPD++;
return ( unsigned char * ) ( ptrIPD );
}
} } delay_ms ( 2 ); //延时等待 }
while ( timeOut-- ); return NULL; //超时还未找到,返回空指针 } unsigned char *ESP8266_GetIPD_main ( void )
{ char *ptrIPD = NULL;
unsigned long num = 0;
static int timeout = 0;
timeout++; if ( timeout < 5 )
{
return NULL;
}
timeout = 0; num = Que_GetCount ( ( STRUCT_QUEUE * ) &g_Queue_Esp );
if ( num ) //如果接收完成
{
delay_ms ( 200 ); ptrIPD = strstr ( ( char * ) g_Queue_Esp.buffer, "IPD," ); //搜索“IPD”头
if ( ptrIPD == NULL ) //如果没找到,可能是IPD头的延迟,还是需要等待一会,但不会超过设定的时间
{
//printf ( "\"IPD\" not found\r\n" );
return NULL;
}
else
{
ptrIPD = strchr ( ptrIPD, ':' ); //找到':'
if ( ptrIPD != NULL )
{
ptrIPD++;
return ( unsigned char * ) ( ptrIPD );
}
else
{
return NULL;
} }
} return NULL; //超时还未找到,返回空指针 } //==========================================================
// 函数名称: ESP8266_Init
//
// 函数功能: 初始化ESP8266
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
u8 ESP8266_Init ( void )
{ char strESP8266_WIFI_INFO[100] = {0}; // GPIO_InitTypeDef GPIO_Initure; sprintf ( strESP8266_WIFI_INFO, "AT+CWJAP=\"%s\",\"%s\"\r\n", g_SysPara.wifi_ssid, g_SysPara.wifi_password ); Usart2_Init ( 115200 );//USART2:串口接ZIGBEE协调器115200 Usart2_Int ( 1 );
// RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOA, ENABLE ); // //ESP8266复位引脚
// GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP;
// GPIO_Initure.GPIO_Pin = GPIO_Pin_0; //GPIOC14-复位
// GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_Init ( GPIOA, &GPIO_Initure ); // GPIO_WriteBit ( GPIOA, GPIO_Pin_0, Bit_RESET );
// delay_ms ( 50 );
// GPIO_WriteBit ( GPIOA, GPIO_Pin_0, Bit_SET );
// delay_ms ( 5000 ); ESP8266_Clear(); if ( ESP8266_SendCmd ( "AT\r\n", "OK", 100 ) )
{ delay_ms ( 1500 ); if ( ESP8266_SendCmd ( "AT\r\n", "OK", 100 ) )
{
//异常
return 1;
//while ( 1 );
}
}
if ( ESP8266_SendCmd ( "ATE0\r\n", "OK", 100 ) )
{ delay_ms ( 1500 ); if ( ESP8266_SendCmd ( "AT\r\n", "OK", 100 ) )
{
//异常
return 1;
//while ( 1 );
}
} if ( ESP8266_SendCmd ( "AT+CWMODE=1\r\n", "OK", 100 ) ) // 设置为 station 模式
{
return 1;
} if ( ESP8266_SendCmd ( "AT+CIPMODE=0\r\n", "OK", 100 ) ) // 0-非透传模式
{
return 1;
} if ( ESP8266_SendCmd ( strESP8266_WIFI_INFO, "CONNECTED", 13000 ) ) //"GOT IP" 连接路由器
{
if ( ESP8266_SendCmd ( strESP8266_WIFI_INFO, "CONNECTED", 13000 ) )
{
return 1; }
}
delay_ms ( 5000 );//WIFI GOT IP,这里如果时间短了,就会出现冲突,发起2次连接的问题。
ESP8266_Clear();
return 0; } //AT+CIPSTART="TCP","183.230.40.39",6002 注意端口
u8 ESP8266_ConnectCloud ( void )
{
char strstrESP8266_WIFI_INFO[100] = {0}; sprintf ( strstrESP8266_WIFI_INFO, "AT+CIPSTART=\"TCP\",\"%s\",%s\r\n", g_SysPara.host_server, g_SysPara.host_port );
delay_ms ( 1500 );
if ( ESP8266_SendCmd ( strstrESP8266_WIFI_INFO, "CONNECT", 22500 ) )
{ delay_ms ( 3500 );
if ( ESP8266_SendCmd ( strstrESP8266_WIFI_INFO, "CONNECT", 22500 ) )
{
//PIN_LED = 1;
return 1;
//while ( 1 );
}
}
return 0;
}

7)初始化参数

3.完整程序下载及说明

程序网盘地址:

链接:https://pan.baidu.com/s/1fHd4xYe_gNJy0xns0iSE4g

提取码:jpxd

程序依赖于硬件(光明顶开发板-WCH32F103C8T6),可以联系作者购买。

最新文章

  1. WinRAR 4.20 beta2 key!注册文件 注册码
  2. 关于js写全选的方法
  3. CabArc to create or extract a cab file
  4. 本地自定义了404 和500 错误处理 部署到IIS上显示 服务器内部错误
  5. 向linux内核中添加外部中断驱动模块
  6. android之消息机制(二)
  7. 安装openJDK 8
  8. NodeJS安全设计:好吃的草莓味糖果,只给好朋友小红
  9. MVC5 学习笔记2
  10. Binary Tree Level Order Traversal 解题思路 &#215;
  11. 安装orcle10g oel5.6
  12. MYSQL数据库-约束
  13. 在Linux(ubuntu 14.04)上部署WeX5跨平台App(HTML5)
  14. koa2+webSocket 聊天室
  15. Service Worker MDN英文笔记
  16. ArcGis Python脚本——批量对影像、要素类定义投影
  17. Android开发工程师文集-1 小时学会Widget小组件开发
  18. mysql contact_ws函数 字符串被截断问题
  19. 每日英语:In Digital Era, What Does &#39;Watching TV&#39; Even Mean?
  20. html标签种类

热门文章

  1. NTP网络时间服务器(时间同步服务器)产品介绍及技术研究分析
  2. LAN8720 调试笔记
  3. Kubernetes--容器重启策略和Pod终止过程
  4. 一、100ASK_IMX6ULL嵌入式裸板学习_LED实验(知识点补充二)
  5. windows server 2012以上版本离线安装 net framework3.5 方法
  6. pyspark 结构化数据开发实例
  7. props其他-混入mixin-插件-elementui使用-localStorage系列-vueRouter-vuex
  8. Charles抓包工具详解
  9. python 的多线程
  10. 树莓派,脚本遍历当前目录下视频文件,并用omxplayer播放