逆向学习物联网-网关ESP8266-03软件编程实现

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 ); //接收到数据,解码处理,转发串口
        //超过多少次,如果还收不到信息,可能是网络断了,需要复位
    }

}
View Code

 

 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;    
}
View Code

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
View Code

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;
}
View Code

7)初始化参数

 

 

 

 

3.完整程序下载及说明

  程序网盘地址:

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

  提取码:jpxd

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

 

posted @ 2022-01-28 17:46  cqmcu_yth  阅读(219)  评论(0编辑  收藏  举报