espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.38k stars 7.22k forks source link

Wifi init causing usb host vcp client to suddenly disconnect, IDF5.1 (IDFGH-10910) #12106

Closed colinb2048 closed 10 months ago

colinb2048 commented 1 year ago

Answers checklist.

IDF version.

5.1.0

Operating System used.

Windows

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

None

Development Kit.

Wemos Lolin S2

Power Supply used.

External 5V

What is the expected behavior?

Init wifi and usb, the order of which shouldn't matter, receive UDP packets and write the data to usb vcp client, and vice versa

What is the actual behavior?

USB vcp client suddenly disconnects after wifi init. Frequency of disconnect is random when no data flowing, but when receiving udp packet and sending via usb, almost always suddenly disconnects.

Steps to reproduce.

I've merged the wifi init sta and usb host vcp examples to create bidirectional comms between usb and wifi devices. With no wifi, usb works as expected. If usb installed first, wifi fails to connect to access point. With wifi active, trying to send to usb vcp clients almost always suddenly disconnects

Debug Logs.

No response

More Information.

Source: ``

include

include

include

include "esp_log.h"

include "freertos/FreeRTOS.h"

include "freertos/task.h"

include "freertos/semphr.h"

include "usb/cdc_acm_host.h"

include "usb/vcp_ch34x.hpp"

include "usb/vcp_cp210x.hpp"

include "usb/vcp_ftdi.hpp"

include "usb/vcp.hpp"

include "usb/usb_host.h"

include "driver/uart.h"

include "driver/gpio.h"

include "freertos/event_groups.h"

include "esp_system.h"

include "esp_wifi.h"

include "esp_event.h"

include "esp_log.h"

include "nvs_flash.h"

include "lwip/err.h"

include "lwip/sys.h"

include "lwip/sockets.h"

include "esp_heap_caps.h"

include "esp_pm.h"

using namespace esp_usb;

// Change these values to match your needs

define EXAMPLE_BAUDRATE (19200)

define EXAMPLE_STOP_BITS (0) // 0: 1 stopbit, 1: 1.5 stopbits, 2: 2 stopbits

define EXAMPLE_PARITY (0) // 0: None, 1: Odd, 2: Even, 3: Mark, 4: Space

define EXAMPLE_DATA_BITS (8)

define EXAMPLE_ESP_WIFI_SSID "..."

define EXAMPLE_ESP_WIFI_PASS "..."

define EXAMPLE_ESP_MAXIMUM_RETRY 5

define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK

define WIFI_CONNECTED_BIT BIT0

define WIFI_FAIL_BIT BIT1

define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_BOTH

define CONFIG_ESP_WIFI_PW_ID ""

define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID

define PACKET_RECEIVE_DELAY 10 // Should be less than the send delay, to prevent buffer overflow & mem issues

namespace { struct sockaddr_storage source_addr; static const char TAG = "VCP example"; static SemaphoreHandle_t device_disconnected_sem; char test_str; int udp_receive_len = 0; char udp_rx_buffer[364]; char usb_rx_buffer[364]; int usb_rx_buffer_counter = 0;

/* 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 */

static int s_retry_num = 0;
int sock;
bool wifiConnected = false;
bool usb_connected = false;
bool usb_rx_started = false;
const unsigned int localport = 1998;

char result[20];
TickType_t xStartTime;
const TickType_t xTimeout = pdMS_TO_TICKS(100);
BaseType_t xWasDelayed;

/* Function Prototypes */
// void int_to_char(uint32_t num, char *result);
static bool handle_rx(const uint8_t *data, size_t data_len, void *arg);
static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx);

void udp_Receive_Task(void *pvParameters)
{
    socklen_t socklen = sizeof(source_addr);

    while (1)
    {
        if (udp_receive_len < 1)
        {
            udp_receive_len = recvfrom(sock, udp_rx_buffer, sizeof(udp_rx_buffer), 0, (struct sockaddr *)&source_addr, &socklen);
        }
        vTaskDelay(pdMS_TO_TICKS(20));
    }
}

void HandleDisconnection()
{
    shutdown(sock, 0);
    close(sock);
    wifiConnected = false;
    s_retry_num = 0;
    xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}

void SetupSocket()
{
    int addr_family = 0;
    int ip_protocol = 0;
    addr_family = AF_INET;
    ip_protocol = IPPROTO_IP; // IPPROTO_IP

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

ifdef DEBUG_SERIAL

        Serial1.printf("Unable to create socket: errno %d", errno);

endif

        return;
    }

    // Set timeout
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 10000;
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout);
    int on = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    struct sockaddr_in6 dest_addr;
    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(localport);
    int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err < 0)
    {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);

