|
ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
|
Layer: Foundation (tutorial) ยท Build flags: none (core features only)
This is the smallest complete server in the library and the right place to start. It brings up WiFi, registers one route, and then services HTTP requests forever. Nothing here is optional or gated behind a feature flag: HTTP/1.1 parsing, routing, and the automatic error responses are always compiled in.
Read this one until the shape is obvious, then move to Advanced, which adds more routes, wildcards, and request parsing on top of exactly this skeleton.
The shape of every sketch. A ProtoCore program is three things: one global server object, a setup() that connects WiFi and registers routes before calling begin(), and a loop() that pumps the pipeline:
handle() is the heartbeat. Each call sweeps idle connections, drains the TCP event queue, dispatches completed requests to your handlers, and emits the automatic protocol error responses. No request is processed off this call, so loop() must never block.
The handler signature. Every route handler takes the connection's slot_id and the parsed request, and replies with server.send():
slot_id identifies the connection you are answering (pass it back to send()); req carries the method, path, version, headers, query parameters, and body. Every buffer in req is fixed-size and bounds-checked at parse time, and req->body is always null-terminated, so handlers never allocate.
Registering a route. on() binds a path and a method to a handler, and must be called before begin(). The method is an enum class, so it carries its type name:
Checking begin(). It returns 1 on success and a negative error code on failure (listener pool full, or an lwIP error). Because -1 is "truthy", test result < 0, not !result:
Getting the device's address. Use the library's own accessor, pc_net_egress_ip(), which returns the egress IP in network byte order. The Arduino WiFi classes are not used anywhere in this library or its examples: all networking goes through the library transport, so the same sketch works regardless of which interface carries the traffic.
What the framework does for you. You write no code for malformed input. The parser auto-sends 400 Bad Request for an RFC 7230 character violation, 413 Payload Too Large when Content-Length exceeds BODY_BUF_SIZE, 414 URI Too Long when the path exceeds MAX_PATH_LEN, and 501 Not Implemented when a Transfer-Encoding header is present (chunked request bodies are not accepted). Those limits are the compile-time capacity constants in protocore_config.h.
Every technique below builds on this skeleton:
| You want to... | See |
|---|---|
| Add more routes and read the body | Advanced |
| Match a path prefix with a wildcard | Advanced |
Read ?query= parameters | Totp |
Handle a POST body | Csrf |
| Install a custom 404 handler | Expert |
| Inspect the connection pool | Expert |
| See every tunable flag and constant | Configuration |
| Serve files from flash or an SD card | FileServing |
| Allow cross-origin browser requests | CORS |
This example needs no feature flags. Set SSID / PASSWORD in the sketch first, then compile for an ESP32 board:
Flash it from a PlatformIO project (pio run -t upload), open the serial monitor at 115200 baud to read the assigned IP, then:
You should see Welcome to ProtoCore!.
If the upload itself fails, or the board never prints an IP, start with docs/HARDWARE_HOOKUP.md: it covers the charge-only USB cable, download mode, and the other failures that look like code problems and are not.
The complete sketch (Basic.ino), reproduced with added explanatory comments: