Introduction: SM5100B GPRS and General Notes
This instructable is for the use of the SM5100B-D shield from sparkfun: https://www.sparkfun.com/products/9607
Its no good to you for the other GSM shields, Sorry.
I spent so, so long hacking at this stupid shield trying to make sense of the +SIND's and other rubbish - these things do work, they just test your patience. The minimal and poorly written documentation is useful if not as frustrating, hopefully this will save you some time.
Dont take this all for gospel, Im not an electronics or programming whizz, I have just persevered and learnt a few things.
Please feel free to question anything written here and Ill try to update the instruct-able if you can contribute.
Its no good to you for the other GSM shields, Sorry.
I spent so, so long hacking at this stupid shield trying to make sense of the +SIND's and other rubbish - these things do work, they just test your patience. The minimal and poorly written documentation is useful if not as frustrating, hopefully this will save you some time.
Dont take this all for gospel, Im not an electronics or programming whizz, I have just persevered and learnt a few things.
Please feel free to question anything written here and Ill try to update the instruct-able if you can contribute.
Step 1: Things to Consider
I have found some good resources,
The most useful being some actual working code:
https://github.com/tobek/SM5100B-GPRS/blob/master/...
&
A good intro into setup
http://tronixstuff.com/2011/01/19/tutorial-arduino...
Some things to note:
1) Software serial is not the best option, in my tests it doesn't keep up with the serial output from the GSM module, and if you cant keep up then the data is dropped and your stuck. Software serial is ok for grabbing the return codes from your AT commands but when it comes to receiving your actual data, like http responses - it just doesn't keep up at all. I have seen mention that the 9600 baud rate that the SM5100B-D is set to is not fast enough, I have not found this to be true, as long as I use a hardware UART I get all the data back from the shield.
2) Power - you cant run this shield from the Arduino Power - you just cant. I tried to ignore these warnings, dont waste your time. Feed it 5 volts from a transformer or battery capable of supplying 2-3 amps. You will find that if you dont it will just keep overdrawing on your Arduino when it goes to transmit and everything will just reset.
3) Antenna, you need one.
I have actually been able to get +SIND:4 without one, but I have been getting full reception with one of those tiny little duck Antennas - I got two for a couple of dollars right here:http://www.electrodragon.com/?product=quad-band-ce... For Australians - You can pick up an antenna from Jaycar - pricey but dont have to wait on delivery: http://www.jaycar.com.au/productView.asp?ID=AR3310
4) Documentation, the official AT command set documentation comes in handy, only the author truly knows what on earth he/she was trying to describe, its bad, real bad, but it comes in handy: https://www.sparkfun.com/datasheets/CellularShield...
I guess sparkfun wrote this more specific GPRS / IP stuff, as it kind of makes sense in comparison:
https://www.sparkfun.com/datasheets/CellularShield/SM5100B%20TCPIP%20App%20Note.pdf
Step 2: Power
Power:
As mentioned, you need external power. Im using a 3 amp 5 volt regulated power supply. Negative into GND and positive into VIN. Picture 1
As mentioned, you need external power. Im using a 3 amp 5 volt regulated power supply. Negative into GND and positive into VIN. Picture 1
Step 3: Serial Connections
As mentioned, I have had no luck using the software serial library to talk to the SM5100B-D.
Since you need to use a Hardware UART for the GSM stuff, and you need to use a serial connection also for your debugging purposes, you need either more hardware UARTS or A USB serial adaptor.
So the options are:
1) Get a Mega 2560 - official from Arduino: http://arduino.cc/en/Main/arduinoBoardMega2560
Or I prefer the free tronics EtherMega:http://www.freetronics.com/collections/arduino/pro...
These Arduinos have 4 hardware UARTS, plenty!
2) Use your standard UNO or whatever and also make use of a USB serial adaptor such as the one I have from freetronics:http://www.freetronics.com/products/usb-serial-ada... or one of the prolific based usb serial converters that are all over ebay. (If using the ebay jobbies - check that they are 5 volt logic not 12v, 12v will fry arduino!)
I'm using option 2, usb Serial Adaptor.
With RX and TX going to pins 2 and 3 on my uno. Vout & Gnd to the Uno's Gnd and Vin.
Conveniently this Serial adaptor will power the uno as well, if your using a standard serial converter cable like the prolific ones you'll need an external 5 volt source for your arduino, you can take it from the SM5100B-D 5v and GND.
From the SM5100B-D i have pins 2 and 3 connected to the Hardware UART on the UNO pins 0 and 1.
You can see I have not stacked the GSM board ontop of the UNO!
This is because when I need to upload sketches to the UNO it cannot be done through the software serial pins that and the USB serial adaptor. I have to disconnect the SM5100B-D serial connection and plug the USB cable into the uno to upload the sketch. The Arduinos will only accept sketches uploaded to pins 0/1!
And I have to remember to disconnect the SM5100B-D from these pins while uploading code it will not know what to do with that info being dropped on pins 0/1 during this time, it might also be dangerous for one or the other board? Either way when uploading code disconnect the serial connection between the uno and SM5100B-D and then reconnect again.
So the first photo is the connection when I am running my GPRS code and debugging, with the software serial port sending data out pins 2/3 to the USB serial adaptor to the serial monitor, and the hardware UART pins 0/1 doing the high speed communication with the SM5100B-D.
The second photo is what I have to do for uploading sketches.
Yes It would be easier to just use a MEGA 2560!
Since you need to use a Hardware UART for the GSM stuff, and you need to use a serial connection also for your debugging purposes, you need either more hardware UARTS or A USB serial adaptor.
So the options are:
1) Get a Mega 2560 - official from Arduino: http://arduino.cc/en/Main/arduinoBoardMega2560
Or I prefer the free tronics EtherMega:http://www.freetronics.com/collections/arduino/pro...
These Arduinos have 4 hardware UARTS, plenty!
2) Use your standard UNO or whatever and also make use of a USB serial adaptor such as the one I have from freetronics:http://www.freetronics.com/products/usb-serial-ada... or one of the prolific based usb serial converters that are all over ebay. (If using the ebay jobbies - check that they are 5 volt logic not 12v, 12v will fry arduino!)
I'm using option 2, usb Serial Adaptor.
With RX and TX going to pins 2 and 3 on my uno. Vout & Gnd to the Uno's Gnd and Vin.
Conveniently this Serial adaptor will power the uno as well, if your using a standard serial converter cable like the prolific ones you'll need an external 5 volt source for your arduino, you can take it from the SM5100B-D 5v and GND.
From the SM5100B-D i have pins 2 and 3 connected to the Hardware UART on the UNO pins 0 and 1.
You can see I have not stacked the GSM board ontop of the UNO!
This is because when I need to upload sketches to the UNO it cannot be done through the software serial pins that and the USB serial adaptor. I have to disconnect the SM5100B-D serial connection and plug the USB cable into the uno to upload the sketch. The Arduinos will only accept sketches uploaded to pins 0/1!
And I have to remember to disconnect the SM5100B-D from these pins while uploading code it will not know what to do with that info being dropped on pins 0/1 during this time, it might also be dangerous for one or the other board? Either way when uploading code disconnect the serial connection between the uno and SM5100B-D and then reconnect again.
So the first photo is the connection when I am running my GPRS code and debugging, with the software serial port sending data out pins 2/3 to the USB serial adaptor to the serial monitor, and the hardware UART pins 0/1 doing the high speed communication with the SM5100B-D.
The second photo is what I have to do for uploading sketches.
Yes It would be easier to just use a MEGA 2560!
Step 4: +SIND 4
We need to see a +SIND: 4 response from the SMB5100B-D, ( im just gonna call that the GSM card now, sick of typing SMB5100B-D).
So some easy code for checking this:
#include
#define GSMSerial Serial // the hardware serial will now be known as GSMSerial, to avoid confusion
SoftwareSerial SMSerial(2,3); //Using Software Serial as second UART -
char incoming_char=0; //Will hold the incoming character from the software Serial Port.
void setup()
{
//Initialize serial ports for communication.
SMSerial.begin(9600);
GSMSerial.begin(9600);
SMSerial.println("Starting SM5100B Communication...");
}
void loop()
{
if(GSMSerial.available()>0) //If a character comes in from the cellular module...
{
SMSerial.write(GSMSerial.read()); //write it out to the serial monitor so we can see it
}
if(SMSerial.available() >0) //If you type something into the serial monitor
{
incoming_char=SMSerial.read(); //Get the character coming from the terminal
GSMSerial.print(incoming_char); //Send the character to the cellular module.
}
}
You should see the following output on your serial monitor:
Starting SM5100B Communication...
+SIND: 1
+SIND: 10,"SM",1,"FD",1,"LD",1,"MC",1,"RC",1,"ME",1
+STIN:0
+SIND: 11
+SIND: 3
+SIND: 4
If you get nothing, you probably mixed up the TX RX pins on the serial monitor.
If you get "Starting SM5100B Communication..." then a whole lot of gobbly gook then you may have mixed up the serial TX RX pins between uno 0/1 and GSM 2/3
If its all just rubbish output then check if your serial monitor is set to 9600!
BUT If the last line you got was "+SIND: 4" then your luckier than most:)
If you got anything else as the last line, you need to find out why. You'll get more info on that here.
+SIND: 4 means we are registered on the mobile network and ready to do stuff.
For Example, type into the serial monitor, ATDXXXXXXXXXX
With the X's replaced with your own mobile number, and hit enter. (Make sure NL & CR are selected in the drop-down box at the bottom of the serial monitor). The sketch you uploaded will pass this to your GSM module and it will dial your mobile. You can send the command ATH to hangup.
Step 5: GPRS
If you got this far and you can either send an SMS or make a call, or some other way validate that you indeed have a functioning connection to the mobile network then we can try to get a GPRS session going.
This code is a hacked up from a sample I found by a guy named Toby: https://github.com/tobek/SM5100B-GPRS/blob/master/... it was pretty much the only functioning bit of code on the internet! Thank You Toby Fox!!
It calls out to httpbin.org and makes a http get request. httpbin.org is a handy set of pages for debugging http stuff.
You may want to just go and grab Toby's sample instead cause its the original, probrably better and is well documented, although this one below is already modified to use the Hardware UART for GSM and software for serial monitor as mentioned earlier.
// Using the Hardware UART Initially to program the Sketch,
// Disconnect the GSM serial connection, plug usb into uno and upload sketch.
//Then to test the sketch, then remove the usb from uno and plug into the USB-Serial module.
// plug the GSM modem serial wires back into the hardware UART pins 0&1 again
//The idea is to use the GSM modem with the hardware UART as the Software Serial Library
//is to slow for GSM modem, but its obviously fine for debugging stuff
#include
#define GSMSerial Serial
SoftwareSerial SMSerial(2,3); //Using Software Serial as second UART -
const String apn = "internet"; // access-point name for GPRS
const String ip = "54.235.174.110"; // IP address of server we're connecting to
const String host = "httpbin.org"; // required in HTTP 1.1 - what's the name of the host at this IP address?
const String request = "GET /get?data=testing HTTP/1.1";
const String useragent = "Mozilla/5.0"; // for our purposes the user agent doesn't matter - if I understand correctly it's helpful to use something generic the server will recognize
void setup()
{
SMSerial.begin(9600);
SMSerial.println("Starting SM5100B Communication...");
GSMSerial.begin(9600);
waitTil("+SIND: 4"); // keep printing GSMSerial output til we get "+SIND: 4"
SMSerial.println("Module ready");
}
void loop()
{
SMSerial.println("Attaching GPRS...");
GSMSerial.println("AT+CGATT=1");
waitFor("OK");
SMSerial.println("Setting up PDP Context...");
GSMSerial.println("AT+CGDCONT=1,\"IP\",\""+apn+"\"");
waitFor("OK");
SMSerial.println("Activating PDP Context...");
GSMSerial.println("AT+CGACT=1,1");
waitFor("OK");
SMSerial.println("Configuring TCP connection to TCP Server...");
GSMSerial.println("AT+SDATACONF=1,\"TCP\",\""+ip+"\",80");
waitFor("OK");
SMSerial.println("Starting TCP Connection...");
GSMSerial.println("AT+SDATASTART=1,1");
waitFor("OK");
while (1) { // now we'll loop forever, checking the socket status and only breaking when we connect
SMSerial.println("Checking socket status:");
GSMSerial.println("AT+SDATASTATUS=1"); // we'll get back SOCKSTATUS and then OK
String sockstat = getMessage();
waitFor("OK");
if (sockstat=="+SOCKSTATUS: 1,0,0104,0,0,0") {
//SMSerial.println("Not connected yet. Waiting 1 second and trying again.");
delay(750);
}
else if (sockstat=="+SOCKSTATUS: 1,1,0102,0,0,0") {
SMSerial.print(" !!!Socket connected!!! ");
break;
}
else {
SMSerial.println("We didn't expect that.");
cellOutputForever();
}
}
int packetLength = 26+host.length()+request.length()+useragent.length(); // 26 is size of the non-variable parts of the packet, see SIZE comments below
SMSerial.println("Sending HTTP packet...");
GSMSerial.print("AT+SDATATSEND=1,"+String(packetLength)+"\r");
waitFor('>'); // wait for GSM module to tell us it's ready to recieve the packet
GSMSerial.print(request+"\r\n"); // SIZE: 2
GSMSerial.print("Host: "+host+"\r\n"); // SIZE: 8
GSMSerial.print("User-Agent: "+useragent+"\r\n\r\n"); // SIZE: 16
GSMSerial.write(26); // ctrl+z character: send the packet
waitFor("OK");
waitTil("+STCPD:1"); // this means data is received
//waitTil("+STCPC:1"); // this means socket is closed
SMSerial.println("Reading data from server...");
GSMSerial.println("AT+SDATAREAD=1"); // how we read data server has sent
cellOutputForever(); // just keep printing whatever GSM module is telling us
}
/* NOTES
*
* what is +STIN:1 ?
*
* to disconnect after transmission: AT+CGACT=0,1 breaks socket. AT+CGATT=0 seems to work more authoritatively?
* AT+SDATASTART=1,0 // close TCP connection
* AT+SDATASTATUS=1 // clear sent/ack bytes from SOCKSTATUS
*
*/
// ====== HELPER FUNCTIONS ====== //
// keep reading the serial messages we receive from the module
// loop forever until we get a nonzero string ending in \r\n - print and return that.
// TODO: implement a timeout that returns 0?
String getMessage() {
String s="";
while(1) {
if(GSMSerial.available()>0) {
s = s+(char)GSMSerial.read();
if (s.length()>1 && s[s.length()-2]=='\r' && s[s.length()-1]=='\n') { // if last 2 chars are \r\n
if (s==" \r\n" || s=="\r\n") { // skip these, move on
s="";
}
else { // we have a message!
SMSerial.println(s.substring(0,s.length()-2));
return s.substring(0,s.length()-2);
}
}
}
}
}
// for eating a single message we expect from the module
// prints out the next message from the module. if it's not the expected value, die
void waitFor(String s) {
String message=getMessage();
if (message != s) {
SMSerial.println("Wait, that's not what we were expecting. We wanted \""+s+"\"");
cellOutputForever();
}
delay(100); // wait for a tiny bit before sending the next command
}
// keep spitting out messages from the module til we get the one we expect
void waitTil(String s) {
String message;
while (1) {
message = getMessage();
if (message == s){
delay(100); // cause we're probably about to send another command
return;
}
}
}
// keep reading characters until we get char c
void waitFor(char c) {
while(1) {
if(GSMSerial.available()>0) {
if ((char)GSMSerial.read() == c) {
delay(100);
return;
}
}
}
}
// if something goes wrong, abort and just display cell module output so we can see error messages
// this will loop forever
void cellOutputForever() {
SMSerial.println("Now displaying cell module output forever");
GSMSerial.println("AT+SDATAREAD=1\r\n");
char incoming_char=0;
while(1)
{
if(GSMSerial.available()>0)
{
SMSerial.write(GSMSerial.read());
}
if(SMSerial.available() >0)
{
incoming_char=SMSerial.read(); //Get the character coming from the terminal
GSMSerial.print(incoming_char); //Send the character to the cellular module.
}
}
}
// like above, but in hex, useful for debugging
void cellHexForever() {
while(1) {
if(GSMSerial.available()>0) {
char c = (char)GSMSerial.read();
// Serial.print("a char: ");
SMSerial.print(c, HEX);
SMSerial.print(" ");
SMSerial.println(c);
}
}
}
// receive string such as "SOCKSTATUS: 1,1,0102,10,10,0"
// 0 is connection id. 1 is whether connected or not. 2 is status (0104 is connecting, 0102 is connected, others)
// 3 is sent bytes. 4 is acknowledged bytes. 5 is "received data counter"
// THIS FUNCTION WILL check that sent bytes == ack bytes, and return that value
// return 0 if they don't match or if amount of data is 0
int checkSocketString(String s) {
if (socketStringSlice(3,s) == 0)
return 0;
else if (socketStringSlice(3,s) == socketStringSlice(4,s))
return socketStringSlice(3,s);
else
return 0;
}
// returns the index of the nth instance of char c in String s
int nthIndexOf(int n, char c, String s) {
int index=0;
for (int i=0; i<=n; i++) {
index = s.indexOf(c,index+1);
}
return index;
}
// expects string such as "SOCKSTATUS: 1,1,0102,10,10,0"
// returns nth chunk of data, delimited by commas
int socketStringSlice(int n, String s) {
String slice = s.substring(nthIndexOf(n-1,',',s)+1,nthIndexOf(n,',',s));
char cArray[slice.length()+1];
slice.toCharArray(cArray, sizeof(cArray));
return atoi(cArray);
}
Here is the Serial monitor output you should expect
Starting SM5100B Communication...
ÐèÐÐÐ
+SIND: 1
+SIND: 10,"SM",1,"FD",1,"LD",1,"MC",1,"RC",1,"ME",1
+STIN:0
+SIND: 11
+SIND: 3
+SIND: 4
Module ready
Attaching GPRS...
OK
Setting up PDP Context...
OK
Activating PDP Context...
OK
Configuring TCP connection to TCP Server...
OK
Starting TCP Connection...
OK
Checking socket status:
+SOCKSTATUS: 1,0,0104,0,0,0
OK
Checking socket status:
+SOCKSTATUS: 1,1,0102,0,0,0
OK
!!!Socket connected!!! Sending HTTP packet...
OK
+STCPD:1
Reading data from server...
Now displaying cell module output forever
+SDATA:1,486,485454502F312E3120323030204F4B0D0A4163636573732D436F6E74726F6C2D416C6C6F772D4F726967696E3A202A0D0A436F6E74656E742D547970653A206170706C69636174696F6E2F6A736F6E0D0A446174653A205468752C203134204E6F7620323031332030353A32373A323320474D540D0A5365727665723A2067756E69636F726E2F302E31372E340D0A436F6E74656E742D4C656E6774683A203236330D0A582D43616368653A204D4953532066726F6D207478323272727065703233620D0A436F6E6E656374696F6E3A206B6565702D616C6976650D0A0D0A7B0A20202275726C223A2022687474703A2F2F6874747062696E2E6F72672F6765743F646174613D74657374696E67222C0A20202268656164657273223A207B0A2020202022436F6E6E656374696F6E223A2022636C6F7365222C0A2020202022486F7374223A20226874747062696E2E6F7267222C0A202020202243616368652D436F6E74726F6C223A20226D61782D6167653D323539323030222C0A2020202022557365722D4167656E74223A20224D6F7A696C6C612F352E30220A20207D2C0A2020226F726967696E223A202234392E3138302E3132322E3337222C0A20202261726773223A207B0A202020202264617461223A202274657374696E67220A20207D0A7D
OK
+SDATA:1,0,
OK
Note that the +SDATA output is in HEX, so that big old string of garble you will receive back needs to be converted.
You can paste it into this site to get an easy look at what the output was: http://www.dolcevie.com/js/converter.html
and after decoding the hex, our output is
HTTP/1.1 200 OK??Access-Control-Allow-Origin: *??Content-Type: application/json??Date: Thu, 14 Nov 2013 05:27:23 GMT??Server: gunicorn/0.17.4??Content-Length: 263??X-Cache: MISS from tx22rrpep23b??Connection: keep-alive????{? "url": "http://httpbin.org/get?data=testing",? "headers": {? "Connection": "close",? "Host": "httpbin.org",? "Cache-Control": "max-age=259200",? "User-Agent": "Mozilla/5.0"? },? "origin": "49.180.122.37",? "args": {? "data": "testing"? }?}?
So a successfull http get request, I never thought it would happen.
EDIT!!!
You can tell the 5100B to replay you the recived data in ASCII!! With one simple command "AT+SDATARXMD=1,1,0"
So before this line:"GSMSerial.println("AT+SDATAREAD=1"); // how we read data server has sent"
add the code:
GSMSerial.println("AT+SDATARXMD=1,1,0");
waitFor("OK");
This will set the output to ASCII just before you call the SDATAREAD!
And the data your presented will now look much more beautiful more like this:
+SSTR:1,HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cont12:16 GMT
Server: gunicorn/0.17.4
Content-Length: 264
X-Cache: MISS from tx22rrpep38a
Connection: keep-alive
{
"args": {
"data": "testing"
},
"headers": {
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0",
"Cache-Control": "max-age=259200",
"Connection": "close"
},
"url": "http://httpbin.org/get?data=testing",
"origin": "49.180.112.246"
}
With no need for you to write a Hex to ASCII conversion function:)
One big old problem this presents is the "OK" that is part of a HTTP response.
OK is also the way the SM5100B tells us its processed a command, whenever we se "OK\r\n" as a string in the serial buffer we know that the GSM module has answered and this forms the end of the message,,, so then its difficult to know if the "OK\r\n" at the beginning of the HTTP response is part of the response or the message at the end of successfully processing the AT Command.
"So look for the second "OK\r\n" I thought... but then what if there is also a "OK\r\n" in the body of the response!!!
Any ideas anyone, bueller, anyone?
This code is a hacked up from a sample I found by a guy named Toby: https://github.com/tobek/SM5100B-GPRS/blob/master/... it was pretty much the only functioning bit of code on the internet! Thank You Toby Fox!!
It calls out to httpbin.org and makes a http get request. httpbin.org is a handy set of pages for debugging http stuff.
You may want to just go and grab Toby's sample instead cause its the original, probrably better and is well documented, although this one below is already modified to use the Hardware UART for GSM and software for serial monitor as mentioned earlier.
// Using the Hardware UART Initially to program the Sketch,
// Disconnect the GSM serial connection, plug usb into uno and upload sketch.
//Then to test the sketch, then remove the usb from uno and plug into the USB-Serial module.
// plug the GSM modem serial wires back into the hardware UART pins 0&1 again
//The idea is to use the GSM modem with the hardware UART as the Software Serial Library
//is to slow for GSM modem, but its obviously fine for debugging stuff
#include
#define GSMSerial Serial
SoftwareSerial SMSerial(2,3); //Using Software Serial as second UART -
const String apn = "internet"; // access-point name for GPRS
const String ip = "54.235.174.110"; // IP address of server we're connecting to
const String host = "httpbin.org"; // required in HTTP 1.1 - what's the name of the host at this IP address?
const String request = "GET /get?data=testing HTTP/1.1";
const String useragent = "Mozilla/5.0"; // for our purposes the user agent doesn't matter - if I understand correctly it's helpful to use something generic the server will recognize
void setup()
{
SMSerial.begin(9600);
SMSerial.println("Starting SM5100B Communication...");
GSMSerial.begin(9600);
waitTil("+SIND: 4"); // keep printing GSMSerial output til we get "+SIND: 4"
SMSerial.println("Module ready");
}
void loop()
{
SMSerial.println("Attaching GPRS...");
GSMSerial.println("AT+CGATT=1");
waitFor("OK");
SMSerial.println("Setting up PDP Context...");
GSMSerial.println("AT+CGDCONT=1,\"IP\",\""+apn+"\"");
waitFor("OK");
SMSerial.println("Activating PDP Context...");
GSMSerial.println("AT+CGACT=1,1");
waitFor("OK");
SMSerial.println("Configuring TCP connection to TCP Server...");
GSMSerial.println("AT+SDATACONF=1,\"TCP\",\""+ip+"\",80");
waitFor("OK");
SMSerial.println("Starting TCP Connection...");
GSMSerial.println("AT+SDATASTART=1,1");
waitFor("OK");
while (1) { // now we'll loop forever, checking the socket status and only breaking when we connect
SMSerial.println("Checking socket status:");
GSMSerial.println("AT+SDATASTATUS=1"); // we'll get back SOCKSTATUS and then OK
String sockstat = getMessage();
waitFor("OK");
if (sockstat=="+SOCKSTATUS: 1,0,0104,0,0,0") {
//SMSerial.println("Not connected yet. Waiting 1 second and trying again.");
delay(750);
}
else if (sockstat=="+SOCKSTATUS: 1,1,0102,0,0,0") {
SMSerial.print(" !!!Socket connected!!! ");
break;
}
else {
SMSerial.println("We didn't expect that.");
cellOutputForever();
}
}
int packetLength = 26+host.length()+request.length()+useragent.length(); // 26 is size of the non-variable parts of the packet, see SIZE comments below
SMSerial.println("Sending HTTP packet...");
GSMSerial.print("AT+SDATATSEND=1,"+String(packetLength)+"\r");
waitFor('>'); // wait for GSM module to tell us it's ready to recieve the packet
GSMSerial.print(request+"\r\n"); // SIZE: 2
GSMSerial.print("Host: "+host+"\r\n"); // SIZE: 8
GSMSerial.print("User-Agent: "+useragent+"\r\n\r\n"); // SIZE: 16
GSMSerial.write(26); // ctrl+z character: send the packet
waitFor("OK");
waitTil("+STCPD:1"); // this means data is received
//waitTil("+STCPC:1"); // this means socket is closed
SMSerial.println("Reading data from server...");
GSMSerial.println("AT+SDATAREAD=1"); // how we read data server has sent
cellOutputForever(); // just keep printing whatever GSM module is telling us
}
/* NOTES
*
* what is +STIN:1 ?
*
* to disconnect after transmission: AT+CGACT=0,1 breaks socket. AT+CGATT=0 seems to work more authoritatively?
* AT+SDATASTART=1,0 // close TCP connection
* AT+SDATASTATUS=1 // clear sent/ack bytes from SOCKSTATUS
*
*/
// ====== HELPER FUNCTIONS ====== //
// keep reading the serial messages we receive from the module
// loop forever until we get a nonzero string ending in \r\n - print and return that.
// TODO: implement a timeout that returns 0?
String getMessage() {
String s="";
while(1) {
if(GSMSerial.available()>0) {
s = s+(char)GSMSerial.read();
if (s.length()>1 && s[s.length()-2]=='\r' && s[s.length()-1]=='\n') { // if last 2 chars are \r\n
if (s==" \r\n" || s=="\r\n") { // skip these, move on
s="";
}
else { // we have a message!
SMSerial.println(s.substring(0,s.length()-2));
return s.substring(0,s.length()-2);
}
}
}
}
}
// for eating a single message we expect from the module
// prints out the next message from the module. if it's not the expected value, die
void waitFor(String s) {
String message=getMessage();
if (message != s) {
SMSerial.println("Wait, that's not what we were expecting. We wanted \""+s+"\"");
cellOutputForever();
}
delay(100); // wait for a tiny bit before sending the next command
}
// keep spitting out messages from the module til we get the one we expect
void waitTil(String s) {
String message;
while (1) {
message = getMessage();
if (message == s){
delay(100); // cause we're probably about to send another command
return;
}
}
}
// keep reading characters until we get char c
void waitFor(char c) {
while(1) {
if(GSMSerial.available()>0) {
if ((char)GSMSerial.read() == c) {
delay(100);
return;
}
}
}
}
// if something goes wrong, abort and just display cell module output so we can see error messages
// this will loop forever
void cellOutputForever() {
SMSerial.println("Now displaying cell module output forever");
GSMSerial.println("AT+SDATAREAD=1\r\n");
char incoming_char=0;
while(1)
{
if(GSMSerial.available()>0)
{
SMSerial.write(GSMSerial.read());
}
if(SMSerial.available() >0)
{
incoming_char=SMSerial.read(); //Get the character coming from the terminal
GSMSerial.print(incoming_char); //Send the character to the cellular module.
}
}
}
// like above, but in hex, useful for debugging
void cellHexForever() {
while(1) {
if(GSMSerial.available()>0) {
char c = (char)GSMSerial.read();
// Serial.print("a char: ");
SMSerial.print(c, HEX);
SMSerial.print(" ");
SMSerial.println(c);
}
}
}
// receive string such as "SOCKSTATUS: 1,1,0102,10,10,0"
// 0 is connection id. 1 is whether connected or not. 2 is status (0104 is connecting, 0102 is connected, others)
// 3 is sent bytes. 4 is acknowledged bytes. 5 is "received data counter"
// THIS FUNCTION WILL check that sent bytes == ack bytes, and return that value
// return 0 if they don't match or if amount of data is 0
int checkSocketString(String s) {
if (socketStringSlice(3,s) == 0)
return 0;
else if (socketStringSlice(3,s) == socketStringSlice(4,s))
return socketStringSlice(3,s);
else
return 0;
}
// returns the index of the nth instance of char c in String s
int nthIndexOf(int n, char c, String s) {
int index=0;
for (int i=0; i<=n; i++) {
index = s.indexOf(c,index+1);
}
return index;
}
// expects string such as "SOCKSTATUS: 1,1,0102,10,10,0"
// returns nth chunk of data, delimited by commas
int socketStringSlice(int n, String s) {
String slice = s.substring(nthIndexOf(n-1,',',s)+1,nthIndexOf(n,',',s));
char cArray[slice.length()+1];
slice.toCharArray(cArray, sizeof(cArray));
return atoi(cArray);
}
Here is the Serial monitor output you should expect
Starting SM5100B Communication...
ÐèÐÐÐ
+SIND: 1
+SIND: 10,"SM",1,"FD",1,"LD",1,"MC",1,"RC",1,"ME",1
+STIN:0
+SIND: 11
+SIND: 3
+SIND: 4
Module ready
Attaching GPRS...
OK
Setting up PDP Context...
OK
Activating PDP Context...
OK
Configuring TCP connection to TCP Server...
OK
Starting TCP Connection...
OK
Checking socket status:
+SOCKSTATUS: 1,0,0104,0,0,0
OK
Checking socket status:
+SOCKSTATUS: 1,1,0102,0,0,0
OK
!!!Socket connected!!! Sending HTTP packet...
OK
+STCPD:1
Reading data from server...
Now displaying cell module output forever
+SDATA:1,486,485454502F312E3120323030204F4B0D0A4163636573732D436F6E74726F6C2D416C6C6F772D4F726967696E3A202A0D0A436F6E74656E742D547970653A206170706C69636174696F6E2F6A736F6E0D0A446174653A205468752C203134204E6F7620323031332030353A32373A323320474D540D0A5365727665723A2067756E69636F726E2F302E31372E340D0A436F6E74656E742D4C656E6774683A203236330D0A582D43616368653A204D4953532066726F6D207478323272727065703233620D0A436F6E6E656374696F6E3A206B6565702D616C6976650D0A0D0A7B0A20202275726C223A2022687474703A2F2F6874747062696E2E6F72672F6765743F646174613D74657374696E67222C0A20202268656164657273223A207B0A2020202022436F6E6E656374696F6E223A2022636C6F7365222C0A2020202022486F7374223A20226874747062696E2E6F7267222C0A202020202243616368652D436F6E74726F6C223A20226D61782D6167653D323539323030222C0A2020202022557365722D4167656E74223A20224D6F7A696C6C612F352E30220A20207D2C0A2020226F726967696E223A202234392E3138302E3132322E3337222C0A20202261726773223A207B0A202020202264617461223A202274657374696E67220A20207D0A7D
OK
+SDATA:1,0,
OK
Note that the +SDATA output is in HEX, so that big old string of garble you will receive back needs to be converted.
You can paste it into this site to get an easy look at what the output was: http://www.dolcevie.com/js/converter.html
and after decoding the hex, our output is
HTTP/1.1 200 OK??Access-Control-Allow-Origin: *??Content-Type: application/json??Date: Thu, 14 Nov 2013 05:27:23 GMT??Server: gunicorn/0.17.4??Content-Length: 263??X-Cache: MISS from tx22rrpep23b??Connection: keep-alive????{? "url": "http://httpbin.org/get?data=testing",? "headers": {? "Connection": "close",? "Host": "httpbin.org",? "Cache-Control": "max-age=259200",? "User-Agent": "Mozilla/5.0"? },? "origin": "49.180.122.37",? "args": {? "data": "testing"? }?}?
So a successfull http get request, I never thought it would happen.
EDIT!!!
You can tell the 5100B to replay you the recived data in ASCII!! With one simple command "AT+SDATARXMD=1,1,0"
So before this line:"GSMSerial.println("AT+SDATAREAD=1"); // how we read data server has sent"
add the code:
GSMSerial.println("AT+SDATARXMD=1,1,0");
waitFor("OK");
This will set the output to ASCII just before you call the SDATAREAD!
And the data your presented will now look much more beautiful more like this:
+SSTR:1,HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cont12:16 GMT
Server: gunicorn/0.17.4
Content-Length: 264
X-Cache: MISS from tx22rrpep38a
Connection: keep-alive
{
"args": {
"data": "testing"
},
"headers": {
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0",
"Cache-Control": "max-age=259200",
"Connection": "close"
},
"url": "http://httpbin.org/get?data=testing",
"origin": "49.180.112.246"
}
With no need for you to write a Hex to ASCII conversion function:)
One big old problem this presents is the "OK" that is part of a HTTP response.
OK is also the way the SM5100B tells us its processed a command, whenever we se "OK\r\n" as a string in the serial buffer we know that the GSM module has answered and this forms the end of the message,,, so then its difficult to know if the "OK\r\n" at the beginning of the HTTP response is part of the response or the message at the end of successfully processing the AT Command.
"So look for the second "OK\r\n" I thought... but then what if there is also a "OK\r\n" in the body of the response!!!
Any ideas anyone, bueller, anyone?
Step 6: Make an Arduino Library
So now I am working on a library so that I can make simple http requests without having to deal with all of that code.
I hope to make it as simple as response = GSMhttpGet("URL").
Anyone want to help with this?
Im making allot of progress with this library already.
Here are the functions I have already got coded and partially tested:
void reboot();
String GetReceptionLvl();
int hasNetworkReg();
int networkSignal();
void placeCall(String phoneNumber);
void hangUp();
void answerCall();
void sendDTMF(String DTMFTone);
void setDTMFTime(String DtmfTime);
void sendSMS(String sendTo, String sendText);
int getSMSMsgCount(String storeType); //Dont use: made obsolete by getSMSIndexs
void getSMSByIndex(int smsIndex, String smsString[]);
bool deleteSMSByIndex(int smsIndex);
void getSMSIndexs(int smsIndexs[], int &arraysize);
bool GPRSCheckAndAttach();
bool PDPSetupAndActivate(String apn);
bool TCPIPSetupAndStart(String IP);
String HttpGet(String Request, String host, String useragent);
Ive basically gone through the AT command documentation and made functions out of all the useful AT commands.
Then made some more goodies like, getmssagecount, and some GPRS functions, and HttpGet which accepts all the required information, makes the HTTP get and returns you back the result. Im not a C coder, Im a hack, so Im not going to post this library till Ive had the best go I can at it, if anyone wants it in its current state or would like to help me build on it, please send me a message!
Ben
I hope to make it as simple as response = GSMhttpGet("URL").
Anyone want to help with this?
Im making allot of progress with this library already.
Here are the functions I have already got coded and partially tested:
void reboot();
String GetReceptionLvl();
int hasNetworkReg();
int networkSignal();
void placeCall(String phoneNumber);
void hangUp();
void answerCall();
void sendDTMF(String DTMFTone);
void setDTMFTime(String DtmfTime);
void sendSMS(String sendTo, String sendText);
int getSMSMsgCount(String storeType); //Dont use: made obsolete by getSMSIndexs
void getSMSByIndex(int smsIndex, String smsString[]);
bool deleteSMSByIndex(int smsIndex);
void getSMSIndexs(int smsIndexs[], int &arraysize);
bool GPRSCheckAndAttach();
bool PDPSetupAndActivate(String apn);
bool TCPIPSetupAndStart(String IP);
String HttpGet(String Request, String host, String useragent);
Ive basically gone through the AT command documentation and made functions out of all the useful AT commands.
Then made some more goodies like, getmssagecount, and some GPRS functions, and HttpGet which accepts all the required information, makes the HTTP get and returns you back the result. Im not a C coder, Im a hack, so Im not going to post this library till Ive had the best go I can at it, if anyone wants it in its current state or would like to help me build on it, please send me a message!
Ben