 }
 
 uint8_t gsmInit() {
-  if (gsmSendCommand("AT")) {
-    gsmSendCommand("AT+IPR=9600");
-    gsmSendCommand("ATE0");
-    gsmSendCommand("AT+CFUN=1", 10000);
-    
-    return 0;
+  pinMode(SIM800_RESET_PIN, OUTPUT);
+  digitalWrite(SIM800_RESET_PIN, HIGH);
+  delay(1000);
+  digitalWrite(SIM800_RESET_PIN, LOW);
+  delay(1000);
+  digitalWrite(SIM800_RESET_PIN, HIGH);
+  delay(3000);
+
+  if (!gsmSendCommand_P(F("AT"))) {
+    return 1;
   }
   
-  return 1;
+  if (!gsmSendCommand_P(F("AT+IPR=9600"))) {
+    return 2;
+  }
+  
+  if (!gsmSendCommand_P(F("AT+CFUN=1"))) {
+    return 3;
+  }
+
+  return 0;
 }
 
 uint8_t gsmSetup(const char* apn) {
   bool success = false;
-  
-  for (byte n = 0; n < 30; n++) {
-    if (gsmSendCommand("AT+CREG?", 2000)) {
-        char *p = strstr(gsmSerialBuffer, "0,");
-        
-        if (p) {
-          char mode = *(p + 2);
-          
-          if (mode == '1' || mode == '5') {
-            success = true;
-            break;
-          }
+
+  for (uint8_t n = 0; n < 30; n++) {
+    if (gsmSendCommand_P(F("AT+CREG?"))) {
+      char *p = strstr(gsmSerialBuffer, "0,");
+
+      if (p) {
+        char mode = *(p + 2);
+
+        if (mode == '1' || mode == '5') {
+          success = true;
+          break;
         }
-    }
-    
+      }
+    }
+
     delay(1000);
   }
-      
+
   if (!success) {
     return 1;
   }
-  
-  if (!gsmSendCommand("AT+CGATT?", 5000)) {
+
+  if (!gsmSendCommand_P(F("AT+CGATT?"))) {
     return 2;
   }
-    
-  if (!gsmSendCommand("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"", 5000)) {
+
+  if (!gsmSendCommand_P(F("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\""))) {
     return 3;
   }
 
   gsmSerial.listen();
-  gsmSerial.print("AT+SAPBR=3,1,\"APN\",\"");
+  gsmSerial.print(F("AT+SAPBR=3,1,\"APN\",\""));
   gsmSerial.print(apn);
-  gsmSerial.println('\"');
-  
-  if (!gsmSendCommand(0, 5000)) {
+  gsmSerial.println(F("\""));
+
+  if (!gsmSendCommand(0)) {
     return 4;
   }
 
-  return 0;
+  if (!gsmSendCommand_P(F("ATS0=1"))) {
+    return 5;
+  }
+
+  if (!gsmSendCommand_P(F("AT+CMGF=1"))) {
+    return 6;
+  }
+
+  if (!gsmSendCommand_P(F("AT+CPMS=\"SM\",\"SM\",\"SM\""))) {
+    return 7;
+  }
+
+  return 0;
+}
+
+void gsmProcessSMS(uint16_t id, const char* sender, const char* text) {
+  Serial.println(id);
+  Serial.println(sender);
+  Serial.println(text);
+
+  if (strcmp(text, "reboot") == 0) reboot();
+
+  char buffer[12];
+  sprintf(buffer, "AT+CMGD=%u", id);
+  gsmSendCommand(buffer);
+}
+
+void gsmCheckSMS() {
+  gsmSendCommand_P(F("AT+CMGL=\"ALL\""));
+
+  char* p = strstr(gsmSerialBuffer, "CMGL: ");
+
+  while (p) {
+    p += 6;
+
+    uint8_t i;
+    char index[3];
+    char sender[20];
+    char text[161];
+
+    i = 0;
+    memset(index, 0, sizeof(index));
+
+    while (*p != ',') {
+      index[i++] = *p++;
+    }
+
+    while (*p != '"') ++p; ++p;
+    while (*p != '"') ++p; ++p;
+    while (*p != '"') ++p; ++p;
+
+    i = 0;
+    memset(sender, 0, sizeof(sender));
+
+    while (*p != '"') {
+      sender[i++] = *p++;
+    }
+
+    while (*p != '\n') ++p; ++p;
+
+    i = 0;
+    memset(text, 0, sizeof(text));
+
+    while (*p != '\r') {
+      text[i++] = *p++;
+    }
+
+    uint16_t id = atoi(index);
+    gsmProcessSMS(id, sender, text);
+
+    p = strstr(p, "CMGL: ");
+  };
 }
 
 int16_t gsmGetSignalQuality() {
-  gsmSendCommand("AT+CSQ", 5000);
-  
-  char *p = strstr(gsmSerialBuffer, "CSQ: ");
-  
+  gsmSendCommand_P(F("AT+CSQ"));
+
+  char* p = strstr(gsmSerialBuffer, "CSQ: ");
+
   if (p) {
     p += 5;
     uint8_t i = 0;

  };
}

uint8_t gsmSendGETRequest(const char* url) {
  if (!gsmSendCommand("AT+SAPBR=1,1", 10000)) {
    return 1;
  }
  
  if (!gsmSendCommand("AT+SAPBR=2,1", 10000)) {
    return 2;
  }
  
  if (!gsmSendCommand("AT+HTTPINIT", 5000)) {
    return 3;
  }
  
  if (!gsmSendCommand("AT+HTTPPARA=\"CID\",1", 5000)) {
    return 4;
  }

  gsmSerial.listen();
  gsmSerial.print("AT+HTTPPARA=\"URL\",\"");
  gsmSerial.print(url);
  gsmSerial.println('\"');
  
  if (!gsmSendCommand(0, 5000)) {
    return 5;
  }

  if (!gsmSendCommand("AT+HTTPPARA=\"UA\",\"bekti-tracker\"", 5000)) {
    return 6;
  }

  if (!gsmSendCommand("AT+HTTPPARA=\"TIMEOUT\",30", 5000)) {
    return 7;
  }
  
  if (!gsmSendCommand("AT+HTTPACTION=0", 45000, "+HTTPACTION:")) {
    return 8;
  }
  
  if (!gsmSendCommand("AT+HTTPTERM", 5000)) {
    return 9;
  }

  if (!gsmSendCommand("AT+SAPBR=0,1", 10000)) {
    return 10;
  }
}

uint8_t gsmSendPOSTRequest(const char* url, const char* params) {
  if (!gsmSendCommand("AT+SAPBR=1,1", 10000)) {
    return 1;
  }
  
  if (!gsmSendCommand("AT+SAPBR=2,1", 10000)) {
    return 2;
  }
  
  if (!gsmSendCommand("AT+HTTPINIT", 5000)) {
    return 3;
  }
  
  if (!gsmSendCommand("AT+HTTPPARA=\"CID\",1", 5000)) {
    return 4;
  }

  gsmSerial.listen();
  gsmSerial.print("AT+HTTPPARA=\"URL\",\"");
  gsmSerial.print(url);
  gsmSerial.println('\"');
  
  if (!gsmSendCommand(0, 5000)) {
    return 5;
  }

  if (!gsmSendCommand("AT+HTTPPARA=\"UA\",\"bekti-tracker\"", 5000)) {
    return 6;
  }

  if (!gsmSendCommand("AT+HTTPPARA=\"CONTENT\",\"application/x-www-form-urlencoded\"", 5000)) {
    return 7;
  }

  if (!gsmSendCommand("AT+HTTPPARA=\"TIMEOUT\",30", 5000)) {
    return 8;
  }


uint32_t date, time, speed, course, altitude, satellites, hdop;
uint32_t age, last;
char gsmSerialBuffer[256];
uint8_t failureCount;

void gpsUpdateValues();
uint8_t gsmInit();
uint8_t gsmSetup(const char* apn);
void gsmProcessSMS(uint16_t id, const char* sender, const char* text);
void gsmCheckSMS();
int16_t gsmGetSignalQuality();
void gsmGetBatteryLevel(uint8_t* percentage, uint16_t* voltage);
uint8_t gsmSendGETRequest(const char* url);
uint8_t gsmSendPOSTRequest(const char* url, const char* params);
void gsmPurgeSerialBuffer();
uint8_t gsmSendCommand(const char* cmd, uint16_t timeout = 5000, const char* expected = 0);
uint8_t gsmSendCommand_P(const __FlashStringHelper* cmd, uint16_t timeout = 5000, const char* expected = 0);
void reboot();

const char format_0[] PROGMEM = "id=%d&lat=%c%u.%lu&lng=%c%u.%lu&date=%lu&time=%lu&speed=%lu&course=%lu&alt=%lu&sat=%lu&hdop=%lu&age=%lu&charge=%d&voltage=%u&signal=%d";
const char format_1[] PROGMEM = "id=%d&charge=%d&voltage=%u&signal=%d";

void gpsUpdateValues() {
  if (gps.location.isUpdated()) {
