Announcing HTTP Client availability for lv_micropython ESP32 port

ESP-IDF HTTP client is now available as part of espidf module.

This is a fully featured HTTP client provided as part of ESP-IDF, including TLS, header parsing, chunked responses, etc.
Now you can use it in Micropython.

It can be useful for interacting with REST API, or simply for fetching data from the web.

Here is a simple example that shows how to send a GET request, print HTTP headers and read the payload stream:

import espidf as esp

def event_handler(evt):
     if evt.event_id == esp.HTTP_EVENT.ON_HEADER:
         print("%s : %s" % (evt.header_key, evt.header_value))
     return 0

conf = esp.esp_http_client_config_t()
conf.register_event_handler(event_handler, None)
client = conf.init()
esp.esp_http_client_open(client, 0)
buffer = bytes(256)
while True:
     bytes_read = esp.esp_http_client_read(client, buffer, len(buffer))
     if bytes_read < 0: break
     print(buffer[:bytes_read].decode("utf-8"), end="")
     if bytes_read < len(buffer): break

What is this “None” parameter? This isn’t meant to be the user_data, is it?

This is IMHO the receiving side:

static inline void esp_http_client_register_event_handler(esp_http_client_config_t *config, http_event_handle_cb http_event_handler, void *user_data)
    config->event_handler = http_event_handler;
    config->user_data = user_data;

This sounds like the None ends up as a NULL pointer in user_data. But that’s not the case, is it?

The Micropython Binding uses the user_data pointer to save the Python callable object (the Python “callback”). When you pass None in this case it won’t set user_data C pointer to NULL, but allocate it for this puspose.

Under the hoods the user_data is really a Python dict, because the same user_data can be used with multiple callbacks and we may need to keep track of multiple callbacks with the same user_data.

The other option is to pass a dict as user_data. In that case the binding adds new entries to the dict instead of allocating a new one. This can be used (rarely) when you need to pass additional Python arguments to a callback, like in this case.