1. 引言

随着物联网(IoT)和嵌入式人工智能技术的飞速发展,智能机器人正从工业领域走向消费级市场。本文旨在介绍一款基于 乐鑫 ESP32-S3 芯片的 Wi-Fi 智能机器人的设计与实现方案。该方案充分利用了 ESP32-S3 强大的双核处理能力、丰富的外设接口以及内置的 Wi-Fi 功能,构建了一个稳定、高效且易于扩展的机器人控制平台。

2. 系统总体架构

本系统采用 中心控制器 + 分布式执行单元 的架构。ESP32-S3 作为核心主控,负责以下关键任务:

  • 网络通信:创建 Wi-Fi 热点,与上位机(如手机App或PC)建立 TCP/UDP 连接。
  • 指令解析:接收并解析来自上位机的控制指令。
  • 任务调度:协调各个硬件模块(如电机、舵机、传感器)的工作。
  • 状态反馈:采集系统状态(如心跳、传感器数据)并回传给上位机。

整个系统软件架构分为三层:硬件抽象层(HAL)中间件层 和 应用逻辑层。这种分层设计极大地提高了代码的可读性、可维护性和可移植性。

3. 核心硬件平台:ESP32-S3

ESP32-S3 是乐鑫推出的一款高性能、低功耗的 SoC,其主要优势包括:

  • 强大的双核处理器:搭载 Xtensa® 32-bit LX7 双核处理器,主频高达 240 MHz,足以胜任复杂的实时控制和数据处理任务。
  • 丰富的外设接口:提供多达 45 个 GPIO,支持 UART、I2C、SPI、I2S、PWM、ADC、DAC 等多种通信协议,可轻松连接各类传感器和执行器。
  • 内置 Wi-Fi & Bluetooth LE:本设计利用其 2.4 GHz Wi-Fi 功能,无需额外模块即可实现无线控制。
  • 充足的内存资源:集成 512 KB SRAM 和 384 KB ROM,并支持外接 SPI Flash,为复杂应用提供了充足的存储空间。
4.1.网络通信模块

为了满足不同指令对实时性的要求,系统设计了 双通道通信机制

  • TCP 慢速通道(端口 19345)

    • 作用:用于传输对可靠性要求高但对实时性要求不高的指令,如系统配置、加密密钥交换、固件升级、心跳包等。
    • 实现:在 tcp_server_task 任务中,ESP32-S3 作为 TCP 服务器监听连接。一旦有客户端(控制器)接入,便通过 do_tcpsock_recv 函数循环接收数据,并交由 cmd_resolve_low_speed 函数进行解析。该通道启用了 Keep-Alive 机制,确保连接的稳定性,并能及时检测到客户端异常断开。
  • UDP 高速通道(端口 19346)

    • 作用:用于传输对实时性要求极高的运动控制指令,如电机速度、舵机角度等。UDP 协议无连接、开销小,能有效降低控制延迟。
    • 实现:在 udp_server_task 任务中,ESP32-S3 绑定到 UDP 端口并等待数据。只有当 TCP 通道已成功建立(global_tcpsock_handle > 0)时,才允许处理 UDP 数据,这保证了系统的安全性。接收到的数据由 cmd_resolve_high_speed 函数快速解析并立即执行。
4.2. 硬件抽象与驱动层 (board_*.h/c)

代码中大量引用了以 board_ 开头的头文件(如 board_motor.hboard_gpio_out.hboard_uart1.h),这体现了优秀的 硬件抽象层(HAL) 设计思想。

  • 优势:将具体的硬件操作(如设置某个GPIO引脚、驱动电机H桥)封装成统一的函数接口。无论底层硬件如何变更(例如更换电机驱动芯片),只需修改对应的 board_motor.c 文件,而上层的应用逻辑(如 cmd_resolve_high_speed)无需任何改动。
  • 示例:在 app_main 中,通过 motor_init() 初始化电机,之后在指令解析函数中调用类似 set_motor_speed(left, right) 的函数即可控制机器人运动。
