单片机通过MQTT协议连接OneNet实现发布/订阅理论篇

本文详细介绍如何使用51单片机与ESP8266模块通过MQTT协议实现远程通信。从硬件连接、AT指令配置到MQTT报文解析,一步步指导读者实现设备间的数据交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

材料:

  1. 51单片机
  2. ESP8266-01S
  3. ESP LINK烧写器(USB-TTL也可以)
  4. 杜邦线若干

MQTT简述:

MQTT是基于发布/订阅范式的消息协议。你发布了一个Topic,只要别人订阅了这个Topic,那么他就能收到信息。同样你要收到别人发布的消息,首先你要订阅别人。(就好比抖音你关注了某个人,当这个人发布视频的时候,系统就会通知你)。假如说好几个人都订阅了一个Topic,那么这几个人都将收到信息。

MQTT的14种报文:

在这里插入图片描述
具体报文格式请参考设备终端接入协议-MQTT,仔阅读你可以从中发现每一个字节所代表的含义。

ESP8266刷AT指令:

AT指令集下载,选择ESP8266 Non-os SDK AT指令集合。

  1. 将ESP8266插入ESP LINK烧写器,并将烧写器插在电脑上。

  2. 初始波特率为115200,选择115200打开串口。

  3. 勾选发送新行,HEX发送不勾选。

  4. 发送AT,返回OK,表明指令运行正常。

  5. ESP8266有三种工作模式:

    1. SoftAP模式(相当于热点或者路由器,用手机可以连接)
    2. Station模式(客户端模式,用来连接服务端的模式)
    3. Station+SoftAP模式(以上两种共存)
  6. 设置为 Station+SoftAP模式并保存到Flash中(下次上电不用在设置),发送AT+CWMODE_DEF=3,返回ok。

  7. 连接WIFI,并保存到Flash中,发送AT+CWJAP_DEF="TP-LINK_1696","123456789",返回WIFI CONNECTED
    WIFI GOT IP

    OK,此时,已经连接上你家的路由器。

  8. 连接远程服务器并保存到Flash,发送AT+SAVETRANSLINK=1,"183.230.40.39",6002,"TCP",返回ok,此时已经连接上OneNet的MQTT服务器,并且开启开机透传模式。

  9. 完成以上步骤只要ESP8266上电就会连接上该路由器和OneNet的MQTT服务器。
    10.实例图:
    在这里插入图片描述

设备连接OneNet:

说明:了解报文的话可直接跳到第21条。

  1. 发送 CONNECT报文进行连接,选择选择HEX发送,我的设备报文如下,每个产品ID,Master-Apikey,设备ID不同所以报文不同。

  2. 10 3B 00 04 4D 51 54 54 04 C2 00 78 00 09 35 33 36 35 32 33 34 30 37 00 06 32 36 32 36 38 35 00 1C 48 45 34 4C 43 73 49 73 59 78 46 37 57 6B 44 50 54 54 54 34 75 61 37 69 73 66 77 3D

  3. 第1个字节10,二进制形式为00010000高4位代表MQTT Packet Type,也就是值为1的报文,查上面的报文表你会发现这是CONNECT报文,

  4. 第2个字节3B表示 remainedLength也就是后面的信息还有多长,3B转化成十进制为59,不信你可以数一数,3B之后确实是59个字节。

  5. 第3-4字节代表所使用的协议名字的长度,00 04也就是占用4个字节。

  6. 那么后面的四个字节4D 51 54 54就是协议的名字了,查一下ASCII码表,你会发现0x4D对应的字母就是M,四个字节对应的单词就是MQTT,也就是我们所使用的协议。

  7. 第9个字节04代表该协议的版本号是4.

  8. 第10个自己字节C2代表userFlag=1 passwordFlag=1 willFlag=0 willRetainFlag=0 willQosFlag=0 clenSessionFlag=1 clenSessionFlag=0详情请查看上面的连接文档。

  9. 第11-12字节00 78装化成十进制就是120,意思就是 keepAlive=120,也就是每隔120s保活一次。不然你的设备会掉线。

  10. 第13-14字节00 09代表你后面的设备ID占用9个字节。

  11. 那么接下来的9个字节就是我的设备ID了,这里就不翻译出来了。

  12. 第24-25字节00 06代表后面你的产品ID占用6个字节。

  13. 显然这后面6个字节就是产品ID。

  14. 在后面两个字节00 1C代表Master-Apikey长度,也就是28个字节。

  15. 后面的28个字节就是Master-Apikey,我做了更改(哈哈)。总共61个字节。

  16. 发送这个报文,之后你就会看到OneNet平台上相应的设备已经在线。
    在这里插入图片描述

  17. 订阅一个名字为Topic1的TOPIC,发送82 0B 00 02 00 06 54 6F 70 69 63 31 00可以自行进行解释。

  18. 发布一个名字为Topic2,内容为1的TOPIC,发送30 09 00 06 54 6F 70 69 63 32 31可以自行解释。

  19. 保活,发送C0 00,意思就是客户端发送PING(连接保活)命令。

  20. 此时,你可以用模拟器发布或订阅测试一下,串口会收到相应的信息。或者使用平台下发命令。

  21. 报文这么难搞,太费劲,可以去这里下载Mqtt-device,可以自动生成相应的报文。实例图:
    在这里插入图片描述

  22. 此时,逻辑上已经完工了,51单片机和esp8266使用串口进行通信,51在编写程序的时候只需要把报文写死就可以了,需要动态更改的报文可以按照报文格式拼接成一个报文。这样就能发布消息了;加个中断程序可以实现从ESP8266中读取消息。这样就能实现双向通信了。除此之外,要有一个定时器,到一定时间需要保活一下(keepalive),不然设备就掉线了。

  23. 会使用SDK开发的推荐使用官方的MQTT_SDK

  24. 下次写博客提供51程序,一样可以使用上个博客提供的Android应用控制51,需要做发布和订阅更改。

  25. 注:当前波特率为115200,当使用单片机的时候波特率要和单片机一致。可以用AT指令调节ESP8266的波特率。例如:AT+UART=9600,8,1,0,0,将波特率改为9600。

