diff --git a/tests/validation/uart/diagram.esp32.json b/tests/validation/uart/diagram.esp32.json
new file mode 100644
index 00000000000..a31c06d8313
--- /dev/null
+++ b/tests/validation/uart/diagram.esp32.json
@@ -0,0 +1,24 @@
+{
+  "version": 1,
+  "author": "lucasssvaz",
+  "editor": "wokwi",
+  "parts": [
+    {
+      "type": "board-esp32-devkit-c-v4",
+      "id": "esp",
+      "attrs": { "cpuFrequency": "40" }
+    }
+  ],
+  "connections": [
+    [
+      "esp:TX",
+      "$serialMonitor:RX",
+      ""
+    ],
+    [
+      "esp:RX",
+      "$serialMonitor:TX",
+      ""
+    ]
+  ]
+}
diff --git a/tests/validation/uart/uart.ino b/tests/validation/uart/uart.ino
index 01c449867db..27bd95da7f8 100644
--- a/tests/validation/uart/uart.ino
+++ b/tests/validation/uart/uart.ino
@@ -2,25 +2,20 @@
  *
  * This test is using UART0 (Serial) only for reporting test status and helping with the auto
  * baudrate detection test.
- * UART1 (Serial1) and UART2 (Serial2), where available, are used for testing.
+ * The other serials are used for testing.
  */
 
-#include <unity.h>
-#include "HardwareSerial.h"
-#include "esp_rom_gpio.h"
-#include "Wire.h"
-
 // Default pins:
-//          |  Name   | ESP32 | S2 | S3 | C3 | C6 | H2 |
-// UART0 RX | SOC_RX0 |   3   | 44 | 44 | 20 | 17 | 23 |
-// UART0 TX | SOC_TX0 |   1   | 43 | 43 | 21 | 16 | 24 |
-// UART1 RX |   RX1   |  26   |  4 | 15 | 18 |  4 |  0 |
-// UART1 TX |   TX1   |  27   |  5 | 16 | 19 |  5 |  1 |
-// UART2 RX |   RX2   |   4   | -- | 19 | -- | -- | -- |
-// UART2 TX |   TX2   |  25   | -- | 20 | -- | -- | -- |
+//          |  Name   | ESP32 | S2 | S3 | C3 | C6 | H2 | P4 |
+// UART0 RX | SOC_RX0 |   3   | 44 | 44 | 20 | 17 | 23 | 38 |
+// UART0 TX | SOC_TX0 |   1   | 43 | 43 | 21 | 16 | 24 | 37 |
+// UART1 RX |   RX1   |  26   |  4 | 15 | 18 |  4 |  0 | 11 |
+// UART1 TX |   TX1   |  27   |  5 | 16 | 19 |  5 |  1 | 10 |
+// UART2 RX |   RX2   |   4   | -- | 19 | -- | -- | -- | -- |
+// UART2 TX |   TX2   |  25   | -- | 20 | -- | -- | -- | -- |
 
 /*
- * For 2 UARTS:
+ * For each UART:
  *
  *           terminal
  *          |       ^
@@ -30,119 +25,95 @@
  *        report status
  *              |
  *         TX <---> RX
- *            UART1
- *
- * For 3 UARTS:
- *
- *     =====terminal======
- *      ^  |       ^    ^
- *      |  v UART0 |    |
- *      |  RX    TX     |
- *      |               |
- *      ^ report status ^
- *      |               |
- *      | TX ---> RX    |
- *  UART2 RX <--- TX UART1
- *
+ *            UARTx
  */
 
-#if SOC_UART_HP_NUM == 2
-// Used for the pin swap test
-#define NEW_RX1 9
-#define NEW_TX1 10
-#endif
+#include <vector>
+#include <unity.h>
+#include "HardwareSerial.h"
+#include "esp_rom_gpio.h"
+#include "Wire.h"
 
