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

news2024/12/28 18:15:19

文章目录

  • 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.coloradmin.cn/o/2113119.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器人助力上下料搬运,加速仓库转运自动化

近年来&#xff0c;国内制造业领域掀起了一股智能化改造的浪潮&#xff0c;众多工厂纷纷采纳富唯智能提供的先进物流解决方案&#xff0c;这一举措显著优化了生产流程&#xff0c;实现了生产效率的飞跃式增长。得益于这些成功案例&#xff0c;某信息技术服务企业在工厂智能物流…

如何打造高校实验室教学管理系统?Java SpringBoot助力,MySQL存储优化,2025届必备设计指南

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

每日一题——第八十一题

打印如下图案: #include<stdio.h> int main() {int i, j;char ch A;for (i 1; i < 5; i, ch){for (j 0; j < 5 - i; j){printf(" ");//控制空格输出}for (j 1; j < 2 * i; j)//条件j < 2 * i{printf("%c", ch);//控制字符输出}prin…

❤《实战纪录片 1 》原生开发小程序中遇到的问题和解决方案

《实战纪录片 1 》原生开发小程序中遇到的问题和解决方案 文章目录 《实战纪录片 1 》原生开发小程序中遇到的问题和解决方案1、问题一&#xff1a;原生开发中 request请求中返回 的数据无法 使用this传递给 data{}中怎么办&#xff1f;2、刚登录后如何将token信息保存&#xf…

[网鼎杯 2020 青龙组]bang-快坚持不下去的第五天

ps:安卓的解壳&#xff0c;也就是frida-dexdump一把嗦&#xff0c;其他wp上说用真机成功的几率大一些&#xff0c;我就用的模拟器 详细的看这一篇博客&#xff1a; 安卓模拟器Frida环境搭建 &#xff08;mumuadbfrida&#xff09; 在python中安装frida时多一步 pip install…

【Dash】feffery_antd_componenet 中的 AntdSpace

一、feffery_antd_componenet 中的 AntdSpace feffery_antd_components&#xff08;简称fac&#xff09;中的AntdSpace组件是一个基于Ant Design的Dash第三方组件&#xff0c;它用于在水平或垂直方向上放置多个元素&#xff0c;并提供元素之间的间距。以下是AntdSpace组件的一…

一文说清什么是AI原生(AI Native)应用以及特点

引言&#xff1a;智能新纪元 如今&#xff0c;走在街头&#xff0c;哪儿不被智能科技包围&#xff1f;智能音箱、自动驾驶汽车、聊天机器人......这些都在用不同的方式提升我们的生活体验。然而&#xff0c;究竟什么才能称得上“AI原生应用”呢&#xff1f; 什么是AI原生&…

C++:STL简介

✨ Blog’s 主页: 白乐天_&#xff3e;&#xff56;&#xff3e; &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山岗&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; 一、STL引入 STL&#xff08;Standard template libaray-标准模板库…

最新厦门新能安社招入职Verify测评笔试攻略敬请查收SHL

作为全球知名锂电池供应商——ATL与CATL的合资公司&#xff0c;新能安专注高端锂电池的研究与创新&#xff0c;聚焦储能系统、微型车、智能产品驱动电池等三大领域的研发、生产、销售与服务。 a.测评内容包含演绎推理数字推理两部分&#xff0c;大约用时40分钟左右; b.正式测评…

node.js实现阿里云短信发送

效果图 实现 一、准备工作 1、官网直达网址&#xff1a; 阿里云 - 短信服务 2、按照首页提示依次完成相应资质认证和短信模板审核&#xff1b; 3、获取你的accessKeySecret和accessKeyId&#xff1b; 方法如下&#xff1a; 获取AccessKey-阿里云帮助中心 4、获取SignNa…

测试你们认为最好的AI工具,是不是好用得自己试试!

