23#if defined(FL_IS_ESP32)
39#include <esp_http_client.h>
44static esp_netif_t* s_netif_ap =
nullptr;
45static bool s_event_loop_initialized =
false;
46static bool s_wifi_initialized =
false;
52static bool initWifiAP() {
58 esp_err_t err = esp_netif_init();
59 if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
60 FL_WARN(
"[NET] esp_netif_init failed: " << esp_err_to_name(err));
65 if (!s_event_loop_initialized) {
66 err = esp_event_loop_create_default();
67 if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
68 FL_WARN(
"[NET] esp_event_loop_create_default failed: " << esp_err_to_name(err));
71 s_event_loop_initialized =
true;
76 s_netif_ap = esp_netif_create_default_wifi_ap();
78 FL_WARN(
"[NET] esp_netif_create_default_wifi_ap failed");
84 if (!s_wifi_initialized) {
85 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
86 err = esp_wifi_init(&cfg);
87 if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
88 FL_WARN(
"[NET] esp_wifi_init failed: " << esp_err_to_name(err));
91 s_wifi_initialized =
true;
95 wifi_config_t wifi_config = {};
100 wifi_config.ap.authmode = WIFI_AUTH_WPA2_PSK;
101 wifi_config.ap.channel = 1;
103 err = esp_wifi_set_mode(WIFI_MODE_AP);
105 FL_WARN(
"[NET] esp_wifi_set_mode failed: " << esp_err_to_name(err));
109 err = esp_wifi_set_config(WIFI_IF_AP, &wifi_config);
111 FL_WARN(
"[NET] esp_wifi_set_config failed: " << esp_err_to_name(err));
115 err = esp_wifi_start();
117 FL_WARN(
"[NET] esp_wifi_start failed: " << esp_err_to_name(err));
130static bool startHttpServer() {
131 if (s_http_server.
get()) {
144 json.
set(
"uptime_ms",
static_cast<int64_t
>(
millis()));
145 json.
set(
"free_heap",
static_cast<int64_t
>(ESP.getFreeHeap()));
146#if defined(FL_IS_ESP_32S3)
147 json.
set(
"chip",
"esp32s3");
148#elif defined(FL_IS_ESP_32C6)
149 json.
set(
"chip",
"esp32c6");
150#elif defined(FL_IS_ESP_32C3)
151 json.
set(
"chip",
"esp32c3");
153 json.
set(
"chip",
"esp32");
169 json.
set(
"num_leds",
static_cast<int64_t
>(10));
170 json.
set(
"brightness",
static_cast<int64_t
>(64));
176 if (!s_http_server->start(
s_net_state.server_port)) {
177 FL_WARN(
"[NET] HTTP server failed to start: " << s_http_server->last_error().c_str());
178 s_http_server.
reset();
192static bool initNetifForLoopback() {
193 esp_err_t err = esp_netif_init();
194 if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
195 FL_WARN(
"[NET] esp_netif_init failed: " << esp_err_to_name(err));
199 if (!s_event_loop_initialized) {
200 err = esp_event_loop_create_default();
201 if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
202 FL_WARN(
"[NET] esp_event_loop_create_default failed: "
203 << esp_err_to_name(err));
206 s_event_loop_initialized =
true;
212static fl::json runHttpGetTest(
const char* url,
const char* test_name) {
214 result.set(
"test", test_name);
216 esp_http_client_config_t config = {};
218 config.timeout_ms = 5000;
220 esp_http_client_handle_t client = esp_http_client_init(&config);
222 result.set(
"passed",
false);
223 result.set(
"error",
"Failed to init HTTP client");
227 esp_err_t err = esp_http_client_perform(client);
229 result.set(
"passed",
false);
231 ss <<
"HTTP request failed: " << esp_err_to_name(err);
233 esp_http_client_cleanup(client);
237 int status = esp_http_client_get_status_code(client);
238 result.set(
"status_code",
static_cast<int64_t
>(status));
241 result.set(
"passed",
false);
243 ss <<
"Expected status 200, got " << status;
246 result.set(
"passed",
true);
249 esp_http_client_cleanup(client);
262 response.set(
"error",
"Failed to start WiFi AP");
266 if (!startHttpServer()) {
268 response.set(
"error",
"Failed to start HTTP server");
285 response.set(
"error",
"Failed to start WiFi AP");
305 snprintf(url_ping,
sizeof(url_ping),
"http://%s:%u/ping", host_ip, port);
306 snprintf(url_data,
sizeof(url_data),
"http://%s:%u/data", host_ip, port);
310 fl::json r = runHttpGetTest(url_ping,
"GET /ping");
312 if (passed.has_value() && passed.value()) {
322 fl::json r = runHttpGetTest(url_data,
"GET /data");
324 if (passed.has_value() && passed.value()) {
346 if (!initNetifForLoopback()) {
348 response.set(
"error",
"Failed to initialize network stack for loopback");
353 if (!startHttpServer()) {
355 response.set(
"error",
"Failed to start HTTP server for loopback test");
366 char url_status[128];
368 snprintf(url_ping,
sizeof(url_ping),
"http://127.0.0.1:%u/ping",
370 snprintf(url_status,
sizeof(url_status),
"http://127.0.0.1:%u/status",
372 snprintf(url_leds,
sizeof(url_leds),
"http://127.0.0.1:%u/leds",
377 fl::json r = runHttpGetTest(url_ping,
"GET /ping (loopback)");
379 if (passed.has_value() && passed.value()) {
389 fl::json r = runHttpGetTest(url_status,
"GET /status (loopback)");
391 if (passed.has_value() && passed.value()) {
401 fl::json r = runHttpGetTest(url_leds,
"GET /leds (loopback)");
403 if (passed.has_value() && passed.value()) {
422 if (s_http_server.
get()) {
423 s_http_server->stop();
424 s_http_server.
reset();
426 FL_WARN(
"[NET] HTTP server stopped");
433 FL_WARN(
"[NET] WiFi AP stopped");
448 response.set(
"success",
false);
449 response.set(
"error",
"Net autoresearch only supported on ESP32");
455 response.set(
"success",
false);
456 response.set(
"error",
"Net autoresearch only supported on ESP32");
464 response.set(
"success",
false);
465 response.set(
"error",
"Net autoresearch only supported on ESP32");
471 response.set(
"success",
false);
472 response.set(
"error",
"Net loopback autoresearch only supported on ESP32");
478 response.set(
"success",
true);
fl::json runNetLoopback()
Run self-contained loopback test: start HTTP server, client GETs localhost.
fl::json startNetClient()
Start WiFi Soft AP only (for net-client mode).
AutoResearchNetState & getNetState()
Get current network autoresearch state.
fl::json runNetClientTest(const char *host_ip, uint16_t port)
Run HTTP client tests against a host server.
static AutoResearchNetState s_net_state
fl::json startNetServer()
Start WiFi Soft AP and HTTP server with test endpoints.
fl::json stopNet()
Stop WiFi AP and HTTP server/client, release all resources.
#define AUTORESEARCH_NET_SSID
#define AUTORESEARCH_NET_PASSWORD
#define AUTORESEARCH_NET_MAX_CONNECTIONS
#define AUTORESEARCH_NET_AP_IP
State for network autoresearch.
const string & body() const
Get request body (for POST/PUT requests)
bool has_body() const
Check if request has a body.
HTTP request object (immutable, passed by const reference)
Response & json(const class json &data)
Set JSON response body with automatic Content-Type header.
static Response ok(const string &body="")
Factory method for 200 OK response.
static Response bad_request(const string &message)
Factory method for 400 Bad Request response.
HTTP response builder (fluent interface)
const char * c_str() const FL_NOEXCEPT
void push_back(const json &value) FL_NOEXCEPT
fl::optional< bool > as_bool() const FL_NOEXCEPT
void set(const fl::string &key, const json &value) FL_NOEXCEPT
static json object() FL_NOEXCEPT
static json array() FL_NOEXCEPT
string str() const FL_NOEXCEPT
pointer get() const FL_NOEXCEPT
void reset(pointer p=nullptr) FL_NOEXCEPT
FastLED's Elegant JSON Library: fl::json
Centralized logging categories for FastLED hardware interfaces and subsystems.
void * memcpy(void *dest, const void *src, size_t n) FL_NOEXCEPT
size_t strlen(const char *s) FL_NOEXCEPT
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NOEXCEPT
void delay(u32 ms, bool run_async=true) FL_NOEXCEPT
Public delay wrapper that keeps bare Arduino delay() preferred after using fl::delay; while still all...
int snprintf(char *buffer, fl::size size, const char *format, const Args &... args) FL_NOEXCEPT
Snprintf-like formatting function that writes to a buffer.
expected< T, E > result
Alias for expected (Rust-style naming)