杂记:使用USB连接PC和ESP32S3,并互相传输数据


尽管esp32s3的特色似乎是其对Wifi和蓝牙的强大支持能力,但在实际使用中随着其其他任务对性能的占用,无线的数据传输愈发不稳定。使用USB连接是一个可能的解决方案。

https://docs.espressif.com/projects/esp-idf/zh_CN/v4.4.8/esp32s3/api-reference/peripherals/usb_device.html

ESP32S3的USB驱动程序允许其借助于TinyUSB堆栈上作为CDC-ACM的USB串行设备使用。


本文使用了ESP32-S3-WROOM-1开发板,本身具备USB连接器。如果使用了带有DATA+/DATA-引脚的开发板,可能需要参照文档连接:


本文的目的是将ESP32-S3作为USB串行设备使用,因此直接参照代码示例。本文使用了4.4.8版本的ESPIDF。

TUSB_SERIAL_DEVICE

https://github.com/espressif/esp-idf/tree/v4.4.8/examples/peripherals/usb/tusb_serial_device

示例中app_main 为主函数。


首先对tinyusb初始化,使用的函数为tinyusb.h中的esp_err_t tinyusb_driver_install(const tinyusb_config_t *config) ,其输入是tinyusb_config_t 类型的结构体的指针。其定义类似于:

1
2
3
4
5
typedef struct {
tusb_desc_device_t *descriptor; /*!< Pointer to a device descriptor */
const char **string_descriptor; /*!< Pointer to an array of string descriptors */
bool external_phy; /*!< Should USB use an external PHY */
} tinyusb_config_t;

在示例中使用默认初始化。


然后使用tusb_cdc_acm.h 中的esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg) 函数对CDCACM进行初始化。其输入是tinyusb_config_cdcacm_t结构体的指针。

其定义类似于:

1
2
3
4
5
6
7
8
9
typedef struct {
tinyusb_usbdev_t usb_dev; // 要设置的 USB 设备
tinyusb_cdcacm_itf_t cdc_port; // CDC 端口
size_t rx_unread_buf_sz; // 一次可以传输给 ACM 的数据量
tusb_cdcacm_callback_t callback_rx; // 接收数据回调函数指针
tusb_cdcacm_callback_t callback_rx_wanted_char; // 接收到指定字符时的回调函数指针
tusb_cdcacm_callback_t callback_line_state_changed; // 线路状态变化时的回调函数指针
tusb_cdcacm_callback_t callback_line_coding_changed; // 线路编码变化时的回调函数指针
} tinyusb_config_cdcacm_t;

1
2
3
4
tinyusb_cdcacm_register_callback(
TINYUSB_CDC_ACM_0,
CDC_EVENT_LINE_STATE_CHANGED,
&tinyusb_cdc_line_state_changed_callback)

是另一种注册callback的方式。


因此程序从USB接受数据,并使用tinyusb_cdc_rx_callback 函数读取并重新发送相同的数据。

使用python程序发送和接受数据


由于常常需要都接受的数据进行进一步的处理,尝试使用python接受数据。使用pyserial库可以简单实现。串口号可以通过设备管理器查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import serial
import time

# 配置参数
PORT = 'COM4' # Windows 上的串口号
BAUDRATE = 115200 # 确保此处的波特率与你的 ESP32-S3 配置匹配
TIMEOUT = 1 # 读取操作的超时时间(秒)
ser = serial.Serial(PORT, BAUDRATE, timeout=TIMEOUT)
time.sleep(2)
ser.write(b'espressif')
response = ser.read(8) # Adjust the number of bytes to read as necessary
ser.close()
print(f"Received response: {response.decode('utf-8')}")