UART Communication Failing between ESP32 and STM32f030c6t6

Hello everyone,

I am trying to make a very basic uart based data echoing in which my esp32 is master and my stm32 is slave whatever esp3 sends (currently which is hardcoded) should return back to it

here is my esp32 code

HardwareSerial STM(2);




void setup() {

  Serial.begin(115200);

  STM.begin(115200, SERIAL_8N1, 16, 17);   // RX, TX

  delay(1000);

  Serial.println("ESP32 UART test started");

}




void loop() {

  const char *msg = "HELLO STM32\r\n";

  STM.print(msg);

  Serial.print("TX: ");

  Serial.print(msg);




  unsigned long t = millis();

  while (millis() - t < 200) {

    while (STM.available()) {

      char c = (char)STM.read();

      Serial.write(c);

    }

  }




  delay(1000);

}

and here is my stm32 code

/* USER CODE BEGIN Header */

#include "main.h"

#include <string.h>

/* USER CODE END Header */



UART_HandleTypeDef huart1;



void SystemClock_Config(void);

static void MX_GPIO_Init(void);

static void MX_USART1_UART_Init(void);



#define RX_BUF_SIZE 128

static uint8_t  rxByte;

static char     lineBuf[RX_BUF_SIZE];

static uint16_t lineIdx = 0;

static volatile uint8_t lineReady = 0;

static volatile uint32_t errorCount = 0;   /* NEW: counts UART errors */



int main(void)

{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();

  MX_USART1_UART_Init();



  char *startMsg = "STM32 ready, waiting for serial data...\r\n";

  HAL_UART_Transmit(&huart1, (uint8_t *)startMsg, strlen(startMsg), HAL_MAX_DELAY);



  /* Kick off the first interrupt-driven receive */

  HAL_UART_Receive_IT(&huart1, &rxByte, 1);



  while (1)

  {

    if (lineReady)

    {

      /* Briefly disable the UART IRQ while we copy out the line and

         reset state, so an incoming byte can't corrupt lineBuf mid-copy */

      char localBuf[RX_BUF_SIZE];



      HAL_NVIC_DisableIRQ(USART1_IRQn);

      strcpy(localBuf, lineBuf);

      lineIdx = 0;

      lineReady = 0;

      HAL_NVIC_EnableIRQ(USART1_IRQn);



      char outBuf[RX_BUF_SIZE + 40];

      int len = snprintf(outBuf, sizeof(outBuf), "Received: %s (errors:%lu)\r\n",

                          localBuf, (unsigned long)errorCount);

      HAL_UART_Transmit(&huart1, (uint8_t *)outBuf, len, HAL_MAX_DELAY);

    }

  }

}



/* Called automatically by HAL every time one byte arrives */

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

  if (huart->Instance == USART1)

  {

    char c = (char)rxByte;



    if (c == '\n' || c == '\r')

    {

      if (lineIdx > 0)          /* ignore empty lines from \r\n pairs */

      {

        lineBuf[lineIdx] = '\0';

        lineReady = 1;

      }

    }

    else if (lineIdx < RX_BUF_SIZE - 1)

    {

      lineBuf[lineIdx++] = c;

    }



    /* Re-arm for the next byte */

    HAL_UART_Receive_IT(&huart1, &rxByte, 1);

  }

}



/* NEW: without this, any framing/noise/overrun error silently kills RX */

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)

{

  if (huart->Instance == USART1)

  {

    errorCount++;

    __HAL_UART_CLEAR_PEFLAG(huart);           /* clear PE/FE/NE/ORE flags */

    HAL_UART_Receive_IT(&huart1, &rxByte, 1); /* re-arm */

  }

}



void SystemClock_Config(void)

{

  RCC_OscInitTypeDef RCC_OscInitStruct = {0};

  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};



  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;

  RCC_OscInitStruct.HSIState = RCC_HSI_ON;

  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;

  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  {

    Error_Handler();

  }



  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

                              |RCC_CLOCKTYPE_PCLK1;

  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;

  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;



  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)

  {

    Error_Handler();

  }

}



static void MX_USART1_UART_Init(void)

{

  huart1.Instance = USART1;

  huart1.Init.BaudRate = 115200;

  huart1.Init.WordLength = UART_WORDLENGTH_8B;

  huart1.Init.StopBits = UART_STOPBITS_1;

  huart1.Init.Parity = UART_PARITY_NONE;

  huart1.Init.Mode = UART_MODE_TX_RX;

  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;

  huart1.Init.OverSampling = UART_OVERSAMPLING_16;

  if (HAL_UART_Init(&huart1) != HAL_OK)

  {

    Error_Handler();

  }

}



static void MX_GPIO_Init(void)

