MQTT物联网项目(基于I.MX6NULL Linux开发板)

1. MQTT简介

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

  • 使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。
  • 基于 TCP/IP 提供网络连接。
  • 支持 QoS 服务质量等级。
  • 小型传输, 开销很小,协议交换最小化,以降低网络流量。
  • 使用 will 遗嘱机制来通知客户端异常断线
  • 基于主题发布/订阅消息,对负载内容屏蔽的消息传输。
  • 支持心跳机制。

MQTT是一种基于客户端-服务端架构的消息传输协议,MQTT协议通信的大致流程如下图所示:

在这里插入图片描述

2. MQTT客户端库API介绍

在编写客户端程序时,需要了解MQTT客户端库提供的常用的API。

2.1 MQTTClient_message结构体

MQTT 客户端应用程序发布消息和接收消息都是围绕着这个结构体。 MQTTClient_message 数据结构描述了 MQTT 消息的负载和属性等相关信息,譬如消息的负载、负载的长度、 qos、消息的保留标志、 dup 标志等。该结构体内容如下:

typedef struct
{
	int payloadlen; //负载长度
	void* payload; //负载
	int qos; //消息的 qos 等级
	int retained; //消息的保留标志
	int dup; //dup 标志(重复标志)
	int msgid; //消息标识符
	......
} MQTTClient_message;

2.2 创建一个客户端对象

在连接服务端之前,需要创建一个客户端对象,使用 MQTTClient_create 函数创建:

int MQTTClient_create(MQTTClient *handle,
const char *serverURI,
const char *clientId,
int persistence_type,
void *persistence_context
);

handle: MQTT 客户端句柄;

serverURL: MQTT 服务器地址;

clientId: 客户端 ID;

persistence_type: 客户端使用的持久化类型:

2.3 连接服务端

int MQTTClient_connect(MQTTClient handle,
MQTTClient_connectOptions *options
);

handle: 客户端句柄;

options: 一个指针。指向一个 MQTTClient_connectOptions 结构体对象。 MQTTClient_connectOptions 结构体中包含keepAlive、 cleanSession 以及一个指向 MQTTClient_willOptions 结构体对象的指针 will_opts;MQTTClient_willOptions 结构体包含了客户端遗嘱相关的信息, 遗嘱主题、遗嘱内容、遗嘱消息的 QoS 等级、遗嘱消息的保留标志等。

2.4 调用回调函数

调用 MQTTClient_setCallbacks 函数为应用程序设置回调函数, MQTTClient_setCallbacks 可设置多个回调函数,包括:

  • 断开连接时的回调函数 cl (当客户端检测到自己掉线时会执行该函数,如果将其设置为 NULL表示应用程序不处理断线的情况) 、

  • 接收消息的回调函数 ma(当客户端接收到服务端发送过来的消息时执行该函数,必须设置此函数否则客户端无法接收消息)、

  • 发布消息的回调函数 dc(当客户端发布的消息已经确认发送时执行该回调函数,如果你的应用程序采用同步方式发布消息或者您不想检查是否成功发送时,您可以将此设置为 NULL) 。

int MQTTClient_setCallbacks(MQTTClient handle,
void *context,
MQTTClient_connectionLost *cl,
MQTTClient_messageArrived *ma,
MQTTClient_deliveryComplete *dc
);

handle: 客户端句柄;

context: 执行回调函数的时候,会将 context 参数传递给回调函数,因为每一个回调函数都设置了一个参数用来接收 context 参数。

cl: 一个 MQTTClient_connectionLost 类型的函数指针,

ma: 一个 MQTTClient_messageArrived 类型的函数指针

dc: 一个 MQTTClient_deliveryComplete 类型的函数指针

注意:调用 MQTTClient_setCallbacks 函数设置回调必须在连接服务器之前完成!

2.5 发布消息

当客户端成功连接到服务端之后,便可以发布消息或订阅主题了,应用程序通过MQTTClient_publishMessage 库函数来发布一个消息:

