13-ESP8266连接MQTT服务器发送数据

这篇具有很好参考价值的文章主要介绍了13-ESP8266连接MQTT服务器发送数据。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Author:teacherXue

一、什么是MQTT

  1. 定义

MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。——来自于百度百科。

  1. 特点

MQTT协议是为大量计算能力有限,且工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议,它具有以下主要的几项特性:

  • 使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合;

  • 对负载内容屏蔽的消息传输;

  • 使用 TCP/IP 提供网络连接;

  • 有三种消息发布服务质量;

  • 小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量;

  • 使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制。

  1. 基本工作结构

通过百度百科的解释,我们可以看到其运行模式非常适合物联网场景中的大量不同的物联网终端和其他应用端的数据交互需求。而运行该协议服务的服务器就称之为MQTT服务器。

其基本工作结构如下图(不考虑终端用户鉴权资源隔离等情况下),MCU硬件端(MCU端可以是任何具备智能网关功能的设备,ESP芯片、开发板、手机、甚至于PC)可以任意数量接入平台,发送任意传感器数据,并接受相关控制指令。同时遵循本协议开发的应用端可以自动匹配所接入的mcu以及相应的传感器和控制方式。平台结构图如下:

13-ESP8266连接MQTT服务器发送数据
  1. 相关角色

1)MCU

微控制器单元,为具备互联网连接能力,以上报传感器数据为主要任务目标的,任意类型物联网接入端,如鸿蒙平台、esp平台、手机平台等。

2)物联网服务器

部署于公网之上的,提供mqtt消息的订阅和用户角色鉴权服务的平台,不进行存储,以获得最高的服务性能。

3)应用端

通过mqtt协议,获得相应mcu端数据,并根据业务的具体需要,提供数据的展示交互控制能力,可以是java、c#、python任意类型开发语言,业务逻辑由应用端实现。

二、mqtt消息模板和规范

大量的设备和应用都要通过MQTT消息来传输数据。那么我们需要保证数据结构的一致性,才可以保证其良好的通用性。如果使用第三方的MQTT物联网平台,则其本身对消息的协议标准还有具体要求。

本例中采用自己部署在公网上的MQTT服务器,因此协议的重点放在产品模板规定上,并适配了具有不同权限和应用的订阅地址进行隔离,有需要的同学可以参考标准订阅方式。

  1. mqtt服务器

如何构建服务器不是本章的重点,个人使用的是Eclipse Mosquitto。推荐使用cedalo综合平台,它包括一个Eclipse Mosquitto物联网服务,一个cedalo基于web的图形化管理web终端,以及EclipseStreamsheets数据可视化定制平台。其采用docker方式进行部署非常的便捷。

其安装也比较简单,重点是官方文档非常详细,提供windows、macos、树莓派、linux多种平台的安装教程。官网地址https://cedalo.com/ ,文档地址https://docs.cedalo.com/mosquitto/installation/。有需要的同学可以自己部署服务,但公网部署就要自己想办法了。

  1. 本次实验消息订阅规范

本文中公网服务器地址为实训中对学校提供,不对外无偿使用。以下协议规范考虑到了多学校、多班级、多组的共同实训,网络上的同学们需根据自己的情况修改。

1) 物联网服务器连接

访问地址:xue1024.XXXX.cn

端口:1883

通信协议标准:mqtt5.0、3.0、2.0

2)以下账户为实训院校账户

MCU用户名:院校缩写_年级_mcu

应用端用户名:院校缩写_年级_stu

密码:XXXXXXXX

3)订阅地址

消息,按不同组织、用图、产品线通过订阅地址进行区分,并按账户角色进行访问权限隔离,基础格式为:

MCU发送消息订阅地址(数据消息获得订阅与此相同):iss/行业/产品/平台/data/芯片ID(进行用户区分时,为iss/行业/产品/平台/data/用户token/芯片ID)。

应用端控制指令发送地址(MCU端控制指令订阅与此相同):iss/行业/产品/平台/order/芯片ID(进行用户区分时,为iss/行业/产品/平台/order/用户token/芯片ID)。

4)某院校实训平台订阅地址使用如下(有实训需求的院校可以和我联系):

MCU数据发布地址:iss/lot/院校缩写_年级/mcu/data/两位组号/MCU_ID/

控制指令订阅地址:iss/lot/院校缩写_年级/mcu/order/两位组号/MCU_ID/

  1. MCU端发送数据消息协议结构

此协议定义传输的数据如何封装和解析的规范,遵循本协议的MCU端和应用端可自动任意数量和传感器类型适配,开发者如使用自己的规范标准,需统MCU和应用端适配,消息长度超过部分MCU端消息默认长度,需对应修改

协议规范标注

