在上一篇中(利用树莓派部署的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和终端上可以看到成功发送了。

ο(=•ω<=)ρ⌒☆!
常见问题排查
-
编译报错
undefined reference- 原因:没有加
-lpaho-mqtt3c或者库没装好。 - 解决:重新执行第一步的安装命令,并确保编译命令末尾有链接参数。
- 原因:没有加
-
连接失败
- 检查
ADDRESS里的 IP 是否正确。 - 检查 EMQX 是否允许 TCP 1883 端口访问。
- 检查
-
内存泄漏警告
- 上面的代码是一个简单的死循环示例。在实际工程中,建议捕获
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,且这两个版本互不兼容。
但在绝大多数物联网开发场景下(包括我们现在的学习阶段),直接在系统层面安装库,然后编译运行是最标准、最高效的做法。