FRDM with Arduino Ethernet Shield R3, Part 3: Embedded Web Server

This is Part 3 of an ongoing tutorial to use the Arduino Ethernet Shield R3 with a Freescale FRDM-KL25Z board (or any other board you like).

In Part 1 I worked on the SD card, in Part 2 I have added basic network connection. Now time to run a web server with my FRDM-KL25Z :-). With this, I can get access to my board through the network, and the board will host a web page where I can do pretty much everything: showing status, or adding functions to turn on things like an LED 🙂

Web Server with W5100 on FRDM-KL25Z

Web Server with W5100 on FRDM-KL25Z

List of Tutorials

  1. FRDM with Arduino Ethernet Shield R3, Part 1: SD Card
  2. FRDM with Arduino Ethernet Shield R3, Part 2: Ping
  3. FRDM with Arduino Ethernet Shield R3, Part 3: Embedded Web Server
  4. FRDM with Arduino Ethernet Shield R3, Part 4: MinIni

W5100 as Web Server

The Wiznet W5100 Ethernet Chip on the Arduino Ethernet Shield R3 makes it easy to host a web server with a small microcontroller: the heavy protocol lifting is implemented in the W5100.

FRDM-KL25Z running Embedded Web Server with W5100

FRDM-KL25Z running Embedded Web Server with W5100

To make a web server, I need 3 things to do:

  1. Initialize the W5100
  2. Using Sockets
  3. Host an HTML web server

There are many good tutorials around this subject available. I recommend “Integrating Wiznet W5100, WIZ811MJ network module with Atmel AVR Microcontroller” as this article describes very nicely the features of the device and how the socket interface works. The example used in that article implements only one socket, while I have extended it to support up to four sockets of the device with the help of this article: “Wiznet 5100 library for ATmega devices“.

Initialization

First, the W5100 needs to be initialized:

  1. Reset the device
  2. Configure the network address and gateway
  3. Set up the total 8 KByte send and receive buffers for the 4 sockets

💡 All the source files are available on GitHub.

static void WiznetSetup(void) {
  CLS1_SendStr((unsigned char*)"Reset W5100.\r\n", CLS1_GetStdio()->stdOut);
  /* reset device */
  if (W5100_MemWriteByte(W5100_MR, W5100_MR_BIT_RST)!=ERR_OK) {
    CLS1_SendStr((unsigned char*)"Failed to reset device!\r\n", CLS1_GetStdio()->stdErr);
  }
  CLS1_SendStr((unsigned char*)"Configure network.\r\n", CLS1_GetStdio()->stdOut);
  /* configure network: IP address, gateway, netmask, MAC */
  if (W5100_WriteConfig((w5100_config_t*)&W5100_config)!=ERR_OK) {
    CLS1_SendStr((unsigned char*)"Failed to set Net Configuration!\r\n", CLS1_GetStdio()->stdErr);
  }
  CLS1_SendStr((unsigned char*)"Configure RX/TX memory.\r\n", CLS1_GetStdio()->stdOut);
  /* we have 8 KByte we can use for the RX and TX sockets: */
  if (W5100_MemWriteByte(W5100_RMSR,
       W5100_xMSR_SOCKET_1_MEM_SIZE_2KB
      |W5100_xMSR_SOCKET_2_MEM_SIZE_2KB
      |W5100_xMSR_SOCKET_3_MEM_SIZE_2KB
      |W5100_xMSR_SOCKET_4_MEM_SIZE_2KB
     )!=ERR_OK)
  {
    CLS1_SendStr((unsigned char*)"Failed to set RX socket memory size!\r\n", CLS1_GetStdio()->stdErr);
  }
  if (W5100_MemWriteByte(W5100_TMSR,
       W5100_xMSR_SOCKET_1_MEM_SIZE_2KB
      |W5100_xMSR_SOCKET_2_MEM_SIZE_2KB
      |W5100_xMSR_SOCKET_3_MEM_SIZE_2KB
      |W5100_xMSR_SOCKET_4_MEM_SIZE_2KB
     )!=ERR_OK)
  {
    CLS1_SendStr((unsigned char*)"Failed to set TX socket memory size!\r\n", CLS1_GetStdio()->stdErr);
  }
  CLS1_SendStr((unsigned char*)"done!\r\n", CLS1_GetStdio()->stdOut);
}

Sockets

The W5100 supports up to 4 sockets. For this I have ‘sockets.h’ and ‘sockets.c’.

/*!
 * \brief Opens a socket
 * \param sock
 * \param eth_protocol Ethernet protocol for the socket (TCP, UPD, PPPOE, ...)
 * \param tcp_port TCP port to be used (typically 80 for HTTP)
 * \return Error code, ERR_OK for success
 */
uint8_t SOCK_OpenSocket(uint8_t sock, uint8_t eth_protocol, uint16_t tcp_port);

/*!
 * \brief Starts listening on the socket.
 * \param sock Socket number, 0..W5100_NUM_SOCKETS.
 * \return Error code, ERR_OK for success
 */
uint8_t SOCK_Listen(uint8_t sock);

/*!
 * \brief Returns the socket status
 * \param sock Socket number, 0..W5100_NUM_SOCKETS.
 * \param status Pointer to where to store the status.
 * \return Error code, ERR_OK for success
 */
uint8_t SOCK_GetStatus(uint8_t sock, uint8_t *status);