4.3. 任务调度与系统维护 (misc_task_demonstrate)

系统创建了一个名为 misc_task_demonstrate 的通用任务,用于处理所有非实时性事务:

  • 消息队列处理:通过 board_queue_recv 从消息队列中获取需要发送回上位机的数据(如传感器读数、执行结果),并通过 do_tcpsock_write 发送出去。
  • 心跳与安全监控:定期调用 hp_check_timeout() 检查与上位机的心跳是否超时。一旦超时,系统会主动关闭 TCP 连接,作为一种安全保护机制,防止设备在失控状态下继续运行。
4.4. Wi-Fi AP 模式与设备发现
  • AP 创建:在 wifi_init_softap 函数中,ESP32-S3 被配置为 SoftAP 模式。其 SSID 并非固定,而是通过读取芯片唯一的 MAC 地址后三位 动态生成(如 R0X-G28-XXYYZZ),这使得在同一环境下可以轻松区分多台设备。
  • IP 配置:AP 的 IP 地址被固定为 192.168.28.1,简化了上位机的连接配置。
  • WPS 支持:代码中包含了 board_ap_wps.h,表明系统预留了 Wi-Fi Protected Setup (WPS) 功能,未来可通过物理按键一键配网,提升用户体验。
5. 系统启动流程 (app_main)

系统上电后,执行以下初始化序列:

  1. Flash 初始化:初始化 NVS(非易失性存储)分区,用于存储配置参数。
  2. 启动验证:执行 board_boot_verify(),可能包含固件校验等安全检查。
  3. Wi-Fi 信道扫描:通过 sta_wifi_scan() 扫描周围 Wi-Fi 环境,选择一个干扰最小的信道(sta_wifi_get_best_channel())来创建自己的 AP,以获得最佳通信质量。
  4. 外设初始化:依次初始化消息队列、串口(UART1)、电机、GPIO、ADC 等。
  5. 任务创建:最后,创建 TCP 服务器、UDP 服务器和杂务处理三个核心任务,系统正式进入运行状态。
6. 总结与展望

本文基于 ESP32-S3 平台,设计并实现了一套结构清晰、功能完备的 Wi-Fi 智能机器人控制系统。该系统通过 双通道网络通信硬件抽象层 和 多任务调度 等关键技术,成功平衡了控制的实时性可靠性可扩展性

未来工作可在此框架上进行以下扩展:

  • 引入 OTA 升级:利用 TCP 通道实现安全的空中固件升级功能。
  • 开发配套 App:为用户提供直观友好的图形化控制界面。
  • 加入语音/AI 功能:利用 ESP32-S3 的 AI 加速指令,实现简单的本地语音识别或图像处理。

此设计不仅适用于轮式机器人,其核心框架同样可以应用于智能小车、机械臂、家庭服务机器人等多种场景,具有很高的实用价值和推广潜力。

代码如下:

/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_mac.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/dns.h"
#include "lwip/ip_addr.h"
#include "lwip/netdb.h"

#include "board_version.h"
#include "board_priority.h"
#include "board_utils.h"
#include "board_xor_enc.h"
#include "board_hp_check.h"

#include "board_sta_wps.h"
#include "board_ap_wps.h"

#include "board_gpio_out.h"
#include "board_gpio_in.h"
#include "board_nvs.h"

#include "board_led.h"
#include "board_motor.h"
#include "board_i2c.h"
#include "board_adc.h"

#include "board_uart0.h"
#include "board_uart1.h"

#include "board_mavlink.h"
#include "board_queue.h"



#include "board_scan_wifi.h"
#include "board_com_check.h"
#include "board_boot_verify.h"

#include "board_servo_ctl.h"
#include "board_protocol.h"



#define EXAMPLE_ESP_WIFI_SSID_PREFIX    "R0X-G28"
#define EXAMPLE_ESP_WIFI_PASS           "12345678"
#define EXAMPLE_ESP_WIFI_CHANNEL        (1)
#define EXAMPLE_MAX_STA_CONN            (2)


