tollan docs Sign up

Host a website on an ESP32#

A personal website, and the server is a €5 microcontroller. No VPS, no Raspberry Pi gateway, no router port forwarding: the ESP32 holds the whole page in flash and answers every visit itself, at a real HTTPS address. It's a slightly ridiculous experiment, and a good first build, because it touches every step you'll use for anything bigger.

DifficultyEasy
Time30 minutes
HardwareAny ESP32 board
PlanFree
The whole build in 40 seconds: name the board, expose port 80, download the project, flash it, and the page is live.

What you need#

  • An ESP32 dev board (ESP32 DevKit, or an S2, S3 or C3 board) and a USB cable.
  • VS Code with the PlatformIO extension.
  • Your page as one HTML file, with its CSS inside it.
  • A free Tollan account in the console.

How it works#

text
  browser ──HTTPS──▶ portfolio-byrne.a.tollan.app
                                ▲
                                │ the board connects out
  ESP32 ────────────────────────┘
    serves index.html from flash

The page is compiled into the firmware. When a visit arrives, the sketch sends the file back, and Tollan puts it on the internet behind a managed HTTPS certificate.

Step 1: Name it and pick your board#

In the console, add a device named portfolio. Choose the esp32 (Arduino / PlatformIO) architecture and your board, such as ESP32 DevKit (classic ESP32). Leave provisioning on Enrollment: the board makes its own key on first boot.

Step 2: Expose port 80#

Add a route with Internal host 127.0.0.1, Internal port 80 and a Hostname (SNI) exposure. The form previews your free HTTPS address, something like portfolio-byrne.a.tollan.app.

Step 3: Download the demo project#

Click Download demo project. It's a complete PlatformIO project for this board: the library, the board's src/tollan_config.h and a starter sketch.

Important

The link works once and expires after 15 minutes, and the token inside enrolls one board. Keep src/tollan_config.h out of git until the board has enrolled.

Step 4: Add your page and flash it#

Save your page as src/index.html, and tell PlatformIO to build it into the firmware by adding one line to platformio.ini:

ini
board_build.embed_txtfiles = src/index.html

Then replace src/main.cpp with this, and fill in your Wi-Fi details (2.4 GHz only):

cpp
// A whole website, served by an ESP32 at its own HTTPS address.
#include <WiFi.h>
#include <TollanAgent.h>
#include "tollan_config.h"   // from the console's demo project
using namespace tollan;

static const char* WIFI_SSID = "your-wifi";
static const char* WIFI_PASS = "your-wifi-password";

TollanAgent agent;

// src/index.html, linked in by board_build.embed_txtfiles (NUL-terminated).
extern const char INDEX_HTML[] asm("_binary_src_index_html_start");

// Send one complete HTTP response on a channel, then close it.
static void reply(uint32_t ch, const char* status, const char* type, const char* body, size_t len) {
  char head[160];
  int n = snprintf(head, sizeof(head),
                   "HTTP/1.1 %s\r\nContent-Type: %s\r\nContent-Length: %u\r\n"
                   "Connection: close\r\n\r\n",
                   status, type, (unsigned)len);
  agent.send(ch, (const uint8_t*)head, n);
  for (size_t off = 0; off < len; off += 1024) {   // send in 1 KB pieces
    agent.send(ch, (const uint8_t*)body + off, len - off < 1024 ? len - off : 1024);
  }
  agent.closeChannel(ch);
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(300);
  agent.log().printf("wifi ok, ip=%s\n", WiFi.localIP().toString().c_str());

  TollanConfig cfg;
  cfg.host = TOLLAN_RELAY_HOST;
  cfg.port = TOLLAN_RELAY_PORT;
  cfg.caCert = CA_CERT;
#ifdef TOLLAN_ENROLL_TOKEN
  cfg.enrollUrl = TOLLAN_ENROLL_URL;     // first boot: the board makes its key and enrolls
  cfg.enrollToken = TOLLAN_ENROLL_TOKEN;
  cfg.enrollCaCert = TOLLAN_ENROLL_CA;
#else
  cfg.clientCert = DEVICE_CERT;
  cfg.clientKey = DEVICE_KEY;
#endif
  cfg.renewUrl = TOLLAN_RENEW_URL;
  cfg.deviceId = TOLLAN_DEVICE_ID;       // health check-in for the console
  cfg.targetsUrl = TOLLAN_TARGETS_URL;

  // The page at /, a 404 for anything else.
  agent.onData([](uint32_t ch, const uint8_t* data, size_t len) {
    if (len >= 6 && memcmp(data, "GET / ", 6) == 0) {
      reply(ch, "200 OK", "text/html; charset=utf-8", INDEX_HTML, strlen(INDEX_HTML));
    } else {
      reply(ch, "404 Not Found", "text/plain", "not found\n", 10);
    }
  });

  agent.begin(cfg);
}

void loop() {
  agent.loop();
  delay(2);
}

embed_txtfiles turns the file into a string in flash, so the sketch never touches a filesystem. Click Upload.

Step 5: It's online#

The device turns online in the console within seconds of the board joining Wi-Fi. Open your address: on the free plan a browser sees a short Tollan notice the first time; click Continue and there's your site, served from a board on your desk.

Gotchas#

  • Only / exists. The sketch serves one page and answers everything else with a 404. For a second page, add another memcmp branch and a second embedded file.
  • Keep it to one file. Put the CSS in a <style> tag and use SVG or small inline images. A large page works, but every visit streams the whole file over the board's Wi-Fi.
  • Changing the page means re-flashing. Edit src/index.html and click Upload again; the board keeps its identity, so there's nothing new to download.

Where to take it next#