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)

具体安装清单:

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 熄灭,只剩下电源灯在亮。

相关推荐
f8979070701 小时前
把文件进行锁死,不要有写的权限。不被恶意攻击
linux
用户2367829801681 小时前
Linux kill 命令:从信号机制到进程管理的深度解析
linux
00后程序媛1 小时前
ubuntu安装qemu和xv6
linux·运维·ubuntu
载数而行5201 小时前
Linux操作系统 5 组管理,权限管理
linux
疯狂打码的少年2 小时前
Cache的三种映射方式(直接/全相联/组相联)
linux·服务器·数据库·笔记
minji...2 小时前
Linux 高级IO(四)多路转接之epoll,epoll 模型及原理
linux·运维·服务器·多路转接·epoll·epoll模型·红黑树/就绪队列/回调
蜡笔婧萱2 小时前
网络服务综合大实验--包含NFS服务器,Web服务器,DNS域名服务器
linux·服务器·网络
bitbrowser2 小时前
2026年Facebook广告账户频频“连坐”被封?聊聊出海投流
运维·服务器·facebook
汽车仪器仪表相关领域2 小时前
Kvaser Hybrid CAN/LIN 单通道三合一总线分析仪:高性价比CAN FD/LIN集成测试利器
运维·服务器·网络·数据挖掘·数据分析·单元测试·集成测试