0

I have a project that uses a Telit LE910 4G LTE Modem to allow me some basic monitoring via SMS and HTTP POST to Exosite.

I can connect to a Exosite server with and send a successful HTTP GET request to pull a UNIX timestamp (as a test).

Problem

The problem lies with receiving. The information received cuts off halfway through the HTTP headers, before reaching the body of the information.

This image shows what prints out on the Serial monitor:

Serial Monitor

This image shows what the expected result should be:

Expected

And this image shows the code the executes to send the request:

enter image description here

The result is the same with PrintModemResponse() and with GetModemResponse(). Additionally, I have the same issue with receiving SMS messages. The data appears to cut off partway through the expected response. The modem serial (SW_Serial) baud rate is set to 115200.

Question

Am I doing something wrong with reading the Serial information from the modem? Am I hitting a timeout of some sort?

1

First, please don't post screen images. Just select the text and paste it into your questions. You can copy code from the editor or messages from the Serial Monitor window. Paste into your post, select the section again, and press ctrl-K to format it as a code block in your question (like below).

Am I doing something wrong with reading the Serial information from the modem?

Yes, you are using delay to "wait" for the response to appear.

Am I hitting a timeout of some sort?

In a way. The input buffer only has room for 64 characters. If you don't read any of them because you're blocked at a delay statement, the 65th character (and all others) will be ignored. These answer your specific questions.


Further Suggestions:

To fix the real problem, not just the symptoms, you will have to change your sketch structure.

There are many ways to implement this in a non-blocking way (i.e., without using delay). I prefer Finite-state Machines; there are many good tutorials for this technique (e.g., Nick Gammon's site).

Although it's not as flexible, you could also use your own while loop for each response, with a timeout. Here's a simple example that reads one line.

char resp[20];
char resp_Len = 0;
const uint32_t TIMEOUT_MS = 5000UL; // 5 seconds

AltSoftSerial modem_port; // pins 8 & 9 ONLY

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

void loop()
{
  uint32_t startTime = millis();
  while (millis() - startTime < TIMEOUT_MS) {
    if (modem_port.available()) {
      char c = modem_port.read();
      if (c == '\n') {
        resp[ respLen ] = '\0'; // NUL terminate
        respLen = 0; // reset for another line
        break;

      } else if (respLen < sizeof(resp)-1) { // Room for more?
        resp[ respLen++ ] = c; // accumulate one more character
      }
    }
  }

  Serial.print( F("Response = '") ); // F macro saves RAM
  Serial.print( resp );
  Serial.println( '\'' );
}

But when the response is long (hundreds of bytes), you may not have sufficient RAM to save the whole response, and then parse it. You MUST use an FSM to parse the bytes as they are received.

For your application, you should skip all the stuff at the front and watch for the "Content-type:" pattern and the two newlines, then save the next line (as above) for parsing:

enum state_t { SKIP_TO_CONTENT_TYPE, SKIP_TO_EMPTY_LINE, SAVING_TIMESTAMP };
state_t state = SKIPPING
uint8_t index = 0;

const char ContentType[] = "Content_type:";

bool watchForTimestamp()
{
  if (modem_port.available()) {
    char c = modem_port.read();

    // Handle this one character, based on our current "state"

    switch (state) {
      case SKIP_TO_CONTENT_TYPE:
        if (c == '\n')
          index = 0;

        else if ((index < sizeof(ContentType) && (c == ContentType[ index ])) {
          // next char matches
          index++;

          if (index == sizeof(ContentType)) {
            // All matched!
            state = SKIP_TO_EMPTY_LINE;
            index = 0;
          }
        }
        break;

      case SKIP_TO_EMPTY_LINE:
        if (c == '\n') {
          index++; // this counts newlines since "Content-type:"

          if (index == 2) {
            state = SAVING_TIMESTAMP;
            index = 0;
          }
        }
        break;

      case SAVING_TIMESTAMP:
            ... Hint: something like the line-saving technique above.
        if (something) {
          return true;
        }
        break;
    }
  }

  return false;
}

Some part of loop will just call waitForTimestamp over and over:

void loop()
{
    ...

  bool gotResponse = false;
  state = SKIP_TO_CONTENT_TYPE;

  while (millis() - startTime < TIMESTAMP_TIMEOUT) {
    gotResponse = waitForTimeStamp();
    if (gotResponse) {
      // parse the timestamp from the resp array
         ...
      break;
    }
  }

  if (gotResponse) {
    Serial.print( F("Timestamp = ") );
    Serial.print( resp );
    // now you can parse the small response array
       ...

  } else {
    Serial.println( F("Didn't receive timestamp!") );
     ... other error code
  }
}

This may be a "main" FSM for loop, with a nested FSM for parsing the HTTP response. The rest is up to you. :)

BTW, SoftwareSerial is very inefficient. It cannot send and receive at the same time, and it disables interrupts for long periods of time.

If you can use pins 8 & 9, you should use AltSoftSerial instead. It is very efficient.

If you can't switch to those pins, you could use NeoSWSerial if the modem is running at 9600, 19200 or 38400.

2
  • Thank you for your response. I will do some research into FSM and your suggestions and attempt to incorporate them. I guess the trick will be in the loop, catching the response before it fills the buffer. Just for clarification, I am using a hardware UART for the modem (defined as SW_Serial, SW for Skywire) at 115200 baud. Thank you for the suggestion of AltSoftSerial though, may come in handy for other projects. Jan 15 '17 at 16:40
  • At the modem's default baud rate of 115200, I found I needed to check SW_Serial for data so frequently that it was basically blocking code in and of itself. I found two alternatives: The first is to implement flow control, which I have not done but may look into should the need arise. The second was easier for me, and it was to just modify the HardwareSerial.h header file and up the buffer to 256 bytes. This should be more than enough for my purposes. Jan 17 '17 at 4:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.