基于ESP32S3芯片的机器人控制器设计与实现

一、系统架构概览

整个系统采用 事件驱动 + 多任务协作 的方式,主要包含以下模块:

模块 功能
Wi-Fi 管理 自动连接、断线重连、WPS 配网、NVS 持久化存储
网络通信 TCP(可靠控制) + UDP(低延迟指令)双通道
任务调度 FreeRTOS 任务划分:控制指令、ADC 采集、UART 监听等
外设驱动 ADC(摇杆/电池)、UART1(关节反馈)、GPIO(WPS 按键)
协议封装 自定义机器人控制协议(心跳、动作、基准匹配等)

系统启动后,优先完成 Wi-Fi 连接,随后并行启动多个任务,形成"感知-决策-执行-反馈"闭环

二、Wi-Fi 连接与容错机制

1. 智能配网策略

系统支持两种配网方式:

  • 静态配置 :通过 EXAMPLE_ESP_WIFI_SSIDEXAMPLE_ESP_WIFI_PASS 编译时写入。

  • 动态 WPS:用户按下物理按键触发 WPS,自动获取 AP 凭据。

    // WPS 成功后保存 SSID(密码可选)
    board_write_string("wifi_name", (char*)event->ssid);

2. 无限重连机制

通过设置 EXAMPLE_ESP_MAXIMUM_RETRY = 0xFFFFFFFF,实现 永不放弃重连

复制代码
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
    esp_wifi_connect();
    s_retry_num++;
}

三、TCP/UDP 双通道通信设计

为什么需要双通道?

协议 优势 适用场景
TCP 可靠、有序、保序 心跳包、基准参数查询、关键控制指令
UDP 低延迟、无连接开销 实时动作指令(如舵机角度、电机速度)

1. TCP 客户端:可靠控制通道

  • 连接到固定 IP 192.168.28.1:19345
  • 接收服务器下发的控制指令(如 cmd_echo_resolve() 解析)
  • 断开后自动重建连接

2. UDP 客户端:实时指令通道

  • 发送至同一 IP 的 192.168.28.1:19346
  • 用于发送 ROBOT_MOVE_CTLROBOT_ACTION_CTL 等高频指令

3. 任务协同

  • misc_task_demonstrate 从队列读取指令,优先通过 TCP 发送

四、FreeRTOS 多任务调度

系统创建了四个核心任务,优先级由 board_priority.h 定义:

任务 功能 周期
tcp_udp_client_connect_task 管理网络连接 持续运行
misc_task_demonstrate 处理控制指令、发送心跳 100ms(心跳 300ms)
adc_sample_task 采集摇杆、电池电压 事件驱动(ADC DMA 完成中断唤醒)
uart1_event_task 监听关节反馈数据 事件驱动(UART 中断)

结语

这份机器人控制器代码展现了嵌入式系统开发的范式:以可靠性为核心,兼顾实时性与可维护性。通过 Wi-Fi 双通道通信、WPS 配网、多任务协同等设计,为中小型机器人提供了低成本、高灵活性的控制方案。

代码如下:

复制代码
/* 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/event_groups.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 <arpa/inet.h>
#include <unistd.h>

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>


#include "lwip/dns.h"
#include "lwip/ip_addr.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_servo_ctl.h"
#include "board_map_joint.h"

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

#include "board_protocol.h"

#include "board_robot_ctl_protocol.h"



/* The examples use WiFi configuration that you can set via project configuration menu

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID      "R0X-G28"
#define EXAMPLE_ESP_WIFI_PASS      "12345678"

//连接不上需要一直尝试连接,断开了也需要一直尝试连接
#define EXAMPLE_ESP_MAXIMUM_RETRY  (0xFFFFFFFF)//(10)



/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event, but we only care about two events:
 * - we are connected to the AP with an IP
 * - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

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


#define HOST_IP_ADDR "192.168.28.1"

#define TCP_PORT (19345)
#define UDP_PORT (19346)


#ifndef PIN2STR
#define PIN2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5], (a)[6], (a)[7]
#define PINSTR "%c%c%c%c%c%c%c%c"
#endif


static wifi_config_t wps_ap_creds[MAX_WPS_AP_CRED];
static int s_ap_creds_num = 0;

//wifi重新连接次数累计
static int s_retry_num = 0;

/**
 * -1:断开
 * 0:正在连接
 * 1:连接上
 * */
int global_wifi_connect = -1;

//udp服务端地址
static struct sockaddr_in dest_addr;

