Продолжаем работу с нашим сервером HTTP и на данном уроке для ответа клиенту мы будем использовать функционал библиотеки IDF. Благодаря данной библиотеке нам не придётся писать код для многих рутинных процессов, присущих протоколу HTTP.
Схема наша, как и в прошлом занятии, будет состоять только из отладочной платы с контроллером ESP32, подключенной к USB-порту компьютера
Проект мы будем использовать из прошлого урока с именем WIFI_STA_HTTP_SERVER и присвоим ему имя WIFI_STA_HTTP_SERVER_IDF.
Откроем проект в Espressif IDE и в функции app_main файла main.c удалим строку с созданием задачи для сервера, так как она нам будет не нужна и сервер будет запускаться несколько по-другому
xTaskCreate(http_task, "http_task", 4096, NULL, 5, NULL);
В файле http.c также удалим функцию данной задачи http_task вместе с телом, не забывая также о прототипе в заголовочном файле.
Объявления глобальных массивов http_header, index_htm и favicon_ico также удалим, мы их добавим позже, так как в них будут некоторые изменения.
В заголовочном файле http.h удалим подключение библиотек отвечающих за работу со стеком протоколов
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
Подключим библиотеку для работы с протоколом http
1 2 |
#include "esp_log.h" #include <esp_http_server.h> |
Вернёмся в файл http.c и добавим функцию, которая будет запускать наш сервер
1 2 3 4 5 6 7 8 9 |
//------------------------------------------------------------- httpd_handle_t start_webserver(void) { httpd_handle_t server = NULL; ESP_LOGI(TAG, "Error starting server!"); return NULL; } //------------------------------------------------------------- |
Ниже добавим функцию, которая будет сервер останавливать
1 2 3 4 5 6 |
//------------------------------------------------------------- void stop_webserver(httpd_handle_t server) { httpd_stop(server); } //------------------------------------------------------------- |
Объявим прототипы для обеих этих функций в заголовочном файле http.h, а в заголовочном файле wifi.h подключим данный заголовочный файл
1 2 |
#include "esp_wifi.h" #include "http.h" |
В функции wifi_start в файле wifi.c объявим переменную типа указателя на экземпляр HTTP-сервера
1 2 |
esp_err_t ret; static httpd_handle_t server = NULL; |
Далее при регистрации обработчиков передадим в параметрах указатель на нашу переменную
ret = esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect, &server);
...
ret = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, &server);
В функции on_got_ip получим наш указатель и, предварительно убедившись, что сервер не запущен, вызовем функцию запуска сервера
1 2 3 4 5 6 |
gpio_set_level(CONFIG_LED_GPIO, 1); httpd_handle_t* server = (httpd_handle_t*) arg; if (*server == NULL) { ESP_LOGI(TAG, "Starting webserver"); *server = start_webserver(); } |
А в функции on_wifi_disconnect аналогичным образом мы наш сервер остановим
1 2 3 4 5 6 7 |
gpio_set_level(CONFIG_LED_GPIO, 0); httpd_handle_t* server = (httpd_handle_t*) arg; if (*server) { ESP_LOGI(TAG, "Stopping webserver"); stop_webserver(*server); *server = NULL; } |
Вернёмся в файл http.c и в функции start_webserver объявим и инициализируем по умолчанию переменную типа структуры для конфигурации сервера HTTP, а также включим опцию очистки наименее использующихся соединений
1 2 3 4 5 6 |
httpd_handle_t server = NULL; httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.lru_purge_enable = true; ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); |
Выше функции start_webserver добавим функцию-обработчик для обработки события запроса сервером главной страницы (ввод в браузере только адреса сервера)
1 2 3 4 5 6 7 8 9 |
//------------------------------------------------------------- static esp_err_t all_get_handler(httpd_req_t *req) { char* buf; size_t buf_len; return ESP_OK; } //------------------------------------------------------------- |
Узнаем длину заголовка в запросе, увеличив её сразу на 1 для завершения нулём
1 2 3 |
size_t buf_len; buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1; |
Скопируем заголовок в буфер и выведем его в лог, затем освободим память, запрошенную под буфер
1 2 3 4 5 6 7 8 9 |
buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1; if (buf_len > 1) { buf = malloc(buf_len); if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) { ESP_LOGI(TAG, "Found header => Host: %s", buf); } free(buf); } |
Объявим локальный указатель и присвоим ему адрес контекста из входного параметра
1 2 3 4 |
free(buf); } const char* resp_str = (const char*) req->user_ctx; |
Объявим и инициализируем глобальный строковый массив с содержимым главной страницы, причём прямо в текстовом виде без всякого заголовка, заголовки формирует библиотека
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
static const char *TAG = "http"; //------------------------------------------------------------- static const char index_html[] = "<!DOCTYPE html>\ <html lang=\"ru\">\ <head>\ <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\ <title>ESP32</title>\ </head>\ <body>\ <h1 style=\"text-align: center;\">Esp 32<br><br>HTTP Server</h1>\ <p><span style=\"font-family: Times New Roman,Times,serif;\">ESP32 is a single 2.4 GHz Wi-Fi-and-Bluetooth combo chip designed with the TSMC ultra-low-power 40 nm\ technology.</span> </p>\ </body>\ </html>"; //------------------------------------------------------------- |
Вернёмся в функцию all_get_handler и отправим нашу страницу клиенту
1 2 |
const char* resp_str = (const char*) req->user_ctx; httpd_resp_send(req, resp_str, strlen(index_html)); |
Убедимся в отсутствии других запросов, попытавшись прочитать заголовок
1 2 3 4 5 |
httpd_resp_send(req, resp_str, strlen(index_html)); if (httpd_req_get_hdr_value_len(req, "Host") == 0) { ESP_LOGI(TAG, "Request headers lost"); } |
Надо нам теперь наш обработчик как-то зарегистрировать. Для этого ниже него объявим и инициализируем переменную типа структуры идентификатора ресурса (URI)
1 2 3 4 5 6 7 8 |
//------------------------------------------------------------- static const httpd_uri_t all = { .uri = "/", .method = HTTP_GET, .handler = all_get_handler, .user_ctx = (void*)index_html }; //------------------------------------------------------------- |
А в функции start_webserver попытаемся запустить сервер. Если всё нормально, то здесь мы и зарегистрируем наш обработчик и выйдем из функции с возвратом указателя на экземпляр сервера
1 2 3 4 5 6 7 8 |
ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); if (httpd_start(&server, &config) == ESP_OK) { ESP_LOGI(TAG, "Registering URI handlers"); httpd_register_uri_handler(server, &all); return server; } |
А для иконки объявим бинарный глобальный массив
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
</html>"; //------------------------------------------------------------- static const uint8_t favicon_ico[] = { 0x00,0x00,0x01,0x00,0x01,0x00,0x10,0x10,0x00,0x00,0x01,0x00,0x20,0x00,0x68,0x04, 0x00,0x00,0x16,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x20,0x00, 0x00,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf4,0xf4, 0xf4,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0xf4,0xf4,0xf4,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff, 0x0c,0xff,0x00,0xff,0x0c,0xff,0x00,0xff,0x0c,0xff,0x69,0x22,0x1a,0xff,0xf4,0xf4, 0xf4,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22, 0x1a,0xff,0x69,0x22,0x1a,0xff,0x69,0x22,0x1a,0xff,0xf4,0xf4,0xf4,0xff,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,}; //------------------------------------------------------------- |
Ниже функции all_get_handler добавим аналогичный обработчик для запроса иконки, хотя, в принципе, можно было бы объединить всё в одном запросе, но в разных, я думаю, будет понятнее и нагляднее
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//------------------------------------------------------------- static esp_err_t favicon_get_handler(httpd_req_t *req) { char* buf; size_t buf_len; /* Get header value string length and allocate memory for length + 1, * extra byte for null termination */ buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1; if (buf_len > 1) { buf = malloc(buf_len); /* Copy null terminated value string into buffer */ if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) { ESP_LOGI(TAG, "Found header => Host: %s", buf); } free(buf); } return ESP_OK; } //------------------------------------------------------------- |
Пока в теле находится только аналогичный код, а дальше уже будут расхождения.
Сначала установим в заголовке тип передаваемого документа, так как по умолчанию текстовый
1 2 3 4 |
free(buf); } httpd_resp_set_type(req, "image/x-icon"); |
А далее поступим как с обычным текстом, присвоив указатель и передав аналогично пакет клиенту
1 2 3 |
httpd_resp_set_type(req, "image/x-icon"); const char* resp_str = (const char*) req->user_ctx; httpd_resp_send(req, resp_str, sizeof(favicon_ico)); |
И далее аналогично проверим отсутствие других запросов, попытавшись прочитать заголовок
1 2 3 4 5 |
httpd_resp_send(req, resp_str, sizeof(favicon_ico)); if (httpd_req_get_hdr_value_len(req, "Host") == 0) { ESP_LOGI(TAG, "Request headers lost"); } |
Выше функции start_webserver объявим и инициализируем переменную типа структуры идентификатора ресурса (URI) для иконки
1 2 3 4 5 6 7 8 |
//------------------------------------------------------------- httpd_uri_t favicon_uri = { .uri = "/favicon.ico", .method = HTTP_GET, .handler = favicon_get_handler, .user_ctx = (void*)favicon_ico }; //------------------------------------------------------------- |
В функции start_webserver зарегистрируем и этот обработчик
1 2 |
httpd_register_uri_handler(server, &all); httpd_register_uri_handler(server, &favicon_uri); |
В принципе, можем начать проверку.
В конфигураторе у нас ничего не меняется, поэтому можем ничего не настраивать.
Соберём проект, прошьём контроллер и в терминале узнаем сетевой адрес платы
Запустим Wireshark, отфильтровавшись по данному адресу, и попробуем сделать запрос главной страницы в браузере
Всё отлично! Страничка загрузилась и иконка, как видим, тоже.
Посмотрим обмен в Wireshark
Здесь также всё соединяется и разъединяется.
При закрытии браузера происходит окончательное разъединение с портом
Итак, сегодня нам удалось модифицировать наш HTTP-сервер. Теперь он работает с использованием функционала комплекта IDF.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP32 Следующий урок
Недорогие отладочные платы ESP32 можно купить здесь Недорогие отладочные платы ESP32
Недорогие отладочные платы ESP32/ESP32-C3/ESP32-S3 можно купить здесь Недорогие отладочные платы ESP32
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)
Добавить комментарий