I’m just starting out with the Spark Core and found myself needing an HTTP Client Library to interact with my REST API. Unfortunately the libraries I found all seemed to rely on the Arduino Ethernet Library which made them unsuitable for the Spark Core. So today I started working on my own library, which you can now find on Github.
Before this I’ve never touched C++ so the code is probably both badly architected and in poor style, but it’s a start and it will improve as I’m learning. Contributions are very welcome and any feedback is appreciated! Don’t hesitate to get in touch.
While adding support for custom HTTP headers today, I noticed that there is an extra character being sent along with the intended string when using the TCP Client. Either the TCP Client is adding an extra character to the beginning of strings being sent or (quite possibly) the reason for that character has something to do with C++ strings and might not have anything to do with the Spark firmware. I thought I’d share it here just in case, maybe the explanation is easy.
I send the HTTP request body like this:
Application.cpp:
request.body = "{\"key\":\"value\"}";
HttpClient.cpp
// Empty line to finish HTTP Headers
client.println();
client.flush();
// Send HTTP Request body.
client.println(aRequest->body);
And for some reason jsontest.com responds with an error, saying there is a character before the first curly brace in the string:
“error”: “A JSONObject text must begin with ‘{’ at 1 [character 2 line 1]”
I haven’t looked too deeply in the TCP Library, but since that is used for many things I’d assume it rather bug-free.
It’s not a big deal, any JSON parser will probably strip leading and trailing whitespace anyway, but I’m guessing it shouldn’t be there.
I wonder if it’s expecting a json content type header, or a \r\n mismatch or something? I would want to do a capture with something like WireShark and see what the extra character is, definitely. I probably won’t get a chance to test that today, but if I do I’ll post my results here.
<error xmlns:yahoo="http://www.yahooapis.com/v1/base.rng" yahoo:lang="en-US"><description>Invalid identfier c. me AND me.ip are the only supported identifier in this context</description></error>
So I tried replacing the ‘c’ with %27c%27 and that worked better.
Like @bko is saying, you need to split the URL between the host (“query.yahooapis.com”) and the path (“v1/public/yql”) — the former is set when you open the connection and the latter when you make a request.
It would be helpful if you posted your code and we can see if the problem is there or with the library!
You need to only have the path in the request, not the full URL (and not the HTTP marker). Also, '%20 ' in paths typically denotes spaces and are generally not to be recommended. Are you sure the request is formatted correctly?
EDIT: I tested it, and it actually works despite the strange formatting so I'm supposing they just designed their API that way. I got a pretty big JSON reply though, so you might also have to look at the TCP buffer size so that the response fits.
This library is coming along, and I think it’s getting ready for more widespread use. Most things have been changed since I originally posted it, and the current usage looks like this:
Example code is not working for me when using in WEB ide
I’ve pasted code of cpp and h files in web IDE
#include "HttpClient.h"
/**
* Declaring the variables.
*/
unsigned int nextTime = 0; // Next time to contact the server
HttpClient http;
// Headers currently need to be set at init, useful for API keys etc.
http_header_t headers[] = {
// { "Content-Type", "application/json" },
// { "Accept" , "application/json" },
{ "Accept" , "*/*"},
{ NULL, NULL } // NOTE: Always terminate headers will NULL
};
http_request_t request;
http_response_t response;
void setup() {
Serial.begin(9600);
Serial1.begin(57600);
Serial1.print("\n");
Spark.function("cmd", fnCmd);
}
int fnCmd(String command) {
Serial1.print(command + "\n");
return 1;
}
void loop() {
if (nextTime > millis()) {
return;
}
Serial.println();
Serial.println("Application>\tStart of Loop.");
// Request path and body can be set at runtime or at setup.
request.hostname = "192.168.1.12";
request.port = 9605;
request.path = "/cmd";
// The library also supports sending a body with your request:
//request.body = "{\"key\":\"value\"}";
// Get request
http.get(response, request, headers);
Serial.print("Application>\tResponse status: ");
Serial.println(response.status);
Serial.print("Application>\tHTTP Response Body: ");
Serial.println(response.body);
nextTime = millis() + 500;
}
Apparently you’ve updated code but not updated readme - responce and request should be switched places in http.get
Though still error: http://pastebin.com/9ywUSgmF
I’ve never tried using the web ide, and have no plans to, so no guarantees there. Maybe try putting your code in a local build environment, and see if you encounter the same problems?
In function `HttpClient::get(http_request_t&, http_response_t&, http_header_t*)':
N:\Spark\core-firmware\build/../src/HttpClient.h:93: undefined reference to `HttpClient::request(http_request_t&, http_response_t&, http_header_t*, char const*)'
obj/src/application.o: In function `__static_initialization_and_destruction_0':
N:\Spark\core-firmware\build/../src/application.cpp:8: undefined reference to `HttpClient::HttpClient()'
collect2.exe: error: ld returned 1 exit status
P.S. builded after adding cpp file to build.mk
Cant test if works as new firmware does not want to connect to Wifi
Do not return from connect code before logging that connect has failed
Add field IPAddress ip to Request struct
Use request.ip if request.hostname is unavailable
Sample usage:
// Request path and body can be set at runtime or at setup.
request.ip = {192,168,1,12};
request.port = 9605;
request.path = "/cmd";
// The library also supports sending a body with your request:
//request.body = "{\"key\":\"value\"}";
// Get request
http.get(request, response, headers);
Serial.print("Application>\tResponse status: ");
Serial.println(response.status);
Serial.print("Application>\tHTTP Response Body: ");
Serial.println(response.body);
@Dave could you take a look why this library produces 8RED errors when using in Web Ide?