diff --git a/components/http_server/CMakeLists.txt b/components/http_server/CMakeLists.txt new file mode 100644 index 0000000..1c13b0b --- /dev/null +++ b/components/http_server/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(INCLUDE_DIRS include REQUIRES util esp_http_server) diff --git a/components/http_server/include/http/server.h b/components/http_server/include/http/server.h new file mode 100644 index 0000000..86360e4 --- /dev/null +++ b/components/http_server/include/http/server.h @@ -0,0 +1,147 @@ +#pragma once + +#include +#include + +#include + +#include + + +#define RESPONSE_BUFFER_LEN 512 + +// +// +// Types +// +// + + +namespace http { + + +enum class Method { get = HTTP_GET, post = HTTP_POST }; + +struct response_t { + response_t() = default; + response_t(const char* str) { + std::size_t respLen = + std::min(strlen(str), static_cast(RESPONSE_BUFFER_LEN)); + std::copy(str, str + respLen, text.begin()); + } + + std::array text = {0}; +}; + +struct handler_t { + Method method; + const char* path; + inplace_function handler; +}; + +struct server_settings_t { + int port = 80; + inplace_function on_error = [] {}; + inplace_function on_not_found = [] {}; +}; + + +} // namespace http + + +// +// +// Implementation details +// +// + + +namespace http::detail { + + +inline auto& get_server() { + static httpd_handle_t server = nullptr; + return server; +} + +template +inline auto& get_handler_storage() { + static std::array, N> handlers; + return handlers; +} + +template +inline auto& get_esp_uri_storage() { + static std::array uris; + return uris; +} + + +// TODO: Currently, the set handler is only valid for GET requests +template +inline void set_handlers_impl(const handler_t (&handlers)[N]) { + auto& uriStorage = get_esp_uri_storage(); + + uriStorage[I] = httpd_uri_t{ + .uri = handlers[I].path, + .method = static_cast(handlers[I].method), + .handler = + [](httpd_req_t* req) { + auto resp = get_handler_storage()[I](); + httpd_resp_send(req, resp.text.data(), HTTPD_RESP_USE_STRLEN); + return ESP_OK; + }, + .user_ctx = nullptr, + }; + + httpd_register_uri_handler(get_server(), &(uriStorage[I])); + + if constexpr (I < N - 1) set_handlers_impl(handlers); +} + + +} // namespace http::detail + + +// +// +// Public API +// +// + + +namespace http { + + +// TODO: Give request information to handlers +template +inline void set_handlers(const handler_t (&handlers)[N]) { + auto& handlerStorage = detail::get_handler_storage(); + std::transform(handlers, handlers + N, handlerStorage.begin(), + [](const auto& handler) { + return handler.handler; + }); + + detail::set_handlers_impl(handlers); +} + +inline void start_server(server_settings_t settings) { + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.server_port = settings.port; + + if (httpd_start(&detail::get_server(), &config) != ESP_OK) { + throw std::runtime_error("Failed to start server"); + } + + // TODO: Set error and not found handlers +} + + +inline void stop_server() { + if (httpd_stop(detail::get_server()) != ESP_OK) { + throw std::runtime_error("Failed to stop server"); + } +} + + +} // namespace http diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 7bd4829..3701e07 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,4 +1,4 @@ set(SOURCES src/main.cpp) set(INCLUDES include) -idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS ${INCLUDES} REQUIRES wifi nvs nanofmt) +idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS ${INCLUDES} REQUIRES wifi nvs nanofmt http_server) diff --git a/main/src/main.cpp b/main/src/main.cpp index f2efe21..2a8d501 100644 --- a/main/src/main.cpp +++ b/main/src/main.cpp @@ -1,45 +1,60 @@ +#include "esp_log.h" + +#include #include #include -#include "esp_log.h" + +void start_ap() { + nvs::init(); + wifi::init(); + + wifi::set_mode(wifi::Mode::soft_ap); + wifi::configure_soft_ap( + "HyperLink", "1234567890", + {.connected = + [](auto mac, auto aid) { + ESP_LOGI("DEBUG - ap callback", "Connected to mac %s", + mac.data()); + }, + .disconnected = + [](auto mac, auto aid) { + ESP_LOGI("DEBUG - ap callback", "Disconnected from mac %s", + mac.data()); + }}); + wifi::start(); +} + +void start_http_server() { + http::start_server({.port = 80, + .on_error = + []() { + // TODO + }, + .on_not_found = + []() { + // TODO + }}); + http::set_handlers({{http::Method::get, "/", + []() { + return http::response_t{ + "Home"}; + }}, + {http::Method::get, "/hello1", + []() { + return http::response_t{"Hello 1"}; + }}, + {http::Method::get, "/hello2", []() { + return http::response_t{"Hello 2"}; + }}}); +} extern "C" void app_main(void) { try { - nvs::init(); - wifi::init(); - - // wifi::set_mode(wifi::Mode::station); - // wifi::configure_station( - // "Vodafone-180C", "giraffen!", - // {.stationStarted = []() { wifi::station_connect(); }, - // .disconnected = [] { wifi::station_connect(); }, - // .gotIp = - // [](auto ip) { - // ESP_LOGI("DEBUG - ip callback ", "Got ip: %s", - // ip.data()); - // }}); - // wifi::start(); - - // wifi::stop(); - // wifi::clear_callbacks(); - // wifi::stop(); - // wifi::deinit(); - - wifi::set_mode(wifi::Mode::soft_ap); - wifi::configure_soft_ap( - "HyperLink", "1234567890", - {.connected = - [](auto mac, auto aid) { - ESP_LOGI("DEBUG - ap callback", "Connected to mac %s", - mac.data()); - }, - .disconnected = - [](auto mac, auto aid) { - ESP_LOGI("DEBUG - ap callback", "Disconnected from mac %s", - mac.data()); - }}); - wifi::start(); + start_ap(); + start_http_server(); } catch (const std::exception& e) { ESP_LOGE("main", "Exception: %s", e.what()); }