-// ESP32-P4 has no UART pin definition for RX2, TX2, RX3, TX3, RX4, TX4
-#ifndef RX2
-#define RX2 RX1
-#endif
-#ifndef TX2
-#define TX2 RX1
-#endif
+/* Utility defines */
 
-/* Utility global variables */
+#define TEST_UART_NUM (uart_test_configs.size())
 
-static String recv_msg = "";
-static int peeked_char = -1;
+/* Utility classes */
 
-/* Utility functions */
+class UARTTestConfig {
+public:
+  int uart_num;
+  HardwareSerial &serial;
+  int peeked_char;
+  int8_t default_rx_pin;
+  int8_t default_tx_pin;
+  String recv_msg;
 
-extern int8_t uart_get_RxPin(uint8_t uart_num);
-extern int8_t uart_get_TxPin(uint8_t uart_num);
+  UARTTestConfig(int num, HardwareSerial &serial_ref, int8_t rx_pin, int8_t tx_pin)
+    : uart_num(num), serial(serial_ref), peeked_char(-1), default_rx_pin(rx_pin), default_tx_pin(tx_pin), recv_msg("") {}
 
-// This function starts all the available test UARTs
-void start_serial(unsigned long baudrate = 115200) {
-#if SOC_UART_HP_NUM >= 2
-  Serial1.begin(baudrate);
-  while (!Serial1) {
-    delay(10);
+  void begin(unsigned long baudrate) {
+    serial.begin(baudrate, SERIAL_8N1, default_rx_pin, default_tx_pin);
+    while (!serial) {
+      delay(10);
+    }
   }
-#endif
 
-#if SOC_UART_HP_NUM >= 3
-  Serial2.begin(baudrate);
-  while (!Serial2) {
-    delay(10);
+  void end() {
+    serial.end();
   }
-#endif
-}
-
-// This function stops all the available test UARTs
-void stop_serial(bool hard_stop = false) {
-#if SOC_UART_HP_NUM >= 2
-  Serial1.end(/*hard_stop*/);
-#endif
-
-#if SOC_UART_HP_NUM >= 3
-  Serial2.end(/*hard_stop*/);
-#endif
-}
 
-// This function transmits a message and checks if it was received correctly
-void transmit_and_check_msg(const String msg_append, bool perform_assert = true) {
-  delay(100);  // Wait for some settings changes to take effect
-#if SOC_UART_HP_NUM == 2
-  Serial1.print("Hello from Serial1 (UART1) >>> via loopback >>> Serial1 (UART1) " + msg_append);
-  Serial1.flush();
-  delay(100);
-  if (perform_assert) {
-    TEST_ASSERT_EQUAL_STRING(("Hello from Serial1 (UART1) >>> via loopback >>> Serial1 (UART1) " + msg_append).c_str(), recv_msg.c_str());
+  void reset_buffers() {
+    recv_msg = "";
+    peeked_char = -1;
   }
-#elif SOC_UART_HP_NUM >= 3
-  Serial1.print("Hello from Serial1 (UART1) >>> to >>> Serial2 (UART2) " + msg_append);
-  Serial1.flush();
-  delay(100);
-  if (perform_assert) {
-    TEST_ASSERT_EQUAL_STRING(("Hello from Serial1 (UART1) >>> to >>> Serial2 (UART2) " + msg_append).c_str(), recv_msg.c_str());
+
+  void transmit_and_check_msg(const String &msg_append, bool perform_assert = true) {
+    reset_buffers();
+    delay(100);
+    serial.print("Hello from Serial" + String(uart_num) + " " + msg_append);
+    serial.flush();
+    delay(100);
+    if (perform_assert) {
+      TEST_ASSERT_EQUAL_STRING(("Hello from Serial" + String(uart_num) + " " + msg_append).c_str(), recv_msg.c_str());
+      log_d("UART%d received message: %s\n", uart_num, recv_msg.c_str());
+    }
   }
 
-  Serial2.print("Hello from Serial2 (UART2) >>> to >>> Serial1 (UART1) " + msg_append);
-  Serial2.flush();
-  delay(100);
-  if (perform_assert) {
-    TEST_ASSERT_EQUAL_STRING(("Hello from Serial2 (UART2) >>> to >>> Serial1 (UART1) " + msg_append).c_str(), recv_msg.c_str());
+  void onReceive() {
+    char c;
+    size_t available = serial.available();
+    if (peeked_char == -1) {
+      peeked_char = serial.peek();
+    }
+    while (available--) {
+      c = (char)serial.read();
+      recv_msg += c;
+    }
   }
-#else
-  log_d("No UARTs available for transmission");
-  TEST_FAIL();
-#endif
-}
+};
+
+/* Utility global variables */
+
+[[maybe_unused]]
+static const int NEW_RX1 = 9;
+[[maybe_unused]]
+static const int NEW_TX1 = 10;
+std::vector<UARTTestConfig *> uart_test_configs;
+
+/* Utility functions */
+
+extern "C" int8_t uart_get_RxPin(uint8_t uart_num);
+extern "C" int8_t uart_get_TxPin(uint8_t uart_num);
 
 /* Tasks */
 
 // This task is used to send a message after a delay to test the auto baudrate detection
 void task_delayed_msg(void *pvParameters) {
-  HardwareSerial *selected_serial;
-
-#if SOC_UART_HP_NUM == 2
-  selected_serial = &Serial;
-#elif SOC_UART_HP_NUM >= 3
-  selected_serial = &Serial1;
-#endif
-
+  HardwareSerial &selected_serial = uart_test_configs.size() == 1 ? Serial : Serial1;
   delay(2000);
-  selected_serial->println("Hello from Serial1 to detect baudrate");
-  selected_serial->flush();
+  selected_serial.println("Hello to detect baudrate");
+  selected_serial.flush();
   vTaskDelete(NULL);
 }
 
@@ -150,67 +121,23 @@ void task_delayed_msg(void *pvParameters) {
 
 // This function is automatically called by unity before each test is run
 void setUp(void) {
-  start_serial(115200);
-#if SOC_UART_HP_NUM == 2
-  log_d("Setup internal loop-back from and back to Serial1 (UART1) TX >> Serial1 (UART1) RX");
-
-  Serial1.onReceive([]() {
-    onReceive_cb(Serial1);
-  });
-  uart_internal_loopback(1, RX1);
-#elif SOC_UART_HP_NUM >= 3
-  log_d("Setup internal loop-back between Serial1 (UART1) <<--->> Serial2 (UART2)");
-
-  Serial1.onReceive([]() {
-    onReceive_cb(Serial1);
-  });
-  Serial2.onReceive([]() {
-    onReceive_cb(Serial2);
-  });
-  uart_internal_loopback(1, RX2);
-  uart_internal_loopback(2, RX1);
-#endif
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    //log_d("Setup internal loop-back from and back to UART%d TX >> UART%d RX", config.uart_num, config.uart_num);
+    config.begin(115200);
+    config.serial.onReceive([&config]() {
+      config.onReceive();
+    });
+    uart_internal_loopback(config.uart_num, uart_get_RxPin(config.uart_num));
+  }
 }
 
 // This function is automatically called by unity after each test is run
 void tearDown(void) {
-  stop_serial();
-}
-
-/* Callback functions */
-
-// This is a callback function that will be activated on UART RX events
-void onReceive_cb(HardwareSerial &selected_serial) {
-  int uart_num = -1;
-  char c;
-
-  (void)uart_num;  // Avoid compiler warning when debug level is set to none
-
-  if (&selected_serial == &Serial) {
-    uart_num = 0;
-#if SOC_UART_HP_NUM >= 2
-  } else if (&selected_serial == &Serial1) {
-    uart_num = 1;
-#endif
-#if SOC_UART_HP_NUM >= 3
-  } else if (&selected_serial == &Serial2) {
-    uart_num = 2;
-#endif
-  }
-
-  recv_msg = "";
-  size_t available = selected_serial.available();
-
-  if (available != 0) {
-    peeked_char = selected_serial.peek();
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    config.end();
   }
-
-  while (available--) {
-    c = (char)selected_serial.read();
-    recv_msg += c;
-  }
-
-  log_d("UART %d received message: %s\n", uart_num, recv_msg.c_str());
 }
 
 /* Test functions */
@@ -219,40 +146,33 @@ void onReceive_cb(HardwareSerial &selected_serial) {
 void basic_transmission_test(void) {
   log_d("Performing basic transmission test");
 
-  transmit_and_check_msg("");
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    config.transmit_and_check_msg("");
+  }
 
   Serial.println("Basic transmission test successful");
 }
 
 // This test checks if the baudrate can be changed and if the message can be transmitted and received correctly after the change
 void change_baudrate_test(void) {
-  //Test first using the updateBaudRate method and then using the begin method
-  log_d("Changing baudrate to 9600");
-
-  //Baudrate error should be within 2% of the target baudrate
-  Serial1.updateBaudRate(9600);
-  TEST_ASSERT_UINT_WITHIN(192, 9600, Serial1.baudRate());
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    log_d("Changing baudrate of UART%d to 9600", config.uart_num);
 
-#if SOC_UART_HP_NUM >= 3
-  Serial2.updateBaudRate(9600);
-  TEST_ASSERT_UINT_WITHIN(192, 9600, Serial2.baudRate());
-#endif
-
-  log_d("Sending string using 9600 baudrate");
-  transmit_and_check_msg("using 9600 baudrate");
+    //Baudrate error should be within 2% of the target baudrate
+    config.serial.updateBaudRate(9600);
+    TEST_ASSERT_UINT_WITHIN(192, 9600, config.serial.baudRate());
 
-  log_d("Changing baudrate back to 115200");
-  start_serial(115200);
+    log_d("Sending string on UART%d using 9600 baudrate", config.uart_num);
+    config.transmit_and_check_msg("using 9600 baudrate");
 
-  //Baudrate error should be within 2% of the target baudrate
-  TEST_ASSERT_UINT_WITHIN(2304, 115200, Serial1.baudRate());
+    config.serial.begin(115200);
+    TEST_ASSERT_UINT_WITHIN(2304, 115200, config.serial.baudRate());
 
-#if SOC_UART_HP_NUM >= 3
-  TEST_ASSERT_UINT_WITHIN(2304, 115200, Serial2.baudRate());
-#endif
-
-  log_d("Sending string using 115200 baudrate");
-  transmit_and_check_msg("using 115200 baudrate");
+    log_d("Sending string on UART%d using 115200 baudrate", config.uart_num);
+    config.transmit_and_check_msg("using 115200 baudrate");
+  }
 
   Serial.println("Change baudrate test successful");
 }
@@ -269,7 +189,7 @@ void resize_buffers_test(void) {
   ret = Serial1.setTxBufferSize(256);
   TEST_ASSERT_EQUAL(0, ret);
 
-  stop_serial();
+  Serial1.end();
 
   log_d("Trying to resize RX buffer while stopped.");
   ret = Serial1.setRxBufferSize(256);
@@ -285,7 +205,12 @@ void resize_buffers_test(void) {
 // This test checks if the begin function can be called when the UART is already running
 void begin_when_running_test(void) {
   log_d("Trying to set up serial twice");
-  start_serial(115200);
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    // Calling twice should not crash
+    config.begin(115200);
+    config.begin(115200);
+  }
   Serial.println("Begin when running test successful");
 }
 
@@ -293,9 +218,12 @@ void begin_when_running_test(void) {
 void end_when_stopped_test(void) {
   log_d("Trying to end serial twice");
 
-  // Calling end(true) twice should not crash
-  stop_serial(true);
-  stop_serial(true);
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    // Calling twice should not crash
+    config.end();
+    config.end();
+  }
 
   Serial.println("End when stopped test successful");
 }
@@ -319,7 +247,7 @@ void enabled_uart_calls_test(void) {
   TEST_ASSERT_EQUAL(true, boolean_ret);
 
   log_d("Checking if Serial 1 is peekable while running");
-  TEST_ASSERT_GREATER_OR_EQUAL(0, peeked_char);
+  TEST_ASSERT_GREATER_OR_EQUAL(0, uart_test_configs[0]->peeked_char);
 
   log_d("Checking if Serial 1 can read bytes while running");
   integer_ret = Serial1.readBytes(test_buf, 1);
@@ -355,7 +283,10 @@ void disabled_uart_calls_test(void) {
   int integer_ret;
   uint8_t test_buf[1];
 
-  stop_serial();
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    config.end();
+  }
 
   log_d("Checking if Serial 1 can set the RX timeout when stopped");
   boolean_ret = Serial1.setRxTimeout(1);
@@ -423,44 +354,35 @@ void disabled_uart_calls_test(void) {
 
 // This test checks if the pins can be changed and if the message can be transmitted and received correctly after the change
 void change_pins_test(void) {
-  //stop_serial();
-
   log_d("Disabling UART loopback");
 
-#if SOC_UART_HP_NUM == 2
-  esp_rom_gpio_connect_out_signal(SOC_RX0, SIG_GPIO_OUT_IDX, false, false);
-#elif SOC_UART_HP_NUM >= 3
-  esp_rom_gpio_connect_out_signal(RX1, SIG_GPIO_OUT_IDX, false, false);
-  esp_rom_gpio_connect_out_signal(RX2, SIG_GPIO_OUT_IDX, false, false);
-#endif
-
-  log_d("Swapping UART pins");
-
-#if SOC_UART_HP_NUM == 2
-  Serial1.setPins(NEW_RX1, NEW_TX1);
-  TEST_ASSERT_EQUAL(NEW_RX1, uart_get_RxPin(1));
-  TEST_ASSERT_EQUAL(NEW_TX1, uart_get_TxPin(1));
-#elif SOC_UART_HP_NUM >= 3
-  Serial1.setPins(RX2, TX2);
-  Serial2.setPins(RX1, TX1);
-  TEST_ASSERT_EQUAL(RX2, uart_get_RxPin(1));
-  TEST_ASSERT_EQUAL(TX2, uart_get_TxPin(1));
-  TEST_ASSERT_EQUAL(RX1, uart_get_RxPin(2));
-  TEST_ASSERT_EQUAL(TX1, uart_get_TxPin(2));
-#endif
-
-  start_serial(115200);
-
-  log_d("Re-enabling UART loopback");
-
-#if SOC_UART_HP_NUM == 2
-  uart_internal_loopback(1, NEW_RX1);
-#elif SOC_UART_HP_NUM >= 3
-  uart_internal_loopback(1, RX1);
-  uart_internal_loopback(2, RX2);
-#endif
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    esp_rom_gpio_connect_out_signal(config.default_rx_pin, SIG_GPIO_OUT_IDX, false, false);
+  }
 
-  transmit_and_check_msg("using new pins");
+  log_d("Swapping UART pins and testing transmission");
+
+  if (TEST_UART_NUM == 1) {
+    UARTTestConfig &config = *uart_test_configs[0];
+    config.serial.setPins(NEW_RX1, NEW_TX1);
+    TEST_ASSERT_EQUAL(NEW_RX1, uart_get_RxPin(config.uart_num));
+    TEST_ASSERT_EQUAL(NEW_TX1, uart_get_TxPin(config.uart_num));
+
+    uart_internal_loopback(config.uart_num, NEW_RX1);
+    config.transmit_and_check_msg("using new pins");
+  } else {
+    for (int i = 0; i < TEST_UART_NUM; i++) {
+      UARTTestConfig &config = *uart_test_configs[i];
+      UARTTestConfig &next_uart = *uart_test_configs[(i + 1) % TEST_UART_NUM];
+      config.serial.setPins(next_uart.default_rx_pin, next_uart.default_tx_pin);
+      TEST_ASSERT_EQUAL(uart_get_RxPin(config.uart_num), next_uart.default_rx_pin);
+      TEST_ASSERT_EQUAL(uart_get_TxPin(config.uart_num), next_uart.default_tx_pin);
+
+      uart_internal_loopback(config.uart_num, next_uart.default_rx_pin);
+      config.transmit_and_check_msg("using new pins");
+    }
+  }
 
   Serial.println("Change pins test successful");
 }
@@ -475,12 +397,15 @@ void auto_baudrate_test(void) {
 
   log_d("Stopping test serial. Using Serial2 for ESP32 and Serial1 for ESP32-S2.");
 
-#if SOC_UART_HP_NUM == 2
-  selected_serial = &Serial1;
-  uart_internal_loopback(0, RX1);
-#elif SOC_UART_HP_NUM >= 3
-  selected_serial = &Serial2;
+  if (TEST_UART_NUM == 1) {
+    selected_serial = &Serial1;
+    uart_internal_loopback(0, RX1);
+  } else {
+#ifdef RX2
+    selected_serial = &Serial2;
+    uart_internal_loopback(1, RX2);
 #endif
+  }
 
   //selected_serial->end(false);
 
@@ -493,10 +418,10 @@ void auto_baudrate_test(void) {
   selected_serial->begin(0);
   baudrate = selected_serial->baudRate();
 
-#if SOC_UART_HP_NUM == 2
-  Serial.end();
-  Serial.begin(115200);
-#endif
+  if (TEST_UART_NUM == 1) {
+    Serial.end();
+    Serial.begin(115200);
+  }
 
   TEST_ASSERT_UINT_WITHIN(2304, 115200, baudrate);
 
@@ -510,32 +435,23 @@ void periman_test(void) {
 
   log_d("Setting up I2C on the same pins as UART");
 
-  Wire.begin(RX1, TX1);
-
-#if SOC_UART_HP_NUM >= 3
-  Wire1.begin(RX2, TX2);
-#endif
-
-  recv_msg = "";
-
-  log_d("Trying to send message using UART with I2C enabled");
-  transmit_and_check_msg("while used by I2C", false);
-  TEST_ASSERT_EQUAL_STRING("", recv_msg.c_str());
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    Wire.begin(config.default_rx_pin, config.default_tx_pin);
+    config.recv_msg = "";
 
-  log_d("Disabling I2C and re-enabling UART");
+    log_d("Trying to send message using UART%d with I2C enabled", config.uart_num);
+    config.transmit_and_check_msg("while used by I2C", false);
+    TEST_ASSERT_EQUAL_STRING("", config.recv_msg.c_str());
 
-  Serial1.setPins(RX1, TX1);
+    log_d("Disabling I2C and re-enabling UART%d", config.uart_num);
 
-#if SOC_UART_HP_NUM >= 3
-  Serial2.setPins(RX2, TX2);
-  uart_internal_loopback(1, RX2);
-  uart_internal_loopback(2, RX1);
-#elif SOC_UART_HP_NUM == 2
-  uart_internal_loopback(1, RX1);
-#endif
+    config.serial.setPins(config.default_rx_pin, config.default_tx_pin);
+    uart_internal_loopback(config.uart_num, config.default_rx_pin);
 
-  log_d("Trying to send message using UART with I2C disabled");
-  transmit_and_check_msg("while I2C is disabled");
+    log_d("Trying to send message using UART%d with I2C disabled", config.uart_num);
+    config.transmit_and_check_msg("while I2C is disabled");
+  }
 
   Serial.println("Peripheral manager test successful");
 }
@@ -551,8 +467,11 @@ void change_cpu_frequency_test(void) {
 
   Serial.updateBaudRate(115200);
 
-  log_d("Trying to send message with the new CPU frequency");
-  transmit_and_check_msg("with new CPU frequency");
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    log_d("Trying to send message with the new CPU frequency on UART%d", config.uart_num);
+    config.transmit_and_check_msg("with new CPU frequency");
+  }
 
   log_d("Changing CPU frequency back to %dMHz", old_freq);
   Serial.flush();
@@ -560,8 +479,11 @@ void change_cpu_frequency_test(void) {
 
   Serial.updateBaudRate(115200);
 
-  log_d("Trying to send message with the original CPU frequency");
-  transmit_and_check_msg("with the original CPU frequency");
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    log_d("Trying to send message with the original CPU frequency on UART%d", config.uart_num);
+    config.transmit_and_check_msg("with the original CPU frequency");
+  }
 
   Serial.println("Change CPU frequency test successful");
 }
@@ -573,30 +495,39 @@ void setup() {
   while (!Serial) {
     delay(10);
   }
-  log_d("SOC_UART_HP_NUM = %d", SOC_UART_HP_NUM);
-
-  // Begin needs to be called before setting up the loopback because it creates the serial object
-  start_serial(115200);
-
-#if SOC_UART_HP_NUM == 2
-  log_d("Setup internal loop-back from and back to Serial1 (UART1) TX >> Serial1 (UART1) RX");
-
-  Serial1.onReceive([]() {
-    onReceive_cb(Serial1);
-  });
-  uart_internal_loopback(1, RX1);
-#elif SOC_UART_HP_NUM >= 3
-  log_d("Setup internal loop-back between Serial1 (UART1) <<--->> Serial2 (UART2)");
-
-  Serial1.onReceive([]() {
-    onReceive_cb(Serial1);
-  });
-  Serial2.onReceive([]() {
-    onReceive_cb(Serial2);
-  });
-  uart_internal_loopback(1, RX2);
-  uart_internal_loopback(2, RX1);
+
+  uart_test_configs = {
+#if SOC_UART_HP_NUM >= 2 && defined(RX1) && defined(TX1)
+    // inverting RX1<->TX1 because ESP32-P4 has a problem with loopback on RX1 :: GPIO11 <-- UART_TX SGINAL
+    new UARTTestConfig(1, Serial1, TX1, RX1),
+#endif
+#if SOC_UART_HP_NUM >= 3 && defined(RX2) && defined(TX2)
+    new UARTTestConfig(2, Serial2, RX2, TX2),
+#endif
+#if SOC_UART_HP_NUM >= 4 && defined(RX3) && defined(TX3)
+    new UARTTestConfig(3, Serial3, RX3, TX3),
 #endif
+#if SOC_UART_HP_NUM >= 5 && defined(RX4) && defined(TX4)
+    new UARTTestConfig(4, Serial4, RX4, TX4)
+#endif
+  };
+
+  if (TEST_UART_NUM == 0) {
+    log_e("This test requires at least one UART besides UART0 configured");
+    abort();
+  }
+
+  log_d("TEST_UART_NUM = %d", TEST_UART_NUM);
+
+  for (auto *ref : uart_test_configs) {
+    UARTTestConfig &config = *ref;
+    config.begin(115200);
+    log_d("Setup internal loop-back from and back to UART%d TX >> UART%d RX", config.uart_num, config.uart_num);
+    config.serial.onReceive([&config]() {
+      config.onReceive();
+    });
+    uart_internal_loopback(config.uart_num, uart_get_RxPin(config.uart_num));
+  }
 
   log_d("Setup done. Starting tests");