mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-09 13:07:03 +03:00
Merge remote-tracking branch 'origin/httpd' into master-cmake
Conflicts: .cproject .gitmodules .project .pydevproject .settings/language.settings.xml .settings/org.eclipse.cdt.core.prefs components/cmd_i2c/CMakeLists.txt components/cmd_i2c/cmd_i2ctools.c components/cmd_i2c/component.mk components/cmd_nvs/cmd_nvs.c components/cmd_nvs/component.mk components/cmd_system/cmd_system.c components/cmd_system/component.mk components/config/config.c components/config/config.h components/config/nvs_utilities.c components/display/CMakeLists.txt components/driver_bt/CMakeLists.txt components/driver_bt/component.mk components/raop/raop.c components/services/CMakeLists.txt components/squeezelite-ota/cmd_ota.c components/squeezelite-ota/squeezelite-ota.c components/squeezelite-ota/squeezelite-ota.h components/squeezelite/component.mk components/telnet/CMakeLists.txt components/wifi-manager/CMakeLists.txt components/wifi-manager/dns_server.c components/wifi-manager/http_server.c components/wifi-manager/http_server.h components/wifi-manager/wifi_manager.c components/wifi-manager/wifi_manager.h main/CMakeLists.txt main/console.c main/esp_app_main.c main/platform_esp32.h
This commit is contained in:
93
components/wifi-manager/_esp_http_server.h
Normal file
93
components/wifi-manager/_esp_http_server.h
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef __ESP_HTTP_SERVER_H_
|
||||
#define __ESP_HTTP_SERVER_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <http_parser.h>
|
||||
#include <sdkconfig.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Starts the web server
|
||||
*
|
||||
* Create an instance of HTTP server and allocate memory/resources for it
|
||||
* depending upon the specified configuration.
|
||||
*
|
||||
* Example usage:
|
||||
* @code{c}
|
||||
*
|
||||
* //Function for starting the webserver
|
||||
* httpd_handle_t start_webserver(void)
|
||||
* {
|
||||
* // Generate default configuration
|
||||
* httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
*
|
||||
* // Empty handle to http_server
|
||||
* httpd_handle_t server = NULL;
|
||||
*
|
||||
* // Start the httpd server
|
||||
* if (httpd_start(&server, &config) == ESP_OK) {
|
||||
* // Register URI handlers
|
||||
* httpd_register_uri_handler(server, &uri_get);
|
||||
* httpd_register_uri_handler(server, &uri_post);
|
||||
* }
|
||||
* // If server failed to start, handle will be NULL
|
||||
* return server;
|
||||
* }
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] config Configuration for new instance of the server
|
||||
* @param[out] handle Handle to newly created instance of the server. NULL on error
|
||||
* @return
|
||||
* - ESP_OK : Instance created successfully
|
||||
* - ESP_ERR_INVALID_ARG : Null argument(s)
|
||||
* - ESP_ERR_HTTPD_ALLOC_MEM : Failed to allocate memory for instance
|
||||
* - ESP_ERR_HTTPD_TASK : Failed to launch server task
|
||||
*/
|
||||
esp_err_t __httpd_start(httpd_handle_t *handle, const httpd_config_t *config);
|
||||
static inline int __httpd_os_thread_create_static(TaskHandle_t *thread,
|
||||
const char *name, uint16_t stacksize, int prio,
|
||||
void (*thread_routine)(void *arg), void *arg,
|
||||
BaseType_t core_id)
|
||||
{
|
||||
|
||||
|
||||
StaticTask_t *xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), (MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT));
|
||||
StackType_t *xStack = heap_caps_malloc(stacksize,(MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT));
|
||||
|
||||
|
||||
//
|
||||
*thread = xTaskCreateStaticPinnedToCore(thread_routine, name, stacksize, arg, prio, xStack,xTaskBuffer,core_id);
|
||||
if (*thread) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ! _ESP_HTTP_SERVER_H_ */
|
||||
373
components/wifi-manager/_esp_httpd_main.c
Normal file
373
components/wifi-manager/_esp_httpd_main.c
Normal file
@@ -0,0 +1,373 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/param.h>
|
||||
#include <errno.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include <_esp_http_server.h>
|
||||
#include "esp_httpd_priv.h"
|
||||
#include "ctrl_sock.h"
|
||||
|
||||
static const char *TAG = "_httpd";
|
||||
|
||||
|
||||
struct httpd_ctrl_data {
|
||||
enum httpd_ctrl_msg {
|
||||
HTTPD_CTRL_SHUTDOWN,
|
||||
HTTPD_CTRL_WORK,
|
||||
} hc_msg;
|
||||
httpd_work_fn_t hc_work;
|
||||
void *hc_work_arg;
|
||||
};
|
||||
|
||||
|
||||
static esp_err_t _httpd_server_init(struct httpd_data *hd)
|
||||
{
|
||||
int fd = socket(PF_INET6, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
ESP_LOGE(TAG, LOG_FMT("error in socket (%d)"), errno);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
struct in6_addr inaddr_any = IN6ADDR_ANY_INIT;
|
||||
struct sockaddr_in6 serv_addr = {
|
||||
.sin6_family = PF_INET6,
|
||||
.sin6_addr = inaddr_any,
|
||||
.sin6_port = htons(hd->config.server_port)
|
||||
};
|
||||
|
||||
/* Enable SO_REUSEADDR to allow binding to the same
|
||||
* address and port when restarting the server */
|
||||
int enable = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
|
||||
/* This will fail if CONFIG_LWIP_SO_REUSE is not enabled. But
|
||||
* it does not affect the normal working of the HTTP Server */
|
||||
ESP_LOGW(TAG, LOG_FMT("error enabling SO_REUSEADDR (%d)"), errno);
|
||||
}
|
||||
|
||||
int ret = bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, LOG_FMT("error in bind (%d)"), errno);
|
||||
close(fd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = listen(fd, hd->config.backlog_conn);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, LOG_FMT("error in listen (%d)"), errno);
|
||||
close(fd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int ctrl_fd = cs_create_ctrl_sock(hd->config.ctrl_port);
|
||||
if (ctrl_fd < 0) {
|
||||
ESP_LOGE(TAG, LOG_FMT("error in creating ctrl socket (%d)"), errno);
|
||||
close(fd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int msg_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (msg_fd < 0) {
|
||||
ESP_LOGE(TAG, LOG_FMT("error in creating msg socket (%d)"), errno);
|
||||
close(fd);
|
||||
close(ctrl_fd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
hd->listen_fd = fd;
|
||||
hd->ctrl_fd = ctrl_fd;
|
||||
hd->msg_fd = msg_fd;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void _httpd_process_ctrl_msg(struct httpd_data *hd)
|
||||
{
|
||||
struct httpd_ctrl_data msg;
|
||||
int ret = recv(hd->ctrl_fd, &msg, sizeof(msg), 0);
|
||||
if (ret <= 0) {
|
||||
ESP_LOGW(TAG, LOG_FMT("error in recv (%d)"), errno);
|
||||
return;
|
||||
}
|
||||
if (ret != sizeof(msg)) {
|
||||
ESP_LOGW(TAG, LOG_FMT("incomplete msg"));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msg.hc_msg) {
|
||||
case HTTPD_CTRL_WORK:
|
||||
if (msg.hc_work) {
|
||||
ESP_LOGD(TAG, LOG_FMT("work"));
|
||||
(*msg.hc_work)(msg.hc_work_arg);
|
||||
}
|
||||
break;
|
||||
case HTTPD_CTRL_SHUTDOWN:
|
||||
ESP_LOGD(TAG, LOG_FMT("shutdown"));
|
||||
hd->hd_td.status = THREAD_STOPPING;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t _httpd_accept_conn(struct httpd_data *hd, int listen_fd)
|
||||
{
|
||||
/* If no space is available for new session, close the least recently used one */
|
||||
if (hd->config.lru_purge_enable == true) {
|
||||
if (!httpd_is_sess_available(hd)) {
|
||||
/* Queue asynchronous closure of the least recently used session */
|
||||
return httpd_sess_close_lru(hd);
|
||||
/* Returning from this allowes the main server thread to process
|
||||
* the queued asynchronous control message for closing LRU session.
|
||||
* Since connection request hasn't been addressed yet using accept()
|
||||
* therefore _httpd_accept_conn() will be called again, but this time
|
||||
* with space available for one session
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
struct sockaddr_in addr_from;
|
||||
socklen_t addr_from_len = sizeof(addr_from);
|
||||
int new_fd = accept(listen_fd, (struct sockaddr *)&addr_from, &addr_from_len);
|
||||
if (new_fd < 0) {
|
||||
ESP_LOGW(TAG, LOG_FMT("error in accept (%d)"), errno);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGD(TAG, LOG_FMT("newfd = %d"), new_fd);
|
||||
|
||||
struct timeval tv;
|
||||
/* Set recv timeout of this fd as per config */
|
||||
tv.tv_sec = hd->config.recv_wait_timeout;
|
||||
tv.tv_usec = 0;
|
||||
setsockopt(new_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
|
||||
|
||||
/* Set send timeout of this fd as per config */
|
||||
tv.tv_sec = hd->config.send_wait_timeout;
|
||||
tv.tv_usec = 0;
|
||||
setsockopt(new_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
|
||||
|
||||
if (ESP_OK != httpd_sess_new(hd, new_fd)) {
|
||||
ESP_LOGW(TAG, LOG_FMT("session creation failed"));
|
||||
close(new_fd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGD(TAG, LOG_FMT("complete"));
|
||||
return ESP_OK;
|
||||
}
|
||||
/* Manage in-coming connection or data requests */
|
||||
static esp_err_t _httpd_server(struct httpd_data *hd)
|
||||
{
|
||||
fd_set read_set;
|
||||
FD_ZERO(&read_set);
|
||||
if (hd->config.lru_purge_enable || httpd_is_sess_available(hd)) {
|
||||
/* Only listen for new connections if server has capacity to
|
||||
* handle more (or when LRU purge is enabled, in which case
|
||||
* older connections will be closed) */
|
||||
FD_SET(hd->listen_fd, &read_set);
|
||||
}
|
||||
FD_SET(hd->ctrl_fd, &read_set);
|
||||
|
||||
int tmp_max_fd;
|
||||
httpd_sess_set_descriptors(hd, &read_set, &tmp_max_fd);
|
||||
int maxfd = MAX(hd->listen_fd, tmp_max_fd);
|
||||
tmp_max_fd = maxfd;
|
||||
maxfd = MAX(hd->ctrl_fd, tmp_max_fd);
|
||||
|
||||
ESP_LOGD(TAG, LOG_FMT("doing select maxfd+1 = %d"), maxfd + 1);
|
||||
int active_cnt = select(maxfd + 1, &read_set, NULL, NULL, NULL);
|
||||
if (active_cnt < 0) {
|
||||
ESP_LOGE(TAG, LOG_FMT("error in select (%d)"), errno);
|
||||
httpd_sess_delete_invalid(hd);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Case0: Do we have a control message? */
|
||||
if (FD_ISSET(hd->ctrl_fd, &read_set)) {
|
||||
ESP_LOGD(TAG, LOG_FMT("processing ctrl message"));
|
||||
_httpd_process_ctrl_msg(hd);
|
||||
if (hd->hd_td.status == THREAD_STOPPING) {
|
||||
ESP_LOGD(TAG, LOG_FMT("stopping thread"));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Case1: Do we have any activity on the current data
|
||||
* sessions? */
|
||||
int fd = -1;
|
||||
while ((fd = httpd_sess_iterate(hd, fd)) != -1) {
|
||||
if (FD_ISSET(fd, &read_set) || (httpd_sess_pending(hd, fd))) {
|
||||
ESP_LOGD(TAG, LOG_FMT("processing socket %d"), fd);
|
||||
if (httpd_sess_process(hd, fd) != ESP_OK) {
|
||||
ESP_LOGD(TAG, LOG_FMT("closing socket %d"), fd);
|
||||
close(fd);
|
||||
/* Delete session and update fd to that
|
||||
* preceding the one being deleted */
|
||||
fd = httpd_sess_delete(hd, fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Case2: Do we have any incoming connection requests to
|
||||
* process? */
|
||||
if (FD_ISSET(hd->listen_fd, &read_set)) {
|
||||
ESP_LOGD(TAG, LOG_FMT("processing listen socket %d"), hd->listen_fd);
|
||||
if (_httpd_accept_conn(hd, hd->listen_fd) != ESP_OK) {
|
||||
ESP_LOGW(TAG, LOG_FMT("error accepting new connection"));
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void _httpd_close_all_sessions(struct httpd_data *hd)
|
||||
{
|
||||
int fd = -1;
|
||||
while ((fd = httpd_sess_iterate(hd, fd)) != -1) {
|
||||
ESP_LOGD(TAG, LOG_FMT("cleaning up socket %d"), fd);
|
||||
httpd_sess_delete(hd, fd);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
/* The main HTTPD thread */
|
||||
static void _httpd_thread(void *arg)
|
||||
{
|
||||
int ret;
|
||||
struct httpd_data *hd = (struct httpd_data *) arg;
|
||||
hd->hd_td.status = THREAD_RUNNING;
|
||||
|
||||
ESP_LOGD(TAG, LOG_FMT("web server started"));
|
||||
while (1) {
|
||||
ret = _httpd_server(hd);
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, LOG_FMT("web server exiting"));
|
||||
close(hd->msg_fd);
|
||||
cs_free_ctrl_sock(hd->ctrl_fd);
|
||||
_httpd_close_all_sessions(hd);
|
||||
close(hd->listen_fd);
|
||||
hd->hd_td.status = THREAD_STOPPED;
|
||||
httpd_os_thread_delete();
|
||||
}
|
||||
static struct httpd_data *__httpd_create(const httpd_config_t *config)
|
||||
{
|
||||
/* Allocate memory for httpd instance data */
|
||||
struct httpd_data *hd = calloc(1, sizeof(struct httpd_data));
|
||||
if (!hd) {
|
||||
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP server instance"));
|
||||
return NULL;
|
||||
}
|
||||
hd->hd_calls = calloc(config->max_uri_handlers, sizeof(httpd_uri_t *));
|
||||
if (!hd->hd_calls) {
|
||||
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP URI handlers"));
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
hd->hd_sd = calloc(config->max_open_sockets, sizeof(struct sock_db));
|
||||
if (!hd->hd_sd) {
|
||||
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP session data"));
|
||||
free(hd->hd_calls);
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
struct httpd_req_aux *ra = &hd->hd_req_aux;
|
||||
ra->resp_hdrs = calloc(config->max_resp_headers, sizeof(struct resp_hdr));
|
||||
if (!ra->resp_hdrs) {
|
||||
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP response headers"));
|
||||
free(hd->hd_sd);
|
||||
free(hd->hd_calls);
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
hd->err_handler_fns = calloc(HTTPD_ERR_CODE_MAX, sizeof(httpd_err_handler_func_t));
|
||||
if (!hd->err_handler_fns) {
|
||||
ESP_LOGE(TAG, LOG_FMT("Failed to allocate memory for HTTP error handlers"));
|
||||
free(ra->resp_hdrs);
|
||||
free(hd->hd_sd);
|
||||
free(hd->hd_calls);
|
||||
free(hd);
|
||||
return NULL;
|
||||
}
|
||||
/* Save the configuration for this instance */
|
||||
hd->config = *config;
|
||||
return hd;
|
||||
}
|
||||
static void _httpd_delete(struct httpd_data *hd)
|
||||
{
|
||||
struct httpd_req_aux *ra = &hd->hd_req_aux;
|
||||
/* Free memory of httpd instance data */
|
||||
free(hd->err_handler_fns);
|
||||
free(ra->resp_hdrs);
|
||||
free(hd->hd_sd);
|
||||
|
||||
/* Free registered URI handlers */
|
||||
httpd_unregister_all_uri_handlers(hd);
|
||||
free(hd->hd_calls);
|
||||
free(hd);
|
||||
}
|
||||
esp_err_t __httpd_start(httpd_handle_t *handle, const httpd_config_t *config)
|
||||
{
|
||||
if (handle == NULL || config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Sanity check about whether LWIP is configured for providing the
|
||||
* maximum number of open sockets sufficient for the server. Though,
|
||||
* this check doesn't guarantee that many sockets will actually be
|
||||
* available at runtime as other processes may use up some sockets.
|
||||
* Note that server also uses 3 sockets for its internal use :
|
||||
* 1) listening for new TCP connections
|
||||
* 2) for sending control messages over UDP
|
||||
* 3) for receiving control messages over UDP
|
||||
* So the total number of required sockets is max_open_sockets + 3
|
||||
*/
|
||||
if (CONFIG_LWIP_MAX_SOCKETS < config->max_open_sockets + 3) {
|
||||
ESP_LOGE(TAG, "Configuration option max_open_sockets is too large (max allowed %d)\n\t"
|
||||
"Either decrease this or configure LWIP_MAX_SOCKETS to a larger value",
|
||||
CONFIG_LWIP_MAX_SOCKETS - 3);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
struct httpd_data *hd = __httpd_create(config);
|
||||
if (hd == NULL) {
|
||||
/* Failed to allocate memory */
|
||||
return ESP_ERR_HTTPD_ALLOC_MEM;
|
||||
}
|
||||
|
||||
if (_httpd_server_init(hd) != ESP_OK) {
|
||||
_httpd_delete(hd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
httpd_sess_init(hd);
|
||||
if (__httpd_os_thread_create_static(&hd->hd_td.handle, "httpd",
|
||||
hd->config.stack_size,
|
||||
hd->config.task_priority,
|
||||
_httpd_thread, hd,
|
||||
hd->config.core_id) != ESP_OK) {
|
||||
/* Failed to launch task */
|
||||
_httpd_delete(hd);
|
||||
return ESP_ERR_HTTPD_TASK;
|
||||
}
|
||||
|
||||
*handle = (httpd_handle_t *)hd;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,28 @@ if (!String.prototype.format) {
|
||||
});
|
||||
};
|
||||
}
|
||||
var nvs_type_t = {
|
||||
NVS_TYPE_U8 : 0x01, /*!< Type uint8_t */
|
||||
NVS_TYPE_I8 : 0x11, /*!< Type int8_t */
|
||||
NVS_TYPE_U16 : 0x02, /*!< Type uint16_t */
|
||||
NVS_TYPE_I16 : 0x12, /*!< Type int16_t */
|
||||
NVS_TYPE_U32 : 0x04, /*!< Type uint32_t */
|
||||
NVS_TYPE_I32 : 0x14, /*!< Type int32_t */
|
||||
NVS_TYPE_U64 : 0x08, /*!< Type uint64_t */
|
||||
NVS_TYPE_I64 : 0x18, /*!< Type int64_t */
|
||||
NVS_TYPE_STR : 0x21, /*!< Type string */
|
||||
NVS_TYPE_BLOB : 0x42, /*!< Type blob */
|
||||
NVS_TYPE_ANY : 0xff /*!< Must be last */
|
||||
} ;
|
||||
|
||||
var task_state_t = {
|
||||
0 : "eRunning", /*!< A task is querying the state of itself, so must be running. */
|
||||
1 : "eReady", /*!< The task being queried is in a read or pending ready list. */
|
||||
2 : "eBlocked", /*!< The task being queried is in the Blocked state. */
|
||||
3 : "eSuspended", /*!< The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
|
||||
4 : "eDeleted"
|
||||
|
||||
}
|
||||
var releaseURL = 'https://api.github.com/repos/sle118/squeezelite-esp32/releases';
|
||||
var recovery = false;
|
||||
var enableAPTimer = true;
|
||||
@@ -19,7 +40,6 @@ var commandHeader = 'squeezelite -b 500:2000 -d all=info ';
|
||||
var pname, ver, otapct, otadsc;
|
||||
var blockAjax = false;
|
||||
var blockFlashButton = false;
|
||||
var lastMsg = '';
|
||||
|
||||
var apList = null;
|
||||
var selectedSSID = "";
|
||||
@@ -189,20 +209,29 @@ $(document).ready(function(){
|
||||
$("input#autoexec-cb").on("click", function() {
|
||||
var data = { 'timestamp': Date.now() };
|
||||
autoexec = (this.checked)?1:0;
|
||||
data['autoexec'] = autoexec;
|
||||
showMessage('please wait for the ESP32 to reboot', 'WARNING');
|
||||
data['config'] = {};
|
||||
data['config'] = {
|
||||
autoexec : {
|
||||
value : autoexec,
|
||||
type : 33
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
showMessage('please wait for the ESP32 to reboot', 'MESSAGING_WARNING');
|
||||
$.ajax({
|
||||
url: '/config.json',
|
||||
dataType: 'text',
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
headers: { "X-Custom-autoexec": autoexec },
|
||||
// headers: { "X-Custom-autoexec": autoexec },
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
data: JSON.stringify(data),
|
||||
data: JSON.stringify(data),
|
||||
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.status);
|
||||
console.log(thrownError);
|
||||
if (thrownError != '') showMessage(thrownError, 'ERROR');
|
||||
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
|
||||
},
|
||||
complete: function(response) {
|
||||
//var returnedResponse = JSON.parse(response.responseText);
|
||||
@@ -219,7 +248,7 @@ $(document).ready(function(){
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.status);
|
||||
console.log(thrownError);
|
||||
if (thrownError != '') showMessage(thrownError, 'ERROR');
|
||||
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
|
||||
},
|
||||
complete: function(response) {
|
||||
console.log('reboot call completed');
|
||||
@@ -232,20 +261,26 @@ $(document).ready(function(){
|
||||
$("input#save-autoexec1").on("click", function() {
|
||||
var data = { 'timestamp': Date.now() };
|
||||
autoexec1 = $("#autoexec1").val();
|
||||
data['autoexec1'] = autoexec1;
|
||||
data['config'] = {};
|
||||
data['config'] = {
|
||||
autoexec1 : {
|
||||
value : autoexec1,
|
||||
type : 33
|
||||
}
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '/config.json',
|
||||
dataType: 'text',
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
headers: { "X-Custom-autoexec1": autoexec1 },
|
||||
// headers: { "X-Custom-autoexec1": autoexec1 },
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
data: JSON.stringify(data),
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.status);
|
||||
console.log(thrownError);
|
||||
if (thrownError != '') showMessage(thrownError, 'ERROR');
|
||||
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
|
||||
}
|
||||
});
|
||||
console.log('sent config JSON with headers:', autoexec1);
|
||||
@@ -254,15 +289,19 @@ $(document).ready(function(){
|
||||
|
||||
$("input#save-gpio").on("click", function() {
|
||||
var data = { 'timestamp': Date.now() };
|
||||
var config = {};
|
||||
|
||||
var headers = {};
|
||||
$("input.gpio").each(function() {
|
||||
var id = $(this)[0].id;
|
||||
var pin = $(this).val();
|
||||
if (pin != '') {
|
||||
headers["X-Custom-"+id] = pin;
|
||||
data[id] = pin;
|
||||
config[id] = {};
|
||||
config[id].value = pin;
|
||||
config[id].type = nvs_type_t.NVS_TYPE_STR;
|
||||
}
|
||||
});
|
||||
data['config'] = config;
|
||||
$.ajax({
|
||||
url: '/config.json',
|
||||
dataType: 'text',
|
||||
@@ -274,7 +313,7 @@ $(document).ready(function(){
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.status);
|
||||
console.log(thrownError);
|
||||
if (thrownError != '') showMessage(thrownError, 'ERROR');
|
||||
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
|
||||
}
|
||||
});
|
||||
console.log('sent config JSON with headers:', JSON.stringify(headers));
|
||||
@@ -284,23 +323,40 @@ $(document).ready(function(){
|
||||
$("#save-nvs").on("click", function() {
|
||||
var headers = {};
|
||||
var data = { 'timestamp': Date.now() };
|
||||
var config = {};
|
||||
$("input.nvs").each(function() {
|
||||
var key = $(this)[0].id;
|
||||
var val = $(this).val();
|
||||
var nvs_type = parseInt($(this)[0].attributes.nvs_type.nodeValue,10);
|
||||
if (key != '') {
|
||||
headers["X-Custom-"+key] = val;
|
||||
data[key] = {};
|
||||
data[key].value = val;
|
||||
data[key].type = 33;
|
||||
config[key] = {};
|
||||
if(nvs_type == nvs_type_t.NVS_TYPE_U8
|
||||
|| nvs_type == nvs_type_t.NVS_TYPE_I8
|
||||
|| nvs_type == nvs_type_t.NVS_TYPE_U16
|
||||
|| nvs_type == nvs_type_t.NVS_TYPE_I16
|
||||
|| nvs_type == nvs_type_t.NVS_TYPE_U32
|
||||
|| nvs_type == nvs_type_t.NVS_TYPE_I32
|
||||
|| nvs_type == nvs_type_t.NVS_TYPE_U64
|
||||
|| nvs_type == nvs_type_t.NVS_TYPE_I64) {
|
||||
config[key].value = parseInt(val);
|
||||
}
|
||||
else {
|
||||
config[key].value = val;
|
||||
}
|
||||
|
||||
|
||||
config[key].type = nvs_type;
|
||||
}
|
||||
});
|
||||
var key = $("#nvs-new-key").val();
|
||||
var val = $("#nvs-new-value").val();
|
||||
if (key != '') {
|
||||
headers["X-Custom-"+key] = val;
|
||||
data[key] = {};
|
||||
data[key].value = val;
|
||||
// headers["X-Custom-" +key] = val;
|
||||
config[key] = {};
|
||||
config[key].value = val;
|
||||
config[key].type = 33;
|
||||
}
|
||||
data['config'] = config;
|
||||
$.ajax({
|
||||
url: '/config.json',
|
||||
dataType: 'text',
|
||||
@@ -308,35 +364,64 @@ $(document).ready(function(){
|
||||
cache: false,
|
||||
headers: headers,
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
data: JSON.stringify(data),
|
||||
data : JSON.stringify(data),
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.status);
|
||||
console.log(thrownError);
|
||||
if (thrownError != '') showMessage(thrownError, 'ERROR');
|
||||
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
|
||||
}
|
||||
});
|
||||
console.log('sent config JSON with headers:', JSON.stringify(headers));
|
||||
console.log('sent config JSON with data:', JSON.stringify(data));
|
||||
});
|
||||
|
||||
$("#fwUpload").on("click", function() {
|
||||
var upload_path = "/flash.json";
|
||||
var fileInput = document.getElementById("flashfilename").files;
|
||||
if (fileInput.length == 0) {
|
||||
alert("No file selected!");
|
||||
} else {
|
||||
var file = fileInput[0];
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (xhttp.readyState == 4) {
|
||||
if (xhttp.status == 200) {
|
||||
showMessage(xhttp.responseText, 'MESSAGING_INFO')
|
||||
} else if (xhttp.status == 0) {
|
||||
showMessage("Upload connection was closed abruptly!", 'MESSAGING_ERROR');
|
||||
} else {
|
||||
showMessage(xhttp.status + " Error!\n" + xhttp.responseText, 'MESSAGING_ERROR');
|
||||
}
|
||||
}
|
||||
};
|
||||
xhttp.open("POST", upload_path, true);
|
||||
xhttp.send(file);
|
||||
}
|
||||
enableStatusTimer = true;
|
||||
});
|
||||
$("#flash").on("click", function() {
|
||||
var data = { 'timestamp': Date.now() };
|
||||
if (blockFlashButton) return;
|
||||
blockFlashButton = true;
|
||||
var url = $("#fwurl").val();
|
||||
data['fwurl'] = url;
|
||||
data['config'] = {
|
||||
fwurl : {
|
||||
value : url,
|
||||
type : 33
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: '/config.json',
|
||||
dataType: 'text',
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
headers: { "X-Custom-fwurl": url },
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
data: JSON.stringify(data),
|
||||
data: JSON.stringify(data),
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.status);
|
||||
console.log(thrownError);
|
||||
if (thrownError != '') showMessage(thrownError, 'ERROR');
|
||||
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
|
||||
}
|
||||
});
|
||||
enableStatusTimer = true;
|
||||
@@ -509,13 +594,17 @@ function performConnect(conntype){
|
||||
dataType: 'text',
|
||||
method: 'POST',
|
||||
cache: false,
|
||||
headers: { 'X-Custom-ssid': selectedSSID, 'X-Custom-pwd': pwd, 'X-Custom-host_name': dhcpname },
|
||||
// headers: { 'X-Custom-ssid': selectedSSID, 'X-Custom-pwd': pwd, 'X-Custom-host_name': dhcpname },
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
data: { 'timestamp': Date.now()},
|
||||
data: JSON.stringify({ 'timestamp': Date.now(),
|
||||
'ssid' : selectedSSID,
|
||||
'pwd' : pwd,
|
||||
'host_name' : dhcpname
|
||||
}),
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.status);
|
||||
console.log(thrownError);
|
||||
if (thrownError != '') showMessage(thrownError, 'ERROR');
|
||||
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -564,11 +653,76 @@ function refreshAPHTML(data){
|
||||
$( "#wifi-list" ).html(h)
|
||||
}
|
||||
|
||||
function getMessages() {
|
||||
$.getJSON("/messages.json?1", function(data) {
|
||||
data.forEach(function(msg) {
|
||||
var msg_age = msg["current_time"] - msg["sent_time"];
|
||||
var msg_time = new Date();
|
||||
msg_time.setTime( msg_time.getTime() - msg_age );
|
||||
switch (msg["class"]) {
|
||||
case "MESSAGING_CLASS_OTA":
|
||||
//message: "{"ota_dsc":"Erasing flash complete","ota_pct":0}"
|
||||
var ota_data = JSON.parse(msg["message"]);
|
||||
if (ota_data.hasOwnProperty('ota_pct') && ota_data['ota_pct'] != 0){
|
||||
otapct = ota_data['ota_pct'];
|
||||
$('.progress-bar').css('width', otapct+'%').attr('aria-valuenow', otapct);
|
||||
$('.progress-bar').html(otapct+'%');
|
||||
}
|
||||
if (ota_data.hasOwnProperty('ota_dsc') && ota_data['ota_dsc'] != ''){
|
||||
otadsc = ota_data['ota_dsc'];
|
||||
$("span#flash-status").html(otadsc);
|
||||
if (otadsc.match(/Error:/) || otapct > 95) {
|
||||
blockFlashButton = false;
|
||||
enableStatusTimer = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "MESSAGING_CLASS_STATS":
|
||||
// for task states, check structure : task_state_t
|
||||
var stats_data = JSON.parse(msg["message"]);
|
||||
console.log(msg_time.toLocaleString() + " - Number of tasks on the ESP32: " + stats_data["ntasks"]);
|
||||
var stats_tasks = stats_data["tasks"];
|
||||
console.log(msg_time.toLocaleString() + '\tname' + '\tcpu' + '\tstate'+ '\tminstk'+ '\tbprio'+ '\tcprio'+ '\tnum' );
|
||||
stats_tasks.forEach(function(task) {
|
||||
console.log(msg_time.toLocaleString() + '\t' + task["nme"] + '\t'+ task["cpu"] + '\t'+ task_state_t[task["st"]]+ '\t'+ task["minstk"]+ '\t'+ task["bprio"]+ '\t'+ task["cprio"]+ '\t'+ task["num"]);
|
||||
});
|
||||
break;
|
||||
case "MESSAGING_CLASS_SYSTEM":
|
||||
showMessage(msg["message"], msg["type"],msg_age);
|
||||
|
||||
$("#syslogTable").append(
|
||||
"<tr class='"+msg["type"]+"'>"+
|
||||
"<td>"+msg_time.toLocaleString()+"</td>"+
|
||||
"<td>"+msg["message"]+"</td>"+
|
||||
"</tr>"
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
.fail(function(xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.status);
|
||||
console.log(thrownError);
|
||||
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
|
||||
});
|
||||
/*
|
||||
Minstk is minimum stack space left
|
||||
Bprio is base priority
|
||||
cprio is current priority
|
||||
nme is name
|
||||
st is task state. I provided a "typedef" that you can use to convert to text
|
||||
cpu is cpu percent used
|
||||
*/
|
||||
}
|
||||
function checkStatus(){
|
||||
RepeatCheckStatusInterval();
|
||||
if (!enableStatusTimer) return;
|
||||
if (blockAjax) return;
|
||||
blockAjax = true;
|
||||
getMessages();
|
||||
$.getJSON( "/status.json", function( data ) {
|
||||
if (data.hasOwnProperty('ssid') && data['ssid'] != ""){
|
||||
if (data["ssid"] === selectedSSID){
|
||||
@@ -659,20 +813,24 @@ function checkStatus(){
|
||||
$("#otadiv").show();
|
||||
$('a[href^="#tab-audio"]').hide();
|
||||
$('a[href^="#tab-gpio"]').show();
|
||||
$('#uploaddiv').show();
|
||||
$("footer.footer").removeClass('sl');
|
||||
$("footer.footer").addClass('recovery');
|
||||
$("#boot-button").html('Reboot');
|
||||
$("#boot-form").attr('action', '/reboot_ota.json');
|
||||
|
||||
enableStatusTimer = true;
|
||||
} else {
|
||||
recovery = false;
|
||||
$("#otadiv").hide();
|
||||
$('a[href^="#tab-audio"]').show();
|
||||
$('a[href^="#tab-gpio"]').hide();
|
||||
$('#uploaddiv').hide();
|
||||
$("footer.footer").removeClass('recovery');
|
||||
$("footer.footer").addClass('sl');
|
||||
$("#boot-button").html('Recovery');
|
||||
$("#boot-form").attr('action', '/recovery.json');
|
||||
|
||||
enableStatusTimer = false;
|
||||
}
|
||||
}
|
||||
@@ -683,29 +841,10 @@ function checkStatus(){
|
||||
ver = data['version'];
|
||||
$("span#foot-fw").html("fw: <strong>"+ver+"</strong>, mode: <strong>"+pname+"</strong>");
|
||||
}
|
||||
if (data.hasOwnProperty('ota_pct') && data['ota_pct'] != 0){
|
||||
otapct = data['ota_pct'];
|
||||
$('.progress-bar').css('width', otapct+'%').attr('aria-valuenow', otapct);
|
||||
$('.progress-bar').html(otapct+'%');
|
||||
}
|
||||
if (data.hasOwnProperty('ota_dsc') && data['ota_dsc'] != ''){
|
||||
otadsc = data['ota_dsc'];
|
||||
$("span#flash-status").html(otadsc);
|
||||
if (otadsc.match(/Error:/) || otapct > 95) {
|
||||
blockFlashButton = false;
|
||||
enableStatusTimer = true;
|
||||
}
|
||||
} else {
|
||||
else {
|
||||
$("span#flash-status").html('');
|
||||
}
|
||||
if (data.hasOwnProperty('message') && data['message'] != ''){
|
||||
var msg = data['message'].text;
|
||||
var severity = data['message'].severity;
|
||||
if (msg != lastMsg) {
|
||||
showMessage(msg, severity);
|
||||
lastMsg = msg;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.hasOwnProperty('Voltage')) {
|
||||
var voltage = data['Voltage'];
|
||||
var layer;
|
||||
@@ -735,7 +874,7 @@ function checkStatus(){
|
||||
.fail(function(xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.status);
|
||||
console.log(thrownError);
|
||||
if (thrownError != '') showMessage(thrownError, 'ERROR');
|
||||
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
|
||||
blockAjax = false;
|
||||
});
|
||||
}
|
||||
@@ -770,7 +909,7 @@ function getConfig() {
|
||||
"<tr>"+
|
||||
"<td>"+key+"</td>"+
|
||||
"<td class='value'>"+
|
||||
"<input type='text' class='form-control nvs' id='"+key+"'>"+
|
||||
"<input type='text' class='form-control nvs' id='"+key+"' nvs_type="+data[key].type+" >"+
|
||||
"</td>"+
|
||||
"</tr>"
|
||||
);
|
||||
@@ -783,7 +922,7 @@ function getConfig() {
|
||||
"<input type='text' class='form-control' id='nvs-new-key' placeholder='new key'>"+
|
||||
"</td>"+
|
||||
"<td>"+
|
||||
"<input type='text' class='form-control' id='nvs-new-value' placeholder='new value'>"+
|
||||
"<input type='text' class='form-control' id='nvs-new-value' placeholder='new value' nvs_type=33 >"+ // todo: provide a way to choose field type
|
||||
"</td>"+
|
||||
"</tr>"
|
||||
);
|
||||
@@ -791,19 +930,23 @@ function getConfig() {
|
||||
.fail(function(xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.status);
|
||||
console.log(thrownError);
|
||||
if (thrownError != '') showMessage(thrownError, 'ERROR');
|
||||
if (thrownError != '') showMessage(thrownError, 'MESSAGING_ERROR');
|
||||
blockAjax = false;
|
||||
});
|
||||
}
|
||||
|
||||
function showMessage(message, severity) {
|
||||
if (severity == 'INFO') {
|
||||
|
||||
function showMessage(message, severity, age=0) {
|
||||
if (severity == 'MESSAGING_INFO') {
|
||||
$('#message').css('background', '#6af');
|
||||
} else if (severity == 'WARNING') {
|
||||
} else if (severity == 'MESSAGING_WARNING') {
|
||||
$('#message').css('background', '#ff0');
|
||||
} else if (severity == 'MESSAGING_ERROR' ) {
|
||||
$('#message').css('background', '#f00');
|
||||
} else {
|
||||
$('#message').css('background', '#f00');
|
||||
}
|
||||
|
||||
$('#message').html(message);
|
||||
$("#content").fadeTo("slow", 0.3, function() {
|
||||
$("#message").show(500).delay(5000).hide(500, function() {
|
||||
@@ -815,3 +958,6 @@ function showMessage(message, severity) {
|
||||
function inRange(x, min, max) {
|
||||
return ((x-min)*(x-max) <= 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,14 +7,8 @@
|
||||
# please read the SDK documents if you need to do this.
|
||||
#
|
||||
COMPONENT_EMBED_FILES := style.css code.js index.html bootstrap.min.css.gz jquery.min.js.gz popper.min.js.gz bootstrap.min.js.gz
|
||||
|
||||
#CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG
|
||||
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO \
|
||||
-I$(COMPONENT_PATH)/../tools
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
COMPONENT_ADD_INCLUDEDIRS += $(COMPONENT_PATH)/../tools
|
||||
COMPONENT_ADD_INCLUDEDIRS += $(COMPONENT_PATH)/../squeezelite-ota
|
||||
COMPONENT_EXTRA_INCLUDES += $(PROJECT_PATH)/main/
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
COMPONENT_EXTRA_INCLUDES += $(IDF_PATH)/components/esp_http_server/src $(IDF_PATH)/components/esp_http_server/src/port/esp32 $(IDF_PATH)/components/esp_http_server/src/util $(IDF_PATH)/components/esp_http_server/src/
|
||||
CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_INFO
|
||||
|
||||
|
||||
|
||||
@@ -73,11 +73,7 @@ void dns_server_stop(){
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dns_server(void *pvParameters) {
|
||||
|
||||
|
||||
|
||||
struct sockaddr_in sa, ra;
|
||||
|
||||
/* Set redirection DNS hijack to the access point IP */
|
||||
|
||||
1127
components/wifi-manager/http_server_handlers.c
Normal file
1127
components/wifi-manager/http_server_handlers.c
Normal file
File diff suppressed because it is too large
Load Diff
148
components/wifi-manager/http_server_handlers.h
Normal file
148
components/wifi-manager/http_server_handlers.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright (c) 2017-2019 Tony Pottier
|
||||
|
||||
Permission is hereby granted, 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.
|
||||
|
||||
@file http_server.h
|
||||
@author Tony Pottier
|
||||
@brief Defines all functions necessary for the HTTP server to run.
|
||||
|
||||
Contains the freeRTOS task for the HTTP listener and all necessary support
|
||||
function to process requests, decode URLs, serve files, etc. etc.
|
||||
|
||||
@note http_server task cannot run without the wifi_manager task!
|
||||
@see https://idyl.io
|
||||
@see https://github.com/tonyp7/esp32-wifi-manager
|
||||
*/
|
||||
|
||||
#ifndef HTTP_SERVER_H_INCLUDED
|
||||
#define HTTP_SERVER_H_INCLUDED
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_http_server.h"
|
||||
#include "wifi_manager.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event_loop.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "mdns.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/raw.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/priv/api_msg.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
#include "esp_vfs.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ESP_LOGE_LOC(t,str, ...) ESP_LOGE(t, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#define ESP_LOGI_LOC(t,str, ...) ESP_LOGI(t, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#define ESP_LOGD_LOC(t,str, ...) ESP_LOGD(t, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#define ESP_LOGW_LOC(t,str, ...) ESP_LOGW(t, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#define ESP_LOGV_LOC(t,str, ...) ESP_LOGV(t, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
|
||||
esp_err_t root_get_handler(httpd_req_t *req);
|
||||
esp_err_t resource_filehandler(httpd_req_t *req);
|
||||
esp_err_t resource_filehandler(httpd_req_t *req);
|
||||
esp_err_t resource_filehandler(httpd_req_t *req);
|
||||
esp_err_t resource_filehandler(httpd_req_t *req);
|
||||
esp_err_t resource_filehandler(httpd_req_t *req);
|
||||
esp_err_t resource_filehandler(httpd_req_t *req);
|
||||
esp_err_t ap_get_handler(httpd_req_t *req);
|
||||
esp_err_t config_get_handler(httpd_req_t *req);
|
||||
esp_err_t config_post_handler(httpd_req_t *req);
|
||||
esp_err_t connect_post_handler(httpd_req_t *req);
|
||||
esp_err_t connect_delete_handler(httpd_req_t *req);
|
||||
esp_err_t reboot_ota_post_handler(httpd_req_t *req);
|
||||
esp_err_t reboot_post_handler(httpd_req_t *req);
|
||||
esp_err_t recovery_post_handler(httpd_req_t *req);
|
||||
#if RECOVERY_APPLICATION
|
||||
esp_err_t flash_post_handler(httpd_req_t *req);
|
||||
#endif
|
||||
esp_err_t status_get_handler(httpd_req_t *req);
|
||||
esp_err_t messages_get_handler(httpd_req_t *req);
|
||||
|
||||
esp_err_t ap_scan_handler(httpd_req_t *req);
|
||||
esp_err_t redirect_ev_handler(httpd_req_t *req);
|
||||
esp_err_t redirect_200_ev_handler(httpd_req_t *req);
|
||||
|
||||
|
||||
esp_err_t err_handler(httpd_req_t *req, httpd_err_code_t error);
|
||||
#define SCRATCH_BUFSIZE (10240)
|
||||
#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128)
|
||||
|
||||
typedef struct rest_server_context {
|
||||
char base_path[ESP_VFS_PATH_MAX + 1];
|
||||
char scratch[SCRATCH_BUFSIZE];
|
||||
} rest_server_context_t;
|
||||
/**
|
||||
* @brief RTOS task for the HTTP server. Do not start manually.
|
||||
* @see void http_server_start()
|
||||
*/
|
||||
void CODE_RAM_LOCATION http_server(void *pvParameters);
|
||||
|
||||
/* @brief helper function that processes one HTTP request at a time */
|
||||
void CODE_RAM_LOCATION http_server_netconn_serve(struct netconn *conn);
|
||||
|
||||
/* @brief create the task for the http server */
|
||||
esp_err_t CODE_RAM_LOCATION http_server_start();
|
||||
|
||||
/**
|
||||
* @brief gets a char* pointer to the first occurence of header_name withing the complete http request request.
|
||||
*
|
||||
* For optimization purposes, no local copy is made. memcpy can then be used in coordination with len to extract the
|
||||
* data.
|
||||
*
|
||||
* @param request the full HTTP raw request.
|
||||
* @param header_name the header that is being searched.
|
||||
* @param len the size of the header value if found.
|
||||
* @return pointer to the beginning of the header value.
|
||||
*/
|
||||
char* CODE_RAM_LOCATION http_server_get_header(char *request, char *header_name, int *len);
|
||||
|
||||
void CODE_RAM_LOCATION strreplace(char *src, char *str, char *rep);
|
||||
/* @brief lock the json config object */
|
||||
bool http_server_lock_json_object(TickType_t xTicksToWait);
|
||||
/* @brief unlock the json config object */
|
||||
void http_server_unlock_json_object();
|
||||
#define PROTECTED_JSON_CALL(a) if(http_server_lock_json_object( portMAX_DELAY )){ \ a; http_server_unlocklock_json_object(); } else{ ESP_LOGE(TAG, "could not get access to json mutex in wifi_scan"); }
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -4,19 +4,12 @@
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<link rel="stylesheet" href="/bootstrap.css">
|
||||
<link rel="stylesheet" href="res/test/bootstrap.min.css"> <!-- TODO delete -->
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<script src="/jquery.js"></script>
|
||||
<script src="/popper.js"></script>
|
||||
<script src="/bootstrap.js"></script>
|
||||
|
||||
<script src="res/test/jquery.min.js"></script> <!-- TODO delete -->
|
||||
<script src="res/test/popper.min.js"></script> <!-- TODO delete -->
|
||||
<script src="res/test/bootstrap.min.js"></script> <!-- TODO delete -->
|
||||
|
||||
<script src="/code.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/res/bootstrap.css">
|
||||
<link rel="stylesheet" href="/res/style.css">
|
||||
<script src="/res/jquery.js"></script>
|
||||
<script src="/res/popper.js"></script>
|
||||
<script src="/res/bootstrap.js"></script>
|
||||
<script src="/res/code.js"></script>
|
||||
|
||||
<title>esp32-wifi-manager</title>
|
||||
</head>
|
||||
@@ -74,7 +67,7 @@
|
||||
<a class="nav-link" data-toggle="tab" href="#tab-firmware">Firmware</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#tab-gpio">GPIO</a>
|
||||
<a class="nav-link" data-toggle="tab" href="#tab-syslog">Syslog</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#tab-nvs">NVS editor</a>
|
||||
@@ -197,7 +190,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- wifi -->
|
||||
|
||||
<div class="tab-pane fade" id="tab-audio">
|
||||
<div id="audioout">
|
||||
@@ -247,72 +240,7 @@
|
||||
<label class="custom-control-label" for="autoexec-cb"></label>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="tab-gpio">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Signal</th>
|
||||
<th scope="col">I2S pin</th>
|
||||
<th scope="col">SPDIF pin</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="gpioTable">
|
||||
<tr>
|
||||
<td>Bit clock</td>
|
||||
<td>
|
||||
<input type="text" class="form-control gpio" id="gpio-i2s-bc" maxlength="2" size="2">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" class="form-control gpio" id="gpio-spdif-bc" maxlength="2" size="2">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Word select</td>
|
||||
<td>
|
||||
<input type="text" class="form-control gpio" id="gpio-i2s-ws" maxlength="2" size="2">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" class="form-control gpio" id="gpio-spdif-ws" maxlength="2" size="2">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Data</td>
|
||||
<td>
|
||||
<input type="text" class="form-control gpio" id="gpio-i2s-data" maxlength="2" size="2">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" class="form-control gpio" id="gpio-spdif-data" maxlength="2" size="2">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<input id="save-gpio" type="button" class="btn btn-success" value="Save" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="tab-nvs">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Key</th>
|
||||
<th scope="col">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="nvsTable">
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<div id="boot-div">
|
||||
<form id="reboot-form" action="/reboot.json" method="post" target="dummyframe">
|
||||
<button id="reboot-button" type="submit" class="btn btn-primary">Reboot</button>
|
||||
</form>
|
||||
</div>
|
||||
<input id="save-nvs" type="button" class="btn btn-success" value="Save" />
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- audio -->
|
||||
|
||||
<div class="tab-pane fade" id="tab-firmware">
|
||||
<div id="boot-div">
|
||||
@@ -343,21 +271,17 @@
|
||||
</table>
|
||||
<h2>Firmware URL:</h2>
|
||||
<textarea id="fwurl" maxlength="350"></textarea>
|
||||
<!--
|
||||
<br />OR<br />
|
||||
<div class="input-group mb-3" id="upload">
|
||||
<div class="custom-file">
|
||||
<input type="file" class="custom-file-input" id="inputGroupFile01">
|
||||
<label class="custom-file-label" for="inputGroupFile01"></label>
|
||||
</div>
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text" id="fwUpload">Upload</span>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<div class="buttons">
|
||||
<input type="button" id="flash" class="btn btn-danger" value="Flash!" /><span id="flash-status"></span>
|
||||
<input type="button" id="flash" class="btn btn-danger" value="Flash!" /><span id="flash-status"></span>
|
||||
</div>
|
||||
<p>OR</p>
|
||||
<div class="form-group">
|
||||
<input type="file" class="form-control-file" id="flashfilename" aria-describedby="fileHelp">
|
||||
<div class="buttons">
|
||||
<button type="button" class="btn btn-danger" id="fwUpload">Upload!</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="otadiv">
|
||||
<div class="progress" id="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuemin="0" aria-valuemax="100" style="width:0%">
|
||||
@@ -365,11 +289,48 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- firmware -->
|
||||
|
||||
<div class="tab-pane fade" id="tab-syslog">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Timestamp</th>
|
||||
<th scope="col">Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="syslogTable">
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<input id="clear-syslog" type="button" class="btn btn-danger btn-sm" value="Clear" />
|
||||
</div>
|
||||
</div> <!-- syslog -->
|
||||
|
||||
<div class="tab-pane fade" id="tab-nvs">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Key</th>
|
||||
<th scope="col">Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="nvsTable">
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<div id="boot-div">
|
||||
<form id="reboot-form" action="/reboot.json" method="post" target="dummyframe">
|
||||
<button id="reboot-button" type="submit" class="btn btn-primary">Reboot</button>
|
||||
</form>
|
||||
</div>
|
||||
<input id="save-nvs" type="button" class="btn btn-success" value="Save" />
|
||||
</div>
|
||||
</div> <!-- nvs -->
|
||||
|
||||
<div class="tab-pane fade" id="tab-credits">
|
||||
<div class="jumbotron">
|
||||
<p><strong><a href="https://github.com/sle118/squeezelite-esp32">squeezelite-esp32</a></strong>, © 2019, philippe44, sle118, daduke<br />Licensed under the GPL</p>
|
||||
<p><strong><a href="https://github.com/sle118/squeezelite-esp32">squeezelite-esp32</a></strong>, © 2020, philippe44, sle118, daduke<br />Licensed under the GPL</p>
|
||||
<p>
|
||||
This app would not be possible without the following libraries:
|
||||
</p>
|
||||
@@ -388,7 +349,7 @@
|
||||
<input type="checkbox" class="custom-control-input" id="show-nvs" checked="checked">
|
||||
<label class="custom-control-label" for="show-nvs"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- credits -->
|
||||
</div>
|
||||
<footer class="footer"><span id="foot-fw"></span><span id="foot-wifi"></span></footer>
|
||||
<iframe width="0" height="0" border="0" name="dummyframe" id="dummyframe"></iframe>
|
||||
|
||||
@@ -212,12 +212,6 @@ input[type='text'], input[type='password'], textarea {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
input.gpio {
|
||||
width: 2em;
|
||||
color: #000;
|
||||
height: 1.8em;
|
||||
}
|
||||
|
||||
.custom-switch {
|
||||
margin-left: 8px;
|
||||
}
|
||||
@@ -273,6 +267,18 @@ textarea#autoexec1, textarea#fwurl, div#upload {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
table tr.MESSAGING_INFO {
|
||||
background: #123;
|
||||
}
|
||||
|
||||
table tr.MESSAGING_WARNING {
|
||||
background: #330;
|
||||
}
|
||||
|
||||
table tr.MESSAGING_ERROR {
|
||||
background: #300;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
border-radius: 3px;
|
||||
border: 1px solid transparent;
|
||||
|
||||
@@ -37,7 +37,6 @@ Contains the freeRTOS task and all necessary support
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dns_server.h"
|
||||
#include "http_server.h"
|
||||
#include "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
@@ -61,6 +60,9 @@ Contains the freeRTOS task and all necessary support
|
||||
#include "platform_config.h"
|
||||
#include "trace.h"
|
||||
#include "cmd_system.h"
|
||||
#include "messaging.h"
|
||||
|
||||
#include "http_server_handlers.h"
|
||||
#include "monitor.h"
|
||||
#include "globdefs.h"
|
||||
|
||||
@@ -69,7 +71,7 @@ Contains the freeRTOS task and all necessary support
|
||||
#endif
|
||||
|
||||
#define STR_OR_BLANK(p) p==NULL?"":p
|
||||
#define FREE_AND_NULL(p) if(p!=NULL){ free(p); p=NULL;}
|
||||
|
||||
/* objects used to manipulate the main queue of events */
|
||||
QueueHandle_t wifi_manager_queue;
|
||||
SemaphoreHandle_t wifi_manager_json_mutex = NULL;
|
||||
@@ -83,7 +85,7 @@ char *ip_info_json = NULL;
|
||||
char * release_url=NULL;
|
||||
cJSON * ip_info_cjson=NULL;
|
||||
wifi_config_t* wifi_manager_config_sta = NULL;
|
||||
static update_reason_code_t last_update_reason_code=0;
|
||||
|
||||
|
||||
static int32_t total_connected_time=0;
|
||||
static int64_t last_connected=0;
|
||||
@@ -202,9 +204,6 @@ bool isGroupBitSet(uint8_t bit){
|
||||
EventBits_t uxBits= xEventGroupGetBits(wifi_manager_event_group);
|
||||
return (uxBits & bit);
|
||||
}
|
||||
void wifi_manager_refresh_ota_json(){
|
||||
wifi_manager_send_message(EVENT_REFRESH_OTA, NULL);
|
||||
}
|
||||
|
||||
void wifi_manager_scan_async(){
|
||||
wifi_manager_send_message(ORDER_START_WIFI_SCAN, NULL);
|
||||
@@ -357,6 +356,7 @@ esp_err_t wifi_manager_save_sta_config(){
|
||||
esp_err = nvs_commit(handle);
|
||||
if (esp_err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Unable to commit changes. Error %s", esp_err_to_name(esp_err));
|
||||
messaging_post_message(MESSAGING_ERROR,MESSAGING_CLASS_SYSTEM,"Unable to save wifi credentials. %s",esp_err_to_name(esp_err));
|
||||
return esp_err;
|
||||
}
|
||||
nvs_close(handle);
|
||||
@@ -449,8 +449,6 @@ cJSON * wifi_manager_get_basic_info(cJSON **old){
|
||||
cJSON_AddItemToObject(root, "version", cJSON_CreateString(desc->version));
|
||||
if(release_url !=NULL) cJSON_AddItemToObject(root, "release_url", cJSON_CreateString(release_url));
|
||||
cJSON_AddNumberToObject(root,"recovery", is_recovery_running?1:0);
|
||||
cJSON_AddItemToObject(root, "ota_dsc", cJSON_CreateString(ota_get_status()));
|
||||
cJSON_AddNumberToObject(root,"ota_pct", ota_get_pct_complete() );
|
||||
cJSON_AddItemToObject(root, "Jack", cJSON_CreateString(jack_inserted_svc() ? "1" : "0"));
|
||||
cJSON_AddNumberToObject(root,"Voltage", battery_value_svc());
|
||||
cJSON_AddNumberToObject(root,"disconnect_count", num_disconnect );
|
||||
@@ -479,12 +477,6 @@ void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code)
|
||||
wifi_config_t *config = wifi_manager_get_wifi_sta_config();
|
||||
ip_info_cjson = wifi_manager_get_basic_info(&ip_info_cjson);
|
||||
|
||||
if(update_reason_code == UPDATE_OTA) {
|
||||
update_reason_code = last_update_reason_code;
|
||||
}
|
||||
else {
|
||||
last_update_reason_code = update_reason_code;
|
||||
}
|
||||
cJSON_AddNumberToObject(ip_info_cjson, "urc", update_reason_code);
|
||||
if(config){
|
||||
cJSON_AddItemToObject(ip_info_cjson, "ssid", cJSON_CreateString((char *)config->sta.ssid));
|
||||
@@ -505,7 +497,7 @@ char * get_mac_string(uint8_t mac[6]){
|
||||
|
||||
char * macStr=malloc(LOCAL_MAC_SIZE);
|
||||
memset(macStr, 0x00, LOCAL_MAC_SIZE);
|
||||
snprintf(macStr, LOCAL_MAC_SIZE-1,MACSTR, MAC2STR(mac));
|
||||
snprintf(macStr, LOCAL_MAC_SIZE,MACSTR, MAC2STR(mac));
|
||||
return macStr;
|
||||
|
||||
}
|
||||
@@ -855,20 +847,6 @@ void wifi_manager_connect_async(){
|
||||
wifi_manager_send_message(ORDER_CONNECT_STA, (void*)CONNECTION_REQUEST_USER);
|
||||
}
|
||||
|
||||
void set_status_message(message_severity_t severity, const char * message){
|
||||
if(ip_info_cjson==NULL){
|
||||
ip_info_cjson = wifi_manager_get_new_json(&ip_info_cjson);
|
||||
}
|
||||
if(ip_info_cjson==NULL){
|
||||
ESP_LOGE(TAG, "Error setting status message. Unable to allocate cJSON.");
|
||||
return;
|
||||
}
|
||||
cJSON * item=cJSON_GetObjectItem(ip_info_cjson, "message");
|
||||
item = wifi_manager_get_new_json(&item);
|
||||
cJSON_AddItemToObject(item, "severity", cJSON_CreateString(severity==INFO?"INFO":severity==WARNING?"WARNING":severity==ERROR?"ERROR":"" ));
|
||||
cJSON_AddItemToObject(item, "text", cJSON_CreateString(message));
|
||||
}
|
||||
|
||||
|
||||
char* wifi_manager_alloc_get_ip_info_json(){
|
||||
return cJSON_PrintUnformatted(ip_info_cjson);
|
||||
@@ -1138,12 +1116,6 @@ void wifi_manager( void * pvParameters ){
|
||||
ESP_LOGD(TAG, "Done Invoking SCAN DONE callback");
|
||||
}
|
||||
break;
|
||||
case EVENT_REFRESH_OTA:
|
||||
if(wifi_manager_lock_json_buffer( portMAX_DELAY )){
|
||||
wifi_manager_generate_ip_info_json( UPDATE_OTA );
|
||||
wifi_manager_unlock_json_buffer();
|
||||
}
|
||||
break;
|
||||
|
||||
case ORDER_START_WIFI_SCAN:
|
||||
ESP_LOGD(TAG, "MESSAGE: ORDER_START_WIFI_SCAN");
|
||||
@@ -1153,6 +1125,7 @@ void wifi_manager( void * pvParameters ){
|
||||
if(esp_wifi_scan_start(&scan_config, false)!=ESP_OK){
|
||||
ESP_LOGW(TAG, "Unable to start scan; wifi is trying to connect");
|
||||
// set_status_message(WARNING, "Wifi Connecting. Cannot start scan.");
|
||||
messaging_post_message(MESSAGING_WARNING,MESSAGING_CLASS_SYSTEM,"Wifi connecting. Cannot start scan.");
|
||||
}
|
||||
else {
|
||||
xEventGroupSetBits(wifi_manager_event_group, WIFI_MANAGER_SCAN_BIT);
|
||||
@@ -1358,6 +1331,8 @@ void wifi_manager( void * pvParameters ){
|
||||
else{
|
||||
/* lost connection ? */
|
||||
ESP_LOGE(TAG, "WiFi Connection lost.");
|
||||
messaging_post_message(MESSAGING_WARNING,MESSAGING_CLASS_SYSTEM,"WiFi Connection lost");
|
||||
|
||||
if(wifi_manager_lock_json_buffer( portMAX_DELAY )){
|
||||
wifi_manager_generate_ip_info_json( UPDATE_LOST_CONNECTION );
|
||||
wifi_manager_unlock_json_buffer();
|
||||
@@ -1439,7 +1414,7 @@ void wifi_manager( void * pvParameters ){
|
||||
if(cb_ptr_arr[msg.code]) (*cb_ptr_arr[msg.code])(NULL);
|
||||
break;
|
||||
case UPDATE_CONNECTION_OK:
|
||||
/* refresh JSON with the new ota data */
|
||||
/* refresh JSON */
|
||||
if(wifi_manager_lock_json_buffer( portMAX_DELAY )){
|
||||
/* generate the connection info with success */
|
||||
wifi_manager_generate_ip_info_json( UPDATE_CONNECTION_OK );
|
||||
|
||||
@@ -42,6 +42,17 @@ extern "C" {
|
||||
#include "squeezelite-ota.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
#ifndef RECOVERY_APPLICATION
|
||||
#error "RECOVERY_APPLICATION not defined. Defaulting to squeezelite"
|
||||
#endif
|
||||
|
||||
#if RECOVERY_APPLICATION==1
|
||||
#elif RECOVERY_APPLICATION==0
|
||||
#else
|
||||
#error "unknown configuration"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Defines the maximum size of a SSID name. 32 is IEEE standard.
|
||||
@@ -187,12 +198,11 @@ typedef enum message_code_t {
|
||||
EVENT_STA_DISCONNECTED = 12,
|
||||
EVENT_SCAN_DONE = 13,
|
||||
EVENT_STA_GOT_IP = 14,
|
||||
EVENT_REFRESH_OTA = 15,
|
||||
ORDER_RESTART_OTA = 16,
|
||||
ORDER_RESTART_RECOVERY = 17,
|
||||
ORDER_RESTART_OTA_URL = 18,
|
||||
ORDER_RESTART = 19,
|
||||
MESSAGE_CODE_COUNT = 20 /* important for the callback array */
|
||||
ORDER_RESTART_OTA = 15,
|
||||
ORDER_RESTART_RECOVERY = 16,
|
||||
ORDER_RESTART_OTA_URL = 17,
|
||||
ORDER_RESTART = 18,
|
||||
MESSAGE_CODE_COUNT = 19 /* important for the callback array */
|
||||
|
||||
}message_code_t;
|
||||
|
||||
@@ -215,8 +225,7 @@ typedef enum update_reason_code_t {
|
||||
UPDATE_CONNECTION_OK = 0,
|
||||
UPDATE_FAILED_ATTEMPT = 1,
|
||||
UPDATE_USER_DISCONNECT = 2,
|
||||
UPDATE_LOST_CONNECTION = 3,
|
||||
UPDATE_OTA=4
|
||||
UPDATE_LOST_CONNECTION = 3
|
||||
}update_reason_code_t;
|
||||
|
||||
typedef enum connection_request_made_by_code_t{
|
||||
|
||||
165
components/wifi-manager/wifi_manager_http_server.c
Normal file
165
components/wifi-manager/wifi_manager_http_server.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Squeezelite for esp32
|
||||
*
|
||||
* (c) Sebastien 2019
|
||||
* Philippe G. 2019, philippe_44@outlook.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "http_server_handlers.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "_esp_http_server.h"
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "config.h"
|
||||
#include "messaging.h"
|
||||
static const char TAG[] = "http_server";
|
||||
|
||||
static httpd_handle_t _server = NULL;
|
||||
rest_server_context_t *rest_context = NULL;
|
||||
RingbufHandle_t messaging=NULL;
|
||||
|
||||
void register_common_handlers(httpd_handle_t server){
|
||||
httpd_uri_t res_get = { .uri = "/res/*", .method = HTTP_GET, .handler = resource_filehandler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &res_get);
|
||||
|
||||
}
|
||||
void register_regular_handlers(httpd_handle_t server){
|
||||
httpd_uri_t root_get = { .uri = "/", .method = HTTP_GET, .handler = root_get_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &root_get);
|
||||
|
||||
httpd_uri_t ap_get = { .uri = "/ap.json", .method = HTTP_GET, .handler = ap_get_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &ap_get);
|
||||
httpd_uri_t scan_get = { .uri = "/scan.json", .method = HTTP_GET, .handler = ap_scan_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &scan_get);
|
||||
httpd_uri_t config_get = { .uri = "/config.json", .method = HTTP_GET, .handler = config_get_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &config_get);
|
||||
httpd_uri_t status_get = { .uri = "/status.json", .method = HTTP_GET, .handler = status_get_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &status_get);
|
||||
httpd_uri_t messages_get = { .uri = "/messages.json", .method = HTTP_GET, .handler = messages_get_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &messages_get);
|
||||
|
||||
httpd_uri_t config_post = { .uri = "/config.json", .method = HTTP_POST, .handler = config_post_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &config_post);
|
||||
httpd_uri_t connect_post = { .uri = "/connect.json", .method = HTTP_POST, .handler = connect_post_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &connect_post);
|
||||
|
||||
httpd_uri_t reboot_ota_post = { .uri = "/reboot_ota.json", .method = HTTP_POST, .handler = reboot_ota_post_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &reboot_ota_post);
|
||||
|
||||
httpd_uri_t reboot_post = { .uri = "/reboot.json", .method = HTTP_POST, .handler = reboot_post_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &reboot_post);
|
||||
|
||||
httpd_uri_t recovery_post = { .uri = "/recovery.json", .method = HTTP_POST, .handler = recovery_post_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &recovery_post);
|
||||
|
||||
httpd_uri_t connect_delete = { .uri = "/connect.json", .method = HTTP_DELETE, .handler = connect_delete_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &connect_delete);
|
||||
|
||||
#if RECOVERY_APPLICATION
|
||||
|
||||
httpd_uri_t flash_post = { .uri = "/flash.json", .method = HTTP_POST, .handler = flash_post_handler, .user_ctx = rest_context };
|
||||
httpd_register_uri_handler(server, &flash_post);
|
||||
#endif
|
||||
|
||||
|
||||
// from https://github.com/tripflex/wifi-captive-portal/blob/master/src/mgos_wifi_captive_portal.c
|
||||
// https://unix.stackexchange.com/questions/432190/why-isnt-androids-captive-portal-detection-triggering-a-browser-window
|
||||
// Known HTTP GET requests to check for Captive Portal
|
||||
|
||||
///kindle-wifi/wifiredirect.html Kindle when requested with com.android.captiveportallogin
|
||||
///kindle-wifi/wifistub.html Kindle before requesting with captive portal login window (maybe for detection?)
|
||||
|
||||
|
||||
httpd_uri_t connect_redirect_1 = { .uri = "/mobile/status.php", .method = HTTP_GET, .handler = redirect_200_ev_handler, .user_ctx = rest_context };// Android 8.0 (Samsung s9+)
|
||||
httpd_register_uri_handler(server, &connect_redirect_1);
|
||||
httpd_uri_t connect_redirect_2 = { .uri = "/generate_204", .method = HTTP_GET, .handler = redirect_200_ev_handler, .user_ctx = rest_context };// Android
|
||||
httpd_register_uri_handler(server, &connect_redirect_2);
|
||||
httpd_uri_t connect_redirect_3 = { .uri = "/gen_204", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context };// Android 9.0
|
||||
httpd_register_uri_handler(server, &connect_redirect_3);
|
||||
// httpd_uri_t connect_redirect_4 = { .uri = "/ncsi.txt", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context };// Windows
|
||||
// httpd_register_uri_handler(server, &connect_redirect_4);
|
||||
httpd_uri_t connect_redirect_5 = { .uri = "/hotspot-detect.html", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context }; // iOS 8/9
|
||||
httpd_register_uri_handler(server, &connect_redirect_5);
|
||||
httpd_uri_t connect_redirect_6 = { .uri = "/library/test/success.html", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context };// iOS 8/9
|
||||
httpd_register_uri_handler(server, &connect_redirect_6);
|
||||
httpd_uri_t connect_redirect_7 = { .uri = "/hotspotdetect.html", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context }; // iOS
|
||||
httpd_register_uri_handler(server, &connect_redirect_7);
|
||||
httpd_uri_t connect_redirect_8 = { .uri = "/success.txt", .method = HTTP_GET, .handler = redirect_ev_handler, .user_ctx = rest_context }; // OSX
|
||||
httpd_register_uri_handler(server, &connect_redirect_8);
|
||||
|
||||
|
||||
|
||||
ESP_LOGD(TAG,"Registering default error handler for 404");
|
||||
httpd_register_err_handler(server, HTTPD_404_NOT_FOUND,&err_handler);
|
||||
|
||||
}
|
||||
|
||||
|
||||
esp_err_t http_server_start()
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing HTTP Server");
|
||||
messaging = messaging_register_subscriber(10, "http_server");
|
||||
rest_context = calloc(1, sizeof(rest_server_context_t));
|
||||
if(rest_context==NULL){
|
||||
ESP_LOGE(TAG,"No memory for http context");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
strlcpy(rest_context->base_path, "/res/", sizeof(rest_context->base_path));
|
||||
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.max_uri_handlers = 25;
|
||||
config.max_open_sockets = 5;
|
||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
||||
//todo: use the endpoint below to configure session token?
|
||||
// config.open_fn
|
||||
|
||||
ESP_LOGI(TAG, "Starting HTTP Server");
|
||||
esp_err_t err= __httpd_start(&_server, &config);
|
||||
if(err != ESP_OK){
|
||||
ESP_LOGE_LOC(TAG,"Start server failed");
|
||||
}
|
||||
else {
|
||||
|
||||
register_common_handlers(_server);
|
||||
register_regular_handlers(_server);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Function to free context */
|
||||
void adder_free_func(void *ctx)
|
||||
{
|
||||
ESP_LOGI(TAG, "/adder Free Context function called");
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
|
||||
void stop_webserver(httpd_handle_t server)
|
||||
{
|
||||
// Stop the httpd server
|
||||
httpd_stop(server);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user