Layer: L7 Application ยท Build flags: PROTOCORE_ENABLE_COAP, PROTOCORE_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 protocore_coap_notify() when the representation changes:
protocore_coap_server_add_resource("/count", CoapMethodMask::COAP_ALLOW_GET, h_count);
protocore_coap_server_begin(5683);
void loop() {
if () {
g_count++;
protocore_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=-DPROTOCORE_ENABLE_COAP=1 -DPROTOCORE_ENABLE_COAP_OBSERVE=1 -DPROTOCORE_ENABLE_UDP=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 PROTOCORE_ENABLE_COAP 1
#define PROTOCORE_ENABLE_COAP_OBSERVE 1
#include "protocore.h"
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);
Physical.wifi->init(SSID, PASSWORD);
Serial.print("Connecting to WiFi");
while (!Physical.wifi->ready())
{
delay(250);
Serial.print('.');
}
uint32_t ip = Physical.link->egress_ip();
Serial.printf("IP: %u.%u.%u.%u\n", (unsigned)(ip & 0xFF), (unsigned)((ip >> 8) & 0xFF),
(unsigned)((ip >> 16) & 0xFF), (unsigned)((ip >> 24) & 0xFF));
protocore_coap_server_reset();
protocore_coap_server_add_resource("/count", CoapMethodMask::COAP_ALLOW_GET, h_count);
protocore_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++;
protocore_coap_notify("/count");
}
}
The CoAP server (RFC 7252): the message codec, a fixed resource table, and the UDP binding.
Layer 1 (Physical) - link bring-up, the interface registry, and live egress reporting.