| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | /* Async Request Handlers HTTP Server Example
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    This example code is in the Public Domain (or CC0 licensed, at your option.) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    Unless required by applicable law or agreed to in writing, this | 
					
						
							|  |  |  |    software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | 
					
						
							|  |  |  |    CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | #include "freertos/FreeRTOS.h"
 | 
					
						
							|  |  |  | #include "freertos/semphr.h"
 | 
					
						
							|  |  |  | #include <esp_wifi.h>
 | 
					
						
							|  |  |  | #include <esp_event.h>
 | 
					
						
							|  |  |  | #include <esp_log.h>
 | 
					
						
							|  |  |  | #include <esp_system.h>
 | 
					
						
							|  |  |  | #include <nvs_flash.h>
 | 
					
						
							|  |  |  | #include <sys/param.h>
 | 
					
						
							|  |  |  | #include "nvs_flash.h"
 | 
					
						
							|  |  |  | #include "esp_netif.h"
 | 
					
						
							|  |  |  | #include "esp_eth.h"
 | 
					
						
							|  |  |  | #include "protocol_examples_common.h"
 | 
					
						
							|  |  |  | #include "esp_tls_crypto.h"
 | 
					
						
							|  |  |  | #include <esp_http_server.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* An example that demonstrates multiple
 | 
					
						
							|  |  |  |    long running http requests running in parallel. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    In this example, multiple long http request can run at | 
					
						
							|  |  |  |    the same time. (uri: /long) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    While these long requests are running, the server can still | 
					
						
							|  |  |  |    respond to other incoming synchronous requests. (uri: /quick) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define ASYNC_WORKER_TASK_PRIORITY      5
 | 
					
						
							|  |  |  | #define ASYNC_WORKER_TASK_STACK_SIZE    2048
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char *TAG = "example"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-26 12:27:54 +02:00
										 |  |  | // Async requests are queued here while they wait to
 | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | // be processed by the workers
 | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  | static QueueHandle_t request_queue; | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Track the number of free workers at any given time
 | 
					
						
							|  |  |  | static SemaphoreHandle_t worker_ready_count; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Each worker has its own thread
 | 
					
						
							|  |  |  | static TaskHandle_t worker_handles[CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef esp_err_t (*httpd_req_handler_t)(httpd_req_t *req); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  |     httpd_req_t* req; | 
					
						
							|  |  |  |     httpd_req_handler_t handler; | 
					
						
							|  |  |  | } httpd_async_req_t; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  | // queue an HTTP req to the worker queue
 | 
					
						
							|  |  |  | static esp_err_t queue_request(httpd_req_t *req, httpd_req_handler_t handler) | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     // must create a copy of the request that we own
 | 
					
						
							|  |  |  |     httpd_req_t* copy = NULL; | 
					
						
							|  |  |  |     esp_err_t err = httpd_req_async_handler_begin(req, ©); | 
					
						
							|  |  |  |     if (err != ESP_OK) { | 
					
						
							|  |  |  |         return err; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     httpd_async_req_t async_req = { | 
					
						
							|  |  |  |         .req = copy, | 
					
						
							|  |  |  |         .handler = handler, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // How should we handle resource exhaustion?
 | 
					
						
							|  |  |  |     // In this example, we immediately respond with an
 | 
					
						
							|  |  |  |     // http error if no workers are available.
 | 
					
						
							|  |  |  |     int ticks = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // counting semaphore: if success, we know 1 or
 | 
					
						
							|  |  |  |     // more asyncReqTaskWorkers are available.
 | 
					
						
							|  |  |  |     if (xSemaphoreTake(worker_ready_count, ticks) == false) { | 
					
						
							|  |  |  |         ESP_LOGE(TAG, "No workers are available"); | 
					
						
							|  |  |  |         httpd_req_async_handler_complete(copy); // cleanup
 | 
					
						
							|  |  |  |         return ESP_FAIL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Since worker_ready_count > 0 the queue should already have space.
 | 
					
						
							|  |  |  |     // But lets wait up to 100ms just to be safe.
 | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  |     if (xQueueSend(request_queue, &async_req, pdMS_TO_TICKS(100)) == false) { | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  |         ESP_LOGE(TAG, "worker queue is full"); | 
					
						
							|  |  |  |         httpd_req_async_handler_complete(copy); // cleanup
 | 
					
						
							|  |  |  |         return ESP_FAIL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  | /* handle long request (on async thread) */ | 
					
						
							|  |  |  | static esp_err_t long_async(httpd_req_t *req) | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  |     ESP_LOGI(TAG, "running: /long"); | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // track the number of long requests
 | 
					
						
							|  |  |  |     static uint8_t req_count = 0; | 
					
						
							|  |  |  |     req_count++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // send a request count
 | 
					
						
							|  |  |  |     char s[100]; | 
					
						
							|  |  |  |     snprintf(s, sizeof(s), "<div>req: %u</div>\n", req_count); | 
					
						
							|  |  |  |     httpd_resp_sendstr_chunk(req, s); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // then every second, send a "tick"
 | 
					
						
							|  |  |  |     for (int i = 0; i < 60; i++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // This delay makes this a "long running task".
 | 
					
						
							|  |  |  |         // In a real application, this may be a long calculation,
 | 
					
						
							|  |  |  |         // or some IO dependent code for instance.
 | 
					
						
							|  |  |  |         vTaskDelay(pdMS_TO_TICKS(1000)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // send a tick
 | 
					
						
							|  |  |  |         snprintf(s, sizeof(s), "<div>%u</div>\n", i); | 
					
						
							|  |  |  |         httpd_resp_sendstr_chunk(req, s); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // send "complete"
 | 
					
						
							|  |  |  |     httpd_resp_sendstr_chunk(req, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  | // each worker thread loops forever, processing requests
 | 
					
						
							|  |  |  | static void worker_task(void *p) | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     ESP_LOGI(TAG, "starting async req task worker"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (true) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // counting semaphore - this signals that a worker
 | 
					
						
							|  |  |  |         // is ready to accept work
 | 
					
						
							|  |  |  |         xSemaphoreGive(worker_ready_count); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // wait for a request
 | 
					
						
							|  |  |  |         httpd_async_req_t async_req; | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  |         if (xQueueReceive(request_queue, &async_req, portMAX_DELAY)) { | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             ESP_LOGI(TAG, "invoking %s", async_req.req->uri); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // call the handler
 | 
					
						
							|  |  |  |             async_req.handler(async_req.req); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Inform the server that it can purge the socket used for
 | 
					
						
							|  |  |  |             // this request, if needed.
 | 
					
						
							|  |  |  |             if (httpd_req_async_handler_complete(async_req.req) != ESP_OK) { | 
					
						
							|  |  |  |                 ESP_LOGE(TAG, "failed to complete async req"); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ESP_LOGW(TAG, "worker stopped"); | 
					
						
							|  |  |  |     vTaskDelete(NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  | // start worker threads
 | 
					
						
							|  |  |  | static void start_workers(void) | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // counting semaphore keeps track of available workers
 | 
					
						
							|  |  |  |     worker_ready_count = xSemaphoreCreateCounting( | 
					
						
							|  |  |  |         CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS,  // Max Count
 | 
					
						
							|  |  |  |         0); // Initial Count
 | 
					
						
							|  |  |  |     if (worker_ready_count == NULL) { | 
					
						
							|  |  |  |         ESP_LOGE(TAG, "Failed to create workers counting Semaphore"); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // create queue
 | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  |     request_queue = xQueueCreate(1, sizeof(httpd_async_req_t)); | 
					
						
							|  |  |  |     if (request_queue == NULL){ | 
					
						
							|  |  |  |         ESP_LOGE(TAG, "Failed to create request_queue"); | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  |         vSemaphoreDelete(worker_ready_count); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // start worker tasks
 | 
					
						
							|  |  |  |     for (int i = 0; i < CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS; i++) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  |         bool success = xTaskCreate(worker_task, "async_req_worker", | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  |                                     ASYNC_WORKER_TASK_STACK_SIZE, // stack size
 | 
					
						
							|  |  |  |                                     (void *)0, // argument
 | 
					
						
							|  |  |  |                                     ASYNC_WORKER_TASK_PRIORITY, // priority
 | 
					
						
							|  |  |  |                                     &worker_handles[i]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!success) { | 
					
						
							|  |  |  |             ESP_LOGE(TAG, "Failed to start asyncReqWorker"); | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  | /* adds /long request to the request queue */ | 
					
						
							|  |  |  | static esp_err_t long_handler(httpd_req_t *req) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ESP_LOGI(TAG, "uri: /long"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // add to the async request queue
 | 
					
						
							|  |  |  |     if (queue_request(req, long_async) == ESP_OK) { | 
					
						
							|  |  |  |         return ESP_OK; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         httpd_resp_set_status(req, "503 Busy"); | 
					
						
							|  |  |  |         httpd_resp_sendstr(req, "<div> no workers available. server busy.</div>"); | 
					
						
							|  |  |  |         return ESP_OK; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* A quick HTTP GET handler, which does not
 | 
					
						
							|  |  |  |    use any asynchronous features */ | 
					
						
							|  |  |  | static esp_err_t quick_handler(httpd_req_t *req) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ESP_LOGI(TAG, "uri: /quick"); | 
					
						
							|  |  |  |     char s[100]; | 
					
						
							|  |  |  |     snprintf(s, sizeof(s), "random: %u\n", rand()); | 
					
						
							|  |  |  |     httpd_resp_sendstr(req, s); | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static esp_err_t index_handler(httpd_req_t *req) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     ESP_LOGI(TAG, "uri: /"); | 
					
						
							|  |  |  |     const char* html = "<div><a href=\"/long\">long</a></div>" | 
					
						
							|  |  |  |         "<div><a href=\"/quick\">quick</a></div>"; | 
					
						
							|  |  |  |     httpd_resp_sendstr(req, html); | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static httpd_handle_t start_webserver(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     httpd_handle_t server = NULL; | 
					
						
							|  |  |  |     httpd_config_t config = HTTPD_DEFAULT_CONFIG(); | 
					
						
							|  |  |  |     config.lru_purge_enable = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // It is advisable that httpd_config_t->max_open_sockets > MAX_ASYNC_REQUESTS
 | 
					
						
							|  |  |  |     // Why? This leaves at least one socket still available to handle
 | 
					
						
							|  |  |  |     // quick synchronous requests. Otherwise, all the sockets will
 | 
					
						
							|  |  |  |     // get taken by the long async handlers, and your server will no
 | 
					
						
							|  |  |  |     // longer be responsive.
 | 
					
						
							|  |  |  |     config.max_open_sockets = CONFIG_EXAMPLE_MAX_ASYNC_REQUESTS + 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Start the httpd server
 | 
					
						
							|  |  |  |     ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); | 
					
						
							|  |  |  |     if (httpd_start(&server, &config) != ESP_OK) { | 
					
						
							|  |  |  |         ESP_LOGI(TAG, "Error starting server!"); | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const httpd_uri_t index_uri = { | 
					
						
							|  |  |  |         .uri       = "/", | 
					
						
							|  |  |  |         .method    = HTTP_GET, | 
					
						
							|  |  |  |         .handler   = index_handler, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const httpd_uri_t long_uri = { | 
					
						
							|  |  |  |         .uri       = "/long", | 
					
						
							|  |  |  |         .method    = HTTP_GET, | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  |         .handler   = long_handler, | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const httpd_uri_t quick_uri = { | 
					
						
							|  |  |  |         .uri       = "/quick", | 
					
						
							|  |  |  |         .method    = HTTP_GET, | 
					
						
							|  |  |  |         .handler   = quick_handler, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Set URI handlers
 | 
					
						
							|  |  |  |     ESP_LOGI(TAG, "Registering URI handlers"); | 
					
						
							|  |  |  |     httpd_register_uri_handler(server, &index_uri); | 
					
						
							|  |  |  |     httpd_register_uri_handler(server, &long_uri); | 
					
						
							|  |  |  |     httpd_register_uri_handler(server, &quick_uri); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return server; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static esp_err_t stop_webserver(httpd_handle_t server) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Stop the httpd server
 | 
					
						
							|  |  |  |     return httpd_stop(server); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void disconnect_handler(void* arg, esp_event_base_t event_base, | 
					
						
							|  |  |  |                                int32_t event_id, void* event_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     httpd_handle_t* server = (httpd_handle_t*) arg; | 
					
						
							|  |  |  |     if (*server) { | 
					
						
							|  |  |  |         ESP_LOGI(TAG, "Stopping webserver"); | 
					
						
							|  |  |  |         if (stop_webserver(*server) == ESP_OK) { | 
					
						
							|  |  |  |             *server = NULL; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             ESP_LOGE(TAG, "Failed to stop http server"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void connect_handler(void* arg, esp_event_base_t event_base, | 
					
						
							|  |  |  |                             int32_t event_id, void* event_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     httpd_handle_t* server = (httpd_handle_t*) arg; | 
					
						
							|  |  |  |     if (*server == NULL) { | 
					
						
							|  |  |  |         ESP_LOGI(TAG, "Starting webserver"); | 
					
						
							|  |  |  |         *server = start_webserver(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void app_main(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static httpd_handle_t server = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ESP_ERROR_CHECK(nvs_flash_init()); | 
					
						
							|  |  |  |     ESP_ERROR_CHECK(esp_netif_init()); | 
					
						
							|  |  |  |     ESP_ERROR_CHECK(esp_event_loop_create_default()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
 | 
					
						
							|  |  |  |      * Read "Establishing Wi-Fi or Ethernet Connection" section in | 
					
						
							|  |  |  |      * examples/protocols/README.md for more information about this function. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     ESP_ERROR_CHECK(example_connect()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Register event handlers to stop the server when Wi-Fi or Ethernet is disconnected,
 | 
					
						
							|  |  |  |      * and re-start it upon connection. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  | #ifdef CONFIG_EXAMPLE_CONNECT_WIFI
 | 
					
						
							|  |  |  |     ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server)); | 
					
						
							|  |  |  |     ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server)); | 
					
						
							|  |  |  | #endif // CONFIG_EXAMPLE_CONNECT_WIFI
 | 
					
						
							|  |  |  | #ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
 | 
					
						
							|  |  |  |     ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &connect_handler, &server)); | 
					
						
							|  |  |  |     ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &disconnect_handler, &server)); | 
					
						
							|  |  |  | #endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // start workers
 | 
					
						
							| 
									
										
										
										
											2024-11-05 14:22:24 -08:00
										 |  |  |     start_workers(); | 
					
						
							| 
									
										
										
										
											2023-04-13 17:05:10 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* Start the server for the first time */ | 
					
						
							|  |  |  |     server = start_webserver(); | 
					
						
							|  |  |  | } |