Skip to content

Commit bdd10d9

Browse files
committed
Replaced mosquitto with paho mqtt.
1 parent 768619f commit bdd10d9

File tree

6 files changed

+105
-68
lines changed

6 files changed

+105
-68
lines changed

CMakeLists.txt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,7 @@ find_package(Gnuradio COMPONENTS
2222
soapy
2323
)
2424
find_package(nlohmann_json REQUIRED)
25-
26-
find_library(MOSQUITTO_LIB mosquitto)
27-
if(NOT MOSQUITTO_LIB)
28-
message(FATAL_ERROR "mosquitto library not found")
29-
endif()
25+
find_package(PahoMqttCpp REQUIRED)
3026

3127
file(GLOB_RECURSE SOURCES
3228
"${PROJECT_SOURCE_DIR}/sources/*.h"
@@ -50,9 +46,9 @@ target_link_libraries(auto_sdr
5046
gnuradio::gnuradio-fft
5147
gnuradio::gnuradio-filter
5248
gnuradio::gnuradio-soapy
53-
mosquitto
5449
nlohmann_json::nlohmann_json
5550
spdlog::spdlog
51+
PahoMqttCpp::paho-mqttpp3
5652
)
5753

5854
add_executable(auto_sdr_test ${TEST_SOURCES} "tests/test_main.cpp")

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FROM ubuntu:24.04 AS build
22
ENV DEBIAN_FRONTEND=noninteractive
33
RUN apt-get update && \
4-
apt-get install -y --no-install-recommends ca-certificates curl git zip build-essential cmake ccache tzdata libspdlog-dev libliquid-dev nlohmann-json3-dev libmosquitto-dev libgtest-dev libgmock-dev libusb-1.0-0-dev libfftw3-dev libboost-all-dev libsoapysdr-dev gnuradio gnuradio-dev libsndfile1-dev
4+
apt-get install -y --no-install-recommends ca-certificates curl git zip build-essential cmake ccache tzdata libspdlog-dev libliquid-dev nlohmann-json3-dev libgtest-dev libgmock-dev libusb-1.0-0-dev libfftw3-dev libboost-all-dev libsoapysdr-dev gnuradio gnuradio-dev libsndfile1-dev libssl-dev libpaho-mqtt-dev libpaho-mqttpp-dev
55

66
COPY sdrplay/SDRplay_RSP_API-Linux-3.15.2.run /
77
RUN mkdir -p /sdrplay && \
@@ -43,7 +43,7 @@ RUN --mount=type=cache,target=/root/.cache/ccache,id=ccache \
4343
FROM ubuntu:24.04 AS run
4444
ENV DEBIAN_FRONTEND=noninteractive
4545
RUN apt-get update && \
46-
apt-get install -y tzdata libspdlog1.12 libliquid1 nlohmann-json3-dev libmosquitto1 libusb-1.0-0 libfftw3-bin && \
46+
apt-get install -y tzdata ca-certificates libspdlog1.12 libliquid1 nlohmann-json3-dev libusb-1.0-0 libfftw3-bin libssl3t64 libpaho-mqtt1.3 libpaho-mqttpp3-1 && \
4747
apt-get install -y --no-install-recommends gnuradio libsoapysdr0.8 soapysdr0.8-module-all && \
4848
apt-get purge -y soapysdr0.8-module-audio soapysdr0.8-module-uhd && \
4949
apt-get clean all && \

sources/config.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ Config::Config(const nlohmann::json& json)
8181
m_recordingTimeout(std::chrono::milliseconds(readKey<int>(json, {"recording", "max_noise_time_ms"}))),
8282
m_recordingTuningStep(readKey<Frequency>(json, {"recording", "step"})),
8383
m_workers(readKey<int>(json, {"workers"})),
84-
m_mqttHostname(getEnv("MQTT_HOST")),
85-
m_mqttPort(stoi(getEnv("MQTT_PORT_TCP"))),
84+
m_mqttUrl(getEnv("MQTT_URL")),
8685
m_mqttUsername(getEnv("MQTT_USER")),
8786
m_mqttPassword(getEnv("MQTT_PASSWORD")) {}
8887

@@ -124,7 +123,7 @@ void Config::saveToFile(const std::string& path, const nlohmann::json& json) {
124123
}
125124

126125
nlohmann::json Config::json() const { return m_json; }
127-
std::string Config::mqtt() const { return fmt::format("{}@{}:{}", m_mqttUsername, m_mqttHostname, m_mqttPort); };
126+
std::string Config::mqtt() const { return fmt::format("{}@{}", m_mqttUsername, m_mqttUrl); };
128127

129128
std::vector<Device> Config::devices() const { return m_devices; }
130129

@@ -143,7 +142,6 @@ std::chrono::milliseconds Config::recordingMinTime() const { return m_recordingM
143142
std::chrono::milliseconds Config::recordingTimeout() const { return m_recordingTimeout; }
144143
Frequency Config::recordingTuningStep() const { return m_recordingTuningStep; }
145144

146-
std::string Config::mqttHostname() const { return m_mqttHostname; }
147-
int Config::mqttPort() const { return m_mqttPort; }
145+
std::string Config::mqttUrl() const { return m_mqttUrl; }
148146
std::string Config::mqttUsername() const { return m_mqttUsername; }
149147
std::string Config::mqttPassword() const { return m_mqttPassword; }

sources/config.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ class Config {
5757
std::chrono::milliseconds recordingTimeout() const;
5858
Frequency recordingTuningStep() const;
5959

60-
std::string mqttHostname() const;
61-
int mqttPort() const;
60+
std::string mqttUrl() const;
6261
std::string mqttUsername() const;
6362
std::string mqttPassword() const;
6463

@@ -80,8 +79,7 @@ class Config {
8079
const Frequency m_recordingTuningStep;
8180
const int m_workers;
8281

83-
const std::string m_mqttHostname;
84-
const int m_mqttPort;
82+
const std::string m_mqttUrl;
8583
const std::string m_mqttUsername;
8684
const std::string m_mqttPassword;
8785
};

sources/network/mqtt.cpp

Lines changed: 88 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,38 @@
44
#include <utils/utils.h>
55

66
constexpr auto LABEL = "mqtt";
7-
constexpr auto KEEP_ALIVE = 60;
8-
constexpr auto LOOP_TIMEOUT_MS = 100;
97
constexpr auto QOS_SUB = 2;
10-
constexpr auto RECONNECT_INTERVAL = std::chrono::seconds(1);
118
constexpr auto QUEUE_MAX_SIZE = 1000;
9+
constexpr auto LOOP_TIMEOUT = std::chrono::milliseconds(10);
10+
constexpr auto RECONNECT_INTERVAL = std::chrono::seconds(5);
11+
constexpr auto CONNECT_TIMEOUT = std::chrono::seconds(5);
12+
constexpr auto KEEP_ALIVE = std::chrono::seconds(60);
1213

13-
Mqtt::Mqtt(const Config &config)
14-
: m_client(mosquitto_new(nullptr, true, this)), m_isRunning(true), m_thread([this, config]() {
14+
Mqtt::Mqtt(const Config& config)
15+
: m_config(config), m_client(config.mqttUrl(), "sdr-scanner"), m_isRunning(true), m_thread([this, config]() {
1516
Logger::info(LABEL, "started");
16-
mosquitto_username_pw_set(m_client, config.mqttUsername().c_str(), config.mqttPassword().c_str());
17-
mosquitto_connect_callback_set(m_client, [](mosquitto *, void *p, int) { reinterpret_cast<Mqtt *>(p)->onConnect(); });
18-
mosquitto_disconnect_callback_set(m_client, [](mosquitto *, void *p, int) { reinterpret_cast<Mqtt *>(p)->onDisconnect(); });
19-
mosquitto_message_callback_set(m_client, [](mosquitto *, void *p, const struct mosquitto_message *m) { reinterpret_cast<Mqtt *>(p)->onMessage(m); });
20-
mosquitto_connect(m_client, config.mqttHostname().c_str(), config.mqttPort(), KEEP_ALIVE);
17+
connect();
2118
while (m_isRunning) {
22-
mosquitto_loop(m_client, LOOP_TIMEOUT_MS, 1);
23-
while (m_isRunning && !m_messages.empty()) {
24-
const auto &[topic, data, qos] = m_messages.front();
25-
mosquitto_publish(m_client, nullptr, topic.c_str(), data.size(), data.data(), qos, false);
26-
std::unique_lock lock(m_mutex);
27-
m_messages.pop();
19+
if (m_client.is_connected()) {
20+
std::shared_ptr<const mqtt::message> message;
21+
while (m_client.try_consume_message_for(&message, LOOP_TIMEOUT) && message) {
22+
onMessage(message->get_topic(), message->get_payload());
23+
}
24+
while (m_isRunning && !m_messages.empty()) {
25+
const auto& [topic, data, qos] = m_messages.front();
26+
m_client.publish(topic, data.data(), data.size(), qos, false);
27+
std::unique_lock lock(m_mutex);
28+
m_messages.pop();
29+
}
30+
} else {
31+
onDisconnected();
32+
while (m_isRunning && !m_client.is_connected()) {
33+
Logger::info(LABEL, "reconnecting...");
34+
connect();
35+
if (!m_client.is_connected()) {
36+
std::this_thread::sleep_for(RECONNECT_INTERVAL);
37+
}
38+
}
2839
}
2940
}
3041
Logger::info(LABEL, "stopped");
@@ -33,75 +44,106 @@ Mqtt::Mqtt(const Config &config)
3344
Mqtt::~Mqtt() {
3445
m_isRunning = false;
3546
m_thread.join();
36-
mosquitto_disconnect(m_client);
37-
mosquitto_destroy(m_client);
47+
if (m_client.is_connected()) {
48+
m_client.disconnect();
49+
}
3850
}
3951

40-
void Mqtt::publish(const std::string &topic, const std::string &data, int qos) {
52+
void Mqtt::publish(const std::string& topic, const std::string& data, int qos) {
4153
std::unique_lock lock(m_mutex);
4254
if (m_messages.size() < QUEUE_MAX_SIZE) {
4355
m_messages.emplace(topic, std::vector<uint8_t>{data.begin(), data.end()}, qos);
4456
Logger::trace(LABEL, "queue size: {}", m_messages.size());
4557
}
4658
}
4759

48-
void Mqtt::publish(const std::string &topic, const std::vector<uint8_t> &data, int qos) {
60+
void Mqtt::publish(const std::string& topic, const std::vector<uint8_t>& data, int qos) {
4961
std::unique_lock lock(m_mutex);
5062
if (m_messages.size() < QUEUE_MAX_SIZE) {
5163
m_messages.emplace(topic, data, qos);
5264
Logger::trace(LABEL, "queue size: {}", m_messages.size());
5365
}
5466
}
5567

56-
void Mqtt::publish(const std::string &topic, const std::vector<uint8_t> &&data, int qos) {
68+
void Mqtt::publish(const std::string& topic, const std::vector<uint8_t>&& data, int qos) {
5769
std::unique_lock lock(m_mutex);
5870
if (m_messages.size() < QUEUE_MAX_SIZE) {
5971
m_messages.emplace(topic, std::move(data), qos);
6072
Logger::trace(LABEL, "queue size: {}", m_messages.size());
6173
}
6274
}
6375

64-
void Mqtt::setMessageCallback(const std::string &topic, std::function<void(const std::string &)> callback) {
76+
void Mqtt::setMessageCallback(const std::string& topic, std::function<void(const std::string&)> callback) {
6577
subscribe(topic);
6678
m_callbacks.emplace_back(topic, callback);
6779
}
6880

69-
void Mqtt::subscribe(const std::string &topic) {
70-
if (m_topics.count(topic) == 0) {
71-
mosquitto_subscribe(m_client, nullptr, topic.c_str(), QOS_SUB);
72-
m_topics.insert(topic);
81+
void Mqtt::connect() {
82+
mqtt::ssl_options ssl_options;
83+
ssl_options.ca_path("/etc/ssl/certs");
84+
85+
const auto options = mqtt::connect_options_builder()
86+
.mqtt_version(MQTTVERSION_3_1_1)
87+
.ssl(ssl_options)
88+
.user_name(m_config.mqttUsername())
89+
.password(m_config.mqttPassword())
90+
.keep_alive_interval(KEEP_ALIVE)
91+
.connect_timeout(CONNECT_TIMEOUT)
92+
.automatic_reconnect(false)
93+
.clean_session(true)
94+
.finalize();
95+
96+
try {
97+
const auto response = m_client.connect(options);
98+
if (response.is_session_present()) {
99+
Logger::info(LABEL, "session already present");
100+
} else {
101+
Logger::info(LABEL, "new session created");
102+
}
103+
} catch (const std::runtime_error& exception) {
104+
Logger::warn(LABEL, "exception: {}", exception.what());
105+
}
106+
if (m_client.is_connected()) {
107+
onConnected();
73108
}
74109
}
75110

76-
void Mqtt::onConnect() {
111+
void Mqtt::onConnected() {
77112
Logger::info(LABEL, "connected");
78-
for (const auto &topic : m_topics) {
113+
std::unique_lock lock(m_mutex);
114+
115+
for (const auto& topic : m_topics) {
79116
Logger::info(LABEL, "subscribe: {}", colored(GREEN, "{}", topic));
80-
mosquitto_subscribe(m_client, nullptr, topic.c_str(), QOS_SUB);
117+
m_client.subscribe(topic, QOS_SUB);
81118
}
82-
}
83119

84-
void Mqtt::onDisconnect() {
85-
if (!m_isRunning) {
86-
return;
87-
}
88-
Logger::warn(LABEL, "disconnected");
89-
while (m_isRunning && mosquitto_reconnect(m_client) != MOSQ_ERR_SUCCESS) {
90-
Logger::info(LABEL, "reconnecting");
91-
std::this_thread::sleep_for(RECONNECT_INTERVAL);
120+
for (const auto& topic : m_waitingTopics) {
121+
Logger::info(LABEL, "subscribe: {}", colored(GREEN, "{}", topic));
122+
m_client.subscribe(topic, QOS_SUB);
123+
m_topics.insert(topic);
92124
}
93-
Logger::info(LABEL, "reconnecting success");
125+
m_waitingTopics.clear();
126+
}
127+
128+
void Mqtt::onDisconnected() { Logger::info(LABEL, "disconnected"); }
129+
130+
void Mqtt::subscribe(const std::string& topic) {
94131
std::unique_lock lock(m_mutex);
95-
while (!m_messages.empty()) {
96-
m_messages.pop();
132+
if (m_client.is_connected()) {
133+
if (m_topics.count(topic) == 0) {
134+
Logger::info(LABEL, "subscribe: {}", colored(GREEN, "{}", topic));
135+
m_client.subscribe(topic, QOS_SUB);
136+
m_topics.insert(topic);
137+
}
138+
} else {
139+
m_waitingTopics.insert(topic);
97140
}
98141
}
99142

100-
void Mqtt::onMessage(const mosquitto_message *message) {
101-
Logger::debug(LABEL, "topic: {}, data: {}", message->topic, static_cast<char *>(message->payload));
102-
const std::string data(static_cast<char *>(message->payload), message->payloadlen);
103-
for (auto &[topic, callback] : m_callbacks) {
104-
if (strcmp(message->topic, topic.c_str()) == 0) {
143+
void Mqtt::onMessage(const std::string& topic, const std::string& data) {
144+
Logger::debug(LABEL, "topic: {}, data: {}", topic, data);
145+
for (auto& [callbackTopic, callback] : m_callbacks) {
146+
if (topic == callbackTopic) {
105147
callback(data);
106148
}
107149
}

sources/network/mqtt.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#pragma once
22

33
#include <config.h>
4-
#include <mosquitto.h>
4+
#include <mqtt/client.h>
55

66
#include <atomic>
77
#include <functional>
@@ -23,16 +23,19 @@ class Mqtt {
2323
void setMessageCallback(const std::string& topic, std::function<void(const std::string&)> callback);
2424

2525
private:
26-
void onConnect();
27-
void onDisconnect();
28-
void onMessage(const mosquitto_message* message);
26+
void connect();
27+
void onConnected();
28+
void onDisconnected();
29+
void onMessage(const std::string& topic, const std::string& data);
2930
void subscribe(const std::string& topic);
3031

31-
mosquitto* m_client;
32+
const Config& m_config;
33+
mqtt::client m_client;
3234
std::atomic_bool m_isRunning;
3335
std::thread m_thread;
3436
std::mutex m_mutex;
3537
std::queue<std::tuple<std::string, std::vector<uint8_t>, int>> m_messages;
3638
std::set<std::string> m_topics;
39+
std::set<std::string> m_waitingTopics;
3740
std::vector<std::pair<std::string, std::function<void(const std::string&)>>> m_callbacks;
3841
};

0 commit comments

Comments
 (0)