ifdef DEBUG_SERIAL

        Serial1.println("Unable to bind");

endif

        HandleDisconnection();
    }
    ESP_LOGI(TAG, "Socket bound, port %d", localport);

    test_str = "Socket Bound\n";
    uart_write_bytes(UART_NUM_1, (const char *)test_str, strlen(test_str));
}

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)
    {
        esp_wifi_connect();
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
        {
            test_str = "Try reconnect\n";
            uart_write_bytes(UART_NUM_1, (const char *)test_str, strlen(test_str));

            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        }
        else
        {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG, "connect to the AP fail");
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {           
        SetupSocket();          

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

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

    /* 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.
     */

    wifi_config_t wifi_config = {};
    strcpy((char *)wifi_config.sta.ssid, (char *)EXAMPLE_ESP_WIFI_SSID);
    strcpy((char *)wifi_config.sta.password, (char *)EXAMPLE_ESP_WIFI_PASS);
    wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* 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);
    }
    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");
    }
}

static bool handle_rx(const uint8_t *data, size_t data_len, void *arg)
{
    if (usb_rx_started == false)
    {
        xStartTime = xTaskGetTickCount();
        usb_rx_started = true;
    }

    for (int i = 0; i < data_len; i++)
    {
        usb_rx_buffer[usb_rx_buffer_counter + i] = data[i];
    }
    usb_rx_buffer_counter += data_len;

    return true;
}

/**
 * @brief Device event callback
 *
 * Apart from handling device disconnection it doesn't do anything useful
 *
 * @param[in] event    Device event type and data
 * @param[in] user_ctx Argument we passed to the device open function
 */
static void handle_event(const cdc_acm_host_dev_event_data_t *event, void *user_ctx)
{
    switch (event->type)
    {
    case CDC_ACM_HOST_ERROR:
    {
        ESP_LOGE(TAG, "CDC-ACM error has occurred, err_no = %d", event->data.error);
        test_str = "CDC-ACM error has occurred, err_no\n";
        uart_write_bytes(UART_NUM_1, (const char *)test_str, strlen(test_str));
        usb_connected = false;
        break;
    }
    case CDC_ACM_HOST_DEVICE_DISCONNECTED:
    {
        ESP_LOGI(TAG, "Device suddenly disconnected");

        test_str = "Device suddenly disconnected\n";
        uart_write_bytes(UART_NUM_1, (const char *)test_str, strlen(test_str));
        usb_connected = false;
        break;
    }
    case CDC_ACM_HOST_SERIAL_STATE:
    {
        ESP_LOGI(TAG, "Serial state notif 0x%04X", event->data.serial_state.val);
        break;
    }
    case CDC_ACM_HOST_NETWORK_CONNECTION:
    {
    }
    default:
        break;
    }
}

/**
 * @brief USB Host library handling task
 *
 * @param arg Unused
 */
static void usb_lib_task(void *arg)
{
    while (1)
    {
        // Start handling system events
        uint32_t event_flags;
        usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
        if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
        {
            ESP_ERROR_CHECK(usb_host_device_free_all());
        }
        if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE)
        {
            ESP_LOGI(TAG, "USB: All devices freed");
            // Continue handling USB events to allow device reconnection
        }
    }
}

