diff --git a/TinyGsmClient.h b/TinyGsmClient.h new file mode 100644 index 0000000..035836e --- /dev/null +++ b/TinyGsmClient.h @@ -0,0 +1,438 @@ +/** + * @file TinyGsmClient.h + * @author Volodymyr Shymanskyy + * @license LGPL-3.0 + * @copyright Copyright (c) 2016 Volodymyr Shymanskyy + * @date Nov 2016 + */ + +#ifndef TinyGsmClient_h +#define TinyGsmClient_h + +#if defined(SPARK) || defined(PARTICLE) + #include "Particle.h" +#elif defined(ARDUINO) + #if ARDUINO >= 100 + #include "Arduino.h" + #else + #include "WProgram.h" + #endif +#endif + +#include +#include + +#if defined(__AVR__) + #define GSM_PROGMEM PROGMEM + typedef const __FlashStringHelper* GsmConstStr; + #define FP(x) (reinterpret_cast(x)) +#else + #define GSM_PROGMEM + typedef const char* GsmConstStr; + #define FP(x) x + #undef F + #define F(x) x +#endif + +//#define GSM_USE_HEX +#define GSM_RX_BUFFER 64 + +#define GSM_NL "\r\n" +static constexpr char GSM_OK[] GSM_PROGMEM = "OK" GSM_NL; +static constexpr char GSM_ERROR[] GSM_PROGMEM = "ERROR" GSM_NL; + +class TinyGsmClient + : public Client +{ + typedef TinyGsmFifo RxFifo; + +#ifdef GSM_DEBUG + template + void DBG(T last) { + GSM_DEBUG.println(last); + } + + template + void DBG(T head, Args... tail) { + GSM_DEBUG.print(head); + GSM_DEBUG.print(' '); + DBG(tail...); + } +#else + #define DBG(...) +#endif + +public: + TinyGsmClient(Stream& stream, uint8_t mux = 1) + : stream(stream) + , mux(mux) + , sock_available(0) + , sock_connected(false) + {} + +public: + + virtual int connect(const char *host, uint16_t port) { + return modemConnect(host, port); + } + + virtual int connect(IPAddress ip, uint16_t port) { + String host; host.reserve(16); + host += ip[0]; + host += "."; + host += ip[1]; + host += "."; + host += ip[2]; + host += "."; + host += ip[3]; + return modemConnect(host.c_str(), port); + } + + virtual void stop() { + sendAT(F("+CIPCLOSE="), mux); + sock_connected = false; + waitResponse(); + } + + virtual size_t write(const uint8_t *buf, size_t size) { + maintain(); + return modemSend(buf, size); + } + + virtual size_t write(uint8_t c) { + return write(&c, 1); + } + + virtual int available() { + maintain(); + size_t res = rx.size(); + if (res > 0) { + return res; + } + return sock_available; + } + + virtual int read(uint8_t *buf, size_t size) { + maintain(); + size_t cnt = 0; + while (cnt < size) { + size_t chunk = min(size-cnt, rx.size()); + if (chunk > 0) { + rx.get(buf, chunk); + buf += chunk; + cnt += chunk; + } else { + modemRead(rx.free()); //TODO: min(rx.free(), sock_available) + } + } + return cnt; + } + + virtual int read() { + uint8_t c; + if (read(&c, 1) == 1) { + return c; + } + return -1; + } + + virtual int peek() { return -1; } //TODO + virtual void flush() { stream.flush(); } + + virtual uint8_t connected() { + maintain(); + return sock_connected; + } + virtual operator bool() { return connected(); } + +public: + bool factoryDefault() { + if (!autoBaud()) { + return false; + } + sendAT(F("&FZE0&W")); // Factory + Reset + Echo Off + Write + waitResponse(); + sendAT(F("+IPR=0")); // Auto-baud + waitResponse(); + sendAT(F("+IFC=0,0")); // No Flow Control + waitResponse(); + sendAT(F("+ICF=3,3")); // 8 data 0 parity 1 stop + waitResponse(); + sendAT(F("+CSCLK=0")); // Disable Slow Clock + waitResponse(); + sendAT(F("&W")); // Write configuration + return waitResponse() == 1; + } + + bool restart() { + autoBaud(); + sendAT(F("+CFUN=0")); + if (waitResponse(10000L) != 1) { + return false; + } + sendAT(F("+CFUN=1,1")); + if (waitResponse(10000L) != 1) { + return false; + } + delay(3000); + autoBaud(); + + sendAT(F("E0")); + if (waitResponse() != 1) { + return false; + } + + return 1 == waitResponse(60000, F("Ready")); + } + + bool networkConnect(const char* apn, const char* user, const char* pwd) { + autoBaud(); + + // AT+CPIN=pin-code + // AT+CREG? + + networkDisconnect(); + + // AT+CGATT? + // AT+CGATT=1 + + sendAT(F("+CIPMUX=1")); + if (waitResponse() != 1) { + return false; + } + + sendAT(F("+CIPQSEND=1")); + if (waitResponse() != 1) { + return false; + } + + sendAT(F("+CIPRXGET=1")); + if (waitResponse() != 1) { + return false; + } + + sendAT(F("+CSTT=\""), apn, F("\",\""), user, F("\",\""), pwd, F("\"")); + if (waitResponse(60000L) != 1) { + return false; + } + + sendAT(F("+CIICR")); + if (waitResponse(60000L) != 1) { + return false; + } + + sendAT(F("+CIFSR;E0")); + String data; + if (waitResponse(10000L, data) != 1) { + data.replace(GSM_NL, ""); + return false; + } + + sendAT(F("+CDNSCFG=\"8.8.8.8\",\"8.8.4.4\"")); + if (waitResponse() != 1) { + return false; + } + + // AT+CIPSTATUS + + return true; + } + + bool networkDisconnect() { + sendAT(F("+CIPSHUT")); + return waitResponse(60000L) == 1; + } + + bool autoBaud(unsigned long timeout = 10000L) { + for (unsigned long start = millis(); millis() - start < timeout; ) { + sendAT(""); + if (waitResponse() == 1) { + delay(100); + return true; + } + delay(100); + } + return false; + } + + void maintain() { + while (stream.available()) { + waitResponse(10); + } + } + +private: + int modemConnect(const char* host, uint16_t port) { + sendAT(F("+CIPSTART="), mux, ',', F("\"TCP"), F("\",\""), host, F("\","), port); + sock_connected = (1 == waitResponse(75000L, F("CONNECT OK" GSM_NL), F("CONNECT FAIL" GSM_NL), F("ALREADY CONNECT" GSM_NL))); + return sock_connected; + } + + int modemSend(const void* buff, size_t len) { + sendAT(F("+CIPSEND="), mux, ',', len); + if (waitResponse(F(">")) != 1) { + return -1; + } + stream.write((uint8_t*)buff, len); + if (waitResponse(F(GSM_NL "DATA ACCEPT:")) != 1) { + return -1; + } + stream.readStringUntil(','); + String data = stream.readStringUntil('\n'); + return data.toInt(); + } + + size_t modemRead(size_t size) { +#ifdef GSM_USE_HEX + sendAT(F("+CIPRXGET=3,"), mux, ',', size); + if (waitResponse(F("+CIPRXGET: 3,")) != 1) { + return 0; + } +#else + sendAT(F("+CIPRXGET=2,"), mux, ',', size); + if (waitResponse(F("+CIPRXGET: 2,")) != 1) { + return 0; + } +#endif + stream.readStringUntil(','); // Skip mux + size_t len = stream.readStringUntil(',').toInt(); + sock_available = stream.readStringUntil('\n').toInt(); + + for (size_t i=0; i + void streamWrite(T last) { + stream.print(last); + } + + template + void streamWrite(T head, Args... tail) { + stream.print(head); + streamWrite(tail...); + } + + int streamRead() { return stream.read(); } + void streamReadAll() { while(stream.available()) { stream.read(); } } + + template + void sendAT(Args... cmd) { + streamWrite("AT", cmd..., GSM_NL); + } + + // TODO: Optimize this! + uint8_t waitResponse(uint32_t timeout, String& data, + GsmConstStr r1=FP(GSM_OK), GsmConstStr r2=FP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + { + data.reserve(64); + bool gotNewData = false; + int index = 0; + for (unsigned long start = millis(); millis() - start < timeout; ) { + while (stream.available() > 0) { + int a = streamRead(); + if (a < 0) continue; //? + data += (char)a; + if (data.indexOf(r1) >= 0) { + index = 1; + goto finish; + } else if (r2 && data.indexOf(r2) >= 0) { + index = 2; + goto finish; + } else if (r3 && data.indexOf(r3) >= 0) { + index = 3; + goto finish; + } else if (r4 && data.indexOf(r4) >= 0) { + index = 4; + goto finish; + } else if (r5 && data.indexOf(r5) >= 0) { + index = 5; + goto finish; + } else if (data.indexOf(F(GSM_NL "+CIPRXGET: 1,1" GSM_NL)) >= 0) { //TODO: use mux + gotNewData = true; + data = ""; + } else if (data.indexOf(F(GSM_NL "1, CLOSED" GSM_NL)) >= 0) { //TODO: use mux + sock_connected = false; + data = ""; + } + } + } +finish: + if (!index) { + if (data.length()) { + DBG("### Unhandled:", data); + } + data = ""; + } + if (gotNewData) { + sock_available = modemGetAvailable(); + } + return index; + } + + uint8_t waitResponse(uint32_t timeout, + GsmConstStr r1=FP(GSM_OK), GsmConstStr r2=FP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + { + String data; + return waitResponse(timeout, data, r1, r2, r3, r4, r5); + } + + uint8_t waitResponse(GsmConstStr r1=FP(GSM_OK), GsmConstStr r2=FP(GSM_ERROR), + GsmConstStr r3=NULL, GsmConstStr r4=NULL, GsmConstStr r5=NULL) + { + return waitResponse(1000, r1, r2, r3, r4, r5); + } + +private: + Stream& stream; + const uint8_t mux; + + RxFifo rx; + uint16_t sock_available; + bool sock_connected; +}; + +#endif diff --git a/TinyGsmFifo.h b/TinyGsmFifo.h new file mode 100644 index 0000000..5c7c078 --- /dev/null +++ b/TinyGsmFifo.h @@ -0,0 +1,136 @@ +#ifndef TinyGsmFifo_h +#define TinyGsmFifo_h + +template +class TinyGsmFifo +{ +public: + TinyGsmFifo() + { + clear(); + } + + void clear() + { + _r = 0; + _w = 0; + } + + // writing thread/context API + //------------------------------------------------------------- + + bool writeable(void) + { + return free() > 0; + } + + int free(void) + { + int s = _r - _w; + if (s <= 0) + s += N; + return s - 1; + } + + T put(const T& c) + { + int i = _w; + int j = i; + i = _inc(i); + while (i == _r) // = !writeable() + /* nothing / just wait */; + _b[j] = c; + _w = i; + return c; + } + + int put(const T* p, int n, bool t = false) + { + int c = n; + while (c) + { + int f; + while ((f = free()) == 0) // wait for space + { + if (!t) return n - c; // no more space and not blocking + /* nothing / just wait */; + } + // check free space + if (c < f) f = c; + int w = _w; + int m = N - w; + // check wrap + if (f > m) f = m; + memcpy(&_b[w], p, f); + _w = _inc(w, f); + c -= f; + p += f; + } + return n - c; + } + + // reading thread/context API + // -------------------------------------------------------- + + bool readable(void) + { + return (_r != _w); + } + + size_t size(void) + { + int s = _w - _r; + if (s < 0) + s += N; + return s; + } + + T get(void) + { + int r = _r; + while (r == _w) // = !readable() + /* nothing / just wait */; + T t = _b[r]; + _r = _inc(r); + return t; + } + + int get(T* p, int n, bool t = false) + { + int c = n; + while (c) + { + int f; + for (;;) // wait for data + { + f = size(); + if (f) break; // free space + if (!t) return n - c; // no space and not blocking + /* nothing / just wait */; + } + // check available data + if (c < f) f = c; + int r = _r; + int m = N - r; + // check wrap + if (f > m) f = m; + memcpy(p, &_b[r], f); + _r = _inc(r, f); + c -= f; + p += f; + } + return n - c; + } + +private: + int _inc(int i, int n = 1) + { + return (i + n) % N; + } + + T _b[N]; + int _w; + int _r; +}; + +#endif diff --git a/examples/BlynkClient/BlynkClient.ino b/examples/BlynkClient/BlynkClient.ino new file mode 100644 index 0000000..e8e58b4 --- /dev/null +++ b/examples/BlynkClient/BlynkClient.ino @@ -0,0 +1,75 @@ +/************************************************************** + * + * For this example, you need to install Blynk library: + * https://github.com/blynkkk/blynk-library/releases/latest + * + * TinyGSM Getting Started guide: + * http://tiny.cc/tiny-gsm-readme + * + ************************************************************** + * + * Blynk is a platform with iOS and Android apps to control + * Arduino, Raspberry Pi and the likes over the Internet. + * You can easily build graphic interfaces for all your + * projects by simply dragging and dropping widgets. + * + * Blynk supports many development boards with WiFi, Ethernet, + * GSM, Bluetooth, BLE, USB/Serial connection methods. + * See more in Blynk library examples and community forum. + * + * http://www.blynk.io/ + * + * Change GPRS apm, user, pass, and Blynk auth token to run :) + **************************************************************/ + +#define BLYNK_PRINT Serial // Comment this out to disable prints and save space + +// Default heartbeat interval for GSM is 60 +// If you want override this value, uncomment and set this option: +//#define BLYNK_HEARTBEAT 30 + +#include +#include + +// You should get Auth Token in the Blynk App. +// Go to the Project Settings (nut icon). +char auth[] = "YourAuthToken"; + +// Your GPRS credentials +// Leave empty, if missing user or pass +char apn[] = "YourAPN"; +char user[] = ""; +char pass[] = ""; + +// Hardware Serial on Mega, Leonardo, Micro +#define GsmSerial Serial1 + +// or Software Serial on Uno, Nano +//#include +//SoftwareSerial GsmSerial(2, 3); // RX, TX + +TinyGsmClient gsm(GsmSerial); + +void setup() +{ + // Set console baud rate + Serial.begin(115200); + delay(10); + + // Set GSM module baud rate + GsmSerial.begin(115200); + delay(3000); + + // Restart takes quite some time + // You can skip it in many cases + Serial.println("Restarting modem..."); + gsm.restart(); + + Blynk.begin(auth, gsm, apn, user, pass); +} + +void loop() +{ + Blynk.run(); +} + diff --git a/examples/FileDownload/FileDownload.ino b/examples/FileDownload/FileDownload.ino new file mode 100644 index 0000000..3598043 --- /dev/null +++ b/examples/FileDownload/FileDownload.ino @@ -0,0 +1,153 @@ +/************************************************************** + * + * For this example, you need to install CRC32 library: + * https://github.com/vshymanskyy/CRC32.git + * + * TinyGSM Getting Started guide: + * http://tiny.cc/tiny-gsm-readme + * + **************************************************************/ + +#include +#include + +// Your GPRS credentials +// Leave empty, if missing user or pass +char apn[] = "YourAPN"; +char user[] = ""; +char pass[] = ""; + +// Use Hardware Serial on Mega, Leonardo, Micro +#define GsmSerial Serial1 + +// or Software Serial on Uno, Nano +//#include +//SoftwareSerial GsmSerial(2, 3); // RX, TX + +TinyGsmClient client(GsmSerial); + +char server[] = "cdn.rawgit.com"; +char resource[] = "/vshymanskyy/tinygsm/master/extras/test_10k.hex"; +uint32_t knownCRC32 = 0x54b3dcbf; +uint32_t knownFileSize = 10240; // In case server does not send it + +void setup() { + // Set console baud rate + Serial.begin(115200); + delay(10); + + // Set GSM module baud rate + GsmSerial.begin(115200); + delay(3000); + + // Restart takes quite some time + // You can skip it in many cases + Serial.println("Restarting modem..."); + client.restart(); +} + +void printPercent(uint32_t readLength, uint32_t contentLength) { + // If we know the total length + if (contentLength != -1) { + Serial.print(String("\r ") + ((100.0 * readLength) / contentLength) + "%"); + } else { + Serial.println(readLength); + } +} + +void loop() { + Serial.print("Connecting to "); + Serial.print(apn); + if (!client.networkConnect(apn, user, pass)) { + Serial.println(" failed"); + delay(10000); + return; + } + Serial.println(" OK"); + + Serial.print("Connecting to "); + Serial.print(server); + + // if you get a connection, report back via serial: + if (!client.connect(server, 80)) { + Serial.println(" failed"); + delay(10000); + return; + } + Serial.println(" OK"); + // Make a HTTP request: + client.print(String("GET ") + resource + " HTTP/1.0\r\n"); + client.print(String("Host: ") + server + "\r\n"); + client.print("Connection: close\r\n\r\n"); + + long timeout = millis(); + while (client.available() == 0) { + if (millis() - timeout > 5000L) { + Serial.println(">>> Client Timeout !"); + client.stop(); + delay(10000L); + return; + } + } + + Serial.println("Reading response header"); + uint32_t contentLength = knownFileSize; + + while (client.available()) { + String line = client.readStringUntil('\n'); + line.trim(); + //Serial.println(line); // Uncomment this to show response header + line.toLowerCase(); + if (line.startsWith("content-length:")) { + contentLength = line.substring(line.lastIndexOf(':') + 1).toInt(); + } else if (line.length() == 0) { + break; + } + } + + Serial.println("Reading response data"); + timeout = millis(); + uint32_t readLength = 0; + CRC32 crc; + + unsigned long timeElapsed = millis(); + printPercent(readLength, contentLength); + while (readLength < contentLength && client.connected() && millis() - timeout < 10000L) { + while (client.available()) { + uint8_t c = client.read(); + //Serial.print((char)c); // Uncomment this to show data + crc.update(c); + readLength++; + if (readLength % (contentLength / 13) == 0) { + printPercent(readLength, contentLength); + } + timeout = millis(); + } + } + printPercent(readLength, contentLength); + timeElapsed = millis() - timeElapsed; + Serial.println(); + + client.stop(); + Serial.println("Server disconnected"); + + client.networkDisconnect(); + Serial.println("GPRS disconnected"); + Serial.println(); + + float timeSpent = float(timeElapsed) / 1000; + float theorLimit = ((8.0 * readLength) / 85.6) / 1000; + + Serial.print("Content-Length: "); Serial.println(contentLength); + Serial.print("Actually read: "); Serial.println(readLength); + Serial.print("Calc. CRC32: 0x"); Serial.println(crc.finalize(), HEX); + Serial.print("Known CRC32: 0x"); Serial.println(knownCRC32, HEX); + Serial.print("Time spent: "); Serial.print(timeSpent); Serial.println("s"); + Serial.print("85.6kBps limit: "); Serial.print(theorLimit); Serial.println("s"); + + // Do nothing forevermore + while (true) { + delay(1000); + } +} + diff --git a/examples/MqttClient/MqttClient.ino b/examples/MqttClient/MqttClient.ino new file mode 100644 index 0000000..95ee880 --- /dev/null +++ b/examples/MqttClient/MqttClient.ino @@ -0,0 +1,130 @@ +/************************************************************** + * + * For this example, you need to install PubSubClient library: + * https://github.com/knolleary/pubsubclient/releases/latest + * + * TinyGSM Getting Started guide: + * http://tiny.cc/tiny-gsm-readme + * + ************************************************************** + * Use Mosquitto client tools to work with MQTT + * Ubuntu/Linux: sudo apt-get install mosquitto-clients + * Windows: https://mosquitto.org/download/ + * + * Subscribe for messages: + * mosquitto_sub -h test.mosquitto.org -t GsmClientTest/init -t GsmClientTest/ledStatus -q 1 + * Toggle led: + * mosquitto_pub -h test.mosquitto.org -t GsmClientTest/led -q 1 -m "toggle" + * + * You can use Node-RED for wiring together MQTT-enabled devices + * https://nodered.org/ + * Also, take a look at these additional Node-RED modules: + * node-red-contrib-blynk-websockets + * node-red-dashboard + * + **************************************************************/ + +#include +#include + +// Your GPRS credentials +// Leave empty, if missing user or pass +char apn[] = "YourAPN"; +char user[] = ""; +char pass[] = ""; + +// Use Hardware Serial on Mega, Leonardo, Micro +#define GsmSerial Serial1 + +// or Software Serial on Uno, Nano +//#include +//SoftwareSerial GsmSerial(2, 3); // RX, TX + +TinyGsmClient gsm(GsmSerial); +PubSubClient mqtt(gsm); + +const char* broker = "test.mosquitto.org"; + +const char* topicLed = "GsmClientTest/led"; +const char* topicInit = "GsmClientTest/init"; +const char* topicLedStatus = "GsmClientTest/ledStatus"; + +#define LED_PIN 13 +int ledStatus = LOW; + +long lastReconnectAttempt = 0; + +void setup() { + pinMode(LED_PIN, OUTPUT); + + // Set console baud rate + Serial.begin(115200); + delay(10); + + // Set GSM module baud rate + GsmSerial.begin(115200); + delay(3000); + + // Restart takes quite some time + // You can skip it in many cases + Serial.println("Restarting modem..."); + gsm.restart(); + + Serial.print("Connecting to "); + Serial.print(apn); + if (!gsm.networkConnect(apn, user, pass)) { + Serial.println(" failed"); + while (true); + } + Serial.println(" OK"); + + // MQTT Broker setup + mqtt.setServer(broker, 1883); + mqtt.setCallback(mqttCallback); +} + +boolean mqttConnect() { + Serial.print("Connecting to "); + Serial.print(broker); + if (!mqtt.connect("GsmClientTest")) { + Serial.println(" failed"); + return false; + } + Serial.println(" OK"); + mqtt.publish(topicInit, "GsmClientTest started"); + mqtt.subscribe(topicLed); + return mqtt.connected(); +} + +void loop() { + + if (mqtt.connected()) { + mqtt.loop(); + } else { + // Reconnect every 10 seconds + unsigned long t = millis(); + if (t - lastReconnectAttempt > 10000L) { + lastReconnectAttempt = t; + if (mqttConnect()) { + lastReconnectAttempt = 0; + } + } + } + +} + +void mqttCallback(char* topic, byte* payload, unsigned int len) { + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("]: "); + Serial.write(payload, len); + Serial.println(); + + // Only proceed if incoming message's topic matches + if (String(topic) == topicLed) { + ledStatus = !ledStatus; + digitalWrite(LED_PIN, ledStatus); + mqtt.publish(topicLedStatus, ledStatus ? "1" : "0"); + } +} + diff --git a/examples/WebClient/WebClient.ino b/examples/WebClient/WebClient.ino new file mode 100644 index 0000000..20091dd --- /dev/null +++ b/examples/WebClient/WebClient.ino @@ -0,0 +1,92 @@ +/************************************************************** + * + * This sketch connects to a website and downloads a page. + * It can be used to perform HTTP/RESTful API calls. + * + * TinyGSM Getting Started guide: + * http://tiny.cc/tiny-gsm-readme + * + **************************************************************/ + +#include + +// Your GPRS credentials +// Leave empty, if missing user or pass +char apn[] = "YourAPN"; +char user[] = ""; +char pass[] = ""; + +// Use Hardware Serial on Mega, Leonardo, Micro +#define GsmSerial Serial1 + +// or Software Serial on Uno, Nano +//#include +//SoftwareSerial GsmSerial(2, 3); // RX, TX + +TinyGsmClient client(GsmSerial); + +char server[] = "cdn.rawgit.com"; +char resource[] = "/vshymanskyy/tinygsm/master/extras/logo.txt"; + +void setup() { + // Set console baud rate + Serial.begin(115200); + delay(10); + + // Set GSM module baud rate + GsmSerial.begin(115200); + delay(3000); + + // Restart takes quite some time + // You can skip it in many cases + Serial.println("Restarting modem..."); + client.restart(); +} + +void loop() { + Serial.print("Connecting to "); + Serial.print(apn); + if (!client.networkConnect(apn, user, pass)) { + Serial.println(" failed"); + delay(10000); + return; + } + Serial.println(" OK"); + + Serial.print("Connecting to "); + Serial.print(server); + if (!client.connect(server, 80)) { + Serial.println(" failed"); + delay(10000); + return; + } + Serial.println(" OK"); + + // Make a HTTP GET request: + client.print(String("GET ") + resource + " HTTP/1.0\r\n"); + client.print(String("Host: ") + server + "\r\n"); + client.print("Connection: close\r\n\r\n"); + + unsigned long timeout = millis(); + while (client.connected() && millis() - timeout < 10000L) { + // Print available data + while (client.available()) { + char c = client.read(); + Serial.print(c); + timeout = millis(); + } + } + Serial.println(); + + client.stop(); + Serial.println("Server disconnected"); + + client.networkDisconnect(); + Serial.println("GPRS disconnected"); + + // Do nothing forevermore + while (true) { + delay(1000); + } +} +