user53790: I am trying to use Topway display for HMI lcd with Arduino. This display using RS232C protocol at docs it has a "Communication Packet Structure" like this.

So my query should start with AA and end with CC 33 C3 3C.  This sample reading code was taking topway youtube channel.
Serial.write(0xaa); // packet head
Serial.write(0x3e); // VP_N16 read command
Serial.write(0x00); // VP_N16 address
Serial.write(0x08);
Serial.write(0x00);
Serial.write(0x00);
Serial.write(0xcc); // packet tail
Serial.write(0x33); // packet tail
Serial.write(0xc3); // packet tail
Serial.write(0x3c); // packet tail

3E is the command (for reading) code and this is the list of all commands. docs over here

The point is parsing of smart LCD's response at the original. They use this code: (at first comment)
     while (!Serial.available()) {}  
     if (Serial.read()==0xaa) {                     // packet head
     packet_OK=1;
     while (!Serial.available()) {}
     if (Serial.read()==0x3e) {                   // packet command
      while (!Serial.available()) {}  
      temp_h=Serial.read();                      // read back value high byte
      while (!Serial.available()) {}  
      temp_l=Serial.read();                      // read back value low byte
      while (!Serial.available()) {}  
      if (!(Serial.read()==0xcc)) {packet_OK=0;} // packet tail
      while (!Serial.available()) {}  
      if (!(Serial.read()==0x33)) {packet_OK=0;} // packet tail
      while (!Serial.available()) {}  
      if (!(Serial.read()==0xc3)) {packet_OK=0;} // packet tail
      while (!Serial.available()) {}  
      if (!(Serial.read()==0x3c)) {packet_OK=0;} // packet tail
      }
      else
      {packet_OK=0;}
     }
     else
     {packet_OK=0;}

It looks nasty and cumbersome because I must write different method for each LCD display commands. Is there any way to read all serial response them as a char array with starting marker "0xAA" and read until finishing marker "0xCC 0x33 0xC3 0x3C" and after that parse it?
You can watch and check this code on youtube

user7508: The short answer to your question is “yes, there is a way”. Multiple
ways actually. You could write a blocking function, which follows more
or less the logic of the example you show, blocking while waiting for
each new byte. Or you could write a non blocking function, which
always return immediately and, either gives you the complete packet,
or tells you that no full packet has been received so far.
The non-blocking way may be a bit more complicated, but it is more
useful, because you program can do other stuff, and be responsive, while
waiting for a packet. Here is my take at it. It is based on a finite
state machine:
// Return whether the payload of this command is of string type.
bool command_payload_is_string(uint8_t command);

// Return the expected packet length for the provided command,
// excluding header and tail.
int packet_length(uint8_t command);

// Attempt to read a packet, but do not block.
// Return a pointer to a static buffer holding the packet,
// or nullptr if no complete packet was received this time.
uint8_t *read_packet() {
    const uint8_t expected_header = 0xaa;
    const uint8_t expected_tail[4] = {0xcc, 0x33, 0xc3, 0x3c};

    // Return immediately unless we have a byte to process.
    if (Serial.available() == 0) return nullptr;
    uint8_t data = Serial.read();

    static uint8_t buffer[BUFFER_SIZE];
    static int bytes_received, bytes_expected;
    static enum {
        EXPECTING_HEADER, EXPECTING_COMMAND,
        EXPECTING_STRING, EXPECTING_PAYLOAD, EXPECTING_TAIL
    } state = EXPECTING_HEADER;
    switch (state) {
        case EXPECTING_HEADER:
            if (data = expected_header)
                state = EXPECTING_COMMAND;
            break;
        case EXPECTING_COMMAND:
            buffer[0] = data;  // command byte
            bytes_received = 1;
            if (command_payload_is_string(data)) {
                state = EXPECTING_STRING;
                // bytes_expected is not relevant in this case.
            } else {
                state = EXPECTING_PAYLOAD;
                bytes_expected = packet_length(data);
            }
            break;
        case EXPECTING_STRING:
            if (data == '\0') {  // end of string
                buffer[bytes_received++] = '\0';
                state = EXPECTING_TAIL;
                bytes_expected = 4;
                bytes_received = 0;
            } else if (bytes_received < BUFFER_SIZE - 1) {
                buffer[bytes_received++] = data;
            } else {
                // Do nothing. As the string is too long
                // to fit in the buffer, this byte is discarded.
            }
            break;
        case EXPECTING_PAYLOAD:
            buffer[bytes_received++] = data;
            if (bytes_received >= bytes_expected) {
                state = EXPECTING_TAIL;
                bytes_expected = 4;
                bytes_received = 0;
            }
            break;
        case EXPECTING_TAIL:
            // If we don't get the expected tail,
            // give up and wait for a new header.
            if (data != expected_tail[bytes_received++]) {
                state = EXPECTING_HEADER;
                break;
            }
            // If the tail is all right, return the buffer
            // and get ready for the next packet.
            if (bytes_received >= bytes_expected) {
                state = EXPECTING_HEADER;
                return buffer;
            }
    }
    return nullptr;
}

