An HTTPS client example for the Raspberry Pi Pico W.
Implemented in C, leveraging the Raspberry Pi Pico C/C++ SDK.
There are many excellent examples of HTTP clients for the Raspberry Pi Pico W platform. The extra complexities (viz. cryptography) introduced by the TLS layer of HTTPS, make corresponding HTTPS examples somewhat less common.
This repository contains a simple yet complete example C application which sends a single request to a web server over HTTPS and reads the resulting response.
- Raspberry Pi Pico C/C++ SDK (tested against 1.5.0)
The following minimum build-time configuration is required for correct execution;
- In picohttps.h;
- Set PICOHTTPS_WIFI_SSID to your wireless network SSID
- Set PICOHTTPS_HOSTNAME to the web server hostname
- Set PICOHTTPS_CA_ROOT_CERT to the CA certificate used to sign the web server's HTTPS certificate
- In CMakeLists.txt;
More detailed documentation of the above (and additional optional) configuration parameters can be found in the source files.
Sample configurations for the lwIP
and Mbed TLS
libraries used by the example application are provided in lwipopts.h and mbedtls_config.h respectively. These should work 'out-of-the-box', but can be adjusted if necessary/desired.
~/picohttps/$ mkdir build
~/picohttps/$ cd build
~/picohttps/build$ PICOHTTPS_WIFI_PASSWORD=mywirelesspassword cmake -D"PICO_BOARD=pico_w" ..
~/picohttps/build$ make
N.b. Whilst the wireless network password can be set in picohttps.h, it is strongly recommended to set this from the build environment instead (as shown above) to minimise the risk of disclosure (e.g. via source code commits).
- picohttps.h: Example application header file
- picohttps.c: Example application implementation file
- CMakeLists.txt: Example application build configuration
- lwipopts.h: lwIP library configuration
- mbedtls_config.h: Mbed TLS library configuration
The example application performs the following actions in sequence;
- Initialise Pico W I/O
- Initialise Pico W wireless hardware
- Connect to wireless network
- Resolve server hostname
- Connect to server over TCP + TLS
- Send HTTP request over TCP + TLS
- Read HTTP response over TCP + TLS
The function calls from picohttps.c:main which perform these actions are not deeply nested, and are declared and documented in picohttps.h.
Whilst the Raspberry Pi Pico C/C++ SDK is the only requirement for the example application, this is only because it bundles several libraries which together provide the functionality required for HTTPS;
- cyw43-driver — Driver for the CYW43 wireless hardware on the Pico W
- lwIP — Network stack. Provides DHCP, DNS and TCP/IP protocols
- Mbed TLS — Cryptography suite providing cryptographic primitives and certificate handling required for TLS
Library functionality used by the example application:
- Pico SDK standard I/O (pico_stdio)
- I/O over USB
- Pico SDK wireless architecture (pico_cyw43_arch)
- Initialization of wireless hardware driver (CYW43) and networking stack (lwIP). Leverages the
thread_safe_background
abstraction for background maintenance of driver and networking stack. - Connection to wireless network
- Initialization of wireless hardware driver (CYW43) and networking stack (lwIP). Leverages the
- lwIP DNS 'raw' API
- Server hostname resolution
- lwIP ALTCP 'raw' API
- Sending/receiving data over TCP. Received data handled asynchronously in interrupt context.
- lwIP ALTCP TLS 'raw' API
- Transparent addition of TLS to TCP code. Leverages the ALTCP compatible Mbed TLS port bundled with the lwIP code base.
- Mbed TLS
- Authentication and cryptography required for TLS. Used indirectly via ALTCP compatible Mbed TLS port bundled with the lwIP code base.
- For simplicity, only most basic of error handling is included;
- All functions return booleans (
true
on success) — no error codes - Errors printed to stdout
- All functions return booleans (
- Functions used as lwIP callbacks are prefixed with
callback_
for clarity. No lock acquisition is required when calling into the lwIP API from these. - The single common argument passed to lwIP connection callbacks is of type
struct altcp_callback_arg
and is used for accessing/modifying application state from callbacks. See struct altcp_callback_arg declaration for further documentation. - Dynamically allocated variables;
- TCP + TLS connection configuration (
struct altcp_tls_config config
): Allocated by lwIP API call (altcp_tls_create_config_client()
), freed by lwIP API call (altcp_close()
) - TCP + TLS connection PCB (
struct altcp_pcb pcb
): Allocated by lwIP API call (altcp_tls_new()
), freed by lwIP API call (altcp_tls_free_config()
) - TCP + TLS connection callback common argument (
struct altcp_callback_arg arg
): Allocated explicitly (malloc()
), freed explicitly (free()
) - lwIP packet buffer chain (
struct pbuf buf
): Allocated by lwIP, freed by lwIP API call (pbuf_free()
)
- TCP + TLS connection configuration (
- Server response printed to stdout on reception in
callback_altcp_recv()
- Currently no clear way to cleanly disconnect from wireless networks
- Hardcoded five second timeout awaiting a response from the server