Skip to content

Commit 6911fa8

Browse files
author
rgoliver
authored
Add RPC support to linux lighting app (project-chip#5866)
* Cleanup RPCs for lighting app - Break apart lighting service and buttons. - Rename RPCs. - Create a common RPC file for Empty message. Used EFR32 to test, will make changes to other platforms when we have concensus on the style. * Add device_common RPC service A new RPC service which holds common CHIP RPCs. * Initial prototype of linux RPC in lighting app This adds RPCs to the lighting app using a server running over a local socket (port 33000). * Add lighting app rpc python tool. This tool has all the protos for the lighting app built in and can be used instead of pw_hdlc.rpc_console. - Added a gn target to install the python tool into venv lighting app is compiled with rpcs enabled. - Will also bundle a wheel file which can be used to deploy without requiring the source Usage: To start the console, provide a serial port as the --device argument python -m lighting_app.rpc_console --device /dev/ttyUSB0 Alternatively to connect to a linux CHIP device provide the port. python -m lighting_app.rpc_console -s localhost:33000 This starts an IPython console for communicating with the connected device. A few variables are predefined in the interactive console. These include: rpcs - used to invoke RPCs device - the serial device used for communication client - the pw_rpc.Client protos - protocol buffer messages indexed by proto package An example RPC command: rpcs.chip.rpc.DeviceCommon.GetDeviceInfo() * Cleanup: Use ArraySize in Rpc.cc for task * Update lighting app RPC build for NRF
1 parent 3510f14 commit 6911fa8

File tree

29 files changed

+802
-58
lines changed

29 files changed

+802
-58
lines changed

config/linux/lib/pw_rpc/BUILD.gn

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright (c) 2021 Project CHIP Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import("//build_overrides/chip.gni")
16+
import("//build_overrides/pigweed.gni")
17+
import("$dir_pw_build/target_types.gni")
18+
19+
config("cpp17") {
20+
cflags_cc = [ "-std=gnu++17" ]
21+
cflags_c = [ "-std=gnu11" ]
22+
cflags_objc = [ "-std=gnu11" ]
23+
cflags_objcc = [ "-std=gnu++17" ]
24+
}
25+
26+
config("std_cpp17") {
27+
configs = [ ":cpp17" ]
28+
}
29+
30+
static_library("pw_rpc") {
31+
output_name = "libPwRpc"
32+
33+
public_configs = [ "${dir_pigweed}/pw_hdlc:default_config" ]
34+
deps = [
35+
"$dir_pw_hdlc:encoder",
36+
"$dir_pw_hdlc:pw_rpc",
37+
"$dir_pw_hdlc:rpc_channel_output",
38+
"$dir_pw_rpc:synchronized_channel_output",
39+
"$dir_pw_rpc/system_server:facade",
40+
"$dir_pw_stream:socket_stream",
41+
dir_pw_log,
42+
]
43+
output_dir = "${root_out_dir}/lib"
44+
45+
complete_static_lib = true
46+
}

examples/common/pigweed/BUILD.gn

+21
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,32 @@
1515
import("//build_overrides/pigweed.gni")
1616

1717
import("$dir_pw_build/target_types.gni")
18+
import("pigweed_rpcs.gni")
1819

1920
config("default_config") {
2021
include_dirs = [ ".." ]
2122
}
2223

24+
if (chip_enable_pw_rpc) {
25+
import("//build_overrides/pigweed.gni")
26+
import("$dir_pw_protobuf_compiler/proto.gni")
27+
28+
pw_proto_library("device_service") {
29+
sources = [ "protos/device_service.proto" ]
30+
inputs = [ "protos/device_service.options" ]
31+
deps = [ "$dir_pw_protobuf:common_protos" ]
32+
strip_prefix = "protos"
33+
prefix = "device_service"
34+
}
35+
36+
pw_proto_library("button_service") {
37+
sources = [ "protos/button_service.proto" ]
38+
deps = [ "$dir_pw_protobuf:common_protos" ]
39+
strip_prefix = "protos"
40+
prefix = "button_service"
41+
}
42+
}
43+
2344
pw_source_set("system_rpc_server") {
2445
sources = [ "system_rpc_server.cc" ]
2546

examples/lighting-app/lighting-common/lighting.gni examples/common/pigweed/pigweed_rpcs.gni

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414

1515
declare_args() {
1616
# Enable Pigweed RPC integration.
17-
enable_pw_rpc = false
17+
chip_enable_pw_rpc = false
1818
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
syntax = "proto3";
2+
3+
import 'pw_protobuf_protos/common.proto';
4+
5+
package chip.rpc;
6+
7+
message ButtonEvent {
8+
uint32 idx = 1;
9+
bool pushed = 2;
10+
}
11+
12+
service Button {
13+
rpc Event(ButtonEvent) returns (pw.protobuf.Empty){}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
chip.rpc.DeviceInfo.serial_number max_size:32 // length defined in chip spec 8.2.3.1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
syntax = "proto3";
2+
3+
package chip.rpc;
4+
5+
import 'pw_protobuf_protos/common.proto';
6+
7+
// type lengths defined in chip spec 8.2.3.1
8+
message DeviceInfo {
9+
uint32 vendor_id = 1;
10+
uint32 product_id = 2;
11+
uint32 software_version = 3;
12+
string serial_number = 4;
13+
}
14+
15+
service Device {
16+
rpc FactoryReset(pw.protobuf.Empty) returns (pw.protobuf.Empty){}
17+
rpc Reboot(pw.protobuf.Empty) returns (pw.protobuf.Empty){}
18+
rpc TriggerOta(pw.protobuf.Empty) returns (pw.protobuf.Empty){}
19+
rpc GetDeviceInfo(pw.protobuf.Empty) returns (DeviceInfo){}
20+
}

examples/lighting-app/efr32/BUILD.gn

+18-5
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@ import("//build_overrides/efr32_sdk.gni")
1818
import("//build_overrides/pigweed.gni")
1919

2020
import("${build_root}/config/defaults.gni")
21-
import("${chip_root}/examples/lighting-app/lighting-common/lighting.gni")
2221
import("${efr32_sdk_build_root}/efr32_executable.gni")
2322
import("${efr32_sdk_build_root}/efr32_sdk.gni")
2423

24+
import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni")
25+
26+
if (chip_enable_pw_rpc) {
27+
import("//build_overrides/pigweed.gni")
28+
}
29+
2530
assert(current_os == "freertos")
2631

2732
efr32_project_dir = "${chip_root}/examples/lighting-app/efr32"
@@ -64,9 +69,7 @@ efr32_sdk("sdk") {
6469
"SL_HEAP_SIZE=(10 * 1024)",
6570
]
6671

67-
if (enable_pw_rpc) {
68-
sources +=
69-
[ "${efr32_sdk_root}/hardware/kit/common/drivers/retargetserial.c" ]
72+
if (chip_enable_pw_rpc) {
7073
defines += [
7174
"HAL_VCOM_ENABLE=1",
7275
"PW_RPC_ENABLED",
@@ -110,7 +113,8 @@ efr32_executable("lighting_app") {
110113
defines += [ "DISPLAY_ENABLED" ]
111114
}
112115

113-
if (enable_pw_rpc) {
116+
if (chip_enable_pw_rpc) {
117+
defines += [ "PW_RPC_ENABLED" ]
114118
sources += [
115119
"${chip_root}/examples/common/pigweed/RpcService.cpp",
116120
"${chip_root}/examples/common/pigweed/efr32/PigweedLoggerMutex.cpp",
@@ -123,8 +127,11 @@ efr32_executable("lighting_app") {
123127
"$dir_pw_checksum",
124128
"$dir_pw_hdlc:rpc_channel_output",
125129
"$dir_pw_stream",
130+
"$dir_pw_stream:sys_io_stream",
126131
"$dir_pw_sys_io",
127132
"${chip_root}/config/efr32/lib/pw_rpc:pw_rpc",
133+
"${chip_root}/examples/common/pigweed:button_service.nanopb_rpc",
134+
"${chip_root}/examples/common/pigweed:device_service.nanopb_rpc",
128135
"${chip_root}/examples/lighting-app/lighting-common:lighting_service.nanopb_rpc",
129136
"${examples_plat_dir}/pw_sys_io:pw_sys_io_efr32",
130137
]
@@ -160,6 +167,12 @@ efr32_executable("lighting_app") {
160167

161168
group("efr32") {
162169
deps = [ ":lighting_app" ]
170+
if (chip_enable_pw_rpc) {
171+
deps += [
172+
"${chip_root}/examples/lighting-app/lighting-common/py:lighting_app.install",
173+
"${chip_root}/examples/lighting-app/lighting-common/py:lighting_app_wheel",
174+
]
175+
}
163176
}
164177

165178
group("default") {

examples/lighting-app/efr32/README.md

+13-5
Original file line numberDiff line numberDiff line change
@@ -280,16 +280,24 @@ via 2002::2
280280
281281
## Running Pigweed RPC console
282282
283-
- If you build the example with pigweed RPC option you can can interact with
284-
the example by UART using the RPC LightingService. Call the following
285-
command in your terminal
283+
- As part of building the example with RPCs enabled the lighting_app python
284+
interactive console is installed into your venv. The python wheel files are
285+
also created in the output folder: out/debug/lighting_app_wheels. To install
286+
the wheel files without rebuilding:
287+
`pip3 install out/debug/lighting_app_wheels/*.whl`
286288
287-
`python -m pw_hdlc.rpc_console --device /dev/tty.<SERIALDEVICE> -b 115200 /<CHIP_ROOT>/examples/lighting-app/lighting-common/lighting_service/pigweed_lighting.proto -o /<YourFolder>/pw_log.out`
289+
- To use the lighting-app console after it has been installed run:
290+
`python3 -m lighting_app.rpc_console --device /dev/tty.<SERIALDEVICE> -b 115200 -o /<YourFolder>/pw_log.out`
288291
289292
- Then you can simulate a button press or realease using the following command
290293
where : idx = 0 or 1 for Button PB0 or PB1 action = 0 for PRESSED, 1 for
291294
RELEASE Test toggling the LED with
292-
`rpcs.chip.rpc.LightingService.ButtonEvent(idx=1,action=0)`
295+
`rpcs.chip.rpc.Button.Event(idx=1, pushed=True)`
296+
297+
- You can also Get and Set the light directly using the RPCs:
298+
`rpcs.chip.rpc.Lighting.Get()`
299+
300+
`rpcs.chip.rpc.Lighting.Set(on=True)`
293301
294302
## Memory settings
295303

examples/lighting-app/efr32/src/Rpc.cpp

+60-8
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
#include "PigweedLoggerMutex.h"
2323
#include "pigweed/RpcService.h"
2424

25-
#include "lighting_service/pigweed_lighting.rpc.pb.h"
25+
#include "button_service/button_service.rpc.pb.h"
26+
#include "device_service/device_service.rpc.pb.h"
27+
#include "lib/support/CodeUtils.h"
28+
#include "lighting_service/lighting_service.rpc.pb.h"
2629
#include "pw_hdlc/rpc_channel.h"
2730
#include "pw_hdlc/rpc_packets.h"
2831
#include "pw_rpc/server.h"
@@ -33,30 +36,79 @@
3336
namespace chip {
3437
namespace rpc {
3538

36-
class LightingService final : public generated::LightingService<LightingService>
39+
class Lighting final : public generated::Lighting<Lighting>
3740
{
3841
public:
39-
pw::Status ButtonEvent(ServerContext & ctx, const chip_rpc_Button & request, chip_rpc_Empty & response)
42+
pw::Status Set(ServerContext &, const chip_rpc_LightingState & request, pw_protobuf_Empty & response)
4043
{
41-
GetAppTask().ButtonEventHandler(request.idx /* PB 0 or PB 1 */, request.action /* 0 =PRESSED 1= RELEASE */);
44+
LightMgr().InitiateAction(AppEvent::kEventType_Light,
45+
request.on ? LightingManager::ON_ACTION : LightingManager::OFF_ACTION);
46+
return pw::OkStatus();
47+
}
48+
49+
pw::Status Get(ServerContext &, const pw_protobuf_Empty & request, chip_rpc_LightingState & response)
50+
{
51+
response.on = LightMgr().IsLightOn();
52+
return pw::OkStatus();
53+
}
54+
};
55+
56+
class Button final : public generated::Button<Button>
57+
{
58+
public:
59+
pw::Status Event(ServerContext &, const chip_rpc_ButtonEvent & request, pw_protobuf_Empty & response)
60+
{
61+
GetAppTask().ButtonEventHandler(request.idx /* PB 0 or PB 1 */, request.pushed);
62+
return pw::OkStatus();
63+
}
64+
};
65+
66+
class Device final : public generated::Device<Device>
67+
{
68+
public:
69+
pw::Status FactoryReset(ServerContext & ctx, const pw_protobuf_Empty & request, pw_protobuf_Empty & response)
70+
{
71+
// TODO: Clear data from KVS
72+
DeviceLayer::ConfigurationMgr().InitiateFactoryReset();
73+
return pw::OkStatus();
74+
}
75+
pw::Status Reboot(ServerContext & ctx, const pw_protobuf_Empty & request, pw_protobuf_Empty & response)
76+
{
77+
NVIC_SystemReset();
78+
// WILL NOT RETURN
79+
return pw::OkStatus();
80+
}
81+
pw::Status TriggerOta(ServerContext & ctx, const pw_protobuf_Empty & request, pw_protobuf_Empty & response)
82+
{
83+
// TODO: auto err = DeviceLayer::SoftwareUpdateMgr().CheckNow();
84+
return pw::Status::Unimplemented();
85+
}
86+
pw::Status GetDeviceInfo(ServerContext &, const pw_protobuf_Empty & request, chip_rpc_DeviceInfo & response)
87+
{
88+
response.vendor_id = 1234;
89+
response.product_id = 5678;
90+
response.software_version = 0;
4291
return pw::OkStatus();
4392
}
4493
};
4594

4695
namespace {
47-
using std::byte;
4896

4997
#define RPC_TASK_STACK_SIZE 4096
5098
#define RPC_TASK_PRIORITY 1
5199
static TaskHandle_t sRpcTaskHandle;
52100
StaticTask_t sRpcTaskBuffer;
53101
StackType_t sRpcTaskStack[RPC_TASK_STACK_SIZE];
54102

55-
chip::rpc::LightingService lighting_service;
103+
chip::rpc::Button button_service;
104+
chip::rpc::Lighting lighting_service;
105+
chip::rpc::Device device_service;
56106

57107
void RegisterServices(pw::rpc::Server & server)
58108
{
59109
server.RegisterService(lighting_service);
110+
server.RegisterService(button_service);
111+
server.RegisterService(device_service);
60112
}
61113

62114
} // namespace
@@ -72,8 +124,8 @@ int Init()
72124
pw_sys_io_Init();
73125

74126
// Start App task.
75-
sRpcTaskHandle = xTaskCreateStatic(RunRpcService, "RPC_TASK", RPC_TASK_STACK_SIZE / sizeof(StackType_t), nullptr,
76-
RPC_TASK_PRIORITY, sRpcTaskStack, &sRpcTaskBuffer);
127+
sRpcTaskHandle = xTaskCreateStatic(RunRpcService, "RPC_TASK", ArraySize(sRpcTaskStack), nullptr, RPC_TASK_PRIORITY,
128+
sRpcTaskStack, &sRpcTaskBuffer);
77129
return err;
78130
}
79131

examples/lighting-app/efr32/with_pw_rpc.gni

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ import("${chip_root}/examples/platform/efr32/args.gni")
2121

2222
efr32_sdk_target = get_label_info(":sdk", "label_no_toolchain")
2323

24-
enable_pw_rpc = true
24+
chip_enable_pw_rpc = true
2525
chip_enable_openthread = true
2626
default_configs_std = [ "${chip_root}/config/efr32/lib/pw_rpc:std_cpp17" ]

examples/lighting-app/lighting-common/BUILD.gn

+5-5
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@
1313
# limitations under the License.
1414

1515
import("//build_overrides/chip.gni")
16-
17-
import("${chip_root}/examples/lighting-app/lighting-common/lighting.gni")
16+
import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni")
1817
import("${chip_root}/src/app/chip_data_model.gni")
1918

20-
if (enable_pw_rpc) {
19+
if (chip_enable_pw_rpc) {
2120
import("//build_overrides/pigweed.gni")
21+
import("$dir_pw_build/python.gni")
2222
import("$dir_pw_protobuf_compiler/proto.gni")
2323

2424
pw_proto_library("lighting_service") {
25-
sources = [ "lighting_service/pigweed_lighting.proto" ]
26-
inputs = [ "lighting_service/pigweed_lighting.options" ]
25+
sources = [ "lighting_service/lighting_service.proto" ]
26+
deps = [ "$dir_pw_protobuf:common_protos" ]
2727
}
2828
}
2929

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
syntax = "proto3";
2+
3+
import 'pw_protobuf_protos/common.proto';
4+
5+
package chip.rpc;
6+
7+
message LightingBrightness {
8+
// level is between 0 and max_level inclusively.
9+
// The device should support rescaling if provided a max_value which doesn't
10+
// match the platforms value.
11+
uint32 level = 1;
12+
uint32 max_level = 2;
13+
}
14+
15+
message LightingState {
16+
bool on = 1;
17+
optional LightingBrightness brightness = 2;
18+
}
19+
20+
service Lighting {
21+
// Set will return OK if all supported fields are successfully applied, any
22+
// unsupported fields will be ignored.
23+
// Get can be used to determine which fields are supported.
24+
rpc Set(LightingState) returns (pw.protobuf.Empty){}
25+
26+
// Get will populate all of the supported lighting state fields with the
27+
// current values. This can be used to discover the devices supported
28+
// lighting features.
29+
rpc Get(pw.protobuf.Empty) returns (LightingState){}
30+
}

0 commit comments

Comments
 (0)