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

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

Thinkgspeakのサポート

File size: 55.9 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 ARDUINO_ARCH_SAMD
57#define ARDUINO_ARCH_AVR
58#include "avr/dtostrf.h"
59#endif /* ARDUINO_ARCH_SAMD */
60
61#ifdef SPARK
62 // Create platform defines for Particle devices
63 #if PLATFORM_ID == 6
64 #define PARTICLE_PHOTON
65 #define PARTICLE_PHOTONELECTRON
66 #elif PLATFORM_ID == 10
67 #define PARTICLE_ELECTRON
68 #define PARTICLE_PHOTONELECTRON
69 #elif PLATFORM_ID == 0
70 #define PARTICLE_CORE
71 #endif
72
73 #include "math.h"
74 #include "application.h"
75 #ifdef PARTICLE_PHOTONELECTRON
76 extern char* dtoa(double val, unsigned char prec, char *sout);
77 // On spark photon, There is no itoa, so map to ltoa.
78 #include "string_convert.h"
79 #define itoa ltoa
80 #else
81 // On spark core, a long and an int are equivalent, and so there's no "ltoa" function defined. Map it to itoa.
82 extern char * itoa(int a, char* buffer, unsigned char radix);
83 #define ltoa itoa
84 extern char *dtostrf (double val, signed char width, unsigned char prec, char *sout);
85 #endif
86#else
87 #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ESP8266)
88 #include "Arduino.h"
89 #include <Client.h>
90 #else
91 #error Only Arduino Yun, Uno/Mega/Due with either Wired or wi-fi Ethernet shield, ESP8266, and Spark Core/Photon/Electron are supported.
92 #endif
93#endif
94
95#define THINGSPEAK_URL "api.thingspeak.com"
96#define THINGSPEAK_IPADDRESS IPAddress(184,106,153,149)
97#define THINGSPEAK_PORT_NUMBER 80
98
99#ifdef ARDUINO_ARCH_AVR
100 #ifdef ARDUINO_AVR_YUN
101 #define TS_USER_AGENT "tslib-arduino/1.0 (arduino yun)"
102 #else
103 #define TS_USER_AGENT "tslib-arduino/1.0 (arduino uno or mega)"
104 #endif
105#elif defined(ARDUINO_ARCH_ESP8266)
106 #define TS_USER_AGENT "tslib-arduino/1.0 (ESP8266)"
107#elif defined(SPARK)
108 #ifdef PARTICLE_CORE
109 #define TS_USER_AGENT "tslib-arduino/1.0 (particle core)"
110 #elif defined(PARTICLE_PHOTON)
111 #define TS_USER_AGENT "tslib-arduino/1.0 (particle photon)"
112 #elif defined(PARTICLE_ELECTRON)
113 #define TS_USER_AGENT "tslib-arduino/1.0 (particle electron)"
114 #endif
115 #define SPARK_PUBLISH_TTL 60 // Spark "time to live" for published messages
116 #define SPARK_PUBLISH_TOPIC "thingspeak-debug"
117#endif
118
119#define FIELDNUM_MIN 1
120#define FIELDNUM_MAX 8
121#define FIELDLENGTH_MAX 255 // Max length for a field in ThingSpeak is 255 bytes (UTF-8)
122
123#define TIMEOUT_MS_SERVERRESPONSE 5000 // Wait up to five seconds for server to respond
124
125#define OK_SUCCESS 200 // OK / Success
126#define ERR_BADAPIKEY 400 // Incorrect API key (or invalid ThingSpeak server address)
127#define ERR_BADURL 404 // Incorrect API key (or invalid ThingSpeak server address)
128#define ERR_OUT_OF_RANGE -101 // Value is out of range or string is too long (> 255 bytes)
129#define ERR_INVALID_FIELD_NUM -201 // Invalid field number specified
130#define ERR_SETFIELD_NOT_CALLED -210 // setField() was not called before writeFields()
131#define ERR_CONNECT_FAILED -301 // Failed to connect to ThingSpeak
132#define ERR_UNEXPECTED_FAIL -302 // Unexpected failure during write to ThingSpeak
133#define ERR_BAD_RESPONSE -303 // Unable to parse response
134#define ERR_TIMEOUT -304 // Timeout waiting for server to respond
135#define ERR_NOT_INSERTED -401 // Point was not inserted (most probable cause is the rate limit of once every 15 seconds)
136
137/**
138 * @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.
139 */
140class ThingSpeakClass
141{
142 public:
143 ThingSpeakClass()
144 {
145 resetWriteFields();
146 this->lastReadStatus = OK_SUCCESS;
147 this->customIP = INADDR_NONE;
148 this->port = THINGSPEAK_PORT_NUMBER;
149 };
150
151 /**
152 * @brief Initializes the ThingSpeak library and network settings using a custom installation of ThingSpeak.
153 * @param client EthernetClient, YunClient, TCPClient, or WiFiClient created earlier in the sketch
154 * @param customHostName Host name of a custom install of ThingSpeak
155 * @param port Port number to use with a custom install of ThingSpeak
156 * @return Always returns true
157 * @comment This does not validate the information passed in, or generate any calls to ThingSpeak.
158 * @code
159 #include <SPI.h>
160 #include <Ethernet.h>
161 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
162 EthernetClient client;
163
164 #include "ThingSpeak.h"
165
166 void setup() {
167 Ethernet.begin(mac);
168 ThingSpeak.begin(client,"api.thingspeak.com", 80);
169 }
170 * @endcode
171 */
172 bool begin(Client & client, const char * customHostName, unsigned int port)
173 {
174#ifdef PRINT_DEBUG_MESSAGES
175 Serial.print("ts::tsBegin (client: Client URL: "); Serial.print(customHostName); Serial.println(")");
176#endif
177 this->setClient(&client);
178 this->setServer(customHostName, port);
179 resetWriteFields();
180 this->lastReadStatus = OK_SUCCESS;
181 return true;
182 };
183
184 /**
185 * @brief Initializes the ThingSpeak library and network settings using a custom installation of ThingSpeak.
186 * @param client EthernetClient, YunClient, TCPClient, or WiFiClient created earlier in the sketch
187 * @param customIP IP address of a custom install of ThingSpeak
188 * @param port Port number to use with a custom install of ThingSpeak
189 * @return Always returns true
190 * @comment This does not validate the information passed in, or generate any calls to ThingSpeak.
191 * @code
192 #include <SPI.h>
193 #include <Ethernet.h>
194 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
195 EthernetClient client;
196
197 #include "ThingSpeak.h"
198
199 void setup() {
200 Ethernet.begin(mac);
201 ThingSpeak.begin(client,IPAddress(184,106,153,149), 80);
202 }
203 * @endcode
204 */
205 bool begin(Client & client, IPAddress customIP, unsigned int port)
206 {
207#ifdef PRINT_DEBUG_MESSAGES
208 Serial.print("ts::tsBegin (client: Client IP: "); Serial.print(customIP); Serial.println(")");
209#endif
210 this->setClient(&client);
211 this->setServer(customIP, port);
212 resetWriteFields();
213 this->lastReadStatus = OK_SUCCESS;
214 return true;
215 };
216
217 /**
218 * @brief Initializes the ThingSpeak library and network settings using the ThingSpeak.com service.
219 * @param client EthernetClient, YunClient, TCPClient, or WiFiClient created earlier in the sketch
220 * @return Always returns true
221 * @comment This does not validate the information passed in, or generate any calls to ThingSpeak.
222 * @code
223 #include <SPI.h>
224 #include <Ethernet.h>
225 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
226 EthernetClient client;
227
228 #include "ThingSpeak.h"
229
230 void setup() {
231 Ethernet.begin(mac);
232 ThingSpeak.begin(client);
233 }
234 * @endcode
235 */
236 bool begin(Client & client)
237 {
238#ifdef PRINT_DEBUG_MESSAGES
239 Serial.print("ts::tsBegin");
240#endif
241 this->setClient(&client);
242 this->setServer();
243 resetWriteFields();
244 this->lastReadStatus = OK_SUCCESS;
245 return true;
246 };
247
248 /**
249 * @brief Write an integer value to a single field in a ThingSpeak channel
250 * @param channelNumber Channel number
251 * @param field Field number (1-8) within the channel to write to.
252 * @param value Integer value (from -32,768 to 32,767) to write.
253 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
254 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
255 * @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.
256 * @code
257 void loop() {
258 int sensorValue = analogRead(A0);
259 ThingSpeak.writeField(myChannelNumber, 1, sensorValue, myWriteAPIKey);
260 delay(20000);
261 }
262 * @endcode
263 */
264 int writeField(unsigned long channelNumber, unsigned int field, int value, const char * writeAPIKey)
265 {
266#ifdef SPARK
267 // On Spark, int and long are the same, so map to the long version
268 return writeField(channelNumber, field, (long)value, writeAPIKey);
269#else
270 char valueString[10]; // int range is -32768 to 32768, so 7 bytes including terminator, plus a little extra
271 itoa(value, valueString, 10);
272 return writeField(channelNumber, field, valueString, writeAPIKey);
273#endif
274 };
275
276 /**
277 * @brief Write a long value to a single field in a ThingSpeak channel
278 * @param channelNumber Channel number
279 * @param field Field number (1-8) within the channel to write to.
280 * @param value Long value (from -2,147,483,648 to 2,147,483,647) to write.
281 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
282 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
283 * @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.
284 * @code
285 void loop() {
286 int sensorValue = analogRead(A0);
287 ThingSpeak.writeField(myChannelNumber, 1, sensorValue, myWriteAPIKey);
288 delay(20000);
289 }
290 * @endcode
291 */
292 int writeField(unsigned long channelNumber, unsigned int field, long value, const char * writeAPIKey)
293 {
294 char valueString[15]; // long range is -2147483648 to 2147483647, so 12 bytes including terminator
295 ltoa(value, valueString, 10);
296 return writeField(channelNumber, field, valueString, writeAPIKey);
297 };
298
299 /**
300 * @brief Write a floating point value to a single field in a ThingSpeak channel
301 * @param channelNumber Channel number
302 * @param field Field number (1-8) within the channel to write to.
303 * @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().
304 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
305 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
306 * @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.
307 * @code
308 void loop() {
309 int sensorValue = analogRead(A0);
310 float voltage = sensorValue * (5.0 / 1023.0);
311 ThingSpeak.writeField(myChannelNumber, 1, voltage, myWriteAPIKey);
312 delay(20000);
313 }
314 * @endcode
315 */
316 int writeField(unsigned long channelNumber, unsigned int field, float value, const char * writeAPIKey)
317 {
318 #ifdef PRINT_DEBUG_MESSAGES
319 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(")");
320 #endif
321 char valueString[20]; // range is -999999000000.00000 to 999999000000.00000, so 19 + 1 for the terminator
322 int status = convertFloatToChar(value, valueString);
323 if(status != OK_SUCCESS) return status;
324
325 return writeField(channelNumber, field, valueString, writeAPIKey);
326 };
327
328 /**
329 * @brief Write a string to a single field in a ThingSpeak channel
330 * @param channelNumber Channel number
331 * @param field Field number (1-8) within the channel to write to.
332 * @param value String to write (UTF8 string). ThingSpeak limits this field to 255 bytes.
333 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
334 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
335 * @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.
336 * @code
337 void loop() {
338 int sensorValue = analogRead(A0);
339 if (sensorValue > 512) {
340 ThingSpeak.writeField(myChannelNumber, 1, "High", myWriteAPIKey);
341 }
342 else {
343 ThingSpeak.writeField(myChannelNumber, 1, "Low", myWriteAPIKey);
344 }
345 delay(20000);
346 }
347 * @endcode
348 */
349 int writeField(unsigned long channelNumber, unsigned int field, const char * value, const char * writeAPIKey)
350 {
351 return writeField(channelNumber, field, String(value), writeAPIKey);
352 };
353
354 /**
355 * @brief Write a String to a single field in a ThingSpeak channel
356 * @param channelNumber Channel number
357 * @param field Field number (1-8) within the channel to write to.
358 * @param value Character array (zero terminated) to write (UTF8). ThingSpeak limits this field to 255 bytes.
359 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
360 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
361 * @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.
362 * @code
363 void loop() {
364 int sensorValue = analogRead(A0);
365 String meaning;
366 if (sensorValue < 400) {
367 meaning = String("Too Cold!");
368 } else if (sensorValue > 600) {
369 meaning = String("Too Hot!");
370 } else {
371 meaning = String("Just Right");
372 }
373 ThingSpeak.writeField(myChannelNumber, 1, meaning, myWriteAPIKey);
374 delay(20000);
375 }
376 * @endcode
377 */
378 int writeField(unsigned long channelNumber, unsigned int field, String value, const char * writeAPIKey)
379 {
380 // Invalid field number specified
381 if(field < FIELDNUM_MIN || field > FIELDNUM_MAX) return ERR_INVALID_FIELD_NUM;
382 // Max # bytes for ThingSpeak field is 255
383 if(value.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
384
385 #ifdef PRINT_DEBUG_MESSAGES
386 #ifdef SPARK
387 Particle.publish(SPARK_PUBLISH_TOPIC, "writeField (" + String(channelNumber) + ", " + String(writeAPIKey) + ", " + String(field) + ", " + String(value) + ")", SPARK_PUBLISH_TTL, PRIVATE);
388 #else
389 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("\")");
390 #endif
391 #endif
392 String postMessage = String("field") + String(field) + "=" + value;
393 return writeRaw(channelNumber, postMessage, writeAPIKey);
394 };
395
396
397 /**
398 * @brief Set the value of a single field that will be part of a multi-field update.
399 * To write multiple fields at once, call setField() for each of the fields you want to write, and then call writeFields()
400 * @param field Field number (1-8) within the channel to set
401 * @param value Integer value (from -32,768 to 32,767) to set.
402 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
403 * @see setLatitude(), setLongitude(), setElevation(), writeFields()
404 * @code
405 void loop() {
406 int sensor1Value = analogRead(A0);
407 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
408 String sensor3Meaning;
409 int sensor3Value = analogRead(A2);
410 if (sensor3Value < 400) {
411 sensor3Meaning = String("Too Cold!");
412 } else if (sensor3Value > 600) {
413 sensor3Meaning = String("Too Hot!");
414 } else {
415 sensor3Meaning = String("Just Right");
416 }
417 long timeRead = millis();
418
419 ThingSpeak.setField(1, sensor1Value);
420 ThingSpeak.setField(2, sensor2Voltage);
421 ThingSpeak.setField(3, sensor3Meaning);
422 ThingSpeak.setField(4, timeRead);
423 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
424 delay(20000);
425 }
426 * @endcode
427 */
428 int setField(unsigned int field, int value)
429 {
430#ifdef SPARK
431 // On Spark, int and long are the same, so map to the long version
432 return setField(field, (long)value);
433#else
434 char valueString[10]; // int range is -32768 to 32768, so 7 bytes including terminator
435 itoa(value, valueString, 10);
436
437 return setField(field, valueString);
438#endif
439 };
440
441 /**
442 * @brief Set the value of a single field that will be part of a multi-field update.
443 * To write multiple fields at once, call setField() for each of the fields you want to write, and then call writeFields()
444 * @param field Field number (1-8) within the channel to set
445 * @param value Long value (from -2,147,483,648 to 2,147,483,647) to write.
446 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
447 * @see setLatitude(), setLongitude(), setElevation(), writeFields()
448 * @code
449 void loop() {
450 int sensor1Value = analogRead(A0);
451 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
452 String sensor3Meaning;
453 int sensor3Value = analogRead(A2);
454 if (sensor3Value < 400) {
455 sensor3Meaning = String("Too Cold!");
456 } else if (sensor3Value > 600) {
457 sensor3Meaning = String("Too Hot!");
458 } else {
459 sensor3Meaning = String("Just Right");
460 }
461 long timeRead = millis();
462
463 ThingSpeak.setField(1, sensor1Value);
464 ThingSpeak.setField(2, sensor2Voltage);
465 ThingSpeak.setField(3, sensor3Meaning);
466 ThingSpeak.setField(4, timeRead);
467 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
468 delay(20000);
469 }
470 * @endcode
471 */
472 int setField(unsigned int field, long value)
473 {
474 char valueString[15]; // long range is -2147483648 to 2147483647, so 12 bytes including terminator
475 ltoa(value, valueString, 10);
476 return setField(field, valueString);
477 };
478
479
480 /**
481 * @brief Set the value of a single field that will be part of a multi-field update.
482 * To write multiple fields at once, call setField() for each of the fields you want to write, and then call writeFields()
483 * @param field Field number (1-8) within the channel to set
484 * @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.
485 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
486 * @see setLatitude(), setLongitude(), setElevation(), writeFields()
487 * @code
488 void loop() {
489 int sensor1Value = analogRead(A0);
490 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
491 String sensor3Meaning;
492 int sensor3Value = analogRead(A2);
493 if (sensor3Value < 400) {
494 sensor3Meaning = String("Too Cold!");
495 } else if (sensor3Value > 600) {
496 sensor3Meaning = String("Too Hot!");
497 } else {
498 sensor3Meaning = String("Just Right");
499 }
500 long timeRead = millis();
501
502 ThingSpeak.setField(1, sensor1Value);
503 ThingSpeak.setField(2, sensor2Voltage);
504 ThingSpeak.setField(3, sensor3Meaning);
505 ThingSpeak.setField(4, timeRead);
506 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
507 delay(20000);
508 }
509 * @endcode
510 */
511 int setField(unsigned int field, float value)
512 {
513 char valueString[20]; // range is -999999000000.00000 to 999999000000.00000, so 19 + 1 for the terminator
514 int status = convertFloatToChar(value, valueString);
515 if(status != OK_SUCCESS) return status;
516
517 return setField(field, valueString);
518 };
519
520 /**
521 * @brief Set the value of a single field that will be part of a multi-field update.
522 * To write multiple fields at once, call setField() for each of the fields you want to write, and then call writeFields()
523 * @param field Field number (1-8) within the channel to set
524 * @param value String to write (UTF8). ThingSpeak limits this to 255 bytes.
525 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
526 * @see setLatitude(), setLongitude(), setElevation(), writeFields()
527 * @code
528 void loop() {
529 int sensor1Value = analogRead(A0);
530 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
531 String sensor3Meaning;
532 int sensor3Value = analogRead(A2);
533 if (sensor3Value < 400) {
534 sensor3Meaning = String("Too Cold!");
535 } else if (sensor3Value > 600) {
536 sensor3Meaning = String("Too Hot!");
537 } else {
538 sensor3Meaning = String("Just Right");
539 }
540 long timeRead = millis();
541
542 ThingSpeak.setField(1, sensor1Value);
543 ThingSpeak.setField(2, sensor2Voltage);
544 ThingSpeak.setField(3, sensor3Meaning);
545 ThingSpeak.setField(4, timeRead);
546 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
547 delay(20000);
548 }
549 * @endcode
550 */
551 int setField(unsigned int field, const char * value)
552 {
553 return setField(field, String(value));
554 };
555
556 /**
557 * @brief Set the value of a single field that will be part of a multi-field update.
558 * To write multiple fields at once, call setField() for each of the fields you want to write, and then call writeFields()
559 * @param field Field number (1-8) within the channel to set
560 * @param value String to write (UTF8). ThingSpeak limits this to 255 bytes.
561 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
562 * @see setLatitude(), setLongitude(), setElevation(), writeFields()
563 * @code
564 void loop() {
565 int sensor1Value = analogRead(A0);
566 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
567 String sensor3Meaning;
568 int sensor3Value = analogRead(A2);
569 if (sensor3Value < 400) {
570 sensor3Meaning = String("Too Cold!");
571 } else if (sensor3Value > 600) {
572 sensor3Meaning = String("Too Hot!");
573 } else {
574 sensor3Meaning = String("Just Right");
575 }
576 long timeRead = millis();
577
578 ThingSpeak.setField(1, sensor1Value);
579 ThingSpeak.setField(2, sensor2Voltage);
580 ThingSpeak.setField(3, sensor3Meaning);
581 ThingSpeak.setField(4, timeRead);
582 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
583 delay(20000);
584 }
585 * @endcode
586 */
587 int setField(unsigned int field, String value)
588 {
589 #ifdef PRINT_DEBUG_MESSAGES
590 #ifdef SPARK
591 Particle.publish(SPARK_PUBLISH_TOPIC, "setField " + String(field) + " to " + String(value), SPARK_PUBLISH_TTL, PRIVATE);
592 #else
593 Serial.print("ts::setField (field: "); Serial.print(field); Serial.print(" value: \""); Serial.print(value); Serial.println("\")");
594 #endif
595 #endif
596 if(field < FIELDNUM_MIN || field > FIELDNUM_MAX) return ERR_INVALID_FIELD_NUM;
597 // Max # bytes for ThingSpeak field is 255 (UTF-8)
598 if(value.length() > FIELDLENGTH_MAX) return ERR_OUT_OF_RANGE;
599 this->nextWriteField[field - 1] = value;
600 return OK_SUCCESS;
601 };
602
603
604 /**
605 * @brief Set the latitude of a multi-field update.
606 * 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()
607 * @param latitude Latitude of the measurement (degrees N, use negative values for degrees S)
608 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
609 * @see setField(), setLongitude(), setElevation(), writeFields()
610 * @code
611 void loop() {
612 int sensor1Value = analogRead(A0);
613 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
614 String sensor3Meaning;
615 int sensor3Value = analogRead(A2);
616 if (sensor3Value < 400) {
617 sensor3Meaning = String("Too Cold!");
618 } else if (sensor3Value > 600) {
619 sensor3Meaning = String("Too Hot!");
620 } else {
621 sensor3Meaning = String("Just Right");
622 }
623 long timeRead = millis();
624
625 ThingSpeak.setField(1, sensor1Value);
626 ThingSpeak.setField(2, sensor2Voltage);
627 ThingSpeak.setField(3, sensor3Meaning);
628 ThingSpeak.setField(4, timeRead);
629 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
630 setLatitude(42.2833);
631 setLongitude(-71.3500);
632 setElevation(100);
633 delay(20000);
634 }
635 * @endcode
636 */
637 int setLatitude(float latitude)
638 {
639 #ifdef PRINT_DEBUG_MESSAGES
640 Serial.print("ts::setLatitude(latitude: "); Serial.print(latitude,3); Serial.println("\")");
641 #endif
642 this->nextWriteLatitude = latitude;
643 return OK_SUCCESS;
644 };
645
646
647 /**
648 * @brief Set the longitude of a multi-field update.
649 * 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()
650 * @param longitude Longitude of the measurement (degrees E, use negative values for degrees W)
651 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
652 * @see setField(), setLatitude(), setElevation(), writeFields()
653 * @code
654 void loop() {
655 int sensor1Value = analogRead(A0);
656 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
657 String sensor3Meaning;
658 int sensor3Value = analogRead(A2);
659 if (sensor3Value < 400) {
660 sensor3Meaning = String("Too Cold!");
661 } else if (sensor3Value > 600) {
662 sensor3Meaning = String("Too Hot!");
663 } else {
664 sensor3Meaning = String("Just Right");
665 }
666 long timeRead = millis();
667
668 ThingSpeak.setField(1, sensor1Value);
669 ThingSpeak.setField(2, sensor2Voltage);
670 ThingSpeak.setField(3, sensor3Meaning);
671 ThingSpeak.setField(4, timeRead);
672 setLatitude(42.2833);
673 setLongitude(-71.3500);
674 setElevation(100);
675 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
676 delay(20000);
677 }
678 * @endcode
679 */
680 int setLongitude(float longitude)
681 {
682 #ifdef PRINT_DEBUG_MESSAGES
683 Serial.print("ts::setLongitude(longitude: "); Serial.print(longitude,3); Serial.println("\")");
684 #endif
685 this->nextWriteLongitude = longitude;
686 return OK_SUCCESS;
687 };
688
689
690 /**
691 * @brief Set the elevation of a multi-field update.
692 * 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()
693 * @param elevation Elevation of the measurement (meters above sea level)
694 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
695 * @see setField(), setLatitude(), setLongitude(), writeFields()
696 * @code
697 void loop() {
698 int sensor1Value = analogRead(A0);
699 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
700 String sensor3Meaning;
701 int sensor3Value = analogRead(A2);
702 if (sensor3Value < 400) {
703 sensor3Meaning = String("Too Cold!");
704 } else if (sensor3Value > 600) {
705 sensor3Meaning = String("Too Hot!");
706 } else {
707 sensor3Meaning = String("Just Right");
708 }
709 long timeRead = millis();
710
711 ThingSpeak.setField(1, sensor1Value);
712 ThingSpeak.setField(2, sensor2Voltage);
713 ThingSpeak.setField(3, sensor3Meaning);
714 ThingSpeak.setField(4, timeRead);
715 setLatitude(42.2833);
716 setLongitude(-71.3500);
717 setElevation(100);
718 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
719 delay(20000);
720 }
721 * @endcode
722 */
723 int setElevation(float elevation)
724 {
725 #ifdef PRINT_DEBUG_MESSAGES
726 Serial.print("ts::setElevation(elevation: "); Serial.print(elevation,3); Serial.println("\")");
727 #endif
728 this->nextWriteElevation = elevation;
729 return OK_SUCCESS;
730 };
731
732
733 /**
734 * @brief Write a multi-field update.
735 * Call setField() for each of the fields you want to write, setLatitude() / setLongitude() / setElevation(), and then call writeFields()
736 * @param channelNumber Channel number
737 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
738 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
739 * @see setField(), setLatitude(), setLongitude(), setElevation()
740 * @code
741 void loop() {
742 int sensor1Value = analogRead(A0);
743 float sensor2Voltage = analogRead(A1) * (5.0 / 1023.0);
744 String sensor3Meaning;
745 int sensor3Value = analogRead(A2);
746 if (sensor3Value < 400) {
747 sensor3Meaning = String("Too Cold!");
748 } else if (sensor3Value > 600) {
749 sensor3Meaning = String("Too Hot!");
750 } else {
751 sensor3Meaning = String("Just Right");
752 }
753 long timeRead = millis();
754
755 ThingSpeak.setField(1, sensor1Value);
756 ThingSpeak.setField(2, sensor2Voltage);
757 ThingSpeak.setField(3, sensor3Meaning);
758 ThingSpeak.setField(4, timeRead);
759 setLatitude(42.2833);
760 setLongitude(-71.3500);
761 setElevation(100);
762 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
763 delay(20000);
764 }
765 * @endcode
766 */
767 int writeFields(unsigned long channelNumber, const char * writeAPIKey)
768 {
769 String postMessage = String("");
770 bool fFirstItem = true;
771 for(size_t iField = 0; iField < 8; iField++)
772 {
773 if(this->nextWriteField[iField].length() > 0)
774 {
775 if(!fFirstItem)
776 {
777 postMessage = postMessage + String("&");
778 }
779 postMessage = postMessage + String("field") + String(iField + 1) + String("=") + this->nextWriteField[iField];
780 fFirstItem = false;
781 this->nextWriteField[iField] = "";
782 }
783 }
784
785 if(!isnan(nextWriteLatitude))
786 {
787 if(!fFirstItem)
788 {
789 postMessage = postMessage + String("&");
790 }
791 postMessage = postMessage + String("lat=") + String(this->nextWriteLatitude);
792 fFirstItem = false;
793 this->nextWriteLatitude = NAN;
794 }
795
796 if(!isnan(this->nextWriteLongitude))
797 {
798 if(!fFirstItem)
799 {
800 postMessage = postMessage + String("&");
801 }
802 postMessage = postMessage + String("long=") + String(this->nextWriteLongitude);
803 fFirstItem = false;
804 this->nextWriteLongitude = NAN;
805 }
806
807
808 if(!isnan(this->nextWriteElevation))
809 {
810 if(!fFirstItem)
811 {
812 postMessage = postMessage + String("&");
813 }
814 postMessage = postMessage + String("elevation=") + String(this->nextWriteElevation);
815 fFirstItem = false;
816 this->nextWriteElevation = NAN;
817 }
818
819 if(fFirstItem)
820 {
821 // setField was not called before writeFields
822 return ERR_SETFIELD_NOT_CALLED;
823 }
824
825 return writeRaw(channelNumber, postMessage, writeAPIKey);
826 };
827
828
829 /**
830 * @brief Write a raw POST to a ThingSpeak channel
831 * @param channelNumber Channel number
832 * @param postMessage Raw URL to write to ThingSpeak as a string. See the documentation at https://thingspeak.com/docs/channels#update_feed.
833 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
834 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
835 * @remark This is low level functionality that will not be required by most users.
836 * @code
837 void loop() {
838 const char postMessage[] = "field1=23&created_at=2014-12-31%2023:59:59";
839
840 ThingSpeak.setField(1, sensor1Value);
841 ThingSpeak.setField(2, sensor2Voltage);
842 ThingSpeak.setField(3, sensor3Meaning);
843 ThingSpeak.setField(4, timeRead);
844 ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
845 delay(20000);
846 }
847 * @endcode
848 */
849 int writeRaw(unsigned long channelNumber, const char * postMessage, const char * writeAPIKey)
850 {
851 return writeRaw(channelNumber, String(postMessage), writeAPIKey);
852 };
853
854
855 /**
856 * @brief Write a raw POST to a ThingSpeak channel
857 * @param channelNumber Channel number
858 * @param postMessage Raw URL to write to ThingSpeak as a String. See the documentation at https://thingspeak.com/docs/channels#update_feed.
859 * @param writeAPIKey Write API key associated with the channel. *If you share code with others, do _not_ share this key*
860 * @return HTTP status code of 200 if successful. See getLastReadStatus() for other possible return values.
861 * @remark This is low level functionality that will not be required by most users.
862 * @code
863 void loop() {
864 String postMessage = String("field1=23&created_at=2014-12-31%2023:59:59");
865 ThingSpeak.writeRaw(myChannelNumber, postMessage, myWriteAPIKey);
866 delay(20000);
867 }
868 * @endcode
869 */
870 int writeRaw(unsigned long channelNumber, String postMessage, const char * writeAPIKey)
871 {
872 #ifdef PRINT_DEBUG_MESSAGES
873 Serial.print("ts::writeRaw (channelNumber: "); Serial.print(channelNumber); Serial.print(" writeAPIKey: "); Serial.print(writeAPIKey); Serial.print(" postMessage: \""); Serial.print(postMessage); Serial.println("\")");
874 #endif
875
876 if(!connectThingSpeak())
877 {
878 // Failed to connect to ThingSpeak
879 return ERR_CONNECT_FAILED;
880 }
881
882 postMessage = postMessage + String("&headers=false");
883
884 #ifdef PRINT_DEBUG_MESSAGES
885 #ifdef SPARK
886 Particle.publish(SPARK_PUBLISH_TOPIC, "Post " + postMessage, SPARK_PUBLISH_TTL, PRIVATE);
887 #else
888 Serial.print(" POST \"");Serial.print(postMessage);Serial.println("\"");
889 #endif
890 #endif
891
892 postMessage = postMessage + String("\n");
893
894 // Post data to thingspeak
895 if(!this->client->print("POST /update HTTP/1.1\n")) return abortWriteRaw();
896 if(!writeHTTPHeader(writeAPIKey)) return abortWriteRaw();
897 if(!this->client->print("Content-Type: application/x-www-form-urlencoded\n")) return abortWriteRaw();
898 if(!this->client->print("Content-Length: ")) return abortWriteRaw();
899 if(!this->client->print(postMessage.length())) return abortWriteRaw();
900 if(!this->client->print("\n\n")) return abortWriteRaw();
901 if(!this->client->print(postMessage)) return abortWriteRaw();
902
903 String entryIDText = String();
904 int status = getHTTPResponse(entryIDText);
905 if(status != OK_SUCCESS)
906 {
907 client->stop();
908 return status;
909 }
910 long entryID = entryIDText.toInt();
911
912 #ifdef PRINT_DEBUG_MESSAGES
913 Serial.print(" Entry ID \"");Serial.print(entryIDText);Serial.print("\" (");Serial.print(entryID);Serial.println(")");
914 #endif
915
916 client->stop();
917
918 #ifdef PRINT_DEBUG_MESSAGES
919 Serial.println("disconnected.");
920 #endif
921 if(entryID == 0)
922 {
923 // ThingSpeak did not accept the write
924 status = ERR_NOT_INSERTED;
925 }
926 return status;
927 };
928
929 /**
930 * @brief Read the latest string from a private ThingSpeak channel
931 * @param channelNumber Channel number
932 * @param field Field number (1-8) within the channel to read from.
933 * @param readAPIKey Read API key associated with the channel. *If you share code with others, do _not_ share this key*
934 * @return Value read (UTF8 string), or empty string if there is an error. Use getLastReadStatus() to get more specific information.
935 * @code
936 void loop() {
937 String message = ThingSpeak.readStringField(myChannelNumber, 1, myReadAPIKey);
938 Serial.print("Latest message is: ");
939 Serial.println(message);
940 delay(30000);
941 }
942 * @endcode
943 */
944 String readStringField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
945 {
946 if(field < FIELDNUM_MIN || field > FIELDNUM_MAX)
947 {
948 this->lastReadStatus = ERR_INVALID_FIELD_NUM;
949 return("");
950 }
951 #ifdef PRINT_DEBUG_MESSAGES
952 Serial.print("ts::readStringField(channelNumber: "); Serial.print(channelNumber);
953 if(NULL != readAPIKey)
954 {
955 Serial.print(" readAPIKey: "); Serial.print(readAPIKey);
956 }
957 Serial.print(" field: "); Serial.print(field); Serial.println(")");
958 #endif
959 return readRaw(channelNumber, String(String("/fields/") + String(field) + String("/last")), readAPIKey);
960 }
961
962
963 /**
964 * @brief Read the latest string from a public ThingSpeak channel
965 * @param channelNumber Channel number
966 * @param field Field number (1-8) within the channel to read from.
967 * @return Value read (UTF8), or empty string if there is an error. Use getLastReadStatus() to get more specific information.
968 * @code
969 void loop() {
970 String message = ThingSpeak.readStringField(myChannelNumber, 1);
971 Serial.print("Latest message is: ");
972 Serial.println(message);
973 delay(30000);
974 }
975 * @endcode
976 */
977 String readStringField(unsigned long channelNumber, unsigned int field)
978 {
979 return readStringField(channelNumber, field, NULL);
980 };
981
982
983 /**
984 * @brief Read the latest float from a private ThingSpeak channel
985 * @param channelNumber Channel number
986 * @param field Field number (1-8) within the channel to read from.
987 * @param readAPIKey Read API key associated with the channel. *If you share code with others, do _not_ share this key*
988 * @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.
989 * @code
990 void loop() {
991 float voltage = ThingSpeak.readFloatField(myChannelNumber, 1, myReadAPIKey);
992 Serial.print("Latest voltage is: ");
993 Serial.print(voltage);
994 Serial.println("V");
995 delay(30000);
996 }
997 * @endcode
998 */
999 float readFloatField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1000 {
1001 return convertStringToFloat(readStringField(channelNumber, field, readAPIKey));
1002 };
1003
1004
1005 /**
1006 * @brief Read the latest float from a public ThingSpeak channel
1007 * @param channelNumber Channel number
1008 * @param field Field number (1-8) within the channel to read from.
1009 * @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.
1010 * @code
1011 void loop() {
1012 float voltage = ThingSpeak.readFloatField(myChannelNumber, 1);
1013 Serial.print("Latest voltage is: ");
1014 Serial.print(voltage);
1015 Serial.println("V");
1016 delay(30000);
1017 }
1018 * @endcode
1019 */
1020 float readFloatField(unsigned long channelNumber, unsigned int field)
1021 {
1022 return readFloatField(channelNumber, field, NULL);
1023 };
1024
1025
1026 /**
1027 * @brief Read the latest long from a private ThingSpeak channel
1028 * @param channelNumber Channel number
1029 * @param field Field number (1-8) within the channel to read from.
1030 * @param readAPIKey Read API key associated with the channel. *If you share code with others, do _not_ share this key*
1031 * @return Value read, or 0 if the field is text or there is an error. Use getLastReadStatus() to get more specific information.
1032 * @code
1033 void loop() {
1034 long value = ThingSpeak.readLongField(myChannelNumber, 1, myReadAPIKey);
1035 Serial.print("Latest value is: ");
1036 Serial.print(value);
1037 delay(30000);
1038 }
1039 * @endcode
1040 */
1041 long readLongField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1042 {
1043 // Note that although the function is called "toInt" it really returns a long.
1044 return readStringField(channelNumber, field, readAPIKey).toInt();
1045 }
1046
1047
1048 /**
1049 * @brief Read the latest long from a public ThingSpeak channel
1050 * @param channelNumber Channel number
1051 * @param field Field number (1-8) within the channel to read from.
1052 * @return Value read, or 0 if the field is text or there is an error. Use getLastReadStatus() to get more specific information.
1053 * @code
1054 void loop() {
1055 long value = ThingSpeak.readLongField(myChannelNumber, 1);
1056 Serial.print("Latest value is: ");
1057 Serial.print(value);
1058 delay(30000);
1059 }
1060 * @endcode
1061 */
1062 long readLongField(unsigned long channelNumber, unsigned int field)
1063 {
1064 return readLongField(channelNumber, field, NULL);
1065 };
1066
1067
1068 /**
1069 * @brief Read the latest int from a private ThingSpeak channel
1070 * @param channelNumber Channel number
1071 * @param field Field number (1-8) within the channel to read from.
1072 * @param readAPIKey Read API key associated with the channel. *If you share code with others, do _not_ share this key*
1073 * @return Value read, or 0 if the field is text or there is an error. Use getLastReadStatus() to get more specific information.
1074 * @remark If the value returned is out of range for an int, the result is undefined.
1075 * @code
1076 void loop() {
1077 int value = ThingSpeak.readIntField(myChannelNumber, 1, myReadAPIKey);
1078 Serial.print("Latest value is: ");
1079 Serial.print(value);
1080 delay(30000);
1081 }
1082 * @endcode
1083 */
1084 int readIntField(unsigned long channelNumber, unsigned int field, const char * readAPIKey)
1085 {
1086 return readLongField(channelNumber, field, readAPIKey);
1087 }
1088
1089
1090 /**
1091 * @brief Read the latest int from a public ThingSpeak channel
1092 * @param channelNumber Channel number
1093 * @param field Field number (1-8) within the channel to read from.
1094 * @return Value read, or 0 if the field is text or there is an error. Use getLastReadStatus() to get more specific information.
1095 * @remark If the value returned is out of range for an int, the result is undefined.
1096 * @code
1097 void loop() {
1098 int value = ThingSpeak.readIntField(myChannelNumber, 1);
1099 Serial.print("Latest value is: ");
1100 Serial.print(value);
1101 delay(30000);
1102 }
1103 * @endcode
1104 */
1105 int readIntField(unsigned long channelNumber, unsigned int field)
1106 {
1107 return readLongField(channelNumber, field, NULL);
1108 };
1109
1110 /**
1111 * @brief Read a raw response from a public ThingSpeak channel
1112 * @param channelNumber Channel number
1113 * @param URLSuffix Raw URL to write to ThingSpeak as a String. See the documentation at https://thingspeak.com/docs/channels#get_feed
1114 * @return Response if successful, or empty string. Use getLastReadStatus() to get more specific information.
1115 * @remark This is low level functionality that will not be required by most users.
1116 * @code
1117 void loop() {
1118 String response = ThingSpeak.readRaw(myChannelNumber, String("feeds/days=1"));
1119 Serial.print("Response: ");
1120 Serial.print(response);
1121 delay(30000);
1122 }
1123 * @endcode
1124 */
1125 String readRaw(unsigned long channelNumber, String URLSuffix)
1126 {
1127 return readRaw(channelNumber, URLSuffix, NULL);
1128 }
1129
1130 /**
1131 * @brief Read a raw response from a private ThingSpeak channel
1132 * @param channelNumber Channel number
1133 * @param URLSuffix Raw URL to write to ThingSpeak as a String. See the documentation at https://thingspeak.com/docs/channels#get_feed
1134 * @param readAPIKey Read API key associated with the channel. *If you share code with others, do _not_ share this key*
1135 * @return Response if successful, or empty string. Use getLastReadStatus() to get more specific information.
1136 * @remark This is low level functionality that will not be required by most users.
1137 * @code
1138 void loop() {
1139 String response = ThingSpeak.readRaw(myChannelNumber, String("feeds/days=1"), myReadAPIKey);
1140 Serial.print("Response: ");
1141 Serial.print(response);
1142 delay(30000);
1143 }
1144 * @endcode
1145 */
1146 String readRaw(unsigned long channelNumber, String URLSuffix, const char * readAPIKey)
1147 {
1148 #ifdef PRINT_DEBUG_MESSAGES
1149 Serial.print("ts::readRaw (channelNumber: "); Serial.print(channelNumber);
1150 if(NULL != readAPIKey)
1151 {
1152 Serial.print(" readAPIKey: "); Serial.print(readAPIKey);
1153 }
1154 Serial.print(" URLSuffix: \""); Serial.print(URLSuffix); Serial.println("\")");
1155 #endif
1156
1157 if(!connectThingSpeak())
1158 {
1159 this->lastReadStatus = ERR_CONNECT_FAILED;
1160 return String("");
1161 }
1162
1163 String URL = String("/channels/") + String(channelNumber) + URLSuffix;
1164
1165 #ifdef PRINT_DEBUG_MESSAGES
1166 Serial.print(" GET \"");Serial.print(URL);Serial.println("\"");
1167 #endif
1168
1169 // Post data to thingspeak
1170 if(!this->client->print("GET ")) return abortReadRaw();
1171 if(!this->client->print(URL)) return abortReadRaw();
1172 if(!this->client->print(" HTTP/1.1\n")) return abortReadRaw();
1173 if(!writeHTTPHeader(readAPIKey)) return abortReadRaw();
1174 if(!this->client->print("\n")) return abortReadRaw();
1175
1176 String content = String();
1177 int status = getHTTPResponse(content);
1178 this->lastReadStatus = status;
1179
1180
1181 #ifdef PRINT_DEBUG_MESSAGES
1182 if(status == OK_SUCCESS)
1183 {
1184 Serial.print("Read: \""); Serial.print(content); Serial.println("\"");
1185 }
1186 #endif
1187
1188 client->stop();
1189 #ifdef PRINT_DEBUG_MESSAGES
1190 Serial.println("disconnected.");
1191 #endif
1192
1193 if(status != OK_SUCCESS)
1194 {
1195 // return status;
1196 return String("");
1197 }
1198
1199 // This is a workaround to a bug in the Spark implementation of String
1200 return String("") + content;
1201 };
1202
1203 /**
1204 * @brief Get the status of the previous read.
1205 * @return Generally, these are HTTP status codes. Negative values indicate an error generated by the library.
1206 * Possible response codes:
1207 * * 200: OK / Success
1208 * * 404: Incorrect API key (or invalid ThingSpeak server address)
1209 * * -101: Value is out of range or string is too long (> 255 characters)
1210 * * -201: Invalid field number specified
1211 * * -210: setField() was not called before writeFields()
1212 * * -301: Failed to connect to ThingSpeak
1213 * * -302: Unexpected failure during write to ThingSpeak
1214 * * -303: Unable to parse response
1215 * * -304: Timeout waiting for server to respond
1216 * * -401: Point was not inserted (most probable cause is the rate limit of once every 15 seconds)
1217 * @remark The read functions will return zero or empty if there is an error. Use this function to retrieve the details.
1218 * @code
1219 void loop() {
1220 String message = ThingSpeak.readStringField(myChannelNumber, 1);
1221 int resultCode = ThingSpeak.getLastReadStatus();
1222 if(resultCode == 200)
1223 {
1224 Serial.print("Latest message is: ");
1225 Serial.println(message);
1226 }
1227 else
1228 {
1229 Serial.print("Error reading message. Status was: ");
1230 Serial.println(resultCode);
1231 }
1232 delay(30000);
1233 }
1234 * @endcode
1235 */
1236 int getLastReadStatus()
1237 {
1238 return this->lastReadStatus;
1239 };
1240private:
1241
1242 int abortWriteRaw()
1243 {
1244 this->client->stop();
1245 return ERR_UNEXPECTED_FAIL;
1246 }
1247
1248 String abortReadRaw()
1249 {
1250 this->client->stop();
1251 #ifdef PRINT_DEBUG_MESSAGES
1252 Serial.println("ReadRaw abort - disconnected.");
1253 #endif
1254 this->lastReadStatus = ERR_UNEXPECTED_FAIL;
1255 return String("");
1256 }
1257
1258 void setServer(const char * customHostName, unsigned int port)
1259 {
1260 #ifdef PRINT_DEBUG_MESSAGES
1261 Serial.print("ts::setServer (URL: \""); Serial.print(customHostName); Serial.println("\")");
1262 #endif
1263 this->customIP = INADDR_NONE;
1264 this->customHostName = customHostName;
1265 this->port = port;
1266 };
1267
1268 void setServer(IPAddress customIP, unsigned int port)
1269 {
1270 #ifdef PRINT_DEBUG_MESSAGES
1271 Serial.print("ts::setServer (IP: \""); Serial.print(customIP); Serial.println("\")");
1272 #endif
1273 this->customIP = customIP;
1274 this->customHostName = NULL;
1275 this->port = port;
1276 };
1277
1278 void setServer()
1279 {
1280 #ifdef PRINT_DEBUG_MESSAGES
1281 Serial.print("ts::setServer (default)");
1282 #endif
1283 this->customIP = INADDR_NONE;
1284 this->customHostName = NULL;
1285 this->port = THINGSPEAK_PORT_NUMBER;
1286 };
1287
1288 void setClient(Client * client) {this->client = client;};
1289
1290 Client * client = NULL;
1291 const char * customHostName = NULL;
1292 IPAddress customIP;
1293 unsigned int port;
1294 String nextWriteField[8];
1295 float nextWriteLatitude;
1296 float nextWriteLongitude;
1297 float nextWriteElevation;
1298 int lastReadStatus;
1299
1300 bool connectThingSpeak()
1301 {
1302 bool connectSuccess = false;
1303 if(this->customIP == INADDR_NONE && NULL == this->customHostName)
1304 {
1305 #ifdef PRINT_DEBUG_MESSAGES
1306 Serial.print(" Connect to default ThingSpeak URL...");
1307 #endif
1308 connectSuccess = client->connect(THINGSPEAK_URL,THINGSPEAK_PORT_NUMBER);
1309 if(!connectSuccess)
1310 {
1311 #ifdef PRINT_DEBUG_MESSAGES
1312 Serial.print("Failed. Try default IP...");
1313 #endif
1314 connectSuccess = client->connect(THINGSPEAK_IPADDRESS,THINGSPEAK_PORT_NUMBER);
1315 }
1316 }
1317 else
1318 {
1319 if(!(this->customIP == INADDR_NONE))
1320 {
1321 // Connect to the server on port 80 (HTTP) at the customIP address
1322 #ifdef PRINT_DEBUG_MESSAGES
1323 Serial.print(" Connect to ");Serial.print(this->customIP);Serial.print("...");
1324 #endif
1325 connectSuccess = client->connect(this->customIP,this->port);
1326 }
1327 if(NULL != this->customHostName)
1328 {
1329 // Connect to the server on port 80 (HTTP) at the URL address
1330 #ifdef PRINT_DEBUG_MESSAGES
1331 #ifdef SPARK
1332 Particle.publish(SPARK_PUBLISH_TOPIC, "Attempt Connect to URL " + String(this->customHostName), SPARK_PUBLISH_TTL, PRIVATE);
1333 #else
1334 Serial.print(" Connect to ");Serial.print(this->customHostName);Serial.print(" ...");
1335 #endif
1336 #endif
1337 connectSuccess = client->connect(customHostName,this->port);
1338 }
1339 }
1340
1341 #ifdef PRINT_DEBUG_MESSAGES
1342 if (connectSuccess)
1343 {
1344 #ifdef SPARK
1345 Particle.publish(SPARK_PUBLISH_TOPIC, "Connection Success", SPARK_PUBLISH_TTL, PRIVATE);
1346 #else
1347 Serial.println("Success.");
1348 #endif
1349 }
1350 else
1351 {
1352 #ifdef SPARK
1353 Particle.publish(SPARK_PUBLISH_TOPIC, "Connection Failure", SPARK_PUBLISH_TTL, PRIVATE);
1354 #else
1355 Serial.println("Failed.");
1356 #endif
1357 }
1358 #endif
1359 return connectSuccess;
1360 };
1361
1362 bool writeHTTPHeader(const char * APIKey)
1363 {
1364 if(NULL != this->customHostName)
1365 {
1366 if (!this->client->print("Host: ")) return false;
1367 if (!this->client->print(this->customHostName)) return false;
1368 if (!this->client->print("\n")) return false;
1369 }
1370 else
1371 {
1372 if (!this->client->print("Host: api.thingspeak.com\n")) return false;
1373 }
1374 if (!this->client->print("Connection: close\n")) return false;
1375 if (!this->client->print("User-Agent: ")) return false;
1376 if (!this->client->print(TS_USER_AGENT)) return false;
1377 if (!this->client->print("\n")) return false;
1378 if(NULL != APIKey)
1379 {
1380 if (!this->client->print("X-THINGSPEAKAPIKEY: ")) return false;
1381 if (!this->client->print(APIKey)) return false;
1382 if (!this->client->print("\n")) return false;
1383 }
1384 return true;
1385 };
1386
1387 int getHTTPResponse(String & response)
1388 {
1389 long startWaitForResponseAt = millis();
1390 while(client->available() == 0 && millis() - startWaitForResponseAt < TIMEOUT_MS_SERVERRESPONSE)
1391 {
1392 delay(100);
1393 }
1394 if(client->available() == 0)
1395 {
1396 return ERR_TIMEOUT; // Didn't get server response in time
1397 }
1398
1399 if(!client->find(const_cast<char *>("HTTP/1.1")))
1400 {
1401 #ifdef PRINT_HTTP
1402 #ifdef SPARK
1403 Particle.publish(SPARK_PUBLISH_TOPIC, "ERROR: Didn't find HTTP/1.1", SPARK_PUBLISH_TTL, PRIVATE);
1404 #else
1405 Serial.println("ERROR: Didn't find HTTP/1.1");
1406 #endif
1407 #endif
1408 return ERR_BAD_RESPONSE; // Couldn't parse response (didn't find HTTP/1.1)
1409 }
1410 int status = client->parseInt();
1411 #ifdef PRINT_HTTP
1412 #ifdef SPARK
1413 Particle.publish(SPARK_PUBLISH_TOPIC, "Got Status of " + String(status), SPARK_PUBLISH_TTL, PRIVATE);
1414 #else
1415 Serial.print("Got Status of ");Serial.println(status);
1416 #endif
1417 #endif
1418 if(status != OK_SUCCESS)
1419 {
1420 return status;
1421 }
1422
1423 if(!client->find(const_cast<char *>("\r\n")))
1424 {
1425 #ifdef PRINT_HTTP
1426 #ifdef SPARK
1427 Particle.publish(SPARK_PUBLISH_TOPIC, "ERROR: Didn't find end of status line", SPARK_PUBLISH_TTL, PRIVATE);
1428 #else
1429 Serial.println("ERROR: Didn't find end of status line");
1430 #endif
1431 #endif
1432 return ERR_BAD_RESPONSE;
1433 }
1434 #ifdef PRINT_HTTP
1435 #ifdef SPARK
1436 Particle.publish(SPARK_PUBLISH_TOPIC, "Found end of status line", SPARK_PUBLISH_TTL, PRIVATE);
1437 #else
1438 Serial.println("Found end of status line");
1439 #endif
1440 #endif
1441
1442 if(!client->find(const_cast<char *>("\n\r\n")))
1443 {
1444 #ifdef PRINT_HTTP
1445 #ifdef SPARK
1446 Particle.publish(SPARK_PUBLISH_TOPIC, "ERROR: Didn't find end of header", SPARK_PUBLISH_TTL, PRIVATE);
1447 #else
1448 Serial.println("ERROR: Didn't find end of header");
1449 #endif
1450 #endif
1451 return ERR_BAD_RESPONSE;
1452 }
1453 #ifdef PRINT_HTTP
1454 #ifdef SPARK
1455 Particle.publish(SPARK_PUBLISH_TOPIC, "Found end of header", SPARK_PUBLISH_TTL, PRIVATE);
1456 #else
1457 Serial.println("Found end of header");
1458 #endif
1459 #endif
1460 // This is a workaround to a bug in the Spark implementation of String
1461 String tempString = client->readStringUntil('\r');
1462 response = tempString;
1463 #ifdef PRINT_HTTP
1464 #ifdef SPARK
1465 Particle.publish(SPARK_PUBLISH_TOPIC, "Response: \"" + tempString + "\"", SPARK_PUBLISH_TTL, PRIVATE);
1466 #else
1467 Serial.print("Response: \"");Serial.print(response);Serial.println("\"");
1468 #endif
1469 #endif
1470 return status;
1471 };
1472
1473 int convertFloatToChar(float value, char *valueString)
1474 {
1475 // Supported range is -999999000000 to 999999000000
1476 if(0 == isinf(value) && (value > 999999000000 || value < -999999000000))
1477 {
1478 // Out of range
1479 return ERR_OUT_OF_RANGE;
1480 }
1481 // 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
1482 #ifdef PARTICLE_PHOTONELECTRON
1483 //Photon doesn't have a dtostrf, but does have dtoa
1484 dtoa((double)value,5, valueString);
1485 #else
1486 dtostrf(value,1,5, valueString);
1487 #endif
1488 return OK_SUCCESS;
1489 };
1490
1491 float convertStringToFloat(String value)
1492 {
1493 // There's a bug in the AVR function strtod that it doesn't decode -INF correctly (it maps it to INF)
1494 float result = value.toFloat();
1495 if(1 == isinf(result) && *value.c_str() == '-')
1496 {
1497 result = (float)-INFINITY;
1498 }
1499 return result;
1500 };
1501
1502 void resetWriteFields()
1503 {
1504 for(size_t iField = 0; iField < 8; iField++)
1505 {
1506 this->nextWriteField[iField] = "";
1507 }
1508 this->nextWriteLatitude = NAN;
1509 this->nextWriteLongitude = NAN;
1510 this->nextWriteElevation = NAN;
1511 };
1512};
1513
1514extern ThingSpeakClass ThingSpeak;
1515
1516#endif //ThingSpeak_h
Note: See TracBrowser for help on using the repository browser.