#define TCP_SERVER_PORT                 (19345)
#define KEEPALIVE_IDLE                  (5)
#define KEEPALIVE_INTERVAL              (5)
#define KEEPALIVE_COUNT                 (3)

#define UDP_SERVER_PORT                 (19346)


static const char *TAG = "BOT-TAG";


//全局tcp socket通信句柄
static int global_tcpsock_handle = -1;

//全局udp socket通信句柄
static int global_udpsock_handle = -1;



//关闭控制端连接
static void do_tcpsock_close(void)
{
	if(global_tcpsock_handle > 0)
	{
	   ESP_LOGE(TAG, "Shutting down tcp socket...");

       shutdown(global_tcpsock_handle, 0);
       close(global_tcpsock_handle);

       global_tcpsock_handle = -1;
	}
}



//tcp数据发送
static void do_tcpsock_write(uint8_t*buf,uint8_t len)
{

//	printf("buf is:");
//	print0x(buf,len);
//	printf("global_sock_handle=%d\n",global_sock_handle);

	if(global_tcpsock_handle <= 0) return;

    // send() can return less bytes than supplied length.
    // Walk-around for robust implementation.
    int to_write = len;

    while (to_write > 0) {
        int written = send(global_tcpsock_handle, buf + (len - to_write), to_write, 0);
        if (written < 0) {
            ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
            // Failed to retransmit, giving up
            return;
        }
        to_write -= written;
    }

}



//tcp数据接收
static void do_tcpsock_recv(void)
{
    int len;
    unsigned char rx_buffer[128];

    do {
        len = recv(global_tcpsock_handle, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0) {
            ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);

            /**
             * 防止hp_check_timeout主动shutdown和本地的shutdown同时冲突
             * */
    		vTaskDelay(100 / portTICK_PERIOD_MS);//100ms


        } else if (len == 0) {

        	/**
        	 * 用户主动关闭,一般控制器不会主动关闭的
        	 * **/
            ESP_LOGW(TAG, "Connection closed");
        } else {
            //rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
            //ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);

            //for test show hex
            //ESP_LOGI(TAG, "Received %d bytes", len);
            //print0x((unsigned char*)rx_buffer, len);
            cmd_resolve_low_speed(rx_buffer,len);

            /**
             * 这是为什么接收的数据要发送回去才能,连续接受不粘包,速度快,
             * 回一个字节也可以,难道是为了维护心跳,还是说数据发送的应答逻辑才能保证,
             * 数据按照队列,不堵塞,不粘包.
             * 间隙大于300ms不会粘包.
             * */
            //do_sock_write(rx_buffer, len);
            //do_sock_write(rx_buffer, 1);

        }

    } while (len > 0);
}




static void do_udpsock_close(void)
{
    if (global_udpsock_handle > 0) {
        ESP_LOGE(TAG, "Shutting down udp socket and restarting...");
        shutdown(global_udpsock_handle, 0);
        close(global_udpsock_handle);

        global_udpsock_handle = -1;
    }
}



/**
 * tcp服务端,慢速通道,处理维护心跳包,加密因子产生,安全相关操作,读写命令
 * */
