ThingSpeak Communication Library
Enables an Arduino, ESP8266, or Particle to write or read data to or from ThingSpeak™
ThingSpeak.h
1 /*
2  ThingSpeak(TM) Communication Library For Arduino, ESP8266, and Particle
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 a free IoT service for building
8  systems that collect, analyze, and react to their environments.
9 
10  Copyright 2016, 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 #ifdef SPARK
57  // Create platform defines for Particle devices
58  #if PLATFORM_ID == 6
59  #define PARTICLE_PHOTON
60  #define PARTICLE_PHOTONELECTRON
61  #elif PLATFORM_ID == 10
62  #define PARTICLE_ELECTRON
63  #define PARTICLE_PHOTONELECTRON
64  #elif PLATFORM_ID == 0
65  #define PARTICLE_CORE
66  #endif
67 
68  #include "math.h"
69  #include "application.h"
70  #ifdef PARTICLE_PHOTONELECTRON
71  extern char* dtoa(double val, unsigned char prec, char *sout);
72  // On spark photon, There is no itoa, so map to ltoa.
73  #include "string_convert.h"
74  #define itoa ltoa
75  #else
76  // On spark core, a long and an int are equivalent, and so there's no "ltoa" function defined. Map it to itoa.
77  extern char * itoa(int a, char* buffer, unsigned char radix);
78  #define ltoa itoa
79  extern char *dtostrf (double val, signed char width, unsigned char prec, char *sout);
80  #endif
81 #else
82  #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ESP8266)
83  #include "Arduino.h"
84  #include <Client.h>
85  #else
86  #error Only Arduino Yun, Uno/Mega/Due with either Wired or wi-fi Ethernet shield, ESP8266, and Spark Core/Photon/Electron are supported.
87  #endif
88 #endif
89 
90 #define THINGSPEAK_URL "api.thingspeak.com"
91 #define THINGSPEAK_IPADDRESS IPAddress(184,106,153,149)
92 #define THINGSPEAK_PORT_NUMBER 80
93 
94 #ifdef ARDUINO_ARCH_AVR
95  #ifdef ARDUINO_AVR_YUN
96  #define TS_USER_AGENT "tslib-arduino/1.0 (arduino yun)"
97  #else
98  #define TS_USER_AGENT "tslib-arduino/1.0 (arduino uno or mega)"
99  #endif
100 #elif defined(ARDUINO_ARCH_ESP8266)
101  #define TS_USER_AGENT "tslib-arduino/1.0 (ESP8266)"
102 #elif defined(SPARK)
103  #ifdef PARTICLE_CORE
104  #define TS_USER_AGENT "tslib-arduino/1.0 (particle core)"
105  #elif defined(PARTICLE_PHOTON)
106  #define TS_USER_AGENT "tslib-arduino/1.0 (particle photon)"
107  #elif defined(PARTICLE_ELECTRON)
108  #define TS_USER_AGENT "tslib-arduino/1.0 (particle electron)"
109  #endif
110  #define SPARK_PUBLISH_TTL 60 // Spark "time to live" for published messages
111  #define SPARK_PUBLISH_TOPIC "thingspeak-debug"
112 #endif
113 
114 #define FIELDNUM_MIN 1
115 #define FIELDNUM_MAX 8
116 #define FIELDLENGTH_MAX 255 // Max length for a field in ThingSpeak is 255 bytes (UTF-8)
117 
118 #define TIMEOUT_MS_SERVERRESPONSE 5000 // Wait up to five seconds for server to respond
119 
120 #define OK_SUCCESS 200 // OK / Success
121 #define ERR_BADAPIKEY 400 // Incorrect API key (or invalid ThingSpeak server address)
122 #define ERR_BADURL 404 // Incorrect API key (or invalid ThingSpeak server address)
123 #define ERR_OUT_OF_RANGE -101 // Value is out of range or string is too long (> 255 bytes)
124 #define ERR_INVALID_FIELD_NUM -201 // Invalid field number specified
125 #define ERR_SETFIELD_NOT_CALLED -210 // setField() was not called before writeFields()
126 #define ERR_CONNECT_FAILED -301 // Failed to connect to ThingSpeak
127 #define ERR_UNEXPECTED_FAIL -302 // Unexpected failure during write to ThingSpeak
128 #define ERR_BAD_RESPONSE -303 // Unable to parse response
129 #define ERR_TIMEOUT -304 // Timeout waiting for server to respond
130 #define ERR_NOT_INSERTED -401 // Point was not inserted (most probable cause is the rate limit of once every 15 seconds)
131 
136 {
137  public:
139  {
140  resetWriteFields();
141  this->lastReadStatus = OK_SUCCESS;
142  };
143 
165  bool begin(Client & client, const char * customHostName, unsigned int port)
166  {
167 #ifdef PRINT_DEBUG_MESSAGES
168  Serial.print("ts::tsBegin (client: Client URL: "); Serial.print(customHostName); Serial.println(")");
169 #endif
170  this->setClient(&client);
171  this->setServer(customHostName, port);
172  resetWriteFields();
173  this->lastReadStatus = OK_SUCCESS;
174  return true;
175  };
176 
198  bool begin(Client & client, IPAddress customIP, unsigned int port)
199  {
200 #ifdef PRINT_DEBUG_MESSAGES
201  Serial.print("ts::tsBegin (client: Client IP: "); Serial.print(customIP); Serial.println(")");
202 #endif
203  this->setClient(&client);
204  this->setServer(customIP, port);
205  resetWriteFields();
206  this->lastReadStatus = OK_SUCCESS;
207  return true;
208  };
209 
229  bool begin(Client & client)
230  {
231 #ifdef PRINT_DEBUG_MESSAGES
232  Serial.print("ts::tsBegin");
233 #endif
234  this->setClient(&client);
235  this->setServer();
236  resetWriteFields();
237  this->lastReadStatus = OK_SUCCESS;
238  return true;
239  };
240 
257  int writeField(unsigned long channelNumber, unsigned int field, int value, const char * writeAPIKey)
258  {
259 #ifdef SPARK
260  // On Spark, int and long are the same, so map to the long version
261  return writeField(channelNumber, field, (long)value, writeAPIKey);
262 #else
263  char valueString[10]; // int range is -32768 to 32768, so 7 bytes including terminator, plus a little extra
264  itoa(value, valueString, 10);
265  return writeField(channelNumber, field, valueString, writeAPIKey);
266 #endif
267  };
268 
285  int writeField(unsigned long channelNumber, unsigned int field, long value, const char * writeAPIKey)
286  {
287  char valueString[15]; // long range is -2147483648 to 2147483647, so 12 bytes including terminator
288  ltoa(value, valueString, 10);
289  return writeField(channelNumber, field, valueString, writeAPIKey);
290  };
291 
309  int writeField(unsigned long channelNumber, unsigned int field, float value, const char * writeAPIKey)
310  {
311  #ifdef PRINT_DEBUG_MESSAGES
312  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(")");
313  #endif
314  char valueString[20]; // range is -999999000000.00000 to 999999000000.00000, so 19 + 1 for the terminator
315  int status = convertFloatToChar(value, valueString);
316  if(status != OK_SUCCESS) return status;
317 
318  return writeField(channelNumber, field, valueString, writeAPIKey);
319  };
320 
342  int writeField(unsigned long channelNumber, unsigned int field, const char * value, const char * writeAPIKey)
343  {
344  return writeField(channelNumber, field, String(value), writeAPIKey);
345  };
346 
371  int writeField(unsigned long channelNumber, unsigned int field, String value, const char * writeAPIKey)
372  {
373  // Invalid field number specified
374  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX) return ERR_INVALID_FIELD_NUM;
375  // Max # bytes for ThingSpeak field is 255
376  if(value.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
377 
378  #ifdef PRINT_DEBUG_MESSAGES
379  #ifdef SPARK
380  Particle.publish(SPARK_PUBLISH_TOPIC, "writeField (" + String(channelNumber) + ", " + String(writeAPIKey) + ", " + String(field) + ", " + String(value) + ")", SPARK_PUBLISH_TTL, PRIVATE);
381  #else
382  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("\")");
383  #endif
384  #endif
385  String postMessage = String("field") + String(field) + "=" + value;
386  return writeRaw(channelNumber, postMessage, writeAPIKey);
387  };
388 
389 
421  int setField(unsigned int field, int value)
422  {
423 #ifdef SPARK
424  // On Spark, int and long are the same, so map to the long version
425  return setField(field, (long)value);
426 #else
427  char valueString[10]; // int range is -32768 to 32768, so 7 bytes including terminator
428  itoa(value, valueString, 10);
429 
430  return setField(field, valueString);
431 #endif
432  };
433 
465  int setField(unsigned int field, long value)
466  {
467  char valueString[15]; // long range is -2147483648 to 2147483647, so 12 bytes including terminator
468  ltoa(value, valueString, 10);
469  return setField(field, valueString);
470  };
471 
472 
504  int setField(unsigned int field, float value)
505  {
506  char valueString[20]; // range is -999999000000.00000 to 999999000000.00000, so 19 + 1 for the terminator
507  int status = convertFloatToChar(value, valueString);
508  if(status != OK_SUCCESS) return status;
509 
510  return setField(field, valueString);
511  };
512 
544  int setField(unsigned int field, const char * value)
545  {
546  return setField(field, String(value));
547  };
548 
580  int setField(unsigned int field, String value)
581  {
582  #ifdef PRINT_DEBUG_MESSAGES
583  #ifdef SPARK
584  Particle.publish(SPARK_PUBLISH_TOPIC, "setField " + String(field) + " to " + String(value), SPARK_PUBLISH_TTL, PRIVATE);
585  #else
586  Serial.print("ts::setField (field: "); Serial.print(field); Serial.print(" value: \""); Serial.print(value); Serial.println("\")");
587  #endif
588  #endif
589  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX) return ERR_INVALID_FIELD_NUM;
590  // Max # bytes for ThingSpeak field is 255 (UTF-8)
591  if(value.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
592  this->nextWriteField[field - 1] = value;
593  return OK_SUCCESS;
594  };
595 
596 
630  int setLatitude(float latitude)
631  {
632  #ifdef PRINT_DEBUG_MESSAGES
633  Serial.print("ts::setLatitude(latitude: "); Serial.print(latitude,3); Serial.println("\")");
634  #endif
635  this->nextWriteLatitude = latitude;
636  return OK_SUCCESS;
637  };
638 
639 
673  int setLongitude(float longitude)
674  {
675  #ifdef PRINT_DEBUG_MESSAGES
676  Serial.print("ts::setLongitude(longitude: "); Serial.print(longitude,3); Serial.println("\")");
677  #endif
678  this->nextWriteLongitude = longitude;
679  return OK_SUCCESS;
680  };
681 
682 
716  int setElevation(float elevation)
717  {
718  #ifdef PRINT_DEBUG_MESSAGES
719  Serial.print("ts::setElevation(elevation: "); Serial.print(elevation,3); Serial.println("\")");
720  #endif
721  this->nextWriteElevation = elevation;
722  return OK_SUCCESS;
723  };
724 
725 
760  int writeFields(unsigned long channelNumber, const char * writeAPIKey)
761  {
762  String postMessage = String("");
763  bool fFirstItem = true;
764  for(size_t iField = 0; iField < 8; iField++)
765  {
766  if(this->nextWriteField[iField].length() > 0)
767  {
768  if(!fFirstItem)
769  {
770  postMessage = postMessage + String("&");
771  }
772  postMessage = postMessage + String("field") + String(iField + 1) + String("=") + this->nextWriteField[iField];
773  fFirstItem = false;
774  this->nextWriteField[iField] = "";
775  }
776  }
777 
778  if(!isnan(nextWriteLatitude))
779  {
780  if(!fFirstItem)
781  {
782  postMessage = postMessage + String("&");
783  }
784  postMessage = postMessage + String("lat=") + String(this->nextWriteLatitude);
785  fFirstItem = false;
786  this->nextWriteLatitude = NAN;
787  }
788 
789  if(!isnan(this->nextWriteLongitude))
790  {
791  if(!fFirstItem)
792  {
793  postMessage = postMessage + String("&");
794  }
795  postMessage = postMessage + String("long=") + String(this->nextWriteLongitude);
796  fFirstItem = false;
797  this->nextWriteLongitude = NAN;
798  }
799 
800 
801  if(!isnan(this->nextWriteElevation))
802  {
803  if(!fFirstItem)
804  {
805  postMessage = postMessage + String("&");
806  }
807  postMessage = postMessage + String("elevation=") + String(this->nextWriteElevation);
808  fFirstItem = false;
809  this->nextWriteElevation = NAN;
810  }
811 
812  if(fFirstItem)
813  {
814  // setField was not called before writeFields
815  return ERR_SETFIELD_NOT_CALLED;
816  }
817 
818  return writeRaw(channelNumber, postMessage, writeAPIKey);
819  };
820 
821 
842  int writeRaw(unsigned long channelNumber, const char * postMessage, const char * writeAPIKey)
843  {
844  return writeRaw(channelNumber, String(postMessage), writeAPIKey);
845  };
846 
847 
863  int writeRaw(unsigned long channelNumber, String postMessage, const char * writeAPIKey)
864  {
865  #ifdef PRINT_DEBUG_MESSAGES
866  Serial.print("ts::writeRaw (channelNumber: "); Serial.print(channelNumber); Serial.print(" writeAPIKey: "); Serial.print(writeAPIKey); Serial.print(" postMessage: \""); Serial.print(postMessage); Serial.println("\")");
867  #endif
868 
869  if(!connectThingSpeak())
870  {
871  // Failed to connect to ThingSpeak
872  return ERR_CONNECT_FAILED;
873  }
874 
875  postMessage = postMessage + String("&headers=false");
876 
877  #ifdef PRINT_DEBUG_MESSAGES
878  #ifdef SPARK
879  Particle.publish(SPARK_PUBLISH_TOPIC, "Post " + postMessage, SPARK_PUBLISH_TTL, PRIVATE);
880  #else
881  Serial.print(" POST \"");Serial.print(postMessage);Serial.println("\"");
882  #endif
883  #endif
884 
885  postMessage = postMessage + String("\n");
886 
887  // Post data to thingspeak
888  if(!this->client->print("POST /update HTTP/1.1\n")) return abortWriteRaw();
889  if(!writeHTTPHeader(writeAPIKey)) return abortWriteRaw();
890  if(!this->client->print("Content-Type: application/x-www-form-urlencoded\n")) return abortWriteRaw();
891  if(!this->client->print("Content-Length: ")) return abortWriteRaw();
892  if(!this->client->print(postMessage.length())) return abortWriteRaw();
893  if(!this->client->print("\n\n")) return abortWriteRaw();
894  if(!this->client->print(postMessage)) return abortWriteRaw();
895 
896  String entryIDText = String();
897  int status = getHTTPResponse(entryIDText);
898  if(status != OK_SUCCESS)
899  {
900  client->stop();
901  return status;
902  }
903  long entryID = entryIDText.toInt();
904 
905  #ifdef PRINT_DEBUG_MESSAGES
906  Serial.print(" Entry ID \"");Serial.print(entryIDText);Serial.print("\" (");Serial.print(entryID);Serial.println(")");
907  #endif
908 
909  client->stop();
910 
911  #ifdef PRINT_DEBUG_MESSAGES
912  Serial.println("disconnected.");
913  #endif
914  if(entryID == 0)
915  {
916  // ThingSpeak did not accept the write
917  status = ERR_NOT_INSERTED;
918  }
919  return status;
920  };
921 
937  String readStringField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
938  {
939  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX)
940  {
941  this->lastReadStatus = ERR_INVALID_FIELD_NUM;
942  return("");
943  }
944  #ifdef PRINT_DEBUG_MESSAGES
945  Serial.print("ts::readStringField(channelNumber: "); Serial.print(channelNumber);
946  if(NULL != readAPIKey)
947  {
948  Serial.print(" readAPIKey: "); Serial.print(readAPIKey);
949  }
950  Serial.print(" field: "); Serial.print(field); Serial.println(")");
951  #endif
952  return readRaw(channelNumber, String(String("/fields/") + String(field) + String("/last")), readAPIKey);
953  }
954 
955 
970  String readStringField(unsigned long channelNumber, unsigned int field)
971  {
972  return readStringField(channelNumber, field, NULL);
973  };
974 
975 
992  float readFloatField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
993  {
994  return convertStringToFloat(readStringField(channelNumber, field, readAPIKey));
995  };
996 
997 
1013  float readFloatField(unsigned long channelNumber, unsigned int field)
1014  {
1015  return readFloatField(channelNumber, field, NULL);
1016  };
1017 
1018 
1034  long readLongField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1035  {
1036  // Note that although the function is called "toInt" it really returns a long.
1037  return readStringField(channelNumber, field, readAPIKey).toInt();
1038  }
1039 
1040 
1055  long readLongField(unsigned long channelNumber, unsigned int field)
1056  {
1057  return readLongField(channelNumber, field, NULL);
1058  };
1059 
1060 
1077  int readIntField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1078  {
1079  return readLongField(channelNumber, field, readAPIKey);
1080  }
1081 
1082 
1098  int readIntField(unsigned long channelNumber, unsigned int field)
1099  {
1100  return readLongField(channelNumber, field, NULL);
1101  };
1102 
1118  String readRaw(unsigned long channelNumber, String URLSuffix)
1119  {
1120  return readRaw(channelNumber, URLSuffix, NULL);
1121  }
1122 
1139  String readRaw(unsigned long channelNumber, String URLSuffix, const char * readAPIKey)
1140  {
1141  #ifdef PRINT_DEBUG_MESSAGES
1142  Serial.print("ts::readRaw (channelNumber: "); Serial.print(channelNumber);
1143  if(NULL != readAPIKey)
1144  {
1145  Serial.print(" readAPIKey: "); Serial.print(readAPIKey);
1146  }
1147  Serial.print(" URLSuffix: \""); Serial.print(URLSuffix); Serial.println("\")");
1148  #endif
1149 
1150  if(!connectThingSpeak())
1151  {
1152  this->lastReadStatus = ERR_CONNECT_FAILED;
1153  return String("");
1154  }
1155 
1156  String URL = String("/channels/") + String(channelNumber) + URLSuffix;
1157 
1158  #ifdef PRINT_DEBUG_MESSAGES
1159  Serial.print(" GET \"");Serial.print(URL);Serial.println("\"");
1160  #endif
1161 
1162  // Post data to thingspeak
1163  if(!this->client->print("GET ")) return abortReadRaw();
1164  if(!this->client->print(URL)) return abortReadRaw();
1165  if(!this->client->print(" HTTP/1.1\n")) return abortReadRaw();
1166  if(!writeHTTPHeader(readAPIKey)) return abortReadRaw();
1167  if(!this->client->print("\n")) return abortReadRaw();
1168 
1169  String content = String();
1170  int status = getHTTPResponse(content);
1171  this->lastReadStatus = status;
1172 
1173 
1174  #ifdef PRINT_DEBUG_MESSAGES
1175  if(status == OK_SUCCESS)
1176  {
1177  Serial.print("Read: \""); Serial.print(content); Serial.println("\"");
1178  }
1179  #endif
1180 
1181  client->stop();
1182  #ifdef PRINT_DEBUG_MESSAGES
1183  Serial.println("disconnected.");
1184  #endif
1185 
1186  if(status != OK_SUCCESS)
1187  {
1188  // return status;
1189  return String("");
1190  }
1191 
1192  // This is a workaround to a bug in the Spark implementation of String
1193  return String("") + content;
1194  };
1195 
1230  {
1231  return this->lastReadStatus;
1232  };
1233 private:
1234 
1235  int abortWriteRaw()
1236  {
1237  this->client->stop();
1238  return ERR_UNEXPECTED_FAIL;
1239  }
1240 
1241  String abortReadRaw()
1242  {
1243  this->client->stop();
1244  #ifdef PRINT_DEBUG_MESSAGES
1245  Serial.println("ReadRaw abort - disconnected.");
1246  #endif
1247  this->lastReadStatus = ERR_UNEXPECTED_FAIL;
1248  return String("");
1249  }
1250 
1251  void setServer(const char * customHostName, unsigned int port)
1252  {
1253  #ifdef PRINT_DEBUG_MESSAGES
1254  Serial.print("ts::setServer (URL: \""); Serial.print(customHostName); Serial.println("\")");
1255  #endif
1256  this->customIP = INADDR_NONE;
1257  this->customHostName = customHostName;
1258  this->port = port;
1259  };
1260 
1261  void setServer(IPAddress customIP, unsigned int port)
1262  {
1263  #ifdef PRINT_DEBUG_MESSAGES
1264  Serial.print("ts::setServer (IP: \""); Serial.print(customIP); Serial.println("\")");
1265  #endif
1266  this->customIP = customIP;
1267  this->customHostName = NULL;
1268  this->port = port;
1269  };
1270 
1271  void setServer()
1272  {
1273  #ifdef PRINT_DEBUG_MESSAGES
1274  Serial.print("ts::setServer (default)");
1275  #endif
1276  this->customIP = INADDR_NONE;
1277  this->customHostName = NULL;
1278  this->port = THINGSPEAK_PORT_NUMBER;
1279  };
1280 
1281  void setClient(Client * client) {this->client = client;};
1282 
1283  Client * client = NULL;
1284  const char * customHostName = NULL;
1285  IPAddress customIP = INADDR_NONE;
1286  unsigned int port = THINGSPEAK_PORT_NUMBER;
1287  String nextWriteField[8];
1288  float nextWriteLatitude;
1289  float nextWriteLongitude;
1290  float nextWriteElevation;
1291  int lastReadStatus;
1292 
1293  bool connectThingSpeak()
1294  {
1295  bool connectSuccess = false;
1296  if(this->customIP == INADDR_NONE && NULL == this->customHostName)
1297  {
1298  #ifdef PRINT_DEBUG_MESSAGES
1299  Serial.print(" Connect to default ThingSpeak URL...");
1300  #endif
1301  connectSuccess = client->connect(THINGSPEAK_URL,THINGSPEAK_PORT_NUMBER);
1302  if(!connectSuccess)
1303  {
1304  #ifdef PRINT_DEBUG_MESSAGES
1305  Serial.print("Failed. Try default IP...");
1306  #endif
1307  connectSuccess = client->connect(THINGSPEAK_IPADDRESS,THINGSPEAK_PORT_NUMBER);
1308  }
1309  }
1310  else
1311  {
1312  if(!(this->customIP == INADDR_NONE))
1313  {
1314  // Connect to the server on port 80 (HTTP) at the customIP address
1315  #ifdef PRINT_DEBUG_MESSAGES
1316  Serial.print(" Connect to ");Serial.print(this->customIP);Serial.print("...");
1317  #endif
1318  connectSuccess = client->connect(this->customIP,this->port);
1319  }
1320  if(NULL != this->customHostName)
1321  {
1322  // Connect to the server on port 80 (HTTP) at the URL address
1323  #ifdef PRINT_DEBUG_MESSAGES
1324  #ifdef SPARK
1325  Particle.publish(SPARK_PUBLISH_TOPIC, "Attempt Connect to URL " + String(this->customHostName), SPARK_PUBLISH_TTL, PRIVATE);
1326  #else
1327  Serial.print(" Connect to ");Serial.print(this->customHostName);Serial.print(" ...");
1328  #endif
1329  #endif
1330  connectSuccess = client->connect(customHostName,this->port);
1331  }
1332  }
1333 
1334  #ifdef PRINT_DEBUG_MESSAGES
1335  if (connectSuccess)
1336  {
1337  #ifdef SPARK
1338  Particle.publish(SPARK_PUBLISH_TOPIC, "Connection Success", SPARK_PUBLISH_TTL, PRIVATE);
1339  #else
1340  Serial.println("Success.");
1341  #endif
1342  }
1343  else
1344  {
1345  #ifdef SPARK
1346  Particle.publish(SPARK_PUBLISH_TOPIC, "Connection Failure", SPARK_PUBLISH_TTL, PRIVATE);
1347  #else
1348  Serial.println("Failed.");
1349  #endif
1350  }
1351  #endif
1352  return connectSuccess;
1353  };
1354 
1355  bool writeHTTPHeader(const char * APIKey)
1356  {
1357  if(NULL != this->customHostName)
1358  {
1359  if (!this->client->print("Host: ")) return false;
1360  if (!this->client->print(this->customHostName)) return false;
1361  if (!this->client->print("\n")) return false;
1362  }
1363  else
1364  {
1365  if (!this->client->print("Host: api.thingspeak.com\n")) return false;
1366  }
1367  if (!this->client->print("Connection: close\n")) return false;
1368  if (!this->client->print("User-Agent: ")) return false;
1369  if (!this->client->print(TS_USER_AGENT)) return false;
1370  if (!this->client->print("\n")) return false;
1371  if(NULL != APIKey)
1372  {
1373  if (!this->client->print("X-THINGSPEAKAPIKEY: ")) return false;
1374  if (!this->client->print(APIKey)) return false;
1375  if (!this->client->print("\n")) return false;
1376  }
1377  return true;
1378  };
1379 
1380  int getHTTPResponse(String & response)
1381  {
1382  long startWaitForResponseAt = millis();
1383  while(client->available() == 0 && millis() - startWaitForResponseAt < TIMEOUT_MS_SERVERRESPONSE)
1384  {
1385  delay(100);
1386  }
1387  if(client->available() == 0)
1388  {
1389  return ERR_TIMEOUT; // Didn't get server response in time
1390  }
1391 
1392  if(!client->find(const_cast<char *>("HTTP/1.1")))
1393  {
1394  #ifdef PRINT_HTTP
1395  #ifdef SPARK
1396  Particle.publish(SPARK_PUBLISH_TOPIC, "ERROR: Didn't find HTTP/1.1", SPARK_PUBLISH_TTL, PRIVATE);
1397  #else
1398  Serial.println("ERROR: Didn't find HTTP/1.1");
1399  #endif
1400  #endif
1401  return ERR_BAD_RESPONSE; // Couldn't parse response (didn't find HTTP/1.1)
1402  }
1403  int status = client->parseInt();
1404  #ifdef PRINT_HTTP
1405  #ifdef SPARK
1406  Particle.publish(SPARK_PUBLISH_TOPIC, "Got Status of " + String(status), SPARK_PUBLISH_TTL, PRIVATE);
1407  #else
1408  Serial.print("Got Status of ");Serial.println(status);
1409  #endif
1410  #endif
1411  if(status != OK_SUCCESS)
1412  {
1413  return status;
1414  }
1415 
1416  if(!client->find(const_cast<char *>("\r\n")))
1417  {
1418  #ifdef PRINT_HTTP
1419  #ifdef SPARK
1420  Particle.publish(SPARK_PUBLISH_TOPIC, "ERROR: Didn't find end of status line", SPARK_PUBLISH_TTL, PRIVATE);
1421  #else
1422  Serial.println("ERROR: Didn't find end of status line");
1423  #endif
1424  #endif
1425  return ERR_BAD_RESPONSE;
1426  }
1427  #ifdef PRINT_HTTP
1428  #ifdef SPARK
1429  Particle.publish(SPARK_PUBLISH_TOPIC, "Found end of status line", SPARK_PUBLISH_TTL, PRIVATE);
1430  #else
1431  Serial.println("Found end of status line");
1432  #endif
1433  #endif
1434 
1435  if(!client->find(const_cast<char *>("\n\r\n")))
1436  {
1437  #ifdef PRINT_HTTP
1438  #ifdef SPARK
1439  Particle.publish(SPARK_PUBLISH_TOPIC, "ERROR: Didn't find end of header", SPARK_PUBLISH_TTL, PRIVATE);
1440  #else
1441  Serial.println("ERROR: Didn't find end of header");
1442  #endif
1443  #endif
1444  return ERR_BAD_RESPONSE;
1445  }
1446  #ifdef PRINT_HTTP
1447  #ifdef SPARK
1448  Particle.publish(SPARK_PUBLISH_TOPIC, "Found end of header", SPARK_PUBLISH_TTL, PRIVATE);
1449  #else
1450  Serial.println("Found end of header");
1451  #endif
1452  #endif
1453  // This is a workaround to a bug in the Spark implementation of String
1454  String tempString = client->readStringUntil('\r');
1455  response = tempString;
1456  #ifdef PRINT_HTTP
1457  #ifdef SPARK
1458  Particle.publish(SPARK_PUBLISH_TOPIC, "Response: \"" + tempString + "\"", SPARK_PUBLISH_TTL, PRIVATE);
1459  #else
1460  Serial.print("Response: \"");Serial.print(response);Serial.println("\"");
1461  #endif
1462  #endif
1463  return status;
1464  };
1465 
1466  int convertFloatToChar(float value, char *valueString)
1467  {
1468  // Supported range is -999999000000 to 999999000000
1469  if(0 == isinf(value) && (value > 999999000000 || value < -999999000000))
1470  {
1471  // Out of range
1472  return ERR_OUT_OF_RANGE;
1473  }
1474  // Given that the resolution of Spark is 1 / 2^12, or ~0.00024 volts, assume that 5 places right of decimal should be sufficient for most applications
1475  #ifdef PARTICLE_PHOTONELECTRON
1476  //Photon doesn't have a dtostrf, but does have dtoa
1477  dtoa((double)value,5, valueString);
1478  #else
1479  dtostrf(value,1,5, valueString);
1480  #endif
1481  return OK_SUCCESS;
1482  };
1483 
1484  float convertStringToFloat(String value)
1485  {
1486  // There's a bug in the AVR function strtod that it doesn't decode -INF correctly (it maps it to INF)
1487  float result = value.toFloat();
1488  if(1 == isinf(result) && *value.c_str() == '-')
1489  {
1490  result = (float)-INFINITY;
1491  }
1492  return result;
1493  };
1494 
1495  void resetWriteFields()
1496  {
1497  for(size_t iField = 0; iField < 8; iField++)
1498  {
1499  this->nextWriteField[iField] = "";
1500  }
1501  this->nextWriteLatitude = NAN;
1502  this->nextWriteLongitude = NAN;
1503  this->nextWriteElevation = NAN;
1504  };
1505 };
1506 
1507 extern ThingSpeakClass ThingSpeak;
1508 
1509 #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:842
String readStringField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest string from a private ThingSpeak channel.
Definition: ThingSpeak.h:937
String readStringField(unsigned long channelNumber, unsigned int field)
Read the latest string from a public ThingSpeak channel.
Definition: ThingSpeak.h:970
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:465
int writeRaw(unsigned long channelNumber, String postMessage, const char *writeAPIKey)
Write a raw POST to a ThingSpeak channel.
Definition: ThingSpeak.h:863
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:198
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:760
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:421
int setLatitude(float latitude)
Set the latitude of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:630
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:165
int getLastReadStatus()
Get the status of the previous read.
Definition: ThingSpeak.h:1229
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:257
Enables an Arduino, ESP8266, Particle or other compatible hardware to write or read data to or from T...
Definition: ThingSpeak.h:135
long readLongField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest long from a private ThingSpeak channel.
Definition: ThingSpeak.h:1034
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:371
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:544
String readRaw(unsigned long channelNumber, String URLSuffix)
Read a raw response from a public ThingSpeak channel.
Definition: ThingSpeak.h:1118
long readLongField(unsigned long channelNumber, unsigned int field)
Read the latest long from a public ThingSpeak channel.
Definition: ThingSpeak.h:1055
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:309
int readIntField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest int from a private ThingSpeak channel.
Definition: ThingSpeak.h:1077
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:580
int setLongitude(float longitude)
Set the longitude of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:673
float readFloatField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest float from a private ThingSpeak channel.
Definition: ThingSpeak.h:992
bool begin(Client &client)
Initializes the ThingSpeak library and network settings using the ThingSpeak.com service.
Definition: ThingSpeak.h:229
float readFloatField(unsigned long channelNumber, unsigned int field)
Read the latest float from a public ThingSpeak channel.
Definition: ThingSpeak.h:1013
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:342
int readIntField(unsigned long channelNumber, unsigned int field)
Read the latest int from a public ThingSpeak channel.
Definition: ThingSpeak.h:1098
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:504
String readRaw(unsigned long channelNumber, String URLSuffix, const char *readAPIKey)
Read a raw response from a private ThingSpeak channel.
Definition: ThingSpeak.h:1139
int setElevation(float elevation)
Set the elevation of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:716
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:285