//全局udp服务端连接句柄
static int global_udp_sock = -1;

//全部tcp服务端连接句柄
static int global_tcp_sock = -1;

//全部tcp客户端连接状态
static int global_tcp_connect = 0;

//获取机器的benchmark状态
int global_is_got_benchmark = 0;


//采集的多通道adc
static int adc_voltages[SAMPLE_CHANNELS_SIZE] = {0};




static void do_udpsock_write(uint8_t*buf,uint8_t len)
{

	if(global_udp_sock <= 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 = sendto(global_udp_sock, buf + (len - to_write), to_write, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
        if (written < 0) {
            ESP_LOGE(TAG, "udp Error occurred during sending: errno %d", errno);
            // Failed to retransmit, giving up
            return;
        }
        to_write -= written;
    }
}




static void udp_client_close(void)
{

	if (global_udp_sock > 0) {

		ESP_LOGE(TAG, "udp Shutting down socket and restarting...");
		shutdown(global_udp_sock, 0);
		close(global_udp_sock);

		global_udp_sock = -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_tcp_sock <= 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_tcp_sock, buf + (len - to_write), to_write, 0);
        if (written < 0) {
            ESP_LOGE(TAG, "tcp Error occurred during sending: errno %d", errno);
            // Failed to retransmit, giving up
            return;
        }
        to_write -= written;
    }

}



static void tcp_client_close(void)
{

    if (global_tcp_sock > 0) {
        ESP_LOGE(TAG, "tcp Shutting down socket and restarting...");
        shutdown(global_tcp_sock, 0);
        close(global_tcp_sock);

        global_tcp_sock = -1;
    }

}




static void udp_client_init(void *pvParameters)
{
    char host_ip[] = HOST_IP_ADDR;
    int addr_family = 0;
    int ip_protocol = 0;


#if defined(CONFIG_EXAMPLE_IPV4)
	dest_addr.sin_addr.s_addr = inet_addr(host_ip);
	dest_addr.sin_family = AF_INET;
	dest_addr.sin_port = htons(UDP_PORT);
	addr_family = AF_INET;
	ip_protocol = IPPROTO_IP;

#elif defined(CONFIG_EXAMPLE_IPV6)

	struct sockaddr_in6 dest_addr = { 0 };
	inet6_aton(HOST_IP_ADDR, &dest_addr.sin6_addr);
	dest_addr.sin6_family = AF_INET6;
	dest_addr.sin6_port = htons(PORT);
	dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
	addr_family = AF_INET6;
	ip_protocol = IPPROTO_IPV6;

#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
	struct sockaddr_storage dest_addr = { 0 };
	ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_DGRAM, &ip_protocol, &addr_family, &dest_addr));
#endif

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

		vTaskDelay(100 / portTICK_PERIOD_MS);//100ms

		return;
	}


	ESP_LOGI(TAG, "udp Socket created, sending to %s:%d", HOST_IP_ADDR, UDP_PORT);


}




/**
 * 处理所有的非实时任务(慢速指令),遥控下发,周期杂项任务
 * */
static void misc_task_demonstrate(void* arg)
{
	uint8_t reusebuf[128];
	uint8_t reusebuflen = 0;

	uint32_t base_cnt = 0;

    uint8_t*sendata = NULL;
    uint8_t sendatalen = 0;


    for(;;)
    {

    	//100ms loop
        if(board_queue_recv(reusebuf, &reusebuflen))
        {
           do_tcpsock_write(reusebuf, reusebuflen);
        }


        //300ms loop
        if(!(base_cnt % 3) && global_tcp_connect)
        {

           if(!global_is_got_benchmark)
           {
        	   sendata = ROBOT_GET_BENCHMARK(sendatalen);
           }
           else
           {
               sendata = ROBOT_HEARTPACKET_CTL(sendatalen);
           }

           board_queue_send(sendata, sendatalen);

        }


//        //for test udp
//        if(!(base_cnt % 5) && global_tcp_connect)
//        {
//        	sendata = ROBOT_MOVE_CTL(1,30,2,90,sendatalen);
//            print0x(sendata,sendatalen);
//        	//board_queue_send(sendata, sendatalen);
//        	do_udpsock_write(sendata, sendatalen);
//
//        	vTaskDelay(10 / portTICK_PERIOD_MS);
//
//        	uint8_t servo_data[] = {0x12,0x88,0x57,0x99};
//        	sendata = ROBOT_ACTION_CTL(1000,servo_data,sizeof(servo_data),sendatalen);
//        	print0x(sendata,sendatalen);
//        	//board_queue_send(sendata, sendatalen);
//        	do_udpsock_write(sendata, sendatalen);
//
//        }


        base_cnt++;

        //100ms
    	vTaskDelay(100 / portTICK_PERIOD_MS);

    }

}





