見出し画像

【4/5ページ】SkyWay SDK for Linuxハンズオン資料 ブラウザからRaspberryPiを遠隔で操作してみる編

このコンテンツはSDK for Linux®︎のハンズオンイベントで利用するコンテンツです。
ハンズオンに参加できなかった方でも、個人で本記事に沿って作成いただけるように構成しています。
ハンズオンの概要・使用する機材の情報については概要ページをご覧ください。

ここまでで、WebブラウザでSkyWayを操作する方法を学んできました。
このページでは、いよいよブラウザからRaspberryPiを操作するようなコードを書いていきます。


アプリの完成イメージ

アプリは実行可能ファイルをコマンドから実行することを想定します。

user ~% ./app.out ${room_name}
// app.out: 実行可能ファイル名
// room_name: 入室するRoomの名前

アプリを実行すると、カメラ映像をPublishし、LED制御用のDataStreamをSubscribeします。
SubscribeしたDataStreamからHIGHの文字列が送られてくるとLEDを点灯、LOWが送られてくるとLEDを消灯します。

アプリの完成イメージ

SkyWay SDK for Linuxを準備する

SkyWay SDK for Linux®︎をダウンロードする

GitHubからSkyWay SDK for Linux®︎の開発に必要なファイルをダウンロードします。 下記コマンドでGitHubからファイルを入手、展開します。

cd ~
git clone https://github.com/skyway/sdk-for-linux.git
wget https://github.com/skyway/sdk-for-linux/releases/download/v1.1.0/skyway-libs-aarch64.zip
unzip skyway-libs-aarch64.zip

各ファイルを作成

SkyWayを利用するためにCMakeLists.txtを作成します。
また、先程ダウンロードしたSDK for Linux®︎のファイル群をコピーしてきます。
記載する内容はSDK for Linux®︎のQuickStartの内容を参考にします。

# 操作例(handsonディレクトリ内)
cd handson
mkdir remote-led
cd remote-led
touch CMakeLists.txt
cp -r ~/sdk-for-linux/include ./ # 先程git cloneしたSDK for Linux®︎のincludeをコピーしてきます。
cp -r ~/libs ./ # wgetしてきたSDK for Linux®︎のバイナリ群をコピーしてきます。
# CMakeLists.txt

cmake_minimum_required(VERSION 3.10.2)

project("handson")

# clang++/C++17を利用します。
enable_language(CXX)
set(CMAKE_CXX_COMPILER "/usr/bin/clang++" CACHE STRING "clang++ compile" FORCE)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# libsにある各ライブラリを読み込みます。
add_library( skyway-linux-core STATIC IMPORTED )
set_target_properties( skyway-linux-core
        PROPERTIES
        IMPORTED_LOCATION
        ${CMAKE_CURRENT_SOURCE_DIR}/libs/libskyway-linux-core.a)

add_library( skyway-linux-room STATIC IMPORTED )
set_target_properties( skyway-linux-room
        PROPERTIES
        IMPORTED_LOCATION
        ${CMAKE_CURRENT_SOURCE_DIR}/libs/libskyway-linux-room.a)

# SkyWayを利用する際に必要な定義します。
set( SKYWAY_LINUX_DEFINITIONS
        WEBRTC_LINUX=1 
        WEBRTC_POSIX=1)

# アプリで利用する依存ライブラリです。
set( SKYWAY_LINUX_LIBS
        skyway-linux-room
        skyway-linux-core
        pthread
        atomic 
        avcodec 
        X11 
        glib-2.0 
        gio-2.0
        gobject-2.0
        dl
        gpiod)

# アプリで利用するincludeパスを設定します。
set( SKYWAY_LINUX_INCLUDES 
        ${CMAKE_CURRENT_SOURCE_DIR}/include/external/libmediasoupclient/include
        ${CMAKE_CURRENT_SOURCE_DIR}/include/external/libmediasoupclient/deps/libsdptransform/include
        ${CMAKE_CURRENT_SOURCE_DIR}/include/external/libwebrtc
        ${CMAKE_CURRENT_SOURCE_DIR}/include/external/libskyway/include
        ${CMAKE_CURRENT_SOURCE_DIR}/include/external/libwebrtc/third_party/abseil-cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/include/external/libwebrtc/third_party/boringssl/src/include
        ${CMAKE_CURRENT_SOURCE_DIR}/include/external/boost
        ${CMAKE_CURRENT_SOURCE_DIR}/include)

