diff --git a/.flake8 b/.flake8 index d03e1999a..eda227ab4 100644 --- a/.flake8 +++ b/.flake8 @@ -160,4 +160,5 @@ exclude = components/wifi_provisioning/python/wifi_scan_pb2.py, components/wifi_provisioning/python/wifi_config_pb2.py, components/wifi_provisioning/python/wifi_constants_pb2.py, + components/esp_local_ctrl/python/esp_local_ctrl_pb2.py, examples/provisioning/custom_config/components/custom_provisioning/python/custom_config_pb2.py, diff --git a/components/esp_local_ctrl/CMakeLists.txt b/components/esp_local_ctrl/CMakeLists.txt new file mode 100644 index 000000000..562ed9191 --- /dev/null +++ b/components/esp_local_ctrl/CMakeLists.txt @@ -0,0 +1,23 @@ +set(include_dirs include) +set(priv_include_dirs proto-c src ../protocomm/proto-c) +set(srcs "src/esp_local_ctrl.c" + "src/esp_local_ctrl_handler.c" + "proto-c/esp_local_ctrl.pb-c.c") + +if(CONFIG_ESP_HTTPS_SERVER_ENABLE) + list(APPEND srcs + "src/esp_local_ctrl_transport_httpd.c") +endif() + +if(CONFIG_BT_ENABLED) + if(CONFIG_BT_BLUEDROID_ENABLED) + list(APPEND srcs + "src/esp_local_ctrl_transport_ble.c") + endif() +endif() + +idf_component_register(SRCS "${srcs}" + INCLUDE_DIRS "${include_dirs}" + PRIV_INCLUDE_DIRS "${priv_include_dirs}" + REQUIRES protocomm esp_https_server + PRIV_REQUIRES protobuf-c mdns) diff --git a/components/esp_local_ctrl/component.mk b/components/esp_local_ctrl/component.mk new file mode 100644 index 000000000..c7d18301d --- /dev/null +++ b/components/esp_local_ctrl/component.mk @@ -0,0 +1,11 @@ +COMPONENT_SRCDIRS := src proto-c +COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_PRIV_INCLUDEDIRS := src proto-c ../protocomm/proto-c/ + +ifndef CONFIG_BT_BLUEDROID_ENABLED + COMPONENT_OBJEXCLUDE += src/esp_local_ctrl_transport_ble.o +endif + +ifndef CONFIG_ESP_HTTPS_SERVER_ENABLE + COMPONENT_OBJEXCLUDE += src/esp_local_ctrl_transport_httpd.o +endif diff --git a/components/esp_local_ctrl/include/esp_local_ctrl.h b/components/esp_local_ctrl/include/esp_local_ctrl.h new file mode 100644 index 000000000..53d062b10 --- /dev/null +++ b/components/esp_local_ctrl/include/esp_local_ctrl.h @@ -0,0 +1,339 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * @brief Property description data structure, which is to be populated + * and passed to the `esp_local_ctrl_add_property()` function + * + * Once a property is added, its structure is available for read-only access + * inside `get_prop_values()` and `set_prop_values()` handlers. + */ +typedef struct esp_local_ctrl_prop { + /** + * Unique name of property + */ + char *name; + + /** + * Type of property. This may be set to application defined enums + */ + uint32_t type; + + /** + * Size of the property value, which: + * - if zero, the property can have values of variable size + * - if non-zero, the property can have values of fixed size only, + * therefore, checks are performed internally by esp_local_ctrl + * when setting the value of such a property + */ + size_t size; + + /** + * Flags set for this property. This could be a bit field. + * A flag may indicate property behavior, e.g. read-only / constant + */ + uint32_t flags; + + /** + * Pointer to some context data relevant for this property. This will + * be available for use inside the `get_prop_values` and `set_prop_values` + * handlers as a part of this property structure. When set, this is valid + * throughout the lifetime of a property, till either the property is + * removed or the esp_local_ctrl service is stopped. + */ + void *ctx; + + /** + * Function used by esp_local_ctrl to internally free the property + * context when `esp_local_ctrl_remove_property()` or + * `esp_local_ctrl_stop()` is called. + */ + void (*ctx_free_fn)(void *ctx); +} esp_local_ctrl_prop_t; + +/** + * @brief Property value data structure. This gets passed to the + * `get_prop_values()` and `set_prop_values()` handlers for + * the purpose of retrieving or setting the present value + * of a property. + */ +typedef struct esp_local_ctrl_prop_val { + /** + * Pointer to memory holding property value + */ + void *data; + + /** + * Size of property value + */ + size_t size; + + /** + * This may be set by the application in `get_prop_values()` handler + * to tell `esp_local_ctrl` to call this function on the data pointer + * above, for freeing its resources after sending the `get_prop_values` + * response. + */ + void (*free_fn)(void *data); +} esp_local_ctrl_prop_val_t; + +/** + * @brief Handlers for receiving and responding to local + * control commands for getting and setting properties. + */ +typedef struct esp_local_ctrl_handlers { + /** + * @brief Handler function to be implemented for retrieving current + * values of properties + * + * @note If any of the properties have fixed sizes, the size field of + * corresponding element in `prop_values` need to be set + * + * @param[in] props_count Total elements in the props array + * @param[in] props Array of properties, the current values for which + * have been requested by the client + * @param[out] prop_values Array of empty property values, the elements of + * which need to be populated with the current values + * of those properties specified by props argument + * @param[in] usr_ctx This provides value of the `usr_ctx` field of + * `esp_local_ctrl_handlers_t` structure + * + * @return Returning different error codes will convey the corresponding + * protocol level errors to the client : + * - ESP_OK : Success + * - ESP_ERR_INVALID_ARG : InvalidArgument + * - ESP_ERR_INVALID_STATE : InvalidProto + * - All other error codes : InternalError + */ + esp_err_t (*get_prop_values)(size_t props_count, + const esp_local_ctrl_prop_t props[], + esp_local_ctrl_prop_val_t prop_values[], + void *usr_ctx); + + /** + * @brief Handler function to be implemented for changing values of properties + * + * @note If any of the properties have variable sizes, the size field + * of the corresponding element in `prop_values` must be checked + * explicitly before making any assumptions on the size. + * + * @param[in] props_count Total elements in the props array + * @param[in] props Array of properties, the values for which the + * client requests to change + * @param[in] prop_values Array of property values, the elements of which + * need to be used for updating those properties + * specified by props argument + * @param[in] usr_ctx This provides value of the `usr_ctx` field of + * `esp_local_ctrl_handlers_t` structure + * + * @return Returning different error codes will convey the corresponding + * protocol level errors to the client : + * - ESP_OK : Success + * - ESP_ERR_INVALID_ARG : InvalidArgument + * - ESP_ERR_INVALID_STATE : InvalidProto + * - All other error codes : InternalError + */ + esp_err_t (*set_prop_values)(size_t props_count, + const esp_local_ctrl_prop_t props[], + const esp_local_ctrl_prop_val_t prop_values[], + void *usr_ctx); + + /** + * Context pointer to be passed to above handler functions upon invocation. + * This is different from the property level context, as this is valid + * throughout the lifetime of the `esp_local_ctrl` service, and freed only + * when the service is stopped. + */ + void *usr_ctx; + + /** + * Pointer to function which will be internally invoked on `usr_ctx` for + * freeing the context resources when `esp_local_ctrl_stop()` is called. + */ + void (*usr_ctx_free_fn)(void *usr_ctx); +} esp_local_ctrl_handlers_t; + +/** + * @brief Transport mode (BLE / HTTPD) over which the service will be provided + * + * This is forward declaration of a private structure, implemented internally + * by `esp_local_ctrl`. + */ +typedef struct esp_local_ctrl_transport esp_local_ctrl_transport_t; + +/** + * @brief Function for obtaining BLE transport mode + */ +const esp_local_ctrl_transport_t *esp_local_ctrl_get_transport_ble(); + +/** + * @brief Function for obtaining HTTPD transport mode + */ +const esp_local_ctrl_transport_t *esp_local_ctrl_get_transport_httpd(); + +#define ESP_LOCAL_CTRL_TRANSPORT_BLE esp_local_ctrl_get_transport_ble() +#define ESP_LOCAL_CTRL_TRANSPORT_HTTPD esp_local_ctrl_get_transport_httpd() + +/** + * @brief Configuration for transport mode BLE + * + * This is a forward declaration for `protocomm_ble_config_t`. + * To use this, application must set CONFIG_BT_BLUEDROID_ENABLED + * and include `protocomm_ble.h`. + */ +typedef struct protocomm_ble_config esp_local_ctrl_transport_config_ble_t; + +/** + * @brief Configuration for transport mode HTTPD + * + * This is a forward declaration for `httpd_ssl_config_t`. + * To use this, application must set CONFIG_ESP_HTTPS_SERVER_ENABLE + * and include `esp_https_server.h` + */ +typedef struct httpd_ssl_config esp_local_ctrl_transport_config_httpd_t; + +/** + * @brief Transport mode (BLE / HTTPD) configuration + */ +typedef union { + /** + * This is same as `protocomm_ble_config_t`. See `protocomm_ble.h` for + * available configuration parameters. + */ + esp_local_ctrl_transport_config_ble_t *ble; + + /** + * This is same as `httpd_ssl_config_t`. See `esp_https_server.h` for + * available configuration parameters. + */ + esp_local_ctrl_transport_config_httpd_t *httpd; +} esp_local_ctrl_transport_config_t; + +/** + * @brief Configuration structure to pass to `esp_local_ctrl_start()` + */ +typedef struct esp_local_ctrl_config { + /** + * Transport layer over which service will be provided + */ + const esp_local_ctrl_transport_t *transport; + + /** + * Transport layer over which service will be provided + */ + esp_local_ctrl_transport_config_t transport_config; + + /** + * Register handlers for responding to get/set requests on properties + */ + esp_local_ctrl_handlers_t handlers; + + /** + * This limits the number of properties that are available at a time + */ + size_t max_properties; +} esp_local_ctrl_config_t; + +/** + * @brief Start local control service + * + * @param[in] config Pointer to configuration structure + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Failure + */ +esp_err_t esp_local_ctrl_start(const esp_local_ctrl_config_t *config); + +/** + * @brief Stop local control service + */ +esp_err_t esp_local_ctrl_stop(void); + +/** + * @brief Add a new property + * + * This adds a new property and allocates internal resources for it. + * The total number of properties that could be added is limited by + * configuration option `max_properties` + * + * @param[in] prop Property description structure + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Failure + */ +esp_err_t esp_local_ctrl_add_property(const esp_local_ctrl_prop_t *prop); + +/** + * @brief Remove a property + * + * This finds a property by name, and releases the internal resources + * which are associated with it. + * + * @param[in] name Name of the property to remove + * + * @return + * - ESP_OK : Success + * - ESP_ERR_NOT_FOUND : Failure + */ +esp_err_t esp_local_ctrl_remove_property(const char *name); + +/** + * @brief Get property description structure by name + * + * This API may be used to get a property's context structure + * `esp_local_ctrl_prop_t` when its name is known + * + * @param[in] name Name of the property to find + * + * @return + * - Pointer to property + * - NULL if not found + */ +const esp_local_ctrl_prop_t *esp_local_ctrl_get_property(const char *name); + +/** + * @brief Register protocomm handler for a custom endpoint + * + * This API can be called by the application to register a protocomm handler + * for an endpoint after the local control service has started. + * + * @note In case of BLE transport the names and uuids of all custom + * endpoints must be provided beforehand as a part of the `protocomm_ble_config_t` + * structure set in `esp_local_ctrl_config_t`, and passed to `esp_local_ctrl_start()`. + * + * @param[in] ep_name Name of the endpoint + * @param[in] handler Endpoint handler function + * @param[in] user_ctx User data + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Failure + */ +esp_err_t esp_local_ctrl_set_handler(const char *ep_name, + protocomm_req_handler_t handler, + void *user_ctx); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_local_ctrl/proto-c/esp_local_ctrl.pb-c.c b/components/esp_local_ctrl/proto-c/esp_local_ctrl.pb-c.c new file mode 100644 index 000000000..a6dd76eb2 --- /dev/null +++ b/components/esp_local_ctrl/proto-c/esp_local_ctrl.pb-c.c @@ -0,0 +1,942 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: esp_local_ctrl.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "esp_local_ctrl.pb-c.h" +void cmd_get_property_count__init + (CmdGetPropertyCount *message) +{ + static const CmdGetPropertyCount init_value = CMD_GET_PROPERTY_COUNT__INIT; + *message = init_value; +} +size_t cmd_get_property_count__get_packed_size + (const CmdGetPropertyCount *message) +{ + assert(message->base.descriptor == &cmd_get_property_count__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_get_property_count__pack + (const CmdGetPropertyCount *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_get_property_count__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_get_property_count__pack_to_buffer + (const CmdGetPropertyCount *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_get_property_count__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdGetPropertyCount * + cmd_get_property_count__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdGetPropertyCount *) + protobuf_c_message_unpack (&cmd_get_property_count__descriptor, + allocator, len, data); +} +void cmd_get_property_count__free_unpacked + (CmdGetPropertyCount *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_get_property_count__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_get_property_count__init + (RespGetPropertyCount *message) +{ + static const RespGetPropertyCount init_value = RESP_GET_PROPERTY_COUNT__INIT; + *message = init_value; +} +size_t resp_get_property_count__get_packed_size + (const RespGetPropertyCount *message) +{ + assert(message->base.descriptor == &resp_get_property_count__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_get_property_count__pack + (const RespGetPropertyCount *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_get_property_count__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_get_property_count__pack_to_buffer + (const RespGetPropertyCount *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_get_property_count__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespGetPropertyCount * + resp_get_property_count__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespGetPropertyCount *) + protobuf_c_message_unpack (&resp_get_property_count__descriptor, + allocator, len, data); +} +void resp_get_property_count__free_unpacked + (RespGetPropertyCount *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_get_property_count__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void property_info__init + (PropertyInfo *message) +{ + static const PropertyInfo init_value = PROPERTY_INFO__INIT; + *message = init_value; +} +size_t property_info__get_packed_size + (const PropertyInfo *message) +{ + assert(message->base.descriptor == &property_info__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t property_info__pack + (const PropertyInfo *message, + uint8_t *out) +{ + assert(message->base.descriptor == &property_info__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t property_info__pack_to_buffer + (const PropertyInfo *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &property_info__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +PropertyInfo * + property_info__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (PropertyInfo *) + protobuf_c_message_unpack (&property_info__descriptor, + allocator, len, data); +} +void property_info__free_unpacked + (PropertyInfo *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &property_info__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_get_property_values__init + (CmdGetPropertyValues *message) +{ + static const CmdGetPropertyValues init_value = CMD_GET_PROPERTY_VALUES__INIT; + *message = init_value; +} +size_t cmd_get_property_values__get_packed_size + (const CmdGetPropertyValues *message) +{ + assert(message->base.descriptor == &cmd_get_property_values__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_get_property_values__pack + (const CmdGetPropertyValues *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_get_property_values__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_get_property_values__pack_to_buffer + (const CmdGetPropertyValues *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_get_property_values__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdGetPropertyValues * + cmd_get_property_values__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdGetPropertyValues *) + protobuf_c_message_unpack (&cmd_get_property_values__descriptor, + allocator, len, data); +} +void cmd_get_property_values__free_unpacked + (CmdGetPropertyValues *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_get_property_values__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_get_property_values__init + (RespGetPropertyValues *message) +{ + static const RespGetPropertyValues init_value = RESP_GET_PROPERTY_VALUES__INIT; + *message = init_value; +} +size_t resp_get_property_values__get_packed_size + (const RespGetPropertyValues *message) +{ + assert(message->base.descriptor == &resp_get_property_values__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_get_property_values__pack + (const RespGetPropertyValues *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_get_property_values__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_get_property_values__pack_to_buffer + (const RespGetPropertyValues *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_get_property_values__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespGetPropertyValues * + resp_get_property_values__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespGetPropertyValues *) + protobuf_c_message_unpack (&resp_get_property_values__descriptor, + allocator, len, data); +} +void resp_get_property_values__free_unpacked + (RespGetPropertyValues *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_get_property_values__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void property_value__init + (PropertyValue *message) +{ + static const PropertyValue init_value = PROPERTY_VALUE__INIT; + *message = init_value; +} +size_t property_value__get_packed_size + (const PropertyValue *message) +{ + assert(message->base.descriptor == &property_value__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t property_value__pack + (const PropertyValue *message, + uint8_t *out) +{ + assert(message->base.descriptor == &property_value__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t property_value__pack_to_buffer + (const PropertyValue *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &property_value__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +PropertyValue * + property_value__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (PropertyValue *) + protobuf_c_message_unpack (&property_value__descriptor, + allocator, len, data); +} +void property_value__free_unpacked + (PropertyValue *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &property_value__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void cmd_set_property_values__init + (CmdSetPropertyValues *message) +{ + static const CmdSetPropertyValues init_value = CMD_SET_PROPERTY_VALUES__INIT; + *message = init_value; +} +size_t cmd_set_property_values__get_packed_size + (const CmdSetPropertyValues *message) +{ + assert(message->base.descriptor == &cmd_set_property_values__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t cmd_set_property_values__pack + (const CmdSetPropertyValues *message, + uint8_t *out) +{ + assert(message->base.descriptor == &cmd_set_property_values__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t cmd_set_property_values__pack_to_buffer + (const CmdSetPropertyValues *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &cmd_set_property_values__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +CmdSetPropertyValues * + cmd_set_property_values__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (CmdSetPropertyValues *) + protobuf_c_message_unpack (&cmd_set_property_values__descriptor, + allocator, len, data); +} +void cmd_set_property_values__free_unpacked + (CmdSetPropertyValues *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &cmd_set_property_values__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void resp_set_property_values__init + (RespSetPropertyValues *message) +{ + static const RespSetPropertyValues init_value = RESP_SET_PROPERTY_VALUES__INIT; + *message = init_value; +} +size_t resp_set_property_values__get_packed_size + (const RespSetPropertyValues *message) +{ + assert(message->base.descriptor == &resp_set_property_values__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t resp_set_property_values__pack + (const RespSetPropertyValues *message, + uint8_t *out) +{ + assert(message->base.descriptor == &resp_set_property_values__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t resp_set_property_values__pack_to_buffer + (const RespSetPropertyValues *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &resp_set_property_values__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +RespSetPropertyValues * + resp_set_property_values__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (RespSetPropertyValues *) + protobuf_c_message_unpack (&resp_set_property_values__descriptor, + allocator, len, data); +} +void resp_set_property_values__free_unpacked + (RespSetPropertyValues *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &resp_set_property_values__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +void local_ctrl_message__init + (LocalCtrlMessage *message) +{ + static const LocalCtrlMessage init_value = LOCAL_CTRL_MESSAGE__INIT; + *message = init_value; +} +size_t local_ctrl_message__get_packed_size + (const LocalCtrlMessage *message) +{ + assert(message->base.descriptor == &local_ctrl_message__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t local_ctrl_message__pack + (const LocalCtrlMessage *message, + uint8_t *out) +{ + assert(message->base.descriptor == &local_ctrl_message__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t local_ctrl_message__pack_to_buffer + (const LocalCtrlMessage *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &local_ctrl_message__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +LocalCtrlMessage * + local_ctrl_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (LocalCtrlMessage *) + protobuf_c_message_unpack (&local_ctrl_message__descriptor, + allocator, len, data); +} +void local_ctrl_message__free_unpacked + (LocalCtrlMessage *message, + ProtobufCAllocator *allocator) +{ + if(!message) + return; + assert(message->base.descriptor == &local_ctrl_message__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +#define cmd_get_property_count__field_descriptors NULL +#define cmd_get_property_count__field_indices_by_name NULL +#define cmd_get_property_count__number_ranges NULL +const ProtobufCMessageDescriptor cmd_get_property_count__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdGetPropertyCount", + "CmdGetPropertyCount", + "CmdGetPropertyCount", + "", + sizeof(CmdGetPropertyCount), + 0, + cmd_get_property_count__field_descriptors, + cmd_get_property_count__field_indices_by_name, + 0, cmd_get_property_count__number_ranges, + (ProtobufCMessageInit) cmd_get_property_count__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_get_property_count__field_descriptors[2] = +{ + { + "status", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespGetPropertyCount, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "count", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(RespGetPropertyCount, count), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_get_property_count__field_indices_by_name[] = { + 1, /* field[1] = count */ + 0, /* field[0] = status */ +}; +static const ProtobufCIntRange resp_get_property_count__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor resp_get_property_count__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespGetPropertyCount", + "RespGetPropertyCount", + "RespGetPropertyCount", + "", + sizeof(RespGetPropertyCount), + 2, + resp_get_property_count__field_descriptors, + resp_get_property_count__field_indices_by_name, + 1, resp_get_property_count__number_ranges, + (ProtobufCMessageInit) resp_get_property_count__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor property_info__field_descriptors[5] = +{ + { + "status", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(PropertyInfo, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "name", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_STRING, + 0, /* quantifier_offset */ + offsetof(PropertyInfo, name), + NULL, + &protobuf_c_empty_string, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "type", + 3, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(PropertyInfo, type), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "flags", + 4, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(PropertyInfo, flags), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "value", + 5, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(PropertyInfo, value), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned property_info__field_indices_by_name[] = { + 3, /* field[3] = flags */ + 1, /* field[1] = name */ + 0, /* field[0] = status */ + 2, /* field[2] = type */ + 4, /* field[4] = value */ +}; +static const ProtobufCIntRange property_info__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 5 } +}; +const ProtobufCMessageDescriptor property_info__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "PropertyInfo", + "PropertyInfo", + "PropertyInfo", + "", + sizeof(PropertyInfo), + 5, + property_info__field_descriptors, + property_info__field_indices_by_name, + 1, property_info__number_ranges, + (ProtobufCMessageInit) property_info__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor cmd_get_property_values__field_descriptors[1] = +{ + { + "indices", + 1, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_UINT32, + offsetof(CmdGetPropertyValues, n_indices), + offsetof(CmdGetPropertyValues, indices), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned cmd_get_property_values__field_indices_by_name[] = { + 0, /* field[0] = indices */ +}; +static const ProtobufCIntRange cmd_get_property_values__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor cmd_get_property_values__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdGetPropertyValues", + "CmdGetPropertyValues", + "CmdGetPropertyValues", + "", + sizeof(CmdGetPropertyValues), + 1, + cmd_get_property_values__field_descriptors, + cmd_get_property_values__field_indices_by_name, + 1, cmd_get_property_values__number_ranges, + (ProtobufCMessageInit) cmd_get_property_values__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_get_property_values__field_descriptors[2] = +{ + { + "status", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespGetPropertyValues, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "props", + 2, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(RespGetPropertyValues, n_props), + offsetof(RespGetPropertyValues, props), + &property_info__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_get_property_values__field_indices_by_name[] = { + 1, /* field[1] = props */ + 0, /* field[0] = status */ +}; +static const ProtobufCIntRange resp_get_property_values__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor resp_get_property_values__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespGetPropertyValues", + "RespGetPropertyValues", + "RespGetPropertyValues", + "", + sizeof(RespGetPropertyValues), + 2, + resp_get_property_values__field_descriptors, + resp_get_property_values__field_indices_by_name, + 1, resp_get_property_values__number_ranges, + (ProtobufCMessageInit) resp_get_property_values__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor property_value__field_descriptors[2] = +{ + { + "index", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_UINT32, + 0, /* quantifier_offset */ + offsetof(PropertyValue, index), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "value", + 2, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_BYTES, + 0, /* quantifier_offset */ + offsetof(PropertyValue, value), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned property_value__field_indices_by_name[] = { + 0, /* field[0] = index */ + 1, /* field[1] = value */ +}; +static const ProtobufCIntRange property_value__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor property_value__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "PropertyValue", + "PropertyValue", + "PropertyValue", + "", + sizeof(PropertyValue), + 2, + property_value__field_descriptors, + property_value__field_indices_by_name, + 1, property_value__number_ranges, + (ProtobufCMessageInit) property_value__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor cmd_set_property_values__field_descriptors[1] = +{ + { + "props", + 1, + PROTOBUF_C_LABEL_REPEATED, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(CmdSetPropertyValues, n_props), + offsetof(CmdSetPropertyValues, props), + &property_value__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned cmd_set_property_values__field_indices_by_name[] = { + 0, /* field[0] = props */ +}; +static const ProtobufCIntRange cmd_set_property_values__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor cmd_set_property_values__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "CmdSetPropertyValues", + "CmdSetPropertyValues", + "CmdSetPropertyValues", + "", + sizeof(CmdSetPropertyValues), + 1, + cmd_set_property_values__field_descriptors, + cmd_set_property_values__field_indices_by_name, + 1, cmd_set_property_values__number_ranges, + (ProtobufCMessageInit) cmd_set_property_values__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor resp_set_property_values__field_descriptors[1] = +{ + { + "status", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(RespSetPropertyValues, status), + &status__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned resp_set_property_values__field_indices_by_name[] = { + 0, /* field[0] = status */ +}; +static const ProtobufCIntRange resp_set_property_values__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 1 } +}; +const ProtobufCMessageDescriptor resp_set_property_values__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "RespSetPropertyValues", + "RespSetPropertyValues", + "RespSetPropertyValues", + "", + sizeof(RespSetPropertyValues), + 1, + resp_set_property_values__field_descriptors, + resp_set_property_values__field_indices_by_name, + 1, resp_set_property_values__number_ranges, + (ProtobufCMessageInit) resp_set_property_values__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCFieldDescriptor local_ctrl_message__field_descriptors[7] = +{ + { + "msg", + 1, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_ENUM, + 0, /* quantifier_offset */ + offsetof(LocalCtrlMessage, msg), + &local_ctrl_msg_type__descriptor, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_get_prop_count", + 10, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(LocalCtrlMessage, payload_case), + offsetof(LocalCtrlMessage, cmd_get_prop_count), + &cmd_get_property_count__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_get_prop_count", + 11, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(LocalCtrlMessage, payload_case), + offsetof(LocalCtrlMessage, resp_get_prop_count), + &resp_get_property_count__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_get_prop_vals", + 12, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(LocalCtrlMessage, payload_case), + offsetof(LocalCtrlMessage, cmd_get_prop_vals), + &cmd_get_property_values__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_get_prop_vals", + 13, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(LocalCtrlMessage, payload_case), + offsetof(LocalCtrlMessage, resp_get_prop_vals), + &resp_get_property_values__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "cmd_set_prop_vals", + 14, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(LocalCtrlMessage, payload_case), + offsetof(LocalCtrlMessage, cmd_set_prop_vals), + &cmd_set_property_values__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "resp_set_prop_vals", + 15, + PROTOBUF_C_LABEL_NONE, + PROTOBUF_C_TYPE_MESSAGE, + offsetof(LocalCtrlMessage, payload_case), + offsetof(LocalCtrlMessage, resp_set_prop_vals), + &resp_set_property_values__descriptor, + NULL, + 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned local_ctrl_message__field_indices_by_name[] = { + 1, /* field[1] = cmd_get_prop_count */ + 3, /* field[3] = cmd_get_prop_vals */ + 5, /* field[5] = cmd_set_prop_vals */ + 0, /* field[0] = msg */ + 2, /* field[2] = resp_get_prop_count */ + 4, /* field[4] = resp_get_prop_vals */ + 6, /* field[6] = resp_set_prop_vals */ +}; +static const ProtobufCIntRange local_ctrl_message__number_ranges[2 + 1] = +{ + { 1, 0 }, + { 10, 1 }, + { 0, 7 } +}; +const ProtobufCMessageDescriptor local_ctrl_message__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "LocalCtrlMessage", + "LocalCtrlMessage", + "LocalCtrlMessage", + "", + sizeof(LocalCtrlMessage), + 7, + local_ctrl_message__field_descriptors, + local_ctrl_message__field_indices_by_name, + 2, local_ctrl_message__number_ranges, + (ProtobufCMessageInit) local_ctrl_message__init, + NULL,NULL,NULL /* reserved[123] */ +}; +static const ProtobufCEnumValue local_ctrl_msg_type__enum_values_by_number[6] = +{ + { "TypeCmdGetPropertyCount", "LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyCount", 0 }, + { "TypeRespGetPropertyCount", "LOCAL_CTRL_MSG_TYPE__TypeRespGetPropertyCount", 1 }, + { "TypeCmdGetPropertyValues", "LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyValues", 4 }, + { "TypeRespGetPropertyValues", "LOCAL_CTRL_MSG_TYPE__TypeRespGetPropertyValues", 5 }, + { "TypeCmdSetPropertyValues", "LOCAL_CTRL_MSG_TYPE__TypeCmdSetPropertyValues", 6 }, + { "TypeRespSetPropertyValues", "LOCAL_CTRL_MSG_TYPE__TypeRespSetPropertyValues", 7 }, +}; +static const ProtobufCIntRange local_ctrl_msg_type__value_ranges[] = { +{0, 0},{4, 2},{0, 6} +}; +static const ProtobufCEnumValueIndex local_ctrl_msg_type__enum_values_by_name[6] = +{ + { "TypeCmdGetPropertyCount", 0 }, + { "TypeCmdGetPropertyValues", 2 }, + { "TypeCmdSetPropertyValues", 4 }, + { "TypeRespGetPropertyCount", 1 }, + { "TypeRespGetPropertyValues", 3 }, + { "TypeRespSetPropertyValues", 5 }, +}; +const ProtobufCEnumDescriptor local_ctrl_msg_type__descriptor = +{ + PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, + "LocalCtrlMsgType", + "LocalCtrlMsgType", + "LocalCtrlMsgType", + "", + 6, + local_ctrl_msg_type__enum_values_by_number, + 6, + local_ctrl_msg_type__enum_values_by_name, + 2, + local_ctrl_msg_type__value_ranges, + NULL,NULL,NULL,NULL /* reserved[1234] */ +}; diff --git a/components/esp_local_ctrl/proto-c/esp_local_ctrl.pb-c.h b/components/esp_local_ctrl/proto-c/esp_local_ctrl.pb-c.h new file mode 100644 index 000000000..1a1723ec4 --- /dev/null +++ b/components/esp_local_ctrl/proto-c/esp_local_ctrl.pb-c.h @@ -0,0 +1,383 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: esp_local_ctrl.proto */ + +#ifndef PROTOBUF_C_esp_5flocal_5fctrl_2eproto__INCLUDED +#define PROTOBUF_C_esp_5flocal_5fctrl_2eproto__INCLUDED + +#include + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1003000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1003001 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + +#include "constants.pb-c.h" + +typedef struct _CmdGetPropertyCount CmdGetPropertyCount; +typedef struct _RespGetPropertyCount RespGetPropertyCount; +typedef struct _PropertyInfo PropertyInfo; +typedef struct _CmdGetPropertyValues CmdGetPropertyValues; +typedef struct _RespGetPropertyValues RespGetPropertyValues; +typedef struct _PropertyValue PropertyValue; +typedef struct _CmdSetPropertyValues CmdSetPropertyValues; +typedef struct _RespSetPropertyValues RespSetPropertyValues; +typedef struct _LocalCtrlMessage LocalCtrlMessage; + + +/* --- enums --- */ + +typedef enum _LocalCtrlMsgType { + LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyCount = 0, + LOCAL_CTRL_MSG_TYPE__TypeRespGetPropertyCount = 1, + LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyValues = 4, + LOCAL_CTRL_MSG_TYPE__TypeRespGetPropertyValues = 5, + LOCAL_CTRL_MSG_TYPE__TypeCmdSetPropertyValues = 6, + LOCAL_CTRL_MSG_TYPE__TypeRespSetPropertyValues = 7 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(LOCAL_CTRL_MSG_TYPE) +} LocalCtrlMsgType; + +/* --- messages --- */ + +struct _CmdGetPropertyCount +{ + ProtobufCMessage base; +}; +#define CMD_GET_PROPERTY_COUNT__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_get_property_count__descriptor) \ + } + + +struct _RespGetPropertyCount +{ + ProtobufCMessage base; + Status status; + uint32_t count; +}; +#define RESP_GET_PROPERTY_COUNT__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_get_property_count__descriptor) \ + , STATUS__Success, 0 } + + +struct _PropertyInfo +{ + ProtobufCMessage base; + Status status; + char *name; + uint32_t type; + uint32_t flags; + ProtobufCBinaryData value; +}; +#define PROPERTY_INFO__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&property_info__descriptor) \ + , STATUS__Success, (char *)protobuf_c_empty_string, 0, 0, {0,NULL} } + + +struct _CmdGetPropertyValues +{ + ProtobufCMessage base; + size_t n_indices; + uint32_t *indices; +}; +#define CMD_GET_PROPERTY_VALUES__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_get_property_values__descriptor) \ + , 0,NULL } + + +struct _RespGetPropertyValues +{ + ProtobufCMessage base; + Status status; + size_t n_props; + PropertyInfo **props; +}; +#define RESP_GET_PROPERTY_VALUES__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_get_property_values__descriptor) \ + , STATUS__Success, 0,NULL } + + +struct _PropertyValue +{ + ProtobufCMessage base; + uint32_t index; + ProtobufCBinaryData value; +}; +#define PROPERTY_VALUE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&property_value__descriptor) \ + , 0, {0,NULL} } + + +struct _CmdSetPropertyValues +{ + ProtobufCMessage base; + size_t n_props; + PropertyValue **props; +}; +#define CMD_SET_PROPERTY_VALUES__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&cmd_set_property_values__descriptor) \ + , 0,NULL } + + +struct _RespSetPropertyValues +{ + ProtobufCMessage base; + Status status; +}; +#define RESP_SET_PROPERTY_VALUES__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&resp_set_property_values__descriptor) \ + , STATUS__Success } + + +typedef enum { + LOCAL_CTRL_MESSAGE__PAYLOAD__NOT_SET = 0, + LOCAL_CTRL_MESSAGE__PAYLOAD_CMD_GET_PROP_COUNT = 10, + LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_GET_PROP_COUNT = 11, + LOCAL_CTRL_MESSAGE__PAYLOAD_CMD_GET_PROP_VALS = 12, + LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_GET_PROP_VALS = 13, + LOCAL_CTRL_MESSAGE__PAYLOAD_CMD_SET_PROP_VALS = 14, + LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_SET_PROP_VALS = 15 + PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(LOCAL_CTRL_MESSAGE__PAYLOAD) +} LocalCtrlMessage__PayloadCase; + +struct _LocalCtrlMessage +{ + ProtobufCMessage base; + LocalCtrlMsgType msg; + LocalCtrlMessage__PayloadCase payload_case; + union { + CmdGetPropertyCount *cmd_get_prop_count; + RespGetPropertyCount *resp_get_prop_count; + CmdGetPropertyValues *cmd_get_prop_vals; + RespGetPropertyValues *resp_get_prop_vals; + CmdSetPropertyValues *cmd_set_prop_vals; + RespSetPropertyValues *resp_set_prop_vals; + }; +}; +#define LOCAL_CTRL_MESSAGE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&local_ctrl_message__descriptor) \ + , LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyCount, LOCAL_CTRL_MESSAGE__PAYLOAD__NOT_SET, {0} } + + +/* CmdGetPropertyCount methods */ +void cmd_get_property_count__init + (CmdGetPropertyCount *message); +size_t cmd_get_property_count__get_packed_size + (const CmdGetPropertyCount *message); +size_t cmd_get_property_count__pack + (const CmdGetPropertyCount *message, + uint8_t *out); +size_t cmd_get_property_count__pack_to_buffer + (const CmdGetPropertyCount *message, + ProtobufCBuffer *buffer); +CmdGetPropertyCount * + cmd_get_property_count__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_get_property_count__free_unpacked + (CmdGetPropertyCount *message, + ProtobufCAllocator *allocator); +/* RespGetPropertyCount methods */ +void resp_get_property_count__init + (RespGetPropertyCount *message); +size_t resp_get_property_count__get_packed_size + (const RespGetPropertyCount *message); +size_t resp_get_property_count__pack + (const RespGetPropertyCount *message, + uint8_t *out); +size_t resp_get_property_count__pack_to_buffer + (const RespGetPropertyCount *message, + ProtobufCBuffer *buffer); +RespGetPropertyCount * + resp_get_property_count__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_get_property_count__free_unpacked + (RespGetPropertyCount *message, + ProtobufCAllocator *allocator); +/* PropertyInfo methods */ +void property_info__init + (PropertyInfo *message); +size_t property_info__get_packed_size + (const PropertyInfo *message); +size_t property_info__pack + (const PropertyInfo *message, + uint8_t *out); +size_t property_info__pack_to_buffer + (const PropertyInfo *message, + ProtobufCBuffer *buffer); +PropertyInfo * + property_info__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void property_info__free_unpacked + (PropertyInfo *message, + ProtobufCAllocator *allocator); +/* CmdGetPropertyValues methods */ +void cmd_get_property_values__init + (CmdGetPropertyValues *message); +size_t cmd_get_property_values__get_packed_size + (const CmdGetPropertyValues *message); +size_t cmd_get_property_values__pack + (const CmdGetPropertyValues *message, + uint8_t *out); +size_t cmd_get_property_values__pack_to_buffer + (const CmdGetPropertyValues *message, + ProtobufCBuffer *buffer); +CmdGetPropertyValues * + cmd_get_property_values__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_get_property_values__free_unpacked + (CmdGetPropertyValues *message, + ProtobufCAllocator *allocator); +/* RespGetPropertyValues methods */ +void resp_get_property_values__init + (RespGetPropertyValues *message); +size_t resp_get_property_values__get_packed_size + (const RespGetPropertyValues *message); +size_t resp_get_property_values__pack + (const RespGetPropertyValues *message, + uint8_t *out); +size_t resp_get_property_values__pack_to_buffer + (const RespGetPropertyValues *message, + ProtobufCBuffer *buffer); +RespGetPropertyValues * + resp_get_property_values__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_get_property_values__free_unpacked + (RespGetPropertyValues *message, + ProtobufCAllocator *allocator); +/* PropertyValue methods */ +void property_value__init + (PropertyValue *message); +size_t property_value__get_packed_size + (const PropertyValue *message); +size_t property_value__pack + (const PropertyValue *message, + uint8_t *out); +size_t property_value__pack_to_buffer + (const PropertyValue *message, + ProtobufCBuffer *buffer); +PropertyValue * + property_value__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void property_value__free_unpacked + (PropertyValue *message, + ProtobufCAllocator *allocator); +/* CmdSetPropertyValues methods */ +void cmd_set_property_values__init + (CmdSetPropertyValues *message); +size_t cmd_set_property_values__get_packed_size + (const CmdSetPropertyValues *message); +size_t cmd_set_property_values__pack + (const CmdSetPropertyValues *message, + uint8_t *out); +size_t cmd_set_property_values__pack_to_buffer + (const CmdSetPropertyValues *message, + ProtobufCBuffer *buffer); +CmdSetPropertyValues * + cmd_set_property_values__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void cmd_set_property_values__free_unpacked + (CmdSetPropertyValues *message, + ProtobufCAllocator *allocator); +/* RespSetPropertyValues methods */ +void resp_set_property_values__init + (RespSetPropertyValues *message); +size_t resp_set_property_values__get_packed_size + (const RespSetPropertyValues *message); +size_t resp_set_property_values__pack + (const RespSetPropertyValues *message, + uint8_t *out); +size_t resp_set_property_values__pack_to_buffer + (const RespSetPropertyValues *message, + ProtobufCBuffer *buffer); +RespSetPropertyValues * + resp_set_property_values__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void resp_set_property_values__free_unpacked + (RespSetPropertyValues *message, + ProtobufCAllocator *allocator); +/* LocalCtrlMessage methods */ +void local_ctrl_message__init + (LocalCtrlMessage *message); +size_t local_ctrl_message__get_packed_size + (const LocalCtrlMessage *message); +size_t local_ctrl_message__pack + (const LocalCtrlMessage *message, + uint8_t *out); +size_t local_ctrl_message__pack_to_buffer + (const LocalCtrlMessage *message, + ProtobufCBuffer *buffer); +LocalCtrlMessage * + local_ctrl_message__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void local_ctrl_message__free_unpacked + (LocalCtrlMessage *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*CmdGetPropertyCount_Closure) + (const CmdGetPropertyCount *message, + void *closure_data); +typedef void (*RespGetPropertyCount_Closure) + (const RespGetPropertyCount *message, + void *closure_data); +typedef void (*PropertyInfo_Closure) + (const PropertyInfo *message, + void *closure_data); +typedef void (*CmdGetPropertyValues_Closure) + (const CmdGetPropertyValues *message, + void *closure_data); +typedef void (*RespGetPropertyValues_Closure) + (const RespGetPropertyValues *message, + void *closure_data); +typedef void (*PropertyValue_Closure) + (const PropertyValue *message, + void *closure_data); +typedef void (*CmdSetPropertyValues_Closure) + (const CmdSetPropertyValues *message, + void *closure_data); +typedef void (*RespSetPropertyValues_Closure) + (const RespSetPropertyValues *message, + void *closure_data); +typedef void (*LocalCtrlMessage_Closure) + (const LocalCtrlMessage *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCEnumDescriptor local_ctrl_msg_type__descriptor; +extern const ProtobufCMessageDescriptor cmd_get_property_count__descriptor; +extern const ProtobufCMessageDescriptor resp_get_property_count__descriptor; +extern const ProtobufCMessageDescriptor property_info__descriptor; +extern const ProtobufCMessageDescriptor cmd_get_property_values__descriptor; +extern const ProtobufCMessageDescriptor resp_get_property_values__descriptor; +extern const ProtobufCMessageDescriptor property_value__descriptor; +extern const ProtobufCMessageDescriptor cmd_set_property_values__descriptor; +extern const ProtobufCMessageDescriptor resp_set_property_values__descriptor; +extern const ProtobufCMessageDescriptor local_ctrl_message__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_esp_5flocal_5fctrl_2eproto__INCLUDED */ diff --git a/components/esp_local_ctrl/proto/CMakeLists.txt b/components/esp_local_ctrl/proto/CMakeLists.txt new file mode 100644 index 000000000..ffe6a5425 --- /dev/null +++ b/components/esp_local_ctrl/proto/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.5) + +set(PROTO_COMPILER "protoc") +set(PROTO_C_COMPILER "protoc-c") +set(C_OUT_PATH "${CMAKE_CURRENT_LIST_DIR}/../proto-c") +set(PY_OUT_PATH "${CMAKE_CURRENT_LIST_DIR}/../python") +set(PROTOCOMM_INCL_PATH "${CMAKE_CURRENT_LIST_DIR}/../../protocomm/proto") + +file(GLOB PROTO_FILES + LIST_DIRECTORIES false + RELATIVE ${CMAKE_CURRENT_LIST_DIR} + "*.proto") + +add_custom_target(c_proto + COMMAND ${PROTO_C_COMPILER} --c_out=${C_OUT_PATH} -I . -I ${PROTOCOMM_INCL_PATH} ${PROTO_FILES} + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + ) + +add_custom_target(python_proto + COMMAND ${PROTO_COMPILER} --python_out=${PY_OUT_PATH} -I . -I ${PROTOCOMM_INCL_PATH} ${PROTO_FILES} + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + ) + +add_custom_target(proto ALL + DEPENDS c_proto python_proto + VERBATIM + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + ) diff --git a/components/esp_local_ctrl/proto/README.md b/components/esp_local_ctrl/proto/README.md new file mode 100644 index 000000000..cdb4e2ce2 --- /dev/null +++ b/components/esp_local_ctrl/proto/README.md @@ -0,0 +1,25 @@ +# Protobuf files for defining ESP Local Control message structures + +The proto files under this directory are used by esp_local_ctrl for defining protobuf messages which are sent and received over protocomm transport layer. These proto files cannot be used directly and have to be compiled into C and Python files. The generated C files are used by esp_local_ctrl itself to create, delete and manipulate transaction packets. The generated Python files can be used by python based applications for implementing client side interface to esp_local_ctrl service running on a device. + +Note : These proto files are not automatically compiled during the build process. + +# Compilation + +Compilation requires protoc (Protobuf Compiler) and protoc-c (Protobuf C Compiler) installed. Since the generated files are to remain the same, as long as the proto files are not modified, therefore the generated files are already available under `components/esp_local_ctrl/proto-c` and `components/esp_local_ctrl/python` directories, and thus running `cmake` / `make` (and installing the Protobuf compilers) is optional. + +If using `cmake` follow the below steps. If using `make`, jump to Step 2 directly. + +## Step 1 (Only for cmake) + +When using cmake, first create a build directory and call cmake from inside: + +``` +mkdir build +cd build +cmake .. +``` + +## Step 2 + +Simply run `make` to generate the respective C and Python files. The newly created files will overwrite those under `components/esp_local_ctrl/proto-c` and `components/esp_local_ctrl/python` diff --git a/components/esp_local_ctrl/proto/esp_local_ctrl.proto b/components/esp_local_ctrl/proto/esp_local_ctrl.proto new file mode 100644 index 000000000..1d91b56c2 --- /dev/null +++ b/components/esp_local_ctrl/proto/esp_local_ctrl.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; + +import "constants.proto"; + +message CmdGetPropertyCount { +} + +message RespGetPropertyCount { + Status status = 1; + uint32 count = 2; +} + +message PropertyInfo { + Status status = 1; + string name = 2; + uint32 type = 3; + uint32 flags = 4; + bytes value = 5; +} + +message CmdGetPropertyValues { + repeated uint32 indices = 1; +} + +message RespGetPropertyValues { + Status status = 1; + repeated PropertyInfo props = 2; +} + +message PropertyValue { + uint32 index = 1; + bytes value = 2; +} + +message CmdSetPropertyValues { + repeated PropertyValue props = 1; +} + +message RespSetPropertyValues { + Status status = 1; +} + +enum LocalCtrlMsgType { + TypeCmdGetPropertyCount = 0; + TypeRespGetPropertyCount = 1; + TypeCmdGetPropertyValues = 4; + TypeRespGetPropertyValues = 5; + TypeCmdSetPropertyValues = 6; + TypeRespSetPropertyValues = 7; +} + +message LocalCtrlMessage { + LocalCtrlMsgType msg = 1; + oneof payload { + CmdGetPropertyCount cmd_get_prop_count = 10; + RespGetPropertyCount resp_get_prop_count = 11; + CmdGetPropertyValues cmd_get_prop_vals = 12; + RespGetPropertyValues resp_get_prop_vals = 13; + CmdSetPropertyValues cmd_set_prop_vals = 14; + RespSetPropertyValues resp_set_prop_vals = 15; + } +} diff --git a/components/esp_local_ctrl/proto/makefile b/components/esp_local_ctrl/proto/makefile new file mode 100644 index 000000000..585e42267 --- /dev/null +++ b/components/esp_local_ctrl/proto/makefile @@ -0,0 +1,7 @@ +all: c_proto python_proto + +c_proto: *.proto + @protoc-c --c_out=../proto-c/ -I . -I ../../protocomm/proto/ *.proto + +python_proto: *.proto + @protoc --python_out=../python/ -I . -I ../../protocomm/proto/ *.proto diff --git a/components/esp_local_ctrl/python/esp_local_ctrl_pb2.py b/components/esp_local_ctrl/python/esp_local_ctrl_pb2.py new file mode 100644 index 000000000..39f081f23 --- /dev/null +++ b/components/esp_local_ctrl/python/esp_local_ctrl_pb2.py @@ -0,0 +1,549 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: esp_local_ctrl.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +import constants_pb2 as constants__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='esp_local_ctrl.proto', + package='', + syntax='proto3', + serialized_options=None, + serialized_pb=_b('\n\x14\x65sp_local_ctrl.proto\x1a\x0f\x63onstants.proto\"\x15\n\x13\x43mdGetPropertyCount\">\n\x14RespGetPropertyCount\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\x12\r\n\x05\x63ount\x18\x02 \x01(\r\"a\n\x0cPropertyInfo\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04type\x18\x03 \x01(\r\x12\r\n\x05\x66lags\x18\x04 \x01(\r\x12\r\n\x05value\x18\x05 \x01(\x0c\"\'\n\x14\x43mdGetPropertyValues\x12\x0f\n\x07indices\x18\x01 \x03(\r\"N\n\x15RespGetPropertyValues\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\x12\x1c\n\x05props\x18\x02 \x03(\x0b\x32\r.PropertyInfo\"-\n\rPropertyValue\x12\r\n\x05index\x18\x01 \x01(\r\x12\r\n\x05value\x18\x02 \x01(\x0c\"5\n\x14\x43mdSetPropertyValues\x12\x1d\n\x05props\x18\x01 \x03(\x0b\x32\x0e.PropertyValue\"0\n\x15RespSetPropertyValues\x12\x17\n\x06status\x18\x01 \x01(\x0e\x32\x07.Status\"\xfb\x02\n\x10LocalCtrlMessage\x12\x1e\n\x03msg\x18\x01 \x01(\x0e\x32\x11.LocalCtrlMsgType\x12\x32\n\x12\x63md_get_prop_count\x18\n \x01(\x0b\x32\x14.CmdGetPropertyCountH\x00\x12\x34\n\x13resp_get_prop_count\x18\x0b \x01(\x0b\x32\x15.RespGetPropertyCountH\x00\x12\x32\n\x11\x63md_get_prop_vals\x18\x0c \x01(\x0b\x32\x15.CmdGetPropertyValuesH\x00\x12\x34\n\x12resp_get_prop_vals\x18\r \x01(\x0b\x32\x16.RespGetPropertyValuesH\x00\x12\x32\n\x11\x63md_set_prop_vals\x18\x0e \x01(\x0b\x32\x15.CmdSetPropertyValuesH\x00\x12\x34\n\x12resp_set_prop_vals\x18\x0f \x01(\x0b\x32\x16.RespSetPropertyValuesH\x00\x42\t\n\x07payload*\xc7\x01\n\x10LocalCtrlMsgType\x12\x1b\n\x17TypeCmdGetPropertyCount\x10\x00\x12\x1c\n\x18TypeRespGetPropertyCount\x10\x01\x12\x1c\n\x18TypeCmdGetPropertyValues\x10\x04\x12\x1d\n\x19TypeRespGetPropertyValues\x10\x05\x12\x1c\n\x18TypeCmdSetPropertyValues\x10\x06\x12\x1d\n\x19TypeRespSetPropertyValues\x10\x07\x62\x06proto3') + , + dependencies=[constants__pb2.DESCRIPTOR,]) + +_LOCALCTRLMSGTYPE = _descriptor.EnumDescriptor( + name='LocalCtrlMsgType', + full_name='LocalCtrlMsgType', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='TypeCmdGetPropertyCount', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='TypeRespGetPropertyCount', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='TypeCmdGetPropertyValues', index=2, number=4, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='TypeRespGetPropertyValues', index=3, number=5, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='TypeCmdSetPropertyValues', index=4, number=6, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='TypeRespSetPropertyValues', index=5, number=7, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=883, + serialized_end=1082, +) +_sym_db.RegisterEnumDescriptor(_LOCALCTRLMSGTYPE) + +LocalCtrlMsgType = enum_type_wrapper.EnumTypeWrapper(_LOCALCTRLMSGTYPE) +TypeCmdGetPropertyCount = 0 +TypeRespGetPropertyCount = 1 +TypeCmdGetPropertyValues = 4 +TypeRespGetPropertyValues = 5 +TypeCmdSetPropertyValues = 6 +TypeRespSetPropertyValues = 7 + + + +_CMDGETPROPERTYCOUNT = _descriptor.Descriptor( + name='CmdGetPropertyCount', + full_name='CmdGetPropertyCount', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=41, + serialized_end=62, +) + + +_RESPGETPROPERTYCOUNT = _descriptor.Descriptor( + name='RespGetPropertyCount', + full_name='RespGetPropertyCount', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='status', full_name='RespGetPropertyCount.status', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='count', full_name='RespGetPropertyCount.count', index=1, + number=2, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=64, + serialized_end=126, +) + + +_PROPERTYINFO = _descriptor.Descriptor( + name='PropertyInfo', + full_name='PropertyInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='status', full_name='PropertyInfo.status', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='name', full_name='PropertyInfo.name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='type', full_name='PropertyInfo.type', index=2, + number=3, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='flags', full_name='PropertyInfo.flags', index=3, + number=4, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='PropertyInfo.value', index=4, + number=5, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=128, + serialized_end=225, +) + + +_CMDGETPROPERTYVALUES = _descriptor.Descriptor( + name='CmdGetPropertyValues', + full_name='CmdGetPropertyValues', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='indices', full_name='CmdGetPropertyValues.indices', index=0, + number=1, type=13, cpp_type=3, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=227, + serialized_end=266, +) + + +_RESPGETPROPERTYVALUES = _descriptor.Descriptor( + name='RespGetPropertyValues', + full_name='RespGetPropertyValues', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='status', full_name='RespGetPropertyValues.status', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='props', full_name='RespGetPropertyValues.props', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=268, + serialized_end=346, +) + + +_PROPERTYVALUE = _descriptor.Descriptor( + name='PropertyValue', + full_name='PropertyValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='index', full_name='PropertyValue.index', index=0, + number=1, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='PropertyValue.value', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=348, + serialized_end=393, +) + + +_CMDSETPROPERTYVALUES = _descriptor.Descriptor( + name='CmdSetPropertyValues', + full_name='CmdSetPropertyValues', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='props', full_name='CmdSetPropertyValues.props', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=395, + serialized_end=448, +) + + +_RESPSETPROPERTYVALUES = _descriptor.Descriptor( + name='RespSetPropertyValues', + full_name='RespSetPropertyValues', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='status', full_name='RespSetPropertyValues.status', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=450, + serialized_end=498, +) + + +_LOCALCTRLMESSAGE = _descriptor.Descriptor( + name='LocalCtrlMessage', + full_name='LocalCtrlMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='msg', full_name='LocalCtrlMessage.msg', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='cmd_get_prop_count', full_name='LocalCtrlMessage.cmd_get_prop_count', index=1, + number=10, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='resp_get_prop_count', full_name='LocalCtrlMessage.resp_get_prop_count', index=2, + number=11, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='cmd_get_prop_vals', full_name='LocalCtrlMessage.cmd_get_prop_vals', index=3, + number=12, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='resp_get_prop_vals', full_name='LocalCtrlMessage.resp_get_prop_vals', index=4, + number=13, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='cmd_set_prop_vals', full_name='LocalCtrlMessage.cmd_set_prop_vals', index=5, + number=14, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='resp_set_prop_vals', full_name='LocalCtrlMessage.resp_set_prop_vals', index=6, + number=15, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='payload', full_name='LocalCtrlMessage.payload', + index=0, containing_type=None, fields=[]), + ], + serialized_start=501, + serialized_end=880, +) + +_RESPGETPROPERTYCOUNT.fields_by_name['status'].enum_type = constants__pb2._STATUS +_PROPERTYINFO.fields_by_name['status'].enum_type = constants__pb2._STATUS +_RESPGETPROPERTYVALUES.fields_by_name['status'].enum_type = constants__pb2._STATUS +_RESPGETPROPERTYVALUES.fields_by_name['props'].message_type = _PROPERTYINFO +_CMDSETPROPERTYVALUES.fields_by_name['props'].message_type = _PROPERTYVALUE +_RESPSETPROPERTYVALUES.fields_by_name['status'].enum_type = constants__pb2._STATUS +_LOCALCTRLMESSAGE.fields_by_name['msg'].enum_type = _LOCALCTRLMSGTYPE +_LOCALCTRLMESSAGE.fields_by_name['cmd_get_prop_count'].message_type = _CMDGETPROPERTYCOUNT +_LOCALCTRLMESSAGE.fields_by_name['resp_get_prop_count'].message_type = _RESPGETPROPERTYCOUNT +_LOCALCTRLMESSAGE.fields_by_name['cmd_get_prop_vals'].message_type = _CMDGETPROPERTYVALUES +_LOCALCTRLMESSAGE.fields_by_name['resp_get_prop_vals'].message_type = _RESPGETPROPERTYVALUES +_LOCALCTRLMESSAGE.fields_by_name['cmd_set_prop_vals'].message_type = _CMDSETPROPERTYVALUES +_LOCALCTRLMESSAGE.fields_by_name['resp_set_prop_vals'].message_type = _RESPSETPROPERTYVALUES +_LOCALCTRLMESSAGE.oneofs_by_name['payload'].fields.append( + _LOCALCTRLMESSAGE.fields_by_name['cmd_get_prop_count']) +_LOCALCTRLMESSAGE.fields_by_name['cmd_get_prop_count'].containing_oneof = _LOCALCTRLMESSAGE.oneofs_by_name['payload'] +_LOCALCTRLMESSAGE.oneofs_by_name['payload'].fields.append( + _LOCALCTRLMESSAGE.fields_by_name['resp_get_prop_count']) +_LOCALCTRLMESSAGE.fields_by_name['resp_get_prop_count'].containing_oneof = _LOCALCTRLMESSAGE.oneofs_by_name['payload'] +_LOCALCTRLMESSAGE.oneofs_by_name['payload'].fields.append( + _LOCALCTRLMESSAGE.fields_by_name['cmd_get_prop_vals']) +_LOCALCTRLMESSAGE.fields_by_name['cmd_get_prop_vals'].containing_oneof = _LOCALCTRLMESSAGE.oneofs_by_name['payload'] +_LOCALCTRLMESSAGE.oneofs_by_name['payload'].fields.append( + _LOCALCTRLMESSAGE.fields_by_name['resp_get_prop_vals']) +_LOCALCTRLMESSAGE.fields_by_name['resp_get_prop_vals'].containing_oneof = _LOCALCTRLMESSAGE.oneofs_by_name['payload'] +_LOCALCTRLMESSAGE.oneofs_by_name['payload'].fields.append( + _LOCALCTRLMESSAGE.fields_by_name['cmd_set_prop_vals']) +_LOCALCTRLMESSAGE.fields_by_name['cmd_set_prop_vals'].containing_oneof = _LOCALCTRLMESSAGE.oneofs_by_name['payload'] +_LOCALCTRLMESSAGE.oneofs_by_name['payload'].fields.append( + _LOCALCTRLMESSAGE.fields_by_name['resp_set_prop_vals']) +_LOCALCTRLMESSAGE.fields_by_name['resp_set_prop_vals'].containing_oneof = _LOCALCTRLMESSAGE.oneofs_by_name['payload'] +DESCRIPTOR.message_types_by_name['CmdGetPropertyCount'] = _CMDGETPROPERTYCOUNT +DESCRIPTOR.message_types_by_name['RespGetPropertyCount'] = _RESPGETPROPERTYCOUNT +DESCRIPTOR.message_types_by_name['PropertyInfo'] = _PROPERTYINFO +DESCRIPTOR.message_types_by_name['CmdGetPropertyValues'] = _CMDGETPROPERTYVALUES +DESCRIPTOR.message_types_by_name['RespGetPropertyValues'] = _RESPGETPROPERTYVALUES +DESCRIPTOR.message_types_by_name['PropertyValue'] = _PROPERTYVALUE +DESCRIPTOR.message_types_by_name['CmdSetPropertyValues'] = _CMDSETPROPERTYVALUES +DESCRIPTOR.message_types_by_name['RespSetPropertyValues'] = _RESPSETPROPERTYVALUES +DESCRIPTOR.message_types_by_name['LocalCtrlMessage'] = _LOCALCTRLMESSAGE +DESCRIPTOR.enum_types_by_name['LocalCtrlMsgType'] = _LOCALCTRLMSGTYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +CmdGetPropertyCount = _reflection.GeneratedProtocolMessageType('CmdGetPropertyCount', (_message.Message,), dict( + DESCRIPTOR = _CMDGETPROPERTYCOUNT, + __module__ = 'esp_local_ctrl_pb2' + # @@protoc_insertion_point(class_scope:CmdGetPropertyCount) + )) +_sym_db.RegisterMessage(CmdGetPropertyCount) + +RespGetPropertyCount = _reflection.GeneratedProtocolMessageType('RespGetPropertyCount', (_message.Message,), dict( + DESCRIPTOR = _RESPGETPROPERTYCOUNT, + __module__ = 'esp_local_ctrl_pb2' + # @@protoc_insertion_point(class_scope:RespGetPropertyCount) + )) +_sym_db.RegisterMessage(RespGetPropertyCount) + +PropertyInfo = _reflection.GeneratedProtocolMessageType('PropertyInfo', (_message.Message,), dict( + DESCRIPTOR = _PROPERTYINFO, + __module__ = 'esp_local_ctrl_pb2' + # @@protoc_insertion_point(class_scope:PropertyInfo) + )) +_sym_db.RegisterMessage(PropertyInfo) + +CmdGetPropertyValues = _reflection.GeneratedProtocolMessageType('CmdGetPropertyValues', (_message.Message,), dict( + DESCRIPTOR = _CMDGETPROPERTYVALUES, + __module__ = 'esp_local_ctrl_pb2' + # @@protoc_insertion_point(class_scope:CmdGetPropertyValues) + )) +_sym_db.RegisterMessage(CmdGetPropertyValues) + +RespGetPropertyValues = _reflection.GeneratedProtocolMessageType('RespGetPropertyValues', (_message.Message,), dict( + DESCRIPTOR = _RESPGETPROPERTYVALUES, + __module__ = 'esp_local_ctrl_pb2' + # @@protoc_insertion_point(class_scope:RespGetPropertyValues) + )) +_sym_db.RegisterMessage(RespGetPropertyValues) + +PropertyValue = _reflection.GeneratedProtocolMessageType('PropertyValue', (_message.Message,), dict( + DESCRIPTOR = _PROPERTYVALUE, + __module__ = 'esp_local_ctrl_pb2' + # @@protoc_insertion_point(class_scope:PropertyValue) + )) +_sym_db.RegisterMessage(PropertyValue) + +CmdSetPropertyValues = _reflection.GeneratedProtocolMessageType('CmdSetPropertyValues', (_message.Message,), dict( + DESCRIPTOR = _CMDSETPROPERTYVALUES, + __module__ = 'esp_local_ctrl_pb2' + # @@protoc_insertion_point(class_scope:CmdSetPropertyValues) + )) +_sym_db.RegisterMessage(CmdSetPropertyValues) + +RespSetPropertyValues = _reflection.GeneratedProtocolMessageType('RespSetPropertyValues', (_message.Message,), dict( + DESCRIPTOR = _RESPSETPROPERTYVALUES, + __module__ = 'esp_local_ctrl_pb2' + # @@protoc_insertion_point(class_scope:RespSetPropertyValues) + )) +_sym_db.RegisterMessage(RespSetPropertyValues) + +LocalCtrlMessage = _reflection.GeneratedProtocolMessageType('LocalCtrlMessage', (_message.Message,), dict( + DESCRIPTOR = _LOCALCTRLMESSAGE, + __module__ = 'esp_local_ctrl_pb2' + # @@protoc_insertion_point(class_scope:LocalCtrlMessage) + )) +_sym_db.RegisterMessage(LocalCtrlMessage) + + +# @@protoc_insertion_point(module_scope) diff --git a/components/esp_local_ctrl/src/esp_local_ctrl.c b/components/esp_local_ctrl/src/esp_local_ctrl.c new file mode 100644 index 000000000..1eead4d7c --- /dev/null +++ b/components/esp_local_ctrl/src/esp_local_ctrl.c @@ -0,0 +1,417 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include +#include +#include + +#include +#include + +#include +#include "esp_local_ctrl_priv.h" +#include "esp_local_ctrl.pb-c.h" + +#define ESP_LOCAL_CTRL_VERSION "v1.0" + +struct inst_ctx { + protocomm_t *pc; + esp_local_ctrl_config_t config; + esp_local_ctrl_prop_t **props; + size_t props_count; +}; + +struct inst_ctx *local_ctrl_inst_ctx; + +static const char *TAG = "esp_local_ctrl"; + +esp_err_t esp_local_ctrl_start(const esp_local_ctrl_config_t *config) +{ + esp_err_t ret; + + if (!config) { + ESP_LOGE(TAG, "NULL configuration provided"); + return ESP_ERR_INVALID_ARG; + } + + if (!config->transport) { + ESP_LOGE(TAG, "No transport provided"); + return ESP_ERR_INVALID_ARG; + } + + if (config->max_properties == 0) { + ESP_LOGE(TAG, "max_properties must be greater than 0"); + return ESP_ERR_INVALID_ARG; + } + + if (!config->handlers.get_prop_values || + !config->handlers.set_prop_values) { + ESP_LOGE(TAG, "Handlers cannot be null"); + return ESP_ERR_INVALID_ARG; + } + + if (local_ctrl_inst_ctx) { + ESP_LOGW(TAG, "Service already active"); + return ESP_ERR_INVALID_STATE; + } + + local_ctrl_inst_ctx = calloc(1, sizeof(struct inst_ctx)); + if (!local_ctrl_inst_ctx) { + ESP_LOGE(TAG, "Failed to allocate memory for instance"); + return ESP_ERR_NO_MEM; + } + memcpy(&local_ctrl_inst_ctx->config, config, sizeof(local_ctrl_inst_ctx->config)); + + local_ctrl_inst_ctx->props = calloc(local_ctrl_inst_ctx->config.max_properties, + sizeof(esp_local_ctrl_prop_t *)); + if (!local_ctrl_inst_ctx->props) { + ESP_LOGE(TAG, "Failed to allocate memory for properties"); + free(local_ctrl_inst_ctx); + local_ctrl_inst_ctx = NULL; + return ESP_ERR_NO_MEM; + } + + /* Since the config structure will be different for different transport modes, each transport may + * implement a `copy_config()` function, which accepts a configuration structure as input and + * creates a copy of that, which can be kept in the context structure of the `esp_local_ctrl` instance. + * This copy can be later be freed using `free_config()` */ + if (config->transport->copy_config) { + ret = config->transport->copy_config(&local_ctrl_inst_ctx->config.transport_config, + &config->transport_config); + if (ret != ESP_OK) { + esp_local_ctrl_stop(); + return ret; + } + } + + /* For a selected transport mode, endpoints may need to be declared prior to starting the + * `esp_local_ctrl` service, e.g. in case of BLE. By declaration it means that the transport layer + * allocates some resources for an endpoint, and later, after service has started, a handler + * is assigned for that endpoint */ + if (config->transport->declare_ep) { + /* UUIDs are 16bit unique IDs for each endpoint. This may or may not be relevant for + * a chosen transport. We reserve all values from FF50 to FFFF for the internal endpoints. + * The remaining endpoints can be used by the application for its own custom endpoints */ + uint16_t start_uuid = 0xFF50; + ret = config->transport->declare_ep(&local_ctrl_inst_ctx->config.transport_config, + "esp_local_ctrl/version", start_uuid++); + if (ret != ESP_OK) { + esp_local_ctrl_stop(); + return ret; + } + ret = config->transport->declare_ep(&local_ctrl_inst_ctx->config.transport_config, + "esp_local_ctrl/session", start_uuid++); + if (ret != ESP_OK) { + esp_local_ctrl_stop(); + return ret; + } + ret = config->transport->declare_ep(&local_ctrl_inst_ctx->config.transport_config, + "esp_local_ctrl/control", start_uuid++); + if (ret != ESP_OK) { + esp_local_ctrl_stop(); + return ret; + } + } + + local_ctrl_inst_ctx->pc = protocomm_new(); + if (!local_ctrl_inst_ctx->pc) { + ESP_LOGE(TAG, "Failed to create new protocomm instance"); + esp_local_ctrl_stop(); + return ESP_FAIL; + } + + if (config->transport->start_service) { + ret = config->transport->start_service(local_ctrl_inst_ctx->pc, + &local_ctrl_inst_ctx->config.transport_config); + if (ret != ESP_OK) { + esp_local_ctrl_stop(); + return ret; + } + } + + ret = protocomm_set_version(local_ctrl_inst_ctx->pc, "esp_local_ctrl/version", + ESP_LOCAL_CTRL_VERSION); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set version endpoint"); + esp_local_ctrl_stop(); + return ret; + } + + ret = protocomm_set_security(local_ctrl_inst_ctx->pc, "esp_local_ctrl/session", + &protocomm_security0, NULL); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set session endpoint"); + esp_local_ctrl_stop(); + return ret; + } + + ret = protocomm_add_endpoint(local_ctrl_inst_ctx->pc, "esp_local_ctrl/control", + esp_local_ctrl_data_handler, NULL); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set control endpoint"); + esp_local_ctrl_stop(); + return ret; + } + return ESP_OK; +} + +esp_err_t esp_local_ctrl_stop(void) +{ + if (local_ctrl_inst_ctx) { + if (local_ctrl_inst_ctx->config.transport->free_config) { + local_ctrl_inst_ctx->config.transport->free_config(&local_ctrl_inst_ctx->config.transport_config); + } + if (local_ctrl_inst_ctx->pc) { + if (local_ctrl_inst_ctx->config.transport->stop_service) { + local_ctrl_inst_ctx->config.transport->stop_service(local_ctrl_inst_ctx->pc); + } + protocomm_delete(local_ctrl_inst_ctx->pc); + } + if (local_ctrl_inst_ctx->config.handlers.usr_ctx_free_fn) { + local_ctrl_inst_ctx->config.handlers.usr_ctx_free_fn( + local_ctrl_inst_ctx->config.handlers.usr_ctx); + } + + /* Iterate through all properties one by one and free them */ + for (uint32_t i = 0; i < local_ctrl_inst_ctx->config.max_properties; i++) { + if (local_ctrl_inst_ctx->props[i] == NULL) { + continue; + } + /* Release memory allocated for property data */ + free(local_ctrl_inst_ctx->props[i]->name); + if (local_ctrl_inst_ctx->props[i]->ctx_free_fn) { + local_ctrl_inst_ctx->props[i]->ctx_free_fn(local_ctrl_inst_ctx->props[i]->ctx); + } + free(local_ctrl_inst_ctx->props[i]); + } + free(local_ctrl_inst_ctx->props); + free(local_ctrl_inst_ctx); + local_ctrl_inst_ctx = NULL; + } + return ESP_OK; +} + +static int esp_local_ctrl_get_property_index(const char *name) +{ + if (!local_ctrl_inst_ctx || !name) { + return -1; + } + + /* Iterate through all properties one by one + * and find the one with matching name */ + for (uint32_t i = 0; i < local_ctrl_inst_ctx->props_count; i++) { + if (strcmp(local_ctrl_inst_ctx->props[i]->name, name) == 0) { + return i; + } + } + return -1; +} + +esp_err_t esp_local_ctrl_add_property(const esp_local_ctrl_prop_t *prop) +{ + if (!local_ctrl_inst_ctx) { + ESP_LOGE(TAG, "Service not running"); + return ESP_ERR_INVALID_STATE; + } + if (!prop || !prop->name) { + return ESP_ERR_INVALID_ARG; + } + if (esp_local_ctrl_get_property_index(prop->name) >= 0) { + ESP_LOGE(TAG, "Property with name %s exists", prop->name); + return ESP_ERR_INVALID_STATE; + } + + if (local_ctrl_inst_ctx->config.max_properties + == local_ctrl_inst_ctx->props_count) { + ESP_LOGE(TAG, "Max properties limit reached. Cannot add property %s", prop->name); + return ESP_ERR_NO_MEM; + } + + uint32_t i = local_ctrl_inst_ctx->props_count; + local_ctrl_inst_ctx->props[i] = calloc(1, sizeof(esp_local_ctrl_prop_t)); + if (!local_ctrl_inst_ctx->props[i]) { + ESP_LOGE(TAG, "Failed to allocate memory for new property %s", prop->name); + return ESP_ERR_NO_MEM; + } + local_ctrl_inst_ctx->props[i]->name = strdup(prop->name); + if (!local_ctrl_inst_ctx->props[i]->name) { + ESP_LOGE(TAG, "Failed to allocate memory for property data %s", prop->name); + free(local_ctrl_inst_ctx->props[i]); + local_ctrl_inst_ctx->props[i] = NULL; + return ESP_ERR_NO_MEM; + } + local_ctrl_inst_ctx->props[i]->type = prop->type; + local_ctrl_inst_ctx->props[i]->size = prop->size; + local_ctrl_inst_ctx->props[i]->flags = prop->flags; + local_ctrl_inst_ctx->props[i]->ctx = prop->ctx; + local_ctrl_inst_ctx->props[i]->ctx_free_fn = prop->ctx_free_fn; + local_ctrl_inst_ctx->props_count++; + return ESP_OK; +} + + +esp_err_t esp_local_ctrl_remove_property(const char *name) +{ + int idx = esp_local_ctrl_get_property_index(name); + if (idx < 0) { + ESP_LOGE(TAG, "Property %s not found", name); + return ESP_ERR_NOT_FOUND; + } + + /* Release memory allocated for property data */ + if (local_ctrl_inst_ctx->props[idx]->ctx_free_fn) { + local_ctrl_inst_ctx->props[idx]->ctx_free_fn( + local_ctrl_inst_ctx->props[idx]->ctx); + } + free(local_ctrl_inst_ctx->props[idx]->name); + free(local_ctrl_inst_ctx->props[idx]); + local_ctrl_inst_ctx->props[idx++] = NULL; + + /* Move the following properties forward, so that there is + * no empty space between two properties */ + for (uint32_t i = idx; i < local_ctrl_inst_ctx->props_count; i++) { + if (local_ctrl_inst_ctx->props[i] == NULL) { + break; + } + local_ctrl_inst_ctx->props[i-1] = local_ctrl_inst_ctx->props[i]; + } + local_ctrl_inst_ctx->props_count--; + return ESP_OK; +} + +const esp_local_ctrl_prop_t *esp_local_ctrl_get_property(const char *name) +{ + int idx = esp_local_ctrl_get_property_index(name); + if (idx < 0) { + ESP_LOGE(TAG, "Property %s not found", name); + return NULL; + } + + return local_ctrl_inst_ctx->props[idx]; +} + +esp_err_t esp_local_ctrl_get_prop_count(size_t *count) +{ + if (!local_ctrl_inst_ctx) { + ESP_LOGE(TAG, "Service not running"); + return ESP_ERR_INVALID_STATE; + } + if (!count) { + return ESP_ERR_INVALID_ARG; + } + *count = local_ctrl_inst_ctx->props_count; + return ESP_OK; +} + +esp_err_t esp_local_ctrl_get_prop_values(size_t total_indices, uint32_t *indices, + esp_local_ctrl_prop_t *props, + esp_local_ctrl_prop_val_t *values) +{ + if (!local_ctrl_inst_ctx) { + ESP_LOGE(TAG, "Service not running"); + return ESP_ERR_INVALID_STATE; + } + if (!indices || !props || !values) { + return ESP_ERR_INVALID_ARG; + } + + /* Convert indices to names */ + for (size_t i = 0; i < total_indices; i++) { + if (indices[i] >= local_ctrl_inst_ctx->props_count) { + ESP_LOGE(TAG, "Invalid property index %d", indices[i]); + return ESP_ERR_INVALID_ARG; + } + props[i].name = local_ctrl_inst_ctx->props[indices[i]]->name; + props[i].type = local_ctrl_inst_ctx->props[indices[i]]->type; + props[i].flags = local_ctrl_inst_ctx->props[indices[i]]->flags; + props[i].size = local_ctrl_inst_ctx->props[indices[i]]->size; + props[i].ctx = local_ctrl_inst_ctx->props[indices[i]]->ctx; + } + + esp_local_ctrl_handlers_t *h = &local_ctrl_inst_ctx->config.handlers; + esp_err_t ret = h->get_prop_values(total_indices, props, values, h->usr_ctx); + + /* Properties with fixed sizes need to be checked */ + for (size_t i = 0; i < total_indices; i++) { + if (local_ctrl_inst_ctx->props[indices[i]]->size != 0) { + values[i].size = local_ctrl_inst_ctx->props[indices[i]]->size; + } + } + return ret; +} + +esp_err_t esp_local_ctrl_set_prop_values(size_t total_indices, uint32_t *indices, + const esp_local_ctrl_prop_val_t *values) +{ + if (!local_ctrl_inst_ctx) { + ESP_LOGE(TAG, "Service not running"); + return ESP_ERR_INVALID_STATE; + } + if (!indices || !values) { + return ESP_ERR_INVALID_ARG; + } + + esp_local_ctrl_prop_t *props = calloc(total_indices, + sizeof(esp_local_ctrl_prop_t)); + if (!props) { + ESP_LOGE(TAG, "Unable to allocate memory for properties array"); + return ESP_ERR_NO_MEM; + } + for (size_t i = 0; i < total_indices; i++) { + if (indices[i] >= local_ctrl_inst_ctx->props_count) { + ESP_LOGE(TAG, "Invalid property index %d", indices[i]); + free(props); + return ESP_ERR_INVALID_ARG; + } + + /* Properties with fixed sizes need to be checked */ + if ((local_ctrl_inst_ctx->props[indices[i]]->size != values[i].size) && + (local_ctrl_inst_ctx->props[indices[i]]->size != 0)) { + ESP_LOGE(TAG, "Invalid property size %d. Expected %d", + values[i].size, local_ctrl_inst_ctx->props[indices[i]]->size); + free(props); + return ESP_ERR_INVALID_ARG; + } + + props[i].name = local_ctrl_inst_ctx->props[indices[i]]->name; + props[i].type = local_ctrl_inst_ctx->props[indices[i]]->type; + props[i].flags = local_ctrl_inst_ctx->props[indices[i]]->flags; + props[i].size = local_ctrl_inst_ctx->props[indices[i]]->size; + props[i].ctx = local_ctrl_inst_ctx->props[indices[i]]->ctx; + } + + esp_local_ctrl_handlers_t *h = &local_ctrl_inst_ctx->config.handlers; + esp_err_t ret = h->set_prop_values(total_indices, props, values, h->usr_ctx); + + free(props); + return ret; +} + +esp_err_t esp_local_ctrl_set_handler(const char *ep_name, + protocomm_req_handler_t handler, + void *priv_data) +{ + esp_err_t ret = ESP_ERR_INVALID_STATE; + + if (local_ctrl_inst_ctx) { + ret = protocomm_add_endpoint(local_ctrl_inst_ctx->pc, ep_name, + handler, priv_data); + } + + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to register endpoint handler"); + } + return ret; +} diff --git a/components/esp_local_ctrl/src/esp_local_ctrl_handler.c b/components/esp_local_ctrl/src/esp_local_ctrl_handler.c new file mode 100644 index 000000000..23a92abd6 --- /dev/null +++ b/components/esp_local_ctrl/src/esp_local_ctrl_handler.c @@ -0,0 +1,298 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include +#include +#include + +#include "esp_local_ctrl.h" +#include "esp_local_ctrl_priv.h" +#include "esp_local_ctrl.pb-c.h" + +#define SAFE_ALLOCATION(type, var) \ + type *var = (type *) malloc(sizeof(type)); \ + if (!var) { \ + ESP_LOGE(TAG, "Error allocating memory"); \ + return ESP_ERR_NO_MEM; \ + } + +static const char* TAG = "esp_local_ctrl_handler"; + +typedef struct esp_local_ctrl_cmd { + int cmd_num; + esp_err_t (*command_handler)(LocalCtrlMessage *req, + LocalCtrlMessage *resp, void **ctx); +} esp_local_ctrl_cmd_t; + +static esp_err_t cmd_get_prop_count_handler(LocalCtrlMessage *req, + LocalCtrlMessage *resp, void **ctx); + +static esp_err_t cmd_get_prop_vals_handler(LocalCtrlMessage *req, + LocalCtrlMessage *resp, void **ctx); + +static esp_err_t cmd_set_prop_vals_handler(LocalCtrlMessage *req, + LocalCtrlMessage *resp, void **ctx); + +static esp_local_ctrl_cmd_t cmd_table[] = { + { + .cmd_num = LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyCount, + .command_handler = cmd_get_prop_count_handler + }, + { + .cmd_num = LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyValues, + .command_handler = cmd_get_prop_vals_handler + }, + { + .cmd_num = LOCAL_CTRL_MSG_TYPE__TypeCmdSetPropertyValues, + .command_handler = cmd_set_prop_vals_handler + } +}; + +static uint16_t err_to_status(esp_err_t err) +{ + uint16_t status; + switch (err) { + case ESP_OK: + status = STATUS__Success; + break; + case ESP_ERR_INVALID_ARG: + status = STATUS__InvalidArgument; + break; + case ESP_ERR_INVALID_STATE: + status = STATUS__InvalidProto; + break; + default: + status = STATUS__InternalError; + } + return status; +} + +static esp_err_t cmd_get_prop_count_handler(LocalCtrlMessage *req, + LocalCtrlMessage *resp, void **ctx) +{ + SAFE_ALLOCATION(RespGetPropertyCount, resp_payload); + resp_get_property_count__init(resp_payload); + + size_t prop_count = 0; + resp_payload->status = err_to_status(esp_local_ctrl_get_prop_count(&prop_count)); + resp_payload->count = prop_count; + resp->payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_GET_PROP_COUNT; + resp->resp_get_prop_count = resp_payload; + ESP_LOGD(TAG, "Got properties count %d", prop_count); + return ESP_OK; +} + +typedef void (*prop_val_free_fn_t)(void *val); + +static esp_err_t cmd_get_prop_vals_handler(LocalCtrlMessage *req, + LocalCtrlMessage *resp, void **ctx) +{ + SAFE_ALLOCATION(RespGetPropertyValues, resp_payload); + resp_get_property_values__init(resp_payload); + + esp_local_ctrl_prop_val_t *vals = calloc(req->cmd_get_prop_vals->n_indices, + sizeof(esp_local_ctrl_prop_val_t)); + esp_local_ctrl_prop_t *descs = calloc(req->cmd_get_prop_vals->n_indices, + sizeof(esp_local_ctrl_prop_t)); + prop_val_free_fn_t *free_fns = calloc(req->cmd_get_prop_vals->n_indices, + sizeof(prop_val_free_fn_t)); + resp_payload->props = calloc(req->cmd_get_prop_vals->n_indices, + sizeof(PropertyInfo *)); + if (!vals || !descs || !free_fns || !resp_payload->props) { + ESP_LOGE(TAG, "Failed to allocate memory for getting values"); + free(vals); + free(descs); + free(free_fns); + free(resp_payload->props); + free(resp_payload); + return ESP_ERR_NO_MEM; + } + + esp_err_t ret = esp_local_ctrl_get_prop_values(req->cmd_get_prop_vals->n_indices, + req->cmd_get_prop_vals->indices, + descs, vals); + resp_payload->status = err_to_status(ret); + if (ret == ESP_OK) { + resp_payload->n_props = 0; + for (size_t i = 0; i < req->cmd_get_prop_vals->n_indices; i++) { + resp_payload->props[i] = malloc(sizeof(PropertyInfo)); + if (!resp_payload->props[i]) { + resp_payload->status = STATUS__InternalError; + break; + } + resp_payload->n_props++; + property_info__init(resp_payload->props[i]); + resp_payload->props[i]->name = descs[i].name; + resp_payload->props[i]->type = descs[i].type; + resp_payload->props[i]->flags = descs[i].flags; + resp_payload->props[i]->value.data = vals[i].data; + resp_payload->props[i]->value.len = vals[i].size; + free_fns[i] = vals[i].free_fn; + } + } + resp->payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_GET_PROP_VALS; + resp->resp_get_prop_vals = resp_payload; + (*ctx) = (void *)free_fns; + free(vals); + free(descs); + + /* Unless it's a fatal error, always return ESP_OK, otherwise + * the underlying connection will be closed by protocomm */ + return ESP_OK; +} + +static esp_err_t cmd_set_prop_vals_handler(LocalCtrlMessage *req, + LocalCtrlMessage *resp, void **ctx) +{ + SAFE_ALLOCATION(RespSetPropertyValues, resp_payload); + resp_set_property_values__init(resp_payload); + + uint32_t *idxs = calloc(req->cmd_set_prop_vals->n_props, sizeof(uint32_t)); + esp_local_ctrl_prop_val_t *vals = calloc(req->cmd_set_prop_vals->n_props, + sizeof(esp_local_ctrl_prop_val_t)); + if (!idxs || !vals) { + ESP_LOGE(TAG, "Failed to allocate memory for setting values"); + free(idxs); + free(vals); + return ESP_ERR_NO_MEM; + } + for (size_t i = 0; i < req->cmd_set_prop_vals->n_props; i++) { + idxs[i] = req->cmd_set_prop_vals->props[i]->index; + vals[i].data = req->cmd_set_prop_vals->props[i]->value.data; + vals[i].size = req->cmd_set_prop_vals->props[i]->value.len; + } + + esp_err_t ret = esp_local_ctrl_set_prop_values(req->cmd_set_prop_vals->n_props, + idxs, vals); + resp_payload->status = err_to_status(ret); + resp->payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_SET_PROP_VALS; + resp->resp_set_prop_vals = resp_payload; + free(idxs); + free(vals); + + /* Unless it's a fatal error, always return ESP_OK, otherwise + * the underlying connection will be closed by protocomm */ + return ESP_OK; +} + +static int lookup_cmd_handler(int cmd_id) +{ + int i; + + for (i = 0; i < sizeof(cmd_table)/sizeof(esp_local_ctrl_cmd_t); i++) { + if (cmd_table[i].cmd_num == cmd_id) { + return i; + } + } + + return -1; +} + +static void esp_local_ctrl_command_cleanup(LocalCtrlMessage *resp, void **ctx) +{ + if (!resp) { + return; + } + + switch (resp->msg) { + case LOCAL_CTRL_MSG_TYPE__TypeRespGetPropertyCount: + free(resp->resp_get_prop_count); + break; + case LOCAL_CTRL_MSG_TYPE__TypeRespGetPropertyValues: { + if (resp->resp_get_prop_vals) { + prop_val_free_fn_t *free_fns = (prop_val_free_fn_t *)(*ctx); + for (size_t i = 0; i < resp->resp_get_prop_vals->n_props; i++) { + if (free_fns[i]) { + free_fns[i](resp->resp_get_prop_vals->props[i]->value.data); + } + free(resp->resp_get_prop_vals->props[i]); + } + free(free_fns); + free(resp->resp_get_prop_vals->props); + free(resp->resp_get_prop_vals); + } + } + break; + case LOCAL_CTRL_MSG_TYPE__TypeRespSetPropertyValues: + free(resp->resp_set_prop_vals); + break; + default: + ESP_LOGE(TAG, "Unsupported response type in cleanup_handler"); + } + return; +} + +static esp_err_t esp_local_ctrl_command_dispatcher(LocalCtrlMessage *req, + LocalCtrlMessage *resp, + void **ctx) +{ + int cmd_index = lookup_cmd_handler(req->msg); + if (cmd_index < 0) { + ESP_LOGE(TAG, "Invalid command handler lookup"); + return ESP_ERR_INVALID_ARG; + } + + esp_err_t ret = cmd_table[cmd_index].command_handler(req, resp, ctx); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Error executing command handler"); + return ret; + } + + return ESP_OK; +} + +esp_err_t esp_local_ctrl_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, void *priv_data) +{ + void *temp_ctx = NULL; + LocalCtrlMessage *req = local_ctrl_message__unpack(NULL, inlen, inbuf); + if (!req) { + ESP_LOGE(TAG, "Unable to unpack payload data"); + return ESP_ERR_INVALID_ARG; + } + + LocalCtrlMessage resp; + local_ctrl_message__init(&resp); + resp.msg = req->msg + 1; /* Response is request + 1 */ + + esp_err_t ret = esp_local_ctrl_command_dispatcher(req, &resp, &temp_ctx); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "command dispatcher failed"); + esp_local_ctrl_command_cleanup(&resp, &temp_ctx); + local_ctrl_message__free_unpacked(req, NULL); + return ESP_FAIL; + } + + local_ctrl_message__free_unpacked(req, NULL); + + *outlen = local_ctrl_message__get_packed_size(&resp); + if (*outlen <= 0) { + ESP_LOGE(TAG, "Invalid encoding for response"); + esp_local_ctrl_command_cleanup(&resp, &temp_ctx); + return ESP_FAIL; + } + + *outbuf = (uint8_t *) malloc(*outlen); + if (!*outbuf) { + ESP_LOGE(TAG, "System out of memory"); + esp_local_ctrl_command_cleanup(&resp, &temp_ctx); + return ESP_ERR_NO_MEM; + } + + local_ctrl_message__pack(&resp, *outbuf); + esp_local_ctrl_command_cleanup(&resp, &temp_ctx); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, *outbuf, *outlen, ESP_LOG_DEBUG); + return ESP_OK; +} diff --git a/components/esp_local_ctrl/src/esp_local_ctrl_priv.h b/components/esp_local_ctrl/src/esp_local_ctrl_priv.h new file mode 100644 index 000000000..9241054e35 --- /dev/null +++ b/components/esp_local_ctrl/src/esp_local_ctrl_priv.h @@ -0,0 +1,153 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** + * @brief `esp_local_ctrl` transport specific structure + * + * Every supported transport layer should have the following functions + * implemented for starting, stopping and configuring a protocomm service + */ +struct esp_local_ctrl_transport { + /** + * Handler for starting a protocomm service as per specified configuration + */ + esp_err_t (*start_service) (protocomm_t *pc, + const esp_local_ctrl_transport_config_t *config); + + /** + * Handler for stopping a protocomm service + */ + void (*stop_service) (protocomm_t *pc); + + /** + * Handler for creating a copy of the transport specific configuration + */ + esp_err_t (*copy_config) (esp_local_ctrl_transport_config_t *dest_config, + const esp_local_ctrl_transport_config_t *src_config); + + /** + * Handler for allocating resources corresponding to a protocomm endpoint. + * Usually when adding a new endpoint `protocomm_endpoint_add()` API is used, + * but the transport layer may need to perform resource allocation for + * each endpoint, prior to starting the protocomm instance. This handler + * is useful in that case, as it is called before `start_service()`. + */ + esp_err_t (*declare_ep) (esp_local_ctrl_transport_config_t *config, + const char *ep_name, uint16_t ep_uuid); + + /** + * Handler for freeing a transport specific configuration + */ + void (*free_config) (esp_local_ctrl_transport_config_t *config); +}; + +/** + * @brief Protocomm handler for `esp_local_ctrl` + * + * This is the handler which is responsible for processing incoming requests + * over a protocomm channel, then invokes one of the following functions + * depending upon the request type: + * - `esp_local_ctrl_get_prop_count()` + * - `esp_local_ctrl_get_prop_values()` + * -` esp_local_ctrl_set_prop_values()` + * The output of the above functions are used to form the response messages + * corresponding to request types. The formed response messages are packed and + * sent back via the protocomm channel. + * + * @param[in] session_id A number to identify an ongoing session between + * device and client + * @param[in] inbuf Buffer which holds serialized / packed request data + * @param[in] inlen Length of input buffer + * @param[out] outbuf Buffer which holds serialized / packed response data + * @param[out] outlen Length of output buffer + * @param[in] priv_data Private data associated with `esp_local_ctrl` endpoint + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Failure + */ +esp_err_t esp_local_ctrl_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen, + uint8_t **outbuf, ssize_t *outlen, void *priv_data); + +/** + * @brief Use this for obtaining total number of properties registered + * with `esp_local_ctrl` service + * + * @param[out] count Pointer to variable where the result is to be stored + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Failure + */ +esp_err_t esp_local_ctrl_get_prop_count(size_t *count); + +/** + * @brief Get descriptions and values of multiple properties at the same time. + * The properties are requested by indices. This internally calls the + * `get_prop_values` handler specified in the `esp_local_ctrl_handlers_t` + * structure. Since `get_prop_values` accepts property structure, the + * indices are first converted to the corresponding `esp_local_ctrl_prop_t` + * internally. + * + * @param[in] total_indices The number of elements in the `indices` array argument + * @param[in] indices An array of indices, that specify which properties to get + * @param[out] props A pre-allocated array of empty property structures, elements of + * which are to be populated with names, types and flags of those + * properties which correspond to the provided indices + * @param[out] values A pre-allocated array of empty value structures, elements of + * which are to be populated with values and sizes of those + * properties which correspond to the provided indices + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Failure + */ +esp_err_t esp_local_ctrl_get_prop_values(size_t total_indices, uint32_t *indices, + esp_local_ctrl_prop_t *props, + esp_local_ctrl_prop_val_t *values); + +/** + * @brief Set values of multiple properties at the same time. The properties to + * set are specified by indices. This internally calls the `set_prop_values` + * handler specified in the `esp_local_ctrl_handlers_t` structure. Since + * `set_prop_values` accepts property structures, the indices are first + * converted to the corresponding `esp_local_ctrl_prop_t` internally. + * + * @param[in] total_indices The number of elements in the `indices` array argument + * @param[in] indices An array of indices, that specify which properties to set + * @param[in] values A array of values. Every value should have the correct + * size, if it is for setting a fixed size property, else + * error will be generated and none of the properties will + * be set to any of the given values + * + * @return + * - ESP_OK : Success + * - ESP_FAIL : Failure + */ +esp_err_t esp_local_ctrl_set_prop_values(size_t total_indices, uint32_t *indices, + const esp_local_ctrl_prop_val_t *values); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_local_ctrl/src/esp_local_ctrl_transport_ble.c b/components/esp_local_ctrl/src/esp_local_ctrl_transport_ble.c new file mode 100644 index 000000000..248b41089 --- /dev/null +++ b/components/esp_local_ctrl/src/esp_local_ctrl_transport_ble.c @@ -0,0 +1,140 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include +#include +#include + +#include +#include + +#include "esp_local_ctrl_priv.h" + +#define LOCAL_CTRL_VERSION "v1.0" + +static const char *TAG = "esp_local_ctrl_transport_ble"; + +static esp_err_t start_ble_transport(protocomm_t *pc, const esp_local_ctrl_transport_config_t *config) +{ + if (!config || !config->ble) { + ESP_LOGE(TAG, "NULL configuration provided"); + return ESP_ERR_INVALID_ARG; + } + return protocomm_ble_start(pc, config->ble); +} + +static void stop_ble_transport(protocomm_t *pc) +{ + protocomm_ble_stop(pc); +} + +static esp_err_t copy_ble_config(esp_local_ctrl_transport_config_t *dest_config, const esp_local_ctrl_transport_config_t *src_config) +{ + if (!dest_config || !src_config || !src_config->ble) { + ESP_LOGE(TAG, "NULL arguments provided"); + return ESP_ERR_INVALID_ARG; + } + + dest_config->ble = calloc(1, sizeof(protocomm_ble_config_t)); + if (!dest_config->ble) { + ESP_LOGE(TAG, "Failed to allocate memory for BLE transport config"); + return ESP_ERR_NO_MEM; + } + + /* Copy BLE device name */ + memcpy(dest_config->ble->device_name, + src_config->ble->device_name, + sizeof(src_config->ble->device_name)); + + /* Copy Service UUID */ + memcpy(dest_config->ble->service_uuid, + src_config->ble->service_uuid, + sizeof(src_config->ble->service_uuid)); + + dest_config->ble->nu_lookup_count = 0; + if (src_config->ble->nu_lookup_count) { + /* Copy any provided name-uuid lookup table */ + dest_config->ble->nu_lookup = calloc(src_config->ble->nu_lookup_count, + sizeof(protocomm_ble_name_uuid_t)); + if (!dest_config->ble->nu_lookup) { + ESP_LOGE(TAG, "Failed to allocate memory for BLE characteristic names"); + free(dest_config->ble); + return ESP_ERR_NO_MEM; + } + for (uint16_t i = 0; i < src_config->ble->nu_lookup_count; i++) { + dest_config->ble->nu_lookup[i].uuid = src_config->ble->nu_lookup[i].uuid; + if (!src_config->ble->nu_lookup[i].name) { + ESP_LOGE(TAG, "Endpoint name cannot be null"); + return ESP_ERR_INVALID_ARG; + } + dest_config->ble->nu_lookup[i].name = strdup(src_config->ble->nu_lookup[i].name); + if (!dest_config->ble->nu_lookup[i].name) { + ESP_LOGE(TAG, "Failed to allocate memory for endpoint name"); + return ESP_ERR_NO_MEM; + } + dest_config->ble->nu_lookup_count++; + } + } + return ESP_OK; +} + +static esp_err_t declare_endpoint(esp_local_ctrl_transport_config_t *config, const char *ep_name, uint16_t ep_uuid) +{ + if (!config || !config->ble) { + ESP_LOGE(TAG, "NULL configuration provided"); + return ESP_ERR_INVALID_ARG; + } + + protocomm_ble_name_uuid_t *nu_lookup = realloc(config->ble->nu_lookup, + (config->ble->nu_lookup_count + 1) + * sizeof(protocomm_ble_name_uuid_t)); + if (!nu_lookup) { + ESP_LOGE(TAG, "Failed to allocate memory for new endpoint entry"); + return ESP_ERR_NO_MEM; + } + config->ble->nu_lookup = nu_lookup; + nu_lookup[config->ble->nu_lookup_count].uuid = ep_uuid; + nu_lookup[config->ble->nu_lookup_count].name = strdup(ep_name); + if (!nu_lookup[config->ble->nu_lookup_count].name) { + ESP_LOGE(TAG, "Failed to allocate memory for new endpoint name"); + return ESP_ERR_NO_MEM; + } + config->ble->nu_lookup_count++; + return ESP_OK; +} + +static void free_config(esp_local_ctrl_transport_config_t *config) +{ + if (config && config->ble) { + for (unsigned int i = 0; i < config->ble->nu_lookup_count; i++) { + free((void*) config->ble->nu_lookup[i].name); + } + free(config->ble->nu_lookup); + free(config->ble); + config->ble = NULL; + } +} + +const esp_local_ctrl_transport_t *esp_local_ctrl_get_transport_ble(void) +{ + static const esp_local_ctrl_transport_t tp = { + .start_service = start_ble_transport, + .stop_service = stop_ble_transport, + .copy_config = copy_ble_config, + .declare_ep = declare_endpoint, + .free_config = free_config + }; + return &tp; +}; diff --git a/components/esp_local_ctrl/src/esp_local_ctrl_transport_httpd.c b/components/esp_local_ctrl/src/esp_local_ctrl_transport_httpd.c new file mode 100644 index 000000000..2089c3dbf --- /dev/null +++ b/components/esp_local_ctrl/src/esp_local_ctrl_transport_httpd.c @@ -0,0 +1,128 @@ +// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 +#include +#include +#include + +#include +#include +#include +#include + +#include "esp_local_ctrl_priv.h" + +#define LOCAL_CTRL_VERSION "v1.0" + +static const char *TAG = "esp_local_ctrl_transport_httpd"; + +static httpd_handle_t server_handle = NULL; + +static esp_err_t start_httpd_transport(protocomm_t *pc, const esp_local_ctrl_transport_config_t *config) +{ + if (!config || !config->httpd) { + ESP_LOGE(TAG, "NULL configuration provided"); + return ESP_ERR_INVALID_ARG; + } + + /* Extract configured port */ + uint16_t port = ( + config->httpd->transport_mode == HTTPD_SSL_TRANSPORT_SECURE ? + config->httpd->port_secure : + config->httpd->port_insecure + ); + + esp_err_t err = mdns_service_add("Local Control Service", "_esp_local_ctrl", + "_tcp", port, NULL, 0); + if (err != ESP_OK) { + /* mDNS is not mandatory for provisioning to work, + * so print warning and return without failure */ + ESP_LOGW(TAG, "Error adding mDNS service! Check if mDNS is running"); + } else { + /* Information to identify the roles of the various + * protocomm endpoint URIs provided by the service */ + err |= mdns_service_txt_item_set("_esp_local_ctrl", "_tcp", + "version_endpoint", "/esp_local_ctrl/version"); + err |= mdns_service_txt_item_set("_esp_local_ctrl", "_tcp", + "session_endpoint", "/esp_local_ctrl/session"); + err |= mdns_service_txt_item_set("_esp_local_ctrl", "_tcp", + "control_endpoint", "/esp_local_ctrl/control"); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error adding mDNS service text item"); + } + } + + err = httpd_ssl_start(&server_handle, config->httpd); + if (ESP_OK != err) { + ESP_LOGE(TAG, "Error starting HTTPS service!"); + mdns_service_remove("_esp_local_ctrl", "_tcp"); + return err; + } + + protocomm_httpd_config_t pc_conf = { + .ext_handle_provided = true, + .data = { + .handle = &server_handle + } + }; + + return protocomm_httpd_start(pc, &pc_conf); +} + +static void stop_httpd_transport(protocomm_t *pc) +{ + mdns_service_remove("_esp_local_ctrl", "_tcp"); + protocomm_httpd_stop(pc); + httpd_ssl_stop(server_handle); + server_handle = NULL; +} + +static esp_err_t copy_httpd_config(esp_local_ctrl_transport_config_t *dest_config, const esp_local_ctrl_transport_config_t *src_config) +{ + if (!dest_config || !src_config || !src_config->httpd) { + ESP_LOGE(TAG, "NULL configuration provided"); + return ESP_ERR_INVALID_ARG; + } + + dest_config->httpd = calloc(1, sizeof(httpd_ssl_config_t)); + if (!dest_config->httpd) { + ESP_LOGE(TAG, "Failed to allocate memory for HTTPD transport config"); + return ESP_ERR_NO_MEM; + } + memcpy(dest_config->httpd, + src_config->httpd, + sizeof(httpd_ssl_config_t)); + return ESP_OK; +} + +static void free_config(esp_local_ctrl_transport_config_t *config) +{ + if (config && config->httpd) { + free(config->httpd); + config->httpd = NULL; + } +} + +const esp_local_ctrl_transport_t *esp_local_ctrl_get_transport_httpd(void) +{ + static const esp_local_ctrl_transport_t tp = { + .start_service = start_httpd_transport, + .stop_service = stop_httpd_transport, + .copy_config = copy_httpd_config, + .declare_ep = NULL, + .free_config = free_config + }; + return &tp; +}; diff --git a/components/protocomm/include/transports/protocomm_ble.h b/components/protocomm/include/transports/protocomm_ble.h index b7d70e59e..b30d5707b 100644 --- a/components/protocomm/include/transports/protocomm_ble.h +++ b/components/protocomm/include/transports/protocomm_ble.h @@ -48,7 +48,7 @@ typedef struct name_uuid { /** * @brief Config parameters for protocomm BLE service */ -typedef struct { +typedef struct protocomm_ble_config { /** * BLE device name being broadcast at the time of provisioning */ diff --git a/docs/Doxyfile b/docs/Doxyfile index ee91aab2b..d9bf17dc2 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -107,8 +107,11 @@ INPUT = \ ../../components/mdns/include/mdns.h \ ../../components/esp_http_client/include/esp_http_client.h \ ../../components/esp_websocket_client/include/esp_websocket_client.h \ + ## HTTP / HTTPS Server ../../components/esp_http_server/include/esp_http_server.h \ ../../components/esp_https_server/include/esp_https_server.h \ + ## ESP Local Ctrl + ../../components/esp_local_ctrl/include/esp_local_ctrl.h \ ## ## Provisioning - API Reference ## diff --git a/docs/en/api-reference/protocols/esp_local_ctrl.rst b/docs/en/api-reference/protocols/esp_local_ctrl.rst new file mode 100644 index 000000000..3c6ba60b3 --- /dev/null +++ b/docs/en/api-reference/protocols/esp_local_ctrl.rst @@ -0,0 +1,206 @@ +ESP Local Control +================= + +Overview +-------- +ESP Local Control (**esp_local_ctrl**) component in ESP-IDF provides capability to control an ESP device over Wi-Fi + HTTPS or BLE. It provides access to application defined **properties** that are available for reading / writing via a set of configurable handlers. + +Initialization of the **esp_local_ctrl** service over BLE transport is performed as follows: + + .. highlight:: c + + :: + + esp_local_ctrl_config_t config = { + .transport = ESP_LOCAL_CTRL_TRANSPORT_BLE, + .transport_config = { + .ble = & (protocomm_ble_config_t) { + .device_name = SERVICE_NAME, + .service_uuid = { + /* LSB <--------------------------------------- + * ---------------------------------------> MSB */ + 0x21, 0xd5, 0x3b, 0x8d, 0xbd, 0x75, 0x68, 0x8a, + 0xb4, 0x42, 0xeb, 0x31, 0x4a, 0x1e, 0x98, 0x3d + } + } + }, + .handlers = { + /* User defined handler functions */ + .get_prop_values = get_property_values, + .set_prop_values = set_property_values, + .usr_ctx = NULL, + .usr_ctx_free_fn = NULL + }, + /* Maximum number of properties that may be set */ + .max_properties = 10 + }; + + /* Start esp_local_ctrl service */ + ESP_ERROR_CHECK(esp_local_ctrl_start(&config)); + + +Similarly for HTTPS transport: + + .. highlight:: c + + :: + + /* Set the configuration */ + httpd_ssl_config_t https_conf = HTTPD_SSL_CONFIG_DEFAULT(); + + /* Load server certificate */ + extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); + extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); + https_conf.cacert_pem = cacert_pem_start; + https_conf.cacert_len = cacert_pem_end - cacert_pem_start; + + /* Load server private key */ + extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); + extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); + https_conf.prvtkey_pem = prvtkey_pem_start; + https_conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start; + + esp_local_ctrl_config_t config = { + .transport = ESP_LOCAL_CTRL_TRANSPORT_HTTPD, + .transport_config = { + .httpd = &https_conf + }, + .handlers = { + /* User defined handler functions */ + .get_prop_values = get_property_values, + .set_prop_values = set_property_values, + .usr_ctx = NULL, + .usr_ctx_free_fn = NULL + }, + /* Maximum number of properties that may be set */ + .max_properties = 10 + }; + + /* Start esp_local_ctrl service */ + ESP_ERROR_CHECK(esp_local_ctrl_start(&config)); + + +Creating a property +=================== + +Now that we know how to start the **esp_local_ctrl** service, let's add a property to it. Each property must have a unique `name` (string), a `type` (e.g. enum), `flags` (bit fields) and `size`. + +The `size` is to be kept 0, if we want our property value to be of variable length (e.g. if its a string or bytestream). For fixed length property value data-types, like int, float, etc., setting the `size` field to the right value, helps **esp_local_ctrl** to perform internal checks on arguments received with write requests. + +The interpretation of `type` and `flags` fields is totally upto the application, hence they may be used as enumerations, bitfields, or even simple integers. One way is to use `type` values to classify properties, while `flags` to specify characteristics of a property. + +Here is an example property which is to function as a timestamp. It is assumed that the application defines `TYPE_TIMESTAMP` and `READONLY`, which are used for setting the `type` and `flags` fields here. + + .. highlight:: c + + :: + + /* Create a timestamp property */ + esp_local_ctrl_prop_t timestamp = { + .name = "timestamp", + .type = TYPE_TIMESTAMP, + .size = sizeof(int32_t), + .flags = READONLY, + .ctx = func_get_time, + .ctx_free_fn = NULL + }; + + /* Now register the property */ + esp_local_ctrl_add_property(×tamp); + + +Also notice that there is a ctx field, which is set to point to some custom `func_get_time()`. This can be used inside the property get / set handlers to retrieve timestamp. + +Here is an example of `get_prop_values()` handler, which is used for retrieving the timestamp. + + .. highlight:: c + + :: + + static esp_err_t get_property_values(size_t props_count, + const esp_local_ctrl_prop_t *props, + esp_local_ctrl_prop_val_t *prop_values, + void *usr_ctx) + { + for (uint32_t i = 0; i < props_count; i++) { + ESP_LOGI(TAG, "Reading %s", props[i].name); + if (props[i].type == TYPE_TIMESTAMP) { + /* Obtain the timer function from ctx */ + int32_t (*func_get_time)(void) = props[i].ctx; + + /* Use static variable for saving the value. + * This is essential because the value has to be + * valid even after this function returns. + * Alternative is to use dynamic allocation + * and set the free_fn field */ + static int32_t ts = func_get_time(); + prop_values[i].data = &ts; + } + } + return ESP_OK; + } + + +Here is an example of `set_prop_values()` handler. Notice how we restrict from writing to read-only properties. + + .. highlight:: c + + :: + + static esp_err_t set_property_values(size_t props_count, + const esp_local_ctrl_prop_t *props, + const esp_local_ctrl_prop_val_t *prop_values, + void *usr_ctx) + { + for (uint32_t i = 0; i < props_count; i++) { + if (props[i].flags & READONLY) { + ESP_LOGE(TAG, "Cannot write to read-only property %s", props[i].name); + return ESP_ERR_INVALID_ARG; + } else { + ESP_LOGI(TAG, "Setting %s", props[i].name); + + /* For keeping it simple, lets only log the incoming data */ + ESP_LOG_BUFFER_HEX_LEVEL(TAG, prop_values[i].data, + prop_values[i].size, ESP_LOG_INFO); + } + } + return ESP_OK; + } + + +For complete example see :example:`protocols/esp_local_ctrl` + +Client Side Implementation +========================== + +The client side implementation will have establish a protocomm session with the device first, over the supported mode of transport, and then send and receive protobuf messages understood by the **esp_local_ctrl** service. The service will translate these messages into requests and then call the appropriate handlers (set / get). Then, the generated response for each handler is again packed into a protobuf message and transmitted back to the client. + +See below the various protobuf messages understood by the **esp_local_ctrl** service: + +1. `get_prop_count` : This should simply return the total number of properties supported by the service +2. `get_prop_values` : This accepts an array of indices and should return the information (name, type, flags) and values of the properties corresponding to those indices +3. `set_prop_values` : This accepts an array of indices and an array of new values, which are used for setting the values of the properties corresponding to the indices + +Note that indices may or may not be the same for a property, across multiple sessions. Therefore, the client must only use the names of the properties to uniquely identify them. So, every time a new session is established, the client should first call `get_prop_count` and then `get_prop_values`, hence form an index to name mapping for all properties. Now when calling `set_prop_values` for a set of properties, it must first convert the names to indexes, using the created mapping. As emphasized earlier, the client must refresh the index to name mapping every time a new session is established with the same device. + +The various protocomm endpoints provided by **esp_local_ctrl** are listed below: + +.. list-table:: Endpoints provided by ESP Local Control + :widths: 10 25 50 + :header-rows: 1 + + * - Endpoint Name (BLE + GATT Server) + - URI (HTTPS Server + mDNS) + - Description + * - esp_local_ctrl/version + - https://.local/esp_local_ctrl/version + - Endpoint used for retrieving version string + * - esp_local_ctrl/control + - https://.local/esp_local_ctrl/control + - Endpoint used for sending / receiving control messages + + +API Reference +------------- + +.. include:: /_build/inc/esp_local_ctrl.inc diff --git a/docs/en/api-reference/protocols/index.rst b/docs/en/api-reference/protocols/index.rst index 97386abe4..c35c954f3 100644 --- a/docs/en/api-reference/protocols/index.rst +++ b/docs/en/api-reference/protocols/index.rst @@ -14,6 +14,7 @@ Application Protocols ASIO ESP-MQTT Modbus + Local Control Code examples for this API section are provided in the :example:`protocols` directory of ESP-IDF examples. diff --git a/docs/zh_CN/api-reference/protocols/esp_local_ctrl.rst b/docs/zh_CN/api-reference/protocols/esp_local_ctrl.rst new file mode 100644 index 000000000..587542d5d --- /dev/null +++ b/docs/zh_CN/api-reference/protocols/esp_local_ctrl.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/protocols/esp_local_ctrl.rst diff --git a/docs/zh_CN/api-reference/protocols/index.rst b/docs/zh_CN/api-reference/protocols/index.rst index 9d893c29b..d385082b3 100644 --- a/docs/zh_CN/api-reference/protocols/index.rst +++ b/docs/zh_CN/api-reference/protocols/index.rst @@ -14,6 +14,7 @@ ASIO ESP-MQTT Modbus slave + Local Control 此 API 部分的示例代码在 ESP-IDF 示例工程的 :example:`protocols` 目录下提供。 diff --git a/examples/protocols/esp_local_ctrl/CMakeLists.txt b/examples/protocols/esp_local_ctrl/CMakeLists.txt new file mode 100644 index 000000000..2ee3492ec --- /dev/null +++ b/examples/protocols/esp_local_ctrl/CMakeLists.txt @@ -0,0 +1,10 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +# (Not part of the boilerplate) +# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(esp_local_ctrl) diff --git a/examples/protocols/esp_local_ctrl/Makefile b/examples/protocols/esp_local_ctrl/Makefile new file mode 100644 index 000000000..e3e0a7ac6 --- /dev/null +++ b/examples/protocols/esp_local_ctrl/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := esp_local_ctrl + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/protocols/esp_local_ctrl/README.md b/examples/protocols/esp_local_ctrl/README.md new file mode 100644 index 000000000..81abdea2e --- /dev/null +++ b/examples/protocols/esp_local_ctrl/README.md @@ -0,0 +1,93 @@ +# ESP Local Control using HTTPS server + +This example creates a `esp_local_ctrl` service over HTTPS transport, for securely controlling the device over local network. In this case the device name is resolved through `mDNS`, which in this example is `my_esp_ctrl_device.local`. + +See the `esp_local_ctrl` component documentation for details. + +Before using the example, run `make menuconfig` (or `idf.py menuconfig` if using CMake build system) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../README.md) for more details. + +## Client Side Implementation + +A python test script `scripts/esp_local_ctrl.py` has been provided for as a client side application for controlling the device over the same Wi-Fi network. The script relies on a pre-generated `main/certs/rootCA.pem` to verify the server certificate. The server side private key and certificate can also be found under `main/certs`, namely `prvtkey.pem` and `cacert.pem`. + +After configuring the Wi-Fi, flashing and booting the device, run: + +``` +python scripts/esp_local_ctrl.py +``` +Sample output: + +``` +python2 scripts/esp_local_ctrl.py + +==== Acquiring properties information ==== + +==== Acquired properties information ==== + +==== Available Properties ==== +S.N. Name Type Flags Value +[ 1] timestamp (us) TIME(us) Read-Only 168561481 +[ 2] property1 INT32 123456 +[ 3] property2 BOOLEAN Read-Only True +[ 4] property3 STRING + +Select properties to set (0 to re-read, 'q' to quit) : 0 + +==== Available Properties ==== +S.N. Name Type Flags Value +[ 1] timestamp (us) TIME(us) Read-Only 22380117 +[ 2] property1 INT32 123456 +[ 3] property2 BOOLEAN Read-Only False +[ 4] property3 STRING + +Select properties to set (0 to re-read, 'q' to quit) : 2,4 +Enter value to set for property (property1) : -5555 +Enter value to set for property (property3) : hello world! + +==== Available Properties ==== +S.N. Name Type Flags Value +[ 1] timestamp (us) TIME(us) Read-Only 55110859 +[ 2] property1 INT32 -5555 +[ 3] property2 BOOLEAN Read-Only False +[ 4] property3 STRING hello world! + +Select properties to set (0 to re-read, 'q' to quit) : q +Quitting... +``` + +The script also allows to connect over BLE, and provide a custom service name. To display the list of supported parameters, run: + +``` +python scripts/esp_local_ctrl.py --help +``` + +## Certificates + +You can generate a new server certificate using the OpenSSL command line tool. + +For the purpose of this example, lets generate a rootCA, which we will use to sign the server certificates and which the client will use to verify the server certificate during SSL handshake. You will need to set a password for encrypting the generated `rootkey.pem`. + +``` +openssl req -new -x509 -subj "/CN=root" -days 3650 -sha256 -out rootCA.pem -keyout rootkey.pem +``` + +Now generate a certificate signing request for the server, along with its private key `prvtkey.pem`. + +``` +openssl req -newkey rsa:2048 -nodes -keyout prvtkey.pem -days 3650 -out server.csr -subj "/CN=my_esp_ctrl_device.local" +``` + +Now use the previously generated rootCA to process the server's certificate signing request, and generate a signed certificate `cacert.pem`. The password set for encrypting `rootkey.pem` earlier, has to be entered during this step. + +``` +openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootkey.pem -CAcreateserial -out cacert.pem -days 500 -sha256 +``` + +Now that we have `rootCA.pem`, `cacert.pem` and `prvtkey.pem`, copy these into main/certs. Note that only the server related files (`cacert.pem` and `prvtkey.pem`) are embedded into the firmware. + +Expiry time and metadata fields can be adjusted in the invocation. + +Please see the `openssl` man pages (man `openssl-req`) for more details. + +It is **strongly recommended** to not reuse the example certificate in your application; +it is included only for demonstration. diff --git a/examples/protocols/esp_local_ctrl/main/CMakeLists.txt b/examples/protocols/esp_local_ctrl/main/CMakeLists.txt new file mode 100644 index 000000000..87f57d107 --- /dev/null +++ b/examples/protocols/esp_local_ctrl/main/CMakeLists.txt @@ -0,0 +1,8 @@ +set(COMPONENT_SRCS "app_main.c" "esp_local_ctrl_service.c") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +set(COMPONENT_EMBED_TXTFILES + "certs/cacert.pem" + "certs/prvtkey.pem") + +register_component() diff --git a/examples/protocols/esp_local_ctrl/main/Kconfig.projbuild b/examples/protocols/esp_local_ctrl/main/Kconfig.projbuild new file mode 100644 index 000000000..7403f8f83 --- /dev/null +++ b/examples/protocols/esp_local_ctrl/main/Kconfig.projbuild @@ -0,0 +1,20 @@ +menu "Example Configuration" + + config ESP_WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config ESP_WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + + config ESP_MAXIMUM_RETRY + int "Maximum retry" + default 5 + help + Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. +endmenu diff --git a/examples/protocols/esp_local_ctrl/main/app_main.c b/examples/protocols/esp_local_ctrl/main/app_main.c new file mode 100644 index 000000000..d19feffda --- /dev/null +++ b/examples/protocols/esp_local_ctrl/main/app_main.c @@ -0,0 +1,111 @@ +/* Local Ctrl Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" + +#include "lwip/err.h" +#include "lwip/sys.h" + +/* The examples use WiFi configuration that you can set via 'make menuconfig'. + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ +#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID +#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD +#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY + +/* FreeRTOS event group to signal when we are connected*/ +static EventGroupHandle_t s_wifi_event_group; + +/* The event group allows multiple bits for each event, but we only care about one event + * - are we connected to the AP with an IP? */ +const int WIFI_CONNECTED_BIT = BIT0; + +static const char *TAG = "local_ctrl_example"; + +static int s_retry_num = 0; + +static void event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { + esp_wifi_connect(); + xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + s_retry_num++; + ESP_LOGI(TAG, "retry to connect to the AP"); + } + ESP_LOGI(TAG,"connect to the AP fail"); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + ESP_LOGI(TAG, "got ip:%s", + ip4addr_ntoa(&event->ip_info.ip)); + s_retry_num = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + } +} + +void wifi_init_sta() +{ + s_wifi_event_group = xEventGroupCreate(); + + tcpip_adapter_init(); + + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL)); + + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_ESP_WIFI_SSID, + .password = EXAMPLE_ESP_WIFI_PASS + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); + ESP_ERROR_CHECK(esp_wifi_start() ); + + ESP_LOGI(TAG, "wifi_init_sta finished."); + ESP_LOGI(TAG, "connect to ap SSID:%s password:%s", + EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); +} + +/* Function responsible for configuring and starting the esp_local_ctrl service. + * See local_ctrl_service.c for implementation */ +extern void start_esp_local_ctrl_service(void); + +void app_main() +{ + //Initialize NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_LOGI(TAG, "ESP_WIFI_MODE_STA"); + wifi_init_sta(); + start_esp_local_ctrl_service(); +} diff --git a/examples/protocols/esp_local_ctrl/main/certs/cacert.pem b/examples/protocols/esp_local_ctrl/main/certs/cacert.pem new file mode 100644 index 000000000..6323ff0dd --- /dev/null +++ b/examples/protocols/esp_local_ctrl/main/certs/cacert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICuTCCAaECFFnose4o8INWoH5BA5TOCz2e9zAOMA0GCSqGSIb3DQEBCwUAMA8x +DTALBgNVBAMMBHJvb3QwHhcNMTkwNjI1MTkyMTU4WhcNMjAxMTA2MTkyMTU4WjAj +MSEwHwYDVQQDDBhteV9lc3BfY3RybF9kZXZpY2UubG9jYWwwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDY2B46AdNfrJRGgHy7cECmEMxOWn8CvygC2g77 +Gog/DWxkqaEksBJt8qQcqGLumv+HfXE4erNPGU+RTNal+zMXHliIMVi2IiVw5uuC +Tze7cK28HDvC5noED/TWGSJIaCQAUP/GdE0sqCJ1O7W0IhrZBjsmen4d0nPrInCz +X9YDOfaWxdwnBJ3q0+7ZTSWETbDrKUJ0tgPe6m96j/zRYCtCo2Dpu/pZvPyIvXwT +zt6enB8cwDtk35KwOrscAJGNqkCRyKaNvOSuHv9/02vpzwqk/J6JbIcXFVNuYSPg +0wb0iltMqn0IwC3KyaI9gBg0VexMeOhFV/gRt8dvEYehtTB7AgMBAAEwDQYJKoZI +hvcNAQELBQADggEBAFnKbunoBvKun4hJV4sp0ztDKpjOJevsQp3X36egm4NGCpEj +cdHxEmAvmeiu/12C6OfvFmZ/QiqNmp2gihpy4DiuxWnI+iC9JjfYuWTsKj+xcVkw +4IvGZbFtE9YW+XwNWqXPi1urVk9wKpZmCWpWgFWnLwPgIQs16+y3+CQF3vefX9Iy +aqmYrTYkBpLEXRjYJeU253mvN6FXQgOoPuld1Ph+IO+DUEJr+zeM88xkmjAo37ej +VkCMXA5HqdT64HuZC1RnnbpP76assgFW2oTycG28jzHSYjuK2q1PIoZtzpW8Sv7i +jn17E6ryf24r1DVkQByR54rvzl6Qu3M8TJe6EYI= +-----END CERTIFICATE----- diff --git a/examples/protocols/esp_local_ctrl/main/certs/prvtkey.pem b/examples/protocols/esp_local_ctrl/main/certs/prvtkey.pem new file mode 100644 index 000000000..fbf3a5026 --- /dev/null +++ b/examples/protocols/esp_local_ctrl/main/certs/prvtkey.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDY2B46AdNfrJRG +gHy7cECmEMxOWn8CvygC2g77Gog/DWxkqaEksBJt8qQcqGLumv+HfXE4erNPGU+R +TNal+zMXHliIMVi2IiVw5uuCTze7cK28HDvC5noED/TWGSJIaCQAUP/GdE0sqCJ1 +O7W0IhrZBjsmen4d0nPrInCzX9YDOfaWxdwnBJ3q0+7ZTSWETbDrKUJ0tgPe6m96 +j/zRYCtCo2Dpu/pZvPyIvXwTzt6enB8cwDtk35KwOrscAJGNqkCRyKaNvOSuHv9/ +02vpzwqk/J6JbIcXFVNuYSPg0wb0iltMqn0IwC3KyaI9gBg0VexMeOhFV/gRt8dv +EYehtTB7AgMBAAECggEBAJSvM6Kgp9fdVNo2tdAsOxfjQsOjB53RhtTVwhnpamyZ +fq5TJZwrYqejDWZdC2ECRJ4ZpG2OrK5a85T0s+Whpbl/ZEMWWvaf2T5eCDQUr2lF +7MqkLVIJiLaKXl4DY990EONqpsbj7hrluqLZ61B1ZiVTQXGz4g/+wt8CgXZtCyiv +7XOTTmQueugq4f54JBX5isdB7/xLaXV3kycaEK1b6ZVFYB3ii5IKKsX7RK/ksA6O +fRrQ8702prqphPfbjZ9wPHif/zLiyiF2FG6OX1Y3aZe1npRsvuH2c3M2h+HGAQUR +3lDxMTNbsE8E+XKZFVAVdMqot2RfxENSHoJHcp1R2YECgYEA9qe1+eOZKd0w5lC1 +PuG6FLAAbK1nuv/ovESEHtILTLFkMijgAqcWjtp1klS86IBJLnjv+GYxZu2n1WI9 +QLnh++NNTjRGCMM2Adf5SBJ/5F85rpgzz7Yur1guqkUQx/2dmErOaWQ4IO304VlM +vrJB8+XmAiysEgJOkK0Mx8xRVcECgYEA4Q9GBADLryvwjisp/PdTRXOvd7pJRGH3 +SdC1k/nBsmpmbouc0ihqzOiiN0kUjE2yLSlhwxxWBJqNSzOk9z5/LB4TNRqH9gCL +rUN67FgzwR5H70OblWpcjWRurFq34+ZWEmCG+1qUwZMT7dYe4CiDYnVjcwfUpQwN +qRpjeMLDrTsCgYEAgo1CRIGzD/WDbGRLinzvgQOnNd6SiOfqx7t8MtP6Jx29as83 +wi+uQO5gTJONaYJ9OZvJaDCu9UvVCZx1z0yT0D7/K+V/LCQm8dLenscr6jR802y7 +/7TuAOEr0fO8bh5Oy8zMc/wXuVY5xwz9EfJH9lA47e23JdESxIDTwuziIAECgYEA +qKQdPtqpxbUTKDTH3bomN6CcFwcL56XQ+wrdROidb+eyoZsUA5YtkSWwh+TG9Osz +XAvqKZ2OBx0YSwWD05CNEq3mjqA2yOtXvpkV/wuInGjoVi0+5BMzDu/2zkecC7WJ +QXP7MVWKqhJfmJQdxrIU4S49OvDfMl15zwDrEI5AugkCgYBn5+/KvrA8GGWD9p1r +qwjoojGBvCJn1Kj/s+IQYePYeiRe6/eSPGFHRyON9aMGjZmeiqyBA4bW71Wf8Rs1 +X5LSwZhJpCTjO4B92w40u0r86Jxmp5Wz+zHUeM3mO2E6lAF+15YjhxpMT0YOmHFE ++oKD8U6dMjkTqntavBauz8M8fQ== +-----END PRIVATE KEY----- diff --git a/examples/protocols/esp_local_ctrl/main/certs/rootCA.pem b/examples/protocols/esp_local_ctrl/main/certs/rootCA.pem new file mode 100644 index 000000000..dcc340d6a --- /dev/null +++ b/examples/protocols/esp_local_ctrl/main/certs/rootCA.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIC/zCCAeegAwIBAgIUH0R6q5vbgMnMZgD5r4xSu+WhSMwwDQYJKoZIhvcNAQEL +BQAwDzENMAsGA1UEAwwEcm9vdDAeFw0xOTA2MjUxOTIxNDVaFw0yOTA2MjIxOTIx +NDVaMA8xDTALBgNVBAMMBHJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDGDetEF+4HEOU5uoxvHsYAksmpF1tjw/M+aKtyGuTWInJPDJ3YjjjnF7hb +ylx5W7Qj4O4N+TmqYkwA4ztq2CXSmX1uc7OOfxU/wED663NoC2P1Mw0fI5fX2518 +WdJeQilYymIOilmdtNqU9ad/3RdSZg+fxL5z9MTidHlUyzJG5LlO1cDiYRRURj9S +Fc2wWEUCETGA78ADCxKsdf2gBZDcZo/PHNXZc7fi2K18T5UmkRd50aoSLWUNY5tT +4DsyL19PUJCmtwcoLMT3p3kmepN4C0JByOxWceIvlAbq7+L3zMURWfpBcIqxXvEP +Y/JXw7GCfTJgjUz1IoHVz/ERNtrnAgMBAAGjUzBRMB0GA1UdDgQWBBSaztoCfcw+ +mBrfMXLCBU8mOyj2BTAfBgNVHSMEGDAWgBSaztoCfcw+mBrfMXLCBU8mOyj2BTAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCSgkWG4QAblyxXN/R5 +tNLKbzQIbaMj8uXSdcVtNHrNfydA0Sq2M8w7mT7N2axiMAusN3fhgztQvkWCvKdy +ou++NpFBb11+QJ2chgatLtoR7QPQ2TVlTUObAh2ZSt1jDOqvGQynbYqJ+9N6BKpK +S8faScaWP78J02TSMiNIvh8iYukZPMdCyJaHw2x0PtCRYVBSlFIwC5dn/sIJgyrV +g8RAlnsKTCQC3X20AQ851aID6JXDIaTn9pn9PN0XJC+iButpLZM4ZHHZpBtSQZ+d +6lD0tvS8bysCEkamDMt3z8/ncsytAS08VoFqwXdY3EXF8T+sKSi7+ACJXE/kivwu +8jvm +-----END CERTIFICATE----- diff --git a/examples/protocols/esp_local_ctrl/main/component.mk b/examples/protocols/esp_local_ctrl/main/component.mk new file mode 100644 index 000000000..b120d66e5 --- /dev/null +++ b/examples/protocols/esp_local_ctrl/main/component.mk @@ -0,0 +1,7 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +COMPONENT_EMBED_TXTFILES := certs/cacert.pem +COMPONENT_EMBED_TXTFILES += certs/prvtkey.pem diff --git a/examples/protocols/esp_local_ctrl/main/esp_local_ctrl_service.c b/examples/protocols/esp_local_ctrl/main/esp_local_ctrl_service.c new file mode 100644 index 000000000..2e80baeeb --- /dev/null +++ b/examples/protocols/esp_local_ctrl/main/esp_local_ctrl_service.c @@ -0,0 +1,274 @@ +/* Local Ctrl Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +static const char *TAG = "control"; + +#define SERVICE_NAME "my_esp_ctrl_device" + +/* Custom allowed property types */ +enum property_types { + PROP_TYPE_TIMESTAMP = 0, + PROP_TYPE_INT32, + PROP_TYPE_BOOLEAN, + PROP_TYPE_STRING, +}; + +/* Custom flags that can be set for a property */ +enum property_flags { + PROP_FLAG_READONLY = (1 << 0) +}; + +/********* Handler functions for responding to control requests / commands *********/ + +static esp_err_t get_property_values(size_t props_count, + const esp_local_ctrl_prop_t props[], + esp_local_ctrl_prop_val_t prop_values[], + void *usr_ctx) +{ + for (uint32_t i = 0; i < props_count; i++) { + ESP_LOGI(TAG, "Reading property : %s", props[i].name); + /* For the purpose of this example, to keep things simple + * we have set the context pointer of each property to + * point to its value (except for timestamp) */ + switch (props[i].type) { + case PROP_TYPE_INT32: + case PROP_TYPE_BOOLEAN: + /* No need to set size for these types as sizes where + * specified when declaring the properties, unlike for + * string type. */ + prop_values[i].data = props[i].ctx; + break; + case PROP_TYPE_TIMESTAMP: { + /* Get the time stamp */ + static int64_t ts = 0; + ts = esp_timer_get_time(); + + /* Set the current time. Since this is statically + * allocated, we don't need to provide a free_fn */ + prop_values[i].data = &ts; + break; + } + case PROP_TYPE_STRING: { + char **prop3_value = (char **) props[i].ctx; + if (*prop3_value == NULL) { + prop_values[i].size = 0; + prop_values[i].data = NULL; + } else { + /* We could try dynamically allocating the output value, + * and it should get freed automatically after use, as + * `esp_local_ctrl` internally calls the provided `free_fn` */ + prop_values[i].size = strlen(*prop3_value); + prop_values[i].data = strdup(*prop3_value); + if (!prop_values[i].data) { + return ESP_ERR_NO_MEM; + } + prop_values[i].free_fn = free; + } + } + default: + break; + } + } + return ESP_OK; +} + +static esp_err_t set_property_values(size_t props_count, + const esp_local_ctrl_prop_t props[], + const esp_local_ctrl_prop_val_t prop_values[], + void *usr_ctx) +{ + for (uint32_t i = 0; i < props_count; i++) { + /* Cannot set the value of a read-only property */ + if (props[i].flags & PROP_FLAG_READONLY) { + ESP_LOGE(TAG, "%s is read-only", props[i].name); + return ESP_ERR_INVALID_ARG; + } + /* For the purpose of this example, to keep things simple + * we have set the context pointer of each property to + * point to its value (except for timestamp) */ + switch (props[i].type) { + case PROP_TYPE_STRING: { + /* Free the previously set string */ + char **prop3_value = (char **) props[i].ctx; + free(*prop3_value); + *prop3_value = NULL; + + /* Copy the input string */ + if (prop_values[i].size) { + *prop3_value = strndup((const char *)prop_values[i].data, prop_values[i].size); + if (*prop3_value == NULL) { + return ESP_ERR_NO_MEM; + } + ESP_LOGI(TAG, "Setting %s value to %s", props[i].name, (const char*)*prop3_value); + } + } + break; + case PROP_TYPE_INT32: { + const int32_t *new_value = (const int32_t *) prop_values[i].data; + ESP_LOGI(TAG, "Setting %s value to %d", props[i].name, *new_value); + memcpy(props[i].ctx, new_value, sizeof(int32_t)); + } + break; + case PROP_TYPE_BOOLEAN: { + const bool *value = (const bool *) prop_values[i].data; + ESP_LOGI(TAG, "Setting %s value to %d", props[i].name, *value); + memcpy(props[i].ctx, value, sizeof(bool)); + } + break; + default: + break; + } + } + return ESP_OK; +} + +/******************************************************************************/ + +/* A custom free_fn to free a pointer to a string as + * well as the string being pointed to */ +static void free_str(void *arg) +{ + char **ptr_to_strptr = (char **)arg; + if (ptr_to_strptr) { + free(*ptr_to_strptr); + free(ptr_to_strptr); + } +} + +/* Function used by app_main to start the esp_local_ctrl service */ +void start_esp_local_ctrl_service(void) +{ + /* Set the configuration */ + httpd_ssl_config_t https_conf = HTTPD_SSL_CONFIG_DEFAULT(); + + /* Load server certificate */ + extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); + extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); + https_conf.cacert_pem = cacert_pem_start; + https_conf.cacert_len = cacert_pem_end - cacert_pem_start; + + /* Load server private key */ + extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); + extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); + https_conf.prvtkey_pem = prvtkey_pem_start; + https_conf.prvtkey_len = prvtkey_pem_end - prvtkey_pem_start; + + esp_local_ctrl_config_t config = { + .transport = ESP_LOCAL_CTRL_TRANSPORT_HTTPD, + .transport_config = { + .httpd = &https_conf + }, + .handlers = { + /* User defined handler functions */ + .get_prop_values = get_property_values, + .set_prop_values = set_property_values, + .usr_ctx = NULL, + .usr_ctx_free_fn = NULL + }, + /* Maximum number of properties that may be set */ + .max_properties = 10 + }; + + mdns_init(); + mdns_hostname_set(SERVICE_NAME); + + /* Start esp_local_ctrl service */ + ESP_ERROR_CHECK(esp_local_ctrl_start(&config)); + ESP_LOGI(TAG, "esp_local_ctrl service started with name : %s", SERVICE_NAME); + + /* Create a timestamp property. The client should see this as a read-only property. + * Property value is fetched using `esp_timer_get_time()` in the `get_prop_values` + * handler */ + esp_local_ctrl_prop_t timestamp = { + .name = "timestamp (us)", + .type = PROP_TYPE_TIMESTAMP, + .size = sizeof(int64_t), + .flags = PROP_FLAG_READONLY, + .ctx = NULL, + .ctx_free_fn = NULL + }; + + /* Create a writable integer property. Use dynamically allocated memory + * for storing its value and pass it as context, so that it can be accessed + * inside the set / get handlers. */ + int32_t *prop1_value = malloc(sizeof(int32_t)); + assert(prop1_value != NULL); + + /* Initialize the property value */ + *prop1_value = 123456789; + + /* Populate the property structure accordingly. Since, we would want the memory + * occupied by the property value to be freed automatically upon call to + * `esp_local_ctrl_stop()` or `esp_local_ctrl_remove_property()`, the `ctx_free_fn` + * field will need to be set with the appropriate de-allocation function, + * which in this case is simply `free()` */ + esp_local_ctrl_prop_t property1 = { + .name = "property1", + .type = PROP_TYPE_INT32, + .size = sizeof(int32_t), + .flags = 0, + .ctx = prop1_value, + .ctx_free_fn = free + }; + + /* Create another read-only property. Just for demonstration, we use statically + * allocated value. No `ctx_free_fn` needs to be set for this */ + static bool prop2_value = false; + + esp_local_ctrl_prop_t property2 = { + .name = "property2", + .type = PROP_TYPE_BOOLEAN, + .size = sizeof(bool), + .flags = PROP_FLAG_READONLY, + .ctx = &prop2_value, + .ctx_free_fn = NULL + }; + + /* Create a variable sized property. Its context is a pointer for storing the + * pointer to a dynamically allocate string, therefore it will require a + * customized free function `free_str()` */ + char **prop3_value = calloc(1, sizeof(char *)); + assert(prop3_value != NULL); + + esp_local_ctrl_prop_t property3 = { + .name = "property3", + .type = PROP_TYPE_STRING, + .size = 0, // When zero, this is assumed to be of variable size + .flags = 0, + .ctx = prop3_value, + .ctx_free_fn = free_str + }; + + /* Now register the properties */ + ESP_ERROR_CHECK(esp_local_ctrl_add_property(×tamp)); + ESP_ERROR_CHECK(esp_local_ctrl_add_property(&property1)); + ESP_ERROR_CHECK(esp_local_ctrl_add_property(&property2)); + ESP_ERROR_CHECK(esp_local_ctrl_add_property(&property3)); + + /* Just for fun, let us keep toggling the value + * of the boolean property2, every 1 second */ + while (1) { + vTaskDelay(1000 / portTICK_RATE_MS); + prop2_value = !prop2_value; + } +} diff --git a/examples/protocols/esp_local_ctrl/scripts/esp_local_ctrl.py b/examples/protocols/esp_local_ctrl/scripts/esp_local_ctrl.py new file mode 100644 index 000000000..23888bd2d --- /dev/null +++ b/examples/protocols/esp_local_ctrl/scripts/esp_local_ctrl.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python +# +# Copyright 2018 Espressif Systems (Shanghai) PTE LTD +# +# 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. +# + +from __future__ import print_function +from future.utils import tobytes +from builtins import input +import os +import sys +import struct +import argparse + +import proto + +try: + import esp_prov + +except ImportError: + idf_path = os.environ['IDF_PATH'] + sys.path.insert(1, idf_path + "/tools/esp_prov") + import esp_prov + + +# Set this to true to allow exceptions to be thrown +config_throw_except = False + + +# Property types enum +PROP_TYPE_TIMESTAMP = 0 +PROP_TYPE_INT32 = 1 +PROP_TYPE_BOOLEAN = 2 +PROP_TYPE_STRING = 3 + + +# Property flags enum +PROP_FLAG_READONLY = (1 << 0) + + +def prop_typestr(prop): + if prop["type"] == PROP_TYPE_TIMESTAMP: + return "TIME(us)" + elif prop["type"] == PROP_TYPE_INT32: + return "INT32" + elif prop["type"] == PROP_TYPE_BOOLEAN: + return "BOOLEAN" + elif prop["type"] == PROP_TYPE_STRING: + return "STRING" + return "UNKNOWN" + + +def encode_prop_value(prop, value): + try: + if prop["type"] == PROP_TYPE_TIMESTAMP: + return struct.pack('q', value) + elif prop["type"] == PROP_TYPE_INT32: + return struct.pack('i', value) + elif prop["type"] == PROP_TYPE_BOOLEAN: + return struct.pack('?', value) + elif prop["type"] == PROP_TYPE_STRING: + return tobytes(value) + return value + except struct.error as e: + print(e) + return None + + +def decode_prop_value(prop, value): + try: + if prop["type"] == PROP_TYPE_TIMESTAMP: + return struct.unpack('q', value)[0] + elif prop["type"] == PROP_TYPE_INT32: + return struct.unpack('i', value)[0] + elif prop["type"] == PROP_TYPE_BOOLEAN: + return struct.unpack('?', value)[0] + elif prop["type"] == PROP_TYPE_STRING: + return value.decode('latin-1') + return value + except struct.error as e: + print(e) + return None + + +def str_to_prop_value(prop, strval): + try: + if prop["type"] == PROP_TYPE_TIMESTAMP: + return int(strval) + elif prop["type"] == PROP_TYPE_INT32: + return int(strval) + elif prop["type"] == PROP_TYPE_BOOLEAN: + return bool(strval) + elif prop["type"] == PROP_TYPE_STRING: + return strval + return strval + except ValueError as e: + print(e) + return None + + +def prop_is_readonly(prop): + return (prop["flags"] & PROP_FLAG_READONLY) is not 0 + + +def on_except(err): + if config_throw_except: + raise RuntimeError(err) + else: + print(err) + + +def get_transport(sel_transport, service_name): + try: + tp = None + if (sel_transport == 'http'): + example_path = os.environ['IDF_PATH'] + "/examples/protocols/esp_local_ctrl" + cert_path = example_path + "/main/certs/rootCA.pem" + tp = esp_prov.transport.Transport_HTTP(service_name, cert_path) + elif (sel_transport == 'ble'): + tp = esp_prov.transport.Transport_BLE( + devname=service_name, service_uuid='0000ffff-0000-1000-8000-00805f9b34fb', + nu_lookup={'esp_local_ctrl/version': '0001', + 'esp_local_ctrl/session': '0002', + 'esp_local_ctrl/control': '0003'} + ) + return tp + except RuntimeError as e: + on_except(e) + return None + + +def version_match(tp, expected, verbose=False): + try: + response = tp.send_data('esp_local_ctrl/version', expected) + return (response.lower() == expected.lower()) + except Exception as e: + on_except(e) + return None + + +def get_all_property_values(tp): + try: + props = [] + message = proto.get_prop_count_request() + response = tp.send_data('esp_local_ctrl/control', message) + count = proto.get_prop_count_response(response) + if count == 0: + raise RuntimeError("No properties found!") + indices = [i for i in range(count)] + message = proto.get_prop_vals_request(indices) + response = tp.send_data('esp_local_ctrl/control', message) + props = proto.get_prop_vals_response(response) + if len(props) != count: + raise RuntimeError("Incorrect count of properties!") + for p in props: + p["value"] = decode_prop_value(p, p["value"]) + return props + except RuntimeError as e: + on_except(e) + return [] + + +def set_property_values(tp, props, indices, values, check_readonly=False): + try: + if check_readonly: + for index in indices: + if prop_is_readonly(props[index]): + raise RuntimeError("Cannot set value of Read-Only property") + message = proto.set_prop_vals_request(indices, values) + response = tp.send_data('esp_local_ctrl/control', message) + return proto.set_prop_vals_response(response) + except RuntimeError as e: + on_except(e) + return False + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(add_help=False) + + parser = argparse.ArgumentParser(description="Control an ESP32 running esp_local_ctrl service") + + parser.add_argument("--version", dest='version', type=str, + help="Protocol version", default='') + + parser.add_argument("--transport", dest='transport', type=str, + help="transport i.e http or ble", default='http') + + parser.add_argument("--name", dest='service_name', type=str, + help="BLE Device Name / HTTP Server hostname or IP", default='') + + parser.add_argument("-v", "--verbose", dest='verbose', help="increase output verbosity", action="store_true") + args = parser.parse_args() + + if args.version != '': + print("==== Esp_Ctrl Version: " + args.version + " ====") + + if args.service_name == '': + args.service_name = 'my_esp_ctrl_device' + if args.transport == 'http': + args.service_name += '.local' + + obj_transport = get_transport(args.transport, args.service_name) + if obj_transport is None: + print("---- Invalid transport ----") + exit(1) + + if args.version != '': + print("\n==== Verifying protocol version ====") + if not version_match(obj_transport, args.version, args.verbose): + print("---- Error in protocol version matching ----") + exit(2) + print("==== Verified protocol version successfully ====") + + while True: + properties = get_all_property_values(obj_transport) + if len(properties) == 0: + print("---- Error in reading property values ----") + exit(4) + + print("\n==== Available Properties ====") + print("{0: >4} {1: <16} {2: <10} {3: <16} {4: <16}".format( + "S.N.", "Name", "Type", "Flags", "Value")) + for i in range(len(properties)): + print("[{0: >2}] {1: <16} {2: <10} {3: <16} {4: <16}".format( + i + 1, properties[i]["name"], prop_typestr(properties[i]), + ["","Read-Only"][prop_is_readonly(properties[i])], + str(properties[i]["value"]))) + + select = 0 + while True: + try: + inval = input("\nSelect properties to set (0 to re-read, 'q' to quit) : ") + if inval.lower() == 'q': + print("Quitting...") + exit(5) + invals = inval.split(',') + selections = [int(val) for val in invals] + if min(selections) < 0 or max(selections) > len(properties): + raise ValueError("Invalid input") + break + except ValueError as e: + print(str(e) + "! Retry...") + + if len(selections) == 1 and selections[0] == 0: + continue + + set_values = [] + set_indices = [] + for select in selections: + while True: + inval = input("Enter value to set for property (" + properties[select - 1]["name"] + ") : ") + value = encode_prop_value(properties[select - 1], + str_to_prop_value(properties[select - 1], inval)) + if value is None: + print("Invalid input! Retry...") + continue + break + set_values += [value] + set_indices += [select - 1] + + if not set_property_values(obj_transport, properties, set_indices, set_values): + print("Failed to set values!") diff --git a/examples/protocols/esp_local_ctrl/scripts/proto.py b/examples/protocols/esp_local_ctrl/scripts/proto.py new file mode 100644 index 000000000..15dda7ab4 --- /dev/null +++ b/examples/protocols/esp_local_ctrl/scripts/proto.py @@ -0,0 +1,93 @@ +# Copyright 2018 Espressif Systems (Shanghai) PTE LTD +# +# 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. +# + + +from __future__ import print_function +from future.utils import tobytes +import os + + +def _load_source(name, path): + try: + from importlib.machinery import SourceFileLoader + return SourceFileLoader(name, path).load_module() + except ImportError: + # importlib.machinery doesn't exists in Python 2 so we will use imp (deprecated in Python 3) + import imp + return imp.load_source(name, path) + + +idf_path = os.environ['IDF_PATH'] +constants_pb2 = _load_source("constants_pb2", idf_path + "/components/protocomm/python/constants_pb2.py") +local_ctrl_pb2 = _load_source("esp_local_ctrl_pb2", idf_path + "/components/esp_local_ctrl/python/esp_local_ctrl_pb2.py") + + +def get_prop_count_request(): + req = local_ctrl_pb2.LocalCtrlMessage() + req.msg = local_ctrl_pb2.TypeCmdGetPropertyCount + payload = local_ctrl_pb2.CmdGetPropertyCount() + req.cmd_get_prop_count.MergeFrom(payload) + return req.SerializeToString() + + +def get_prop_count_response(response_data): + resp = local_ctrl_pb2.LocalCtrlMessage() + resp.ParseFromString(tobytes(response_data)) + if (resp.resp_get_prop_count.status == 0): + return resp.resp_get_prop_count.count + else: + return 0 + + +def get_prop_vals_request(indices): + req = local_ctrl_pb2.LocalCtrlMessage() + req.msg = local_ctrl_pb2.TypeCmdGetPropertyValues + payload = local_ctrl_pb2.CmdGetPropertyValues() + payload.indices.extend(indices) + req.cmd_get_prop_vals.MergeFrom(payload) + return req.SerializeToString() + + +def get_prop_vals_response(response_data): + resp = local_ctrl_pb2.LocalCtrlMessage() + resp.ParseFromString(tobytes(response_data)) + results = [] + if (resp.resp_get_prop_vals.status == 0): + for prop in resp.resp_get_prop_vals.props: + results += [{ + "name": prop.name, + "type": prop.type, + "flags": prop.flags, + "value": tobytes(prop.value) + }] + return results + + +def set_prop_vals_request(indices, values): + req = local_ctrl_pb2.LocalCtrlMessage() + req.msg = local_ctrl_pb2.TypeCmdSetPropertyValues + payload = local_ctrl_pb2.CmdSetPropertyValues() + for i, v in zip(indices, values): + prop = payload.props.add() + prop.index = i + prop.value = v + req.cmd_set_prop_vals.MergeFrom(payload) + return req.SerializeToString() + + +def set_prop_vals_response(response_data): + resp = local_ctrl_pb2.LocalCtrlMessage() + resp.ParseFromString(tobytes(response_data)) + return (resp.resp_set_prop_vals.status == 0) diff --git a/examples/protocols/esp_local_ctrl/sdkconfig.defaults b/examples/protocols/esp_local_ctrl/sdkconfig.defaults new file mode 100644 index 000000000..a9595bf0c --- /dev/null +++ b/examples/protocols/esp_local_ctrl/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ESP_HTTPS_SERVER_ENABLE=y diff --git a/tools/esp_prov/esp_prov.py b/tools/esp_prov/esp_prov.py index ad199143a..d80db3cd7 100644 --- a/tools/esp_prov/esp_prov.py +++ b/tools/esp_prov/esp_prov.py @@ -61,7 +61,7 @@ def get_transport(sel_transport, softap_endpoint=None, ble_devname=None): try: tp = None if (sel_transport == 'softap'): - tp = transport.Transport_Softap(softap_endpoint) + tp = transport.Transport_HTTP(softap_endpoint) elif (sel_transport == 'ble'): # BLE client is now capable of automatically figuring out # the primary service from the advertisement data and the diff --git a/tools/esp_prov/transport/__init__.py b/tools/esp_prov/transport/__init__.py index 809d1335d..907df1f3c 100644 --- a/tools/esp_prov/transport/__init__.py +++ b/tools/esp_prov/transport/__init__.py @@ -14,5 +14,5 @@ # from .transport_console import * # noqa: F403, F401 -from .transport_softap import * # noqa: F403, F401 +from .transport_http import * # noqa: F403, F401 from .transport_ble import * # noqa: F403, F401 diff --git a/tools/esp_prov/transport/transport_softap.py b/tools/esp_prov/transport/transport_http.py similarity index 71% rename from tools/esp_prov/transport/transport_softap.py rename to tools/esp_prov/transport/transport_http.py index 5b0d2908f..47e8b28ef 100644 --- a/tools/esp_prov/transport/transport_softap.py +++ b/tools/esp_prov/transport/transport_http.py @@ -16,14 +16,25 @@ from __future__ import print_function from future.utils import tobytes +import socket import http.client +import ssl from .transport import Transport -class Transport_Softap(Transport): - def __init__(self, url): - self.conn = http.client.HTTPConnection(url, timeout=30) +class Transport_HTTP(Transport): + def __init__(self, hostname, certfile=None): + try: + socket.gethostbyname(hostname.split(':')[0]) + except socket.gaierror: + raise RuntimeError("Unable to resolve hostname :" + hostname) + + if certfile is None: + self.conn = http.client.HTTPConnection(hostname, timeout=30) + else: + ssl_ctx = ssl.create_default_context(cafile=certfile) + self.conn = http.client.HTTPSConnection(hostname, context=ssl_ctx, timeout=30) self.headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "text/plain"} def _send_post_request(self, path, data):