MQTTDeviceDriver

ref:https://mosquitto.org/api/files/mosquitto-h.html#mosquitto_new

include/mqtt_device_driver/MQTTDeviceDriver.hpp

#pragma once

#include <mosquitto.h>
#include <string>
#include <vector>
#include <rclcpp/rclcpp.hpp>

class MQTTDeviceDriver {
public:
    MQTTDeviceDriver(const std::string& id,
                     const std::string& host,
                     int port,
                     int keepalive,
                     const std::vector<std::string>& topics,
                     rclcpp::Logger logger);
    ~MQTTDeviceDriver();

    void connect();
    void spin_async(); // non-blocking loop

private:
    static void on_connect(struct mosquitto* mosq, void* obj, int rc);
    static void on_message(struct mosquitto* mosq, void* obj, const struct mosquitto_message* message);

    std::string client_id_;
    std::string host_;
    int port_;
    int keepalive_;
    std::vector<std::string> topics_;
    struct mosquitto* mosq_;
    rclcpp::Logger logger_;
};

src/MQTTDeviceDriver.cpp

#include "mqtt_device_driver/MQTTDeviceDriver.hpp"
#include <iostream>
#include <thread>

MQTTDeviceDriver::MQTTDeviceDriver(const std::string& id,
                                   const std::string& host,
                                   int port,
                                   int keepalive,
                                   const std::vector<std::string>& topics,
                                   rclcpp::Logger logger)
    : client_id_(id), host_(host), port_(port), keepalive_(keepalive),
      topics_(topics), mosq_(nullptr), logger_(logger)
{
    mosquitto_lib_init();
    mosq_ = mosquitto_new(client_id_.c_str(), true, this);
    if (!mosq_) throw std::runtime_error("Failed to create Mosquitto instance");

    mosquitto_connect_callback_set(mosq_, on_connect);
    mosquitto_message_callback_set(mosq_, on_message);
}

MQTTDeviceDriver::~MQTTDeviceDriver()
{
    if (mosq_) {
        mosquitto_disconnect(mosq_);
        mosquitto_destroy(mosq_);
    }
    mosquitto_lib_cleanup();
}

void MQTTDeviceDriver::connect()
{
    int rc = mosquitto_connect(mosq_, host_.c_str(), port_, keepalive_);
    if (rc != MOSQ_ERR_SUCCESS)
        throw std::runtime_error("Mosquitto connect failed: " + std::string(mosquitto_strerror(rc)));
}

void MQTTDeviceDriver::spin_async()
{
    std::thread([this]() {
        mosquitto_loop_forever(mosq_, -1, 1);
    }).detach();
}

void MQTTDeviceDriver::on_connect(struct mosquitto* mosq, void* obj, int rc)
{
    auto* self = static_cast<MQTTDeviceDriver*>(obj);
    if (rc == 0) {
        RCLCPP_INFO(self->logger_, "Connected to MQTT broker.");
        for (const auto& topic : self->topics_) {
            mosquitto_subscribe(mosq, nullptr, topic.c_str(), 0);
            RCLCPP_INFO(self->logger_, "Subscribed to %s", topic.c_str());
        }
    } else {
        RCLCPP_ERROR(self->logger_, "Connect failed: %s", mosquitto_strerror(rc));
    }
}

void MQTTDeviceDriver::on_message(struct mosquitto* mosq, void* obj, const struct mosquitto_message* message)
{
    auto* self = static_cast<MQTTDeviceDriver*>(obj);
    std::string topic(message->topic);
    std::string payload(static_cast<char*>(message->payload), message->payloadlen);

    RCLCPP_INFO(self->logger_, "[Received] %s: %s", topic.c_str(), payload.c_str());

    std::string reply_topic = topic + "/reply";
    std::string response = "Reply: " + payload;

    mosquitto_publish(mosq, nullptr, reply_topic.c_str(), response.size(), response.c_str(), 0, false);
}

src/mqtt_node.cpp

#include <rclcpp/rclcpp.hpp>
#include "mqtt_device_driver/MQTTDeviceDriver.hpp"

