source: rtos_arduino/trunk/arduino_lib/libraries/thingspeak-arduino/src/ThingSpeak_org.h@ 189

Last change on this file since 189 was 189, checked in by ertl-honda, 8 years ago

Thinkgspeakのサポート

File size: 55.8 KB
Line 
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
15/**
16 @mainpage
17 *
18 * \ref ThingSpeakClass "For technical documentation, visit this page"
19 *
20 * ThingSpeak offers free data storage and analysis of time-stamped numeric or alphanumeric data.
21 * Users can access ThingSpeak by visiting http://thingspeak.com and creating a ThingSpeak user account.
22 *
23 * ThingSpeak stores data in channels. Channels support an unlimited number of timestamped observations (think of these as rows in a spreadsheet).
24 * Each channel has up to 8 fields (think of these as columns in a speadsheet). Check out this <a href="http://www.mathworks.com/videos/introduction-to-thingspeak-107749.html">video</a> for an overview.
25 *
26 * Channels may be public, where anyone can see the data, or private, where only the owner and select users can read the data.
27 * Each channel has an associated Write API Key that is used to control who can write to a channel.
28 * In addition, private channels have one or more Read API Keys to control who can read from private channel.
29 * An API Key is not required to read from public channels. Each channel can have up to 8 fields. One field is created by default.
30 *
31 * You can visualize and do online analytics of your data on ThingSpeak using the built in version of MATLAB, or use the desktop version of MATLAB to get
32 * deeper historical insight. Visit https://www.mathworks.com/hardware-support/thingspeak.html to learn more.
33 *
34 * <h3>Compatible Hardware</h3>
35 * * <a href="http://www.arduino.cc">Arduino</a> or compatible using a wired or Wi-Fi ethernet shield (we have tested with <a href="http://www.arduino.cc/en/Main/ArduinoBoardUno">Uno</a> and <a href="http://www.arduino.cc/en/Main/ArduinoBoardMega2560">Mega</a>), should work with Arduino WiFi Shield 101
36 * * <a href="http://www.arduino.cc/en/Main/ArduinoBoardYun">Arduino Yun</a> running OpenWRT-Yun Release 1.5.3 (November 13th, 2014) or later. There are known issues with earlier versions. Visit [this page](http://www.arduino.cc/en/Main/Software) to get the latest version.
37 * * ESP8266 (tested with <a href="https://www.sparkfun.com/products/13711">SparkFun ESP8266 Thing - Dev Board</a> and <a href="http://www.seeedstudio.com/depot/NodeMCU-v2-Lua-based-ESP8266-development-kit-p-2415.html">NodeMCU 1.0 module</a>)
38 * * Particle (Formally Spark) Core, <a href="https://www.particle.io/prototype#photon">Photon</a>, and <a href="https://www.particle.io/prototype#electron">Electron</a>
39 *
40 * <h3>Examples</h3>
41 * The library includes several examples to help you get started. These are accessible in the Examples/ThingSpeak menu off the File menu in the Arduino IDE.
42 * * <b>CheerLights:</b> Reads the latest <a href="http://www.cheerlights.com">CheerLights</a> color on ThingSpeak, and sets an RGB LED.
43 * * <b>ReadLastTemperature:</b> Reads the latest temperature from the public <a href="https://thingspeak.com/channels/12397">MathWorks weather station</a> in Natick, MA on ThingSpeak.
44 * * <b>ReadPrivateChannel:</b> Reads the latest voltage value from a private channel on ThingSpeak.
45 * * <b>ReadWeatherStation:</b> Reads the latest weather data from the public <a href="https://thingspeak.com/channels/12397">MathWorks weather station</a> in Natick, MA on ThingSpeak.
46 * * <b>WriteMultipleVoltages:</b> Reads analog voltages from pins 0-7 and writes them to the 8 fields of a channel on ThingSpeak.
47 * * <b>WriteVoltage:</b> Reads an analog voltage from pin 0, converts to a voltage, and writes it to a channel on ThingSpeak.
48 */
49
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
132/**
133 * @brief Enables an Arduino, ESP8266, Particle or other compatible hardware to write or read data to or from ThingSpeak, an open data platform for the Internet of Things with MATLAB analytics and visualization.
134 */
135class ThingSpeakClass
136{
137 public:
138 ThingSpeakClass()
139 {
140 resetWriteFields();
141 this->lastReadStatus = OK_SUCCESS;
142 };
143
144 /**
145 * @brief Initializes the ThingSpeak library and network settings using a custom installation of ThingSpeak.
146 * @param client EthernetClient, YunClient, TCPClient, or WiFiClient created earlier in the sketch
147 * @param customHostName Host name of a custom install of ThingSpeak
148 * @param port Port number to use with a custom install of ThingSpeak
149 * @return Always returns true
150 * @comment This does not validate the information passed in, or generate any calls to ThingSpeak.
151 * @code
152 #include <SPI.h>
153 #include <Ethernet.h>
154 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
155 EthernetClient client;
156
157 #include "ThingSpeak.h"
158
159 void setup() {
160 Ethernet.begin(mac);
161 ThingSpeak.begin(client,"api.thingspeak.com", 80);
162 }
163 * @endcode
164 */
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
177 /**
178 * @brief Initializes the ThingSpeak library and network settings using a custom installation of ThingSpeak.
179 * @param client EthernetClient, YunClient, TCPClient, or WiFiClient created earlier in the sketch
180 * @param customIP IP address of a custom install of ThingSpeak
181 * @param port Port number to use with a custom install of ThingSpeak
182 * @return Always returns true
183 * @comment This does not validate the information passed in, or generate any calls to ThingSpeak.
184 * @code
185 #include <SPI.h>
186 #include <Ethernet.h>
187 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
188 EthernetClient client;
189
190 #include "ThingSpeak.h"
191
192 void setup() {
193 Ethernet.begin(mac);
194 ThingSpeak.begin(client,IPAddress(184,106,153,149), 80);
195 }
196 * @endcode
197 */
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
210 /**
211 * @brief Initializes the ThingSpeak library and network settings using the ThingSpeak.com service.
212 * @param client EthernetClient, YunClient, TCPClient, or WiFiClient created earlier in the sketch
213 * @return Always returns true
214 * @comment This does not validate the information passed in, or generate any calls to ThingSpeak.
215 * @code
216 #include <SPI.h>
217 #include <Ethernet.h>
218 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
219 EthernetClient client;
220
221 #include "ThingSpeak.h"
222
223 void setup() {
224 Ethernet.begin(mac);
225 ThingSpeak.begin(client);
226 }
227 * @endcode
228 */
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
241 /**
242 * @brief Write an integer value to a single field in a ThingSpeak channel
243 * @param channelNumber Channel number
244 * @param field Field number (1-8) within the channel to write to.
245 * @param value Integer value (from -32,768 to 32,767) to write.
246 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
247 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
248 * @remark Visit https://thingspeak.com/docs/channels for more information about channels, API keys, and fields. ThingSpeak limits the number of writes to a channel to once every 15 seconds.
249 * @code
250 void loop() {
251 int sensorValue = analogRead(A0);
252 ThingSpeak.writeField(myChannelNumber, 1, sensorValue, myWriteAPIKey);
253 delay(20000);
254 }
255 * @endcode
256 */
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
269 /**
270 * @brief Write a long value to a single field in a ThingSpeak channel
271 * @param channelNumber Channel number
272 * @param field Field number (1-8) within the channel to write to.
273 * @param value Long value (from -2,147,483,648 to 2,147,483,647) to write.
274 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
275 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
276 * @remark Visit https://thingspeak.com/docs/channels for more information about channels, API keys, and fields. ThingSpeak limits the number of writes to a channel to once every 15 seconds.
277 * @code
278 void loop() {
279 int sensorValue = analogRead(A0);
280 ThingSpeak.writeField(myChannelNumber, 1, sensorValue, myWriteAPIKey);
281 delay(20000);
282 }
283 * @endcode
284 */
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
292 /**
293 * @brief Write a floating point value to a single field in a ThingSpeak channel
294 * @param channelNumber Channel number
295 * @param field Field number (1-8) within the channel to write to.
296 * @param value Floating point value (from -999999000000 to 999999000000) to write. If you need more accuracy, or a wider range, you should format the number using <tt>dtostrf</tt> and writeField().
297 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
298 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
299 * @remark Visit https://thingspeak.com/docs/channels for more information about channels, API keys, and fields. ThingSpeak limits the number of writes to a channel to once every 15 seconds.
300 * @code
301 void loop() {
302 int sensorValue = analogRead(A0);
303 float voltage = sensorValue * (5.0 / 1023.0);
304 ThingSpeak.writeField(myChannelNumber, 1, voltage, myWriteAPIKey);
305 delay(20000);
306 }
307 * @endcode
308 */
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
321 /**
322 * @brief Write a string to a single field in a ThingSpeak channel
323 * @param channelNumber Channel number
324 * @param field Field number (1-8) within the channel to write to.
325 * @param value String to write (UTF8 string). ThingSpeak limits this field to 255 bytes.
326 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
327 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
328 * @remark Visit https://thingspeak.com/docs/channels for more information about channels, API keys, and fields. ThingSpeak limits the number of writes to a channel to once every 15 seconds.
329 * @code
330 void loop() {
331 int sensorValue = analogRead(A0);
332 if (sensorValue > 512) {
333 ThingSpeak.writeField(myChannelNumber, 1, "High", myWriteAPIKey);
334 }
335 else {
336 ThingSpeak.writeField(myChannelNumber, 1, "Low", myWriteAPIKey);
337 }
338 delay(20000);
339 }
340 * @endcode
341 */
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
347 /**
348 * @brief Write a String to a single field in a ThingSpeak channel
349 * @param channelNumber Channel number
350 * @param field Field number (1-8) within the channel to write to.
351 * @param value Character array (zero terminated) to write (UTF8). ThingSpeak limits this field to 255 bytes.
352 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
353 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
354 * @remark Visit https://thingspeak.com/docs/channels for more information about channels, API keys, and fields. ThingSpeak limits the number of writes to a channel to once every 15 seconds.
355 * @code
356 void loop() {
357 int sensorValue = analogRead(A0);
358 String meaning;
359 if (sensorValue < 400) {
360 meaning = String("Too Cold!");
361 } else if (sensorValue > 600) {
362 meaning = String("Too Hot!");
363 } else {
364 meaning = String("Just Right");
365 }
366 ThingSpeak.writeField(myChannelNumber, 1, meaning, myWriteAPIKey);
367 delay(20000);
368 }
369 * @endcode
370 */
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
390 /**
391 * @brief Set the value of a single field that will be part of a multi-field update.
392 * To write multiple fields at once, call setField() for each of the fields you want to write, and then call writeFields()
393 * @param field Field number (1-8) within the channel to set
394 * @param value Integer value (from -32,768 to 32,767) to set.
395 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
396 * @see setLatitude(), setLongitude(), setElevation(), writeFields()
397 * @code
398 void loop() {
399 int sensor1Value = analogRead(A0);
400 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
401 String sensor3Meaning;
402 int sensor3Value = analogRead(A2);
403 if (sensor3Value < 400) {
404 sensor3Meaning = String("Too Cold!");
405 } else if (sensor3Value > 600) {
406 sensor3Meaning = String("Too Hot!");
407 } else {
408 sensor3Meaning = String("Just Right");
409 }
410 long timeRead = millis();
411
412 ThingSpeak.setField(1, sensor1Value);
413 ThingSpeak.setField(2, sensor2Voltage);
414 ThingSpeak.setField(3, sensor3Meaning);
415 ThingSpeak.setField(4, timeRead);
416 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
417 delay(20000);
418 }
419 * @endcode
420 */
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
434 /**
435 * @brief Set the value of a single field that will be part of a multi-field update.
436 * To write multiple fields at once, call setField() for each of the fields you want to write, and then call writeFields()
437 * @param field Field number (1-8) within the channel to set
438 * @param value Long value (from -2,147,483,648 to 2,147,483,647) to write.
439 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
440 * @see setLatitude(), setLongitude(), setElevation(), writeFields()
441 * @code
442 void loop() {
443 int sensor1Value = analogRead(A0);
444 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
445 String sensor3Meaning;
446 int sensor3Value = analogRead(A2);
447 if (sensor3Value < 400) {
448 sensor3Meaning = String("Too Cold!");
449 } else if (sensor3Value > 600) {
450 sensor3Meaning = String("Too Hot!");
451 } else {
452 sensor3Meaning = String("Just Right");
453 }
454 long timeRead = millis();
455
456 ThingSpeak.setField(1, sensor1Value);
457 ThingSpeak.setField(2, sensor2Voltage);
458 ThingSpeak.setField(3, sensor3Meaning);
459 ThingSpeak.setField(4, timeRead);
460 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
461 delay(20000);
462 }
463 * @endcode
464 */
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
473 /**
474 * @brief Set the value of a single field that will be part of a multi-field update.
475 * To write multiple fields at once, call setField() for each of the fields you want to write, and then call writeFields()
476 * @param field Field number (1-8) within the channel to set
477 * @param value Floating point value (from -999999000000 to 999999000000) to write. If you need more accuracy, or a wider range, you should format the number yourself (using <tt>dtostrf</tt>) and setField() using the resulting string.
478 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
479 * @see setLatitude(), setLongitude(), setElevation(), writeFields()
480 * @code
481 void loop() {
482 int sensor1Value = analogRead(A0);
483 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
484 String sensor3Meaning;
485 int sensor3Value = analogRead(A2);
486 if (sensor3Value < 400) {
487 sensor3Meaning = String("Too Cold!");
488 } else if (sensor3Value > 600) {
489 sensor3Meaning = String("Too Hot!");
490 } else {
491 sensor3Meaning = String("Just Right");
492 }
493 long timeRead = millis();
494
495 ThingSpeak.setField(1, sensor1Value);
496 ThingSpeak.setField(2, sensor2Voltage);
497 ThingSpeak.setField(3, sensor3Meaning);
498 ThingSpeak.setField(4, timeRead);
499 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
500 delay(20000);
501 }
502 * @endcode
503 */
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
513 /**
514 * @brief Set the value of a single field that will be part of a multi-field update.
515 * To write multiple fields at once, call setField() for each of the fields you want to write, and then call writeFields()
516 * @param field Field number (1-8) within the channel to set
517 * @param value String to write (UTF8). ThingSpeak limits this to 255 bytes.
518 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
519 * @see setLatitude(), setLongitude(), setElevation(), writeFields()
520 * @code
521 void loop() {
522 int sensor1Value = analogRead(A0);
523 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
524 String sensor3Meaning;
525 int sensor3Value = analogRead(A2);
526 if (sensor3Value < 400) {
527 sensor3Meaning = String("Too Cold!");
528 } else if (sensor3Value > 600) {
529 sensor3Meaning = String("Too Hot!");
530 } else {
531 sensor3Meaning = String("Just Right");
532 }
533 long timeRead = millis();
534
535 ThingSpeak.setField(1, sensor1Value);
536 ThingSpeak.setField(2, sensor2Voltage);
537 ThingSpeak.setField(3, sensor3Meaning);
538 ThingSpeak.setField(4, timeRead);
539 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
540 delay(20000);
541 }
542 * @endcode
543 */
544 int setField(unsigned int field, const char * value)
545 {
546 return setField(field, String(value));
547 };
548
549 /**
550 * @brief Set the value of a single field that will be part of a multi-field update.
551 * To write multiple fields at once, call setField() for each of the fields you want to write, and then call writeFields()
552 * @param field Field number (1-8) within the channel to set
553 * @param value String to write (UTF8). ThingSpeak limits this to 255 bytes.
554 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
555 * @see setLatitude(), setLongitude(), setElevation(), writeFields()
556 * @code
557 void loop() {
558 int sensor1Value = analogRead(A0);
559 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
560 String sensor3Meaning;
561 int sensor3Value = analogRead(A2);
562 if (sensor3Value < 400) {
563 sensor3Meaning = String("Too Cold!");
564 } else if (sensor3Value > 600) {
565 sensor3Meaning = String("Too Hot!");
566 } else {
567 sensor3Meaning = String("Just Right");
568 }
569 long timeRead = millis();
570
571 ThingSpeak.setField(1, sensor1Value);
572 ThingSpeak.setField(2, sensor2Voltage);
573 ThingSpeak.setField(3, sensor3Meaning);
574 ThingSpeak.setField(4, timeRead);
575 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
576 delay(20000);
577 }
578 * @endcode
579 */
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
597 /**
598 * @brief Set the latitude of a multi-field update.
599 * To record latitude, longitude and elevation of a write, call setField() for each of the fields you want to write, setLatitude() / setLongitude() / setElevation(), and then call writeFields()
600 * @param latitude Latitude of the measurement (degrees N, use negative values for degrees S)
601 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
602 * @see setField(), setLongitude(), setElevation(), writeFields()
603 * @code
604 void loop() {
605 int sensor1Value = analogRead(A0);
606 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
607 String sensor3Meaning;
608 int sensor3Value = analogRead(A2);
609 if (sensor3Value < 400) {
610 sensor3Meaning = String("Too Cold!");
611 } else if (sensor3Value > 600) {
612 sensor3Meaning = String("Too Hot!");
613 } else {
614 sensor3Meaning = String("Just Right");
615 }
616 long timeRead = millis();
617
618 ThingSpeak.setField(1, sensor1Value);
619 ThingSpeak.setField(2, sensor2Voltage);
620 ThingSpeak.setField(3, sensor3Meaning);
621 ThingSpeak.setField(4, timeRead);
622 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
623 setLatitude(42.2833);
624 setLongitude(-71.3500);
625 setElevation(100);
626 delay(20000);
627 }
628 * @endcode
629 */
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
640 /**
641 * @brief Set the longitude of a multi-field update.
642 * To record latitude, longitude and elevation of a write, call setField() for each of the fields you want to write, setLatitude() / setLongitude() / setElevation(), and then call writeFields()
643 * @param longitude Longitude of the measurement (degrees E, use negative values for degrees W)
644 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
645 * @see setField(), setLatitude(), setElevation(), writeFields()
646 * @code
647 void loop() {
648 int sensor1Value = analogRead(A0);
649 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
650 String sensor3Meaning;
651 int sensor3Value = analogRead(A2);
652 if (sensor3Value < 400) {
653 sensor3Meaning = String("Too Cold!");
654 } else if (sensor3Value > 600) {
655 sensor3Meaning = String("Too Hot!");
656 } else {
657 sensor3Meaning = String("Just Right");
658 }
659 long timeRead = millis();
660
661 ThingSpeak.setField(1, sensor1Value);
662 ThingSpeak.setField(2, sensor2Voltage);
663 ThingSpeak.setField(3, sensor3Meaning);
664 ThingSpeak.setField(4, timeRead);
665 setLatitude(42.2833);
666 setLongitude(-71.3500);
667 setElevation(100);
668 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
669 delay(20000);
670 }
671 * @endcode
672 */
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
683 /**
684 * @brief Set the elevation of a multi-field update.
685 * To record latitude, longitude and elevation of a write, call setField() for each of the fields you want to write, setLatitude() / setLongitude() / setElevation(), and then call writeFields()
686 * @param elevation Elevation of the measurement (meters above sea level)
687 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
688 * @see setField(), setLatitude(), setLongitude(), writeFields()
689 * @code
690 void loop() {
691 int sensor1Value = analogRead(A0);
692 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
693 String sensor3Meaning;
694 int sensor3Value = analogRead(A2);
695 if (sensor3Value < 400) {
696 sensor3Meaning = String("Too Cold!");
697 } else if (sensor3Value > 600) {
698 sensor3Meaning = String("Too Hot!");
699 } else {
700 sensor3Meaning = String("Just Right");
701 }
702 long timeRead = millis();
703
704 ThingSpeak.setField(1, sensor1Value);
705 ThingSpeak.setField(2, sensor2Voltage);
706 ThingSpeak.setField(3, sensor3Meaning);
707 ThingSpeak.setField(4, timeRead);
708 setLatitude(42.2833);
709 setLongitude(-71.3500);
710 setElevation(100);
711 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
712 delay(20000);
713 }
714 * @endcode
715 */
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
726 /**
727 * @brief Write a multi-field update.
728 * Call setField() for each of the fields you want to write, setLatitude() / setLongitude() / setElevation(), and then call writeFields()
729 * @param channelNumber Channel number
730 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
731 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
732 * @see setField(), setLatitude(), setLongitude(), setElevation()
733 * @code
734 void loop() {
735 int sensor1Value = analogRead(A0);
736 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
737 String sensor3Meaning;
738 int sensor3Value = analogRead(A2);
739 if (sensor3Value < 400) {
740 sensor3Meaning = String("Too Cold!");
741 } else if (sensor3Value > 600) {
742 sensor3Meaning = String("Too Hot!");
743 } else {
744 sensor3Meaning = String("Just Right");
745 }
746 long timeRead = millis();
747
748 ThingSpeak.setField(1, sensor1Value);
749 ThingSpeak.setField(2, sensor2Voltage);
750 ThingSpeak.setField(3, sensor3Meaning);
751 ThingSpeak.setField(4, timeRead);
752 setLatitude(42.2833);
753 setLongitude(-71.3500);
754 setElevation(100);
755 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
756 delay(20000);
757 }
758 * @endcode
759 */
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
822 /**
823 * @brief Write a raw POST to a ThingSpeak channel
824 * @param channelNumber Channel number
825 * @param postMessage Raw URL to write to ThingSpeak as a string. See the documentation at https://thingspeak.com/docs/channels#update_feed.
826 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
827 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
828 * @remark This is low level functionality that will not be required by most users.
829 * @code
830 void loop() {
831 const char postMessage[] = "field1=23&created_at=2014-12-31%2023:59:59";
832
833 ThingSpeak.setField(1, sensor1Value);
834 ThingSpeak.setField(2, sensor2Voltage);
835 ThingSpeak.setField(3, sensor3Meaning);
836 ThingSpeak.setField(4, timeRead);
837 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
838 delay(20000);
839 }
840 * @endcode
841 */
842 int writeRaw(unsigned long channelNumber, const char * postMessage, const char * writeAPIKey)
843 {
844 return writeRaw(channelNumber, String(postMessage), writeAPIKey);
845 };
846
847
848 /**
849 * @brief Write a raw POST to a ThingSpeak channel
850 * @param channelNumber Channel number
851 * @param postMessage Raw URL to write to ThingSpeak as a String. See the documentation at https://thingspeak.com/docs/channels#update_feed.
852 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
853 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
854 * @remark This is low level functionality that will not be required by most users.
855 * @code
856 void loop() {
857 String postMessage = String("field1=23&created_at=2014-12-31%2023:59:59");
858 ThingSpeak.writeRaw(myChannelNumber, postMessage, myWriteAPIKey);
859 delay(20000);
860 }
861 * @endcode
862 */
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
922 /**
923 * @brief Read the latest string from a private ThingSpeak channel
924 * @param channelNumber Channel number
925 * @param field Field number (1-8) within the channel to read from.
926 * @param readAPIKey Read API key associated with the channel. *If you share code with others, do _not_ share this key*
927 * @return Value read (UTF8 string), or empty string if there is an error. Use getLastReadStatus() to get more specific information.
928 * @code
929 void loop() {
930 String message = ThingSpeak.readStringField(myChannelNumber, 1, myReadAPIKey);
931 Serial.print("Latest message is: ");
932 Serial.println(message);
933 delay(30000);
934 }
935 * @endcode
936 */
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
956 /**
957 * @brief Read the latest string from a public ThingSpeak channel
958 * @param channelNumber Channel number
959 * @param field Field number (1-8) within the channel to read from.
960 * @return Value read (UTF8), or empty string if there is an error. Use getLastReadStatus() to get more specific information.
961 * @code
962 void loop() {
963 String message = ThingSpeak.readStringField(myChannelNumber, 1);
964 Serial.print("Latest message is: ");
965 Serial.println(message);
966 delay(30000);
967 }
968 * @endcode
969 */
970 String readStringField(unsigned long channelNumber, unsigned int field)
971 {
972 return readStringField(channelNumber, field, NULL);
973 };
974
975
976 /**
977 * @brief Read the latest float from a private ThingSpeak channel
978 * @param channelNumber Channel number
979 * @param field Field number (1-8) within the channel to read from.
980 * @param readAPIKey Read API key associated with the channel. *If you share code with others, do _not_ share this key*
981 * @return Value read, or 0 if the field is text or there is an error. Use getLastReadStatus() to get more specific information. Note that NAN, INFINITY, and -INFINITY are valid results.
982 * @code
983 void loop() {
984 float voltage = ThingSpeak.readFloatField(myChannelNumber, 1, myReadAPIKey);
985 Serial.print("Latest voltage is: ");
986 Serial.print(voltage);
987 Serial.println("V");
988 delay(30000);
989 }
990 * @endcode
991 */
992 float readFloatField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
993 {
994 return convertStringToFloat(readStringField(channelNumber, field, readAPIKey));
995 };
996
997
998 /**
999 * @brief Read the latest float from a public ThingSpeak channel
1000 * @param channelNumber Channel number
1001 * @param field Field number (1-8) within the channel to read from.
1002 * @return Value read, or 0 if the field is text or there is an error. Use getLastReadStatus() to get more specific information. Note that NAN, INFINITY, and -INFINITY are valid results.
1003 * @code
1004 void loop() {
1005 float voltage = ThingSpeak.readFloatField(myChannelNumber, 1);
1006 Serial.print("Latest voltage is: ");
1007 Serial.print(voltage);
1008 Serial.println("V");
1009 delay(30000);
1010 }
1011 * @endcode
1012 */
1013 float readFloatField(unsigned long channelNumber, unsigned int field)
1014 {
1015 return readFloatField(channelNumber, field, NULL);
1016 };
1017
1018
1019 /**
1020 * @brief Read the latest long from a private ThingSpeak channel
1021 * @param channelNumber Channel number
1022 * @param field Field number (1-8) within the channel to read from.
1023 * @param readAPIKey Read API key associated with the channel. *If you share code with others, do _not_ share this key*
1024 * @return Value read, or 0 if the field is text or there is an error. Use getLastReadStatus() to get more specific information.
1025 * @code
1026 void loop() {
1027 long value = ThingSpeak.readLongField(myChannelNumber, 1, myReadAPIKey);
1028 Serial.print("Latest value is: ");
1029 Serial.print(value);
1030 delay(30000);
1031 }
1032 * @endcode
1033 */
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
1041 /**
1042 * @brief Read the latest long from a public ThingSpeak channel
1043 * @param channelNumber Channel number
1044 * @param field Field number (1-8) within the channel to read from.
1045 * @return Value read, or 0 if the field is text or there is an error. Use getLastReadStatus() to get more specific information.
1046 * @code
1047 void loop() {
1048 long value = ThingSpeak.readLongField(myChannelNumber, 1);
1049 Serial.print("Latest value is: ");
1050 Serial.print(value);
1051 delay(30000);
1052 }
1053 * @endcode
1054 */
1055 long readLongField(unsigned long channelNumber, unsigned int field)
1056 {
1057 return readLongField(channelNumber, field, NULL);
1058 };
1059
1060
1061 /**
1062 * @brief Read the latest int from a private ThingSpeak channel
1063 * @param channelNumber Channel number
1064 * @param field Field number (1-8) within the channel to read from.
1065 * @param readAPIKey Read API key associated with the channel. *If you share code with others, do _not_ share this key*
1066 * @return Value read, or 0 if the field is text or there is an error. Use getLastReadStatus() to get more specific information.
1067 * @remark If the value returned is out of range for an int, the result is undefined.
1068 * @code
1069 void loop() {
1070 int value = ThingSpeak.readIntField(myChannelNumber, 1, myReadAPIKey);
1071 Serial.print("Latest value is: ");
1072 Serial.print(value);
1073 delay(30000);
1074 }
1075 * @endcode
1076 */
1077 int readIntField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1078 {
1079 return readLongField(channelNumber, field, readAPIKey);
1080 }
1081
1082
1083 /**
1084 * @brief Read the latest int from a public ThingSpeak channel
1085 * @param channelNumber Channel number
1086 * @param field Field number (1-8) within the channel to read from.
1087 * @return Value read, or 0 if the field is text or there is an error. Use getLastReadStatus() to get more specific information.
1088 * @remark If the value returned is out of range for an int, the result is undefined.
1089 * @code
1090 void loop() {
1091 int value = ThingSpeak.readIntField(myChannelNumber, 1);
1092 Serial.print("Latest value is: ");
1093 Serial.print(value);
1094 delay(30000);
1095 }
1096 * @endcode
1097 */
1098 int readIntField(unsigned long channelNumber, unsigned int field)
1099 {
1100 return readLongField(channelNumber, field, NULL);
1101 };
1102
1103 /**
1104 * @brief Read a raw response from a public ThingSpeak channel
1105 * @param channelNumber Channel number
1106 * @param URLSuffix Raw URL to write to ThingSpeak as a String. See the documentation at https://thingspeak.com/docs/channels#get_feed
1107 * @return Response if successful, or empty string. Use getLastReadStatus() to get more specific information.
1108 * @remark This is low level functionality that will not be required by most users.
1109 * @code
1110 void loop() {
1111 String response = ThingSpeak.readRaw(myChannelNumber, String("feeds/days=1"));
1112 Serial.print("Response: ");
1113 Serial.print(response);
1114 delay(30000);
1115 }
1116 * @endcode
1117 */
1118 String readRaw(unsigned long channelNumber, String URLSuffix)
1119 {
1120 return readRaw(channelNumber, URLSuffix, NULL);
1121 }
1122
1123 /**
1124 * @brief Read a raw response from a private ThingSpeak channel
1125 * @param channelNumber Channel number
1126 * @param URLSuffix Raw URL to write to ThingSpeak as a String. See the documentation at https://thingspeak.com/docs/channels#get_feed
1127 * @param readAPIKey Read API key associated with the channel. *If you share code with others, do _not_ share this key*
1128 * @return Response if successful, or empty string. Use getLastReadStatus() to get more specific information.
1129 * @remark This is low level functionality that will not be required by most users.
1130 * @code
1131 void loop() {
1132 String response = ThingSpeak.readRaw(myChannelNumber, String("feeds/days=1"), myReadAPIKey);
1133 Serial.print("Response: ");
1134 Serial.print(response);
1135 delay(30000);
1136 }
1137 * @endcode
1138 */
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
1196 /**
1197 * @brief Get the status of the previous read.
1198 * @return Generally, these are HTTP status codes. Negative values indicate an error generated by the library.
1199 * Possible response codes:
1200 * * 200: OK / Success
1201 * * 404: Incorrect API key (or invalid ThingSpeak server address)
1202 * * -101: Value is out of range or string is too long (> 255 characters)
1203 * * -201: Invalid field number specified
1204 * * -210: setField() was not called before writeFields()
1205 * * -301: Failed to connect to ThingSpeak
1206 * * -302: Unexpected failure during write to ThingSpeak
1207 * * -303: Unable to parse response
1208 * * -304: Timeout waiting for server to respond
1209 * * -401: Point was not inserted (most probable cause is the rate limit of once every 15 seconds)
1210 * @remark The read functions will return zero or empty if there is an error. Use this function to retrieve the details.
1211 * @code
1212 void loop() {
1213 String message = ThingSpeak.readStringField(myChannelNumber, 1);
1214 int resultCode = ThingSpeak.getLastReadStatus();
1215 if(resultCode == 200)
1216 {
1217 Serial.print("Latest message is: ");
1218 Serial.println(message);
1219 }
1220 else
1221 {
1222 Serial.print("Error reading message. Status was: ");
1223 Serial.println(resultCode);
1224 }
1225 delay(30000);
1226 }
1227 * @endcode
1228 */
1229 int getLastReadStatus()
1230 {
1231 return this->lastReadStatus;
1232 };
1233private:
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
1507extern ThingSpeakClass ThingSpeak;
1508
1509#endif //ThingSpeak_h
Note: See TracBrowser for help on using the repository browser.