正点原子STM32F103+ESP8266+DS18B20+DHT11连接阿里云

文章目录

  • MQTT协议
    • 1. 基础知识
    • 2. 报文形式
    • 3. 连接报文
    • 4. 心跳报文
    • 5. 订阅报文
      • 5.1. 订阅主题报文SUBSCRIBE
      • 5.2. 订阅确认SUBACK
      • 5.3. 取消订阅UNSUBSCRIBE
      • 5.4. 取消订阅确认UNSUBACK
    • 6. 发布报文
      • 6.1. 发布消息PUBLISH
      • 6.2. 发布确认PUBACK
    • 7. 阿里云账号创建
    • 8. 网络调试助手接入阿里云
      • 8.1. CONNECT连接服务器
      • 8.2. 心跳请求与心跳响应
      • 8.3. 主题订阅
      • 8.4. 取消订阅
      • 8.5. 发布消息
      • 8.6. 发布确认
    • 9. 串口调试助手+ESP8266接入阿里云
    • 10. 使用正点原子战舰F103连接阿里云

MQTT协议

1. 基础知识

  • mqtt简介:

    MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议)是一种基于发布/订阅模式的轻量级通信协议,由IBM于1999年发布。MQTT专门针对物联网设备开发,是一种低开销低带宽占用的即时通讯协议。该协议构建于TCP/IP协议上,旨在为低带宽和不稳定网络环境中的物联网设备,提供可靠的网络服务。它的设计思想是简单、开放、规范,易于实现,这些特点使其非常适合机器间通信(M2M)、物联网(IoT)等场景。

  • mqtt特点:

    • 使用发布/订阅消息模式,提供了一对多的消息分发和应用之间的解耦;
    • 消息传输不需要知道负载内容;
    • 提供三种等级的服务质量。
      • 最多一次”,尽操作环境所能提供的最大努力分发消息,消息可能会丢失。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久之后会再次发送。
      • 至少一次”,保证消息可以到达,但是可能会重复。
      • 仅一次”,保证消息只到达一次。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。
    • 很小的传输消耗和协议数据交换,最大限度减少网络流量;
    • 异常连接断开发生时,能通知到相关各方。
  • mqtt实现原理:

在这里插入图片描述

三种身份: 发布者(Publish)、 代理者(Broker)、 订阅者(Subscribe)

发布者和代理者都是客户端,代理者是服务器。

消息分类: 主题(Topic)和负载(payload)

主题:消息的类型/主题,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);

负载:消息的内容,指订阅者需要的具体内容。

2. 报文形式

在这里插入图片描述

  • 报文格式:

    名称所属情况
    Fixed Header 固定报头所有控制报文都包含
    Variable Header 可变报头部分控制报文包含
    Payload 有效载荷部分控制报文包含
  • 固定报头:

    在这里插入图片描述

  • 控制报文的类型:

    字节1 4-7位:

    报文类型功能描述流动方 向固定报 头可变报 头报文标识符负载
    CONNECT客户端请求连接服务端C->S1
    CONNACK服务端确认连接建立S->C2
    PUBLISH发布消息C<=>S3有 (Qos>0)
    PUBACK收到发布消息确认(QoS1等 级)C<=>S4
    PUBREC发布消息收到 (QoS2等级)C<=>S5
    PUBREL发布消息释放 (QoS2等级)C<=>S6
    PUBCOMP发布消息完成 (QoS2等级)C<=>S7
    SUBSCRIBE订阅请求C->S8
    SUBACK订阅确认S->C9
    UNSUBSCRIBE取消订阅C->S10
    UNSUBACK取消订阅确认S->C11
    PINGREQ(保活)心跳请求C->S12
    PINGRESP心跳响应S->C13
    DISCONNECT客户端断开连接C->S14

    重点关注加粗的报文类型

    在这里插入图片描述

    字节1 0-3位:

    标志位:

    在这里插入图片描述

    字节2… 剩余长度值:

    剩余长度等于可变报头的长度(10字节)加上有效载荷的长度

    剩余长度(Remaining Length)表示当前报文剩余部分的字节数,包括可变报头和负载的数据

    剩余长度不包括用于编码剩余长度字段本身的字节数。

    字节长度:

    字节数表示长度的最小值 (字节)表示长度的最大值 (字节)
    102^7 - 1 = 127
    22^7 = 128 (注意进位标志)2^(7*2) - 1 = 16383
    32^(7*2) = 163842^(7*3) - 1 = 2097151
    42^(7*3) = 20971522^(7*4) - 1 = 268435455

3. 连接报文

客户端到服务端的网络连接建立后,客户端发送给服务端的第一条报文必须是 CONNECT 报文

在一个网络连接上,客户端只能发送一次CONNECT报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接。

  • 连接报文帧格式:

    功能字节空间
    固定报头MQTT报文类型+保留位1 Byte
    剩余长度1 ~ 4 Byte
    可变报头协议名6 Byte
    协议级别1 Byte
    连接标志1 Byte
    保持连接时间2 Byte
    有效载荷
    • 固定报头:

      在这里插入图片描述

    • 可变报头: 协议名(Protocol Name)协议级别(Protocol Level)连接标志(Connect Flags)保持连接(Keep Alive)

      协议名(Protocol Name):6个字节保持不变

      在这里插入图片描述

      协议级别(Protocol Level):3.1.1版本的MQTT协议规定

      在这里插入图片描述

      连接标志(Connect Flags):最常见组合 1100 0010 ,即0xC2

      在这里插入图片描述

      保持连接(Keep Alive):以秒为单位的连接时间,最大时间 18小时12分15秒

      在这里插入图片描述

  • 有效负载:

    CONNECT报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的标志决定是否包含这些字段。如果包含的话,必须按这个顺序出现:客户端标识符遗嘱主题遗嘱消息用户名密码

  • CONNACK连接响应

    功能
    byte 1固定报头MQTT报文类型+保留位
    byte 2剩余长度
    byte 3可变报头连接确认标志
    byte 4连接返回码

    可变报头返回值:

    返回码响应描述
    00x00连接已接受连接已被服务端接受
    10x01连接已拒绝,不支持的协议版本服务端不支持客户端请求的MQTT协议级别
    20x02连接已拒绝,不合格的客户端标识符客户端标识符是正确的UTF-8编码,但服务端不允许使用
    30x03连接已拒绝,服务端不可用网络连接已建立,但MQTT服务不可用
    40x04连接已拒绝,无效的用户名或密码用户名或密码的数据格式无效
    50x05连接已拒绝,未授权客户端未被授权连接到此服务器
    6 ~ 255Reserved 保留

    具体结果:

    正确,返回:20 02 00 00,并且保持连接。00 连接成功。
    错误,返回:20 02 00 04,并且断开连接。04 无效的用户名和密码。

  • DISCONNECT断开连接

    在这里插入图片描述

