Skip to content

Instantly share code, notes, and snippets.

@gabonator
Last active April 29, 2018 22:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabonator/88b80d9c6b16d699d6f4234ebc3a2587 to your computer and use it in GitHub Desktop.
Save gabonator/88b80d9c6b16d699d6f4234ebc3a2587 to your computer and use it in GitHub Desktop.
SIM900 nonblocking and reliable gprs http request for arduino and soft serial (at command logger)
GPRS Initialization
0: Send AT
1: Expect 'OK ' Got '' = no
1: Timeout 1000
1: Expect 'OK ' Got '' = no
1: Timeout -1 !!!TIMEOUT ERROR!!!
2: Send AT
3: Expect 'OK ' Got '' = no
3: Timeout 1000
3: Expect 'OK ' Got '' = no
3: Timeout -1 !!!TIMEOUT ERROR!!!
*** ERROR: no AT response buffer: ''
3: Goto -1
0: DigitalWrite 4, 0
1: Sleep 1000
2: DigitalWrite 4, 1
4: DigitalWrite 4, 0
6: Return 1
0: Send AT
1: Expect 'OK ' Got 'AT ' = no
1: Timeout 1000
1: Expect 'OK ' Got 'AT ' = no
1: Timeout 998
1: Expect 'OK ' Got 'AT OK ' = yes
2: Goto 4
4: Send ATE0
5: Expect 'OK ' Got 'ATE0 ' = no
5: Timeout 1000
5: Expect 'OK ' Got 'ATE0 ' = no
5: Timeout 998
5: Expect 'OK ' Got 'ATE0 OK ' = yes
6: Send ACMEE=1
7: Expect 'OK ' Got 'ATE0 OK ' = yes
8: Send AT+CFUN=1
9: Expect 'OK ' Got ' ' = no
9: Timeout 1000
9: Expect 'OK ' Got ' OK ' = yes
10: Send AT+CPIN?
11: Expect '+CPIN: READY ' Got ' ' = no
11: Timeout 1000
11: Expect '+CPIN: READY ' Got ' +CPIN: READY ' = yes
12: Expect 'OK ' Got ' ' = no
12: Timeout 1000
12: Expect 'OK ' Got ' OK ' = yes
13: Return 1
Starting upload
0: Send AT+CSTT="o2internet","",""
1: Expect 'OK ' Got ' ' = no
1: Expect '+CME ERROR' Got ' ' = no
1: Timeout 20000
1: Expect 'OK ' Got ' OK ' = yes
2: Expect '+CME ERROR' Got ' OK ' = no
2: Send AT+CIICR
3: Expect 'OK ' Got ' ' = no
3: Timeout 1000
3: Expect 'OK ' Got ' OK ' = yes
4: Send AT+CIFSR
5: Timeout 1000
Valid IP in buffer: 100.126.133.135
6: Send AT+CIPSTART="TCP","api.*****.***",80
7: Expect 'CONNECT OK ' Got ' ' = no
7: Expect 'ALREADY CONNECTED' Got ' ' = no
7: Timeout 10000
7: Expect 'CONNECT OK ' Got ' OK ' = no
7: Expect 'ALREADY CONNECTED' Got ' OK ' = no
7: Timeout 9997
7: Expect 'CONNECT OK ' Got ' OK ' = no
7: Expect 'ALREADY CONNECTED' Got ' OK ' = no
7: Timeout 9151
7: Expect 'CONNECT OK ' Got ' OK ' = no
7: Expect 'ALREADY CONNECTED' Got ' OK ' = no
7: Timeout 8980
7: Expect 'CONNECT OK ' Got ' OK CONNECT OK ' = yes
8: Expect 'ALREADY CONNECTED' Got ' OK CONNECT OK ' = no
8: Send AT+CIPSEND=95
9: Expect '>' Got ' ' = no
9: Timeout 5000
9: Expect '>' Got ' > ' = yes
10: Send GET /*****/?n=123 HTTP/1.0 Host: api.*****.*** User-Agent: sim900 on esp8266 by valky.eu
11: Expect 'SEND OK ' Got ' ' = no
11: Timeout 5000
11: Expect 'SEND OK ' Got ' SEND OK ' = yes
12: Expect 'CLOSED ' Got ' SEND OK ' = no
12: Timeout 20000
12: Expect 'CLOSED ' Got 'HTTP/1.1 200 OK ' = no
12: Timeout 19918
12: Expect 'CLOSED ' Got 'Server: openresty ' = no
12: Timeout 19898
12: Expect 'CLOSED ' Got 'Date: Sun, 29 Apr 2018 17:24:40 GMT ' = no
12: Timeout 19860
12: Expect 'CLOSED ' Got 'Content-Type: text/html ' = no
12: Timeout 19834
12: Expect 'CLOSED ' Got 'Connection: close ' = no
12: Timeout 19814
12: Expect 'CLOSED ' Got 'Access-Control-Allow-Origin: * ' = no
12: Timeout 19781
12: Expect 'CLOSED ' Got 'Access-Control-Allow-Methods: POST, GET, OPTIONS ' = no
12: Timeout 19727
12: Expect 'CLOSED ' Got 'Access-Control-Allow-Headers: X-Requested-With, content-type ' = no
12: Timeout 19663
12: Expect 'CLOSED ' Got 'Vary: Accept-Encoding,User-Agent ' = no
12: Timeout 19628
12: Expect 'CLOSED ' Got 'Vary: Accept-Encoding,User-Agent ' = no
12: Timeout 19625
12: Expect 'CLOSED ' Got 'Moze byt ' = no
12: Timeout 19617
12: Expect 'CLOSED ' Got 'Moze byt ' = no
12: Timeout 18999
12: Expect 'CLOSED ' Got 'Moze byt ' = no
12: Timeout 17998
12: Expect 'CLOSED ' Got 'Moze byt ' = no
12: Timeout 16997
12: Expect 'CLOSED ' Got 'Moze byt ' = no
12: Timeout 15996
12: Expect 'CLOSED ' Got 'Moze byt ' = no
12: Timeout 15466
12: Expect 'CLOSED ' Got 'CLOSED ' = yes
13: Send AT+CIPSTATUS
14: Expect 'CONNECTED ' Got ' ' = no
14: Expect 'STATE: TCP CLOSED ' Got ' ' = no
14: Timeout 5000
14: Expect 'CONNECTED ' Got ' OK ' = no
14: Expect 'STATE: TCP CLOSED ' Got ' OK ' = no
14: Timeout 4995
14: Expect 'CONNECTED ' Got ' OK ' = no
14: Expect 'STATE: TCP CLOSED ' Got ' OK ' = no
14: Timeout 4985
14: Expect 'CONNECTED ' Got ' OK STATE: TCP CLOSED ' = no
14: Expect 'STATE: TCP CLOSED ' Got ' OK STATE: TCP CLOSED ' = yes
15: Goto 17
17: Send AT+CIPSHUT
18: Expect 'SHUT OK ' Got ' ' = no
18: Timeout 5000
18: Expect 'SHUT OK ' Got ' SHUT OK ' = yes
19: Return 1
Upload ok
// Efficient and reliable nonblocking SIM900 driver for arduino
// Analyze and debug AT commands & responses easily
// Suitable for cooperative multitasking applications
// tinysine SIM900 Quad-band GSM/GPRS Shield for Arduino UNO/MEGA/Leonardo
#include "SoftwareSerial.h"
#define pinTX D3 // TX: pin D3 of ESP8266 connected to pin D2 of SIM900
#define pinRX D2 // RX: pin D2 of ESP8266 connected to pin D3 of SIM900
#define pinPower D4 // PWRKEY: pin D4 of ESP8266 connected to pin D8 of SIM900
const char* _urlHost = "api.*****.***";
const char* _urlPath = "/vendea/?n=%d&time=%lu";
void buildUrl(char* url)
{
static int counter = 0;
sprintf(url, _urlPath, counter++, millis());
}
SoftwareSerial soft(pinTX, pinRX);
//#define QUIET
#ifdef QUIET
#define Error_print Serial.print
#define Debug_print(x) void()
#define Debug_printnocrlf(x) void()
#else
#define Debug_print Serial.print
#define Error_print Serial.print
#define Debug_printnocrlf Error_printnocrlf
#endif
void Error_printnocrlf(const char* msg) { Error_printnocrlf((char*)msg); }
void Error_printnocrlf(char* msg)
{
while (*msg)
{
char c = *msg++;
if ( c!=0x0d && c!=0x0a)
Debug_print(c);
else
Debug_print(' ');
}
}
class CProgram
{
int mPc{0};
CProgram* mChild{nullptr};
bool mHasReturnCode{false};
bool mReturnCode{false};
long mFrozenTill{0};
int mLastTimeoutLabel{-1};
long mLastTimeout{0};
bool bThisFrameJumped{false};
int mBufferPos{0};
bool mPipe{false};
protected:
char mBuffer[128];
public:
void Reset()
{
mHasReturnCode = false;
mPc = 0;
mLastTimeoutLabel = 0;
}
void Pipe(bool b)
{
mPipe = b;
}
void operator << (char c)
{
if (mPipe)
{
static bool flushOnChar = false;
if (c == 0x0d || c == 0x0a)
{
flushOnChar = true;
//mBufferPos = 0;
} else
{
if (flushOnChar)
{
mBufferPos = 0;
flushOnChar = false;
}
}
if (mBufferPos >= sizeof(mBuffer)-1)
return;
}
if (mBufferPos >= sizeof(mBuffer)-1)
{
Serial.print("Buffer overflow\n");
return;
}
mBuffer[mBufferPos++] = c;
mBuffer[mBufferPos] = 0;
}
int Pc()
{
return mPc;
}
virtual void Program()
{
Serial.print("No program defined\n");
}
void Sleep(int x)
{
mFrozenTill = millis() + x;
Debug_print(Pc());
Debug_print(": Sleep ");
Debug_print(x);
Debug_print("\n");
}
void DigitalWrite(int p, int v)
{
Debug_print(Pc());
Debug_print(": DigitalWrite ");
Debug_print(p);
Debug_print(", ");
Debug_print(v);
Debug_print("\n");
digitalWrite(p, v);
Next();
}
void Send(const char* p)
{
mBufferPos = 0;
mBuffer[0] = 0;
Debug_print(Pc());
Debug_print(": Send ");
Debug_printnocrlf(p);
Debug_print("\n");
soft.print(p);
Next();
}
int BufferLength()
{
return (int)mBufferPos;
}
char BufferAt(int i)
{
//assert(i>=0 && i<mBufferPos);
return mBuffer[i];
}
bool Expect(const char* p)
{
bool found = (strstr(mBuffer, p) != nullptr);
Debug_print(Pc());
Debug_print(": Expect '");
Debug_printnocrlf(p);
Debug_print("' Got '");
Debug_printnocrlf(mBuffer);
Debug_print("' = ");
Debug_print(found ? "yes" : "no");
Debug_print("\n");
if (found)
{
mBufferPos = 0;
Next();
}
return found;
}
bool Timeout(int n)
{
if (bThisFrameJumped)
return false;
bool passed = false;
long now = millis();
if (mLastTimeoutLabel != Pc())
{
mLastTimeoutLabel = Pc();
mLastTimeout = now;
} else
{
if (now > mLastTimeout + n)
passed = true;
}
if (passed)
{
Error_print("*** GPRS ");
Error_print(Pc());
Error_print(" TIMEOUT: ");
Error_print(n);
Error_print(" buffer: '");
Error_printnocrlf(mBuffer);
Error_print("'\n");
} else
{
Debug_print(Pc());
Debug_print(": Timeout ");
Debug_print(mLastTimeout + n - now);
if (passed)
Debug_print(" !!!TIMEOUT ERROR!!!");
Debug_print("\n");
}
return passed;
}
void Error(const char* message)
{
Error_print("GPRS *** ");
Error_print(Pc());
Error_print(" ERROR: ");
Error_print(message);
Error_print(" buffer: '");
Error_printnocrlf(mBuffer);
Error_print("'\n");
}
void Call(CProgram& program)
{
mChild = &program;
mChild->Reset();
}
void Return(bool b)
{
Debug_print(Pc());
Debug_print(": Return ");
Debug_print(b);
Debug_print("\n");
mHasReturnCode = true;
mReturnCode = b;
}
bool HasReturnCode(bool& ret)
{
ret = mReturnCode;
return mHasReturnCode;
}
void Goto(int label)
{
bThisFrameJumped = true;
Debug_print(Pc());
Debug_print(": Goto ");
Debug_print(label);
Debug_print("\n");
mPc = label;
}
void Next()
{
bThisFrameJumped = true;
mPc++;
}
void operator()()
{
if (mHasReturnCode)
return;
if (mFrozenTill != 0)
{
if (millis() < mFrozenTill)
return;
mFrozenTill = 0;
Next();
return;
}
if (mChild)
{
(*mChild)();
bool returnCode;
if (mChild->HasReturnCode(returnCode))
{
Next();
mChild = nullptr;
}
return;
}
bThisFrameJumped = false;
Program();
}
};
class CProgToggleSwitch : public CProgram
{
public:
virtual void Program()
{
switch (Pc())
{
case 0: DigitalWrite(pinPower, LOW); break;
case 1: Sleep(1000); break;
case 2: DigitalWrite(pinPower, HIGH); break;
case 3: Sleep(2000); break;
case 4: DigitalWrite(pinPower, LOW); break;
case 5: Sleep(3000); break;
default: Return(true);
}
}
} progToggleSwitch;
class CProgInit : public CProgram
{
public:
virtual void Program()
{
switch (Pc())
{
case -2: Call(progToggleSwitch); break;
case -1: Call(progToggleSwitch); break;
// at response
case 0: Send("AT\r\n"); break;
case 1: if (Expect("OK\r\n")) Goto(4);
if (Expect("NORMAL POWER DOWN")) Goto(-1);
if (Timeout(1000)) Next();
break;
// at response - second try
case 2: Send("AT\r\n"); break;
case 3: if (Expect("OK\r\n")) Goto(4);
if (Expect("NORMAL POWER DOWN")) Goto(-1);
if (Timeout(1000)) { Error("no AT response"); Goto(-1); } break;
// disable echo
case 4: Send("ATE0\r\n"); break;
case 5: Expect("OK\r\n"); if (Timeout(1000)) Goto(0); break;
case 6: Send("AT+CMEE=1\r\n"); break;
case 7: Expect("OK\r\n"); if (Timeout(1000)) Goto(0); break;
case 8: Send("AT+CFUN=1\r\n"); break;
case 9: Expect("OK\r\n"); if (Timeout(1000)) Goto(0); break;
case 10: Send("AT+CPIN?\r\n"); break;
case 11: Expect("+CPIN: READY\r\n"); if (Timeout(2000)) { Error("wrong PIN response"); Goto(0); } break;
case 12: Expect("OK\r\n"); if (Timeout(2000)) Next(); break;
case 13: Sleep(3000); break; // needs some time before attaching to apn
default: Return(true); break;
}
}
} progInit;
class CProgTransfer : public CProgram
{
char mRequest[256];
bool mRetry{false};
public:
bool ReadIpAddress()
{
bool begin = true;
uint8_t addressBytes = 0;
for (int i=0; i<BufferLength(); i++)
{
char c = BufferAt(i);
if (begin)
{
if (c == 0x0d || c == 0x0a)
continue;
begin = false;
}
if (c == 0x0d || c == 0x0a)
{
Debug_print("Assigned IP address: ");
Debug_printnocrlf(mBuffer);
Debug_print("\n");
return (addressBytes > 5);
}
if (c == '.' || (c >= '0' && c <= '9'))
{
addressBytes++;
continue;
}
break;
}
return false;
}
void Init(const char* host, const char *url)
{
mRetry = false;
sprintf(mRequest,
"GET %s HTTP/1.0\r\n"
"Host: %s\r\n"
"User-Agent: sim900 on esp8266 by valky.eu\r\n"
"\r\n", url, host);
}
void Retry(int label)
{
if (mRetry)
Return(false);
else
{
Debug_print("Retrying...\n");
mRetry = true;
Goto(label);
}
}
virtual void Program()
{
char buffer[128];
switch (Pc())
{
case 0: Send("AT+CSTT=\"o2internet\",\"\",\"\"\r\n"); break;
case 1: Expect("OK\r\n");
if (Expect("+CME ERROR")) { Error("CME Error when attaching apn"); Return(false);}
if (Timeout(20000)) Return(false);
break;
case 2: Send("AT+CIICR\r\n"); break;
case 3: Expect("OK\r\n"); if (Timeout(1000)) Next(); break;
case 4: Send("AT+CIFSR\r\n"); break;
case 5: if (ReadIpAddress()) Next(); if (Timeout(5000)) Return(false); break;
case 6: Send("AT+CIPSTART=\"TCP\",\"api.gabo.guru\",80\r\n"); break;
case 7: Expect("CONNECT OK\r\n");
Expect("ALREADY CONNECTED");
if (Expect("CONNECT FAIL")) Retry(6);
if (Timeout(10000)) Return(false); break;
case 8: sprintf(buffer, "AT+CIPSEND=%d\r\n", (int)strlen(mRequest)); Send(buffer); break;
case 9: Expect(">");
if (Expect("ERROR")) Retry(8);
if (Timeout(5000)) Return(false); break;
case 10: Send(mRequest); break;
case 11: if (Expect("SEND OK\r\n")) Pipe(true);
if (Expect("CLOSED\r\n")) Retry(10);
if (Timeout(15000)) Return(false); break;
case 12: if (Expect("CLOSED\r\n")) Pipe(false);
if (Timeout(20000)) { Pipe(false); Return(false);} break;
case 13: Send("AT+CIPSTATUS\r\n"); break;
case 14: if (Expect("CONNECTED\r\n")) Goto(15);
if (Expect("STATE: TCP CLOSED\r\n")) Goto(17);
if (Timeout(5000)) Return(false); break;
case 15: Send("AT+CIPCLOSE\r\n"); break;
case 16: Expect("CLOSE OK\r\n"); if (Timeout(5000)) Return(false); break;
case 17: Send("AT+CIPSHUT\r\n"); break;
case 18: Expect("SHUT OK\r\n"); if (Timeout(5000)) Return(false); break;
case 19: Sleep(1000); break; // relax for a while
default: Return(true); break;
}
}
} progTransfer;
class CUploader
{
bool uploadProgress{false};
bool lastUploadOk{false};
public:
bool isInitOk()
{
bool bOk;
if (progInit.HasReturnCode(bOk))
return bOk;
return false;
}
bool isTransferOk()
{
bool bOk;
if (progTransfer.HasReturnCode(bOk))
return bOk;
return false;
}
bool isTransferError()
{
bool bOk;
if (progTransfer.HasReturnCode(bOk))
return !bOk;
return false;
}
bool isReady()
{
return isInitOk() && !uploadProgress;
}
void request(char* urlHost, char* urlPath)
{
if (lastUploadOk)
{
Serial.print("Resuming upload\n");
progTransfer.Reset();
progTransfer.Goto(6);
} else
{
Serial.print("Starting upload\n");
progTransfer.Reset();
}
progTransfer.Init(urlHost, urlPath);
uploadProgress = true;
}
void operator()()
{
if (!isInitOk())
{
progInit();
return;
}
if (uploadProgress)
{
progTransfer();
if (isTransferOk())
{
Serial.print("Upload ok\n");
uploadProgress = false;
lastUploadOk = true;
}
if (isTransferError())
{
Serial.print("Upload failed, resetting\n");
progInit.Reset();
progInit.Goto(-2);
uploadProgress = false;
lastUploadOk = false;
}
}
}
void operator <<(char c)
{
if (!isInitOk())
progInit << c;
else
{
if (uploadProgress)
progTransfer << c;
else
{
Serial.print("finished, char:");
Serial.println(c);
}
}
}
} uploader;
void setup()
{
soft.begin(9600);
pinMode(pinPower, OUTPUT);
Serial.begin(115200);
Serial.print("\nGPRS Initialization\n");
}
void loop()
{
static long last = 0;
bool shouldProcess = false;
yield();
while (soft.available())
{
yield();
char c = soft.read();
uploader << c;
if (c == '\n')
shouldProcess = true;
}
long now = millis();
if (shouldProcess || now > last + 1000)
{
last = now;
uploader();
if (uploader.isReady())
{
char urlPath[128];
buildUrl(urlPath);
uploader.request((char*)_urlHost, urlPath);
Serial.print("New request ");
Serial.print(urlPath);
Serial.print("\n");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment