Layer: L7 Application ยท Build flags: PC_ENABLE_COAP, PC_ENABLE_COAP_OBSERVE
What this example teaches
CoAP Observe (RFC 7641) turns request/response into publish/subscribe: a client sends a GET with the Observe option and the server keeps pushing updates as the resource changes - the CoAP equivalent of server-sent events, but over UDP. This serves an observable /count resource; every second it increments the counter and notifies all observers. It builds on the plain CoAP server in CoAP.
Register an ordinary resource, then notify on change. The handler is the same shape as any CoAP handler; what makes it observable is that observers are tracked by the library and you call pc_coap_notify() when the representation changes:
pc_coap_server_add_resource("/count", CoapMethodMask::COAP_ALLOW_GET, h_count);
pc_coap_server_begin(5683);
void loop() {
if () {
g_count++;
pc_coap_notify("/count");
}
}
Each notification carries an increasing Observe sequence number so a client can detect reordering. Observe with coap-client -m get -s 30 coap://<ip>/count or aiocoap-client --observe coap://<ip>/count.
Build and run
pio ci --board=esp32dev --project-option="framework=arduino" \
--project-option="build_flags=-DPC_ENABLE_COAP=1 -DPC_ENABLE_COAP_OBSERVE=1" \
--lib="." examples/L7-Application/CoapObserve/CoapObserve.ino
coap-client -m get -s 30 coap://<ip>/count # libcoap, -s = observe for 30s
aiocoap-client --observe coap://<ip>/count
Annotated source
The complete sketch (CoapObserve.ino), reproduced verbatim with added explanatory comments:
#define PC_ENABLE_COAP 1
#define PC_ENABLE_COAP_OBSERVE 1
static const char *SSID = "YOUR_SSID";
static const char *PASSWORD = "YOUR_PASSWORD";
static uint32_t g_count = 0;
void h_count(const CoapRequest *req, CoapResponse *resp)
{
(void)req;
int n = snprintf((char *)resp->payload, resp->payload_cap, "%lu", (unsigned long)g_count);
resp->payload_len = (n > 0) ? (size_t)n : 0;
resp->content_format = CoapContentFormat::COAP_CF_TEXT;
resp->code = (uint8_t)CoapResponseCode::COAP_RSP_CONTENT;
}
void setup()
{
Serial.begin(115200);
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));
pc_coap_server_reset();
pc_coap_server_add_resource("/count", CoapMethodMask::COAP_ALLOW_GET, h_count);
pc_coap_server_begin(5683);
Serial.println("CoAP server on :5683, observe coap://<ip>/count");
}
void loop()
{
static uint32_t last = 0;
if (millis() - last >= 1000)
{
last = millis();
g_count++;
pc_coap_notify("/count");
}
}
Zero-heap CoAP server (RFC 7252): message codec + a fixed resource table.
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.
Layer 7 (Application) - public HTTP routing API.