int MQTTClient_publishMessage(MQTTClient handle,
const char *topicName,
MQTTClient_message *msg,
MQTTClient_deliveryToken *dt
);

handle: 客户端句柄;

topicName: 主题名称。向该主题发布消息。

msg: 指向一个 MQTTClient_message 对象的指针。

dt: 返回给应用程序的传递令牌。

返回值: 成功返回 MQTTCLIENT_SUCCESS,失败返回错误码。

2.6 订阅主题和取消订阅主题

客户端应用程序调用 MQTTClient_subscribe 函数来订阅主题:

int MQTTClient_subscribe(MQTTClient handle,
const char *topic,
int qos
);

handle: 客户端句柄;

topic: 主题名称。客户端订阅的主题。

qos: QoS 等级。

返回值: 成功返回 MQTTCLIENT_SUCCESS,失败返回错误码 。

当客户端想取消之前订阅的主题时,可调用 MQTTClient_unsubscribe 函数,如下所示:

int MQTTClient_unsubscribe(MQTTClient handle,
const char *topic
);

handle: 客户端句柄;

topic: 主题名称。取消订阅该主题。

返回值: 成功返回 MQTTCLIENT_SUCCESS,失败返回错误码。

2.7 断开服务器连接

当客户端需要主动断开与客户端连接时,可调用 MQTTClient_disconnect 函数:

int MQTTClient_disconnect(MQTTClient handle,
int timeout
);

handle: 客户端句柄;

timeout: 超时时间。 客户端将断开连接延迟最多 timeout 时间(以毫秒为单位),以便完成正在进行中的消息传输。

返回值: 如果客户端成功从服务器断开连接,则返回 MQTTCLIENT_SUCCESS; 如果客户端无法与服务器断开连接,则返回错误代码。

3. 编写客户端程序

3.1功能设计

  • 基于公共MQTT服务器实现个人物联网小项目
  • 用户可通过手机或电脑远程控制Linux开发板上的外设,譬如LED、蜂鸣器
  • 开发板客户端每隔一段时间向服务端发送当前开发板的运行状态信息,用户可从服务端远程获取该信息

3.2 客户端应用程序源文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "MQTTClient.h"

#define BROKER_ADDRESS "......"  //公共MQTT服务器地址

#define CLIENTID "......"  //客户端id
// #define USERNAME ""     //用户名
// #define PASSWORD ""     //密码

#define WILL_TOPIC "mqtt/will"  //遗嘱主题
#define LED_TOPIC "mqtt/led"  ////LED主题
#define TEMP_TOPIC "mqtt/temperature"  //温度主题

/* 接收消息的回调函数 */
static int msgarrvd(void *context,char *topicName,int topicLen,MQTTClient_message *message)
{
    if(!strcmp(topicName,LED_TOPIC)){  //校验消息的主题
        if(!strcmp("2",message->payload))  //如果接收到的消息是"2"则设置LED为呼吸灯模式
            system("echo heartbeat > /sys/class/leds/sys-led/trigger");

        if(!strcmp("1",message->payload)){  //如果是"1"则LED常量
            system("echo none > /sys/class/leds/sys-led/trigger");    
            system("echo 1 > /sys/class/leds/sys-led/brightness");    
        }

        else if(!strcmp("0",message->payload)){  //如果是"0"则LED熄灭
            system("echo none > /sys/class/leds/sys-led/trigger");    
            system("echo 0 > /sys/class/leds/sys-led/brightness");    
        }

        // 接收到其它数据 不做处理
    }

    /* 释放占用的内存空间 */
    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);

    return 1;
}

/* 断开连接时的回调函数 */
static void connlost(void *context, char *cause)
{
    printf("\nConnection lost\n");
    printf("       cause:%s\n",cause);
}