static void tcp_server_task(void *pvParameters)
{
    char addr_str[128];
    int addr_family = (int)pvParameters;//ipv4 or ipv6
    int ip_protocol = 0;
    int keepAlive = 1;
    int option = 1;

    int keepIdle = KEEPALIVE_IDLE;
    int keepInterval = KEEPALIVE_INTERVAL;
    int keepCount = KEEPALIVE_COUNT;
    struct sockaddr_storage dest_addr;

#ifdef CONFIG_EXAMPLE_IPV4
    if (addr_family == AF_INET) {
        struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
        dest_addr_ip4->sin_family = AF_INET;
        dest_addr_ip4->sin_port = htons(TCP_SERVER_PORT);
        ip_protocol = IPPROTO_IP;
    }
#endif
#ifdef CONFIG_EXAMPLE_IPV6
    if (addr_family == AF_INET6) {
        struct sockaddr_in6 *dest_addr_ip6 = (struct sockaddr_in6 *)&dest_addr;
        bzero(&dest_addr_ip6->sin6_addr.un, sizeof(dest_addr_ip6->sin6_addr.un));
        dest_addr_ip6->sin6_family = AF_INET6;
        dest_addr_ip6->sin6_port = htons(PORT);
        ip_protocol = IPPROTO_IPV6;
    }
#endif

    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0) {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
    // Note that by default IPV6 binds to both protocols, it is must be disabled
    // if both protocols used at the same time (used in CI)
    setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
#endif

    ESP_LOGI(TAG, "tcp Socket created");

    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0) {
        ESP_LOGE(TAG, "tcp Socket unable to bind: errno %d", errno);
        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
        goto CLEAN_UP;
    }
    ESP_LOGI(TAG, "tcp Socket bound, port %d", TCP_SERVER_PORT);

    /**
     * 当tcp已经有一客户连接成功以后,偶尔还能接收一个客户的连接,
     * 最后需要说明的是:listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen()
     * 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
     * */
    err = listen(listen_sock, 1);
    if (err != 0) {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

    while (1) {

        ESP_LOGI(TAG, "Socket listening");

        //建立握手随机数校验标志
        generate_com_check();

        hp_check_load();


        struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
        socklen_t addr_len = sizeof(source_addr);
        global_tcpsock_handle = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (global_tcpsock_handle < 0) {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        }

        // Set tcp keepalive option
        setsockopt(global_tcpsock_handle, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
        setsockopt(global_tcpsock_handle, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
        setsockopt(global_tcpsock_handle, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
        setsockopt(global_tcpsock_handle, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));

        //no effect
        setsockopt(global_tcpsock_handle, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(int));


        // Convert ip address to string
#ifdef CONFIG_EXAMPLE_IPV4
        if (source_addr.ss_family == PF_INET) {
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
        }
#endif
#ifdef CONFIG_EXAMPLE_IPV6
        if (source_addr.ss_family == PF_INET6) {
            inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
        }
#endif
        ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);


        //接收控制器端的下发数据
        do_tcpsock_recv();

        //用户主动关闭sock
        do_tcpsock_close();


        do_udpsock_close();

    }

CLEAN_UP:
    close(listen_sock);
    vTaskDelete(NULL);
}






/**
 * udp服务器,高速通信,控制器控制命令传输通道(不需要返回的)
 * */
static void udp_server_task(void *pvParameters)
{
    unsigned char rx_buffer[128];
    char addr_str[128];
    int addr_family = (int)pvParameters;//ipv4 or ipv6
    int ip_protocol = 0;
    struct sockaddr_in6 dest_addr;

    while (1) {

        if (addr_family == AF_INET) {
            struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
            /***
             * 接收广播地址:
             * 192.168.28.1
             * 192.168.28.255
             * 255.255.255.255
             */
            dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
            dest_addr_ip4->sin_family = AF_INET;
            dest_addr_ip4->sin_port = htons(UDP_SERVER_PORT);
            ip_protocol = IPPROTO_IP;
        } else if (addr_family == AF_INET6) {
            bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
            dest_addr.sin6_family = AF_INET6;
            dest_addr.sin6_port = htons(UDP_SERVER_PORT);
            ip_protocol = IPPROTO_IPV6;
        }

        global_udpsock_handle = socket(addr_family, SOCK_DGRAM, ip_protocol);
        if (global_udpsock_handle < 0) {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);

            /**因为正在创建的时候网络可能还没有完全连接上,不能退出*/
            vTaskDelay(100 / portTICK_PERIOD_MS);//100ms
            continue;
        }
        ESP_LOGI(TAG, "UDP Socket created");

#if defined(CONFIG_LWIP_NETBUF_RECVINFO) && !defined(CONFIG_EXAMPLE_IPV6)
        int enable = 1;
        lwip_setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable));