4. 心跳报文

  • PINGREQ:客户端发给服务器
    作用:
    1. 在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着;
    2. 请求服务端发送 响应确认它还活着;
    3. 使用网络以确认网络连接没有断开。

  • 保持连接(Keep Alive)是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个PINGREQ报文。

    客户端发送了PINGREQ报文之后,如果在合理的时间内仍没有收到服务器发来的PINGRESP报文,它应该关闭到服务端的网络连接。

  • 报文形式: C0 00

    在这里插入图片描述

  • PINGRESP:服务器发给客户端

    作用:服务端发送 PINGRESP 报文响应客户端的 PINGREQ 报文。表示服务端还活着。

  • 报文形式: D0 00

    在这里插入图片描述

5. 订阅报文

5.1. 订阅主题报文SUBSCRIBE

客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。 SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。

  • 固定报头: 82 ?

    在这里插入图片描述

    剩余长度字段:等于SUBSCRIBE可变报头的长度(2字节)加上有效载荷的长度。

  • 可变报头: 00 0A

    在这里插入图片描述

  • 有效负载:

    在这里插入图片描述

5.2. 订阅确认SUBACK

服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。 SUBACK报文包含一个返▣码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。

  • 固定报头: 90 ?

    在这里插入图片描述

  • 可变报头:

    在这里插入图片描述

  • 有效负载:

    在这里插入图片描述

    允许的返回码:

    0x00 - 最大 Qos0 ;

    0x01 - 成功 - 最大 Qos1 ;

    0x02 - 成功 - 最大 Qos2 ;

    0x80 - Failure 失败 ;

    0x00, 0x01, 0x02, 0x80之外的SUBACK返回码是保留的,不能使用。

5.3. 取消订阅UNSUBSCRIBE

客户端发送UNSUBSCRIBE报文给服务端,用于取消订阅主题。

  • 固定报头:

    在这里插入图片描述

    UNSUBSCRIBE报文固定报头的第3,2,1,0位是保留位且必须分别设置为0,0,1,0。服务端必须认为任何其它的值都是不合法的并关闭网络连接。

  • 可变报头:

    在这里插入图片描述

  • 有效负载:

    UNSUBSCRIBE报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。UNSUBSCRIBE报文中的主题过滤器必须是连续打包的UTF-8编码字符串。 UNSUBSCRIBE报文的有效载荷必须至少包含一个消息过滤器。没有有效载荷的UNSUBSCRIBE报文是违反协议的。

    在这里插入图片描述

5.4. 取消订阅确认UNSUBACK

服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。

  • 固定报头:

    在这里插入图片描述

  • 可变报头:

    在这里插入图片描述

  • 有效负载:

    UNSUBACK 报文没有有效载荷。

6. 发布报文

6.1. 发布消息PUBLISH

PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。

  • 固定报头:

    在这里插入图片描述

    DUP位:

    如果DUP标志被设置为0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果DUP标志被设置为1,表示这可能是一个早前报文请求的重发。

    客户端或服务端请求重发一个PUBLISH报文时,必须将DUP标志设置为1。对于QoS 0的消息,DUP标志必须设置为0。

    QoS等级位:

    在这里插入图片描述

    RETAIN保留标志位:

    如果客户端发给服务端的PUBLISH报文的保留(RETAIN)标志被设置为1,服务端必须存储这个应用消息和它的服务质量等级(QoS),以便它可以被分发给未来的主题名匹配的订阅者。

  • 可变报头:

    可变报头按顺序包含主题名报文标识符

    byte 1Length MSB
    byte 2Length LSB
    byte 3…N主题名
    byte N+1报文标识符 MSB (Qos = 1/2时)
    byte N+2报文标识符 LSB (Qos = 1/2时)

    主题名(Topic Name)用于识别有效载荷数据应该被发布到哪一个信息通道。

    主题名必须是PUBLISH报文可变报头的第一个字段。

    PUBLISH报文中的主题名不能包含通配符

    服务端发送给订阅客户端的PUBLISH报文的主题名必须匹配该订阅的主题过滤器。

  • 有效负载:

    有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度。包含零长度有效载荷的PUBLISH报文是合法的。

    响应:PUBLISH报文的接收者必须按照根据PUBLISH报文中的QoS等级发送响应。

    服务质量等级预期响应
    Qos 0无响应
    Qos 1PUBACK 报文
    Qos 2PUBREC 报文

    动作:

    • 客户端使用PUBLISH报文发送应用消息给服务端,目的是分发到其它订阅匹配的客户端。
    • 服务端使用PUBLISH报文发送应用消息给每一个订阅匹配的客户端。

6.2. 发布确认PUBACK

PUBACK报文是对QoS 1等级的PUBLISH报文的响应。

  • 固定报头:

    在这里插入图片描述

  • 可变报头:

    在这里插入图片描述

    包含等待确认的PUBLISH报文的报文标识符。

  • 有效负载:

    PUBACK 报文没有有效载荷。

7. 阿里云账号创建

在这里插入图片描述