/**
 * adc采集任务,比如按键,摇杆,电池等
 * */
static  void adc_sample_task(void* arg)
{

	int i;
	esp_err_t ret;
	uint32_t ret_num = 0;
	//读取一帧采集数据,每四个字节解析一次
	uint8_t result[EXAMPLE_READ_LEN] = {0xCC};

	//每个通道一次采集的数据就是 EXAMPLE_READ_LEN / 4 / SAMPLE_CHANNELS_SIZE
    uint32_t chan_sum[SAMPLE_CHANNELS_SIZE] = {0x00};
    uint32_t chan_sum_cnt[SAMPLE_CHANNELS_SIZE] = {0x00};


    /**
     * 注意s_task_handle必须保证初始化和执行读取在同一个线程中,
     * 否则ulTaskNotifyTake接收不到事件
     * */
    board_adc_init_and_start();



	while(1)
	{
    	//wifi没有连接上就处于空闲等待状态
    	if(global_wifi_connect <= 0)
    	{
    		vTaskDelay(100 / portTICK_PERIOD_MS);//100ms

            continue;
    	}

		ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

		while (1)
		{
			ret = adc_continuous_read(adc_handle, result, EXAMPLE_READ_LEN, &ret_num, 0);

			if(ret == ESP_OK)
			{
				for (i = 0; i < ret_num; i += SOC_ADC_DIGI_RESULT_BYTES)
				{
					//每四个字节一个结果,结果中包含channel和data
					adc_digi_output_data_t *p = (void*)&result[i];
					uint32_t chan_num = EXAMPLE_ADC_GET_CHANNEL(p);
					uint32_t data = EXAMPLE_ADC_GET_DATA(p);

					/* Check the channel number validation, the data is invalid if the channel num exceed the maximum channel */
					if (chan_num < SOC_ADC_CHANNEL_NUM(EXAMPLE_ADC_UNIT))
					{
						chan_sum[chan_num - 1] += data;
						chan_sum_cnt[chan_num - 1]++;
					}
				}

				////////一次采集读取完成
				for(i = 0;i < SAMPLE_CHANNELS_SIZE;i++)
				{
					adc_cali_raw_to_voltage(adc_cali_handel[i],
							chan_sum[i] / chan_sum_cnt[i],
							&adc_voltages[i]);

					//复位累加计和
					chan_sum[i] = chan_sum_cnt[i] = 0;
				}

//				for(i = 0;i < SAMPLE_CHANNELS_SIZE;i++)
//				{
//					printf("adc_voltages[%d]=%d\n",i,adc_voltages[i]);
//				}

			}
			else if (ret == ESP_ERR_TIMEOUT)
			{
				break;
			}
		}

	}

}






