C/C++

本文主要介绍如何在 C 项目中使用 Eclipse Paho C ,实现 ST200TH 设备与 MQTT 服务器的连接、订阅、收发消息等功能。

[Eclipse Paho C](https://www.eclipse.org/paho/index.php?page=clients/c/index.php) 与 Eclipse Paho Embedded C 均为 Eclipse Paho 项目下的 C 语言客户端库(MQTT C Client),均为使用 ANSI C 编写的功能齐全的 MQTT 客户端。

前提条件

安装依赖包

1sudo apt-get update
2sudo apt-get -y install build-essential git cmake openssl libssl-dev

编译 Paho MQTT 库

Linux/Mac

1git clone https://github.com/eclipse/paho.mqtt.c.git
2cd paho.mqtt.c
3make
4sudo make install

Windows

1mkdir build.paho
2cd build.paho
3call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
4cmake -G "NMake Makefiles" -DPAHO_WITH_SSL=TRUE -DPAHO_BUILD_DOCUMENTATION=FALSE -DPAHO_BUILD_SAMPLES=TRUE -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=TRUE ..
5nmake

连接使用

连接设置

本文将使用自定义的接入认证方式,服务器接入信息如下:

  • Broker: mqtt.geek-smart.cn

  • TCP Port: 1883

  • WebSocket Port: 8083

导入依赖包

1#include "stdlib.h"
2#include "string.h"
3#include "unistd.h"
4#include "MQTTClient.h"

定义连接地址、认证信息以及消息发布主题

1#define ADDRESS     "tcp://mqtt.geek-smart.cn:1883"
2#define USERNAME    "************"  // 用户名
3#define PASSWORD    "************"  // 密码
4#define CLIENTID    "c-client"
5#define QOS         0
6#define SUB_TOPIC       "/HYUGHV/lVtAcHuor***/4cebd60bf***/subscribe" // 订阅主题
7#define PUB_TOPIC       "/HYUGHV/lVtAcHuor***/4cebd60bf***/publish"   // 发布主题
8#define TIMEOUT     10000L

定义消息发布函数

 1void publish(MQTTClient client, char *topic, char *payload) {
 2    MQTTClient_message message = MQTTClient_message_initializer;
 3    message.payload = payload;
 4    message.payloadlen = strlen(payload);
 5    message.qos = QOS;
 6    message.retained = 0;
 7    MQTTClient_deliveryToken token;
 8    MQTTClient_publishMessage(client, topic, &message, &token);
 9    MQTTClient_waitForCompletion(client, token, TIMEOUT);
10    printf("Send `%s` to topic `%s` \n", payload, PUB_TOPIC);
11}

定义 on_message 回调函数,用于打印订阅主题接收的消息内容

1int on_message(void *context, char *topicName, int topicLen, MQTTClient_message *message) {
2    char *payload = message->payload;
3    printf("Received `%s` from `%s` topic \n", payload, topicName);
4    MQTTClient_freeMessage(&message);
5    MQTTClient_free(topicName);
6    return 1;
7}

初始化 MQTT 客户端并订阅主题

 1int rc;
 2MQTTClient client;
 3
 4MQTTClient_create(&client, ADDRESS, CLIENTID, 0, NULL);
 5MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
 6conn_opts.username = USERNAME;
 7conn_opts.password = PASSWORD;
 8MQTTClient_setCallbacks(client, NULL, NULL, on_message, NULL);
 9if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
10    printf("Failed to connect, return code %d\n", rc);
11    exit(-1);
12} else {
13    printf("Connected to MQTT Broker!\n");
14}
15// subscribe topic
16MQTTClient_subscribe(client, SUB_TOPIC, QOS);

循环调用 publish 函数,完成消息发布

1char payload[64];
2for (int i = 0; i < 100; i += 1) {
3    // 获取设备基本信息 每隔两秒获取一次
4    snprintf(payload, 64, "\{\"type\":\"info\"\}");
5    publish(client, PUB_TOPIC, payload);
6    sleep(2);
7}

完整代码

 1#include "stdlib.h"
 2#include "string.h"
 3#include "unistd.h"
 4#include "MQTTClient.h"
 5
 6#define ADDRESS     "tcp://mqtt.geek-smart.cn:1883"
 7#define USERNAME    "************"  // username
 8#define PASSWORD    "************" // password
 9#define CLIENTID    "c-client"
10#define QOS         0
11#define SUB_TOPIC       "/HYUGHV/lVtAcHuor***/4cebd60bf***/subscribe" // subscribe
12#define PUB_TOPIC       "/HYUGHV/lVtAcHuor***/4cebd60bf***/publish"   // publish
13#define TIMEOUT     10000L
14
15
16void publish(MQTTClient client, char *topic, char *payload) {
17    MQTTClient_message message = MQTTClient_message_initializer;
18    message.payload = payload;
19    message.payloadlen = strlen(payload);
20    message.qos = QOS;
21    message.retained = 0;
22    MQTTClient_deliveryToken token;
23    MQTTClient_publishMessage(client, topic, &message, &token);
24    MQTTClient_waitForCompletion(client, token, TIMEOUT);
25    printf("Send `%s` to topic `%s` \n", payload, PUB_TOPIC);
26}
27
28int on_message(void *context, char *topicName, int topicLen, MQTTClient_message *message) {
29    char *payload = message->payload;
30    printf("Received `%s` from `%s` topic \n", payload, topicName);
31    MQTTClient_freeMessage(&message);
32    MQTTClient_free(topicName);
33    return 1;
34}
35
36int main(int argc, char *argv[]) {
37    int rc;
38    MQTTClient client;
39
40    MQTTClient_create(&client, ADDRESS, CLIENTID, 0, NULL);
41    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
42    conn_opts.username = USERNAME;
43    conn_opts.password = PASSWORD;
44    MQTTClient_setCallbacks(client, NULL, NULL, on_message, NULL);
45    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
46        printf("Failed to connect, return code %d\n", rc);
47        exit(-1);
48    } else {
49        printf("Connected to MQTT Broker!\n");
50    }
51    // 订阅获取到的内容
52    MQTTClient_subscribe(client, SUB_TOPIC, QOS);
53    char payload[64];
54    for (int i = 0; i < 100; i += 1) {
55        // 获取设备基本信息 每隔两秒获取一次
56        snprintf(payload, 64, "\{\"type\":\"info\"\}");
57        publish(client, PUB_TOPIC, payload);
58        sleep(2);
59    }
60    MQTTClient_disconnect(client, TIMEOUT);
61    MQTTClient_destroy(&client);
62    return rc;
63}

测试验证

编写 CMakeLists.txt

1cmake_minimum_required(VERSION 3.17)
2project(mqtt_c C)
3include_directories(/usr/local/include)
4link_directories(/usr/local/lib)
5set(CMAKE_C_STANDARD 99)
6add_executable(mqtt_c main.c)
7target_link_libraries(mqtt_c paho-mqtt3c)

编译

1cmake .
2make

运行

1./mqtt_c