Skip to content

Minimal HTTP Server With W5500

December 11, 2014

simplest2
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

14-12-02 Hardware
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,&regval,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.

From → Uncategorized

10 Comments
  1. Jim Jun permalink

    This is good. I’d like to post this article in WIZnet Museum(http://wiznetmuseum.com) for others. What do you think?

  2. Jim Jun permalink

    Thanks. I’ll leave a comment after posting.

  3. 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.

Trackbacks & Pingbacks

  1. TCP/HTTP h*ll – but some light | olduino
  2. Ten Thousand Views | olduino

Leave a comment