mirror of
https://gitee.com/beecue/fastbee.git
synced 2025-12-19 17:35:54 +08:00
添加智能灯固件代码
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# 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)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp_local_ctrl)
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# 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 `idf.py 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 the following command to test the device name
|
||||
resolution through mDNS:
|
||||
|
||||
```
|
||||
ping my_esp_ctrl_device.local
|
||||
```
|
||||
|
||||
Sample output:
|
||||
|
||||
```
|
||||
64 bytes from 192.168.32.156 (192.168.32.156): icmp_seq=1 ttl=255 time=58.1 ms
|
||||
64 bytes from 192.168.32.156 (192.168.32.156): icmp_seq=2 ttl=255 time=89.9 ms
|
||||
64 bytes from 192.168.32.156 (192.168.32.156): icmp_seq=3 ttl=255 time=123 ms
|
||||
```
|
||||
|
||||
After you've tested the name resolution, run:
|
||||
|
||||
```
|
||||
python scripts/esp_local_ctrl.py
|
||||
```
|
||||
Sample output:
|
||||
|
||||
```
|
||||
python 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.
|
||||
@@ -0,0 +1,78 @@
|
||||
from __future__ import unicode_literals
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_WIFI')
|
||||
def test_examples_esp_local_ctrl(env, extra_data):
|
||||
|
||||
rel_project_path = os.path.join('examples', 'protocols', 'esp_local_ctrl')
|
||||
dut = env.get_dut('esp_local_ctrl', rel_project_path)
|
||||
idf_path = dut.app.get_sdk_path()
|
||||
dut.start_app()
|
||||
|
||||
dut_ip = dut.expect(re.compile(r'esp_netif_handlers: sta ip: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'))[0]
|
||||
dut.expect('esp_https_server: Starting server')
|
||||
dut.expect('esp_https_server: Server listening on port 443')
|
||||
dut.expect('control: esp_local_ctrl service started with name : my_esp_ctrl_device')
|
||||
|
||||
def dut_expect_read():
|
||||
dut.expect('control: Reading property : timestamp (us)')
|
||||
dut.expect('control: Reading property : property1')
|
||||
dut.expect('control: Reading property : property2')
|
||||
dut.expect('control: Reading property : property3')
|
||||
|
||||
# Running mDNS services in docker is not a trivial task. Therefore, the script won't connect to the host name but
|
||||
# to IP address. However, the certificates were generated for the host name and will be rejected.
|
||||
cmd = ' '.join([sys.executable, os.path.join(idf_path, rel_project_path, 'scripts/esp_local_ctrl.py'),
|
||||
'--name', dut_ip,
|
||||
'--dont-check-hostname']) # don't reject the certificate because of the hostname
|
||||
esp_local_ctrl_log = os.path.join(idf_path, rel_project_path, 'esp_local_ctrl.log')
|
||||
with ttfw_idf.CustomProcess(cmd, esp_local_ctrl_log) as ctrl_py:
|
||||
|
||||
def expect_properties(prop1, prop3):
|
||||
dut_expect_read()
|
||||
ctrl_py.pexpect_proc.expect_exact('==== Available Properties ====')
|
||||
ctrl_py.pexpect_proc.expect(re.compile(r'S.N. Name\s+Type\s+Flags\s+Value'))
|
||||
ctrl_py.pexpect_proc.expect(re.compile(r'\[ 1\] timestamp \(us\)\s+TIME\(us\)\s+Read-Only\s+\d+'))
|
||||
ctrl_py.pexpect_proc.expect(re.compile(r'\[ 2\] property1\s+INT32\s+{}'.format(prop1)))
|
||||
ctrl_py.pexpect_proc.expect(re.compile(r'\[ 3\] property2\s+BOOLEAN\s+Read-Only\s+(True)|(False)'))
|
||||
ctrl_py.pexpect_proc.expect(re.compile(r'\[ 4\] property3\s+STRING\s+{}'.format(prop3)))
|
||||
ctrl_py.pexpect_proc.expect_exact('Select properties to set (0 to re-read, \'q\' to quit) :')
|
||||
|
||||
property1 = 123456789
|
||||
property3 = ''
|
||||
|
||||
ctrl_py.pexpect_proc.expect_exact('Connecting to {}'.format(dut_ip))
|
||||
dut.expect('esp_https_server: performing session handshake', timeout=60)
|
||||
expect_properties(property1, property3)
|
||||
|
||||
ctrl_py.pexpect_proc.sendline('1')
|
||||
ctrl_py.pexpect_proc.expect_exact('Enter value to set for property (timestamp (us)) :')
|
||||
ctrl_py.pexpect_proc.sendline('2')
|
||||
ctrl_py.pexpect_proc.expect_exact('Failed to set values!')
|
||||
dut.expect('control: timestamp (us) is read-only')
|
||||
expect_properties(property1, property3)
|
||||
|
||||
property1 = 638
|
||||
ctrl_py.pexpect_proc.sendline('2')
|
||||
ctrl_py.pexpect_proc.expect_exact('Enter value to set for property (property1) :')
|
||||
ctrl_py.pexpect_proc.sendline(str(property1))
|
||||
dut.expect('control: Setting property1 value to {}'.format(property1))
|
||||
expect_properties(property1, property3)
|
||||
|
||||
property3 = 'test'
|
||||
ctrl_py.pexpect_proc.sendline('4')
|
||||
ctrl_py.pexpect_proc.expect_exact('Enter value to set for property (property3) :')
|
||||
ctrl_py.pexpect_proc.sendline(property3)
|
||||
dut.expect('control: Setting property3 value to {}'.format(property3))
|
||||
expect_properties(property1, property3)
|
||||
|
||||
ctrl_py.pexpect_proc.sendline('q')
|
||||
ctrl_py.pexpect_proc.expect_exact('Quitting...')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_esp_local_ctrl()
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "app_main.c" "esp_local_ctrl_service.c"
|
||||
INCLUDE_DIRS "."
|
||||
EMBED_TXTFILES "certs/cacert.pem" "certs/prvtkey.pem")
|
||||
@@ -0,0 +1,20 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config EXAMPLE_WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "mypassword"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use.
|
||||
|
||||
config EXAMPLE_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
|
||||
@@ -0,0 +1,145 @@
|
||||
/* 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 <string.h>
|
||||
#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 "sdkconfig.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
/* The examples use WiFi configuration that you can set via 'idf.py 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_EXAMPLE_WIFI_SSID
|
||||
#define EXAMPLE_ESP_WIFI_PASS CONFIG_EXAMPLE_WIFI_PASSWORD
|
||||
#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_EXAMPLE_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 two events:
|
||||
* - we are connected to the AP with an IP
|
||||
* - we failed to connect after the maximum amount of retries */
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_FAIL_BIT BIT1
|
||||
|
||||
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();
|
||||
s_retry_num++;
|
||||
ESP_LOGI(TAG, "retry to connect to the AP");
|
||||
} else {
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
}
|
||||
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:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
s_retry_num = 0;
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t wifi_init_sta(void)
|
||||
{
|
||||
esp_err_t ret_value = ESP_OK;
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
|
||||
assert(sta_netif);
|
||||
|
||||
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.");
|
||||
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE,
|
||||
pdFALSE,
|
||||
portMAX_DELAY);
|
||||
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
|
||||
* happened. */
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
|
||||
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
|
||||
} else if (bits & WIFI_FAIL_BIT) {
|
||||
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
|
||||
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
|
||||
ret_value = ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "UNEXPECTED EVENT");
|
||||
ret_value = ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
|
||||
vEventGroupDelete(s_wifi_event_group);
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
/* 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(void)
|
||||
{
|
||||
//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");
|
||||
if (wifi_init_sta() == ESP_OK) {
|
||||
start_esp_local_ctrl_service();
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Connection failed, not starting esp_local_ctrl service");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICrjCCAZYCCQDGnK9OU3UN2TANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARy
|
||||
b290MB4XDTIwMTExMDExMzExNVoXDTMwMTExMDExMzExNVowIzEhMB8GA1UEAwwY
|
||||
bXlfZXNwX2N0cmxfZGV2aWNlLmxvY2FsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA2NgeOgHTX6yURoB8u3BAphDMTlp/Ar8oAtoO+xqIPw1sZKmhJLAS
|
||||
bfKkHKhi7pr/h31xOHqzTxlPkUzWpfszFx5YiDFYtiIlcObrgk83u3CtvBw7wuZ6
|
||||
BA/01hkiSGgkAFD/xnRNLKgidTu1tCIa2QY7Jnp+HdJz6yJws1/WAzn2lsXcJwSd
|
||||
6tPu2U0lhE2w6ylCdLYD3upveo/80WArQqNg6bv6Wbz8iL18E87enpwfHMA7ZN+S
|
||||
sDq7HACRjapAkcimjbzkrh7/f9Nr6c8KpPyeiWyHFxVTbmEj4NMG9IpbTKp9CMAt
|
||||
ysmiPYAYNFXsTHjoRVf4EbfHbxGHobUwewIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
|
||||
AQBWg9Xh1MG4d4gGGx920OJBm+qQ9XY9oV2Aap81ZYshgBlnUKoKLhYolp/CHXyr
|
||||
IXy7YA01ASX2wzohguqakdo0ghYkhwuRoly+0+uzphmqyMqXnTUDCgEcZF4l90xl
|
||||
jRdMenqEgfOXDNk2VAK/rmAZ2jZsaGpBI4NRbEdwH1MVd61g2NVBk0nEI73cW6Ki
|
||||
BPxMw2aGFizTwcPT9gwbQgLdLZeEuvcPrdzK5swqccZ+MBHMcwW/qvcmwqJGeLL2
|
||||
zmx7o2ODQyElIKLKUDWAFIYrb7DXR4oajjhUa0+SOj9Ydj/5+eZ+Wx7NJoG+oH7N
|
||||
DB0jK2qB8eexplQj1KLWS2Un
|
||||
-----END CERTIFICATE-----
|
||||
@@ -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-----
|
||||
@@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICmjCCAYICCQCOEQkjYe2QMTANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARy
|
||||
b290MB4XDTIwMTExMDExMjgyOVoXDTMwMTExMDExMjgyOVowDzENMAsGA1UEAwwE
|
||||
cm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOqS7H7+XeFNcf5m
|
||||
qlH04t0ru56MCDYv9JV3byILgUnk1j+ld74m2q4T+Xxiw5ruMXh41W2xryMLF3+3
|
||||
jql8b7isJFwCXud4/WLr4KzCEKgqvr6Nez9Hb9rIBbQsGWtDTjfe06F/D9Zioyt3
|
||||
RnoT+5ItpX0+9IJn3TmAx7g1wU2dlXeaTp48RWPtJBqxp80Lq4SR3CdxI9+eVHv9
|
||||
sRA3sI9ggqFWzDNJDiTLZoJU1Z+n/MnHTUBt7WRZcMToMsHbj2Gtd4LruB3J46qO
|
||||
bjoL4im9oUrfXJZh87nW9KQ/+gOVv8t0zU70A/JMrazb/YnE6xO7+40JfrGNuFMm
|
||||
ZyylUyECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAvCJMjXDNO/zUv7SBlr8hlHse
|
||||
KprCDEp91DlXsewpTB3h6s1gyZzDCygPtz80qRD6zy+T4r1veaYQeLecsIyfYNV1
|
||||
qnhNPpHnxjuXrrVwpEYOk/aP0yVlv0PiHsjyxzblLQPomX4m43Ec8/wW0Nlw0Aau
|
||||
K0sD5+Mv/3NNQIneGFsLF4JPRkJwLjSbjPdKLpjWdLsTKQwVg0FIslzI9RmBIQIq
|
||||
Nz2RWNHSqfGzsRpne9deqx9/9M4N8URUcmo0j7Ly7mYuxTkF7sft6sxbWDYQx1S1
|
||||
4GjAEFWe4352O0sFl0PWr+o8rd245yAu5SEahRFvjvnSNg8VlYcnezBmsp2rbQ==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -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
|
||||
@@ -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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#include <mdns.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_timer.h>
|
||||
#include <esp_local_ctrl.h>
|
||||
#include <esp_https_server.h>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
#!/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 ssl
|
||||
|
||||
import proto
|
||||
|
||||
# The tools directory is already in the PATH in environment prepared by install.sh which would allow to import
|
||||
# esp_prov as file but not as complete module.
|
||||
sys.path.insert(0, os.path.join(os.environ['IDF_PATH'], 'tools/esp_prov'))
|
||||
import esp_prov # noqa: E402
|
||||
|
||||
|
||||
# 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) != 0
|
||||
|
||||
|
||||
def on_except(err):
|
||||
if config_throw_except:
|
||||
raise RuntimeError(err)
|
||||
else:
|
||||
print(err)
|
||||
|
||||
|
||||
def get_transport(sel_transport, service_name, check_hostname):
|
||||
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"
|
||||
ssl_ctx = ssl.create_default_context(cafile=cert_path)
|
||||
ssl_ctx.check_hostname = check_hostname
|
||||
tp = esp_prov.transport.Transport_HTTP(service_name, ssl_ctx)
|
||||
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("--dont-check-hostname", action="store_true",
|
||||
# If enabled, the certificate won't be rejected for hostname mismatch.
|
||||
# This option is hidden because it should be used only for testing purposes.
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
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, not args.dont_check_hostname)
|
||||
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!")
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_ESP_HTTPS_SERVER_ENABLE=y
|
||||
Reference in New Issue
Block a user