Adventures with GPRS

Over the summer I purchased a GPRS Shield for Arduino, which is shield that allows the Arduino to communicate over the cell phone network using the General Packet Radio Service (GPRS).  The shield I purchased is based on a SIM900 module, which seems to be a widely-available component for this purpose.  I purchased my shield on eBay, but they are available from other source, too.  I'll explain more later, but I strongly suggest that you proceed carefully if you do decide to order from an eBay seller, as I had a rather unexpected issue pop up that made trying to use the shield more than a little frustrating.

My GPRS Shield

Step 1: Purchase a Prepaid SIM Card

Before I could use the modem, I needed to purchase a SIM Card and a Matching Service plan.  I didn;t want to wind up with a new monthly bill for this thing, as I was planning to use it only occasionally for a very special purpose.  So, I had hoped to find some kind of "pay as you use it" service plan.  After a lot of research, I found a plan on Amazon that was offered by T-Mobile called the "T-Mobile Sim Card Prepaid Kit."  It cost only $5.98 to purchase the plan and, by carefully selecting my options when I signed up, I was able to get a plan that costs me only $2/day and charges this only when I use the device.  Surprisingly, once I signed up I found I had a $30 credit included with the plan.  The sign up process seemed to require me to add additional money to activate it, so I added $10 and would up with $40 of service credit for a total cost of only $15.98.  Unfortunately, the plan came with a Micro size SIM and not the Standard size SIM my shield needed, so I was forced to make a trip to the T_Mobile store where they sold me a thin piece of cut plastic (a SIM adapter) for $5...

Step 2: Insert SIM Card and Get Frustrated

According to the instructions, I had to first insert my SIM card to activate the GPRS shield so it could identify itself to the network.  This seemed like a simple step but, after spending several hours trying to get the LED that's supposed indicate connection status to blink in the expected way, I was no closer to making anything work.  I was starting to wonder if the card was broken, or if perhaps my bargain service plan was not such a bargain after all.  So, I did what any good engineer does when faced with a problem like this, I started to scour the internet for advice.  There seemed to be a lot of posts suggesting that I needed to add a capacitor to the pins that supply power to the SIM.  So, using a diagram that showed proper SIM pinout, I went probing around to find how to do this.  However, after about 30 minutes of head scratching, I had another mystery on my hands.  The voltage to the SIM was not showing up on the expected pin.  I sat staring at the diagram for more than a few minutes until it suddenly hit me ... the SIM hard holder on the PCB was installed incorrectly.  It was reversed!  Pulling up a photo of a nearly identical shield on the Internet, I was quickly able to confirm this.  And, after a little de-soldering and re-soldering to rotate the SIM holder 180 degrees, I finally had a working GPRS Shield.  Note: insert snide comment here about the dangers of using cheap, imported crap purchased via eBay.

The incorrectly installed SIM Holder

Step 3: Look for Compatible Software

The SIM900 module, which is the heart of the shield, is a serial device that's intended to be commanded by Hayes-style AT commands.  So, I started out by programming the Arduino to simply pass through commands typed into the Arduino's Serial Monitor page.  Using these I was able to send an SMS (TXT message) to my phone, which was a real confidence booster.  Unfortunately, things started to downhill again when I tried to use a GPRS Socket connection to read data from a web server.  Rather than bore you with the long version of this story, here's a list of things I learned the hard way over the next several days:

Step 4: Write my Own Software

After wasting a lot of time trying to get other people software to operate in a consistent fashion, I decided to write a simple piece of "test bench" software I could use to explore the effects of the different AT commands and find out why they seemed to work one minute only to fail the next time I tried the same sequence.  The result is attached below as Sim900TestBench.ino.  I'm not going to hold this code up as an example of great code design, but it should help you understand the sequence of commands needed to use GPRS to fetch a web page using the HTTP protocol.  The code is driven by a set of single letter commands that select and run different AT command sequences.  To send a command, type a letter into the Serial Monitor's text box and press "Send".  The set of supported letter commands is, as follows:

Note: the code contains debug statements that echo the commands and responses sent to and from the SIM900 module.  Here's an example of what's displayed in response to the '.' (period) command in the above list with all debugging features enabled:

Send HTTP Request

SND: ATE0\r

RSP: \r\nOK\r\n

SND: AT+CGATT?\r

RSP: \r\n+CGATT: 0\r\n\r\nOK\r\n

SND: AT+CGATT=1\r

RSP: \r\nOK\r\n

SND: AT+CSTT="epc.tmobile.com"\r

RSP: \r\nOK\r\n

SND: AT+CIICR\r

RSP: \r\nOK\r\n

SND: AT+CIFSR\r

RSP: \r\n100.129.140.152\r\n

SND: AT+CIPSTART="TCP","www.play-hookey.com","80"\r

RSP: \r\nOK\r\n\r\nCONNECT OK\r\n

SND: AT+CIPSEND=27\r

RSP: \r\n>

 

SEND OK

HTTP/1.1 200 OK

Date: Sun, 09 Feb 2014 04:10:08 GMT

Server: Apache

Content-Type: text/html

Connection: close

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN">

<html>

<head>

<title>Demonstration HTML Test Page</title>

