ESP32 Serial Won't Start After Serial.end()

I wrote program to begin serial 1, send a byte then disable serial1 and loop. If Serial1.end() is called then the TX pin will not function properly. The first time loop() runs it send the byte then stays low for about 600 microseconds and stays high after that. The TX channel stops toggling after that.

The reason I need to call Serial1.end() is because my TX pin is multiplexed and I need to use it as a general IO later.
I tried using Serial1.flush() before and after end() but it doesn't help.

#include "HardwareSerial.h"
uint8_t a= 10;

void setup() {
}

void loop() {
  Serial1.begin(250000);
  Serial1.write(a);
  Serial1.end();

  delay(100);
}

Using the ESP32 Pico with 1.0.2-rc1 version in Arduino.

Cheers

ESP32 Serial.

3 Serial ports (0), (1), (2), that you can use. Leave Serial1 for the serial monitor. Serial (0) is a bit tricky to use. Which leaves (1) and (2); not to confused with Serial1 and Serial2.

You’ll want to have a: #include <HardwareSerial.h> as part of the includes.

You’ll want to define your serial devices:

HardwareSerial SerialTFMini( 1 );
HardwareSerial SerialContoller( 2 );
////// serial(1) = pin27=RX green, pin26=TX white
////// serial(2) = pin16=RXgreen , pin17=TX white
#define SerialDataBits 115200

Serial (2) is at pins 16 for rx and 17 for tx. Serial (1) is defaulted to pins that you should not, at this time, use. So you’ll want to redefine your (1) pins in the setup.

In setup you’ll want to begin your serial ports:

Serial.begin( SerialDataBits );
SerialController.begin( SerialDataBits );
SerialTFMini.begin( SerialDataBits, SERIAL_8N1, 27, 26 );

Notice that for serial (1) I defined the rx and tx pins.

After that, use as you wish.

Receiving Serial with the ESP32:

void fReceiveSerial_LIDAR( void * parameters  )
{
  bool BeginSentence = false;
  sSerial.reserve ( StringBufferSize300 );
  char OneChar;
  for ( ;; )
  {
    EventBits_t xbit = xEventGroupWaitBits (eg, evtReceiveSerial_LIDAR, pdTRUE, pdTRUE, portMAX_DELAY);
    if ( LIDARSerial.available() >= 1 )
    {
      while ( LIDARSerial.available() )
      {
        OneChar = LIDARSerial.read();
        if ( BeginSentence )
        {
          if ( OneChar == '>')
          {
            if ( xSemaphoreTake( sema_ParseLIDAR_ReceivedSerial, xSemaphoreTicksToWait10 ) == pdTRUE )
            {
              xQueueOverwrite( xQ_LIDAR_Display_INFO, ( void * ) &sSerial );
              xEventGroupSetBits( eg, evtParseLIDAR_ReceivedSerial );
              //
            }
            BeginSentence = false;
            break;
          }
          sSerial.concat ( OneChar );With the
        }
        else
        {
          if ( OneChar == '<' )
          {
            sSerial = ""; // clear string buffer
            BeginSentence = true; // found beginning of sentence
          }
        }
      } //  while ( LIDARSerial.available() )
    } //if ( LIDARSerial.available() >= 1 )
    xSemaphoreGive( sema_ReceiveSerial_LIDAR );
  }
  vTaskDelete( NULL );
} //void fParseSerial( void * parameters  )

Note my use of if ( LIDARSerial.available() >= 1 ) and not if ( LIDARSerial.available() >= 0 ). I found that with the Due, STM32, and ESP32 a “1” works more reliably then a “0”.

Thanks. I tried using HardwareSerial like you wrote but the results are the same.

#include <HardwareSerial.h>
uint8_t a= 10;
HardwareSerial MySerial(1);

void setup() {
}

void loop() {
  MySerial.begin(250000, SERIAL_8N1, 9, 10);
  MySerial.write(a);
  MySerial.end();
  //MAKE PIN 10 GENERAL IO AND DO SOMETHING WITH digitalWrite(10,LOW) or  digitalWrite(10,HIGH) HERE

  delay(100);
}

I have to end() serial so I can use the TX as a general IO pin for another purpose in the middle of the application. Then I have to use that pin for Serial TX again. All other IO of the ESP32 will be used up for other reasons.
So this test program is to see if that pin can be reassigned to an IO and pack to serial but its giving problems.

Adding a flush and delay makes this program work as expected now. A byte is written on the TX pin every 100 ms.

#include <HardwareSerial.h>
uint8_t a= 10;
HardwareSerial MySerial(1);

void setup() {
  pinMode(10, OUTPUT);
}

void loop() {  
  MySerial.begin(250000, SERIAL_8N1, 9, 10);
  MySerial.write(a);
  MySerial.flush();
  delay(1);
  MySerial.end();

  delay(100);
}

The problem now is that I want to have TX pin be a digital output after MySerial.end(). If I try to do that then the output stays HIGH until the loop() starts again and the serial commands are run. There is no difference in the output of the TX pin in the bottom code vs the top code.

#include <HardwareSerial.h>
uint8_t a= 10;
HardwareSerial MySerial(1);

void setup() {
  pinMode(10, OUTPUT);
}

