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?

[日記] [C++] Observerパターンを使ってBLEアドバタイズパケットのデータを受け取る

Last updated at Posted at 2025-04-20

以前からBLE機器からの情報を配信するアプリを作っているが、今回は複数のBLE機器を扱えるように更新した

変更方針

image.png

  • 複数のBLE機器からのアドバタイズパケットをモニタリングできるようにしたい
  • 具体的にはBluezAbstructLayerはSensorDataHandlerを複数持てるようにして、センサーからの変更があれば各ハンドラに通知するようにしたい
  • そのためSensorDataHandlerにObserverパターンを適用して、アドバタイズパケットがあればBluezAbstructLayerが各SensorDataHandlerのupdate関数を呼び出すように変更したい

SensorDataHandler

  • SensorDataHandlerにデータの更新を受け取るためのupdate関数と、その中で呼ばれるコールバック関数を設定できるset_update_cbを用意した
  • 後に説明するが、このコールバック関数は更新された値をMqttManagerに渡すために使われる
using UpdateCb = std::function <void(std::vector<uint8_t>)>;

class SensorDataHandler
{
public:
:
    virtual void set_update_cb(UpdateCb cb)=0;
    virtual void update(DBusMessage* const reply) = 0;
:
};

SensorDataHanlerサブクラス

  • 今回新たにSwitchBotの人感センサを追加するので、SensorDataHanlderのサブクラスとしてMotionSensorDataHanlerを新たに追加した
  • update関数ではアドバタイズパケットがDBusMessageとして渡されるので、必要な情報をパースしそのバイト列をコールバック関数へ渡すようにした
  • 温湿度計のデータを取り扱うWoSensorTHDataHandlerにも同様に変更を加えた
class MotionSensorDataHandler : public SensorDataHandler
{
public:
  void set_update_cb(UpdateCb cb) override { m_update_cb = cb; }
  void update(DBusMessage* const reply) override;

private:
  UpdateCb m_update_cb;
};

void MotionSensorDataHandler::update(DBusMessage* const reply)
{
    if (m_update_cb)
    {
        std::vector<uint8_t> data = parse_reply(reply);
        m_update_cb(data);
    }
}

BluezAbstructLayer

  • 複数のSensorDataHandlerをvecotrで持つように変更した
  • また外部からSensorDataHandlerを追加するためのadd_sensor_data_handler()を用意した
class BluezAbstructLayer
{
public:
:
    void add_sensor_data_handler(std::shared_ptr<SensorDataHandler> sensorDataHandler);
:
private:
    std::vector<std::shared_ptr<SensorDataHandler>> m_sensorDataHandlers;
 :
};
  • BlueZにアドバタイズパケットを確認する際、各SensorDataHandlerに対して確認するように変更した
  • パケットが見つかった場合はそのSensorDataHanlerに対して通知を行うようにした
void BluezAbstructLayer::check_adv_data()
{
    std::vector<uint8_t> byte_data={};
    // Iterate vector of SensorDataHandler 
    for(auto itr = m_sensorDataHandlers.begin(); itr != m_sensorDataHandlers.end(); ++itr)
    {
        (*itr)->get_device_mac();
        // Obtain MAC address from SensorDataHandler
        std::string device_path = m_crate_device_path((*itr)->get_device_mac());
        DBusMessage* msg = dbus_message_new_method_call(BLUEZ_SERVICE.c_str(), device_path.c_str(), DBUS_PROPERTIES.c_str(), METHOD_GET_ALL.c_str());
        :
        const char* interface_name = BLUEZ_DEVICE.c_str();
        dbus_message_append_args(msg, DBUS_TYPE_STRING, &interface_name, DBUS_TYPE_INVALID);
        :
        DBusMessage* reply = dbus_connection_send_with_reply_and_block(m_conn, msg, -1, &err);
        dbus_message_unref(msg);
        :
        if (!reply)
        {
            std::cerr << "Failed to get properties for " << device_path << "\n";
            break;
        }

        // Notify BLE data via SensorDataHandler::update()
        (*itr)->update(reply);
        dbus_message_unref(reply);
    }
}

IotDeviceHubManager

  • BluezAbstructLayerにモニタリングしたいBLE機器のSensorDataHandlerを追加した
  • またアドバタイズパケットを見つけたらMQTTでPusblishできるようにするため、コールバック関数を追加した
bool IotDeviceHubManager::init()
{
    :
    m_th_sensor_data_handler = std::make_shared<WoSensorTHDataHandler>();
    m_th_sensor_data_handler->set_update_cb(std::bind(&IotDeviceHubManager::on_th_update, this, std::placeholders::_1));
    m_bluez->add_sensor_data_handler(m_th_sensor_data_handler);

    m_motion_sensor_data_handler = std::make_shared<MotionSensorDataHandler>();
    m_motion_sensor_data_handler->set_update_cb(std::bind(&IotDeviceHubManager::on_motion_update, this, std::placeholders::_1));
    m_bluez->add_sensor_data_handler(m_motion_sensor_data_handler);
    :
}
  • このコールバック関数はアドバタイズパケットが見つかった時に、センサーの値をバイト列として渡される
  • そのためバイト列からMQTTのPublishメッセージに変換するcreatePublishMessages関数をこのコールバック内で呼ぶように変更した
void IotDeviceHubManager::on_th_update(std::vector<uint8_t> data)
{
    if (!data.empty())
    {
        std::vector<MqttMessage> messages = m_th_sensor_data_handler->createPublishMessages(data);
        for(auto message : messages)
        {
            m_mqtt->publishMessage(message.topic, message.message);
        }
    }
}

void IotDeviceHubManager::on_motion_update(std::vector<uint8_t> data)
{
    if (!data.empty())
    {
        std::vector<MqttMessage> messages = m_motion_sensor_data_handler->createPublishMessages(data);
        for(auto message : messages)
        {
            m_mqtt->publishMessage(message.topic, message.message);
        }
    }
    

実行結果

RasperryPi上にMQTTのSubscriberアプリを作り、温湿度計とモーションセンサーのトピックをサブスクライブしたところ、以下のようにデータがPublishされていることが確認できた

Message received on topic iot_device_hub/sensor_data/bed_room/temperature: Temperature: 21°C
Log: Client iot_device_client received PUBLISH (d0, q0, r0, m0, 'iot_device_hub/sensor_data/bed_room/humidity', ... (13 bytes))
Message received on topic iot_device_hub/sensor_data/bed_room/humidity: Humidity: 48%
Log: Client iot_device_client received PUBLISH (d0, q0, r0, m0, 'iot_device_hub/sensor_data/entrance/pir_utc', ... (39 bytes))
Message received on topic iot_device_hub/sensor_data/entrance/pir_utc: Since the last trigger PIR time (s): 32
Log: Client iot_device_client received PUBLISH (d0, q0, r0, m0, 'iot_device_hub/sensor_data/entrance/light_intensity', ... (23 bytes))
Message received on topic iot_device_hub/sensor_data/entrance/light_intensity: Light Intensity: bright

まとめ

  • Observerパターンを使って複数のBLE機器のアドバタイズパケットを配信できるようにした
  • 少ない追加実装でBLE機器の追加ができるようになった

今後の課題

今はアドバタイズパケットを見るだけだが、GATTを通してデバイス対して何か操作できるようにしたい

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?