#endif

#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
        if (addr_family == AF_INET6) {
            // Note that by default IPV6 binds to both protocols, it is must be disabled
            // if both protocols used at the same time (used in CI)
            int opt = 1;
            setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
            setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
        }
#endif
//        // Set timeout 接收广播数据超时时间
//        struct timeval timeout;
//        timeout.tv_sec = 10;
//        timeout.tv_usec = 0;
//        setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout);

        /**
         * E (106995) BOT-TAG: Socket unable to bind: errno 112
         * */
        int opt = 1;
        setsockopt(global_udpsock_handle, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));


        int err = bind(global_udpsock_handle, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
        if (err < 0) {
            ESP_LOGE(TAG, "udp Socket unable to bind: errno %d", errno);

            close(global_udpsock_handle);
            /**因为正在创建的时候网络可能还没有完全连接上,不能退出*/
            vTaskDelay(100 / portTICK_PERIOD_MS);//100ms
            continue;
        }
        ESP_LOGI(TAG, "udp Socket bound, port %d",UDP_SERVER_PORT);

        struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
        socklen_t socklen = sizeof(source_addr);

#if defined(CONFIG_LWIP_NETBUF_RECVINFO) && !defined(CONFIG_EXAMPLE_IPV6)
        struct iovec iov;
        struct msghdr msg;
        struct cmsghdr *cmsgtmp;
        u8_t cmsg_buf[CMSG_SPACE(sizeof(struct in_pktinfo))];

        iov.iov_base = rx_buffer;
        iov.iov_len = sizeof(rx_buffer);
        msg.msg_control = cmsg_buf;
        msg.msg_controllen = sizeof(cmsg_buf);
        msg.msg_flags = 0;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_name = (struct sockaddr *)&source_addr;
        msg.msg_namelen = socklen;
#endif

        ESP_LOGI(TAG, "udp start Waiting for data");

        /**
         * 重新启动udp server可以清除之前接收的缓存数据,防止对下一个连接影响
         * */
        while (1) {

        	//tcp没有有效连接则处于睡眠等待状态
        	if(global_tcpsock_handle <= 0)
        	{
        		vTaskDelay(100 / portTICK_PERIOD_MS);//100ms
                continue;
        	}

#if defined(CONFIG_LWIP_NETBUF_RECVINFO) && !defined(CONFIG_EXAMPLE_IPV6)
            int len = recvmsg(global_udpsock_handle, &msg, 0);
#else
            int len = recvfrom(global_udpsock_handle, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
#endif
            // Error occurred during receiving
            if (len < 0) {
                ESP_LOGE(TAG, "udp recvfrom failed: errno %d", errno);

                /**
                 * E (615075) BOT-TAG: udp Socket unable to bind: errno 9
                 * */
                //sock关闭后稍等一下,不用立即去创建和bind
                vTaskDelay(100 / portTICK_PERIOD_MS);

                break;
            }
            // Data received
            else {
                // Get the sender's ip address as string
                if (source_addr.ss_family == PF_INET) {
                    inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
#if defined(CONFIG_LWIP_NETBUF_RECVINFO) && !defined(CONFIG_EXAMPLE_IPV6)
                    for ( cmsgtmp = CMSG_FIRSTHDR(&msg); cmsgtmp != NULL; cmsgtmp = CMSG_NXTHDR(&msg, cmsgtmp) ) {
                        if ( cmsgtmp->cmsg_level == IPPROTO_IP && cmsgtmp->cmsg_type == IP_PKTINFO ) {
                            struct in_pktinfo *pktinfo;
                            pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsgtmp);
                            ESP_LOGI(TAG, "dest ip: %s\n", inet_ntoa(pktinfo->ipi_addr));
                        }
                    }
#endif
                } 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; // Null-terminate whatever we received and treat like a string...
                //ESP_LOGI(TAG, "udp Received %d bytes from %s:", len, addr_str);
                //ESP_LOGI(TAG, "%s", rx_buffer);
                //print0x(rx_buffer,len);

                cmd_resolve_high_speed(rx_buffer, len);

//                int err = sendto(global_udpsock_handle, rx_buffer, len, 0, (struct sockaddr *)&source_addr, sizeof(source_addr));
//                if (err < 0) {
//                    ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
//                    break;
//                }
            }
        }

//        if (global_udpsock_handle != -1) {
//            ESP_LOGE(TAG, "Shutting down socket and restarting...");
//            shutdown(global_udpsock_handle, 0);
//            close(global_udpsock_handle);
//        }
    }
    vTaskDelete(NULL);
}