void loop() {  
  MySerial.begin(250000, SERIAL_8N1, 9, 10);
  MySerial.write(a);
  MySerial.flush();
  delay(1);
  MySerial.end();

  //MAKE PIN 10 GENERAL IO AND DO SOMETHING WITH digitalWrite(10,LOW) or  digitalWrite(10,HIGH) HERE
  pinMode(10, OUTPUT);
  digitalWrite(10, LOW);
  delay(1);
  digitalWrite(10, HIGH);
  delay(1);
  digitalWrite(10, LOW);
  delay(1);
  digitalWrite(10, HIGH);
  delay(1);
  
  delay(100);
}

Serial.end() calls uartDetachRx(uart) and uartDetachTx(uart). Is it possible the detach method is not working?
Or pinMode does not attach the pin because it stays locked?

Along those lines. I do not, for the ESP32, put code in the void loop. The void loop runs with the least priority and its main function, when empty, becomes clean up. I had thought that what may be happening is that the serial object is being cleaned up with serial end.

HardwareSerial MySerial(1); may need to be moved from the global declaration to being ahead of serial.begin function in the loop function?

Thanks. I tried this with the same results

HardwareSerial MySerial(1);
    MySerial.begin(250000, SERIAL_8N1, 9, 10);
    MySerial.write(a);
    MySerial.flush();
    vTaskDelay(1);
    MySerial.end();

I tried changing the program to use FreeRTOS tasks. That had similar results to the original code, the Serial works but the general IO does not. But there was a long delay between each repetition. Perhaps this has to do with how task is scheduled?

#include <HardwareSerial.h>
uint8_t a= 10;
uint8_t state=0;
//HardwareSerial MySerial(1);

void setup() {
  pinMode(10, OUTPUT);
 
  xTaskCreate(
                    taskOne,          /* Task function. */
                    "TaskOne",        /* String with name of task. */
                    10000,            /* Stack size in bytes. */
                    NULL,             /* Parameter passed as input of the task */
                    1,                /* Priority of the task. */
                    NULL);            /* Task handle. */
 
}
 
void loop() {
  //delay(1000);
}
 
void taskOne( void * parameter )
{
  if(state== 1)
  {
    HardwareSerial MySerial(1);
    MySerial.begin(250000, SERIAL_8N1, 9, 10);
    MySerial.write(a);
    MySerial.flush();
    vTaskDelay(1);
    MySerial.end();
  
    state= 0;
  }
  else if(state== 0)
  {
    pinMode(10, OUTPUT);
    
    digitalWrite(10, LOW);
    vTaskDelay(1);
    digitalWrite(10, HIGH);
    vTaskDelay(1);
    digitalWrite(10, LOW);
    vTaskDelay(1);
    digitalWrite(10, HIGH);
    vTaskDelay(1);

    state= 1;
  }
}

Old topic, but google led me here so ill answer with my solution :slight_smile:

I had this very same problem with the esp32 devkit. I needed to end the serial comms and then pull both lines down (otherwise the connected device would not shut off). The solution that worked for me, was after the serial.end() to set the pinmode to OUTPUT_OPEN_DRAIN.

I am facing the same problem. I have tried your solution but it does not work in my case.

Here is a code snippet I am using.

Serial2.end();
delay(1000);
pinMode(K_OUT, OUTPUT_OPEN_DRAIN);
pinMode(K_IN, OUTPUT_OPEN_DRAIN);

digitalWrite(K_IN, HIGH);
digitalWrite(K_OUT, HIGH);
delay(300);
digitalWrite(K_OUT, LOW);
delay(25);
digitalWrite(K_OUT, HIGH);
delay(25);

Serial2.begin(10417, SERIAL_8N1, K_IN, K_OUT);  // K-LINE

The serial still works fine but the digital writes never have any effect in the GPIOs. No HIGH or LOW.
It only works the first time since serial would not be yet initialized.

luketanti:
I am facing the same problem. I have tried your solution but it does not work in my case.

Here is a code snippet I am using.

Serial2.end();

delay(1000);
pinMode(K_OUT, OUTPUT_OPEN_DRAIN);
pinMode(K_IN, OUTPUT_OPEN_DRAIN);

digitalWrite(K_IN, HIGH);
digitalWrite(K_OUT, HIGH);
delay(300);
digitalWrite(K_OUT, LOW);
delay(25);
digitalWrite(K_OUT, HIGH);
delay(25);

Serial2.begin(10417, SERIAL_8N1, K_IN, K_OUT);  // K-LINE




The serial still works fine but the digital writes never have any effect in the GPIOs. No HIGH or LOW.
It only works the first time since serial would not be yet initialized.

After digging up the ESP32 core I have found that probably the pin is not being MUXed back as a GPIO pin.

I have added this line "gpio_matrix_out(K_OUT, SIG_GPIO_OUT_IDX, false, false);" and now it works.
So the pin is now working as a GPIO and a UART TX line.

Serial2.end();
  delay(1000);
  gpio_matrix_out(K_OUT, SIG_GPIO_OUT_IDX, false, false);
  pinMode(K_OUT, OUTPUT);
  digitalWrite(K_OUT, HIGH);
  delay(300);
  digitalWrite(K_OUT, LOW);
  delay(25);
  digitalWrite(K_OUT, HIGH);
  delay(25);