Arduino Web Server Code Publication

Today on my Arduino www home automation website I have published my webserver procedure. This procedure receives and processes html requests, takes various actions and causes the next web page to display. You can see this code at:

http://www.2wg.co.nz/PUBLIC/WEBSERVR.TXT/

This code is over 600 lines and supports more that 30 web initiated activities and/or web page displays.

I have published this code for users who want to use their Arduino equipment to publish a full website and are struggling with html request processing. Via http://www.2wg.co.nz/SCRNINFO.DIR/ you can also access the Arduino C language source code that generates the html for all of the application's main web pages. (All the code is not fully current.)

Other major blocks of application functionality that I have published and other Arduino developers might be interested in include:

http://www.2wg.co.nz/PUBLIC/COOKIES.TXT/ - how to use browser cookies within an Arduino web application to support application security.

http://www.2wg.co.nz/PUBLIC/RAMUSAGE.TXT/ - an implementation of realtime memory usage tracking that I use to profile my application and find out which code is likely using up all my memory.

http://www.2wg.co.nz/SCRNINFO/SD_CARD.TXT/ and http://www.2wg.co.nz/SCRNINFO/SD_FILE.TXT/ - An implementation of a web based "sorted" SD card directory browser with file display of TXT and JPG files. (JPG file display is terribly slow for large files.)

As my application development continues (I am over 6,500 lines of C code now) I will publish other blocks of interesting code including my suite of general code utilities.

The public is of course welcome to browse my Arduino home automation application at http://www.2wg.co.nz and see what can be achieved using Arduino equipment to publish a web site.

Cheers

Catweazle NZ

Excellent example - now to study your code .....
Thanks

Hi,

I've been studying your code some and I have been 'borrowing' some of it as well. ;D

I have a question about the ReadTimeUDP(). I'm surprised you got that working. I did see a forum append where it was giving you trouble at one point but after you changed some stuff it started to work.

The reason I'm surprised is because of this forum discussion:
http://forum.arduino.cc/index.php?topic=162799.0

which in the last reply references:

Seems like the only way I keep the UDP port operational is if I start it up once and never stop it.

Any thoughts?

Seems like the only way I keep the UDP port operational is if I start it up once and never stop it.

Any thoughts?

Yes. I don't believe that is true. I use NTP and DNS, and both work fine obtaining a socket, sending and receiving the packets, and releasing the socket.

The reason I'm surprised is because of this forum discussion:
W5100/Ethernet cannot use socket as UDP after using it as TCP - Networking, Protocols, and Devices - Arduino Forum

Did you not see my posts in that discussion? Mine worked fine once I discovered the SD card problem. If you have a SD card in the shield's slot, you must initialize or disable it. It will even cause the dhcp requests to fail.

I do have a SD card, but I have the CS set to HIGH for it... so I think it's out of the picture.

As long as I don't stop/begin the UDP socket, it seems to work just fine; but once I stop it and the socket gets reused for a simple Webserver access, it's toast if I try and restart the UDP socket. If I reset the w5100, it will come back to life.

Have you tried the ShowSockStatus function? This shows the status of all sockets. Try your problem again, then call this function to determine what has happened at the socket level. I would like to see the results of this test if y9ou don't mind.

#include <utility/w5100.h>

byte socketStat[MAX_SOCK_NUM];

void ShowSockStatus()
{
  for (int i = 0; i < MAX_SOCK_NUM; i++) {
    Serial.print(F("Socket#"));
    Serial.print(i);
    uint8_t s = W5100.readSnSR(i);
    socketStat[i] = s;
    Serial.print(F(":0x"));
    Serial.print(s,16);
    Serial.print(F(" "));
    Serial.print(W5100.readSnPORT(i));
    Serial.print(F(" D:"));
    uint8_t dip[4];
    W5100.readSnDIPR(i, dip);
    for (int j=0; j<4; j++) {
      Serial.print(dip[j],10);
      if (j<3) Serial.print(".");
    }
    Serial.print(F("("));
    Serial.print(W5100.readSnDPORT(i));
    Serial.println(F(")"));
  }
}

Sure, no problem... I've been doing quite of bit of reading over the past 24hrs so I already had that debug code in there. I'm printing out the socket status just before I stop the Udp port.

