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 🙂
List of Tutorials
- FRDM with Arduino Ethernet Shield R3, Part 1: SD Card
- FRDM with Arduino Ethernet Shield R3, Part 2: Ping
- FRDM with Arduino Ethernet Shield R3, Part 3: Embedded Web Server
- 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.
To make a web server, I need 3 things to do:
- Initialize the W5100
- Using Sockets
- 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:
- Reset the device
- Configure the network address and gateway
- 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:
- 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.
- 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:
- Enter the address of the board in the browser (e.g. http://192.168.0.12)
- 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).- In the server, I get a
W5100_SOCK_ESTABLISHED
status.- The browser sends an HTTP “GET“or “POST” request to the server. With
SOCK_ReceivedSize()
the server knows the size of the request, and withSOCK_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.
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-aliveThe 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:
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.
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=0Depending 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
- Detect that it is a POST message (this is known by the “POST /” at the beginning)
- 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 🙂
I think, it would be wonderful watching it to run on a video
LikeLike
VERY COOL!
Thanks Erich.
LikeLike
Ordered this Arduino shield and tested. Also managed to switch this application to FRDM-KL46Z.
It works!
Thank you!
LikeLike
Excellent :-), thanks for letting us know!
LikeLike
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?
LikeLike
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.
LikeLike
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?
LikeLike
VERY NICE! Altough the web page does not show properly in my Firefox?
LikeLike
Interestingly, it works with Internet Explorer 🙂
LikeLike
Only IE works, I don’t know why
LikeLike
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
LikeLike
My original page/project worked both for IE and Firefox. it must be something about HTML header/compatibility?
LikeLike
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;
}
LikeLike
wrong text arrived here, something happened with Copy-Paste…
LikeLike
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?
LikeLike
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
LikeLike
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.
LikeLike
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;
}
LikeLike
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
LikeLike
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!
LikeLike
Interesting: the counter increments by 1, 2, 3 …depending on the number of browsers accessing the Web Server simultaneously
LikeLike
Pingback: FRDM with Arduino Ethernet Shield R3, Part 2: Ping | MCU on Eclipse
Pingback: FRDM with Arduino Ethernet Shield R3, Part 1: SD Card | MCU on Eclipse
Pingback: Binary Files for the mbed Bootloader with Eclipse and GNU ARM Eclipse Plugins | MCU on Eclipse
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
LikeLike
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!
LikeLike
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;
}
LikeLike
Thanks, I need to check this out next weekend (hopfully I will find some time)!
LikeLike
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.
LikeLike
Pingback: FRDM with Arduino Ethernet Shield R3, Part 4: MinIni | MCU on Eclipse
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. 🙂
LikeLike
Hi Amanda,
I have not done this, so unfortunately I’m of little help. But maybe someone else can help out?
Erich
LikeLike
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.
LikeLike
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.
LikeLike
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.
LikeLike
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
LikeLike
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.
LikeLike
Will this project work on a Arduino Uno with the W5100?
LikeLike
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++.
LikeLike
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!
LikeLike
Thanks!
No, I have not used the SD card in that project to store web page content, but this is certainly possible.
LikeLike
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!
LikeLike
It seems to me that communication with the SD card has failed. You need to use the debugger and debug it to find out what is going on.
LikeLike
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!
LikeLike
Excellent, thanks for that update!
LikeLike
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!
LikeLike
It seems to me that you are using mingw32 which does not seem right to me.
LikeLike
Thanks for you reply.
What must i use? How can i change that?
Greetings
LikeLike
Use KDS (Kinetis Design Studio)
LikeLike
Erich,
Good beginning of the year!
I have resumed the project and worked with KDS, but in both cases I have the same mistakes. It is as if a file with definition of constants and functions is missing, for DHCP mode.
On the other hand, have you tried reading the time from the internet once connected?
Regards!
LikeLike
Hi Gabriel,
I have not connected to a time server with that project, but I have used UDP time servers with ESP32. I wrote a few posts about this project: https://mcuoneclipse.com/2019/09/01/programming-the-esp32-with-an-arm-cortex-m-usb-cdc-gateway/
Erich
LikeLike