現在、特定のメッセージが表示されたときに GPIO アクションをトリガーすることになっている Raspberry Pi 用のドアベル アプリケーションに取り組んでいます。
したがって、私はメインクラスを持っていますdoorbell
:
ドアベル.h
class doorbell {
public:
// ...
void registerMqttListener();
// ...
}
ドアベル.cpp
#include "doorbell.h"
#include "threadLauncher.h"
doorbell::doorbell() {
// ...
threadLauncher& launcher { threadLauncher::inst() };
launcher.go(std::make_unique<std::thread>(&doorbell::registerMqttListener, this));
}
void doorbell::registerMqttListener()
{
// create our MQTT client and start subscription
mqttClient client(_conf);
client.subscribeToKnownTopic();
}
そして、ドアベル クラスから開始されたスレッドを処理する単純なthreadLauncher
クラス:
threadLauncher.h
#include <vector>
#include <thread>
#include <functional>
class threadLauncher
{
public:
void go(const std::string& shellCmd);
void go(std::unique_ptr<std::thread> thread);
std::string execShellCmd(std::string const& cmd);
static threadLauncher& inst()
{
static threadLauncher inst;
return inst;
}
private:
threadLauncher() {}
~threadLauncher();
std::vector<std::unique_ptr<std::thread>> _asyncThreads;
};
スレッドランチャー.cpp
#include "threadLauncher.h"
#include "log.h"
void threadLauncher::go(const std::string& args)
{
_asyncThreads.emplace_back(std::make_unique<std::thread>(&threadLauncher::execShellCmd, this, args));
}
void threadLauncher::go(std::unique_ptr<std::thread> thread)
{
_asyncThreads.emplace_back(std::move(thread));
}
std::string threadLauncher::execShellCmd(std::string const& cmd)
{
std::array<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe { popen(cmd.c_str(), "r"), pclose };
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
threadLauncher::~threadLauncher()
{
for (auto& at : _asyncThreads) {
if (at) {
if (at->joinable()) {
at->join();
}
at.release();
}
}
}
最後にmqttClient
、MQTT を処理するクラスがあります。それはmosquittoppに基づいています:
mqttClient.h
#ifndef MQTTCLIENT_H
#define MQTTCLIENT_H
#include <string>
// 3rd-party
#include <mosquittopp.h>
#include "config.h"
class mqttClient : public mosqpp::mosquittopp
{
public:
mqttClient();
~mqttClient();
bool send(const std::string& message);
bool subscribeTo(const std::string& topic);
bool subscribeToKnownTopic();
private:
std::string _host;
std::string _id;
std::string _topic;
std::string _token;
int _port;
int _keepalive;
void on_connect(int rc) override;
void on_disconnect(int rc) override;
void on_publish(int mid) override;
void on_message(const struct mosquitto_message* message) override;
void on_subscribe(int mid, int qos_count, const int* granted_qos) override;
};
#endif // MQTTCLIENT_H
mqttClient.cpp
#include "mqttClient.h"
#include <iostream>
#include "threadLauncher.h"
mqttClient::mqttClient()
{
_id = "myid";
_topic = "mytopic";
_host = "myhost";
_port = 1337;
_token = "mytoken";
mosqpp::lib_init();
connect(_host.c_str(), _port, _keepalive);
loop_start();
}
mqttClient::~mqttClient()
{
loop_stop();
mosqpp::lib_cleanup();
}
/**
* @brief Publishes a given message to the MQTT client
* @param message - The message to be sent.
* @return success or failure.
*/
bool mqttClient::send(std::string const& message)
{
int retval = publish(nullptr, _topic.c_str(), message.length(), message.c_str(), 1, false);
return MOSQ_ERR_SUCCESS == retval;
}
/**
* @brief Subscribes to a given topic.
* @param topic - The topic to be subscribed to.
* @return success or failure.
*/
bool mqttClient::subscribeTo(std::string const& topic)
{
int retval = subscribe(nullptr, topic.c_str(), 1);
return MOSQ_ERR_SUCCESS == retval;
}
/**
* @brief Subscribes to a topic that was set on instantiation of the mqttClient object.
* @return success or failure.
*/
bool mqttClient::subscribeToKnownTopic()
{
int retval = subscribe(nullptr, _topic.c_str(), 1);
return MOSQ_ERR_SUCCESS == retval;
}
/**
* @brief Connected callback.
* @param rc - return code
*/
void mqttClient::on_connect(int rc)
{
if (0 == rc) {
std::cout << "[MQTT] Connected (" + std::to_string(rc) + ").";
} else {
std::cout << "[MQTT] Connection failed (" + std::to_string(rc) + ")!";
}
}
/**
* @brief Disconnect callback.
* @author taibsu
* @param rc - return code
*/
void mqttClient::on_disconnect(int rc)
{
}
/**
* @brief TODO implement
*/
void mqttClient::on_publish(int mid)
{
}
/**
* @brief Message callback.
* @author taiBsu
* @param message - The message to be reacted to.
* @TODO All this stuff should be called from another class.
*/
void mqttClient::on_message(const struct mosquitto_message* message)
{
std::string msg { reinterpret_cast<char*>(message->payload) };
if (msg.find(_token) == std::string::npos) {
return;
}
// @TODO rework this
if (msg.find("onOpenDoor") != std::string::npos) {
std::string openDoorCmd { "python /home/pi/open.py" };
threadLauncher::inst().go(openDoorCmd);
}
if (msg.find("onPlayNotAtHome") != std::string::npos) {
std::string notAtHomeCmd { "aplay /home/pi/sounds/notAtHome.wav" };
threadLauncher::inst().go(notAtHomeCmd);
}
if (msg.find("onPlayOnOurWay") != std::string::npos) {
std::string onOurWayCmd { "aplay /home/pi/sounds/onOurWay.wav" };
threadLauncher::inst().go(onOurWayCmd);
}
if (msg.find("onTakePicture") != std::string::npos) {
std::string chat_id { msg.substr(msg.find_last_of(':') + 1, msg.size() - 1) };
std::string quality { "80" };
std::string delay { "1s" };
std::string rotation { "270" };
std::string iso { "600" };
std::string width { "600" };
std::string height { "800" };
std::string pictureName { "door_manual.jpg" };
std::string cmd {
"raspistill -q " + quality +
" -t " + delay +
" --rotation " + rotation +
" -ISO " + iso +
" -w " + width +
" -h " + height +
" -o " + pictureName
};
threadLauncher::inst().go(cmd);
}
}
/**
* @brief Subscription callback
*/
void mqttClient::on_subscribe(int mid, int qos_count, const int* granted_qos)
{
std::cout << "[MQTT] Subscription started for topic " + _topic + ".";
}
最後に、私のエントリ ポイント:
main.cpp
#include "doorbell.h"
int main() {
doorbell bell;
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
これが私の問題です:
Raspberry Pi 4 B でアプリケーションを起動すると、4 つの CPU のいずれかで 100% の CPU 負荷がかかり続けます。
launcher.go(std::make_unique<std::thread>(&doorbell::registerMqttListener, this));
コンストラクターで行を削除するとdoorbell.cpp
、すべて正常に機能します。
これはmosquittoppライブラリの誤った動作である可能性がありますか、それともここで何か間違ったことをしていますか?
追加の質問: Visual Studio で IoT デバイスをリモート デバッグするときに、CPU や RAM の使用状況をプロファイルするにはどうすればよいですか?
編集:loop_start()
からの関数mosquittopp
が 100% の CPU 負荷を引き起こしている
ことを確認できるようになりました。これは mosquittopp の問題ですか、それとも間違った方法で使用していますか?