MQTT(Message Queuing Telemetry Transport)是物联网(IoT)领域最受欢迎的轻量级通信。
1、 通信架构
- IMX6ULL开发板:作为MQTT客户端,每10秒发布温度数据,订阅LED控制指令。
- 电脑/手机:作为MQTT客户端,订阅温度数据,发布LED控制指令。
- 本地 MQTT 服务器(Mosquitto):作为消息代理,转发主题消息。
2 、 具体功能
- 电脑/手机:读取 imx6ull 发布的温度数据
- 电脑/手机:向 imx6ull 发布指令 → 控制 开发板的LED:
0:关灯(brightness=0)
1:开灯(brightness=1)
2:心跳模式(trigger=heartbeat)
具体安装清单:
一 、 MQT T 服务器(Broker)装在 Windows 上, 也可以安装在虚拟机,我安装在Windows 上。
1:访问 Mosquitto 官方下载页: https://mosquitto.org/download/
2: 默认 Mosquitto 只允许(127.0.0.1)本地连接,需要修改配置让开发板能连上。打开安装 Mosquitto目录下的 mosquitto.conf(用记事本打开即可),在文件末尾添加2行:
listener 1883 0.0.0.0
allow_anonymous true
添加完后,保存退出。
listener 1883 0.0.0.0 意思是:监听所有网卡的 1883 端口
allow_anonymous true 意思是:允许匿名登录(调试用,生产环境建议加密码)
3:重启服务(管理员 PowerShell)执行命令:
net stop mosquitto
net start mosquitto
也可以 鼠标点击重新启动

二 、MQT TX 客户端软件 也装在 Windows 上。
访问 https://mqttx.app/zh/downloads 下载 windows-x64.exe
三 、i.MX6ull 开发板 安装 Mosquitto 客户端库,通过网络连接 Windows 的 IP 即可。
开发板nfs挂载的跟文件系统,在虚拟机用 apt命令安装 Mosquitto 客户端库。

先挂载 ./mount.sh 下面是 mount.sh 代码内容
#!/bin/bash
echo "MOUNTING"
sudo mount -t proc /proc /home/leo/linux/nfs/ubuntu_rootfs/proc
sudo mount -t sysfs /sys /home/leo/linux/nfs/ubuntu_rootfs/sys
sudo mount -o bind /dev /home/leo/linux/nfs/ubuntu_rootfs/dev
sudo mount -o bind /dev/pts /home/leo/linux/nfs/ubuntu_rootfs/dev/pts
sudo chroot /home/leo/linux/nfs/ubuntu_rootfs
执行命令,安装
sudo apt update
sudo apt install libmosquitto-dev mosquitto-clients build-essential
| 包 | 需要装吗 |
|---|---|
libmosquitto-dev |
✅ 必须,MQTT 开发库 |
mosquitto-clients |
✅ 推荐,调试工具 |
build-essential |
❌ 已有 gcc 就不需要 |
# 安装后,查看是否安装及版本号 dpkg -l | grep libmosquitto