大家好&#xff0c;我是凡人&#xff0c;在 OpenAI 春季发布会后&#xff0c; GPT-4o 一时风光无量&#xff0c;一个同事不信邪&#xff0c;非要用 GPT-4o 版本对 OpenAI 官网上的例子尝试生成&#xff0c;本来还是嘲笑他的心态&#xff0c;但他还真的发现了点有意思的事情。 …

多线程篇(阻塞队列- BlockingQueue)(持续更新迭代)

目录 一、了解什么是阻塞队列之前&#xff0c;需要先知道队列 1. Queue&#xff08;接口&#xff09; 二、阻塞队列 1. 前言 2. 什么是阻塞队列 3. Java里面常见的阻塞队列 三、BlockingQueue&#xff08;接口&#xff09; 1. 前言 2. 简介 3. 特性 3.1. 队列类型 …

实时地图+瞬移,黑神话地图工具来了

工具下载: https://pan.quark.cn/s/12b9cef46bf0 瞬移功能使用说明&#xff1a; 1、必须在一修大师客户端: 使用猫修APP扫码登陆后使用&#xff1b; 2、打开《黑神话》游戏&#xff1b; 3、点击修改器页面右上角“开始使用”按钮&#xff1b; 4、点击你想要瞬移的点位图标…

使用Azure Devops Pipeline将Docker应用部署到你的Raspberry Pi上

文章目录 1. 添加树莓派到 Agent Pool1.1 添加pool1.2 添加agent 2. 将树莓派添加到 Deployment Pool2.1 添加pool2.2 添加target 3. 添加编译流水线3.1 添加编译命令3.2 配置触发器 4. 添加发布流水线4.1 添加命令行4.2 配置artifact和触发器 5. 完成 1. 添加树莓派到 Agent P…

spring项目使用邮箱验证码校验

本项目采用免费QQ邮箱验证码方式进行登录安全的校验。 前期工作 申请邮箱安全授权码 打开QQ邮箱官网点击设置 进入设置页面后点击账户按钮 进入账户后一直往下拉页面找到POP3服务栏&#xff0c;然后点击管理服务&#xff08;如果没有开启服务需要先开启服务&#xff0c;按照…

vivado 时钟交互报告

步骤6&#xff1a;时钟交互报告 在创建约束之后或期间&#xff0c;必须验证约束是否完整且安全。 Vivado Design Suite默认情况下将所有时钟一起计时&#xff0c;除非您通过定义另有指定 时钟组或其他定时异常。set_clock_groups命令指定 异步或独占时钟域&#xff0c;并禁用它…

GIT | git提交注释自动添加信息头

GIT | git提交注释自动添加信息头 时间&#xff1a;2024年9月6日10:20:11 文章目录 GIT | git提交注释自动添加信息头1.操作2.commit-msg文件 1.操作 2.commit-msg文件 #!/bin/sh # # An example hook script to check the commit log message. # Called by "git commit&q…

OXC:光交叉连接(optical cross-connect)-介绍

1. 引用 https://zhuanlan.zhihu.com/p/259797386 实现对光网络的监控和光路切换 - MEMS 光开关-CSDN博客 MEMS光学器件— MEMS OXC&#xff08;光交叉互连开关&#xff09;_基于1n端口光开关构建的oxc-CSDN博客 OXC和OADM_oadm oxc-CSDN博客 光的世界第二弹&#xff1a;O…

2024全国大学生数学建模竞赛B题完整论文讲解

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024 年全国大学生数学建模竞赛B 题 生产过程中的决策问题 完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 …

SCI论文中通过图更好的呈现出自己性能的优越性

1.在大家广义的对自己工作与前人工作比较的时候&#xff0c;一般大家选用的都是表格比较方法。表格比较确实在一点程度上展示出了差异性&#xff0c;接下来我将展示用图的方法比较。例如以下的数据比较。 2.首先按照数据画出图形&#xff0c;然后对图形进行优化。 3.双击图形…