diff --git a/docs/en/api-reference/protocols/esp_local_ctrl.rst b/docs/en/api-reference/protocols/esp_local_ctrl.rst index bf1e47d1ff..6dddd41d20 100644 --- a/docs/en/api-reference/protocols/esp_local_ctrl.rst +++ b/docs/en/api-reference/protocols/esp_local_ctrl.rst @@ -1,6 +1,8 @@ ESP Local Control ================= +:link_to_translation:`zh_CN:[中文]` + Overview -------- @@ -88,21 +90,20 @@ Similarly for HTTPS transport: You may set security for transport in ESP local control using following options: -1. ``PROTOCOM_SEC2``: specifies that SRP6a based key exchange and end to end encryption based on AES-GCM is used. This is the most preffered option as it adds a robust security with Augmented PAKE protocol i.e., SRP6a. -2. ``PROTOCOM_SEC1``: specifies that Curve25519 based key exchange and end to end encryption based on AES-CTR is used. +1. ``PROTOCOM_SEC2``: specifies that SRP6a-based key exchange and end-to-end encryption based on AES-GCM are used. This is the most preferred option as it adds a robust security with Augmented PAKE protocol, i.e., SRP6a. +2. ``PROTOCOM_SEC1``: specifies that Curve25519-based key exchange and end-to-end encryption based on AES-CTR are used. 3. ``PROTOCOM_SEC0``: specifies that data will be exchanged as a plain text (no security). 4. ``PROTOCOM_SEC_CUSTOM``: you can define your own security requirement. Please note that you will also have to provide ``custom_handle`` of type ``protocomm_security_t *`` in this context. .. note:: - The respective security schemes need to be enabled through the project configuration menu. Please refer to the Enabling protocom security version section in :doc:`Protocol Communication ` for more details. Creating a Property ------------------- -Now that we know how to start the **esp_local_ctrl** service, let us add a property to it. Each property must have a unique ``name`` (string), a ``type`` (e.g., enum), ``flags`` (bit fields) and ``size``. +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 ``size`` is to be kept 0, if we want our property value to be of variable length (e.g., if it is a string or bytestream). For data types with fixed-length property value, 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. @@ -124,7 +125,7 @@ Here is an example property which is to function as a timestamp. It is assumed t 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. +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. @@ -175,20 +176,20 @@ Here is an example of ``set_prop_values()`` handler. Notice how we restrict from } -For complete example see :example:`protocols/esp_local_ctrl` +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. +The client side implementation establishes 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 translates 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 +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. +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: @@ -204,7 +205,7 @@ The various protocomm endpoints provided by **esp_local_ctrl** are listed below: - Endpoint used for retrieving version string * - esp_local_ctrl/control - https://.local/esp_local_ctrl/control - - Endpoint used for sending / receiving control messages + - Endpoint used for sending or receiving control messages API Reference diff --git a/docs/zh_CN/api-reference/protocols/esp_local_ctrl.rst b/docs/zh_CN/api-reference/protocols/esp_local_ctrl.rst index 587542d5d8..fb09163164 100644 --- a/docs/zh_CN/api-reference/protocols/esp_local_ctrl.rst +++ b/docs/zh_CN/api-reference/protocols/esp_local_ctrl.rst @@ -1 +1,214 @@ -.. include:: ../../../en/api-reference/protocols/esp_local_ctrl.rst +ESP 本地控制 +================ + +:link_to_translation:`en:[English]` + +概述 +---- + +通过 ESP-IDF 的 ESP 本地控制 (**esp_local_ctrl**) 组件,可使用 HTTPS 或 BLE 协议控制 ESP 设备。通过一系列可配置的处理程序,该组件允许你对应用程序定义的读/写 **属性** 进行访问。 + +通过 BLE 传输协议初始化 **esp_local_ctrl** 的过程如下: + + .. code-block:: 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 + } + } + }, + .proto_sec = { + .version = PROTOCOM_SEC0, + .custom_handle = NULL, + .sec_params = NULL, + }, + .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)); + + +同样,对于 HTTP 传输: + + .. code-block:: c + + /* Set the configuration */ + httpd_ssl_config_t https_conf = HTTPD_SSL_CONFIG_DEFAULT(); + + /* Load server certificate */ + extern const unsigned char servercert_start[] asm("_binary_servercert_pem_start"); + extern const unsigned char servercert_end[] asm("_binary_servercert_pem_end"); + https_conf.servercert = servercert_start; + https_conf.servercert_len = servercert_end - servercert_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 + }, + .proto_sec = { + .version = PROTOCOM_SEC0, + .custom_handle = NULL, + .sec_params = NULL, + }, + .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)); + +你可以利用以下选项设置 ESP 本地控制的传输安全性: + +1. ``PROTOCOM_SEC2``: 指定使用基于 SRP6a 的密钥交换和基于 AES-GCM 的端到端加密。这一选项通过增强的 PAKE 协议(即 SRP6a)提供了强大的安全保障,是最受欢迎的选项。 +2. ``PROTOCOM_SEC1``: 指定使用基于 Curve25519 的密钥交换和基于 AES-CTR 的端到端加密。 +3. ``PROTOCOM_SEC0``: 指定以明文(无安全性)方式交换数据。 +4. ``PROTOCOM_SEC_CUSTOM``: 自定义安全需求。注意,使用这一选项时,必须提供 ``protocomm_security_t *`` 类型的 ``custom_handle``。 + +.. note:: + 相应的安全方案需通过项目配置菜单启用。要了解详情,请参考 :doc:`Protocol Communication ` 中关于启用 protocomm 安全版本的章节。 + +创建属性 +-------- + +启用 **esp_local_ctrl** 后,可以为其添加属性。每个属性必须具有唯一的名称 ``name`` (字符串),还需具有类型 ``type`` (如枚举)、标记 ``flags`` (位域)和大小 ``size``。 + +如果希望属性值的长度可变(例如,字符串或字节流),则 ``size`` 值应保持为 0。对于有固定长度属性值的数据类型,如整型、浮点型等,将 ``size`` 字段设置为正确的值,有助于 **esp_local_ctrl** 对接收到的写入请求的参数进行内部检查。 + +``type`` 和 ``flags`` 字段的含义取决于具体的应用程序,它们可以是枚举、位域、甚至整型。可以使用 ``type`` 表示属性,用 ``flags`` 指定属性的特征。 + +例如,以下属性被用作时间戳。此处假设应用程序定义了 ``TYPE_TIMESTAMP`` 和 ``READONLY`` 来设置此处的 ``type`` 和 ``flags`` 字段: + + .. code-block:: 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); + + +另外,此示例中还设置了一个 ctx 字段,指向自定义的 ``func_get_time()``,用于在属性的 get/set 处理程序中检索时间戳。 + +以下为 ``get_prop_values()`` 处理程序的一个示例,用于检索时间戳: + + .. code-block:: 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; + } + + +以下为 ``set_prop_values()`` 应用程序的一个示例,注意此示例是如何为只读属性限制写入操作: + + .. code-block:: 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; + } + + +完整示例请参见 :example:`protocols/esp_local_ctrl`。 + +客户端实现 +-------------- + +在客户端的实现过程中,首先,通过支持的传输模式与设备建立 protocomm 会话,然后发送并接收 **esp_local_ctrl** 服务能够处理的 protobuf 信息。 **esp_local_ctrl** 服务会将这些信息转换为请求,并发起相应的处理程序 (set/get)。接着,为每个处理程序生成的响应会被再次打包到一条 protobuf 信息中,传输回客户端。 + +以下是 **esp_local_ctrl** 服务能够处理的各种 protobuf 信息: + +1. ``get_prop_count`` : 返回服务支持的属性总数。 +2. ``get_prop_values`` : 接受一个索引数组,并返回这些索引相对应的属性信息(名称、类型、标志)和属性值。 +3. ``set_prop_values`` : 接受一个索引数组和一个新值数组,用于设置索引对应的属性值。 + +注意,在多个会话中,一个属性的索引可能相同,也可能不同。因此,客户端必须用唯一的属性名称来识别属性。每次建立新会话时,客户端都应首先调用 ``get_prop_count``,然后调用 ``get_prop_values``,为所有属性建立从索引到名称的映射。为一组属性调用 ``set_prop_values`` 时,必须先用创建的映射将名称转换为索引。如前所述,每次使用同一设备建立新会话时,客户端必须刷新该映射。 + +下面列出了 **esp_local_ctrl** 服务提供的各种 protocomm 端点: + +.. list-table:: ESP本地控制服务提供的端点 + :widths: 10 25 50 + :header-rows: 1 + + * - 端点名称 (BLE + GATT 服务器) + - URI (HTTPS 服务器 + mDNS) + - 描述 + * - esp_local_ctrl/version + - `https://.local/esp_local_ctrl/version` + - 检索版本字符串 + * - esp_local_ctrl/control + - `https://.local/esp_local_ctrl/control` + - 发送或接收控制信息 + + +API 参考 +---------- + +.. include-build-file:: inc/esp_local_ctrl.inc