diff --git a/components/esp-face b/components/esp-face index 07e9bab..b45ecf2 160000 --- a/components/esp-face +++ b/components/esp-face @@ -1 +1 @@ -Subproject commit 07e9bab3fae52c927991bf884a62534555b48260 +Subproject commit b45ecf2e3df66e7536b6ae470cfa65e099c2ee22 diff --git a/components/esp32-camera b/components/esp32-camera index 165a47f..113629b 160000 --- a/components/esp32-camera +++ b/components/esp32-camera @@ -1 +1 @@ -Subproject commit 165a47fe6a7c76daf23fa40512ae4a90a994e938 +Subproject commit 113629b1cf8769f65c09db0c3577a597e652860b diff --git a/examples/single_chip/recognition_wechat/Makefile b/examples/single_chip/recognition_wechat/Makefile new file mode 100644 index 0000000..568ebf3 --- /dev/null +++ b/examples/single_chip/recognition_wechat/Makefile @@ -0,0 +1,12 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := recognition_wechat + +SOLUTION_PATH ?= $(abspath $(shell pwd))/../../.. + +include $(SOLUTION_PATH)/components/component_conf.mk +include $(IDF_PATH)/make/project.mk + diff --git a/examples/single_chip/recognition_wechat/components/component.mk b/examples/single_chip/recognition_wechat/components/component.mk new file mode 100644 index 0000000..0b9d758 --- /dev/null +++ b/examples/single_chip/recognition_wechat/components/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/single_chip/recognition_wechat/components/network_blufi/component.mk b/examples/single_chip/recognition_wechat/components/network_blufi/component.mk new file mode 100644 index 0000000..a98f634 --- /dev/null +++ b/examples/single_chip/recognition_wechat/components/network_blufi/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/single_chip/recognition_wechat/components/network_blufi/include/wechat_blufi.h b/examples/single_chip/recognition_wechat/components/network_blufi/include/wechat_blufi.h new file mode 100644 index 0000000..dd2b5e0 --- /dev/null +++ b/examples/single_chip/recognition_wechat/components/network_blufi/include/wechat_blufi.h @@ -0,0 +1,108 @@ +// Copyright 2015-2016 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 _WECHAT_BLUFI_H_ +#define _WECHAT_BLUFI_H_ + +#include "esp_wifi.h" +#include "nvs_flash.h" +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include + +/* nvs typedef */ +#define USERDATANAMESPACE "userdata" +#define NVS_KEY_WIFI_SSID_PASS "ssid_pass" +#define NVS_KEY_RESTART_COUNT "restart_cou" +#define WIFI_SSID_PASS_SIZE (32+64) + +#define BLUFI_EXAMPLE_TAG "ESP32_BLUFI" +#define BLUFI_DEVICE_NAME "BLUFI_DEVICE_ESP_WHO1" + +#define DEFAULT_SSID "ESP_Who_Wechat" +#define DEFAULT_PWD "12345678" +#define MAX_STA_CONN 2 + +#define NOTICE_MDNS_HOSTNAME "notice-mDNS" +#define NOTICE_MDNS_INSTANCE "notice with ESP-WHO" +#define NOTICE_MDNS_SERVICE_TYPE "_notice_service" +#define NOTICE_MDNS_PROTO "_tcp" + +#define TCP_DEFAULT_PORT 8899 +#define UDP_NOTICE_PORT 7899 +#define DEFAULT_PKTSIZE 135 + +#define WIFI_IP4_CONNECTED_BIT BIT0 +#define WIFI_IP6_CONNECTED_BIT BIT1 +#define ESPTOUCH_DONE_BIT BIT2 + +#define WIFI_LIST_NUM 10 +#define RESTART_TIMEOUT_MS (5000) +#define WIFI_CONFIG_TIMEOUT (3 * 60 * 1000) + +#define NOTICE_UDP_BUF_SIZE (64) +#define NOTICE_UDP_RETRY_COUNT (3) + +#define BLUFI_INFO(fmt, ...) ESP_LOGI(BLUFI_EXAMPLE_TAG, fmt, ##__VA_ARGS__) +#define BLUFI_ERROR(fmt, ...) ESP_LOGE(BLUFI_EXAMPLE_TAG, fmt, ##__VA_ARGS__) + +/** + * @brief wifi_info_erase + */ +esp_err_t wifi_info_erase(const char *key); + +/** + * @brief load_info_nvs + */ +esp_err_t load_info_nvs(char *key, void *value, size_t len); + +/** + * @brief save_info_nvs + */ +esp_err_t save_info_nvs(char *key, void *value, size_t len); + +/** + * @brief 蓝牙、wifi 初始化、mdns 初始化、udp 发现初始化 + */ +void blufi_main(); + +/** + * @brief wifi initialise for Software AP + Sta + */ +void wifi_init_ap_sta(); + +/** + * @brief 等待网络连接成功,获取到 IP 地址 + */ +void wait_net_connected(); + +/** + * @brief 进入 蓝牙配网模式 + */ +void enter_blufi_config_wifi(); + +void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free); + +int blufi_aes_encrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len); + +int blufi_aes_decrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len); + +uint16_t blufi_crc_checksum(uint8_t iv8, uint8_t *data, int len); + +int blufi_security_init(void); + +void blufi_security_deinit(void); + +#endif diff --git a/examples/single_chip/recognition_wechat/components/network_blufi/wechat_blufi.c b/examples/single_chip/recognition_wechat/components/network_blufi/wechat_blufi.c new file mode 100644 index 0000000..2094816 --- /dev/null +++ b/examples/single_chip/recognition_wechat/components/network_blufi/wechat_blufi.c @@ -0,0 +1,957 @@ +// Copyright 2015-2016 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. + +/* C Includes */ +#include +#include +#include + +/* FreeRTOS Includes */ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +/* ESP32 Includes */ +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "esp_bt.h" +#include "nvs_flash.h" +#include "mdns.h" + +/* Bluetooth Includes */ +#include "esp_blufi_api.h" +#include "esp_bt_defs.h" +#include "esp_gap_ble_api.h" +#include "esp_bt_main.h" +#include "esp_bt_device.h" + +/* wechat Includes */ +#include "wechat_blufi.h" + +static uint8_t ble_service_uuid128[32] = { + /* LSB <--------------------------------------------------------------------------------> MSB */ + //first uuid, 16bit, [12],[13] is the value + 0xfb, + 0x34, + 0x9b, + 0x5f, + 0x80, + 0x00, + 0x00, + 0x80, + 0x00, + 0x10, + 0x00, + 0x00, + 0xFF, + 0xFF, + 0x00, + 0x00, +}; + +//static uint8_t test_manufacturer[TEST_MANUFACTURER_DATA_LEN] = {0x12, 0x23, 0x45, 0x56}; +static esp_ble_adv_data_t ble_adv_data = { + .set_scan_rsp = false, + .include_name = true, + .include_txpower = true, + .min_interval = 0x100, + .max_interval = 0x100, + .appearance = 0x00, + .manufacturer_len = 0, + .p_manufacturer_data = NULL, + .service_data_len = 0, + .p_service_data = NULL, + .service_uuid_len = 16, + .p_service_uuid = ble_service_uuid128, + .flag = 0x6, +}; + +static esp_ble_adv_params_t ble_adv_params = { + .adv_int_min = 0x100, + .adv_int_max = 0x100, + .adv_type = ADV_TYPE_IND, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + //.peer_addr = + //.peer_addr_type = + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, +}; + +wifi_config_t sta_config = {0}; +wifi_config_t ap_config = {0}; + +/* FreeRTOS event group to signal when we are connected & ready to make a request */ +static EventGroupHandle_t wifi_event_group; + +/* The event group allows multiple bits for each event, + but we only care about one event - are we connected + to the AP with an IP? */ +const int CONNECTED_BIT = BIT0; + +/* store the station info for send back to phone */ +static bool gl_sta_connected = false; +static uint8_t gl_sta_bssid[6]; +static uint8_t gl_sta_ssid[32]; +static int gl_sta_ssid_len; + +/* connect infor*/ +static uint8_t server_if; +static uint16_t conn_id; + +static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param); + +void enter_blufi_config_wifi(); + +void wifi_connect_succuess(); + +/** + * @brief wifi_info_erase + */ +esp_err_t wifi_info_erase(const char *key) +{ + esp_err_t ret = ESP_OK; + nvs_handle handle = 0; + + ret = nvs_open(USERDATANAMESPACE, NVS_READWRITE, &handle); + if (ret != ESP_OK) { + ESP_LOGE("nvs", "unable to open NVS namespace, errcode is %d\n", ret); + return ESP_FAIL; + } + + /** + * @brief If key is USERDATANAMESPACE, erase all info in USERDATANAMESPACE + */ + if (!strcmp(key, USERDATANAMESPACE)) { + ret = nvs_erase_all(handle); + } else { + ret = nvs_erase_key(handle, key); + } + + nvs_commit(handle); + if (handle > 0) { + nvs_close(handle); + } + + return ESP_OK; +} + +/** + * @brief load_info_nvs + */ +esp_err_t load_info_nvs(char *key, void *value, size_t len) +{ + esp_err_t err; + nvs_handle handle = 0; + size_t length = len; + + err = nvs_open(USERDATANAMESPACE, NVS_READWRITE, &handle); + if (err != ESP_OK) { + ESP_LOGE("nvs", "unable to open NVS namespace, errcode is %d\n", err); + return ESP_FAIL; + } + if (value == NULL) { + ESP_LOGE("nvs", "value NVS_ERR_PARAMETER_NULL\n"); + return ESP_FAIL; + } + + err = nvs_get_blob(handle, key, value, &length); + if (err != ESP_OK) { + ESP_LOGE("load_info_nvs", "load_info_nvs\n"); + return err; + } + + if (handle > 0) { + nvs_close(handle); + } + return ESP_OK; +} + +/** + * @brief save_info_nvs + */ +esp_err_t save_info_nvs(char *key, void *value, size_t len) +{ + esp_err_t err; + nvs_handle handle = 0; + size_t length = len; + + err = nvs_open(USERDATANAMESPACE, NVS_READWRITE, &handle); + if (err != ESP_OK) { + ESP_LOGE("nvs", "unable to open NVS namespace, errcode is %d\n", err); + return ESP_FAIL; + } + if (value == NULL) { + ESP_LOGE("nvs", "value NVS_ERR_PARAMETER_NULL\n"); + return ESP_FAIL; + } + + err = nvs_set_blob(handle, key, value, length); + if (err == ESP_OK) { + ESP_LOGI("save_info_nvs", "save_info_nvs\n"); + } + + nvs_commit(handle); + if (handle > 0) { + nvs_close(handle); + } + return err; +} + +/** + * @brief 等待网络连接成功,获取到 IP 地址 + */ +void wait_net_connected() +{ + xEventGroupWaitBits(wifi_event_group, WIFI_IP4_CONNECTED_BIT, false, true, portMAX_DELAY); + // xEventGroupWaitBits(wifi_event_group, WIFI_IP4_CONNECTED_BIT | WIFI_IP6_CONNECTED_BIT, false, true, portMAX_DELAY); +} + +/** + * @brief wifi 事件处理函数 + */ +static esp_err_t event_handler(void *ctx, system_event_t *event) +{ + wifi_mode_t mode; + static uint8_t connect_try_count = 0; + + switch (event->event_id) { + case SYSTEM_EVENT_STA_START: + ESP_LOGI("event_handler", "SYSTEM_EVENT_STA_START\n"); + if (strlen((char *)sta_config.sta.ssid) != 0 && strlen((char *)sta_config.sta.password) != 0) { + ESP_ERROR_CHECK(esp_wifi_connect()); + } + break; + + case SYSTEM_EVENT_STA_STOP: + ESP_LOGI("event_handler", "SYSTEM_EVENT_STA_STOP\n"); + break; + + case SYSTEM_EVENT_STA_CONNECTED: + ESP_LOGI("event_handler", "SYSTEM_EVENT_STA_CONNECTED\n"); + gl_sta_connected = true; + connect_try_count = 0; + memcpy(gl_sta_bssid, event->event_info.connected.bssid, 6); + memcpy(gl_sta_ssid, event->event_info.connected.ssid, event->event_info.connected.ssid_len); + gl_sta_ssid_len = event->event_info.connected.ssid_len; + + save_info_nvs(NVS_KEY_WIFI_SSID_PASS, (void *)(sta_config.sta.ssid), WIFI_SSID_PASS_SIZE); + /* enable ipv6 */ + tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA); + break; + + case SYSTEM_EVENT_STA_DISCONNECTED: + ESP_LOGI("event_handler", "SYSTEM_EVENT_STA_DISCONNECTED\n"); + /* This is a workaround as ESP32 WiFi libs don't currently auto-reassociate. */ + if (connect_try_count++ > 5 && connect_try_count < 10) { + // connect_try_count = 0; + esp_wifi_get_mode(&mode); + if (esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL) != ESP_OK) { + ESP_LOGI("blufi","esp_fail\n"); + } + } else { + gl_sta_connected = false; + memset(gl_sta_ssid, 0, 32); + memset(gl_sta_bssid, 0, 6); + gl_sta_ssid_len = 0; + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, WIFI_IP4_CONNECTED_BIT | WIFI_IP6_CONNECTED_BIT); + } + break; + + case SYSTEM_EVENT_STA_GOT_IP: { + esp_blufi_extra_info_t info; + ESP_LOGI("event_handler", "SYSTEM_EVENT_STA_GOT_IP\n"); + ESP_LOGI("event_handler", "got ip4:%s\n", ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); + xEventGroupSetBits(wifi_event_group, WIFI_IP4_CONNECTED_BIT); + esp_wifi_get_mode(&mode); + + memset(&info, 0, sizeof(esp_blufi_extra_info_t)); + memcpy(info.sta_bssid, gl_sta_bssid, 6); + info.sta_bssid_set = true; + info.sta_ssid = gl_sta_ssid; + info.sta_ssid_len = gl_sta_ssid_len; + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info); + + wifi_connect_succuess(); + break; + } + + case SYSTEM_EVENT_AP_STA_GOT_IP6: + ESP_LOGI("event_handler", "SYSTEM_EVENT_AP_STA_GOT_IP6\n"); + ESP_LOGI("event_handler", "got ip6:%s\n", ip6addr_ntoa(&event->event_info.got_ip6.ip6_info.ip)); + xEventGroupSetBits(wifi_event_group, WIFI_IP6_CONNECTED_BIT); + break; + + case SYSTEM_EVENT_AP_START: + ESP_LOGI("event_handler", "SYSTEM_EVENT_AP_START\n"); + esp_wifi_get_mode(&mode); + + /* TODO: get config or information of softap, then set to report extra_info */ + if (gl_sta_connected) { + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, NULL); + } else { + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL); + } + break; + + case SYSTEM_EVENT_AP_STOP: + ESP_LOGI("event_handler", "SYSTEM_EVENT_AP_STOP\n"); + break; + + case SYSTEM_EVENT_AP_STAIPASSIGNED: + ESP_LOGI("event_handler", "SYSTEM_EVENT_AP_STAIPASSIGNED\n"); + break; + + case SYSTEM_EVENT_AP_STACONNECTED: + ESP_LOGI("event_handler", "SYSTEM_EVENT_AP_STACONNECTED\n"); + ESP_LOGI("event_handler", "station:" MACSTR " join,AID=%d\n", + MAC2STR(event->event_info.sta_connected.mac), + event->event_info.sta_connected.aid); + xEventGroupSetBits(wifi_event_group, WIFI_IP4_CONNECTED_BIT); + break; + + case SYSTEM_EVENT_AP_STADISCONNECTED: + ESP_LOGI("event_handler", "SYSTEM_EVENT_AP_STADISCONNECTED\n"); + ESP_LOGI("event_handler", "station:" MACSTR "leave,AID=%d\n", + MAC2STR(event->event_info.sta_disconnected.mac), + event->event_info.sta_disconnected.aid); + xEventGroupClearBits(wifi_event_group, WIFI_IP4_CONNECTED_BIT); + break; + + case SYSTEM_EVENT_SCAN_DONE: { + uint16_t apCount = 0; + esp_wifi_scan_get_ap_num(&apCount); + if (apCount == 0) { + BLUFI_INFO("Nothing AP found"); + break; + } + wifi_ap_record_t *ap_list = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * apCount); + if (!ap_list) { + BLUFI_ERROR("malloc error, ap_list is NULL"); + break; + } + ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&apCount, ap_list)); + esp_blufi_ap_record_t *blufi_ap_list = (esp_blufi_ap_record_t *)malloc(apCount * sizeof(esp_blufi_ap_record_t)); + if (!blufi_ap_list) { + if (ap_list) { + free(ap_list); + } + BLUFI_ERROR("malloc error, blufi_ap_list is NULL"); + break; + } + for (int i = 0; i < apCount; ++i) { + blufi_ap_list[i].rssi = ap_list[i].rssi; + memcpy(blufi_ap_list[i].ssid, ap_list[i].ssid, sizeof(ap_list[i].ssid)); + } + esp_blufi_send_wifi_list(apCount, blufi_ap_list); + esp_wifi_scan_stop(); + free(ap_list); + free(blufi_ap_list); + break; + } + default: + break; + } +#ifdef USE_MDNS + mdns_handle_system_event(ctx, event); +#endif + return ESP_OK; +} + +// wifi initialise for Software AP + Sta +void wifi_init_ap_sta() +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + + wifi_config_t ap_wifi_config = { + .ap = { + .ssid = DEFAULT_SSID, + .ssid_len = 0, + .max_connection = MAX_STA_CONN, + .password = DEFAULT_PWD, + .authmode = WIFI_AUTH_WPA_WPA2_PSK + }, + }; + + + // memcpy(sta_config.sta.ssid, sta_wifi_config.sta.ssid, WIFI_SSID_MAX_SIZE); + // memcpy(sta_config.sta.password, sta_wifi_config.sta.password, WIFI_SSID_PASS_SIZE); + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &ap_wifi_config)); + ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &sta_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + if (strlen((char *)sta_config.sta.ssid) == 0 && strlen((char *)sta_config.sta.password) == 0) { + enter_blufi_config_wifi(); + } + + ESP_LOGI("wifi_init_ap_sta", "AP SSID:%s, password:%s; Station SSID:%s, pass:%s\n", + DEFAULT_SSID, DEFAULT_PWD, sta_config.sta.ssid, sta_config.sta.password); +} + +/** + * @brief blufi 回调函数结构体 + */ +static esp_blufi_callbacks_t blufi_callbacks = { + .event_cb = blufi_event_callback, + .negotiate_data_handler = blufi_dh_negotiate_data_handler, + .encrypt_func = blufi_aes_encrypt, + .decrypt_func = blufi_aes_decrypt, + .checksum_func = blufi_crc_checksum, +}; + +/** + * @brief 蓝牙 blufi 时间处理函数 + */ +static void blufi_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param) +{ + /* actually, should post to blufi_task handle the procedure, + * now, as a example, we do it more simply */ + switch (event) { + case ESP_BLUFI_EVENT_INIT_FINISH: + BLUFI_INFO("BLUFI init finish\n"); + esp_ble_gap_set_device_name(BLUFI_DEVICE_NAME); + esp_ble_gap_config_adv_data(&ble_adv_data); + break; + + case ESP_BLUFI_EVENT_DEINIT_FINISH: + BLUFI_INFO("BLUFI deinit finish\n"); + break; + + case ESP_BLUFI_EVENT_BLE_CONNECT: + BLUFI_INFO("BLUFI ble connect\n"); + server_if = param->connect.server_if; + conn_id = param->connect.conn_id; + esp_ble_gap_stop_advertising(); + blufi_security_init(); + break; + + case ESP_BLUFI_EVENT_BLE_DISCONNECT: + BLUFI_INFO("BLUFI ble disconnect\n"); + vTaskDelay(200 / portTICK_PERIOD_MS); + blufi_security_deinit(); + esp_ble_gap_start_advertising(&ble_adv_params); + break; + + case ESP_BLUFI_EVENT_SET_WIFI_OPMODE: + BLUFI_INFO("BLUFI Set WIFI opmode %d\n", param->wifi_mode.op_mode); + ESP_ERROR_CHECK(esp_wifi_set_mode(param->wifi_mode.op_mode)); + break; + + case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP: + BLUFI_INFO("BLUFI requset wifi connect to AP\n"); + /* there is no wifi callback when the device has already connected to this wifi + so disconnect wifi before connection. + */ + esp_wifi_disconnect(); + esp_wifi_connect(); + break; + + case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP: + BLUFI_INFO("BLUFI requset wifi disconnect from AP\n"); + esp_wifi_disconnect(); + break; + + case ESP_BLUFI_EVENT_REPORT_ERROR: + BLUFI_ERROR("BLUFI report error, error code %d\n", param->report_error.state); + esp_blufi_send_error_info(param->report_error.state); + break; + + case ESP_BLUFI_EVENT_GET_WIFI_STATUS: { + wifi_mode_t mode; + esp_blufi_extra_info_t info; + + esp_wifi_get_mode(&mode); + + if (gl_sta_connected) { + memset(&info, 0, sizeof(esp_blufi_extra_info_t)); + memcpy(info.sta_bssid, gl_sta_bssid, 6); + info.sta_bssid_set = true; + info.sta_ssid = gl_sta_ssid; + info.sta_ssid_len = gl_sta_ssid_len; + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info); + } else { + esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL); + } + BLUFI_INFO("BLUFI get wifi status from AP\n"); + + break; + } + + case ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE: + BLUFI_INFO("blufi close a gatt connection"); + vTaskDelay(200 / portTICK_PERIOD_MS); + esp_blufi_close(server_if, conn_id); + break; + + case ESP_BLUFI_EVENT_DEAUTHENTICATE_STA: + BLUFI_INFO("ESP_BLUFI_EVENT_DEAUTHENTICATE_STA\n"); + /* TODO */ + break; + + case ESP_BLUFI_EVENT_RECV_STA_BSSID: + memcpy(sta_config.sta.bssid, param->sta_bssid.bssid, 6); + sta_config.sta.bssid_set = 1; + esp_wifi_set_config(WIFI_IF_STA, &sta_config); + BLUFI_INFO("Recv STA BSSID %s\n", sta_config.sta.ssid); + break; + + case ESP_BLUFI_EVENT_RECV_STA_SSID: + strncpy((char *)sta_config.sta.ssid, (char *)param->sta_ssid.ssid, param->sta_ssid.ssid_len); + sta_config.sta.ssid[param->sta_ssid.ssid_len] = '\0'; + esp_wifi_set_config(WIFI_IF_STA, &sta_config); + BLUFI_INFO("Recv STA SSID %s\n", sta_config.sta.ssid); + break; + + case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + strncpy((char *)sta_config.sta.password, (char *)param->sta_passwd.passwd, param->sta_passwd.passwd_len); + sta_config.sta.password[param->sta_passwd.passwd_len] = '\0'; + esp_wifi_set_config(WIFI_IF_STA, &sta_config); + BLUFI_INFO("Recv STA PASSWORD %s\n", sta_config.sta.password); + break; + + case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + strncpy((char *)ap_config.ap.ssid, (char *)param->softap_ssid.ssid, param->softap_ssid.ssid_len); + ap_config.ap.ssid[param->softap_ssid.ssid_len] = '\0'; + ap_config.ap.ssid_len = param->softap_ssid.ssid_len; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP SSID %s, ssid len %d\n", ap_config.ap.ssid, ap_config.ap.ssid_len); + break; + + case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + strncpy((char *)ap_config.ap.password, (char *)param->softap_passwd.passwd, param->softap_passwd.passwd_len); + ap_config.ap.password[param->softap_passwd.passwd_len] = '\0'; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP PASSWORD %s len = %d\n", ap_config.ap.password, param->softap_passwd.passwd_len); + break; + + case ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM: + if (param->softap_max_conn_num.max_conn_num > 4) { + return; + } + ap_config.ap.max_connection = param->softap_max_conn_num.max_conn_num; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP MAX CONN NUM %d\n", ap_config.ap.max_connection); + break; + + case ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE: + if (param->softap_auth_mode.auth_mode >= WIFI_AUTH_MAX) { + return; + } + ap_config.ap.authmode = param->softap_auth_mode.auth_mode; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP AUTH MODE %d\n", ap_config.ap.authmode); + break; + + case ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL: + if (param->softap_channel.channel > 13) { + return; + } + ap_config.ap.channel = param->softap_channel.channel; + esp_wifi_set_config(WIFI_IF_AP, &ap_config); + BLUFI_INFO("Recv SOFTAP CHANNEL %d\n", ap_config.ap.channel); + break; + + case ESP_BLUFI_EVENT_GET_WIFI_LIST: { + wifi_scan_config_t scanConf = { + .ssid = NULL, + .bssid = NULL, + .channel = 0, + .show_hidden = false + }; + ESP_ERROR_CHECK(esp_wifi_scan_start(&scanConf, true)); + BLUFI_INFO("ESP_BLUFI_EVENT_GET_WIFI_LIST\n"); + break; + } + + case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA: + BLUFI_INFO("Recv Custom Data %d\n", param->custom_data.data_len); + esp_log_buffer_hex("Custom Data", param->custom_data.data, param->custom_data.data_len); + if (strcmp("restart", (char *)param->custom_data.data)) { + esp_restart(); + } + break; + + case ESP_BLUFI_EVENT_RECV_USERNAME: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_CA_CERT: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + /* Not handle currently */ + break; + case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + /* Not handle currently */ + break; + ; + case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + /* Not handle currently */ + break; + default: + break; + } +} + +/** + * @brief 蓝牙 gap 事件处理 + */ +static void ble_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(&ble_adv_params); + break; + + default: + break; + } +} + +/** + * @brief 蓝牙初始化 + */ +static esp_err_t bluetooth_init() +{ + esp_err_t ret; + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ret = esp_bt_controller_init(&bt_cfg); + if (ret) { + BLUFI_ERROR("%s initialize bt controller failed: %s\n", __func__, esp_err_to_name(ret)); + return ret; + } + + ret = esp_bt_controller_enable(ESP_BT_MODE_BLE); + if (ret) { + BLUFI_ERROR("%s enable bt controller failed: %s\n", __func__, esp_err_to_name(ret)); + return ret; + } + + ret = esp_bluedroid_init(); + if (ret) { + BLUFI_ERROR("%s init bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); + return ret; + } + + ret = esp_bluedroid_enable(); + if (ret) { + BLUFI_ERROR("%s init bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); + return ret; + } + + BLUFI_INFO("BD ADDR: " ESP_BD_ADDR_STR "\n", ESP_BD_ADDR_HEX(esp_bt_dev_get_address())); + + BLUFI_INFO("BLUFI VERSION %04x\n", esp_blufi_get_version()); + + ret = esp_ble_gap_register_callback(ble_gap_event_handler); + if (ret) { + BLUFI_ERROR("%s gap register failed, error code = %x\n", __func__, ret); + return ret; + } + + ret = esp_blufi_register_callbacks(&blufi_callbacks); + if (ret) { + BLUFI_ERROR("%s blufi register failed, error code = %x\n", __func__, ret); + return ret; + } + + ret = esp_blufi_profile_init(); + if (ret) { + BLUFI_ERROR("%s blufi profile init failed, error code = %x\n", __func__, ret); + return ret; + } + return ret; +} + +/** + * @brief 蓝牙配网析构 + */ +static bool blufi_init = true; +static void blufi_deinitialize() +{ + if (blufi_init) { + blufi_init = false; + ESP_ERROR_CHECK(esp_blufi_profile_deinit()); + ESP_ERROR_CHECK(esp_bluedroid_disable()); + ESP_ERROR_CHECK(esp_bluedroid_deinit()); + ESP_ERROR_CHECK(esp_bt_controller_disable()); + ESP_ERROR_CHECK(esp_bt_controller_deinit()); + BLUFI_INFO("blufi_deinitialize\n"); + } + // ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BTDM)); +} + +/** + * @brief 蓝牙配网初始化 + */ +static void blufi_initialize() +{ + if (!blufi_init) { + blufi_init = true; + // initialise blutooth + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg)); + ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE)); + ESP_ERROR_CHECK(esp_bluedroid_init()); + ESP_ERROR_CHECK(esp_bluedroid_enable()); + ESP_ERROR_CHECK(esp_blufi_profile_init()); + BLUFI_INFO("blufi_initialize\n"); + } +} + +// wifi config timer +TimerHandle_t wifi_config_timer; +static bool wifi_configing = false; + +/** + * @brief wifi 连接成功 + */ +void wifi_connect_succuess() +{ + +} + +/** + * @brief 蓝牙配网超时 + */ +void wifi_config_timer_callback(void *arg) +{ + if (wifi_configing == true){ + wifi_configing = false; + // if (connnected) { + // } else { + // } + BLUFI_ERROR("wifi_config_timer_callback out\n"); + + // de-initialise blufi + blufi_deinitialize(); + + xTimerDelete(wifi_config_timer, portMAX_DELAY); + } +} + +/** + * @brief 进入 蓝牙配网模式 + */ +void enter_blufi_config_wifi() +{ + if (wifi_configing == false) { + wifi_configing = true; + + // initialise blufi + blufi_initialize(); + + // start timer for 1 minute + wifi_config_timer = xTimerCreate( + "wifi_config_timer", + WIFI_CONFIG_TIMEOUT / portTICK_PERIOD_MS, //period time + pdFALSE, //auto load + (void *)NULL, //timer parameter + wifi_config_timer_callback); //timer callback + + xTimerStart(wifi_config_timer, portMAX_DELAY); + } else { + xTimerReset(wifi_config_timer, portMAX_DELAY); + } +} + +/** + * @brief udp 发现 server 初始化 + */ +static int notice_udp_server_create() +{ + esp_err_t ret = ESP_OK; + int sockfd = 0; + struct sockaddr_in server_addr = { + .sin_family = AF_INET, + .sin_port = htons(UDP_NOTICE_PORT), + .sin_addr.s_addr = htonl(INADDR_ANY), + }; + +retry: + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + goto ERR_EXIT; + } + + ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); + if (sockfd < 0) { + goto ERR_EXIT; + } + + struct timeval socket_timeout = {0, 100 * 1000}; + ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &socket_timeout, sizeof(struct timeval)); + if (sockfd < 0) { + goto ERR_EXIT; + } + + ESP_LOGD("udp notice", "create udp server, port: %d, sockfd: %d", UDP_NOTICE_PORT, sockfd); + + return sockfd; + +ERR_EXIT: + + if (sockfd != -1) { + ret = close(sockfd); + + if (ret != ESP_OK) { + ESP_LOGD("udp notice", "close fail, ret: %d", ret); + } + } + goto retry; + + return -1; +} + +/** + * @brief udp 发现初始化 + */ +static void notice_udp_task(void *arg) +{ + uint8_t root_mac[6] = {0}; + char *udp_server_buf = malloc(NOTICE_UDP_BUF_SIZE); + struct sockaddr_in from_addr = {0}; + socklen_t from_addr_len = sizeof(struct sockaddr_in); + + // wait network connected + wait_net_connected(); + + int udp_server_sockfd = notice_udp_server_create(); + + if (udp_server_sockfd == -1) { + ESP_LOGE("notice udp", "Failed to create UDP notification service"); + + vTaskDelete(NULL); + return ; + } + + ESP_ERROR_CHECK(esp_wifi_get_mac(ESP_IF_WIFI_STA, root_mac)); + + while(1){ + memset(udp_server_buf, 0, NOTICE_UDP_BUF_SIZE); + if (recvfrom(udp_server_sockfd, udp_server_buf, NOTICE_UDP_BUF_SIZE, + 0, (struct sockaddr *)&from_addr, (socklen_t *)&from_addr_len) > 0) { + ESP_LOGD("udp notice task", "Mlink notice udp recvfrom, sockfd: %d, port: %d, ip: %s, udp_server_buf: %s", + udp_server_sockfd, ntohs(((struct sockaddr_in *)&from_addr)->sin_port), + inet_ntoa(((struct sockaddr_in *)&from_addr)->sin_addr), udp_server_buf); + + if (strcmp(udp_server_buf, "Are You ESP_Who_Wechat Device?")) { + continue; + } + + sprintf(udp_server_buf, "WeChat MAC:%02x%02x%02x%02x%02x%02x TCP:%d", + MAC2STR(root_mac), TCP_DEFAULT_PORT); + + ESP_LOGD("udp notice task", "Mlink notice udp sendto, sockfd: %d, data: %s", udp_server_sockfd, udp_server_buf); + + for (int i = 0, delay_time_ms = 0; i < NOTICE_UDP_RETRY_COUNT; ++i, delay_time_ms += delay_time_ms) { + vTaskDelay(delay_time_ms); + delay_time_ms = (i == 0) ? (10 / portTICK_RATE_MS) : delay_time_ms; + + if (sendto(udp_server_sockfd, udp_server_buf, strlen(udp_server_buf), + 0, (struct sockaddr *)&from_addr, from_addr_len) <= 0) { + ESP_LOGW("udp notice task", "Mlink notice udp sendto, errno: %d, errno_str: %s", errno, strerror(errno)); + break; + } + } + } + vTaskDelay(200 / portTICK_PERIOD_MS); + } +} + +// mDNS initialise +static void initialise_mdns(void) +{ + int ret = 0; + uint8_t retry_count = 30; + uint8_t root_mac[6] = {0}; + char mac_str[16] = {0}; + + // wait network connected + wait_net_connected(); + + //initialize mDNS + ESP_ERROR_CHECK(mdns_init()); + //set mDNS hostname (required if you want to advertise services) + ESP_ERROR_CHECK(mdns_hostname_set(NOTICE_MDNS_HOSTNAME)); + //set default mDNS instance name + ESP_ERROR_CHECK(mdns_instance_name_set(NOTICE_MDNS_INSTANCE)); + + mdns_txt_item_t mdns_txt_data[] = { + {"esp-who", "WeChat"}, + {"mac", mac_str}, + }; + + ESP_ERROR_CHECK(esp_wifi_get_mac(ESP_IF_WIFI_STA, root_mac)); + sprintf(mac_str, "%02x%02x%02x%02x%02x%02x", MAC2STR(root_mac)); + + do { + //initialize MDNS service + ret = mdns_service_add(NOTICE_MDNS_INSTANCE, NOTICE_MDNS_SERVICE_TYPE, NOTICE_MDNS_PROTO, TCP_DEFAULT_PORT, + mdns_txt_data, sizeof(mdns_txt_data) / sizeof(mdns_txt_item_t)); + + if (ret != 0) { + vTaskDelay(100 / portTICK_RATE_MS); + } + } while (ret != 0 && retry_count--); +} + +/** + * @brief mdns 初始化、udp 发现初始化 + */ +static void initialise_notice() +{ + // initialise mdns + initialise_mdns(); + + // initialise udp notice service + xTaskCreatePinnedToCore(notice_udp_task, "notice_udp", 3 * 1024, NULL, 4, NULL, 1); +} + + +/** + * @brief 蓝牙、wifi 初始化、mdns 初始化、udp 发现初始化 + */ +void blufi_main() +{ + // initialise blutooth + ESP_ERROR_CHECK(bluetooth_init()); + + // de-initialise blufi + blufi_deinitialize(); + + // load wifi ssid passwd + load_info_nvs(NVS_KEY_WIFI_SSID_PASS, (void *)(sta_config.sta.ssid), WIFI_SSID_PASS_SIZE); + + // initialise wifi + wifi_init_ap_sta(); + + // initialise notice service + initialise_notice(); +} diff --git a/examples/single_chip/recognition_wechat/components/network_blufi/wechat_security.c b/examples/single_chip/recognition_wechat/components/network_blufi/wechat_security.c new file mode 100644 index 0000000..a8baf9d --- /dev/null +++ b/examples/single_chip/recognition_wechat/components/network_blufi/wechat_security.c @@ -0,0 +1,235 @@ +// Copyright 2015-2016 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. + +/* C Includes */ +#include +#include +#include + +/* FreeRTOS Includes */ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +/* ESP32 Includes */ +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_bt.h" + +/* Blutooth Includes */ +#include "esp_blufi_api.h" +#include "esp_bt_defs.h" +#include "esp_gap_ble_api.h" +#include "esp_bt_main.h" + +/* TLS Includes */ +#include "mbedtls/aes.h" +#include "mbedtls/dhm.h" +#include "mbedtls/md5.h" +#include "rom/crc.h" + +/* wechat Includes */ +#include "wechat_blufi.h" + +/* + The SEC_TYPE_xxx is for self-defined packet data type in the procedure of "BLUFI negotiate key" + If user use other negotiation procedure to exchange(or generate) key, should redefine the type by yourself. + */ +#define SEC_TYPE_DH_PARAM_LEN 0x00 +#define SEC_TYPE_DH_PARAM_DATA 0x01 +#define SEC_TYPE_DH_P 0x02 +#define SEC_TYPE_DH_G 0x03 +#define SEC_TYPE_DH_PUBLIC 0x04 + +struct blufi_security { +#define DH_SELF_PUB_KEY_LEN 128 +#define DH_SELF_PUB_KEY_BIT_LEN (DH_SELF_PUB_KEY_LEN * 8) + uint8_t self_public_key[DH_SELF_PUB_KEY_LEN]; +#define SHARE_KEY_LEN 128 +#define SHARE_KEY_BIT_LEN (SHARE_KEY_LEN * 8) + uint8_t share_key[SHARE_KEY_LEN]; + size_t share_len; +#define PSK_LEN 16 + uint8_t psk[PSK_LEN]; + uint8_t *dh_param; + int dh_param_len; + uint8_t iv[16]; + mbedtls_dhm_context dhm; + mbedtls_aes_context aes; +}; + +static struct blufi_security *blufi_sec; + +extern void btc_blufi_report_error(esp_blufi_error_state_t state); + +static int myrand(void *rng_state, unsigned char *output, size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) { + output[i] = esp_random(); + } + + return (0); +} + +void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free) +{ + int ret; + uint8_t type = data[0]; + + if (blufi_sec == NULL) { + BLUFI_ERROR("BLUFI Security is not initialized"); + btc_blufi_report_error(ESP_BLUFI_INIT_SECURITY_ERROR); + return; + } + + switch (type) { + case SEC_TYPE_DH_PARAM_LEN: + blufi_sec->dh_param_len = ((data[1] << 8) | data[2]); + if (blufi_sec->dh_param) { + free(blufi_sec->dh_param); + blufi_sec->dh_param = NULL; + } + blufi_sec->dh_param = (uint8_t *)malloc(blufi_sec->dh_param_len); + if (blufi_sec->dh_param == NULL) { + btc_blufi_report_error(ESP_BLUFI_DH_MALLOC_ERROR); + BLUFI_ERROR("%s, malloc failed\n", __func__); + return; + } + break; + case SEC_TYPE_DH_PARAM_DATA: { + if (blufi_sec->dh_param == NULL) { + BLUFI_ERROR("%s, blufi_sec->dh_param == NULL\n", __func__); + btc_blufi_report_error(ESP_BLUFI_DH_PARAM_ERROR); + return; + } + uint8_t *param = blufi_sec->dh_param; + memcpy(blufi_sec->dh_param, &data[1], blufi_sec->dh_param_len); + ret = mbedtls_dhm_read_params(&blufi_sec->dhm, ¶m, ¶m[blufi_sec->dh_param_len]); + if (ret) { + BLUFI_ERROR("%s read param failed %d\n", __func__, ret); + btc_blufi_report_error(ESP_BLUFI_READ_PARAM_ERROR); + return; + } + free(blufi_sec->dh_param); + blufi_sec->dh_param = NULL; + ret = mbedtls_dhm_make_public(&blufi_sec->dhm, (int)mbedtls_mpi_size(&blufi_sec->dhm.P), blufi_sec->self_public_key, blufi_sec->dhm.len, myrand, NULL); + if (ret) { + BLUFI_ERROR("%s make public failed %d\n", __func__, ret); + btc_blufi_report_error(ESP_BLUFI_MAKE_PUBLIC_ERROR); + return; + } + + mbedtls_dhm_calc_secret(&blufi_sec->dhm, + blufi_sec->share_key, + SHARE_KEY_BIT_LEN, + &blufi_sec->share_len, + NULL, NULL); + + mbedtls_md5(blufi_sec->share_key, blufi_sec->share_len, blufi_sec->psk); + + mbedtls_aes_setkey_enc(&blufi_sec->aes, blufi_sec->psk, 128); + + /* alloc output data */ + *output_data = &blufi_sec->self_public_key[0]; + *output_len = blufi_sec->dhm.len; + *need_free = false; + } + break; + case SEC_TYPE_DH_P: + break; + case SEC_TYPE_DH_G: + break; + case SEC_TYPE_DH_PUBLIC: + break; + } +} + +int blufi_aes_encrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len) +{ + int ret; + size_t iv_offset = 0; + uint8_t iv0[16]; + + memcpy(iv0, blufi_sec->iv, sizeof(blufi_sec->iv)); + iv0[0] = iv8; /* set iv8 as the iv0[0] */ + + ret = mbedtls_aes_crypt_cfb128(&blufi_sec->aes, MBEDTLS_AES_ENCRYPT, crypt_len, &iv_offset, iv0, crypt_data, crypt_data); + if (ret) { + return -1; + } + + return crypt_len; +} + +int blufi_aes_decrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len) +{ + int ret; + size_t iv_offset = 0; + uint8_t iv0[16]; + + memcpy(iv0, blufi_sec->iv, sizeof(blufi_sec->iv)); + iv0[0] = iv8; /* set iv8 as the iv0[0] */ + + ret = mbedtls_aes_crypt_cfb128(&blufi_sec->aes, MBEDTLS_AES_DECRYPT, crypt_len, &iv_offset, iv0, crypt_data, crypt_data); + if (ret) { + return -1; + } + + return crypt_len; +} + +uint16_t blufi_crc_checksum(uint8_t iv8, uint8_t *data, int len) +{ + /* This iv8 ignore, not used */ + return crc16_be(0, data, len); +} + +esp_err_t blufi_security_init(void) +{ + blufi_sec = (struct blufi_security *)malloc(sizeof(struct blufi_security)); + if (blufi_sec == NULL) { + return ESP_FAIL; + } + + memset(blufi_sec, 0x0, sizeof(struct blufi_security)); + + mbedtls_dhm_init(&blufi_sec->dhm); + mbedtls_aes_init(&blufi_sec->aes); + + memset(blufi_sec->iv, 0x0, 16); + return 0; +} + +void blufi_security_deinit(void) +{ + if (blufi_sec == NULL) { + return; + } + if (blufi_sec->dh_param) { + free(blufi_sec->dh_param); + blufi_sec->dh_param = NULL; + } + mbedtls_dhm_free(&blufi_sec->dhm); + mbedtls_aes_free(&blufi_sec->aes); + + memset(blufi_sec, 0x0, sizeof(struct blufi_security)); + + free(blufi_sec); + blufi_sec = NULL; +} diff --git a/examples/single_chip/recognition_wechat/main/Kconfig.projbuild b/examples/single_chip/recognition_wechat/main/Kconfig.projbuild new file mode 100644 index 0000000..eae51b5 --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/Kconfig.projbuild @@ -0,0 +1,24 @@ +menu "Example Configuration" + +config ESP_WIFI_SSID + string "WiFi SSID" + default "" + help + SSID (network name) for the example to connect to. + +config ESP_WIFI_PASSWORD + string "WiFi Password" + default "" + help + WiFi password (WPA or WPA2) for the example to use. + +config MAX_STA_CONN + int "Maximal STA connections" + default 1 + help + Max number of the STA connects to AP. + +config SERVER_IP + string "IP address of server" + default "192.168.4.1" +endmenu diff --git a/examples/single_chip/recognition_wechat/main/app_camera.c b/examples/single_chip/recognition_wechat/main/app_camera.c new file mode 100644 index 0000000..55aa7c1 --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/app_camera.c @@ -0,0 +1,73 @@ +/* ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "app_camera.h" + +static const char *TAG = "app_camera"; + +void app_camera_init() +{ + /* IO13, IO14 is designed for JTAG by default, + * to use it as generalized input, + * firstly declair it as pullup input */ + gpio_config_t conf; + conf.mode = GPIO_MODE_INPUT; + conf.pull_up_en = GPIO_PULLUP_ENABLE; + conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + conf.intr_type = GPIO_INTR_DISABLE; + conf.pin_bit_mask = 1LL << 13; + gpio_config(&conf); + conf.pin_bit_mask = 1LL << 14; + gpio_config(&conf); + + camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.xclk_freq_hz = XCLK_FREQ; + config.pixel_format = CAMERA_PIXEL_FORMAT; + config.frame_size = CAMERA_FRAME_SIZE; + config.jpeg_quality = 10; + config.fb_count = 2; + + // camera init + esp_err_t err = esp_camera_init(&config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Camera init failed with error 0x%x", err); + return; + } +} diff --git a/examples/single_chip/recognition_wechat/main/app_facenet.c b/examples/single_chip/recognition_wechat/main/app_facenet.c new file mode 100644 index 0000000..d02444a --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/app_facenet.c @@ -0,0 +1,121 @@ +/* ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "esp_log.h" +#include "app_httpserver.h" +#include "fd_forward.h" +#include "image_util.h" +#include "app_main.h" + +static const char *TAG = "app_process"; + +face_id_name_list st_face_list = {0}; +static dl_matrix3du_t *aligned_face = NULL; + +extern QueueHandle_t gpst_input_image; +extern QueueHandle_t gpst_output; + +static inline mtmn_config_t app_mtmn_config() +{ + mtmn_config_t mtmn_config; + mtmn_config.min_face = 80; + mtmn_config.pyramid = 0.7; + mtmn_config.p_threshold.score = 0.6; + mtmn_config.p_threshold.nms = 0.7; + mtmn_config.p_threshold.candidate_number = 100; + mtmn_config.r_threshold.score = 0.6; + mtmn_config.r_threshold.nms = 0.7; + mtmn_config.r_threshold.candidate_number = 4; + mtmn_config.o_threshold.score = 0.6; + mtmn_config.o_threshold.nms = 0.4; + mtmn_config.o_threshold.candidate_number = 1; + + return mtmn_config; +} + +static inline box_array_t *do_detection(camera_fb_t *fb, dl_matrix3du_t *image_mat, mtmn_config_t *mtmn_config) +{ + if(!fmt2rgb888(fb->buf, fb->len, fb->format, image_mat->item)) + { + ESP_LOGW(TAG, "fmt2rgb888 failed"); + return NULL; + } + + box_array_t *net_boxes = face_detect(image_mat, mtmn_config); + return net_boxes; +} + + +void task_process(void *arg) +{ + camera_fb_t * fb = NULL; + dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, 320, 240, 3); + if (!image_matrix) + { + ESP_LOGE(TAG, "dl_matrix3du_alloc failed"); + return; + } + + mtmn_config_t mtmn_config = app_mtmn_config(); + http_img_process_result out_res = {0}; + //out_res.lock = xSemaphoreCreateBinary(); + + out_res.image = image_matrix->item; + + while (1) + { + xQueueReceive(gpst_input_image, &fb, portMAX_DELAY); + + //xSemaphoreTake(out_res.lock, portMAX_DELAY); + out_res.net_boxes = NULL; + out_res.face_id = NULL; + + out_res.net_boxes = do_detection(fb, image_matrix, &mtmn_config); + + if (out_res.net_boxes) + { + if (g_state == START_DETECT) + { + xQueueSend(gpst_output, &out_res, portMAX_DELAY); + continue; + } + + // Get face ID + if (align_face(out_res.net_boxes, image_matrix, aligned_face) == ESP_OK) + { + out_res.face_id = get_face_id(aligned_face); + } + } + xQueueSend(gpst_output, &out_res, portMAX_DELAY); + } + + dl_matrix3du_free(image_matrix); +} + +void app_facenet_main() +{ + face_id_name_init(&st_face_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES); + aligned_face = dl_matrix3du_alloc(1, FACE_WIDTH, FACE_HEIGHT, 3); + read_face_id_from_flash_with_name(&st_face_list); + + xTaskCreatePinnedToCore(task_process, "process", 4 * 1024, NULL, 5, NULL, 1); +} diff --git a/examples/single_chip/recognition_wechat/main/app_httpserver.c b/examples/single_chip/recognition_wechat/main/app_httpserver.c new file mode 100644 index 0000000..fbfb2dc --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/app_httpserver.c @@ -0,0 +1,616 @@ +/* ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "app_httpserver.h" +#include "esp_log.h" +#include "image_util.h" +#include "fb_gfx.h" +#include "app_main.h" + +static const char *TAG = "app_httpserver"; + +#define FACE_COLOR_WHITE 0x00FFFFFF +#define FACE_COLOR_BLACK 0x00000000 +#define FACE_COLOR_RED 0x000000FF +#define FACE_COLOR_GREEN 0x0000FF00 +#define FACE_COLOR_BLUE 0x00FF0000 +#define FACE_COLOR_YELLOW (FACE_COLOR_RED | FACE_COLOR_GREEN) +#define FACE_COLOR_CYAN (FACE_COLOR_BLUE | FACE_COLOR_GREEN) +#define FACE_COLOR_PURPLE (FACE_COLOR_BLUE | FACE_COLOR_RED) + + +#define PART_BOUNDARY "123456789000000000000987654321" +static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; +static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; +static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; + +extern face_id_name_list st_face_list; +static int8_t g_is_enrolling = 0; +static int8_t g_stop_stream = 0; +static int8_t g_detection_enabled = 0; +static int8_t g_recognition_enabled = 0; +typedef struct +{ + char enroll_name[ENROLL_NAME_LEN]; +} httpd_resp_value; + +httpd_resp_value st_name; +QueueHandle_t gpst_input_image = NULL; +QueueHandle_t gpst_output = NULL; + +// Profiling +static int64_t fr_start = 0; +static int64_t fr_ready = 0; +static int64_t fr_face = 0; +static int64_t fr_recognize = 0; +static int64_t fr_encode = 0; + +//static void oneshot_timer_callback(void* arg); +char *number_suffix(int32_t number) +{ + uint8_t n = number % 10; + + if (n == 0) + return "zero"; + else if (n == 1) + return "st"; + else if (n == 2) + return "nd"; + else if (n == 3) + return "rd"; + else + return "th"; +} + + +static void rgb_print(void *image, int w, int h, uint32_t color, const char * str){ + fb_data_t fb; + fb.width = w; + fb.height = h; + fb.data = image; + fb.bytes_per_pixel = 3; + fb.format = FB_BGR888; + fb_gfx_print(&fb, (fb.width - (strlen(str) * 14)) / 2, 10, color, str); +} + +static int rgb_printf(void *image, int w, int h, uint32_t color, const char *format, ...) +{ + char loc_buf[64]; + char * temp = loc_buf; + int len; + va_list arg; + va_list copy; + va_start(arg, format); + va_copy(copy, arg); + len = vsnprintf(loc_buf, sizeof(loc_buf), format, arg); + va_end(copy); + if(len >= sizeof(loc_buf)){ + temp = (char*)malloc(len+1); + if(temp == NULL) { + return 0; + } + } + vsnprintf(temp, len+1, format, arg); + va_end(arg); + rgb_print(image, w, h, color, temp); + if(len > 64){ + free(temp); + } + return len; +} + +static void draw_face_boxes(void *image, int width, int height, box_array_t *boxes){ + int x, y, w, h, i; + uint32_t color = FACE_COLOR_YELLOW; + fb_data_t fb; + fb.width = width; + fb.height = height; + fb.data = image; + fb.bytes_per_pixel = 3; + fb.format = FB_BGR888; + for (i = 0; i < boxes->len; i++){ + // rectangle box + x = (int)boxes->box[i].box_p[0]; + y = (int)boxes->box[i].box_p[1]; + w = (int)boxes->box[i].box_p[2] - x + 1; + h = (int)boxes->box[i].box_p[3] - y + 1; + fb_gfx_drawFastHLine(&fb, x, y, w, color); + fb_gfx_drawFastHLine(&fb, x, y+h-1, w, color); + fb_gfx_drawFastVLine(&fb, x, y, h, color); + fb_gfx_drawFastVLine(&fb, x+w-1, y, h, color); +#if 0 + // landmark + int x0, y0, j; + for (j = 0; j < 10; j+=2) { + x0 = (int)boxes->landmark[i].landmark_p[j]; + y0 = (int)boxes->landmark[i].landmark_p[j+1]; + fb_gfx_fillRect(&fb, x0, y0, 3, 3, color); + } +#endif + } +} + +static inline int do_enrollment(face_id_name_list *face_list, dl_matrix3d_t *new_id) +{ + ESP_LOGD(TAG, "START ENROLLING"); + + int left_sample_face = enroll_face_id_to_flash_with_name(face_list, new_id, st_name.enroll_name); + ESP_LOGD(TAG, "Face ID %s Enrollment: Taken the %d%s sample", + face_list->tail->id_name, + ENROLL_CONFIRM_TIMES - left_sample_face, + number_suffix(ENROLL_CONFIRM_TIMES - left_sample_face)); + gpio_set_level(GPIO_LED_RED, 0); + return left_sample_face; +} + +esp_err_t facenet_stream_handler(httpd_req_t *req) +{ + esp_err_t res = ESP_OK; + g_state = START_STREAM; + g_stop_stream = 0; + + camera_fb_t * fb = NULL; + size_t _jpg_buf_len = 0; + uint8_t * _jpg_buf = NULL; + char * part_buf[64]; + + http_img_process_result out_res = {0}; + + static int64_t last_frame = 0; + if(!last_frame) { + last_frame = esp_timer_get_time(); + } + + res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); + if(res != ESP_OK){ + return res; + } + + ESP_LOGI(TAG, "Get count %d\n", st_face_list.count); + + while(true) + { + // update fsm state + if (g_stop_stream) + { + g_stop_stream = 0; + httpd_resp_send_404(req); + break; + } + else if (g_is_enrolling) + { + g_state = START_ENROLL; + } + else if (g_recognition_enabled && (st_face_list.count > 0)) + { + g_state = START_RECOGNITION; + } + else if (g_detection_enabled) + { + g_state = START_DETECT; + } + else + { + g_state = START_STREAM; + } + + ESP_LOGD(TAG, "State: %d, count:%d", g_state, st_face_list.count); + // exec event + fb = esp_camera_fb_get(); + if (!fb) + { + ESP_LOGE(TAG, "Camera capture failed"); + res = ESP_FAIL; + break; + } + + if (g_state == START_STREAM) + { + _jpg_buf = fb->buf; + _jpg_buf_len = fb->len; + goto http_response; + } + + fr_start = esp_timer_get_time(); + fr_ready = fr_start; + fr_face = fr_start; + fr_encode = fr_start; + fr_recognize = fr_start; + + xQueueSend(gpst_input_image, &fb, portMAX_DELAY); + + xQueueReceive(gpst_output, &out_res, portMAX_DELAY); + + fr_recognize = fr_face; + + if (out_res.net_boxes) + { + draw_face_boxes(out_res.image, fb->width, fb->height, out_res.net_boxes); + free(out_res.net_boxes->box); + free(out_res.net_boxes->landmark); + free(out_res.net_boxes); + + if (out_res.face_id) + { + if (g_state == START_ENROLL) + { + int left_sample_face = do_enrollment(&st_face_list, out_res.face_id); + rgb_print(out_res.image, fb->width, fb->height, FACE_COLOR_YELLOW, "START ENROLLING"); + rgb_printf(out_res.image, fb->width, fb->height, FACE_COLOR_CYAN, "\nThe %u%s sample", + ENROLL_CONFIRM_TIMES - left_sample_face, + number_suffix(ENROLL_CONFIRM_TIMES - left_sample_face)); + if (left_sample_face == 0) + { + ESP_LOGI(TAG, "Enrolled Face ID: %s", st_face_list.tail->id_name); + rgb_printf(out_res.image, fb->width, fb->height, FACE_COLOR_CYAN, "\n\nFace ID: %s", st_face_list.tail->id_name); + g_is_enrolling = 0; + g_state = START_RECOGNITION; + } + } + else + { + face_id_node *f = recognize_face_with_name(&st_face_list, out_res.face_id); + + if (f) + { + gpio_set_level(GPIO_LED_RED, 1); + rgb_printf(out_res.image, fb->width, fb->height, FACE_COLOR_GREEN, "Hello %s", f->id_name); + } + else + { + rgb_print(out_res.image, fb->width, fb->height, FACE_COLOR_RED, "\nWHO?"); + } + } // START_ENROLL + dl_matrix3d_free(out_res.face_id); + } + //xSemaphoreGive(out_res.lock); + + if(!fmt2jpg(out_res.image, fb->width*fb->height*3, fb->width, fb->height, PIXFORMAT_RGB888, 90, &_jpg_buf, &_jpg_buf_len)) + { + ESP_LOGE(TAG, "fmt2jpg failed"); + } + esp_camera_fb_return(fb); + fb = NULL; + } + else + { + _jpg_buf = fb->buf; + _jpg_buf_len = fb->len; + } + fr_encode = esp_timer_get_time(); + + +http_response: + if(res == ESP_OK){ + size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); + res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); + } + if(res == ESP_OK){ + res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len); + } + if(res == ESP_OK){ + res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); + } + if(fb){ + esp_camera_fb_return(fb); + fb = NULL; + _jpg_buf = NULL; + } else if(_jpg_buf){ + free(_jpg_buf); + _jpg_buf = NULL; + } + if(res != ESP_OK){ + break; + } + int64_t fr_end = esp_timer_get_time(); + + int64_t ready_time = (fr_ready - fr_start)/1000; + int64_t face_time = (fr_face - fr_ready)/1000; + int64_t recognize_time = (fr_recognize - fr_face)/1000; + int64_t encode_time = (fr_encode - fr_recognize)/1000; + int64_t process_time = (fr_encode - fr_start)/1000; + + int64_t frame_time = fr_end - last_frame; + last_frame = fr_end; + frame_time /= 1000; + ESP_LOGI(TAG, "MJPG: %uKB %ums (%.1ffps), %u+%u+%u+%u=%u", + (uint32_t)(_jpg_buf_len/1024), + (uint32_t)frame_time, 1000.0 / (uint32_t)frame_time, + (uint32_t)ready_time, (uint32_t)face_time, (uint32_t)recognize_time, (uint32_t)encode_time, (uint32_t)process_time); + } + + last_frame = 0; + g_state = WAIT_FOR_CONNECT; + return ESP_OK; +} + +httpd_uri_t _face_stream_handler = { + .uri = "/stream", + .method = HTTP_GET, + .handler = facenet_stream_handler, + .user_ctx = NULL +}; + +static esp_err_t cmd_handler(httpd_req_t *req){ + char* buf; + size_t buf_len; + char variable[32] = {0,}; + char value[FACE_ID_SAVE_NUMBER * ENROLL_NAME_LEN] = {0,}; + + buf_len = httpd_req_get_url_query_len(req) + 1; + if (buf_len > 1) { + buf = (char*)malloc(buf_len); + if(!buf){ + httpd_resp_send_500(req); + return ESP_FAIL; + } + if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { + if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK && + httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) { + } else { + free(buf); + httpd_resp_send_404(req); + return ESP_FAIL; + } + } else { + free(buf); + httpd_resp_send_404(req); + return ESP_FAIL; + } + free(buf); + } else { + httpd_resp_send_404(req); + return ESP_FAIL; + } +int val = atoi(value); + ESP_LOGI(TAG, "%s = %d", variable, val); + sensor_t * s = esp_camera_sensor_get(); + int res = 0; + + if(!strcmp(variable, "framesize")) { + if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val); + } + else if(!strcmp(variable, "quality")) res = s->set_quality(s, val); + else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val); + else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val); + else if(!strcmp(variable, "saturation")) res = s->set_saturation(s, val); + else if(!strcmp(variable, "gainceiling")) res = s->set_gainceiling(s, (gainceiling_t)val); + else if(!strcmp(variable, "colorbar")) res = s->set_colorbar(s, val); + else if(!strcmp(variable, "awb")) res = s->set_whitebal(s, val); + else if(!strcmp(variable, "agc")) res = s->set_gain_ctrl(s, val); + else if(!strcmp(variable, "aec")) res = s->set_exposure_ctrl(s, val); + else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val); + else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val); + else if(!strcmp(variable, "awb_gain")) res = s->set_awb_gain(s, val); + else if(!strcmp(variable, "agc_gain")) res = s->set_agc_gain(s, val); + else if(!strcmp(variable, "aec_value")) res = s->set_aec_value(s, val); + else if(!strcmp(variable, "aec2")) res = s->set_aec2(s, val); + else if(!strcmp(variable, "dcw")) res = s->set_dcw(s, val); + else if(!strcmp(variable, "bpc")) res = s->set_bpc(s, val); + else if(!strcmp(variable, "wpc")) res = s->set_wpc(s, val); + else if(!strcmp(variable, "raw_gma")) res = s->set_raw_gma(s, val); + else if(!strcmp(variable, "lenc")) res = s->set_lenc(s, val); + else if(!strcmp(variable, "special_effect")) res = s->set_special_effect(s, val); + else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val); + else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val); + + else if(!strcmp(variable, "stop_stream")) g_stop_stream = 1; + else if(!strcmp(variable, "face_detect")) { + g_detection_enabled = val; + if(!g_detection_enabled) { + g_recognition_enabled = 0; + g_is_enrolling = 0; + } + } + else if(!strcmp(variable, "face_enroll")) + { + if (!g_is_enrolling && g_recognition_enabled) + { + memcpy(st_name.enroll_name, value, strlen(value) + 1); + g_is_enrolling = 1; + } + } + else if(!strcmp(variable, "face_delete")) + { + // ["aaa","bbb"] + char delete_name[ENROLL_NAME_LEN * FACE_ID_SAVE_NUMBER]; + uint8_t len = strlen(value) - strlen("%5b%22") - strlen("%22%5d"); + memcpy(delete_name, value + strlen("%5b%22"), len); + delete_name[len] = 0; + char *p = delete_name; + char *q = NULL; + + do + { + q = strstr(p, "%22%2C%22"); + if (!q) + break; + p[q - p] = 0; + int8_t left = delete_face_id_in_flash_with_name(&st_face_list, p); + if (left > 0) + ESP_LOGW(TAG, "%s ID Delete", p); + p = q + 9; + q = strstr(p, "%22%2C%22"); + } while (q); + int8_t left = delete_face_id_in_flash_with_name(&st_face_list, p); + if (left > 0) + ESP_LOGW(TAG, "%s ID Delete", p); + } + else if(!strcmp(variable, "delete_all")) + { + delete_face_all_in_flash_with_name(&st_face_list); + } + else if(!strcmp(variable, "face_recognize")) { + g_recognition_enabled = val; + if(g_recognition_enabled){ + g_detection_enabled = val; + } + } + else if(!strcmp(variable, "get_id")) { + static char id_json[1024]; + char *p = id_json; + *p++ = '{'; + p += sprintf(p, "\"len\":%d,", st_face_list.count); + p += sprintf(p, "\"list\":["); + face_id_node *head = st_face_list.head; + for (int i = 0; i < st_face_list.count; i++) + { + p += sprintf(p, "\"%s\",", head->id_name); + head = head->next; + } + if (st_face_list.count) + p--; + *p++ = ']'; + *p++ = '}'; + *p++ = 0; + httpd_resp_set_type(req, "application/json"); + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + return httpd_resp_send(req, id_json, strlen(id_json)); + } + else { + res = -1; + } + + if(res){ + return httpd_resp_send_500(req); + } + + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + return httpd_resp_send(req, NULL, 0); +} + +httpd_uri_t cmd_uri = { + .uri = "/control", + .method = HTTP_GET, + .handler = cmd_handler, + .user_ctx = NULL +}; +httpd_handle_t camera_httpd = NULL; + + +static esp_err_t status_handler(httpd_req_t *req){ + static char json_response[1024]; + + sensor_t * s = esp_camera_sensor_get(); + char * p = json_response; + *p++ = '{'; + + p+=sprintf(p, "\"framesize\":%u,", s->status.framesize); + p+=sprintf(p, "\"quality\":%u,", s->status.quality); + p+=sprintf(p, "\"brightness\":%d,", s->status.brightness); + p+=sprintf(p, "\"contrast\":%d,", s->status.contrast); + p+=sprintf(p, "\"saturation\":%d,", s->status.saturation); + p+=sprintf(p, "\"special_effect\":%u,", s->status.special_effect); + p+=sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode); + p+=sprintf(p, "\"awb\":%u,", s->status.awb); + p+=sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain); + p+=sprintf(p, "\"aec\":%u,", s->status.aec); + p+=sprintf(p, "\"aec2\":%u,", s->status.aec2); + p+=sprintf(p, "\"ae_level\":%d,", s->status.ae_level); + p+=sprintf(p, "\"aec_value\":%u,", s->status.aec_value); + p+=sprintf(p, "\"agc\":%u,", s->status.agc); + p+=sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain); + p+=sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling); + p+=sprintf(p, "\"bpc\":%u,", s->status.bpc); + p+=sprintf(p, "\"wpc\":%u,", s->status.wpc); + p+=sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma); + p+=sprintf(p, "\"lenc\":%u,", s->status.lenc); + p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror); + p+=sprintf(p, "\"dcw\":%u,", s->status.dcw); + p+=sprintf(p, "\"colorbar\":%u", s->status.colorbar); + + p+=sprintf(p, ",\"face_detect\":%u", g_detection_enabled); + p+=sprintf(p, ",\"face_enroll\":%u,", g_is_enrolling); + p+=sprintf(p, "\"face_recognize\":%u", g_recognition_enabled); + *p++ = '}'; + *p++ = 0; + httpd_resp_set_type(req, "application/json"); + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + return httpd_resp_send(req, json_response, strlen(json_response)); +} +httpd_uri_t status_uri = { + .uri = "/status", + .method = HTTP_GET, + .handler = status_handler, + .user_ctx = NULL +}; +#if 0 +const esp_timer_create_args_t oneshot_timer_args = { + .callback = &oneshot_timer_callback, + /* argument specified here will be passed to timer callback function */ + .name = "one-shot" +}; +esp_timer_handle_t oneshot_timer; + +static void oneshot_timer_callback(void* arg) +{ + if(g_state != START_ENROLL) + g_is_enrolling = 1; +} + + +static void IRAM_ATTR gpio_isr_handler(void* arg) +{ + esp_err_t err = esp_timer_start_once(oneshot_timer, 500000); + if(err == ESP_ERR_INVALID_STATE) + { + ESP_ERROR_CHECK(esp_timer_stop(oneshot_timer)); + g_is_enrolling = 0; + g_is_deleting = 1; + gpio_set_level(GPIO_LED_WHITE, 0); + } +} +#endif + +void app_httpserver_init () +{ + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.stack_size = 4096 * 2; + +#if 0 + ESP_ERROR_CHECK(esp_timer_create(&oneshot_timer_args, &oneshot_timer)); + + gpio_config_t io_conf = {0}; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.intr_type = GPIO_PIN_INTR_POSEDGE; + io_conf.pin_bit_mask = 1LL << GPIO_BUTTON; + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + gpio_config(&io_conf); + gpio_isr_handler_add(GPIO_BUTTON, gpio_isr_handler, NULL); +#endif + + gpst_input_image = xQueueCreate(1, sizeof(void *)); + gpst_output = xQueueCreate(1, sizeof(http_img_process_result)); + + + if (httpd_start(&camera_httpd, &config) == ESP_OK) + { + httpd_register_uri_handler(camera_httpd, &cmd_uri); + httpd_register_uri_handler(camera_httpd, &status_uri); + } + config.server_port += 1; + config.ctrl_port += 1; + if (httpd_start(&camera_httpd, &config) == ESP_OK) + { + httpd_register_uri_handler(camera_httpd, &_face_stream_handler); + } +} diff --git a/examples/single_chip/recognition_wechat/main/app_main.c b/examples/single_chip/recognition_wechat/main/app_main.c new file mode 100644 index 0000000..b85e6f4 --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/app_main.c @@ -0,0 +1,174 @@ +/* ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "app_main.h" +#include "esp_partition.h" +#include "wechat_blufi.h" + +void gpio_led_init() +{ + gpio_config_t gpio_conf; + gpio_conf.mode = GPIO_MODE_OUTPUT; + gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE; + gpio_conf.intr_type = GPIO_INTR_DISABLE; + gpio_conf.pin_bit_mask = 1LL << GPIO_LED_RED; + gpio_config(&gpio_conf); + gpio_conf.pin_bit_mask = 1LL << GPIO_LED_WHITE; + gpio_config(&gpio_conf); + +} + +void led_task(void *arg) +{ + while(1) + { + switch (g_state) + { + case WAIT_FOR_WAKEUP: + gpio_set_level(GPIO_LED_RED, 1); + gpio_set_level(GPIO_LED_WHITE, 0); + break; + + case WAIT_FOR_CONNECT: + gpio_set_level(GPIO_LED_WHITE, 0); + gpio_set_level(GPIO_LED_RED, 1); + vTaskDelay(1000 / portTICK_PERIOD_MS); + gpio_set_level(GPIO_LED_RED, 0); + break; + + case START_DETECT: + case START_RECOGNITION: + gpio_set_level(GPIO_LED_WHITE, 1); + gpio_set_level(GPIO_LED_RED, 0); + break; + + case START_ENROLL: + gpio_set_level(GPIO_LED_WHITE, 1); + gpio_set_level(GPIO_LED_RED, 1); + break; + + default: + gpio_set_level(GPIO_LED_WHITE, 1); + gpio_set_level(GPIO_LED_RED, 0); + break; + } + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} + +en_fsm_state g_state = WAIT_FOR_WAKEUP; + +static void restart_count_erase_timercb(void *timer) +{ + if (!xTimerStop(timer, portMAX_DELAY)) { + ESP_LOGD("esp-eye", "xTimerStop timer: %p", timer); + } + + if (!xTimerDelete(timer, portMAX_DELAY)) { + ESP_LOGD("esp-eye", "xTimerDelete timer: %p", timer); + } + + wifi_info_erase(NVS_KEY_RESTART_COUNT); + + ESP_LOGI("esp-eye", "Erase restart count"); +} + +static int restart_count_get() +{ + esp_err_t ret = ESP_OK; + TimerHandle_t timer = NULL; + uint32_t restart_count = 0; + + /**< If the device restarts within the instruction time, + the event_mdoe value will be incremented by one */ + load_info_nvs(NVS_KEY_RESTART_COUNT, &restart_count, sizeof(uint32_t)); + restart_count++; + ret = save_info_nvs(NVS_KEY_RESTART_COUNT, &restart_count, sizeof(uint32_t)); + if (ret != ESP_OK) { + ESP_LOGE("size_info_nvs", "size_info_nvs\n"); + return ret; + } + + timer = xTimerCreate("restart_count_erase", RESTART_TIMEOUT_MS / portTICK_RATE_MS, + false, NULL, restart_count_erase_timercb); + + xTimerStart(timer, 0); + + return restart_count; +} + + +void printTask(void *arg) +{ + #define BUF_SIZE 1 * 1024 + char *tasklist = malloc(BUF_SIZE); + while (1) { + memset(tasklist, 0, BUF_SIZE); + vTaskGetRunTimeStats(tasklist); + printf("Running tasks CPU usage: \n %s\r\n", (char *)tasklist); + printf("RAM size: %dKB, with PSRAM: %dKB\n", heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) / 1024, heap_caps_get_free_size(MALLOC_CAP_8BIT)); + vTaskDelay(5000 / portTICK_RATE_MS); + } + + free(tasklist); +} + +void app_facenet_main(); +void app_main() +{ + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + // NVS partition was truncated and needs to be erased + // Compatible V3.1. NVS format has been changed between v3.1 and v3.2 to support longer blob values. + // Retry nvs_flash_init + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + + if (restart_count_get() > 3) { + ESP_LOGI("esp-eye", "Erase information saved in flash"); + wifi_info_erase(USERDATANAMESPACE); + } + + gpio_led_init(); + app_camera_init(); + + xTaskCreatePinnedToCore(&led_task, "blink_task", configMINIMAL_STACK_SIZE, NULL, 5, NULL, 0); + + g_state = WAIT_FOR_CONNECT; + + ESP_LOGI("esp-eye", "Version "VERSION); + + // app_wifi_init(); + + // wifi init and blufi + blufi_main(); + + // wait for network connected + wait_net_connected(); + + app_facenet_main(); + app_httpserver_init(); + printf("Mem availabe after: %d\n", heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL)); + ESP_LOGI("esp-eye", "Version "VERSION" success"); + //xTaskCreatePinnedToCore(&printTask, "printTask", 2*1024, NULL, 5, NULL, 1); +} diff --git a/examples/single_chip/recognition_wechat/main/app_wifi.c b/examples/single_chip/recognition_wechat/main/app_wifi.c new file mode 100644 index 0000000..9e56a68 --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/app_wifi.c @@ -0,0 +1,182 @@ +/* ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "sdkconfig.h" + +#include "lwip/err.h" +#include "lwip/sys.h" +#include "app_wifi.h" + +static const char *TAG = "app_wifi"; + +#define EXAMPLE_ESP_WIFI_MODE_AP 1 //TRUE:AP FALSE:STA +#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID +#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD +#define EXAMPLE_MAX_STA_CONN CONFIG_MAX_STA_CONN +#define EXAMPLE_IP_ADDR CONFIG_SERVER_IP + +static esp_err_t event_handler(void *ctx, system_event_t *event) +{/*{{{*/ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_GOT_IP: + ESP_LOGI(TAG, "got ip:%s", + ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); + break; + case SYSTEM_EVENT_AP_STACONNECTED: + ESP_LOGI(TAG, "station:" MACSTR " join, AID=%d", + MAC2STR(event->event_info.sta_connected.mac), + event->event_info.sta_connected.aid); + break; + case SYSTEM_EVENT_AP_STADISCONNECTED: + ESP_LOGI(TAG, "station:" MACSTR " leave, AID=%d", + MAC2STR(event->event_info.sta_disconnected.mac), + event->event_info.sta_disconnected.aid); + + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + esp_wifi_connect(); + break; + default: + break; + } + return ESP_OK; +}/*}}}*/ + +#if EXAMPLE_ESP_WIFI_MODE_AP +static void wifi_init_softap() +{ + tcpip_adapter_init(); + + if (strcmp(EXAMPLE_IP_ADDR, "192.168.4.1")) + { + int a, b, c, d; + sscanf(EXAMPLE_IP_ADDR, "%d.%d.%d.%d", &a, &b, &c, &d); + tcpip_adapter_ip_info_t ip_info; + IP4_ADDR(&ip_info.ip, a, b, c, d); + IP4_ADDR(&ip_info.gw, a, b, c, d); + IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0); + ESP_ERROR_CHECK(tcpip_adapter_dhcps_stop(WIFI_IF_AP)); + ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(WIFI_IF_AP, &ip_info)); + ESP_ERROR_CHECK(tcpip_adapter_dhcps_start(WIFI_IF_AP)); + } + + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + uint8_t mac[6]; + ESP_ERROR_CHECK(esp_wifi_get_mac(ESP_IF_WIFI_AP, mac)); + + wifi_config_t wifi_config; + memset(&wifi_config, 0, sizeof(wifi_config_t)); + if (strlen(EXAMPLE_ESP_WIFI_SSID) == 0) + { + snprintf((char *)wifi_config.ap.ssid, 32, "esp-eye-%x%x", mac[4], mac[5]); + } + else + { + memcpy(wifi_config.ap.ssid, EXAMPLE_ESP_WIFI_SSID, sizeof(EXAMPLE_ESP_WIFI_SSID)); + } + memcpy(wifi_config.ap.password, EXAMPLE_ESP_WIFI_PASS, sizeof(EXAMPLE_ESP_WIFI_PASS)); + wifi_config.ap.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID); + wifi_config.ap.max_connection = EXAMPLE_MAX_STA_CONN; + wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK; + if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) { + wifi_config.ap.authmode = WIFI_AUTH_OPEN; + } + + esp_wifi_set_ps(WIFI_PS_NONE); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); + + ESP_ERROR_CHECK(esp_wifi_start()); + + ESP_LOGI(TAG, "wifi_init_softap finished.SSID:%s password:%s", + wifi_config.ap.ssid, EXAMPLE_ESP_WIFI_PASS); + + char buf[80]; + sprintf(buf, "SSID:%s", wifi_config.ap.ssid); + sprintf(buf, "PASSWORD:%s", wifi_config.ap.password); + +} + +#else + +static void wifi_init_sta() +{ + tcpip_adapter_init(); + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + wifi_config_t wifi_config = {0}; + memset(&wifi_config, 0, sizeof(wifi_config_t)); + memcpy(wifi_config.sta.ssid, EXAMPLE_ESP_WIFI_SSID, sizeof(EXAMPLE_ESP_WIFI_SSID)); + memcpy(wifi_config.sta.password, EXAMPLE_ESP_WIFI_PASS, sizeof(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, "connect to ap SSID:%s password:%s", wifi_config.sta.ssid, wifi_config.sta.password); + + char buf[80]; + sprintf(buf, "SSID:%s", wifi_config.sta.ssid); + sprintf(buf, "PASSWORD:%s", wifi_config.sta.password); + +} +#endif + +void app_wifi_init () +{ + //Initialize NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + +#if EXAMPLE_ESP_WIFI_MODE_AP + ESP_LOGI(TAG, "ESP_WIFI_MODE_AP"); + wifi_init_softap(); +#else + ESP_LOGI(TAG, "ESP_WIFI_MODE_STA"); + wifi_init_sta(); +#endif /*EXAMPLE_ESP_WIFI_MODE_AP*/ + +} + diff --git a/examples/single_chip/recognition_wechat/main/component.mk b/examples/single_chip/recognition_wechat/main/component.mk new file mode 100644 index 0000000..b31d3af --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/component.mk @@ -0,0 +1,9 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the SDK documents if you need to do this. +# + diff --git a/examples/single_chip/recognition_wechat/main/include/app_camera.h b/examples/single_chip/recognition_wechat/main/include/app_camera.h new file mode 100644 index 0000000..22075f5 --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/include/app_camera.h @@ -0,0 +1,77 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2017 + * + * Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef _APP_CAMERA_H_ +#define _APP_CAMERA_H_ + +#include "esp_log.h" +#include "esp_system.h" +#include "esp_camera.h" + +/** + * PIXFORMAT_RGB565, // 2BPP/RGB565 + * PIXFORMAT_YUV422, // 2BPP/YUV422 + * PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE + * PIXFORMAT_JPEG, // JPEG/COMPRESSED + * PIXFORMAT_RGB888, // 3BPP/RGB888 + */ +#define CAMERA_PIXEL_FORMAT PIXFORMAT_JPEG + +/* + * FRAMESIZE_QQVGA, // 160x120 + * FRAMESIZE_QQVGA2, // 128x160 + * FRAMESIZE_QCIF, // 176x144 + * FRAMESIZE_HQVGA, // 240x176 + * FRAMESIZE_QVGA, // 320x240 + * FRAMESIZE_CIF, // 400x296 + * FRAMESIZE_VGA, // 640x480 + * FRAMESIZE_SVGA, // 800x600 + * FRAMESIZE_XGA, // 1024x768 + * FRAMESIZE_SXGA, // 1280x1024 + * FRAMESIZE_UXGA, // 1600x1200 + */ +#define CAMERA_FRAME_SIZE FRAMESIZE_QVGA + +#define PWDN_GPIO_NUM -1 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 4 +#define SIOD_GPIO_NUM 18 +#define SIOC_GPIO_NUM 23 + +#define Y9_GPIO_NUM 36 +#define Y8_GPIO_NUM 37 +#define Y7_GPIO_NUM 38 +#define Y6_GPIO_NUM 39 +#define Y5_GPIO_NUM 35 +#define Y4_GPIO_NUM 14 +#define Y3_GPIO_NUM 13 +#define Y2_GPIO_NUM 34 +#define VSYNC_GPIO_NUM 5 +#define HREF_GPIO_NUM 27 +#define PCLK_GPIO_NUM 25 + +#define XCLK_FREQ 20000000 + +void app_camera_init(); + +#endif diff --git a/examples/single_chip/recognition_wechat/main/include/app_httpserver.h b/examples/single_chip/recognition_wechat/main/include/app_httpserver.h new file mode 100644 index 0000000..222515c --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/include/app_httpserver.h @@ -0,0 +1,55 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2017 + * + * Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef _APP_FACENET_H_ +#define _APP_FACENET_H_ + +#if __cplusplus +extern "C" { +#endif + +#include "esp_http_server.h" +#include "app_camera.h" +#include "fr_forward.h" +#include "fr_flash.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +#define ENROLL_CONFIRM_TIMES 3 +#define FACE_ID_SAVE_NUMBER 10 +#define ENROLL_NAME_LEN 16 + + typedef struct + { + void *image; + box_array_t *net_boxes; + void *face_id; + SemaphoreHandle_t lock; + } http_img_process_result; + +void app_httpserver_init (); + +#if __cplusplus +} +#endif +#endif diff --git a/examples/single_chip/recognition_wechat/main/include/app_main.h b/examples/single_chip/recognition_wechat/main/include/app_main.h new file mode 100644 index 0000000..6d5ecfa --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/include/app_main.h @@ -0,0 +1,21 @@ +#include "app_camera.h" +#include "app_httpserver.h" +#include "app_wifi.h" + +#define VERSION "0.9.2" + +#define GPIO_LED_RED 21 +#define GPIO_LED_WHITE 22 +#define GPIO_BUTTON 15 + +typedef enum +{ + WAIT_FOR_WAKEUP, + WAIT_FOR_CONNECT, + START_STREAM, + START_DETECT, + START_RECOGNITION, + START_ENROLL, +} en_fsm_state; + +extern en_fsm_state g_state; diff --git a/examples/single_chip/recognition_wechat/main/include/app_wifi.h b/examples/single_chip/recognition_wechat/main/include/app_wifi.h new file mode 100644 index 0000000..e0cf53d --- /dev/null +++ b/examples/single_chip/recognition_wechat/main/include/app_wifi.h @@ -0,0 +1,6 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + +extern EventGroupHandle_t g_wifi_event_group; + +void app_wifi_init(); diff --git a/examples/single_chip/recognition_wechat/partitions.csv b/examples/single_chip/recognition_wechat/partitions.csv new file mode 100644 index 0000000..8c732ea --- /dev/null +++ b/examples/single_chip/recognition_wechat/partitions.csv @@ -0,0 +1,6 @@ +# Espressif ESP32 Partition Table +# Name, Type, SubType, Offset, Size +factory, app, factory, 0x010000, 3M +nvs, data, nvs, 0x310000, 16K +fr, 32, 32, 0x320000, 128K + diff --git a/examples/single_chip/recognition_wechat/sdkconfig.defaults b/examples/single_chip/recognition_wechat/sdkconfig.defaults new file mode 100644 index 0000000..ac20d40 --- /dev/null +++ b/examples/single_chip/recognition_wechat/sdkconfig.defaults @@ -0,0 +1,97 @@ + +CONFIG_ESPTOOLPY_PORT="/dev/ttyUSB0" +CONFIG_ESPTOOLPY_BAUD_115200B= +CONFIG_ESPTOOLPY_BAUD_230400B= +CONFIG_ESPTOOLPY_BAUD_921600B=y +CONFIG_ESPTOOLPY_BAUD_2MB= +CONFIG_ESPTOOLPY_BAUD_OTHER= +CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 +CONFIG_ESPTOOLPY_BAUD=921600 +CONFIG_ESPTOOLPY_COMPRESSED=y +CONFIG_FLASHMODE_QIO=y +CONFIG_FLASHMODE_QOUT= +CONFIG_FLASHMODE_DIO= +CONFIG_FLASHMODE_DOUT= +CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_40M= +CONFIG_ESPTOOLPY_FLASHFREQ_26M= +CONFIG_ESPTOOLPY_FLASHFREQ_20M= +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_ESPTOOLPY_FLASHSIZE_1MB= +CONFIG_ESPTOOLPY_FLASHSIZE_2MB= +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB= +CONFIG_ESPTOOLPY_FLASHSIZE_16MB= +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y +CONFIG_ESPTOOLPY_BEFORE_RESET=y +CONFIG_ESPTOOLPY_BEFORE_NORESET= +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +CONFIG_ESPTOOLPY_AFTER_NORESET= +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_MONITOR_BAUD_9600B= +CONFIG_MONITOR_BAUD_57600B= +CONFIG_MONITOR_BAUD_115200B=y +CONFIG_MONITOR_BAUD_230400B= +CONFIG_MONITOR_BAUD_921600B= +CONFIG_MONITOR_BAUD_2MB= +CONFIG_MONITOR_BAUD_OTHER= +CONFIG_MONITOR_BAUD_OTHER_VAL=115200 +CONFIG_MONITOR_BAUD=115200 + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_SINGLE_APP= +CONFIG_PARTITION_TABLE_TWO_OTA= +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# +# Camera configuration +# +CONFIG_ENABLE_TEST_PATTERN= +CONFIG_OV2640_SUPPORT=y +CONFIG_OV7725_SUPPORT= + +# +# ESP32-specific +# +CONFIG_ESP32_DEFAULT_CPU_FREQ_80= +CONFIG_ESP32_DEFAULT_CPU_FREQ_160= +CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 +CONFIG_SPIRAM_SUPPORT=y + +# +# SPI RAM config +# +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_IGNORE_NOTFOUND= +CONFIG_SPIRAM_USE_MEMMAP= +CONFIG_SPIRAM_USE_CAPS_ALLOC= +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_TYPE_AUTO=y +CONFIG_SPIRAM_TYPE_ESPPSRAM32= +CONFIG_SPIRAM_TYPE_ESPPSRAM64= +CONFIG_SPIRAM_SIZE=-1 +CONFIG_SPIRAM_SPEED_40M= +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_CACHE_WORKAROUND=y +CONFIG_SPIRAM_BANKSWITCH_ENABLE=y +CONFIG_SPIRAM_BANKSWITCH_RESERVE=8 +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=3096 +CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST= +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY= +CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY= +CONFIG_SPIRAM_OCCUPY_HSPI_HOST= +CONFIG_SPIRAM_OCCUPY_VSPI_HOST=y + +CONFIG_TASK_WDT= +CONFIG_BT_ENABLED=y