/**
 * 处理所有的非实时任务
 * */
static void misc_task_demonstrate(void* arg)
{
	uint8_t reusebuf[128];
	uint8_t reusebuflen = 0;


    for(;;)
    {

        if(board_queue_recv(reusebuf, &reusebuflen))
        {
           do_tcpsock_write(reusebuf, reusebuflen);
        }


        if(global_tcpsock_handle > 0)
        {
           if(hp_check_timeout())
           {
        	   /**
        	    * tcp关闭触发read函数异常导致udp sock关闭,而不需要主动关闭udp,
        	    * tcp即使多关闭一次也没事。
        	    * */
        	   do_tcpsock_close();
           }
        }


        //100ms
    	vTaskDelay(100 / portTICK_PERIOD_MS);

    }

}



/*
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" leave, AID=%d",
                 MAC2STR(event->mac), event->aid);
    }
}*/

static void wifi_event_handler(void* arg, esp_event_base_t event_base,
		int32_t event_id, void* event_data)
{
	switch (event_id) {
	case WIFI_EVENT_AP_START:
		ESP_LOGI(TAG, "WIFI_EVENT_AP_START");
		break;
	case WIFI_EVENT_AP_STADISCONNECTED:
		{
			ESP_LOGI(TAG, "WIFI_EVENT_AP_STADISCONNECTED");
			wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
			ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
					MAC2STR(event->mac), event->aid);
		}
		break;
	case WIFI_EVENT_AP_STACONNECTED:
		{
			ESP_LOGI(TAG, "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);
		}
		break;
	case WIFI_EVENT_AP_WPS_RG_SUCCESS:
		{
			ESP_LOGI(TAG, "WIFI_EVENT_AP_WPS_RG_SUCCESS");
			wifi_event_ap_wps_rg_success_t *evt = (wifi_event_ap_wps_rg_success_t *)event_data;
			ESP_LOGI(TAG, "station "MACSTR" WPS successful",
					MAC2STR(evt->peer_macaddr));

			ap_wps_set_rg_status(true);
		}
		break;
	case WIFI_EVENT_AP_WPS_RG_FAILED:
		{
			ESP_LOGI(TAG, "WIFI_EVENT_AP_WPS_RG_FAILED");
			wifi_event_ap_wps_rg_fail_reason_t *evt = (wifi_event_ap_wps_rg_fail_reason_t *)event_data;
			ESP_LOGI(TAG, "station "MACSTR" WPS failed, reason=%d",
						MAC2STR(evt->peer_macaddr), evt->reason);
		    ap_wps_restart();
		}
		break;
	case WIFI_EVENT_AP_WPS_RG_TIMEOUT:
		{
			ESP_LOGI(TAG, "WIFI_EVENT_AP_WPS_RG_TIMEOUT");
			ap_wps_restart();
		}
		break;
	default:
		break;
	}
}






