feat: enhance UDPClient initialization with local port binding and add WebServerHandler for HTTP server management
This commit is contained in:
@@ -19,7 +19,7 @@ UDPClient::~UDPClient() {
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t UDPClient::init() {
|
esp_err_t UDPClient::init(uint16_t local_port) {
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
ESP_LOGW(TAG, "Already initialized");
|
ESP_LOGW(TAG, "Already initialized");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@@ -31,6 +31,23 @@ esp_err_t UDPClient::init() {
|
|||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind to local port if specified
|
||||||
|
if (local_port > 0) {
|
||||||
|
struct sockaddr_in local_addr;
|
||||||
|
memset(&local_addr, 0, sizeof(local_addr));
|
||||||
|
local_addr.sin_family = AF_INET;
|
||||||
|
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
local_addr.sin_port = htons(local_port);
|
||||||
|
|
||||||
|
if (bind(sock_fd_, (struct sockaddr*)&local_addr, sizeof(local_addr)) < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to bind to port %u: errno %d", local_port, errno);
|
||||||
|
::close(sock_fd_);
|
||||||
|
sock_fd_ = -1;
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Bound to local port %u", local_port);
|
||||||
|
}
|
||||||
|
|
||||||
// Set socket to non-blocking mode
|
// Set socket to non-blocking mode
|
||||||
esp_err_t err = set_nonblocking();
|
esp_err_t err = set_nonblocking();
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize UDP socket
|
* @brief Initialize UDP socket
|
||||||
|
* @param local_port Local port to bind to (0 = system assigns port)
|
||||||
* @return ESP_OK on success, error code otherwise
|
* @return ESP_OK on success, error code otherwise
|
||||||
*/
|
*/
|
||||||
esp_err_t init();
|
esp_err_t init(uint16_t local_port = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Configure remote endpoint
|
* @brief Configure remote endpoint
|
||||||
|
|||||||
113
main/network/web_server_handler.cpp
Normal file
113
main/network/web_server_handler.cpp
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#include "web_server_handler.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static const char* TAG = "WebServerHandler";
|
||||||
|
|
||||||
|
WebServerHandler::WebServerHandler() { }
|
||||||
|
|
||||||
|
WebServerHandler::~WebServerHandler() {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t WebServerHandler::start(const std::string& auth_key, uint16_t base_port) {
|
||||||
|
if (server_ != nullptr) {
|
||||||
|
ESP_LOGW(TAG, "Server already running on port %d", current_port_);
|
||||||
|
return current_port_;
|
||||||
|
}
|
||||||
|
|
||||||
|
auth_key_ = auth_key;
|
||||||
|
|
||||||
|
// Try to find a free port
|
||||||
|
uint16_t port = base_port;
|
||||||
|
const uint16_t max_attempts = 100;
|
||||||
|
|
||||||
|
for (uint16_t attempt = 0; attempt < max_attempts; attempt++) {
|
||||||
|
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||||
|
config.server_port = port;
|
||||||
|
config.ctrl_port = port + 1000; // Control port
|
||||||
|
config.max_open_sockets = 7;
|
||||||
|
config.lru_purge_enable = true;
|
||||||
|
|
||||||
|
esp_err_t err = httpd_start(&server_, &config);
|
||||||
|
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
current_port_ = port;
|
||||||
|
ESP_LOGI(TAG, "Web server started successfully on port %d", current_port_);
|
||||||
|
return current_port_;
|
||||||
|
} else if (err == ESP_ERR_HTTPD_ALLOC_MEM) {
|
||||||
|
ESP_LOGE(TAG, "Failed to allocate memory for web server");
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
// Port likely in use, try next port
|
||||||
|
ESP_LOGD(TAG, "Port %d in use, trying next port", port);
|
||||||
|
port++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGE(TAG, "Failed to find free port after %d attempts", max_attempts);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t WebServerHandler::stop() {
|
||||||
|
if (server_ == nullptr) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = httpd_stop(server_);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to stop web server: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
server_ = nullptr;
|
||||||
|
current_port_ = 0;
|
||||||
|
auth_key_.clear();
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Web server stopped");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t WebServerHandler::register_uri_handler(const httpd_uri_t* uri_handler) {
|
||||||
|
if (server_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Server not running, cannot register URI handler");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = httpd_register_uri_handler(server_, uri_handler);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to register URI handler: %s", esp_err_to_name(err));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebServerHandler::validate_auth(const char* query_string) const {
|
||||||
|
if (!query_string || auth_key_.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for "auth=<key>" in query string
|
||||||
|
const char* auth_param = strstr(query_string, "auth=");
|
||||||
|
if (!auth_param) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip "auth="
|
||||||
|
auth_param += 5;
|
||||||
|
|
||||||
|
// Find end of auth value (& or end of string)
|
||||||
|
const char* end = strchr(auth_param, '&');
|
||||||
|
size_t auth_len = end ? (size_t)(end - auth_param) : strlen(auth_param);
|
||||||
|
|
||||||
|
// Compare with stored auth key
|
||||||
|
if (auth_len != auth_key_.length()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strncmp(auth_param, auth_key_.c_str(), auth_len) == 0;
|
||||||
|
}
|
||||||
42
main/network/web_server_handler.h
Normal file
42
main/network/web_server_handler.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "esp_http_server.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
class WebServerHandler {
|
||||||
|
public:
|
||||||
|
WebServerHandler();
|
||||||
|
~WebServerHandler();
|
||||||
|
|
||||||
|
// Start web server, finds a free port starting from base_port
|
||||||
|
// Returns the actual port used, or 0 on failure
|
||||||
|
uint16_t start(const std::string& auth_key, uint16_t base_port = 8080);
|
||||||
|
|
||||||
|
// Stop web server
|
||||||
|
esp_err_t stop();
|
||||||
|
|
||||||
|
// Check if server is running
|
||||||
|
bool is_running() const { return server_ != nullptr; }
|
||||||
|
|
||||||
|
// Get the current port
|
||||||
|
uint16_t get_port() const { return current_port_; }
|
||||||
|
|
||||||
|
// Get the auth key
|
||||||
|
std::string get_auth_key() const { return auth_key_; }
|
||||||
|
|
||||||
|
// Register a URI handler
|
||||||
|
esp_err_t register_uri_handler(const httpd_uri_t* uri_handler);
|
||||||
|
|
||||||
|
// Validate auth key from query string
|
||||||
|
bool validate_auth(const char* query_string) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Prevent copying
|
||||||
|
WebServerHandler(const WebServerHandler&) = delete;
|
||||||
|
WebServerHandler& operator=(const WebServerHandler&) = delete;
|
||||||
|
|
||||||
|
httpd_handle_t server_ = nullptr;
|
||||||
|
uint16_t current_port_ = 0;
|
||||||
|
std::string auth_key_;
|
||||||
|
};
|
||||||
@@ -539,3 +539,23 @@ EventBits_t WifiHandler::wait_for_connection(TickType_t ticks_to_wait) {
|
|||||||
ticks_to_wait
|
ticks_to_wait
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string WifiHandler::get_current_ip() const {
|
||||||
|
esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
|
||||||
|
if (!netif) {
|
||||||
|
ESP_LOGW(TAG, "Failed to get netif handle");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_netif_ip_info_t ip_info;
|
||||||
|
esp_err_t err = esp_netif_get_ip_info(netif, &ip_info);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "Failed to get IP info: %s", esp_err_to_name(err));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert IP address to string
|
||||||
|
char ip_str[16];
|
||||||
|
snprintf(ip_str, sizeof(ip_str), IPSTR, IP2STR(&ip_info.ip));
|
||||||
|
return std::string(ip_str);
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ public:
|
|||||||
uint16_t& ap_count
|
uint16_t& ap_count
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Get current IP address (empty string if not connected)
|
||||||
|
std::string get_current_ip() const;
|
||||||
|
|
||||||
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data);
|
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
Reference in New Issue
Block a user