Open krupis opened 11 months ago
I have tested out the following esp-idf example:
esp-idf\examples\peripherals\usb\device\tusb_serial_device
And it seems to work fine, I can send/receive serial commands via USB, but there is a couple of problems:
After the device is flashed for the initial time, I will no longer be able to flash the device because the USB has been reconfigured and will not allow flashing. The only way to solve this issue to manually put the device into boot mode by holding boot button and reseting the device. Is that correct?
I can send/receive UART commands via serial terminal such as "Termite" "PuTTy" and etc.. But printf
and ESP_LOG
statements are not visible. Is there any way to see the debug printf
and ESP_LOG
statements while also being able to send serial commands?
@krupis
Is there any way to send/receive serial commands via USB in this manner?
Yes, there is. ESP32-S3 has a peripheral called "USB-Serial-JTAG", which has only the respective functions, Serial and JTAG, and is connected to GPIO19/20 unless it's configured differently. If you want to use it, you just need to configure IDF to use the USB-serial as primary console output. You do that in menuconfig by going to Component config → ESP System Settings → Channel for console output, then choosing "USB Serial/JTAG Controller". If you do this, you should be able to flash and debug examples the same way as for ESP32 devices (which needed a UART-to-USB converter, usually located on the developement boards).
The confusion probably comes from the actual USB devices that the ESP32-S3 has, too. This is a generic USB peripheral. Via the example you mentioned, the generic USB peripheral can also be configured as a serial device, but with USB drivers and software stack in the background. The USB-Serial/JTAG peripheral is much simpler, but can only do Serial and JTAG.
@0xjakob
Thanks for informative answer. I think I have managed to brick 2 ESP32-S3 devices and realized what I have done.
I have enabled Channel for console output "USB Serial/JTAG Controller" option.
and I call the following code in my program:
void UART0_setup() {
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
//.rx_flow_ctrl_thresh = 122,
//.use_ref_tick = false,
};
// Configure UART parameters
ESP_ERROR_CHECK(uart_param_config(UART_NUM_0, &uart_config));
// Set UART pins(TX: IO4, RX: IO5, RTS: IO18, CTS: IO19)
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_0, 20, 19, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_0, UART0_COMMAND_LINE_MAX_SIZE, 0, 0, NULL, 0));
}
as you can see from above, I have reconfigured GPIO19 and GPIO20 (USB pins) to UART. So as soon as device boots up, the USB pins are reconfigured and the device bricks. I have managed to restore by manually putting the device into boot mode and reflash the device without UART0_setup()
. Lesson learned.
Can you please confirm to me how can I send/receive serial commands via the USB Serial/JTAG controller (Exactly how I am sending and receiving commands using tusb_serial_device example) or that is not possible?
From what I understand, in order to do that, I need to config UART but I cannot do that on the USB pins GPIO 19 and GPIO 20. Do you understand what I am trying to do or I am not being clear enough?
In short, when I plug USB cable on the ESP32-S3 , I want to use it as I am using it on the normal ESP32. I use it for 3 things:
At the moment on the ESP32-S3 I can only do 2/3:
I cannot send/receive serial commands since the USB is not connected to any UART pins hence I wonder can I achieve that.
I think your issue is that you assume the USB-serial-JTAG controller 'looks like' an UART on the side of the ESP32 as well - it does not and requires a slightly different API to communicate if you do it on a lower level. If all you need to do is send/receive data, you should be able to work around that by fopen()ing /dev/usbserjtag and using fread/fwrite against that. This is portable to a chip where you want to use a 'real' uart as well; you'd simply open /dev/uartX in that case.
Another alternative (on the same level as using the UART driver would be) is to use the driver-level USB-serial-JTAG code, as described here:https://github.com/espressif/esp-idf/blob/master/components/driver/usb_serial_jtag/include/driver/usb_serial_jtag.h
@Spritetm
Thanks for the response. I am still trying to get a grasp on how it is even possible to emulate UART via USB/JTAG. I think I need to get the basic example running then I can examine the code.
Since you mentioned driver-level USB-serial-JTAG, I have found a relevant ESP32 forum thread regarding this: https://esp32.com/viewtopic.php?t=27944
I have written a simple program for this:
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "nvs_flash.h"
#include "driver/usb_serial_jtag.h"
#include "esp_vfs_usb_serial_jtag.h"
#include "esp_vfs_dev.h"
static void initUart();
void app_main(void)
{
esp_err_t ret;
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();
}
printf("hello world \n");
initUart();
}
static void initUart() {
/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
esp_vfs_dev_usb_serial_jtag_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_usb_serial_jtag_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
/* Enable non-blocking mode on stdin and stdout */
fcntl(fileno(stdout), F_SETFL, 0);
fcntl(fileno(stdin), F_SETFL, 0);
usb_serial_jtag_driver_config_t usb_serial_jtag_config;
usb_serial_jtag_config.rx_buffer_size = 1024;
usb_serial_jtag_config.tx_buffer_size = 1024;
esp_err_t ret = ESP_OK;
/* Install USB-SERIAL-JTAG driver for interrupt-driven reads and writes */
ret = usb_serial_jtag_driver_install(&usb_serial_jtag_config);
if (ret != ESP_OK) {
return;
}
/* Tell vfs to use usb-serial-jtag driver */
esp_vfs_usb_serial_jtag_use_driver();
}
Am I on the right track here? The termite log:
Would you be able to suggest how can I attach a simple receive callback to this? For example, I have written command "ping" via termite and I want to parse this message and respond accordingly.
That should work. The driver at that level doesn't really have receive callback; what you'd do is start a task and then use a blocking getchar() or fgets() in that. Since you installed the driver, that call will not return until it received a char or an entire string, respectively.
@Spritetm
Understood!.
Have a look at my code:
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "nvs_flash.h"
#include "driver/usb_serial_jtag.h"
#include "esp_vfs_usb_serial_jtag.h"
#include "esp_vfs_dev.h"
static void initUart();
static void Receive_callback(void *argument);
void app_main(void)
{
esp_err_t ret;
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();
}
printf("hello world before init Uart\n");
initUart();
printf("hello world after init Uart\n");
xTaskCreate(Receive_callback,"UART receive callback",4096,NULL,5,NULL); // receiving commands from main uart
}
static void initUart() {
/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
esp_vfs_dev_usb_serial_jtag_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_usb_serial_jtag_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
/* Enable non-blocking mode on stdin and stdout */
fcntl(fileno(stdout), F_SETFL, 0);
fcntl(fileno(stdin), F_SETFL, 0);
usb_serial_jtag_driver_config_t usb_serial_jtag_config;
usb_serial_jtag_config.rx_buffer_size = 1024;
usb_serial_jtag_config.tx_buffer_size = 1024;
esp_err_t ret = ESP_OK;
/* Install USB-SERIAL-JTAG driver for interrupt-driven reads and writes */
ret = usb_serial_jtag_driver_install(&usb_serial_jtag_config);
if (ret != ESP_OK) {
return;
}
/* Tell vfs to use usb-serial-jtag driver */
esp_vfs_usb_serial_jtag_use_driver();
}
static void Receive_callback(void *argument)
{
for (;;)
{
uint8_t ch;
ch = fgetc(stdin);
if (ch!=0xFF)
{
//fputc(ch, stdout);
printf("char received = %c \n",ch);
}
vTaskDelay(10/portTICK_PERIOD_MS);
}
}
The terminal log:
As you can see I can now receive the command that I have sent via Termite. That was exactly what I was trying to achieve.
Although I am not really sure how that even works?
Although I am not really sure how that even works?
Printf and getchar etc are standard C functions, which read/write from/to two file descriptors called 'stdin' and 'stdout', which are effectively defined as 'the terminal the user is on'. On an Unix system, they'd point to the terminal window you're using, for instance; in ESP-IDF they usually point to UART0 with a secondary output going to the USB-serial-JTAG adapter. What you did was install a driver for USB-serial-JTAG that replaces that, making stdin and stdout always refer to the USB-serial-JTAG adapter. From then on, things like printf() will write to and things like getchar() will read from the USB-serial-JTAG device.
@Spritetm Thanks for clarifying :)
Answers checklist.
General issue report
Hello. I have been using UART0 commands when debugging various ESP32 devices. Example code:
I can then connect to the device using some terminal for example PuTTY, Termite, MobaXterm and etc.. and send various commands to the device.
I have recently purchased ESP32-S3 Lilygo device: https://www.lilygo.cc/products/t-display-s3
and after experimenting with it a little I have realized that I can program the device via USB and the device com port will appear on the serial terminal software but I cannot send any commands to it. I have looked at the device schematic:
As you can see from above, the USB connects to GPIO19 and GPIO20 respectively.
Is there any way to send/receive serial commands via USB in this manner?