Master The Pico WiFi: Simplest HTTP Client
Written by Harry Fairhead & Mike James   
Tuesday, 11 April 2023
Article Index
Master The Pico WiFi: Simplest HTTP Client
HTTP
HTTP Client
CMakeLists etc

An HTTP Client

As a first example we can construct the simplest possible HTTP client. In the sake of simplicity no error checking is performed and only the callbacks that are needed are defined. This is as simple as it gets and a starting point for a more complete client.

The most basic transaction the client can have with the server is to send a GET request for the server to send back a particular file. Thus the simplest header is:

"GET /index.html HTTP/1.1\r\n\r\n"

which is a request for the server to send index.html. In most cases we need one more header, HOST, which gives the domain name of the server. Why do we need it? Simply because HTTP says you should and many websites are hosted by a single server at the same IP address. Which website the server retrieves the file from is governed by the domain name you specify in the HOST header.

This means that the simplest set of headers we can send the server is:

"GET /index.htm HTTP/1.1\r\nHOST:example.org\r\n\r\"

which corresponds to the headers:

GET /index.html HTTP/1.1
HOST:example.org

An HTTP request always ends with a blank line. If you don't send the blank line then you will get no response from most servers. In addition, the HOST header has to have the domain name with no additional syntax - no slashes and no http: or similar.

#define BUF_SIZE 2048
char myBuff[BUF_SIZE];
char header[] = 
 "GET /index.html HTTP/1.1\r\nHOST:example.com\r\n\r\n";

Now we are ready to send our request to the server and the first thing we need is a PCB set to call a recv callback:

struct tcp_pcb *pcb = tcp_new();
tcp_recv(pcb, recv);

In this case we only need to define a callback to handle the received data. Now we have a PCB we can connect to the server:

ip_addr_t ip;
IP4_ADDR(&ip, 93, 184, 216, 34);
cyw43_arch_lwip_begin();
err_t err = tcp_connect(pcb, &ip, 80, connected);
cyw43_arch_lwip_end();

Now all we have to do is define the two callbacks. The connected callback is used to send the headers to the server:

static err_t connected(void *arg, struct tcp_pcb *pcb,
err_t err)
{
err = tcp_write(pcb, header, strlen(header), 0);
err = tcp_output(pcb);
return ERR_OK;
}

The recv callback simply checks to see that there really is something to read and then displays some statistics, converts the PBUF to a character array and displays the content. Finally it reports that the data has been received and frees the PBUF.

err_t recv(void *arg, struct tcp_pcb *pcb, 
struct pbuf *p, err_t err)
{
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);
tcp_recved(pcb, p->tot_len);
pbuf_free(p);
}
return ERR_OK;
}

This is a very simple receive function and it doesn’t make any attempt to deal with empty responses from the server or to close the connection.

Usually an HTTP 1.1 connection is left open even after the requested data has been transmitted by the server just in case it can be reused by the client for another request. In this case the connection eventually times out. You can include an additional header Connection: close to ask the server not to keep the connection open but some will ignore this.

The complete program is:

#include <stdio.h>
#include "pico/stdlib.h" #include "pico/cyw43_arch.h"
#include "setupWifi.h"
#include "lwip/tcp.h"
#define BUF_SIZE 2048
char myBuff[BUF_SIZE];
char header[] =
"GET /index.html HTTP/1.1\r\n
HOST:example.com\r\n";
err_t recv(void *arg, struct tcp_pcb *pcb,
struct pbuf *p, err_t err)
{
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);
tcp_recved(pcb, p->tot_len);
pbuf_free(p);
}
return ERR_OK;
}
err_t connected(void *arg, struct tcp_pcb *pcb,
err_t err)
{
err = tcp_write(pcb, header, strlen(header), 0);
err = tcp_output(pcb);
return ERR_OK;
}
int main()
{
stdio_init_all();
connect();
struct tcp_pcb *pcb = tcp_new();
tcp_recv(pcb, recv);
ip_addr_t ip;
IP4_ADDR(&ip, 93, 184, 216, 34);
cyw43_arch_lwip_begin();
err_t err = tcp_connect(pcb, &ip, 80, connected);
cyw43_arch_lwip_end();
while (true)
{
sleep_ms(500);
}
}


Last Updated ( Tuesday, 11 April 2023 )