0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

リモートのMQTTブローカー経由でRaspberryPIからBLE操作を行う

Posted at

背景

前回の記事でMQTTブローカーをクラウド上に設定し、スマホアプリ上のPublisher・Subscriberから出版と購読ができることを確認した。

今回はRaspberryPiからこのブローカーに接続して、何かコマンドを送れるようにする。
以前から作成しているIotDeviceHubをアップデートして、MQTTでPublishメッセージを受信したら対応するコマンドをBLE機器に送るようにする。
https://github.com/futail2a/IotDeviceHub/

libmosquittoの設定

前回の記事で作成したCA証明書とクライアント証明書、クライアント鍵をダウンロードして、RaspberryPiに送る。
mosquitto_tls_set()で証明書のパスを指定するので、場所はIotDeviceHubアプリから読める所ならどこでもいい。

また、リモートにあるブローカのアドレスとポートも設定する必要があるので、そのあたりの値をまとめてJSONファイルに設定するようにした。

// IotDeviceHub/config/config.json
{
  "mqtt":
  {
    "brokerIpv4": <address>,
    "brokerPort": <port>,
    "caCert": <caCertPath>,
    "clientCert": <clientCertPath>,
    "clientKey": <keyPath>
  }
}

JSONファイルの読み込みはPOCOを使う。
MQTTを管理しているクラスのコンストラクタでJSON内の値を読み込むようにした。

MqttManager::MqttManager()
{
    try
    {
        Poco::Util::JSONConfiguration config = Poco::Util::JSONConfiguration(CONFIG_FILE_PATH);
        mBrokerIpv4 = config.getString("mqtt.brokerIpv4");
        std::cout << "MQTT broker address: " << mBrokerIpv4 << std::endl;
        mBrokerPort = config.getUInt16("mqtt.brokerPort");
        std::cout << "MQTT broker port: " << mBrokerPort << std::endl;

        mCaCertPath = config.getString("mqtt.caCert");
        std::cout << "MQTT CA cert path: " << mCaCertPath << std::endl;
        mClientCertPath = config.getString("mqtt.clientCert");
        std::cout << "MQTT client cert path: " << mClientCertPath << std::endl;
        mClientKeyPath = config.getString("mqtt.clientKey");
        std::cout << "MQTT client key path: "<< mClientKeyPath << std::endl;
    }
    catch (Poco::Exception& ex)
    {
        std::cerr << "Error: " << ex.displayText() << std::endl;
    }

}

証明書やブローカのアドレスはmosquitto_tls_set()とmosquitto_connect_bind_v5()で設定できる。

//IotDeviceHub/mqtt/MqttManager.cpp
auto ret = mosquitto_tls_set(m_mosq, mCaCertPath.c_str(), NULL, mClientCertPath.c_str(), mClientKeyPath.c_str(), NULL);
:
ret = mosquitto_connect_bind_v5(m_mosq, mBrokerIpv4.c_str(), mBrokerPort, 60, nullptr, nullptr);

MQTTメッセージに対応するコールバックの設定

MQTTのPublishメッセージを受信したら登録していたコールバックから対応するBLEコマンドを送れるようにしたい。

libmosquittoではmosquitto_message_v5_callback_set()でメッセージ受信時のコールバックを設定することができる。
しかしlibmosquittoはC言語ライブラリなのでメンバ関数をバインドする、みたいなことはできない。
これだと少し不都合があるので、mosquittoインスタンスを作るときにthisポインタを渡す。
こうすることでコールバックのobjにthisが渡されるので、そこからpublicメソッドにアクセスすることができる。

//bool MqttManager::init(std::string client_id)
 m_mosq = mosquitto_new(client_id.c_str(), true, this);
void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *props)
{
    auto *manager = static_cast<MqttManager*>(obj);
    if(manager)
    {
        manager->onMessageReceived(msg);
    }

}

ちなみに、イベントのハンドリングにはMediatorパターンを使っている(詳細は以下の記事)。
MQTTトピック名をイベント、ペイロードをイベントデータとして扱っている。

void MqttManager::onMessageReceived(const struct mosquitto_message *msg)
{
    if(mMediator)
    {
        std::string eventData="";
        if(msg->payload == nullptr || msg->payloadlen == 0)
        {
            std::cerr << "Received empty message on topic " << msg->topic << std::endl;
        }
        else
        {
            eventData=std::string((char *)msg->payload, msg->payloadlen);
        }
        mMediator->onEvent(msg->topic, eventData);
    }
    else
    {
        std::cerr << "Mediator not set, cannot handle message on topic " << msg->topic << std::endl;
    }
}

MQTTのクライアント側で以下のように設定すればイベントとするMQTTトピックとイベントに対応するアクションを定義できる。
この例だと"exec_bot"をサブスクライブして、そのトピックを受信するとBLE機器(mBotDevice)にコマンドを送るラムダ式が実行される。

//bool IotDeviceHubManager::init()
mMqtt->subscribe("exec_bot");
mEventManager->registerEventHandler("exec_bot", [this](const std::string& eventData)
{
    std::cout << "Event: exec_bot" << std::endl;
    auto command = mBotDevice->getExecActionCommand();
    mBle->sendBleCommand(command);
}
);
mMqtt->setMediator(mEventManager);

実験

実験として、Switchbotボットのスイッチ実行をMQTT経由で実行できるか確かめた。

まずはSwitchbotボットをエアコンのリモコンに固定する。
中途半端に固定すると動作したときにボットが動いてボタンが押せないのでマスキングテープでガチガチに固定する。
リモコンの画面が見えなくなるという致命的な欠陥があるが、一旦無視する。

WoHand.jpg

その後、冒頭の記事で紹介したスマホアプリに"exec_bot"トピックをPublishするボタンパネルを設定し、ボタンを押す。

結果

WoHand.gif

スマホアプリからexec_botトピックをパブリッシュするとボットが動作した。

さすがにこれだと通常の使い方ができないのでメカニカルな改良がもっと必要だが、インターネット経由で室内の機器を操作できるようになったのでIoTらしくなってきたと思う。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?