/*

   This Sketch demonstrates the effect of changing RX Timeout parameter into HardwareSerial Class
   Serial.setRxTimeout(byte) is used to change it.
   By default, UART ISR will wait for an RX Timeout equivalent to 2 UART symbols to understand that a flow.
   of UART data has ended. For example, if just one byte is received, UART will send about 10 to
   11 bits depending of the configuration (parity, number of stopbits). The timeout is measured in
   number of UART symbols, with 10 or 11 bits, in the current baudrate.
   For 9600 baud, 1 bit takes 1/9600 of a second, equivalent to 104 microseconds, therefore, for 10 bits, 
   it takes about 1ms. A timeout of 2 UART symbols, with about 20 bits, would take about 2.1 milliseconds 
   for the ESP32 UART to trigger an IRQ telling the UART driver that the transmission has ended.
   Just at this point, the data will be made available to Arduino HardwareSerial API (read(), available(), etc).

   The way we demonstrate the effect of this parameter is by measuring the time the Sketch takes
   to read data using Arduino HardwareSerial API.

   The higher RX Timeout is, the longer it will take to make the data available, when a flow of data ends.
   UART driver works copying data from UART FIFO to Arduino internal buffer.
   The driver will copy data from FIFO when RX Timeout is detected or when FIFO is full.
   ESP32 FIFO has 128 bytes and by default, the driver will copy the data when FIFO reaches 120 bytes.
   If UART receives less than 120 bytes, it will wait RX Timeout to understand that the bus is IDLE and
   then copy the data from the FIFO to the Arduino internal buffer, making it availble to the Arduino API.

*/

#include <Arduino.h>

// There are two ways to make this sketch work:
// By physically connecting the pins 4 and 5 and then create a physical UART loopback,
// Or by using the internal IO_MUX to connect the TX signal to the RX pin, creating the
// same loopback internally.
#define USE_INTERNAL_PIN_LOOPBACK 1   // 1 uses the internal loopback, 0 for wiring pins 4 and 5 externally

#define DATA_SIZE 10    // 10 bytes is lower than the default 120 bytes of RX FIFO FULL 
#define BAUD 9600       // Any baudrate from 300 to 115200
#define TEST_UART 1     // Serial1 will be used for the loopback testing with different RX FIFO FULL values
#define RXPIN 4         // GPIO 4 => RX for Serial1
#define TXPIN 5         // GPIO 5 => TX for Serial1

uint8_t rxTimeoutTestCases[] = {50, 20, 10, 5, 1};

void setup() {
  // UART0 will be used to log information into Serial Monitor
  Serial.begin(115200);

  // UART1 will have its RX<->TX cross connected
  // GPIO4 <--> GPIO5 using external wire
  Serial1.begin(BAUD, SERIAL_8N1, RXPIN, TXPIN); // Rx = 4, Tx = 5 will work for ESP32, S2, S3 and C3
#if USE_INTERNAL_PIN_LOOPBACK
  uart_internal_loopback(TEST_UART, RXPIN);
#endif

  for (uint8_t i = 0; i < sizeof(rxTimeoutTestCases); i++) {
    Serial.printf("\n\n================================\nTest Case #%d\n================================\n", i + 1);
    testAndReport(rxTimeoutTestCases[i]);
  }

}

void loop() {
}

void testAndReport(uint8_t rxTimeout) {
  // Let's send 10 bytes from Serial1 rx<->tx and mesaure time using diferent Rx Timeout configurations
  uint8_t bytesReceived = 0;
  uint8_t dataSent[DATA_SIZE], dataReceived[DATA_SIZE];
  uint8_t i;
  // initialize all data
  for (i = 0; i < DATA_SIZE; i++) {
    dataSent[i] = '0' + (i % 10); // fill it with a repeated sequence of 0..9 characters
    dataReceived[i] = 0;
  }

  Serial.printf("Testing the time for receiving %d bytes at %d baud, using RX Timeout = %d:", DATA_SIZE, BAUD, rxTimeout);
  Serial.flush(); // wait Serial FIFO to be empty and then spend almost no time processing it
  Serial1.setRxTimeout(rxTimeout); // testing diferent results based on Rx Timeout setup
  // For baud rates lower or equal to 57600, ESP32 Arduino makes it get byte-by-byte from FIFO, thus we will change it here:
  Serial1.setRxFIFOFull(120);      // forces it to wait receiving 120 bytes in FIFO before making it availble to Arduino

  size_t sentBytes = Serial1.write(dataSent, sizeof(dataSent)); // ESP32 TX FIFO is about 128 bytes, 10 bytes will fit fine
  uint32_t now = millis();
  while (bytesReceived < DATA_SIZE) {
    bytesReceived += Serial1.read(dataReceived, DATA_SIZE);
    // safety for array limit && timeout... in 5 seconds...
    if (millis() - now > 5000) break;
  }

  uint32_t pastTime = millis() - now;
  Serial.printf("\nIt has sent %d bytes from Serial1 TX to Serial1 RX\n", sentBytes);
  Serial.printf("It took %lu milliseconds to read %d bytes\n", pastTime, bytesReceived);
  Serial.print("Received data: [");
  Serial.write(dataReceived, DATA_SIZE);
  Serial.println("]");
  Serial.println("========================\nFinished!");
}
