Minimal HTTP Server With W5500
This is a stripped down version of a web server that just serves a static page. I have cut everything out that wasn’t strictly necessary. It’s not useful as such but it may be of interest to someone who wants to write code for the W5500 for a non-arduino processor that can be programmed in C. I’ve included the code below but it is also available at this link. To expand the code below, click on the title lines.
I have much more interesting plans for the W5500 but I’ll get to them later.
A Note On the Hardware Connections
The ioShield-A is meant to connect to an Arduino-type processor using mostly the 6 pin ICSP connector plus one Slave Select line. In the picture above the SPI control lines are shown at the ICSP connector, 5V power, Ground, and Slave Select are shown on the arduino headers. Reset is also pointed out but it is not strictly necessary. Power, Ground, and Reset can also be connected at the ICSP but Slave Select cannot.
The Simplest Web Server Software
/***************************************************************************** // File Name : w5500simplest.c // Version : 1 // Description : Simplest Functional Web Server With Wiznet w5500 // Author : Bill Rowe // Target : Olduino // Compiler : LCC1802 // Created : Dec 10, 2014 *****************************************************************************/ #include <nstdlib.h> //for printf etc. #include <cpu1802spd4port7.h> //defines processor type, speed, and host port #include <olduino.h> //for digitalRead, digitalWrite, delay #include <hspi2.h> //hardware spi header //end of olduino/1802 specific declares #include "w5500data.h" //wiznet definitions #include "w5500simplestcode.h" //wiznet code definitions #define MAX_BUF 512 unsigned char buf[MAX_BUF]; //memory buffer for incoming & outgoing data void sendresp(){ static unsigned char hdr1[]="HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" "<html>" "<span style=\"color:#0000A0\">\r\n" "<center><h1>Simplest Server on Wiznet W5500</h1></center"; static unsigned char trlr[]="</body></html>\r\n\r\n"; sendconst(hdr1); // Now Send the HTTP Response first part sendconst(trlr); // Now Send the rest of the page } void handlesession(){ unsigned int rsize; rsize=wizGetCtl16(SnRX_RSR); //get the size of the received data if (rsize>0){ sendresp(); //send a response flush(rsize); //get rid of the received data } wizCmd(CR_DISCON); } void main(void){ unsigned char socket0status; unsigned char ip_addr[] = {169,254,180,2}; delay(500); wiz_Init(ip_addr); //initialize the wiznet chip while(1){ // Loop forever socket0status=wizGetCtl8(SnSR); //socket 0 status switch (socket0status){ case SOCK_CLOSED: //initial condition socket0_init(); //initialize socket 0 break; case SOCK_ESTABLISHED: //someone wants to talk to the server handlesession(); break; //following are cases where we have to reset and reopen the socket case SOCK_FIN_WAIT: case SOCK_CLOSING: case SOCK_TIME_WAIT: case SOCK_CLOSE_WAIT: case SOCK_LAST_ACK: wizCmd(CR_CLOSE); break; } delay(100); } } #include <olduino.c> #include <nstdlib.c> #include <hspi2.c> #include "w5500simplestcode.c"
//14-11-26 routines to address the wiznet w5500 //14-11-27 switched to single read & write routines //14-12-8 wrapper routines for reading and writing commands and registers void wizWrite(unsigned int addr, unsigned char opcode, void * data, unsigned int len){ //variable length write to the wiznet W5500 //opcode is xxxx x100 // xxxxx is 00000 for common area, 00001 for socket 0 register, 00010 for socket 0 tx buffer enablewiz(); // Activate the CS pin spiSendN((unsigned char *)&addr,2); spiSend(opcode); // Send Wiznet Write OpCode spiSendN(data,len); //Send the payload disablewiz(); // make CS pin not active } void wizRead(unsigned int addr,unsigned char opcode, void * data, unsigned int len){ //variable length read from the wiznet w5500 //opcode is xxxx x000 // xxxxx is 00000 for common area, 00001 for socket 0 register, 00011 for socket 0 rx buffer enablewiz(); // Activate the CS pin spiSendN((unsigned char *)&addr,2); spiSend(opcode); // Send Wiznet Read OpCode spiReceiveN(data,len); // Send the data byte disablewiz(); // make CS pin not active } void wizCmd(unsigned char cmd){ //send a command to socket 0 and wait for completion wizWrite(SnCR,WIZNET_WRITE_S0R,&cmd,1); //send the command while(wizRead(SnCR,WIZNET_READ_S0R,&cmd,1),cmd); //wait til command completes } void wizSetCtl8(unsigned int ctlreg, unsigned char val){//write to a socket 0 control register wizWrite(ctlreg, WIZNET_WRITE_S0R,&val,1); } unsigned char wizGetCtl8(unsigned int ctlregaddr){ unsigned char regval; //spot to hold the register contents wizRead(ctlregaddr,WIZNET_READ_S0R,®val,1); return regval; } void wizSetCtl16(unsigned int ctlregaddr, unsigned int ctlregvalue){ wizWrite(ctlregaddr,WIZNET_WRITE_S0R,(unsigned char *) &ctlregvalue,2); } unsigned int wizGetCtl16(unsigned int ctlregaddr){ union WReg regval; //spot to hold the register contents wizRead(ctlregaddr,WIZNET_READ_S0R,regval.c,2); return regval.i; } void wiz_Init(unsigned char ip_addr[]){// Ethernet Setup wizWrite(SIPR,WIZNET_WRITE_COMMON,ip_addr,4);// Set the Wiznet W5100 IP Address } void socket0_init(){ //initialize socket 0 for http server wizCmd(CR_CLOSE); //make sure port is closed wizSetCtl8(SnIR,0xFF); //reset interrupt reg wizSetCtl8(SnMR,MR_TCP); //set mode register to tcp wizSetCtl16(SnPORT,80); //set tcp port to 80 wizCmd(CR_OPEN); //open the port wizCmd(CR_LISTEN); //listen for a conection } unsigned int send0(unsigned char *buf,unsigned int buflen){ unsigned int timeout,txsize,txfree; unsigned char crsend=CR_SEND,crreadback; unsigned int txwr; if (buflen <= 0) return 0; //make sure there is something to send //make sure there is room in the transmit buffer for what we want to send txfree=wizGetCtl16(SnTX_FSR); //this is the size of the available buffer area timeout=0; while (txfree < buflen) { delay(1); txfree=wizGetCtl16(SnTX_FSR); if (timeout++ > 1000) {// Timeout for approx 1 sec printf("TX Free Size Error!\n"); wizCmd(CR_DISCON);// Disconnect the connection return 0; } } //space is available so we will send the buffer txwr=wizGetCtl16(SnTX_WR); // Read the Tx Write Pointer wizWrite(txwr,WIZNET_WRITE_S0TX,buf, buflen); //write the outgoing data to the transmit buffer wizSetCtl16(SnTX_WR,txwr+buflen);//update the buffer pointer wizCmd(CR_SEND); // Now Send the SEND command which tells the wiznet the pointer is updated return 1; } unsigned int recv0(unsigned char *buf,unsigned int buflen){ unsigned int rxrd; if (buflen <= 0) return 1; if (buflen > MAX_BUF) // If the request size > MAX_BUF,just truncate it buflen=MAX_BUF - 2; rxrd = wizGetCtl16(SnRX_RD); // get the address where the wiznet is holding the data wizRead(rxrd,WIZNET_READ_S0RX,buf,buflen); //read the data *(buf+buflen)='\0'; // terminate string return 1; } void flush(unsigned int rsize){ //this just gets rid of data that i don't want to process unsigned int rxrd; if (rsize>0){ rxrd=wizGetCtl16(SnRX_RD); //retrieve read data pointer wizSetCtl16(SnRX_RD,rxrd+rsize); //replace read data pointer wizCmd(CR_RECV); //tell the wiznet we`ve retrieved the data } }
//14-11-26 w5500simplestcode.h - header for routines to address the wiznet w5500 #define sendconst(x) send0((unsigned char*)x,sizeof(x)-1) union IPaddr{ long l; unsigned char c[4]; }; union WReg{ //used to retrieve a 16 bit value from the wiznet unsigned int i; unsigned char c[2]; }; void wizRead(unsigned int addr,unsigned char opcode, void * data, unsigned int len); void wiz_Init(unsigned char ip_addr[]);// Ethernet Setup void socket0_init(); //initialize socket 0 for http server unsigned int send0(unsigned char *buf,unsigned int buflen); unsigned int recv0(unsigned char *buf,unsigned int buflen); void flush(unsigned int rsize); //this just gets rid of data that i don't want to process void wizCmd(unsigned char); //execure a wiznet sommand for socket 0 & wait for completion unsigned char wizGetCtl8(unsigned int ctlregaddr);//read a socket 0 8 bit control register void wizSetCtl8(unsigned int ctlreg, unsigned char val);//write to an 8 bit socket 0 control register unsigned int wizGetCtl16(unsigned int ctlregaddr);//read a socket 0 16 bit control register void wizSetCtl16(unsigned int ctlregaddr, unsigned int ctlregvalue);//write to a socket 0 16 bit control register //following two defines are for the 1802/olduino only, quick ways of flipping a bit to enable the wiznet chip #define disablewiz() asm(" ldad memaddr,_PIN4\n ldn memaddr\n ori 0x80\n str memaddr\n" \ " sex memaddr\n out 4\n sex 2\n"); //high level on 7(SS) disables the w5100 from the spi bus #define enablewiz() asm(" ldad memaddr,_PIN4\n ldn memaddr\n ani 0x7f\n str memaddr\n" \ " sex memaddr\n out 4\n sex 2\n");
//w5500data.h - defines for wiznet w5500 data structures // Wiznet W5500 Op Codes #define WIZNET_WRITE_COMMON 0x04 //opcode to write to one of the common block of registers #define WIZNET_READ_COMMON 0x00 //opcode to wread one of the common block of registers #define WIZNET_WRITE_S0R 0x0C //(000 01 1 00) opcode to write to one of the socket 0 registers #define WIZNET_READ_S0R 0x08 //(000 01 0 00) opcode to read one of the socket 0 registers #define WIZNET_WRITE_S0TX 0x14 //(000 10 1 00) opcode to write to the socket 0 transmit buffer #define WIZNET_READ_S0RX 0x18 //(000 11 0 00) opcode to read from the socket 0 receive buffer // Wiznet W5500 Register Addresses #define MR 0x0000 // Mode #define GAR 0x0001 // Gateway IP address #define SUBR 0x0005 // Subnet mask address #define SHAR 0x0009 // Source MAC address #define SIPR 0x000F // Source IP address #define IR 0x0015 // Interrupt #define IMR 0x0016 // Interrupt Mask #define RTR 0x0019 // Timeout address #define RCR 0x001B // Retry count #define UIPR 0x0028 // Unreachable IP address in UDP mode #define UPORT 0x002C // Unreachable Port address in UDP mode //W5500 Socket Registers follow #define SnMR 0x0000 // Mode #define SnCR 0x0001 // Command #define SnIR 0x0002 // Interrupt #define SnSR 0x0003 // Status #define SnPORT 0x0004 // Source Port #define SnDHAR 0x0006 // Destination Hardw Addr #define SnDIPR 0x000C // Destination IP Addr #define SnDPORT 0x0010 // Destination Port #define SnMSSR 0x0012 // Max Segment Size #define SnPROTO 0x0014 // Protocol in IP RAW Mode #define SnTOS 0x0015 // IP TOS #define SnTTL 0x0016 // IP TTL #define SnRX_BSZ 0x001E // RX Buffer Size #define SnTX_BSZ 0x001F // TX Buffer Size #define SnTX_FSR 0x0020 // TX Free Size #define SnTX_RD 0x0022 // TX Read Pointer #define SnTX_WR 0x0024 // TX Write Pointer #define SnRX_RSR 0x0026 // RX RECEIVED SIZE REGISTER #define SnRX_RD 0x0028 // RX Read Pointer #define SnRX_WR 0x002A // RX Write Pointer (supported? // S0_MR values #define MR_CLOSE 0x00 // Unused socket #define MR_TCP 0x01 // TCP #define MR_UDP 0x02 // UDP #define MR_IPRAW 0x03 // IP LAYER RAW SOCK #define MR_MACRAW 0x04 // MAC LAYER RAW SOCK #define MR_PPPOE 0x05 // PPPoE #define MR_ND 0x20 // No Delayed Ack(TCP) flag #define MR_MULTI 0x80 // support multicating // S0_CR values #define CR_OPEN 0x01 // Initialize or open socket #define CR_LISTEN 0x02 // Wait connection request in tcp mode(Server mode) #define CR_CONNECT 0x04 // Send connection request in tcp mode(Client mode) #define CR_DISCON 0x08 // Send closing reqeuset in tcp mode #define CR_CLOSE 0x10 // Close socket #define CR_SEND 0x20 // Update Tx memory pointer and send data #define CR_SEND_MAC 0x21 // Send data with MAC address, so without ARP process #define CR_SEND_KEEP 0x22 // Send keep alive message #define CR_RECV 0x40 // Update Rx memory buffer pointer and receive data // S0_SR values #define SOCK_CLOSED 0x00 // Closed #define SOCK_INIT 0x13 // Init state #define SOCK_LISTEN 0x14 // Listen state #define SOCK_SYNSENT 0x15 // Connection state #define SOCK_SYNRECV 0x16 // Connection state #define SOCK_ESTABLISHED 0x17 // Success to connect #define SOCK_FIN_WAIT 0x18 // Closing state #define SOCK_CLOSING 0x1A // Closing state #define SOCK_TIME_WAIT 0x1B // Closing state #define SOCK_CLOSE_WAIT 0x1C // Closing state #define SOCK_LAST_ACK 0x1D // Closing state #define SOCK_UDP 0x22 // UDP socket #define SOCK_IPRAW 0x32 // IP raw mode socket #define SOCK_MACRAW 0x42 // MAC raw mode socket #define SOCK_PPPOE 0x5F // PPPOE socket #define TCP_PORT 80 // TCP/IP Port
Software Notes
The W5500 requires very little in the way of initialization. It initializes itself with a MAC as well as a static ip address, gateway address, and subnet mask suitable for a home network. I override its static IP address to 169.254.180.2 so that it will work on a direct ethernet cable connection to my windows box.
For anyone who gets the impression the W5500 is tough to understand or program for: it’s not. I spent a lot of time at a very detailed level because I am working with an offbeat processor and had to bull my way through C++ libraries. The W5500 is more powerful than the W5100 and W5200 and the SPI transactions are more intuitive and much much faster.
This is good. I’d like to post this article in WIZnet Museum(http://wiznetmuseum.com) for others. What do you think?
Certainly, I’d be pleased to see it.
Thanks. I’ll leave a comment after posting.
Refer to http://wiznetmuseum.com/portfolio-items/minimal-http-server-with-w5500/
Thanks.
Hi,
I have tried to understand the Web server code, but I have not succeeded so far. I have been looking for two functions and wondering where might be the functions which do the transmission? I mean the following ones:
sendconst(hdr1); // Now Send the HTTP Response first part
sendconst(trlr); // Now Send the rest of the page
Could someone help me to find these functions.
Hi: sendconst is a #define right at the top of w5500simplestcode.h above
#define sendconst(x) send0((unsigned char*)x,sizeof(x)-1)
hdr1 and trlr are char constants defined in sendresp() in w5500simplest.c above
good luck and let me know if i’m not answering the right question!
what processare are you working on?
Hi,
And thank you, I did not notice that before, my mistake.
I have W5200 and Atmel ATmega. Now I can get the response but it prints that “Hello World” text several times and also several lines unwanted marks like “>><<><" etc. I do not know what is wrong at the moment.
In my opinion the W5200 datasheet does not include very simple instructions or I do not understand what they are trying to say in the pseudo C codes. For me it was quite confusing how to write the data into W5200 TX buffer. I do it like this way: 1) I read the write pointer first 2) I calculate the physical address by using BASE value added with the MASK with the read write pointer value, 3) I write the data into that physical address one character at a time and counting the physical address one up and next character. Is this the correct way?
Thank you about the help!
Your write sounds sort of ok but i’d have to see the details.
Can you show me your code? You can email it to me at bill_rowe_ottawa on hotmail.
For a w5200 and atmega you might find something on avrfreaks or arduino forums.