Skip to content

Instantly share code, notes, and snippets.

@vitoho
Last active July 30, 2019 08:42
Show Gist options
  • Save vitoho/7526d18e3c8697aa4f3c575706828542 to your computer and use it in GitHub Desktop.
Save vitoho/7526d18e3c8697aa4f3c575706828542 to your computer and use it in GitHub Desktop.
Simple Captive Portal -P1
// 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
}
<!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