### Onenet服务端订阅中消费组的使用方法 在OneNet的服务端订阅机制中,消费组(Consumer Group)是一个重要的概念。它允许多个消费者共同处理同一个主题下的消息流,从而提高系统的吞吐量和可靠性[^1]。 #### 1. 消费组的作用 消费组的主要作用是对同一主题的消息进行负载均衡分配。当一个主题被绑定到某个消费组时,该消费组内的所有消费者会共享这个主题的消息。每个消费者只会接收到部分消息,而不是全部消息。这种设计可以有效减少重复计算,并提升整体性能[^3]。 #### 2. 创建消费组 要使用消费组功能,首先需要创建一个新的消费组。可以通过OneNet提供的API接口来完成这一操作。以下是创建消费组的一个简单示例: ```python import requests url = "https://api.heclouds.com/mqs/consumers" headers = { "Authorization": "Bearer YOUR_ACCESS_TOKEN", "Content-Type": "application/json" } data = { "name": "my_consumer_group", # 设置消费组名称 "topic_name": "your_topic_name" # 绑定的主题名称 } response = requests.post(url, headers=headers, json=data) if response.status_code == 201: print("消费组创建成功:", response.json()) else: print("消费组创建失败:", response.text) ``` 此代码片段展示了如何通过HTTP POST请求向OneNet服务器发送指令以建立新的消费组[^3]。 #### 3. 配置消费者加入消费组 一旦消费组创建完毕,下一步就是让各个消费者实例加入该消费组。这通常是在初始化阶段完成设置,在实际开发过程中可能涉及修改客户端配置文件或者动态调整参数等方式实现。下面给出一段伪代码表示如何指定所属消费组: ```java Properties props = new Properties(); props.put("bootstrap.servers", "mqtts://your-onenet-broker-address"); props.put("group.id", "my_consumer_group"); // 设定为刚才创建好的消费组名 KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("your_topic_name")); ``` 这里假设采用的是类似于Apache Kafka这样的高级抽象库来进行编程工作,则只需简单地把`group.id`属性设为我们刚刚定义过的消费组ID即可[^1]。 #### 4. 处理偏移量管理 对于任何分布式系统而言,保持状态一致性是非常关键的一环;因此,在利用消费组的时候也需要特别注意维护好每条已读取记录的位置信息——即所谓的“偏移量”。一般情况下,默认行为是由框架自动提交最新的确认位置给Broker保存起来以便后续继续从中断处恢复运作流程。然而有时候为了满足特定业务需求也可能手动控制何时以及怎样更新这些标记值。 例如,在某些场景下希望确保只有当某批次的所有项目都被妥善存储至数据库之后才正式告知源端不要再重发它们了,那么就可以关闭自动化选项而改由应用程序自行决定最佳时机执行commit动作: ```csharp var optionsBuilder = new ConsumerConfig() { BootstrapServers = "mqtts://your-onenet-broker-address", GroupId = "my_consumer_group", EnableAutoCommit = false, }; using (var consumer = new ConsumerBuilder<byte[], byte[]>(optionsBuilder).Build()) { consumer.Subscribe(new List<string>() { "your_topic_name" }); try { while (true) { var consumeResult = consumer.Consume(TimeSpan.FromMilliseconds(100)); ProcessMessage(consumeResult.Message); if(IsBatchComplete()) consumer.Commit(consumeResult.Offset); } } catch(Exception ex){ Console.WriteLine($"Error occurred: {ex.Message}"); }finally{ consumer.Close(); } } ``` 以上C#样例演示了一个更精细粒度的操作模式,其中包含了自定义逻辑判断是否应该立即提交当前进度还是等待更多条件达成后再统一行动。 --- ### 注意事项 - **高可用性**: 如果计划长期依赖于此类架构,请务必考虑冗余备份方案以防止单点故障影响整个链条稳定性。 - **扩展能力评估**: 根据预期流量规模合理规划初始节点数量及其资源配额以免高峰期出现瓶颈现象。 - **安全性考量**: 对敏感数据传输过程实施加密保护措施防止潜在窃听风险发生。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小魏先森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值