/*!
 * \brief Returns the received data size in the socket.
 * \param sock Socket number, 0..W5100_NUM_SOCKETS.
 * \param rxSize Pointer to where to store the amount bytes in socket.
 * \return Error code, ERR_OK for success
 */
uint8_t SOCK_ReceivedSize(uint8_t sock, uint16_t *rxSize);

/*!
 * \brief Receive data from a socket
 * \param sock Socket number, 0..W5100_NUM_SOCKETS
 * \param buf Pointer to buffer with data
 * \param bufSize Size of buf in bytes
 * \param readLen Size of data to read in bytes
 * \return Error code, ERR_OK for success
 */
uint8_t SOCK_Receive(uint8_t sock, uint8_t *buf, size_t bufSize, size_t readLen);

/*!
 * \brief Send data to a socket
 * \param sock Socket number, 0..W5100_NUM_SOCKETS
 * \param buf Pointer to buffer with data
 * \param bufLen Size of data in bytes
 * \return Error code, ERR_OK for success
 */
uint8_t SOCK_Send(uint8_t sock, const uint8_t *buf, size_t buflen);

/*!
 * \brief Disconnects a socket
 * \param sock Socket number, 0..W5100_NUM_SOCKETS
 * \return Error code, ERR_OK for success
 */
uint8_t SOCK_Disconnect(uint8_t sock);

/*!
 * \brief Close a socket
 * \param sock Socket number, 0..W5100_NUM_SOCKETS
 */
uint8_t SOCK_CloseSocket(uint8_t sock);

/*!
 * \brief Driver initialization.
 */
void SOCK_Init(void);

/*!
 * \brief Driver de-initialization.
 */
void SOCK_Deinit(void);

The implementation uses the W5100 low-level driver I presented in Part 2:

#include "socket.h"
#include "w5100.h"
#include "WAIT1.h"

uint8_t SOCK_CloseSocket(uint8_t sock) {
  uint8_t val;
  uint16_t sockaddr;

  if (sock>=W5100_NUM_SOCKETS) {
    return ERR_VALUE;
  }
  sockaddr = W5100_SKT_BASE(sock);
  /* Send Close Command */
  W5100_MemWriteByte(sockaddr+W5100_CR_OFFSET, W5100_CR_CLOSE);
  do { /* Waiting until the S0_CR is clear */
    W5100_MemReadByte(sockaddr+W5100_CR_OFFSET, &val);
  } while(val!=0);
  return ERR_OK;
}

uint8_t SOCK_Disconnect(uint8_t sock) {
  uint8_t val;
  uint16_t sockaddr;

  if (sock>=W5100_NUM_SOCKETS) {
    return ERR_VALUE;
  }
  sockaddr = W5100_SKT_BASE(sock);
  /* Send Disconnect Command */
  W5100_MemWriteByte(sockaddr+W5100_CR_OFFSET, W5100_CR_DISCON);
  do { /* Wait for Disconnecting Process */
    W5100_MemReadByte(sockaddr+W5100_CR_OFFSET, &val);
  } while(val!=0);
  return ERR_OK;
}

uint8_t SOCK_OpenSocket(uint8_t sock, uint8_t eth_protocol, uint16_t tcp_port) {
  uint8_t val;
  uint16_t sockaddr;

  if (sock>=W5100_NUM_SOCKETS) {
    return ERR_VALUE; /* out of range */
  }
  sockaddr =  W5100_SKT_BASE(sock);
  /* Make sure we close the socket first */
  if (W5100_MemReadByte(sockaddr+W5100_SR_OFFSET, &val)!=ERR_OK) {
    return ERR_FAILED; /* failure */
  }
  if (val==W5100_SOCK_CLOSED) {
    /* make sure we close the socket */
    SOCK_CloseSocket(sock);
  }
  /* Assigned Socket 0 Mode Register */
  W5100_MemWriteByte(sockaddr+W5100_MR_OFFSET, eth_protocol);
  /* Now open the Socket 0 */
  W5100_MemWriteWord(sockaddr+W5100_PORT_OFFSET, tcp_port);
  W5100_MemWriteByte(sockaddr+W5100_CR_OFFSET, W5100_CR_OPEN);  /* Open Socket */
  /* Wait for Opening Process */
  do {
    W5100_MemReadByte(sockaddr+W5100_CR_OFFSET, &val);
  } while(val!=0);
  /* Check for Init Status */
  W5100_MemReadByte(sockaddr+W5100_SR_OFFSET, &val);
  if (val==W5100_SOCK_INIT) {
    return ERR_OK;
  } else {
    SOCK_CloseSocket(sock);
  }
  return ERR_FAILED;
}

uint8_t SOCK_Listen(uint8_t sock) {
  uint8_t val;
  uint16_t sockaddr;

  if (sock>=W5100_NUM_SOCKETS) {
    return ERR_VALUE;
  }
  sockaddr = W5100_SKT_BASE(sock);
  W5100_MemReadByte(sockaddr+W5100_SR_OFFSET, &val);
  if (val==W5100_SOCK_INIT) {
    /* Send the LISTEN Command */
    W5100_MemWriteByte(sockaddr+W5100_CR_OFFSET, W5100_CR_LISTEN);
    /* Wait for Listening Process */
    do {
      W5100_MemReadByte(sockaddr+W5100_CR_OFFSET, &val);
    } while(val!=0);
    /* Check for Listen Status */
    W5100_MemReadByte(sockaddr+W5100_SR_OFFSET, &val);
    if (val==W5100_SOCK_LISTEN) {
      return ERR_OK;
    } else {
      SOCK_CloseSocket(sock);
    }
  }
  return ERR_FAILED;
}