add_executable(app.out main.cpp handson_room.cpp)
target_compile_definitions(app.out PRIVATE ${SKYWAY_LINUX_DEFINITIONS})
target_include_directories(app.out PRIVATE ${SKYWAY_LINUX_INCLUDES})
target_link_libraries(app.out ${SKYWAY_LINUX_LIBS})

add_executable(app.out main.cpp handson_room.cpp)にあるようにmain.cppとhandson_room.cppを作成します。
また、handson_room.cppのヘッダーとしてhandson_room.hppも作成します。

# 操作例(handson/remote-ledディレクトリ内)
cd handson/remote-led
touch main.cpp
touch handson_room.cpp
touch handson_room.hpp

各ファイルの内容は以下の通りです。 このコンテンツでは機能を括弧[]の順に実装をしていきます。

// main.cpp

#include <iostream>

#include "handson_room.hpp"

int main(int argc, char* argv[]) {
    // 実行時引数にroom_nameを取るようにします。
    if (argc != 2) {
        std::cerr << "app.out <room_name>" << std::endl;
        return -1;
    }

    // 第一引数をRoomNameとします。
    std::string room_name = argv[1];

    // [1] SkyWay Auth Tokenを取得します。

    // [2] SkyWayとGPIOをセットアップをします。

    // [3] RoomにJoinします。

    // [4] カメラ映像をPublishします。
    
    // [5] DataStreamをSubscribeします。
    
    // キー入力を待ちます。
    std::cout << "- Press ENTER key to close..." << std::endl;
    std::cin.get();

    // [6] Roomから退出します。

    // [7] リソースを破棄します。
    return 0;
}
// handson_room.hpp

#ifndef SKYWAY_HANDSON_ROOM_HPP_
#define SKYWAY_HANDSON_ROOM_HPP_

#include <gpiod.h>

#include <skyway/context.hpp>
#include <skyway/core/stream/remote/data_stream.hpp>
#include <skyway/media/device_manager.hpp>
#include <skyway/media/stream_factory.hpp>
#include <skyway/media/v4l2_video_renderer.hpp>
#include <skyway/room/p2p/p2p_room.hpp>

// Roomの操作を行うクラスです。
// [x] イベントリスナーはこのクラスに実装する
class HandsOnRoom {
public:
    // [a] コンストラクタとアプリのセットアップを行います。

    // [b] RoomにJoinをします。
    
    // [c] カメラ映像をPublishします。

    // [d] DataStreamをSubscribeします。

    // [e] 指定のPublicationをSubscribeしているかチェックします。

    // [f] Roomイベントのリスナを実装します。

    // [g] DataStreamのイベントのリスナを実装します。

    // [h] Roomから退出します。

    // [i] リソースを破棄します。

private:
    // 今回利用するメンバー変数です。
    std::shared_ptr<skyway::room::p2p::P2PRoom> p2proom_;
    std::unique_ptr<skyway::room::p2p::LocalP2PRoomMember> room_member_;
    std::vector<std::unique_ptr<std::thread>> threads_;
    bool is_leaving_;
    bool is_notify_;
    struct gpiod_chip *gpio_chip_;
    struct gpiod_line *gpio_line_out_;
};
#endif  // SKYWAY_HANDSON_ROOM_HPP_

// handson_room.cpp

#include "handson_room.hpp"

#include <iostream>

// [A] コンストラクタとアプリのセットアップをします。

// [B] RoomにJoinします。

// [C] カメラ映像をPublishします。

// [D] DataStreamをSubscribeします。

// [E] 指定のPublicationをSubscribeしているかチェックします。

// [F] Roomイベントのリスナを実装します。

// [G] DataStreamのイベントのリスナを実装します。

// [H] Roomから退出します。

// [I] リソースを破棄します。

作成したところで、ビルド、実行してみます。

# 操作例
cmake -B build
cd build
make -j
./app.out hoge
# - Press ENTER key to close... と表示されれば成功です。

それではSkyWayについてのコードを書いていきましょう。

PulseAudioの起動

linuxにおいてaudioを扱うために、先ほどインストールしたPulseAudioの起動を行います。
以下のコマンドを実行してください。

pulseaudio -D

SkyWay Auth Tokenの取得

