[Fabric-Sync] Add the IPC support between Fabric_Admin and Fabric_Bridge (#33603)
* Add the RPC support between Fabric_Admin and Fabric_Bridge
* Add bridge_enable_pw_rpc build flag
* Address review comments
* Add RpcClientProcessor
* Update API comments
diff --git a/examples/common/pigweed/BUILD.gn b/examples/common/pigweed/BUILD.gn
index c0178e4..e523bea 100644
--- a/examples/common/pigweed/BUILD.gn
+++ b/examples/common/pigweed/BUILD.gn
@@ -80,6 +80,20 @@
prefix = "button_service"
}
+pw_proto_library("fabric_admin_service") {
+ sources = [ "protos/fabric_admin_service.proto" ]
+ deps = [ "$dir_pw_protobuf:common_protos" ]
+ strip_prefix = "protos"
+ prefix = "fabric_admin_service"
+}
+
+pw_proto_library("fabric_bridge_service") {
+ sources = [ "protos/fabric_bridge_service.proto" ]
+ deps = [ "$dir_pw_protobuf:common_protos" ]
+ strip_prefix = "protos"
+ prefix = "fabric_bridge_service"
+}
+
pw_proto_library("lighting_service") {
sources = [ "protos/lighting_service.proto" ]
deps = [ "$dir_pw_protobuf:common_protos" ]
diff --git a/examples/common/pigweed/protos/fabric_admin_service.proto b/examples/common/pigweed/protos/fabric_admin_service.proto
new file mode 100644
index 0000000..e52fd29
--- /dev/null
+++ b/examples/common/pigweed/protos/fabric_admin_service.proto
@@ -0,0 +1,20 @@
+syntax = "proto3";
+
+import 'pw_protobuf_protos/common.proto';
+
+package chip.rpc;
+
+// Define the message for a synchronized end device with necessary fields
+message DeviceInfo {
+ uint64 node_id = 1;
+}
+
+// Define the response message to convey the status of the operation
+message OperationStatus {
+ bool success = 1;
+}
+
+service FabricAdmin {
+ rpc OpenCommissioningWindow(DeviceInfo) returns (OperationStatus){}
+}
+
diff --git a/examples/common/pigweed/protos/fabric_bridge_service.proto b/examples/common/pigweed/protos/fabric_bridge_service.proto
new file mode 100644
index 0000000..5bd4f8e
--- /dev/null
+++ b/examples/common/pigweed/protos/fabric_bridge_service.proto
@@ -0,0 +1,15 @@
+syntax = "proto3";
+
+import 'pw_protobuf_protos/common.proto';
+
+package chip.rpc;
+
+// Define the message for a synchronized end device with necessary fields
+message SynchronizedDevice {
+ uint64 node_id = 1;
+}
+
+service FabricBridge {
+ rpc AddSynchronizedDevice(SynchronizedDevice) returns (pw.protobuf.Empty){}
+}
+
diff --git a/examples/common/pigweed/rpc_console/py/BUILD.gn b/examples/common/pigweed/rpc_console/py/BUILD.gn
index a03dc98..db9f22f 100644
--- a/examples/common/pigweed/rpc_console/py/BUILD.gn
+++ b/examples/common/pigweed/rpc_console/py/BUILD.gn
@@ -46,6 +46,8 @@
"${chip_root}/examples/common/pigweed:descriptor_service.python",
"${chip_root}/examples/common/pigweed:device_service.python",
"${chip_root}/examples/common/pigweed:echo_service.python",
+ "${chip_root}/examples/common/pigweed:fabric_admin_service.python",
+ "${chip_root}/examples/common/pigweed:fabric_bridge_service.python",
"${chip_root}/examples/common/pigweed:lighting_service.python",
"${chip_root}/examples/common/pigweed:locking_service.python",
"${chip_root}/examples/common/pigweed:ot_cli_service.python",
diff --git a/examples/common/pigweed/rpc_console/py/chip_rpc/console.py b/examples/common/pigweed/rpc_console/py/chip_rpc/console.py
index 1591722..50f0b03 100644
--- a/examples/common/pigweed/rpc_console/py/chip_rpc/console.py
+++ b/examples/common/pigweed/rpc_console/py/chip_rpc/console.py
@@ -53,6 +53,8 @@
from descriptor_service import descriptor_service_pb2
from device_service import device_service_pb2
from echo_service import echo_pb2
+from fabric_admin_service import fabric_admin_service_pb2
+from fabric_bridge_service import fabric_bridge_service_pb2
from lighting_service import lighting_service_pb2
from locking_service import locking_service_pb2
from ot_cli_service import ot_cli_service_pb2
@@ -136,6 +138,8 @@
descriptor_service_pb2,
device_service_pb2,
echo_pb2,
+ fabric_admin_service_pb2,
+ fabric_bridge_service_pb2,
lighting_service_pb2,
locking_service_pb2,
ot_cli_service_pb2,
diff --git a/examples/common/pigweed/rpc_services/FabricAdmin.h b/examples/common/pigweed/rpc_services/FabricAdmin.h
new file mode 100644
index 0000000..5254b9e
--- /dev/null
+++ b/examples/common/pigweed/rpc_services/FabricAdmin.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "app/util/attribute-storage.h"
+#include "fabric_admin_service/fabric_admin_service.rpc.pb.h"
+#include "pigweed/rpc_services/internal/StatusUtils.h"
+#include <app-common/zap-generated/attributes/Accessors.h>
+#include <app-common/zap-generated/ids/Attributes.h>
+#include <app-common/zap-generated/ids/Clusters.h>
+#include <platform/PlatformManager.h>
+
+namespace chip {
+namespace rpc {
+
+class FabricAdmin : public pw_rpc::nanopb::FabricAdmin::Service<FabricAdmin>
+{
+public:
+ virtual ~FabricAdmin() = default;
+
+ virtual pw::Status OpenCommissioningWindow(const chip_rpc_DeviceInfo & request, chip_rpc_OperationStatus & response)
+ {
+ return pw::Status::Unimplemented();
+ }
+};
+
+} // namespace rpc
+} // namespace chip
diff --git a/examples/common/pigweed/rpc_services/FabricBridge.h b/examples/common/pigweed/rpc_services/FabricBridge.h
new file mode 100644
index 0000000..bce32eb
--- /dev/null
+++ b/examples/common/pigweed/rpc_services/FabricBridge.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "app/util/attribute-storage.h"
+#include "fabric_bridge_service/fabric_bridge_service.rpc.pb.h"
+#include "pigweed/rpc_services/internal/StatusUtils.h"
+#include <app-common/zap-generated/attributes/Accessors.h>
+#include <app-common/zap-generated/ids/Attributes.h>
+#include <app-common/zap-generated/ids/Clusters.h>
+#include <platform/PlatformManager.h>
+
+namespace chip {
+namespace rpc {
+
+class FabricBridge : public pw_rpc::nanopb::FabricBridge::Service<FabricBridge>
+{
+public:
+ virtual ~FabricBridge() = default;
+
+ virtual pw::Status AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & request, pw_protobuf_Empty & response)
+ {
+ return pw::Status::Unimplemented();
+ }
+};
+
+} // namespace rpc
+} // namespace chip
diff --git a/examples/fabric-admin/BUILD.gn b/examples/fabric-admin/BUILD.gn
index ddaa334..ad7eb21 100644
--- a/examples/fabric-admin/BUILD.gn
+++ b/examples/fabric-admin/BUILD.gn
@@ -22,10 +22,18 @@
assert(chip_build_tools)
+import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni")
+
+if (chip_enable_pw_rpc) {
+ import("//build_overrides/pigweed.gni")
+ import("$dir_pw_build/target_types.gni")
+}
+
config("config") {
include_dirs = [
".",
"${chip_root}/examples/common",
+ "${chip_root}/examples/platform/linux",
"${chip_root}/zzz_generated/app-common/app-common",
"${chip_root}/zzz_generated/chip-tool",
"${chip_root}/src/lib",
@@ -39,6 +47,10 @@
}
cflags = [ "-Wconversion" ]
+
+ if (chip_enable_pw_rpc) {
+ defines += [ "PW_RPC_ENABLED" ]
+ }
}
static_library("fabric-admin-utils") {
@@ -59,6 +71,7 @@
"commands/common/HexConversion.h",
"commands/common/RemoteDataModelLogger.cpp",
"commands/common/RemoteDataModelLogger.h",
+ "commands/fabric-sync/FabricSyncCommand.cpp",
"commands/pairing/OpenCommissioningWindowCommand.cpp",
"commands/pairing/OpenCommissioningWindowCommand.h",
"commands/pairing/PairingCommand.cpp",
@@ -95,6 +108,40 @@
public_configs = [ ":config" ]
+ if (chip_enable_pw_rpc) {
+ defines = [
+ "PW_RPC_FABRIC_ADMIN_SERVICE=1",
+ "PW_RPC_FABRIC_BRIDGE_SERVICE=1",
+ ]
+
+ sources += [
+ "${chip_root}/examples/platform/linux/RpcClientProcessor.cpp",
+ "${chip_root}/examples/platform/linux/RpcClientProcessor.h",
+ "${chip_root}/examples/platform/linux/system_rpc_server.cc",
+ "rpc/RpcClient.cpp",
+ "rpc/RpcClient.h",
+ "rpc/RpcServer.cpp",
+ "rpc/RpcServer.h",
+ ]
+
+ deps += [
+ "$dir_pw_hdlc:default_addresses",
+ "$dir_pw_hdlc:rpc_channel_output",
+ "$dir_pw_log",
+ "$dir_pw_rpc:server",
+ "$dir_pw_rpc/system_server:facade",
+ "$dir_pw_rpc/system_server:socket",
+ "$dir_pw_stream:socket_stream",
+ "$dir_pw_sync:mutex",
+ "${chip_root}/config/linux/lib/pw_rpc:pw_rpc",
+ "${chip_root}/examples/common/pigweed:fabric_admin_service.nanopb_rpc",
+ "${chip_root}/examples/common/pigweed:fabric_bridge_service.nanopb_rpc",
+ "${chip_root}/examples/common/pigweed:rpc_services",
+ ]
+
+ deps += pw_build_LINK_DEPS
+ }
+
if (chip_enable_transport_trace) {
public_deps +=
[ "${chip_root}/examples/common/tracing:trace_handlers_decoder" ]
diff --git a/examples/fabric-admin/commands/fabric-sync/Commands.h b/examples/fabric-admin/commands/fabric-sync/Commands.h
new file mode 100644
index 0000000..f2be577
--- /dev/null
+++ b/examples/fabric-admin/commands/fabric-sync/Commands.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <commands/common/Commands.h>
+#include <commands/fabric-sync/FabricSyncCommand.h>
+
+void registerCommandsFabricSync(Commands & commands, CredentialIssuerCommands * credsIssuerConfig)
+{
+ const char * clusterName = "FabricSync";
+
+ commands_list clusterCommands = {
+ make_unique<FabricSyncAddDeviceCommand>(credsIssuerConfig),
+ };
+
+ commands.RegisterCommandSet(clusterName, clusterCommands, "Commands for fabric synchronization.");
+}
diff --git a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp
new file mode 100644
index 0000000..c2a1b5d
--- /dev/null
+++ b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "FabricSyncCommand.h"
+#include <commands/common/RemoteDataModelLogger.h>
+#include <thread>
+#include <unistd.h>
+
+#if defined(PW_RPC_ENABLED)
+#include "pw_assert/check.h"
+#include "pw_hdlc/decoder.h"
+#include "pw_hdlc/default_addresses.h"
+#include "pw_hdlc/rpc_channel.h"
+#include "pw_rpc/client.h"
+#include "pw_stream/socket_stream.h"
+
+#include <rpc/RpcClient.h>
+#endif
+
+using namespace ::chip;
+
+CHIP_ERROR FabricSyncAddDeviceCommand::RunCommand(NodeId remoteId)
+{
+#if defined(PW_RPC_ENABLED)
+ AddSynchronizedDevice(remoteId);
+ return CHIP_NO_ERROR;
+#else
+ return CHIP_ERROR_NOT_IMPLEMENTED;
+#endif
+}
diff --git a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h
new file mode 100644
index 0000000..cf739cc
--- /dev/null
+++ b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#pragma once
+
+#include <commands/common/CHIPCommand.h>
+
+class FabricSyncAddDeviceCommand : public CHIPCommand
+{
+public:
+ FabricSyncAddDeviceCommand(CredentialIssuerCommands * credIssuerCommands) : CHIPCommand("add-device", credIssuerCommands)
+ {
+ AddArgument("nodeid", 0, UINT64_MAX, &mNodeId);
+ }
+
+ /////////// CHIPCommand Interface /////////
+ CHIP_ERROR RunCommand() override { return RunCommand(mNodeId); }
+
+ chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(1); }
+
+private:
+ chip::NodeId mNodeId;
+
+ CHIP_ERROR RunCommand(NodeId remoteId);
+};
diff --git a/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp b/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp
index aaf3c36..c5c8442 100644
--- a/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp
+++ b/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp
@@ -28,11 +28,18 @@
#include <string>
#include <vector>
+#if defined(PW_RPC_ENABLED)
+#include <rpc/RpcClient.h>
+#endif
+
+using namespace chip;
+
+namespace {
+
constexpr char kInteractiveModePrompt[] = ">>> ";
constexpr char kInteractiveModeHistoryFileName[] = "chip_tool_history";
constexpr char kInteractiveModeStopCommand[] = "quit()";
-
-namespace {
+constexpr uint16_t kRetryIntervalS = 5;
// File pointer for the log file
FILE * sLogFile = nullptr;
@@ -67,7 +74,7 @@
return;
}
- uint64_t timeMs = chip::System::SystemClock().GetMonotonicMilliseconds64().count();
+ uint64_t timeMs = System::SystemClock().GetMonotonicMilliseconds64().count();
uint64_t seconds = timeMs / 1000;
uint64_t milliseconds = timeMs % 1000;
@@ -82,6 +89,26 @@
funlockfile(sLogFile);
}
+#if defined(PW_RPC_ENABLED)
+void AttemptRpcClientConnect(System::Layer * systemLayer, void * appState)
+{
+ if (InitRpcClient(kFabricBridgeServerPort) == CHIP_NO_ERROR)
+ {
+ ChipLogProgress(NotSpecified, "Connected to Fabric-Bridge");
+ }
+ else
+ {
+ ChipLogError(NotSpecified, "Failed to connect to Fabric-Bridge, retry in %d seconds....", kRetryIntervalS);
+ systemLayer->StartTimer(System::Clock::Seconds16(kRetryIntervalS), AttemptRpcClientConnect, nullptr);
+ }
+}
+
+void ExecuteDeferredConnect(intptr_t ignored)
+{
+ AttemptRpcClientConnect(&DeviceLayer::SystemLayer(), nullptr);
+}
+#endif
+
} // namespace
char * InteractiveStartCommand::GetCommand(char * command)
@@ -134,9 +161,13 @@
OpenLogFile(mLogFilePath.Value());
// Redirect logs to the custom logging callback
- chip::Logging::SetLogRedirectCallback(LoggingCallback);
+ Logging::SetLogRedirectCallback(LoggingCallback);
}
+#if defined(PW_RPC_ENABLED)
+ DeviceLayer::PlatformMgr().ScheduleWork(ExecuteDeferredConnect, 0);
+#endif
+
char * command = nullptr;
int status;
while (true)
@@ -167,7 +198,7 @@
// If scheduling the cleanup fails, there is not much we can do.
// But if something went wrong while the application is leaving it could be because things have
// not been cleaned up properly, so it is still useful to log the failure.
- LogErrorOnFailure(chip::DeviceLayer::PlatformMgr().ScheduleWork(ExecuteDeferredCleanups, 0));
+ LogErrorOnFailure(DeviceLayer::PlatformMgr().ScheduleWork(ExecuteDeferredCleanups, 0));
return false;
}
diff --git a/examples/fabric-admin/main.cpp b/examples/fabric-admin/main.cpp
index a1002d8..f5f98cc 100644
--- a/examples/fabric-admin/main.cpp
+++ b/examples/fabric-admin/main.cpp
@@ -18,6 +18,7 @@
#include <commands/clusters/SubscriptionsCommands.h>
#include <commands/common/Commands.h>
+#include <commands/fabric-sync/Commands.h>
#include <commands/interactive/Commands.h>
#include <commands/pairing/Commands.h>
#include <zap-generated/cluster/Commands.h>
@@ -26,6 +27,20 @@
#include <string>
#include <vector>
+#if defined(PW_RPC_ENABLED)
+#include <rpc/RpcServer.h>
+#endif
+
+using namespace chip;
+
+void ApplicationInit()
+{
+#if defined(PW_RPC_ENABLED)
+ InitRpcServer(kFabricAdminServerPort);
+ ChipLogProgress(NotSpecified, "PW_RPC initialized.");
+#endif
+}
+
// ================================================================================
// Main Code
// ================================================================================
@@ -45,6 +60,7 @@
ExampleCredentialIssuerCommands credIssuerCommands;
Commands commands;
+ registerCommandsFabricSync(commands, &credIssuerCommands);
registerCommandsInteractive(commands, &credIssuerCommands);
registerCommandsPairing(commands, &credIssuerCommands);
registerClusters(commands, &credIssuerCommands);
@@ -56,5 +72,7 @@
c_args.push_back(const_cast<char *>(arg.c_str()));
}
+ ApplicationInit();
+
return commands.Run(static_cast<int>(c_args.size()), c_args.data());
}
diff --git a/examples/fabric-admin/rpc/RpcClient.cpp b/examples/fabric-admin/rpc/RpcClient.cpp
new file mode 100644
index 0000000..a1a34d3
--- /dev/null
+++ b/examples/fabric-admin/rpc/RpcClient.cpp
@@ -0,0 +1,89 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RpcClient.h"
+#include "RpcClientProcessor.h"
+
+#include <string>
+#include <thread>
+#include <unistd.h>
+
+#include "fabric_bridge_service/fabric_bridge_service.rpc.pb.h"
+#include "pw_assert/check.h"
+#include "pw_function/function.h"
+#include "pw_hdlc/decoder.h"
+#include "pw_hdlc/default_addresses.h"
+#include "pw_hdlc/rpc_channel.h"
+#include "pw_rpc/client.h"
+#include "pw_stream/socket_stream.h"
+
+using namespace chip;
+
+namespace {
+
+// Constants
+constexpr uint32_t kDefaultChannelId = 1;
+
+// Fabric Bridge Client
+rpc::pw_rpc::nanopb::FabricBridge::Client fabricBridgeClient(rpc::client::GetDefaultRpcClient(), kDefaultChannelId);
+pw::rpc::NanopbUnaryReceiver<::pw_protobuf_Empty> addSynchronizedDeviceCall;
+
+// Callback function to be called when the RPC response is received
+void OnAddDeviceResponseCompleted(const pw_protobuf_Empty & response, pw::Status status)
+{
+ if (status.ok())
+ {
+ ChipLogProgress(NotSpecified, "AddSynchronizedDevice RPC call succeeded!");
+ }
+ else
+ {
+ ChipLogProgress(NotSpecified, "AddSynchronizedDevice RPC call failed with status: %d\n", status.code());
+ }
+}
+
+} // namespace
+
+CHIP_ERROR InitRpcClient(uint16_t rpcServerPort)
+{
+ rpc::client::SetRpcServerPort(rpcServerPort);
+ return rpc::client::StartPacketProcessing();
+}
+
+CHIP_ERROR AddSynchronizedDevice(chip::NodeId nodeId)
+{
+ ChipLogProgress(NotSpecified, "AddSynchronizedDevice");
+
+ if (addSynchronizedDeviceCall.active())
+ {
+ ChipLogError(NotSpecified, "OpenCommissioningWindow is in progress\n");
+ return CHIP_ERROR_BUSY;
+ }
+
+ chip_rpc_SynchronizedDevice device;
+ device.node_id = nodeId;
+
+ // The RPC will remain active as long as `addSynchronizedDeviceCall` is alive.
+ addSynchronizedDeviceCall = fabricBridgeClient.AddSynchronizedDevice(device, OnAddDeviceResponseCompleted);
+
+ if (!addSynchronizedDeviceCall.active())
+ {
+ return CHIP_ERROR_INTERNAL;
+ }
+
+ return CHIP_NO_ERROR;
+}
diff --git a/examples/fabric-admin/rpc/RpcClient.h b/examples/fabric-admin/rpc/RpcClient.h
new file mode 100644
index 0000000..efe3c24
--- /dev/null
+++ b/examples/fabric-admin/rpc/RpcClient.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <platform/CHIPDeviceLayer.h>
+
+constexpr uint16_t kFabricBridgeServerPort = 33002;
+
+/**
+ * @brief Initializes the RPC client with the specified server port.
+ *
+ * This function sets the RPC server port and starts packet processing for the RPC client.
+ *
+ * @param rpcServerPort The port number on which the RPC server is running.
+ * @return CHIP_NO_ERROR on successful initialization, or an appropriate CHIP_ERROR on failure.
+ */
+CHIP_ERROR InitRpcClient(uint16_t rpcServerPort);
+
+/**
+ * @brief Adds a synchronized device to the RPC client.
+ *
+ * This function attempts to add a device identified by its `nodeId` to the synchronized device list.
+ * It logs the progress and checks if an `OpenCommissioningWindow` operation is already in progress.
+ * If an operation is in progress, it returns `CHIP_ERROR_BUSY`.
+ *
+ * @param nodeId The Node ID of the device to be added.
+ * @return CHIP_ERROR An error code indicating the success or failure of the operation.
+ * - CHIP_NO_ERROR: The RPC command was successfully sent.
+ * - CHIP_ERROR_BUSY: Another operation is currently in progress.
+ * - CHIP_ERROR_INTERNAL: An internal error occurred while activating the RPC call.
+ */
+CHIP_ERROR AddSynchronizedDevice(chip::NodeId nodeId);
diff --git a/examples/fabric-admin/rpc/RpcServer.cpp b/examples/fabric-admin/rpc/RpcServer.cpp
new file mode 100644
index 0000000..b3cbdce
--- /dev/null
+++ b/examples/fabric-admin/rpc/RpcServer.cpp
@@ -0,0 +1,70 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pw_rpc/server.h"
+#include "pw_rpc_system_server/rpc_server.h"
+#include "pw_rpc_system_server/socket.h"
+
+#include <system/SystemClock.h>
+#include <thread>
+
+#if defined(PW_RPC_FABRIC_ADMIN_SERVICE) && PW_RPC_FABRIC_ADMIN_SERVICE
+#include "pigweed/rpc_services/FabricAdmin.h"
+#endif
+
+namespace {
+
+#if defined(PW_RPC_FABRIC_ADMIN_SERVICE) && PW_RPC_FABRIC_ADMIN_SERVICE
+class FabricAdmin final : public chip::rpc::FabricAdmin
+{
+public:
+ pw::Status OpenCommissioningWindow(const chip_rpc_DeviceInfo & request, chip_rpc_OperationStatus & response) override
+ {
+ chip::NodeId nodeId = request.node_id;
+ ChipLogProgress(NotSpecified, "Received OpenCommissioningWindow request: 0x%lx", nodeId);
+ response.success = false;
+
+ return pw::OkStatus();
+ }
+};
+
+FabricAdmin fabric_admin_service;
+#endif // defined(PW_RPC_FABRIC_ADMIN_SERVICE) && PW_RPC_FABRIC_ADMIN_SERVICE
+
+void RegisterServices(pw::rpc::Server & server)
+{
+#if defined(PW_RPC_FABRIC_ADMIN_SERVICE) && PW_RPC_FABRIC_ADMIN_SERVICE
+ server.RegisterService(fabric_admin_service);
+#endif
+}
+
+} // namespace
+
+void RunRpcService()
+{
+ pw::rpc::system_server::Init();
+ RegisterServices(pw::rpc::system_server::Server());
+ pw::rpc::system_server::Start();
+}
+
+void InitRpcServer(uint16_t rpcServerPort)
+{
+ pw::rpc::system_server::set_socket_port(rpcServerPort);
+ std::thread rpc_service(RunRpcService);
+ rpc_service.detach();
+}
diff --git a/examples/fabric-admin/rpc/RpcServer.h b/examples/fabric-admin/rpc/RpcServer.h
new file mode 100644
index 0000000..bc03bc0
--- /dev/null
+++ b/examples/fabric-admin/rpc/RpcServer.h
@@ -0,0 +1,23 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+constexpr uint16_t kFabricAdminServerPort = 33001;
+
+void InitRpcServer(uint16_t rpcServerPort);
diff --git a/examples/fabric-admin/with_pw_rpc.gni b/examples/fabric-admin/with_pw_rpc.gni
new file mode 100644
index 0000000..abb9ac6
--- /dev/null
+++ b/examples/fabric-admin/with_pw_rpc.gni
@@ -0,0 +1,42 @@
+# Copyright (c) 2024 Project CHIP Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# add this gni as import in your build args to use pigweed in the example
+# 'import("//with_pw_rpc.gni")'
+
+import("//build_overrides/chip.gni")
+
+import("${chip_root}/config/standalone/args.gni")
+
+import("//build_overrides/pigweed.gni")
+
+pw_log_BACKEND = "$dir_pw_log_basic"
+pw_assert_BACKEND = "$dir_pw_assert_log:check_backend"
+pw_sys_io_BACKEND = "$dir_pw_sys_io_stdio"
+pw_trace_BACKEND = "$dir_pw_trace_tokenized"
+pw_unit_test_MAIN = "$dir_pw_unit_test:logging_main"
+pw_rpc_system_server_BACKEND = "${chip_root}/config/linux/lib/pw_rpc:pw_rpc"
+dir_pw_third_party_nanopb = "${chip_root}/third_party/nanopb/repo"
+pw_chrono_SYSTEM_CLOCK_BACKEND = "$dir_pw_chrono_stl:system_clock"
+pw_sync_MUTEX_BACKEND = "$dir_pw_sync_stl:mutex_backend"
+pw_thread_YIELD_BACKEND = "$dir_pw_thread_stl:yield"
+pw_thread_SLEEP_BACKEND = "$dir_pw_thread_stl:sleep"
+
+pw_build_LINK_DEPS = [
+ "$dir_pw_assert:impl",
+ "$dir_pw_log:impl",
+]
+
+chip_enable_pw_rpc = true
+chip_use_pw_logging = true
diff --git a/examples/fabric-bridge-app/linux/BUILD.gn b/examples/fabric-bridge-app/linux/BUILD.gn
index ea7e6e0..3e82f04 100644
--- a/examples/fabric-bridge-app/linux/BUILD.gn
+++ b/examples/fabric-bridge-app/linux/BUILD.gn
@@ -16,8 +16,20 @@
import("${chip_root}/build/chip/tools.gni")
+import("//with_pw_rpc.gni")
+
assert(chip_build_tools)
+declare_args() {
+ bridge_enable_pw_rpc = false
+}
+
+if (bridge_enable_pw_rpc) {
+ import("//build_overrides/pigweed.gni")
+ import("$dir_pw_build/target_types.gni")
+ import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni")
+}
+
executable("fabric-bridge-app") {
sources = [
"${chip_root}/examples/fabric-bridge-app/fabric-bridge-common/include/CHIPProjectAppConfig.h",
@@ -38,6 +50,45 @@
include_dirs = [ "include" ]
+ if (bridge_enable_pw_rpc) {
+ defines = [
+ "PW_RPC_FABRIC_ADMIN_SERVICE=1",
+ "PW_RPC_FABRIC_BRIDGE_SERVICE=1",
+ ]
+
+ sources += [
+ "${chip_root}/examples/platform/linux/RpcClientProcessor.cpp",
+ "${chip_root}/examples/platform/linux/RpcClientProcessor.h",
+ "${chip_root}/examples/platform/linux/system_rpc_server.cc",
+ "RpcClient.cpp",
+ "RpcServer.cpp",
+ "include/RpcClient.h",
+ "include/RpcServer.h",
+ ]
+
+ deps += [
+ "$dir_pw_hdlc:default_addresses",
+ "$dir_pw_hdlc:rpc_channel_output",
+ "$dir_pw_log",
+ "$dir_pw_rpc:server",
+ "$dir_pw_rpc/system_server:facade",
+ "$dir_pw_rpc/system_server:socket",
+ "$dir_pw_stream:socket_stream",
+ "$dir_pw_sync:mutex",
+ "${chip_root}/config/linux/lib/pw_rpc:pw_rpc",
+ "${chip_root}/examples/common/pigweed:fabric_admin_service.nanopb_rpc",
+ "${chip_root}/examples/common/pigweed:fabric_bridge_service.nanopb_rpc",
+ "${chip_root}/examples/common/pigweed:rpc_services",
+ ]
+
+ deps += pw_build_LINK_DEPS
+
+ include_dirs += [
+ "${chip_root}/examples/common",
+ "${chip_root}/examples/platform/linux",
+ ]
+ }
+
output_dir = root_out_dir
}
diff --git a/examples/fabric-bridge-app/linux/RpcClient.cpp b/examples/fabric-bridge-app/linux/RpcClient.cpp
new file mode 100644
index 0000000..c09a447
--- /dev/null
+++ b/examples/fabric-bridge-app/linux/RpcClient.cpp
@@ -0,0 +1,88 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RpcClient.h"
+#include "RpcClientProcessor.h"
+
+#include <string>
+#include <thread>
+#include <unistd.h>
+
+#include "fabric_admin_service/fabric_admin_service.rpc.pb.h"
+#include "pw_assert/check.h"
+#include "pw_hdlc/decoder.h"
+#include "pw_hdlc/default_addresses.h"
+#include "pw_hdlc/rpc_channel.h"
+#include "pw_rpc/client.h"
+#include "pw_stream/socket_stream.h"
+
+using namespace chip;
+
+namespace {
+
+// Constants
+constexpr uint32_t kDefaultChannelId = 1;
+
+// Fabric Admin Client
+rpc::pw_rpc::nanopb::FabricAdmin::Client fabricAdminClient(rpc::client::GetDefaultRpcClient(), kDefaultChannelId);
+pw::rpc::NanopbUnaryReceiver<::chip_rpc_OperationStatus> openCommissioningWindowCall;
+
+// Callback function to be called when the RPC response is received
+void OnOpenCommissioningWindowCompleted(const chip_rpc_OperationStatus & response, pw::Status status)
+{
+ if (status.ok())
+ {
+ ChipLogProgress(NotSpecified, "OpenCommissioningWindow received operation status: %d", response.success);
+ }
+ else
+ {
+ ChipLogProgress(NotSpecified, "OpenCommissioningWindow RPC call failed with status: %d\n", status.code());
+ }
+}
+
+} // namespace
+
+CHIP_ERROR InitRpcClient(uint16_t rpcServerPort)
+{
+ rpc::client::SetRpcServerPort(rpcServerPort);
+ return rpc::client::StartPacketProcessing();
+}
+
+CHIP_ERROR OpenCommissioningWindow(NodeId nodeId)
+{
+ ChipLogProgress(NotSpecified, "OpenCommissioningWindow\n");
+
+ if (openCommissioningWindowCall.active())
+ {
+ ChipLogError(NotSpecified, "OpenCommissioningWindow is in progress\n");
+ return CHIP_ERROR_BUSY;
+ }
+
+ chip_rpc_DeviceInfo device;
+ device.node_id = nodeId;
+
+ // The RPC will remain active as long as `openCommissioningWindowCall` is alive.
+ openCommissioningWindowCall = fabricAdminClient.OpenCommissioningWindow(device, OnOpenCommissioningWindowCompleted);
+
+ if (!openCommissioningWindowCall.active())
+ {
+ return CHIP_ERROR_INTERNAL;
+ }
+
+ return CHIP_NO_ERROR;
+}
diff --git a/examples/fabric-bridge-app/linux/RpcServer.cpp b/examples/fabric-bridge-app/linux/RpcServer.cpp
new file mode 100644
index 0000000..c971811
--- /dev/null
+++ b/examples/fabric-bridge-app/linux/RpcServer.cpp
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pw_rpc/server.h"
+#include "pw_rpc_system_server/rpc_server.h"
+#include "pw_rpc_system_server/socket.h"
+
+#include <system/SystemClock.h>
+#include <thread>
+
+#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
+#include "pigweed/rpc_services/FabricBridge.h"
+#endif
+
+namespace {
+
+#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
+class FabricBridge final : public chip::rpc::FabricBridge
+{
+public:
+ pw::Status AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & request, pw_protobuf_Empty & response) override
+ {
+ chip::NodeId nodeId = request.node_id;
+ ChipLogProgress(NotSpecified, "Received AddSynchronizedDevice: " ChipLogFormatX64, ChipLogValueX64(nodeId));
+ return pw::OkStatus();
+ }
+};
+
+FabricBridge fabric_bridge_service;
+#endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
+
+void RegisterServices(pw::rpc::Server & server)
+{
+#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
+ server.RegisterService(fabric_bridge_service);
+#endif
+}
+
+} // namespace
+
+void RunRpcService()
+{
+ pw::rpc::system_server::Init();
+ RegisterServices(pw::rpc::system_server::Server());
+ pw::rpc::system_server::Start();
+}
+
+void InitRpcServer(uint16_t rpcServerPort)
+{
+ pw::rpc::system_server::set_socket_port(rpcServerPort);
+ std::thread rpc_service(RunRpcService);
+ rpc_service.detach();
+}
diff --git a/examples/fabric-bridge-app/linux/include/RpcClient.h b/examples/fabric-bridge-app/linux/include/RpcClient.h
new file mode 100644
index 0000000..bd424e9
--- /dev/null
+++ b/examples/fabric-bridge-app/linux/include/RpcClient.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <platform/CHIPDeviceLayer.h>
+
+constexpr uint16_t kFabricAdminServerPort = 33001;
+
+/**
+ * Initializes the RPC client by setting the server port and starting packet processing.
+ *
+ * @param rpcServerPort The port number of the RPC server.
+ * @return CHIP_ERROR An error code indicating the success or failure of the initialization process.
+ * - CHIP_NO_ERROR: Initialization was successful.
+ * - Other error codes indicating specific failure reasons.
+ */
+CHIP_ERROR InitRpcClient(uint16_t rpcServerPort);
+
+/**
+ * Opens a commissioning window for a specified node.
+ *
+ * @param nodeId The identifier of the node for which the commissioning window should be opened.
+ * @return CHIP_ERROR An error code indicating the success or failure of the operation.
+ * - CHIP_NO_ERROR: The RPC command was successfully sent.
+ * - CHIP_ERROR_BUSY: Another commissioning window is currently in progress.
+ * - CHIP_ERROR_INTERNAL: An internal error occurred.
+ */
+CHIP_ERROR OpenCommissioningWindow(chip::NodeId nodeId);
diff --git a/examples/fabric-bridge-app/linux/include/RpcServer.h b/examples/fabric-bridge-app/linux/include/RpcServer.h
new file mode 100644
index 0000000..f86858b
--- /dev/null
+++ b/examples/fabric-bridge-app/linux/include/RpcServer.h
@@ -0,0 +1,23 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+constexpr uint16_t kFabricBridgeServerPort = 33002;
+
+void InitRpcServer(uint16_t rpcServerPort);
diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp
index 600d4a2..4767056 100644
--- a/examples/fabric-bridge-app/linux/main.cpp
+++ b/examples/fabric-bridge-app/linux/main.cpp
@@ -25,19 +25,26 @@
#include <app/AttributeAccessInterfaceRegistry.h>
#include <lib/support/ZclString.h>
+#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
+#include "RpcClient.h"
+#include "RpcServer.h"
+#endif
+
#include <string>
#include <sys/ioctl.h>
#include <thread>
using namespace chip;
-#define POLL_INTERVAL_MS (100)
#define ZCL_DESCRIPTOR_CLUSTER_REVISION (1u)
#define ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_REVISION (2u)
#define ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_FEATURE_MAP (0u)
namespace {
+constexpr uint16_t kPollIntervalMs = 100;
+constexpr uint16_t kRetryIntervalS = 3;
+
bool KeyboardHit()
{
int bytesWaiting;
@@ -57,20 +64,50 @@
ChipLogProgress(NotSpecified, "Exiting.....");
exit(0);
}
+#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
+ else if (ch == 'o')
+ {
+ CHIP_ERROR err = OpenCommissioningWindow(0x1234);
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(NotSpecified, "Failed to call OpenCommissioningWindow RPC: %" CHIP_ERROR_FORMAT, err.Format());
+ }
+ }
+#endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
continue;
}
// Sleep to avoid tight loop reading commands
- usleep(POLL_INTERVAL_MS * 1000);
+ usleep(kPollIntervalMs * 1000);
}
}
+#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
+void AttemptRpcClientConnect(System::Layer * systemLayer, void * appState)
+{
+ if (InitRpcClient(kFabricAdminServerPort) == CHIP_NO_ERROR)
+ {
+ ChipLogProgress(NotSpecified, "Connected to Fabric-Admin");
+ }
+ else
+ {
+ ChipLogError(NotSpecified, "Failed to connect to Fabric-Admin, retry in %d seconds....", kRetryIntervalS);
+ systemLayer->StartTimer(System::Clock::Seconds16(kRetryIntervalS), AttemptRpcClientConnect, nullptr);
+ }
+}
+#endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
+
DeviceManager gDeviceManager;
} // namespace
void ApplicationInit()
{
+#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
+ InitRpcServer(kFabricBridgeServerPort);
+ AttemptRpcClientConnect(&DeviceLayer::SystemLayer(), nullptr);
+#endif
+
// Start a thread for bridge polling
std::thread pollingThread(BridgePollingThread);
pollingThread.detach();
diff --git a/examples/fabric-bridge-app/linux/with_pw_rpc.gni b/examples/fabric-bridge-app/linux/with_pw_rpc.gni
new file mode 100644
index 0000000..e1bd567
--- /dev/null
+++ b/examples/fabric-bridge-app/linux/with_pw_rpc.gni
@@ -0,0 +1,42 @@
+# Copyright (c) 2024 Project CHIP Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# add this gni as import in your build args to use pigweed in the example
+# 'import("//with_pw_rpc.gni")'
+
+import("//build_overrides/chip.gni")
+
+import("${chip_root}/config/standalone/args.gni")
+
+import("//build_overrides/pigweed.gni")
+
+pw_log_BACKEND = "$dir_pw_log_basic"
+pw_assert_BACKEND = "$dir_pw_assert_log:check_backend"
+pw_sys_io_BACKEND = "$dir_pw_sys_io_stdio"
+pw_trace_BACKEND = "$dir_pw_trace_tokenized"
+pw_unit_test_MAIN = "$dir_pw_unit_test:logging_main"
+pw_rpc_system_server_BACKEND = "${chip_root}/config/linux/lib/pw_rpc:pw_rpc"
+dir_pw_third_party_nanopb = "${chip_root}/third_party/nanopb/repo"
+pw_chrono_SYSTEM_CLOCK_BACKEND = "$dir_pw_chrono_stl:system_clock"
+pw_sync_MUTEX_BACKEND = "$dir_pw_sync_stl:mutex_backend"
+pw_thread_YIELD_BACKEND = "$dir_pw_thread_stl:yield"
+pw_thread_SLEEP_BACKEND = "$dir_pw_thread_stl:sleep"
+
+pw_build_LINK_DEPS = [
+ "$dir_pw_assert:impl",
+ "$dir_pw_log:impl",
+]
+
+chip_use_pw_logging = true
+bridge_enable_pw_rpc = true
diff --git a/examples/platform/linux/RpcClientProcessor.cpp b/examples/platform/linux/RpcClientProcessor.cpp
new file mode 100644
index 0000000..a2cbe56
--- /dev/null
+++ b/examples/platform/linux/RpcClientProcessor.cpp
@@ -0,0 +1,124 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "RpcClientProcessor.h"
+
+#include <string>
+#include <thread>
+
+#include "pw_hdlc/decoder.h"
+#include "pw_hdlc/default_addresses.h"
+#include "pw_hdlc/rpc_channel.h"
+#include "pw_stream/socket_stream.h"
+
+namespace chip {
+namespace rpc {
+namespace client {
+namespace {
+
+// Constants
+constexpr size_t kMaxTransmissionUnit = 256;
+constexpr uint32_t kDefaultChannelId = 1;
+const char * kDefaultRpcServerAddress = "127.0.0.1";
+
+// RPC Stream and Channel Setup
+pw::stream::SocketStream rpcSocketStream;
+pw::hdlc::RpcChannelOutput hdlcChannelOutput(rpcSocketStream, pw::hdlc::kDefaultRpcAddress, "HDLC channel");
+pw::rpc::Channel channels[] = { pw::rpc::Channel::Create<1>(&hdlcChannelOutput) };
+pw::rpc::Client rpcClient(channels);
+
+// RPC Stream and Channel Setup
+uint16_t rpcServerPort = 0;
+const char * rpcServerAddress = kDefaultRpcServerAddress;
+
+// Function to process incoming packets
+void ProcessPackets()
+{
+ std::array<std::byte, kMaxTransmissionUnit> inputBuf;
+ pw::hdlc::Decoder decoder(inputBuf);
+
+ while (true)
+ {
+ std::array<std::byte, kMaxTransmissionUnit> data;
+ auto ret = rpcSocketStream.Read(data);
+ if (!ret.ok())
+ {
+ if (ret.status() == pw::Status::OutOfRange())
+ {
+ // Handle remote disconnect
+ rpcSocketStream.Close();
+ return;
+ }
+ continue;
+ }
+
+ for (std::byte byte : ret.value())
+ {
+ auto result = decoder.Process(byte);
+ if (!result.ok())
+ {
+ // Wait for more bytes that form a complete packet
+ continue;
+ }
+ pw::hdlc::Frame & frame = result.value();
+ if (frame.address() != pw::hdlc::kDefaultRpcAddress)
+ {
+ // Wrong address; ignore the packet
+ continue;
+ }
+
+ rpcClient.ProcessPacket(frame.data()).IgnoreError();
+ }
+ }
+}
+
+} // namespace
+
+void SetRpcServerAddress(const char * address)
+{
+ rpcServerAddress = address;
+}
+
+void SetRpcServerPort(uint16_t port)
+{
+ rpcServerPort = port;
+}
+
+pw::rpc::Client & GetDefaultRpcClient()
+{
+ return rpcClient;
+}
+
+CHIP_ERROR StartPacketProcessing()
+{
+ if (rpcSocketStream.Connect(rpcServerAddress, rpcServerPort) != PW_STATUS_OK)
+ {
+ // Handle connection error
+ return CHIP_ERROR_NOT_CONNECTED;
+ }
+
+ // Start a thread to process incoming packets
+ std::thread packet_processor(ProcessPackets);
+ packet_processor.detach();
+
+ return CHIP_NO_ERROR;
+}
+
+} // namespace client
+} // namespace rpc
+} // namespace chip
diff --git a/examples/platform/linux/RpcClientProcessor.h b/examples/platform/linux/RpcClientProcessor.h
new file mode 100644
index 0000000..f2305df
--- /dev/null
+++ b/examples/platform/linux/RpcClientProcessor.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright (c) 2024 Project CHIP Authors
+ * All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "pw_rpc/client.h"
+#include <platform/CHIPDeviceLayer.h>
+#include <unistd.h>
+
+namespace chip {
+namespace rpc {
+namespace client {
+
+void SetRpcServerAddress(const char * address);
+void SetRpcServerPort(uint16_t port);
+pw::rpc::Client & GetDefaultRpcClient();
+CHIP_ERROR StartPacketProcessing();
+
+} // namespace client
+} // namespace rpc
+} // namespace chip