ESP32S2 - serial port weird behaviour

I am working on a project where an ESP32S2 is connected via USB directly to the PC. There is no UART and the port is behaving rather weirdly.

The port is set up in the program as follows:

Code: Select all

mySerial =(Stream*) &(UsbCdcSerial);
	UsbCdcSerial.begin();
	USB.begin();

then used for example like this:

mySerial->println("Some text");

Don’t ask why. I didn’t set it up that way. What happens is that when initially connected to Serial Monitor, it does not respond until RESET is pressed. After showing the connection lost and then re-established, the Serial Monitor then accepts and receives data as expected. The problem is that in any other application, the connection is dropped the moment that RESET is pressed.

In PlatformIO/pioarduino, I tried adding the following to the board profile:

	-D ARDUINO_USB_CDC_ON_BOOT=1
	-D ARDUINO_USB_MODE=1

which used different libraries and there is a bug in HardwareSerial.cpp in v2.0 of the Espressif Arduino core causing compilation to fail. Removing the second line reverts back to the Espressif CDC method but results in the same problem. When ARDUINO_USB_CDC_ON_BOOT is set to 0, then there will be not response at all, not even in the Serial Monitor, so that line is essential. I also tried commenting out the offending line in HardwareSerial.cpp that referred to ‘Serial’ (ESP32 has Serial1 and Serial2) but it didn’t make any difference to the result unfortunately.

I also tried disabling the DSR/DTR signals:

monitor_rts = 0
monitor_dtr = 0

which actually causes the Serial Monitor to quit on startup.

Is there anything else that needs to be added to force the serial port to open? I have tried this with both Arduino core 2 and 3 I am working on a project where an ESP32S2 is connected via USB directly to the PC. There is no UART and the port is behaving rather weirdly.

The port is set up in the program as follows:

Code: Select all

mySerial =(Stream*) &(UsbCdcSerial);
UsbCdcSerial.begin();
USB.begin();

then used for exaple:

mySerial.println(“Some text”);

Don’t ask why. I didn’t set it up that way. What happens is that when initially connected to Serial Monitor, it does not respond but only after RESET is pressed. After showing the connection lost and then re-established, the Serial Monitor then accepts and receives data as expected. The problem is that in any other application, the connection is dropped the moment that RESET is pressed.

In PlatformIO/pioarduino, I tried adding the following has been added to the board profile:

-D ARDUINO_USB_CDC_ON_BOOT=1
-D ARDUINO_USB_MODE=1

I also tried disabling the DSR/DTR signals:

monitor_rts = 0
monitor_dtr = 0

which actually causes the Serial Monitor to quit almost immediately.

Is there anything else that needs to be added to force the serial connection to open? I have tried this with both Arduino core 2 and Arduino core 3 with the same result.

When I compile the Arduino IDE version of the project, via conditional directives it uses standard Arduino serial port methods (Serial.begin()) to initialise the port and that seems to work normally. Therefore it doesn’t appear to be a problem with hardware as such, but maybe the way that serial is handled using CDC methods in the Espressif core or PaltformIO/pioarduino? Is there something I am missing in addition to the above that I need to add to make the Serial port work when using CDC serial?with replacing the serial headers and files with the ones from Arduino core 3 with the same result.

When I compile the Arduino IDE version of the project, via conditional directives it uses standard Arduino serial port methods (Serial.begin()) to initialise the port and that seems to work normally. Therefore it doesn’t appear to be a problem with hardware as such, but maybe the way that serial is handled using CDC methods in the Espressif core or PaltformIO/pioarduino? Is there something I am missing in addition to the above that I need to add to make the Serial port work when using CDC serial?

My knowledge of the ESP32-S2 is only theoretical, as I don’t own one.

The S2 was initially not supported at all by Espressif Arduino and is still an outsider in the ESP range. So that inherently makes your issue a bit more complex.

Allow me to start with the following questions:

  1. Which board are you using and what is the complete content of the platformio.ini?

  2. Where and how is UsbCdcSerial defined in your code, as this does not come from the Arduino framework - See arduino-esp32/cores/esp32/HardwareSerial.h at master · espressif/arduino-esp32 · GitHub

  3. Which bug (line) in HardwareSerial.cpp are you referring to?

Both valid questions. The board is custom designed by a third party and for all intents and purposes I am communicating directly with the ESP32S2 module. There is no UART.

It turns out that it is possible to replicate the problem quite easily in a minimal Arduino IDE sketch:

#include <USB.h>

USBCDC USBSerial;

void setup() {
  // put your setup code here, to run once:
  USBSerial.begin();
  USB.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  USBSerial.println("Hello :-)");
  delay(1000);
}

When using the standard Arduino approach, there is no problem:

#include <Arduino.h>

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println("Hello");
  delay(1000);
}

So evidently it is not a PlatformIO or pioarduino issue but related to the core USB CDC code somehow. I don’t know whether its unique to the ESP32S2, but I don’t have any other ESP32 module without a UART to test. I do have two of these ESP32S2 boards and both exhibit the same problem. Who knows, it could be some issue with the board design, or perhaps it is down to the USB CDC emulation in the ESP32S2 firmware.