static void uart1_event_task(void *pvParameters)
{
    uart_event_t event;
    size_t buffered_size;
    uint8_t* dtmp = (uint8_t*) malloc(RD_BUF_SIZE);

    for(;;) {
        //Waiting for UART event.
        if(xQueueReceive(uart1_queue, (void * )&event, (TickType_t)portMAX_DELAY)) {

            //bzero(dtmp, RD_BUF_SIZE);
            //ESP_LOGI(UART1_TAG, "uart[%d] event:", EX_UART_NUM);

            switch(event.type) {
                //Event of UART receving data
                /*We'd better handler data event fast, there would be much more data events than
                other types of events. If we take too much time on data event, the queue might
                be full.*/
                case UART_DATA:

                    //ESP_LOGI(UART1_TAG, "[UART DATA]: %d", event.size);
                    uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);
                    //ESP_LOGI(UART1_TAG, "[DATA EVT]:");
                    //uart_write_bytes(EX_UART_NUM, (const char*) dtmp, event.size);
                    print0x(dtmp,event.size);

                    break;
                //Event of HW FIFO overflow detected
                case UART_FIFO_OVF:
                    ESP_LOGI(UART1_TAG, "hw fifo overflow");
                    // If fifo overflow happened, you should consider adding flow control for your application.
                    // The ISR has already reset the rx FIFO,
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(EX_UART_NUM);
                    xQueueReset(uart1_queue);
                    break;
                //Event of UART ring buffer full
                case UART_BUFFER_FULL:
                    ESP_LOGI(UART1_TAG, "ring buffer full");
                    // If buffer full happened, you should consider increasing your buffer size
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(EX_UART_NUM);
                    xQueueReset(uart1_queue);
                    break;
                //Event of UART RX break detected
                case UART_BREAK:
                    ESP_LOGI(UART1_TAG, "uart rx break");
                    break;
                //Event of UART parity check error
                case UART_PARITY_ERR:
                    ESP_LOGI(UART1_TAG, "uart parity error");
                    break;
                //Event of UART frame error
                case UART_FRAME_ERR:
                    ESP_LOGI(UART1_TAG, "uart frame error");
                    break;
                //UART_PATTERN_DET
                case UART_PATTERN_DET:
                    uart_get_buffered_data_len(EX_UART_NUM, &buffered_size);
                    int pos = uart_pattern_pop_pos(EX_UART_NUM);
                    ESP_LOGI(UART1_TAG, "[UART PATTERN DETECTED] pos: %d, buffered size: %d", pos, buffered_size);
                    if (pos == -1) {
                        // There used to be a UART_PATTERN_DET event, but the pattern position queue is full so that it can not
                        // record the position. We should set a larger queue size.
                        // As an example, we directly flush the rx buffer here.
                        uart_flush_input(EX_UART_NUM);
                    } else {
                        uart_read_bytes(EX_UART_NUM, dtmp, pos, 100 / portTICK_PERIOD_MS);
                        uint8_t pat[PATTERN_CHR_NUM + 1];
                        memset(pat, 0, sizeof(pat));
                        uart_read_bytes(EX_UART_NUM, pat, PATTERN_CHR_NUM, 100 / portTICK_PERIOD_MS);
                        ESP_LOGI(UART1_TAG, "read data: %s", dtmp);
                        ESP_LOGI(UART1_TAG, "read pat : %s", pat);
                    }
                    break;
                //Others
                default:
                    ESP_LOGI(UART1_TAG, "uart event type: %d", event.type);
                    break;
            }
        }
    }

    free(dtmp);
    dtmp = NULL;
    vTaskDelete(NULL);
}




/**tcp & udp网络通信双通道管理和控制
 * */
static void tcp_udp_client_connect_task(void *pvParameters)
{
    unsigned char rx_buffer[128];
    char host_ip[] = HOST_IP_ADDR;
    int addr_family = 0;
    int ip_protocol = 0;
    int len;


    while (1) {

    	//wifi没有连接上就处于空闲等待状态
    	if(global_wifi_connect <= 0)
    	{
    		vTaskDelay(100 / portTICK_PERIOD_MS);//100ms

            continue;
    	}

    	global_tcp_connect = 0;
    	global_is_got_benchmark = 0;

    	udp_client_init(pvParameters);


#if defined(CONFIG_EXAMPLE_IPV4)

        struct sockaddr_in dest_addr;
        inet_pton(AF_INET, host_ip, &dest_addr.sin_addr);
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(TCP_PORT);
        addr_family = AF_INET;
        ip_protocol = IPPROTO_IP;

#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
        struct sockaddr_storage dest_addr = { 0 };
        ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr));
#endif

        global_tcp_sock =  socket(addr_family, SOCK_STREAM, ip_protocol);
        if (global_tcp_sock < 0) {
            ESP_LOGE(TAG, "tcp nable to create socket: errno %d", errno);

            vTaskDelay(100 / portTICK_PERIOD_MS);//100ms

            continue;
        }
        ESP_LOGI(TAG, "tcp Socket created, connecting to %s:%d", host_ip, TCP_PORT);

        int err = connect(global_tcp_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
        if (err != 0) {
            ESP_LOGE(TAG, "tcp Socket unable to connect: errno %d", errno);

            tcp_client_close();

            vTaskDelay(100 / portTICK_PERIOD_MS);//100ms

            continue;
        }

        global_tcp_connect = 1;
        ESP_LOGI(TAG, "tcp Successfully connected");


        while (1) {

//            int err = send(sock, payload, strlen(payload), 0);
//            if (err < 0) {
//                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
//                break;
//            }

            len = recv(global_tcp_sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
            // Error occurred during receiving
            if (len < 0) {
                ESP_LOGE(TAG, "tcp recv failed: errno %d", errno);
                break;
            }
            else if(0 == len)
            {
                ESP_LOGE(TAG, "tcp server force closed!");
                break;
            }
            // Data received
            else {
                //rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
                //ESP_LOGI(TAG, "tcp Received %d bytes from %s:", len, host_ip);
                //ESP_LOGI(TAG, "%s", rx_buffer);

            	cmd_echo_resolve(rx_buffer,len);
            }
        }


        global_tcp_connect = 0;


        tcp_client_close();

        udp_client_close();

    }

}





