前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ESA2GJK1DH1K基础篇: APP使用SmartConfig绑定Wi-Fi 设备并通过MQTT控制设备(SimplePackage)

ESA2GJK1DH1K基础篇: APP使用SmartConfig绑定Wi-Fi 设备并通过MQTT控制设备(SimplePackage)

作者头像
杨奉武
发布2020-03-23 11:45:03
6900
发布2020-03-23 11:45:03
举报
文章被收录于专栏:知识分享

前言

  近期刚刚封装好了比较完善的MQTT库

  后期的文章将对最新封装的库做一下补充

  如果是初学者可以先学习51单片机实现MQTT实现通信控制的文章

https://cloud.tencent.com/developer/article/1602665

  之所以又封装了一套是因为前面用的官方的库太大,小容量的单片机无法承受

  当然主要还是为了大家可以方便理解MQTT协议

  用51单片机跑了MQTT,这样便可以方便让更多的人入门学习.

实现功能概要

   STM32控制WI-Fi模块以AT指令TCP透传方式连接MQTT服务器, 实现MQTT通信控制.

测试准备工作(详细下载步骤请参考 硬件使用说明 )

一,下载单片机程序

  工程目录: STM32F10xTemplate\Progect

  hex文件目录: STM32F10xTemplate\Progect\Progect

二,安装APP软件

三,调整波动开关位置,STM32和Wi-Fi通信

四,短接STM32的PB2和Wi-Fi模块的RST引脚(为了做项目稳定可靠,请使用单片机硬件复位Wi-Fi)

注意:版本号2.5及其以上的版本不需要跳线

注意:版本号2.5及其以上的版本不需要跳线

注意:版本号2.5及其以上的版本不需要跳线

开始测试

一.打开手机APP,点击右上角菜单 "添加设备" ,手动输入自家路由器密码.(路由器名称为自动获取,不需要用户填写)

二.长按PB5大约4S,等待指示灯快闪,松开PB5,Wi-Fi模块进入配网状态

三.点击APP的搜索设备按钮,开始搜索设备,搜索成功,将自动跳转到主页面,并显示设备

四.单片机控制Wi-Fi连接上MQTT服务器以后,指示灯1S闪耀

五.点击设备进入,设备控制页面,页面显示当前温湿度数据,显示当前设备的状态

六.远程控制继电器吸合

七.远程控制继电器断开

八.请自行控制家电(最大支持10A,注意安全!)

关于程序

  整个程序是STM32使用AT指令控制Wi-Fi模块实现SmartConfig配网和MQTT通信控制

  程序的整体结构:   https://cloud.tencent.com/developer/article/1520558

  程序的按键处理:   https://cloud.tencent.com/developer/article/1520555

  串口接收数据   :    https://cloud.tencent.com/developer/article/1520554

  配置AT指令模板(阻塞版):  https://cloud.tencent.com/developer/article/1521443

  配置AT指令模板(非阻塞版): https://cloud.tencent.com/developer/article/1521442

SmartConfig实现部分

  一,AT指令配置模块启动SmartConfig的程序处理模板是:配置AT指令模板(阻塞版) 

  二,按键按下3S以后 变量 SmartConfigFlage = 1;

    定时器里面开始控制 指示灯100Ms闪耀

  三,AT指令控制Wi-Fi模块执行SmartConfig 配网程序部分

  三,SmartConfig执行流程

    实际上启用SmartConfig指令是   AT+CWSTARTSMART=3\r\n    

    最后的参数 1-SmartConfig配网    2-微信Airkiss配网    3-SmartConfig配网+微信Airkiss配网

    下面进入了 while(1) 循环    我设置的30S超时

    实际上此时Wi-Fi模块正在监听APP在空气中发出的无线信号

    下图只要执行了搜索设备,APP就在不停的发出无线信号

    Wi-Fi模块接收到APP发出的路由器信息以后,就会根据信息去连接路由器

    Wi-Fi模块连接上了路由器以后便会返回  WIFI CONNECTED  和  WIFI GOT IP

    注:只要配网一次,以后Wi-Fi模块便会自动连接此路由器,不需要重复配网!

  四,SmartConfig执行流程-等待路由器把自己的MAC信息返回给APP

    为了让APP确定Wi-Fi模块确实连接上了路由器,Wi-Fi连接上路由器以后

    需要返回给APP自己的MAC地址和自己连接路由器后分得的IP地址

    所以延时了5S时间,让Wi-Fi模块把信息发给APP

    下图中,显示的就是所配网的Wi-Fi模块的MAC地址信息

    当然MAC地址很有用(全球唯一),通信的时候可以用来区分设备.

MQTT实现部分