SkyWayの利用にはSkyWay Auth Tokenが必要です。
SkyWay Explorerで作成し、メモしておいたトークンを以下のコマンドで環境変数に設定し、それをアプリから読み出す形とします。

# 操作例
export SKYWAY_AUTH_TOKEN=eyJhb...

[1]に以下の内容を追加します。

// main.cpp

    // [1] SkyWay Auth Tokenを取得します。
    const char* token = getenv("SKYWAY_AUTH_TOKEN");
    if (!token) {
        std::cerr << "Please set SKYWAY_AUTH_TOKEN environment variable." << std::endl;
        return -1;
    }

アプリのセットアップ

SkyWayのセットアップおよびGPIOの初期化処理を記載していきます。 SkyWayのセットアップはSkyWay Explorerでの操作のように、AuthTokenを作成し、SkyWayのContext.Create()をコールする必要があります。

// main.cpp

    // [2] SkyWayとGPIOをセットアップします。
    // SkyWayの詳細なログの出力を表示したい場合はLogLevelをkInfoなどに変更してください。
    auto skyway_log_level = skyway::global::interface::Logger::kError;
    
    // handson_room内に実装されているログ出力が必要な場合にはtrueに変更してください。
    auto app_is_notify = false;

    // 使用するGPIOピン番号を設定します。
    auto gpio_line_number = 26;
    
    auto handson_room = HandsOnRoom(app_is_notify);
    if (!handson_room.Setup(token,gpio_line_number,skyway_log_level)) {
        return -1;
    }
// handson_room.hpp

    // [a] コンストラクタとアプリのセットアップを行います。
    HandsOnRoom(bool is_notify);

    // GPIOの初期化およびSkyWayのセットアップを行います。
    bool Setup(const std::string& token,const int gpio_line_number,skyway::global::interface::Logger::Level log_level);
// handson_room.cpp

// [A] コンストラクタとアプリのセットアップをします。
HandsOnRoom::HandsOnRoom(bool is_notify)
    : p2proom_(nullptr),
      room_member_(nullptr),
      threads_(std::vector<std::unique_ptr<std::thread>>()),
      is_leaving_(false),
      is_notify_(is_notify),
      gpio_chip_(nullptr),
      gpio_line_out_(nullptr) {}

// GPIOの初期化およびSkyWayのセットアップを行います。
bool HandsOnRoom::Setup(const std::string& token,const int gpio_line_number,skyway::global::interface::Logger::Level log_level) {
    gpio_chip_ = gpiod_chip_open("/dev/gpiochip4");
    gpio_line_out_ = gpiod_chip_get_line(gpio_chip_, gpio_line_number);
    gpiod_line_request_output(gpio_line_out_, "SkyWay HandsOn", 0);
    
    skyway::Context::SkyWayOptions context_options{};
    context_options.log_level = log_level;
    if (!skyway::Context::Setup(token, nullptr, context_options)) {
        std::cerr << "- [Error] setup failed." << std::endl;
        return false;
    }
    return true;
}

初期化ができるかビルドしてみます。

操作例
cmake -B build
cd build
make -j

実行するとトークンに問題なければそのままキー入力待ちに入ります。

export SKYWAY_AUTH_TOKEN=eyJhb...
./app.out test_room
- Press ENTER key to close...

Roomに入室してみる

SkyWayのセットアップが出来るようになったので、Roomに入室してみます。
コードに入室用のコードを追記していきます。

// main.cpp

    // [3] RoomにJoinします。
    // 実行時引数として受け取ったRoom名をHandsOnRoom::JoinRoomの引数として渡す
    if (!handson_room.JoinRoom(room_name)) {
        return -1;
    }

// handson_room.hpp

    // [b] RoomにJoinします。
    bool JoinRoom(const std::string& room_name);
// handson_room.cpp