Thanks for your answer. I’m still missing the content of your platformio.ini.

I am sure that 99% this is just about the correct settings.

The S2 supports HWCDC and USBSerial, but the settings must be correct.
In PlatformIO you can control these settings with the build_flags. As you are not using the UART but the USB you have to set
-DARDUINO_USB_CDC_ON_BOOT=1
If -DARDUINO_USB_MODE=0 the USB Serial / JTAG peripheral will be used
If -DARDUINO_USB_MODE=1 the USB OTG peripheral is used

All you need to do is

mySerial =(Stream*) &(Serial);

as Serial is just a macro in this case which “points” to the corresponding object (which is controlled by the macros explained above)

This is the board configuration in the project I am trying to work with:

[env:ESP32-Rev4]
platform = espressif32
framework = arduino
board = esp32-s2-saola-1
upload_protocol = esptool
monitor_speed = 115200
build_flags =
	${esp32v2.build_flags}
	-D ARDUINO_USB_MODE=1
	-D ARDUINO_USB_CDC_ON_BOOT=1

I tried re-creating the minimal example Arduino IDE project in Vscode, but this simple program is giving me so many errors it doesn’t bear thinking about!

Platformio.ini:

[env:ESP32S2]
platform = espressif32
framework = arduino
board = esp32-s2-saola-1
monitor_speed = 115200
build_flags =
  -D ARDUINO_USB_CDC_ON_BOOT=1
  -D ARDUINO_USB_MODE=1

main.cpp:

#include <Arduino.h>
#include <USB.h>

USBCDC USBSerial;

void setup() {
  USBSerial.begin();
  USB.begin();
}

void loop() {
  USBSerial.ptintln("Hello :-)");
  delay(1000);
}

Use the following

platformio.ini:

[env:esp32-s2-saola-1]
platform = espressif32
board = esp32-s2-saola-1
framework = arduino
build_flags =
  -DARDUINO_USB_CDC_ON_BOOT=1

main.cpp:

#include <Arduino.h>

void setup() {
    Serial.begin();
}

void loop() {
    Serial.println("Hello :-)");
    delay(1000);
}

When ARDUINO_USB_CDC_ON_BOOT is set to 1 and ARDUINO_USB_MODE is not defined or set to 0 line 416 in HardwareSerial.h gets activated

Note: As this is using the built-in USB peripheral of the ESP32 and not an external USB to Serial Chip, the serial connection will always gets interrupted / disconnected as soon the ESP32 resets. This is expected behavior!

For some reason which I didn’t track down yet -DARDUINO_USB_MODE=1 doesn’t seem to work.

I tried the code in the previous post, but that won’t compile either.

The weird thing is that the other day it did as I had already tried it and it did work. Today, the same project - without modification - does not compile… Don’t know whether there have been some updates overnight or something, but its messed up so probably no point wasting time on it for now.

Oh wait a second… I’m using the latest Espressif Arduino 3.2 core via pioarduino :wink:

So change your platform setting to

platform = https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20/platform-espressif32.zip

But the code above compiles on both (platformio’s espressif32 platform and pioarduino’s espressif32 platform) without any issues.

There are no over night updates that installs automatically. But you did not specify the platform version to be used. If you do not pin to a specific version, the one with the highest available version number is picked.

Ok, so that did finally work. Took a while for the latest core version to download and install. I also discovered a silly mistake which I didn’t expect would make much difference. It should not be:

build_flags =
  -D ARDUINO_USB_CDC_ON_BOOT = 1
  -D ARDUINO_USB_MODE = 0

but:

build_flags =
  -D ARDUINO_USB_CDC_ON_BOOT=1
  -D ARDUINO_USB_MODE=0

Note the absence of spaces around the ‘=’. Apparently this does make a difference to the compiler.

Also, one can only enable one parameter at a time but not both together as the compile will fail. The above does now compile, but having managed that, I find that I get the same problem with the proposed Arduino Serial port version:

#include <Arduino.h>

void setup() {
    Serial.begin();
}

void loop() {
    Serial.println("Hello :-)");
    delay(1000);
}

So it seems this not because the original project uses USBCDC. If this behaviour is normal for the built-in USB peripheral on the ESP32, then it begs the question of how do I “wake up” the serial port? I tried a fairly standard example Python serial script and ran into the exact same problem as I did with the PuTTy terminal. There was simply no output from /dev/ttyACM0.

Thank you for the pointer to the later version of the Arduino core. I was already using pioarduino just with the older version. I think adding the spaces to make it look clearer is what mucked it up though.

That would make sense and explain the disconnection, but the problem here is that when the ESP32S2 is plugged in to the USB port, although it does appear as a serial port (/dev/ttyACM0), there is no serial communication. Its as if a CDC device has been created, but there is no active virtual serial port.

Does a “blink LED” firmware work? Then code execution in general is working.