void usb_connect_task(void *arg)
{
    while (1)
    {
        VCP::register_driver<FT23x>();
        VCP::register_driver<CP210x>();
        VCP::register_driver<CH34x>();

        const cdc_acm_host_device_config_t dev_config = {
            .connection_timeout_ms = 5000, // 5 seconds, enough time to plug the device in or experiment with timeout
            .out_buffer_size = 512,
            .in_buffer_size = 512,
            .event_cb = handle_event,
            .data_cb = handle_rx,
            .user_arg = NULL,
        };

        while (1)
        {
            // You don't need to know the device's VID and PID.
            // Just plug in any device and the VCP service will
            // load correct (already registered) driver for the device

            auto vcp = std::unique_ptr<CdcAcmDevice>(VCP::open(&dev_config));

            if (vcp == nullptr)
            {
                continue;
            }
            vTaskDelay(pdMS_TO_TICKS(100));

            cdc_acm_line_coding_t line_coding = {
                .dwDTERate = EXAMPLE_BAUDRATE,
                .bCharFormat = EXAMPLE_STOP_BITS,
                .bParityType = EXAMPLE_PARITY,
                .bDataBits = EXAMPLE_DATA_BITS,
            };

            ESP_ERROR_CHECK(vcp->line_coding_set(&line_coding));
            ESP_ERROR_CHECK(vcp->set_control_line_state(false, false));
            usb_connected = true;

            while (usb_connected)
            {
                if (udp_receive_len > 7)
                {
                    ESP_ERROR_CHECK(vcp->tx_blocking((uint8_t *)udp_rx_buffer, udp_receive_len));
                    uart_write_bytes(UART_NUM_1, (uint8_t *)udp_rx_buffer, udp_receive_len); // debug logging
                    memset(&udp_rx_buffer, 0, sizeof(udp_rx_buffer));
                    udp_receive_len = 0;
                }
                if (usb_rx_buffer_counter > 0)
                {
                    xWasDelayed = xTaskDelayUntil(&xStartTime, xTimeout);

                    if (xWasDelayed == pdTRUE)
                    {
                        sendto(sock, (const void *)&usb_rx_buffer, usb_rx_buffer_counter, 0, (struct sockaddr *)&source_addr, (socklen_t)sizeof(source_addr));
                        uart_write_bytes(UART_NUM_1, (uint8_t *)usb_rx_buffer, usb_rx_buffer_counter); // debug logging
                        memset(&usb_rx_buffer, 0, sizeof(usb_rx_buffer));
                        usb_rx_buffer_counter = 0;
                        usb_rx_started = false;
                    }
                }

                vTaskDelay(pdMS_TO_TICKS(10));
            }
        }
    }
}

}