static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{

    //普通连接
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
    	global_wifi_connect = -1;

        esp_wifi_connect();

    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
    	if(!board_gpio_get_wps_key_status())
    	{
			/**
			 * 这种基于事件的重连方式不错
			 * */
			if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {

				global_wifi_connect = 0;

				esp_wifi_connect();
				s_retry_num++;
				ESP_LOGI(TAG, "retry to connect to the AP");


			} else {

				global_wifi_connect = -1;

				xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);

			}

			ESP_LOGI(TAG,"connect to the AP fail");

    	}
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED)
    {
    	//此处获取bssid,wifi ap's wlan mac
    	wifi_event_sta_connected_t* event = (wifi_event_sta_connected_t*) event_data;
        //printf("get ap's bssid:");
        //print0x(event->bssid,sizeof(event->bssid));

        uint16_t chs = Crc_Calculate(event->bssid, sizeof(event->bssid));
        set_send_mavlink_protocol_crc(chs >> 8,chs & 0xFF);

        //printf("ap's mac chs=%04X\n",chs);

        /**
         * 很奇怪在WIFI_EVENT_STA_WPS_ER_SUCCESS中获取不到wifi ssid和passwd,
         * 反正wps配对成功后会连接成功,在此时保存连接信息即可,可惜没有passwd,不过使用公用的也可以
         * */
        //save wifi info
        //board_write_string("wifi_name", (char*)event->ssid);
        //board_write_string("wifi_passwd", EXAMPLE_ESP_WIFI_PASS);

        char str[32] = {0};
        size_t str_len = sizeof(str);
        esp_err_t err;

        err = board_read_string("wifi_name", str, &str_len);
        if(err == ESP_OK && str_len > 0)
        {
        	str[str_len] = 0;

        	//和已经保存的ssid不一致才需要重新保存
            if(strcmp((char*)event->ssid,(char*)str))
            {
            	board_write_string("wifi_name", (char*)event->ssid);

            	printf("wifi name differ,need save!\n");
            }
        }
        else
        {
        	//没有保存ssid就立即保存
        	if(ESP_ERR_NVS_NOT_FOUND == err)
        	{
        		board_write_string("wifi_name", (char*)event->ssid);

        		printf("wifi name not found,need save!\n");
        	}
        }

    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));

        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);

        global_wifi_connect = 1;

    }



	//wps连接
    switch (event_id) {

        case WIFI_EVENT_STA_WPS_ER_SUCCESS:
            ESP_LOGI(TAG, "WIFI_EVENT_STA_WPS_ER_SUCCESS");
            {
                wifi_event_sta_wps_er_success_t *evt =
                    (wifi_event_sta_wps_er_success_t *)event_data;
                int i;

                if (evt) {
                    s_ap_creds_num = evt->ap_cred_cnt;
                    for (i = 0; i < s_ap_creds_num; i++) {
                        memcpy(wps_ap_creds[i].sta.ssid, evt->ap_cred[i].ssid,
                               sizeof(evt->ap_cred[i].ssid));
                        memcpy(wps_ap_creds[i].sta.password, evt->ap_cred[i].passphrase,
                               sizeof(evt->ap_cred[i].passphrase));
                    }
                    /* If multiple AP credentials are received from WPS, connect with first one */
                    ESP_LOGI(TAG, "Connecting to SSID: %s, Passphrase: %s",
                             wps_ap_creds[0].sta.ssid, wps_ap_creds[0].sta.password);
                    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wps_ap_creds[0]) );
                }


                //此处已经说明只有一个ap wps则没有event data,直接连接即可
                /*
                 * If only one AP credential is received from WPS, there will be no event data and
                 * esp_wifi_set_config() is already called by WPS modules for backward compatibility
                 * with legacy apps. So directly attempt connection here.
                 */
                ESP_ERROR_CHECK(esp_wifi_wps_disable());

                sta_wps_set_er_status(true);

                esp_wifi_connect();

            }
            break;
        case WIFI_EVENT_STA_WPS_ER_FAILED:

            ESP_LOGI(TAG, "WIFI_EVENT_STA_WPS_ER_FAILED");
            sta_wps_restart();

            break;
        case WIFI_EVENT_STA_WPS_ER_TIMEOUT:
            ESP_LOGI(TAG, "WIFI_EVENT_STA_WPS_ER_TIMEOUT");
            sta_wps_restart();

            break;
        case WIFI_EVENT_STA_WPS_ER_PIN:

            ESP_LOGI(TAG, "WIFI_EVENT_STA_WPS_ER_PIN");
            /* display the PIN code */
            wifi_event_sta_wps_er_pin_t* event = (wifi_event_sta_wps_er_pin_t*) event_data;
            printf("display pincode=%s\n",(char*)event->pin_code);

            break;
        default:
            break;
    }
}