It will be up to you to implement packet_length(). See jstola's
comment on why it is needed.
You would use read_packet() like this:
void loop() {
    uint8_t *packet = read_packet();
    if (packet)
        parse_and_handle(packet);

    // the rest of the program, never blocked by read_packet()...
}

Edit: According to your comment, some commands have a payload which
is a string of unknown length, but seemingly NUL-terminated. I
edited the code, and added an extra EXPECTING_STRING state to handle
this special case. It is up to you to implement the boolean function
command_payload_is_string().

53790: <p>I am trying to use <a href="https://www.topwaydisplay.com/product/smart-lcd/smart-lcd-module-HMT070ATA-1C" rel="nofollow noreferrer">Topway display</a> for HMI lcd with Arduino. This display using RS232C protocol <a href="https://www.topwaydisplay.com/sites/default/files/2020-06/Topway-Smart-LCD-SGTools-Handbook.pdf" rel="nofollow noreferrer">at docs</a> it has a &quot;Communication Packet Structure&quot; like this.</p>
<p><a href="https://i.stack.imgur.com/CF810.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/CF810.png" alt="enter image description here" /></a></p>
<p><strong>So my query should start with AA and end with CC 33 C3 3C</strong>.  This sample reading code was taking <a href="https://www.youtube.com/watch?v=t7VGK24kyLE" rel="nofollow noreferrer">topway youtube channel</a>.</p>
<pre><code>Serial.write(0xaa); // packet head
Serial.write(0x3e); // VP_N16 read command
Serial.write(0x00); // VP_N16 address
Serial.write(0x08);
Serial.write(0x00);
Serial.write(0x00);
Serial.write(0xcc); // packet tail
Serial.write(0x33); // packet tail
Serial.write(0xc3); // packet tail
Serial.write(0x3c); // packet tail
</code></pre>
<p>3E is the command (for reading) code and this is the list of all commands. <a href="https://www.topwaydisplay.com/sites/default/files/2020-06/Topway-Smart-LCD-SGTools-Handbook.pdf" rel="nofollow noreferrer">docs over here</a></p>
<p><a href="https://i.stack.imgur.com/y1WwH.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/y1WwH.png" alt="enter image description here" /></a></p>
<p>The point is parsing of smart LCD's response at the original. They use <a href="https://www.youtube.com/watch?v=t7VGK24kyLE" rel="nofollow noreferrer">this code</a>: (at first comment)</p>
<pre><code>     while (!Serial.available()) {}  
     if (Serial.read()==0xaa) {                     // packet head
     packet_OK=1;
     while (!Serial.available()) {}
     if (Serial.read()==0x3e) {                   // packet command
      while (!Serial.available()) {}  
      temp_h=Serial.read();                      // read back value high byte
      while (!Serial.available()) {}  
      temp_l=Serial.read();                      // read back value low byte
      while (!Serial.available()) {}  
      if (!(Serial.read()==0xcc)) {packet_OK=0;} // packet tail
      while (!Serial.available()) {}  
      if (!(Serial.read()==0x33)) {packet_OK=0;} // packet tail
      while (!Serial.available()) {}  
      if (!(Serial.read()==0xc3)) {packet_OK=0;} // packet tail
      while (!Serial.available()) {}  
      if (!(Serial.read()==0x3c)) {packet_OK=0;} // packet tail
      }
      else
      {packet_OK=0;}
     }
     else
     {packet_OK=0;}