If your serial monitor doesn’t assert the DTR line, nothing will show up. It’s a virtual that enables the serial device to send. You can check with e.g. HTerm (https://www.der-hammer.info/pages/terminal.html) that individually allows you to click on the DTR button to assert or deassert it.

Yes, code execution in general is indeed working. There is no onboard LED to blink, but I can run programs that use the GPIO pins and they do work and respond as expected.

Thank you. I have been looking for just such a program! The operation of DTR signal is one of the things I was wondering about but had no way of visualising or switching it to test. I believe that Terra Term can do this on Windows but I couldn’t find anything equivalent on Linux. One nice feature of Terra Term is that it also shows the status of all serial signals which would be nice to have in Hterm. Neither PuTTy, cu nor CKermit appear to provide granular access to handshaking signals unfortunately. Although PuTTy and CKermit do allow selection of CTS/RTS handshaking, neither provide access to DTR/RTS.

I tried connecting to the ESP32S2 and then clicking the DTR button several times. I was hoping to see Hello :-) scroll down the screen, but nothing happened unfortunately. There is still no output being generated, except as previously described, by using Serial Monitor and pressing reset.

I tested Hterm on another device to make sure I had it set up correctly and it worked OK in that instance so I am confident that it was configured correctly.

Can you post the output of

udevadm info /dev/ttyACM0

?

Does your ESP32S2 dev board have two USB ports, maybe one labeled “UART” the other “USB”?

No. Only one port. No UART.

Output of udevadm info /dev/ttyACM0:

$ udevadm info /dev/ttyACM0
P: /devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/tty/ttyACM0
M: ttyACM0
R: 0
U: tty
D: c 166:0
N: ttyACM0
L: 0
S: serial/by-path/pci-0000:00:14.0-usb-0:3:1.0
S: serial/by-id/usb-Espressif_Systems_ESP32S2_DEV_0-if00
E: DEVPATH=/devices/pci0000:00/0000:00:14.0/usb2/2-3/2-3:1.0/tty/ttyACM0
E: DEVNAME=/dev/ttyACM0
E: MAJOR=166
E: MINOR=0
E: SUBSYSTEM=tty
E: USEC_INITIALIZED=201993594326
E: ID_BUS=usb
E: ID_MODEL=ESP32S2_DEV
E: ID_MODEL_ENC=ESP32S2_DEV
E: ID_MODEL_ID=0002
E: ID_SERIAL=Espressif_Systems_ESP32S2_DEV_0
E: ID_SERIAL_SHORT=0
E: ID_VENDOR=Espressif_Systems
E: ID_VENDOR_ENC=Espressif\x20Systems
E: ID_VENDOR_ID=303a
E: ID_REVISION=0100
E: ID_TYPE=generic
E: ID_USB_MODEL=ESP32S2_DEV
E: ID_USB_MODEL_ENC=ESP32S2_DEV
E: ID_USB_MODEL_ID=0002
E: ID_USB_SERIAL=Espressif_Systems_ESP32S2_DEV_0
E: ID_USB_SERIAL_SHORT=0
E: ID_USB_VENDOR=Espressif_Systems
E: ID_USB_VENDOR_ENC=Espressif\x20Systems
E: ID_USB_VENDOR_ID=303a
E: ID_USB_REVISION=0100
E: ID_USB_TYPE=generic
E: ID_USB_INTERFACES=:020200:0a0000:
E: ID_USB_INTERFACE_NUM=00
E: ID_USB_DRIVER=cdc_acm
E: ID_USB_CLASS_FROM_DATABASE=Miscellaneous Device
E: ID_USB_PROTOCOL_FROM_DATABASE=Interface Association
E: ID_PATH=pci-0000:00:14.0-usb-0:3:1.0
E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_3_1_0
E: ID_FOR_SEAT=tty-pci-0000_00_14_0-usb-0_3_1_0
E: ID_MM_CANDIDATE=1
E: DEVLINKS=/dev/serial/by-path/pci-0000:00:14.0-usb-0:3:1.0 /dev/serial/by-id/usb-Espressif_Syste>
E: TAGS=:systemd:uaccess:seat:
E: CURRENT_TAGS=:systemd:uaccess:seat:

Okay that definitely looks like hte native ESP32 USB port and not a generic uart-to-usb converter.

Is the behavior the same when using the native USB mode instead of TinyUSB?

build_flags =
  -D ARDUINO_USB_CDC_ON_BOOT=1
  -D ARDUINO_USB_MODE=1

(or, leavning ARDUINO_USB_MODE out completely)

Hi Max!

The native USB mode doesn’t work for the ESP32-S2. But I think it should?!

When the ARDUINO_USB_MODE flag is set to 1 the content of HWCDC.cpp gets deactivated due to a missing SOC_USB_SERIAL_JTAG_SUPPORTED flag in the "soc/soc_caps.h"
If this is set to 1 manually in the platformio.ini it fails due to missing files "soc/usb_serial_jtag_struct.h" and "hal/usb_serial_jtag_ll.h".