Last active
July 30, 2019 08:42
-
-
Save vitoho/7526d18e3c8697aa4f3c575706828542 to your computer and use it in GitHub Desktop.
Simple Captive Portal -P1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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. | |
#include "esp_wifi.h" | |
#include "esp_wifi_types.h" | |
#include "tcpip_adapter.h" | |
#include "app_httpd.h" | |
#include "esp_http_server.h" | |
#include "esp_timer.h" | |
#include "esp_camera.h" | |
#include "img_converters.h" | |
#include "fb_gfx.h" | |
//#include "camera_index.h" | |
#include "sdkconfig.h" | |
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG) | |
#include "esp32-hal-log.h" | |
#define TAG "" | |
#else | |
#include "esp_log.h" | |
static const char* TAG = "camera_httpd"; | |
#endif | |
#if CONFIG_ESP_FACE_DETECT_ENABLED | |
#include "fd_forward.h" | |
#if CONFIG_ESP_FACE_RECOGNITION_ENABLED | |
#include "fr_forward.h" | |
#define ENROLL_CONFIRM_TIMES 5 | |
#define FACE_ID_SAVE_NUMBER 7 | |
#endif | |
#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) | |
#endif | |
typedef struct { | |
size_t size; //number of values used for filtering | |
size_t index; //current value index | |
size_t count; //value count | |
int sum; | |
int * values; //array to be filled with values | |
} ra_filter_t; | |
typedef struct { | |
httpd_req_t *req; | |
size_t len; | |
} jpg_chunking_t; | |
#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"; | |
static ra_filter_t ra_filter; | |
httpd_handle_t stream_httpd = NULL; | |
httpd_handle_t camera_httpd = NULL; | |
#if CONFIG_ESP_FACE_DETECT_ENABLED | |
static mtmn_config_t mtmn_config = {0}; | |
static int8_t detection_enabled = 0; | |
#if CONFIG_ESP_FACE_RECOGNITION_ENABLED | |
static int8_t recognition_enabled = 0; | |
static int8_t is_enrolling = 0; | |
static face_id_list id_list = {0}; | |
#endif | |
#endif | |
static ra_filter_t ra_filter; | |
static ra_filter_t * ra_filter_init(ra_filter_t * filter, size_t sample_size){ | |
memset(filter, 0, sizeof(ra_filter_t)); | |
filter->values = (int *)malloc(sample_size * sizeof(int)); | |
if(!filter->values){ | |
free(filter); | |
return NULL; | |
} | |
memset(filter->values, 0, sample_size * sizeof(int)); | |
filter->size = sample_size; | |
return filter; | |
} | |
#if 0 | |
static int ra_filter_run(ra_filter_t * filter, int value){ | |
if(!filter->values){ | |
return value; | |
} | |
filter->sum -= filter->values[filter->index]; | |
filter->values[filter->index] = value; | |
filter->sum += filter->values[filter->index]; | |
filter->index++; | |
filter->index = filter->index % filter->size; | |
if (filter->count < filter->size) { | |
filter->count++; | |
} | |
return filter->sum / filter->count; | |
} | |
#endif | |
#if CONFIG_ESP_FACE_DETECT_ENABLED | |
#if CONFIG_ESP_FACE_RECOGNITION_ENABLED | |
static void rgb_print(dl_matrix3du_t *image_matrix, uint32_t color, const char * str){ | |
fb_data_t fb; | |
fb.width = image_matrix->w; | |
fb.height = image_matrix->h; | |
fb.data = image_matrix->item; | |
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(dl_matrix3du_t *image_matrix, 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_matrix, color, temp); | |
if(len > 64){ | |
free(temp); | |
} | |
return len; | |
} | |
#endif | |
static void draw_face_boxes(dl_matrix3du_t *image_matrix, box_array_t *boxes, int face_id){ | |
int x, y, w, h, i; | |
uint32_t color = FACE_COLOR_YELLOW; | |
if(face_id < 0){ | |
color = FACE_COLOR_RED; | |
} else if(face_id > 0){ | |
color = FACE_COLOR_GREEN; | |
} | |
fb_data_t fb; | |
fb.width = image_matrix->w; | |
fb.height = image_matrix->h; | |
fb.data = image_matrix->item; | |
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 | |
} | |
} | |
#if CONFIG_ESP_FACE_RECOGNITION_ENABLED | |
static int run_face_recognition(dl_matrix3du_t *image_matrix, box_array_t *net_boxes){ | |
dl_matrix3du_t *aligned_face = NULL; | |
int matched_id = 0; | |
aligned_face = dl_matrix3du_alloc(1, FACE_WIDTH, FACE_HEIGHT, 3); | |
if(!aligned_face){ | |
ESP_LOGE(TAG, "Could not allocate face recognition buffer"); | |
return matched_id; | |
} | |
if (align_face(net_boxes, image_matrix, aligned_face) == ESP_OK){ | |
if (is_enrolling == 1){ | |
int8_t left_sample_face = enroll_face(&id_list, aligned_face); | |
if(left_sample_face == (ENROLL_CONFIRM_TIMES - 1)){ | |
ESP_LOGD(TAG, "Enrolling Face ID: %d", id_list.tail); | |
} | |
ESP_LOGD(TAG, "Enrolling Face ID: %d sample %d", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face); | |
rgb_printf(image_matrix, FACE_COLOR_CYAN, "ID[%u] Sample[%u]", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face); | |
if (left_sample_face == 0){ | |
is_enrolling = 0; | |
ESP_LOGD(TAG, "Enrolled Face ID: %d", id_list.tail); | |
} | |
} else { | |
matched_id = recognize_face(&id_list, aligned_face); | |
if (matched_id >= 0) { | |
ESP_LOGW(TAG, "Match Face ID: %u", matched_id); | |
rgb_printf(image_matrix, FACE_COLOR_GREEN, "Hello Subject %u", matched_id); | |
} else { | |
ESP_LOGW(TAG, "No Match Found"); | |
rgb_print(image_matrix, FACE_COLOR_RED, "Intruder Alert!"); | |
matched_id = -1; | |
} | |
} | |
} else { | |
ESP_LOGW(TAG, "Face Not Aligned"); | |
//rgb_print(image_matrix, FACE_COLOR_YELLOW, "Human Detected"); | |
} | |
dl_matrix3du_free(aligned_face); | |
return matched_id; | |
} | |
#endif | |
#endif | |
//------------------------------------------------ | |
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){ | |
jpg_chunking_t *j = (jpg_chunking_t *)arg; | |
if(!index){ | |
j->len = 0; | |
} | |
if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){ | |
return 0; | |
} | |
j->len += len; | |
return len; | |
} | |
static esp_err_t capture_handler(httpd_req_t *req){ | |
camera_fb_t * fb = NULL; | |
esp_err_t res = ESP_OK; | |
int64_t fr_start = esp_timer_get_time(); | |
fb = esp_camera_fb_get(); | |
if (!fb) { | |
ESP_LOGE(TAG, "Camera capture failed"); | |
httpd_resp_send_500(req); | |
return ESP_FAIL; | |
} | |
httpd_resp_set_type(req, "image/jpeg"); | |
httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg"); | |
#if CONFIG_ESP_FACE_DETECT_ENABLED | |
size_t out_len, out_width, out_height; | |
uint8_t * out_buf; | |
bool s; | |
bool detected = false; | |
int face_id = 0; | |
if(!detection_enabled || fb->width > 400){ | |
#endif | |
size_t fb_len = 0; | |
if(fb->format == PIXFORMAT_JPEG){ | |
fb_len = fb->len; | |
res = httpd_resp_send(req, (const char *)fb->buf, fb->len); | |
} else { | |
jpg_chunking_t jchunk = {req, 0}; | |
res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL; | |
httpd_resp_send_chunk(req, NULL, 0); | |
fb_len = jchunk.len; | |
} | |
esp_camera_fb_return(fb); | |
int64_t fr_end = esp_timer_get_time(); | |
ESP_LOGI(TAG, "JPG: %uB %ums", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000)); | |
return res; | |
#if CONFIG_ESP_FACE_DETECT_ENABLED | |
} | |
dl_matrix3du_t *image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3); | |
if (!image_matrix) { | |
esp_camera_fb_return(fb); | |
ESP_LOGE(TAG, "dl_matrix3du_alloc failed"); | |
httpd_resp_send_500(req); | |
return ESP_FAIL; | |
} | |
out_buf = image_matrix->item; | |
out_len = fb->width * fb->height * 3; | |
out_width = fb->width; | |
out_height = fb->height; | |
s = fmt2rgb888(fb->buf, fb->len, fb->format, out_buf); | |
esp_camera_fb_return(fb); | |
if(!s){ | |
dl_matrix3du_free(image_matrix); | |
ESP_LOGE(TAG, "to rgb888 failed"); | |
httpd_resp_send_500(req); | |
return ESP_FAIL; | |
} | |
box_array_t *net_boxes = face_detect(image_matrix, &mtmn_config); | |
if (net_boxes){ | |
detected = true; | |
#if CONFIG_ESP_FACE_RECOGNITION_ENABLED | |
if(recognition_enabled){ | |
face_id = run_face_recognition(image_matrix, net_boxes); | |
} | |
#endif | |
draw_face_boxes(image_matrix, net_boxes, face_id); | |
free(net_boxes->score); | |
free(net_boxes->box); | |
free(net_boxes->landmark); | |
free(net_boxes); | |
} | |
jpg_chunking_t jchunk = {req, 0}; | |
s = fmt2jpg_cb(out_buf, out_len, out_width, out_height, PIXFORMAT_RGB888, 90, jpg_encode_stream, &jchunk); | |
dl_matrix3du_free(image_matrix); | |
if(!s){ | |
ESP_LOGE(TAG, "JPEG compression failed"); | |
return ESP_FAIL; | |
} | |
int64_t fr_end = esp_timer_get_time(); | |
ESP_LOGI(TAG, "FACE: %uB %ums %s%d", (uint32_t)(jchunk.len), (uint32_t)((fr_end - fr_start)/1000), detected?"DETECTED ":"", face_id); | |
return res; | |
#endif | |
} | |
static esp_err_t status_handler(httpd_req_t *req){ | |
static char json_response[1024]; | |
char* buf; | |
size_t buf_len; | |
char operation[16] = {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, "do", operation, sizeof(operation)) == 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; | |
} | |
//--------------------------------------- | |
if(!strcmp(operation, "fetchap")){ | |
//sensor_t * s = esp_camera_sensor_get(); | |
wifi_scan_config_t scan_conf; | |
uint16_t ap_num = 0; | |
uint16_t maxAp = 10; | |
wifi_ap_record_t apList[10]; | |
memset(&scan_conf, 0, sizeof(scan_conf)); | |
//ESP_ERROR_CHECK(esp_wifi_disconnect()); | |
if(esp_wifi_scan_start(&scan_conf,true)==ESP_OK){ | |
esp_wifi_scan_get_ap_num(&ap_num); | |
if(ap_num>maxAp) ap_num=maxAp; | |
esp_wifi_scan_get_ap_records(&maxAp, apList); | |
} | |
else{ | |
esp_wifi_disconnect(); | |
if(esp_wifi_scan_start(&scan_conf,true)==ESP_OK){ | |
esp_wifi_scan_get_ap_num(&ap_num); | |
if(ap_num>maxAp) ap_num=maxAp; | |
esp_wifi_scan_get_ap_records(&maxAp, apList); | |
} | |
} | |
char * p = json_response; | |
*p++ = '{'; | |
//p+=sprintf(p, "\"framesize\":%u,", s->status.framesize); | |
p+=sprintf(p, "\"ap_num\":%u,", ap_num); | |
for(uint8_t i = 0; i<ap_num; i++){ | |
wifi_ap_record_t *ap = apList + i; | |
p+=sprintf(p, "\"ssid.ap.%d\":\"%s\",\"bssid.ap.%d\":\"%s\",\"rssi.ap.%d\":%d,\"chan.ap.%d\":%d," | |
, i, ap->ssid, i, ap->ssid, i, ap->rssi, i, ap->primary); | |
} | |
p+=sprintf(p,"\"end\":true"); | |
*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)); | |
} | |
else { | |
httpd_resp_send_404(req); | |
return ESP_FAIL; | |
} | |
} | |
static esp_err_t cmd_handler(httpd_req_t *req){ | |
char* buf; | |
size_t buf_len; | |
char operation[16] = {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, "do", operation, sizeof(operation)) == ESP_OK) { | |
ESP_LOGI(TAG, "Got query:%s",buf); | |
} 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; | |
} | |
if(!strcmp(operation, "connect")) { | |
//char ssid[33]; | |
//char key[65]; | |
wifi_config_t wifi_config; | |
/* Get header value string length and allocate memory for length + 1, | |
* extra byte for null termination */ | |
buf_len = httpd_req_get_hdr_value_len(req, "ssid") + 1; | |
buf = malloc(buf_len); | |
/* Copy null terminated value string into buffer */ | |
if (httpd_req_get_hdr_value_str(req, "ssid", buf, buf_len) == ESP_OK) { | |
if(buf!=NULL){ | |
ESP_LOGI(TAG, "Found ssid:%s, len:%d", buf,buf_len); | |
strcpy((char *)wifi_config.sta.ssid, buf); | |
buf_len = httpd_req_get_hdr_value_len(req, "key") + 1; | |
if(buf_len>1){ | |
httpd_req_get_hdr_value_str(req, "key", buf, buf_len); | |
} else buf=""; | |
strcpy((char *)wifi_config.sta.password, buf); | |
}else{ | |
ESP_LOGE(TAG, "Wrong WiFi AP info!"); | |
httpd_resp_sendstr_chunk(req, "{\"ERR\":\"Not valid SSID\"}"); | |
httpd_resp_sendstr_chunk(req, NULL); | |
return ESP_FAIL; | |
} | |
} | |
free(buf); | |
ESP_LOGI(TAG, "connect to ssid:%s, pwd:%s", wifi_config.sta.ssid, wifi_config.sta.password); | |
ESP_ERROR_CHECK(esp_wifi_disconnect()); | |
char res[512]; | |
char* p=res; | |
switch(esp_wifi_set_config(WIFI_IF_STA,&wifi_config)){ | |
case ESP_ERR_WIFI_PASSWORD: | |
*p++ = '{'; | |
p+=sprintf(p, "\"error\":\"Password Wrong\""); | |
*p++ = '}'; | |
*p++ = 0; | |
httpd_resp_set_type(req, "application/json"); | |
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); | |
ESP_LOGI(TAG, "Connect to AP:%s,PWD:%s",wifi_config.sta.ssid,wifi_config.sta.password); | |
return httpd_resp_send(req, res, strlen(res)); | |
break; | |
case ESP_OK: | |
esp_wifi_connect(); | |
ESP_LOGI(TAG, "Connected:%s",wifi_config.sta.ssid); | |
httpd_resp_sendstr_chunk(req, "OK"); | |
httpd_resp_sendstr_chunk(req, NULL); | |
return ESP_OK; | |
break; | |
} | |
} | |
return ESP_OK; | |
} | |
//---------------------------------------------------------------------------------------------- | |
static esp_err_t redirect_handler(httpd_req_t *req){ | |
extern const unsigned char redirect_html_gz_start[] asm("_binary_redirect_html_gz_start"); | |
extern const unsigned char redirect_html_gz_end[] asm("_binary_redirect_html_gz_end"); | |
size_t redirect_html_gz_len = redirect_html_gz_end - redirect_html_gz_start; | |
httpd_resp_set_type(req, "text/html"); | |
httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); | |
//httpd_resp_set_status(req, HTTPD_204); | |
return httpd_resp_send(req, (const char *)redirect_html_gz_start, redirect_html_gz_len); | |
} | |
static esp_err_t index_handler(httpd_req_t *req){ | |
extern const unsigned char index_html_gz_start[] asm("_binary_index_html_gz_start"); | |
extern const unsigned char index_html_gz_end[] asm("_binary_index_html_gz_end"); | |
size_t index_html_gz_len = index_html_gz_end - index_html_gz_start; | |
httpd_resp_set_type(req, "text/html"); | |
httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); | |
//httpd_resp_set_status(req, HTTPD_204); | |
return httpd_resp_send(req, (const char *)index_html_gz_start, index_html_gz_len); | |
} | |
static esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err){ | |
//httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Some 404 error message"); | |
return ESP_OK; | |
} | |
void app_httpd_main(){ | |
ESP_LOGI(TAG, "Starting HTTP Server"); | |
httpd_config_t config = HTTPD_DEFAULT_CONFIG(); | |
httpd_uri_t index_uri = { | |
.uri = "/", | |
.method = HTTP_GET, | |
.handler = index_handler, | |
.user_ctx = NULL | |
}; | |
httpd_uri_t capture_uri = { | |
.uri = "/stream", | |
.method = HTTP_GET, | |
.handler = capture_handler, | |
.user_ctx = NULL | |
}; | |
httpd_uri_t gen204_uri = { //Android Gen204 | |
.uri = "/generate_204", | |
.method = HTTP_GET, | |
.handler = redirect_handler, | |
.user_ctx = NULL | |
}; | |
httpd_uri_t ios_uri = { //ios hotspot detact | |
.uri = "/hotspot-detect.html", | |
.method = HTTP_GET, | |
.handler = redirect_handler, | |
.user_ctx = NULL | |
}; | |
httpd_uri_t status_uri = { | |
.uri = "/status", | |
.method = HTTP_GET, | |
.handler = status_handler, | |
.user_ctx = NULL | |
}; | |
httpd_uri_t cmd_uri = { | |
.uri = "/cmd", | |
.method = HTTP_GET, | |
.handler = cmd_handler, | |
.user_ctx = NULL | |
}; | |
ra_filter_init(&ra_filter, 20); | |
ESP_LOGI(TAG, "Starting web server on port: '%d'", config.server_port); | |
config.max_resp_headers = 1204; | |
if (httpd_start(&camera_httpd, &config) == ESP_OK) { | |
httpd_register_uri_handler(camera_httpd, &index_uri); | |
httpd_register_uri_handler(camera_httpd, &gen204_uri); | |
httpd_register_uri_handler(camera_httpd, &capture_uri); | |
httpd_register_uri_handler(camera_httpd, &ios_uri); | |
httpd_register_uri_handler(camera_httpd, &status_uri); | |
httpd_register_uri_handler(camera_httpd, &cmd_uri); | |
//err handler | |
httpd_register_err_handler(camera_httpd,HTTPD_404_NOT_FOUND,http_404_error_handler); | |
} | |
#if 0 | |
config.server_port += 1; | |
config.ctrl_port += 1; | |
ESP_LOGI(TAG, "Starting stream server on port: '%d'", config.server_port); | |
if (httpd_start(&stream_httpd, &config) == ESP_OK) { | |
httpd_register_uri_handler(stream_httpd, &stream_uri); | |
} | |
#endif | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="Pragma" content="no-cache"> | |
<meta http-equiv="Cache-Control" content="no-cache"> | |
<meta http-equiv="Expires" content="0"> | |
<meta name="viewport" content="width=device-width,initial-scale=1"> | |
<title>Homepage</title> | |
<style> | |
body { | |
font: 1.0em Lato, "Helvetica Neue", Arial, Helvetica, sans-serif; | |
} | |
#header { | |
box-sizing: border-box; | |
color: rgba(0, 0, 0, 0.87); | |
height: auto; | |
font-size: 2em; | |
text-align: center; | |
width: 100%; | |
border-bottom: 1px solid rgba(34, 36, 38, 0.15); | |
padding: 0px 0px 3px; | |
} | |
.message { | |
bottom: 0px; | |
box-sizing: border-box; | |
height: auto; | |
left: 0px; | |
min-height: 14px; | |
position: relative; | |
right: 0px; | |
width: 100%; | |
border-radius: 4px 4px 4px 4px; | |
margin: 14px 0px; | |
padding: 14px 21px; | |
display: none; | |
} | |
#info { | |
color: rgb(33, 133, 208); | |
box-shadow: rgba(34, 36, 38, 0.22) 0px 0px 0px 1px inset, rgba(0, 0, 0, 0) 0px 0px 0px 0px; | |
background: rgb(223, 240, 255) none repeat scroll 0% 0% / auto padding-box border-box; | |
border: 1px none rgb(33, 133, 208); | |
} | |
#error { | |
color: rgb(212, 22, 22); | |
box-shadow: rgba(34, 36, 38, 0.22) 0px 0px 0px 1px inset, rgba(0, 0, 0, 0) 0px 0px 0px 0px; | |
background: rgb(255, 223, 231) none repeat scroll 0% 0% / auto padding-box border-box; | |
border: 1px none rgb(212, 22, 22); | |
} | |
#userlabel, | |
#netlabel, | |
#passlabel { | |
box-sizing: border-box; | |
color: rgba(0, 0, 0, 0.87); | |
display: block; | |
margin-top: 10px; | |
margin-bottom: 5px; | |
} | |
#networks { | |
bottom: 0px; | |
box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px 0px inset; | |
color: rgba(0, 0, 0, 0.87); | |
cursor: pointer; | |
display: block; | |
height: 4em; | |
left: 0px; | |
min-height: 15px; | |
position: relative; | |
right: 0px; | |
text-align: left; | |
top: 0px; | |
width: 100%; | |
border: 1px solid rgba(34, 36, 38, 0.15); | |
border-radius: 5px 5px 5px 5px; | |
font-size: 1.0em; | |
} | |
#peapuserwrap, | |
#passwrap { | |
margin-bottom: 0.7em; | |
display: none; | |
padding-right:18px; | |
} | |
#puser, | |
#password { | |
box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px 0px inset; | |
cursor: auto; | |
vertical-align: top; | |
width: 100%; | |
border: 1px solid rgba(34, 36, 38, 0.15); | |
border-radius: 4px 4px 4px 4px; | |
padding: 8px 8px; | |
font-size: 1em; | |
margin-top: 10px; | |
height: 4em; | |
} | |
.btn { | |
box-sizing: border-box; | |
display: inline-block; | |
text-align: center; | |
width: 100%; | |
cursor: pointer; | |
font: normal normal 700 normal 14px / 14px Lato, "Helvetica Neue", Arial, Helvetica, sans-serif; | |
border-radius: 4px 4px 4px 4px; | |
height: 4em; | |
} | |
#rescan, #check, #camera { | |
box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px 1px inset, rgba(34, 36, 38, 0.15) 0px 0px 0px 0px inset; | |
color: rgba(0, 0, 0, 0.6); | |
background: rgb(224, 225, 226) none repeat scroll 0% 0% / auto padding-box border-box; | |
border: 0px none rgba(0, 0, 0, 0.6); | |
margin-top: 0.8em; | |
} | |
.btn:disabled { | |
opacity: 0.5; | |
cursor: not-allowed; | |
} | |
#save { | |
box-shadow: rgba(34, 36, 38, 0.15) 0px 0px 0px 0px inset; | |
color: rgb(255, 255, 255); | |
height: 5em; | |
background: rgb(33, 133, 208) none repeat scroll 0% 0% / auto padding-box border-box; | |
border: 0px none rgb(255, 255, 255); | |
margin-top: 0.8em; | |
} | |
pre { | |
outline: 1px solid #ccc; | |
padding: 5px; | |
margin: 5px; | |
} | |
.string { | |
color: green; | |
} | |
.number { | |
color: darkorange; | |
} | |
.boolean { | |
color: blue; | |
} | |
.null { | |
color: magenta; | |
} | |
.key { | |
color: red; | |
} | |
</style> | |
</head> | |
<body> | |
<div> | |
<div id="form"> | |
<div id="header"> | |
Configure WiFi | |
</div> | |
<div id="error" class="message"></div> | |
<div id="info" class="message"></div> | |
<div id="networkwrap"> | |
<label id="netlabel"> | |
Network | |
</label> | |
<select name="dropdown" id="networks" disabled="disabled" required="required"> | |
<option value="-1" disabled="disabled" selected="selected"> | |
Waiting for networks... | |
</option> | |
</select> | |
</div> | |
<div id="peapuserwrap"> | |
<label id="userlabel"> | |
SSID | |
</label> | |
<input name="user" type="text" placeholder="SSID" id="puser" autocomplete="off" /> | |
</div> | |
<div id="passwrap"> | |
<label id="passlabel"> | |
Password | |
</label> | |
<input name="password" type="text" placeholder="password" id="password" autocomplete="off" /> | |
</div> | |
<button id="save" class="btn" onclick="connect()"> | |
✔ |Save & Test | |
</button> | |
<button id="rescan" class="btn" onclick="rescan()">🔍 |Rescan</button> | |
<button id="check" class="btn">Check</button> | |
<button id="camera" class="btn" onclick="f_cam()">📷 |Camera</button> | |
</div> | |
<div style="margin-top: 20px; display: block;" align="center"> | |
<img id="cam_img" style="max-width: 300px; display: none;"></img> | |
</div> | |
<div style="margin-top: 20px; display: block;"> | |
<pre id="response"></pre> | |
</div> | |
</div> | |
<script type="text/javascript"> | |
var WiFiPortal ={ | |
_msg_proto: function (elID) { | |
return { | |
$el: document.getElementById(elID), | |
show: function (msg) { | |
this.$el.innerHTML = msg; | |
this.$el.style.display = 'block'; | |
}, | |
hide: function () { | |
this.$el.style.display = 'none'; | |
} | |
}; | |
}, | |
disable_all:function(){ | |
if(document.getElementById("cam_img").style.display != "none") f_cam(); | |
var selector = document.getElementById("networks"); | |
selector.disabled = true; | |
var pwfield = document.getElementById("password"); | |
pwfield.disabled = true; | |
}, | |
fetch_complete:function(){ | |
//document.getElementById | |
var passField = document.getElementById("passwrap"); | |
passField.style.display = "block"; | |
var pwfield = document.getElementById("password"); | |
pwfield.disabled = false; | |
}, | |
fetch:function(){ | |
WiFiPortal.disable_all(); | |
var userField = document.getElementById("peapuserwrap"); | |
userField.style.display = "none"; | |
WiFiPortal.Info.show('Scanning for WiFi AP around...'); | |
var baseHost = document.location.origin; | |
var selector = document.getElementById("networks"); | |
fetch(`${baseHost}/status?do=fetchap`) | |
.then(function (response) { | |
return response.json() | |
}) | |
.then(function (state) { | |
var ap_num = state["ap_num"]; | |
//console.log("ap number:"+state["ap_num"]); | |
//console.log(state); | |
if (ap_num>0){ | |
selector.options.length=0; | |
for(var i=0;i<ap_num;i++){ | |
var opt = document.createElement("option"); | |
var key_ssid = "ssid.ap."+i; | |
//var key_bssid = "bssid.ap."+i; | |
var key_chan = "chan.ap."+i; | |
var key_rssi = "rssi.ap."+i; | |
opt.value = state[key_ssid]; | |
if (i<9) var no = '0'+(i+1); else var no=i+1; | |
opt.innerText = no+' | '+state[key_ssid]+'('+state[key_ssid]+') | chl:'+state[key_chan]+' | rssi:'+state[key_rssi]; | |
selector.appendChild(opt); | |
WiFiPortal.fetch_complete(); | |
} | |
selector.disabled = false; | |
selector.value="ssid.ap.0" | |
//---custormize ssid | |
var optlast = document.createElement("option"); | |
optlast.value = '-2'; | |
optlast.innerText = 'Or type your SSID...'; | |
selector.appendChild(optlast); | |
//--- | |
WiFiPortal.Info.show(''+ap_num+' AP(s) have been found.'); | |
WiFiPortal.fetch_complete(); | |
} | |
else{ | |
WiFiPortal.Error.show('Sorry, can not find any APs arround.'); | |
} | |
}) | |
}, | |
init:function(){ | |
WiFiPortal.Info = new WiFiPortal._msg_proto( "info" ); | |
WiFiPortal.Error = new WiFiPortal._msg_proto( "error" ); | |
document.getElementById('networks').addEventListener('change',function(){ | |
var userField = document.getElementById("peapuserwrap"); | |
if(this.value === '-2') { | |
userField.style.display = "block"; | |
}else{ | |
userField.style.display = "none"; | |
} | |
},true); | |
//---------- | |
WiFiPortal.fetch(); | |
} | |
} | |
function rescan(){ | |
//WiFiPortal.Info.show('Scanning for WiFi AP around...'); | |
// read initial values | |
WiFiPortal.fetch(); | |
} | |
function reflash(){ | |
//console.log('123'); | |
var img_src = document.getElementById('cam_img'); | |
img_src.src = "./stream?"+Math.random(); | |
} | |
function f_cam(){ | |
//console.log('123'); | |
var s = document.getElementById("cam_img"); | |
if(s.style.display == "none"){ | |
s.style.display="block"; | |
reflashVar = setInterval(reflash,500); | |
} | |
else{ | |
s.style.display = "none"; | |
clearInterval(reflashVar); | |
delete reflashVar; | |
} | |
} | |
function state_fb(){ | |
WiFiPortal.Info.show("😊|WiFi Connected.") | |
} | |
function connect(){ | |
var network = document.getElementById('networks').value | |
const password = document.getElementById('password').value | |
if(network==='-2'){ | |
if(document.getElementById('puser').value!==null) | |
network = document.getElementById('puser').value; | |
else network = null; | |
} | |
WiFiPortal.disable_all(); | |
var baseHost = document.location.origin; | |
//const query = `${baseHost}/cmd?do=connect&ssid=${network}&key=${password}` | |
const query = `${baseHost}/cmd?do=connect` | |
fetch(query,{ | |
headers: { | |
'key' : password, | |
'ssid': network | |
} | |
}) | |
.then(function (response) { | |
return response.json() | |
console.log(`request to ${query} finished, status: ${response.status}`) | |
}) | |
.then(function (state) { | |
if (state["ERR"]!==''){ | |
WiFiPortal.Error.show(state["ERR"]); | |
} | |
}) | |
WiFiPortal.Info.show('Connecting to ['+network+']...'); | |
state_fb(); | |
} | |
// Init once the entire DOM is loaded | |
document.addEventListener('DOMContentLoaded', WiFiPortal.init ); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment