在树莓派中用*C语言*实现MQTT通信

在上一篇中(利用树莓派部署的emqx向mqttx发送信息(python)),我使用了Python来实现MQTT通信。但是我还是喜欢C,并且熟悉C更多,这次我使用C语言来在树莓派上实现MQTT 通信。

在 C 语言中实现 MQTT 通信,最主流且稳定的方案是使用 Eclipse Paho MQTT C 客户端库。它是由 Eclipse 基金会维护的,专门针对嵌入式设备(如我们现在使用的树莓派)进行了优化。

一、安装 Paho MQTT C 库

在树莓派终端中执行以下命令。我们需要安装开发库(-dev)以便编译代码。

bash 复制代码
sudo apt-get update
sudo apt-get install libpaho-mqtt-dev

注意 :如果使用的是较旧的树莓派系统(如 Raspbian Stretch),可能需要手动编译源码,但现在的 Raspberry Pi OS (Bullseye/Bookworm) 直接 apt 安装即可。

二、编写C语言代码

创建一个名为 mqtt_test.c 的文件。

nano mqtt_test.c(这里我依旧习惯的选择在nano编译器中编写)

下面的代码实现了:连接 -> 订阅 -> 循环发送消息

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "MQTTClient.h" // 引用 Paho 库的头文件

// --- 配置信息 (根据你的实际情况修改) ---
#define ADDRESS     "tcp://10.211.182.16:1883" // EMQX 地址
#define CLIENTID    "RaspberryPi_C_Client"      // 客户端 ID,必须唯一
#define TOPIC       "test/hello"                // 主题
#define USERNAME    "admin"                     // EMQX 用户名
#define PASSWORD    "public"                    // EMQX 密码
#define QOS         1                           // 消息质量 (0, 1, 2)
#define TIMEOUT     10000L                      // 超时时间 (毫秒)

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

    // 1. 创建客户端句柄
    MQTTClient_create(&client, ADDRESS, CLIENTID,
        MQTTCLIENT_PERSISTENCE_NONE, NULL);

    // 2. 设置连接选项 (用户名/密码)
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    conn_opts.username = USERNAME;
    conn_opts.password = PASSWORD;

    // 3. 连接到 Broker
    printf("Trying to connect to %s ...\n", ADDRESS);
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
    printf("Connected successfully!\n");

    // 4. 订阅主题 (为了演示自发自收)
    MQTTClient_subscribe(client, TOPIC, QOS);

    // 5. 进入循环发送逻辑
    int count = 0;
    while(1) {
        char payload[100];
        // 构造消息内容
        sprintf(payload, "Hello from C! Count: %d", count++);

        // 准备发布消息
        pubmsg.payload = payload;
        pubmsg.payloadlen = strlen(payload);
        pubmsg.qos = QOS;
        pubmsg.retained = 0;

        // 发布消息
        MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
        printf("Published message: %s\n", payload);

        // 等待确认 (可选,QoS 1 需要)
        MQTTClient_waitForCompletion(client, token, TIMEOUT);

        // 暂停 1 秒
        sleep(1);
    }

    // 6. 断开连接并清理资源 (实际上因为 while(1) 不会运行到这里,除非 Ctrl+C)
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    return rc;
}

三、编译与运行

C 语言是编译型语言,不像 Python 那样可以直接运行,需要先编译成可执行文件。

1.编译命令

在终端输入:

bash 复制代码
gcc mqtt_test.c -o mqtt_test -lpaho-mqtt3c

gcc:编译器。

-o mqtt_test:生成的可执行文件名叫 mqtt_test。

-lpaho-mqtt3c:链接 Paho MQTT 库(这是关键,不加会报错)。

2.运行程序
bash 复制代码
./mqtt_test
3.结果

在mqttx和终端上可以看到成功发送了。

ο(=•ω<=)ρ⌒☆!

常见问题排查

  1. 编译报错 undefined reference

    • 原因:没有加 -lpaho-mqtt3c 或者库没装好。
    • 解决:重新执行第一步的安装命令,并确保编译命令末尾有链接参数。
  2. 连接失败

    • 检查 ADDRESS 里的 IP 是否正确。
    • 检查 EMQX 是否允许 TCP 1883 端口访问。
  3. 内存泄漏警告

    • 上面的代码是一个简单的死循环示例。在实际工程中,建议捕获 SIGINT (Ctrl+C) 信号来优雅地退出循环并执行 MQTTClient_disconnect,但在测试阶段上面的代码完全够用。

补充:

在使用python实现时,我们创建了虚拟环境(venv)。但是在C语言开发中,我们不需要像Python那样创建虚拟环境。

为什么不需要了?(⊙_⊙)?

1、Python 的痛点: Python 使用虚拟环境主要是为了解决"依赖包版本冲突"和"权限问题"。比如项目 A 需要 paho-mqtt 1.0,项目 B 需要 2.0,如果不隔离就会打架。

2、C 语言的处理方式: C 语言的库(如 libpaho-mqtt)是编译成二进制文件(.so.a)安装在系统目录(如 /usr/lib)下的。

  • 当你编写代码时,只是引用头文件(.h)。
  • 当你编译代码时,链接器会把库"打包"进你的可执行文件,或者在运行时动态加载。
  • 结果: 你的 C 程序是一个独立的 .exe (Windows) 或可执行文件 (Linux),它自带了运行所需的逻辑,不依赖 Python 那种复杂的包管理器环境。

因此,只需要确保我们的树莓派系统里安装了 MQTT 的开发库即可。这相当于给系统装了一个"基础工具",所有 C 程序都可以共用它。

那我们什么时候才需要"隔离"?(◎﹏◎)

只有一种情况需要类似虚拟环境的操作:如果我们的树莓派上跑了两个极其特殊的 C 项目,一个必须用 MQTT 库版本 A,另一个必须用 MQTT 库版本 B,且这两个版本互不兼容。

但在绝大多数物联网开发场景下(包括我们现在的学习阶段),直接在系统层面安装库,然后编译运行是最标准、最高效的做法。

相关推荐
笨鸟先飞的橘猫16 小时前
skynet——服务发现学习
学习·服务发现
-To be number.wan16 小时前
算法日记 | C++ 结构体
数据结构·学习·算法
保安大队王队长16 小时前
对于单片机以及单片机的应用,要学的还有涉及到的电路都有哪些
单片机·嵌入式硬件
嵌入式×边缘AI:打怪升级日志16 小时前
PIR 人体红外控制板载 LED — 保姆级笔记
笔记
IT199517 小时前
Dify笔记-一种知识库文件上传失败报错500解决方法
笔记
大连好光景17 小时前
Skills索引大全
学习·ai编程
大卡片17 小时前
时钟控制器原理
单片机·嵌入式硬件
Cat_Rocky17 小时前
Linux学习-ansible自动化
linux·学习·ansible
IronMurphy17 小时前
AI Agent 学习笔记 Day 1:大模型基础、API 调用与 Prompt 工程
人工智能·笔记·学习