class MQTTNode : public rclcpp::Node {
public:
    MQTTNode() : Node("mqtt_device_node")
    {
        declare_parameter<std::string>("mqtt_host", "localhost");
        declare_parameter<int>("mqtt_port", 1883);
        declare_parameter<int>("keepalive", 60);
        declare_parameter<std::vector<std::string>>("topics", {"/topic/A", "/topic/B"});

        auto host = get_parameter("mqtt_host").as_string();
        auto port = get_parameter("mqtt_port").as_int();
        auto keepalive = get_parameter("keepalive").as_int();
        auto topics = get_parameter("topics").as_string_array();

        driver_ = std::make_shared<MQTTDeviceDriver>(
            "ros2-mqtt-node", host, port, keepalive, topics, get_logger());

        try {
            driver_->connect();
            driver_->spin_async();
        } catch (const std::exception& e) {
            RCLCPP_ERROR(this->get_logger(), "Exception during MQTT init: %s", e.what());
        }
    }

private:
    std::shared_ptr<MQTTDeviceDriver> driver_;
};

int main(int argc, char* argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<MQTTNode>());
    rclcpp::shutdown();
    return 0;
}

ros2 run mqtt_device_driver mqtt_node

订阅多个话题

std::unordered_map<std::string, std::function<std::string(const std::string& payload)>> callbacks_;

#include <unordered_map>
#include <functional>

// 新增回调map,回调类型:输入payload,输出响应字符串
using ResponseCallback = std::function<std::string(const std::string& payload)>;

class MQTTDeviceDriver {
public:
    // 省略之前参数,新增回调map参数
    MQTTDeviceDriver(const std::string& id,
                    const std::string& host,
                    int port,
                    int keepalive,
                    const std::unordered_map<std::string, ResponseCallback>& topic_callbacks,
                    rclcpp::Logger logger);

    // ...

private:
    std::unordered_map<std::string, ResponseCallback> callbacks_;

    // ...
};

MQTTDeviceDriver::MQTTDeviceDriver(const std::string& id,
                                   const std::string& host,
                                   int port,
                                   int keepalive,
                                   const std::unordered_map<std::string, ResponseCallback>& topic_callbacks,
                                   rclcpp::Logger logger)
    : client_id_(id), host_(host), port_(port), keepalive_(keepalive),
      callbacks_(topic_callbacks), mosq_(nullptr), logger_(logger)
{
    mosquitto_lib_init();
    mosq_ = mosquitto_new(client_id_.c_str(), true, this);
    if (!mosq_) throw std::runtime_error("Failed to create Mosquitto instance");

    mosquitto_connect_callback_set(mosq_, on_connect);
    mosquitto_message_callback_set(mosq_, on_message);
}

void MQTTDeviceDriver::on_connect(struct mosquitto* mosq, void* obj, int rc)
{
    auto* self = static_cast<MQTTDeviceDriver*>(obj);
    if (rc == 0) {
        RCLCPP_INFO(self->logger_, "Connected to MQTT broker.");
        // 订阅所有回调中声明的topic
        for (const auto& kv : self->callbacks_) {
            const std::string& topic = kv.first;
            mosquitto_subscribe(mosq, nullptr, topic.c_str(), 0);
            RCLCPP_INFO(self->logger_, "Subscribed to %s", topic.c_str());
        }
    } else {
        RCLCPP_ERROR(self->logger_, "Connect failed: %s", mosquitto_strerror(rc));
    }
}

void MQTTDeviceDriver::on_message(struct mosquitto* mosq, void* obj, const struct mosquitto_message* message)
{
    auto* self = static_cast<MQTTDeviceDriver*>(obj);
    std::string topic(message->topic);
    std::string payload(static_cast<char*>(message->payload), message->payloadlen);

    RCLCPP_INFO(self->logger_, "[Received] %s: %s", topic.c_str(), payload.c_str());

    auto it = self->callbacks_.find(topic);
    if (it != self->callbacks_.end()) {
        // 调用对应话题的回调,生成响应字符串
        std::string response = it->second(payload);

        std::string reply_topic = topic + "/reply";
        mosquitto_publish(mosq, nullptr, reply_topic.c_str(), response.size(), response.c_str(), 0, false);
        RCLCPP_INFO(self->logger_, "[Published] %s: %s", reply_topic.c_str(), response.c_str());
    } else {
        RCLCPP_WARN(self->logger_, "No callback registered for topic %s", topic.c_str());
    }
}

std::unordered_map<std::string, MQTTDeviceDriver::ResponseCallback> callbacks{
    {"/topic/A", [](const std::string& payload) {
        return "Response for A: " + payload;
    }},
    {"/topic/B", [](const std::string& payload) {
        return "Response for B with fixed format {\"status\":\"ok\", \"data\":\"" + payload + "\"}";
    }},
};

driver_ = std::make_shared<MQTTDeviceDriver>(
    "ros2-mqtt-node", host, port, keepalive, callbacks, get_logger());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

colorful_stars

您是我见过全宇宙最可爱的人!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值