diff --git a/WebServer.h b/WebServer.h index 5bc6323..c8c43c3 100644 --- a/WebServer.h +++ b/WebServer.h @@ -1,7 +1,8 @@ /* -*- 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 + Copyright 2009-2014 Ben Combee, Ran Talbott, Christopher Lee, Martin Lormes + Francisco M Cuenca-Acuna Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -28,6 +29,7 @@ #include #include +#include #include #include @@ -38,12 +40,6 @@ #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" @@ -57,6 +53,10 @@ #define WEBDUINO_READ_TIMEOUT_IN_MS 1000 #endif +#ifndef WEBDUINO_COMMANDS_COUNT +#define WEBDUINO_COMMANDS_COUNT 8 +#endif + #ifndef WEBDUINO_URL_PATH_COMMAND_LENGTH #define WEBDUINO_URL_PATH_COMMAND_LENGTH 8 #endif @@ -77,6 +77,10 @@ #define WEBDUINO_SERVER_ERROR_MESSAGE "

500 Internal Server Error

" #endif // WEBDUINO_SERVER_ERROR_MESSAGE +#ifndef WEBDUINO_OUTPUT_BUFFER_SIZE +#define WEBDUINO_OUTPUT_BUFFER_SIZE 32 +#endif // WEBDUINO_OUTPUT_BUFFER_SIZE + // 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 @@ -127,11 +131,18 @@ extern "C" unsigned long millis(void); // declare a static string -#define P(name) static const prog_uchar name[] PROGMEM +#ifdef __AVR__ +#define P(name) static const unsigned char name[] __attribute__(( section(".progmem." #name) )) +#else +#define P(name) static const unsigned char name[] +#endif // returns the number of elements in the array #define SIZE(array) (sizeof(array) / sizeof(*array)) +#ifdef _VARIANT_ARDUINO_DUE_X_ +#define pgm_read_byte(ptr) (unsigned char)(* ptr) +#endif /******************************************************************** * DECLARATIONS ********************************************************************/ @@ -140,11 +151,11 @@ extern "C" unsigned long millis(void); * 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 +enum URLPARAM_RESULT { URLPARAM_OK, + URLPARAM_NAME_OFLO, + URLPARAM_VALUE_OFLO, + URLPARAM_BOTH_OFLO, + URLPARAM_EOS // No params left }; class WebServer: public Print @@ -170,7 +181,7 @@ class WebServer: public Print bool tail_complete); // constructor for webserver object - WebServer(const char *urlPrefix = "", int port = 80); + WebServer(const char *urlPrefix = "", uint16_t port = 80); // start listening for connections void begin(); @@ -205,13 +216,19 @@ class WebServer: public Print // output a string stored in program memory, usually one defined // with the P macro - void printP(const prog_uchar *str); + void printP(const unsigned char *str); // inline overload for printP to handle signed char strings - void printP(const prog_char *str) { printP((prog_uchar*)str); } + void printP(const char *str) { printP((unsigned char*)str); } + + // support for C style formating + void printf(char *fmt, ... ); + #ifdef F + void printf(const __FlashStringHelper *format, ... ); + #endif // output raw data stored in program memory - void writeP(const prog_uchar *data, size_t length); + void writeP(const unsigned char *data, size_t length); // output HTML for a radio button void radioButton(const char *name, const char *val, @@ -265,7 +282,7 @@ class WebServer: public Print // output headers and a message indicating a server error void httpFail(); - + // output headers and a message indicating "401 Unauthorized" void httpUnauthorized(); @@ -288,20 +305,23 @@ class WebServer: public Print // 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); // tells if there is anything to process uint8_t available(); + // Flush the send buffer + void flushBuf(); + + // Close the current connection and flush ethernet buffers + void reset(); private: EthernetServer m_server; EthernetClient m_client; const char *m_urlPrefix; unsigned char m_pushback[32]; - char m_pushbackDepth; + unsigned char m_pushbackDepth; int m_contentLength; char m_authCredentials[51]; @@ -313,11 +333,13 @@ class WebServer: public Print { const char *verb; Command *cmd; - } m_commands[8]; - char m_cmdCount; + } m_commands[WEBDUINO_COMMANDS_COUNT]; + unsigned char m_cmdCount; UrlPathCommand *m_urlPathCmd; - void reset(); + uint8_t m_buffer[WEBDUINO_OUTPUT_BUFFER_SIZE]; + uint8_t m_bufFill; + void getRequest(WebServer::ConnectionType &type, char *request, int *length); bool dispatchCommand(ConnectionType requestType, char *verb, bool tail_complete); @@ -341,19 +363,22 @@ class WebServer: public Print * IMPLEMENTATION ********************************************************************/ -WebServer::WebServer(const char *urlPrefix, int port) : +WebServer::WebServer(const char *urlPrefix, uint16_t port) : m_server(port), - m_client(255), + m_client(), m_urlPrefix(urlPrefix), m_pushbackDepth(0), - m_cmdCount(0), m_contentLength(0), m_failureCmd(&defaultFailCmd), m_defaultCmd(&defaultFailCmd), - m_urlPathCmd(NULL) + m_cmdCount(0), + m_urlPathCmd(NULL), + m_bufFill(0) { } +P(webServerHeader) = "Server: Webduino/" WEBDUINO_VERSION_STRING CRLF; + void WebServer::begin() { m_server.begin(); @@ -385,80 +410,91 @@ void WebServer::setUrlPathCommand(UrlPathCommand *cmd) size_t WebServer::write(uint8_t ch) { - return m_client.write(ch); -} + m_buffer[m_bufFill++] = ch; -size_t WebServer::write(const char *str) -{ - return m_client.write(str); + if(m_bufFill == sizeof(m_buffer)) + { + m_client.write(m_buffer, sizeof(m_buffer)); + m_bufFill = 0; + } + + return sizeof(ch); } size_t WebServer::write(const uint8_t *buffer, size_t size) { + flushBuf(); //Flush any buffered output return m_client.write(buffer, size); } -size_t WebServer::write(const char *buffer, size_t length) +void WebServer::flushBuf() { - return m_client.write((const uint8_t *)buffer, length); + if(m_bufFill > 0) + { + m_client.write(m_buffer, m_bufFill); + m_bufFill = 0; + } } -void WebServer::writeP(const prog_uchar *data, size_t length) +void WebServer::writeP(const unsigned char *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; + // copy data out of program memory into local storage while (length--) { - if (bufferEnd == 32) - { - m_client.write(buffer, 32); - bufferEnd = 0; - } - - buffer[bufferEnd++] = pgm_read_byte(data++); + write(pgm_read_byte(data++)); } - - if (bufferEnd > 0) - m_client.write(buffer, bufferEnd); } -void WebServer::printP(const prog_uchar *str) +void WebServer::printP(const unsigned char *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++)) + // copy data out of program memory into local storage + + while (uint8_t value = pgm_read_byte(str++)) { - if (bufferEnd == 32) - { - m_client.write(buffer, 32); - bufferEnd = 0; - } + write(value); } - - // 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); + print(CRLF); +} + +void WebServer::printf(char *fmt, ... ) +{ + char tmp[128]; // resulting string limited to 128 chars + va_list args; + va_start (args, fmt ); + vsnprintf(tmp, 128, fmt, args); + va_end (args); + print(tmp); } +#ifdef F +void WebServer::printf(const __FlashStringHelper *format, ... ) +{ + char buf[128]; // resulting string limited to 128 chars + va_list ap; + va_start(ap, format); +#ifdef __AVR__ + vsnprintf_P(buf, sizeof(buf), (const char *)format, ap); // progmem for AVR +#else + vsnprintf(buf, sizeof(buf), (const char *)format, ap); // for the rest of the world +#endif + va_end(ap); + print(buf); +} +#endif + 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 + // 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); + m_defaultCmd(*this, requestType, (char*)"", tail_complete); return true; } // if the URL is just a slash followed by a question mark @@ -473,10 +509,10 @@ bool WebServer::dispatchCommand(ConnectionType requestType, char *verb, // if the first character is a slash, there's more after it. if (verb[0] == '/') { - char i; + uint8_t i; char *qm_loc; - int verb_len; - int qm_offset; + uint16_t verb_len; + uint8_t qm_offset; // Skip over the leading "/", because it makes the code more // efficient and easier to understand. verb++; @@ -503,7 +539,7 @@ bool WebServer::dispatchCommand(ConnectionType requestType, char *verb, { // Initialize with null bytes, so number of parts can be determined. char *url_path[WEBDUINO_URL_PATH_COMMAND_LENGTH] = {0}; - int part = 0; + uint8_t part = 0; // URL path should be terminated with null byte. *(verb + verb_len) = 0; @@ -596,6 +632,8 @@ void WebServer::processConnection(char *buff, int *bufflen) m_failureCmd(*this, requestType, buff, (*bufflen) >= 0); } + flushBuf(); + #if WEBDUINO_SERIAL_DEBUGGING > 1 Serial.println("*** stopping connection ***"); #endif @@ -606,21 +644,26 @@ void WebServer::processConnection(char *buff, int *bufflen) bool WebServer::checkCredentials(const char authCredentials[45]) { char basic[7] = "Basic "; - if((0 == strncmp(m_authCredentials,basic,6)) && + 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 + P(failMsg1) = "HTTP/1.0 400 Bad Request" CRLF; + printP(failMsg1); + +#ifndef WEBDUINO_SUPRESS_SERVER_HEADER + printP(webServerHeader); +#endif + + P(failMsg2) = "Content-Type: text/html" CRLF CRLF WEBDUINO_FAIL_MESSAGE; - printP(failMsg); + printP(failMsg2); } void WebServer::defaultFailCmd(WebServer &server, @@ -653,50 +696,70 @@ void WebServer::favicon(ConnectionType type) void WebServer::httpUnauthorized() { - P(failMsg) = - "HTTP/1.0 401 Authorization Required" CRLF - WEBDUINO_SERVER_HEADER + P(unauthMsg1) = "HTTP/1.0 401 Authorization Required" CRLF; + printP(unauthMsg1); + +#ifndef WEBDUINO_SUPRESS_SERVER_HEADER + printP(webServerHeader); +#endif + + P(unauthMsg2) = "Content-Type: text/html" CRLF "WWW-Authenticate: Basic realm=\"" WEBDUINO_AUTH_REALM "\"" CRLF CRLF WEBDUINO_AUTH_MESSAGE; - printP(failMsg); + printP(unauthMsg2); } void WebServer::httpServerError() { - P(failMsg) = - "HTTP/1.0 500 Internal Server Error" CRLF - WEBDUINO_SERVER_HEADER + P(servErrMsg1) = "HTTP/1.0 500 Internal Server Error" CRLF; + printP(servErrMsg1); + +#ifndef WEBDUINO_SUPRESS_SERVER_HEADER + printP(webServerHeader); +#endif + + P(servErrMsg2) = "Content-Type: text/html" CRLF CRLF WEBDUINO_SERVER_ERROR_MESSAGE; - printP(failMsg); + printP(servErrMsg2); } void WebServer::httpNoContent() { - P(noContentMsg) = - "HTTP/1.0 204 NO CONTENT" CRLF - WEBDUINO_SERVER_HEADER + P(noContentMsg1) = "HTTP/1.0 204 NO CONTENT" CRLF; + printP(noContentMsg1); + +#ifndef WEBDUINO_SUPRESS_SERVER_HEADER + printP(webServerHeader); +#endif + + P(noContentMsg2) = CRLF CRLF; - printP(noContentMsg); + printP(noContentMsg2); } void WebServer::httpSuccess(const char *contentType, const char *extraHeaders) { - P(successMsg1) = - "HTTP/1.0 200 OK" CRLF - WEBDUINO_SERVER_HEADER + P(successMsg1) = "HTTP/1.0 200 OK" CRLF; + printP(successMsg1); + +#ifndef WEBDUINO_SUPRESS_SERVER_HEADER + printP(webServerHeader); +#endif + + P(successMsg2) = "Access-Control-Allow-Origin: *" CRLF "Content-Type: "; - printP(successMsg1); + printP(successMsg2); print(contentType); printCRLF(); if (extraHeaders) @@ -706,12 +769,15 @@ void WebServer::httpSuccess(const char *contentType, void WebServer::httpSeeOther(const char *otherURL) { - P(seeOtherMsg) = - "HTTP/1.0 303 See Other" CRLF - WEBDUINO_SERVER_HEADER - "Location: "; + P(seeOtherMsg1) = "HTTP/1.0 303 See Other" CRLF; + printP(seeOtherMsg1); - printP(seeOtherMsg); +#ifndef WEBDUINO_SUPRESS_SERVER_HEADER + printP(webServerHeader); +#endif + + P(seeOtherMsg2) = "Location: "; + printP(seeOtherMsg2); print(otherURL); printCRLF(); printCRLF(); @@ -719,7 +785,7 @@ void WebServer::httpSeeOther(const char *otherURL) int WebServer::read() { - if (m_client == NULL) + if (!m_client) return -1; if (m_pushbackDepth == 0) @@ -926,7 +992,7 @@ bool WebServer::readPOSTparam(char *name, int nameLen, int ch2 = read(); if (ch1 == -1 || ch2 == -1) return false; - char hex[3] = { ch1, ch2, 0 }; + char hex[3] = { (char)ch1, (char)ch2, '\0' }; ch = strtoul(hex, NULL, 16); } @@ -1033,7 +1099,7 @@ URLPARAM_RESULT WebServer::nextURLparam(char **tail, char *name, int nameLen, *name++ = ch; --nameLen; } - else + else if(keep_scanning) result = URLPARAM_NAME_OFLO; } @@ -1089,7 +1155,7 @@ URLPARAM_RESULT WebServer::nextURLparam(char **tail, char *name, int nameLen, *value++ = ch; --valueLen; } - else + else if(keep_scanning) result = (result == URLPARAM_OK) ? URLPARAM_VALUE_OFLO : URLPARAM_BOTH_OFLO; @@ -1161,7 +1227,7 @@ void WebServer::getRequest(WebServer::ConnectionType &type, void WebServer::processHeaders() { - // look for three things: the Content-Length header, the Authorization + // 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. diff --git a/examples/Web_Net_Setup/EEPROMAnything.h b/examples/Web_Net_Setup/EEPROMAnything.h new file mode 100644 index 0000000..c548cbf --- /dev/null +++ b/examples/Web_Net_Setup/EEPROMAnything.h @@ -0,0 +1,20 @@ +#include +#include // for type definitions + +template int EEPROM_writeAnything(int ee, const T& value) +{ + const byte* p = (const byte*)(const void*)&value; + unsigned int i; + for (i = 0; i < sizeof(value); i++) + EEPROM.write(ee++, *p++); + return i; +} + +template int EEPROM_readAnything(int ee, T& value) +{ + byte* p = (byte*)(void*)&value; + unsigned int i; + for (i = 0; i < sizeof(value); i++) + *p++ = EEPROM.read(ee++); + return i; +} diff --git a/examples/Web_Net_Setup/System.cpp b/examples/Web_Net_Setup/System.cpp new file mode 100644 index 0000000..25ef78f --- /dev/null +++ b/examples/Web_Net_Setup/System.cpp @@ -0,0 +1,59 @@ +#include "Arduino.h" +#include "System.h" + +char* System::uptime() +{ + char buffer[65]; + + long days=0; + long hours=0; + long mins=0; + long secs=0; + + secs = millis()/1000; //convect milliseconds to seconds + mins=secs/60; //convert seconds to minutes + hours=mins/60; //convert minutes to hours + days=hours/24; //convert hours to days + secs=secs-(mins*60); //subtract the coverted seconds to minutes in order to display 59 secs max + mins=mins-(hours*60); //subtract the coverted minutes to hours in order to display 59 minutes max + hours=hours-(days*24); //subtract the coverted hours to days in order to display 23 hours max + + if (days > 0) { + ltoa(days,buffer,10); + strcpy(retval,buffer); + } + else { + strcpy(retval,"0"); + } + + strcat(retval,":"); + ltoa(hours,buffer,10); + strcat(retval,buffer); + + strcat(retval,":"); + ltoa(mins,buffer,10); + strcat(retval,buffer); + + strcat(retval,":"); + ltoa(secs,buffer,10); + strcat(retval,buffer); + + strcat(retval,'\0'); + + return retval; +} + +int System::ramFree () { + extern int __heap_start, *__brkval; + int v; + int a = (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); + return a; +} + +int System::ramSize() { + int v; + int a = (int) &v; + return a; +} + + diff --git a/examples/Web_Net_Setup/System.h b/examples/Web_Net_Setup/System.h new file mode 100644 index 0000000..13397c4 --- /dev/null +++ b/examples/Web_Net_Setup/System.h @@ -0,0 +1,39 @@ +#ifndef System_h +#define System_h + +#include + +/** +* System Class. +* +* @author Matthias Maderer +* @version 1.1.7 +*/ +class System { +public: + /** + * Returns the uptime of the arduino with a char pointer. + * Format: DAYS:HOURS:MINUTES:SECONDS + * Sample: 1:20:23:50 = 1 day, 20 hours, 23 minutes and 50 seconds + * @return char *: pointer! + */ + char * uptime(); + + /** + * Returns the free RAM + * @return int: free RAM + */ + int ramFree(); + + /** + * Returns the size of the RAM + * @return int: RAM size + */ + int ramSize(); + +private: + char retval[25]; +}; + +#endif + diff --git a/examples/Web_Net_Setup/Web_Net_Setup.pde b/examples/Web_Net_Setup/Web_Net_Setup.pde new file mode 100644 index 0000000..2425433 --- /dev/null +++ b/examples/Web_Net_Setup/Web_Net_Setup.pde @@ -0,0 +1,785 @@ +/* Web_Net_Setup.pde - example for a webinterface to set the network configuration +Author: Matthias Maderer +Date: 07.03.2013 +Version: 1.0.1 +web: www.edvler-blog.de/arduino_networksetup_webinterface_with_eeprom + +This is a sample Sketch for Webduino! +More informations about Webduino can be found at https://github.com/sirleech/Webduino + +For more informations about EEPROMAnything.h look at http://playground.arduino.cc/Code/EEPROMWriteAnything +*/ + +/* +* With this example its possible to configure the network configuration of the +* Arduino Ethernet Shield with a webinterface. Imagine like your router setup. +* +* It's possible to configure the following network settings: +* - MAC address +* - IP address +* - Subnet +* - Gateway +* - DNS Server +* - Webserver port +* - USE DHCP YES/NO (if you use DHCP connect per serial port - 9600 baud - on powerup to see which ip address is assigned) +* - DHCP renew interval +* +* Other functions: +* - Display DHCP renew status +* - Display DHCP renew timestamp +* - Display Arduino uptime +* - Display used RAM +* +* You can configure default settings. This settings are used wenn no configuration is present. +* Look at the function set_EEPROM_Default(). +* +* It is possible to connect a RESET button. If the button is pressed and the Arduino is turned on +* the default values would be restored too. +* See #define RESET_PIN. +* +* +* All settings are stored in EEPROM. This means they are permanent. +* Please look at http://arduino.cc/en/Reference/EEPROM for a short description. +* +* +* To setup your arduino upload this sketch. +* +* If you don't change the sourcecode the default IP address is http://192.168.0.111/ +* Don't forget to change the IP of your network adapter to a suitable address (e.g. to IP 192.168.0.1, NETMASK 255.255.255.0)! +* +* Enter the following URL for the setup page: +* http://192.168.0.111/setupNet.html +* +* Please note that no input checks are done!! +* This means that no error would be thrown if you type a wrong IP address or other failures. +* Keep this in mind. +* +* Resources: +* There are many Strings for the HTML site. The compiled size is about 27.000 Bytes. Aprox. 2000 byte of SRAM is used. +* On smaller Arduinos this may cause problems. I've tested it on a MEGA 2560. +* +* BUGS: +* - After uploading your sketch the arduino is not reachable. --> Reset your Arduino!! +*/ + + +#define WEBDUINO_FAVICON_DATA "" // no favicon +//#define DEBUG //uncomment for serial debug output +#define USE_SYSTEM_LIBRARY //comment out if you want to save some space (about 1 Byte). You wouldn't see uptime and free RAM if it's commented out. +#define SERIAL_BAUD 9600 + + +#include "SPI.h" // new include +#include "avr/pgmspace.h" // new include +#include "Ethernet.h" +#include "WebServer.h" + + +/* ############################################################################################################################################################# +* Code for the EEPROM related things +* +*/ +#include +#include "EEPROMAnything.h" + +#define RESET_PIN 40 //Connect a button to this PIN. If the button is hold, an the device is turned on the default ethernet settings are restored. + +/* structure which is stored in the eeprom. +* Look at "EEPROMAnything.h" for the functions storing and reading the struct +*/ +struct config_t +{ + byte config_set; + byte use_dhcp; + byte dhcp_refresh_minutes; + byte mac[6]; + byte ip[4]; + byte gateway[4]; + byte subnet[4]; + byte dns_server[4]; + unsigned int webserverPort; +} eeprom_config; + +/** +* set_EEPROM_Default() function +* +* The default settings. +* This settings are used when no config is present or the reset button is pressed. +*/ +void set_EEPROM_Default() { + eeprom_config.config_set=1; // dont change! It's used to check if the config is already set + + eeprom_config.use_dhcp=0; // use DHCP per default + eeprom_config.dhcp_refresh_minutes=60; // refresh the DHCP every 60 minutes + + // set the default MAC address. In this case its DE:AD:BE:EF:FE:ED + eeprom_config.mac[0]=0xDE; + eeprom_config.mac[1]=0xAD; + eeprom_config.mac[2]=0xBE; + eeprom_config.mac[3]=0xEF; + eeprom_config.mac[4]=0xFE; + eeprom_config.mac[5]=0xED; + + // set the default IP address for the arduino. In this case its 192.168.0.111 + eeprom_config.ip[0]=192; + eeprom_config.ip[1]=168; + eeprom_config.ip[2]=0; + eeprom_config.ip[3]=111; + + // set the default GATEWAY. In this case its 192.168.0.254 + eeprom_config.gateway[0]=192; + eeprom_config.gateway[1]=168; + eeprom_config.gateway[2]=0; + eeprom_config.gateway[3]=254; + + // set the default SUBNET. In this case its 255.255.255.0 + eeprom_config.subnet[0]=255; + eeprom_config.subnet[1]=255; + eeprom_config.subnet[2]=255; + eeprom_config.subnet[3]=0; + + // set the default DNS SERVER. In this case its 192.168.0.254 + eeprom_config.dns_server[0]=192; + eeprom_config.dns_server[1]=168; + eeprom_config.dns_server[2]=0; + eeprom_config.dns_server[3]=254; + + // set the default Webserver Port. In this case its Port 80 + eeprom_config.webserverPort=80; + + #ifdef DEBUG + Serial.println("Config reset"); + #endif +} + + +/** +* read_EEPROM_Settings function +* This function is used to read the EEPROM settings at startup +* +* Overview: +* - Set the PIN for the RESET-button to input and activate pullups +* - Load the stored data from EEPROM into the eeprom_config struct +* - Check if a config is stored or the reset button is pressed. If one of the conditions is ture, set the defaults +*/ +void read_EEPROM_Settings() { + pinMode(RESET_PIN, INPUT); + digitalWrite(RESET_PIN, HIGH); + + // read the current config + EEPROM_readAnything(0, eeprom_config); + + // check if config is present or if reset button is pressed + if (eeprom_config.config_set != 1 || digitalRead(RESET_PIN) == LOW) { + // set default values + set_EEPROM_Default(); + + // write the config to eeprom + EEPROM_writeAnything(0, eeprom_config); + } +} + +/** +* print_EEPROM_Settings() function +* +* This function is used for debugging the configuration. +* It prints the actual configuration to the serial port. +*/ +#ifdef DEBUG +void print_EEPROM_Settings() { + Serial.print("IP: "); + for(int i = 0; i<4; i++) { + Serial.print(eeprom_config.ip[i]); + if (i<3) { + Serial.print('.'); + } + } + Serial.println(); + + Serial.print("Subnet: "); + for(int i = 0; i<4; i++) { + Serial.print(eeprom_config.subnet[i]); + if (i<3) { + Serial.print('.'); + } + } + Serial.println(); + + Serial.print("Gateway: "); + for(int i = 0; i<4; i++) { + Serial.print(eeprom_config.gateway[i]); + if (i<3) { + Serial.print('.'); + } + } + Serial.println(); + + Serial.print("DNS Server: "); + for(int i = 0; i<4; i++) { + Serial.print(eeprom_config.dns_server[i]); + if (i<3) { + Serial.print('.'); + } + } + Serial.println(); + + Serial.print("MAC: "); + for (int a=0;a<6;a++) { + Serial.print(eeprom_config.mac[a],HEX); + if(a<5) { + Serial.print(":"); + } + } + Serial.println(); + + Serial.print("Webserver Port: "); + Serial.println(eeprom_config.webserverPort); + + Serial.print("USE DHCP: "); + Serial.println(eeprom_config.use_dhcp); + + Serial.print(" DHCP renew every "); + Serial.print(eeprom_config.dhcp_refresh_minutes); + Serial.println(" minutes"); + + Serial.print("Config Set: "); + Serial.println(eeprom_config.config_set); + +} +#endif + +// ############################################################################################################################################################# + + +/* START Network section ####################################################################################################################################### +* Code for setting up network connection +*/ +unsigned long last_dhcp_renew; +byte dhcp_state; + +/** +* renewDHCP() function +* Renew the DHCP relase in a given interval. +* +* Overview: +* - Check if interval = 0 and set it to 1 +* - Check if renew interval is reached and renew the lease +*/ +void renewDHCP(int interval) { + unsigned long interval_millis = interval * 60000; + + if (interval == 0 ) { + interval = 1; + } + if (eeprom_config.use_dhcp==1) { + if((millis() - last_dhcp_renew) > interval_millis) { + last_dhcp_renew=millis(); + dhcp_state = Ethernet.maintain(); + } + } +} + + +/** +* setupNetwork() function +* This function is used to setupup the network according to the values stored in the eeprom +* +* Overview: +* - First of all read the EEPROM settings +* - Display a link to the ethernet setup +* - Check if DHCP should be used, if not create instaces of IPAddress for ip, gateway, subnet and dns_server +* - Invoke Ethernet.begin with all parameters if no dhcp is active (Ethernet.begin(mac, ip, dns_server, gateway, subnet);). +* - If DHCP is used invoke only with mac (Ethernet.begin(mac);) and display the ip on the serial console. +*/ +void setupNetwork() { + read_EEPROM_Settings(); + + #ifdef DEBUG + print_EEPROM_Settings(); + #endif + + // byte mac[] = { eeprom_config.mac[0], eeprom_config.mac[1], eeprom_config.mac[2], eeprom_config.mac[3], eeprom_config.mac[4], eeprom_config.mac[5] }; + + if (eeprom_config.use_dhcp != 1) { + IPAddress ip(eeprom_config.ip[0], eeprom_config.ip[1], eeprom_config.ip[2], eeprom_config.ip[3]); + IPAddress gateway (eeprom_config.gateway[0],eeprom_config.gateway[1],eeprom_config.gateway[2],eeprom_config.gateway[3]); + IPAddress subnet (eeprom_config.subnet[0], eeprom_config.subnet[1], eeprom_config.subnet[2], eeprom_config.subnet[3]); + IPAddress dns_server (eeprom_config.dns_server[0], eeprom_config.dns_server[1], eeprom_config.dns_server[2], eeprom_config.dns_server[3]); + Ethernet.begin(eeprom_config.mac, ip, dns_server, gateway, subnet); + } else { + if (Ethernet.begin(eeprom_config.mac) == 0) { + Serial.print("Failed to configure Ethernet using DHCP"); + } + Serial.println(Ethernet.localIP()); + } +} +// END Network section ######################################################################################################################################### + + +/* WEB-Server section ####################################################################################################################################### +* Webserver Code +*/ + +#ifdef USE_SYSTEM_LIBRARY +#include "system.h" +System sys; +#endif + +/* Store all string in the FLASH storage to free SRAM. +The P() is a function from Webduino. +*/ +P(Page_start) = "Web_EEPROM_Setup\n"; +P(Page_end) = ""; + +P(Http400) = "HTTP 400 - BAD REQUEST"; +P(Index) = "

index.html


This is your main site!
The code is found in the indexHTML() function.
You can add more sites if you need. Please see the well documented source code.

Use the following link to setup the network.
NETWORK SETUP"; + +P(Form_eth_start) = "
"; +P(Form_end) = ""; +P(Form_input_send) = ""; + +P(Form_input_text_start) = "\n"; + +P(MAC) = "MAC address: "; +P(IP) = "IP address: "; +P(SUBNET) = "Subnet: "; +P(GW) = "GW address: "; +P(DNS_SERVER) = "DNS server: "; +P(WEB_PORT) = "Webserver port (1-65535): "; +P(DHCP_ACTIVE) = "Use DHCP: "; +P(DHCP_REFRESH) = "Renew interval for DHCP in minutes (1 - 255): "; + +P(Form_cb) = "New configuration stored!
Please turn off and on your Arduino or use the reset button!
"; + +P(DHCP_STATE_TIME) = "DHCP last renew timestamp (sec)"; +P(DHCP_STATE) = "DHCP renew return code (sec)"; + + +P(UPTIME) = "Uptime: "; + +#ifdef USE_SYSTEM_LIBRARY +P(RAM_1) = "RAM (byte): "; +P(RAM_2) = " free of "; +#endif + +/* This creates an pointer to instance of the webserver. */ +WebServer * webserver; + + +/** +* indexHTML() function +* This function is used to send the index.html to the client. +*/ +void indexHTML(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); + + server.printP(Index); + + server.printP(Page_end); + +} + +/** +* setupNetHTML() function +* This function is used to send the setupNet.html to the client. +* +* Overview: +* - Send a HTTP 200 OK Header +* - If get parameters exists assign them to the corresponding variable in the eeprom_config struct +* - Print the configuration +* +* Parameters are simple numbers. The name of the parameter is converted to an int with the atoi function. +* This saves some code for setting the MAC and IP addresses. +*/ +#define NAMELEN 5 +#define VALUELEN 7 +void setupNetHTML(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + URLPARAM_RESULT rc; + char name[NAMELEN]; + char value[VALUELEN]; + boolean params_present = false; + byte param_number = 0; + + /* 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); + + // check for parameters + if (strlen(url_tail)) { + while (strlen(url_tail)) { + rc = server.nextURLparam(&url_tail, name, NAMELEN, value, VALUELEN); + if (rc != URLPARAM_EOS) { + params_present=true; + // debug output for parameters + #ifdef DEBUG + Serial.print(name); + server.print(name); + Serial.print(" - "); + server.print(" - "); + Serial.println(value); + server.print(value); + server.print("
"); + #endif + + + param_number = atoi(name); + + // read MAC address + if (param_number >=0 && param_number <=5) { + eeprom_config.mac[param_number]=strtol(value,NULL,16); + } + + // read IP address + if (param_number >=6 && param_number <=9) { + eeprom_config.ip[param_number-6]=atoi(value); + } + + // read SUBNET + if (param_number >=10 && param_number <=13) { + eeprom_config.subnet[param_number-10]=atoi(value); + } + + // read GATEWAY + if (param_number >=14 && param_number <=17) { + eeprom_config.gateway[param_number-14]=atoi(value); + } + + // read DNS-SERVER + if (param_number >=18 && param_number <=21) { + eeprom_config.dns_server[param_number-18]=atoi(value); + } + + // read WEBServer port + if (param_number == 22) { + eeprom_config.webserverPort=atoi(value); + } + + // read DHCP ON/OFF + if (param_number == 23) { + eeprom_config.use_dhcp=atoi(value); + } + + // read DHCP renew interval + if (param_number == 24) { + eeprom_config.dhcp_refresh_minutes=atoi(value); + } + } + } + EEPROM_writeAnything(0, eeprom_config); + } + + //print the form + server.printP(Form_eth_start); + + if(params_present==true) { + server.printP(Config_set); + } + + server.printP(table_start); + + // print the current MAC + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(MAC); + server.printP(table_td_end); + server.printP(table_td_start); + for (int a=0;a<6;a++) { + server.printP(Form_input_text_start); + server.print(a); + server.printP(Form_input_value); + server.print(eeprom_config.mac[a],HEX); + server.printP(Form_input_size2); + server.printP(Form_input_end); + } + server.printP(table_td_end); + server.printP(table_tr_end); + + // print the current IP + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(IP); + server.printP(table_td_end); + server.printP(table_td_start); + for (int a=0;a<4;a++) { + server.printP(Form_input_text_start); + server.print(a+6); + server.printP(Form_input_value); + server.print(eeprom_config.ip[a]); + server.printP(Form_input_size3); + server.printP(Form_input_end); + } + server.printP(table_td_end); + server.printP(table_tr_end); + + + // print the current SUBNET + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(SUBNET); + server.printP(table_td_end); + server.printP(table_td_start); + for (int a=0;a<4;a++) { + server.printP(Form_input_text_start); + server.print(a+10); + server.printP(Form_input_value); + server.print(eeprom_config.subnet[a]); + server.printP(Form_input_size3); + server.printP(Form_input_end); + } + server.printP(table_td_end); + server.printP(table_tr_end); + + // print the current GATEWAY + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(GW); + server.printP(table_td_end); + server.printP(table_td_start); + for (int a=0;a<4;a++) { + server.printP(Form_input_text_start); + server.print(a+14); + server.printP(Form_input_value); + server.print(eeprom_config.gateway[a]); + server.printP(Form_input_size3); + server.printP(Form_input_end); + } + server.printP(table_td_end); + server.printP(table_tr_end); + + // print the current DNS-SERVER + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(DNS_SERVER); + server.printP(table_td_end); + server.printP(table_td_start); + for (int a=0;a<4;a++) { + server.printP(Form_input_text_start); + server.print(a+18); + server.printP(Form_input_value); + server.print(eeprom_config.dns_server[a]); + server.printP(Form_input_size3); + server.printP(Form_input_end); + } + server.printP(table_td_end); + server.printP(table_tr_end); + + + // print the current webserver port + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(WEB_PORT); + server.printP(table_td_end); + server.printP(table_td_start); + server.printP(Form_input_text_start); + server.print(22); + server.printP(Form_input_value); + server.print(eeprom_config.webserverPort); + server.printP(Form_input_end); + server.printP(table_td_end); + server.printP(table_tr_end); + + //print the current DHCP config + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(DHCP_ACTIVE); + server.printP(table_td_end); + server.printP(table_td_start); + server.printP(Form_cb); + server.print("0\""); + if(eeprom_config.use_dhcp != 1) { + server.printP(Form_cb_checked); + } + server.printP(Form_cb_off); + + server.printP(Form_cb); + server.print("1\""); + if(eeprom_config.use_dhcp == 1) { + server.printP(Form_cb_checked); + } + server.printP(Form_cb_on); + server.printP(table_td_end); + server.printP(table_tr_end); + + //print the current DHCP renew time + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(DHCP_REFRESH); + server.printP(table_td_end); + server.printP(table_td_start); + server.printP(Form_input_text_start); + server.print(24); + server.printP(Form_input_value); + server.print(eeprom_config.dhcp_refresh_minutes); + server.printP(Form_input_size3); + server.printP(Form_input_end); + server.printP(table_td_end); + server.printP(table_tr_end); + + //print DHCP status + if(eeprom_config.use_dhcp == 1) { + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(DHCP_STATE); + server.printP(table_td_end); + server.printP(table_td_start); + server.print(dhcp_state); + server.printP(table_td_end); + server.printP(table_tr_end); + + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(DHCP_STATE_TIME); + server.printP(table_td_end); + server.printP(table_td_start); + server.print(last_dhcp_renew/1000); + server.printP(table_td_end); + server.printP(table_tr_end); + } + + #ifdef USE_SYSTEM_LIBRARY + //print uptime + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(UPTIME); + server.printP(table_td_end); + server.printP(table_td_start); + server.print(sys.uptime()); + server.printP(table_td_end); + server.printP(table_tr_end); + + server.printP(table_tr_start); + server.printP(table_td_start); + server.printP(RAM_1); + server.print(sys.ramFree()); + server.printP(RAM_2); + server.print(sys.ramSize()); + server.printP(table_td_end); + server.printP(table_tr_end); + #endif + + server.printP(table_end); + + //print the send button + server.printP(Form_input_send); + server.printP(Form_end); + + + + server.printP(Page_end); + +} + +/** +* errorHTML() function +* This function is called whenever a non extisting page is called. +* It sends a HTTP 400 Bad Request header and the same as text. +*/ +void errorHTML(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + /* this line sends the standard "HTTP 400 Bad Request" 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(Http400); + + server.printP(Page_end); +} + +// END WEBCODE ###################################################################################################################################################### + +/** +* setup() function +* This function is called whenever the arduino is turned on. +*/ +void setup() +{ + Serial.begin(SERIAL_BAUD); + + /* initialize the Ethernet adapter with the settings from eeprom */ + delay(200); // some time to settle + setupNetwork(); + delay(200); // some time to settle + + #define PREFIX "" + webserver = new WebServer(PREFIX, eeprom_config.webserverPort); + + /* setup our default command that will be run when the user accesses + * the root page on the server */ + webserver->setDefaultCommand(&indexHTML); + + /* setup our default command that will be run when the user accesses + * a page NOT on the server */ + webserver->setFailureCommand(&errorHTML); + + /* run the same command if you try to load /index.html, a common + * default page name */ + webserver->addCommand("index.html", &indexHTML); + + /* display a network setup form. The configuration is stored in eeprom */ + webserver->addCommand("setupNet.html", &setupNetHTML); + + /* start the webserver */ + webserver->begin(); +} + +/** +* loop() function +* Runs forver .... +* +* Overview: +* - Renew the DHCP lease +* - Serve web clients +* +*/ +void loop() +{ + // renew DHCP lease + renewDHCP(eeprom_config.dhcp_refresh_minutes); + + char buff[200]; + int len = 200; + + /* process incoming connections one at a time forever */ + webserver->processConnection(buff, &len); +} diff --git a/examples/Web_Parms/Web_Parms.ino b/examples/Web_Parms/Web_Parms.ino index 4b848a5..504bc81 100644 --- a/examples/Web_Parms/Web_Parms.ino +++ b/examples/Web_Parms/Web_Parms.ino @@ -154,9 +154,7 @@ void parsedCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail { 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 */ @@ -219,7 +217,7 @@ void parsedCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail 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 + /* this line sends the "HTTP 400 - Bad Request" headers back to the browser */ server.httpFail(); diff --git a/readme.md b/readme.md index 2ef7e78..e312bc1 100644 --- a/readme.md +++ b/readme.md @@ -19,9 +19,11 @@ You can put the examples in your own sketchbook directory, or in hardware/librar 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 +## Resources + +- [Wedbuino Presentation on Google Docs](http://docs.google.com/present/view?id=dd8gqxt8_5c8w9qfg3) +- [A Wedbuino based file server](http://playground.arduino.cc//Main/WebduinoFileServer) -[Wedbuino Presentation on Google Docs](http://docs.google.com/present/view?id=dd8gqxt8_5c8w9qfg3) ## Compatible Ethernet Shields