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/version | https://my_esp_ctrl_device.local/esp_local_ctrl/version | For retrieving version strings |
esp_local_ctrl/control | https://my_esp_ctrl_device.local/esp_local_ctrl/control | For sending/receiving control messages |