Etlas is a news, stock market, and local weather tracking system for your home.

Consisting of an ESP32 NodeMCU D1, e-paper display, and DHT22 sensor module, the Etlas client operates with less than 240mA (often much less), making it very energy efficient. The modular embedded system lends itself well to repairing as you can replace every major part independently and effortlessly without having to solder.

Etlas client

front back circuit

Etlas client needs a wired connection to the power source. I use a 9V phone charging cable to power the MCU via its micro USB port. The 3.3V output pin on the MCU supplies power to the peripherals.

Etlas requires a 2.4GHz WiFi access point to download news and stock data on demand. In Singapore, almost every household with WiFi tends to have 5GHz and 2.4GHz access points set up, which likely means extra network configuration is unnecessary.

I wrote the embedded application for the ESP32 in C with the help of ESP-IDF v5.2.1 and the freeRTOS. To build and upload the application, install ESP-IDF on a computer and run idf.py build flash from the source directory.

I did not change much in the SDK menuconfig except for device-specific settings like the flash size. One thing to note is that ESP-IDF default settings enforce SSL certificate validation. Hence, we should either include SSL certificates in the build or override the TLS policy to skip server certificate verification.

If the extra layer of security added by certificate validation is relevant to you, follow the steps below to modify the program to enable that feature.

How to enable server certificate validation

Suppose we want to connect to https://example.com. First, we shall download the SSL certificates for the domain using the following command.

$ openssl s_client -showcerts -connect example.com:443 </dev/null.

Then we save the root certificate (the last certificate in the certificate chain) to a file, say, example_cert.pem, in the same directory as the top-level CMakeLists.txt and update the CMakeLists.txt file to include it in the build.

idf_component_register(SRCS ${SRC}
    EMBED_TXTFILES example_cert.pem
    INCLUDE_DIRS ".")

Now that we have the certificates in the program binary, we can add the following lines to the source files that perform HTTPS operations (e.g., news.c and stock.c) to verify the certificates the servers use.

extern const char example_cert_pem_start[] asm("_binary_example_cert_pem_start");
extern const char example_cert_pem_end[] asm("_binary_example_cert_pem_end");

esp_http_client_config_t conf = {
    .url = "https://example.com",
    .is_async = true,
    .timeout_ms = 5000,
    .event_handler = http_evt_handler,
    .cert_pem = example_cert_pem_start,
    .disable_auto_redirect = true,
};

Etlas server

Etlas client depends on a simple Flask web app to act as a halfway server between the stock market data provider and itself. The following diagram outlines the Etlas system architecture.

architecture

In the future, I will update Etlas to relay the news feed through the same server for improved security and flexibility.

I chose FastCGI to host the Flask web app because it is an open, lightweight, secure, and language-independent interface. With this architecture, we delegate web request handling, TLS, and authentication to OpenBSD's httpd, which, in its own right, is as simple and secure a web server as they come.

How to configure httpd for FastCGI

This section outlines the steps for setting up OpenBSD's httpd with FastCGI. However, the general idea, by and large, applies to other platforms like Linux and web servers that support FastCGI. If you decide to use OpenBSD, please consider supporting the project by donating to it.

The OpenBSD handbook has a complete, easy-to-follow guide for setting up httpd with SSL. There is little benefit in repeating that here. The handbook, however, does not have any FastCGI examples. Articles I found on the web on this subject were confusing.

FastCGI is a binary protocol where a web server like httpd talks to a FastCGI server over a Unix domain socket or port. Consequently, we should route requests to any path we want the FastCGI process to handle to the FastCGI socket.

The following sample configuration sends all requests to /fastcgi-path to the socket (the FastCGI server creates the etlas.sock file). In addition, the sample configuration secures the path with basic authentication and forces TLS for that route to prevent clients from exchanging credentials in plain text.

server "example.com" {
  listen on * tls port 443
  # SSL configuration goes here...

  # FastCGI path
  location "/fastcgi-path" {
    # Enable basic auth with htpasswd
    authenticate with "/htdocs/etlas/.htpasswd"
    fastcgi {
      socket "/run/etlas.sock"
    }
  }
}

# Force HTTPS for path with basic auth
server "example.com" {
  listen on * port 80
  root "/htdocs/example.com"

  location "/fastcgi-path" {
    block return 301 "https://$HTTP_HOST$REQUEST_URI"
  }
}

The project readme contains a step-by-step guide for running the server, creating the Etlas client user, granting permissions correctly to the socket, etc. Follow those steps to run the FastCGI process as a daemon. Also, don't forget to update the PF rules to allow connections to the ports httpd is listening on.

Files: source.tar.gz