一,前言

    Wi-Fi模块发布的主题: device/设备MAC  

    Wi-Fi模块订阅的主题: user/设备MAC

    APP通过SmartConfig获取Wi-Fi的MAC,然后设置

    订阅的主题:device/设备MAC  

    发布的主题:user/设备MAC

  二,连接TCP服务器(MQTT服务器)

    AT指令配置模块连接TCP的程序处理模板是:配置AT指令模板(非阻塞版) 

    配置Wi-Fi模块连接TCP服务器是使用的  "AT+SAVETRANSLINK=1,\"%s\",%s,\"TCP\"\r\n",IP,Port

    这个指令配置好以后,Wi-Fi模块便是透传模式,而且是自动连接

    (串口接收的数据,自动发给TCP服务器)

    (从TCP服务器接收的数据自动发给串口)

三,连接MQTT

四,判断是够连接成功

  五,连接成功以后订阅主题

    提示:该底层库大部分都是使用了注册回调函数的形式

  六,发布消息

  七,接收处理消息

  八,处理心跳包

    处理心跳包已经封装在了内部,只需要在连接上MQTT的地方写上处理函数即可!

结语

  这版的MQTT底层包采用了数据缓存处理的方式

  更加详细的移植使用,请继续阅读后续文章

  支持处理消息等级0,1,2

  主要代码如下

代码语言:javascript
复制
/**
* @brief   发送MQTT数据
* 把发送数据给网络模块的函数放在此处
* @param   buffer   发送的数据
* @param   length   数据长度
* @retval  None
* @warning None
* @example
**/
void mqtt_send_function(mqtt_t *mqtt)
{
    /*有缓存的数据需要发送;同时未启动超时检测*/
    if(mqtt->buff_manage_struct_t.SendLen !=0 && mqtt->timer_out_cnt<=0)//需要发送数据
    {
        if(mqtt_get_type(mqtt->send_buff) == MQTT_MSG_TYPE_SUBSCRIBE){//发送的消息是订阅
            mqtt->mqtt_message_id = mqtt_get_id(mqtt->send_buff, mqtt->buff_manage_struct_t.SendLen);//获取消息ID
            mqtt->mqtt_message_type = MQTT_MSG_TYPE_SUBSCRIBE;
            mqtt->timer_out_cnt = mqtt_timerout_default;//设置超时时间
        }
        else if(mqtt_get_type(mqtt->send_buff) == MQTT_MSG_TYPE_PUBLISH){//发送的消息是发布
            if(mqtt_get_qos(mqtt->send_buff) == 1 || mqtt_get_qos(mqtt->send_buff) == 2)//消息等级是1或者2
            {
                mqtt->mqtt_message_id = mqtt_get_id(mqtt->send_buff, mqtt->buff_manage_struct_t.SendLen);//获取消息ID,以便应答
                mqtt->mqtt_message_type = MQTT_MSG_TYPE_PUBLISH;
                mqtt->timer_out_cnt = mqtt_timerout_default;//设置超时时间
            }
        }
        //使用串口发送数据(发给网络模块):请替换自己的发送函数
        //mqtt->send_buff:发送的数据
        //mqtt->buff_manage_struct_t.SendLen:发送的数据长度
        //Send function
        UsartOutStrIT(mqtt->send_buff,mqtt->buff_manage_struct_t.SendLen);//请替换自己的发送函数
        mqtt->buff_manage_struct_t.SendLen=0;
    }
}


