ThingSpeak Communication Library
Enables an Arduino, ESP8266 or ESP32 to write or read data to or from ThingSpeak™
ThingSpeak.h
1 /*
2  ThingSpeak(TM) Communication Library For Arduino, ESP8266 and ESP32
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 
51 #ifndef ThingSpeak_h
52 #define ThingSpeak_h
53 
54 //#define PRINT_DEBUG_MESSAGES
55 //#define PRINT_HTTP
56 
57 
58 #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM) || defined(ARDUINO_ARCH_ESP32)
59  #include "Arduino.h"
60  #include <Client.h>
61 #else
62  #error Only Arduino MKR1000, Yun, Uno/Mega/Due with either WiFi101 or Ethernet shield. ESP8266 and ESP32 are also supported.
63 #endif
64 
65 
66 #define THINGSPEAK_URL "api.thingspeak.com"
67 #define THINGSPEAK_IPADDRESS IPAddress(184,106,153,149)
68 #define THINGSPEAK_PORT_NUMBER 80
69 
70 #ifdef ARDUINO_ARCH_AVR
71  #ifdef ARDUINO_AVR_YUN
72  #define TS_USER_AGENT "tslib-arduino/1.3 (arduino yun)"
73  #else
74  #define TS_USER_AGENT "tslib-arduino/1.3 (arduino uno or mega)"
75  #endif
76 #elif defined(ARDUINO_ARCH_ESP8266)
77  #define TS_USER_AGENT "tslib-arduino/1.3 (ESP8266)"
78 #elif defined(ARDUINO_SAMD_MKR1000)
79  #define TS_USER_AGENT "tslib-arduino/1.3 (arduino mkr1000)"
80 #elif defined(ARDUINO_SAM_DUE)
81  #define TS_USER_AGENT "tslib-arduino/1.3 (arduino due)"
82 #elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM)
83  #define TS_USER_AGENT "tslib-arduino/1.3 (arduino unknown sam or samd)"
84 #elif defined(ARDUINO_ARCH_ESP32)
85  #define TS_USER_AGENT "tslib-arduino/1.3 (ESP32)"
86 #else
87  #error "Platform not supported"
88 #endif
89 
90 #define FIELDNUM_MIN 1
91 #define FIELDNUM_MAX 8
92 #define FIELDLENGTH_MAX 255 // Max length for a field in ThingSpeak is 255 bytes (UTF-8)
93 
94 #define TIMEOUT_MS_SERVERRESPONSE 5000 // Wait up to five seconds for server to respond
95 
96 #define OK_SUCCESS 200 // OK / Success
97 #define ERR_BADAPIKEY 400 // Incorrect API key (or invalid ThingSpeak server address)
98 #define ERR_BADURL 404 // Incorrect API key (or invalid ThingSpeak server address)
99 #define ERR_OUT_OF_RANGE -101 // Value is out of range or string is too long (> 255 bytes)
100 #define ERR_INVALID_FIELD_NUM -201 // Invalid field number specified
101 #define ERR_SETFIELD_NOT_CALLED -210 // setField() was not called before writeFields()
102 #define ERR_CONNECT_FAILED -301 // Failed to connect to ThingSpeak
103 #define ERR_UNEXPECTED_FAIL -302 // Unexpected failure during write to ThingSpeak
104 #define ERR_BAD_RESPONSE -303 // Unable to parse response
105 #define ERR_TIMEOUT -304 // Timeout waiting for server to respond
106 #define ERR_NOT_INSERTED -401 // Point was not inserted (most probable cause is the rate limit of once every 15 seconds)
107 
112 {
113  public:
115  {
116  resetWriteFields();
117  this->lastReadStatus = OK_SUCCESS;
118  };
119 
141  bool begin(Client & client, const char * customHostName, unsigned int port)
142  {
143 #ifdef PRINT_DEBUG_MESSAGES
144  Serial.print("ts::tsBegin (client: Client URL: "); Serial.print(customHostName); Serial.println(")");
145 #endif
146  this->setClient(&client);
147  this->setServer(customHostName, port);
148  resetWriteFields();
149  this->lastReadStatus = OK_SUCCESS;
150  return true;
151  };
152 
174  bool begin(Client & client, IPAddress customIP, unsigned int port)
175  {
176 #ifdef PRINT_DEBUG_MESSAGES
177  Serial.print("ts::tsBegin (client: Client IP: "); Serial.print(customIP); Serial.println(")");
178 #endif
179  this->setClient(&client);
180  this->setServer(customIP, port);
181  resetWriteFields();
182  this->lastReadStatus = OK_SUCCESS;
183  return true;
184  };
185 
205  bool begin(Client & client)
206  {
207 #ifdef PRINT_DEBUG_MESSAGES
208  Serial.print("ts::tsBegin");
209 #endif
210  this->setClient(&client);
211  this->setServer();
212  resetWriteFields();
213  this->lastReadStatus = OK_SUCCESS;
214  return true;
215  };
216 
233  int writeField(unsigned long channelNumber, unsigned int field, int value, const char * writeAPIKey)
234  {
235  char valueString[10]; // int range is -32768 to 32768, so 7 bytes including terminator, plus a little extra
236  itoa(value, valueString, 10);
237  return writeField(channelNumber, field, valueString, writeAPIKey);
238  };
239 
256  int writeField(unsigned long channelNumber, unsigned int field, long value, const char * writeAPIKey)
257  {
258  char valueString[15]; // long range is -2147483648 to 2147483647, so 12 bytes including terminator
259  ltoa(value, valueString, 10);
260  return writeField(channelNumber, field, valueString, writeAPIKey);
261  };
262 
280  int writeField(unsigned long channelNumber, unsigned int field, float value, const char * writeAPIKey)
281  {
282  #ifdef PRINT_DEBUG_MESSAGES
283  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(")");
284  #endif
285  char valueString[20]; // range is -999999000000.00000 to 999999000000.00000, so 19 + 1 for the terminator
286  int status = convertFloatToChar(value, valueString);
287  if(status != OK_SUCCESS) return status;
288 
289  return writeField(channelNumber, field, valueString, writeAPIKey);
290  };
291 
313  int writeField(unsigned long channelNumber, unsigned int field, const char * value, const char * writeAPIKey)
314  {
315  return writeField(channelNumber, field, String(value), writeAPIKey);
316  };
317 
342  int writeField(unsigned long channelNumber, unsigned int field, String value, const char * writeAPIKey)
343  {
344  // Invalid field number specified
345  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX) return ERR_INVALID_FIELD_NUM;
346  // Max # bytes for ThingSpeak field is 255
347  if(value.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
348 
349  #ifdef PRINT_DEBUG_MESSAGES
350  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("\")");
351  #endif
352  String postMessage = String("field") + String(field) + "=" + value;
353  return writeRaw(channelNumber, postMessage, writeAPIKey);
354  };
355 
356 
388  int setField(unsigned int field, int value)
389  {
390 
391  char valueString[10]; // int range is -32768 to 32768, so 7 bytes including terminator
392  itoa(value, valueString, 10);
393 
394  return setField(field, valueString);
395 
396  };
397 
429  int setField(unsigned int field, long value)
430  {
431  char valueString[15]; // long range is -2147483648 to 2147483647, so 12 bytes including terminator
432  ltoa(value, valueString, 10);
433  return setField(field, valueString);
434  };
435 
436 
468  int setField(unsigned int field, float value)
469  {
470  char valueString[20]; // range is -999999000000.00000 to 999999000000.00000, so 19 + 1 for the terminator
471  int status = convertFloatToChar(value, valueString);
472  if(status != OK_SUCCESS) return status;
473 
474  return setField(field, valueString);
475  };
476 
508  int setField(unsigned int field, const char * value)
509  {
510  return setField(field, String(value));
511  };
512 
544  int setField(unsigned int field, String value)
545  {
546  #ifdef PRINT_DEBUG_MESSAGES
547  Serial.print("ts::setField (field: "); Serial.print(field); Serial.print(" value: \""); Serial.print(value); Serial.println("\")");
548  #endif
549  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX) return ERR_INVALID_FIELD_NUM;
550  // Max # bytes for ThingSpeak field is 255 (UTF-8)
551  if(value.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
552  this->nextWriteField[field - 1] = value;
553  return OK_SUCCESS;
554  };
555 
556 
590  int setLatitude(float latitude)
591  {
592  #ifdef PRINT_DEBUG_MESSAGES
593  Serial.print("ts::setLatitude(latitude: "); Serial.print(latitude,3); Serial.println("\")");
594  #endif
595  this->nextWriteLatitude = latitude;
596  return OK_SUCCESS;
597  };
598 
599 
633  int setLongitude(float longitude)
634  {
635  #ifdef PRINT_DEBUG_MESSAGES
636  Serial.print("ts::setLongitude(longitude: "); Serial.print(longitude,3); Serial.println("\")");
637  #endif
638  this->nextWriteLongitude = longitude;
639  return OK_SUCCESS;
640  };
641 
642 
676  int setElevation(float elevation)
677  {
678  #ifdef PRINT_DEBUG_MESSAGES
679  Serial.print("ts::setElevation(elevation: "); Serial.print(elevation,3); Serial.println("\")");
680  #endif
681  this->nextWriteElevation = elevation;
682  return OK_SUCCESS;
683  };
684 
718  int setStatus(const char * status)
719  {
720  return setStatus(String(status));
721  };
722 
755  int setStatus(String status)
756  {
757  #ifdef PRINT_DEBUG_MESSAGES
758  Serial.print("ts::setStatus(status: "); Serial.print(status); Serial.println("\")");
759  #endif
760  // Max # bytes for ThingSpeak field is 255 (UTF-8)
761  if(status.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
762  this->nextWriteStatus = status;
763  return OK_SUCCESS;
764  };
765 
798  int setTwitterTweet(const char * twitter, const char * tweet)
799  {
800  return setTwitterTweet(String(twitter), String(tweet));
801  };
802 
835  int setTwitterTweet(String twitter, const char * tweet)
836  {
837  return setTwitterTweet(twitter, String(tweet));
838  };
839 
872  int setTwitterTweet(const char * twitter, String tweet)
873  {
874  return setTwitterTweet(String(twitter), tweet);
875  };
876 
909  int setTwitterTweet(String twitter, String tweet){
910  #ifdef PRINT_DEBUG_MESSAGES
911  Serial.print("ts::setTwitterTweet(twitter: "); Serial.print(twitter); Serial.print(", tweet: "); Serial.print(tweet); Serial.println("\")");
912  #endif
913  // Max # bytes for ThingSpeak field is 255 (UTF-8)
914  if((twitter.length() > FIELDLENGTH_MAX) || (tweet.length() > FIELDLENGTH_MAX)) return ERR_OUT_OF_RANGE;
915 
916  this->nextWriteTwitter = twitter;
917  this->nextWriteTweet = tweet;
918 
919  return OK_SUCCESS;
920  };
921 
954  int setCreatedAt(const char * createdAt)
955  {
956  return setCreatedAt(String(createdAt));
957  }
958 
991  int setCreatedAt(String createdAt)
992  {
993  #ifdef PRINT_DEBUG_MESSAGES
994  Serial.print("ts::setCreatedAt(createdAt: "); Serial.print(createdAt); Serial.println("\")");
995  #endif
996 
997  // the ISO 8601 format is too complicated to check for valid timestamps here
998  // we'll need to reply on the api to tell us if there is a problem
999  // Max # bytes for ThingSpeak field is 255 (UTF-8)
1000  if(createdAt.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
1001  this->nextWriteCreatedAt = createdAt;
1002 
1003  return OK_SUCCESS;
1004  }
1005 
1006 
1041  int writeFields(unsigned long channelNumber, const char * writeAPIKey)
1042  {
1043  String postMessage = String("");
1044  bool fFirstItem = true;
1045  for(size_t iField = 0; iField < 8; iField++)
1046  {
1047  if(this->nextWriteField[iField].length() > 0)
1048  {
1049  if(!fFirstItem)
1050  {
1051  postMessage = postMessage + String("&");
1052  }
1053  postMessage = postMessage + String("field") + String(iField + 1) + String("=") + this->nextWriteField[iField];
1054  fFirstItem = false;
1055  this->nextWriteField[iField] = "";
1056  }
1057  }
1058 
1059  if(!isnan(nextWriteLatitude))
1060  {
1061  if(!fFirstItem)
1062  {
1063  postMessage = postMessage + String("&");
1064  }
1065  postMessage = postMessage + String("lat=") + String(this->nextWriteLatitude);
1066  fFirstItem = false;
1067  this->nextWriteLatitude = NAN;
1068  }
1069 
1070  if(!isnan(this->nextWriteLongitude))
1071  {
1072  if(!fFirstItem)
1073  {
1074  postMessage = postMessage + String("&");
1075  }
1076  postMessage = postMessage + String("long=") + String(this->nextWriteLongitude);
1077  fFirstItem = false;
1078  this->nextWriteLongitude = NAN;
1079  }
1080 
1081 
1082  if(!isnan(this->nextWriteElevation))
1083  {
1084  if(!fFirstItem)
1085  {
1086  postMessage = postMessage + String("&");
1087  }
1088  postMessage = postMessage + String("elevation=") + String(this->nextWriteElevation);
1089  fFirstItem = false;
1090  this->nextWriteElevation = NAN;
1091  }
1092 
1093  if(this->nextWriteStatus.length() > 0)
1094  {
1095  if(!fFirstItem)
1096  {
1097  postMessage = postMessage + String("&");
1098  }
1099  postMessage = postMessage + String("status=") + String(this->nextWriteStatus);
1100  fFirstItem = false;
1101  this->nextWriteStatus = "";
1102  }
1103 
1104  if(this->nextWriteTwitter.length() > 0)
1105  {
1106  if(!fFirstItem)
1107  {
1108  postMessage = postMessage + String("&");
1109  }
1110  postMessage = postMessage + String("twitter=") + String(this->nextWriteTwitter);
1111  fFirstItem = false;
1112  this->nextWriteTwitter = "";
1113  }
1114 
1115  if(this->nextWriteTweet.length() > 0)
1116  {
1117  if(!fFirstItem)
1118  {
1119  postMessage = postMessage + String("&");
1120  }
1121  postMessage = postMessage + String("tweet=") + String(this->nextWriteTweet);
1122  fFirstItem = false;
1123  this->nextWriteTweet = "";
1124  }
1125 
1126  if(this->nextWriteCreatedAt.length() > 0)
1127  {
1128  if(!fFirstItem)
1129  {
1130  postMessage = postMessage + String("&");
1131  }
1132  postMessage = postMessage + String("created_at=") + String(this->nextWriteCreatedAt);
1133  fFirstItem = false;
1134  this->nextWriteCreatedAt = "";
1135  }
1136 
1137 
1138  if(fFirstItem)
1139  {
1140  // setField was not called before writeFields
1141  return ERR_SETFIELD_NOT_CALLED;
1142  }
1143 
1144  return writeRaw(channelNumber, postMessage, writeAPIKey);
1145  };
1146 
1147 
1168  int writeRaw(unsigned long channelNumber, const char * postMessage, const char * writeAPIKey)
1169  {
1170  return writeRaw(channelNumber, String(postMessage), writeAPIKey);
1171  };
1172 
1173 
1189  int writeRaw(unsigned long channelNumber, String postMessage, const char * writeAPIKey)
1190  {
1191  #ifdef PRINT_DEBUG_MESSAGES
1192  Serial.print("ts::writeRaw (channelNumber: "); Serial.print(channelNumber); Serial.print(" writeAPIKey: "); Serial.print(writeAPIKey); Serial.print(" postMessage: \""); Serial.print(postMessage); Serial.println("\")");
1193  #endif
1194 
1195  if(!connectThingSpeak())
1196  {
1197  // Failed to connect to ThingSpeak
1198  return ERR_CONNECT_FAILED;
1199  }
1200 
1201  postMessage = postMessage + String("&headers=false");
1202 
1203  #ifdef PRINT_DEBUG_MESSAGES
1204  Serial.print(" POST \"");Serial.print(postMessage);Serial.println("\"");
1205  #endif
1206 
1207  postMessage = postMessage + String("\n");
1208 
1209  // Post data to thingspeak
1210  if(!this->client->print("POST /update HTTP/1.1\r\n")) return abortWriteRaw();
1211  if(!writeHTTPHeader(writeAPIKey)) return abortWriteRaw();
1212  if(!this->client->print("Content-Type: application/x-www-form-urlencoded\r\n")) return abortWriteRaw();
1213  if(!this->client->print("Content-Length: ")) return abortWriteRaw();
1214  if(!this->client->print(postMessage.length())) return abortWriteRaw();
1215  if(!this->client->print("\r\n\r\n")) return abortWriteRaw();
1216  if(!this->client->print(postMessage)) return abortWriteRaw();
1217 
1218  String entryIDText = String();
1219  int status = getHTTPResponse(entryIDText);
1220  if(status != OK_SUCCESS)
1221  {
1222  client->stop();
1223  return status;
1224  }
1225  long entryID = entryIDText.toInt();
1226 
1227  #ifdef PRINT_DEBUG_MESSAGES
1228  Serial.print(" Entry ID \"");Serial.print(entryIDText);Serial.print("\" (");Serial.print(entryID);Serial.println(")");
1229  #endif
1230 
1231  client->stop();
1232 
1233  #ifdef PRINT_DEBUG_MESSAGES
1234  Serial.println("disconnected.");
1235  #endif
1236  if(entryID == 0)
1237  {
1238  // ThingSpeak did not accept the write
1239  status = ERR_NOT_INSERTED;
1240  }
1241  return status;
1242  };
1243 
1259  String readStringField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1260  {
1261  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX)
1262  {
1263  this->lastReadStatus = ERR_INVALID_FIELD_NUM;
1264  return("");
1265  }
1266  #ifdef PRINT_DEBUG_MESSAGES
1267  Serial.print("ts::readStringField(channelNumber: "); Serial.print(channelNumber);
1268  if(NULL != readAPIKey)
1269  {
1270  Serial.print(" readAPIKey: "); Serial.print(readAPIKey);
1271  }
1272  Serial.print(" field: "); Serial.print(field); Serial.println(")");
1273  #endif
1274  return readRaw(channelNumber, String(String("/fields/") + String(field) + String("/last")), readAPIKey);
1275  }
1276 
1277 
1292  String readStringField(unsigned long channelNumber, unsigned int field)
1293  {
1294  return readStringField(channelNumber, field, NULL);
1295  };
1296 
1297 
1314  float readFloatField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1315  {
1316  return convertStringToFloat(readStringField(channelNumber, field, readAPIKey));
1317  };
1318 
1319 
1335  float readFloatField(unsigned long channelNumber, unsigned int field)
1336  {
1337  return readFloatField(channelNumber, field, NULL);
1338  };
1339 
1340 
1356  long readLongField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1357  {
1358  // Note that although the function is called "toInt" it really returns a long.
1359  return readStringField(channelNumber, field, readAPIKey).toInt();
1360  }
1361 
1362 
1377  long readLongField(unsigned long channelNumber, unsigned int field)
1378  {
1379  return readLongField(channelNumber, field, NULL);
1380  };
1381 
1382 
1399  int readIntField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1400  {
1401  return readLongField(channelNumber, field, readAPIKey);
1402  }
1403 
1404 
1420  int readIntField(unsigned long channelNumber, unsigned int field)
1421  {
1422  return readLongField(channelNumber, field, NULL);
1423  };
1424 
1439  String readStatus(unsigned long channelNumber, const char * readAPIKey)
1440  {
1441  String content = readRaw(channelNumber, "/feeds/last.txt?status=true", readAPIKey);
1442 
1443  if(getLastReadStatus() != OK_SUCCESS){
1444  return String("");
1445  }
1446 
1447  return getJSONValueByKey(content, "status");
1448  };
1449 
1463  String readStatus(unsigned long channelNumber)
1464  {
1465  return readStatus(channelNumber, NULL);
1466  };
1467 
1482  String readCreatedAt(unsigned long channelNumber, const char * readAPIKey)
1483  {
1484  String content = readRaw(channelNumber, "/feeds/last.txt", readAPIKey);
1485 
1486  if(getLastReadStatus() != OK_SUCCESS){
1487  return String("");
1488  }
1489 
1490  return getJSONValueByKey(content, "created_at");
1491  };
1492 
1506  String readCreatedAt(unsigned long channelNumber)
1507  {
1508  return readCreatedAt(channelNumber, NULL);
1509  };
1510 
1526  String readRaw(unsigned long channelNumber, String URLSuffix)
1527  {
1528  return readRaw(channelNumber, URLSuffix, NULL);
1529  }
1530 
1547  String readRaw(unsigned long channelNumber, String URLSuffix, const char * readAPIKey)
1548  {
1549  #ifdef PRINT_DEBUG_MESSAGES
1550  Serial.print("ts::readRaw (channelNumber: "); Serial.print(channelNumber);
1551  if(NULL != readAPIKey)
1552  {
1553  Serial.print(" readAPIKey: "); Serial.print(readAPIKey);
1554  }
1555  Serial.print(" URLSuffix: \""); Serial.print(URLSuffix); Serial.println("\")");
1556  #endif
1557 
1558  if(!connectThingSpeak())
1559  {
1560  this->lastReadStatus = ERR_CONNECT_FAILED;
1561  return String("");
1562  }
1563 
1564  String URL = String("/channels/") + String(channelNumber) + URLSuffix;
1565 
1566  #ifdef PRINT_DEBUG_MESSAGES
1567  Serial.print(" GET \"");Serial.print(URL);Serial.println("\"");
1568  #endif
1569 
1570  // Post data to thingspeak
1571  if(!this->client->print("GET ")) return abortReadRaw();
1572  if(!this->client->print(URL)) return abortReadRaw();
1573  if(!this->client->print(" HTTP/1.1\r\n")) return abortReadRaw();
1574  if(!writeHTTPHeader(readAPIKey)) return abortReadRaw();
1575  if(!this->client->print("\r\n")) return abortReadRaw();
1576 
1577  String content = String();
1578  int status = getHTTPResponse(content);
1579 
1580  this->lastReadStatus = status;
1581 
1582 
1583  #ifdef PRINT_DEBUG_MESSAGES
1584  if(status == OK_SUCCESS)
1585  {
1586  Serial.print("Read: \""); Serial.print(content); Serial.println("\"");
1587  }
1588  #endif
1589 
1590  client->stop();
1591  #ifdef PRINT_DEBUG_MESSAGES
1592  Serial.println("disconnected.");
1593  #endif
1594 
1595  if(status != OK_SUCCESS)
1596  {
1597  // return status;
1598  return String("");
1599  }
1600 
1601  // This is a workaround to a bug in the Spark implementation of String
1602  return String("") + content;
1603  };
1604 
1639  {
1640  return this->lastReadStatus;
1641  };
1642 private:
1643 
1644  String getJSONValueByKey(String textToSearch, String key)
1645  {
1646  if(textToSearch.length() == 0){
1647  return String("");
1648  }
1649 
1650  String searchPhrase = String("\"") + key + String("\":\"");
1651 
1652  int fromPosition = textToSearch.indexOf(searchPhrase,0);
1653 
1654  if(fromPosition == -1){
1655  // return because there is no status or it's null
1656  return String("");
1657  }
1658 
1659  fromPosition = fromPosition + searchPhrase.length();
1660 
1661  int toPosition = textToSearch.indexOf("\"", fromPosition);
1662 
1663 
1664  if(toPosition == -1){
1665  // return because there is no end quote
1666  return String("");
1667  }
1668 
1669  textToSearch.remove(toPosition);
1670 
1671  return textToSearch.substring(fromPosition);
1672  }
1673 
1674  int abortWriteRaw()
1675  {
1676  this->client->stop();
1677  return ERR_UNEXPECTED_FAIL;
1678  }
1679 
1680  String abortReadRaw()
1681  {
1682  this->client->stop();
1683  #ifdef PRINT_DEBUG_MESSAGES
1684  Serial.println("ReadRaw abort - disconnected.");
1685  #endif
1686  this->lastReadStatus = ERR_UNEXPECTED_FAIL;
1687  return String("");
1688  }
1689 
1690  void setServer(const char * customHostName, unsigned int port)
1691  {
1692  #ifdef PRINT_DEBUG_MESSAGES
1693  Serial.print("ts::setServer (URL: \""); Serial.print(customHostName); Serial.println("\")");
1694  #endif
1695  this->customIP = INADDR_NONE;
1696  this->customHostName = customHostName;
1697  this->port = port;
1698  };
1699 
1700  void setServer(IPAddress customIP, unsigned int port)
1701  {
1702  #ifdef PRINT_DEBUG_MESSAGES
1703  Serial.print("ts::setServer (IP: \""); Serial.print(customIP); Serial.println("\")");
1704  #endif
1705  this->customIP = customIP;
1706  this->customHostName = NULL;
1707  this->port = port;
1708  };
1709 
1710  void setServer()
1711  {
1712  #ifdef PRINT_DEBUG_MESSAGES
1713  Serial.print("ts::setServer (default)");
1714  #endif
1715  this->customIP = INADDR_NONE;
1716  this->customHostName = NULL;
1717  this->port = THINGSPEAK_PORT_NUMBER;
1718  };
1719 
1720  void setClient(Client * client) {this->client = client;};
1721 
1722  Client * client = NULL;
1723  const char * customHostName = NULL;
1724  IPAddress customIP = INADDR_NONE;
1725  unsigned int port = THINGSPEAK_PORT_NUMBER;
1726  String nextWriteField[8];
1727  float nextWriteLatitude;
1728  float nextWriteLongitude;
1729  float nextWriteElevation;
1730  int lastReadStatus;
1731  String nextWriteStatus;
1732  String nextWriteTwitter;
1733  String nextWriteTweet;
1734  String nextWriteCreatedAt;
1735 
1736  bool connectThingSpeak()
1737  {
1738  bool connectSuccess = false;
1739  if(this->customIP == INADDR_NONE && NULL == this->customHostName)
1740  {
1741  #ifdef PRINT_DEBUG_MESSAGES
1742  Serial.print(" Connect to default ThingSpeak URL...");
1743  #endif
1744  connectSuccess = client->connect(THINGSPEAK_URL,THINGSPEAK_PORT_NUMBER);
1745  if(!connectSuccess)
1746  {
1747  #ifdef PRINT_DEBUG_MESSAGES
1748  Serial.print("Failed. Try default IP...");
1749  #endif
1750  connectSuccess = client->connect(THINGSPEAK_IPADDRESS,THINGSPEAK_PORT_NUMBER);
1751  }
1752  }
1753  else
1754  {
1755  if(!(this->customIP == INADDR_NONE))
1756  {
1757  // Connect to the server on port 80 (HTTP) at the customIP address
1758  #ifdef PRINT_DEBUG_MESSAGES
1759  Serial.print(" Connect to ");Serial.print(this->customIP);Serial.print("...");
1760  #endif
1761  connectSuccess = client->connect(this->customIP,this->port);
1762  }
1763  if(NULL != this->customHostName)
1764  {
1765  // Connect to the server on port 80 (HTTP) at the URL address
1766  #ifdef PRINT_DEBUG_MESSAGES
1767  Serial.print(" Connect to ");Serial.print(this->customHostName);Serial.print(" ...");
1768  #endif
1769  connectSuccess = client->connect(customHostName,this->port);
1770  }
1771  }
1772 
1773  #ifdef PRINT_DEBUG_MESSAGES
1774  if (connectSuccess)
1775  {
1776  Serial.println("Success.");
1777  }
1778  else
1779  {
1780  Serial.println("Failed.");
1781  }
1782  #endif
1783  return connectSuccess;
1784  };
1785 
1786  bool writeHTTPHeader(const char * APIKey)
1787  {
1788  if(NULL != this->customHostName)
1789  {
1790  if (!this->client->print("Host: ")) return false;
1791  if (!this->client->print(this->customHostName)) return false;
1792  if (!this->client->print("\r\n")) return false;
1793  }
1794  else
1795  {
1796  if (!this->client->print("Host: api.thingspeak.com\r\n")) return false;
1797  }
1798  if (!this->client->print("Connection: close\r\n")) return false;
1799  if (!this->client->print("User-Agent: ")) return false;
1800  if (!this->client->print(TS_USER_AGENT)) return false;
1801  if (!this->client->print("\r\n")) return false;
1802  if(NULL != APIKey)
1803  {
1804  if (!this->client->print("X-THINGSPEAKAPIKEY: ")) return false;
1805  if (!this->client->print(APIKey)) return false;
1806  if (!this->client->print("\r\n")) return false;
1807  }
1808  return true;
1809  };
1810 
1811  int getHTTPResponse(String & response)
1812  {
1813  long startWaitForResponseAt = millis();
1814  while(client->available() == 0 && millis() - startWaitForResponseAt < TIMEOUT_MS_SERVERRESPONSE)
1815  {
1816  delay(100);
1817  }
1818  if(client->available() == 0)
1819  {
1820  return ERR_TIMEOUT; // Didn't get server response in time
1821  }
1822 
1823  if(!client->find(const_cast<char *>("HTTP/1.1")))
1824  {
1825  #ifdef PRINT_HTTP
1826  Serial.println("ERROR: Didn't find HTTP/1.1");
1827  #endif
1828  return ERR_BAD_RESPONSE; // Couldn't parse response (didn't find HTTP/1.1)
1829  }
1830  int status = client->parseInt();
1831  #ifdef PRINT_HTTP
1832  Serial.print("Got Status of ");Serial.println(status);
1833  #endif
1834  if(status != OK_SUCCESS)
1835  {
1836  return status;
1837  }
1838 
1839  if(!client->find(const_cast<char *>("\r\n")))
1840  {
1841  #ifdef PRINT_HTTP
1842  Serial.println("ERROR: Didn't find end of status line");
1843  #endif
1844  return ERR_BAD_RESPONSE;
1845  }
1846  #ifdef PRINT_HTTP
1847  Serial.println("Found end of status line");
1848  #endif
1849 
1850  if(!client->find(const_cast<char *>("\n\r\n")))
1851  {
1852  #ifdef PRINT_HTTP
1853  Serial.println("ERROR: Didn't find end of header");
1854  #endif
1855  return ERR_BAD_RESPONSE;
1856  }
1857  #ifdef PRINT_HTTP
1858  Serial.println("Found end of header");
1859  #endif
1860 
1861  String tempString = client->readString();
1862  response = tempString;
1863  #ifdef PRINT_HTTP
1864  Serial.print("Response: \"");Serial.print(response);Serial.println("\"");
1865  #endif
1866  return status;
1867  };
1868 
1869  int convertFloatToChar(float value, char *valueString)
1870  {
1871  // Supported range is -999999000000 to 999999000000
1872  if(0 == isinf(value) && (value > 999999000000 || value < -999999000000))
1873  {
1874  // Out of range
1875  return ERR_OUT_OF_RANGE;
1876  }
1877  // assume that 5 places right of decimal should be sufficient for most applications
1878 
1879  #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAM)
1880  sprintf(valueString, "%.5f", value);
1881  #else
1882  dtostrf(value,1,5, valueString);
1883  #endif
1884  return OK_SUCCESS;
1885  };
1886 
1887  float convertStringToFloat(String value)
1888  {
1889  // There's a bug in the AVR function strtod that it doesn't decode -INF correctly (it maps it to INF)
1890  float result = value.toFloat();
1891 
1892  if(1 == isinf(result) && *value.c_str() == '-')
1893  {
1894  result = (float)-INFINITY;
1895  }
1896  return result;
1897  };
1898 
1899  void resetWriteFields()
1900  {
1901  for(size_t iField = 0; iField < 8; iField++)
1902  {
1903  this->nextWriteField[iField] = "";
1904  }
1905  this->nextWriteLatitude = NAN;
1906  this->nextWriteLongitude = NAN;
1907  this->nextWriteElevation = NAN;
1908  this->nextWriteStatus = "";
1909  this->nextWriteTwitter = "";
1910  this->nextWriteTweet = "";
1911  this->nextWriteCreatedAt = "";
1912  };
1913 };
1914 
1915 extern ThingSpeakClass ThingSpeak;
1916 
1917 #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:1168
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:798
String readStringField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest string from a private ThingSpeak channel.
Definition: ThingSpeak.h:1259
String readStringField(unsigned long channelNumber, unsigned int field)
Read the latest string from a public ThingSpeak channel.
Definition: ThingSpeak.h:1292
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:429
String readCreatedAt(unsigned long channelNumber)
Read the created-at timestamp associated with the latest update to a private ThingSpeak channel...
Definition: ThingSpeak.h:1506
int writeRaw(unsigned long channelNumber, String postMessage, const char *writeAPIKey)
Write a raw POST to a ThingSpeak channel.
Definition: ThingSpeak.h:1189
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:174
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:1041
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:388
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:872
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:835
int setLatitude(float latitude)
Set the latitude of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:590
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:1482
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:755
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:141
int getLastReadStatus()
Get the status of the previous read.
Definition: ThingSpeak.h:1638
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:233
Enables an Arduino, ESP8266, ESP32 or other compatible hardware to write or read data to or from Thin...
Definition: ThingSpeak.h:111
long readLongField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest long from a private ThingSpeak channel.
Definition: ThingSpeak.h:1356
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:342
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:718
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:508
String readRaw(unsigned long channelNumber, String URLSuffix)
Read a raw response from a public ThingSpeak channel.
Definition: ThingSpeak.h:1526
long readLongField(unsigned long channelNumber, unsigned int field)
Read the latest long from a public ThingSpeak channel.
Definition: ThingSpeak.h:1377
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:280
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:954
int readIntField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest int from a private ThingSpeak channel.
Definition: ThingSpeak.h:1399
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:544
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:991
int setLongitude(float longitude)
Set the longitude of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:633
float readFloatField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest float from a private ThingSpeak channel.
Definition: ThingSpeak.h:1314
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:909
String readStatus(unsigned long channelNumber, const char *readAPIKey)
Read the latest status from a private ThingSpeak channel.
Definition: ThingSpeak.h:1439
bool begin(Client &client)
Initializes the ThingSpeak library and network settings using the ThingSpeak.com service.
Definition: ThingSpeak.h:205
float readFloatField(unsigned long channelNumber, unsigned int field)
Read the latest float from a public ThingSpeak channel.
Definition: ThingSpeak.h:1335
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:313
int readIntField(unsigned long channelNumber, unsigned int field)
Read the latest int from a public ThingSpeak channel.
Definition: ThingSpeak.h:1420
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:468
String readRaw(unsigned long channelNumber, String URLSuffix, const char *readAPIKey)
Read a raw response from a private ThingSpeak channel.
Definition: ThingSpeak.h:1547
String readStatus(unsigned long channelNumber)
Read the latest status from a public ThingSpeak channel.
Definition: ThingSpeak.h:1463
int setElevation(float elevation)
Set the elevation of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:676
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:256