ThingSpeak Communication Library for Arduino
Enables an Arduino or other compatible hardware to write or read data to or from ThingSpeak™
ThingSpeak.h
1 /*
2  ThingSpeak(TM) Communication Library For Arduino
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 2015, The MathWorks, Inc.
11 
12  See the accompaning licence file for licensing information.
13 */
14 
49 #ifndef ThingSpeak_h
50 #define ThingSpeak_h
51 
52 //#define PRINT_DEBUG_MESSAGES
53 //#define PRINT_HTTP
54 
55 #ifdef SPARK
56  // Create platform defines for Particle devices
57  #if PLATFORM_ID == 6
58  #define PARTICLE_PHOTON
59  #elif PLATFORM_ID == 0
60  #define PARTICLE_CORE
61  #endif
62 
63  #include "math.h"
64  #include "application.h"
65  #ifdef PARTICLE_PHOTON
66  extern char* dtoa(double val, unsigned char prec, char *sout);
67  // On spark photon, There is no itoa, so map to ltoa.
68  #include "string_convert.h"
69  #define itoa ltoa
70  #else
71  // On spark core, a long and an int are equivalent, and so there's no "ltoa" function defined. Map it to itoa.
72  extern char * itoa(int a, char* buffer, unsigned char radix);
73  #define ltoa itoa
74  extern char *dtostrf (double val, signed char width, unsigned char prec, char *sout);
75  #endif
76 #else
77  #ifdef ARDUINO_ARCH_AVR
78  #include "Arduino.h"
79  #include <Client.h>
80  #else
81  #error Only Arduino Yun, Uno/Mega/Due with either Wired or wi-fi Ethernet shield, and Spark Core/Photon are supported.
82  #endif
83 #endif
84 
85 #define THINGSPEAK_URL "api.thingspeak.com"
86 #define THINGSPEAK_IPADDRESS IPAddress(184,106,153,149)
87 #define THINGSPEAK_PORT_NUMBER 80
88 
89 #ifdef ARDUINO_ARCH_AVR
90  #ifdef ARDUINO_AVR_YUN
91  #define TS_USER_AGENT "tslib-arduino/1.0 (arduino uno or mega)"
92  #else
93  #define TS_USER_AGENT "tslib-arduino/1.0 (arduino yun)"
94  #endif
95 #endif
96 #ifdef SPARK
97  #define TS_USER_AGENT "tslib-arduino/1.0 (particle core or photon)"
98  #define SPARK_PUBLISH_TTL 60 // Spark "time to live" for published messages
99  #define SPARK_PUBLISH_TOPIC "thingspeak-debug"
100 #endif
101 
102 #define FIELDNUM_MIN 1
103 #define FIELDNUM_MAX 8
104 #define FIELDLENGTH_MAX 255 // Max length for a field in ThingSpeak is 255 bytes (UTF-8)
105 
106 #define TIMEOUT_MS_SERVERRESPONSE 5000 // Wait up to five seconds for server to respond
107 
108 #define OK_SUCCESS 200 // OK / Success
109 #define ERR_BADAPIKEY 400 // Incorrect API key (or invalid ThingSpeak server address)
110 #define ERR_BADURL 404 // Incorrect API key (or invalid ThingSpeak server address)
111 #define ERR_OUT_OF_RANGE -101 // Value is out of range or string is too long (> 255 bytes)
112 #define ERR_INVALID_FIELD_NUM -201 // Invalid field number specified
113 #define ERR_SETFIELD_NOT_CALLED -210 // setField() was not called before writeFields()
114 #define ERR_CONNECT_FAILED -301 // Failed to connect to ThingSpeak
115 #define ERR_UNEXPECTED_FAIL -302 // Unexpected failure during write to ThingSpeak
116 #define ERR_BAD_RESPONSE -303 // Unable to parse response
117 #define ERR_TIMEOUT -304 // Timeout waiting for server to respond
118 #define ERR_NOT_INSERTED -401 // Point was not inserted (most probable cause is the rate limit of once every 15 seconds)
119 
124 {
125  public:
127  {
128  resetWriteFields();
129  this->lastReadStatus = OK_SUCCESS;
130  };
131 
153  bool begin(Client & client, const char * customHostName, unsigned int port)
154  {
155 #ifdef PRINT_DEBUG_MESSAGES
156  Serial.print("ts::tsBegin (client: Client URL: "); Serial.print(customHostName); Serial.println(")");
157 #endif
158  this->setClient(&client);
159  this->setServer(customHostName, port);
160  resetWriteFields();
161  this->lastReadStatus = OK_SUCCESS;
162  return true;
163  };
164 
186  bool begin(Client & client, IPAddress customIP, unsigned int port)
187  {
188 #ifdef PRINT_DEBUG_MESSAGES
189  Serial.print("ts::tsBegin (client: Client IP: "); Serial.print(customIP); Serial.println(")");
190 #endif
191  this->setClient(&client);
192  this->setServer(customIP, port);
193  resetWriteFields();
194  this->lastReadStatus = OK_SUCCESS;
195  return true;
196  };
197 
217  bool begin(Client & client)
218  {
219 #ifdef PRINT_DEBUG_MESSAGES
220  Serial.print("ts::tsBegin");
221 #endif
222  this->setClient(&client);
223  this->setServer();
224  resetWriteFields();
225  this->lastReadStatus = OK_SUCCESS;
226  return true;
227  };
228 
245  int writeField(unsigned long channelNumber, unsigned int field, int value, const char * writeAPIKey)
246  {
247 #ifdef SPARK
248  // On Spark, int and long are the same, so map to the long version
249  return writeField(channelNumber, field, (long)value, writeAPIKey);
250 #else
251  char valueString[10]; // int range is -32768 to 32768, so 7 bytes including terminator, plus a little extra
252  itoa(value, valueString, 10);
253  return writeField(channelNumber, field, valueString, writeAPIKey);
254 #endif
255  };
256 
273  int writeField(unsigned long channelNumber, unsigned int field, long value, const char * writeAPIKey)
274  {
275  char valueString[15]; // long range is -2147483648 to 2147483647, so 12 bytes including terminator
276  ltoa(value, valueString, 10);
277  return writeField(channelNumber, field, valueString, writeAPIKey);
278  };
279 
297  int writeField(unsigned long channelNumber, unsigned int field, float value, const char * writeAPIKey)
298  {
299  #ifdef PRINT_DEBUG_MESSAGES
300  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(")");
301  #endif
302  char valueString[20]; // range is -999999000000.00000 to 999999000000.00000, so 19 + 1 for the terminator
303  int status = convertFloatToChar(value, valueString);
304  if(status != OK_SUCCESS) return status;
305 
306  return writeField(channelNumber, field, valueString, writeAPIKey);
307  };
308 
330  int writeField(unsigned long channelNumber, unsigned int field, const char * value, const char * writeAPIKey)
331  {
332  return writeField(channelNumber, field, String(value), writeAPIKey);
333  };
334 
359  int writeField(unsigned long channelNumber, unsigned int field, String value, const char * writeAPIKey)
360  {
361  // Invalid field number specified
362  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX) return ERR_INVALID_FIELD_NUM;
363  // Max # bytes for ThingSpeak field is 255
364  if(value.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
365 
366  #ifdef PRINT_DEBUG_MESSAGES
367  #ifdef SPARK
368  Spark.publish(SPARK_PUBLISH_TOPIC, "writeField (" + String(channelNumber) + ", " + String(writeAPIKey) + ", " + String(field) + ", " + String(value) + ")", SPARK_PUBLISH_TTL, PRIVATE);
369  #else
370  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("\")");
371  #endif
372  #endif
373  String postMessage = String("field") + String(field) + "=" + value;
374  return writeRaw(channelNumber, postMessage, writeAPIKey);
375  };
376 
377 
409  int setField(unsigned int field, int value)
410  {
411 #ifdef SPARK
412  // On Spark, int and long are the same, so map to the long version
413  return setField(field, (long)value);
414 #else
415  char valueString[10]; // int range is -32768 to 32768, so 7 bytes including terminator
416  itoa(value, valueString, 10);
417 
418  return setField(field, valueString);
419 #endif
420  };
421 
453  int setField(unsigned int field, long value)
454  {
455  char valueString[15]; // long range is -2147483648 to 2147483647, so 12 bytes including terminator
456  ltoa(value, valueString, 10);
457  return setField(field, valueString);
458  };
459 
460 
492  int setField(unsigned int field, float value)
493  {
494  char valueString[20]; // range is -999999000000.00000 to 999999000000.00000, so 19 + 1 for the terminator
495  int status = convertFloatToChar(value, valueString);
496  if(status != OK_SUCCESS) return status;
497 
498  return setField(field, valueString);
499  };
500 
532  int setField(unsigned int field, const char * value)
533  {
534  return setField(field, String(value));
535  };
536 
568  int setField(unsigned int field, String value)
569  {
570  #ifdef PRINT_DEBUG_MESSAGES
571  #ifdef SPARK
572  Spark.publish(SPARK_PUBLISH_TOPIC, "setField " + String(field) + " to " + String(value), SPARK_PUBLISH_TTL, PRIVATE);
573  #else
574  Serial.print("ts::setField (field: "); Serial.print(field); Serial.print(" value: \""); Serial.print(value); Serial.println("\")");
575  #endif
576  #endif
577  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX) return ERR_INVALID_FIELD_NUM;
578  // Max # bytes for ThingSpeak field is 255 (UTF-8)
579  if(value.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
580  this->nextWriteField[field - 1] = value;
581  return OK_SUCCESS;
582  };
583 
584 
618  int setLatitude(float latitude)
619  {
620  #ifdef PRINT_DEBUG_MESSAGES
621  Serial.print("ts::setLatitude(latitude: "); Serial.print(latitude,3); Serial.println("\")");
622  #endif
623  this->nextWriteLatitude = latitude;
624  return OK_SUCCESS;
625  };
626 
627 
661  int setLongitude(float longitude)
662  {
663  #ifdef PRINT_DEBUG_MESSAGES
664  Serial.print("ts::setLongitude(longitude: "); Serial.print(longitude,3); Serial.println("\")");
665  #endif
666  this->nextWriteLongitude = longitude;
667  return OK_SUCCESS;
668  };
669 
670 
704  int setElevation(float elevation)
705  {
706  #ifdef PRINT_DEBUG_MESSAGES
707  Serial.print("ts::setElevation(elevation: "); Serial.print(elevation,3); Serial.println("\")");
708  #endif
709  this->nextWriteElevation = elevation;
710  return OK_SUCCESS;
711  };
712 
713 
748  int writeFields(unsigned long channelNumber, const char * writeAPIKey)
749  {
750  String postMessage = String("");
751  bool fFirstItem = true;
752  for(size_t iField = 0; iField < 8; iField++)
753  {
754  if(this->nextWriteField[iField].length() > 0)
755  {
756  if(!fFirstItem)
757  {
758  postMessage = postMessage + String("&");
759  }
760  postMessage = postMessage + String("field") + String(iField + 1) + String("=") + this->nextWriteField[iField];
761  fFirstItem = false;
762  this->nextWriteField[iField] = "";
763  }
764  }
765 
766  if(!isnan(nextWriteLatitude))
767  {
768  if(!fFirstItem)
769  {
770  postMessage = postMessage + String("&");
771  }
772  postMessage = postMessage + String("lat=") + String(this->nextWriteLatitude);
773  fFirstItem = false;
774  this->nextWriteLatitude = NAN;
775  }
776 
777  if(!isnan(this->nextWriteLongitude))
778  {
779  if(!fFirstItem)
780  {
781  postMessage = postMessage + String("&");
782  }
783  postMessage = postMessage + String("long=") + String(this->nextWriteLongitude);
784  fFirstItem = false;
785  this->nextWriteLongitude = NAN;
786  }
787 
788 
789  if(!isnan(this->nextWriteElevation))
790  {
791  if(!fFirstItem)
792  {
793  postMessage = postMessage + String("&");
794  }
795  postMessage = postMessage + String("elevation=") + String(this->nextWriteElevation);
796  fFirstItem = false;
797  this->nextWriteElevation = NAN;
798  }
799 
800  if(fFirstItem)
801  {
802  // setField was not called before writeFields
803  return ERR_SETFIELD_NOT_CALLED;
804  }
805 
806  return writeRaw(channelNumber, postMessage, writeAPIKey);
807  };
808 
809 
830  int writeRaw(unsigned long channelNumber, const char * postMessage, const char * writeAPIKey)
831  {
832  return writeRaw(channelNumber, String(postMessage), writeAPIKey);
833  };
834 
835 
851  int writeRaw(unsigned long channelNumber, String postMessage, const char * writeAPIKey)
852  {
853  #ifdef PRINT_DEBUG_MESSAGES
854  Serial.print("ts::writeRaw (channelNumber: "); Serial.print(channelNumber); Serial.print(" writeAPIKey: "); Serial.print(writeAPIKey); Serial.print(" postMessage: \""); Serial.print(postMessage); Serial.println("\")");
855  #endif
856 
857  if(!connectThingSpeak())
858  {
859  // Failed to connect to ThingSpeak
860  return ERR_CONNECT_FAILED;
861  }
862 
863  postMessage = postMessage + String("&headers=false");
864 
865  #ifdef PRINT_DEBUG_MESSAGES
866  #ifdef SPARK
867  Spark.publish(SPARK_PUBLISH_TOPIC, "Post " + postMessage, SPARK_PUBLISH_TTL, PRIVATE);
868  #else
869  Serial.print(" POST \"");Serial.print(postMessage);Serial.println("\"");
870  #endif
871  #endif
872 
873  postMessage = postMessage + String("\n");
874 
875  // Post data to thingspeak
876  if(!this->client->print("POST /update HTTP/1.1\n")) return abortWriteRaw();
877  if(!writeHTTPHeader(writeAPIKey)) return abortWriteRaw();
878  if(!this->client->print("Content-Type: application/x-www-form-urlencoded\n")) return abortWriteRaw();
879  if(!this->client->print("Content-Length: ")) return abortWriteRaw();
880  if(!this->client->print(postMessage.length())) return abortWriteRaw();
881  if(!this->client->print("\n\n")) return abortWriteRaw();
882  if(!this->client->print(postMessage)) return abortWriteRaw();
883 
884  String entryIDText = String();
885  int status = getHTTPResponse(entryIDText);
886  if(status != OK_SUCCESS)
887  {
888  client->stop();
889  return status;
890  }
891  long entryID = entryIDText.toInt();
892 
893  #ifdef PRINT_DEBUG_MESSAGES
894  Serial.print(" Entry ID \"");Serial.print(entryIDText);Serial.print("\" (");Serial.print(entryID);Serial.println(")");
895  #endif
896 
897  client->stop();
898 
899  #ifdef PRINT_DEBUG_MESSAGES
900  Serial.println("disconnected.");
901  #endif
902  if(entryID == 0)
903  {
904  // ThingSpeak did not accept the write
905  status = ERR_NOT_INSERTED;
906  }
907  return status;
908  };
909 
925  String readStringField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
926  {
927  if(field < FIELDNUM_MIN || field > FIELDNUM_MAX)
928  {
929  this->lastReadStatus = ERR_INVALID_FIELD_NUM;
930  return("");
931  }
932  #ifdef PRINT_DEBUG_MESSAGES
933  Serial.print("ts::readStringField(channelNumber: "); Serial.print(channelNumber);
934  if(NULL != readAPIKey)
935  {
936  Serial.print(" readAPIKey: "); Serial.print(readAPIKey);
937  }
938  Serial.print(" field: "); Serial.print(field); Serial.println(")");
939  #endif
940  return readRaw(channelNumber, String(String("/fields/") + String(field) + String("/last")), readAPIKey);
941  }
942 
943 
958  String readStringField(unsigned long channelNumber, unsigned int field)
959  {
960  return readStringField(channelNumber, field, NULL);
961  };
962 
963 
980  float readFloatField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
981  {
982  return convertStringToFloat(readStringField(channelNumber, field, readAPIKey));
983  };
984 
985 
1001  float readFloatField(unsigned long channelNumber, unsigned int field)
1002  {
1003  return readFloatField(channelNumber, field, NULL);
1004  };
1005 
1006 
1022  long readLongField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1023  {
1024  // Note that although the function is called "toInt" it really returns a long.
1025  return readStringField(channelNumber, field, readAPIKey).toInt();
1026  }
1027 
1028 
1043  long readLongField(unsigned long channelNumber, unsigned int field)
1044  {
1045  return readLongField(channelNumber, field, NULL);
1046  };
1047 
1048 
1065  int readIntField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1066  {
1067  return readLongField(channelNumber, field, readAPIKey);
1068  }
1069 
1070 
1086  int readIntField(unsigned long channelNumber, unsigned int field)
1087  {
1088  return readLongField(channelNumber, field, NULL);
1089  };
1090 
1106  String readRaw(unsigned long channelNumber, String URLSuffix)
1107  {
1108  return readRaw(channelNumber, URLSuffix, NULL);
1109  }
1110 
1127  String readRaw(unsigned long channelNumber, String URLSuffix, const char * readAPIKey)
1128  {
1129  #ifdef PRINT_DEBUG_MESSAGES
1130  Serial.print("ts::readRaw (channelNumber: "); Serial.print(channelNumber);
1131  if(NULL != readAPIKey)
1132  {
1133  Serial.print(" readAPIKey: "); Serial.print(readAPIKey);
1134  }
1135  Serial.print(" URLSuffix: \""); Serial.print(URLSuffix); Serial.println("\")");
1136  #endif
1137 
1138  if(!connectThingSpeak())
1139  {
1140  this->lastReadStatus = ERR_CONNECT_FAILED;
1141  return String("");
1142  }
1143 
1144  String URL = String("/channels/") + String(channelNumber) + URLSuffix;
1145 
1146  #ifdef PRINT_DEBUG_MESSAGES
1147  Serial.print(" GET \"");Serial.print(URL);Serial.println("\"");
1148  #endif
1149 
1150  // Post data to thingspeak
1151  if(!this->client->print("GET ")) return abortReadRaw();
1152  if(!this->client->print(URL)) return abortReadRaw();
1153  if(!this->client->print(" HTTP/1.1\n")) return abortReadRaw();
1154  if(!writeHTTPHeader(readAPIKey)) return abortReadRaw();
1155  if(!this->client->print("\n")) return abortReadRaw();
1156 
1157  String content = String();
1158  int status = getHTTPResponse(content);
1159  this->lastReadStatus = status;
1160 
1161 
1162  #ifdef PRINT_DEBUG_MESSAGES
1163  if(status == OK_SUCCESS)
1164  {
1165  Serial.print("Read: \""); Serial.print(content); Serial.println("\"");
1166  }
1167  #endif
1168 
1169  client->stop();
1170  #ifdef PRINT_DEBUG_MESSAGES
1171  Serial.println("disconnected.");
1172  #endif
1173 
1174  if(status != OK_SUCCESS)
1175  {
1176  // return status;
1177  return String("");
1178  }
1179 
1180  // This is a workaround to a bug in the Spark implementation of String
1181  return String("") + content;
1182  };
1183 
1218  {
1219  return this->lastReadStatus;
1220  };
1221 private:
1222 
1223  int abortWriteRaw()
1224  {
1225  this->client->stop();
1226  return ERR_UNEXPECTED_FAIL;
1227  }
1228 
1229  String abortReadRaw()
1230  {
1231  this->client->stop();
1232  #ifdef PRINT_DEBUG_MESSAGES
1233  Serial.println("ReadRaw abort - disconnected.");
1234  #endif
1235  this->lastReadStatus = ERR_UNEXPECTED_FAIL;
1236  return String("");
1237  }
1238 
1239  void setServer(const char * customHostName, unsigned int port)
1240  {
1241  #ifdef PRINT_DEBUG_MESSAGES
1242  Serial.print("ts::setServer (URL: \""); Serial.print(customHostName); Serial.println("\")");
1243  #endif
1244  this->customIP = INADDR_NONE;
1245  this->customHostName = customHostName;
1246  this->port = port;
1247  };
1248 
1249  void setServer(IPAddress customIP, unsigned int port)
1250  {
1251  #ifdef PRINT_DEBUG_MESSAGES
1252  Serial.print("ts::setServer (IP: \""); Serial.print(customIP); Serial.println("\")");
1253  #endif
1254  this->customIP = customIP;
1255  this->customHostName = NULL;
1256  this->port = port;
1257  };
1258 
1259  void setServer()
1260  {
1261  #ifdef PRINT_DEBUG_MESSAGES
1262  Serial.print("ts::setServer (default)");
1263  #endif
1264  this->customIP = INADDR_NONE;
1265  this->customHostName = NULL;
1266  this->port = THINGSPEAK_PORT_NUMBER;
1267  };
1268 
1269  void setClient(Client * client) {this->client = client;};
1270 
1271  Client * client = NULL;
1272  const char * customHostName = NULL;
1273  IPAddress customIP = INADDR_NONE;
1274  unsigned int port = THINGSPEAK_PORT_NUMBER;
1275  String nextWriteField[8];
1276  float nextWriteLatitude;
1277  float nextWriteLongitude;
1278  float nextWriteElevation;
1279  int lastReadStatus;
1280 
1281  bool connectThingSpeak()
1282  {
1283  bool connectSuccess = false;
1284  if(this->customIP == INADDR_NONE && NULL == this->customHostName)
1285  {
1286  #ifdef PRINT_DEBUG_MESSAGES
1287  Serial.print(" Connect to default ThingSpeak URL...");
1288  #endif
1289  connectSuccess = client->connect(THINGSPEAK_URL,THINGSPEAK_PORT_NUMBER);
1290  if(!connectSuccess)
1291  {
1292  #ifdef PRINT_DEBUG_MESSAGES
1293  Serial.print("Failed. Try default IP...");
1294  #endif
1295  connectSuccess = client->connect(THINGSPEAK_IPADDRESS,THINGSPEAK_PORT_NUMBER);
1296  }
1297  }
1298  else
1299  {
1300  if(!(this->customIP == INADDR_NONE))
1301  {
1302  // Connect to the server on port 80 (HTTP) at the customIP address
1303  #ifdef PRINT_DEBUG_MESSAGES
1304  Serial.print(" Connect to ");Serial.print(this->customIP);Serial.print("...");
1305  #endif
1306  connectSuccess = client->connect(this->customIP,this->port);
1307  }
1308  if(NULL != this->customHostName)
1309  {
1310  // Connect to the server on port 80 (HTTP) at the URL address
1311  #ifdef PRINT_DEBUG_MESSAGES
1312  #ifdef SPARK
1313  Spark.publish(SPARK_PUBLISH_TOPIC, "Attempt Connect to URL " + String(this->customHostName), SPARK_PUBLISH_TTL, PRIVATE);
1314  #else
1315  Serial.print(" Connect to ");Serial.print(this->customHostName);Serial.print(" ...");
1316  #endif
1317  #endif
1318  connectSuccess = client->connect(customHostName,this->port);
1319  }
1320  }
1321 
1322  #ifdef PRINT_DEBUG_MESSAGES
1323  if (connectSuccess)
1324  {
1325  #ifdef SPARK
1326  Spark.publish(SPARK_PUBLISH_TOPIC, "Connection Success", SPARK_PUBLISH_TTL, PRIVATE);
1327  #else
1328  Serial.println("Success.");
1329  #endif
1330  }
1331  else
1332  {
1333  #ifdef SPARK
1334  Spark.publish(SPARK_PUBLISH_TOPIC, "Connection Failure", SPARK_PUBLISH_TTL, PRIVATE);
1335  #else
1336  Serial.println("Failed.");
1337  #endif
1338  }
1339  #endif
1340  return connectSuccess;
1341  };
1342 
1343  bool writeHTTPHeader(const char * APIKey)
1344  {
1345  if(NULL != this->customHostName)
1346  {
1347  if (!this->client->print("Host: ")) return false;
1348  if (!this->client->print(this->customHostName)) return false;
1349  if (!this->client->print("\n")) return false;
1350  }
1351  else
1352  {
1353  if (!this->client->print("Host: api.thingspeak.com\n")) return false;
1354  }
1355  if (!this->client->print("Connection: close\n")) return false;
1356  if (!this->client->print("User-Agent: ")) return false;
1357  if (!this->client->print(TS_USER_AGENT)) return false;
1358  if (!this->client->print("\n")) return false;
1359  if(NULL != APIKey)
1360  {
1361  if (!this->client->print("X-THINGSPEAKAPIKEY: ")) return false;
1362  if (!this->client->print(APIKey)) return false;
1363  if (!this->client->print("\n")) return false;
1364  }
1365  return true;
1366  };
1367 
1368  int getHTTPResponse(String & response)
1369  {
1370  long startWaitForResponseAt = millis();
1371  while(client->available() == 0 && millis() - startWaitForResponseAt < TIMEOUT_MS_SERVERRESPONSE)
1372  {
1373  delay(100);
1374  }
1375  if(client->available() == 0)
1376  {
1377  return ERR_TIMEOUT; // Didn't get server response in time
1378  }
1379 
1380  if(!client->find(const_cast<char *>("HTTP/1.1")))
1381  {
1382  #ifdef PRINT_HTTP
1383  Serial.println("ERROR: Didn't find HTTP/1.1");
1384  #endif
1385  return ERR_BAD_RESPONSE; // Couldn't parse response (didn't find HTTP/1.1)
1386  }
1387  int status = client->parseInt();
1388  #ifdef PRINT_HTTP
1389  Serial.print("Got Status of ");Serial.println(status);
1390  #endif
1391  if(status != OK_SUCCESS)
1392  {
1393  return status;
1394  }
1395 
1396  if(!client->find(const_cast<char *>("\n\r\n")))
1397  {
1398  #ifdef PRINT_HTTP
1399  Serial.println("ERROR: Didn't find end of header");
1400  #endif
1401  return ERR_BAD_RESPONSE;
1402  }
1403  #ifdef PRINT_HTTP
1404  Serial.println("Found end of header");
1405  #endif
1406  #ifndef PRINT_HTTP
1407  client->parseInt();
1408  #else
1409  long length = client->parseInt();
1410  Serial.print("Got length of ");Serial.println(length);
1411  #endif
1412  if(!client->find(const_cast<char *>("\r\n")))
1413  {
1414  #ifdef PRINT_HTTP
1415  Serial.println("ERROR: Didn't find end of length");
1416  #endif
1417  return ERR_BAD_RESPONSE;
1418  }
1419  #ifdef PRINT_HTTP
1420  Serial.println("Found end of length");
1421  #endif
1422  // This is a workaround to a bug in the Spark implementation of String
1423  String tempString = client->readStringUntil('\r');
1424  response = tempString;
1425  #ifdef PRINT_HTTP
1426  Serial.print("Response: \"");Serial.print(response);Serial.println("\"");
1427  #endif
1428  return status;
1429  };
1430 
1431  int convertFloatToChar(float value, char *valueString)
1432  {
1433  // Supported range is -999999000000 to 999999000000
1434  if(0 == isinf(value) && (value > 999999000000 || value < -999999000000))
1435  {
1436  // Out of range
1437  return ERR_OUT_OF_RANGE;
1438  }
1439  // 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
1440  #ifdef PARTICLE_PHOTON
1441  //Photon doesn't have a dtostrf, but does have dtoa
1442  dtoa((double)value,5, valueString);
1443  #else
1444  dtostrf(value,1,5, valueString);
1445  #endif
1446  return OK_SUCCESS;
1447  };
1448 
1449  float convertStringToFloat(String value)
1450  {
1451  // There's a bug in the AVR function strtod that it doesn't decode -INF correctly (it maps it to INF)
1452  float result = value.toFloat();
1453  if(1 == isinf(result) && *value.c_str() == '-')
1454  {
1455  result = (float)-INFINITY;
1456  }
1457  return result;
1458  };
1459 
1460  void resetWriteFields()
1461  {
1462  for(size_t iField = 0; iField < 8; iField++)
1463  {
1464  this->nextWriteField[iField] = "";
1465  }
1466  this->nextWriteLatitude = NAN;
1467  this->nextWriteLongitude = NAN;
1468  this->nextWriteElevation = NAN;
1469  };
1470 };
1471 
1472 extern ThingSpeakClass ThingSpeak;
1473 
1474 #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:830
String readStringField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest string from a private ThingSpeak channel.
Definition: ThingSpeak.h:925
String readStringField(unsigned long channelNumber, unsigned int field)
Read the latest string from a public ThingSpeak channel.
Definition: ThingSpeak.h:958
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:453
int writeRaw(unsigned long channelNumber, String postMessage, const char *writeAPIKey)
Write a raw POST to a ThingSpeak channel.
Definition: ThingSpeak.h:851
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:186
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:748
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:409
int setLatitude(float latitude)
Set the latitude of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:618
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:153
int getLastReadStatus()
Get the status of the previous read.
Definition: ThingSpeak.h:1217
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:245
This library enables an Arduino or other compatible hardware to write or read data to or from ThingSp...
Definition: ThingSpeak.h:123
long readLongField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest long from a private ThingSpeak channel.
Definition: ThingSpeak.h:1022
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:359
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:532
String readRaw(unsigned long channelNumber, String URLSuffix)
Read a raw response from a public ThingSpeak channel.
Definition: ThingSpeak.h:1106
long readLongField(unsigned long channelNumber, unsigned int field)
Read the latest long from a public ThingSpeak channel.
Definition: ThingSpeak.h:1043
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:297
int readIntField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest int from a private ThingSpeak channel.
Definition: ThingSpeak.h:1065
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:568
int setLongitude(float longitude)
Set the longitude of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:661
float readFloatField(unsigned long channelNumber, unsigned int field, const char *readAPIKey)
Read the latest float from a private ThingSpeak channel.
Definition: ThingSpeak.h:980
bool begin(Client &client)
Initializes the ThingSpeak library and network settings using the ThingSpeak.com service.
Definition: ThingSpeak.h:217
float readFloatField(unsigned long channelNumber, unsigned int field)
Read the latest float from a public ThingSpeak channel.
Definition: ThingSpeak.h:1001
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:330
int readIntField(unsigned long channelNumber, unsigned int field)
Read the latest int from a public ThingSpeak channel.
Definition: ThingSpeak.h:1086
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:492
String readRaw(unsigned long channelNumber, String URLSuffix, const char *readAPIKey)
Read a raw response from a private ThingSpeak channel.
Definition: ThingSpeak.h:1127
int setElevation(float elevation)
Set the elevation of a multi-field update. To record latitude, longitude and elevation of a write...
Definition: ThingSpeak.h:704
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:273