// [B] RoomにJoinします。
bool HandsOnRoom::JoinRoom(const std::string& room_name) {
    skyway::room::interface::RoomInitOptions room_init;
    room_init.name = room_name;

    // P2PRoomを検索/作成します。
    // 既存のRoomから同じ名前のRoomを検索もしくはRoomがなければその名前のRoomを作成します。
    p2proom_       = skyway::room::p2p::P2PRoom::FindOrCreate(room_init);
    if (!p2proom_) {
        std::cerr << "- [Error] room failed." << std::endl;
        return false;
    }

    // P2PRoomにイベントリスナ(skyway::room::interface::Room::EventListenerの実装)を登録します。
    p2proom_->AddEventListener(this);

    // P2PRoomの情報を出力します。
    std::cout << "# Room" << std::endl;
    if (p2proom_->Name()) {
        std::cout << "- Name: " << p2proom_->Name().get() << std::endl;
    }
    std::cout << "- Id: " << p2proom_->Id() << std::endl;

    // P2PRoomに既にいるメンバーの一覧を表示します。
    std::cout << "- Room Members" << std::endl;
    for (auto& members : p2proom_->Members()) {
        std::cout << "  - Id: " << members->Id() << std::endl;
    }

    // P2PRoomにメンバーをJoinさせます。
    skyway::room::interface::RoomMemberInitOptions room_options;
    room_member_ = p2proom_->Join(room_options);
    if (!room_member_) {
        std::cerr << "- [Error] p2proom join failed." << std::endl;
        return false;
    }
    std::cout << "- LocalRoomMember Joined" << std::endl;
    std::cout << "  - Id: " << room_member_->Id() << std::endl;

    return true;
}
// handson_room.hpp
// イベントリスナーはこのクラスに実装します。
// `Room::EventListener`ではRoomで発生したイベントを受け取ることができます。
// 誰かが入室した、誰かがPublishしたなどです。
class HandsOnRoom : public skyway::room::interface::Room::EventListener {

ここまで実装できたら、SkyWay Explorerを使ってRoomを用意します。
3ページ目と同様にSkyWay ExplorerのRoomタブから「CREATE - SkyWayRoom.FindOrCreate()」を実行し、Roomを作成/検索します。
今回は仮にtest_roomという名前で作成します。
作成できたらLinuxアプリ側でそのRoomにJoinします。

# 操作例
cmake -B build
cd build
make -j
./app.out test_room
実行時の表示
# Room
- Name: test_room
- Id: xxx
- Room Members
- LocalRoomMember Joined
  - Id: xxx
- Press ENTER key to close...

アプリを実行して、SkyWay ExplorerのRemoteRoomMemberタブでMemberが増えていれば成功です。

カメラ映像を送信する

カメラ映像をPublishできるようにします。
カメラを接続後、以下のコマンドでカメラデバイスが認識されているか確認してください。

# 操作例
v4l2-ctl --list-devices

確認ができたら、以下のコードを追記してカメラをPublishできるようにします。

// main.cpp

    // [4] カメラ映像をPublishします。
    handson_room.PublishCamera();
// handson_room.hpp

    // [c] カメラ映像をPublishします。
    void PublishCamera();
// handson_room.cpp

// [C] カメラデバイスの一覧を取得し、選択したデバイスの映像をPublishします。
void HandsOnRoom::PublishCamera() {
    auto video_devices = skyway::media::DeviceManager::GetVideoDevices();
    if (video_devices.size() > 0) {
        std::cout << "- VideoDevices" << std::endl;
        for (auto device : video_devices) {
            std::cout << "  - Index: " << device.index << " Name: " << device.name << std::endl;
        }

        // デバイスのindex番号を入力します。
        int device_index;
        std::cout << "- Enter the index of the video device to be published: ";
        std::cin >> device_index;
        std::cin.ignore();
        if (device_index >= 0 && device_index < video_devices.size()) {
            auto video_stream =
                skyway::media::StreamFactory::CreateVideoStream(video_devices[device_index]);
            skyway::room::interface::LocalRoomMember::PublicationOptions publication_options {};
            auto publication = room_member_->Publish(video_stream, publication_options);
            if (publication) {
                std::cout << "  - VideoStream Published" << std::endl;
                std::cout << "    - Publication Id: " << publication->Id() << std::endl;
            }
        } else {
            std::cout << "  - Out of range" << std::endl;
        }
    }
}

アプリを実行すると以下のような内容が表示され、Index番号を入力することになります。

./app.out test_room
# Room
- Name: test_room
- Id: xxx
- Room Members
- LocalRoomMember Joined
  - Id: xxx
- DataStream Subscribed
  - Publication Id: xxx
  - Subscription Id: xxx
- VideoDevices
  - Index: 0 Name: HD Webcam
  - Index: 1 Name: bcm2835-isp
  - Index: 2 Name: bcm2835-isp
  - Index: 3 Name: bcm2835-isp
  - Index: 4 Name: bcm2835-isp
- Enter the index of the video device to be published: 

多くの場合、接続したカメラのIndexは0番になっています。
確認したら0と入力してEnterを押してください。
するとカメラ映像が配信され、SkyWay ExplorerのRoomSubscriptionタブからその内容を確認できるようになります。

Dataを受信する

カメラ映像の確認ができたら、いよいよLEDの制御をしていきます。 カメラをLEDに向けたら以下のコードを書いていきます。

// main.cpp