uint8_t SOCK_Send(uint8_t sock, const uint8_t *buf, size_t buflen) {
  uint16_t offaddr, realaddr, txsize, timeout, sockaddr;
  uint8_t val;

  if (buflen<=0 || sock>=W5100_NUM_SOCKETS) {
    return ERR_VALUE;
  }
  sockaddr = W5100_SKT_BASE(sock);
  /* Make sure the TX Free Size Register is available */
  W5100_MemReadWord(sockaddr+W5100_TX_FSR_OFFSET, &txsize);
  timeout=0;
  while (txsize<buflen) {="" wait1_waitosms(1);="" w5100_memreadword(sockaddr+w5100_tx_fsr_offset,="" &<span="" class="hiddenSpellError" pre="">txsize);    /* Timeout for approximately 1000 ms */    if (timeout++ > 1000) {
     /* Disconnect the connection */
     SOCK_Disconnect(sock);
     return ERR_FAILED;
    }
  }
  /* Read the Tx Write Pointer */
  W5100_MemReadWord(sockaddr+W5100_TX_WR_OFFSET, &offaddr);
  while(buflen) {
    buflen--;
    /* Calculate the real W5100 physical Tx Buffer Address */
    realaddr = (W5100_TXBUFADDR+(0x0800*sock)) + (offaddr&W5100_TX_BUF_MASK);
    /* Copy the application data to the W5100 Tx Buffer */
    W5100_MemWriteByte(realaddr, *buf);
    offaddr++;
    buf++;
  }
  /* Increase the S0_TX_WR value, so it point to the next transmit */
  W5100_MemWriteWord(sockaddr+W5100_TX_WR_OFFSET, offaddr);
  /* Now Send the SEND command */
  W5100_MemWriteByte(sockaddr+W5100_CR_OFFSET, W5100_CR_SEND);
  /* Wait for Sending Process */
  do {
    W5100_MemReadByte(sockaddr+W5100_CR_OFFSET, &val);
  } while(val!=0);
  return ERR_OK;
}

uint8_t SOCK_Receive(uint8_t sock, uint8_t *buf, size_t bufSize, size_t readSize) {
  uint16_t offaddr, realaddr;
  uint16_t sockaddr;

  if (readSize<=0 || sock>=W5100_NUM_SOCKETS) {
    return ERR_VALUE; /* failure */
  }
  if (readSize>bufSize) { /* If the requested size > MAX_BUF, just truncate it */
    readSize = bufSize-2;
  }
  sockaddr = W5100_SKT_BASE(sock);
  /* Read the Rx Read Pointer */
  W5100_MemReadWord(sockaddr+W5100_RX_RD_OFFSET, &offaddr);
  while (readSize) {
    readSize--;
    realaddr = (W5100_RXBUFADDR + (0x0800*sock))+(offaddr&W5100_RX_BUF_MASK);
    W5100_MemReadByte(realaddr, buf);
    offaddr++;
    buf++;
  }
  *buf='\0';  /* string terminated character */
  /* Increase the S0_RX_RD value, so it point to the next receive */
  W5100_MemWriteWord(sockaddr+W5100_RX_RD_OFFSET, offaddr);
  /* Now Send the RECV command */
  W5100_MemWriteByte(sockaddr+W5100_CR_OFFSET, W5100_CR_RECV);
  WAIT1_Waitus(5);    /* Wait for Receive Process */
  return ERR_OK;
}

uint8_t SOCK_ReceivedSize(uint8_t sock, uint16_t *rxSize) {
  uint16_t sockaddr;

  *rxSize = 0;
  if (sock>=W5100_NUM_SOCKETS)  {
    return ERR_VALUE;
  }
  sockaddr = W5100_SKT_BASE(sock); // calc base addr for this socket
  return W5100_MemReadWord(sockaddr+W5100_RX_RSR_OFFSET, rxSize);
}

uint8_t SOCK_GetStatus(uint8_t sock, uint8_t *status) {
  uint16_t sockaddr;

  if (sock>=W5100_NUM_SOCKETS)  {
    return ERR_VALUE;
  }
  sockaddr = W5100_SKT_BASE(sock);
  return W5100_MemReadByte(sockaddr+W5100_SR_OFFSET, status);
}

void SOCK_Deinit(void){
  /* nothing needed */
}

void SOCK_Init(void) {
  /* nothing needed */
}

Web Server

The web server ‘task’ gets called from the application main loop:

  1. It gets the status from the socket: If the socket is closed, it opens the socket with TCP protocol on the HTTP port (80), and then listens to the socket.
  2. Once a communication connection is established, it checks how many bytes has been received and reads them into a buffer. If there is data, it inspects the data.
