-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
994 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
# RestClient for Arduino | ||
|
||
HTTP Request library for Arduino and the Ethernet shield. | ||
|
||
# Install | ||
|
||
Clone (or download and unzip) the repository to `~/Documents/Arduino/libraries` | ||
where `~/Documents/Arduino` is your sketchbook directory. | ||
|
||
> cd ~/Documents/Arduino | ||
> mkdir libraries | ||
> cd libraries | ||
> git clone https://github.com/csquared/arduino-restclient.git RestClient | ||
|
||
# Usage | ||
|
||
### Include | ||
|
||
You need to have the `Ethernet` library already included. | ||
|
||
```c++ | ||
#include <Ethernet.h> | ||
#include <SPI.h> | ||
#include "RestClient.h" | ||
``` | ||
|
||
### RestClient(host/ip, [port]) | ||
|
||
Constructor to create an RestClient object to make requests against. | ||
|
||
Use domain name and default to port 80: | ||
```c++ | ||
RestClient client = RestClient("arduino-http-lib-test.herokuapp.com"); | ||
``` | ||
|
||
Use a local IP and an explicit port: | ||
```c++ | ||
RestClient client = RestClient("192.168.1.50",5000); | ||
``` | ||
|
||
### dhcp() | ||
|
||
Sets up `EthernetClient` with a mac address of `DEADBEEFFEED` | ||
|
||
```c++ | ||
client.dhcp() | ||
``` | ||
|
||
Note: you can have multiple RestClient objects but only need to call | ||
this once. | ||
|
||
Note: if you have multiple Arduinos on the same network, you'll need | ||
to give each one a different mac address. | ||
|
||
### begin(byte mac[]) | ||
|
||
It just wraps the `EthernetClient` call to `begin` and DHCPs. | ||
Use this if you need to explicitly set the mac address. | ||
|
||
```c++ | ||
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; | ||
if (client.begin(mac) == 0) { | ||
Serial.println("Failed to configure Ethernet using DHCP"); | ||
} | ||
``` | ||
### Manual Ethernet Setup | ||
You can skip the above methods and just configure the EthernetClient yourself: | ||
```c++ | ||
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; | ||
//the IP address for the shield: | ||
byte ip[] = { 192, 168, 2, 11 }; | ||
Ethernet.begin(mac,ip); | ||
``` | ||
|
||
```c++ | ||
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; | ||
Ethernet.begin(mac); | ||
``` | ||
This is especially useful for debugging network connection issues. | ||
## RESTful methods | ||
All methods return an HTTP status code or 0 if there was an error. | ||
### `get(const char* path)` | ||
### `get(const char* path, String* response)` | ||
Start making requests! | ||
```c++ | ||
int statusCode = client.get("/")); | ||
``` | ||
|
||
Pass in a string *by reference* for the response: | ||
``` | ||
String response = ""; | ||
int statusCode = client.get("/", &response); | ||
``` | ||
|
||
### post(const char* path, const char* body) | ||
### post(const char* path, String* response) | ||
### post(const char* path, const char* body, String* response) | ||
|
||
``` | ||
String response = ""; | ||
int statusCode = client.post("/", &response); | ||
statusCode = client.post("/", "foo=bar"); | ||
response = ""; | ||
statusCode = client.post("/", "foo=bar", &response); | ||
``` | ||
|
||
### put(const char* path, const char* body) | ||
### put(const char* path, String* response) | ||
### put(const char* path, const char* body, String* response) | ||
|
||
``` | ||
String response = ""; | ||
int statusCode = client.put("/", &response); | ||
statusCode = client.put("/", "foo=bar"); | ||
response = ""; | ||
statusCode = client.put("/", "foo=bar", &response); | ||
``` | ||
|
||
### del(const char* path) | ||
### del(const char* path, const char* body) | ||
### del(const char* path, String* response) | ||
### del(const char* path, const char* body, String* response) | ||
|
||
``` | ||
String response = ""; | ||
int statusCode = client.del("/", &response); | ||
``` | ||
|
||
## Full Example | ||
|
||
I test every way of calling the library (against a public heroku app)[https://github.com/csquared/arduino-http-test]. | ||
|
||
You can find the file in File->Examples->RestClient->full_test_suite | ||
|
||
## Debug Mode | ||
|
||
If you're having trouble, you can always open `RestClient.cpp` and throw at the top: | ||
|
||
```c++ | ||
#define HTTP_DEBUG | ||
``` | ||
|
||
Everything happening in the client will get printed to the Serial port. | ||
|
||
# Thanks | ||
|
||
[ricardochimal](https://github.com/ricardochimal) For all his c++ help. Couldn't have done this without you! | ||
|
||
[theycallmeswift](https://github.com/theycallmeswift) Helping incept and debug v1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
#include "RestClient.h" | ||
|
||
#ifdef HTTP_DEBUG | ||
#define HTTP_DEBUG_PRINT(string) (Serial.print(string)) | ||
#endif | ||
|
||
#ifndef HTTP_DEBUG | ||
#define HTTP_DEBUG_PRINT(string) | ||
#endif | ||
|
||
RestClient::RestClient(const char* _host){ | ||
host = _host; | ||
port = 80; | ||
num_headers = 0; | ||
contentTypeSet = false; | ||
} | ||
|
||
RestClient::RestClient(const char* _host, int _port){ | ||
host = _host; | ||
port = _port; | ||
num_headers = 0; | ||
contentTypeSet = false; | ||
} | ||
|
||
RestClient::RestClient(const char* _host, int _port, const char* _contentType){ | ||
host = _host; | ||
port = _port; | ||
num_headers = 0; | ||
contentTypeSet = true; | ||
contentType = _contentType; | ||
} | ||
|
||
void RestClient::dhcp(){ | ||
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; | ||
if (begin(mac) == 0) { | ||
Serial.println("Failed to configure Ethernet using DHCP"); | ||
} | ||
//give it time to initialize | ||
delay(1000); | ||
} | ||
|
||
int RestClient::begin(byte mac[]){ | ||
return Ethernet.begin(mac); | ||
//give it time to initialize | ||
delay(1000); | ||
} | ||
|
||
// GET path | ||
int RestClient::get(const char* path){ | ||
return request("GET", path, NULL, NULL); | ||
} | ||
|
||
//GET path with response | ||
int RestClient::get(const char* path, String* response){ | ||
return request("GET", path, NULL, response); | ||
} | ||
|
||
// POST path and body | ||
int RestClient::post(const char* path, const char* body){ | ||
return request("POST", path, body, NULL); | ||
} | ||
|
||
// POST path and body with response | ||
int RestClient::post(const char* path, const char* body, String* response){ | ||
return request("POST", path, body, response); | ||
} | ||
|
||
// PUT path and body | ||
int RestClient::put(const char* path, const char* body){ | ||
return request("PUT", path, body, NULL); | ||
} | ||
|
||
// PUT path and body with response | ||
int RestClient::put(const char* path, const char* body, String* response){ | ||
return request("PUT", path, body, response); | ||
} | ||
|
||
// DELETE path | ||
int RestClient::del(const char* path){ | ||
return request("DELETE", path, NULL, NULL); | ||
} | ||
|
||
// DELETE path and response | ||
int RestClient::del(const char* path, String* response){ | ||
return request("DELETE", path, NULL, response); | ||
} | ||
|
||
// DELETE path and body | ||
int RestClient::del(const char* path, const char* body ){ | ||
return request("DELETE", path, body, NULL); | ||
} | ||
|
||
// DELETE path and body with response | ||
int RestClient::del(const char* path, const char* body, String* response){ | ||
return request("DELETE", path, body, response); | ||
} | ||
|
||
void RestClient::write(const char* string){ | ||
HTTP_DEBUG_PRINT(string); | ||
client.print(string); | ||
} | ||
|
||
void RestClient::setHeader(const char* header){ | ||
headers[num_headers] = header; | ||
num_headers++; | ||
} | ||
|
||
// The mother- generic request method. | ||
// | ||
int RestClient::request(const char* method, const char* path, | ||
const char* body, String* response){ | ||
|
||
HTTP_DEBUG_PRINT("HTTP: connect\n"); | ||
|
||
if(client.connect(host, port)){ | ||
HTTP_DEBUG_PRINT("HTTP: connected\n"); | ||
HTTP_DEBUG_PRINT("REQUEST: \n"); | ||
// Make a HTTP request line: | ||
write(method); | ||
write(" "); | ||
write(path); | ||
write(" HTTP/1.1\r\n"); | ||
for(int i=0; i<num_headers; i++){ | ||
write(headers[i]); | ||
write("\r\n"); | ||
} | ||
write("Host: "); | ||
write(host); | ||
write("\r\n"); | ||
write("Connection: keep-alive\r\n"); | ||
|
||
if(body != NULL){ | ||
char contentLength[30]; | ||
sprintf(contentLength, "Content-Length: %d\r\n", strlen(body)); | ||
write(contentLength); | ||
|
||
if(!contentTypeSet){ | ||
write("Content-Type: application/json\r\n"); | ||
} | ||
else{ | ||
char contentTypestr[50]; | ||
sprintf(contentTypestr, "Content-Type: %s\r\n", contentType); | ||
write(contentTypestr); | ||
} | ||
} | ||
|
||
write("\r\n"); | ||
|
||
if(body != NULL){ | ||
write(body); | ||
write("\r\n"); | ||
write("\r\n"); | ||
} | ||
|
||
//make sure you write all those bytes. | ||
delay(100); | ||
|
||
HTTP_DEBUG_PRINT("HTTP: call readResponse\n"); | ||
int statusCode = readResponse(response); | ||
HTTP_DEBUG_PRINT("HTTP: return readResponse\n"); | ||
|
||
//cleanup | ||
HTTP_DEBUG_PRINT("HTTP: stop client\n"); | ||
num_headers = 0; | ||
client.stop(); | ||
delay(50); | ||
HTTP_DEBUG_PRINT("HTTP: client stopped\n"); | ||
|
||
return statusCode; | ||
}else{ | ||
HTTP_DEBUG_PRINT("HTTP Connection failed\n"); | ||
return 0; | ||
} | ||
} | ||
|
||
int RestClient::readResponse(String* response) { | ||
|
||
// an http request ends with a blank line | ||
boolean currentLineIsBlank = true; | ||
boolean httpBody = false; | ||
boolean inStatus = false; | ||
|
||
char statusCode[4]; | ||
int i = 0; | ||
int code = 0; | ||
|
||
if(response == NULL){ | ||
HTTP_DEBUG_PRINT("HTTP: NULL RESPONSE POINTER: \n"); | ||
}else{ | ||
HTTP_DEBUG_PRINT("HTTP: NON-NULL RESPONSE POINTER: \n"); | ||
} | ||
|
||
HTTP_DEBUG_PRINT("HTTP: RESPONSE: \n"); | ||
while (client.connected()) { | ||
HTTP_DEBUG_PRINT("."); | ||
if (client.available()) { | ||
HTTP_DEBUG_PRINT(","); | ||
|
||
char c = client.read(); | ||
HTTP_DEBUG_PRINT(c); | ||
|
||
if(c == ' ' && !inStatus){ | ||
inStatus = true; | ||
} | ||
|
||
if(inStatus && i < 3 && c != ' '){ | ||
statusCode[i] = c; | ||
i++; | ||
} | ||
if(i == 3){ | ||
statusCode[i] = '\0'; | ||
code = atoi(statusCode); | ||
} | ||
|
||
//only write response if its not null | ||
if(httpBody){ | ||
if(response != NULL) response->concat(c); | ||
} | ||
if (c == '\n' && httpBody){ | ||
HTTP_DEBUG_PRINT("HTTP: return readResponse2\n"); | ||
return code; | ||
} | ||
if (c == '\n' && currentLineIsBlank) { | ||
httpBody = true; | ||
} | ||
if (c == '\n') { | ||
// you're starting a new lineu | ||
currentLineIsBlank = true; | ||
} | ||
else if (c != '\r') { | ||
// you've gotten a character on the current line | ||
currentLineIsBlank = false; | ||
} | ||
} | ||
} | ||
|
||
HTTP_DEBUG_PRINT("HTTP: return readResponse3\n"); | ||
return code; | ||
} |
Oops, something went wrong.