    // [5] DataStreamをSubscribe
    handson_room.SubscribeAllDataStream();
// handson_room.hpp

    // [d] DataStreamをSubscribe
    bool SubscribeDataStream(std::unique_ptr<skyway::room::interface::RoomPublication> publication);
    void SubscribeAllDataStream();

    // [e] 指定のPublicationをSubscribeしているかチェック
    bool IsSubscribed(skyway::room::interface::RoomPublication* publication);

// handson_room.cpp

// [D] 他のMemberがPublishしたDataStreamをSubscribeします。
bool HandsOnRoom::SubscribeDataStream(std::unique_ptr<skyway::room::interface::RoomPublication> publication) {
    if (room_member_->Id() == publication->Publisher()->Id()) {
        // 自身がPublishしたPublicationはSubscribeできないので無視します。
        return false;
    }
    if (this->IsSubscribed(publication.get())) {
        // 既にSubscribeしている場合は無視します。
        return false;
    }

    // DataStreamをSubscribeします。
    if (publication->ContentType() == skyway::model::ContentType::kData) {
        skyway::room::interface::LocalRoomMember::SubscriptionOptions subscription_options {};
        auto subscription = room_member_->Subscribe(publication->Id(), subscription_options);
        if (!subscription) {
            return false;
        }
        auto data_stream =
            std::dynamic_pointer_cast<skyway::core::stream::remote::RemoteDataStream>(
                subscription->Stream());
        // P2PRoomにイベントリスナ(skyway::core::stream::remote::RemoteDataStream::Listenerの実装)を登録します。
        data_stream->AddListener(this);
        std::cout << "- DataStream Subscribed" << std::endl;
        std::cout << "  - Publication Id: " << publication->Id() << std::endl;
        std::cout << "  - Subscription Id: " << subscription->Id() << std::endl;
    }
    return true;
}

void HandsOnRoom::SubscribeAllDataStream() {
    for (auto& publication : p2proom_->Publications()) {
        this->SubscribeDataStream(std::move(publication));
    }
}

// [E] 指定のPublicationをSubscribeしているかチェックします。
bool HandsOnRoom::IsSubscribed(skyway::room::interface::RoomPublication* publication) {
    auto subscriptions = publication->Subscriptions();
    auto find =
        std::find_if(subscriptions.begin(),
                     subscriptions.end(),
                     [&](std::unique_ptr<skyway::room::interface::RoomSubscription>& subscription) {
                         return subscription->Subscriber()->Id() == room_member_->Id();
                     });
    return find != subscriptions.end();
}

DataStreamをSubscribeすると受信したDataをイベントリスナーを使用して取得できるようになります。
Dataを受信するためにHandsOnRoomをRemoteDataStream::Listenerの継承クラスにします。
以下のコードにイベントリスナーを追加します。

// handson_room.hpp
// イベントリスナーはこのクラスに実装します。
// `Room::EventListener`ではRoomで発生したイベントを受け取ることができます。
// 誰かが入室した、誰かがPublishしたなどです。
//
// `RemoteDataStream::Listener`では、データの受信に成功すると発火するイベントがあります。
class HandsOnRoom : public skyway::room::interface::Room::EventListener,
                    public skyway::core::stream::remote::RemoteDataStream::Listener {

Roomのイベントリスナの実装も追加しましょう。
Roomのイベントリスナには、他のMemberがPublishするたびに発火するイベントがあります。
これを使ってPublicationが作成されるたびに自動でSubscribeするようにします。

// handson_room.hpp

    // [f] Roomイベントのリスナ実装です。
    // Room内の誰かがPublishするとこのイベントが発火します。
    void OnStreamPublished(
        std::unique_ptr<skyway::room::interface::RoomPublication> publication) override;

    // [g] DataStreamのイベントのリスナ実装です。
    // 文字列を受信すると発火するイベントです。
    void OnData(const std::string& data) override;
    // バイナリデータを受信すると発火するイベントです。
    // 今回は使用しません。
    void OnDataBuffer(const uint8_t* data, size_t length) override;
// handson_room.cpp

// [F] Roomイベントのリスナ実装です。
// Room内の誰かがPublishするとこのイベントが発火します。
void HandsOnRoom::OnStreamPublished(
    std::unique_ptr<skyway::room::interface::RoomPublication> publication) {
    if (is_notify_) {
        std::cout << "<!-- [Event] StreamPublished: Id " << publication->Id() << "-->" << std::endl;
    }

    // イベントリスナーからSkyWayの操作を行う場合は別スレッドで実行する必要があります。
    auto subscribe_thread = std::make_unique<std::thread>(
        [this, threads_publication = std::move(publication)]() mutable {
            this->SubscribeDataStream(std::move(threads_publication));
        });
    threads_.emplace_back(std::move(subscribe_thread));
}

// [G] DataStreamのイベントのリスナ実装
// 文字列を受信すると発火するイベントです。
void HandsOnRoom::OnData(const std::string& data) {
    // 送られてくるDataの内容に合わせてGPIOの出力を変更します。
    if(data == "HIGH"){
        if (is_notify_) {
            std::cout << "<!-- [Event] Change GPIO to HIGH-->" << std::endl;
        }
        gpiod_line_set_value(gpio_line_out_, 1);
    }
    else if(data == "LOW"){
        if (is_notify_) {
            std::cout << "<!-- [Event] Change GPIO to LOW-->" << std::endl;
        }
        gpiod_line_set_value(gpio_line_out_, 0);
    }
    else{
        if (is_notify_) {
            std::cout << "<!-- [Event] Unknown Command: [" << data << "]-->" << std::endl;
        }
    }
}
// バイナリデータを受信すると発火するイベントです。
// 今回は使用しません。
void HandsOnRoom::OnDataBuffer(const uint8_t* data, size_t length) {};

HandsOnRoom::OnDataでは、ブラウザ側から送られてきたテキストデータを使ってLEDの制御を行います。
コードにあるように、送信データがHIGHにすればLEDを点灯、LOWにすればLEDを消灯します。
文字列は大文字と小文字を判別します。

ここまで作成したところで、アプリを実行します。 アプリでカメラ映像をPublishすると、DataStreamがPublishされるまで待機します。
SkyWay ExplorerでDataStreamをPublishすると、アプリがそのDataStreamをSubscribeします。
DataStreamでHIGHと送信すると、LEDが点灯することを確認します。

退室およびリソースの破棄

最低限の機能は作れましたが、このアプリには問題があります。
現状、アプリでは退出処理を行っていないため、終了してもMemberがRoomに一定時間残り続けてしまいます。
残らないようにするためにもRoomから退出する処理が必要です。
また、終了時にアプリが持つSkyWayのリソースやGPIOのリソースも破棄することも必要です。
これまでのアプリではアプリの終了時に破棄が出来ていないことに起因する何らかのエラーなどが発生することがありました。

// main.cpp

    // [6] Roomから退出します。
    handson_room.LeaveRoom();

    // [7] リソースを破棄します。
    handson_room.Dispose();
// handson_room.hpp
    // [h] Roomから退出します。
    bool LeaveRoom();

    // [i] リソースを破棄します。
    void Dispose();
// handson_room.cpp

// [H] Roomから退出します。
bool HandsOnRoom::LeaveRoom() {
    if (!room_member_) {
        return false;
    }
    is_leaving_ = true;
    // P2PRoomに紐づくイベントリスナの登録を解除します。
    if (p2proom_) {
        p2proom_->RemoveEventListener(this);
    }
    // 各threadの終了を待ちます。
    for (auto& th : threads_) {
        if (th && th->joinable()) {
            th->join();
        }
    }
    is_leaving_ = false;
    return room_member_->Leave();
}

// [I] リソースを破棄します。
void HandsOnRoom::Dispose() { 
    skyway::Context::Dispose(); 
    gpiod_chip_close(gpio_chip_);
}

アプリを実行、終了処理を行うとSkyWay ExplorerのRemoteRoomMemberタブにあるアプリ側のMemberがすぐに消えたことが確認できます。 ここまで実装したところでアプリの実装は完了です。


ここまででLEDを点滅させることができたでしょうか?
最後にまとめがありますので、ご覧ください。


いいなと思ったら応援しよう!