{
    //协议版本号,发送方端和解析端需遵循相同标准*
    "protocol":"1.0",
    //iss_mcu芯片编号,取微控制器芯片MAC地址,同时是设备的局域网络接入名称*
    "chip_id": "value",
    //mcu型号,业务处理备用
    "chip_type": "芯片型号",
    //所属产品线,用以区分场景*
    "product_line": "产品线",
    //数据发送时间戳,long型*
    "timestamp": "发送时间戳",
    //微控制器接入网络的公网ip地址
    "public_ip": "公网ip",
    //内网IP地址,供客户端显示使用
    "private_ip": "内网IP",
    //提供给客户端做进一步鉴权验证使用
    "user_name": "用户名",
    //提供给客户端做进一步鉴权验证使用,用户名和密码加指令发送时间经md5加密后得到
    "user_token": "用户令牌",
    //MCU接驳传感器列表,数量任意,多数据传感器需拆分成独立逻辑传感器*
    "sensor_list": [
        {
       //mcu所接驳传感器在传感器上的调用编号,唯一整数*
            "number":  "value",
       //当前传感器型号,可重复*
            "type": "传感器型号",
       //当前传感器数据名*
            "name": "传感器名字",
       //返回消息备用
            "message": "消息",
       //传感器数据值*
            "data": {
       //数据列表,单一传感器有多个值统一控制时,例如灯光R、G、B、亮度。颜色名称、亮度等。*
             "data_vals":[
                  {
       //数据名称*
                       "data_name":"value",
       //数据值*
                        "data_val":"value",
       //该数据的上阈值
                "max_val ":  "value",
       //该数据的下阈值
                "min_val ":  "value"
                  },
                  {
                       "data_name":"value",
                         "data_val":"value",
                         "max_val ":"value",
                         "min_val ":"value"
                  }
              ],          
       //该数据类型读写状态*
                "data_type":  "read/write",
       //该数据的有效时间,具体时间时间戳的长整形
                "effective_time":  "value",
       //该数据的当前开关状态,开、关、不可控。例如当前灯光的开关状态
                "data_state":  "on/off/invalid"
            },
       //任务集合,集合方式数量任意,任务分单次任务和周期重复任务。此处为微控制器回传数据,任务列表作为应用端解析显示使用,无此需求的可以不用回传。
            "tasks": [
                {
       //任务类型,单次任务\周期任务\延迟任务*
                    "task_type":  "single/repeat/delay",
       //任务编号,唯一整数*
                    "task_number":  "value",
       //任务名称*
                    "task_name":  "value",
       //任务状态,运行中、停止中、失效的(执行端因故未能运行)
                    "task_state":  "run/stop/invalid",
       //任务状态为延时,延期执行时间,单位毫秒,也可以作为任务执行时长                  
                    "delay_time":  "value",                  
       //任务状态为repeat时,覆盖每周几 ,全部则是每天 ,单次和延期任务时无此项目              
                    "week_day": [  0, 1, 2, 3, 4, 5, 6 ],
       //任务开始时间,长整形,当前日期零时长整形到具体时间的差值,如时间段任务设置,需增加开始和结束两项任务.
                    "start_time":  "value",   
       //单次任务预定执行时间,长整形,如时间段任务设置,需增加开始和结束两项任务
                   "single_time":"value",
       //任务指令,具体被控制的属性和值
                    "exec_order": [
                        {
       //控制名
                         "order_name":  "value",
       //控制值
                             "order_val": "value"
                        },
                        {  "order_name": "value","order_val":  "value"},
                        {  "order_name": "value","order_val":  "value"}
                    ]
                },
                {            
                     "task_type": "single/repeat/delay",
                     "task_number": "value",
                     "task_name": "value",
                     "task_state": "on/off/invalid",
                     "delay_time": "timestamp",
                     "repeat_type": "no/week",
                     "week_day": [ 0, 1, 2, 3, 4, 5, 6 ],
                     "start_time": "value",
                     "single_time":"value",
                     "exec_order": [
                        {"order_name":"value","order_val":  "value"},
                         {"order_name":"value","order_val":  "value"},
                         {"order_name":"value","order_val":  "value"}
                     ]
                 },
               {             
                     "task_type":  "single/repeat/delay",
                     "task_number": "value",
                     "task_name": "value",
                     "task_state": "on/off/invalid",
                     "delay_time": "timestamp",
                     "repeat_type": "no/week",
                     "week_day": [ 0, 1, 2, 3, 4, 5, 6 ],
                     "start_time": "value",
                     "single_time":"value",
                     "exec_order": [
                         {"order_name":"value","order_val":  "value"},
                         {"order_name":"value","order_val":  "value"},
                         {"order_name":"value","order_val":  "value"}
                     ]
                 }
                 ]
        },
        {
             "...": "..."
        }
    ]
}

不含注释版本

{
    "protocol":"1.0",
    "chip_id":"value",
    "chip_type":"芯片型号",
    "product_line":"产品线",
    "timestamp":"发送时间戳",
    "public_ip":"公网ip",
    "private_ip":"内网IP",
    "user_name":"用户名",
    "user_token":"用户令牌",
    "sensor_list":[
          {
              "number":"value",
              "type":"传感器型号",
              "name":"传感器名字",
              "message":"消息",
              "data":{
                  "data_vals":[
                      {
                          "data_name":"value",
                          "data_val":"value",
                          "max_val ":"value",
                          "min_val ":"value"
                      },
                      {
                          "data_name":"value",
                          "data_val":"value",
                          "max_val ":"value",
                          "min_val ":"value"
                      }
                  ],
                  "data_type":"read/write",
                  "effective_time":"value",
                  "data_state":"on/off/invalid"
              },
              "tasks":[
                  {
                      "task_type":"single/repeat/delay",
                      "task_number":"value",
                      "task_name":"value",
                      "task_state":"run/stop/invalid",
                      "delay_time":"value",
                      "week_day":[0, 1, 2, 3, 4, 5, 6],
                      "start_time":"value",
                      "single_time":"value",
                      "exec_order":[
                          {
                              "order_name":"value",
                              "order_val":"value"
                          },
                          {
                              "order_name":"value",
                              "order_val":"value"
                          },
                          {
                              "order_name":"value",
                              "order_val":"value"
                          }
                      ]
                  },
                  {
                      "task_type":"single/repeat/delay",
                      "task_number":"value",
                      "task_name":"value",
                      "task_state":"on/off/invalid",
                      "delay_time":"timestamp",
                      "repeat_type":"no/week",
                      "week_day":[0, 1, 2, 3, 4, 5, 6],
                    "start_time":"value",
                      "single_time":"value",
                      "exec_order":[
                          {
                              "order_name":"value",
                              "order_val":"value"
                          },
                          {
                              "order_name":"value",
                              "order_val":"value"
                          },
                          {
                              "order_name":"value",
                              "order_val":"value"
                          }
                      ]
                  },
                  {
                      "task_type":"single/repeat/delay",
                      "task_number":"value",
                      "task_name":"value",
                      "task_state":"on/off/invalid",
                      "delay_time":"timestamp",
                      "repeat_type":"no/week",
                      "week_day":[0, 1, 2, 3, 4, 5, 6],
                      "start_time":"value",
                      "single_time":"value",
                      "exec_order":[
                          {
                              "order_name":"value",
                              "order_val":"value"
                          },
                          {
                              "order_name":"value",
                              "order_val":"value"
                          },
                          {
                              "order_name":"value",
                              "order_val":"value"
                          }
                      ]
                  }
              ]
          },
          {
              "...":"..."
          }
      ]
}
  1. 协议项说明

