Can I make a Swift-wrapped HTTP GET request from the ESP32C6?

This article is part of a series.

So for this I created another component to act as a bridge. Right now this turned out to be mostly C while I figure out how to / whether to bring in things like URL and URL session, HTTPRequest…

I’ve written a client in Swift before, but it relied heavily on Foundation types. The only example in the the Swift Embedded Examples repo that uses Foundation is a beautiful Swift MMIO based example for the stm32 which is making me rethink my chip choices. Although SVD data is available for the esp32c6 even though it’s RISCV, not ARM.

As an alternative to Foundation, Hummingbird brings in SwiftNIO.

Both Foundation and SwiftNIO are very large and I’m not in the mood to wrestle with the CMake file today.

While I overthink this, I just made the proof of concept version with C.

The Swift Files

Main.swift

Right above the while loop but after the delay to wait for the WiFi to connect…

  //Waiting for wifi to connect...
  delay(2000);
  let client = HTTPClient()
  client.getAndPrint(from: "example.com", route: "/")

HTTP.swift

Again with the ugly string passing which I hope is temporary.

THe result code is something I could turn into a thrown error at some point. It’s a really common pattern in C that the result is written at a shared pointer and the error code is the thing that’s returned.

final class HTTPClient {

    func getAndPrint(from host: String, route: String) {
        print("Getting...")
        print(http_bridge_bridge_return_twelve())
        let local_host = host.utf8CString
        let local_route = route.utf8CString

        //TODO: test with span on beta?
        local_route.withContiguousStorageIfAvailable { route_buffer in
            local_host.withContiguousStorageIfAvailable { host_buffer in
                let resultCode = http_bridge_get(host_buffer.baseAddress, route_buffer.baseAddress)
                print("result code: \(resultCode)")
            }
        }

    }
}

The Component Code

The esp-idf has a HTTP Client library, which a more robust version of this code could use. esp-idf example

The C File

Similar code is in the HTTP_Request example.

Both do the following basic steps:

Making sure to close the socket in case of any failures as well.

int http_bridge_get(const char *host, const char *path) {
    printf("A");
    struct addrinfo hints = {
        .ai_family = AF_INET,
        .ai_socktype = SOCK_STREAM,
    };
    struct addrinfo *res;
    int s;

    printf("B");
    char port_str[] = "80";
    int err = getaddrinfo(host, port_str, &hints, &res);
    //int err = getaddrinfo(host, port_str, &hints, &res);
    if (err != 0 || res == NULL) {
        printf("DNS lookup failed for %s", host);
        return 1;
    }

    printf("C");
    s = socket(res->ai_family, res->ai_socktype, 0);
    if (s < 0) {
        printf("socket failed");
        freeaddrinfo(res);
        return 2;
    }

    printf("D");
    if (connect(s, res->ai_addr, res->ai_addrlen) != 0) {
        printf("connect failed");
        close(s);
        freeaddrinfo(res);
        return 3;
    }

    printf("E");
    freeaddrinfo(res);
    
    printf("F");
    char req[128];
    snprintf(req, sizeof(req),
             "GET %s HTTP/1.0\r\n"
             "Host: %s\r\n"
             "User-Agent: esp-idf/5.5\r\n"
             "\r\n",
             path, host);

    if (write(s, req, strlen(req)) < 0) {
        printf("send failed");
        close(s);
        return 4;
    }

    char recv_buf[512];
    int r;
    while ((r = read(s, recv_buf, sizeof(recv_buf) - 1)) > 0) {
        recv_buf[r] = 0; // null terminate
        printf("%s", recv_buf);
    }
    close(s);
    return 0;
}

The header

#include <string.h>
#include <sys/socket.h>
#include <netdb.h>   


int http_bridge_bridge_return_twelve(void);

int http_bridge_get(const char *host, const char *path);

The CMake

No REQUIRED in this because the includes are part of the the C implementation.

idf_component_register(SRCS "http_bridge.c"
                    INCLUDE_DIRS "include")

Summary

Kinda punted on this part a bit while I figure out how much time I have to really dig in. Need to do some parts ordering, get the real server up, etc.

This article is part of a series.