From 03ac4436b758cfd55b53e4520932587d87e1b2d6 Mon Sep 17 00:00:00 2001 From: Peter Hardy Date: Sun, 6 Jan 2013 20:50:01 +1100 Subject: [PATCH 01/23] Updated PROGMEM data types for current avr-libc. --- WebServer.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WebServer.h b/WebServer.h index 5bc6323..8647e09 100644 --- a/WebServer.h +++ b/WebServer.h @@ -127,7 +127,7 @@ extern "C" unsigned long millis(void); // declare a static string -#define P(name) static const prog_uchar name[] PROGMEM +#define P(name) static const unsigned char name[] PROGMEM // returns the number of elements in the array #define SIZE(array) (sizeof(array) / sizeof(*array)) @@ -205,13 +205,13 @@ 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); } // 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, @@ -403,7 +403,7 @@ 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) +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 @@ -425,7 +425,7 @@ void WebServer::writeP(const prog_uchar *data, size_t length) 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 From 56929bf5a1291e57093f6cf2180634114f35c276 Mon Sep 17 00:00:00 2001 From: cat101 Date: Fri, 8 Feb 2013 19:30:01 -0200 Subject: [PATCH 02/23] Update readme.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a link to a snippet that shows how to do a file server on top of webduino --- readme.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 From 2d224fd83d429b838dcf824619809f943c802c9e Mon Sep 17 00:00:00 2001 From: cat101 Date: Tue, 12 Feb 2013 10:39:43 -0200 Subject: [PATCH 03/23] Update WebServer.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nextURLparam logic was broken. On succesfull parse would return URLPARAM_BOTH_OFLO --- WebServer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WebServer.h b/WebServer.h index 8647e09..9d46871 100644 --- a/WebServer.h +++ b/WebServer.h @@ -1033,7 +1033,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 +1089,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; From 5df8eb8c17c9bfe69f5c450a7b085f72c661e98a Mon Sep 17 00:00:00 2001 From: EDVler Date: Fri, 1 Mar 2013 17:16:52 +0100 Subject: [PATCH 04/23] Corrected one comment and remove to variables - Corrected the comment in the my_failCmd function. - Remove declaration of int name_len and int value_len (not used) --- examples/Web_Parms/Web_Parms.ino | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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(); From 4e41386ec3a706f6728fef7f6103b18cc57e3535 Mon Sep 17 00:00:00 2001 From: "www.edvler-blog.de" Date: Thu, 7 Mar 2013 17:59:06 +0100 Subject: [PATCH 05/23] Added first version of Web_Net_Setup.pde example With this example Sketch it's possible to configure the network settings over a webinterface. It's simple to add some custom fields. --- examples/Web_Net_Setup/EEPROMAnything.h | 20 + examples/Web_Net_Setup/Web_Net_Setup.pde | 679 +++++++++++++++++++++++ 2 files changed, 699 insertions(+) create mode 100644 examples/Web_Net_Setup/EEPROMAnything.h create mode 100644 examples/Web_Net_Setup/Web_Net_Setup.pde 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/Web_Net_Setup.pde b/examples/Web_Net_Setup/Web_Net_Setup.pde new file mode 100644 index 0000000..3ad54c7 --- /dev/null +++ b/examples/Web_Net_Setup/Web_Net_Setup.pde @@ -0,0 +1,679 @@ +/* Web_Net_Setup.pde - example for a webinterface to set the network configuration +Author: Matthias Maderer +Date: 07.03.2013 +Version: 1.0 +web: www.edvler-blog.de + +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 board over a webinterface. Imagine like your router setup. +* +* It's possible to configure the following network settings: +* - IP address +* - Subnet +* - Gateway +* - DNS Server +* - Webserver port +* - USE DHCP YES/NO +* - DHCP renew interval +* +* 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: +* 27.000 Bytes +* +* 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 + + +#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; + +/* The default Ethernet 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:11:22 + eeprom_config.mac[0]=0xDE; + eeprom_config.mac[1]=0xAD; + eeprom_config.mac[2]=0xBE; + eeprom_config.mac[3]=0xEF; + eeprom_config.mac[4]=0x11; + eeprom_config.mac[5]=0x11; + + // set the default IP address for the arduino. In this case its 172.31.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 172.31.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 172.31.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); + } +} + +#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 ####################################################################################################################################### +* Webserver Code +*/ +long last_dhcp_renew; +byte dhcp_state; + +/** +renewDHCP() function +Renew the DHCP relase in a given interval. +*/ +void renewDHCP(int interval) { + if (interval == 0 ) { + interval = 1; + } + if (eeprom_config.use_dhcp==1) { + if((millis() - last_dhcp_renew) > (interval*60*1000)) { + last_dhcp_renew=millis(); + dhcp_state = Ethernet.maintain(); + } + } +} + + +/** +* setupNetwork() function (webpage) +* 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 ######################################################################################################################################### + + + +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!
"; + +/* + + + .. mehr auf http://w-w-w.ms/a1v0si#3127029 +*/ + +//P(Form_input_cb_start) = ""); + #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); + } + + // if no params given show the actual configuration + + 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); + + + + server.printP(table_end); + + //print the send button + server.printP(Form_input_send); + server.printP(Form_end); + + server.printP(Page_end); + +} + +void errorHTML(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) +{ + /* this line sends the standard "HTPP 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); +} + + + + +void setup() +{ + #ifdef DEBUG + Serial.begin(9600); + #endif + + /* 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(); +} + +void loop() +{ + char buff[200]; + int len = 200; + + /* process incoming connections one at a time forever */ + webserver->processConnection(buff, &len); +} From 168ad89497e4d188852f0c50828f893f1ca41f82 Mon Sep 17 00:00:00 2001 From: "www.edvler-blog.de" Date: Fri, 8 Mar 2013 18:56:50 +0100 Subject: [PATCH 06/23] Updated Web_Net_Setup and added some featues - Updated some comments - Fixed some failures in the source code - Added System.h and System.cpp to display RAM usage and uptime --- examples/Web_Net_Setup/System.cpp | 59 +++ examples/Web_Net_Setup/System.h | 39 ++ examples/Web_Net_Setup/Web_Net_Setup.pde | 436 ++++++++++++++--------- 3 files changed, 368 insertions(+), 166 deletions(-) create mode 100644 examples/Web_Net_Setup/System.cpp create mode 100644 examples/Web_Net_Setup/System.h 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 index 3ad54c7..196f287 100644 --- a/examples/Web_Net_Setup/Web_Net_Setup.pde +++ b/examples/Web_Net_Setup/Web_Net_Setup.pde @@ -1,7 +1,7 @@ /* Web_Net_Setup.pde - example for a webinterface to set the network configuration Author: Matthias Maderer Date: 07.03.2013 -Version: 1.0 +Version: 1.0.1 web: www.edvler-blog.de This is a sample Sketch for Webduino! @@ -22,6 +22,10 @@ For more informations about EEPROMAnything.h look at http://playground.arduino.c * - Webserver port * - USE DHCP YES/NO * - DHCP renew interval +* +* Other functions: +* - Display DHCP renew status +* - Display used RAM * * You can configure default settings. This settings are used wenn no configuration is present. * Look at the function set_EEPROM_Default(). @@ -48,7 +52,8 @@ For more informations about EEPROMAnything.h look at http://playground.arduino.c * Keep this in mind. * * Resources: -* 27.000 Bytes +* 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!! @@ -57,6 +62,7 @@ For more informations about EEPROMAnything.h look at http://playground.arduino.c #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. #include "SPI.h" // new include @@ -90,7 +96,10 @@ struct config_t unsigned int webserverPort; } eeprom_config; -/* The default Ethernet settings. +/** +* 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() { @@ -99,21 +108,21 @@ void set_EEPROM_Default() { 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:11:22 + // 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]=0x11; - eeprom_config.mac[5]=0x11; + eeprom_config.mac[4]=0xFE; + eeprom_config.mac[5]=0xED; - // set the default IP address for the arduino. In this case its 172.31.0.111 + // 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 172.31.0.254 + // 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; @@ -125,7 +134,7 @@ void set_EEPROM_Default() { eeprom_config.subnet[2]=255; eeprom_config.subnet[3]=0; - // set the default DNS SERVER. In this case its 172.31.0.254 + // 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; @@ -166,6 +175,12 @@ void read_EEPROM_Settings() { } } +/** +* 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: "); @@ -233,21 +248,27 @@ void print_EEPROM_Settings() { /* START Network section ####################################################################################################################################### -* Webserver Code +* Code for setting up network connection */ -long last_dhcp_renew; +unsigned long last_dhcp_renew; byte dhcp_state; /** -renewDHCP() function -Renew the DHCP relase in a given interval. +* 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*60*1000)) { + if((millis() - last_dhcp_renew) > interval_millis) { last_dhcp_renew=millis(); dhcp_state = Ethernet.maintain(); } @@ -256,7 +277,7 @@ void renewDHCP(int interval) { /** -* setupNetwork() function (webpage) +* setupNetwork() function * This function is used to setupup the network according to the values stored in the eeprom * * Overview: @@ -291,7 +312,18 @@ void setupNetwork() { // 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) = ""; @@ -333,25 +365,25 @@ P(table_end) = ""; P(Config_set) = "New configuration stored!
Please turn off and on your Arduino or use the reset button!

"; -/* - - - .. mehr auf http://w-w-w.ms/a1v0si#3127029 -*/ +P(UPTIME) = "Uptime: "; -//P(Form_input_cb_start) = "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; From 871af354ce8975d3d7cd0164deff21ffbf6290da Mon Sep 17 00:00:00 2001 From: "www.edvler-blog.de" Date: Wed, 13 Mar 2013 17:23:05 +0100 Subject: [PATCH 07/23] Updated comments. Fixed some bugs. --- examples/Web_Net_Setup/Web_Net_Setup.pde | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/Web_Net_Setup/Web_Net_Setup.pde b/examples/Web_Net_Setup/Web_Net_Setup.pde index 196f287..2425433 100644 --- a/examples/Web_Net_Setup/Web_Net_Setup.pde +++ b/examples/Web_Net_Setup/Web_Net_Setup.pde @@ -2,7 +2,7 @@ Author: Matthias Maderer Date: 07.03.2013 Version: 1.0.1 -web: www.edvler-blog.de +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 @@ -12,19 +12,22 @@ For more informations about EEPROMAnything.h look at http://playground.arduino.c /* * With this example its possible to configure the network configuration of the -* Arduino board over a webinterface. Imagine like your router setup. +* 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 +* - 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. @@ -63,6 +66,7 @@ For more informations about EEPROMAnything.h look at http://playground.arduino.c #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 @@ -730,9 +734,7 @@ void errorHTML(WebServer &server, WebServer::ConnectionType type, char *url_tail */ void setup() { - #ifdef DEBUG - Serial.begin(9600); - #endif + Serial.begin(SERIAL_BAUD); /* initialize the Ethernet adapter with the settings from eeprom */ delay(200); // some time to settle From b030ea9886599933914743a0349edcc52437878c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jupp=20M=C3=BCller?= Date: Wed, 27 Mar 2013 14:29:00 +0100 Subject: [PATCH 08/23] Ported to Arduino Due platform --- WebServer.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/WebServer.h b/WebServer.h index 9d46871..c438ebd 100644 --- a/WebServer.h +++ b/WebServer.h @@ -127,11 +127,18 @@ extern "C" unsigned long millis(void); // declare a static string +#ifdef __AVR__ #define P(name) static const unsigned char name[] PROGMEM +#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 ********************************************************************/ From 9e2633ab7b5226ffabcd926b1b4b5bd1e858f1d1 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Mon, 30 Dec 2013 12:12:50 +0000 Subject: [PATCH 09/23] Remove typedef specifier from URLPARAM_RESULT enum Remove the typedef storage class specifier from the declaration of the URLPARAM_RESULT enum as this is not required and causes gcc to emit the warning "'typedef' was ignored in this declaration" when compiling with -Wall. --- WebServer.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/WebServer.h b/WebServer.h index c438ebd..879b6a8 100644 --- a/WebServer.h +++ b/WebServer.h @@ -147,11 +147,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 From 5df418fa5c0cbf9bc607a07a40e121f08d764043 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Mon, 30 Dec 2013 12:33:27 +0000 Subject: [PATCH 10/23] Re-order initializer list to match member order Correct the order of the initializer list so that gcc doesn't emit the warning "'WebServer::m_cmdCount' will be initialized after 'int WebServer::m_contentLength' when initialized here" when compiling using -Wall. --- WebServer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebServer.h b/WebServer.h index 879b6a8..a1b2ba2 100644 --- a/WebServer.h +++ b/WebServer.h @@ -353,10 +353,10 @@ WebServer::WebServer(const char *urlPrefix, int port) : m_client(255), m_urlPrefix(urlPrefix), m_pushbackDepth(0), - m_cmdCount(0), m_contentLength(0), m_failureCmd(&defaultFailCmd), m_defaultCmd(&defaultFailCmd), + m_cmdCount(0), m_urlPathCmd(NULL) { } From 8388e16e33438cf2bb596119feb9b3b784017f5d Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Mon, 30 Dec 2013 12:38:57 +0000 Subject: [PATCH 11/23] Add extra brackets to while loop in printP Add an additional set of brackets around the while loop condition in WebServer::printP to stop gcc emitting the warning "suggest parentheses around assignment used as truth value" when compiling using -Wall. --- WebServer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebServer.h b/WebServer.h index a1b2ba2..791b22f 100644 --- a/WebServer.h +++ b/WebServer.h @@ -439,7 +439,7 @@ void WebServer::printP(const unsigned char *str) uint8_t buffer[32]; size_t bufferEnd = 0; - while (buffer[bufferEnd++] = pgm_read_byte(str++)) + while ((buffer[bufferEnd++] = pgm_read_byte(str++))) { if (bufferEnd == 32) { From 0b91dc0bb8b27ffa837154dd94f72312cad9d8c3 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Mon, 30 Dec 2013 12:44:22 +0000 Subject: [PATCH 12/23] Change some ints and chars to unsigned Convert some of the int and char variables to be unsigned int and unsigned char types. This prevents gcc generating the warnings "comparison between signed and unsigned integer expressions" and "array subscript has type 'char'", as well as having a nice side-effect of shaving 42 bytes off the compiled size when building for the Uno. --- WebServer.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WebServer.h b/WebServer.h index 791b22f..74b0502 100644 --- a/WebServer.h +++ b/WebServer.h @@ -308,7 +308,7 @@ class WebServer: public Print const char *m_urlPrefix; unsigned char m_pushback[32]; - char m_pushbackDepth; + unsigned char m_pushbackDepth; int m_contentLength; char m_authCredentials[51]; @@ -321,7 +321,7 @@ class WebServer: public Print const char *verb; Command *cmd; } m_commands[8]; - char m_cmdCount; + unsigned char m_cmdCount; UrlPathCommand *m_urlPathCmd; void reset(); @@ -480,9 +480,9 @@ bool WebServer::dispatchCommand(ConnectionType requestType, char *verb, // if the first character is a slash, there's more after it. if (verb[0] == '/') { - char i; + unsigned char i; char *qm_loc; - int verb_len; + unsigned int verb_len; int qm_offset; // Skip over the leading "/", because it makes the code more // efficient and easier to understand. From 128c7f4c2c90f2cb632e92af0dfecc9e7f6979f8 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Mon, 30 Dec 2013 12:58:22 +0000 Subject: [PATCH 13/23] Hide warning when passing string to m_defaultCmd The Command prototype has a char* parameter for the URL tail, but WebServer::dispatchCommand calls this with an empty string as a parameter if there is no URL. As gcc treats empty strings as const char*s this causes a warning to be emitted when compiling using -Wall, so suppress it by explicitly casting it to char*. --- WebServer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebServer.h b/WebServer.h index 74b0502..022e45b 100644 --- a/WebServer.h +++ b/WebServer.h @@ -465,7 +465,7 @@ bool WebServer::dispatchCommand(ConnectionType requestType, char *verb, // 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 From 8b022f601ddb475c2dda4a75273823f44250958f Mon Sep 17 00:00:00 2001 From: Ben Combee Date: Mon, 30 Dec 2013 09:08:03 -0600 Subject: [PATCH 14/23] Merge with local changes for spacing --- WebServer.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/WebServer.h b/WebServer.h index 022e45b..a486bca 100644 --- a/WebServer.h +++ b/WebServer.h @@ -272,7 +272,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(); @@ -438,7 +438,7 @@ void WebServer::printP(const unsigned char *str) // 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) @@ -461,7 +461,7 @@ void WebServer::printCRLF() 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))) { @@ -613,7 +613,7 @@ 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; } @@ -1168,7 +1168,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. From 3125b21fa9d2b246d7f61b5eafa3db61eaccb015 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Wed, 1 Jan 2014 16:08:43 +0000 Subject: [PATCH 15/23] Fix compile now EthernetClient == is overloaded Remove comparison of m_client against NULL as this causes a compilation error since arduino/Arduino@ca37de4ba4ecbdb941f14ac1fe7dd40f3008af75. Also initialize m_client with MAX_SOCK_NUMBER to make boolean test of m_client work correctly. --- WebServer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WebServer.h b/WebServer.h index a486bca..df3905c 100644 --- a/WebServer.h +++ b/WebServer.h @@ -350,7 +350,7 @@ class WebServer: public Print WebServer::WebServer(const char *urlPrefix, int port) : m_server(port), - m_client(255), + m_client(MAX_SOCK_NUM), m_urlPrefix(urlPrefix), m_pushbackDepth(0), m_contentLength(0), @@ -726,7 +726,7 @@ void WebServer::httpSeeOther(const char *otherURL) int WebServer::read() { - if (m_client == NULL) + if (!m_client) return -1; if (m_pushbackDepth == 0) From 8ce7dbc39d10f9cf5a071410ef784ea50f72485b Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Thu, 16 Jan 2014 17:37:18 +0000 Subject: [PATCH 16/23] Add buffering for all output Update WebServer::write(uint8_t) to write to a buffer instead of sending single characters (or short strings) as small packets. Remove the other signatures of write so that the base ones in the Print class (which call write(uint8_t)) are used instead and simplify the writeP/printP definitions to do this too. This greatly improves the performance of pages which are built from a large number of small strings or values at the expense of a small additional amount of memory (which can be adjusted with the use of a new macro called WEBDUINO_OUTPUT_BUFFER_SIZE) and also saves ~85 bytes of flash. --- WebServer.h | 75 ++++++++++++++++++++++------------------------------- 1 file changed, 31 insertions(+), 44 deletions(-) diff --git a/WebServer.h b/WebServer.h index df3905c..4e25ce3 100644 --- a/WebServer.h +++ b/WebServer.h @@ -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 @@ -295,9 +299,6 @@ 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(); @@ -324,6 +325,9 @@ class WebServer: public Print unsigned char m_cmdCount; UrlPathCommand *m_urlPathCmd; + uint8_t m_buffer[WEBDUINO_OUTPUT_BUFFER_SIZE]; + uint8_t m_bufFill; + void reset(); void getRequest(WebServer::ConnectionType &type, char *request, int *length); bool dispatchCommand(ConnectionType requestType, char *verb, @@ -337,6 +341,7 @@ class WebServer: public Print char *url_tail, bool tail_complete); void noRobots(ConnectionType type); void favicon(ConnectionType type); + void flushBuf(); }; /* define this macro if you want to include the header in a sketch source @@ -357,7 +362,8 @@ WebServer::WebServer(const char *urlPrefix, int port) : m_failureCmd(&defaultFailCmd), m_defaultCmd(&defaultFailCmd), m_cmdCount(0), - m_urlPathCmd(NULL) + m_urlPathCmd(NULL), + m_bufFill(0) { } @@ -392,70 +398,49 @@ 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; + } -size_t WebServer::write(const uint8_t *buffer, size_t size) -{ - return m_client.write(buffer, size); + return sizeof(ch); } -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 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 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; + // copy data out of program memory into local storage - while ((buffer[bufferEnd++] = pgm_read_byte(str++))) + 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); } bool WebServer::dispatchCommand(ConnectionType requestType, char *verb, @@ -603,6 +588,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 From c50b210497aa7fe446401ec229aaee2b63993d54 Mon Sep 17 00:00:00 2001 From: cat101 Date: Sun, 2 Feb 2014 22:32:22 -0200 Subject: [PATCH 17/23] Update WebServer.h Hi again. Here is a summary of the changes - The recent patches did not compile. On Arduino 1.0.5 MAX_SOCK_NUM was missing so I included ethernet.h - I added back write(const uint8_t *buffer, size_t size) which was removed with the addition of buffering - I added printf for convenience - I made reset & flushBuf public. I'll continue to use the current version and report any bugs. I think that it may be a good time to up the version number Thanks --- WebServer.h | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/WebServer.h b/WebServer.h index 4e25ce3..02a9a85 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 @@ -221,6 +223,12 @@ class WebServer: public Print // inline overload for printP to handle signed char strings 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 unsigned char *data, size_t length); @@ -299,10 +307,16 @@ class WebServer: public Print // implementation of write used to implement Print interface virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buffer, size_t size); // 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; @@ -328,7 +342,6 @@ class WebServer: public Print uint8_t m_buffer[WEBDUINO_OUTPUT_BUFFER_SIZE]; uint8_t m_bufFill; - void reset(); void getRequest(WebServer::ConnectionType &type, char *request, int *length); bool dispatchCommand(ConnectionType requestType, char *verb, bool tail_complete); @@ -341,7 +354,6 @@ class WebServer: public Print char *url_tail, bool tail_complete); void noRobots(ConnectionType type); void favicon(ConnectionType type); - void flushBuf(); }; /* define this macro if you want to include the header in a sketch source @@ -409,6 +421,12 @@ size_t WebServer::write(uint8_t ch) 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); +} + void WebServer::flushBuf() { if(m_bufFill > 0) @@ -443,6 +461,32 @@ void WebServer::printCRLF() 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) { From c41b558b888250771c7a7e80e9b75a0c325992b1 Mon Sep 17 00:00:00 2001 From: ntruchsess Date: Fri, 28 Feb 2014 13:52:37 +0100 Subject: [PATCH 18/23] constructor EthernetClient(MAX_SOCK_NUM) is undocumented and equivalent to EthernetClient() anyway --- WebServer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebServer.h b/WebServer.h index 02a9a85..6025264 100644 --- a/WebServer.h +++ b/WebServer.h @@ -367,7 +367,7 @@ class WebServer: public Print WebServer::WebServer(const char *urlPrefix, int port) : m_server(port), - m_client(MAX_SOCK_NUM), + m_client(), m_urlPrefix(urlPrefix), m_pushbackDepth(0), m_contentLength(0), From b2f650fb3bdcdb774dd55c9de6ef44240a96aeae Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Sun, 16 Mar 2014 15:45:34 +0000 Subject: [PATCH 19/23] Reduce size and clarify type of some integer vars Trim down the size of a couple of variables as they didn't need to be as large, clarify the type of some others, and change port from int to uint16_t to match EthernetServer. --- WebServer.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WebServer.h b/WebServer.h index 6025264..7cb5c7c 100644 --- a/WebServer.h +++ b/WebServer.h @@ -183,7 +183,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(); @@ -365,7 +365,7 @@ 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(), m_urlPrefix(urlPrefix), @@ -509,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] == '/') { - unsigned char i; + uint8_t i; char *qm_loc; - unsigned 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++; @@ -539,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; From f95122d70309f343740be6264853c9274fdef766 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Sun, 23 Mar 2014 16:58:12 +0000 Subject: [PATCH 20/23] Put flash strings into separate sections Change the definition of the P() macro under the AVR architecture to store the strings in sections named after the variables. This allows unused strings to be removed by the linker, saving space in the flash memory. Also change the variable name for all but one of the instances of failMsg to give the maximum benefit from the above. --- WebServer.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/WebServer.h b/WebServer.h index 7cb5c7c..e88ce74 100644 --- a/WebServer.h +++ b/WebServer.h @@ -134,7 +134,7 @@ extern "C" unsigned long millis(void); // declare a static string #ifdef __AVR__ -#define P(name) static const unsigned char name[] PROGMEM +#define P(name) static const unsigned char name[] __attribute__(( section(".progmem." #name) )) #else #define P(name) static const unsigned char name[] #endif @@ -691,7 +691,7 @@ void WebServer::favicon(ConnectionType type) void WebServer::httpUnauthorized() { - P(failMsg) = + P(unauthMsg) = "HTTP/1.0 401 Authorization Required" CRLF WEBDUINO_SERVER_HEADER "Content-Type: text/html" CRLF @@ -699,19 +699,19 @@ void WebServer::httpUnauthorized() CRLF WEBDUINO_AUTH_MESSAGE; - printP(failMsg); + printP(unauthMsg); } void WebServer::httpServerError() { - P(failMsg) = + P(servErrMsg) = "HTTP/1.0 500 Internal Server Error" CRLF WEBDUINO_SERVER_HEADER "Content-Type: text/html" CRLF CRLF WEBDUINO_SERVER_ERROR_MESSAGE; - printP(failMsg); + printP(servErrMsg); } void WebServer::httpNoContent() From 6b974fd4e9245e8e3029d7cf93456dfc9d592da3 Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Sun, 23 Mar 2014 17:54:28 +0000 Subject: [PATCH 21/23] Save flash by only storing the server header once Modify the functions which send headers to re-use one string containing the server name instead of including it inline in all of the different headers. --- WebServer.h | 86 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/WebServer.h b/WebServer.h index e88ce74..dadc763 100644 --- a/WebServer.h +++ b/WebServer.h @@ -40,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" @@ -379,6 +373,8 @@ WebServer::WebServer(const char *urlPrefix, uint16_t port) : { } +P(webServerHeader) = "Server: Webduino/" WEBDUINO_VERSION_STRING CRLF; + void WebServer::begin() { m_server.begin(); @@ -651,14 +647,19 @@ bool WebServer::checkCredentials(const char authCredentials[45]) 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, @@ -691,50 +692,70 @@ void WebServer::favicon(ConnectionType type) void WebServer::httpUnauthorized() { - P(unauthMsg) = - "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(unauthMsg); + printP(unauthMsg2); } void WebServer::httpServerError() { - P(servErrMsg) = - "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(servErrMsg); + 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) @@ -744,12 +765,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); + +#ifndef WEBDUINO_SUPRESS_SERVER_HEADER + printP(webServerHeader); +#endif - printP(seeOtherMsg); + P(seeOtherMsg2) = "Location: "; + printP(seeOtherMsg2); print(otherURL); printCRLF(); printCRLF(); From 4ee01d3cd37422a5edf74c00c47a4e59ceb1d1ef Mon Sep 17 00:00:00 2001 From: mkldon Date: Thu, 10 Apr 2014 16:44:09 +0400 Subject: [PATCH 22/23] Add WEBDUINO_COMMANDS_COUNT macro Add macro for configuring maximum number of commands that can be defined. This macro replaces hardcoded "8" number. --- WebServer.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WebServer.h b/WebServer.h index dadc763..6360460 100644 --- a/WebServer.h +++ b/WebServer.h @@ -53,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 @@ -329,7 +333,7 @@ class WebServer: public Print { const char *verb; Command *cmd; - } m_commands[8]; + } m_commands[WEBDUINO_COMMANDS_COUNT]; unsigned char m_cmdCount; UrlPathCommand *m_urlPathCmd; From a68507ef6d99045fd55b21b4d64df84f3b5422fa Mon Sep 17 00:00:00 2001 From: Matt Robinson Date: Thu, 17 Jul 2014 19:13:58 +0100 Subject: [PATCH 23/23] Fix compiler warnings about narrowing conversion The latest 1.5.7 beta release of Arduino includes an updated toolchain which now issues warnings about narrowing of ints to chars inside curly braces becoming ill-formed in C++11. Explicitly cast the values to char to prevent this. --- WebServer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebServer.h b/WebServer.h index 6360460..c8c43c3 100644 --- a/WebServer.h +++ b/WebServer.h @@ -992,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); }