</head>

<frameset rows="115, *"> 

<frame name="Header" src="header.html" noresize marginwidth="4" marginheight="0" scrolling="no" id="Header">

<frameset cols="50%,*"> 

<frame name="Source" src="source.html" id="Source">

<frame name="Result" src="result.html" id="Result">

</frameset>

<noframes> 

<body bgcolor="#FFCCCC">

<h1 style="align:center">

Frames and JavaScript Required!

</h1>

<p style="text-indent:20px">

This page uses both frames and JavaScript, to enable users to type in their own HTML and then display it

side by side with the source. You will need Netscape 3 or later, or IE4 or later (or a compatible browser

that recognizes both frames and JavaScript) in order to make use of this page.

</p>

</body>

</noframes> </frameset>

</html>

CLOSED

HTTP timeout

Special Notes about the Code

If you look closely at the example code, you see places where the #else case of the SHOW_RSP code contains a call to delay(100).  I'm not sure why, but this delay seems to be needed to get a consistent response from the commands.  If you remove the delay the code works sometimes, but not others.

Also, you can issue a '.' command after a power up and this will fetch the web page, but leave the GPRS subsystem power up.  You can then use the 'r' command to fetch the same page again and again for as long as you like.  However, after a substantial delay (unsure as to the exact time needed) you will need to use the 's' (shutdown) command to close down the GPRS connection before you can use the '.' (period) command again.  You can also use the 's' command after an error to put the system back into a known state (or power down, the power up again).

Unresolved Issues

The code documented here is only a test bench, but does demonstrate the steps needed to fetch data via an HTTP GET request.  It should be fairly easy to extend this to perform other network protocols, such as an HTTP POST operation, but I have yet to try this.  One issue that bugs me, however, is that it's difficult to handle GET requests that don't return an indication of how many bytes the code should expect to read.  The HTTP protocol does provide the content-length header element to handle this, but this is optional and, as you can see, one is not returned by GET request shown above.  This means that I had to use a rather long timeout to be sure I'd read all the bytes.  The SIP900 does return a "CLOSED" response in the text stream that could be used for this purpose, but that poor practice from a coding standpoint because the word CLOSED could be included as part of the data being returned.  However, even using a timeout to detect the end of data is problematic, as the return data could be delayed by a variety of network issues such as a poor cellular connection, or a server that responds slowly due to congestion, or other factors.  Ideally, the best solution is to have control of the server side, too, and make sure that a proper content-length value is included in the response header.  In that case, the connection can then be terminated on the receiving side by issuing a "AT+CIPCLOSE" command in order to turn around the socket connection more quickly.  Note: as I've yet to fully explore all the available AT commands for the SIM900, it's possible that there are commands, or modes that may help me solve these problems.  If so, I'll try to update this page when I've discovered a new solution, or technique.

The AT+CIPHEAD Command

Well, the digital ink was hardly dry on this web page when I discovered a possible solution to detecting how many bytes to read on an incoming socket connection.  The trick is to use the AT+CIPHEAD=1 command to turn on a special mode that adds an IP header prefix to chunks of incoming data that indicates how many bytes of data follow.  For example, if I add the following line of code just after the "case 'r':" case statement:

sendWaitOk("AT+CIPHEAD=1\r"); 

and then rerun the '.' (period) command the returned data will now look like this:

SND: AT+CIPSEND=27\r

RSP: \r\n>

 

SEND OK

+IPD,931:HTTP/1.1 200 OK

Date: Sun, 09 Feb 2014 22:30:36 GMT

Server: Apache

Content-Type: text/html

Connection: close

<html>

... truncated for brevity

As you can see, the beginning of the HTTP response sent back from the server is now prefixed with the text "+IPD,931:", which indicates that 931 bytes of data will follow just after the ":".  In this case, the entire response from the web server fits into a single, 931 byte chunk.  To get more data, you can change the URL to point to a different server, such as www.google.com by making a few changes to the code, such as:

    if (!sendWaitSeq("AT+CIPSTART=\"TCP\",\"www.google.com\",\"80\"\r", "CONNECT OK\r\n"))

      break;

    // Note: 27 is the length of the GET command string written after this command

    if (!sendWaitSeq("AT+CIPSEND=28\r", "\r\n>"))

      break;

    delay(100);

    Serial1.print("GET /index.html HTTP/1.0\r\n\r\n");

Ignoring the data that comes back, you should see a series of blocks of "+IPD" prefixes, like this:

+IPD,1400:

+IPD,1400:

+IPD,1400:

+IPD,1460:

+IPD,1460:

... several more blocks of 1460 bytes

+IPD,814:

Now, I have no idea why the first three blocks are 1400 bytes in size and remaining full blocks are all 1460 bytes in size, but this does suggest a possible way to detect when all the data has beens sent by the server.  That is, look for the block that has a size <1400 and assume that this is the last block of data.  But, there are a few problems with this.  First, the code will need to made more complex in order to parse and strip out the "+IPD" prefix blocks.  Also, what if the last block of data is exactly 1460 (or 1400) bytes long?  How do we know this is the last block?  Hmm, more experiments are needed here.  Stay tuned...