int main(int argc,char *argv[])
{
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    MQTTClient_willOptions will_opts = MQTTClient_willOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    int rc;

    /* 创建mqtt客户端对象 */
    if(MQTTCLIENT_SUCCESS != 
        (rc = MQTTClient_create(&client,BROKER_ADDRESS,CLIENTID,MQTTCLIENT_PERSISTENCE_NONE,NULL))){
            printf("Failed to create client,return code %d\n",rc);
            rc = EXIT_FAILURE;
            goto exit;
        }

    /* 设置回调 */
    if(MQTTCLIENT_SUCCESS != (rc = MQTTClient_setCallbacks(client,NULL,connlost,msgarrvd,NULL))){
        printf("Failed to set callbacks,return code %d\n",rc);
        rc = EXIT_FAILURE;
        goto destory_exit;
    }

	/* 连接MQTT服务器 */
	will_opts.topicName = WILL_TOPIC;	//遗嘱主题
	will_opts.message = "Unexpected disconnection";//遗嘱消息
	will_opts.retained = 1;	//保留消息
	will_opts.qos = 0;		//QoS0

	conn_opts.will = &will_opts;
	conn_opts.keepAliveInterval = 30;	//心跳包间隔时间
	conn_opts.cleansession = 0;			//cleanSession标志
    // conn_opts.username = USERNAME;		//用户名
    // conn_opts.password = PASSWORD;       //密码

    if(MQTTCLIENT_SUCCESS != (rc = MQTTClient_connect(client,&conn_opts))){
        printf("Failed to connect,return code %d\n",rc);
        rc = EXIT_FAILURE;
        goto destory_exit;
    }

    printf("MQTT connect success!\n");

    /* 发布上线消息 */
    pubmsg.payload = "Online";  //消息的内容
    pubmsg.payloadlen = 6;      //内容的长度
    pubmsg.qos = 0;             //QoS等级
    pubmsg.retained = 1;        //保留消息
    if(MQTTCLIENT_SUCCESS != (rc = MQTTClient_publishMessage(client,WILL_TOPIC,&pubmsg,NULL))){
        printf("Failed to publish message,return code %d\n",rc);
        rc = EXIT_FAILURE;
        goto disconnect_exit;
    }

    /* 订阅主题 dt_mqtt/led */
    if(MQTTCLIENT_SUCCESS != (rc = MQTTClient_subscribe(client,LED_TOPIC,0))){
        printf("Failed to subscribe,return code %d\n",rc);
        rc = EXIT_FAILURE;
        goto disconnect_exit;
    }

    /* 向服务端发布开发板运行状态信息 */
    for(;;){
        MQTTClient_message tempmsg = MQTTClient_message_initializer;
        char temp_str[10] = {0x0};
        int fd;

        /* 读取温度值 */
        fd = open("/sys/class/thermal/thermal_zone0/temp",O_RDONLY);
        if(0 > fd){
            perror("open error");
        }
        if(0 > read(fd,temp_str,sizeof(temp_str))){  //读取temp属性文件即可获取温度
            perror("read error");
        }
        close(fd);

        /* 发布温度信息 */
        tempmsg.payload = temp_str;  //消息的内容
        tempmsg.payloadlen = strlen(temp_str);  //内容的长度
        tempmsg.qos = 0;  //QoS等级
        tempmsg.retained = 1;  //保留消息
        if(MQTTCLIENT_SUCCESS != (rc = MQTTClient_publishMessage(client,TEMP_TOPIC,&tempmsg,NULL))){
            printf("Failed to publish message,return code %d\n",rc);
            rc = EXIT_FAILURE;
            goto unsubscribe_exit;
        }

        sleep(30);  //每隔30秒 更新一次数据
    }

unsubscribe_exit:
    if(MQTTCLIENT_SUCCESS != (rc = MQTTClient_unsubscribe(client,LED_TOPIC))){
        printf("Failed to unsubscribe,return code %d\n",rc);
        rc = EXIT_FAILURE;
    }

disconnect_exit:
    if(MQTTCLIENT_SUCCESS != (rc = MQTTClient_disconnect(client,10000))){
        printf("Failed to disconnect,return code %d\n",rc);
        rc = EXIT_FAILURE;
    }

destory_exit:
    MQTTClient_destroy(&client);

exit:
    return rc;

}

