diff options
author | Mike Beattie <mike@ethernal.org> | 2012-03-20 23:25:34 +1300 |
---|---|---|
committer | Mike Beattie <mike@ethernal.org> | 2012-03-20 23:25:34 +1300 |
commit | 25a6bca31c63840261141d764bbd70331a268f19 (patch) | |
tree | 98d195184d0d033aab6905585a5a23ba26e456f2 /Webduino | |
parent | cb7bd533cb2b505441ca9a35c9897db358a30b47 (diff) |
Add Webduino library
Signed-off-by: Mike Beattie <mike@ethernal.org>
Diffstat (limited to 'Webduino')
-rw-r--r-- | Webduino/WebServer.h | 1170 | ||||
-rw-r--r-- | Webduino/examples/Web_AjaxBuzzer/Web_AjaxBuzzer.ino | 126 | ||||
-rw-r--r-- | Webduino/examples/Web_AjaxRGB/Web_AjaxRGB.ino | 142 | ||||
-rw-r--r-- | Webduino/examples/Web_AjaxRGB_mobile/Web_AjaxRGB_mobile.ino | 140 | ||||
-rw-r--r-- | Webduino/examples/Web_Authentication/Web_Authentication.ino | 106 | ||||
-rw-r--r-- | Webduino/examples/Web_Buzzer/Web_Buzzer.ino | 123 | ||||
-rw-r--r-- | Webduino/examples/Web_Demo/Web_Demo.ino | 189 | ||||
-rw-r--r-- | Webduino/examples/Web_HelloWorld/Web_HelloWorld.ino | 71 | ||||
-rw-r--r-- | Webduino/examples/Web_Image/Web_Image.ino | 117 | ||||
-rw-r--r-- | Webduino/examples/Web_Image/led.png | bin | 0 -> 253 bytes | |||
-rw-r--r-- | Webduino/examples/Web_Parms/Web_Parms.ino | 287 | ||||
-rw-r--r-- | Webduino/keywords.txt | 34 | ||||
-rw-r--r-- | Webduino/readme.md | 103 |
13 files changed, 2608 insertions, 0 deletions
diff --git a/Webduino/WebServer.h b/Webduino/WebServer.h new file mode 100644 index 0000000..a034b03 --- /dev/null +++ b/Webduino/WebServer.h @@ -0,0 +1,1170 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-file-style: "k&r"; c-basic-offset: 2; -*- + + Webduino, a simple Arduino web server + Copyright 2009-2012 Ben Combee, Ran Talbott, Christopher Lee, Martin Lormes + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef WEBDUINO_H_ +#define WEBDUINO_H_ + +#include <string.h> +#include <stdlib.h> + +#include <EthernetClient.h> +#include <EthernetServer.h> + +/******************************************************************** + * CONFIGURATION + ********************************************************************/ + +#define WEBDUINO_VERSION 1007 +#define WEBDUINO_VERSION_STRING "1.7" + +#if WEBDUINO_SUPRESS_SERVER_HEADER +#define WEBDUINO_SERVER_HEADER "" +#else +#define WEBDUINO_SERVER_HEADER "Server: Webduino/" WEBDUINO_VERSION_STRING CRLF +#endif + +// standard END-OF-LINE marker in HTTP +#define CRLF "\r\n" + +// If processConnection is called without a buffer, it allocates one +// of 32 bytes +#define WEBDUINO_DEFAULT_REQUEST_LENGTH 32 + +// How long to wait before considering a connection as dead when +// reading the HTTP request. Used to avoid DOS attacks. +#ifndef WEBDUINO_READ_TIMEOUT_IN_MS +#define WEBDUINO_READ_TIMEOUT_IN_MS 1000 +#endif + +#ifndef WEBDUINO_FAIL_MESSAGE +#define WEBDUINO_FAIL_MESSAGE "<h1>EPIC FAIL</h1>" +#endif + +#ifndef WEBDUINO_AUTH_REALM +#define WEBDUINO_AUTH_REALM "Webduino" +#endif // #ifndef WEBDUINO_AUTH_REALM + +#ifndef WEBDUINO_AUTH_MESSAGE +#define WEBDUINO_AUTH_MESSAGE "<h1>401 Unauthorized</h1>" +#endif // #ifndef WEBDUINO_AUTH_MESSAGE + +#ifndef WEBDUINO_SERVER_ERROR_MESSAGE +#define WEBDUINO_SERVER_ERROR_MESSAGE "<h1>500 Internal Server Error</h1>" +#endif // WEBDUINO_SERVER_ERROR_MESSAGE + +// add '#define WEBDUINO_FAVICON_DATA ""' to your application +// before including WebServer.h to send a null file as the favicon.ico file +// otherwise this defaults to a 16x16 px black diode on blue ground +// (or include your own icon if you like) +#ifndef WEBDUINO_FAVICON_DATA +#define WEBDUINO_FAVICON_DATA { 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, \ + 0x10, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, \ + 0xb0, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, \ + 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, \ + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, \ + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, \ + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, \ + 0x00, 0xff, 0xff, 0x00, 0x00, 0xcf, 0xbf, \ + 0x00, 0x00, 0xc7, 0xbf, 0x00, 0x00, 0xc3, \ + 0xbf, 0x00, 0x00, 0xc1, 0xbf, 0x00, 0x00, \ + 0xc0, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0xc0, 0xbf, 0x00, 0x00, 0xc1, 0xbf, \ + 0x00, 0x00, 0xc3, 0xbf, 0x00, 0x00, 0xc7, \ + 0xbf, 0x00, 0x00, 0xcf, 0xbf, 0x00, 0x00, \ + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00 } +#endif // #ifndef WEBDUINO_FAVICON_DATA + +// add "#define WEBDUINO_SERIAL_DEBUGGING 1" to your application +// before including WebServer.h to have incoming requests logged to +// the serial port. +#ifndef WEBDUINO_SERIAL_DEBUGGING +#define WEBDUINO_SERIAL_DEBUGGING 0 +#endif +#if WEBDUINO_SERIAL_DEBUGGING +#include <HardwareSerial.h> +#endif + +// declared in wiring.h +extern "C" unsigned long millis(void); + +// declare a static string +#define P(name) static const prog_uchar name[] PROGMEM + +// returns the number of elements in the array +#define SIZE(array) (sizeof(array) / sizeof(*array)) + +/******************************************************************** + * DECLARATIONS + ********************************************************************/ + +/* Return codes from nextURLparam. NOTE: URLPARAM_EOS is returned + * when you call nextURLparam AFTER the last parameter is read. The + * last actual parameter gets an "OK" return code. */ + +typedef enum URLPARAM_RESULT { URLPARAM_OK, + URLPARAM_NAME_OFLO, + URLPARAM_VALUE_OFLO, + URLPARAM_BOTH_OFLO, + URLPARAM_EOS // No params left +}; + +class WebServer: public Print +{ +public: + // passed to a command to indicate what kind of request was received + enum ConnectionType { INVALID, GET, HEAD, POST, PUT, DELETE, PATCH }; + + // any commands registered with the web server have to follow + // this prototype. + // url_tail contains the part of the URL that wasn't matched against + // the registered command table. + // tail_complete is true if the complete URL fit in url_tail, false if + // part of it was lost because the buffer was too small. + typedef void Command(WebServer &server, ConnectionType type, + char *url_tail, bool tail_complete); + + // constructor for webserver object + WebServer(const char *urlPrefix = "", int port = 80); + + // start listening for connections + void begin(); + + // check for an incoming connection, and if it exists, process it + // by reading its request and calling the appropriate command + // handler. This version is for compatibility with apps written for + // version 1.1, and allocates the URL "tail" buffer internally. + void processConnection(); + + // check for an incoming connection, and if it exists, process it + // by reading its request and calling the appropriate command + // handler. This version saves the "tail" of the URL in buff. + void processConnection(char *buff, int *bufflen); + + // set command that's run when you access the root of the server + void setDefaultCommand(Command *cmd); + + // set command run for undefined pages + void setFailureCommand(Command *cmd); + + // add a new command to be run at the URL specified by verb + void addCommand(const char *verb, Command *cmd); + + // utility function to output CRLF pair + void printCRLF(); + + // output a string stored in program memory, usually one defined + // with the P macro + void printP(const prog_uchar *str); + + // inline overload for printP to handle signed char strings + void printP(const prog_char *str) { printP((prog_uchar*)str); } + + // output raw data stored in program memory + void writeP(const prog_uchar *data, size_t length); + + // output HTML for a radio button + void radioButton(const char *name, const char *val, + const char *label, bool selected); + + // output HTML for a checkbox + void checkBox(const char *name, const char *val, + const char *label, bool selected); + + // returns next character or -1 if we're at end-of-stream + int read(); + + // put a character that's been read back into the input pool + void push(int ch); + + // returns true if the string is next in the stream. Doesn't + // consume any character if false, so can be used to try out + // different expected values. + bool expect(const char *expectedStr); + + // returns true if a number, with possible whitespace in front, was + // read from the server stream. number will be set with the new + // value or 0 if nothing was read. + bool readInt(int &number); + + // reads a header value, stripped of possible whitespace in front, + // from the server stream + void readHeader(char *value, int valueLen); + + // Read the next keyword parameter from the socket. Assumes that other + // code has already skipped over the headers, and the next thing to + // be read will be the start of a keyword. + // + // returns true if we're not at end-of-stream + bool readPOSTparam(char *name, int nameLen, char *value, int valueLen); + + // Read the next keyword parameter from the buffer filled by getRequest. + // + // returns 0 if everything weent okay, non-zero if not + // (see the typedef for codes) + URLPARAM_RESULT nextURLparam(char **tail, char *name, int nameLen, + char *value, int valueLen); + + // compare string against credentials in current request + // + // authCredentials must be Base64 encoded outside of Webduino + // (I wanted to be easy on the resources) + // + // returns true if strings match, false otherwise + bool checkCredentials(const char authCredentials[45]); + + // output headers and a message indicating a server error + void httpFail(); + + // output headers and a message indicating "401 Unauthorized" + void httpUnauthorized(); + + // output headers and a message indicating "500 Internal Server Error" + void httpServerError(); + + // output standard headers indicating "200 Success". You can change the + // type of the data you're outputting or also add extra headers like + // "Refresh: 1". Extra headers should each be terminated with CRLF. + void httpSuccess(const char *contentType = "text/html; charset=utf-8", + const char *extraHeaders = NULL); + + // used with POST to output a redirect to another URL. This is + // preferable to outputting HTML from a post because you can then + // refresh the page without getting a "resubmit form" dialog. + void httpSeeOther(const char *otherURL); + + // implementation of write used to implement Print interface + virtual size_t write(uint8_t); + virtual size_t write(const char *str); + virtual size_t write(const uint8_t *buffer, size_t size); + size_t write(const char *data, size_t length); + +private: + EthernetServer m_server; + EthernetClient m_client; + const char *m_urlPrefix; + + unsigned char m_pushback[32]; + char m_pushbackDepth; + + int m_contentLength; + char m_authCredentials[51]; + bool m_readingContent; + + Command *m_failureCmd; + Command *m_defaultCmd; + struct CommandMap + { + const char *verb; + Command *cmd; + } m_commands[8]; + char m_cmdCount; + + void reset(); + void getRequest(WebServer::ConnectionType &type, char *request, int *length); + bool dispatchCommand(ConnectionType requestType, char *verb, + bool tail_complete); + void processHeaders(); + void outputCheckboxOrRadio(const char *element, const char *name, + const char *val, const char *label, + bool selected); + + static void defaultFailCmd(WebServer &server, ConnectionType type, + char *url_tail, bool tail_complete); + void noRobots(ConnectionType type); + void favicon(ConnectionType type); +}; + +/* define this macro if you want to include the header in a sketch source + file but not define any of the implementation. This is useful if + multiple source files are using the Webduino class. */ +#ifndef WEBDUINO_NO_IMPLEMENTATION + +/******************************************************************** + * IMPLEMENTATION + ********************************************************************/ + +WebServer::WebServer(const char *urlPrefix, int port) : + m_server(port), + m_client(255), + m_urlPrefix(urlPrefix), + m_pushbackDepth(0), + m_cmdCount(0), + m_contentLength(0), + m_failureCmd(&defaultFailCmd), + m_defaultCmd(&defaultFailCmd) +{ +} + +void WebServer::begin() +{ + m_server.begin(); +} + +void WebServer::setDefaultCommand(Command *cmd) +{ + m_defaultCmd = cmd; +} + +void WebServer::setFailureCommand(Command *cmd) +{ + m_failureCmd = cmd; +} + +void WebServer::addCommand(const char *verb, Command *cmd) +{ + if (m_cmdCount < SIZE(m_commands)) + { + m_commands[m_cmdCount].verb = verb; + m_commands[m_cmdCount++].cmd = cmd; + } +} + +size_t WebServer::write(uint8_t ch) +{ + return m_client.write(ch); +} + +size_t WebServer::write(const char *str) +{ + return m_client.write(str); +} + +size_t WebServer::write(const uint8_t *buffer, size_t size) +{ + return m_client.write(buffer, size); +} + +size_t WebServer::write(const char *buffer, size_t length) +{ + return m_client.write((const uint8_t *)buffer, length); +} + +void WebServer::writeP(const prog_uchar *data, size_t length) +{ + // copy data out of program memory into local storage, write out in + // chunks of 32 bytes to avoid extra short TCP/IP packets + uint8_t buffer[32]; + size_t bufferEnd = 0; + + while (length--) + { + if (bufferEnd == 32) + { + m_client.write(buffer, 32); + bufferEnd = 0; + } + + buffer[bufferEnd++] = pgm_read_byte(data++); + } + + if (bufferEnd > 0) + m_client.write(buffer, bufferEnd); +} + +void WebServer::printP(const prog_uchar *str) +{ + // copy data out of program memory into local storage, write out in + // chunks of 32 bytes to avoid extra short TCP/IP packets + uint8_t buffer[32]; + size_t bufferEnd = 0; + + while (buffer[bufferEnd++] = pgm_read_byte(str++)) + { + if (bufferEnd == 32) + { + m_client.write(buffer, 32); + bufferEnd = 0; + } + } + + // write out everything left but trailing NUL + if (bufferEnd > 1) + m_client.write(buffer, bufferEnd - 1); +} + +void WebServer::printCRLF() +{ + m_client.write((const uint8_t *)"\r\n", 2); +} + +bool WebServer::dispatchCommand(ConnectionType requestType, char *verb, + bool tail_complete) +{ + // if there is no URL, i.e. we have a prefix and it's requested without a + // trailing slash or if the URL is just the slash + if ((verb[0] == 0) || ((verb[0] == '/') && (verb[1] == 0))) + { + m_defaultCmd(*this, requestType, "", tail_complete); + return true; + } + // if the URL is just a slash followed by a question mark + // we're looking at the default command with GET parameters passed + if ((verb[0] == '/') && (verb[1] == '?')) + { + verb+=2; // skip over the "/?" part of the url + m_defaultCmd(*this, requestType, verb, tail_complete); + return true; + } + // We now know that the URL contains at least one character. And, + // if the first character is a slash, there's more after it. + if (verb[0] == '/') + { + char i; + char *qm_loc; + int verb_len; + int qm_offset; + // Skip over the leading "/", because it makes the code more + // efficient and easier to understand. + verb++; + // Look for a "?" separating the filename part of the URL from the + // parameters. If it's not there, compare to the whole URL. + qm_loc = strchr(verb, '?'); + verb_len = (qm_loc == NULL) ? strlen(verb) : (qm_loc - verb); + qm_offset = (qm_loc == NULL) ? 0 : 1; + for (i = 0; i < m_cmdCount; ++i) + { + if ((verb_len == strlen(m_commands[i].verb)) + && (strncmp(verb, m_commands[i].verb, verb_len) == 0)) + { + // Skip over the "verb" part of the URL (and the question + // mark, if present) when passing it to the "action" routine + m_commands[i].cmd(*this, requestType, + verb + verb_len + qm_offset, + tail_complete); + return true; + } + } + } + return false; +} + +// processConnection with a default buffer +void WebServer::processConnection() +{ + char request[WEBDUINO_DEFAULT_REQUEST_LENGTH]; + int request_len = WEBDUINO_DEFAULT_REQUEST_LENGTH; + processConnection(request, &request_len); +} + +void WebServer::processConnection(char *buff, int *bufflen) +{ + int urlPrefixLen = strlen(m_urlPrefix); + + m_client = m_server.available(); + + if (m_client) { + m_readingContent = false; + buff[0] = 0; + ConnectionType requestType = INVALID; +#if WEBDUINO_SERIAL_DEBUGGING > 1 + Serial.println("*** checking request ***"); +#endif + getRequest(requestType, buff, bufflen); +#if WEBDUINO_SERIAL_DEBUGGING > 1 + Serial.print("*** requestType = "); + Serial.print((int)requestType); + Serial.print(", request = \""); + Serial.print(buff); + Serial.println("\" ***"); +#endif + + // don't even look further at invalid requests. + // this is done to prevent Webduino from hanging + // - when there are illegal requests, + // - when someone contacts it through telnet rather than proper HTTP, + // - etc. + if (requestType != INVALID) + { + processHeaders(); +#if WEBDUINO_SERIAL_DEBUGGING > 1 + Serial.println("*** headers complete ***"); +#endif + + if (strcmp(buff, "/robots.txt") == 0) + { + noRobots(requestType); + } + else if (strcmp(buff, "/favicon.ico") == 0) + { + favicon(requestType); + } + } + if (requestType == INVALID || + strncmp(buff, m_urlPrefix, urlPrefixLen) != 0 || + !dispatchCommand(requestType, buff + urlPrefixLen, + (*bufflen) >= 0)) + { + m_failureCmd(*this, requestType, buff, (*bufflen) >= 0); + } + +#if WEBDUINO_SERIAL_DEBUGGING > 1 + Serial.println("*** stopping connection ***"); +#endif + reset(); + } +} + +bool WebServer::checkCredentials(const char authCredentials[45]) +{ + char basic[7] = "Basic "; + if((0 == strncmp(m_authCredentials,basic,6)) && + (0 == strcmp(authCredentials, m_authCredentials + 6))) return true; + return false; +} + +void WebServer::httpFail() +{ + P(failMsg) = + "HTTP/1.0 400 Bad Request" CRLF + WEBDUINO_SERVER_HEADER + "Content-Type: text/html" CRLF + CRLF + WEBDUINO_FAIL_MESSAGE; + + printP(failMsg); +} + +void WebServer::defaultFailCmd(WebServer &server, + WebServer::ConnectionType type, + char *url_tail, + bool tail_complete) +{ + server.httpFail(); +} + +void WebServer::noRobots(ConnectionType type) +{ + httpSuccess("text/plain"); + if (type != HEAD) + { + P(allowNoneMsg) = "User-agent: *" CRLF "Disallow: /" CRLF; + printP(allowNoneMsg); + } +} + +void WebServer::favicon(ConnectionType type) +{ + httpSuccess("image/x-icon","Cache-Control: max-age=31536000\r\n"); + if (type != HEAD) + { + P(faviconIco) = WEBDUINO_FAVICON_DATA; + writeP(faviconIco, sizeof(faviconIco)); + } +} + +void WebServer::httpUnauthorized() +{ + P(failMsg) = + "HTTP/1.0 401 Authorization Required" CRLF + WEBDUINO_SERVER_HEADER + "Content-Type: text/html" CRLF + "WWW-Authenticate: Basic realm=\"" WEBDUINO_AUTH_REALM "\"" CRLF + CRLF + WEBDUINO_AUTH_MESSAGE; + + printP(failMsg); +} + +void WebServer::httpServerError() +{ + P(failMsg) = + "HTTP/1.0 500 Internal Server Error" CRLF + WEBDUINO_SERVER_HEADER + "Content-Type: text/html" CRLF + CRLF + WEBDUINO_SERVER_ERROR_MESSAGE; + + printP(failMsg); +} + +void WebServer::httpSuccess(const char *contentType, + const char *extraHeaders) +{ + P(successMsg1) = + "HTTP/1.0 200 OK" CRLF + WEBDUINO_SERVER_HEADER + "Access-Control-Allow-Origin: *" CRLF + "Content-Type: "; + + printP(successMsg1); + print(contentType); + printCRLF(); + if (extraHeaders) + print(extraHeaders); + printCRLF(); +} + +void WebServer::httpSeeOther(const char *otherURL) +{ + P(seeOtherMsg) = + "HTTP/1.0 303 See Other" CRLF + WEBDUINO_SERVER_HEADER + "Location: "; + + printP(seeOtherMsg); + print(otherURL); + printCRLF(); + printCRLF(); +} + +int WebServer::read() +{ + if (m_client == NULL) + return -1; + + if (m_pushbackDepth == 0) + { + unsigned long timeoutTime = millis() + WEBDUINO_READ_TIMEOUT_IN_MS; + + while (m_client.connected()) + { + // stop reading the socket early if we get to content-length + // characters in the POST. This is because some clients leave + // the socket open because they assume HTTP keep-alive. + if (m_readingContent) + { + if (m_contentLength == 0) + { +#if WEBDUINO_SERIAL_DEBUGGING > 1 + Serial.println("\n*** End of content, terminating connection"); +#endif + return -1; + } + --m_contentLength; + } + + int ch = m_client.read(); + + // if we get a character, return it, otherwise continue in while + // loop, checking connection status + if (ch != -1) + { +#if WEBDUINO_SERIAL_DEBUGGING + if (ch == '\r') + Serial.print("<CR>"); + else if (ch == '\n') + Serial.println("<LF>"); + else + Serial.print((char)ch); +#endif + return ch; + } + else + { + unsigned long now = millis(); + if (now > timeoutTime) + { + // connection timed out, destroy client, return EOF +#if WEBDUINO_SERIAL_DEBUGGING + Serial.println("*** Connection timed out"); +#endif + reset(); + return -1; + } + } + } + + // connection lost, return EOF +#if WEBDUINO_SERIAL_DEBUGGING + Serial.println("*** Connection lost"); +#endif + return -1; + } + else + return m_pushback[--m_pushbackDepth]; +} + +void WebServer::push(int ch) +{ + // don't allow pushing EOF + if (ch == -1) + return; + + m_pushback[m_pushbackDepth++] = ch; + // can't raise error here, so just replace last char over and over + if (m_pushbackDepth == SIZE(m_pushback)) + m_pushbackDepth = SIZE(m_pushback) - 1; +} + +void WebServer::reset() +{ + m_pushbackDepth = 0; + m_client.flush(); + m_client.stop(); +} + +bool WebServer::expect(const char *str) +{ + const char *curr = str; + while (*curr != 0) + { + int ch = read(); + if (ch != *curr++) + { + // push back ch and the characters we accepted + push(ch); + while (--curr != str) + push(curr[-1]); + return false; + } + } + return true; +} + +bool WebServer::readInt(int &number) +{ + bool negate = false; + bool gotNumber = false; + int ch; + number = 0; + + // absorb whitespace + do + { + ch = read(); + } while (ch == ' ' || ch == '\t'); + + // check for leading minus sign + if (ch == '-') + { + negate = true; + ch = read(); + } + + // read digits to update number, exit when we find non-digit + while (ch >= '0' && ch <= '9') + { + gotNumber = true; + number = number * 10 + ch - '0'; + ch = read(); + } + + push(ch); + if (negate) + number = -number; + return gotNumber; +} + +void WebServer::readHeader(char *value, int valueLen) +{ + int ch; + memset(value, 0, valueLen); + --valueLen; + + // absorb whitespace + do + { + ch = read(); + } while (ch == ' ' || ch == '\t'); + + // read rest of line + do + { + if (valueLen > 1) + { + *value++=ch; + --valueLen; + ch = read(); + } + } while (ch != '\r'); + push(ch); +} + +bool WebServer::readPOSTparam(char *name, int nameLen, + char *value, int valueLen) +{ + // assume name is at current place in stream + int ch; + // to not to miss the last parameter + bool foundSomething = false; + + // clear out name and value so they'll be NUL terminated + memset(name, 0, nameLen); + memset(value, 0, valueLen); + + // decrement length so we don't write into NUL terminator + --nameLen; + --valueLen; + + while ((ch = read()) != -1) + { + foundSomething = true; + if (ch == '+') + { + ch = ' '; + } + else if (ch == '=') + { + /* that's end of name, so switch to storing in value */ + nameLen = 0; + continue; + } + else if (ch == '&') + { + /* that's end of pair, go away */ + return true; + } + else if (ch == '%') + { + /* handle URL encoded characters by converting back to original form */ + int ch1 = read(); + int ch2 = read(); + if (ch1 == -1 || ch2 == -1) + return false; + char hex[3] = { ch1, ch2, 0 }; + ch = strtoul(hex, NULL, 16); + } + + // output the new character into the appropriate buffer or drop it if + // there's no room in either one. This code will malfunction in the + // case where the parameter name is too long to fit into the name buffer, + // but in that case, it will just overflow into the value buffer so + // there's no harm. + if (nameLen > 0) + { + *name++ = ch; + --nameLen; + } + else if (valueLen > 0) + { + *value++ = ch; + --valueLen; + } + } + + if (foundSomething) + { + // if we get here, we have one last parameter to serve + return true; + } + else + { + // if we get here, we hit the end-of-file, so POST is over and there + // are no more parameters + return false; + } +} + +/* Retrieve a parameter that was encoded as part of the URL, stored in + * the buffer pointed to by *tail. tail is updated to point just past + * the last character read from the buffer. */ +URLPARAM_RESULT WebServer::nextURLparam(char **tail, char *name, int nameLen, + char *value, int valueLen) +{ + // assume name is at current place in stream + char ch, hex[3]; + URLPARAM_RESULT result = URLPARAM_OK; + char *s = *tail; + bool keep_scanning = true; + bool need_value = true; + + // clear out name and value so they'll be NUL terminated + memset(name, 0, nameLen); + memset(value, 0, valueLen); + + if (*s == 0) + return URLPARAM_EOS; + // Read the keyword name + while (keep_scanning) + { + ch = *s++; + switch (ch) + { + case 0: + s--; // Back up to point to terminating NUL + // Fall through to "stop the scan" code + case '&': + /* that's end of pair, go away */ + keep_scanning = false; + need_value = false; + break; + case '+': + ch = ' '; + break; + case '%': + /* handle URL encoded characters by converting back + * to original form */ + if ((hex[0] = *s++) == 0) + { + s--; // Back up to NUL + keep_scanning = false; + need_value = false; + } + else + { + if ((hex[1] = *s++) == 0) + { + s--; // Back up to NUL + keep_scanning = false; + need_value = false; + } + else + { + hex[2] = 0; + ch = strtoul(hex, NULL, 16); + } + } + break; + case '=': + /* that's end of name, so switch to storing in value */ + keep_scanning = false; + break; + } + + + // check against 1 so we don't overwrite the final NUL + if (keep_scanning && (nameLen > 1)) + { + *name++ = ch; + --nameLen; + } + else + result = URLPARAM_NAME_OFLO; + } + + if (need_value && (*s != 0)) + { + keep_scanning = true; + while (keep_scanning) + { + ch = *s++; + switch (ch) + { + case 0: + s--; // Back up to point to terminating NUL + // Fall through to "stop the scan" code + case '&': + /* that's end of pair, go away */ + keep_scanning = false; + need_value = false; + break; + case '+': + ch = ' '; + break; + case '%': + /* handle URL encoded characters by converting back to original form */ + if ((hex[0] = *s++) == 0) + { + s--; // Back up to NUL + keep_scanning = false; + need_value = false; + } + else + { + if ((hex[1] = *s++) == 0) + { + s--; // Back up to NUL + keep_scanning = false; + need_value = false; + } + else + { + hex[2] = 0; + ch = strtoul(hex, NULL, 16); + } + + } + break; + } + + + // check against 1 so we don't overwrite the final NUL + if (keep_scanning && (valueLen > 1)) + { + *value++ = ch; + --valueLen; + } + else + result = (result == URLPARAM_OK) ? + URLPARAM_VALUE_OFLO : + URLPARAM_BOTH_OFLO; + } + } + *tail = s; + return result; +} + + + +// Read and parse the first line of the request header. +// The "command" (GET/HEAD/POST) is translated into a numeric value in type. +// The URL is stored in request, up to the length passed in length +// NOTE 1: length must include one byte for the terminating NUL. +// NOTE 2: request is NOT checked for NULL, nor length for a value < 1. +// Reading stops when the code encounters a space, CR, or LF. If the HTTP +// version was supplied by the client, it will still be waiting in the input +// stream when we exit. +// +// On return, length contains the amount of space left in request. If it's +// less than 0, the URL was longer than the buffer, and part of it had to +// be discarded. + +void WebServer::getRequest(WebServer::ConnectionType &type, + char *request, int *length) +{ + --*length; // save room for NUL + + type = INVALID; + + // store the HTTP method line of the request + if (expect("GET ")) + type = GET; + else if (expect("HEAD ")) + type = HEAD; + else if (expect("POST ")) + type = POST; + else if (expect("PUT ")) + type = PUT; + else if (expect("DELETE ")) + type = DELETE; + else if (expect("PATCH ")) + type = PATCH; + + // if it doesn't start with any of those, we have an unknown method + // so just get out of here + else + return; + + int ch; + while ((ch = read()) != -1) + { + // stop storing at first space or end of line + if (ch == ' ' || ch == '\n' || ch == '\r') + { + break; + } + if (*length > 0) + { + *request = ch; + ++request; + } + --*length; + } + // NUL terminate + *request = 0; +} + +void WebServer::processHeaders() +{ + // look for three things: the Content-Length header, the Authorization + // header, and the double-CRLF that ends the headers. + + // empty the m_authCredentials before every run of this function. + // otherwise users who don't send an Authorization header would be treated + // like the last user who tried to authenticate (possibly successful) + m_authCredentials[0]=0; + + while (1) + { + if (expect("Content-Length:")) + { + readInt(m_contentLength); +#if WEBDUINO_SERIAL_DEBUGGING > 1 + Serial.print("\n*** got Content-Length of "); + Serial.print(m_contentLength); + Serial.print(" ***"); +#endif + continue; + } + + if (expect("Authorization:")) + { + readHeader(m_authCredentials,51); +#if WEBDUINO_SERIAL_DEBUGGING > 1 + Serial.print("\n*** got Authorization: of "); + Serial.print(m_authCredentials); + Serial.print(" ***"); +#endif + continue; + } + + if (expect(CRLF CRLF)) + { + m_readingContent = true; + return; + } + + // no expect checks hit, so just absorb a character and try again + if (read() == -1) + { + return; + } + } +} + +void WebServer::outputCheckboxOrRadio(const char *element, const char *name, + const char *val, const char *label, + bool selected) +{ + P(cbPart1a) = "<label><input type='"; + P(cbPart1b) = "' name='"; + P(cbPart2) = "' value='"; + P(cbPart3) = "' "; + P(cbChecked) = "checked "; + P(cbPart4) = "/> "; + P(cbPart5) = "</label>"; + + printP(cbPart1a); + print(element); + printP(cbPart1b); + print(name); + printP(cbPart2); + print(val); + printP(cbPart3); + if (selected) + printP(cbChecked); + printP(cbPart4); + print(label); + printP(cbPart5); +} + +void WebServer::checkBox(const char *name, const char *val, + const char *label, bool selected) +{ + outputCheckboxOrRadio("checkbox", name, val, label, selected); +} + +void WebServer::radioButton(const char *name, const char *val, + const char *label, bool selected) +{ + outputCheckboxOrRadio("radio", name, val, label, selected); +} + +#endif // WEBDUINO_NO_IMPLEMENTATION + +#endif // WEBDUINO_H_ diff --git a/Webduino/examples/Web_AjaxBuzzer/Web_AjaxBuzzer.ino b/Webduino/examples/Web_AjaxBuzzer/Web_AjaxBuzzer.ino new file mode 100644 index 0000000..e546030 --- /dev/null +++ b/Webduino/examples/Web_AjaxBuzzer/Web_AjaxBuzzer.ino @@ -0,0 +1,126 @@ +/* Web_Buzzer.pde - example sketch for Webduino library */ + +#include "SPI.h" +#include "Ethernet.h" +#include "WebServer.h" + +// CHANGE THIS TO YOUR OWN UNIQUE VALUE +static uint8_t mac[6] = { 0x02, 0xAA, 0xBB, 0xCC, 0x00, 0x22 }; + +// CHANGE THIS TO MATCH YOUR HOST NETWORK +static uint8_t ip[4] = { 192, 168, 1, 210 }; // area 51! + +/* all URLs on this server will start with /buzz because of how we + * define the PREFIX value. We also will listen on port 80, the + * standard HTTP service port */ +#define PREFIX "/buzz" +WebServer webserver(PREFIX, 80); + +/* the piezo speaker on the Danger Shield is on PWM output pin #3 */ +#define BUZZER_PIN 3 + +/* this is the number of microseconds to wait after turning the + * speaker on before turning it off. */ +int buzzDelay = 0; + +/* toggle is used to only turn on the speaker every other loop +iteration. */ +char toggle = 0; + +/* This command is set as the default command for the server. It + * handles both GET and POST requests. For a GET, it returns a simple + * page with some buttons. For a POST, it saves the value posted to + * the buzzDelay variable, affecting the output of the speaker */ +void buzzCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) +{ + if (type == WebServer::POST) + { + bool repeat; + char name[16], value[16]; + do + { + /* readPOSTparam returns false when there are no more parameters + * to read from the input. We pass in buffers for it to store + * the name and value strings along with the length of those + * buffers. */ + repeat = server.readPOSTparam(name, 16, value, 16); + + /* this is a standard string comparison function. It returns 0 + * when there's an exact match. We're looking for a parameter + * named "buzz" here. */ + if (strcmp(name, "buzz") == 0) + { + /* use the STRing TO Unsigned Long function to turn the string + * version of the delay number into our integer buzzDelay + * variable */ + buzzDelay = strtoul(value, NULL, 10); + } + } while (repeat); + + // after procesing the POST data, tell the web browser to reload + // the page using a GET method. + server.httpSeeOther(PREFIX); + return; + } + + /* for a GET or HEAD, send the standard "it's all OK headers" */ + server.httpSuccess(); + + /* we don't output the body for a HEAD request */ + if (type == WebServer::GET) + { + /* store the HTML in program memory using the P macro */ + P(message) = +"<!DOCTYPE html><html><head>" + "<title>Webduino AJAX Buzzer Example</title>" + "<link href='http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/base/jquery-ui.css' rel=stylesheet />" + //"<meta http-equiv='Content-Script-Type' content='text/javascript'>" + "<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js'></script>" + "<script src='http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js'></script>" + "<style> #slider { margin: 10px; } </style>" + "<script>" + "function changeBuzz(event, ui) { $('#indicator').text(ui.value); $.post('/buzz', { buzz: ui.value } ); }" + "$(document).ready(function(){ $('#slider').slider({min: 0, max:8000, change:changeBuzz}); });" + "</script>" +"</head>" +"<body style='font-size:62.5%;'>" + "<h1>Test the Buzzer!</h1>" + "<div id=slider></div>" + "<p id=indicator>0</p>" +"</body>" +"</html>"; + + server.printP(message); + } +} + +void setup() +{ + // set the PWM output for the buzzer to out + pinMode(BUZZER_PIN, OUTPUT); + + // setup the Ehternet library to talk to the Wiznet board + Ethernet.begin(mac, ip); + + /* register our default command (activated with the request of + * http://x.x.x.x/buzz */ + webserver.setDefaultCommand(&buzzCmd); + + /* start the server to wait for connections */ + webserver.begin(); +} + +void loop() +{ + // process incoming connections one at a time forever + webserver.processConnection(); + + /* every other time through the loop, turn on and off the speaker if + * our delay isn't set to 0. */ + if ((++toggle & 1) && (buzzDelay > 0)) + { + digitalWrite(BUZZER_PIN, HIGH); + delayMicroseconds(buzzDelay); + digitalWrite(BUZZER_PIN, LOW); + } +} diff --git a/Webduino/examples/Web_AjaxRGB/Web_AjaxRGB.ino b/Webduino/examples/Web_AjaxRGB/Web_AjaxRGB.ino new file mode 100644 index 0000000..8e05a8c --- /dev/null +++ b/Webduino/examples/Web_AjaxRGB/Web_AjaxRGB.ino @@ -0,0 +1,142 @@ +/* Web_AjaxRGB.pde - example sketch for Webduino library */ + +#include "SPI.h" +#include "Ethernet.h" +#include "WebServer.h" + +// CHANGE THIS TO YOUR OWN UNIQUE VALUE +static uint8_t mac[6] = { 0x02, 0xAA, 0xBB, 0xCC, 0x00, 0x22 }; + +// CHANGE THIS TO MATCH YOUR HOST NETWORK +static uint8_t ip[4] = { 192, 168, 1, 210 }; // area 51! + +/* all URLs on this server will start with /rgb because of how we + * define the PREFIX value. We also will listen on port 80, the + * standard HTTP service port */ +#define PREFIX "/rgb" +WebServer webserver(PREFIX, 80); + +#define RED_PIN 5 +#define GREEN_PIN 3 +#define BLUE_PIN 6 + +int red = 0; //integer for red darkness +int blue = 0; //integer for blue darkness +int green = 0; //integer for green darkness + +/* This command is set as the default command for the server. It + * handles both GET and POST requests. For a GET, it returns a simple + * page with some buttons. For a POST, it saves the value posted to + * the red/green/blue variable, affecting the output of the speaker */ +void rgbCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) +{ + if (type == WebServer::POST) + { + bool repeat; + char name[16], value[16]; + do + { + /* readPOSTparam returns false when there are no more parameters + * to read from the input. We pass in buffers for it to store + * the name and value strings along with the length of those + * buffers. */ + repeat = server.readPOSTparam(name, 16, value, 16); + + /* this is a standard string comparison function. It returns 0 + * when there's an exact match. We're looking for a parameter + * named red/green/blue here. */ + if (strcmp(name, "red") == 0) + { + /* use the STRing TO Unsigned Long function to turn the string + * version of the color strength value into our integer red/green/blue + * variable */ + red = strtoul(value, NULL, 10); + } + if (strcmp(name, "green") == 0) + { + green = strtoul(value, NULL, 10); + } + if (strcmp(name, "blue") == 0) + { + blue = strtoul(value, NULL, 10); + } + } while (repeat); + + // after procesing the POST data, tell the web browser to reload + // the page using a GET method. + server.httpSeeOther(PREFIX); +// Serial.print(name); +// Serial.println(value); + + return; + } + + /* for a GET or HEAD, send the standard "it's all OK headers" */ + server.httpSuccess(); + + /* we don't output the body for a HEAD request */ + if (type == WebServer::GET) + { + /* store the HTML in program memory using the P macro */ + P(message) = +"<!DOCTYPE html><html><head>" + "<title>Webduino AJAX RGB Example</title>" + "<link href='http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/base/jquery-ui.css' rel=stylesheet />" + "<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js'></script>" + "<script src='http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js'></script>" + "<style> body { background: black; } #red, #green, #blue { margin: 10px; } #red { background: #f00; } #green { background: #0f0; } #blue { background: #00f; } </style>" + "<script>" + +// change color on mouse up, not while sliding (causes much less traffic to the Arduino): +// "function changeRGB(event, ui) { var id = $(this).attr('id'); if (id == 'red') $.post('/rgb', { red: ui.value } ); if (id == 'green') $.post('/rgb', { green: ui.value } ); if (id == 'blue') $.post('/rgb', { blue: ui.value } ); } " +// "$(document).ready(function(){ $('#red, #green, #blue').slider({min: 0, max:255, change:changeRGB}); });" + +// change color on slide and mouse up (causes more traffic to the Arduino): + "function changeRGB(event, ui) { jQuery.ajaxSetup({timeout: 110}); /*not to DDoS the Arduino, you might have to change this to some threshold value that fits your setup*/ var id = $(this).attr('id'); if (id == 'red') $.post('/rgb', { red: ui.value } ); if (id == 'green') $.post('/rgb', { green: ui.value } ); if (id == 'blue') $.post('/rgb', { blue: ui.value } ); } " + "$(document).ready(function(){ $('#red, #green, #blue').slider({min: 0, max:255, change:changeRGB, slide:changeRGB}); });" + + "</script>" +"</head>" +"<body style='font-size:62.5%;'>" + "<div id=red></div>" + "<div id=green></div>" + "<div id=blue></div>" +"</body>" +"</html>"; + + server.printP(message); + } +} + +void setup() +{ + pinMode(RED_PIN, OUTPUT); + pinMode(GREEN_PIN, OUTPUT); + pinMode(BLUE_PIN, OUTPUT); + +// Serial.begin(9600); + + // setup the Ehternet library to talk to the Wiznet board + Ethernet.begin(mac, ip); + + /* register our default command (activated with the request of + * http://x.x.x.x/rgb */ + webserver.setDefaultCommand(&rgbCmd); + + /* start the server to wait for connections */ + webserver.begin(); +} + +void loop() +{ + // process incoming connections one at a time forever + webserver.processConnection(); +// Serial.print(red); +// Serial.print(" "); +// Serial.print(green); +// Serial.print(" "); +// Serial.println(blue); + analogWrite(RED_PIN, red); + analogWrite(GREEN_PIN, green); + analogWrite(BLUE_PIN, blue); +} diff --git a/Webduino/examples/Web_AjaxRGB_mobile/Web_AjaxRGB_mobile.ino b/Webduino/examples/Web_AjaxRGB_mobile/Web_AjaxRGB_mobile.ino new file mode 100644 index 0000000..aa49ee2 --- /dev/null +++ b/Webduino/examples/Web_AjaxRGB_mobile/Web_AjaxRGB_mobile.ino @@ -0,0 +1,140 @@ +/* Web_AjaxRGB_mobile.pde - example sketch for Webduino library */ +/* - offers web-based slider controllers for RGB led - */ + +#include "SPI.h" +#include "Ethernet.h" +#include "WebServer.h" + +// CHANGE THIS TO YOUR OWN UNIQUE VALUE +static uint8_t mac[6] = { 0x02, 0xAA, 0xBB, 0xCC, 0x00, 0x22 }; + +// CHANGE THIS TO MATCH YOUR HOST NETWORK +static uint8_t ip[4] = { 192, 168, 1, 210 }; // area 51! + +/* all URLs on this server will start with /rgb because of how we + * define the PREFIX value. We also will listen on port 80, the + * standard HTTP service port */ +#define PREFIX "/rgb" +WebServer webserver(PREFIX, 80); + +#define RED_PIN 5 +#define GREEN_PIN 3 +#define BLUE_PIN 6 + +int red = 0; //integer for red darkness +int blue = 0; //integer for blue darkness +int green = 0; //integer for green darkness + +/* This command is set as the default command for the server. It + * handles both GET and POST requests. For a GET, it returns a simple + * page with some buttons. For a POST, it saves the value posted to + * the red/green/blue variable, affecting the output of the speaker */ +void rgbCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) +{ + if (type == WebServer::POST) + { + bool repeat; + char name[16], value[16]; + do + { + /* readPOSTparam returns false when there are no more parameters + * to read from the input. We pass in buffers for it to store + * the name and value strings along with the length of those + * buffers. */ + repeat = server.readPOSTparam(name, 16, value, 16); + + /* this is a standard string comparison function. It returns 0 + * when there's an exact match. We're looking for a parameter + * named red/green/blue here. */ + if (strcmp(name, "red") == 0) + { + /* use the STRing TO Unsigned Long function to turn the string + * version of the color strength value into our integer red/green/blue + * variable */ + red = strtoul(value, NULL, 10); + } + if (strcmp(name, "green") == 0) + { + green = strtoul(value, NULL, 10); + } + if (strcmp(name, "blue") == 0) + { + blue = strtoul(value, NULL, 10); + } + } while (repeat); + + // after procesing the POST data, tell the web browser to reload + // the page using a GET method. + server.httpSeeOther(PREFIX); +// Serial.print(name); +// Serial.println(value); + + return; + } + + /* for a GET or HEAD, send the standard "it's all OK headers" */ + server.httpSuccess(); + + /* we don't output the body for a HEAD request */ + if (type == WebServer::GET) + { + /* store the HTML in program memory using the P macro */ + P(message) = +"<!DOCTYPE html><html><head>" + "<meta charset=\"utf-8\"><meta name=\"apple-mobile-web-app-capable\" content=\"yes\" /><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\"><meta name=\"viewport\" content=\"width=device-width, user-scalable=no\">" + "<title>Webduino RGB</title>" + "<link rel=\"stylesheet\" href=\"http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css\" />" + "<script src=\"http://code.jquery.com/jquery-1.6.4.min.js\"></script>" + "<script src=\"http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js\"></script>" + "<style> body, .ui-page { background: black; } .ui-body { padding-bottom: 1.5em; } div.ui-slider { width: 88%; } #red, #green, #blue { display: block; margin: 10px; } #red { background: #f00; } #green { background: #0f0; } #blue { background: #00f; } </style>" + "<script>" +// causes the Arduino to hang quite frequently (more often than Web_AjaxRGB.pde), probably due to the different event triggering the ajax requests + "$(document).ready(function(){ $('#red, #green, #blue').slider; $('#red, #green, #blue').bind( 'change', function(event, ui) { jQuery.ajaxSetup({timeout: 110}); /*not to DDoS the Arduino, you might have to change this to some threshold value that fits your setup*/ var id = $(this).attr('id'); var strength = $(this).val(); if (id == 'red') $.post('/rgb', { red: strength } ); if (id == 'green') $.post('/rgb', { green: strength } ); if (id == 'blue') $.post('/rgb', { blue: strength } ); });});" + "</script>" +"</head>" +"<body>" + "<div data-role=\"header\" data-position=\"inline\"><h1>Webduino RGB</h1></div>" + "<div class=\"ui-body ui-body-a\">" + "<input type=\"range\" name=\"slider\" id=\"red\" value=\"0\" min=\"0\" max=\"255\" />" + "<input type=\"range\" name=\"slider\" id=\"green\" value=\"0\" min=\"0\" max=\"255\" />" + "<input type=\"range\" name=\"slider\" id=\"blue\" value=\"0\" min=\"0\" max=\"255\" />" + "</div>" + "</body>" +"</html>"; + + server.printP(message); + } +} + +void setup() +{ + pinMode(RED_PIN, OUTPUT); + pinMode(GREEN_PIN, OUTPUT); + pinMode(BLUE_PIN, OUTPUT); + +// Serial.begin(9600); + + // setup the Ehternet library to talk to the Wiznet board + Ethernet.begin(mac, ip); + + /* register our default command (activated with the request of + * http://x.x.x.x/rgb */ + webserver.setDefaultCommand(&rgbCmd); + + /* start the server to wait for connections */ + webserver.begin(); +} + +void loop() +{ + // process incoming connections one at a time forever + webserver.processConnection(); +// Serial.print(red); +// Serial.print(" "); +// Serial.print(green); +// Serial.print(" "); +// Serial.println(blue); + analogWrite(RED_PIN, red); + analogWrite(GREEN_PIN, green); + analogWrite(BLUE_PIN, blue); +} diff --git a/Webduino/examples/Web_Authentication/Web_Authentication.ino b/Webduino/examples/Web_Authentication/Web_Authentication.ino new file mode 100644 index 0000000..4a2384d --- /dev/null +++ b/Webduino/examples/Web_Authentication/Web_Authentication.ino @@ -0,0 +1,106 @@ +/* Web_Authentication.ino - Webduino Authentication example */ + +/* This example assumes that you're familiar with the basics + * of the Ethernet library (particularly with setting MAC and + * IP addresses) and with the basics of Webduino. If you + * haven't had a look at the HelloWorld example you should + * probably check it out first */ + +/* you can change the authentication realm by defining + * WEBDUINO_AUTH_REALM before including WebServer.h */ +#define WEBDUINO_AUTH_REALM "Weduino Authentication Example" + +#include "SPI.h" +#include "Ethernet.h" +#include "WebServer.h" + +/* CHANGE THIS TO YOUR OWN UNIQUE VALUE. The MAC number should be + * different from any other devices on your network or you'll have + * problems receiving packets. */ +static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +/* CHANGE THIS TO MATCH YOUR HOST NETWORK. Most home networks are in + * the 192.168.0.XXX or 192.168.1.XXX subrange. Pick an address + * that's not in use and isn't going to be automatically allocated by + * DHCP from your router. */ +static uint8_t ip[] = { 192, 168, 1, 210 }; + +/* This creates an instance of the webserver. By specifying a prefix + * of "", all pages will be at the root of the server. */ +#define PREFIX "" +WebServer webserver(PREFIX, 80); + +void defaultCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) +{ + server.httpSuccess(); + if (type != WebServer::HEAD) + { + P(helloMsg) = "<h1>Hello, World!</h1><a href=\"private.html\">Private page</a>"; + server.printP(helloMsg); + } +} + +void privateCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) +{ + /* if the user has requested this page using the following credentials + * username = user + * password = user + * display a page saying "Hello User" + * + * the credentials have to be concatenated with a colon like + * username:password + * and encoded using Base64 - this should be done outside of your Arduino + * to be easy on your resources + * + * in other words: "dXNlcjp1c2Vy" is the Base64 representation of "user:user" + * + * if you need to change the username/password dynamically please search + * the web for a Base64 library */ + if (server.checkCredentials("dXNlcjp1c2Vy")) + { + server.httpSuccess(); + if (type != WebServer::HEAD) + { + P(helloMsg) = "<h1>Hello User</h1>"; + server.printP(helloMsg); + } + } + /* if the user has requested this page using the following credentials + * username = admin + * password = admin + * display a page saying "Hello Admin" + * + * in other words: "YWRtaW46YWRtaW4=" is the Base64 representation of "admin:admin" */ + else if (server.checkCredentials("YWRtaW46YWRtaW4=")) + { + server.httpSuccess(); + if (type != WebServer::HEAD) + { + P(helloMsg) = "<h1>Hello Admin</h1>"; + server.printP(helloMsg); + } + } + else + { + /* send a 401 error back causing the web browser to prompt the user for credentials */ + server.httpUnauthorized(); + } +} + +void setup() +{ + Ethernet.begin(mac, ip); + webserver.setDefaultCommand(&defaultCmd); + webserver.addCommand("index.html", &defaultCmd); + webserver.addCommand("private.html", &privateCmd); + webserver.begin(); +} + +void loop() +{ + char buff[64]; + int len = 64; + + /* process incoming connections one at a time forever */ + webserver.processConnection(buff, &len); +}
diff --git a/Webduino/examples/Web_Buzzer/Web_Buzzer.ino b/Webduino/examples/Web_Buzzer/Web_Buzzer.ino new file mode 100644 index 0000000..083f315 --- /dev/null +++ b/Webduino/examples/Web_Buzzer/Web_Buzzer.ino @@ -0,0 +1,123 @@ +/* Web_Buzzer.pde - example sketch for Webduino library */ + +#include "SPI.h" +#include "Ethernet.h" +#include "WebServer.h" + +/* CHANGE THIS TO YOUR OWN UNIQUE VALUE. The MAC number should be + * different from any other devices on your network or you'll have + * problems receiving packets. */ +static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +/* CHANGE THIS TO MATCH YOUR HOST NETWORK. Most home networks are in + * the 192.168.0.XXX or 192.168.1.XXX subrange. Pick an address + * that's not in use and isn't going to be automatically allocated by + * DHCP from your router. */ +static uint8_t ip[] = { 192, 168, 1, 210 }; + +/* all URLs on this server will start with /buzz because of how we + * define the PREFIX value. We also will listen on port 80, the + * standard HTTP service port */ +#define PREFIX "/buzz" +WebServer webserver(PREFIX, 80); + +/* the piezo speaker on the Danger Shield is on PWM output pin #3 */ +#define BUZZER_PIN 3 + +/* this is the number of microseconds to wait after turning the + * speaker on before turning it off. */ +int buzzDelay = 0; + +/* toggle is used to only turn on the speaker every other loop +iteration. */ +char toggle = 0; + +/* This command is set as the default command for the server. It + * handles both GET and POST requests. For a GET, it returns a simple + * page with some buttons. For a POST, it saves the value posted to + * the buzzDelay variable, affecting the output of the speaker */ +void buzzCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + if (type == WebServer::POST) + { + bool repeat; + char name[16], value[16]; + do + { + /* readPOSTparam returns false when there are no more parameters + * to read from the input. We pass in buffers for it to store + * the name and value strings along with the length of those + * buffers. */ + repeat = server.readPOSTparam(name, 16, value, 16); + + /* this is a standard string comparison function. It returns 0 + * when there's an exact match. We're looking for a parameter + * named "buzz" here. */ + if (strcmp(name, "buzz") == 0) + { + /* use the STRing TO Unsigned Long function to turn the string + * version of the delay number into our integer buzzDelay + * variable */ + buzzDelay = strtoul(value, NULL, 10); + } + } while (repeat); + + // after procesing the POST data, tell the web browser to reload + // the page using a GET method. + server.httpSeeOther(PREFIX); + return; + } + + /* for a GET or HEAD, send the standard "it's all OK headers" */ + server.httpSuccess(); + + /* we don't output the body for a HEAD request */ + if (type == WebServer::GET) + { + /* store the HTML in program memory using the P macro */ + P(message) = + "<html><head><title>Webduino Buzzer Example</title>" + "<body>" + "<h1>Test the Buzzer!</h1>" + "<form action='/buzz' method='POST'>" + "<p><button name='buzz' value='0'>Turn if Off!</button></p>" + "<p><button name='buzz' value='500'>500</button></p>" + "<p><button name='buzz' value='1975'>1975</button></p>" + "<p><button name='buzz' value='3000'>3000</button></p>" + "<p><button name='buzz' value='8000'>8000</button></p>" + "</form></body></html>"; + + server.printP(message); + } +} + +void setup() +{ + // set the PWM output for the buzzer to out + pinMode(BUZZER_PIN, OUTPUT); + + // setup the Ehternet library to talk to the Wiznet board + Ethernet.begin(mac, ip); + + /* register our default command (activated with the request of + * http://x.x.x.x/buzz */ + webserver.setDefaultCommand(&buzzCmd); + + /* start the server to wait for connections */ + webserver.begin(); +} + +void loop() +{ + // process incoming connections one at a time forever + webserver.processConnection(); + + /* every other time through the loop, turn on and off the speaker if + * our delay isn't set to 0. */ + if ((++toggle & 1) && (buzzDelay > 0)) + { + digitalWrite(BUZZER_PIN, HIGH); + delayMicroseconds(buzzDelay); + digitalWrite(BUZZER_PIN, LOW); + } +} diff --git a/Webduino/examples/Web_Demo/Web_Demo.ino b/Webduino/examples/Web_Demo/Web_Demo.ino new file mode 100644 index 0000000..bb4a016 --- /dev/null +++ b/Webduino/examples/Web_Demo/Web_Demo.ino @@ -0,0 +1,189 @@ +/* Web_Demo.pde -- sample code for Webduino server library */ + +/* + * To use this demo, enter one of the following USLs into your browser. + * Replace "host" with the IP address assigned to the Arduino. + * + * http://host/ + * http://host/json + * + * This URL brings up a display of the values READ on digital pins 0-9 + * and analog pins 0-5. This is done with a call to defaultCmd. + * + * + * http://host/form + * + * This URL also brings up a display of the values READ on digital pins 0-9 + * and analog pins 0-5. But it's done as a form, by the "formCmd" function, + * and the digital pins are shown as radio buttons you can change. + * When you click the "Submit" button, it does a POST that sets the + * digital pins, re-reads them, and re-displays the form. + * + */ + +#include "SPI.h" +#include "Ethernet.h" +#include "WebServer.h" + +// no-cost stream operator as described at +// http://sundial.org/arduino/?page_id=119 +template<class T> +inline Print &operator <<(Print &obj, T arg) +{ obj.print(arg); return obj; } + + +// CHANGE THIS TO YOUR OWN UNIQUE VALUE +static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +// CHANGE THIS TO MATCH YOUR HOST NETWORK +static uint8_t ip[] = { 192, 168, 1, 210 }; + +#define PREFIX "" + +WebServer webserver(PREFIX, 80); + +// commands are functions that get called by the webserver framework +// they can read any posted data from client, and they output to server + +void jsonCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + if (type == WebServer::POST) + { + server.httpFail(); + return; + } + + //server.httpSuccess(false, "application/json"); + server.httpSuccess("application/json"); + + if (type == WebServer::HEAD) + return; + + int i; + server << "{ "; + for (i = 0; i <= 9; ++i) + { + // ignore the pins we use to talk to the Ethernet chip + int val = digitalRead(i); + server << "\"d" << i << "\": " << val << ", "; + } + + for (i = 0; i <= 5; ++i) + { + int val = analogRead(i); + server << "\"a" << i << "\": " << val; + if (i != 5) + server << ", "; + } + + server << " }"; +} + +void outputPins(WebServer &server, WebServer::ConnectionType type, bool addControls = false) +{ + P(htmlHead) = + "<html>" + "<head>" + "<title>Arduino Web Server</title>" + "<style type=\"text/css\">" + "BODY { font-family: sans-serif }" + "H1 { font-size: 14pt; text-decoration: underline }" + "P { font-size: 10pt; }" + "</style>" + "</head>" + "<body>"; + + int i; + server.httpSuccess(); + server.printP(htmlHead); + + if (addControls) + server << "<form action='" PREFIX "/form' method='post'>"; + + server << "<h1>Digital Pins</h1><p>"; + + for (i = 0; i <= 9; ++i) + { + // ignore the pins we use to talk to the Ethernet chip + int val = digitalRead(i); + server << "Digital " << i << ": "; + if (addControls) + { + char pinName[4]; + pinName[0] = 'd'; + itoa(i, pinName + 1, 10); + server.radioButton(pinName, "1", "On", val); + server << " "; + server.radioButton(pinName, "0", "Off", !val); + } + else + server << (val ? "HIGH" : "LOW"); + + server << "<br/>"; + } + + server << "</p><h1>Analog Pins</h1><p>"; + for (i = 0; i <= 5; ++i) + { + int val = analogRead(i); + server << "Analog " << i << ": " << val << "<br/>"; + } + + server << "</p>"; + + if (addControls) + server << "<input type='submit' value='Submit'/></form>"; + + server << "</body></html>"; +} + +void formCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + if (type == WebServer::POST) + { + bool repeat; + char name[16], value[16]; + do + { + repeat = server.readPOSTparam(name, 16, value, 16); + if (name[0] == 'd') + { + int pin = strtoul(name + 1, NULL, 10); + int val = strtoul(value, NULL, 10); + digitalWrite(pin, val); + } + } while (repeat); + + server.httpSeeOther(PREFIX "/form"); + } + else + outputPins(server, type, true); +} + +void defaultCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + outputPins(server, type, false); +} + +void setup() +{ + // set pins 0-8 for digital input + for (int i = 0; i <= 9; ++i) + pinMode(i, INPUT); + pinMode(9, OUTPUT); + + Ethernet.begin(mac, ip); + webserver.begin(); + + webserver.setDefaultCommand(&defaultCmd); + webserver.addCommand("json", &jsonCmd); + webserver.addCommand("form", &formCmd); +} + +void loop() +{ + // process incoming connections one at a time forever + webserver.processConnection(); + + // if you wanted to do other work based on a connecton, it would go here +} diff --git a/Webduino/examples/Web_HelloWorld/Web_HelloWorld.ino b/Webduino/examples/Web_HelloWorld/Web_HelloWorld.ino new file mode 100644 index 0000000..aebf90a --- /dev/null +++ b/Webduino/examples/Web_HelloWorld/Web_HelloWorld.ino @@ -0,0 +1,71 @@ +/* Web_HelloWorld.pde - very simple Webduino example */ + +#include "SPI.h" +#include "Ethernet.h" +#include "WebServer.h" + +/* CHANGE THIS TO YOUR OWN UNIQUE VALUE. The MAC number should be + * different from any other devices on your network or you'll have + * problems receiving packets. */ +static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + + +/* CHANGE THIS TO MATCH YOUR HOST NETWORK. Most home networks are in + * the 192.168.0.XXX or 192.168.1.XXX subrange. Pick an address + * that's not in use and isn't going to be automatically allocated by + * DHCP from your router. */ +static uint8_t ip[] = { 192, 168, 1, 210 }; + +/* This creates an instance of the webserver. By specifying a prefix + * of "", all pages will be at the root of the server. */ +#define PREFIX "" +WebServer webserver(PREFIX, 80); + +/* commands are functions that get called by the webserver framework + * they can read any posted data from client, and they output to the + * server to send data back to the web browser. */ +void helloCmd(WebServer &server, WebServer::ConnectionType type, char *, bool) +{ + /* this line sends the standard "we're all OK" headers back to the + browser */ + server.httpSuccess(); + + /* if we're handling a GET or POST, we can output our data here. + For a HEAD request, we just stop after outputting headers. */ + if (type != WebServer::HEAD) + { + /* this defines some HTML text in read-only memory aka PROGMEM. + * This is needed to avoid having the string copied to our limited + * amount of RAM. */ + P(helloMsg) = "<h1>Hello, World!</h1>"; + + /* this is a special form of print that outputs from PROGMEM */ + server.printP(helloMsg); + } +} + +void setup() +{ + /* initialize the Ethernet adapter */ + Ethernet.begin(mac, ip); + + /* setup our default command that will be run when the user accesses + * the root page on the server */ + webserver.setDefaultCommand(&helloCmd); + + /* run the same command if you try to load /index.html, a common + * default page name */ + webserver.addCommand("index.html", &helloCmd); + + /* start the webserver */ + webserver.begin(); +} + +void loop() +{ + char buff[64]; + int len = 64; + + /* process incoming connections one at a time forever */ + webserver.processConnection(buff, &len); +} diff --git a/Webduino/examples/Web_Image/Web_Image.ino b/Webduino/examples/Web_Image/Web_Image.ino new file mode 100644 index 0000000..cafb149 --- /dev/null +++ b/Webduino/examples/Web_Image/Web_Image.ino @@ -0,0 +1,117 @@ +/* Web_Image.pde - example sketch for Webduino library */ +/* For webduino version 1.2 */ + +/* DISCLAIMER -- the Webduino server can only handle one web connection + * at a time. Because of this, loading the root page on this sketch may + * not correctly show the LED icon if the browser starts requesting that + * picture before the page has finished loading. + * + * This problem should be reduced greatly once the Ethernet library + * has been modified to do proper packet buffering. With the library + * in Arduino 15, each character is sent in its own TCP/IP packet. + * This is very inefficient and means that it takes much longer to + * send a web page than it should. When this bug is fixed, the web + * server will have sent its last data and closed the connection by + * the time the client reads the HTML, so it's ready to handle the + * image request. + */ + +#include "SPI.h" +#include "Ethernet.h" +#include "WebServer.h" + +// CHANGE THIS TO YOUR OWN UNIQUE VALUE +static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +static uint8_t ip[] = { 192, 168, 1, 210 }; + +WebServer webserver("", 80); + +/* The default page just returns HTML to show the image */ +void defaultCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + if (type == WebServer::POST) + { + // ignore POST data + server.httpFail(); + return; + } + + /* for a GET or HEAD, send the standard "it's all OK headers" */ + server.httpSuccess(); + + /* we don't output the body for a HEAD request */ + if (type == WebServer::GET) + { + /* store the HTML in program memory using the P macro */ + P(message) = + "<html><head><title>Webduino Image Example</title>" + "<body>" + "<h2>LED Image</h2>" + "<img src='led.png' width=256 height=256>" + "</body></html>"; + + server.printP(message); + } +} + +void imageCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + /* this data was taken from a PNG file that was converted to a C data structure + * by running it through the directfb-csource application. */ + P(ledData) = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x91, 0x68, + 0x36, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, 0x00, + 0x00, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x00, 0x00, 0x7a, 0x26, 0x00, 0x00, 0x80, 0x84, 0x00, 0x00, + 0xfa, 0x00, 0x00, 0x00, 0x80, 0xe8, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0xea, 0x60, 0x00, 0x00, + 0x3a, 0x98, 0x00, 0x00, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x00, 0x00, 0x00, 0x18, 0x74, 0x45, + 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x00, 0x50, 0x61, 0x69, 0x6e, 0x74, + 0x2e, 0x4e, 0x45, 0x54, 0x20, 0x76, 0x33, 0x2e, 0x33, 0x36, 0xa9, 0xe7, 0xe2, 0x25, 0x00, 0x00, + 0x00, 0x57, 0x49, 0x44, 0x41, 0x54, 0x38, 0x4f, 0x95, 0x52, 0x5b, 0x0a, 0x00, 0x30, 0x08, 0x6a, + 0xf7, 0x3f, 0xf4, 0x1e, 0x14, 0x4d, 0x6a, 0x30, 0x8d, 0x7d, 0x0d, 0x45, 0x2d, 0x87, 0xd9, 0x34, + 0x71, 0x36, 0x41, 0x7a, 0x81, 0x76, 0x95, 0xc2, 0xec, 0x3f, 0xc7, 0x8e, 0x83, 0x72, 0x90, 0x43, + 0x11, 0x10, 0xc4, 0x12, 0x50, 0xb6, 0xc7, 0xab, 0x96, 0xd0, 0xdb, 0x5b, 0x41, 0x5c, 0x6a, 0x0b, + 0xfd, 0x57, 0x28, 0x5b, 0xc2, 0xfd, 0xb2, 0xa1, 0x33, 0x28, 0x45, 0xd0, 0xee, 0x20, 0x5c, 0x9a, + 0xaf, 0x93, 0xd6, 0xbc, 0xdb, 0x25, 0x56, 0x61, 0x01, 0x17, 0x12, 0xae, 0x53, 0x3e, 0x66, 0x32, + 0xba, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + }; + + if (type == WebServer::POST) + { + // ignore POST data + server.httpFail(); + return; + } + + /* for a GET or HEAD, send the standard "it's all OK headers" but identify our data as a PNG file */ + server.httpSuccess("image/png"); + + /* we don't output the body for a HEAD request */ + if (type == WebServer::GET) + { + server.writeP(ledData, sizeof(ledData)); + } +} + +void setup() +{ + // setup the Ehternet library to talk to the Wiznet board + Ethernet.begin(mac, ip); + + /* register our default command (activated with the request of + * http://x.x.x.x/ */ + webserver.setDefaultCommand(&defaultCmd); + + /* register our image output command */ + webserver.addCommand("led.png", &imageCmd); + + /* start the server to wait for connections */ + webserver.begin(); +} + +void loop() +{ + // process incoming connections one at a time forever + webserver.processConnection(); +} diff --git a/Webduino/examples/Web_Image/led.png b/Webduino/examples/Web_Image/led.png Binary files differnew file mode 100644 index 0000000..aa97b62 --- /dev/null +++ b/Webduino/examples/Web_Image/led.png diff --git a/Webduino/examples/Web_Parms/Web_Parms.ino b/Webduino/examples/Web_Parms/Web_Parms.ino new file mode 100644 index 0000000..4b848a5 --- /dev/null +++ b/Webduino/examples/Web_Parms/Web_Parms.ino @@ -0,0 +1,287 @@ +/* Web_Parms_1.pde - very simple Webduino example of parameter passing and parsing */ + +/* + * This is mostly a tool for testing and debugging the library, but can + * also be used as an example of coding for it. + * + * To use it, enter one of the following USLs into your browser. + * Replace "host" with the IP address assigned to the Arduino. + * + * http://host/ + * http://host/index.html + * + * These return a "success" HTTP result and display the parameters + * (if any) passed to them as a single string, without attempting to + * parse them. This is done with a call to defaultCmd. + * + * + * http://host/raw.html + * + * This is essentially the same as the index.html URL processing, + * but is done by calling rawCmd. + * + * + * http://host/parsed.html + * + * This invokes parsedCmd, which displays the "raw" parameter string, + * but also uses the "nexyURLparam" routine to parse out the individual + * parameters, and display them. + */ + + +#define WEBDUINO_FAIL_MESSAGE "<h1>Request Failed</h1>" +#include "SPI.h" // new include +#include "avr/pgmspace.h" // new include +#include "Ethernet.h" +#include "WebServer.h" + +#define VERSION_STRING "0.1" + +/* CHANGE THIS TO YOUR OWN UNIQUE VALUE. The MAC number should be + * different from any other devices on your network or you'll have + * problems receiving packets. */ +static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + + +/* CHANGE THIS TO MATCH YOUR HOST NETWORK. Most home networks are in + * the 192.168.0.XXX or 192.168.1.XXX subrange. Pick an address + * that's not in use and isn't going to be automatically allocated by + * DHCP from your router. */ +static uint8_t ip[] = { 192, 168, 1, 210 }; + +// ROM-based messages used by the application +// These are needed to avoid having the strings use up our limited +// amount of RAM. + +P(Page_start) = "<html><head><title>Web_Parms_1 Version " VERSION_STRING "</title></head><body>\n"; +P(Page_end) = "</body></html>"; +P(Get_head) = "<h1>GET from "; +P(Post_head) = "<h1>POST to "; +P(Unknown_head) = "<h1>UNKNOWN request for "; +P(Default_head) = "unidentified URL requested.</h1><br>\n"; +P(Raw_head) = "raw.html requested.</h1><br>\n"; +P(Parsed_head) = "parsed.html requested.</h1><br>\n"; +P(Good_tail_begin) = "URL tail = '"; +P(Bad_tail_begin) = "INCOMPLETE URL tail = '"; +P(Tail_end) = "'<br>\n"; +P(Parsed_tail_begin) = "URL parameters:<br>\n"; +P(Parsed_item_separator) = " = '"; +P(Params_end) = "End of parameters<br>\n"; +P(Post_params_begin) = "Parameters sent by POST:<br>\n"; +P(Line_break) = "<br>\n"; + + + +/* This creates an instance of the webserver. By specifying a prefix + * of "", all pages will be at the root of the server. */ +#define PREFIX "" +WebServer webserver(PREFIX, 80); + + + +/* commands are functions that get called by the webserver framework + * they can read any posted data from client, and they output to the + * server to send data back to the web browser. */ +void helloCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + /* this line sends the standard "we're all OK" headers back to the + browser */ + server.httpSuccess(); + + /* if we're handling a GET or POST, we can output our data here. + For a HEAD request, we just stop after outputting headers. */ + if (type == WebServer::HEAD) + return; + + server.printP(Page_start); + switch (type) + { + case WebServer::GET: + server.printP(Get_head); + break; + case WebServer::POST: + server.printP(Post_head); + break; + default: + server.printP(Unknown_head); + } + + server.printP(Default_head); + server.printP(tail_complete ? Good_tail_begin : Bad_tail_begin); + server.print(url_tail); + server.printP(Tail_end); + server.printP(Page_end); + +} + + +void rawCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + /* this line sends the standard "we're all OK" headers back to the + browser */ + server.httpSuccess(); + + /* if we're handling a GET or POST, we can output our data here. + For a HEAD request, we just stop after outputting headers. */ + if (type == WebServer::HEAD) + return; + + server.printP(Page_start); + switch (type) + { + case WebServer::GET: + server.printP(Get_head); + break; + case WebServer::POST: + server.printP(Post_head); + break; + default: + server.printP(Unknown_head); + } + + server.printP(Raw_head); + server.printP(tail_complete ? Good_tail_begin : Bad_tail_begin); + server.print(url_tail); + server.printP(Tail_end); + server.printP(Page_end); + +} + +#define NAMELEN 32 +#define VALUELEN 32 + +void parsedCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + URLPARAM_RESULT rc; + char name[NAMELEN]; + int name_len; + char value[VALUELEN]; + int value_len; + + /* this line sends the standard "we're all OK" headers back to the + browser */ + server.httpSuccess(); + + /* if we're handling a GET or POST, we can output our data here. + For a HEAD request, we just stop after outputting headers. */ + if (type == WebServer::HEAD) + return; + + server.printP(Page_start); + switch (type) + { + case WebServer::GET: + server.printP(Get_head); + break; + case WebServer::POST: + server.printP(Post_head); + break; + default: + server.printP(Unknown_head); + } + + server.printP(Parsed_head); + server.printP(tail_complete ? Good_tail_begin : Bad_tail_begin); + server.print(url_tail); + server.printP(Tail_end); + + if (strlen(url_tail)) + { + server.printP(Parsed_tail_begin); + while (strlen(url_tail)) + { + rc = server.nextURLparam(&url_tail, name, NAMELEN, value, VALUELEN); + if (rc == URLPARAM_EOS) + server.printP(Params_end); + else + { + server.print(name); + server.printP(Parsed_item_separator); + server.print(value); + server.printP(Tail_end); + } + } + } + if (type == WebServer::POST) + { + server.printP(Post_params_begin); + while (server.readPOSTparam(name, NAMELEN, value, VALUELEN)) + { + server.print(name); + server.printP(Parsed_item_separator); + server.print(value); + server.printP(Tail_end); + } + } + server.printP(Page_end); + +} + +void my_failCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + /* this line sends the standard "we're all OK" headers back to the + browser */ + server.httpFail(); + + /* if we're handling a GET or POST, we can output our data here. + For a HEAD request, we just stop after outputting headers. */ + if (type == WebServer::HEAD) + return; + + server.printP(Page_start); + switch (type) + { + case WebServer::GET: + server.printP(Get_head); + break; + case WebServer::POST: + server.printP(Post_head); + break; + default: + server.printP(Unknown_head); + } + + server.printP(Default_head); + server.printP(tail_complete ? Good_tail_begin : Bad_tail_begin); + server.print(url_tail); + server.printP(Tail_end); + server.printP(Page_end); + +} + + + + +void setup() +{ + /* initialize the Ethernet adapter */ + Ethernet.begin(mac, ip); + + /* setup our default command that will be run when the user accesses + * the root page on the server */ + webserver.setDefaultCommand(&helloCmd); + + /* setup our default command that will be run when the user accesses + * a page NOT on the server */ + webserver.setFailureCommand(&my_failCmd); + + /* run the same command if you try to load /index.html, a common + * default page name */ + webserver.addCommand("index.html", &helloCmd); + + /*This command is called if you try to load /raw.html */ + webserver.addCommand("raw.html", &rawCmd); + webserver.addCommand("parsed.html", &parsedCmd); + + /* start the webserver */ + webserver.begin(); +} + +void loop() +{ + char buff[64]; + int len = 64; + + /* process incoming connections one at a time forever */ + webserver.processConnection(buff, &len); +} diff --git a/Webduino/keywords.txt b/Webduino/keywords.txt new file mode 100644 index 0000000..f72c5a9 --- /dev/null +++ b/Webduino/keywords.txt @@ -0,0 +1,34 @@ +WebServer KEYWORD1
+ConnectionType KEYWORD1
+INVALID KEYWORD2
+GET KEYWORD2
+HEAD KEYWORD2
+POST KEYWORD2
+PUT KEYWORD2
+DELETE KEYWORD2
+PATCH KEYWORD2
+begin KEYWORD2
+processConnection KEYWORD2
+setDefaultCommand KEYWORD2
+setFailureCommand KEYWORD2
+addCommand KEYWORD2
+printCRLF KEYWORD2
+printP KEYWORD2
+writeP KEYWORD2
+radioButton KEYWORD2
+checkBox KEYWORD2
+read KEYWORD2
+push KEYWORD2
+expect KEYWORD2
+readInt KEYWORD2
+readHeader KEYWORD2
+readPOSTparam KEYWORD2
+nextURLparam KEYWORD2
+checkCredentials KEYWORD2
+httpFail KEYWORD2
+httpUnauthorized KEYWORD2
+httpServerError KEYWORD2
+httpSuccess KEYWORD2
+httpSeeOther KEYWORD2
+write KEYWORD2
+P KEYWORD2
diff --git a/Webduino/readme.md b/Webduino/readme.md new file mode 100644 index 0000000..2ef7e78 --- /dev/null +++ b/Webduino/readme.md @@ -0,0 +1,103 @@ +# Webduino + +This is an Arduino-based Web Server library, originally developed for a class at NYC Resistor. It's called Webduino, and it's an extensible web server library for the Arduino using the Wiznet-based Ethernet shields. It's released under the MIT license allowing all sorts of reuse. + +## Features + +- URL parameter parsing +- Handle the following HTTP Methods: GET, HEAD, POST, PUT, DELETE, PATCH +- Web Forms +- Images +- JSON/RESTful interface +- HTTP Basic Authentication + +## Installation Notes + +With Arduino 1.0, add the Webduino folder to the "libraries" folder of your sketchbook directory. + +You can put the examples in your own sketchbook directory, or in hardware/libraries/webduino/examples, as you prefer. + +If you get an error message when building the examples similar to "WebServer.h not found", it's a problem with where you put the Webduino folder. The server won't work if the header is directly in the libraries folder. + +## Presentation + +[Wedbuino Presentation on Google Docs](http://docs.google.com/present/view?id=dd8gqxt8_5c8w9qfg3) + +## Compatible Ethernet Shields + +These have all been tested with the library successfully: + +- [Freetronics Etherten](http://www.freetronics.com/products/etherten) +- [Freetronics Ethernet Shield](http://www.freetronics.com/products/ethernet-shield-with-poe) +- [Arduino Ethernet](http://arduino.cc/en/Main/ArduinoBoardEthernet) +- [Arduino Ethernet Shield, both original and updated microSD version](http://arduino.cc/en/Main/ArduinoEthernetShield) +- [Adafruit Ethernet Shield w/ Wiznet 811MJ module](http://www.ladyada.net/make/eshield/) +- [NKC Electronics Ethernet Shield DIY Kit](http://store.nkcelectronics.com/nkc-ethernet-shield-diy-kit-without-wiz812mj-mod812.html) + +Shields using the Microchip ENC28J60 chip won't work with the library as that requires more software support for implementating +the TCP/IP stack. + +## Version history + +### 1.7 released in Jan 2012 + +- fixed Google Code issue [4](http://code.google.com/p/webduino/issues/detail?id=4) where expect fails with high-ASCII characters due to sign issues +- fixed Google Code issue [8](http://code.google.com/p/webduino/issues/detail?id=8) by adding WEBDUINO_NO_IMPLEMENTATION macro that allows including the class definition without the implementation code +- fixed Google Code issue [9](http://code.google.com/p/webduino/issues/detail?id=9): allowing prog_char* strings for printP +- added httpServerError() method to output 500 Internal Server Error message +- added support for HTTP PUT, DELETE, and PATCH methods (see Google Code issue [11](http://code.google.com/p/webduino/issues/detail?id=11) +- fixed Google Code issue [12](http://code.google.com/p/webduino/issues/detail?id=12): off-by-one error in name/value parser (readPOSTparam) where the buffer wouldn't ever be completely filled +- updated copyright string for 2012 and major authors +- GitHub fork now the official version; all open issues on Google Code site fixed or closed and moved to GitHub + +### 1.6 released in Jan 2012 + +- added [checkCredentials](http://ten-fingers-and-a-brain.com/arduino-projects/webduino/checkcredentials/) and [httpUnauthorized](http://ten-fingers-and-a-brain.com/arduino-projects/webduino/httpunauthorized/) methods as well as readHeader method for HTTP Basic Authentication; currently users will have to do the Base64 encoding outside of Webduino and I'm uncertain whether I ever want this inside the library or not... +- fixed the request parser: previously the command dispatcher would always pass true for tail_complete, even if the tail was incomplete +- fixed the command dispatcher: previously the default command could not have a tail, but the EPIC FAIL was returned + +### 1.5 released in Dec 2011 + +- added a default favicon.ico based on the led.png from the Image example to save resources on Firefox trying to load this file on every request if it can't be loaded +- added keywords.txt for syntax highlighting in Arduino IDE +- bumped the version number up in response headers and compiler variables +- added version history to readme +- fixed default value for prefix +- fixed /index.html in Hello World example + +### releases between Jul 2011 and Dec 2011 + +- project forked on GitHub by Chris Lee +- JSON/RESTful interface +- header Access-Control-Allow-Origin added +- code split in .h and .cpp files +- Arduino 1.0 changes by Dave Falkenburg and others +- adding CRLF after extraHeaders + +### 1.4.1 released in Nov 2009 + +- fix examples to use readPOSTparam method + +### 1.4 released in Sep 2009 + +- bug fix for multple connections + +### 1.3.1 released in Aug 2009 + +- major bug fix for 1.3 for posts where Content-Length is last header sent + +### 1.3 released in Aug 2009 + +- mostly robustness fixes (beta) + +### 1.2.1 released in Aug 2009 + +- fixed HelloWorld example + +### 1.2 released in Jul 2009 + +- now with GET parameter handling + +### 1.1 released in Apr 2009 + +### 1.0 released in Mar 2009 |