void SERVER_Process(uint8_t socket) {
  uint8_t status;
  uint16_t rsize;

  if (SOCK_GetStatus(socket, &status)!=ERR_OK) {
    return; /* failed */
  }
  switch (status) {  /* based on current status of socket... */
    case W5100_SOCK_CLOSED:  /* socket closed */
      if (SOCK_OpenSocket(socket, W5100_SKT_MR_TCP, SERVER_HTTP_PORT)==ERR_OK) {
        /* successful opened the socket */
        (void)SOCK_Listen(socket);
      }
      break;
    case W5100_SOCK_ESTABLISHED:
      (void)SOCK_ReceivedSize(socket, &rsize); /* how many bytes received? */
      if (rsize>0) { /* received data */
        if (SOCK_Receive(socket, buf, sizeof(buf), rsize)!=ERR_OK)  {
          break; /* something failed, get out here */
        }
        /* check the content */
        CheckData(socket, buf, sizeof(buf), rsize);
      }
      break;

    case W5100_SOCK_INIT:
      break;

    case W5100_SOCK_FIN_WAIT:
    case W5100_SOCK_CLOSING:
    case W5100_SOCK_TIME_WAIT:
    case W5100_SOCK_CLOSE_WAIT:
    case W5100_SOCK_LAST_ACK:
      SOCK_CloseSocket(socket);
      break;
  } /* switch */
}

Communication between Client and Server