{

  GPIO_InitTypeDef GPIO_InitStruct = {0};



  __HAL_RCC_GPIOB_CLK_ENABLE();

  __HAL_RCC_GPIOA_CLK_ENABLE();



  /* PA9 = USART1_TX, PA10 = USART1_RX */

  GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;

  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

  GPIO_InitStruct.Pull = GPIO_NOPULL;

  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

  GPIO_InitStruct.Alternate = GPIO_AF1_USART1;

  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);



  __HAL_RCC_USART1_CLK_ENABLE();



  HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);

  HAL_NVIC_EnableIRQ(USART1_IRQn);

}



void USART1_IRQHandler(void)

{

  HAL_UART_IRQHandler(&huart1);

}



void Error_Handler(void)

{

  __disable_irq();

  while (1) {}

}



#ifdef USE_FULL_ASSERT

void assert_failed(uint8_t *file, uint32_t line) {}

#endif


and here are the serial prints which i am getting
22:35:55.428 -> TX: HELLO STM32

22:35:56.625 -> TX: HELLO STM32

22:35:57.817 -> TX: HELLO STM32

22:35:59.041 -> TX: HELLO STM32

22:36:00.235 -> TX: HELLO STM32

22:36:01.441 -> TX: HELLO STM32

22:36:02.630 -> TX: HELLO STM32

22:36:03.827 -> TX: HELLO STM32

22:36:05.040 -> TX: HELLO STM32

22:36:06.234 -> TX: HELLO STM32

22:36:07.438 -> TX: HELLO STM32

22:36:08.633 -> TX: HELLO STM32

If you have one of these usb-to-serial adapter boards you could test to see whether data is being sent or receive on each board. Just make sure its set to 3.3v operation.

If you connect that to the STM32 Tx/Rx pins, does what you send to it get echoed back?

Conversely, if you connect it to pins 16 + 17 of the ESP, can you see the data being sent by the ESP?

Can you show an annotated schematic showing how you connected these two devices. You must have Rx connected to Tx on the other unit.

You can use this code for the STM32. I am using the NUCLEO-F303RE development board, whic features the STM32F303RE microcontroller, however, adapting it to your STM32F030C6T6 microcontroller is a straightforward process.

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2026 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define RX_BUFFER_SIZE 128
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
char rx_buffer[RX_BUFFER_SIZE]; // I accumulate the characters for reception here
uint8_t rx_index = 0; // I am goin gto receive a byte per interrupt
uint8_t rx_data;
volatile _Bool msg_ready = 0;
volatile _Bool tx_busy = 0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
void UART_Communication(void);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    UART_Communication();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2;
  PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
  PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* USER CODE BEGIN MX_GPIO_Init_1 */

  /* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

  /* USER CODE BEGIN MX_GPIO_Init_2 */

  /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */
void UART_Communication(void)
{
  if (msg_ready && !tx_busy)
  {
    char console_buffer[256];

    snprintf(console_buffer, sizeof(console_buffer), "Received: \"%s\"\r\n", rx_buffer);
    HAL_UART_Transmit(&huart2, (uint8_t*)console_buffer, strlen(console_buffer), HAL_MAX_DELAY);

    snprintf(console_buffer, sizeof(console_buffer), "Sent: \"%s\"\r\n", rx_buffer);
    HAL_UART_Transmit(&huart2, (uint8_t*)console_buffer, strlen(console_buffer), HAL_MAX_DELAY);

    strcat(rx_buffer, "\r\n");

    tx_busy = 1;

    HAL_UART_Transmit_IT(&huart1, (uint8_t*)rx_buffer, strlen(rx_buffer));
  }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1)
  {
    if (rx_index < RX_BUFFER_SIZE - 1 && !msg_ready)
    {
      if (rx_data == '\n' || rx_data == '\r')
      {
        if (rx_index > 0)
        {
          rx_buffer[rx_index] = '\0'; 
          msg_ready = 1;             
        }
      }
      else
      {
        rx_buffer[rx_index] = rx_data; 
        rx_index++;
      }
    }
    HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  }
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1)
  {
    memset(rx_buffer, 0, RX_BUFFER_SIZE);
    rx_index = 0;
    
    msg_ready = 0;
    tx_busy = 0;
  }
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

Just like you, I am using USART1 to receive and respond to the frame (pins PA9 [TX] and PA10 [RX]).
Additionally, I am using USART2 which is internally connected via the ST-Link board to print messages and view them on the console.

Also, I am using interrupts (for both transmission and reception) to prevent the processor from becoming blocked while waiting for data to arrive or be transmitted, thereby allowing it to perform other tasks.

Since I don't have another development board on hand, I am using a USB-to-Serial converter (CP210x) with my own computer to simulate the ESP32. I am using Tera Term to input and view the data.