协议名

作用

值列表

protocol

当前协议版本,为解析依据

1.0

chip_id

MCU唯一识别编号,为MAC地址,同时为MCU端的连接ID

Iss_十六进制

chip_type

//mcu型号,业务处理备用

product_line

所属产品线,用以区分场景

智慧农业、智慧家居、智慧园区…

timestamp

数据发送时间戳,long型

public_ip

MCU接入时的公网IP地址,可以大数据做地域分析。应用端可以根据地域查询地域公共信息。

private_ip

MCU接入局域网IP地址,应用端如需管理设备时使用。

user_name

用户名,应用端做进一步鉴权使用

user_token

用户访问令牌

sensor_list[ ]

MCU接驳传感器列表,应用端获得MCU所挂载的传感器集合

传感器

{ sensor_list }number

传感器在MCU上管理的序号,MCU端唯一。

{ sensor_list }type

传感器型号,应用端做界面构建时的类型判断。

例如lamp(灯)、led(led灯)、T(温度)、H(湿度)、PW(电源)、RGB(三色全彩灯)、LUX(光照)、GAS(气体)、curtain(窗帘)、motor(电机)、lamp_n(不可调光),voice(声音),flame(火焰),smoke(烟雾),PIR(红外遥控器),buzzer(蜂鸣器),body(人体),knob(旋钮控制器),water(水位)、soil(土壤)、hm(距离)、valve(阀门控制),具体取值参考设备名称对应表

{ sensor_list }name

传感器名字,应用端做界面构建时控制类型判断

{ sensor_list }message

返回消息备用

字符长度小于等于50

{ sensor_list }data

传感器数据值,用于返回检测数据

data_vals子对象集合

{ sensor_list }.{data}data_vals[ ]

数据列表,单一传感器有多个值统一控制时,

例如灯光R、G、B、亮度。颜色名称、亮度等

{sensor_list}.{data}.[data_vals] data_name

传感器数据对应名称

例如可控灯的亮度、色温。具体取值参考控制参数名称对应列表。

{ sensor_list }.{data}.[data_vals]data_val

传感器对应数据值。

{ sensor_list }.{data}.[data_vals]max_val

该数据的上阈值

{ sensor_list }.{data}.[data_vals]min_val

该数据的下阈值

{ sensor_list }.{data}data_type

传感器数据类型,是否可读可控

r w rw

{ sensor_list }.{data}effective_time

数据发送后是否有有效时间

毫秒为单位的长整形

{ sensor_list }.{data}data_state

该传感器此数据项状态

开、关、不可控,具体取值参考控制参数名称对应列表。

{ sensor_list }.{ tasks }[ ]

当前传感器任务集合,集合方式数量任意,任务分单次任务和周期重复任务。此处为微控制器回传数据,任务列表作为应用端解析显示使用,无此需求的可以不用回传。

任务项集合

{ sensor_list }.{ tasks }[ ] task_type

任务类型

任务类型,单次任务\周期任务\延迟任务,single/repeat/delay,具体取值参考控制参数名称对应列表。

{ sensor_list }.{ tasks }[ ] task_number

任务编号,当传感器唯一整数

{ sensor_list }.{ tasks }[ ] task_name

任务名称

{ sensor_list }.{ tasks }[ ] task_state

任务状态

运行中、停止中、失效的(执行端因故未能运行),run/stop/invalid,具体取值参考控制参数名称对应列表。

{ sensor_list }.{ tasks }[ ] delay_time

任务状态为延时,延期执行时间,单位毫秒,也可以作为任务执行时长

毫秒为单位的长整形

{ sensor_list }.{ tasks }[ ] week_day[ ]

任务状态为repeat时,覆盖每周几 ,全部则是每天 ,单次和延期任务时无此项目

0~6序列,代表周日到周六。

0,1,2,3,4,5,6

{ sensor_list }.{ tasks }[ ] start_time

任务开始时间,长整形,当前日期零时长整形到具体时间的差值,如时间段任务设置,需增加开始和结束两项任务.

{ sensor_list }.{ tasks }[ ] single_time

单次任务预定执行时间,长整形,如时间段任务设置,需增加开始和结束两项任务

{ sensor_list }.{ tasks }[ ] exec_order[ ]

任务指令,具体被控制的属性和值

控制项:控制值

{ sensor_list }.{ tasks }[ ] exec_order[ ] order_name

控制项名称,

如转速、预定角度、亮度,具体取值参考控制参数名称对应列表。