In the below output, I read the Udp port twice before sending a simple webserver request (actually two web request get sent). The closing client messages are the end of the web requests. Then a upd port starts up again (and in this case, it got assigned back to socket#1) but the udp request fails.

Setup
My IP address: 192.168.1.149.
waiting for good packet 0   <---- Read successful 
The EST time is 08:58:41 PM
255.255.255.0
Socket#0:0x14 80 D:255.255.255.255(67)
Socket#1:0x22 8888 D:132.163.4.101(123)
Socket#2:0x0 0 D:0.0.0.0(0)
Socket#3:0x0 0 D:0.0.0.0(0)
All ready....  2/22/2015  20:58:41
waiting for good packet 0   <---- Read sucsessful
The EST time is 08:59:11 PM
255.255.255.0
Socket#0:0x14 80 D:255.255.255.255(67)
Socket#1:0x22 8888 D:132.163.4.101(123)
Socket#2:0x0 0 D:0.0.0.0(0)
Socket#3:0x0 0 D:0.0.0.0(0)
closing client  2/22/2015  20:59:23  <----- Webpage ack/sent
closing client  2/22/2015  20:59:26  <----- Webpage ack/sent
waiting for good packet 0
waiting for good packet 0
waiting for good packet 0
waiting for good packet 0
waiting for good packet 0
waiting for good packet 0
waiting for good packet 0
waiting for good packet 0
waiting for good packet 0
waiting for good packet 0
waiting for good packet 0
waiting for good packet 0  <----- upd failed!!!!
255.255.255.0
Socket#0:0x14 80 D:192.168.1.132(58978)
Socket#1:0x22 8888 D:132.163.4.101(123)
Socket#2:0x0 0 D:0.0.0.0(0)
Socket#3:0x0 0 D:0.0.0.0(0)

What is socket#0 doing? It appears to be a server socket listening for a client. Is that correct?

Socket#1 is a UDP socket listening on port 8888. From a socket perspective, all appears to be working well.

Just to be clear:
you are starting a server,
then starting a UDP socket,
sending a NTP request, waiting for an answer,
then stopping the UDP socket.

Then when you try to start another UDP socket, that is when the trouble starts?

BTW, I don't see a call to ShowSockStatus after you stop the UDP socket. Does socket#1 show a status of 0x00 after the stop?

Does socket#1 show a status of 0x00 after the stop?

Yes

Then when you try to start another UDP socket, that is when the trouble starts?

Yes, but only if I request a few web pages. If I don't 'pollute' the socket with a webpage request, it works just fine.

Hopefully this pseudo code will show exactly what I'm doing...
(Eventually I'll be picking up more of your WebServerST code, but for now I just have stupid code in for the server, it just returns simple web page)

EthernetServer mEServer(80);

void setup() {
  ...
  startEthernet();
  ...

}

startEthernet() {
  ....
  Ethernet.begin(mMacAddr);
  ...
  mEServer.begin();
  ...
}


// This is the function that gets called every 30 seconds.
unsigned long readNTPTime() {
  
  EthernetUDP eUdp;

  mEUdpOk = eUdp.begin(NTP_LOCAL_PORT);
  ...
  send_the_request/wait for answer;
  ...
  eUdp.stop();
  ...
 
}


void loop() {

  EthernetClient client = mEServer.available();  // try to get client

  if (client and any_request ) {  // got client?
     ....
     send_back_response_header
     send_back_simple_web_page
     ...
     client.stop(); // close the connection
  }
  .....
}

I'm using the new web server code I have in the playground, and have merged a NTP client function call, starting and stopping the UDP socket for each NTP call, and it is working fine. I also "polluted" the sockets with web page requests, and both server and NTP still work. ??

The code I released ten months ago is well out of date. The WebServerProcess functionality has been substantially restructured and greatly expanded since then.

100% of my Arduino application is now available on the application's website at http://www.2wg.co.nz - there is a list of files to download on the main dashboard web page.

My application does run successfully and continuously as a web server on port 80 and it uses UDP NTP everyday to update its time. As of now it has been running for 19 straight days.

However please note that I was unable to get UDP NTP working using an IP address assigned by my sketch. The only way i was able to get everything going ok was to let my ADSL modem assign my Arduino board a fixed IP address using DHCP based on a fixed MAC address I have embedded in my sketch. I never tried to find out why this was necessary - I was just happy to be able to keep moving forward.

Cheers

Catweazle NZ

Hi Catweazle,

Thanks for the reply. I just picked up a copy of your code last week from the site you mentioned above, so I do have the latest and greatest. The code looked pretty fault tolerant around the UDP NTP so it was unclear to me if you would even notice if the UDP NTP requests were failing... so I figured it was worth while asking about.

I tried both a fixed IP address and just letting my router assign a IP address, but I get the same results with UDP NTP request failing if I don't keep a 'fixed' socket for them.

I'll debug a bit more while I'm waiting for a few more parts to arrive.. but then to, I'll probably just move on with my workaround.

CatweazleNZ:
My application does run successfully and continuously as a web server on port 80 and it uses UDP NTP everyday to update its time. As of now it has been running for 19 straight days.

Do you use Udp.begin() and Udp.stop() at each NTP request? Or do you keep a socket reserved for that?

CatweazleNZ:
However please note that I was unable to get UDP NTP working using an IP address assigned by my sketch. The only way i was able to get everything going ok was to let my ADSL modem assign my Arduino board a fixed IP address using DHCP based on a fixed MAC address I have embedded in my sketch. I never tried to find out why this was necessary - I was just happy to be able to keep moving forward.

Moving forward is good, but you need to back up and fix that. Most server situations will require a static IP.

SurferTim, do you know what a normal NTP packet looks like? Currently I can only capture packets being directed to my computer... I'm working on fixing that. Anyway, I decided to startup Wireshark and look at incoming packet and surprisingly a NTP packet showed up. (only the 'bad' packets show up, imagine that)

Packet info:
Source Mac Adr: Good - mac of Arduino
Dest Mac Adr: BAD - mac of my computer
Source IP Adr: Good - IP Adr of Arduino
Dest IP Adr: Good - IP of the time server
Source Port: Good
Dest Port: Good

As I said, I'm not sure if this get broadcast anywhere else besides my computer or not..... I'm guessing not.

SurferTim, do you know what a normal NTP packet looks like?

Yes.

only the 'bad' packets show up, imagine that

I'm not sure what you mean by "bad". Bear in mind if the NTP server is not on the localnet, then the packet will be sent to your router (gateway), and will return from the gateway device.

As an example,
if you had Internet Connection Sharing enabled on your PC
and
the PC connected to the internet via wireless
and
the Arduino was connected to the PC's ethernet interface
and
the PC was the "router" for the Arduino's network,
then
that would not be a bad packet. All non-localnet packets will show the mac address of the gateway as the remote (destination) mac address.

But I do not...

The Pc is connected to a router
and
the Arduino is connected to the same router
and
the router is connected to the cable modem

SurferTim:
Do you use Udp.begin() and Udp.stop() at each NTP request? Or do you keep a socket reserved for that?

Yes - I use udp.begin() and udp.stop(). I run udp ntp on port 8888. And I only run udp ntp operations when there are no active http internet connections running on port 80. So I always have plenty of free ports.

SurferTim:
Moving forward is good, but you need to back up and fix that. Most server situations will require a static IP.

I don't think I need to fix anything. My Arduino system and my internet connection both have static IP addresses. My Arduino static IP address is assigned via DHCP running on my ADSL modem - it assigns a static IP address based on the static MAC address embedded in my application sketch and used in the call to Ethernet.begin().

Cheers

Catweazle NZ

CatweazleNZ:
I don't think I need to fix anything. My Arduino system and my internet connection both have static IP addresses. My Arduino static IP address is assigned via DHCP running on my ADSL modem - it assigns a static IP address based on the static MAC address embedded in my application sketch and used in the call to Ethernet.begin().

That is a static dhcp assignment, not a static ip, but that is just semantics.

dgbair:
Hi Catweazle,

Thanks for the reply. I just picked up a copy of your code last week from the site you mentioned above, so I do have the latest and greatest. The code looked pretty fault tolerant around the UDP NTP so it was unclear to me if you would even notice if the UDP NTP requests were failing... so I figured it was worth while asking about.

I tried both a fixed IP address and just letting my router assign a IP address, but I get the same results with UDP NTP request failing if I don't keep a 'fixed' socket for them.

I'll debug a bit more while I'm waiting for a few more parts to arrive.. but then to, I'll probably just move on with my workaround.

CatweasleNZ feels nothing needs fixing. I guess you are on your own. :frowning:

dgbair:
Hi Catweazle,

Thanks for the reply. I just picked up a copy of your code last week from the site you mentioned above, so I do have the latest and greatest. The code looked pretty fault tolerant around the UDP NTP so it was unclear to me if you would even notice if the UDP NTP requests were failing... so I figured it was worth while asking about.

I tried both a fixed IP address and just letting my router assign a IP address, but I get the same results with UDP NTP request failing if I don't keep a 'fixed' socket for them.

I'll debug a bit more while I'm waiting for a few more parts to arrive.. but then to, I'll probably just move on with my workaround.

dgbair

SurferTim seems to think this is my problem so I will comment further.

You have access to my current code - although there have been unreleased minor inconsequential updates in the last month.

The discussion over at W5100/Ethernet cannot use socket as UDP after using it as TCP - Networking, Protocols, and Devices - Arduino Forum is too complicated for me. But I take up SurferTim's comment about SD card initialisation. During my application development I have had a lot of problems with the fact that both ethernet and the SD card share my Freetronics Ethermega SPI interface. But provided you initialise each one correctly and enable/disable (switch between) them correctly everything works OK.

In summary about SPI - make sure both ethernet and SD card are initialised and never enable both at the same time, make sure you never send ethernet calls to the SD card, make sure you never send SD card calls to ethernet.

Within my implementation look at the SPIDeviceSelect() procedure - and make sure you understand how it is used within the application to switch between my two SPI devices - including the situation where my application is reading an SD card file and writing the data to an ethernet web page at the same time. (Well almost.)

If you create a test app with a simple web page and an UDP NTP function I will try to debug it on my Freetronics hardware. I should be able to get something working since I have a well proven working solution and I would be happy to do some further testing to see if I can get my system working with its own static IP address rather that the current ADSL modem assigned static IP address.

Cheers

Catweazle NZ

Hi,

I agree with the guys on the other discussion. The W5100 is messing up the ARP tables. It's assigning wrong MAC addresses to the NTP packets. Using wireshark on the computer and tcpdump on my router it's very obvious the NTP packets contain the wrong header information.... hence they are getting routed to my computer instead of being routed out bound. ie the router recognizes the MAC address and sends it to the proper place where the MAC address lives.

At some point I may try updating the library and using SEND_MAC vs SEND for UDP/NTP packets (just to see if I can work around this) but that's a install/build process I don't feel like doing right now.