diff --git a/examples/single_chip/camera_web_server/main/CMakeLists.txt b/examples/single_chip/camera_web_server/main/CMakeLists.txt index 86bc4e9..35ef042 100644 --- a/examples/single_chip/camera_web_server/main/CMakeLists.txt +++ b/examples/single_chip/camera_web_server/main/CMakeLists.txt @@ -1,4 +1,4 @@ -set(COMPONENT_SRCS "app_main.c" "app_wifi.c" "app_camera.c" "app_httpd.c") +set(COMPONENT_SRCS "app_main.c" "app_wifi.c" "app_camera.c" "app_httpd.c" "app_mdns.c") set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_REQUIRES diff --git a/examples/single_chip/camera_web_server/main/app_httpd.c b/examples/single_chip/camera_web_server/main/app_httpd.c index 3099791..d4d53f4 100644 --- a/examples/single_chip/camera_web_server/main/app_httpd.c +++ b/examples/single_chip/camera_web_server/main/app_httpd.c @@ -20,6 +20,7 @@ #include "driver/ledc.h" //#include "camera_index.h" #include "sdkconfig.h" +#include "app_mdns.h" #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) #include "esp32-hal-log.h" @@ -705,8 +706,12 @@ static esp_err_t cmd_handler(httpd_req_t *req) int res = 0; if (!strcmp(variable, "framesize")) { - if (s->pixformat == PIXFORMAT_JPEG) + if (s->pixformat == PIXFORMAT_JPEG) { res = s->set_framesize(s, (framesize_t)val); + if (res == 0) { + app_mdns_update_framesize(val); + } + } } else if (!strcmp(variable, "quality")) res = s->set_quality(s, val); @@ -878,6 +883,15 @@ static esp_err_t status_handler(httpd_req_t *req) return httpd_resp_send(req, json_response, strlen(json_response)); } +static esp_err_t mdns_handler(httpd_req_t *req) +{ + size_t json_len = 0; + const char * json_response = app_mdns_query(&json_len); + httpd_resp_set_type(req, "application/json"); + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); + return httpd_resp_send(req, json_response, json_len); +} + static esp_err_t xclk_handler(httpd_req_t *req) { char *buf = NULL; @@ -1137,6 +1151,12 @@ void app_httpd_main() .handler = win_handler, .user_ctx = NULL}; + httpd_uri_t mdns_uri = { + .uri = "/mdns", + .method = HTTP_GET, + .handler = mdns_handler, + .user_ctx = NULL}; + ra_filter_init(&ra_filter, 20); #if CONFIG_ESP_FACE_DETECT_ENABLED @@ -1179,6 +1199,8 @@ void app_httpd_main() httpd_register_uri_handler(camera_httpd, &greg_uri); httpd_register_uri_handler(camera_httpd, &pll_uri); httpd_register_uri_handler(camera_httpd, &win_uri); + + httpd_register_uri_handler(camera_httpd, &mdns_uri); } config.server_port += 1; diff --git a/examples/single_chip/camera_web_server/main/app_main.c b/examples/single_chip/camera_web_server/main/app_main.c index b0e9619..ed67ee4 100755 --- a/examples/single_chip/camera_web_server/main/app_main.c +++ b/examples/single_chip/camera_web_server/main/app_main.c @@ -23,10 +23,12 @@ #include "app_camera.h" #include "app_wifi.h" #include "app_httpd.h" +#include "app_mdns.h" void app_main() { app_wifi_main(); app_camera_main(); app_httpd_main(); + app_mdns_main(); } diff --git a/examples/single_chip/camera_web_server/main/app_mdns.c b/examples/single_chip/camera_web_server/main/app_mdns.c new file mode 100644 index 0000000..681c17a --- /dev/null +++ b/examples/single_chip/camera_web_server/main/app_mdns.c @@ -0,0 +1,242 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2020 + * + * 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. + * + */ +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_camera.h" +#include "mdns.h" +#include "app_camera.h" + +static const char *TAG = "camera mdns"; + +static const char * service_name = "_esp-cam"; +static const char * proto = "_tcp"; +static mdns_result_t * found_cams = NULL; +static SemaphoreHandle_t query_lock = NULL; +static char iname[64]; +static char hname[64]; +static char framesize[4]; +static char pixformat[4]; +static const char * model = NULL; + +static void mdns_query_for_cams() +{ + mdns_result_t * new_cams = NULL; + esp_err_t err = mdns_query_ptr(service_name, proto, 5000, 4, &new_cams); + if(err){ + ESP_LOGE(TAG, "MDNS Query Failed: %s", esp_err_to_name(err)); + return; + } + xSemaphoreTake(query_lock, portMAX_DELAY); + if (found_cams != NULL) { + mdns_query_results_free(found_cams); + } + found_cams = new_cams; + xSemaphoreGive(query_lock); +} + +static void mdns_task(void * arg) +{ + for (;;) { + mdns_query_for_cams(); + //delay 55 seconds + vTaskDelay((55 * 1000) / portTICK_PERIOD_MS); + } + vTaskDelete(NULL); +} + +/* +* Public Functions +*/ + +const char * app_mdns_query(size_t * out_len) +{ + //build JSON + static char json_response[2048]; + char *p = json_response; + *p++ = '['; + + //add own data first + tcpip_adapter_ip_info_t ip; + if (strlen(CONFIG_ESP_WIFI_SSID)) { + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip); + } else { + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip); + } + *p++ = '{'; + p += sprintf(p, "\"instance\":\"%s\",", iname); + p += sprintf(p, "\"host\":\"%s.local\",", hname); + p += sprintf(p, "\"port\":80,"); + p += sprintf(p, "\"txt\":{"); + p += sprintf(p, "\"pixformat\":\"%s\",", pixformat); + p += sprintf(p, "\"framesize\":\"%s\",", framesize); + p += sprintf(p, "\"stream_port\":\"81\","); + p += sprintf(p, "\"board\":\"%s\",", CAM_BOARD); + p += sprintf(p, "\"model\":\"%s\",", model); + *p++ = '}'; + *p++ = ','; + p += sprintf(p, "\"ip\":\"" IPSTR "\",", IP2STR(&(ip.ip))); + p += sprintf(p, "\"id\":\"" IPSTR ":80\",", IP2STR(&(ip.ip))); + p += sprintf(p, "\"service\":\"%s\",", service_name); + p += sprintf(p, "\"proto\":\"%s\"", proto); + *p++ = '}'; + + xSemaphoreTake(query_lock, portMAX_DELAY); + if (found_cams) { + *p++ = ','; + } + mdns_result_t * r = found_cams; + mdns_ip_addr_t * a = NULL; + int t; + while(r){ + *p++ = '{'; + if(r->instance_name){ + p += sprintf(p, "\"instance\":\"%s\",", r->instance_name); + } + if(r->hostname){ + p += sprintf(p, "\"host\":\"%s.local\",", r->hostname); + p += sprintf(p, "\"port\":%u,", r->port); + } + if(r->txt_count){ + p += sprintf(p, "\"txt\":{"); + for(t=0; ttxt_count; t++){ + if (t > 0) { + *p++ = ','; + } + p += sprintf(p, "\"%s\":\"%s\"", r->txt[t].key, r->txt[t].value?r->txt[t].value:"NULL"); + } + *p++ = '}'; + *p++ = ','; + } + a = r->addr; + while(a){ + if(a->addr.type != IPADDR_TYPE_V6){ + p += sprintf(p, "\"ip\":\"" IPSTR "\",", IP2STR(&(a->addr.u_addr.ip4))); + p += sprintf(p, "\"id\":\"" IPSTR ":%u\",", IP2STR(&(a->addr.u_addr.ip4)), r->port); + break; + } + a = a->next; + } + p += sprintf(p, "\"service\":\"%s\",", service_name); + p += sprintf(p, "\"proto\":\"%s\"", proto); + *p++ = '}'; + r = r->next; + if (r) { + *p++ = ','; + } + } + xSemaphoreGive(query_lock); + *p++ = ']'; + *out_len = (uint32_t)p - (uint32_t)json_response; + *p++ = '\0'; + ESP_LOGI(TAG, "JSON: %uB", *out_len); + return (const char *)json_response; +} + +void app_mdns_update_framesize(int size) +{ + snprintf(framesize, 4, "%d", size); + if(mdns_service_txt_item_set(service_name, proto, "framesize", (char*)framesize)){ + ESP_LOGE(TAG, "mdns_service_txt_item_set() framesize Failed"); + } +} + +void app_mdns_main() +{ + uint8_t mac[6]; + + query_lock = xSemaphoreCreateBinary(); + if (query_lock == NULL) { + ESP_LOGE(TAG, "xSemaphoreCreateMutex() Failed"); + return; + } + xSemaphoreGive(query_lock); + + if (esp_read_mac(mac, ESP_MAC_WIFI_STA) != ESP_OK) { + ESP_LOGE(TAG, "esp_read_mac() Failed"); + return; + } + + sensor_t * s = esp_camera_sensor_get(); + switch(s->id.PID){ + case OV2640_PID: model = "OV2640"; break; + case OV3660_PID: model = "OV3660"; break; + case OV5640_PID: model = "OV5640"; break; + case OV7725_PID: model = "OV7725"; break; + default: model = "UNKNOWN"; break; + } + snprintf(iname, 64, "%s-%s-%02X%02X%02X", CAM_BOARD, model, mac[3], mac[4], mac[5]); + snprintf(framesize, 4, "%d", s->status.framesize); + snprintf(pixformat, 4, "%d", s->pixformat); + + char * src = iname, * dst = hname, c; + while (*src) { + c = *src++; + if (c >= 'A' && c <= 'Z') { + c -= 'A' - 'a'; + } + *dst++ = c; + } + *dst++ = '\0'; + + if(mdns_init() != ESP_OK){ + ESP_LOGE(TAG, "mdns_init() Failed"); + return; + } + + if(mdns_hostname_set(hname) != ESP_OK){ + ESP_LOGE(TAG, "mdns_hostname_set(\"%s\") Failed", hname); + return; + } + + if(mdns_instance_name_set(iname) != ESP_OK){ + ESP_LOGE(TAG, "mdns_instance_name_set(\"%s\") Failed", iname); + return; + } + + if(mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0) != ESP_OK){ + ESP_LOGE(TAG, "mdns_service_add() HTTP Failed"); + return; + } + + + mdns_txt_item_t camera_txt_data[] = { + {(char*)"board" ,(char*)CAM_BOARD}, + {(char*)"model" ,(char*)model}, + {(char*)"stream_port" ,(char*)"81"}, + {(char*)"framesize" ,(char*)framesize}, + {(char*)"pixformat" ,(char*)pixformat} + }; + + if(mdns_service_add(NULL, service_name, proto, 80, camera_txt_data, 5)) { + ESP_LOGE(TAG, "mdns_service_add() ESP-CAM Failed"); + return; + } + + xTaskCreate(mdns_task, "mdns-cam", 2048, NULL, 2, NULL); +} \ No newline at end of file diff --git a/examples/single_chip/camera_web_server/main/app_wifi.c b/examples/single_chip/camera_web_server/main/app_wifi.c index e56e2a8..d7d719a 100644 --- a/examples/single_chip/camera_web_server/main/app_wifi.c +++ b/examples/single_chip/camera_web_server/main/app_wifi.c @@ -33,6 +33,8 @@ #include "lwip/err.h" #include "lwip/sys.h" +#include "mdns.h" + /* The examples use WiFi configuration that you can set via 'make menuconfig'. If you'd rather not, just change the below entries to strings with @@ -83,6 +85,7 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) default: break; } + mdns_handle_system_event(ctx, event); return ESP_OK; } @@ -169,4 +172,5 @@ void app_wifi_main() wifi_init_sta(); } ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); } diff --git a/examples/single_chip/camera_web_server/main/include/app_camera.h b/examples/single_chip/camera_web_server/main/include/app_camera.h index be229b4..333fd3e 100755 --- a/examples/single_chip/camera_web_server/main/include/app_camera.h +++ b/examples/single_chip/camera_web_server/main/include/app_camera.h @@ -25,6 +25,7 @@ #define _APP_CAMERA_H_ #if CONFIG_CAMERA_MODEL_WROVER_KIT +#define CAM_BOARD "WROVER-KIT" #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 21 @@ -44,6 +45,7 @@ #define PCLK_GPIO_NUM 22 #elif CONFIG_CAMERA_MODEL_ESP32_CAM_BOARD +#define CAM_BOARD "ESP-DEVCAM" #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM 33 #define XCLK_GPIO_NUM 4 @@ -63,6 +65,7 @@ #define PCLK_GPIO_NUM 25 #elif CONFIG_CAMERA_MODEL_ESP_EYE +#define CAM_BOARD "ESP-EYE" #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 4 @@ -82,6 +85,7 @@ #define PCLK_GPIO_NUM 25 #elif CONFIG_CAMERA_MODEL_M5STACK_PSRAM +#define CAM_BOARD "M5CAM" #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 @@ -101,6 +105,7 @@ #define PCLK_GPIO_NUM 21 #elif CONFIG_CAMERA_MODEL_M5STACK_WIDE +#define CAM_BOARD "M5CAMW" #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 @@ -120,6 +125,7 @@ #define PCLK_GPIO_NUM 21 #elif CONFIG_CAMERA_MODEL_AI_THINKER +#define CAM_BOARD "AI-THINKER" #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 0 @@ -140,23 +146,24 @@ #elif CONFIG_CAMERA_MODEL_CUSTOM -#define PWDN_GPIO_NUM CONFIG_CAMERA_PIN_PWDN -#define RESET_GPIO_NUM CONFIG_CAMERA_PIN_RESET -#define XCLK_GPIO_NUM CONFIG_CAMERA_PIN_XCLK -#define SIOD_GPIO_NUM CONFIG_CAMERA_PIN_SIOD -#define SIOC_GPIO_NUM CONFIG_CAMERA_PIN_SIOC +#define CAM_BOARD "CUSTOM" +#define PWDN_GPIO_NUM CONFIG_CAMERA_PIN_PWDN +#define RESET_GPIO_NUM CONFIG_CAMERA_PIN_RESET +#define XCLK_GPIO_NUM CONFIG_CAMERA_PIN_XCLK +#define SIOD_GPIO_NUM CONFIG_CAMERA_PIN_SIOD +#define SIOC_GPIO_NUM CONFIG_CAMERA_PIN_SIOC -#define Y9_GPIO_NUM CONFIG_CAMERA_PIN_Y9 -#define Y8_GPIO_NUM CONFIG_CAMERA_PIN_Y8 -#define Y7_GPIO_NUM CONFIG_CAMERA_PIN_Y7 -#define Y6_GPIO_NUM CONFIG_CAMERA_PIN_Y6 -#define Y5_GPIO_NUM CONFIG_CAMERA_PIN_Y5 -#define Y4_GPIO_NUM CONFIG_CAMERA_PIN_Y4 -#define Y3_GPIO_NUM CONFIG_CAMERA_PIN_Y3 -#define Y2_GPIO_NUM CONFIG_CAMERA_PIN_Y2 -#define VSYNC_GPIO_NUM CONFIG_CAMERA_PIN_VSYNC -#define HREF_GPIO_NUM CONFIG_CAMERA_PIN_HREF -#define PCLK_GPIO_NUM CONFIG_CAMERA_PIN_PCLK +#define Y9_GPIO_NUM CONFIG_CAMERA_PIN_Y9 +#define Y8_GPIO_NUM CONFIG_CAMERA_PIN_Y8 +#define Y7_GPIO_NUM CONFIG_CAMERA_PIN_Y7 +#define Y6_GPIO_NUM CONFIG_CAMERA_PIN_Y6 +#define Y5_GPIO_NUM CONFIG_CAMERA_PIN_Y5 +#define Y4_GPIO_NUM CONFIG_CAMERA_PIN_Y4 +#define Y3_GPIO_NUM CONFIG_CAMERA_PIN_Y3 +#define Y2_GPIO_NUM CONFIG_CAMERA_PIN_Y2 +#define VSYNC_GPIO_NUM CONFIG_CAMERA_PIN_VSYNC +#define HREF_GPIO_NUM CONFIG_CAMERA_PIN_HREF +#define PCLK_GPIO_NUM CONFIG_CAMERA_PIN_PCLK #endif #ifdef __cplusplus diff --git a/examples/single_chip/camera_web_server/main/include/app_mdns.h b/examples/single_chip/camera_web_server/main/include/app_mdns.h new file mode 100644 index 0000000..90769d1 --- /dev/null +++ b/examples/single_chip/camera_web_server/main/include/app_mdns.h @@ -0,0 +1,31 @@ +// Copyright 2015-2020 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 _CAMERA_MDNS_H_ +#define _CAMERA_MDNS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void app_mdns_main(); +void app_mdns_update_framesize(int size); +const char * app_mdns_query(size_t * out_len); + +#ifdef __cplusplus +} +#endif + +#endif /* _CAMERA_MDNS_H_ */ diff --git a/examples/single_chip/camera_web_server/main/www/index_ov5640.html b/examples/single_chip/camera_web_server/main/www/index_ov5640.html index 2d1cf8f..a6c01f9 100644 --- a/examples/single_chip/camera_web_server/main/www/index_ov5640.html +++ b/examples/single_chip/camera_web_server/main/www/index_ov5640.html @@ -135,7 +135,7 @@ padding: 0 5px } - button { + button, .button { display: block; margin: 5px; padding: 0 12px; @@ -335,6 +335,17 @@ display: none } + .save { + position: absolute; + right: 25px; + top: 0px; + height: 16px; + line-height: 16px; + padding: 0 4px; + text-decoration: none; + cursor: pointer + } + input[type=text] { border: 1px solid #363636; font-size: 14px; @@ -391,7 +402,7 @@