创建过程

  1. 登录阿里云平台 —>产品—>物联网平台—>管理控制台—>公共实例

  2. 创建产品—>设备管理—>产品—>创建产品

    填写 " 产品名称 “、” 所属品类 “、” 节点类型 “、” 连接方式 “、” 数据格式 “、” 认证方式 ",点击下方保存

  3. 创建设备—>产品—>管理设备—>添加设备

  4. 获取三元组: 相当于每个设备的账号和密码

    设备—>查看—>右上角(查看)

    ProductKey: k1p51foVXYV
    DeviceName: mseeding
    DeviceSecret: 2474847d77290ce2fd498a6317615c9c
    

8. 网络调试助手接入阿里云

8.1. CONNECT连接服务器

  • 有效载荷字段中,客户端ID、用户名、密码的合成方法:

    1. 客户端ID:

      方法:    *|securemode=3,signmethod=hmacsha1|      //*为设备名称
      最终样式: mseeding|securemode=3,signmethod=hmacsha1|
      
    2. 用户名:

      方法:    *&#      //*为设备名称,#是ProductKey
      最终样式: mseeding&k1p51foVXYV
      
    3. 密码:

      方法:用DeviceSecret做为秘钥对clientId*deviceName*productKey#进行hmacsha1加密后的结果
           //*为设备名称,#是ProductKey 
      计算过程: clientIdmseedingdeviceNamemseedingproductKeyk1p51foVXYV
          	  用DeviceSecret做为秘钥,进行HmacSHA1加密
      最终样式: 31c4033811c5772f3ae38bf8f1ba8e1caf0ccd79
      
  • 转换成16进制形式:

    字符格式16 进制ASC2编码格式长度
    客户端IDmseeding|securemode=3,signmethod=hmacsha1|6D 73 65 65 64 69 6E 67 7C 73 65 63 75 72 65 6D 6F 64 65 3D 33 2C 73 69 67 6E 6D 65 74 68 6F 64 3D 68 6D 61 63 73 68 61 31 7C42
    用户名mseeding&k1p51foVXYV6D 73 65 65 64 69 6E 67 26 6B 31 70 35 31 66 6F 56 58 59 5620
    密码31c4033811c5772f3ae38bf8f1ba8e1caf0ccd7933 31 63 34 30 33 33 38 31 31 63 35 37 37 32 66 33 61 65 33 38 62 66 38 66 31 62 61 38 65 31 63 61 66 30 63 63 64 37 3940
  • CONNECT 报文帧的合成过程:

    1. 固定报头:2字节
       10 ?
    2. 可变报头:10字节
       00 04 4D 51 54 54 04 C2 01 2C
    3. 客户端ID的字节长度42 + 客户端ID:44字节
       00 2A 6D 73 65 65 64 69 6E 67 7C 73 65 63 75 72 65 6D 6F 64 65 3D 33 2C 73 69 67      6E 6D 65 74 68 6F 64 3D 68 6D 61 63 73 68 61 31 7C
    4. 用户名的字节长度20 + 用户名:22字节
       00 14 6D 73 65 65 64 69 6E 67 26 6B 31 70 35 31 66 6F 56 58 59 56
    5. 密码的字节长度40 + 密码:42字节
       00 28 33 31 63 34 30 33 33 38 31 31 63 35 37 37 32 66 33 61 65 33 38 62 66 38 66      31 62 61 38 65 31 63 61 66 30 63 63 64 37 39
    6. 可变报头 + 有效载荷 的总长度 = 118字节
        10 76
    7. 合成最终报文
    

    最终报文

    10 76 00 04 4D 51 54 54 04 C2 01 2C 00 2A 6D 73 65 65 64 69 6E 67 7C 73 65 63 75 72 65 6D 6F 64 65 3D 33 2C 73 69 67 6E 6D 65 74 68 6F 64 3D 68 6D 61 63 73 68 61 31 7C 00 14 6D 73 65 65 64 69 6E 67 26 6B 31 70 35 31 66 6F 56 58 59 56 00 28 33 31 63 34 30 33 33 38 31 31 63 35 37 37 32 66 33 61 65 33 38 62 66 38 66 31 62 61 38 65 31 63 61 66 30 63 63 64 37 39
    
  • 串口显示

    在这里插入图片描述
    在这里插入图片描述

8.2. 心跳请求与心跳响应

  • PINGREQ: 客户端发给服务器 报文形式: C0 00

    PINGRESP:服务器发给客户端 报文形式: D0 00

    在这里插入图片描述

8.3. 主题订阅

查看:产品-> Topic类列表 -> 物模型通信Topic, 可查看与物模型通信用到的所有主题。

在这里插入图片描述

客户端到服务器: /sys/k1p51foVXYV/${deviceName}/thing/event/property/post

服务器到客户端: /sys/k1p51foVXYV/meseeding/thing/service/property/set

  • 订阅报文帧SUBSCRIBE

    1. 固定报头:2字节
       82 ?
    2. 可变报头:2字节
       00 03
    3. 要订阅的Topic字节长度 + Topic:54字节
       /sys/k1p51foVXYV/mseeding/thing/service/property/set
       00 34 2F 73 79 73 2F 6B 31 70 35 31 66 6F 56 58 59 56 2F 6D 73 65 65 64 69 6E 67      2F 74 68 69 6E 67 2F 73 65 72 76 69 63 65 2F 70 72 6F 70 65 72 74 79 2F 73 65 74
    4. Qos=01字节
       00 
    5. 可变报头 + 有效载荷 的总长度 = 57字节
        82 39
    6. 合成最终报文
    

    最终报文:

    82 39 00 03 00 34 2F 73 79 73 2F 6B 31 70 35 31 66 6F 56 58 59 56 2F 6D 73 65 65 64 69 6E 67 2F 74 68 69 6E 67 2F 73 65 72 76 69 63 65 2F 70 72 6F 70 65 72 74 79 2F 73 65 74 00
    
  • 上传报文帧SUBSCRIBE

    1. 固定报头:2字节
       82 ?
    2. 可变报头:2字节
       00 04
    3. 要订阅的Topic字节长度 + Topic:53字节
       /sys/k1p51foVXYV/mseeding/thing/event/property/post
       00 33 2F 73 79 73 2F 6B 31 70 35 31 66 6F 56 58 59 56 2F 6D 73 65 65 64 69 6E 67      2F 74 68 69 6E 67 2F 65 76 65 6E 74 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74
    4. Qos=01字节
       00 
    5. 可变报头 + 有效载荷 的总长度 = 56字节
        82 38
    6. 合成最终报文
    

    最终报文:

    82 38 00 04 00 33 2F 73 79 73 2F 6B 31 70 35 31 66 6F 56 58 59 56 2F 6D 73 65 65 64 69 6E 67 2F 74 68 69 6E 67 2F 65 76 65 6E 74 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74 00
    
  • 串口显示:

    在这里插入图片描述

  • 阿里云界面: 设备管理 -> 设备 -> Topic列表

    在这里插入图片描述

  • 订阅确认SUBACK:

    90 03 00 03 01 (00 03是自定义的报文标识符。01是返回码,表示订阅成功,且最大Qos等级为1)

    90 03 00 04 01 (00 04是自定义的报文标识符。01是返回码,表示订阅成功,且最大Qos等级为1)