static void wifi_init_softap(void)
{
	//sta wifi scanning已经初始化了网络接口和事件!
    //ESP_ERROR_CHECK(esp_netif_init());
    //ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t*mynetif = esp_netif_create_default_wifi_ap();


    /**
     * 设置本地IP和网关成功!
     * */
    esp_netif_ip_info_t ipInfo;

    IP4_ADDR(&ipInfo.ip, 192,168,28,1);
	IP4_ADDR(&ipInfo.gw, 192,168,28,1);
	IP4_ADDR(&ipInfo.netmask, 255,255,255,0);

	esp_netif_dhcps_stop(mynetif);
	esp_netif_set_ip_info(mynetif, &ipInfo);
	esp_netif_dhcps_start(mynetif);


    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));

    /**
     * mac make ssid
     * ESP32 芯片未烧录唯一 chip_id,但设备默认烧录有全球唯一 MAC 地址,可以读取 MAC 地址替代 chip_id。

       ESP32 默认的 MAC 是在 efuse 里,没法改。可以改是 IDF 支持使用用户自定义 MAC,但 efuse 里的 MAC 是物理级存在。可以用以下接口获取此 MAC:
     * */
    //获取全球唯一的MAC地址     MAC地址,一共48 bit,即6个字节。

    //这个是默认mac base,比如softap是+1等等.
    //本地无线网卡地址
    uint8_t local_mac[6];
    //esp_efuse_mac_get_default(mac);
    esp_read_mac(local_mac,ESP_MAC_WIFI_SOFTAP);

    //for(int i=0;i<sizeof(mac);i++)
    //    printf("%2X ",mac[i]);
    //printf("\n");
    uint8_t buf[32] = {0};

    sprintf((char*)buf,"%s-%02X%02X%02X",EXAMPLE_ESP_WIFI_SSID_PREFIX,local_mac[3],local_mac[4],local_mac[5]);

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID_PREFIX,//此处只能常量
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID_PREFIX),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
#ifdef CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT
            .authmode = WIFI_AUTH_WPA3_PSK,
            .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
#else /* CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT */
            .authmode = WIFI_AUTH_WPA2_PSK,
#endif
            .pmf_cfg = {
                    .required = true,
            },
        },
    };
//wifi_config.ap.pairwise_cipher
    wifi_config.ap.ssid_len = strlen((char*)buf);
    memcpy(wifi_config.ap.ssid,buf,wifi_config.ap.ssid_len);


    //没有效果,难道只能通过常量输入?
    wifi_config.ap.channel = sta_wifi_get_best_channel();


    if (strlen(EXAMPLE_ESP_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));

    //禁止modem休眠,降低延迟
    ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));

    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
             buf, EXAMPLE_ESP_WIFI_PASS, wifi_config.ap.channel);

}






void app_main(void)
{
    //ESP_ERROR_CHECK(nvs_flash_init());
    //ESP_ERROR_CHECK(esp_netif_init());
    //ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    //ESP_ERROR_CHECK(example_connect());

	esp_err_t ret;

    //Initialize NVS
	//flash模拟eeprom
    ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);



    board_boot_verify();

    //wifi scanning
    sta_wifi_scan();


    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
    wifi_init_softap();


    board_queue_init();

    uart1_init();

    motor_init();

    //wps key
    gpio_in_init();

    //test_servo();
    //test_gpio_out();
    //test_ledc();
    //test_motor();
    //test_gpio_in();
    //test_mavlink();
    //test_queue();
    //test_xor_enc();
    //test_nvs();
    //board_adc_init_and_start();
    //board_get_adc_value();

#ifdef CONFIG_EXAMPLE_IPV4
    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET, TCP_SERVER_TASK_PRIORITY, NULL);
#endif
//#ifdef CONFIG_EXAMPLE_IPV6
//    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET6, 5, NULL);
//#endif

#ifdef CONFIG_EXAMPLE_IPV4
    xTaskCreate(udp_server_task, "udp_server", 4096, (void*)AF_INET, UDP_SERVER_TASK_PRIORITY, NULL);
#endif
//#ifdef CONFIG_EXAMPLE_IPV6
//    xTaskCreate(udp_server_task, "udp_server", 4096, (void*)AF_INET6, 5, NULL);
//#endif

    xTaskCreate(misc_task_demonstrate, "misc_task_demonstrate", 4096, NULL, MISC_TASK_PRIORITY, NULL);


}

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