ThingSpeak Communication Library
Enables an Arduino or ESP8266 to write or read data to or from ThingSpeak™
ThingSpeak.h
1 /*
2  ThingSpeak(TM) Communication Library For Arduino and ESP8266
3 
4  Enables an Arduino or other compatible hardware to write or read data to or from ThingSpeak,
5  an open data platform for the Internet of Things with MATLAB analytics and visualization.
6 
7  ThingSpeak ( https://www.thingspeak.com ) is an analytic IoT platform service that allows you to aggregate, visualize and
8  analyze live data streams in the cloud.
9 
10  Copyright 2017, The MathWorks, Inc.
11 
12  See the accompaning licence file for licensing information.
13 */
14 
50 #ifndef ThingSpeak_h
51 #define ThingSpeak_h
52 
53 //#define PRINT_DEBUG_MESSAGES
54 //#define PRINT_HTTP
55 
56 
57 #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM)
58  #include "Arduino.h"
59  #include <Client.h>
60 #else
61  #error Only Arduino MKR1000, Yun, Uno/Mega/Due with either WiFi101 or Ethernet shield. ESP8266 also supported.
62 #endif
63 
64 
65 #define THINGSPEAK_URL "api.thingspeak.com"
66 #define THINGSPEAK_IPADDRESS IPAddress(184,106,153,149)
67 #define THINGSPEAK_PORT_NUMBER 80
68 
69 #ifdef ARDUINO_ARCH_AVR
70  #ifdef ARDUINO_AVR_YUN
71  #define TS_USER_AGENT "tslib-arduino/1.3 (arduino yun)"
72  #else
73  #define TS_USER_AGENT "tslib-arduino/1.3 (arduino uno or mega)"
74  #endif
75 #elif defined(ARDUINO_ARCH_ESP8266)
76  #define TS_USER_AGENT "tslib-arduino/1.3 (ESP8266)"
77 #elif defined(ARDUINO_SAMD_MKR1000)
78  #define TS_USER_AGENT "tslib-arduino/1.3 (arduino mkr1000)"
79 #elif defined(ARDUINO_SAM_DUE)
80  #define TS_USER_AGENT "tslib-arduino/1.3 (arduino due)"
81 #elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM)
82  #define TS_USER_AGENT "tslib-arduino/1.3 (arduino unknown sam or samd)"
83 #else
84  #error "Platform not supported"
85 #endif
86 
87 #define FIELDNUM_MIN 1
88 #define FIELDNUM_MAX 8
89 #define FIELDLENGTH_MAX 255 // Max length for a field in ThingSpeak is 255 bytes (UTF-8)
90 
91 #define TIMEOUT_MS_SERVERRESPONSE 5000 // Wait up to five seconds for server to respond
92 
93 #define OK_SUCCESS 200 // OK / Success
94 #define ERR_BADAPIKEY 400 // Incorrect API key (or invalid ThingSpeak server address)
95 #define ERR_BADURL 404 // Incorrect API key (or invalid ThingSpeak server address)
96 #define ERR_OUT_OF_RANGE -101 // Value is out of range or string is too long (> 255 bytes)
97 #define ERR_INVALID_FIELD_NUM -201 // Invalid field number specified
98 #define ERR_SETFIELD_NOT_CALLED -210 // setField() was not called before writeFields()
99 #define ERR_CONNECT_FAILED -301 // Failed to connect to ThingSpeak
100 #define ERR_UNEXPECTED_FAIL -302 // Unexpected failure during write to ThingSpeak
101 #define ERR_BAD_RESPONSE -303 // Unable to parse response
102 #define ERR_TIMEOUT -304 // Timeout waiting for server to respond
103 #define ERR_NOT_INSERTED -401 // Point was not inserted (most probable cause is the rate limit of once every 15 seconds)
104 
109 {
110  public:
112  {
113  resetWriteFields();
114  this->lastReadStatus = OK_SUCCESS;
115  };
116 
138  bool begin(Client & client, const char * customHostName, unsigned int port)
139  {
140 #ifdef PRINT_DEBUG_MESSAGES
141  Serial.print("ts::tsBegin (client: Client URL: "); Serial.print(customHostName); Serial.println(")");
142 #endif
143  this->setClient(&client);
144  this->setServer(customHostName, port);
145  resetWriteFields();
146  this->lastReadStatus = OK_SUCCESS;
147  return true;
148  };
149 
171  bool begin(Client & client, IPAddress customIP, unsigned int port)
172  {
173 #ifdef PRINT_DEBUG_MESSAGES
174  Serial.print("ts::tsBegin (client: Client IP: "); Serial.print(customIP); Serial.println(")");
175 #endif
176  this->setClient(&client);
177  this->setServer(customIP, port);
178  resetWriteFields();
179  this->lastReadStatus = OK_SUCCESS;
180  return true;
181  };
182 
202  bool begin(Client & client)
203  {
204 #ifdef PRINT_DEBUG_MESSAGES
205  Serial.print("ts::tsBegin");
206 #endif
207  this->setClient(&client);
208  this->setServer();
209  resetWriteFields();
210  this->lastReadStatus = OK_SUCCESS;
211  return true;
212  };
213 
230  int writeField(unsigned long channelNumber, unsigned int field, int value, const char * writeAPIKey)
231  {
232  char valueString[10]; // int range is -32768 to 32768, so 7 bytes including terminator, plus a little extra
233  itoa(value, valueString, 10);
234  return writeField(channelNumber, field, valueString, writeAPIKey);
235  };
236 
253  int writeField(unsigned long channelNumber, unsigned int field, long value, const char * writeAPIKey)
254  {
255  char valueString[15]; // long range is -2147483648 to 2147483647, so 12 bytes including terminator
256  ltoa(value, valueString, 10);
257  return writeField(channelNumber, field, valueString, writeAPIKey);
258  };
259 
277  int writeField(unsigned long channelNumber, unsigned int field, float value, const char * writeAPIKey)
278  {
279  #ifdef PRINT_DEBUG_MESSAGES
280  Serial.print("ts::writeField (channelNumber: "); Serial.print(channelNumber); Serial.print(" writeAPIKey: "); Serial.print(writeAPIKey); Serial.print(" field: "); Serial.print(field); Serial.print(" value: "); Serial.print(value,5); Serial.println(")");
281  #endif
282  char valueString[20]; // range is -999999000000.00000 to 999999000000.00000, so 19 + 1 for the terminator
283  int status = convertFloatToChar(value, valueString);
284  if(status != OK_SUCCESS) return status;
285 
286  return writeField(channelNumber, field, valueString, writeAPIKey);
287  };
288 
310  int writeField(unsigned long channelNumber, unsigned int field, const char * value, const char * writeAPIKey)
311  {
312  return writeField(channelNumber, field, String(value), writeAPIKey);
313  };
314 
339  int writeField(unsigned long channelNumber, unsigned int field, String value, const char * writeAPIKey)
340  {
341  // Invalid field number specified
342  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX) return ERR_INVALID_FIELD_NUM;
343  // Max # bytes for ThingSpeak field is 255
344  if(value.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
345 
346  #ifdef PRINT_DEBUG_MESSAGES
347  Serial.print("ts::writeField (channelNumber: "); Serial.print(channelNumber); Serial.print(" writeAPIKey: "); Serial.print(writeAPIKey); Serial.print(" field: "); Serial.print(field); Serial.print(" value: \""); Serial.print(value); Serial.println("\")");
348  #endif
349  String postMessage = String("field") + String(field) + "=" + value;
350  return writeRaw(channelNumber, postMessage, writeAPIKey);
351  };
352 
353 
385  int setField(unsigned int field, int value)
386  {
387 
388  char valueString[10]; // int range is -32768 to 32768, so 7 bytes including terminator
389  itoa(value, valueString, 10);
390 
391  return setField(field, valueString);
392 
393  };
394 
426  int setField(unsigned int field, long value)
427  {
428  char valueString[15]; // long range is -2147483648 to 2147483647, so 12 bytes including terminator
429  ltoa(value, valueString, 10);
430  return setField(field, valueString);
431  };
432 
433 
465  int setField(unsigned int field, float value)
466  {
467  char valueString[20]; // range is -999999000000.00000 to 999999000000.00000, so 19 + 1 for the terminator
468  int status = convertFloatToChar(value, valueString);
469  if(status != OK_SUCCESS) return status;
470 
471  return setField(field, valueString);
472  };
473 
505  int setField(unsigned int field, const char * value)
506  {
507  return setField(field, String(value));
508  };
509 
541  int setField(unsigned int field, String value)
542  {
543  #ifdef PRINT_DEBUG_MESSAGES
544  Serial.print("ts::setField (field: "); Serial.print(field); Serial.print(" value: \""); Serial.print(value); Serial.println("\")");
545  #endif
546  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX) return ERR_INVALID_FIELD_NUM;
547  // Max # bytes for ThingSpeak field is 255 (UTF-8)
548  if(value.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
549  this->nextWriteField[field - 1] = value;
550  return OK_SUCCESS;
551  };
552 
553 
587  int setLatitude(float latitude)
588  {
589  #ifdef PRINT_DEBUG_MESSAGES
590  Serial.print("ts::setLatitude(latitude: "); Serial.print(latitude,3); Serial.println("\")");
591  #endif
592  this->nextWriteLatitude = latitude;
593  return OK_SUCCESS;
594  };
595 
596 
630  int setLongitude(float longitude)
631  {
632  #ifdef PRINT_DEBUG_MESSAGES
633  Serial.print("ts::setLongitude(longitude: "); Serial.print(longitude,3); Serial.println("\")");
634  #endif
635  this->nextWriteLongitude = longitude;
636  return OK_SUCCESS;
637  };
638 
639 
673  int setElevation(float elevation)
674  {
675  #ifdef PRINT_DEBUG_MESSAGES
676  Serial.print("ts::setElevation(elevation: "); Serial.print(elevation,3); Serial.println("\")");
677  #endif
678  this->nextWriteElevation = elevation;
679  return OK_SUCCESS;
680  };
681 
715  int setStatus(const char * status)
716  {
717  return setStatus(String(status));
718  };
719 
752  int setStatus(String status)
753  {
754  #ifdef PRINT_DEBUG_MESSAGES
755  Serial.print("ts::setStatus(status: "); Serial.print(status); Serial.println("\")");
756  #endif
757  // Max # bytes for ThingSpeak field is 255 (UTF-8)
758  if(status.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
759  this->nextWriteStatus = status;
760  return OK_SUCCESS;
761  };
762 
795  int setTwitterTweet(const char * twitter, const char * tweet)
796  {
797  return setTwitterTweet(String(twitter), String(tweet));
798  };
799 
832  int setTwitterTweet(String twitter, const char * tweet)
833  {
834  return setTwitterTweet(twitter, String(tweet));
835  };
836 
869  int setTwitterTweet(const char * twitter, String tweet)
870  {
871  return setTwitterTweet(String(twitter), tweet);
872  };
873 
906  int setTwitterTweet(String twitter, String tweet){
907  #ifdef PRINT_DEBUG_MESSAGES
908  Serial.print("ts::setTwitterTweet(twitter: "); Serial.print(twitter); Serial.print(", tweet: "); Serial.print(tweet); Serial.println("\")");
909  #endif
910  // Max # bytes for ThingSpeak field is 255 (UTF-8)
911  if((twitter.length() > FIELDLENGTH_MAX) || (tweet.length() > FIELDLENGTH_MAX)) return ERR_OUT_OF_RANGE;
912 
913  this->nextWriteTwitter = twitter;
914  this->nextWriteTweet = tweet;
915 
916  return OK_SUCCESS;
917  };
918 
951  int setCreatedAt(const char * createdAt)
952  {
953  return setCreatedAt(String(createdAt));
954  }
955 
988  int setCreatedAt(String createdAt)
989  {
990  #ifdef PRINT_DEBUG_MESSAGES
991  Serial.print("ts::setCreatedAt(createdAt: "); Serial.print(createdAt); Serial.println("\")");
992  #endif
993 
994  // the ISO 8601 format is too complicated to check for valid timestamps here
995  // we'll need to reply on the api to tell us if there is a problem
996  // Max # bytes for ThingSpeak field is 255 (UTF-8)
997  if(createdAt.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
998  this->nextWriteCreatedAt = createdAt;
999 
1000  return OK_SUCCESS;
1001  }
1002 
1003 
1038  int writeFields(unsigned long channelNumber, const char * writeAPIKey)
1039  {
1040  String postMessage = String("");
1041  bool fFirstItem = true;
1042  for(size_t iField = 0; iField < 8; iField++)
1043  {
1044  if(this->nextWriteField[iField].length() > 0)
1045  {
1046  if(!fFirstItem)
1047  {
1048  postMessage = postMessage + String("&");
1049  }
1050  postMessage = postMessage + String("field") + String(iField + 1) + String("=") + this->nextWriteField[iField];
1051  fFirstItem = false;
1052  this->nextWriteField[iField] = "";
1053  }
1054  }
1055 
1056  if(!isnan(nextWriteLatitude))
1057  {
1058  if(!fFirstItem)
1059  {
1060  postMessage = postMessage + String("&");
1061  }
1062  postMessage = postMessage + String("lat=") + String(this->nextWriteLatitude);
1063  fFirstItem = false;
1064  this->nextWriteLatitude = NAN;
1065  }
1066 
1067  if(!isnan(this->nextWriteLongitude))
1068  {
1069  if(!fFirstItem)
1070  {
1071  postMessage = postMessage + String("&");
1072  }
1073  postMessage = postMessage + String("long=") + String(this->nextWriteLongitude);
1074  fFirstItem = false;
1075  this->nextWriteLongitude = NAN;
1076  }
1077 
1078 
1079  if(!isnan(this->nextWriteElevation))
1080  {
1081  if(!fFirstItem)
1082  {
1083  postMessage = postMessage + String("&");
1084  }
1085  postMessage = postMessage + String("elevation=") + String(this->nextWriteElevation);
1086  fFirstItem = false;
1087  this->nextWriteElevation = NAN;
1088  }
1089 
1090  if(this->nextWriteStatus.length() > 0)
1091  {
1092  if(!fFirstItem)
1093  {
1094  postMessage = postMessage + String("&");
1095  }
1096  postMessage = postMessage + String("status=") + String(this->nextWriteStatus);
1097  fFirstItem = false;
1098  this->nextWriteStatus = "";
1099  }
1100 
1101  if(this->nextWriteTwitter.length() > 0)
1102  {
1103  if(!fFirstItem)
1104  {
1105  postMessage = postMessage + String("&");
1106  }
1107  postMessage = postMessage + String("twitter=") + String(this->nextWriteTwitter);
1108  fFirstItem = false;
1109  this->nextWriteTwitter = "";
1110  }
1111 
1112  if(this->nextWriteTweet.length() > 0)
1113  {
1114  if(!fFirstItem)
1115  {
1116  postMessage = postMessage + String("&");
1117  }
1118  postMessage = postMessage + String("tweet=") + String(this->nextWriteTweet);
1119  fFirstItem = false;
1120  this->nextWriteTweet = "";
1121  }
1122 
1123  if(this->nextWriteCreatedAt.length() > 0)
1124  {
1125  if(!fFirstItem)
1126  {
1127  postMessage = postMessage + String("&");
1128  }
1129  postMessage = postMessage + String("created_at=") + String(this->nextWriteCreatedAt);
1130  fFirstItem = false;
1131  this->nextWriteCreatedAt = "";
1132  }
1133 
1134 
1135  if(fFirstItem)
1136  {
1137  // setField was not called before writeFields
1138  return ERR_SETFIELD_NOT_CALLED;
1139  }
1140 
1141  return writeRaw(channelNumber, postMessage, writeAPIKey);
1142  };
1143 
1144 
1165  int writeRaw(unsigned long channelNumber, const char * postMessage, const char * writeAPIKey)
1166  {
1167  return writeRaw(channelNumber, String(postMessage), writeAPIKey);
1168  };
1169 
1170 
1186  int writeRaw(unsigned long channelNumber, String postMessage, const char * writeAPIKey)
1187  {
1188  #ifdef PRINT_DEBUG_MESSAGES
1189  Serial.print("ts::writeRaw (channelNumber: "); Serial.print(channelNumber); Serial.print(" writeAPIKey: "); Serial.print(writeAPIKey); Serial.print(" postMessage: \""); Serial.print(postMessage); Serial.println("\")");
1190  #endif
1191 
1192  if(!connectThingSpeak())
1193  {
1194  // Failed to connect to ThingSpeak
1195  return ERR_CONNECT_FAILED;
1196  }
1197 
1198  postMessage = postMessage + String("&headers=false");
1199 
1200  #ifdef PRINT_DEBUG_MESSAGES
1201  Serial.print(" POST \"");Serial.print(postMessage);Serial.println("\"");
1202  #endif
1203 
1204  postMessage = postMessage + String("\n");
1205 
1206  // Post data to thingspeak
1207  if(!this->client->print("POST /update HTTP/1.1\r\n")) return abortWriteRaw();
1208  if(!writeHTTPHeader(writeAPIKey)) return abortWriteRaw();
1209  if(!this->client->print("Content-Type: application/x-www-form-urlencoded\r\n")) return abortWriteRaw();
1210  if(!this->client->print("Content-Length: ")) return abortWriteRaw();
1211  if(!this->client->print(postMessage.length())) return abortWriteRaw();
1212  if(!this->client->print("\r\n\r\n")) return abortWriteRaw();
1213  if(!this->client->print(postMessage)) return abortWriteRaw();
1214 
1215  String entryIDText = String();
1216  int status = getHTTPResponse(entryIDText);
1217  if(status != OK_SUCCESS)
1218  {
1219  client->stop();
1220  return status;
1221  }
1222  long entryID = entryIDText.toInt();
1223 
1224  #ifdef PRINT_DEBUG_MESSAGES
1225  Serial.print(" Entry ID \"");Serial.print(entryIDText);Serial.print("\" (");Serial.print(entryID);Serial.println(")");
1226  #endif
1227 
1228  client->stop();
1229 
1230  #ifdef PRINT_DEBUG_MESSAGES
1231  Serial.println("disconnected.");
1232  #endif
1233  if(entryID == 0)
1234  {
1235  // ThingSpeak did not accept the write
1236  status = ERR_NOT_INSERTED;
1237  }
1238  return status;
1239  };
1240 
1256  String readStringField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1257  {
1258  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX)
1259  {
1260  this->lastReadStatus = ERR_INVALID_FIELD_NUM;
1261  return("");
1262  }
1263  #ifdef PRINT_DEBUG_MESSAGES
1264  Serial.print("ts::readStringField(channelNumber: "); Serial.print(channelNumber);
1265  if(NULL != readAPIKey)
1266  {
1267  Serial.print(" readAPIKey: "); Serial.print(readAPIKey);
1268  }
1269  Serial.print(" field: "); Serial.print(field); Serial.println(")");
1270  #endif
1271  return readRaw(channelNumber, String(String("/fields/") + String(field) + String("/last")), readAPIKey);
1272  }
1273 
1274 
1289  String readStringField(unsigned long channelNumber, unsigned int field)
1290  {
1291  return readStringField(channelNumber, field, NULL);
1292  };
1293 
1294 
1311  float readFloatField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1312  {
1313  return convertStringToFloat(readStringField(channelNumber, field, readAPIKey));
1314  };
1315 
1316 
1332  float readFloatField(unsigned long channelNumber, unsigned int field)
1333  {
1334  return readFloatField(channelNumber, field, NULL);
1335  };
1336 
1337 
1353  long readLongField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1354  {
1355  // Note that although the function is called "toInt" it really returns a long.
1356  return readStringField(channelNumber, field, readAPIKey).toInt();
1357  }
1358 
1359 
1374  long readLongField(unsigned long channelNumber, unsigned int field)
1375  {
1376  return readLongField(channelNumber, field, NULL);
1377  };
1378 
1379 
1396  int readIntField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1397  {
1398  return readLongField(channelNumber, field, readAPIKey);
1399  }
1400 
1401 
1417  int readIntField(unsigned long channelNumber, unsigned int field)
1418  {
1419  return readLongField(channelNumber, field, NULL);
1420  };
1421 
1436  String readStatus(unsigned long channelNumber, const char * readAPIKey)
1437  {
1438  String content = readRaw(channelNumber, "/feeds/last.txt?status=true", readAPIKey);
1439 
1440  if(getLastReadStatus() != OK_SUCCESS){
1441  return String("");
1442  }
1443 
1444  return getJSONValueByKey(content, "status");
1445  };
1446 
1460  String readStatus(unsigned long channelNumber)
1461  {
1462  return readStatus(channelNumber, NULL);
1463  };
1464 
1479  String readCreatedAt(unsigned long channelNumber, const char * readAPIKey)
1480  {
1481  String content = readRaw(channelNumber, "/feeds/last.txt", readAPIKey);
1482 
1483  if(getLastReadStatus() != OK_SUCCESS){
1484  return String("");
1485  }
1486 
1487  return getJSONValueByKey(content, "created_at");
1488  };
1489 
1503  String readCreatedAt(unsigned long channelNumber)
1504  {
1505  return readCreatedAt(channelNumber, NULL);
1506  };
1507 
1523  String readRaw(unsigned long channelNumber, String URLSuffix)
1524  {
1525  return readRaw(channelNumber, URLSuffix, NULL);
1526  }
1527 
1544  String readRaw(unsigned long channelNumber, String URLSuffix, const char * readAPIKey)
1545  {
1546  #ifdef PRINT_DEBUG_MESSAGES
1547  Serial.print("ts::readRaw (channelNumber: "); Serial.print(channelNumber);
1548  if(NULL != readAPIKey)
1549  {
1550  Serial.print(" readAPIKey: "); Serial.print(readAPIKey);
1551  }
1552  Serial.print(" URLSuffix: \""); Serial.print(URLSuffix); Serial.println("\")");
1553  #endif
1554 
1555  if(!connectThingSpeak())
1556  {
1557  this->lastReadStatus = ERR_CONNECT_FAILED;
1558  return String("");
1559  }
1560 
1561  String URL = String("/channels/") + String(channelNumber) + URLSuffix;
1562 
1563  #ifdef PRINT_DEBUG_MESSAGES
1564  Serial.print(" GET \"");Serial.print(URL);Serial.println("\"");
1565  #endif
1566 
1567  // Post data to thingspeak
1568  if(!this->client->print("GET ")) return abortReadRaw();
1569  if(!this->client->print(URL)) return abortReadRaw();
1570  if(!this->client->print(" HTTP/1.1\r\n")) return abortReadRaw();
1571  if(!writeHTTPHeader(readAPIKey)) return abortReadRaw();
1572  if(!this->client->print("\r\n")) return abortReadRaw();
1573 
1574  String content = String();
1575  int status = getHTTPResponse(content);
1576 
1577  this->lastReadStatus = status;
1578 
1579 
1580  #ifdef PRINT_DEBUG_MESSAGES
1581  if(status == OK_SUCCESS)
1582  {
1583  Serial.print("Read: \""); Serial.print(content); Serial.println("\"");
1584  }
1585  #endif
1586 
1587  client->stop();
1588  #ifdef PRINT_DEBUG_MESSAGES
1589  Serial.println("disconnected.");
1590  #endif
1591 
1592  if(status != OK_SUCCESS)
1593  {
1594  // return status;
1595  return String("");
1596  }
1597 
1598  // This is a workaround to a bug in the Spark implementation of String
1599  return String("") + content;
1600  };
1601 
1636  {
1637  return this->lastReadStatus;
1638  };
1639 private:
1640 
1641  String getJSONValueByKey(String textToSearch, String key)
1642  {
1643  if(textToSearch.length() == 0){
1644  return String("");
1645  }
1646 
1647  String searchPhrase = String("\"") + key + String("\":\"");
1648 
1649  int fromPosition = textToSearch.indexOf(searchPhrase,0);
1650 
1651  if(fromPosition == -1){
1652  // return because there is no status or it's null
1653  return String("");
1654  }
1655 
1656  fromPosition = fromPosition + searchPhrase.length();
1657 
1658  int toPosition = textToSearch.indexOf("\"", fromPosition);
1659 
1660 
1661  if(toPosition == -1){
1662  // return because there is no end quote
1663  return String("");
1664  }
1665 
1666  textToSearch.remove(toPosition);
1667 
1668  return textToSearch.substring(fromPosition);
1669  }
1670 
1671  int abortWriteRaw()
1672  {
1673  this->client->stop();
1674  return ERR_UNEXPECTED_FAIL;
1675  }
1676 
1677  String abortReadRaw()
1678  {
1679  this->client->stop();
1680  #ifdef PRINT_DEBUG_MESSAGES
1681  Serial.println("ReadRaw abort - disconnected.");
1682  #endif
1683  this->lastReadStatus = ERR_UNEXPECTED_FAIL;
1684  return String("");
1685  }
1686 
1687  void setServer(const char * customHostName, unsigned int port)
1688  {
1689  #ifdef PRINT_DEBUG_MESSAGES
1690  Serial.print("ts::setServer (URL: \""); Serial.print(customHostName); Serial.println("\")");
1691  #endif
1692  this->customIP = INADDR_NONE;
1693  this->customHostName = customHostName;
1694  this->port = port;
1695  };
1696 
1697  void setServer(IPAddress customIP, unsigned int port)
1698  {
1699  #ifdef PRINT_DEBUG_MESSAGES
1700  Serial.print("ts::setServer (IP: \""); Serial.print(customIP); Serial.println("\")");
1701  #endif
1702  this->customIP = customIP;
1703  this->customHostName = NULL;
1704  this->port = port;
1705  };
1706 
1707  void setServer()
1708  {
1709  #ifdef PRINT_DEBUG_MESSAGES
1710  Serial.print("ts::setServer (default)");
1711  #endif
1712  this->customIP = INADDR_NONE;
1713  this->customHostName = NULL;
1714  this->port = THINGSPEAK_PORT_NUMBER;
1715  };
1716 
1717  void setClient(Client * client) {this->client = client;};
1718 
1719  Client * client = NULL;
1720  const char * customHostName = NULL;
1721  IPAddress customIP = INADDR_NONE;
1722  unsigned int port = THINGSPEAK_PORT_NUMBER;
1723  String nextWriteField[8];
1724  float nextWriteLatitude;
1725  float nextWriteLongitude;
1726  float nextWriteElevation;
1727  int lastReadStatus;
1728  String nextWriteStatus;
1729  String nextWriteTwitter;
1730  String nextWriteTweet;
1731  String nextWriteCreatedAt;
1732 
1733  bool connectThingSpeak()
1734  {
1735  bool connectSuccess = false;
1736  if(this->customIP == INADDR_NONE && NULL == this->customHostName)
1737  {
1738  #ifdef PRINT_DEBUG_MESSAGES
1739  Serial.print(" Connect to default ThingSpeak URL...");
1740  #endif
1741  connectSuccess = client->connect(THINGSPEAK_URL,THINGSPEAK_PORT_NUMBER);
1742  if(!connectSuccess)
1743  {
1744  #ifdef PRINT_DEBUG_MESSAGES
1745  Serial.print("Failed. Try default IP...");
1746  #endif
1747  connectSuccess = client->connect(THINGSPEAK_IPADDRESS,THINGSPEAK_PORT_NUMBER);
1748  }
1749  }
1750  else
1751  {
1752  if(!(this->customIP == INADDR_NONE))
1753  {
1754  // Connect to the server on port 80 (HTTP) at the customIP address
1755  #ifdef PRINT_DEBUG_MESSAGES
1756  Serial.print(" Connect to ");Serial.print(this->customIP);Serial.print("...");
1757  #endif
1758  connectSuccess = client->connect(this->customIP,this->port);
1759  }
1760  if(NULL != this->customHostName)
1761  {
1762  // Connect to the server on port 80 (HTTP) at the URL address
1763  #ifdef PRINT_DEBUG_MESSAGES
1764  Serial.print(" Connect to ");Serial.print(this->customHostName);Serial.print(" ...");
1765  #endif
1766  connectSuccess = client->connect(customHostName,this->port);
1767  }
1768  }
1769 
1770  #ifdef PRINT_DEBUG_MESSAGES
1771  if (connectSuccess)
1772  {
1773  Serial.println("Success.");
1774  }
1775  else
1776  {
1777  Serial.println("Failed.");
1778  }
1779  #endif
1780  return connectSuccess;
1781  };
1782 
1783  bool writeHTTPHeader(const char * APIKey)
1784  {
1785  if(NULL != this->customHostName)
1786  {
1787  if (!this->client->print("Host: ")) return false;
1788  if (!this->client->print(this->customHostName)) return false;
1789  if (!this->client->print("\r\n")) return false;
1790  }
1791  else
1792  {
1793  if (!this->client->print("Host: api.thingspeak.com\r\n")) return false;
1794  }
1795  if (!this->client->print("Connection: close\r\n")) return false;
1796  if (!this->client->print("User-Agent: ")) return false;
1797  if (!this->client->print(TS_USER_AGENT)) return false;
1798  if (!this->client->print("\r\n")) return false;
1799  if(NULL != APIKey)
1800  {
1801  if (!this->client->print("X-THINGSPEAKAPIKEY: ")) return false;
1802  if (!this->client->print(APIKey)) return false;
1803  if (!this->client->print("\r\n")) return false;
1804  }
1805  return true;
1806  };
1807 
1808  int getHTTPResponse(String & response)
1809  {
1810  long startWaitForResponseAt = millis();
1811  while(client->available() == 0 && millis() - startWaitForResponseAt < TIMEOUT_MS_SERVERRESPONSE)
1812  {
1813  delay(100);
1814  }
1815  if(client->available() == 0)
1816  {
1817  return ERR_TIMEOUT; // Didn't get server response in time
1818  }
1819 
1820  if(!client->find(const_cast<char *>("HTTP/1.1")))
1821  {
1822  #ifdef PRINT_HTTP
1823  Serial.println("ERROR: Didn't find HTTP/1.1");
1824  #endif
1825  return ERR_BAD_RESPONSE; // Couldn't parse response (didn't find HTTP/1.1)
1826  }
1827  int status = client->parseInt();
1828  #ifdef PRINT_HTTP
1829  Serial.print("Got Status of ");Serial.println(status);
1830  #endif
1831  if(status != OK_SUCCESS)
1832  {
1833  return status;
1834  }
1835 
1836  if(!client->find(const_cast<char *>("\r\n")))
1837  {
1838  #ifdef PRINT_HTTP
1839  Serial.println("ERROR: Didn't find end of status line");
1840  #endif
1841  return ERR_BAD_RESPONSE;
1842  }
1843  #ifdef PRINT_HTTP
1844  Serial.println("Found end of status line");
1845  #endif
1846 
1847  if(!client->find(const_cast<char *>("\n\r\n")))
1848  {
1849  #ifdef PRINT_HTTP
1850  Serial.println("ERROR: Didn't find end of header");
1851  #endif
1852  return ERR_BAD_RESPONSE;
1853  }
1854  #ifdef PRINT_HTTP
1855  Serial.println("Found end of header");
1856  #endif
1857 
1858  String tempString = client->readString();
1859  response = tempString;
1860  #ifdef PRINT_HTTP
1861  Serial.print("Response: \"");Serial.print(response);Serial.println("\"");
1862  #endif
1863  return status;
1864  };
1865 
1866  int convertFloatToChar(float value, char *valueString)
1867  {
1868  // Supported range is -999999000000 to 999999000000
1869  if(0 == isinf(value) && (value > 999999000000 || value < -999999000000))
1870  {
1871  // Out of range
1872  return ERR_OUT_OF_RANGE;
1873  }
1874  // assume that 5 places right of decimal should be sufficient for most applications
1875 
1876  #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM)
1877  sprintf(valueString, "%.5f", value);
1878  #else
1879  dtostrf(value,1,5, valueString);
1880  #endif
1881  return OK_SUCCESS;
1882  };
1883 
1884  float convertStringToFloat(String value)
1885  {
1886  // There's a bug in the AVR function strtod that it doesn't decode -INF correctly (it maps it to INF)
1887  float result = value.toFloat();
1888 
1889  if(1 == isinf(result) && *value.c_str() == '-')
1890  {
1891  result = (float)-INFINITY;
1892  }
1893  return result;
1894  };
1895 
1896  void resetWriteFields()
1897  {
1898  for(size_t iField = 0; iField < 8; iField++)
1899  {
1900  this->nextWriteField[iField] = "";
1901  }
1902  this->nextWriteLatitude = NAN;
1903  this->nextWriteLongitude = NAN;
1904  this->nextWriteElevation = NAN;
1905  this->nextWriteStatus = "";
1906  this->nextWriteTwitter = "";
1907  this->nextWriteTweet = "";
1908  this->nextWriteCreatedAt = "";
1909  };
1910 };
1911 
1912 extern ThingSpeakClass ThingSpeak;
1913 
1914 #endif //ThingSpeak_h
int writeRaw(unsigned long channelNumber, const char *postMessage, const char *writeAPIKey)
Write a raw POST to a ThingSpeak channel.
Definition: ThingSpeak.h:1165
int setTwitterTweet(const char *twitter, const char *tweet)
Set the Twitter account and message to use for an update to be tweeted. To send a message to twitter ...
Definition: ThingSpeak.h:795
String readStringField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest string from a private ThingSpeak channel.
Definition: ThingSpeak.h:1256
String readStringField(unsigned long channelNumber, unsigned int field)
Read the latest string from a public ThingSpeak channel.
Definition: ThingSpeak.h:1289
int setField(unsigned int field, long value)
Set the value of a single field that will be part of a multi-field update. To write multiple fields a...
Definition: ThingSpeak.h:426
String readCreatedAt(unsigned long channelNumber)
Read the created-at timestamp associated with the latest update to a private ThingSpeak channel...
Definition: ThingSpeak.h:1503
int writeRaw(unsigned long channelNumber, String postMessage, const char *writeAPIKey)
Write a raw POST to a ThingSpeak channel.
Definition: ThingSpeak.h:1186
bool begin(Client &client, IPAddress customIP, unsigned int port)
Initializes the ThingSpeak library and network settings using a custom installation of ThingSpeak...
Definition: ThingSpeak.h:171
int writeFields(unsigned long channelNumber, const char *writeAPIKey)
Write a multi-field update. Call setField() for each of the fields you want to write, setLatitude() / setLongitude() / setElevation(), and then call writeFields()
Definition: ThingSpeak.h:1038
int setField(unsigned int field, int value)
Set the value of a single field that will be part of a multi-field update. To write multiple fields a...
Definition: ThingSpeak.h:385
int setTwitterTweet(const char *twitter, String tweet)
Set the Twitter account and message to use for an update to be tweeted. To send a message to twitter ...
Definition: ThingSpeak.h:869
int setTwitterTweet(String twitter, const char *tweet)
Set the Twitter account and message to use for an update to be tweeted. To send a message to twitter ...
Definition: ThingSpeak.h:832
int setLatitude(float latitude)
Set the latitude of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:587
String readCreatedAt(unsigned long channelNumber, const char *readAPIKey)
Read the created-at timestamp associated with the latest update to a private ThingSpeak channel...
Definition: ThingSpeak.h:1479
int setStatus(String status)
Set the status of a multi-field update. To record a status message on a write, call setStatus() then ...
Definition: ThingSpeak.h:752
bool begin(Client &client, const char *customHostName, unsigned int port)
Initializes the ThingSpeak library and network settings using a custom installation of ThingSpeak...
Definition: ThingSpeak.h:138
int getLastReadStatus()
Get the status of the previous read.
Definition: ThingSpeak.h:1635
int writeField(unsigned long channelNumber, unsigned int field, int value, const char *writeAPIKey)
Write an integer value to a single field in a ThingSpeak channel.
Definition: ThingSpeak.h:230
Enables an Arduino, ESP8266 or other compatible hardware to write or read data to or from ThingSpeak...
Definition: ThingSpeak.h:108
long readLongField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest long from a private ThingSpeak channel.
Definition: ThingSpeak.h:1353
int writeField(unsigned long channelNumber, unsigned int field, String value, const char *writeAPIKey)
Write a String to a single field in a ThingSpeak channel.
Definition: ThingSpeak.h:339
int setStatus(const char *status)
Set the status of a multi-field update. To record a status message on a write, call setStatus() then ...
Definition: ThingSpeak.h:715
int setField(unsigned int field, const char *value)
Set the value of a single field that will be part of a multi-field update. To write multiple fields a...
Definition: ThingSpeak.h:505
String readRaw(unsigned long channelNumber, String URLSuffix)
Read a raw response from a public ThingSpeak channel.
Definition: ThingSpeak.h:1523
long readLongField(unsigned long channelNumber, unsigned int field)
Read the latest long from a public ThingSpeak channel.
Definition: ThingSpeak.h:1374
int writeField(unsigned long channelNumber, unsigned int field, float value, const char *writeAPIKey)
Write a floating point value to a single field in a ThingSpeak channel.
Definition: ThingSpeak.h:277
int setCreatedAt(const char *createdAt)
Set the created-at date of a multi-field update. To record created-at of a write, call setField() for...
Definition: ThingSpeak.h:951
int readIntField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest int from a private ThingSpeak channel.
Definition: ThingSpeak.h:1396
int setField(unsigned int field, String value)
Set the value of a single field that will be part of a multi-field update. To write multiple fields a...
Definition: ThingSpeak.h:541
int setCreatedAt(String createdAt)
Set the created-at date of a multi-field update. To record created-at of a write, call setField() for...
Definition: ThingSpeak.h:988
int setLongitude(float longitude)
Set the longitude of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:630
float readFloatField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest float from a private ThingSpeak channel.
Definition: ThingSpeak.h:1311
int setTwitterTweet(String twitter, String tweet)
Set the Twitter account and message to use for an update to be tweeted. To send a message to twitter ...
Definition: ThingSpeak.h:906
String readStatus(unsigned long channelNumber, const char *readAPIKey)
Read the latest status from a private ThingSpeak channel.
Definition: ThingSpeak.h:1436
bool begin(Client &client)
Initializes the ThingSpeak library and network settings using the ThingSpeak.com service.
Definition: ThingSpeak.h:202
float readFloatField(unsigned long channelNumber, unsigned int field)
Read the latest float from a public ThingSpeak channel.
Definition: ThingSpeak.h:1332
int writeField(unsigned long channelNumber, unsigned int field, const char *value, const char *writeAPIKey)
Write a string to a single field in a ThingSpeak channel.
Definition: ThingSpeak.h:310
int readIntField(unsigned long channelNumber, unsigned int field)
Read the latest int from a public ThingSpeak channel.
Definition: ThingSpeak.h:1417
int setField(unsigned int field, float value)
Set the value of a single field that will be part of a multi-field update. To write multiple fields a...
Definition: ThingSpeak.h:465
String readRaw(unsigned long channelNumber, String URLSuffix, const char *readAPIKey)
Read a raw response from a private ThingSpeak channel.
Definition: ThingSpeak.h:1544
String readStatus(unsigned long channelNumber)
Read the latest status from a public ThingSpeak channel.
Definition: ThingSpeak.h:1460
int setElevation(float elevation)
Set the elevation of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:673
int writeField(unsigned long channelNumber, unsigned int field, long value, const char *writeAPIKey)
Write a long value to a single field in a ThingSpeak channel.
Definition: ThingSpeak.h:253