Layer: Foundation (tutorial) ยท Build flags: core (this example overrides defaults rather than enabling features)
What this example teaches
Everything in the library is sized at compile time, and this example is the reference for the knobs. It strips optional features down to a lean REST-only profile, tightens every capacity constant, exposes routes that echo the active configuration so you can confirm your overrides took effect, and demonstrates a runtime config struct. The sketch's header comment is itself a cheat-sheet of every flag and constant.
Overrides go BEFORE the include. Each #define must appear before #include "protocore.h" so the library compiles with your value instead of the default. Here the optional subsystems are stripped and the buffers are shrunk for a small sensor node:
#define PC_ENABLE_WEBSOCKET 0
#define PC_ENABLE_SSE 0
#define PC_ENABLE_MULTIPART 0
#define PC_ENABLE_FILE_SERVING 0
#define PC_ENABLE_AUTH 0
#define MAX_CONNS 2
#define RX_BUF_SIZE 512
#define BODY_BUF_SIZE 128
Layer 7 (Application) - public HTTP routing API.
When compiling with pio ci (separate library translation units), these in-sketch #defines only affect the sketch. Pass them as build_flags so the library compiles with them too - see the build-flag tree and the repository's contributing notes.
Capacity constants are visible at runtime. GET /config serializes the active sizing constants as JSON, so you can verify a build without a debugger:
snprintf(body, sizeof(body), "{\"MAX_CONNS\":%u,\"RX_BUF_SIZE\":%u,\"BODY_BUF_SIZE\":%u,...}",
The limits are real. With BODY_BUF_SIZE=128, a POST body over 128 bytes is rejected with an automatic 413 before your handler runs. With MAX_QUERY_PARAMS=4, a fifth query parameter is parsed but silently dropped - GET /search echoes exactly what was kept so you can see the boundary.
Runtime config struct. A WebServerConfig passed to begin() overrides the idle timeout without a rebuild (pass nullptr to use the built-in default, CONN_TIMEOUT_MS):
int32_t result = server.begin(80, &cfg);
#define CONN_TIMEOUT_MS
Compile-time default for connection idle timeout in milliseconds.
Runtime-tunable server parameters.
The header comment also documents the feature-dependency tree (a child flag needs its parent; illegal combinations fail with a compile-time #error).
Build and run
pio ci --board=esp32dev --project-option="framework=arduino" \
--project-option="build_flags=-DPC_ENABLE_DIAG=1" \
--lib="." examples/Foundation/Configuration/Configuration.ino
curl http://<ip>/config # see the active sizing constants
curl -X POST http://<ip>/echo -d "hello" # bodies > BODY_BUF_SIZE get a 413
curl "http://<ip>/search?q=esp32&sort=date" # extra params past MAX_QUERY_PARAMS are dropped
Annotated source
The complete sketch (Configuration.ino). The long header comment (a full flag/constant reference) is kept verbatim; the C++ is annotated.
#define PC_ENABLE_WEBSOCKET 0
#define PC_ENABLE_SSE 0
#define PC_ENABLE_MULTIPART 0
#define PC_ENABLE_FILE_SERVING 0
#define PC_ENABLE_AUTH 0
#define PC_ENABLE_DIAG 1
#define MAX_CONNS 2
#define EVT_QUEUE_DEPTH 8
#define RX_BUF_SIZE 512
#define BODY_BUF_SIZE 128
#define MAX_ROUTES 8
#define MAX_HEADERS 6
#define MAX_PATH_LEN 48
#define MAX_KEY_LEN 16
#define MAX_VAL_LEN 32
#define MAX_QUERY_LEN 64
#define MAX_QUERY_PARAMS 4
#define QUERY_KEY_LEN 16
#define QUERY_VAL_LEN 24
#define CONN_TIMEOUT_MS 3000
static const char *SSID = "YOUR_SSID";
static const char *PASSWORD = "YOUR_PASSWORD";
void handle_config(uint8_t slot_id,
HttpReq *req)
{
char body[384];
snprintf(body, sizeof(body),
"{"
"\"MAX_CONNS\":%u,"
"\"RX_BUF_SIZE\":%u,"
"\"CONN_TIMEOUT_MS\":%u,"
"\"MAX_HEADERS\":%u,"
"\"MAX_PATH_LEN\":%u,"
"\"MAX_KEY_LEN\":%u,"
"\"MAX_VAL_LEN\":%u,"
"\"MAX_QUERY_LEN\":%u,"
"\"MAX_QUERY_PARAMS\":%u,"
"\"QUERY_KEY_LEN\":%u,"
"\"QUERY_VAL_LEN\":%u,"
"\"BODY_BUF_SIZE\":%u,"
"\"MAX_ROUTES\":%u"
"}",
server.
send(slot_id, 200,
"application/json", body);
}
void handle_echo(uint8_t slot_id,
HttpReq *req)
{
server.
send(slot_id, 200,
"text/plain", (
const char *)req->
body);
}
void handle_search(uint8_t slot_id,
HttpReq *req)
{
char body[256] = "params: ";
{
if (i > 0)
strncat(body, ", ", sizeof(body) - strlen(body) - 1);
strncat(body, "=", sizeof(body) - strlen(body) - 1);
}
server.
send(slot_id, 200,
"text/plain", body);
}
void setup()
{
Serial.begin(115200);
Serial.println("\n--- Active PC config ---");
Serial.printf(
" MAX_CONNS = %u\n", (
unsigned)
MAX_CONNS);
Serial.printf(
" RX_BUF_SIZE = %u\n", (
unsigned)
RX_BUF_SIZE);
Serial.println("----------------------------------");
Serial.print("Connecting to WiFi");
{
delay(250);
Serial.print('.');
}
Serial.printf("IP: %u.%u.%u.%u\n", (unsigned)(ip & 0xFF), (unsigned)((ip >> 8) & 0xFF),
(unsigned)((ip >> 16) & 0xFF), (unsigned)((ip >> 24) & 0xFF));
int32_t result = server.
begin(80, &cfg);
if (result < 0)
{
Serial.printf("begin() failed (error %d)\n", result);
return;
}
Serial.println("Server started on port 80");
}
void loop()
{
}
Single-port HTTP server with deterministic, zero-allocation execution.
void send(uint8_t slot_id, int code, const char *content_type, const char *payload)
Send an HTTP response with a body and close the connection.
int32_t begin(const WebServerConfig *cfg=nullptr)
Initialize all connection slots and open all registered listeners.
void on(const char *path, HttpMethod method, Handler callback)
Register a route handler.
void handle()
Drive the server - call every Arduino loop() iteration.
bool init_wifi_physical(const char *, const char *)
Connect to a WiFi access point.
uint32_t pc_net_egress_ip(void)
IPv4 (network byte order) of the current egress interface, or 0 if none.
bool wifi_ready()
True if the WiFi station link is up (associated + an IP is assigned).
Layer 1 (Physical) - link bring-up and live egress-interface reporting.
@ HTTP_POST
Non-idempotent create / action.
@ HTTP_GET
Safe, idempotent read.
#define MAX_VAL_LEN
Maximum header field-value length.
#define MAX_QUERY_PARAMS
Maximum number of parsed query-string parameters.
#define MAX_QUERY_LEN
Maximum raw query-string length (everything after ?).
#define QUERY_VAL_LEN
Maximum query-parameter value length.
#define MAX_PATH_LEN
Maximum URL path length (including leading /).
#define QUERY_KEY_LEN
Maximum query-parameter key length.
#define MAX_KEY_LEN
Maximum header field-name length (e.g. "Content-Type").
Fully-parsed HTTP/1.1 request.
QueryParam query_params[MAX_QUERY_PARAMS]
Parsed key=value pairs.
uint8_t body[BODY_BUF_SIZE+1]
Stored body bytes, always null-terminated.
uint8_t query_count
Valid entries in query_params[].
char val[QUERY_VAL_LEN]
Parameter value (empty string if absent).
char key[QUERY_KEY_LEN]
Parameter name, null-terminated.