3.3 演示

在进行测试之前,确保开发板是能够上网的,也就是能够连接外网。之后执行mqttClient客户端应用程序,如下所示:
在这里插入图片描述
这样,我们的开发板作为 MQTT 客户端就成功连接上了 MQTT 服务器,连接服务器时还会向服务器发送一条上线信息,并且每隔 30 秒向服务器发布温度信息,同样也会接收 LED 主题的信息。 如果我们在电脑上的客户端订阅了will和temperature主题,就会收到如下图所示的信息:
在这里插入图片描述
接下来我们在客户端上发布LED主题,控制开发板上LED灯的亮灭:

发0让LED熄灭:
在这里插入图片描述
效果:
在这里插入图片描述

发1让LED点亮:
在这里插入图片描述
效果:
在这里插入图片描述
发2让LED处于呼吸灯模式:
在这里插入图片描述
当开发板上的客户端意外掉线,被动与服务器断开连接,服务端就会把该客户端写好的遗嘱发布给其他订阅了该遗嘱主题的客户端,如下图中最后一条消息所示:
在这里插入图片描述

<think>好的,用户现在想了解如何使用RT-Thread Smart开发板实现光伏运维的思路和概念。首先,我需要回顾之前的对话,看看之前给出的系统架构和功能模块是怎样的。之前提到过基于RT-Thread Smart的故障诊断和追光功能,现在用户可能希望更详细的概念性解释,特别是在开发板上的具体实现思路。 用户提供的引用中提到ART-Pi Smart开发板使用i.MX6ULL核心板,属于Cortex-A7架构,这比之前的Cortex-M系列性能更强,适合运行RT-Thread Smart。需要结合这一点,说明如何利用该开发板的外设接口和性能优势。 首先,我需要明确光伏运维的主要需求:实时数据采集、故障诊断、追光控制、远程通信。然后,将这些需求映射到RT-Thread Smart的功能上,比如实时性、多任务处理、外设驱动支持等。 考虑到用户提到的引用[1]中ART-Pi Smart的硬件规格,应该强调该开发板的丰富外设(如GPIO、ADC、PWM、以太网/WiFi)如何支持传感器数据采集和电机控制。同时,RT-Thread Smart的混合内核架构(微内核+宏内核)适合处理实时任务和复杂逻辑。 接下来需要分步骤说明实现思路,包括硬件选型、系统分层设计、关键功能实现(数据采集、故障诊断、追光算法、通信)、系统优化。同时,结合之前的代码示例,可能需要调整以适配Cortex-A7的开发板,比如使用更高性能的模型或更复杂的多线程管理。 还要注意用户提到的引用[2]中的MQTT实验,说明在光伏运维中如何集成物联网通信,比如使用MQTT协议上传数据到云端,可能需要用到LwIP或AT组件,确保数据安全和稳定传输。 最后,生成相关问题时要考虑用户可能的后续疑问,比如实时任务调度、传感器数据处理、模型验证、通信协议选择等,帮助用户深入理解实现细节。</think>### 基于RT-Thread Smart开发板的光伏运维实现思路与核心概念 --- #### 一、硬件选型与RT-Thread Smart特性匹配 **硬件基础**:以**ART-Pi Smart开发板(i.MX6ULL Cortex-A7)**为核心,其优势与光伏运维需求高度契合: - **多外设支持**:GPIO(光敏传感器接口)、ADC(IV曲线采集)、PWM(电机控制)、以太网/WiFi(数据上传)[^1] - **性能适配**:Cortex-A7主频达800MHz,满足轻量化CNN模型推理(故障诊断)与实时追光控制 - **RT-Thread Smart特性**: - **混合内核架构**:微内核(实时任务) + 宏内核(复杂逻辑),兼顾实时性与功能扩展性 - **资源隔离**:进程间通信(IPC)保护关键任务(如故障中断响应) --- #### 二、系统分层设计思路 **分层架构**: $$ \begin{array}{ccc} \text{感知层} & \xrightarrow{\text{实时采集}} & \text{边缘计算层} \\ (\text{IV传感器/光敏阵列}) & & (\text{RT-Thread Smart}) \\ & \xleftarrow{\text{控制指令}} & \downarrow \text{分析决策} \\ \text{执行层} & \leftarrow & \text{云平台} \\ (\text{电机/继电器}) & & (\text{数据存储/可视化}) \end{array} $$ --- #### 三、关键功能实现路径 1. **数据采集与预处理** - **IV曲线采样**: - 使用开发板ADC模块(12位精度)采集光伏组串电流/电压,采样率≥1kHz - 代码示例(基于RT-Thread ADC框架): ```c struct rt_adc_device *adc_dev = RT_NULL; adc_dev = (struct rt_adc_device *)rt_device_find("adc1"); rt_adc_enable(adc_dev, ADC_CHANNEL_3); // 启用ADC通道 rt_uint32_t value = rt_adc_read(adc_dev, ADC_CHANNEL_3); // 读取原始数据 ``` - **光照方向检测**: - 光敏电阻阵列通过GPIO扩展板连接,利用差分电压计算角度偏差(公式见前文) 2. **故障诊断实现** - **模型部署优化**: - 使用NCNN框架将CNN模型转换为INT8量化格式,模型大小控制在200KB以内[^1] - 通过RT-Thread的**动态加载模块**(DLM)实现模型热更新 - **实时性保障**: - 为诊断任务分配独立进程,设置SCHED_FIFO调度策略,优先级高于普通任务 3. **追光控制算法** - **双闭环控制**: - 外环(光照强度差→目标角度) + 内环(PID电机位置控制) - 控制周期:50ms(通过RT-Thread定时器实现) - **能耗优化**: - 动态调整PWM占空比,在稳定状态下降低电机功耗 4. **物联网通信集成** - **协议选择**: - 使用RT-Thread内置的**Paho MQTT**包实现低带宽数据传输(如故障代码、发电量统计) - 通过LwIP协议栈支持HTTP RESTful API(用于固件OTA升级) - **安全机制**: - 启用mbedTLS加密通信,密钥存储在开发板SE安全芯片中 --- #### 四、典型开发流程 1. **BSP适配**:基于ART-Pi Smart的现有BSP,添加光伏专用外设驱动(如IV传感器SPI驱动) 2. **功能进程划分**: - **高实时性进程**:数据采集、紧急停机控制(优先级90+) - **普通进程**:模型推理、数据上传(优先级50-80) 3. **调试工具链**: - 使用RDB(RT-Thread Debug Bridge)实时监控任务状态 - 通过ULog模块记录运行时日志,定位性能瓶颈 --- #### 五、效能优化方向 1. **混合任务调度**: - CPU密集型任务(模型推理)绑定到**Linux兼容层**,实时任务(控制)运行在**微内核** 2. **内存分级管理**: - 关键数据(如故障历史)存入FRAM非易失存储器,避免文件系统损耗 3. **低功耗策略**: - 夜间自动切换至IDLE模式,仅保留看门狗和RTC唤醒功能 --- ### 相关问题 1. 如何利用RT-Thread Smart的混合内核实现高优先级任务抢占? 2. 光伏IV曲线采集时如何抑制电磁干扰? 3. RT-Thread Smart开发板如何实现多传感器数据的时间同步? 4. 在资源受限环境下如何验证故障诊断模型的泛化能力? 5. 光伏运维系统中MQTT和CoAP协议应如何选择?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值