imx6ull 开发板,手机,MQTT 物联网通信实验。
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)
具体安装清单:
一、MQTT服务器(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
也可以 鼠标点击重新启动
二、MQTTX客户端软件 也装在 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 熄灭,只剩下电源灯在亮。