/**

DarmorGamz commented 1 year ago

Can you provide your SDKConfig. Do you have Enable USB when phy init?

colinb2048 commented 1 year ago

Thanks for the suggestion, it wasn't, but after enabling 'Enable USB when phy init' i get a compilation error: undefined reference to `phy_bbpll_en_usb'

I've found references to the same compilation error but doesn't appear those threads reached a solution?

roma-jam commented 12 months ago

Hey @colinb2048,

Seems like there could be two problems: either power consumption spikes during WiFi data transfer, which leads to the usb host device disconnection or some interlacing in driver code, which is not so trivial. To proceed, I would like to reproduce the problem and verify all theories.

To be able to do that, may I ask to share the KConfig options as well? (regarding the partitions and other configuration for WiFi). Probably, it will be easy to share the whole project (main, sdkconfig and partitions.csv).

Meanwhile, I'll try to find the problem. Thanks.

roma-jam commented 11 months ago

Hey @colinb2048,

I have tried to reproduce the problem on ESP32-S2 devkit with example code you had provided, but no luck. I tried to send UDP packets from HOST with several length, all seems to correctly received from socket in udp_Receive_Task() in udp_rx_buffer and then transmitted via vcp->tx_blocking() as well.

To proceed, I need to clarify some details.

  1. How are you powering the USB device? How the USB-OTG wire is connected to the Wemos Lolin S2 board?
  2. May I ask you about your UDP packet length? I saw in code that you have a 7-byte length threshold. Probably, it is a some-command length. Do you use variable length packets? If yes, which size?
  3. I checked out that Wemos Lolin S2 has a me6211c33 LDO on board, which has a 500 mA OC. Mine dev board has a sgm2212-33, which has 800 mA OC. Probably, it is important in your case, but without answer on first question it is hard to say. With a USB-OTG we need to power the attachable device as well. Do you have any option to measure the Voltage level during disconnection error?

Thanks.

mzhboy commented 11 months ago

Hi , I'm using micropython/circuitpython , and encountered the same issue. In fact, it's one of two esp32-s2-lolin boards encountered this issue, while the other one (usually) works fine. I also modified en pin circuit on both boards, replace pull-up reisstor(r3) with 1k and C14 with 4.7uF. The input capacitor is too small, so another 220uF capacitor added to VBUS.

REPL works a short while after running code below.

micropython test code

from ssid_passwd import ssid,passwd # ssid and passwd of ap
import esp
esp.osdebug(None)       # turn off vendor O/S debugging messages
#esp.osdebug(0)          # redirect vendor O/S debugging messages to UART(0)

print('------- boot start --------')

def do_connect():
    import network
    import time
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)        
    wlan.config(pm=wlan.PM_PERFORMANCE)
    print('wlan.status()', wlan.status())    
    t1 = time.ticks_us()

    if not wlan.isconnected():
        # print('connecting to network...')
        wlan.connect(ssid, passwd)
        timeout = time.ticks_ms() + 10_000 # timeout 10s
        while not wlan.isconnected() and time.ticks_ms() < timeout:
            time.sleep(1)
            print('wlan.status()', wlan.status())

    if not wlan.isconnected():
        print('error: connect wifi timeout')
        wlan.active( False )
    else :
        print('ip:', wlan.ifconfig()[0], '\trssi:', wlan.status('rssi'))
    print('connect time consume:', time.ticks_diff(time.ticks_us(),t1)/1000, 'ms')

def light_led():
    import re, os
    from machine import Pin, PWM
    str = os.uname().machine
    # esp32-s2
    #searchObj = re.search(r'ESP32-?S2',str)
    if re.search(r'ESP32-?S2',str):
        print('MCU:ESP32-S2')
        #led = Pin(15, Pin.OUT, value=1)
        led = PWM(15, freq=2000, duty=128)
    elif re.search(r'ESP32-UNICORE',str):
        print ("MCU:ESP32-SOLO-01")
        led = PWM(22, freq=2000, duty=228)
    else :
        print('unknown MCU')

def print_vcp():
    import network
    import time
    import gc
    wl = network.WLAN(network.STA_IF)
    tm = time.time()
    print('---print vcp---')
    while True :
        if wl.isconnected():
            print('uptime', time.time() - tm,'\tip:', wl.ifconfig()[0], '\trssi:', wl.status('rssi'))
        else :
            print('uptime', time.time() - tm)
        time.sleep(0.99)

light_led()
do_connect()
print('------- boot end --------')

print_vcp()

uptime 253 ip: 192.168.1.249 rssi: -55

roma-jam commented 11 months ago

Hi @mzhboy,

Thanks for the reporting that! I have tried to reproduce the problem, but with another board and didn't succeeded.

I have a couple of questions, answering to them could help us to move forward.

  1. From which source do you provide the VBUS to the connected device?
  2. How do you supply the Wemos Lolin S2 board? With some USB port or +5V external power?
  3. Is there any option to oscilloscope the VBUS level during encountering the issue?

Thanks!

mzhboy commented 11 months ago

@roma-jam Powering from usb hub, the hub has external PSU, output is 5.2V 3A. Others esp32 boards with onboard usbserial bridge have no issue.

I will investigate this issue with oscilloscope when I have time.

a

roma-jam commented 10 months ago

Hi @mzhboy,

Thanks for sharing additional information! But I am not sure that your issue is connected to the topic-starter issue. Anyway, let me clear some things to understand it better.

  1. In python-example you had provided, you are using the vcp which is the usb-serial bridge on the boards. Do you use the esp32 as a USB Host to be able to plug external USB Device (with using external USB jack port)?
  2. the original issue was about the device, that has a initialized WiFi and uses the esp32s2 option to be able a USB Host for attaching external USB Device. It is little bit unclear, but are you using the usb_host driver from esp-idf components?

In case, that it seems that you are not using the usb_host driver (but please, correct me if I am wrong), seems that you issue 'may be' related but definitely not because you are using the usb_host driver.

Which means, that the problem is not related to usb_host driver itself...

Otherwise, for us to be able to proceed, could you please provide the simplest possible example with usb_host driver usage to reproduce the failure? Thanks.

mzhboy commented 10 months ago

esp32 as a USB Device, as micropython don't support usb_host mode

roma-jam commented 10 months ago

Hi everyone,

Considering the information, that micropython code also has the same problem but doesn't use the usb_host library, it seems that the problem could be deeper, than the layer usb_host library works at.

There is a chance, that this issue has the same nature as this one.

Regarding the problem with micropython, I could suggest to create a new ticket if you still have problems with it.

In case there will be any new information, regarding the usb_host library and working ESP32 as a USB Host, feel free to reopen this ticket. Thanks.

tore-espressif commented 10 months ago

Hello everyone, we revisited the topic again, but we could not reproduce it. So, I'd like to ask for more information:

  1. Which chip version do you use? (command esptool chip_id)
  2. Could you please share dump of your eFuses? (command espefuse.py -p PORT dump)
  3. Could you please try updating to IDF v5.1.2? This https://github.com/espressif/esp-idf/commit/c98aa927f689a23533b4851e1b6acce218bfe4dd commit might fix it for you