</code></pre>
<p>It looks nasty and cumbersome because I must write different method for each LCD display commands. <strong>Is there any way to read all serial response them as a char array with starting marker &quot;0xAA&quot; and read until finishing marker &quot;0xCC 0x33 0xC3 0x3C&quot; and after that parse it?</strong></p>
<p>You can watch and check this code on <a href="https://www.youtube.com/watch?v=t7VGK24kyLE" rel="nofollow noreferrer">youtube</a></p>
 7508: <p>The short answer to your question is “yes, there is a way”. Multiple
ways actually. You could write a <em>blocking</em> function, which follows more
or less the logic of the example you show, blocking while waiting for
each new byte. Or you could write a <em>non blocking</em> function, which
always return <em>immediately</em> and, either gives you the complete packet,
or tells you that no full packet has been received so far.</p>
<p>The non-blocking way may be a bit more complicated, but it is more
useful, because you program can do other stuff, and be responsive, while
waiting for a packet. Here is my take at it. It is based on a <a href="https://majenko.co.uk/blog/finite-state-machine" rel="nofollow noreferrer">finite
state machine</a>:</p>
<pre class="lang-cpp prettyprint-override"><code>// Return whether the payload of this command is of string type.
bool command_payload_is_string(uint8_t command);

// Return the expected packet length for the provided command,
// excluding header and tail.
int packet_length(uint8_t command);

// Attempt to read a packet, but do not block.
// Return a pointer to a static buffer holding the packet,
// or nullptr if no complete packet was received this time.
uint8_t *read_packet() {
    const uint8_t expected_header = 0xaa;
    const uint8_t expected_tail[4] = {0xcc, 0x33, 0xc3, 0x3c};

    // Return immediately unless we have a byte to process.
    if (Serial.available() == 0) return nullptr;
    uint8_t data = Serial.read();

    static uint8_t buffer[BUFFER_SIZE];
    static int bytes_received, bytes_expected;
    static enum {
        EXPECTING_HEADER, EXPECTING_COMMAND,
        EXPECTING_STRING, EXPECTING_PAYLOAD, EXPECTING_TAIL
    } state = EXPECTING_HEADER;
    switch (state) {
        case EXPECTING_HEADER:
            if (data = expected_header)
                state = EXPECTING_COMMAND;
            break;
        case EXPECTING_COMMAND:
            buffer[0] = data;  // command byte
            bytes_received = 1;
            if (command_payload_is_string(data)) {
                state = EXPECTING_STRING;
                // bytes_expected is not relevant in this case.
            } else {
                state = EXPECTING_PAYLOAD;
                bytes_expected = packet_length(data);
            }
            break;
        case EXPECTING_STRING:
            if (data == '\0') {  // end of string
                buffer[bytes_received++] = '\0';
                state = EXPECTING_TAIL;
                bytes_expected = 4;
                bytes_received = 0;
            } else if (bytes_received &lt; BUFFER_SIZE - 1) {
                buffer[bytes_received++] = data;
            } else {
                // Do nothing. As the string is too long
                // to fit in the buffer, this byte is discarded.
            }
            break;
        case EXPECTING_PAYLOAD:
            buffer[bytes_received++] = data;
            if (bytes_received &gt;= bytes_expected) {
                state = EXPECTING_TAIL;
                bytes_expected = 4;
                bytes_received = 0;
            }
            break;
        case EXPECTING_TAIL:
            // If we don't get the expected tail,
            // give up and wait for a new header.
            if (data != expected_tail[bytes_received++]) {
                state = EXPECTING_HEADER;
                break;
            }
            // If the tail is all right, return the buffer
            // and get ready for the next packet.
            if (bytes_received &gt;= bytes_expected) {
                state = EXPECTING_HEADER;
                return buffer;
            }
    }
    return nullptr;
}
</code></pre>
<p>It will be up to you to implement <code>packet_length()</code>. See jstola's
comment on why it is needed.</p>
<p>You would use <code>read_packet()</code> like this:</p>
<pre class="lang-cpp prettyprint-override"><code>void loop() {
    uint8_t *packet = read_packet();
    if (packet)
        parse_and_handle(packet);

    // the rest of the program, never blocked by read_packet()...
}
</code></pre>
<p><strong>Edit</strong>: According to your comment, some commands have a payload which
is a string of unknown length, but <a href="https://arduino.stackexchange.com/questions/85360/how-to-convert-a-string-to-hex-array">seemingly NUL-terminated</a>. I
edited the code, and added an extra <code>EXPECTING_STRING</code> state to handle
this special case. It is up to you to implement the boolean function
<code>command_payload_is_string()</code>.</p>