void wifi_init_sta(void)
{
    s_wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
            /* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (pasword len => 8).
             * If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value
             * to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to
             * WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards.
             */
            .threshold.authmode = WIFI_AUTH_WPA3_PSK,
            .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
            .sae_h2e_identifier = "",
        },
    };



    /**
     * 获取已经保存的wifi&password
     * */
    char str[32] = {0};
    size_t str_len = sizeof(str);
    esp_err_t err;

    err = board_read_string("wifi_name", str, &str_len);
    if(err == ESP_OK && str_len > 0)
    {
    	str[str_len] = 0;
    	strcpy((char*)wifi_config.sta.ssid,str);

    	printf("get saved wifi_name=%s\n",str);

        memset(str,0,sizeof(str));
        str_len = sizeof(str);
    }


    err = board_read_string("wifi_passwd", str, &str_len);
    if(err == ESP_OK && str_len > 0)
    {
    	str[str_len] = 0;
    	strcpy((char*)wifi_config.sta.password,str);

    	printf("get saved wifi_passwd=%s\n",str);
    }



    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );

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

    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    //不需要堵塞等候连接!不过没有连接堵塞在这里也不搓,也没必要后续初始化和任务启动,只需要wps已经启动即可
    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT) {
//        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
//                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 wifi_config.sta.ssid, wifi_config.sta.password);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

}




void app_main(void)
{

	/**
	 * 可以屏蔽app启动后log,但是启动前包括bootloader,freertos的还需要menuconfig
	 * */
	//esp_log_level_set(TAG, ESP_LOG_NONE);
    //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());

    //Initialize NVS
	//flash模拟eeprom
    esp_err_t 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();

    //wps key start
    gpio_in_init();

    //uart1关节采集
    uart1_init();

    //wifi一直连接直到成功为止,会堵塞后面的
    wifi_init_sta();


    board_queue_init();


//#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(tcp_udp_client_connect_task, "tcp_udp_client_connect_task", 4096, NULL, TCP_CLIENT_TASK_PRIORITY, NULL);

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

    xTaskCreate(adc_sample_task, "adc_sample_task", 4096, NULL, ADC_SAMPLE_TASK_PRIORITY, NULL);

    xTaskCreate(uart1_event_task, "uart1_event_task", 4096, NULL, UART1_EVENT_TASK_PRIORITY, NULL);


}
相关推荐
deephub2 小时前
从贝叶斯视角解读Transformer的内部几何:mHC的流形约束与大模型训练稳定性
人工智能·深度学习·神经网络·transformer·残差链接
CoderJia程序员甲2 小时前
2025年度总结之-如何构建 2025 专属的 GitHub AI 项目情报库
人工智能·ai·大模型·github·ai教程
专业开发者2 小时前
2021 年蓝牙市场新趋势与预测更新报告
物联网
阿正的梦工坊2 小时前
VisualTrap:一种针对 GUI Agent 的隐蔽视觉后门攻击
人工智能·深度学习·机器学习·语言模型·自然语言处理
渡我白衣2 小时前
从直觉到公式——线性模型的原理、实现与解释
人工智能·深度学习·神经网络·机器学习·计算机视觉·自然语言处理·caffe
大模型任我行2 小时前
美团:统一生成理解多模态大模型
人工智能·计算机视觉·语言模型·论文笔记
不一样的故事1262 小时前
1. 公司质量体系的维护与申办监管•
大数据·运维·人工智能
向量引擎小橙2 小时前
数字孪生进阶版:“全脑城市”如何改变我们的生活
大数据·人工智能·深度学习·生活·集成学习
白日做梦Q2 小时前
图像去噪算法对比:传统方法与深度学习方法
人工智能·深度学习·算法