ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
DeviceUuid - a stable MAC-derived device UUID

Layer: L7 Application ยท Build flags: PC_ENABLE_DEVICE_ID

What this example teaches

A fleet of identical firmware images needs a stable per-device identity. pc_device_uuid() derives a deterministic RFC 4122 v5 UUID from the chip's factory MAC: the same value on every boot, with no storage to wear out or provision. Use it for mDNS hostnames, MQTT client IDs, telemetry tags, and the like.

Compute once, reuse everywhere:

static char g_uuid[PC_UUID_STR_LEN];
pc_device_uuid(g_uuid); // stable per-chip UUID string

PC_UUID_STR_LEN sizes the caller-owned buffer (no heap). Because it is derived (hashed from the MAC, not random) it is reproducible and needs no NVS. GET /id returns it as JSON.

Build and run

pio ci --board=esp32dev --project-option="framework=arduino" \
--project-option="build_flags=-DPC_ENABLE_DEVICE_ID=1" \
--lib="." examples/L7-Application/DeviceUuid/DeviceUuid.ino
curl http://<ip>/id # {"uuid":"...."} - identical across reboots

Annotated source

The complete sketch (DeviceUuid.ino), reproduced verbatim with added explanatory comments:

// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-or-later
#define PC_ENABLE_DEVICE_ID 1
#include "protocore.h"
static const char *SSID = "YOUR_SSID";
static const char *PASSWORD = "YOUR_PASSWORD";
PC server;
static char g_uuid[PC_UUID_STR_LEN]; // caller-owned (no heap)
void setup()
{
Serial.begin(115200);
init_wifi_physical(SSID, PASSWORD);
Serial.print("Connecting to WiFi");
while (!wifi_ready())
{
delay(250);
Serial.print('.');
}
uint32_t ip = pc_net_egress_ip(); // library egress IP (network byte order), no Arduino WiFi
Serial.printf("IP: %u.%u.%u.%u\n", (unsigned)(ip & 0xFF), (unsigned)((ip >> 8) & 0xFF),
(unsigned)((ip >> 16) & 0xFF), (unsigned)((ip >> 24) & 0xFF));
pc_device_uuid(g_uuid); // stable per-chip UUID (v5, derived from the MAC)
Serial.printf("device UUID: %s\n", g_uuid);
server.on("/id", HttpMethod::HTTP_GET, [](uint8_t id, HttpReq *) {
char body[64];
snprintf(body, sizeof(body), "{\"uuid\":\"%s\"}", g_uuid);
server.send(id, 200, "application/json", body);
});
server.begin(80);
}
void loop()
{
server.handle();
}
Single-port HTTP server with deterministic, zero-allocation execution.
Definition protocore.h:348
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.
Stable device UUID derived from the chip MAC (PC_ENABLE_DEVICE_ID).
bool init_wifi_physical(const char *, const char *)
Connect to a WiFi access point.
Definition physical.cpp:41
uint32_t pc_net_egress_ip(void)
IPv4 (network byte order) of the current egress interface, or 0 if none.
Definition physical.cpp:77
bool wifi_ready()
True if the WiFi station link is up (associated + an IP is assigned).
Definition physical.cpp:45
Layer 1 (Physical) - link bring-up and live egress-interface reporting.
Layer 7 (Application) - public HTTP routing API.
@ HTTP_GET
Safe, idempotent read.
Fully-parsed HTTP/1.1 request.