{ sensor_list }.{ tasks }[ ] exec_order[ ] order_val

控制值

  1. 传感器名称和数据对应表

以下规范为传感器数据特性,非产品本身,产品可以是多个传感器控制器的封装。MCU端提报数据时按此协议规范,包括传感器数据和控制器当前状态数据。开发人员可根据自身平台设备进行扩充定义,如命名方式尊循该规范可以获得更好的适配性(on/off no/yes 为布尔型)。

以下规范为传感器数据特性,非产品本身,产品可以是多个传感器控制器的封装。MCU端提报数据时按此协议规范,包括传感器数据和控制器当前状态数据。开发人员可根据自身平台设备进行扩充定义,如命名方式尊循该规范可以获得更好的适配性(on/off no/yes 为布尔型)。

  • 智能家居类别

名称

设备类型

值类型

控制参数说明

灯光

控制类

lamp_no

不可调光灯光

开关状态

data_vals[{"data_name": “state” ,"data_val":“on/off”}]

lamp_ctl

可调光

开关状态

百分比亮度值

data_vals[

{"data_name": “state” ,"data_val": “on/off”},

{"data_name": “extent” ,"data_val": “0~100”}

]

lamp_cct

色温可调灯光

开关状态

百分比亮度值

百分比色温值

data_vals[

{"data_name": “state” ,"data_val": “on/off”},

{"data_name": “extent” ,"data_val": “0~100”},

{"data_name": “cct_val” ,"data_val": “0~100”}

]

lamp_rgb

全彩可调灯光

开关状态

百分比亮度值

百分比色温值

颜色名称/

红色通道

绿色通道

蓝色通道/

H色调通道

S饱和度通道

V明度通道

data_vals[

{"data_name": “state” ,"data_val": “on/off”},

{"data_name": “extent” ,"data_val": “0~100”},

{"data_name": “cct_val” ,"data_val": “0~100”},

{"data_name": “color”,"data_val": “red/blue/golden…标准颜色名称”},

{"data_name": “r_val” ,"data_val": “0~255”},

{"data_name": “g_val” ,"data_val": “0~255”},

{"data_name": “b_val” ,"data_val": “0~255”},

{"data_name": “h_val” ,"data_val": “0~255”},

{"data_name": “s_val” ,"data_val": “0~255”},

{"data_name": “v_val” ,"data_val": “0~255”}

]

lamp_pro

程控灯光

由对应设备开发人员自行定义控制项目

开关伺服

控制类

pw_sw

电源开关

开关状态

data_vals[{"data_name": “state” ,"data_val": “on/off”}]

knob_ctl

旋转开关

开关状态

开关幅度

data_vals[

{"data_name": “state” ,"data_val": “on/off”},

{"data_name": “extent” ,"data_val": “0~100”}

]

valve_ctl

可控阀门

阀门打开程度

data_vals[{"data_name": “state” ,"data_val":“0~100”}]

curtain_ctl

可控窗帘

窗帘打开幅度

data_vals[{"data_val": “0~100”}]

jalousie_ctl

可控百叶窗帘

窗帘打开幅度

百叶方向

百叶偏转幅度

data_vals[

{"data_name": “extent” ,"data_val": “0~100”},

{"data_name": “direction” ,"data_val": “up/down”},

{"data_name": “angle” ,"data_val": “0~100”}

]

window_ctl

可控窗户

窗户打开幅度

data_vals[{"data_val": “0~100”}]

buzzer

蜂鸣器

蜂鸣状态

data_vals[{"data_val": “on/off”}]

servo

舵机执行器

舵机角度

data_vals[

{"data_val": “浮点数”}

]

step_motor

步进电机

当前步数

目标步数

速度

data_vals[

{"data_name": “val” ,"data_val": “整数},

{"data_name": “target” ,"data_val": “整数},

{"data_name": “speed” ,"data_val": “整数”}

]

door_ctl

可控门

门打开幅度

data_vals[{"data_val": “0~100”}]

数据传感器类

temp

温度传感器

温度值

data_vals[{"data_val": “有符号浮点数”}]

hum

湿度传感器

湿度值

data_vals[{"data_val": “有符号浮点数”}]

th

温湿度传感器

温度值

湿度值

data_vals[

{"data_name": “t_val” ,"data_val": “有符号浮点数”},

{"data_name": “h_val” ,"data_val": “有符号浮点数”}

]

lx

光照强度传感器

照度值

data_vals[

{"data_val": “整数”}]

voice

声音传感器

声响响应

模拟量值

data_vals[

{"data_name": “rps” ,"data_val": “no/yes”},

{"data_name": “val” ,"data_val": “浮点数”}

]

smoke

烟雾传感器

烟雾响应

模拟量值

data_vals[

{"data_name": “rps” ,"data_val": “no/yes”},

{"data_name": “val” ,"data_val": “浮点数”}

]

body

人体传感器

人体响应

data_vals[{"data_val": “no/yes”}]

flame

火焰传感器

火焰响应

模拟量值

data_vals[{"data_name": “rps” ,"data_val": “no/yes”},

{"data_name": “val” ,"data_val": “浮点数”}

]

water

水传感器

浸润响应

模拟量值

data_vals[{"data_name": “rps” ,"data_val": “no/yes”},

{"data_name": “val” ,"data_val": “浮点数”}

]

water_lv

水位传感器

水位值

data_vals[{"data_val": “有符号浮点数”}]

raindrop

雨滴传感器

下雨状态

模拟量值

data_vals[

{"data_name": “rps” ,"data_val": “no/yes”},

{"data_name": “val” ,"data_val": “浮点数”}

]

