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?

asked Jan 13, 2017 at 23:33

1 Answer 1

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.

answered Jan 15, 2017 at 4:29
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. Commented Jan 15, 2017 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. Commented Jan 17, 2017 at 4:26

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.