6.1 KiB
ESP-Brookesia Custom Service
Overview
brookesia_service_custom provides a ready-to-use CustomService for the ESP-Brookesia ecosystem, enabling user-defined function and event registration and invocation without creating a dedicated service component.
Typical Use Cases:
- Lightweight Features: For small features that do not warrant a full Brookesia component (e.g., LED control, PWM, GPIO toggling, simple sensors), expose and call them through CustomService's interfaces.
- Rapid Prototyping: Quickly expose application logic as callable functions or events for local or remote (RPC) access.
- Extensibility: Add custom capabilities to your application without modifying the service framework.
Key Features:
- Dynamic Registration: Register functions and events at runtime via
register_function()andregister_event() - Fixed Handler Signature: Handler receives
FunctionParameterMapand returnsFunctionResult; supports lambda,std::function, free function, functor, andstd::bind - Event Publish/Subscribe: Full event lifecycle: register, publish, subscribe, and unregister
- ServiceManager Integration: Works with ServiceManager for local calls and remote RPC
- Optional Worker: Configurable task scheduler for thread-safe execution
Table of Contents
Handler Signature
The function handler has a fixed signature:
FunctionResult(const FunctionParameterMap &args)
- Input:
argsis a map of parameter name toFunctionValue(supportsbool,double,std::string,boost::json::object,boost::json::array) - Output:
FunctionResultwithsuccess, optionaldata, and optionalerror_message
You can use lambda, std::function, free function, functor, or std::bind as the handler, as long as the signature matches.
When registering a function or event, CustomService stores its own internal copy of the schema and handler. After registration, changing the original schema object or handler variable will not affect the already registered function or event.
Important Notes
Warning
Variable Lifecycle: Handlers are often lambda expressions. Ensure that any captured variables remain valid for the entire lifetime of the registered function. Avoid capturing stack-allocated objects that may go out of scope before the function is unregistered. Prefer value capture for values that must outlive the current scope.
Note
Copying the Handler Object Does Not Extend Captured References: CustomService stores a copy of the handler object itself, but if the handler captures external variables by reference, those referenced objects still need to remain alive. In other words, registration makes the handler object persistent, but it does not automatically make referenced data persistent.
Note
Keep Handlers Simple: CustomService is designed for lightweight operations. Avoid executing overly complex or long-running logic in handlers. For heavy computation or blocking I/O, consider implementing a dedicated service component with proper task scheduling.
Development Environment Requirements
Before using this library, ensure you have installed the following SDK development environment:
- ESP-IDF:
>=5.5,<6
Note
For SDK installation instructions, please refer to ESP-IDF Programming Guide - Installation
Adding to Project
brookesia_service_custom depends on brookesia_service_manager. Add it to your project as follows:
-
Using Command Line
Run the following command in your project directory:
idf.py add-dependency "espressif/brookesia_service_custom" -
Modify Configuration File
Create or modify the idf_component.yml file in your project directory:
dependencies: espressif/brookesia_service_custom: "*"
For detailed instructions, please refer to Espressif Documentation - IDF Component Manager.
Quick Start
#include "brookesia/lib_utils.hpp"
#include "brookesia/service_manager.hpp"
#include "brookesia/service_custom/service_custom.hpp"
using namespace esp_brookesia::service;
void app_main()
{
auto &service_manager = ServiceManager::get_instance();
auto &custom_service = CustomService::get_instance();
// 1. Start service manager
if (!service_manager.start()) {
BROOKESIA_LOGE("Failed to start service manager");
return;
}
// 2. Bind CustomService (starts the service)
auto binding = service_manager.bind(CustomServiceName);
if (!binding.is_valid()) {
BROOKESIA_LOGE("Failed to bind CustomService");
return;
}
// 3. Register a function (handler receives FunctionParameterMap, returns FunctionResult)
custom_service.register_function(
FunctionSchema{
.name = "SetLED",
.description = "Set LED state",
.parameters = {
{.name = "On", .type = FunctionValueType::Boolean},
},
},
[](const FunctionParameterMap &args) -> FunctionResult {
bool on = std::get<bool>(args.at("On"));
// Set LED state here (e.g., gpio_set_level(...))
return FunctionResult{.success = true};
}
);
// 4. Call the function (pass parameters as boost::json::object)
auto result = custom_service.call_function_sync(
"SetLED",
boost::json::object{{"On", true}},
1000
);
if (result.success) {
BROOKESIA_LOGI("LED set successfully");
}
// 5. Unbind when done
binding.release();
}