Creating a Wi-Fi-based Local Control Server

The following sample code implements the Wi-Fi-based local control server. The local control is based on HTTP for data communication, and the data is encrypted using the TLS protocol. Additionally, the sample adds the mDNS module for device discovery.

📝 Source code

For the complete example code, please refer to book-esp32c3-iot-projects/test_case/local_control.

#define PROPERTY_NAME_STATUS "status"
static char light_status[64] = "{\"status\": true}";

//Property type definition, used with scripts
enum property_types {
    PROP_TYPE_TIMESTAMP = 0,
    PROP_TYPE_INT32,
    PROP_TYPE_BOOLEAN,
    PROP_TYPE_STRING,
};

//Get attribute value
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)

{
    int i = 0;
    for (i = 0; i < props_count; i ++) {
        ESP_LOGI(TAG, "Reading property : %s", props[i].name);
        if (!strncmp(PROPERTY_NAME_STATUS,
                    props[i].name,
                    strlen(props[i].name))) {
            prop_values[i].size = strlen(light_status);
            prop_values[i].data = &light_status;//prop_values[i].data is just a pointer, and cannot be assigned.
            break;
        }
    }
    if (i == props_count) {
        ESP_LOGE(TAG, "Not found property %s", props[i].name);
        return ESP_FAIL;
    }
    return ESP_OK;
}

//Set property value
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)
{
    int i = 0;
    for (i = 0; i < props_count; i ++) {
        ESP_LOGI(TAG, "Setting property : %s", props[i].name);
        if (!strncmp(PROPERTY_NAME_STATUS,
                    props[i].name,
                    strlen(props[i].name))) {
            memset(light_status, 0, sizeof(light_status));
            strncpy(light_status,
                    (const char *)prop_values[i].data,
                    prop_values[i].size);
            if (strstr(light_status, "true")) {
                app_driver_set_state(true);     //Turn on the smart light
            } else {
                app_driver_set_state(false);    //Turn off the smart light
            }
            break;
        }
    }
    if (i == props_count) {
        ESP_LOGE(TAG, "Not found property %s", props[i].name);
        return ESP_FAIL;
    }
    return ESP_OK;
}
#define SERVICE_NAME "my_esp_ctrl_device"
void esp_local_ctrl_service_start(void)
{
    //Initialise the HTTPS server-side configuration
    httpd_ssl_config_t https_conf = HTTPD_SSL_CONFIG_DEFAULT();
    //Load the 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-side 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,
            .pop = NULL,
        },
        .handlers = {

            //User-defined processing function
            .get_prop_values = get_property_values,
            .set_prop_values = set_property_values,
            .usr_ctx         = NULL,
            .usr_ctx_free_fn = NULL
        },

        //Set the maximum number of attributes
        .max_properties = 10
    };

    //Initialise local discovery
    mdns_init();
    mdns_hostname_set(SERVICE_NAME);

    //Start the local control service
    ESP_ERROR_CHECK(esp_local_ctrl_start(&config));

    ESP_LOGI(TAG, "esp_local_ctrl service started with name : %s", SERVICE_NAME);
    esp_local_ctrl_prop_t status = {
        .name        = PROPERTY_NAME_STATUS,
        .type        = PROP_TYPE_STRING,
        .size        = 0,
        .flags       = 0,
        .ctx         = NULL,
        .ctx_free_fn = NULL
    };
    //Add attribute value
    ESP_ERROR_CHECK(esp_local_ctrl_add_property(&status));
}

The above sample code implements the discovery of a device (domain name: my_esp_ctrl_device.local) through the local discovery protocol (mDNS), establishes an HTTPS local control connection, and allows the client to set and query attribute values via a registered endpoint.

Users can enable the transmission security protection for local control via the following options:

  • PROTOCOM_SEC0: specifies the end-to-end encryption algorithm used.
  • PROTOCOM_SEC1: specifies that data is exchanged as plain text.
  • PROTOCOM_SEC_CUSTOM: customises security requirements.

Each attribute must have a unique name (a string), type (e.g., int, bool, or string), flag (e.g., read-only, or readable and writable), and size. If the property value is expected to be of variable length (e.g., if the property value is a string or byte stream), the size should be kept at 0. For fixed-length property value data types, such as int, float, etc., setting the size field to the correct value helps esp_local_ctrl perform internal checks on parameters received via write requests.

You can process it by matching props[i].name with the corresponding property name, and further checking the flag and type of the property to determine if the property satisfies the corresponding flag and type requirements.

The default endpoints are shown in Table 8.5.

Table 8.5. Default endpoints

Endpoint name (BLE + GATT Server)URI (HTTPS Server + mDNS)Description
esp_local_ctrl/versionhttps://my_esp_ctrl_device.local/esp_local_ctrl/versionFor retrieving version strings
esp_local_ctrl/controlhttps://my_esp_ctrl_device.local/esp_local_ctrl/controlFor sending/receiving control messages