8.4. 取消订阅

  • 取消订阅:

    /sys/k1p51foVXYV/mseeding/thing/service/property/set

  • 取消订阅UNSUBSCRIBE

    1. 固定报头:2字节
       A2 ?
    2. 可变报头:2字节
       00 03
    3. 要取消订阅的Topic字节长度 + Topic:53字节
       /sys/k1p51foVXYV/mseeding/thing/event/property/post
       00 33 2F 73 79 73 2F 6B 31 70 35 31 66 6F 56 58 59 56 2F 6D 73 65 65 64 69 6E 67      2F 74 68 69 6E 67 2F 65 76 65 6E 74 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74
    4. 可变报头 + 有效载荷 的总长度 = 55字节
        A2 37
    5. 合成最终报文
    

    最终报文:

    A2 37 00 03 00 33 2F 73 79 73 2F 6B 31 70 35 31 66 6F 56 58 59 56 2F 6D 73 65 65 64 69 6E 67 2F 74 68 69 6E 67 2F 65 76 65 6E 74 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74
    
  • 串口显示:

    在这里插入图片描述

  • 阿里云界面:

    在这里插入图片描述

  • 取消订阅确认:

    B0 02 00 03 (00 03是自定义的报文标识符)

8.5. 发布消息

  • 服务器发送给客户端

    在这里插入图片描述

    接收的数据:

    在这里插入图片描述

    解析过程:

    30:固定报头PUBLISH;

    A3 01: 剩余长度,163字节;

    00 34:可变报头,52字节;

    2F 73 79 73 2F 6B 31 70 35 31 66 6F 56 58 59 56 2F 6D 73 65 65 64 69 6E 67 2F 74 68 69 6E 67 2F 73 65 72 76 69 63 65 2F 70 72 6F 70 65 72 74 79 2F 73 65 74

    /sys/k1p51foVXYV/mseeding/thing/service/property/set

    7B 22 6D 65 74 68 6F 64 22 3A 22 74 68 69 6E 67 2E 73 65 72 76 69 63 65 2E 70 72 6F 70 65 72 74 79 2E 73 65 74 22 2C 22 69 64 22 3A 22 31 33 39 32 39 37 37 39 38 39 22 2C 22 70 61 72 61 6D 73 22 3A 7B 22 43 75 72 72 65 6E 74 54 65 6D 70 65 72 61 74 75 72 65 22 3A 32 33 7D 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 2E 30 22 7D
    {“method”:“thing.service.property.set”,“id”:“1392977989”,“params”:{“CurrentTemperature”:23},“version”:“1.0.0”}

  • 服务器发送给客户端

    解析过程:

    {“method”:“thing.event.property.post”,“id”:“123456789”,“params”:{“CurrentTemperature”:37},“version”:“1.0.0”}

    7B 22 6D 65 74 68 6F 64 22 3A 22 74 68 69 6E 67 2E 65 76 65 6E 74 2E 70 72 6F 70 65 72 74 79 2E 70 6F 73 74 22 2C 22 69 64 22 3A 22 31 32 33 34 35 36 37 38 39 22 2C 22 70 61 72 61 6D 73 22 3A 7B 22 43 75 72 72 65 6E 74 54 65 6D 70 65 72 61 74 75 72 65 22 3A 33 37 7D 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 2E 30 22 7D (108字节)

    固定报头PUBLISH:30

    剩余长度,163字节:A1 01

    Topic的字节数51 + Topic:/sys/k1p51foVXYV/mseeding/thing/event/property/post

    00 33 2F 73 79 73 2F 6B 31 70 35 31 66 6F 56 58 59 56 2F 6D 73 65 65 64 69 6E 67 2F 74 68 69 6E 67 2F 65 76 65 6E 74 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74

    最终报文:

    30 A1 01 00 33 2F 73 79 73 2F 6B 31 70 35 31 66 6F 56 58 59 56 2F 6D 73 65 65 64 69 6E 67 2F 74 68 69 6E 67 2F 65 76 65 6E 74 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74 7B 22 6D 65 74 68 6F 64 22 3A 22 74 68 69 6E 67 2E 65 76 65 6E 74 2E 70 72 6F 70 65 72 74 79 2E 70 6F 73 74 22 2C 22 69 64 22 3A 22 31 32 33 34 35 36 37 38 39 22 2C 22 70 61 72 61 6D 73 22 3A 7B 22 43 75 72 72 65 6E 74 54 65 6D 70 65 72 61 74 75 72 65 22 3A 33 37 7D 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 2E 30 22 7D

  • 串口调试助手和阿里云平台结果:

    在这里插入图片描述

  • 阿里云显示: 设备->查看->物模型数据->温度->参看数据

    在这里插入图片描述

8.6. 发布确认

MQTT协议规定,PUBACK 是对 Qos=1时的 PUBLISH的发布消息确认。也就是说Qos=1 才有 PUBACK,但阿里云服务器仅支持 Qos=0的POST,所以压根就不会有PUBACK。而用户强行发送Qos=1的POST,会返回错误:40 02 7B 22 ,并且不会对本帧的内容进行处理,开关状态不会得到更新。
在这里插入图片描述

