Master The Pico WiFi: HTTP Server
Written by Mike James & Harry Fairhead   
Monday, 13 May 2024
Article Index
Master The Pico WiFi: HTTP Server
The Response
Content Length
A Simple Server

Content Length

Finally the Content-Length header can be constructed using the same technique:

char ContLen[100] = {0};
snprintf(ContLen, sizeof ContLen, 
"Content-Length:%d \r\n", strlen(html));

Finally we put all the headers together into a single string.

snprintf(headers, sizeof headers,
"%s%s%s\r\n", Status, Date, ContLen);

Now we can assemble the data ready to be sent to the client:

char data[2048] = {0};
snprintf(data, sizeof data, "%s %s", headers, html);

sendData

Putting all this together into a function gives:

void sendData(struct altcp_pcb *pcb)
{
err_t err;
char html[] = "<html><head><title>Temperature</title>
</head>
<body><p>{\"humidity\":81%,\"airtemperature\":23.5C} </p></body></html>\r\n";
char headers[1024] = {0};
char Status[] = "HTTP/1.1 200 OK\r\nContent-Type: text/html;charset=UTF-8\r\nServer:Picow\r\n";
struct tm t;
getDateNow(&t);
char Date[100];
strftime(Date, sizeof(Date), "Date: %a, %d %b %Y %k:%M:%S %Z\r\n", &t);
char ContLen[100] = {0};
snprintf(ContLen, sizeof ContLen, "Content-Length:%d \r\n", strlen(html));
snprintf(headers, sizeof headers, "%s%s%s\r\n", Status, Date, ContLen);
char data[2048] = {0};
snprintf(data, sizeof data, "%s%s", headers, html);
err = altcp_write(pcb, data, strlen(data), 0);
err = altcp_output(pcb); }

This function can be called from the recv callback handler to respond to the client:

err_t recv(void *arg, struct altcp_pcb *pcb, 
struct pbuf *p, err_t err)
{
char myBuff[BUF_SIZE];
if (p != NULL)
{
printf("recv total %d this buffer %d next %d err %d\n",
p->tot_len, p->len, p->next, err);
pbuf_copy_partial(p, myBuff, p->tot_len, 0);
myBuff[p->tot_len] = 0;
printf("Buffer= %s\n", myBuff);
altcp_recved(pcb, p->tot_len);
pbuf_free(p);
sendData(pcb);
}
return ERR_OK;
}

Managing Connections

If you put all this together then you will find that it works, but there are some rough edges to the way it behaves. In particular, it can’t handle more than one or at most two requests at a time. It also does nothing about closing connections. Of the two, closing connections is the more complicated. The problem is that HTTP 1.1 expects the connection to be kept open for more requests. This is complicated and the simplest solution is to close the connection as soon as the server has finished with it. The easiest place to do this is in the sent callback:

static err_t sent(void *arg, struct altcp_pcb *pcb, 
u16_t len)
{
altcp_close(pcb);
}
static err_t accept(void *arg, struct altcp_pcb *pcb,
err_t err)
{
altcp_recv(pcb, recv);
altcp_sent(pcb, sent);
printf("connect!\n");
return ERR_OK;
}

The only problem with this is that it might also kill any additional requests that the client has made on the same connection. How important this is depends on the data you are trying to transfer and on the way the client handles the abrupt disconnection. Notice that even with this modification the server will still deal with two requests from a general browser – one for the data and one for the site’s icon which it responds to by sending the data again.

If you want the server to deal with multiple requests on a single connection then your only choice is to use the poll callback to close a connection after it has been idle for the specified time:

static err_t poll(void *arg, struct altcp_pcb *pcb){
printf("Connection Closed");
altcp_close(pcb);
}
altcp_poll(pcb, poll,10);

Of course, if you choose to leave connections open until they have been idle for a long time, you are more likely to need to handle multiple connections. As lwIP isn’t being used in a multithreaded environment this has to be done using a queue. There are two additional versions of the listen function:

struct altcp_pcb* altcp_listen_with_backlog(
struct altcp_pcb *pcb,
u8_t backlog)

and:

struct altcp_pcb* altcp_listen_with_backlog_and_err(
struct altcp_pcb *pcb, u8_t backlog, err_t *err)

In practice, the first two listen functions are implemented as macros that call altcp_listen_with_backlog_and_err with defaults for the remaining parameters. The backlog parameter sets the size of the queue and the err parameter returns an error code if the listen returns NULL.

To allow this to work you also need:

#define TCP_LISTEN_BACKLOG 1

in your lwipopts.h.

For example, changing the listen function to:

pcb = altcp_listen_with_backlog(pcb,3);

allows for three connection attempts to be queued. In practice you may not notice the difference if you are trying to connect with a browser as these use various retry algorithms that cover up an inability to connect at once.

picomaster180

A Simple Server



Last Updated ( Monday, 13 May 2024 )