hpa

气压传感器

气压值

data_vals[{"data_val": “有符号浮点数”}]

fuelgas

燃气传感器

燃气报警

模拟量值

data_vals[

{"data_name": “rps” ,"data_val": “no/yes”},

{"data_name": “val” ,"data_val": “浮点数”}

]

soil

土壤传感器

土壤湿度响应

模拟量值

data_vals[

{"data_name": “rps” ,"data_val": “no/yes”},

{"data_name": “val” ,"data_val": “浮点数”}

]

hm

距离传感器

距离响应状态

距离值

距离单位

data_vals[

{"data_name": “rps” ,"data_val": “no/yes”},

{"data_name": “val” ,"data_val": “浮点数”},

{"data_name": “unit” ,"data_val": “value”}

]

voltage

电压传感器

电压值

data_vals[

{"data_val": “浮点数”}

]

current

电流传感器

电流值

data_vals[

{"data_val": “浮点数”}

]

  1. 应用端指令发送协议结构

协议规范

{

//芯片ID

"chip_id":value,

//发送时间戳

"timestamp":value,

//指令列表

"task_list":[

//指令1

{

//要控制的外设编号,和MCU端外设匹配

"number":value,

//控制项和控制值,处理时要先判断是否有值,再进行处理

"控制项1": value },

//指令2

{

//要控制的外设编号,和MCU端外设匹配

"number": value,

//控制项和控制值,处理时要先判断是否有值,再进行处理

"控制项1": value,

//可以有多个控制项,控制项参考传感器数据定义协议

"控制项2": value

}

]}

案例

{

"chip_id": "xm_00749E03",

"timestamp": 1677856857,

"task_list": [{

"number": 1,

"state": true

},

{

"number": 2,

"state": true,

"extent": 30

}

]

}

三、ESP8266实现MQTT消息发送

下面我们将按照预定标准,使用之前章节构建的平台进行mqtt数据的发送。此实验网络中上的小伙伴需自行解决MQTT服务器问题,可以自己安装。

  1. 创建项目

1)新建项目Lot_mqtt_json_test_v2.0,我们将在上一章节项目上修改。

2)配置串口波特率115200

3)需要通过扩展库管理器安装如下扩展库

  • DHT sensor library

  • ArduinoJson

  • TaskScheduler

  • JC_Button

  • NTPClient

  • PubSubClient:MQTT订阅扩展库,本例中数据较大,初始化时需定义缓冲大小。

4)在项目lib文件夹本地安装库的方式安装以下扩展库:

  • WiFiManager:如果无高需求,也可以采用上面扩展库安装方式。

  1. 代码解析

1)在上一章节项目中增加头文件导入mqtt消息库,以及网络时间服务的NTPClient,以及所需的UDP库

#include <Arduino.h>
#include <JC_Button.h>
#include <WiFiManager.h>
// 导入多任务库
#include <TaskScheduler.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
// 导入NTP网络时间服务扩展库
#include <NTPClient.h>
// 导入UDP扩展库,
#include <WiFiUdp.h>
// json对象处理类,6.x 和5.x不一样 ,这里用的是6.19
#include <ArduinoJson.h>
#include <PubSubClient.h> // mqtt消息订阅库

2)声明mqtt消息服务所需的参数

// mqtt服务器连接信息
// xue1024.tpddns.cn
const char *mqttServer = "xue1024.XXXXX.cn"; // 个人mqtt服务器
const int mqttPort = 1883;                    // mqtt端口号
const char *mqttUser = "院校缩写_年级_mcu";        // 用户名
const char *mqttPassword = "你的密码";      // 密码

3)准备存储订阅发送和订阅接收的消息地址缓冲

// 传感器数据订阅地址,mcu向里面发数据,客户端去里面取数据
char outTopic[50]; // 订阅发送地址
// 控制指令订阅地址,mcu订阅,客户端发送
char inTopic[50]; // 订阅接收地址缓存

4)创建新增扩展库对象

// mqtt连接对象
WiFiClient espClient;
PubSubClient client(espClient);

// 创建UDP对象
WiFiUDP ntpUDP;
//NTP时间服务器对象
NTPClient timeClient(ntpUDP, "ntp.aliyun.com");

5)函数前置声明,其中callback是mqtt接收到消息后执行的回调函数,reconnect为保持mqtt连接的方法,publishMQTT为发送消息的方法。

// 前置函数声明
void getTH();
void getKnob();
void getJson();
void callback(char *topic, byte *payload, unsigned int length); // mqtt回调函数
void reconnect();                        // mqtt保持连接
void publishMQTT();                      // 发送mqtt数据

6)上一章节的getJson不在需要任务调度,由消息发送时调度生成即可。

Task t3(5000, TASK_FOREVER, &publishMQTT); // 任务名称t3,间隔5秒,一直执行。

7)Begin方法中增加ntp时间服务器对象的初始化,并调整中国地区的时区差,标准时区+8。

// 初始化NTP时间服务器连接
  timeClient.begin();
  // 设置时区偏差,中国地区偏差+8
  // GMT +1 = 3600
  // GMT +8 = 28800
  // GMT -1 = -3600
  // GMT 0 = 0
  timeClient.setTimeOffset(28800);

8)Setup方法内增加订阅消息地址的完善,修改mqtt消息大小和连接超时时间

