Skip to content

Commit 9379894

Browse files
committed
Add Arduino client support & ESP8266 client version.
1 parent 03da319 commit 9379894

File tree

5 files changed

+449
-15
lines changed

5 files changed

+449
-15
lines changed

Adafruit_IO_Arduino.cpp

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,9 @@ FeedData::FeedData(const FeedData& copy) {
3434
}
3535

3636
FeedData::FeedData(const char* value) {
37-
// Copy the provided string value as the feed data value. If the string
38-
// can't fit into the feed data's memory then keep the value in an invalid
39-
// state (all zeros).
37+
// Copy the provided string value as the feed data value.
4038
memset(_value, 0, sizeof(_value));
41-
if (strlen(value) <= FEEDDATA_LENGTH-1) {
42-
strncpy(_value, value, sizeof(_value)-1);
43-
}
39+
strncpy(_value, value, FEEDDATA_LENGTH-1);
4440
}
4541

4642
FeedData::FeedData(Stream& stream, uint16_t length, uint32_t timeoutMS) {
@@ -51,11 +47,13 @@ FeedData::FeedData(Stream& stream, uint16_t length, uint32_t timeoutMS) {
5147
memset(_value, 0, sizeof(_value));
5248
if (length > FEEDDATA_LENGTH-1) {
5349
// Not enough space to store the value.
50+
DEBUG_PRINTLN(F("Not enough space to store feed data!"));
5451
return;
5552
}
5653
stream.setTimeout(timeoutMS);
5754
if (stream.readBytes(_value, length) != length) {
5855
// Didn't find enough data, set the value as invalid.
56+
DEBUG_PRINTLN(F("Failed to find expected data!"));
5957
memset(_value, 0, sizeof(_value));
6058
}
6159
}
@@ -72,7 +70,13 @@ bool FeedData::uintValue(unsigned int* value) {
7270
// Attempt to convert the value to an unsigned integer. Returns true if it
7371
// succeeds, and false if it fails for some reason.
7472
char* endptr;
75-
*value = (unsigned int)strtoul(_value, &endptr, 10);
73+
#ifdef ESP8266
74+
// For some reason strtoul is not defined on the ESP8266 platform right now.
75+
// Just use a strtol function and hope for the best.
76+
*value = (unsigned int)strtol(_value, &endptr, 10);
77+
#else
78+
*value = (unsigned int)strtoul(_value, &endptr, 10);
79+
#endif
7680
return (*_value != 0 && *endptr == 0);
7781
}
7882

@@ -88,7 +92,13 @@ bool FeedData::ulongValue(unsigned long* value) {
8892
// Attempt to convert the value to an unsigned long. Returns true if it
8993
// succeeds, and false if it fails for some reason.
9094
char* endptr;
91-
*value = strtoul(_value, &endptr, 10);
95+
#ifdef ESP8266
96+
// For some reason strtoul is not defined on the ESP8266 platform right now.
97+
// Just use a strtol function and hope for the best.
98+
*value = (unsigned long)strtol(_value, &endptr, 10);
99+
#else
100+
*value = strtoul(_value, &endptr, 10);
101+
#endif
92102
return (*_value != 0 && *endptr == 0);
93103
}
94104

@@ -143,14 +153,26 @@ bool Adafruit_IO_Feed::send(float value) {
143153
// Convert float to string using scientific notation, then send the value
144154
// (being careful not to quote it).
145155
memset(_converted, 0, sizeof(_converted));
146-
dtostre(value, _converted, 10, 0);
156+
#if defined(ARDUINO_ARCH_AVR)
157+
// Use avrlibc dtostre function on AVR platforms.
158+
dtostre(value, _converted, 10, 0);
159+
#else
160+
// Otherwise fall back to snprintf on other platforms.
161+
snprintf(_converted, sizeof(_converted)-1, "%f", value);
162+
#endif
147163
return _adapter->send(_name, _converted, _key, false);
148164
}
149165

150166
bool Adafruit_IO_Feed::send(double value) {
151167
// Convert double to string using scientific notation, then send the value
152168
// (being careful not to quote it).
153169
memset(_converted, 0, sizeof(_converted));
154-
dtostre(value, _converted, 10, 0);
170+
#if defined(ARDUINO_ARCH_AVR)
171+
// Use avrlibc dtostre function on AVR platforms.
172+
dtostre(value, _converted, 10, 0);
173+
#else
174+
// Otherwise fall back to snprintf on other platforms.
175+
snprintf(_converted, sizeof(_converted)-1, "%f", value);
176+
#endif
155177
return _adapter->send(_name, _converted, _key, false);
156178
}

Adafruit_IO_Arduino.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434

3535
// Macros for debug output (only enabled when debug mode is enabled.)
3636
#ifdef ADAFRUIT_IO_DEBUG
37-
#define DEBUG_PRINT(t) { Serial.print(t); }
38-
#define DEBUG_PRINTLN(t) { Serial.println(t); }
37+
#define DEBUG_PRINT(...) { Serial.print(__VA_ARGS__); }
38+
#define DEBUG_PRINTLN(...) { Serial.println(__VA_ARGS__); }
3939
#else
40-
#define DEBUG_PRINT(t) {}
41-
#define DEBUG_PRINTLN(t) {}
40+
#define DEBUG_PRINT(...) {}
41+
#define DEBUG_PRINTLN(...) {}
4242
#endif
4343

4444

@@ -85,7 +85,7 @@ class AIOService {
8585
};
8686

8787

88-
// Main IO feed class that uses an AIO serviec reference to send and receive
88+
// Main IO feed class that uses an AIO service reference to send and receive
8989
// data with IO.
9090
class Adafruit_IO_Feed {
9191
public:

Adafruit_IO_Client.cpp

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// The MIT License (MIT)
2+
//
3+
// Copyright (c) 2015 Adafruit Industries
4+
// Author: Tony DiCola
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
#include "Adafruit_IO_Client.h"
24+
25+
bool Adafruit_IO_Client::send(const char* feed, const char* value,
26+
const char* key, bool quoted) {
27+
// Make HTTP POST to send feed data as JSON object.
28+
29+
// First make sure a connection to the service is available.
30+
if (!connected()) {
31+
DEBUG_PRINTLN(F("Failed to connect to service!"));
32+
return false;
33+
}
34+
35+
// Compute size of request.
36+
uint16_t len = 10 + strlen(value);
37+
if (quoted) {
38+
len += 2;
39+
}
40+
41+
// Send HTTP POST and headers.
42+
_client.print(F("POST /api/feeds/"));
43+
_client.print(feed);
44+
_client.print(F("/data/send HTTP/1.1\r\n"));
45+
sendHeaders(key);
46+
_client.print(F("Content-Type: application/json\r\n"));
47+
_client.print(F("Content-Length: "));
48+
_client.print(len, DEC);
49+
_client.print(F("\r\n\r\n"));
50+
51+
// Send HTTP POST data.
52+
_client.print(F("{\"value\":"));
53+
if (quoted) {
54+
_client.print('"');
55+
_client.print(value);
56+
_client.print('"');
57+
}
58+
else {
59+
_client.print(value);
60+
}
61+
_client.print('}');
62+
63+
// Serial.println("WRITTEN");
64+
// char c = 0;
65+
// int timeout=5000;
66+
// while (timeout > 0) {
67+
// if (!_client.connected()) {
68+
// Serial.println("DISCONNECTED!");
69+
// return false;
70+
// }
71+
// while (_client.available()) {
72+
// char c = _client.read();
73+
// Serial.print(c);
74+
// }
75+
// timeout -= 10;
76+
// delay(10);
77+
// }
78+
// Serial.println("DONE!");
79+
// _client.stop();
80+
// return false;
81+
82+
// Now wait to read response (up to the client's configured stream timeout).
83+
// First read the HTTP/1.1 response.
84+
char recvbuffer[IO_CLIENT_RECV_SIZE] = {0};
85+
if ((_client.readBytesUntil(' ', recvbuffer, sizeof(recvbuffer)) != 8) ||
86+
(strcmp_P(recvbuffer, PSTR("HTTP/1.1")) != 0)) {
87+
DEBUG_PRINTLN(F("Failed to find expected HTTP/1.1 response!"));
88+
_client.stop();
89+
return false;
90+
}
91+
// Now read the status code and expect a 200-level response.
92+
memset(recvbuffer, 0, sizeof(recvbuffer));
93+
if ((_client.readBytesUntil(' ', recvbuffer, sizeof(recvbuffer)-1) != 3) ||
94+
(recvbuffer[0] != '2')) {
95+
DEBUG_PRINT(F("HTTP POST failed with error code: "));
96+
DEBUG_PRINTLN(recvbuffer);
97+
_client.stop();
98+
return false;
99+
}
100+
101+
// Ignore parsing the response data for now. Close connection and return
102+
// success.
103+
_client.stop();
104+
return true;
105+
}
106+
107+
FeedData Adafruit_IO_Client::receive(const char* feed, const char* key) {
108+
// Make HTTP GET request to read latest feed item and then parse response
109+
// into FeedData object.
110+
111+
// First make sure a connection to the service is available.
112+
if (!connected()) {
113+
DEBUG_PRINTLN(F("Failed to connect to service!"));
114+
return FeedData();
115+
}
116+
117+
// Send HTTP GET and headers.
118+
_client.print(F("GET /api/feeds/"));
119+
_client.print(feed);
120+
_client.print(F("/data/last.txt HTTP/1.1\r\n"));
121+
sendHeaders(key);
122+
_client.print(F("Accept: text/plain\r\n\r\n"));
123+
124+
// Parse HTTP GET response.
125+
// First read the HTTP/1.1 response.
126+
char recvbuffer[IO_CLIENT_RECV_SIZE] = {0};
127+
if ((_client.readBytesUntil(' ', recvbuffer, sizeof(recvbuffer)) != 8) ||
128+
(strcmp_P(recvbuffer, PSTR("HTTP/1.1")) != 0)) {
129+
DEBUG_PRINTLN(F("Failed to find expected HTTP/1.1 response!"));
130+
_client.stop();
131+
return FeedData();
132+
}
133+
// Now read the status code and expect a 200-level response.
134+
memset(recvbuffer, 0, sizeof(recvbuffer));
135+
if ((_client.readBytesUntil(' ', recvbuffer, sizeof(recvbuffer)-1) != 3) ||
136+
(recvbuffer[0] != '2')) {
137+
DEBUG_PRINT(F("HTTP GET failed with error code: "));
138+
DEBUG_PRINTLN(recvbuffer);
139+
_client.stop();
140+
return FeedData();
141+
}
142+
// Read the rest of the line.
143+
if (!_client.find("\r\n")) {
144+
DEBUG_PRINT(F("Unexpected HTTP GET response!"));
145+
_client.stop();
146+
return FeedData();
147+
}
148+
// Now parse all the header lines and look for an explicit content length.
149+
// If no content length is found then assume a chunked transfer encoding.
150+
uint16_t len = 0;
151+
while (true) {
152+
char c = _client.peek();
153+
// Check for \r\n blank line to signify end of headers.
154+
if (c == '\r') {
155+
if (!_client.find("\r\n")) {
156+
// Something unexpected, fail.
157+
DEBUG_PRINT(F("Expected blank line after headers!"));
158+
_client.stop();
159+
return FeedData();
160+
}
161+
// Else found the end of the headers so stop parsing them.
162+
break;
163+
}
164+
// Parse a header name.
165+
char recvbuffer[IO_CLIENT_RECV_SIZE] = {0};
166+
if (!_client.readBytesUntil(':', recvbuffer, sizeof(recvbuffer)-1)) {
167+
DEBUG_PRINT(F("Expected header name!"));
168+
_client.stop();
169+
return FeedData();
170+
}
171+
// Check for content-length header and read its value, otherwise just
172+
// swallow the header value.
173+
if (strcmp_P(recvbuffer, PSTR("Content-Length")) == 0) {
174+
len = (uint16_t)_client.parseInt();
175+
}
176+
if (!_client.find("\r\n")) {
177+
DEBUG_PRINT(F("Failed to find end of header line!"));
178+
_client.stop();
179+
return FeedData();
180+
}
181+
}
182+
// If we didn't see a content-length header then assume a chunked transfer
183+
// encoding and read the length as the first line.
184+
if (len == 0) {
185+
len = (uint16_t)_client.parseInt();
186+
if (!_client.find("\r\n")) {
187+
DEBUG_PRINT(F("Failed to find end of chunk size line!"));
188+
_client.stop();
189+
return FeedData();
190+
}
191+
}
192+
193+
// Let FeedData parse out the result.
194+
return FeedData(_client, len);
195+
}
196+
197+
bool Adafruit_IO_Client::connected() {
198+
// Create connection to AIO service. Return true if successful and connection
199+
// is open, or false if something failed.
200+
201+
// If connection is already open close it to start fresh.
202+
if (_client.connected()) {
203+
_client.stop();
204+
}
205+
206+
// Create connection.
207+
return _client.connect(_serviceHost, _servicePort) != 0;
208+
}
209+
210+
void Adafruit_IO_Client::sendHeaders(const char* key) {
211+
// Send standard HTTP headers used by AIO REST requests.
212+
_client.print(F("Connection: close\r\n"));
213+
_client.print(F("User-Agent: Adafruit_IO_Client\r\n"));
214+
_client.print(F("Host: "));
215+
_client.print(_serviceHost);
216+
_client.print(':');
217+
_client.print(_servicePort, DEC);
218+
_client.print(F("\r\n"));
219+
_client.print(F("X-AIO-Key: "));
220+
_client.print(key);
221+
_client.print(F("\r\n"));
222+
}

0 commit comments

Comments
 (0)