--- /dev/null
+set(COMPONENT_ADD_INCLUDEDIRS include/common
+ include/security
+ include/transports)
+set(COMPONENT_PRIV_INCLUDEDIRS proto-c src/common src/simple_ble)
+set(COMPONENT_SRCS "src/common/protocomm.c"
+ "src/security/security0.c"
+ "src/security/security1.c"
+ "proto-c/constants.pb-c.c"
+ "proto-c/sec0.pb-c.c"
+ "proto-c/sec1.pb-c.c"
+ "proto-c/session.pb-c.c"
+ "src/transports/protocomm_console.c"
+ "src/transports/protocomm_httpd.c")
+
+set(COMPONENT_PRIV_REQUIRES protobuf-c mbedtls console http_server bt)
+
+if(CONFIG_BT_ENABLED)
+ if(CONFIG_BLUEDROID_ENABLED)
+ list(APPEND COMPONENT_SRCS
+ "src/simple_ble/simple_ble.c"
+ "src/transports/protocomm_ble.c")
+ endif()
+endif()
+
+register_component()
-COMPONENT_ADD_INCLUDEDIRS := include/common include/security proto-c
-COMPONENT_PRIV_INCLUDEDIRS := src/common
-COMPONENT_SRCDIRS := src/common src/security proto-c
+COMPONENT_ADD_INCLUDEDIRS := include/common include/security include/transports
+COMPONENT_PRIV_INCLUDEDIRS := proto-c src/common src/simple_ble
+COMPONENT_SRCDIRS := src/common src/security proto-c src/simple_ble src/transports
+
+ifneq ($(filter y, $(CONFIG_BT_ENABLED) $(CONFIG_BLUEDROID_ENABLED)),y y)
+ COMPONENT_OBJEXCLUDE := src/simple_ble/simple_ble.o src/transports/protocomm_ble.o
+endif
--- /dev/null
+// 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.
+
+#pragma once
+
+#include <protocomm.h>
+
+/**
+ * BLE device name cannot be larger than this value
+ */
+#define MAX_BLE_DEVNAME_LEN 13
+
+/**
+ * @brief This structure maps handler required by protocomm layer to
+ * UUIDs which are used to uniquely identify BLE characteristics
+ * from a smartphone or a similar client device.
+ */
+typedef struct name_uuid {
+ /**
+ * Name of the handler, which is passed to protocomm layer
+ */
+ char *name;
+
+ /**
+ * UUID to be assigned to the BLE characteristic which is
+ * mapped to the handler
+ */
+ uint16_t uuid;
+} protocomm_ble_name_uuid_t;
+
+/**
+ * @brief Config parameters for protocomm BLE service
+ */
+typedef struct {
+ /**
+ * BLE device name being broadcast at the time of provisioning
+ */
+ char device_name[MAX_BLE_DEVNAME_LEN];
+ uint8_t service_uuid[16]; /*!< SSID of the provisioning service */
+ ssize_t nu_lookup_count; /*!< Number of entries in the Name-UUID lookup table */
+
+ /**
+ * Pointer to the Name-UUID lookup table
+ */
+ protocomm_ble_name_uuid_t *nu_lookup;
+} protocomm_ble_config_t;
+
+/**
+ * @brief Start Bluetooth Low Energy based transport layer for provisioning
+ *
+ * Initialize and start required BLE service for provisioning. This includes
+ * the initialization for characteristics/service for BLE.
+ *
+ * @param[in] pc Protocomm instance pointer obtained from protocomm_new()
+ * @param[in] config Pointer to config structure for initialising BLE
+ *
+ * @return
+ * - ESP_OK : if successful
+ * - ESP_FAIL : Simple BLE start error
+ * - ESP_ERR_NO_MEM : Error allocating memory for internal resources
+ * - ESP_ERR_INVALID_STATE : Error in ble config
+ * - ESP_ERR_INVALID_ARG : Null arguments
+ */
+esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *config);
+
+/**
+ * @brief Stop Bluetooth Low Energy based transport layer for provisioning
+ *
+ * Stops service/task responsible for BLE based interactions for provisioning
+ *
+ * @note You might want to optionally reclaim memory from Bluetooth.
+ * Refer to the documentation of `esp_bt_mem_release` in that case.
+ *
+ * @param[in] pc Same protocomm instance that was passed to protocomm_ble_start()
+ *
+ * @return
+ * - ESP_OK : For success or appropriate error code
+ * - ESP_FAIL : Simple BLE stop error
+ * - ESP_ERR_INVALID_ARG : Null / incorrect protocomm instance
+ */
+esp_err_t protocomm_ble_stop(protocomm_t *pc);
--- /dev/null
+// 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.
+
+#pragma once
+
+#include <protocomm.h>
+
+#define PROTOCOMM_CONSOLE_DEFAULT_CONFIG() { \
+ .stack_size = 4096, \
+ .task_priority = tskIDLE_PRIORITY + 3, \
+}
+
+/**
+ * @brief Config parameters for protocomm console
+ */
+typedef struct {
+ size_t stack_size; /*!< Stack size of console taks */
+ unsigned task_priority; /*!< Priority of console task */
+} protocomm_console_config_t;
+
+/**
+ * @brief Start console based protocomm transport
+ *
+ * @note This is a singleton. ie. Protocomm can have multiple instances, but only
+ * one instance can be bound to a console based transport layer.
+ *
+ * @param[in] pc Protocomm instance pointer obtained from protocomm_new()
+ * @param[in] config Config param structure for protocomm console
+ *
+ * @return
+ * - ESP_OK : Server started succefully
+ * - ESP_ERR_INVALID_ARG : Null arguments
+ * - ESP_ERR_NOT_SUPPORTED : Transport layer bound to another protocomm instance
+ * - ESP_ERR_INVALID_STATE : Transport layer already bound to this protocomm instance
+ * - ESP_FAIL : Failed to start console thread
+ */
+esp_err_t protocomm_console_start(protocomm_t *pc, const protocomm_console_config_t *config);
+
+/**
+ * @brief Stop console protocomm transport
+ *
+ * @param[in] pc Same protocomm instance that was passed to protocomm_console_start()
+ *
+ * @return
+ * - ESP_OK : Server stopped succefully
+ * - ESP_ERR_INVALID_ARG : Null / incorrect protocomm instance pointer
+ */
+esp_err_t protocomm_console_stop(protocomm_t *pc);
--- /dev/null
+// 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.
+
+#pragma once
+
+#include <protocomm.h>
+
+#define PROTOCOMM_HTTPD_DEFAULT_CONFIG() { \
+ .port = 80, \
+ .stack_size = 4096, \
+ .task_priority = tskIDLE_PRIORITY + 5, \
+}
+
+/**
+ * @brief Config parameters for protocomm HTTP server
+ */
+typedef struct {
+ uint16_t port; /*!< Port on which the http server will listen */
+
+ /**
+ * Stack size of server task, adjusted depending
+ * upon stack usage of endpoint handler
+ */
+ size_t stack_size;
+ unsigned task_priority; /*!< Priority of server task */
+} protocomm_httpd_config_t;
+
+/**
+ * @brief Start HTTPD protocomm transport
+ *
+ * This API internally creates a framework to allow endpoint registration and security
+ * configuration for the protocomm.
+ *
+ * @note This is a singleton. ie. Protocomm can have multiple instances, but only
+ * one instance can be bound to an HTTP transport layer.
+ *
+ * @param[in] pc Protocomm instance pointer obtained from protocomm_new()
+ * @param[in] config Pointer to config structure for initialising HTTP server
+ *
+ * @return
+ * - ESP_OK : Server started succefully
+ * - ESP_ERR_INVALID_ARG : Null arguments
+ * - ESP_ERR_NOT_SUPPORTED : Transport layer bound to another protocomm instance
+ * - ESP_ERR_INVALID_STATE : Transport layer already bound to this protocomm instance
+ * - ESP_ERR_NO_MEM : Memory allocation for server resource failed
+ * - ESP_ERR_HTTPD_* : HTTP server error on start
+ */
+esp_err_t protocomm_httpd_start(protocomm_t *pc, const protocomm_httpd_config_t *config);
+
+/**
+ * @brief Stop HTTPD protocomm transport
+ *
+ * This API cleans up the HTTPD transport protocomm and frees all the handlers registered
+ * with the protocomm.
+ *
+ * @param[in] pc Same protocomm instance that was passed to protocomm_httpd_start()
+ *
+ * @return
+ * - ESP_OK : Server stopped succefully
+ * - ESP_ERR_INVALID_ARG : Null / incorrect protocomm instance pointer
+ */
+esp_err_t protocomm_httpd_stop(protocomm_t *pc);
--- /dev/null
+// Copyright 2015-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.
+
+#include <freertos/FreeRTOS.h>
+#include <esp_system.h>
+#include <esp_log.h>
+#include <esp_bt.h>
+#include <esp_gap_ble_api.h>
+#include <esp_gatts_api.h>
+#include <esp_bt_main.h>
+#include <esp_gatt_common_api.h>
+
+#include "simple_ble.h"
+
+#define SIMPLE_BLE_MAX_GATT_TABLE_SIZE 20
+
+static const char *TAG = "simple_ble";
+
+static simple_ble_cfg_t *g_ble_cfg_p;
+static uint16_t g_gatt_table_map[SIMPLE_BLE_MAX_GATT_TABLE_SIZE];
+
+uint16_t simple_ble_get_uuid(uint16_t handle)
+{
+ uint16_t *uuid_ptr;
+
+ for (int i = 0; i < SIMPLE_BLE_MAX_GATT_TABLE_SIZE; i++) {
+ if (g_gatt_table_map[i] == handle) {
+ uuid_ptr = (uint16_t *) g_ble_cfg_p->gatt_db[i].att_desc.uuid_p;
+ return *uuid_ptr;
+ }
+ }
+ return -1;
+}
+
+static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
+{
+ switch (event) {
+ case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
+ esp_ble_gap_start_advertising(&g_ble_cfg_p->adv_params);
+
+ break;
+ default:
+ break;
+ }
+}
+
+static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t p_gatts_if, esp_ble_gatts_cb_param_t *param)
+{
+ static esp_gatt_if_t gatts_if = ESP_GATT_IF_NONE;
+ esp_err_t ret;
+ uint8_t service_instance_id = 0;
+ if (event == ESP_GATTS_REG_EVT) {
+ if (param->reg.status == ESP_GATT_OK) {
+ gatts_if = p_gatts_if;
+ } else {
+ ESP_LOGE(TAG, "reg app failed, app_id 0x0x%x, status %d",
+ param->reg.app_id,
+ param->reg.status);
+ return;
+ }
+ }
+
+ if (gatts_if != ESP_GATT_IF_NONE && gatts_if != p_gatts_if) {
+ return;
+ }
+
+ switch (event) {
+ case ESP_GATTS_REG_EVT:
+ ret = esp_ble_gatts_create_attr_tab(g_ble_cfg_p->gatt_db, gatts_if, g_ble_cfg_p->gatt_db_count, service_instance_id);
+ if (ret) {
+ ESP_LOGE(TAG, "create attr table failed, error code = 0x%x", ret);
+ return;
+ }
+ ret = esp_ble_gap_set_device_name(g_ble_cfg_p->device_name);
+ if (ret) {
+ ESP_LOGE(TAG, "set device name failed, error code = 0x%x", ret);
+ return;
+ }
+ ret = esp_ble_gap_config_adv_data(&g_ble_cfg_p->adv_data);
+ if (ret) {
+ ESP_LOGE(TAG, "config adv data failed, error code = 0x%x", ret);
+ return;
+ }
+ break;
+ case ESP_GATTS_READ_EVT:
+ g_ble_cfg_p->read_fn(event, gatts_if, param);
+ break;
+ case ESP_GATTS_WRITE_EVT:
+ g_ble_cfg_p->write_fn(event, gatts_if, param);
+ break;
+ case ESP_GATTS_EXEC_WRITE_EVT:
+ g_ble_cfg_p->exec_write_fn(event, gatts_if, param);
+ break;
+ case ESP_GATTS_MTU_EVT:
+ ESP_LOGV(TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu);
+ if (g_ble_cfg_p->set_mtu_fn) {
+ g_ble_cfg_p->set_mtu_fn(event, gatts_if, param);
+ }
+ break;
+ case ESP_GATTS_CONF_EVT:
+ ESP_LOGD(TAG, "ESP_GATTS_CONF_EVT, status = %d", param->conf.status);
+ break;
+ case ESP_GATTS_START_EVT:
+ ESP_LOGD(TAG, "SERVICE_START_EVT, status %d, service_handle %d", param->start.status, param->start.service_handle);
+ break;
+ case ESP_GATTS_CONNECT_EVT:
+ ESP_LOGD(TAG, "ESP_GATTS_CONNECT_EVT, conn_id = %d", param->connect.conn_id);
+ g_ble_cfg_p->connect_fn(event, gatts_if, param);
+ esp_ble_conn_update_params_t conn_params = {0};
+ memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
+ /* For the iOS system, please refer the official Apple documents about BLE connection parameters restrictions. */
+ conn_params.latency = 0;
+ conn_params.max_int = 0x20; // max_int = 0x20*1.25ms = 40ms
+ conn_params.min_int = 0x10; // min_int = 0x10*1.25ms = 20ms
+ conn_params.timeout = 400; // timeout = 400*10ms = 4000ms
+ esp_ble_gap_update_conn_params(&conn_params);
+ break;
+ case ESP_GATTS_DISCONNECT_EVT:
+ ESP_LOGD(TAG, "ESP_GATTS_DISCONNECT_EVT, reason = %d", param->disconnect.reason);
+ g_ble_cfg_p->disconnect_fn(event, gatts_if, param);
+ esp_ble_gap_start_advertising(&g_ble_cfg_p->adv_params);
+ break;
+ case ESP_GATTS_CREAT_ATTR_TAB_EVT: {
+ if (param->add_attr_tab.status != ESP_GATT_OK) {
+ ESP_LOGE(TAG, "creating the attribute table failed, error code=0x%x", param->add_attr_tab.status);
+ } else if (param->add_attr_tab.num_handle != g_ble_cfg_p->gatt_db_count) {
+ ESP_LOGE(TAG, "created attribute table abnormally ");
+ } else {
+ ESP_LOGD(TAG, "created attribute table successfully, the number handle = %d", param->add_attr_tab.num_handle);
+ memcpy(g_gatt_table_map, param->add_attr_tab.handles, param->add_attr_tab.num_handle * sizeof(g_gatt_table_map[0]));
+ /* We assume, for now, that the first entry is always the index to the 'service' definition */
+ esp_ble_gatts_start_service(g_gatt_table_map[0]);
+ }
+ break;
+ }
+ case ESP_GATTS_STOP_EVT:
+ case ESP_GATTS_OPEN_EVT:
+ case ESP_GATTS_CANCEL_OPEN_EVT:
+ case ESP_GATTS_CLOSE_EVT:
+ case ESP_GATTS_LISTEN_EVT:
+ case ESP_GATTS_CONGEST_EVT:
+ case ESP_GATTS_UNREG_EVT:
+ case ESP_GATTS_DELETE_EVT:
+ default:
+ break;
+ }
+}
+
+simple_ble_cfg_t *simple_ble_init()
+{
+ simple_ble_cfg_t *ble_cfg_p = (simple_ble_cfg_t *) malloc(sizeof(simple_ble_cfg_t));
+ if (ble_cfg_p == NULL) {
+ ESP_LOGE(TAG, "No memory for simple_ble_cfg_t");
+ return NULL;
+ }
+ return ble_cfg_p;
+}
+
+esp_err_t simple_ble_deinit()
+{
+ free(g_ble_cfg_p->gatt_db);
+ free(g_ble_cfg_p);
+ g_ble_cfg_p = NULL;
+ return ESP_OK;
+}
+
+/* Expects the pointer stays valid throughout */
+esp_err_t simple_ble_start(simple_ble_cfg_t *cfg)
+{
+ g_ble_cfg_p = cfg;
+ ESP_LOGD(TAG, "Free mem at start of simple_ble_init %d", esp_get_free_heap_size());
+ esp_err_t ret;
+
+ esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
+ ret = esp_bt_controller_init(&bt_cfg);
+ if (ret) {
+ ESP_LOGE(TAG, "%s enable controller failed %d", __func__, ret);
+ return ret;
+ }
+
+ ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
+ if (ret) {
+ ESP_LOGE(TAG, "%s enable controller failed %d", __func__, ret);
+ return ret;
+ }
+
+ ret = esp_bluedroid_init();
+ if (ret) {
+ ESP_LOGE(TAG, "%s init bluetooth failed %d", __func__, ret);
+ return ret;
+ }
+
+ ret = esp_bluedroid_enable();
+ if (ret) {
+ ESP_LOGE(TAG, "%s enable bluetooth failed %d", __func__, ret);
+ return ret;
+ }
+
+ ret = esp_ble_gatts_register_callback(gatts_profile_event_handler);
+ if (ret) {
+ ESP_LOGE(TAG, "gatts register error, error code = 0x%x", ret);
+ return ret;
+ }
+
+ ret = esp_ble_gap_register_callback(gap_event_handler);
+ if (ret) {
+ ESP_LOGE(TAG, "gap register error, error code = 0x%x", ret);
+ return ret;
+ }
+
+ uint16_t app_id = 0x55;
+ ret = esp_ble_gatts_app_register(app_id);
+ if (ret) {
+ ESP_LOGE(TAG, "gatts app register error, error code = 0x%x", ret);
+ return ret;
+ }
+
+ esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
+ if (local_mtu_ret) {
+ ESP_LOGE(TAG, "set local MTU failed, error code = 0x%x", local_mtu_ret);
+ }
+ ESP_LOGD(TAG, "Free mem at end of simple_ble_init %d", esp_get_free_heap_size());
+ return ESP_OK;
+}
+
+esp_err_t simple_ble_stop()
+{
+ esp_err_t err;
+ ESP_LOGD(TAG, "Free mem at start of simple_ble_stop %d", esp_get_free_heap_size());
+ err = esp_bluedroid_disable();
+ if (err != ESP_OK) {
+ return ESP_FAIL;
+ }
+ ESP_LOGD(TAG, "esp_bluedroid_disable called successfully");
+ err = esp_bluedroid_deinit();
+ if (err != ESP_OK) {
+ return err;
+ }
+ ESP_LOGD(TAG, "esp_bluedroid_deinit called successfully");
+ err = esp_bt_controller_disable();
+ if (err != ESP_OK) {
+ return ESP_FAIL;
+ }
+
+ /* The API `esp_bt_controller_deinit` will have to be removed when we add support for
+ * `reset to provisioning`
+ */
+ ESP_LOGD(TAG, "esp_bt_controller_disable called successfully");
+ err = esp_bt_controller_deinit();
+ if (err != ESP_OK) {
+ return ESP_FAIL;
+ }
+ ESP_LOGD(TAG, "esp_bt_controller_deinit called successfully");
+
+ ESP_LOGD(TAG, "Free mem at end of simple_ble_stop %d", esp_get_free_heap_size());
+ return ESP_OK;
+}
--- /dev/null
+// Copyright 2015-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.
+#ifndef _SIMPLE_BLE_
+#define _SIMPLE_BLE_
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <esp_gap_ble_api.h>
+#include <esp_gatts_api.h>
+
+typedef void (simple_ble_cb_t)(esp_gatts_cb_event_t event, esp_gatt_if_t p_gatts_if, esp_ble_gatts_cb_param_t *param);
+
+/**
+ * This structure is populated with the details required
+ * to create an instance of BLE easily. It requires function
+ * pointers, advertising parameters and gatt description table
+ */
+typedef struct {
+ /** Name to be displayed to devices scanning for ESP32 */
+ const char *device_name;
+ /** Advertising data content, according to "Supplement to the Bluetooth Core Specification" */
+ esp_ble_adv_data_t adv_data;
+ /** Parameters to configure the nature of advertising */
+ esp_ble_adv_params_t adv_params;
+ /** Descriptor table which consists the configuration required by services and characteristics */
+ esp_gatts_attr_db_t *gatt_db;
+ /** Number of entries in the gatt_db descriptor table */
+ ssize_t gatt_db_count;
+ /** BLE read callback */
+ simple_ble_cb_t *read_fn;
+ /** BLE write callback */
+ simple_ble_cb_t *write_fn;
+ /** BLE exec write callback */
+ simple_ble_cb_t *exec_write_fn;
+ /** Client disconnect callback */
+ simple_ble_cb_t *disconnect_fn;
+ /** Client connect callback */
+ simple_ble_cb_t *connect_fn;
+ /** MTU set callback */
+ simple_ble_cb_t *set_mtu_fn;
+} simple_ble_cfg_t;
+
+
+/** Initialise a simple ble connection
+ *
+ * This function allocates memory and returns a pointer to the
+ * configuration structure.
+ *
+ * @return simple_ble_cfg_t* Pointer to configuration structure
+ */
+simple_ble_cfg_t *simple_ble_init();
+
+/** Deallocates memory
+ *
+ * This function deallocate memory of the configuration structure.
+ *
+ * @return ESP_OK
+ */
+esp_err_t simple_ble_deinit();
+
+/** Starts BLE service
+ *
+ * This function makes calls to the GATT and GAP APIs
+ * to initialize the BLE service as per parameters stored
+ * in the config structure. At the end of this function,
+ * one should be able to scan and connect to the ESP32 device
+ * using BLE.
+ * This API sets the MTU size to 500 (this is not part of the config structure)
+ *
+ * @return ESP_OK on success, and appropriate error code for failure
+ */
+esp_err_t simple_ble_start(simple_ble_cfg_t *cfg);
+
+/** Stops the BLE service
+ *
+ * This API is called to stop the BLE service.
+ * This includes calls to disable and deinit bluedroid and bt controller.
+ *
+ * @return ESP_OK on success, and appropriate error code for failure
+ */
+esp_err_t simple_ble_stop();
+
+/** Convert handle to UUID of characteristic
+ *
+ * This function can be easily used to get the corresponding
+ * UUID for a characteristic that has been created, and the one for
+ * which we only have the handle for.
+ *
+ * @return uuid the UUID of the handle, -1 in case of invalid handle
+ */
+uint16_t simple_ble_get_uuid(uint16_t handle);
+
+#endif /* _SIMPLE_BLE_ */
--- /dev/null
+// 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.
+
+#include <sys/param.h>
+#include <esp_log.h>
+#include <esp_gatt_common_api.h>
+
+#include <protocomm.h>
+#include <protocomm_ble.h>
+
+#include "protocomm_priv.h"
+#include "simple_ble.h"
+
+#define CHAR_VAL_LEN_MAX (256 + 1)
+#define PREPARE_BUF_MAX_SIZE CHAR_VAL_LEN_MAX
+
+static const char *TAG = "protocomm_ble";
+
+/* BLE specific configuration parameters */
+const uint16_t GATTS_SERVICE_UUID_PROV = 0xFFFF;
+const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;
+const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;
+const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE;
+
+typedef struct {
+ uint8_t *prepare_buf;
+ int prepare_len;
+ uint16_t handle;
+} prepare_type_env_t;
+
+static prepare_type_env_t prepare_write_env;
+
+typedef struct _protocomm_ble {
+ protocomm_t *pc_ble;
+ protocomm_ble_name_uuid_t *g_nu_lookup;
+ ssize_t g_nu_lookup_count;
+ uint16_t gatt_mtu;
+} _protocomm_ble_internal_t;
+
+static _protocomm_ble_internal_t *protoble_internal;
+
+static esp_ble_adv_params_t adv_params = {
+ .adv_int_min = 0x100,
+ .adv_int_max = 0x100,
+ .adv_type = ADV_TYPE_IND,
+ .own_addr_type = BLE_ADDR_TYPE_PUBLIC,
+ .channel_map = ADV_CHNL_ALL,
+ .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
+};
+
+static char* protocomm_ble_device_name = NULL;
+
+/* The length of adv data must be less than 31 bytes */
+static esp_ble_adv_data_t adv_data = {
+ .set_scan_rsp = false,
+ .include_name = true,
+ .include_txpower = true,
+ .min_interval = 0x100,
+ .max_interval = 0x100,
+ .appearance = ESP_BLE_APPEARANCE_UNKNOWN,
+ .manufacturer_len = 0,
+ .p_manufacturer_data = NULL,
+ .service_data_len = 0,
+ .p_service_data = NULL,
+ .service_uuid_len = 0,
+ .p_service_uuid = NULL,
+ .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
+};
+
+static void hexdump(const char *msg, uint8_t *buf, int len)
+{
+ ESP_LOGD(TAG, "%s:", msg);
+ ESP_LOG_BUFFER_HEX_LEVEL(TAG, buf, len, ESP_LOG_DEBUG);
+}
+
+static const char *handle_to_handler(uint16_t handle)
+{
+ uint16_t uuid = simple_ble_get_uuid(handle);
+ for (int i = 0; i < protoble_internal->g_nu_lookup_count; i++) {
+ if (protoble_internal->g_nu_lookup[i].uuid == uuid ) {
+ return protoble_internal->g_nu_lookup[i].name;
+ }
+ }
+ return NULL;
+}
+
+static void transport_simple_ble_read(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
+{
+ static const uint8_t *read_buf = NULL;
+ static uint16_t read_len = 0;
+ esp_gatt_status_t status = ESP_OK;
+
+ ESP_LOGD(TAG, "Inside read w/ session - %d on param %d %d",
+ param->read.conn_id, param->read.handle, read_len);
+ if (!read_len) {
+ ESP_LOGD(TAG, "Reading attr value first time");
+ status = esp_ble_gatts_get_attr_value(param->read.handle, &read_len, &read_buf);
+ } else {
+ ESP_LOGD(TAG, "Subsequent read request for attr value");
+ }
+
+ esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *) malloc(sizeof(esp_gatt_rsp_t));
+ if (gatt_rsp != NULL) {
+ gatt_rsp->attr_value.len = MIN(read_len, (protoble_internal->gatt_mtu - 1));
+ if (read_len && read_buf) {
+ memcpy(gatt_rsp->attr_value.value,
+ read_buf + param->read.offset,
+ gatt_rsp->attr_value.len);
+ }
+ read_len -= gatt_rsp->attr_value.len;
+ } else {
+ ESP_LOGE(TAG, "%s, malloc failed", __func__);
+ return;
+ }
+ esp_err_t err = esp_ble_gatts_send_response(gatts_if, param->read.conn_id,
+ param->read.trans_id, status, gatt_rsp);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Send response error in read");
+ }
+ free(gatt_rsp);
+}
+
+static esp_err_t prepare_write_event_env(esp_gatt_if_t gatts_if,
+ esp_ble_gatts_cb_param_t *param)
+{
+ ESP_LOGD(TAG, "prepare write, handle = %d, value len = %d",
+ param->write.handle, param->write.len);
+ esp_gatt_status_t status = ESP_GATT_OK;
+ if (prepare_write_env.prepare_buf == NULL) {
+ prepare_write_env.prepare_buf = (uint8_t *) malloc(PREPARE_BUF_MAX_SIZE * sizeof(uint8_t));
+ if (prepare_write_env.prepare_buf == NULL) {
+ ESP_LOGE(TAG, "%s , failed tp allocate preparebuf", __func__);
+ status = ESP_GATT_NO_RESOURCES;
+ }
+ /* prepare_write_env.prepare_len = 0; */
+ } else {
+ if (param->write.offset > PREPARE_BUF_MAX_SIZE) {
+ status = ESP_GATT_INVALID_OFFSET;
+ } else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) {
+ status = ESP_GATT_INVALID_ATTR_LEN;
+ }
+ }
+ memcpy(prepare_write_env.prepare_buf + param->write.offset,
+ param->write.value,
+ param->write.len);
+ prepare_write_env.prepare_len += param->write.len;
+ prepare_write_env.handle = param->write.handle;
+ if (param->write.need_rsp) {
+ esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *) malloc(sizeof(esp_gatt_rsp_t));
+ if (gatt_rsp != NULL) {
+ gatt_rsp->attr_value.len = param->write.len;
+ gatt_rsp->attr_value.handle = param->write.handle;
+ gatt_rsp->attr_value.offset = param->write.offset;
+ gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
+ memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len);
+ esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id,
+ param->write.trans_id, status,
+ gatt_rsp);
+ if (response_err != ESP_OK) {
+ ESP_LOGE(TAG, "Send response error in prep write");
+ }
+ free(gatt_rsp);
+ } else {
+ ESP_LOGE(TAG, "%s, malloc failed", __func__);
+ }
+ }
+ if (status != ESP_GATT_OK) {
+ if (prepare_write_env.prepare_buf) {
+ free(prepare_write_env.prepare_buf);
+ prepare_write_env.prepare_buf = NULL;
+ prepare_write_env.prepare_len = 0;
+ }
+ return ESP_FAIL;
+ }
+ return ESP_OK;
+}
+
+static void transport_simple_ble_write(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
+{
+ uint8_t *outbuf = NULL;
+ ssize_t outlen = 0;
+ esp_err_t ret;
+
+ ESP_LOGD(TAG, "Inside write with session - %d on attr handle - %d \nLen -%d IS Prep - %d",
+ param->write.conn_id, param->write.handle, param->write.len, param->write.is_prep);
+
+ if (param->write.is_prep) {
+ ret = prepare_write_event_env(gatts_if, param);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Error appending to prepare buffer");
+ }
+ return;
+ } else {
+ ESP_LOGD(TAG, "is_prep not set");
+ }
+
+ ret = protocomm_req_handle(protoble_internal->pc_ble,
+ handle_to_handler(param->write.handle),
+ param->exec_write.conn_id,
+ param->write.value,
+ param->write.len,
+ &outbuf, &outlen);
+ if (ret == ESP_OK) {
+ ret = esp_ble_gatts_set_attr_value(param->write.handle, outlen, outbuf);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to set the session attribute value");
+ }
+ ret = esp_ble_gatts_send_response(gatts_if, param->write.conn_id,
+ param->write.trans_id, ESP_GATT_OK, NULL);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Send response error in write");
+ }
+ hexdump("Response from write", outbuf, outlen);
+
+ } else {
+ ESP_LOGE(TAG, "Invalid content received, killing connection");
+ esp_ble_gatts_close(gatts_if, param->write.conn_id);
+ }
+ if (outbuf) {
+ free(outbuf);
+ }
+}
+
+
+static void transport_simple_ble_exec_write(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
+{
+ esp_err_t err;
+ uint8_t *outbuf = NULL;
+ ssize_t outlen = 0;
+ ESP_LOGD(TAG, "Inside exec_write w/ session - %d", param->exec_write.conn_id);
+
+ if ((param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC)
+ &&
+ prepare_write_env.prepare_buf) {
+ err = protocomm_req_handle(protoble_internal->pc_ble,
+ handle_to_handler(prepare_write_env.handle),
+ param->exec_write.conn_id,
+ prepare_write_env.prepare_buf,
+ prepare_write_env.prepare_len,
+ &outbuf, &outlen);
+
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Invalid content received, killing connection");
+ esp_ble_gatts_close(gatts_if, param->write.conn_id);
+ } else {
+ hexdump("Response from exec write", outbuf, outlen);
+ esp_ble_gatts_set_attr_value(prepare_write_env.handle, outlen, outbuf);
+ }
+ }
+ if (prepare_write_env.prepare_buf) {
+ free(prepare_write_env.prepare_buf);
+ prepare_write_env.prepare_buf = NULL;
+ prepare_write_env.prepare_len = 0;
+ }
+
+ err = esp_ble_gatts_send_response(gatts_if, param->exec_write.conn_id, param->exec_write.trans_id, ESP_GATT_OK, NULL);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Send response error in exec write");
+ }
+ if (outbuf) {
+ free(outbuf);
+ }
+}
+
+static void transport_simple_ble_disconnect(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
+{
+ esp_err_t ret;
+ ESP_LOGV(TAG, "Inside disconnect w/ session - %d", param->disconnect.conn_id);
+ if (protoble_internal->pc_ble->sec &&
+ protoble_internal->pc_ble->sec->close_transport_session) {
+ ret = protoble_internal->pc_ble->sec->close_transport_session(param->disconnect.conn_id);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "error closing the session after disconnect");
+ }
+ }
+ protoble_internal->gatt_mtu = ESP_GATT_DEF_BLE_MTU_SIZE;
+}
+
+static void transport_simple_ble_connect(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
+{
+ esp_err_t ret;
+ ESP_LOGV(TAG, "Inside BLE connect w/ conn_id - %d", param->connect.conn_id);
+ if (protoble_internal->pc_ble->sec &&
+ protoble_internal->pc_ble->sec->new_transport_session) {
+ ret = protoble_internal->pc_ble->sec->new_transport_session(param->connect.conn_id);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "error creating the session");
+ }
+ }
+}
+
+static void transport_simple_ble_set_mtu(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
+{
+ protoble_internal->gatt_mtu = param->mtu.mtu;
+ return;
+}
+
+static esp_err_t protocomm_ble_add_endpoint(const char *ep_name,
+ protocomm_req_handler_t req_handler,
+ void *priv_data)
+{
+ /* Endpoint UUID already added when protocomm_ble_start() was called */
+ return ESP_OK;
+}
+
+static esp_err_t protocomm_ble_remove_endpoint(const char *ep_name)
+{
+ /* Endpoint UUID will be removed when protocomm_ble_stop() is called */
+ return ESP_OK;
+}
+
+
+static ssize_t populate_gatt_db(esp_gatts_attr_db_t **gatt_db_generated)
+{
+ int i;
+ /* We need esp_gatts_attr_db_t of size 2 * number of handlers + 1 for service */
+ ssize_t gatt_db_generated_entries = 2 * protoble_internal->g_nu_lookup_count + 1;
+
+ *gatt_db_generated = (esp_gatts_attr_db_t *) malloc(sizeof(esp_gatts_attr_db_t) *
+ (gatt_db_generated_entries));
+ if ((*gatt_db_generated) == NULL) {
+ ESP_LOGE(TAG, "Failed to assign memory to gatt_db");
+ return -1;
+ }
+ /* Declare service */
+ (*gatt_db_generated)[0].attr_control.auto_rsp = ESP_GATT_RSP_BY_APP;
+
+ (*gatt_db_generated)[0].att_desc.uuid_length = ESP_UUID_LEN_16;
+ (*gatt_db_generated)[0].att_desc.uuid_p = (uint8_t *) &primary_service_uuid;
+ (*gatt_db_generated)[0].att_desc.perm = ESP_GATT_PERM_READ;
+ (*gatt_db_generated)[0].att_desc.max_length = sizeof(uint16_t);
+ (*gatt_db_generated)[0].att_desc.length = sizeof(GATTS_SERVICE_UUID_PROV);
+ (*gatt_db_generated)[0].att_desc.value = (uint8_t *) &GATTS_SERVICE_UUID_PROV;
+
+ /* Declare characteristics */
+ for (i = 1 ; i < gatt_db_generated_entries ; i++) {
+ (*gatt_db_generated)[i].attr_control.auto_rsp = ESP_GATT_RSP_BY_APP;
+
+ (*gatt_db_generated)[i].att_desc.uuid_length = ESP_UUID_LEN_16;
+ (*gatt_db_generated)[i].att_desc.perm = ESP_GATT_PERM_READ |
+ ESP_GATT_PERM_WRITE;
+
+ if (i % 2 == 1) { /* Char Declaration */
+ (*gatt_db_generated)[i].att_desc.uuid_p = (uint8_t *) &character_declaration_uuid;
+ (*gatt_db_generated)[i].att_desc.max_length = sizeof(uint8_t);
+ (*gatt_db_generated)[i].att_desc.length = sizeof(uint8_t);
+ (*gatt_db_generated)[i].att_desc.value = (uint8_t *) &char_prop_read_write;
+ } else { /* Char Value */
+ (*gatt_db_generated)[i].att_desc.uuid_p = (uint8_t *)&protoble_internal->g_nu_lookup[i / 2 - 1].uuid;
+ (*gatt_db_generated)[i].att_desc.max_length = CHAR_VAL_LEN_MAX;
+ (*gatt_db_generated)[i].att_desc.length = 0;
+ (*gatt_db_generated)[i].att_desc.value = NULL;
+ }
+ }
+ return gatt_db_generated_entries;
+}
+
+static void protocomm_ble_cleanup(void)
+{
+ if (protoble_internal) {
+ if (protoble_internal->g_nu_lookup) {
+ for (unsigned i = 0; i < protoble_internal->g_nu_lookup_count; i++) {
+ if (protoble_internal->g_nu_lookup[i].name) {
+ free(protoble_internal->g_nu_lookup[i].name);
+ }
+ }
+ free(protoble_internal->g_nu_lookup);
+ }
+ free(protoble_internal);
+ protoble_internal = NULL;
+ }
+ if (protocomm_ble_device_name) {
+ free(protocomm_ble_device_name);
+ protocomm_ble_device_name = NULL;
+ }
+ if (adv_data.p_service_uuid) {
+ free(adv_data.p_service_uuid);
+ adv_data.p_service_uuid = NULL;
+ adv_data.service_uuid_len = 0;
+ }
+}
+
+esp_err_t protocomm_ble_start(protocomm_t *pc, const protocomm_ble_config_t *config)
+{
+ if (!pc || !config || !config->device_name || !config->nu_lookup) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (protoble_internal) {
+ ESP_LOGE(TAG, "Protocomm BLE already started");
+ return ESP_FAIL;
+ }
+
+ /* Store service UUID internally */
+ adv_data.service_uuid_len = sizeof(config->service_uuid);
+ adv_data.p_service_uuid = malloc(sizeof(config->service_uuid));
+ if (adv_data.p_service_uuid == NULL) {
+ ESP_LOGE(TAG, "Error allocating memory for storing service UUID");
+ protocomm_ble_cleanup();
+ return ESP_ERR_NO_MEM;
+ }
+ memcpy(adv_data.p_service_uuid, config->service_uuid, adv_data.service_uuid_len);
+
+ /* Store BLE device name internally */
+ protocomm_ble_device_name = strdup(config->device_name);
+ if (protocomm_ble_device_name == NULL) {
+ ESP_LOGE(TAG, "Error allocating memory for storing BLE device name");
+ protocomm_ble_cleanup();
+ return ESP_ERR_NO_MEM;
+ }
+
+ protoble_internal = (_protocomm_ble_internal_t *) calloc(1, sizeof(_protocomm_ble_internal_t));
+ if (protoble_internal == NULL) {
+ ESP_LOGE(TAG, "Error allocating internal protocomm structure");
+ protocomm_ble_cleanup();
+ return ESP_ERR_NO_MEM;
+ }
+
+ protoble_internal->g_nu_lookup_count = config->nu_lookup_count;
+ protoble_internal->g_nu_lookup = malloc(config->nu_lookup_count * sizeof(protocomm_ble_name_uuid_t));
+ if (protoble_internal->g_nu_lookup == NULL) {
+ ESP_LOGE(TAG, "Error allocating internal name UUID table");
+ protocomm_ble_cleanup();
+ return ESP_ERR_NO_MEM;
+ }
+
+ for (unsigned i = 0; i < protoble_internal->g_nu_lookup_count; i++) {
+ protoble_internal->g_nu_lookup[i].uuid = config->nu_lookup[i].uuid;
+ protoble_internal->g_nu_lookup[i].name = strdup(config->nu_lookup[i].name);
+ if (protoble_internal->g_nu_lookup[i].name == NULL) {
+ ESP_LOGE(TAG, "Error allocating internal name UUID entry");
+ protocomm_ble_cleanup();
+ return ESP_ERR_NO_MEM;
+ }
+ }
+
+ pc->add_endpoint = protocomm_ble_add_endpoint;
+ pc->remove_endpoint = protocomm_ble_remove_endpoint;
+ protoble_internal->pc_ble = pc;
+ protoble_internal->gatt_mtu = ESP_GATT_DEF_BLE_MTU_SIZE;
+
+ simple_ble_cfg_t *ble_config = simple_ble_init();
+ if (ble_config == NULL) {
+ ESP_LOGE(TAG, "Ran out of memory for BLE config");
+ protocomm_ble_cleanup();
+ return ESP_ERR_NO_MEM;
+ }
+
+ /* Set function pointers required for simple BLE layer */
+ ble_config->read_fn = transport_simple_ble_read;
+ ble_config->write_fn = transport_simple_ble_write;
+ ble_config->exec_write_fn = transport_simple_ble_exec_write;
+ ble_config->disconnect_fn = transport_simple_ble_disconnect;
+ ble_config->connect_fn = transport_simple_ble_connect;
+ ble_config->set_mtu_fn = transport_simple_ble_set_mtu;
+
+ /* Set parameters required for advertising */
+ ble_config->adv_data = adv_data;
+ ble_config->adv_params = adv_params;
+
+ ble_config->device_name = protocomm_ble_device_name;
+ ble_config->gatt_db_count = populate_gatt_db(&ble_config->gatt_db);
+
+ if (ble_config->gatt_db_count == -1) {
+ ESP_LOGE(TAG, "Invalid GATT database count");
+ simple_ble_deinit();
+ protocomm_ble_cleanup();
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ esp_err_t err = simple_ble_start(ble_config);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "simple_ble_start failed w/ error code 0x%x", err);
+ simple_ble_deinit();
+ protocomm_ble_cleanup();
+ return err;
+ }
+
+ prepare_write_env.prepare_buf = NULL;
+ ESP_LOGV(TAG, "Waiting for client to connect ......");
+ return ESP_OK;
+}
+
+esp_err_t protocomm_ble_stop(protocomm_t *pc)
+{
+ if ((pc != NULL) &&
+ (protoble_internal != NULL ) &&
+ (pc == protoble_internal->pc_ble)) {
+ esp_err_t ret = ESP_OK;
+ ret = simple_ble_stop();
+ if (ret) {
+ ESP_LOGE(TAG, "BLE stop failed");
+ }
+ simple_ble_deinit();
+ protocomm_ble_cleanup();
+ return ret;
+ }
+ return ESP_ERR_INVALID_ARG;
+}
--- /dev/null
+// 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.
+
+#include <stdio.h>
+#include <string.h>
+#include <esp_log.h>
+#include <esp_console.h>
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+#include <freertos/queue.h>
+#include <driver/uart.h>
+
+#include <protocomm.h>
+#include <protocomm_console.h>
+
+#include "protocomm_priv.h"
+
+static const char *TAG = "protocomm_console";
+
+static uint32_t session_id = PROTOCOMM_NO_SESSION_ID;
+static protocomm_t *pc_console = NULL; /* The global protocomm instance for console */
+static TaskHandle_t console_task = NULL;
+
+esp_err_t protocomm_console_stop(protocomm_t *pc)
+{
+ if (pc != pc_console) {
+ ESP_LOGE(TAG, "Incorrect stop request");
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ ESP_LOGI(TAG, "Stopping console...");
+ xTaskNotifyGive(console_task);
+ return ESP_OK;
+}
+
+static ssize_t hex2bin(const char *hexstr, uint8_t *bytes)
+{
+ size_t hexstrLen = strlen(hexstr);
+ ssize_t bytesLen = hexstrLen / 2;
+
+ int count = 0;
+ const char *pos = hexstr;
+
+ for(count = 0; count < bytesLen; count++) {
+ sscanf(pos, "%2hhx", &bytes[count]);
+ pos += 2;
+ }
+
+ return bytesLen;
+}
+
+static bool stopped(void)
+{
+ uint32_t flag = 0;
+ xTaskNotifyWait(0, 0, &flag, (TickType_t) 10/portTICK_RATE_MS);
+ return (flag != 0);
+}
+
+static void protocomm_console_task(void *arg)
+{
+ int uart_num = (int) arg;
+ uint8_t linebuf[256];
+ int i, cmd_ret;
+ esp_err_t ret;
+ QueueHandle_t uart_queue;
+ uart_event_t event;
+
+ ESP_LOGV(TAG, "Initialising UART on port %d", uart_num);
+ uart_driver_install(uart_num, 256, 0, 8, &uart_queue, 0);
+ /* Initialize the console */
+ esp_console_config_t console_config = {
+ .max_cmdline_args = 8,
+ .max_cmdline_length = 256,
+ };
+
+ esp_console_init(&console_config);
+ esp_console_register_help_command();
+
+ while (!stopped()) {
+ uart_write_bytes(uart_num, "\n>> ", 4);
+ memset(linebuf, 0, sizeof(linebuf));
+ i = 0;
+ do {
+ ret = xQueueReceive(uart_queue, (void * )&event, (TickType_t) 10/portTICK_RATE_MS);
+ if (ret != pdPASS) {
+ if (stopped()) {
+ break;
+ } else {
+ continue;
+ }
+ }
+ if (event.type == UART_DATA) {
+ while (uart_read_bytes(uart_num, (uint8_t *) &linebuf[i], 1, 0)) {
+ if (linebuf[i] == '\r') {
+ uart_write_bytes(uart_num, "\r\n", 2);
+ } else {
+ uart_write_bytes(uart_num, (char *) &linebuf[i], 1);
+ }
+ i++;
+ }
+ }
+ } while ((i < 255) && linebuf[i-1] != '\r');
+ if (stopped()) {
+ break;
+ }
+ ret = esp_console_run((char *) linebuf, &cmd_ret);
+ if (ret < 0) {
+ ESP_LOGE(TAG, "Console dispatcher error\n");
+ break;
+ }
+ }
+
+ if (pc_console->sec && pc_console->sec->cleanup) {
+ pc_console->sec->cleanup();
+ }
+
+ pc_console = NULL;
+ esp_console_deinit();
+
+ ESP_LOGI(TAG, "Console stopped");
+ vTaskDelete(NULL);
+}
+
+static int common_cmd_handler(int argc, char** argv)
+{
+ int i, ret;
+
+ uint32_t cur_session_id = atoi(argv[1]);
+
+ uint8_t *buf = (uint8_t *) malloc(strlen(argv[2]));
+ uint8_t *outbuf;
+ ssize_t outlen;
+ ssize_t len = hex2bin(argv[2], buf);
+
+ if (cur_session_id != session_id) {
+ if (pc_console->sec && pc_console->sec->new_transport_session) {
+ ret = pc_console->sec->new_transport_session(cur_session_id);
+ if (ret == ESP_OK) {
+ session_id = cur_session_id;
+ }
+ }
+ }
+
+ ret = protocomm_req_handle(pc_console, argv[0], cur_session_id, buf, len, &outbuf, &outlen);
+ free(buf);
+
+ if (ret == ESP_OK) {
+ printf("\r\n");
+ for (i = 0; i < outlen; i++) {
+ printf("%02x", outbuf[i]);
+ }
+ printf("\r\n");
+
+ /* Transport is responsible for freeing the transmit buffer */
+ free(outbuf);
+
+ return ESP_OK;
+ } else {
+ return ret;
+ }
+}
+
+static esp_err_t protocomm_console_add_endpoint(const char *ep_name, protocomm_req_handler_t req_handler, void *priv_data)
+{
+ (void) req_handler;
+ (void) priv_data;
+
+ esp_err_t ret;
+ esp_console_cmd_t cmd;
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.command = ep_name;
+ cmd.help = "";
+ cmd.func = common_cmd_handler;
+
+ ret = esp_console_cmd_register(&cmd);
+
+ return ret;
+}
+
+static esp_err_t protocomm_console_remove_endpoint(const char *ep_name)
+{
+ /* Command deletion happens internally in esp_console_deinit function */
+
+ return ESP_OK;
+}
+
+esp_err_t protocomm_console_start(protocomm_t *pc, const protocomm_console_config_t *config)
+{
+ if (pc == NULL) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (pc_console != NULL) {
+ if (pc_console == pc) {
+ return ESP_ERR_INVALID_STATE;
+ }
+ else {
+ return ESP_ERR_NOT_SUPPORTED;
+ }
+ }
+
+
+ if (xTaskCreate(protocomm_console_task, "protocomm_console",
+ config->stack_size, NULL, config->task_priority, &console_task) != pdPASS) {
+ return ESP_FAIL;
+ }
+
+ pc->add_endpoint = protocomm_console_add_endpoint;
+ pc->remove_endpoint = protocomm_console_remove_endpoint;
+ pc_console = pc;
+ return ESP_OK;
+}
--- /dev/null
+// 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.
+
+#include <freertos/FreeRTOS.h>
+#include <freertos/task.h>
+#include <esp_log.h>
+#include <esp_err.h>
+
+#include <http_server.h>
+
+#include <protocomm.h>
+#include <protocomm_httpd.h>
+
+#include "protocomm_priv.h"
+
+static const char *TAG = "protocomm_httpd";
+static protocomm_t *pc_httpd; /* The global protocomm instance for HTTPD */
+static uint32_t session_id = PROTOCOMM_NO_SESSION_ID;
+
+#define MAX_REQ_BODY_LEN 4096
+
+static esp_err_t common_post_handler(httpd_req_t *req)
+{
+ esp_err_t ret;
+ uint8_t *outbuf = NULL;
+ char *req_body = NULL;
+ const char *ep_name = NULL;
+ ssize_t outlen;
+
+ int cur_session_id = httpd_req_to_sockfd(req);
+
+ if (cur_session_id != session_id) {
+ /* Initialise new security session */
+ if (session_id != PROTOCOMM_NO_SESSION_ID) {
+ ESP_LOGV(TAG, "Closing session with ID: %d", session_id);
+ /* Presently HTTP server doesn't support callback on socket closure so
+ * previous session can only be closed when new session is requested */
+ if (pc_httpd->sec && pc_httpd->sec->close_transport_session) {
+ ret = pc_httpd->sec->close_transport_session(session_id);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to close session with ID: %d", session_id);
+ ret = ESP_FAIL;
+ goto out;
+ }
+ }
+ session_id = PROTOCOMM_NO_SESSION_ID;
+ }
+ if (pc_httpd->sec && pc_httpd->sec->new_transport_session) {
+ ret = pc_httpd->sec->new_transport_session(cur_session_id);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to launch new session with ID: %d", cur_session_id);
+ ret = ESP_FAIL;
+ goto out;
+ }
+ }
+ session_id = cur_session_id;
+ ESP_LOGV(TAG, "New session with ID: %d", cur_session_id);
+ }
+
+ if (req->content_len <= 0) {
+ ESP_LOGE(TAG, "Content length not found");
+ ret = ESP_FAIL;
+ goto out;
+ } else if (req->content_len > MAX_REQ_BODY_LEN) {
+ ESP_LOGE(TAG, "Request content length should be less than 4kb");
+ ret = ESP_FAIL;
+ goto out;
+ }
+
+ req_body = (char *) malloc(req->content_len);
+ if (!req_body) {
+ ESP_LOGE(TAG, "Unable to allocate for request length %d", req->content_len);
+ ret = ESP_ERR_NO_MEM;
+ goto out;
+ }
+
+ size_t recv_size = 0;
+ while (recv_size < req->content_len) {
+ ret = httpd_req_recv(req, req_body + recv_size, req->content_len - recv_size);
+ if (ret < 0) {
+ ret = ESP_FAIL;
+ goto out;
+ }
+ recv_size += ret;
+ }
+
+ /* Extract the endpoint name from URI string of type "/ep_name" */
+ ep_name = req->uri + 1;
+
+ ret = protocomm_req_handle(pc_httpd, ep_name, session_id,
+ (uint8_t *)req_body, recv_size, &outbuf, &outlen);
+
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Data handler failed");
+ ret = ESP_FAIL;
+ goto out;
+ }
+
+ ret = httpd_resp_send(req, (char *)outbuf, outlen);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "HTTP send failed");
+ ret = ESP_FAIL;
+ goto out;
+ }
+ ret = ESP_OK;
+out:
+ if (req_body) {
+ free(req_body);
+ }
+ if (outbuf) {
+ free(outbuf);
+ }
+ return ret;
+}
+
+esp_err_t protocomm_httpd_add_endpoint(const char *ep_name,
+ protocomm_req_handler_t req_handler,
+ void *priv_data)
+{
+ if (pc_httpd == NULL) {
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ ESP_LOGV(TAG, "Adding endpoint : %s", ep_name);
+
+ /* Construct URI name by prepending '/' to ep_name */
+ char* ep_uri = calloc(1, strlen(ep_name) + 2);
+ if (!ep_uri) {
+ ESP_LOGE(TAG, "Malloc failed for ep uri");
+ return ESP_ERR_NO_MEM;
+ }
+
+ /* Create URI handler structure */
+ sprintf(ep_uri, "/%s", ep_name);
+ httpd_uri_t config_handler = {
+ .uri = ep_uri,
+ .method = HTTP_POST,
+ .handler = common_post_handler,
+ .user_ctx = NULL
+ };
+
+ /* Register URI handler */
+ esp_err_t err;
+ httpd_handle_t *server = (httpd_handle_t *) pc_httpd->priv;
+ if ((err = httpd_register_uri_handler(*server, &config_handler)) != ESP_OK) {
+ ESP_LOGE(TAG, "Uri handler register failed: %s", esp_err_to_name(err));
+ free(ep_uri);
+ return ESP_FAIL;
+ }
+
+ free(ep_uri);
+ return ESP_OK;
+}
+
+static esp_err_t protocomm_httpd_remove_endpoint(const char *ep_name)
+{
+ if (pc_httpd == NULL) {
+ return ESP_ERR_INVALID_STATE;
+ }
+
+ ESP_LOGV(TAG, "Removing endpoint : %s", ep_name);
+
+ /* Construct URI name by prepending '/' to ep_name */
+ char* ep_uri = calloc(1, strlen(ep_name) + 2);
+ if (!ep_uri) {
+ ESP_LOGE(TAG, "Malloc failed for ep uri");
+ return ESP_ERR_NO_MEM;
+ }
+ sprintf(ep_uri, "/%s", ep_name);
+
+ /* Unregister URI handler */
+ esp_err_t err;
+ httpd_handle_t *server = (httpd_handle_t *) pc_httpd->priv;
+ if ((err = httpd_unregister_uri_handler(*server, ep_uri, HTTP_POST)) != ESP_OK) {
+ ESP_LOGE(TAG, "Uri handler de-register failed: %s", esp_err_to_name(err));
+ free(ep_uri);
+ return ESP_FAIL;
+ }
+
+ free(ep_uri);
+ return ESP_OK;
+}
+
+esp_err_t protocomm_httpd_start(protocomm_t *pc, const protocomm_httpd_config_t *config)
+{
+ if (!pc || !config) {
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ if (pc_httpd) {
+ if (pc == pc_httpd) {
+ ESP_LOGE(TAG, "HTTP server already running for this protocomm instance");
+ return ESP_ERR_INVALID_STATE;
+ }
+ ESP_LOGE(TAG, "HTTP server started for another protocomm instance");
+ return ESP_ERR_NOT_SUPPORTED;
+ }
+
+ /* Private data will point to the HTTP server handle */
+ pc->priv = calloc(1, sizeof(httpd_handle_t));
+ if (!pc->priv) {
+ ESP_LOGE(TAG, "Malloc failed for HTTP server handle");
+ return ESP_ERR_NO_MEM;
+ }
+
+ /* Configure the HTTP server */
+ httpd_config_t server_config = HTTPD_DEFAULT_CONFIG();
+ server_config.server_port = config->port;
+ server_config.stack_size = config->stack_size;
+ server_config.task_priority = config->task_priority;
+ server_config.lru_purge_enable = true;
+ server_config.max_open_sockets = 1;
+
+ esp_err_t err;
+ if ((err = httpd_start((httpd_handle_t *)pc->priv, &server_config)) != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to start http server: %s", esp_err_to_name(err));
+ free(pc->priv);
+ return err;
+ }
+
+ pc->add_endpoint = protocomm_httpd_add_endpoint;
+ pc->remove_endpoint = protocomm_httpd_remove_endpoint;
+ pc_httpd = pc;
+ return ESP_OK;
+}
+
+esp_err_t protocomm_httpd_stop(protocomm_t *pc)
+{
+ if ((pc != NULL) && (pc == pc_httpd)) {
+ httpd_handle_t *server_handle = (httpd_handle_t *) pc_httpd->priv;
+ httpd_stop(*server_handle);
+ free(server_handle);
+ pc_httpd->priv = NULL;
+ pc_httpd = NULL;
+ return ESP_OK;
+ }
+ return ESP_ERR_INVALID_ARG;
+}