//-------------- 订阅地址(xm替换为组号)--------------
  sprintf(inTopic, "iss/lot/xust_19/mcu/order/两位组号/%s/", chipId);
  sprintf(outTopic, "iss/lot/xust_19/mcu/data/两位组号/%s/", chipId);
  Serial.printf("inTopic:%s\n", inTopic);
  Serial.printf("outTopic:%s\n", outTopic);
  // mqtt连接
  client.setServer(mqttServer, mqttPort);
  client.setBufferSize(2048);//设置mqtt消息传输包的大小
  client.setSocketTimeout(60);//设置mqtt连接超时
  client.setCallback(callback);//接收到消息后的回调处理方法
  //-----------------------------------------------

9) 保持mqtt连接的方法,首次连接时也是此方法

// mqtt保持连接方法
void reconnect()
{
  // Loop until we're reconnected
  while (!client.connected())
  {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    // chipId 在这里是客户端连接的sessionid,同账户名下,不能相同,这里用芯片id
   String clientId = String(chipId)+"-"+String(random(0xffff), HEX);
    if (client.connect(clientId.c_str(), mqttUser, mqttPassword))
    {
      Serial.println("connected...");
    }
    else
    {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);//如果失败延迟五秒后连接
    }
  }
}

10) 发布mqtt消息的方法

// 发布mqtt消息
void publishMQTT()
{
  getJson(); // 生成要发送的json数据
//保持连接
  if (!client.connected())
  {
    reconnect();
  }
  client.loop();

  // 发送mqtt数据
  client.publish(outTopic, messageBuff, true);
  // Serial.println(messageBuff);
  //client.subscribe(inTopic);
  //关闭连接
  client.endPublish();  
  // 发送完成后清除缓存数据
  memset(messageBuff, 0, 1024);
}

11) 完整代码

#include <Arduino.h>
#include <JC_Button.h>
#include <WiFiManager.h>
// 导入多任务库
#include <TaskScheduler.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
// 导入NTP网络时间服务扩展库
#include <NTPClient.h>
// 导入UDP扩展库,
#include <WiFiUdp.h>
// json对象处理类,6.x 和5.x不一样 ,这里用的是6.19
#include <ArduinoJson.h>
#include <PubSubClient.h> // mqtt消息订阅库

#define LED_1 D5
#define LED_2 D6
#define BUTTON_PIN D4
#define DHTPIN D3      // DHT温湿度传感器引脚
#define analogInPin A0 // 模拟输入引脚A0

#define DHTTYPE DHT22     // 声明DHT传感器类型
DHT dht(DHTPIN, DHTTYPE); // 创建DHT对象
Scheduler runner;         // 任务调度器对象
Button myBtn(BUTTON_PIN); // 按钮对象

// 温湿度值全局保存
float t = 0.0;
float h = 0.0;
// led灯光状态
bool led1state = false;
bool led2state = false;
// 旋钮值和灯光亮度的保存
unsigned int knobValue = 0;
unsigned int dutyCycle = 0;

// 存放芯片ID的缓冲
char chipId[10];
// 消息发送缓冲,json最后字符串方式传输
char messageBuff[1024];

// mqtt服务器连接信息
// xue1024.tpddns.cn
const char *mqttServer = "xue1024.tpddns.cn"; // 个人mqtt服务器
const int mqttPort = 1883;                    // mqtt端口号
const char *mqttUser = "shixun_admin";        // 用户名
const char *mqttPassword = "teacherxue";      // 密码
// long mqtt_interval = 2000;
// long mqtt_comiit = 0;
// 传感器数据订阅地址,mcu向里面发数据,客户端去里面取数据
char outTopic[50]; // 发送地址
// 控制指令订阅地址,mcu订阅,客户端发送
char inTopic[50]; // 订阅地址缓存
// mqtt连接对象
WiFiClient espClient;
PubSubClient client(espClient);

// 创建UDP对象
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp.aliyun.com");

// 前置函数声明
void getTH();
void getKnob();
void getJson();
void callback(char *topic, byte *payload, unsigned int length); // mqtt回调函数
void reconnect();                                               // mqtt保持连接
void publishMQTT();                                             // 发送mqtt数据

Task t1(2000, TASK_FOREVER, &getTH);       // 任务名称t1,间隔2秒一直执行.
Task t2(30, TASK_FOREVER, &getKnob);       // 任务名称t2,间隔30毫秒,一直执行。
Task t3(5000, TASK_FOREVER, &publishMQTT); // 任务名称t3,间隔5秒,一直执行。
// Task t3(3000, TASK_FOREVER, &getJson);

void setup()
{
  Serial.begin(115200);
  pinMode(LED_1, OUTPUT);
  pinMode(LED_2, OUTPUT);
  // led1为关灯
  digitalWrite(LED_1, LOW);
  // led2使用pwm调光
  analogWrite(LED_2, 0);

  dht.begin();    // DHT传感器对象工作
  myBtn.begin();  // button按钮运作
  WiFiManager wm; // wifi管理对象,配网用
  bool res;
  // 拼接芯片的hostname
  sprintf(chipId, "xm_%08X", ESP.getChipId());
  res = wm.autoConnect(chipId, "12345678"); // 密码认证模式的AP
  if (!res)
  {
    Serial.println("Failed to connect");
    // ESP.restart();
  }
  else
  {
    // if you get here you have connected to the WiFi
    WiFi.setHostname(chipId); // 从模式后设置设备名
    Serial.println("connected...yeey :)");
  }
  // 初始化NTP时间服务器连接
  timeClient.begin();
  // 设置时区偏差,中国地区偏差+8
  // GMT +1 = 3600
  // GMT +8 = 28800
  // GMT -1 = -3600
  // GMT 0 = 0
  timeClient.setTimeOffset(28800);
  // 初始化任务调度器,规划任务链
  runner.init();
  runner.addTask(t1);
  runner.addTask(t2);
  runner.addTask(t3);
  t1.enable();
  t2.enable();
  t3.enable();

  //-------------- 订阅地址(xm替换为组号)-------------------
  sprintf(inTopic, "iss/lot/xust_19/mcu/order/xm/%s/", chipId);
  sprintf(outTopic, "iss/lot/xust_19/mcu/data/xm/%s/", chipId);
  Serial.printf("inTopic:%s\n", inTopic);
  Serial.printf("outTopic:%s\n", outTopic);
  // mqtt连接  
  client.setServer(mqttServer, mqttPort);
  client.setBufferSize(2048);//设置mqtt消息传输包的大小
  client.setSocketTimeout(60);//设置mqtt连接超时
  client.setCallback(callback);
  //-----------------------------------------------
}