As for the connections I have, they are as follows:
TX (CP210x) – USART1 RX on STM32 (PA10)
RX (CP210x) – USART1 TX on STM32 (PA9)
GND (CP210x) – STM32 GND

I have two separate Tera Term windows open; in one, I select the CP210x COM port, and it is configured as follows:

And another window where I select the COM port for the NUCLEO board. Both with a baud rate of 115,200.

In the following figure, the top window represents your ESP32; you type the desired message, and in response, you receive the same message sent by the STM32.
The bottom window represents the STM32, showing the information it receives from the ESP32 and the information it sends back.

esp32 code using Serial1 to communicate with STM32

// ESP32  Serial1 test - for loopback test connect pins RXD1 and TXD1

#define RXD1 16 // can map Serial1 and Serial2 to many ESP32 GPIO pins
#define TXD1 17 // check pin usage https://randomnerdtutorials.com/esp32-pinout-reference-gpios/

// for RS232 shield connect
// ESP32 RXD1 to TTL/RS232 Rx
// ESP32 TXD1 to TTL/RS232 Tx
// connect GND pins together and VCC to 3.3V on ESP32 5V on UNO ect
// for loopback test connect 9-pin D_type connector pins 2 Tx to 3 Rx (pin 5 is GND)

void setup() {
  // initialize both serial ports:
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, RXD1, TXD1);
  Serial.printf("\n\nESP32 serial1  test RXD1 pin %d TXD1 pin %d\n", RXD1, TXD1);
  Serial.printf(" loopback test connect pin %d to pin %d\n", RXD1, TXD1);
  Serial.printf("RS232: ESP32 pin %d RXD1 to TTL/RS232 Rx and pin %d TXD1 to TTL/RS232 Tx\n", RXD1, TXD1);
  Serial.printf("RS232 - loopback connect 9-pin D-type pin 2 Tx to pin 3 Rx\n");
}

void loop() {
  // read from Serial1, send to Serial
  if (Serial1.available()) {
    int inByte = Serial1.read();
    Serial.write(inByte);
  }
  // read from Serial, send to Serial1
  if (Serial.available()) {
    int inByte = Serial.read();
    //Serial.write(inByte);     // local echo if required
    Serial1.write(inByte);
  }
}

Arduino IDE code for STM32 Nucleo-LO73RZ board using Serial1 mapped to RX D2 (PA10) and TX D8 (PA9) to communicate with ESP32

// STM32 Nucleo-LO73RZ board serial1 test

// Serial is to pins D0 RX and D1 Tx NOT USB
// connect FTDI USB-RS232-3v3
// orange to D0 RX
// yellow to D1 TX
// black to GND

// Serial1 mapped to RX D2 (PA10) and TX D8 (PA9) 
//HardwareSerial Serial1(PA10, PA9);
Uart Serial1(PA10, PA9);

void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
  while (!Serial)
    ;
  Serial.begin(115200);
  Serial1.begin(115200);
  delay(2000);
  Serial.println();
  Serial.println("STM32 Nucleo board Serial1 test \nConnect FTDI USB-RS232-3V3 cable");
  Serial.println("orange to D0 RX - yellow to D1 TX -  black to GND");
  Serial.println("Serial1 mapped to RX D2 (PA10) and TX D8 (PA9) - connect for loopback test");
}

// display *, blink LED and read Serial echo ASCII code
void loop() {
  //Serial.print('*');
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay(100);                      // wait for a second
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  delay(100);                      // wait for a second
  if (Serial.available()){
    char ch=Serial.read();
    //Serial.println(ch);  // print ASCII code of character
    Serial1.write(ch);
  }
  if(Serial1.available()){
    char ch=Serial1.read();
    //Serial.print("\nSerial1 read ");
    Serial.print(ch);
   // Serial.println(" <<");
  }
}

STM32 Serial output is via a FTDI cable connected to D1 Tx and D0 Rx

connect ESP32 GPIO17 Tx to STM32 D2 Rx
connect ESP32 GPIO16 Rx to STM32 D8 Rx

ESP32 serial monitor output (no local echo)

ESP32 serial1  test RXD1 pin 16 TXD1 pin 17
 loopback test connect pin 16 to pin 17
RS232: ESP32 pin 16 RXD1 to TTL/RS232 Rx and pin 17 TXD1 to TTL/RS232 Tx
RS232 - loopback connect 9-pin D-type pin 2 Tx to pin 3 Rx
hello from STM32
test2 from STM32

STM32 serial monitor output on a terminal emulator (local echo on)

STM32 Nucleo board Serial1 test 
Connect FTDI USB-RS232-3V3 cable
orange to D0 RX - yellow to D1 TX -  black to GND
Serial1 mapped to RX D2 (PA10) and TX D8 (PA9) - connect for loopback test
hello from ESP32
test2 from ESP32
hello from STM32
test2 from STM32

photo of setup