├── keywords.txt ├── library.properties ├── README.md ├── examples └── BasicWebServer │ └── BasicWebServer.ino └── src └── EasyWebServer.h /keywords.txt: -------------------------------------------------------------------------------- 1 | EasyWebServer KEYWORD1 2 | serveUrl KEYWORD2 3 | redirect KEYWORD2 4 | EWS_TYPE_HTML KEYWORD2 5 | EWS_TYPE_TEXT KEYWORD2 6 | EWS_TYPE_JSON KEYWORD2 7 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=EasyWebServer 2 | version=1.0 3 | author=Kalle Lundberg k@llelundberg.se 4 | maintainer=Kalle Lundberg k@llelundberg.se 5 | sentence=An easy-to-use web server 6 | paragraph=A web server that validates the request and supports multiple pages. 7 | category=Communication 8 | url=https://github.com/llelundberg/EasyWebServer 9 | architectures=avr 10 | includes=EasyWebServer.h 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EasyWebServer 2 | An easy-to-use web server for Arduino. 3 | 4 | The purpose of this web server is not to be the most powerful, but to be very easy to use. 5 | 6 | Have you tried the default web server example of the Ethernet library? And whant to add more 7 | than one URL? Then this is you next stop! 8 | 9 | This server will parse the HTTP request and deliver different response based on the URL given 10 | by the client. 11 | 12 | It will also do some very basic validation of the incoming reguest and respond with a 13 | 404 Not Found message if the URL was not implemented. 14 | 15 | See http://techblog.se/2016/09/22/easywebserver/ for a quick taste. 16 | 17 | -------------------------------------------------------------------------------- /examples/BasicWebServer/BasicWebServer.ino: -------------------------------------------------------------------------------- 1 | 2 | // For W5100 based ethernet modules 3 | #include 4 | #include 5 | 6 | // For ENC28J60 based ethernet modules 7 | //#include 8 | //#include 9 | //#include 10 | 11 | #include // Must be included AFTER the ethernet libraries. See comment in EasyWebServer.h. 12 | 13 | // Enter a MAC address and IP address for your controller below. 14 | // The IP address will be dependent on your local network: 15 | byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; 16 | IPAddress ip(192, 168, 1, 177); 17 | 18 | // Initialize the Ethernet server library 19 | // with the IP address and port you want to use 20 | // (port 80 is default for HTTP): 21 | EthernetServer server(80); 22 | 23 | void setup() { 24 | // Open serial communications and wait for port to open: 25 | Serial.begin(9600); 26 | while (!Serial) { ; } // wait for serial port to connect. Needed for native USB port only 27 | 28 | // Start the Ethernet connection and the server: 29 | Ethernet.begin(mac, ip); 30 | server.begin(); 31 | Serial.print("Server is at "); 32 | Serial.println(Ethernet.localIP()); 33 | 34 | // Set pin 2-8 as digital input pins. 35 | for (int digitalPin = 2; digitalPin < 8; digitalPin++) 36 | pinMode(digitalPin,INPUT); 37 | } 38 | 39 | void loop() { 40 | // Listen for incoming clients 41 | EthernetClient client = server.available(); 42 | if (client) { 43 | Serial.println("New client!"); 44 | EasyWebServer w(client); // Read and parse the HTTP Request 45 | w.serveUrl("/",rootPage); // Root page 46 | w.serveUrl("/analog",analogSensorPage); // Analog sensor page 47 | w.serveUrl("/digital",digitalSensorPage); // Digital sensor page 48 | } 49 | } 50 | 51 | void rootPage(EasyWebServer &w){ 52 | w.client.println(F("")); 53 | w.client.println(F("EasyWebServer")); 54 | w.client.println(F("

Welcome to my little web server.

")); 55 | w.client.println(F("

Click here to see the analog sensors

")); 56 | w.client.println(F("

Click here to see the digital sensors

