Serial Data from Sensor using readBytesUntil blocking function need unblocking function

Hi,

I am using below code to read temperature and humidity values from Serial Sensor.

But while using this serial extraction ESP32 get's too slow even to detect the keypress by polling method it takes 2-3 seconds.

How to use non blocking read function to achieve the same results without affecting the speed of ESP32.

void serial_extraction()
{
  if(Serial2.available() == 0)
  {
    Serial2.print("{F00R}\r"); // SEND read command to ready values from sensor
  }
   if(Serial2.available() > 0) {
     i = Serial2.readBytesUntil('\n', values, sizeof(values) - 1);
     values[i] = '\0';                 // Now it's a string, so  
     for(i=0;i<6;i++)
     {
     temperature[i] = (values[i+19]);
     } 
     temp = atof(temperature);

     for(i=0;i<6;i++)
     {
     humidity[i] = (values[i+10]);           
     }
     rh = atof(humidity);

   }
}

I use serialized sentences, no blocking code.

void fReceiveSerial_LIDAR( void * parameters  )
{
  char OneChar;
  char *str;
  str = (char *)ps_calloc(300, sizeof(char) ); // put str buffer into PSRAM
  bool BeginSentence = false;
  sSerial.reserve ( StringBufferSize300 );
  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 );
        }
        else
        {
          if ( OneChar == ‘<’ )
          {
            sSerial = “”; // clear string buffer
            BeginSentence = true; // found begining of sentence
          }
        }
      } //  while ( LIDARSerial.available() )
    } //if ( LIDARSerial.available() >= 1 )
    xSemaphoreGive( sema_ReceiveSerial_LIDAR );
  }
  vTaskDelete( NULL );
} //void fReceiveSerial_LIDAR( void * parameters  )
//////////////////////////////////////////////////////////////////////////////////////////

The "<" marks the beginning of a new serial sentence and the ">" marks the end of a serialized sentence. A quick read of the serial buffer is done, if there is something there store it till the end of the sentence. At the end of the sentence send the received info to be parsed.

I would suggest to study Serial Input Basics to understand the typical way for dealing with asynchronous input

Take a look at Serial input basics - updated for some ideas

Alternatively, the ESP has a multitasking system (FreeRTOS )

See ESP32 Dual Core with Arduino IDE | Random Nerd Tutorials for examples

I used the following functions in one of my projects.

/*! Thread safe, memory safe non blocking read until delimiter is found.
 *
 * \param stream Stream.
 * \param buf Receiving buffer.
 * \param delim Delimeter.
 * \param i Buffer index.
 * \param j Delimiter index.
 *
 * \return true when delimiter is found or the buffer is full, false otherwise.
 */
template <size_t n> bool readUntil_r(
    Stream& stream, char (&buf)[n], char const* delim, size_t& i, size_t& j) {
  for (; i < n and delim[j]; i++) {
    if (not stream.available()) {
      return false;
    }
    buf[i] = stream.read();
    if (buf[i] == delim[j]) {
      j++;
    }
    else {
      j = 0;
    }
  }
  for (; i < n; i++) {
    buf[i] = 0;
  }
  i = 0;
  j = 0;
  return true;
}

/*! Memory safe non blocking read until delimiter is found.
 *
 * \param stream Stream.
 * \param buf Receiving buffer.
 * \param delim Delimeter.
 *
 * \return true when delimiter is found or the buffer is full, false otherwise.
 */
template <size_t n> bool readUntil(Stream& stream, char (&buf)[n], char const* delim) {
  static size_t i = 0;
  static size_t j = 0;
  return readUntil_r(stream, buf, delim, i, j);
}

Usage:

char buf[80];

// ...

void loop() {
  if (readUntil(Serial, buf, "\r")) {
    // `buf` contains the delimiter, it can now be used for parsing.
  }

  // ...
}

you can set the timeout with setTimeout

consider which waits until a terminal char like readBytesUntil


char *
getInput (
    char  termChar )
{
    static char buf [80];
    static int  idx = 0;

    while (Serial.available ())  {
        char c = Serial.read (); 
        buf [idx++] = c;
    
        if (termChar == c)  {
            buf [idx] = '\0';
            idx = 0;
            return buf;
        }
    }

    return NULL;
}

unsigned long msecLst;

void
loop (void)
{
    unsigned long msec = millis ();

    if ( msec - msecLst >= 1000)  {
        Serial.println (msec);

        msecLst = msec - msec % 1000;
    }

    char *s = getInput (';');
    if (s)
        Serial.println (s);
}

void
setup (void)
{
    Serial.begin (9600);
}

careful on buffer overrun if the ';' does not come before the 80th byte

Worked Perfectly!! with 7ms timeout.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.