summaryrefslogtreecommitdiff
path: root/Webduino
diff options
context:
space:
mode:
Diffstat (limited to 'Webduino')
-rw-r--r--Webduino/WebServer.h1170
-rw-r--r--Webduino/examples/Web_AjaxBuzzer/Web_AjaxBuzzer.ino126
-rw-r--r--Webduino/examples/Web_AjaxRGB/Web_AjaxRGB.ino142
-rw-r--r--Webduino/examples/Web_AjaxRGB_mobile/Web_AjaxRGB_mobile.ino140
-rw-r--r--Webduino/examples/Web_Authentication/Web_Authentication.ino106
-rw-r--r--Webduino/examples/Web_Buzzer/Web_Buzzer.ino123
-rw-r--r--Webduino/examples/Web_Demo/Web_Demo.ino189
-rw-r--r--Webduino/examples/Web_HelloWorld/Web_HelloWorld.ino71
-rw-r--r--Webduino/examples/Web_Image/Web_Image.ino117
-rw-r--r--Webduino/examples/Web_Image/led.pngbin0 -> 253 bytes
-rw-r--r--Webduino/examples/Web_Parms/Web_Parms.ino287
-rw-r--r--Webduino/keywords.txt34
-rw-r--r--Webduino/readme.md103
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
new file mode 100644
index 0000000..aa97b62
--- /dev/null
+++ b/Webduino/examples/Web_Image/led.png
Binary files differ
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