commit 77eecb30568f1edf16c9eb5ef00f68e9b554572f
parent d233aae48f469cbb1c6a071d2dea3e6d5298acea
Author: Christian Ermann <christianermann@gmail.com>
Date:   Sat, 24 May 2025 19:07:39 -0700
Add Wifi AP and UDP server
Diffstat:
3 files changed, 207 insertions(+), 0 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.16)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(laser-server)
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
@@ -0,0 +1,4 @@
+idf_component_register(
+    SRCS "main.c"
+    INCLUDE_DIRS ""
+)
diff --git a/main/main.c b/main/main.c
@@ -0,0 +1,199 @@
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_mac.h"
+#include "esp_wifi.h"
+#include "esp_event.h"
+#include "esp_log.h"
+#include "nvs_flash.h"
+
+#include "lwip/err.h"
+#include "lwip/sockets.h"
+#include "lwip/sys.h"
+
+#define WIFI_SSID "laserbeam"
+#define WIFI_PASS ""
+#define WIFI_CHANNEL 1
+#define MAX_CONNECTIONS 1
+
+#define PORT 6969
+
+static const char* TAG = "laser-server";
+
+static void wifi_event_handler(
+    void* arg,
+    esp_event_base_t event_base,
+    int32_t event_id,
+    void* event_data
+)
+{
+    if (event_id == WIFI_EVENT_AP_STACONNECTED) {
+        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
+        ESP_LOGI(
+            TAG,
+           "station "MACSTR" join, AID=%d",
+           MAC2STR(event->mac),
+           event->aid
+        );
+    }
+    else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
+        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
+        ESP_LOGI(
+            TAG,
+            "station "MACSTR" join, AID=%d, reason=%d",
+            MAC2STR(event->mac),
+            event->aid,
+            event->reason
+        );
+    }
+    else {
+        ESP_LOGI(TAG, "unhandled wifi event: %ld\n", event_id);
+    }
+}
+
+void wifi_init_softap(void) {
+    esp_netif_create_default_wifi_ap();
+
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+
+    ESP_ERROR_CHECK(esp_event_handler_instance_register(
+        WIFI_EVENT,
+        ESP_EVENT_ANY_ID,
+        &wifi_event_handler,
+        NULL,
+        NULL
+    ));
+
+    wifi_config_t wifi_config = {
+        .ap = {
+            .ssid = WIFI_SSID,
+            .ssid_len = strlen(WIFI_SSID),
+            .channel = WIFI_CHANNEL,
+            .password = WIFI_PASS,
+            .max_connection = MAX_CONNECTIONS,
+            .authmode = WIFI_AUTH_WPA2_PSK,
+            .pmf_cfg = {
+                .required = true,
+            },
+        },
+    };
+    if (strlen(WIFI_PASS) == 0) {
+        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
+    }
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
+    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
+    ESP_ERROR_CHECK(esp_wifi_start());
+
+    ESP_LOGI(
+        TAG,
+        "wifi_init_softap finished. SSID:%s password:%s channel:%d",
+        WIFI_SSID,
+        WIFI_PASS,
+        WIFI_CHANNEL
+    );
+}
+
+static void udp_server_task(void *params) {
+    char rx_buffer[128];
+    char addr_str[128];
+    int addr_family = AF_INET;
+    int ip_protocol = IPPROTO_IP;
+
+    struct sockaddr_in dest_addr;
+
+    while (true) {
+        dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+        dest_addr.sin_family = AF_INET;
+        dest_addr.sin_port = htons(PORT);
+
+        int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
+        if (sock < 0) {
+            ESP_LOGE(TAG, "unable to create socket: errno %d", errno);
+            break;
+        }
+        ESP_LOGI(TAG, "socket created");
+
+        struct timeval timeout;
+        timeout.tv_sec = 10;
+        timeout.tv_usec = 0;
+        setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
+
+        int err = bind(sock, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
+        if (err < 0) {
+            ESP_LOGE(TAG, "socket unable to bind: errno %d", errno);
+            break;
+        }
+        ESP_LOGI(TAG, "socket bound, port %d", PORT);
+
+        struct sockaddr_storage source_addr;
+        socklen_t socklen = sizeof(source_addr);
+
+        while (true) {
+            ESP_LOGI(TAG, "waiting for data");
+
+            int len = recvfrom(
+                sock,
+                rx_buffer,
+                sizeof(rx_buffer) - 1,
+                0,
+                (struct sockaddr*)&source_addr,
+                &socklen
+            );
+            if (len < 0) {
+                ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
+                break;
+            }
+
+            if (source_addr.ss_family == PF_INET) {
+                inet_ntoa_r(
+                    ((struct sockaddr_in*)&source_addr)->sin_addr,
+                    addr_str,
+                    sizeof(addr_str) - 1
+                );
+            }
+            else if (source_addr.ss_family == PF_INET6) {
+                inet6_ntoa_r(
+                    ((struct sockaddr_in6*)&source_addr)->sin6_addr,
+                    addr_str,
+                    sizeof(addr_str) - 1
+                );
+            }
+
+            rx_buffer[len] = 0;
+            ESP_LOGI(TAG, "received %d bytes from %s:", len, addr_str);
+            ESP_LOGI(TAG, "%s", rx_buffer);
+
+            int err = sendto(
+                sock,
+                rx_buffer,
+                len,
+                0,
+                (struct sockaddr*)&source_addr,
+                sizeof(source_addr)
+            );
+            if (err < 0) {
+                ESP_LOGE(TAG, "sendto failed: errno %d", errno);
+                break;
+            }
+        }
+
+        if (sock != -1) {
+            ESP_LOGE(TAG, "shutting down socket and restarting");
+            shutdown(sock, 0);
+            close(sock);
+        }
+    }
+    vTaskDelete(NULL);
+}
+
+
+void app_main(void) {
+    ESP_ERROR_CHECK(nvs_flash_init());
+    ESP_ERROR_CHECK(esp_netif_init());
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+
+    wifi_init_softap();
+
+    xTaskCreate(udp_server_task, "udp_server", 4096, NULL, 5, NULL);
+}