9. 串口调试助手+ESP8266接入阿里云

  • wifi模块固件烧录

    使用正点原子ATK-ESP-01模块,进行固件烧录,固件来源于安信可官网

    安信可官网:https://docs.ai-thinker.com/

    1. 选择Wifi模组系列 -> 2. ESP8266系列 -> 3. 各类AT固件 -> 4. 选择第⑦个进行固件下载 -> 5. 开发工具 -> 选择第二个烧录软件

      在这里插入图片描述

    2. 把下载的文件加压,找到bin文件,手动输入0x00进行烧录,注意串口选择,烧录完成有提示
      在这里插入图片描述

  • 串口发送指令

    1. 发送AT+RESTORE 重新启动ESP模块

    2. 发送AT+CWMODE=1 配置WIFI模式
      在这里插入图片描述

    3. 发送AT+CIPSNTPCFG=1,8,"cn.ntp.org.cn","ntp.sjtu.edu.cn" 进行服务器设置
      在这里插入图片描述

    4. 发送AT+CWJAP="vivo","Li.12345678"(输入自己的wifi和密码) 连接wifi
      在这里插入图片描述

    5. 发送AT+MQTTUSERCFG=0,1,"NULL","mseeding&k1p51foVXYV","58445ef1b7fca73e26015e0777d63490a03c18312ab91eae1d65763f1450afe4",0,0,"" 设置MQTT属性
      在这里插入图片描述

    登录阿里云,设备界面,查看MQTT连接参数
    在这里插入图片描述

    1. 发送AT+MQTTCLIENTID=0,"k1p51foVXYV.mseeding|securemode=2\,signmethod=hmacsha256\,timestamp=1725277665467|" (注意要在","前加上"\")设置MQTT ID
      在这里插入图片描述

    2. 发送AT+MQTTCONN=0,"iot-06z00c3rc5nqns1.mqtt.iothub.aliyuncs.com",1883,1 发送MQTT域名,域名获取
      在这里插入图片描述

    3. 发送AT+MQTTSUB=0,"/k1p51foVXYV/mseeding/user/get",1 订阅主题
      在这里插入图片描述

    阿里云界面:
    在这里插入图片描述
    阿里云发送给串口助手的数据:

    在这里插入图片描述

    1. 发送AT+MQTTPUB=0,"/sys/k1p51foVXYV/mseeding/thing/event/property/post","{\"params\":{\"LEDSwitch\":1}}",1,0 发布主题
      在这里插入图片描述

    阿里云显示:
    在这里插入图片描述

  • 串口指令集合

    //1. 重新启动ESP模块
        AT+RESTORE
    //2. 配置WIFI模式
        AT+CWMODE=1
    //3. 进行服务器设置
        AT+CIPSNTPCFG=1,8,"cn.ntp.org.cn","ntp.sjtu.edu.cn"
    //4. 连接wifi
        AT+CWJAP="vivo","Li.12345678"
    //5. 设置MQTT属性
      AT+MQTTUSERCFG=0,1,"NULL","mseeding&k1p51foVXYV","58445ef1b7fca73e26015e0777d63490a03c18312ab91eae1d65763f1450afe4",0,0,""
    //6. 设置MQTT ID
      AT+MQTTCLIENTID=0,"k1p51foVXYV.mseeding|securemode=2\,signmethod=hmacsha256\,timestamp=1725277665467|"
    //7. 发送MQTT域名,域名获取
        AT+MQTTCONN=0,"iot-06z00c3rc5nqns1.mqtt.iothub.aliyuncs.com",1883,1
    //8. 订阅主题  
        AT+MQTTSUB=0,"/k1p51foVXYV/mseeding/user/get",1
    //9. 发布主题 
        AT+MQTTPUB=0,"/sys/k1p51foVXYV/mseeding/thing/event/property/post","{\"params\":{\"LEDSwitch\":1}}",1,0
    

10. 使用正点原子战舰F103连接阿里云

  • 阿里云相关配置

    #define  PRODUCTKEY           "k1p51foVXYV"                                        //产品ID
    #define  PRODUCTKEY_LEN       strlen(PRODUCTKEY)                                   //产品ID长度
    #define  DEVICENAME           "mseeding"                                               //设备名  
    #define  DEVICENAME_LEN       strlen(DEVICENAME)                                   //设备名长度
    #define  DEVICESECRE          "31c4033811c5772f3ae38bf8f1ba8e1caf0ccd79"                   //设备秘钥   
    #define  DEVICESECRE_LEN      strlen(DEVICESECRE)                                  //设备秘钥长度
    #define  S_TOPIC_NAME         "/sys/k1p51foVXYV/mseeding/thing/service/property/set"   //需要订阅的主题  
    #define  P_TOPIC_NAME         "/sys/k1p51foVXYV/mseeding/thing/event/property/post"    //需要发布的主题   
    
    void AliIoT_Parameter_Init(void)
    {	
    	char temp[128];                                                       
    
    	memset(ClientID,128,0);                                               
    	sprintf(ClientID,"%s|securemode=3,signmethod=hmacsha1|",DEVICENAME);  
    	ClientID_len = strlen(ClientID);                                      
    	
    	memset(Username,128,0);                                               
    	sprintf(Username,"%s&%s",DEVICENAME,PRODUCTKEY);                      
    	Username_len = strlen(Username);                                      
    	
    	memset(temp,128,0);                                                                     
    	sprintf(temp,"clientId%sdeviceName%sproductKey%s",DEVICENAME,DEVICENAME,PRODUCTKEY);        
    	utils_hmac_sha1(temp,strlen(temp),Passward,DEVICESECRE,DEVICESECRE_LEN);                 
    	Passward_len = strlen(Passward);                                                         
    	
    	memset(ServerIP,128,0);  
    	sprintf(ServerIP,"%s.iot-as-mqtt.cn-shanghai.aliyuncs.com",PRODUCTKEY);                 
    	ServerPort = 1883;                                                                       
    	
    	u1_printf("服 务 器:%s:%d\r\n",ServerIP,ServerPort);
    	u1_printf("客户端ID:%s\r\n",ClientID);               
    	u1_printf("用 户 名:%s\r\n",Username);               
    	u1_printf("密    码:%s\r\n",Passward);               
    }
    
  • 发布消息

      /*-------------------------------------------------------------*/
       			/*                     处理发送缓冲区数据                      */
       			/*-------------------------------------------------------------*/
       			if(MQTT_TxDataOutPtr != MQTT_TxDataInPtr) //if成立的话,说明发送缓冲区有数据了
       			{   
       				//3种情况可进入if
       				//第1种:0x10 连接报文
       				//第2种:0x82 订阅报文,且ConnectPack_flag置位,表示连接报文成功
       				//第3种:SubcribePack_flag置位,说明连接和订阅均成功,其他报文可发
       				if((MQTT_TxDataOutPtr[2]==0x10)||((MQTT_TxDataOutPtr[2]==0x82)&&(ConnectPack_flag==1))||(SubcribePack_flag==1))
       				{    
       					u1_printf("发送数据:0x%x\r\n",MQTT_TxDataOutPtr[2]);  //串口提示信息
       					MQTT_TxData(MQTT_TxDataOutPtr);                       //发送数据
       					MQTT_TxDataOutPtr += TBUFF_UNIT;                      //指针下移
    					if(MQTT_TxDataOutPtr==MQTT_TxDataEndPtr)              //如果指针到缓冲区尾部了
    						MQTT_TxDataOutPtr = MQTT_TxDataBuf[0];            //指针归位到缓冲区开头
    				} 				
    			}//处理发送缓冲区数据的else if分支结尾
    
    
    void set_temp_humid(void)
    {
    	
    	char temp[256];  
    	DHT11_Read_Data(&humiH,&humiL,&tempH,&tempL);
    	sprintf(temp,"{\"method\":\"thing.event.property.post\",\"id\":\"1234556789\",\"params\":{\"humid\":%d,\"temp\":%d},\"version\":\"1.0.0\"}",(humiH*10+humiL),(tempH*10+tempL));  //构建回复温湿度数据
    	MQTT_PublishQs0(P_TOPIC_NAME,temp,strlen(temp));   //添加数据,发布给服务器	
    	//delay_ms(100);
    }
    
    
    void MQTT_PublishQs0(char *topic, char *data, int data_len)
    {	
    	int temp,Remaining_len;
    	
    	Fixed_len = 1;                              //固定报头长度暂时先等于:1字节
    	Variable_len = 2 + strlen(topic);           //可变报头长度:2字节(topic长度)+ topic字符串的长度
    	Payload_len = data_len;                     //有效负荷长度:就是data_len
    	Remaining_len = Variable_len + Payload_len; //剩余长度=可变报头长度+负载长度
    	
    	temp_buff[0]=0x30;                       //固定报头第1个字节 :固定0x30   	
    	do{                                      //循环处理固定报头中的剩余长度字节,字节量根据剩余字节的真实长度变化
    		temp = Remaining_len%128;            //剩余长度取余128
    		Remaining_len = Remaining_len/128;   //剩余长度取整128
    		if(Remaining_len>0)               	
    			temp |= 0x80;                    //按协议要求位7置位          
    		temp_buff[Fixed_len] = temp;         //剩余长度字节记录一个数据
    		Fixed_len++;	                     //固定报头总长度+1    
    	}while(Remaining_len>0);                 //如果Remaining_len>0的话,再次进入循环
    		             
    	temp_buff[Fixed_len+0]=strlen(topic)/256;                      //可变报头第1个字节     :topic长度高字节
    	temp_buff[Fixed_len+1]=strlen(topic)%256;		               //可变报头第2个字节     :topic长度低字节
    	memcpy(&temp_buff[Fixed_len+2],topic,strlen(topic));           //可变报头第3个字节开始 :拷贝topic字符串	
    	memcpy(&temp_buff[Fixed_len+2+strlen(topic)],data,data_len);   //有效负荷:拷贝data数据
    	
    	TxDataBuf_Deal(temp_buff, Fixed_len + Variable_len + Payload_len);  //加入发送数据缓冲区
    }
    
  • 订阅消息

    if(MQTT_RxDataOutPtr != MQTT_RxDataInPtr)  //if成立的话,说明接收缓冲区有数据了	
    			{  													
    				u1_printf("接收到数据:");
    				/*                    处理CONNACK报文                  */			
    				//if判断,如果第一个字节是0x20,表示收到的是CONNACK报文
    				//接着我们要判断第4个字节,看看CONNECT报文是否成功
    				if(MQTT_RxDataOutPtr[2]==0x20)
    				{             			
    				    switch(MQTT_RxDataOutPtr[5])
    					{					
    						case 0x00 : u1_printf("CONNECT报文成功\r\n");                            //串口输出信息	
    								    ConnectPack_flag = 1;                                        //CONNECT报文成功,订阅报文可发
    									break;                                                       //跳出分支case 0x00                                              
    						case 0x01 : u1_printf("连接已拒绝,不支持的协议版本,准备重启\r\n");     //串口输出信息
    									Connect_flag = 0;                                            //Connect_flag置零,重启连接
    									break;                                                       //跳出分支case 0x01   
    						case 0x02 : u1_printf("连接已拒绝,不合格的客户端标识符,准备重启\r\n"); //串口输出信息
    									Connect_flag = 0;                                            //Connect_flag置零,重启连接
    									break;                                                       //跳出分支case 0x02 
    						case 0x03 : u1_printf("连接已拒绝,服务端不可用,准备重启\r\n");         //串口输出信息
    									Connect_flag = 0;                                            //Connect_flag置零,重启连接
    									break;                                                       //跳出分支case 0x03
    						case 0x04 : u1_printf("连接已拒绝,无效的用户名或密码,准备重启\r\n");   //串口输出信息
    									Connect_flag = 0;                                            //Connect_flag置零,重启连接						
    									break;                                                       //跳出分支case 0x04
    						case 0x05 : u1_printf("连接已拒绝,未授权,准备重启\r\n");               //串口输出信息
    									Connect_flag = 0;                                            //Connect_flag置零,重启连接						
    									break;                                                       //跳出分支case 0x05 		
    						default   : u1_printf("连接已拒绝,未知状态,准备重启\r\n");             //串口输出信息 
    									Connect_flag = 0;                                            //Connect_flag置零,重启连接					
    									break;                                                       //跳出分支case default 								
    					}				
    				}			
    				//if判断,第一个字节是0x90,表示收到的是SUBACK报文
    				//接着我们要判断订阅回复,看看是不是成功
    				else if(MQTT_RxDataOutPtr[2]==0x90)
    				{ 
    					switch(MQTT_RxDataOutPtr[6])
    					{					
    						case 0x00 :
    						case 0x01 : u1_printf("订阅成功\r\n");            //串口输出信息
    									SubcribePack_flag = 1;                //SubcribePack_flag置1,表示订阅报文成功,其他报文可发送
    									Ping_flag = 0;                        //Ping_flag清零
    									TIM3_ENABLE_30S();                    //启动30s的PING定时器
    									set_temp_humid();	//启动数据传输	
    									TIM2_ENABLE_6S();	 //判断开关状态,并发布给服务器  定时器
    									break;                                //跳出分支                                             
    						default   : u1_printf("订阅失败,准备重启\r\n");  //串口输出信息 
    									Connect_flag = 0;                     //Connect_flag置零,重启连接
    									break;                                //跳出分支 								
    					}					
    				}
    				//if判断,第一个字节是0xD0,表示收到的是PINGRESP报文
    				else if(MQTT_RxDataOutPtr[2]==0xD0)
    				{ 
    					u1_printf("PING报文回复\r\n"); 		  //串口输出信息 
    					if(Ping_flag==1)
    					{                     //如果Ping_flag=1,表示第一次发送
    						 Ping_flag = 0;    				  //要清除Ping_flag标志
    					}
    					else if(Ping_flag>1)
    					{ 				  //如果Ping_flag>1,表示是多次发送了,而且是2s间隔的快速发送
    						Ping_flag = 0;     				  //要清除Ping_flag标志
    						TIM3_ENABLE_30S(); 				  //PING定时器重回30s的时间
    						
    					}				
    				}	
    				//if判断,如果第一个字节是0x30,表示收到的是服务器发来的推送数据
    				//我们要提取控制命令
    				else if((MQTT_RxDataOutPtr[2]==0x30))
    				{ 
    					u1_printf("服务器等级0推送\r\n"); 		   //串口输出信息 
    					MQTT_DealPushdata_Qs0(MQTT_RxDataOutPtr);  //处理等级0推送数据
    				}				
    								  
    				MQTT_RxDataOutPtr +=RBUFF_UNIT;                     //指针下移
    				if(MQTT_RxDataOutPtr==MQTT_RxDataEndPtr)            //如果指针到缓冲区尾部了
    					MQTT_RxDataOutPtr = MQTT_RxDataBuf[0];          //指针归位到缓冲区开头                        
    			}//处理接收缓冲区数据的else if分支结尾
    
  • 接收控制命令

    if(MQTT_CMDOutPtr != MQTT_CMDInPtr)
      {                             //if成立的话,说明命令缓冲区有数据了			       
      		u1_printf("命令:%s\r\n",&MQTT_CMDOutPtr[2]);                 //串口输出信息
      		if(strstr((char *)MQTT_CMDOutPtr+2,"\"params\":{\"LED\":1}"))  //云平台下发命令控制LED关
      		{      
      			LED_OFF;
    
      		}
      		else if(strstr((char *)MQTT_CMDOutPtr+2,"\"params\":{\"LED\":0}"))   //云平台下发命令控制LED开
      		{          
      			LED_ON;
      		}
      		MQTT_CMDOutPtr += CBUFF_UNIT;                             	 //指针下移
      		if(MQTT_CMDOutPtr==MQTT_CMDEndPtr)           	             //如果指针到缓冲区尾部了
      			MQTT_CMDOutPtr = MQTT_CMDBuf[0];          	             //指针归位到缓冲区开头				
      }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/872818.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java | Leetcode Java题解之第389题找不同

题目&#xff1a; 题解&#xff1a; class Solution {public char findTheDifference(String s, String t) {int ret 0;for (int i 0; i < s.length(); i) {ret ^ s.charAt(i);}for (int i 0; i < t.length(); i) {ret ^ t.charAt(i);}return (char) ret;} }

Matplotlib 颜色设置详解

在使用matplotlib进行颜色绘制的时候,如绘制图表、背景色或者对文字设置的时候都可以配置颜色, 以下说明主流的三种颜色使用方法 颜色名称 可以是直接使用颜色名称的字符串对color进行赋值,包括可以使用首字母缩写或者完整拼写的形式,以下为部分颜色的书写形式 缩写版 • …

Spring Boot 多数据源配置(JPA)

目录 前言 前置环境 pom yml Entity Dao Config Controller 演示 前言 一般一个系统至少有一个数据源&#xff0c;用来持久化业务数据以及查询。单个数据源的系统很常见&#xff0c;在 Spring Boot 框架下配置也很简单。在约定大于配置这个思想下&#xff0c;只需要在…

递推,CF 353D - Queue

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 353D - Queue 二、解题报告 1、思路分析 手玩一下&#xff0c;我们发现相…

[数据集][目标检测]人脸口罩佩戴目标检测数据集VOC+YOLO格式8068张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;8068 标注数量(xml文件个数)&#xff1a;8068 标注数量(txt文件个数)&#xff1a;8068 标注…

uniapp写的一个年月日时分秒时间选择功能

代码: <template><view><picker mode"multiSelector" :value"multiIndex" :range"multiRange" change"onMultiChange"><view class"picker">当前选择&#xff1a;{{ formattedDateTime }}</vie…

VisualStudio环境搭建C++

Visual Studio环境搭建 说明 C程序编写中&#xff0c;经常需要链接头文件(.h/.hpp)和源文件(.c/.cpp)。这样的好处是&#xff1a;控制主文件的篇幅&#xff0c;让代码架构更加清晰。一般来说头文件里放的是类的申明&#xff0c;函数的申明&#xff0c;全局变量的定义等等。源…

大路灯护眼灯有必要吗安全吗?性价比高落地护眼灯推荐

大路灯护眼灯有必要吗安全吗&#xff1f;近几年来&#xff0c;随着生活节奏的加快&#xff0c;目前青少年的近视率呈现一个直线上升的趋势&#xff0c;其中占比达到了70%以上&#xff0c;并且最令人意外的是小学生竟然也占着比较大的比重&#xff0c;这一系列的数据不仅表明着近…

MySQL(CRUD)

MySQL mysql -u root -ply MySQL的三层结构 1.安装MySQL数据库本质就是在主机安装一个数据库管理系统(DBMS),这个管理程序可以管理多个数据库. 2.一个数据库中可以创建多个表,以保存数据 SQL语句分类 1.DDL:数据定义语句[create 表,库] 2.DML:数据操作语句[增加insert,修改…

【Java】基于JWT+Token实现完整登入功能(实操)

Java系列文章目录 补充内容 Windows通过SSH连接Linux 第一章 Linux基本命令的学习与Linux历史 文章目录 Java系列文章目录一、前言二、学习内容&#xff1a;三、问题描述四、解决方案&#xff1a;4.1 认识依赖4.2 使用JWT4.3 登入实现4.4 配置拦截器4.5 获取数据 五、总结&…

EMC技术

目录 EMC 天线效应 公式 措施 EMC测试 展频技术 如何展频 OTA测试 EMC 三大要素&#xff1a;干扰源、传输介质、敏感设备。 EMI&#xff1a;Electromagnetic Interference&#xff0c;电磁干扰。 EMS&#xff1a;Electro Magnetic Susceptibility&#xff0c;电磁抗扰…

二百五十九、Java——采集Kafka数据,解析成一条条数据,写入另一Kafka中(一般JSON)

一、目的 由于部分数据类型频率为1s&#xff0c;从而数据规模特别大&#xff0c;因此完整的JSON放在Hive中解析起来&#xff0c;尤其是在单机环境下&#xff0c;效率特别慢&#xff0c;无法满足业务需求。 而Flume的拦截器并不能很好的转换数据&#xff0c;因为只能采用Java方…

JVM系列(十) -垃圾收集器介绍

一、摘要 在之前的几篇文章中,我们介绍了 JVM 内部布局、对象的创建过程、运行期的相关优化手段以及垃圾对象的回收算法等相关知识。 今天通过这篇文章,结合之前的知识,我们一起来了解一下 JVM 中的垃圾收集器。 二、垃圾收集器 如果说收集算法是内存回收的方法论,那么…

集成电路学习:什么是NOR Flash Memory非易失性闪存存储器

一、NOR Flash Memory&#xff1a;非易失性闪存存储器 NOR Flash Memory&#xff0c;即非易失性闪存存储器的一种&#xff0c;是Flash存储器的一个重要分支。Flash存储器&#xff0c;又称为闪存&#xff0c;结合了ROM&#xff08;只读存储器&#xff09;和RAM&#xff08;随机存…

Windows下Python和PyCharm的应用(三)__Numpy与矩阵

1、背景介绍 矩阵运算是Python语言的基石。 而支持矩阵运算的基础语言包就是Numpy。 参考链接&#xff1a; Python中Numpy的使用_numpy在python中的用法-CSDN博客 这篇博客介绍的numpy比我的这篇博客介绍的更加的详细。本博客只是根据本人 的实际应用&#xff0c;对最关键的…

Clean Minimalist GUI Pack (简约风格UI界面)

Unity 最简洁易用的 GUI 资源包。如果你在寻找资源商店上 UI 极简主义革命的发起者,你已经找到了。 这一极干净简约的 GUI 资源包是一款适合移动设备使用的游戏 UI 资源包,其中包含许多图标和元素,可用于创建具有简洁风格的完整游戏 UI。 功能: • 包括 3 种皮肤:深色、浅…

【数据结构】详细介绍各种排序算法,包含希尔排序,堆排序,快排,归并,计数排序

目录 1. 排序 1.1 概念 1.2 常见排序算法 2. 插入排序 2.1 直接插入排序 2.1.1 基本思想 2.1.2 代码实现 2.1.3 特性 2.2 希尔排序(缩小增量排序) 2.2.1 基本思想 2.2.2 单个gap组的比较 2.2.3 多个gap组比较(一次预排序) 2.2.4 多次预排序 2.2.5 特性 3. 选择排…

AFSim 仿真系统----性能工具

什么是 WPR/WPA&#xff1f; Windows 性能记录器 (WPR) 和 Windows 性能分析器 (WPA) 是 Windows 性能工具包中提供的性能监控工具。它们是免费的工具&#xff0c;可以通过下载和安装 Windows 评估和部署工具包 (ADK) 来获得。 WPR 是一个工具&#xff0c;允许用户动态部署事…

Unity3D在2D游戏中获取触屏物体的方法

我们的需求是&#xff1a; 假如屏幕中一个棋盘&#xff0c;每个棋子是button构成的&#xff0c;我们希望手指或者鼠标在哪里&#xff0c;就显示那个位置的button信息。 网上有很多获取触屏物体信息的信息的方法如下面代码所示&#xff1a; Camera cam Camera.main; // pre-de…

一个php快速项目搭建框架源码,带一键CURD等功能

介绍&#xff1a; 框架易于功能扩展&#xff0c;代码维护&#xff0c;方便二次开发&#xff0c;帮助开发者简单高效降低二次开发成本&#xff0c;满足专注业务深度开发的需求。 百度网盘下载 图片&#xff1a;