Using all of 3 UART`s on ESP32 (ESP-WROOM-32)

Hello,

I have this project that assumes I will be using all of 3 UART`s. Two of them for MITM/Interceptor of other devices communications, and one for debugging since I am still learning.

After some problems with Serial1 I have came to a point in which it is all good (after remapping GPIO`s), but Serial2 started acting. If I disable it below debug line goes away.

[env:esp32dev]

platform = espressif32

board = esp32dev

framework = arduino

#include <Arduino.h>
#include <HardwareSerial.h>

void setup()
{

	Serial.begin(9600);

	// UART1  -> Serial1
	//  RX Pin -> GPIO 14
	//  TX Pin -> GPIO 12
	// Speaker
	Serial1.begin(57600, SERIAL_8O1, 4, 2);
	// // UART2  -> Serial2
	// //  RX Pin -> GPIO 16
	// //  TX Pin -> GPIO 17
	// // Console
	Serial2.begin(57600, SERIAL_8O1, 16 ,17);
}

void loop()
{
	if (Serial2.available())
	{								   // If anything comes in Serial2,
		Serial1.write(Serial2.read()); // read it and send it out Serial1 (pins 0 & 1)
		Serial.println("console_says: " + char(Serial2.read()));
	}
	if (Serial1.available())
	{								   // If anything comes in Serial1 (pins 0 & 1)
		Serial2.write(Serial1.read()); // read it and send it out Serial2
		Serial.println("speaker_says: " + char(Serial1.read()));
	}
}

Unfortunately my current config spits %6u][E][%s:%u] %s(): Serial number is invalid, please use numers from 0 to %u
I am to little experienced to evaluate if this will have any impact on communication, but sure it has bad impact on debugging.
Digging down I figured out it comes (kind of obvious) from HardwareSerial.cpp.

void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd)
{
    if(_uart_nr >= SOC_UART_NUM) {
        log_e("Serial number is invalid, please use numers from 0 to %u", SOC_UART_NUM - 1);
        return;
    }

So:

  1. It is weird that %u parameter is not outputted properly in serial monitor - and i can see that this by itself can be a problem since it is part of condition.
  2. SOC_UART_NUM variable comes from IDF libsoc_caps.h (IDF lib?) which states amount of accessible UARTs. All in all when testing it expands to 3 as it should.
  3. _uart_nr I cant tell if this is behaving right but i have tried to fix this issue by adressing Serial 1 and 2 like so with no avail.
HardwareSerial Serial1(1);  //if using UART1
HardwareSerial Serial2(2);  //if using UART2

Any suggestions ?

Edit: ok this github issue seems to be connected.

Give this a try:

#include <Arduino.h>

void setup() {
    Serial.begin(9600);
    Serial1.begin(57600, SERIAL_8O1, 4, 2);
    Serial2.begin(57600, SERIAL_8O1, 16, 17);
}

void streamSerial2Serial(Stream& source, Stream& dest, const char* name) {
  int length = source.available();                // check if data is available on source
  if (length <= 0) return;                        // no data available? nothing to do... return!
               
  char buffer[length];                            // create correct sized buffer
  source.readBytes(buffer, length);               // read data from source into buffer
  dest.write(buffer, length);                     // write buffer to dest
  Serial.printf("%s says: %s\r\n", name, buffer); // debug output to Serial
}

void loop() {
  streamSerial2Serial(Serial1, Serial2, "console");
  streamSerial2Serial(Serial2, Serial1, "speaker");
}

Also this is a double read. You call Serial2.read() once, which consumes the received character, and writes it to Serial1, then you do another read, wich either returns -1 (0xffffffff, ‘no character available’) or a new character if one really arrived, and try to write it to Serial.

Exactly this the cause for the weird output “%6u][E][%s:%u] %s(): Serial number is invalid, please use numers from 0 to %u

Thank you both very much, it made significant difference.
Now it is not complaining about serial number.
Downside is that for some reason now only speaker ( in fact I think it is console but considering described protocoll it talks to much) is sending messages.

€p"ű?ř[1B]ü?H[1C]ü?A)
€p"ű?ř[1B]ü?ĺ[1B]ÜÎ.[01]@?ř[1B]ü?@"ű?@"ű?
speaker says: ˙ţ˙˙űöţ÷˙˙·ö˙ţ·ţţ˙˙÷ö—÷˙ř[1B]ü?H[1C]ü?A)
€p"ű?ř[1B]ü?ĺ[1B]ÜÎ.[01]@?ř[1B]ü?@"ű?@"ű?
speaker says: ţľű˙˙‡˙˙˙˙˙ň˙¶ţ˙ţ˙˙˙˙"ű?ř[1B]ü?H[1C]ü?A)
€p"ű?ř[1B]ü?ĺ[1B]ÜÎ.[01]@?ř[1B]ü?@"ű?@"ű?
speaker says: ˙)
€p"ű?ř[1B]ü?H[1C]ü?A)
€p"ű?ř[1B]ü?ĺ[1B]ÜÎ.[01]@?ř[1B]ü?@"ű?@"ű?
speaker says: ÷˙ö˙÷ňţöţôţöţ˙÷ö¶˙ň˙v"ű?ř[1B]ü?H[1C]ü?A)
€p"ű?ř[1B]ü?ĺ[1B]ÜÎ.[01]@?ř[1B]ü?@"ű?@"ű?
speaker says: ß)
€p"ű?ř[1B]ü?H[1C]ü?A)
€p"ű?ř[1B]ü?ĺ[1B]ÜÎ.[01]@?ř[1B]ü?@"ű?@"ű?
speaker says: ß»÷öwű˙˙÷˙˙˙¶·ö¶˙ýö¶˙ö×ţ˙[1B]ü?H[1C]ü?A)
€p"ű?ř[1B]ü?ĺ[1B]ÜÎ.[01]@?ř[1B]ü?@"ű?@"ű?

Switching terminal (Serial) to HEX does not give me any values according to above protocol.

I have already tried to change that GPIO2 (connected with onboard LED) thinking that this may be the cause.
It will take some time but I will try to debug things on my side once more.
for future research I have eddited:

void loop() {
  streamSerial2Serial(Serial1, Serial2, "speaker"); //* to match my physical connection
  streamSerial2Serial(Serial2, Serial1, "console"); //* to match my physical connection
  streamSerial2Serial(Serial, Serial1, "terminal"); //* to be able to inject commands
}

Mh this is also not right because with the %s in printf you’re interpreting buffer as a zero-terminated string, however there’s no guarantee that it is. You would have to allocate buffer with one more byte and initialize it with all-zeroes, then it’s safe to handle as a null-terminated ASCII string.

But the protocol is a binary one anyways so it’ll not be ASCII readable text, but should be HEX encoded.

I would propose


void streamSerial2Serial(Stream& source, Stream& dest, const char* name) {
  int length = source.available();                // check if data is available on source
  if (length <= 0) return;                        // no data available? nothing to do... return!
               
  char buffer[length];                            // create correct sized buffer
  source.readBytes(buffer, length);               // read data from source into buffer
  dest.write(buffer, length);                     // write buffer to dest
  Serial.printf("%s says (hex):\n", name); // debug output
  for(int i = 0; i < length; i++) {
     uint8_t d = buffer[i];
     if(d < 0x10) Serial.print("0");
     Serial.print(d, HEX);
     Serial.print(' ');
  }
  Serial.println();
}
1 Like

I have improved wiring and now can see both console and speaker talking but console is complaining about lack of connection.
I will try Your code tomorrow and if something will be off I will try to hook up logic analyzer and if something I will test code with some ESP-based UART device.

Of course you’re absolutely right. I had implemented the debug output later and hadn’t thought about it.

For the sake of completeness, here is the correct implementation for ASCII-based protocols.

void streamSerial2Serial(Stream& source, Stream& dest, const char* name) {
  int length = source.available();                // check if data is available on source
  if (length <= 0) return;                        // no data available? nothing to do... return!
               
  char buffer[length+1];                          // create correct sized buffer + 1 byte for null-termination
  buffer[length] = '\0';                          // add zero termination at the end of the buffer
  source.readBytes(buffer, length);               // read data from source into buffer
  dest.write(buffer, length);                     // write buffer to dest
  Serial.printf("%s says: %s\r\n", name, buffer); // debug output to Serial
}

I can confirm that current config is working good with my test setup. I have made dummy ESP8266 UART device sending 3 bytes of data to ESP32 on Serial1. Serial 2 has jumper on TX and RX so full circle, takes about 0,59ms. Looks good, and similar configuration had worked with ESPhome effectively allowing console and speaker talk to each other. I have to investigate my previous setup.
Edit. Yep ! everything works now. I had most simple UART fault in my setup :wink:
Thank You For helping me out.