void loop()
{
  runner.execute(); // 执行任务
  // 处理按钮事件
  myBtn.read();
  if (myBtn.wasPressed())
  {
    led1state = !led1state;    
  }
digitalWrite(LED_1, led1state);
  analogWrite(LED_2, dutyCycle); // 根据状态控灯
}

// 获得温湿度
void getTH()
{
  h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  t = dht.readTemperature();
}

// 旋钮操作
void getKnob()
{
  // 关灯时不做任何调整
  // if (!led2state)
  // {
  //   return;
  // }
  knobValue = analogRead(analogInPin);
  dutyCycle = map(knobValue, 15, 1008, 0, 255);
}

// 获得json封装的数据
void getJson()
{
  //------------封装json对象传递----------------------------
  /* 申明一个大小为1K的DynamicJsonDocument对象JSON_Buffer,
     用于存储反序列化后的(即由字符串转换成JSON格式的)JSON报文,
  */
  // DynamicJsonDocument  doc(2048);
  // 创建json对象
  StaticJsonDocument<1024> doc;
  // 创建json根节点对象
  JsonObject root = doc.to<JsonObject>();
  // root节点下创建子节点并赋值
  root["protocol"] = "1.0";
  root["chip_id"] = chipId;
  root["chip_type"] = "ESP8266-12E";
  root["product_line"] = "xust_19_teacher";
  root["private_ip"] = WiFi.localIP();
  root["public_ip"] = "";
  timeClient.update();
  root["timestamp"] = timeClient.getEpochTime();

  // 创建json对象集合,存放该mcu节点下的所有传感器列表
  JsonArray sensors = root.createNestedArray("sensor_list");
  // 集合节点,创建子节点对象
  // 1.不可调光主灯
  JsonObject s1 = sensors.createNestedObject();
  s1["number"] = 1;
  s1["type"] = "amp_no";
  s1["name"] = "厨房灯";
  JsonObject s1_data = s1.createNestedObject("data");
  JsonArray s1_data_ls = s1_data.createNestedArray("data_vals");
  JsonObject s1_data1 = s1_data_ls.createNestedObject();
  s1_data1["data_name"] = "state";
  s1_data1["data_val"] = led1state;

  // 2.可调光主灯
  JsonObject s2 = sensors.createNestedObject();
  s2["number"] = 2;
  s2["type"] = "lamp_ctl";
  s2["name"] = "主卧灯";
  JsonObject s2_data = s2.createNestedObject("data");
  JsonArray s2_data_ls = s2_data.createNestedArray("data_vals");
  JsonObject s2_data1 = s2_data_ls.createNestedObject();
  s2_data1["data_name"] = "state";
  s2_data1["data_val"] = led2state;
  JsonObject s2_data2 = s2_data_ls.createNestedObject();
  s2_data2["data_name"] = "extent";
  s2_data2["data_val"] = dutyCycle;

  // 3.温湿度传感器
  JsonObject s3 = sensors.createNestedObject();
  s3["number"] = 3;
  s3["type"] = "th";
  s3["name"] = "温湿度";
  JsonObject s3_data = s3.createNestedObject("data");
  JsonArray s3_data_ls = s3_data.createNestedArray("data_vals");
  JsonObject s3_data1 = s3_data_ls.createNestedObject();
  s3_data1["data_name"] = "t_val";
  s3_data1["data_val"] = t;
  JsonObject s3_data2 = s3_data_ls.createNestedObject();
  s3_data2["data_name"] = "h_val";
  s3_data2["data_val"] = h;
}

