Ammar
Ammar

Reputation: 1233

How to handle GSM buffer on the Microcontroller?

I have a GSM module hooked up to PIC18F87J11 and they communicate just fine . I can send an AT command from the Microcontroller and read the response back. However, I have to know how many characters are in the response so I can have the PIC wait for that many characters. But if an error occurs, the response length might change. What is the best way to handle such scenario?

For Example:

AT+CMGF=1  

Will result in the following response.

\r\nOK\r\n

So I have to tell the PIC to wait for 6 characters. However, if there response was an error message. It would be something like this.

\r\nERROR\r\n

And if I already told the PIC to wait for only 6 characters then it will mess out the rest of characters, as a result they might appear on the next time I tell the PIC to read the response of a new AT command.

What is the best way to find the end of the line automatically and handle any error messages?

Thanks!

Upvotes: 1

Views: 1254

Answers (2)

vines
vines

Reputation: 5225

In a single line

There is no single best way, only trade-offs.

In detail

The problem can be divided in two related subproblems.

1. Receiving messages of arbitrary finite length

The trade-offs:

  • available memory vs implementation complexity;
  • bandwidth overhead vs implementation complexity.

In the simplest case, the amount of available RAM is not restricted. We just use a buffer wide enough to hold the longest possible message and keep receiving the messages bytewise. Then, we have to determine somehow that a complete message has been received and can be passed to further processing. That essentially means analyzing the received data.

2. Parsing the received messages

Analyzing the data in search of its syntactic structure is parsing by definition. And that is where the subtasks are related. Parsing in general is a very complex topic, dealing with it is expensive, both in computational and laboriousness senses. It's often possible to reduce the costs if we limit the genericity of the data: the simpler the data structure, the easier to parse it. And that limitation is called "transport layer protocol".

Thus, we have to read the data to parse it, and parse the data to read it. This kind of interlocked problems is generally solved with coroutines.

In your case we have to deal with the AT protocol. It is old and it is human-oriented by design. That's bad news, because parsing it correctly can be challenging despite how simple it can look sometimes. It has some terribly inconvenient features, such as '+++' escape timing!

Things become worse when you're short of memory. In such situation we can't defer parsing until the end of the message, because it very well might not even fit in the available RAM -- we have to parse it chunkwise.

...And we are not even close to opening the TCP connections or making calls! And you'll meet some unexpected troubles there as well, such as these dreaded "unsolicited result codes". The matter is wide enough for a whole book. Please have a look at least here: http://en.wikibooks.org/wiki/Serial_Programming/Modems_and_AT_Commands. The wikibook discloses many more problems with the Hayes protocol, and describes some approaches to solve them.

Upvotes: 3

kkrambo
kkrambo

Reputation: 7057

Let's break the problem down into some layers of abstraction.

At the top layer is your application. The application layer deals with the response message as a whole and understands the meaning of a message. It shouldn't be mired down with details such as how many characters it should expect to receive.

The next layer is responsible from framing a message from a stream of characters. Framing is extracting the message from a stream by identifying the beginning and end of a message.

The bottom layer is responsible for reading individual characters from the port.

Your application could call a function such as GetResponse(), which implements the framing layer. And GetResponse() could call GetChar(), which implements the bottom layer. It sounds like you've got the bottom layer under control and your question is about the framing layer.

A good pattern for framing a stream of characters into a message is to use a state machine. In your case the state machine includes states such as BEGIN_DELIM, MESSAGE_BODY, and END_DELIM. For more complex serial protocols other states might include MESSAGE_HEADER and MESSAGE_CHECKSUM, for example.

Here is some very basic code to give you an idea of how to implement the state machine in GetResponse(). You should add various types of error checking to prevent a buffer overflow and to handle dropped characters and such.

void GetResponse(char *message_buffer)
{
  unsigned int state = BEGIN_DELIM1;
  bool is_message_complete = false;

  while(!is_message_complete)
  {
    char c = GetChar();
    switch(state)
    {
      case BEGIN_DELIM1:
        if (c = '\r')
          state = BEGIN_DELIM2;
        break;

      case BEGIN_DELIM2:
        if (c = '\n')
          state = MESSAGE_BODY:
        break;

      case MESSAGE_BODY:
        if (c = '\r')
          state = END_DELIM;
        else
          *message_buffer++ = c;
        break;

      case END_DELIM:
        if (c = '\n')
          is_message_complete = true;
        break;
    }
  }
}

Upvotes: 2

Related Questions