")); 57 | w.client.println(F("")); 58 | } 59 | 60 | void analogSensorPage(EasyWebServer &w){ 61 | w.client.println(F("")); 62 | w.client.println(F("Analog Sensors")); 63 | for (int analogChannel = 0; analogChannel < 6; analogChannel++) { 64 | int sensorReading = analogRead(analogChannel); // Note that analogReaad uses CHANNELS insead of pin. 65 | w.client.print(F("analog input ")); 66 | w.client.print(analogChannel); 67 | w.client.print(F(" is ")); 68 | w.client.print(sensorReading); 69 | w.client.println(F("
")); 70 | } 71 | w.client.println(F("

Home")); 72 | } 73 | 74 | void digitalSensorPage(EasyWebServer &w){ 75 | w.client.println(F("")); 76 | w.client.println(F("Digital Sensors")); 77 | for (int digitalPin = 2; digitalPin < 8; digitalPin++) { 78 | int sensorReading = digitalRead(digitalPin); 79 | w.client.print(F("digital pin ")); 80 | w.client.print(digitalPin); 81 | w.client.print(F(" is ")); 82 | w.client.print(sensorReading); 83 | w.client.println(F("
")); 84 | } 85 | w.client.println(F("

Home")); 86 | } 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/EasyWebServer.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | EasyWebServer - A simple lightweight web server for arduino. 4 | 5 | k@llelundberg.se - http://techblog.se/ 6 | 7 | This web server works on top of the EthernetClient class, defined in Ethernet.h. 8 | Since there are Other network libraries (e.g. UIPEthernet.h) the headers can not 9 | be included from this file. 10 | 11 | Thats why this file contains both declaration and implementarion. You need include 12 | EasyWebServer.h AFTER the Ethernet library in your source code. 13 | 14 | */ 15 | #ifndef EasyWebServer_h 16 | #define EasyWebServer_h 17 | 18 | #define EWS_REQUEST_BUF_LEN 25 // Adjust this to support longer URLs. 19 | 20 | class EasyWebServer; // Forward declaration, needed for typedef below. 21 | 22 | typedef void (*EwsRequestHandler)(EasyWebServer &w); 23 | typedef enum {EWS_TYPE_HTML, EWS_TYPE_TEXT, EWS_TYPE_JSON} EwsContentType; 24 | 25 | class EasyWebServer { 26 | public: 27 | const char *verb = NULL; // A string that will contain the VERB (GET, POST) 28 | const char *url = NULL; // The URL of the request, without any querystring. 29 | const char *querystring = NULL; // The qyerystring, everything behind the questionmark. 30 | EthernetClient client; // A reference to the EthernetClient object to work with. 31 | 32 | EasyWebServer(EthernetClient &client); 33 | ~EasyWebServer(); 34 | 35 | void serveUrl(const char* url, EwsRequestHandler func, EwsContentType contentType); 36 | void redirect(const char* url, const char* newurl); 37 | 38 | private: 39 | char _request[EWS_REQUEST_BUF_LEN]; // A buffer to store the HTTP request. 40 | void disconnect(); // Close the client connection. 41 | void throwError(const __FlashStringHelper *error); // HTTP error response. 42 | }; 43 | 44 | 45 | EasyWebServer::EasyWebServer(EthernetClient &ec):client(ec){ 46 | 47 | /* READ THE REQUEST */ 48 | bool firstChar = true; // Keep track of line beginnings to see the empty line marking end of header. 49 | int i = 0; // Number of characters read. 50 | char c = '\0'; 51 | while (client.connected()) { // While the TCP session is connected. 52 | if (client.available()) { // Client data available to read? 53 | 54 | char c = client.read(); // Read 1 byte (character) from client 55 | if (i < EWS_REQUEST_BUF_LEN) 56 | _request[i] = c; // Add the char to the global request buffer 57 | 58 | i++; // Increase the byte counter. 59 | 60 | if (c == '\n') { // If this char is a end-of-line... 61 | if (firstChar) break; // If it was the first char in a line, the line is empty. Break out. 62 | firstChar = true; // Note that the next char will be the first in a line. 63 | } 64 | 65 | if (c != '\n' && c != '\r') // If the char is not a line ending character... 66 | firstChar = false; // ...the next char is not going to be the first char of a line. 67 | 68 | } 69 | } 70 | 71 | /* PARSE THE REQUEST */ 72 | int rlen = min(i, EWS_REQUEST_BUF_LEN - 1); // Calculate the request length 73 | _request[rlen]='\0'; // At least one string terminator in the buffer. 74 | verb = url = querystring = &_request[rlen]; // Reset all the strings to empty string. 75 | 76 | char *spc1=NULL, *spc2=NULL, *q=NULL; // Initiate some marker variables. 77 | spc1 = strchr(_request,' '); // Search for the space that separates the verb and the URL. 78 | if(spc1==NULL){ 79 | throwError(F("400 Bad Request")); 80 | return; 81 | } 82 | verb = _request; 83 | spc1[0]='\0'; // Terminate the verb string 84 | 85 | spc2 = strchr(spc1+1,' '); // Search for the space that after the URL 86 | if(spc2==NULL){ 87 | throwError(F("414 Request-URI Too Long")); 88 | return; 89 | } 90 | url = spc1 + 1; 91 | spc2[0] = '\0'; // Terminate the URL string. 92 | 93 | q = strchr(spc1+1,'?'); // Search for a question mark inside the URL 94 | if(q!=NULL){ 95 | q[0]='\0'; 96 | querystring = q + 1; 97 | } 98 | 99 | // Reject all HTTP verbs except GET 100 | if (strcmp(verb, "GET")!=0) { 101 | throwError(F("405 Method Not Allowed")); 102 | disconnect(); 103 | return; 104 | } 105 | } 106 | 107 | void EasyWebServer::serveUrl(const char* url, EwsRequestHandler func, const EwsContentType responseContentType=EWS_TYPE_HTML){ 108 | 109 | 110 | if(client){ 111 | /*Serial.print("serveUrl"); 112 | Serial.println(url);*/ 113 | 114 | // Compare the url parameter with the url in the http request. 115 | if(strcmp(this->url,url)==0){ 116 | 117 | // Write the response header 118 | client.println(F("HTTP/1.1 200 OK")); // Normal HTTP response. 119 | client.println(F("Connection: close")); // Indicates to the client that the TCP connections will be closed and not reused. 120 | if(responseContentType==EWS_TYPE_JSON) 121 | client.println(F("Content-Type: application/json; charset=utf-8"));// Content type is set to JSON 122 | else if(responseContentType==EWS_TYPE_TEXT) 123 | client.println(F("Content-Type: text/plain; charset=utf-8")); // Content type is set to plain text 124 | else 125 | client.println(F("Content-Type: text/html; charset=utf-8")); // Content type is set to HTML (default) 126 | 127 | client.println(); // Empty line to indicate the end of the header. 128 | 129 | func(*this); // Call the specified function and pass the current object as parameter. 130 | 131 | disconnect(); // Disconnect the TCP connection 132 | 133 | } 134 | } 135 | } 136 | 137 | void EasyWebServer::redirect(const char* url, const char* newurl){ 138 | if(client){ 139 | 140 | // Compare the url parameter with the url in the http request. 141 | if(strcmp(this->url,url)==0){ 142 | client.println(F("HTTP/1.1 301 Moved Permanently")); 143 | client.println(F("Connection: close")); 144 | client.print(F("Location: ")); 145 | client.println(newurl); 146 | client.println(); 147 | disconnect(); 148 | } 149 | } 150 | } 151 | 152 | EasyWebServer::~EasyWebServer(){ 153 | if(client){ 154 | throwError(F("404 Not Found")); // If no URL has been served, throw a 404. 155 | } 156 | } 157 | 158 | void EasyWebServer::disconnect(){ 159 | client.flush(); // Flush the streams, make sure that the response has been delivered to the network. 160 | delay(1); // Delay a little bit, shouldnt be needed if the flush() works. 161 | client.stop(); // Close the TCP Connection. 162 | } 163 | 164 | void EasyWebServer::throwError(const __FlashStringHelper* error){ 165 | client.print(F("HTTP/1.1 ")); 166 | client.println(error); 167 | client.println(F("Connection: close")); 168 | client.println(F("Content-Type: text/html")); 169 | client.println(); 170 | client.print(F("")); 171 | client.print(error); 172 | client.print(F("")); 173 | client.print(error); 174 | client.print(F("")); 175 | disconnect(); 176 | } 177 | 178 | #endif 179 | --------------------------------------------------------------------------------