├── LICENSE.md ├── README.md ├── RestClient.cpp ├── RestClient.h └── examples ├── full_test_suite └── full_test_suite.ino ├── simple_GET └── simple_GET.ino └── simple_POST └── simple_POST.ino /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) [2014] [Christopher Continanza] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RestClient for Arduino 2 | 3 | HTTP Request library for Arduino and the Ethernet shield. 4 | 5 | # Install 6 | 7 | Clone (or download and unzip) the repository to `~/Documents/Arduino/libraries` 8 | where `~/Documents/Arduino` is your sketchbook directory. 9 | 10 | > cd ~/Documents/Arduino 11 | > mkdir libraries 12 | > cd libraries 13 | > git clone https://github.com/csquared/arduino-restclient.git RestClient 14 | 15 | # Usage 16 | 17 | ### Include 18 | 19 | You need to have the `Ethernet` library already included. 20 | 21 | ```c++ 22 | #include 23 | #include 24 | #include "RestClient.h" 25 | ``` 26 | 27 | ### RestClient(host/ip, [port]) 28 | 29 | Constructor to create an RestClient object to make requests against. 30 | 31 | Use domain name and default to port 80: 32 | ```c++ 33 | RestClient client = RestClient("arduino-http-lib-test.herokuapp.com"); 34 | ``` 35 | 36 | Use a local IP and an explicit port: 37 | ```c++ 38 | RestClient client = RestClient("192.168.1.50",5000); 39 | ``` 40 | 41 | ### dhcp() 42 | 43 | Sets up `EthernetClient` with a mac address of `DEADBEEFFEED`. Returns `true` or `false` to indicate if setting up DHCP 44 | was successful or not 45 | 46 | ```c++ 47 | client.dhcp() 48 | ``` 49 | 50 | Note: you can have multiple RestClient objects but only need to call 51 | this once. 52 | 53 | Note: if you have multiple Arduinos on the same network, you'll need 54 | to give each one a different mac address. 55 | 56 | ### begin(byte mac[]) 57 | 58 | It just wraps the `EthernetClient` call to `begin` and DHCPs. 59 | Use this if you need to explicitly set the mac address. 60 | 61 | ```c++ 62 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 63 | if (client.begin(mac) == 0) { 64 | Serial.println("Failed to configure Ethernet using DHCP"); 65 | } 66 | ``` 67 | 68 | ### Manual Ethernet Setup 69 | 70 | You can skip the above methods and just configure the EthernetClient yourself: 71 | 72 | ```c++ 73 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 74 | //the IP address for the shield: 75 | byte ip[] = { 192, 168, 2, 11 }; 76 | Ethernet.begin(mac,ip); 77 | ``` 78 | 79 | ```c++ 80 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 81 | Ethernet.begin(mac); 82 | ``` 83 | 84 | This is especially useful for debugging network connection issues. 85 | 86 | ## RESTful methods 87 | 88 | All methods return an HTTP status code or 0 if there was an error. 89 | 90 | ### `get(const char* path)` 91 | ### `get(const char* path, String* response)` 92 | 93 | Start making requests! 94 | 95 | ```c++ 96 | int statusCode = client.get("/")); 97 | ``` 98 | 99 | Pass in a string *by reference* for the response: 100 | ``` 101 | String response = ""; 102 | int statusCode = client.get("/", &response); 103 | ``` 104 | 105 | ### post(const char* path, const char* body) 106 | ### post(const char* path, String* response) 107 | ### post(const char* path, const char* body, String* response) 108 | 109 | ``` 110 | String response = ""; 111 | int statusCode = client.post("/", &response); 112 | statusCode = client.post("/", "foo=bar"); 113 | response = ""; 114 | statusCode = client.post("/", "foo=bar", &response); 115 | ``` 116 | 117 | ### put(const char* path, const char* body) 118 | ### put(const char* path, String* response) 119 | ### put(const char* path, const char* body, String* response) 120 | 121 | ``` 122 | String response = ""; 123 | int statusCode = client.put("/", &response); 124 | statusCode = client.put("/", "foo=bar"); 125 | response = ""; 126 | statusCode = client.put("/", "foo=bar", &response); 127 | ``` 128 | 129 | ### del(const char* path) 130 | ### del(const char* path, const char* body) 131 | ### del(const char* path, String* response) 132 | ### del(const char* path, const char* body, String* response) 133 | 134 | ``` 135 | String response = ""; 136 | int statusCode = client.del("/", &response); 137 | ``` 138 | 139 | ## Full Example 140 | 141 | I test every way of calling the library (against a public heroku app)[https://github.com/csquared/arduino-http-test]. 142 | 143 | You can find the file in File->Examples->RestClient->full_test_suite 144 | 145 | ## Debug Mode 146 | 147 | If you're having trouble, you can always open `RestClient.cpp` and throw at the top: 148 | 149 | ```c++ 150 | #define HTTP_DEBUG 151 | ``` 152 | 153 | Everything happening in the client will get printed to the Serial port. 154 | 155 | # Thanks 156 | 157 | [ricardochimal](https://github.com/ricardochimal) For all his c++ help. Couldn't have done this without you! 158 | 159 | [theycallmeswift](https://github.com/theycallmeswift) Helping incept and debug v1.0 160 | -------------------------------------------------------------------------------- /RestClient.cpp: -------------------------------------------------------------------------------- 1 | #include "RestClient.h" 2 | 3 | #ifdef HTTP_DEBUG 4 | #define HTTP_DEBUG_PRINT(string) (Serial.print(string)) 5 | #endif 6 | 7 | #ifndef HTTP_DEBUG 8 | #define HTTP_DEBUG_PRINT(string) 9 | #endif 10 | 11 | RestClient::RestClient(const char* _host){ 12 | host = _host; 13 | port = 80; 14 | num_headers = 0; 15 | contentType = "application/x-www-form-urlencoded"; // default 16 | } 17 | 18 | RestClient::RestClient(const char* _host, int _port){ 19 | host = _host; 20 | port = _port; 21 | num_headers = 0; 22 | contentType = "application/x-www-form-urlencoded"; // default 23 | } 24 | 25 | bool RestClient::dhcp(){ 26 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 27 | if (begin(mac) == 0) { 28 | Serial.println("Failed to configure Ethernet using DHCP"); 29 | return false; 30 | } 31 | 32 | //give it time to initialize 33 | delay(1000); 34 | return true; 35 | } 36 | 37 | int RestClient::begin(byte mac[]){ 38 | return Ethernet.begin(mac); 39 | //give it time to initialize 40 | delay(1000); 41 | } 42 | 43 | // GET path 44 | int RestClient::get(const char* path){ 45 | return request("GET", path, NULL, NULL); 46 | } 47 | 48 | //GET path with response 49 | int RestClient::get(const char* path, String* response){ 50 | return request("GET", path, NULL, response); 51 | } 52 | 53 | // POST path and body 54 | int RestClient::post(const char* path, const char* body){ 55 | return request("POST", path, body, NULL); 56 | } 57 | 58 | // POST path and body with response 59 | int RestClient::post(const char* path, const char* body, String* response){ 60 | return request("POST", path, body, response); 61 | } 62 | 63 | // PUT path and body 64 | int RestClient::put(const char* path, const char* body){ 65 | return request("PUT", path, body, NULL); 66 | } 67 | 68 | // PUT path and body with response 69 | int RestClient::put(const char* path, const char* body, String* response){ 70 | return request("PUT", path, body, response); 71 | } 72 | 73 | // DELETE path 74 | int RestClient::del(const char* path){ 75 | return request("DELETE", path, NULL, NULL); 76 | } 77 | 78 | // DELETE path and response 79 | int RestClient::del(const char* path, String* response){ 80 | return request("DELETE", path, NULL, response); 81 | } 82 | 83 | // DELETE path and body 84 | int RestClient::del(const char* path, const char* body ){ 85 | return request("DELETE", path, body, NULL); 86 | } 87 | 88 | // DELETE path and body with response 89 | int RestClient::del(const char* path, const char* body, String* response){ 90 | return request("DELETE", path, body, response); 91 | } 92 | 93 | void RestClient::write(const char* string){ 94 | HTTP_DEBUG_PRINT(string); 95 | client.print(string); 96 | } 97 | 98 | void RestClient::setHeader(const char* header){ 99 | headers[num_headers] = header; 100 | num_headers++; 101 | } 102 | 103 | void RestClient::setContentType(const char* contentTypeValue){ 104 | contentType = contentTypeValue; 105 | } 106 | 107 | // The mother- generic request method. 108 | // 109 | int RestClient::request(const char* method, const char* path, 110 | const char* body, String* response){ 111 | 112 | HTTP_DEBUG_PRINT("HTTP: connect\n"); 113 | 114 | if(client.connect(host, port)){ 115 | HTTP_DEBUG_PRINT("HTTP: connected\n"); 116 | HTTP_DEBUG_PRINT("REQUEST: \n"); 117 | // Make a HTTP request line: 118 | write(method); 119 | write(" "); 120 | write(path); 121 | write(" HTTP/1.1\r\n"); 122 | for(int i=0; iconcat(c); 213 | } 214 | else 215 | { 216 | if (c == '\n' && currentLineIsBlank) { 217 | httpBody = true; 218 | } 219 | 220 | if (c == '\n') { 221 | // you're starting a new line 222 | currentLineIsBlank = true; 223 | } 224 | else if (c != '\r') { 225 | // you've gotten a character on the current line 226 | currentLineIsBlank = false; 227 | } 228 | } 229 | } 230 | } 231 | 232 | HTTP_DEBUG_PRINT("HTTP: return readResponse3\n"); 233 | return code; 234 | } 235 | -------------------------------------------------------------------------------- /RestClient.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | class RestClient { 6 | 7 | public: 8 | RestClient(const char* host); 9 | RestClient(const char* _host, int _port); 10 | 11 | //Client Setup 12 | bool dhcp(); 13 | int begin(byte*); 14 | 15 | //Generic HTTP Request 16 | int request(const char* method, const char* path, 17 | const char* body, String* response); 18 | // Set a Request Header 19 | void setHeader(const char*); 20 | // Set Content-Type Header 21 | void setContentType(const char*); 22 | 23 | // GET path 24 | int get(const char*); 25 | // GET path and response 26 | int get(const char*, String*); 27 | 28 | // POST path and body 29 | int post(const char* path, const char* body); 30 | // POST path and body and response 31 | int post(const char* path, const char* body, String*); 32 | 33 | // PUT path and body 34 | int put(const char* path, const char* body); 35 | // PUT path and body and response 36 | int put(const char* path, const char* body, String*); 37 | 38 | // DELETE path 39 | int del(const char*); 40 | // DELETE path and body 41 | int del(const char*, const char*); 42 | // DELETE path and response 43 | int del(const char*, String*); 44 | // DELETE path and body and response 45 | int del(const char*, const char*, String*); 46 | 47 | private: 48 | EthernetClient client; 49 | int readResponse(String*); 50 | void write(const char*); 51 | const char* host; 52 | int port; 53 | int num_headers; 54 | const char* headers[10]; 55 | const char* contentType; 56 | }; 57 | -------------------------------------------------------------------------------- /examples/full_test_suite/full_test_suite.ino: -------------------------------------------------------------------------------- 1 | /* RestClient full test suite 2 | * 3 | * Every REST method is called. 4 | * 5 | * by Chris Continanza (csquared) 6 | */ 7 | 8 | #include 9 | #include 10 | #include "RestClient.h" 11 | 12 | int test_delay = 1000; //so we don't spam the API 13 | boolean describe_tests = true; 14 | 15 | RestClient client = RestClient("arduino-http-lib-test.herokuapp.com"); 16 | //RestClient client = RestClient("10.0.1.47",5000); 17 | 18 | 19 | //Setup 20 | void setup() { 21 | Serial.begin(9600); 22 | // Connect via DHCP 23 | Serial.println("connect to network"); 24 | client.dhcp(); 25 | /* 26 | //Can still fall back to manual config: 27 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 28 | //the IP address for the shield: 29 | byte ip[] = { 192, 168, 2, 11 }; 30 | Ethernet.begin(mac,ip); 31 | */ 32 | Serial.println("Setup!"); 33 | } 34 | 35 | void test_status(int statusCode){ 36 | delay(test_delay); 37 | if(statusCode == 200){ 38 | Serial.print("TEST RESULT: ok ("); 39 | Serial.print(statusCode); 40 | Serial.println(")"); 41 | }else{ 42 | Serial.print("TEST RESULT: fail ("); 43 | Serial.print(statusCode); 44 | Serial.println(")"); 45 | } 46 | } 47 | 48 | String response; 49 | void test_response(){ 50 | //Serial.println(response); 51 | if(response == "OK"){ 52 | Serial.println("TEST RESULT: ok (response body)"); 53 | }else{ 54 | Serial.println("TEST RESULT: fail (response body = " + response + ")"); 55 | } 56 | response = ""; 57 | } 58 | 59 | void describe(char * description){ 60 | if(describe_tests) Serial.println(description); 61 | } 62 | 63 | //reusable test variables 64 | char* post_body = "POSTDATA"; 65 | 66 | void GET_tests(){ 67 | describe("Test GET with path"); 68 | test_status(client.get("/get")); 69 | 70 | describe("Test GET with path and response"); 71 | test_status(client.get("/get", &response)); 72 | test_response(); 73 | 74 | describe("Test GET with path and header"); 75 | client.setHeader("X-Test-Header: true"); 76 | test_status(client.get("/get-header")); 77 | 78 | describe("Test GET with path and header and response"); 79 | client.setHeader("X-Test-Header: true"); 80 | test_status(client.get("/get-header", &response)); 81 | test_response(); 82 | 83 | describe("Test GET with 2 headers and response"); 84 | client.setHeader("X-Test-Header1: one"); 85 | client.setHeader("X-Test-Header2: two"); 86 | test_status(client.get("/get-headers", &response)); 87 | test_response(); 88 | } 89 | 90 | void POST_tests(){ 91 | // POST TESTS 92 | describe("Test POST with path and body"); 93 | test_status(client.post("/data", post_body)); 94 | 95 | describe("Test POST with path and body and response"); 96 | test_status(client.post("/data", post_body, &response)); 97 | test_response(); 98 | 99 | describe("Test POST with path and body and header"); 100 | client.setHeader("X-Test-Header: true"); 101 | test_status(client.post("/data-header", post_body)); 102 | 103 | describe("Test POST with path and body and header and response"); 104 | client.setHeader("X-Test-Header: true"); 105 | test_status(client.post("/data-header", post_body, &response)); 106 | test_response(); 107 | 108 | describe("Test POST with 2 headers and response"); 109 | client.setHeader("X-Test-Header1: one"); 110 | client.setHeader("X-Test-Header2: two"); 111 | test_status(client.post("/data-headers", post_body, &response)); 112 | test_response(); 113 | } 114 | 115 | void PUT_tests(){ 116 | describe("Test PUT with path and body"); 117 | test_status(client.put("/data", post_body)); 118 | 119 | describe("Test PUT with path and body and response"); 120 | test_status(client.put("/data", post_body, &response)); 121 | test_response(); 122 | 123 | describe("Test PUT with path and body and header"); 124 | client.setHeader("X-Test-Header: true"); 125 | test_status(client.put("/data-header", post_body)); 126 | 127 | describe("Test PUT with path and body and header and response"); 128 | client.setHeader("X-Test-Header: true"); 129 | test_status(client.put("/data-header", post_body, &response)); 130 | test_response(); 131 | 132 | describe("Test PUT with 2 headers and response"); 133 | client.setHeader("X-Test-Header1: one"); 134 | client.setHeader("X-Test-Header2: two"); 135 | test_status(client.put("/data-headers", post_body, &response)); 136 | test_response(); 137 | } 138 | 139 | void DELETE_tests(){ 140 | describe("Test DELETE with path"); 141 | //note: requires a special endpoint 142 | test_status(client.del("/del")); 143 | 144 | describe("Test DELETE with path and body"); 145 | test_status(client.del("/data", post_body)); 146 | 147 | describe("Test DELETE with path and body and response"); 148 | test_status(client.del("/data", post_body, &response)); 149 | test_response(); 150 | 151 | describe("Test DELETE with path and body and header"); 152 | client.setHeader("X-Test-Header: true"); 153 | test_status(client.del("/data-header", post_body)); 154 | 155 | describe("Test DELETE with path and body and header and response"); 156 | client.setHeader("X-Test-Header: true"); 157 | test_status(client.del("/data-header", post_body, &response)); 158 | test_response(); 159 | 160 | describe("Test DELETE with 2 headers and response"); 161 | client.setHeader("X-Test-Header1: one"); 162 | client.setHeader("X-Test-Header2: two"); 163 | test_status(client.del("/data-headers", post_body, &response)); 164 | test_response(); 165 | } 166 | 167 | 168 | // Run the tests! 169 | void loop(){ 170 | GET_tests(); 171 | POST_tests(); 172 | PUT_tests(); 173 | DELETE_tests(); 174 | } 175 | -------------------------------------------------------------------------------- /examples/simple_GET/simple_GET.ino: -------------------------------------------------------------------------------- 1 | /* RestClient simple GET request 2 | * 3 | * by Chris Continanza (csquared) 4 | */ 5 | 6 | #include 7 | #include 8 | #include "RestClient.h" 9 | 10 | RestClient client = RestClient("arduino-http-lib-test.herokuapp.com"); 11 | 12 | //Setup 13 | void setup() { 14 | Serial.begin(9600); 15 | // Connect via DHCP 16 | Serial.println("connect to network"); 17 | client.dhcp(); 18 | /* 19 | // Can still fall back to manual config: 20 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 21 | //the IP address for the shield: 22 | byte ip[] = { 192, 168, 2, 11 }; 23 | Ethernet.begin(mac,ip); 24 | */ 25 | Serial.println("Setup!"); 26 | } 27 | 28 | String response; 29 | void loop(){ 30 | response = ""; 31 | int statusCode = client.get("/get", &response); 32 | Serial.print("Status code from server: "); 33 | Serial.println(statusCode); 34 | Serial.print("Response body from server: "); 35 | Serial.println(response); 36 | delay(1000); 37 | } 38 | -------------------------------------------------------------------------------- /examples/simple_POST/simple_POST.ino: -------------------------------------------------------------------------------- 1 | /* RestClient simple GET request 2 | * 3 | * by Chris Continanza (csquared) 4 | */ 5 | 6 | #include 7 | #include 8 | #include "RestClient.h" 9 | 10 | RestClient client = RestClient("arduino-http-lib-test.herokuapp.com"); 11 | 12 | //Setup 13 | void setup() { 14 | Serial.begin(9600); 15 | // Connect via DHCP 16 | Serial.println("connect to network"); 17 | client.dhcp(); 18 | /* 19 | // Can still fall back to manual config: 20 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 21 | //the IP address for the shield: 22 | byte ip[] = { 192, 168, 2, 11 }; 23 | Ethernet.begin(mac,ip); 24 | */ 25 | Serial.println("Setup!"); 26 | } 27 | 28 | String response; 29 | void loop(){ 30 | response = ""; 31 | int statusCode = client.post("/data", "POSTDATA", &response); 32 | Serial.print("Status code from server: "); 33 | Serial.println(statusCode); 34 | Serial.print("Response body from server: "); 35 | Serial.println(response); 36 | delay(1000); 37 | } 38 | --------------------------------------------------------------------------------