To display a page from our web server in a client (a web browser, such as FireFox), the following happens:

  1. Enter the address of the board in the browser (e.g. http://192.168.0.12)
  2. The browser connects to the socket I have created with SOCK_OpenSocket() and which is listening to the incoming requests with SOCK_Listen(). By default it uses the standard TCP/IP port 80 (standard HTTP server port).
  3. In the server, I get a W5100_SOCK_ESTABLISHED status.
  4. The browser sends an HTTP “GET“or “POST” request to the server. With  SOCK_ReceivedSize() the server knows the size of the request, and with SOCK_Receive() the server can get the data from the socket.

GET Request

The client is sending a GET request to retrieve the HTML text to display.

HTTP GET Request

HTTP GET Request

A GET is sent as well if I do a refresh in the web browser. The text of the GET request is what I get from the socket using SOCK_Receive(), and looks like this, with a “GET /” at the beginning:

GET / HTTP/1.1
Host: 192.168.0.80
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive

The web server responds to this with the HTML code. And this is done in my server implementation with this function SendPage().

❗ Because that source code contains HTML code, WordPress was not able to properly show the source code here. Have look in the Server.c on GitHub.

This constructs the HTML code for my page, which then looks like this:

Web Server with W5100 on FRDM-KL25Z

Web Server with W5100 on FRDM-KL25Z

At the end, it performs a disconnect of the socket so other clients can connect to that socket.

POST Request

A POST request is sent by the client for that ‘Submit Query’ button: it will send me the state of the radio buttons so I can turn the red LED on or off.

HTTP Post

HTTP Post

Such a POST message looks like this:

POST / HTTP/1.1
Host: 192.168.0.80
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.80/
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 7

radio=0

Depending on which radio button is selected, there is “radio=0” or “radio=1” in the message. So for such a POST message, the web server needs to

  1. Detect that it is a POST message (this is known by the “POST /” at the beginning)
  2. Find the “radio=” in the message.

strFind()

To find such substrings in the message, I have implemented a simple ‘search substring inside a string and return the position’. This function I have put into my utility component:

int16_t UTIL1_strFind(byte *str, byte *subStr)
{
  int i, len;

  len = UTIL1_strlen((char*)subStr);
  for (i=0; *str!='\0'; i++, str++) {
    if (UTIL1_strncmp((char*)str, (char*)subStr, len)==0) {
      return i; /* found */
    }
  }
  return -1; /* not found */
}

Checkin the HTML Data from the Client

The last piece of the server is the function to find the “GET” and “POST” messages, and then to send the result back to the client:

static void CheckData(uint8_t socket, uint8_t *buf, size_t bufSize, uint16_t rsize) {
  int getidx, postidx;

#if 1 /* debug output */
  CLS1_SendStr((uint8_t*)"\r\n-------------------------\r\n", CLS1_GetStdio()->stdOut);
  CLS1_SendStr(buf, CLS1_GetStdio()->stdOut);
  CLS1_SendStr((uint8_t*)"\r\n-------------------------\r\n", CLS1_GetStdio()->stdOut);
#endif
  getidx = UTIL1_strFind(buf, (unsigned char*)"GET /");
  postidx = UTIL1_strFind(buf, (unsigned char*)"POST /");
  if (getidx >= 0 || postidx >= 0) {
    /* check the Radio Button for POST request */
    if (postidx>=0) {
      if (UTIL1_strFind(buf, (unsigned char*)"radio=0")>0) {
        LED1_Off();
      }
      if (UTIL1_strFind(buf, (unsigned char*)"radio=1")>0) {
        LED1_On();
      }
    }
    SendPage(socket, 23, LED1_Get());
  }
}

For debugging purposes I print the received message to the console.

Putting everything together

Oh, one thing is missing: how to call the server processing function. I do this in my application inside a FreeRTOS task, but that can be done easily without an RTOS too:

static portTASK_FUNCTION(Task1, pvParameters) {
  (void)pvParameters; /* parameter not used */
  WiznetSetup();
  CLS1_SendStr((unsigned char*)"Running web server...\r\n", CLS1_GetStdio()->stdOut);
  for(;;) {
    SERVER_Process(0);
    FRTOS1_vTaskDelay(50/portTICK_RATE_MS);
  }
}

void APP_Run(void) {
  cardMounted = FALSE;
  SHELL_Init();
  W5100_Init();
  SOCK_Init();
  SERVER_Init();
  if (FRTOS1_xTaskCreate(
    Task1,  /* pointer to the task */
    (signed char *)"Task1", /* task name for kernel awareness debugging */
    configMINIMAL_STACK_SIZE+100, /* task stack size */
    (void*)NULL, /* optional task startup argument */
    tskIDLE_PRIORITY,  /* initial priority */
    (xTaskHandle*)NULL /* optional task handle to create */
  ) != pdPASS) {
    /*lint -e527 */
    for(;;){}; /* error! probably out of memory */
    /*lint +e527 */
   }
  FRTOS1_vTaskStartScheduler();
}

With this I have a functional web server on my FRDM-KL25Z board. I can use it to give status, but with the POST commands I can do actions (e.g. turning on or off an LED, etc).

Summary

The combination of the Arduino Ethernet Shield with the FRDM-KL25Z makes a web server implementation very easy. With this, I can use and control the board through the network, a first and simple step into the IoT (Internet of Things) 🙂

The current project is available on GitHub here.

Other ideas for a continuation of this series:

  • Having the HTML pages with Java scripts and images on the SD card
  • Adding easier network integration, like using DHCP instead of static IP addresses

Happy Webing 🙂

51 thoughts on “FRDM with Arduino Ethernet Shield R3, Part 3: Embedded Web Server

  1. Hi Erich!
    Now I’m trying to access this embedded web server from remote locations via internet. No success yet…
    Have you managed to do it?

    Like

    • Cristian,
      no, I have not done this. But there are two things needed for this: a) your router needs to allow that connection from the outside and b) your device needs to have an address which is visible from the outside. DynDNS.org is probably something you will need.

      Like

  2. Hi everybody!
    Because I have a static IP I can access the WEB server without DynDNS via the router:
    http://89.122.69.248:8080/
    Here one can see the temperature from the magnetometer (unfortunately the 7805 arrived on top of it and the temperature is influenced by it.
    The page is refreshed automatically every 3 seconds and a counter is incremented.
    Also the data from the accelerometer and magnetometer is displayed.

    TODO: How can I upgrade the program of the FRDM board remotely?

    Like

  3. In order to change the led you have to press quickly “Submit Query” button after changing its state, eventually insist 2-3 times, this is caused by the refresh function

    Like

      • static uint8_t SendPage(uint8_t socket, int8_t temperature, int16_t contor, int16_t mag_x, int16_t mag_y, int16_t mag_z, int16_t acc_x, int16_t acc_y, int16_t acc_z, bool ledIsOn) {
        /* construct web page content */
        UTIL1_strcpy(buf, sizeof(buf), (uint8_t*)”HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n”);
        UTIL1_strcpy(buf, sizeof(buf), (uint8_t*)”\r\n”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n”);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n\r\n”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”W5100 Web Server\r\n”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Web Server using Wiznet W5100\r\n”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n”);
        if (SOCK_Send(socket, buf, UTIL1_strlen((char *)buf))!=ERR_OK) {
        return ERR_FAILED; /* something failed, get out here */
        }
        UTIL1_strcpy(buf, sizeof(buf), (uint8_t*)”Temp: OC “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Contor: “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”MagX: grade “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”AccelX: “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”MagY: grade “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”AccelY: “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”MagZ: grade “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”AccelZ: “);

        if (ledIsOn) {
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Red LED off”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Red LED on”);
        } else {
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Red LED off”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Red LED on”);
        }
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”
        “);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n\r\n”);
        if (SOCK_Send(socket, buf, UTIL1_strlen((char *)buf))!=ERR_OK) {
        return ERR_FAILED; /* something failed, get out here */
        }
        SOCK_Disconnect(socket);
        return ERR_OK;
        }

        Like

        • Hi Cristian,
          Why are you doing the operations with “\r\n”

          UTIL1_strcpy(buf, sizeof(buf), (uint8_t*)”HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n”);
          UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n”);
          UTIL1_strcpy(buf, sizeof(buf), (uint8_t*)”\r\n”);
          UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n”);
          UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n\r\n”);

          The 3rd line with UTIL1_strcpy() is the problem: it *copies* the string (to the beginning), and does not append/concatenate it.

          Have a look at my Server.c source file on GitHub?

          Like

        • Which brings again the question of being able to upgrade the firmware remotely. I think the new code has to be transferred to the SD card and from here using the bootloader to program the processor.

          Cristian

          Like

        • Hi Cristian,
          yes, this is how I would do it: transfer the code to the SD card, then reboot the controller, and at reboot it starts the bootloader which then loads the file from the card. It is just that using SD cards is not a super safe method, but for hobyy purposes that’s fine.

          Like

      • Please ignore the html code pasted yesterday, the correct code is:
        static uint8_t SendPage(uint8_t socket, int8_t temperature, int16_t contor, int16_t mag_x, int16_t mag_y, int16_t mag_z, int16_t acc_x, int16_t acc_y, int16_t acc_z, bool ledIsOn) {
        /* construct web page content */
        UTIL1_strcpy(buf, sizeof(buf), (uint8_t*)”HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n”);
        UTIL1_strcpy(buf, sizeof(buf), (uint8_t*)”\r\n”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n”);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n\r\n”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”W5100 Web Server\r\n”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Web Server using Wiznet W5100\r\n”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n”);
        if (SOCK_Send(socket, buf, UTIL1_strlen((char *)buf))!=ERR_OK) {
        return ERR_FAILED; /* something failed, get out here */
        }
        UTIL1_strcpy(buf, sizeof(buf), (uint8_t*)”Temp: OC “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Contor: “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”MagX: grade “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”AccelX: “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”MagY: grade “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”AccelY: “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”MagZ: grade “);

        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”AccelZ: “);

        if (ledIsOn) {
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Red LED off”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Red LED on”);
        } else {
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Red LED off”);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”Red LED on”);
        }
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”
        “);
        UTIL1_strcat(buf, sizeof(buf), (uint8_t*)”\r\n\r\n”);
        if (SOCK_Send(socket, buf, UTIL1_strlen((char *)buf))!=ERR_OK) {
        return ERR_FAILED; /* something failed, get out here */
        }
        SOCK_Disconnect(socket);
        return ERR_OK;
        }

        Like

      • UPS!
        It happened again! The code that arrived here is NOT what I pasted!
        It appears to be correct but after I click “Post Comment” it is posted wrong!
        Sorry

        Like

      • Ok, I noticed what you said, there were some UTIL1_strcpy after the fist one and the string was altered…
        Will have to go to the lab tomorrow and change the code.
        Thank you!

        Like

  4. Interesting: the counter increments by 1, 2, 3 …depending on the number of browsers accessing the Web Server simultaneously

    Like

  5. Pingback: FRDM with Arduino Ethernet Shield R3, Part 2: Ping | MCU on Eclipse

  6. Pingback: FRDM with Arduino Ethernet Shield R3, Part 1: SD Card | MCU on Eclipse

  7. Pingback: Binary Files for the mbed Bootloader with Eclipse and GNU ARM Eclipse Plugins | MCU on Eclipse

  8. Hi Erich, everybody,
    I just managed to modify the code to connect in Active mode, so that the board now acts like a Client and connects to a Server.
    According to the W5100 datasheet in order to achieve Active connection you have to issue SocketConnect command (instead of SocketOpen), providing before the server’s IP and Port. Fort this I created a function
    uint8 SOCK_Connect(uint8_t sock, uint8 * server_addr, uint16 eth_protocol, uint16 server_port)
    similar to
    uint8_t SOCK_OpenSocket(uint8_t sock, uint8_t eth_protocol, uint16_t tcp_port) provided by Erich.

    This will be used also for sending emails via a SMTP server. The code and/or assistance is available if needed.
    Happy Easter

    Like

    • Hi Cristian,
      good timing 🙂 I started again working with my Ethernet shield and I’m in the process of making it more flexible as it goes with configuring it. If you can share your code or more, that would be appreciated for sure by the community.
      Happy Easter too!

      Like

  9. Hi!
    So:
    In Application.c in portTASK_FUNCTION(Task1, pvParameters),
    instead of calling SERVER_Process(0); replaced with
    CLIENT_Process(0);

    In Server.h declared:
    /*!
    * \brief Client main process. Needs to be called frequently from the application.
    * \param socket Socket number, 0 to 4
    */
    void CLIENT_Process(uint8_t socket);

    In Server.c added:
    #define SERVER_HTTP_PORT 8000 /* TCP port for HTTP protocol */
    uint8_t SERVER_ipaddr[4] = {192, 168, 1, 10}; /*Remote Server ip address */;

    void CLIENT_Process(uint8_t socket) {
    uint8_t status;
    uint16_t rsize;

    if (SOCK_GetStatus(socket, &status)!=ERR_OK) {
    return; /* failed */
    }
    CLS1_SendStr((unsigned char*)”Status:”, CLS1_GetStdio()->stdOut);
    CLS1_SendNum8u(status, CLS1_GetStdio()->stdOut);
    CLS1_SendStr((unsigned char*)”\n”, CLS1_GetStdio()->stdOut);

    switch (status) { /* based on current status of socket… */
    case W5100_SOCK_CLOSED: /* socket closed */
    CLS1_SendStr((unsigned char*)”Status closed\n”, CLS1_GetStdio()->stdOut);
    if (SOCK_Connect(socket, SERVER_ipaddr, W5100_SKT_MR_TCP, SERVER_HTTP_PORT)==ERR_OK) {
    /* successful opened the socket */
    CLS1_SendStr((unsigned char*)”Status success\n”, CLS1_GetStdio()->stdOut);
    //(void)SOCK_Listen(socket);
    }
    break;
    case W5100_SOCK_ESTABLISHED:
    CLS1_SendStr((unsigned char*)”Sock Established “, CLS1_GetStdio()->stdOut);
    //Send something
    UTIL1_strcpy(buf, sizeof(buf), (uint8_t*)”Test active connection\n”);
    if (SOCK_Send(socket, buf, UTIL1_strlen((char *)buf))!=ERR_OK) {
    //return ERR_FAILED; /* something failed, get out here */
    }

    //Check if something received
    (void)SOCK_ReceivedSize(socket, &rsize); /* how many bytes received? */
    CLS1_SendStr((unsigned char*)”Received:”, CLS1_GetStdio()->stdOut);
    //CLS1_SendNum16u(rsize, CLS1_GetStdio()->stdOut);
    if (rsize>0) { /* received data */
    if (SOCK_Receive(socket, buf, sizeof(buf), rsize)!=ERR_OK) {
    break; /* something failed, get out here */
    }
    /* check the content and echo to CLS1*/
    CLS1_SendStr((unsigned char*)buf, CLS1_GetStdio()->stdOut);
    //CheckData(socket, buf, sizeof(buf), rsize);
    }
    break;
    case W5100_SOCK_INIT:
    CLS1_SendStr((unsigned char*)”init “, CLS1_GetStdio()->stdOut);
    break;
    case W5100_SOCK_FIN_WAIT:
    CLS1_SendStr((unsigned char*)”fin”, CLS1_GetStdio()->stdOut);
    case W5100_SOCK_CLOSING:
    case W5100_SOCK_TIME_WAIT:
    CLS1_SendStr((unsigned char*)”time”, CLS1_GetStdio()->stdOut);
    case W5100_SOCK_CLOSE_WAIT:
    case W5100_SOCK_LAST_ACK:
    SOCK_CloseSocket(socket);
    break;
    } /* switch */
    }

    In Socket.h declared:
    /*!
    * \brief Connects in active mode.
    * \param sock Socket number, 0..W5100_NUM_SOCKETS.
    * \return Error code, ERR_OK for success
    */
    uint8 SOCK_Connect(uint8_t sock, uint8 * server_addr, uint16 eth_protocol, uint16 server_port);

    In Socket.c added the function:
    /**
    @brief This function established the connection for the channel in Active (client) mode.
    This function waits for the until the connection is established.
    @return 1 for success else 0.
    */
    uint8 SOCK_Connect(uint8_t sock, uint8* server_addr, uint16 eth_protocol, uint16 server_port)
    {
    uint8_t val;
    uint16_t sockaddr;
    uint8 ret;
    CLS1_SendStr((unsigned char*)”Connecting..\n”, CLS1_GetStdio()->stdOut);

    sockaddr = W5100_SKT_BASE(sock);
    /* Make sure we close the socket first */
    if (W5100_MemReadByte(sockaddr+W5100_SR_OFFSET, &val)!=ERR_OK) {
    return ERR_FAILED; /* failure */
    }
    if (val==W5100_SOCK_CLOSED) {
    /* make sure we close the socket */
    SOCK_CloseSocket(sock);
    }
    /* Assigned Socket 0 Mode Register */
    W5100_MemWriteByte(sockaddr+W5100_MR_OFFSET, eth_protocol);

    ret = 1;
    // set destination IP
    W5100_MemWriteByte(sockaddr+W5100_DIPR_OFFSET, server_addr[0]);
    W5100_MemWriteByte(sockaddr+W5100_DIPR_OFFSET+1, server_addr[1]);
    W5100_MemWriteByte(sockaddr+W5100_DIPR_OFFSET+2, server_addr[2]);
    W5100_MemWriteByte(sockaddr+W5100_DIPR_OFFSET+3, server_addr[3]);
    W5100_MemWriteByte(sockaddr+W5100_DPORT_OFFSET, (uint8)((server_port & 0xff00) >> 8));
    W5100_MemWriteByte(sockaddr+W5100_DPORT_OFFSET+1, (uint8)(server_port & 0x00ff));

    W5100_MemWriteByte(sockaddr+W5100_CR_OFFSET, W5100_CR_OPEN); /* Open Socket */
    /* Wait for Opening Process */
    do {
    W5100_MemReadByte(sockaddr+W5100_CR_OFFSET, &val);
    } while(val!=0);

    W5100_MemWriteByte(sockaddr+W5100_CR_OFFSET, W5100_CR_CONNECT); /* Connect Socket */
    /* Wait for Opening Process */
    do {
    W5100_MemReadByte(sockaddr+W5100_CR_OFFSET, &val);
    } while(val!=0);
    return ret;
    }

    Like

  10. In order to test it you need a server listening on port 8000. For this I built a small LabWiew application, running on PC. If needed, I can also provide this application as an executable
    The next step these days will be to have 2 FRDM-KL-xx boards talk to each other, one as client ans one as server.

    Like

  11. Pingback: FRDM with Arduino Ethernet Shield R3, Part 4: MinIni | MCU on Eclipse

  12. Hi Erich.
    I hope you can help me. I would like to know how to use this project as a client, and not as a server.
    Just to make you know, I’m doing a project where the server already is in the computer (wamp server based on windows) where I access a database. In this database I intend to save the data read by FRDM KL25Z and sent by the ethernet to my page.

    So, my ethernet project should behave as a cliente.. Do you have any idea how to do that?

    Anyway, I appreciate your time. 🙂

    Like

  13. Hello Erich,
    I’m not pretty sure whether you still are answering questions about this post, but I will try anyway. 😀
    I’m doing an application with kl25z which has an embedded web server just as showed in the post .However I’m having a problem which, the server, after a while (sometimes 3 minutes or even 30 seconds, it depends) the server simply drops . I still can ping it, but I can’t access the webpage anymore.
    I already debugged it many times, and I still don’t get what is going on. It seems to work properly. So, I was wondering if you would know what it could be or if you already had some similar problem.
    The code is pretty much your example of Embedded Server, I just included a few features, that’s all.
    Thank you in advance.
    Renan Fonteles.

    Like

    • Hi Renan,
      I try to answer questions always :-). I have not seen such disconnects on my side. Have you debugged the thing on the KL25Z? Is your communication to the server from the KL25Z still working? Maybe your KL25Z is hanging (heap out of memory?). That you still can ping the server tells me that the Ethernet module is working, but not the connection to the microcontroller.

      Like

      • Hello Erich ,

        Thank you very much for replying . I’ve fixed the bug, even though I’m not pretty sure what was it. There was not any software bug , it seems that there was some problem in hardware, probably some bad contact, nosy or something like this . As I was using many other peripherals , the wiring was a bit messy . Haha, I did not expect this though , mainly because it started working oO. Anyway, thank you very much for your time. And congratulation for the great job in mcuonecplise.

        Kind regards,

        Renan Fonteles.

        Like

  14. Hello Erich

    I am trying to get Ethernet started on the FRDM K64F running FreeRTOS. The K64F has an onboard ethernet controller and PEx comes with an ethernet logic device driver component. FreeRTOS Labs has also released a TCP/IP stack. If there a port available for the FRDM K64F?

    Thanks
    Ketan

    Like

    • Hi Ketan,
      yes, there should be several ports for the FRDM-K64F, and there should be one included in the Kinetis SDK too. But I have not used Ethernet on the FRDM-K64F myself.

      Like

    • Hi David,
      yes and no. Yes, because you could use the same principle and methods using an Uno with C/C++. No, because that particular project file is written for an ARM Cortex M0+ from Freescale in normal C/C++.

      Like

  15. Hello Erich,

    Do you was try to read the web page from the SD Card, to make biggest website without use too much memory?

    Other thing, do you was try tu use images on you web site?

    Thanks and very good work!

    Like

      • Thanks for your answer!

        Other consulting, i have an error on mounting the SD Card, the console print this:

        ERROR: mount failed: (11) The logical drive number is invalid
        failed!
        *** Failed or unknown command: app mount
        *** Type help to get a list of available commands

        and i can’t use the config.ini or wherever other file. What can be?

        Regards!

        Like

        • I found the problem. In the new Processor Expert Libraries, when you make the mount the SD, it take the NULL and return -1, so, an error. To avoid that, i put a little string with a cast, (uint8_t *) “0:”. So that resolve the problem with the SD Card.

          Another thing, when i test the WebServer and i try to change the LED state, that function only with the Internet Explorer. With Chrome, nothing hapen. But this is for the size of the buffer readed from the W5100, is to little. I put 1KB and it’s work fine.

          Now, i going to continue my design. I need to create one task to manage the access of an office with RFID Cards and take dataloggin from all the movements. All that, with the possibility to view the movements, create and delete user, through the WebServer.

          Thanks for all and very good projects!

          Like

  16. Hello Erich,

    It´s me again. I’m trying to use the DHCP mode to flexibilize the installation but, when i enable the DHCP and try to compile, it’s doesn’t do. Don’t compile. The errors are about constant not finded.

    All in the same file, dhcp.c

    – ‘Sn_CR_RECV’ undeclared (first use in this function)
    – ‘Sn_CR_SEND’ undeclared (first use in this function) (implicit declaration of function ‘W5100_Sn_CR’ [-Wimplicit-function-declaration])
    – ‘W5100_MAC_ADDRESS_0’ undeclared (first use in this function)
    – ‘W5100_MAC_ADDRESS_0’ undeclared (first use in this function) ‘W5100_MAC_ADDRESS_1’ undeclared (first use in this function) ‘W5100_MAC_ADDRESS_2’ undeclared (first use in this function)
    – ‘W5100_MAC_ADDRESS_1’ undeclared (first use in this function)
    – ‘W5100_MAC_ADDRESS_2’ undeclared (first use in this function)
    – ‘W5100_MAC_ADDRESS_3’ undeclared (first use in this function)
    – ‘W5100_MAC_ADDRESS_3’ undeclared (first use in this function) ‘W5100_MAC_ADDRESS_4’ undeclared (first use in this function) ‘W5100_MAC_ADDRESS_5’ undeclared (first use in this function)
    – ‘W5100_MAC_ADDRESS_4’ undeclared (first use in this function)
    – ‘W5100_MAC_ADDRESS_5’ undeclared (first use in this function)
    – ‘W5100_RX_MEMORY_SOCKET_0_ADDRESS’ undeclared (first use in this function) ‘W5100_RX_SOCKET_0_SIZE_BYTES’ undeclared (first use in this function)
    – ‘W5100_RX_MEMORY_SOCKET_1_ADDRESS’ undeclared (first use in this function) ‘W5100_RX_SOCKET_1_SIZE_BYTES’ undeclared (first use in this function)
    – ‘W5100_RX_MEMORY_SOCKET_2_ADDRESS’ undeclared (first use in this function) ‘W5100_RX_SOCKET_2_SIZE_BYTES’ undeclared (first use in this function)
    – ‘W5100_RX_MEMORY_SOCKET_3_ADDRESS’ undeclared (first use in this function) ‘W5100_RX_SOCKET_3_SIZE_BYTES’ undeclared (first use in this function)
    – ‘W5100_TX_MEMORY_SOCKET_0_ADDRESS’ undeclared (first use in this function) ‘W5100_TX_SOCKET_0_SIZE_BYTES’ undeclared (first use in this function)
    – ‘W5100_TX_MEMORY_SOCKET_1_ADDRESS’ undeclared (first use in this function) ‘W5100_TX_SOCKET_1_SIZE_BYTES’ undeclared (first use in this function)
    – ‘W5100_TX_MEMORY_SOCKET_2_ADDRESS’ undeclared (first use in this function) ‘W5100_TX_SOCKET_2_SIZE_BYTES’ undeclared (first use in this function)
    – ‘W5100_TX_MEMORY_SOCKET_3_ADDRESS’ undeclared (first use in this function) ‘W5100_TX_SOCKET_3_SIZE_BYTES’ undeclared (first use in this function)
    – mingw32-make: *** [Sources/dhcp.o] Error 1
    – mingw32-make: *** Waiting for unfinished jobs….

    Could you solve these problems? Or do you know how define this constants?

    Greetings!

    Like

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.