安装结束后,exit 退出, 执行 ./unmount.sh 卸载
下面是 ./unmount.sh 代码
#!/bin/bash
echo "UNMOUNTING"
sudo umount /home/leo/linux/nfs/ubuntu_rootfs/proc
sudo umount /home/leo/linux/nfs/ubuntu_rootfs/sys
sudo umount /home/leo/linux/nfs/ubuntu_rootfs/dev/pts
sudo umount /home/leo/linux/nfs/ubuntu_rootfs/dev
四 、手机安装 MQTT app客户端,向本地MQTT 服务器 收发消息。如果手机端无法安装,那就只能测试电脑和imx6ull 通信。
网络要点:
确保 imx6ull 和 Windows 电脑在同一局域网(我是开发板 用网线 直连电脑,也可以连同一台路由器)。在 i.MX6ull 上 ping -> Windows IP,能通则网络没有问题,开发板的MQTT客户端就能连接 Windows 上的本地MQTT服务器。
笔记本电脑连接手机热点,这样的话,手机的MQTT客户端就能连接 Windows 上的本地MQTT服务器,在同一个局域网。
MQTTX 客户端/服务器 都就位后,电脑/手机 打开 MQTTX客户端,新建连接 "New Connection" ,填写连接信息:
|-------------------|--------------------|
| 字段 | 值 |
| Name | (随意) |
| Host | 目标 IP 或者 127.0.0.1 |
| Port | 1883 |
| Client ID | 留空(自动生成) |
| Username/Password | 留空(已开启匿名) |
点击右上角 "Connect"(连接),连接成功后,上方状态会变绿,显示 "Connected"。
订阅:
点击 "new Subscription"(添加订阅)。
Topic 填写 要订阅的名字 → 点击确认 confirm
发布:
在底部输入框,Topic 填写 要订阅的名字。
Message 填写 要发送的信息。
点击发送图标 发送信息。
MQTTX 支持同时创建多个连接,连接到同一个服务端 IP 。
修改配置信息后,如果客户端连接成功后,总是重复弹出Reconnecting,需要重启客户端,清掉了缓存,恢复正常。
imx6ull 客户端程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <mosquitto.h>
#define BROKER_ADDRESS "192.168.137.1" // 电脑IP
#define BROKER_PORT 1883
/* MQTT 服务器靠这个 ID 区分不同的设备,
如果两个相同的 ID 同时连接服务器,服务器会把前一个踢下线 */
#define CLIENTID "imx6ull_client"
#define WILL_TOPIC "dt_mqtt/will"
#define LED_TOPIC "dt_mqtt/led"
#define TEMP_TOPIC "dt_mqtt/temperature"
volatile sig_atomic_t stop = 0;
void handle_sigint(int sig) {
stop = 1;
}
void on_message(struct mosquitto *mosq, void *userdata,
const struct mosquitto_message *msg)
{
// 防御性编程:防止空指针和空载荷
if (msg->topic == NULL || msg->payload == NULL || msg->payloadlen == 0)
return;
// 将 payload 转换为字符串并确保以 '\0' 结尾
char payload_str[8] = {0};
strncpy(payload_str, (char *)msg->payload, sizeof(payload_str) - 1);
if (!strcmp(msg->topic, LED_TOPIC)) {
int fd;
if (!strcmp("2", payload_str)) { // 心跳模式
// 如果报错,检查自己开发板的trigger路径
fd = open("/sys/class/leds/red/trigger", O_WRONLY);
if (fd >= 0) {
write(fd, "heartbeat\n", 10); // 直接写入 heartbeat
close(fd);
}
}
else if (!strcmp("1", payload_str)) {
// 开灯 (先关闭触发模式,再设置亮度)
fd = open("/sys/class/leds/red/trigger", O_WRONLY);
if (fd >= 0) {
write(fd, "none\n", 5);
close(fd);
}
fd = open("/sys/class/leds/red/brightness", O_WRONLY);
if (fd >= 0) {
write(fd, "1\n", 2);
close(fd);
}
}
else if (!strcmp("0", payload_str)) {
// 关灯
fd = open("/sys/class/leds/red/trigger", O_WRONLY);
if (fd >= 0) {
write(fd, "none\n", 5);
close(fd);
}
fd = open("/sys/class/leds/red/brightness", O_WRONLY);
if (fd >= 0) {
write(fd, "0\n", 2);
close(fd);
}
}
}
}
void on_connect(struct mosquitto *mosq, void *userdata, int rc)
{
if (rc == 0) {
printf("MQTT 服务器连接成功!\n");
/* 发布上线消息:
NULL 表示我不关心这条消息的发送结果追踪,(通常 QoS=0 时填 NULL)
WILL_TOPIC:目标主题是WILL_TOPIC
"Online":消息内容
0:QoS(服务质量) , true:Retain(保留消息) */
mosquitto_publish(mosq, NULL, WILL_TOPIC, strlen("Online"), "Online", 0, true);
/* 订阅 LED 主题, NULL 表示不追踪, 0:订阅的 QoS 级别 */
mosquitto_subscribe(mosq, NULL, LED_TOPIC, 0);
printf("已订阅 %s\n", LED_TOPIC);
} else {
printf("连接失败, 错误码: %d\n", rc);
}
}
int main()
{
int i;
struct mosquitto *mosq;
/* 捕获 Ctrl+C 信号 */
signal(SIGINT, handle_sigint);
signal(SIGTERM, handle_sigint);
/* 初始化 MQTT 库的运行环境 */
mosquitto_lib_init();
/* 创建一个 MQTT 客户端实例(对象),
true:表示这是一个全新的干净连接,服务器不保存该设备之前的订阅和离线消息,断线即丢
false:表示这是一个持久会话,断线后服务器会帮你暂存消息,重连后还能收到消息 */
mosq = mosquitto_new(CLIENTID, true, NULL);
if (!mosq) {
fprintf(stderr, "无法创建MQTT客户端\n");
return 1;
}
/* 注册"连接成功"的回调函数 */
mosquitto_connect_callback_set(mosq, on_connect);
/* 注册"收到消息"时的回调函数 */
mosquitto_message_callback_set(mosq, on_message);
/* 设置遗嘱消息:
WILL_TOPIC: 遗嘱要发到 WILL_TOPIC 主题
遗嘱内容的长度, 遗嘱的具体内容(载荷)
0:QoS(服务质量), true:Retain(保留消息)服务器会保留这条死讯 */
mosquitto_will_set(mosq, WILL_TOPIC, strlen("Unexpected disconnection"),
"Unexpected disconnection", 0, true);
// 设置用户名密码(Mosquitto 当前是匿名,这两行可以注释掉)
// mosquitto_username_pw_set(mosq, "mqtt1", "123456");
/* 向服务器发起连接请求, 30:Keep Alive(心跳时间),单位是秒
返回 0:表示底层网络握手请求成功发出(注意,只是拨号成功,不代表服务器验证通过)
真正的连接成功与否,是上面注册的 on_connect 回调函数来通知的 */
if (mosquitto_connect(mosq, BROKER_ADDRESS, BROKER_PORT, 30)) {
printf("无法连接服务器\n");
mosquitto_destroy(mosq); // 销毁
mosquitto_lib_cleanup(); // 清理资源
return 1;
}
/* 在后台启动一个新线程,专门负责处理所有的网络通信,
调用这个函数后,libmosquitto 库会调用操作系统的 pthread 接口,创建一个新的线程 */
mosquitto_loop_start(mosq);
// 每 10 秒发布温度
while (!stop) {
char temp_str[10] = {0};
int fd = -1;
fd = open("/sys/class/thermal/thermal_zone0/temp", O_RDONLY);
if (fd >= 0) {
ssize_t bytes_read = read(fd, temp_str, sizeof(temp_str) - 1);//预留一位给 \0
close(fd);
if (bytes_read > 0) {
// 去除末尾的换行符 \n,避免 printf 打印时多出一个空行
if (temp_str[bytes_read - 1] == '\n') {
temp_str[bytes_read - 1] = '\0';
}
mosquitto_publish(mosq, NULL, TEMP_TOPIC, strlen(temp_str),
temp_str, 0, true);
printf("发布温度: %s\n", temp_str);
} else {
perror("读取温度失败");
}
}
// 用循环代替纯 sleep,可以更快的响应 Ctrl+C 退出信号
for (i = 0; i < 10 && !stop; i++) {
sleep(1);
}
}
printf("\n正在断开连接...\n");
mosquitto_loop_stop(mosq, true); // 停止后台线程
mosquitto_destroy(mosq); // 销毁
mosquitto_lib_cleanup(); // 清理资源
return 0;
}
在虚拟机的挂载目录,编译客户端程序: 
在开发板运行测试:向服务器 发布温度数据

Windows 上的 MQTTX 客户端:收到订阅的温度数据

Windows 上的 MQTTX 客户端:监控到 手机向 imx6ull 发送的 LED 控制指令


收到手机客户端 发送的指令后,红色 LED 熄灭,只剩下电源灯在亮。
