..

Raspberry Pi Pico W: HTTP Client Part I - Simple Client

Time to try some networking with Raspberry Pi Pico W. This example from the documentation works for me:

    if (cyw43_arch_init_with_country(CYW43_COUNTRY_UK)) {
        printf("failed to initialise\n");
        return 1;
    }
    printf("initialised\n");

    cyw43_arch_enable_sta_mode();

    if (cyw43_arch_wifi_connect_timeout_ms(SSID, PASS, CYW43_AUTH_WPA2_AES_PSK, 10000)) {
        printf("failed to connect\n");
        return 1;
    }
    printf("connected\n");

But just connecting is not enough. So let’s try to make an HTTP request.

Adding:

target_link_libraries(<project>
        pico_cyw43_arch_lwip_threadsafe_background
        pico_lwip_http
        pico_stdlib
        )

to CMakeLists.txt to get the HTTP client. Then:

#include "lwip/apps/http_client.h"

But what’s next? The documentation, I guess: http://lwip.nongnu.org/2_1_x/group__httpc.html

First Attempt: httpc_get_file_dns

The simplest option seems to be httpc_get_file_dns, which also resolves the hostname.

Added the following code:

    httpc_connection_t settings = {
            .use_proxy = 0,
            .headers_done_fn = headers_done_fn,
            .result_fn = result_fn
    };
    httpc_state_t *connection = NULL;

    err_t err = httpc_get_file_dns("neverssl.com", HTTP_DEFAULT_PORT, "/", &settings, recv_fn, NULL, &connection);
    printf("err = %d\n", err);

with some dummy callbacks:

static err_t headers_done_fn(httpc_state_t *connection, void *arg,
                             struct pbuf *hdr, u16_t hdr_len, u32_t content_len)
{
    printf("in headers_done_fn\n");
    return ERR_OK;
}

static void result_fn(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err)
{
    printf("in result_fn\n");
}

static err_t recv_fn(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    printf("in recv_fn\n");
    return ERR_OK;
}

Unfortunately, err is 0 but nothing else is logged. The output:

initialised
Version: 7.95.49 (2271bb6 CY) CRC: b7a28ef3 Date: Mon 2021-11-29 22:50:27 PST Ucode Ver: 1043.2162 FWID 01-c51d9400
cyw43 loaded ok, mac 28:cd:c1:00:5f:62
API: 12.2
Data: RaspberryPi.PicoW
Compiler: 1.29.4
ClmImport: 1.47.1
Customization: v5 22/06/24
Creation: 2022-06-24 06:55:08
connect status: joining
connect status: no ip
connect status: link up
connected
err = 0

But it’s possible that the problem is that I’m not waiting long enough. It’s not a blocking request, but rather a “request start” call, and the callbacks are called when and if things are received. So let’s add a 10-second delay.

The result:

in headers_done_fn
in recv_fn
in recv_fn
in recv_fn
in result_fn
10 second timeout done

Lovely. Next we want to see what we’re getting.

static void result_fn(void *arg, httpc_result_t httpc_result, u32_t rx_content_len, u32_t srv_res, err_t err)
{
    printf(">>> result_fn >>>\n");
    printf("httpc_result: %s\n",
             httpc_result == HTTPC_RESULT_OK              ? "HTTPC_RESULT_OK"
           : httpc_result == HTTPC_RESULT_ERR_UNKNOWN     ? "HTTPC_RESULT_ERR_UNKNOWN"
           : httpc_result == HTTPC_RESULT_ERR_CONNECT     ? "HTTPC_RESULT_ERR_CONNECT"
           : httpc_result == HTTPC_RESULT_ERR_HOSTNAME    ? "HTTPC_RESULT_ERR_HOSTNAME"
           : httpc_result == HTTPC_RESULT_ERR_CLOSED      ? "HTTPC_RESULT_ERR_CLOSED"
           : httpc_result == HTTPC_RESULT_ERR_TIMEOUT     ? "HTTPC_RESULT_ERR_TIMEOUT"
           : httpc_result == HTTPC_RESULT_ERR_SVR_RESP    ? "HTTPC_RESULT_ERR_SVR_RESP"
           : httpc_result == HTTPC_RESULT_ERR_MEM         ? "HTTPC_RESULT_ERR_MEM"
           : httpc_result == HTTPC_RESULT_LOCAL_ABORT     ? "HTTPC_RESULT_LOCAL_ABORT"
           : httpc_result == HTTPC_RESULT_ERR_CONTENT_LEN ? "HTTPC_RESULT_ERR_CONTENT_LEN"
           : "*UNKNOWN*");
    printf("received %ld bytes\n", rx_content_len);
    printf("server response: %ld\n", srv_res);
    printf("err: %d\n", err);
    printf("<<< result_fn <<<\n");
}

All is well:

>>> result_fn >>>
httpc_result: HTTPC_RESULT_OK
received 3961 bytes
server response: 200
err: 0
<<< result_fn <<<

What about actual data? Added this:

static err_t recv_fn(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    printf(">>> recv_fn >>>\n");
    if (p == NULL) {
        printf("p is NULL\n");
    } else {
        printf("p: %p\n", p);
        printf("next: %p\n", p->next);
        printf("payload: %p\n", p->payload);
        printf("len: %d\n", p->len);
    }
    printf("<<< recv_fn <<<\n");
    return ERR_OK;
}

And the result:

>>> recv_fn >>>
p: 2000A704
next: 00000000
payload: 2000A87D
len: 1153
<<< recv_fn <<<
>>> recv_fn >>>
p: 2000A108
next: 00000000
payload: 2000A14E
len: 1460
<<< recv_fn <<<
>>> recv_fn >>>
p: 20009B0C
next: 00000000
payload: 20009B52
len: 1348
<<< recv_fn <<<

I’ll skip headers for now. I think that this is a good start. But of course, most of the internet is HTTPS.