/**
* @brief   接收处理MQTT数据
*用户需要把网络模块接收到的MQTT数据传给此函数处理
* @param   buffer   接收的MQTT数据
* @param   length   数据长度
* @retval  None
* @warning None
* @example
**/
void mqtt_read_function(mqtt_t *mqtt,unsigned char* buffer, uint16_t length)
{
    uint8_t msg_type;
    uint8_t msg_qos;
    uint16_t msg_id;
    
    msg_type = mqtt_get_type(buffer);
    msg_qos = mqtt_get_qos(buffer);
    msg_id = mqtt_get_id(buffer, length);
    
    if(mqtt->mqtt_message_type == MQTT_MSG_TYPE_SUBSCRIBE){//上次发送的是订阅主题
        mqtt_keep_alive_init(mqtt);//初始化心跳包变量
        //上次发送的消息是订阅
        if(msg_type == MQTT_MSG_TYPE_SUBACK){//获取应答
            mqtt->timer_out_cnt = 0;//停止超时定时器
            
            if(msg_id == mqtt->mqtt_message_id && ( buffer[length-1]&0xff) != 0x80 )
            {
                mqtt->mqtt_message_type = MQTT_MSG_TYPE_SUBACK;
                if(mqtt->subscribedCb!=NULL){
                    mqtt->subscribedCb(mqtt->mqtt_message_id);
                }
            }
            else
            {
                mqtt->mqtt_message_type = 0;
                if(mqtt->failsubscribedCb!=NULL)mqtt->failsubscribedCb(mqtt->mqtt_message_id);
            }
        }
        else{
            mqtt->mqtt_message_type = 0;
            if(mqtt->failsubscribedCb!=NULL)mqtt->failsubscribedCb(mqtt->mqtt_message_id);
        }
    }
    
    switch (msg_type)
    {
        case MQTT_MSG_TYPE_PUBLISH://接收到消息
            mqtt_keep_alive_init(mqtt);//初始化心跳包变量
            if (msg_qos == 1){//消息等级是1,打包需要返回的PUBACK数据
                mqtt->mqtt_send_data_len = mqtt_msg_puback(msg_id,&mqtt->ptr,mqtt->mqtt_data_buff,mqtt_send_buff_len);
            }
            else if (msg_qos == 2){//消息等级是2,打包需要返回的PUBREC
                mqtt->mqtt_send_data_len = mqtt_msg_pubrec(msg_id,&mqtt->ptr,mqtt->mqtt_data_buff,mqtt_send_buff_len);
            }
            
            if (msg_qos == 1 || msg_qos == 2) {
                    if(mqtt->mqtt_send_data_len >0 ){                        
                        BufferManageWrite(&mqtt->buff_manage_struct_t,mqtt->ptr,mqtt->mqtt_send_data_len);/*把协议存入存入缓存*/
                }
            }
            
            //调用接收回调函数
            if(mqtt->recCb){
                mqtt->topic_length = length;
                mqtt->topic = mqtt_get_publish_topic(buffer, &mqtt->topic_length);
                mqtt->data_length = length;
                mqtt->data = mqtt_get_publish_data(buffer, &mqtt->data_length);
                
                mqtt->recCb(mqtt->topic,mqtt->topic_length,mqtt->data,mqtt->data_length);
            }
            break;
            
        case MQTT_MSG_TYPE_PUBACK://客户端上次发送了消息等级是1的消息,服务器返回PUBACK,说明消息已经送达
            if(mqtt->mqtt_message_type == MQTT_MSG_TYPE_PUBLISH && mqtt->mqtt_message_id == msg_id){
                mqtt->mqtt_message_type = 0;
                mqtt->timer_out_cnt = 0;//停止超时定时器
                if(mqtt->PublishedCb){
                    mqtt->PublishedCb();
                }
            }
            break;
        
        case MQTT_MSG_TYPE_PUBREC://客户端上次发送了消息等级是2的消息,服务器返回PUBREC,客户端需要返回PUBREL
            mqtt->mqtt_send_data_len = mqtt_msg_pubrel(msg_id,&mqtt->ptr,mqtt->mqtt_data_buff,mqtt_send_buff_len);
        
            //使用串口发送数据(发给网络模块):请替换自己的发送函数
            //mqtt->ptr:发送的数据
            //mqtt->mqtt_send_data_len:发送的数据长度
            //Send function
            UsartOutStrIT(mqtt->ptr,mqtt->mqtt_send_data_len);//请替换自己的发送函数
            break;
        case MQTT_MSG_TYPE_PUBCOMP://客户端上次发送了消息等级是2的消息,服务器返回PUBREC,客户端返回了PUBREL,服务器最后返回PUBCOMP,说明消息已经送达
            if (mqtt->mqtt_message_type == MQTT_MSG_TYPE_PUBLISH && mqtt->mqtt_message_id == msg_id) {
                mqtt->mqtt_message_type = 0;
                mqtt->timer_out_cnt = 0;//停止超时定时器
                if(mqtt->PublishedCb){
                    mqtt->PublishedCb();
                }
            }
            break;
        case MQTT_MSG_TYPE_PUBREL://客户端收到了消息等级为2的消息,同时回复了PUBREC,服务器返回PUBREL,客户端最后需要返回PUBCOMP
            mqtt->mqtt_send_data_len = mqtt_msg_pubcomp(msg_id,&mqtt->ptr,mqtt->mqtt_data_buff,mqtt_send_buff_len);
            
            BufferManageWrite(&mqtt->buff_manage_struct_t,mqtt->ptr,mqtt->mqtt_send_data_len);/*把协议存入存入缓存*/
            break;
        case MQTT_MSG_TYPE_PINGRESP://接收到心跳包数据
                mqtt_keep_alive_init(mqtt);//初始化心跳包变量
            break;
        
        default:
            break;
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-03-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 实现功能概要
  • 测试准备工作(详细下载步骤请参考 硬件使用说明 )
  • 开始测试
  • 关于程序
  • SmartConfig实现部分
  • MQTT实现部分
  • 结语
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档