// mqtt保持连接方法
void reconnect()
{
  // Loop until we're reconnected
  while (!client.connected())
  {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    // chipId 在这里是客户端连接的sessionid,同账户名下,不能相同,这里用芯片,固定配置下写死即可uc_01_keting_01
   String clientId = String(chipId)+"-"+String(random(0xffff), HEX);
    if (client.connect(clientId.c_str(), mqttUser, mqttPassword))
    {
      Serial.println("connected...");
    }
    else
    {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

// 发布mqtt消息
void publishMQTT()
{
  getJson(); // 生成要发送的json数据
  if (!client.connected())
  {
    reconnect();
  }
  client.loop();

  // 发送mqtt数据
  client.publish(outTopic, messageBuff, true);
  Serial.print("messageBuff:");
  Serial.println(messageBuff);
  //关闭连接
  client.endPublish();  
  // 发送完成后清除缓存数据
  memset(messageBuff, 0, 1024);
}

// mqtt回调函数_这里未做进一步处理,下一章节进行解析
void callback(char *topic, byte *payload, unsigned int length)
{
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  payload[length] = '\0'; // 追加字符串结束字符
  for (int i = 0; i < length; i++)
  {
    Serial.print((char)payload[i]);
  }
  Serial.print("\n");
  Serial.println();
  }
  1. 补全说明

本例重点为常规数据的发送,无紧急需求,所以采取发送时才建立连接,像ESP01芯片单独部署的温湿度传感器5分钟上报一次数据已经足够。下一章节中接收指令时,如果要求有即时性的命令,改为保持连接。

本例中未处理控制指令,LED2没有对APP控制做出兼容。下一章节将完整实现远程和本地的旋钮都可以控制LED2的亮度。文章来源地址https://www.toymoban.com/news/detail-436152.html

到了这里,关于13-ESP8266连接MQTT服务器发送数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32 x ESP8266】连接阿里云 MQTT 服务器(报文连接)

    具体如何在阿里云创建一个 MQTT 服务器我就不写出来了,可以去百度一下(主要是我忘了,不好演示)。 有关 MQTT 报文的一些标识在我前面的 一篇文章 中讲过,感兴趣的可以去看看( 有关代码部分讲解的内容也在那里 ,这里就不细讲了,主要讲如何连接的) 插一嘴,如果

    2024年02月06日
    浏览(57)
  • STM32通过esp8266连接WiFi接入MQTT服务器

    上文我们讲到如何搭建本地MQTT服务器http://t.csdn.cn/aHNcO,现在介绍如何通过stm32连接MQTT 一.首先我们初始化esp8266这里我们使用的是USART4与其通信代码如下 二.通过数据间隔时间来判断是否是一串数据如果间隔时间大于30ms则判定为不是一串数据,接收缓冲器清零。 代码如下!

    2024年02月07日
    浏览(54)
  • ESP8266-01 MQTT固件烧录并连接阿里云服务器

    1、烧录工具:flash_download_tool :   链接:https://pan.baidu.com/s/1jJJ07G56jNJ3SrOwstPzVA?pwd=8i93  提取码:8i93 2、串口调试助手:串口调试助手 3、阿里云配置生成工具: 链接:https://pan.baidu.com/s/13tjpFwGMeR0juS5OSEBiJQ?pwd=61me  提取码:61me 4、USB TO TTL 5、esp8266 01s 6、AT固件:下载   向esp8266烧

    2024年02月06日
    浏览(69)
  • ESP8266连接OneNet服务器

    配置ESP8266为客户端,连接公网,连接上OneNet服务器,通过MQTT协议与服务器进行交互。 一 准备工作 OneNet平台上创建自己的产品设备信息。 对MQTT协议进行一定了解。 准备一块ESP8266,通过串口进行ESP8266的配置。 二 配置ESP8266 1.AT 验证硬件是否正常 硬件正常返回:AT OK 2.AT+RS

    2024年02月01日
    浏览(44)
  • 【雕爷学编程】Arduino智慧校园之使用ESP8266连接WiFi并上传温度数据到服务器

    Arduino是一个开放源码的电子原型平台,它可以让你用简单的硬件和软件来创建各种互动的项目。Arduino的核心是一个微控制器板,它可以通过一系列的引脚来连接各种传感器、执行器、显示器等外部设备。Arduino的编程是基于C/C++语言的,你可以使用Arduino IDE(集成开发环境)来

    2024年02月21日
    浏览(51)
  • 从零开始用Nodejs搭建一个MQTT服务器,并且用stm32通过esp8266进行消息订阅和发布

    最近在做一个物联网项目,需要用到服务器进行数据的存储和数据的请求和发送,之前我用过onenet平台上的http服务,虽然能通过get和post请求进行数据的提交和发送,但是平台上的数据发生改变却不能主动推送给esp8266,与我此次的项目不符合,所以pass。然后我了解了下mqtt协

    2024年02月04日
    浏览(52)
  • esp8266WIFI模块教程:ATK-ESP8266——TCP网络通讯和服务器连接

      ATK-MW8266D 模块是正点原子推出的一款高性能 UART-WIFI(串口-无线)模块,ATK-MW8266D 模块板载了正点原子公司自主研发的 ATK-ESP-01 模块,该模块通过 FCC、CE 认证,可直接用于出口欧美地区的产品。   ATK-MW8266D 模块采用串口(LVTTL)与 MCU(或其他串口设备)通讯,且内置

    2024年04月15日
    浏览(47)
  • ESP-01S烧录及使用,连接本地MQTT服务器

    网上的教程大多无法正常烧录与使用,因此在这里进行总结。 固件烧录建议使用专用的烧录器,十分省心。 本文讨论的是使用普通的CH340串口模块进行烧写。 必须使用的工具:杜邦线、CH340串口模块、另外一块有3V3电源和GND的开发板。 CH340 TTL转USB模块的引脚数量不够,因此

    2024年02月16日
    浏览(42)
  • 手机、电脑mqtt客户端通过腾讯云服务器远程连接ESP32

            本文将实现:         1、esp32与腾讯云物联网服务器通过mqtt协议通信         2、电脑和手机客户端通过mqtt与腾讯云相通信         3、腾讯云服务器内部消息转发,将手机、电脑发布的主题转发给esp32订阅,实现手机、电脑与esp32的远程通信。      

    2024年02月11日
    浏览(62)
  • 【MQTT基础篇(四)】ESP8266连接MQTT服务端

    首先一起来学习如何使用电脑软件来实现客户端与服务端的连接。为电脑安装一款免费的MQTT客户端软件–MQTTfx。MQTT客户端软件有很多款,不过经过使用和比较,发现免费的MQTTfx软件在易用性和功能性都非常出色。 通过MQTTfx软件,我们的电脑将会成为一台MQTT客户端。您可以通

    2023年04月23日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包