# 27.CoapObserve - CoAP resource observation (server push)

**Layer:** L7 Application · **Build flags:** `DETWS_ENABLE_COAP`, `DETWS_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
[13.CoAP](../13.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 `coap_notify()` when the representation changes:

```cpp
coap_server_add_resource("/count", COAP_ALLOW_GET, h_count);
coap_server_begin_udp(5683);
```

```cpp
void loop() {
    if (/* once a second */) {
        g_count++;
        coap_notify("/count"); // push the new value to every observer
    }
}
```

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

```sh
pio ci --board=esp32dev --project-option="framework=arduino" \
  --project-option="build_flags=-DDETWS_ENABLE_COAP=1 -DDETWS_ENABLE_COAP_OBSERVE=1" \
  --lib="." examples/L7-Application/27.CoapObserve/27.CoapObserve.ino
```

```sh
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 ([27.CoapObserve.ino](27.CoapObserve.ino)), reproduced
verbatim with added explanatory comments:

```cpp
// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

#define DETWS_ENABLE_COAP 1
#define DETWS_ENABLE_COAP_OBSERVE 1

#include "dwserver.h"
#include "network_drivers/physical/physical.h"
#include "services/coap/coap.h"
#include <WiFi.h>

static const char *SSID = "YOUR_SSID";
static const char *PASSWORD = "YOUR_PASSWORD";

static uint32_t g_count = 0;

// GET /count -> the current counter value (also pushed to observers on change).
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 = COAP_CF_TEXT;
    resp->code = COAP_RSP_CONTENT;
}

void setup()
{
    Serial.begin(115200);

    init_wifi_physical(SSID, PASSWORD);
    Serial.print("Connecting to WiFi");
    while (!wifi_ready())
    {
        delay(250);
        Serial.print('.');
    }
    Serial.print("\nIP: ");
    Serial.println(WiFi.localIP());
    WiFi.setSleep(false);

    coap_server_init();
    coap_server_add_resource("/count", COAP_ALLOW_GET, h_count);
    coap_server_begin_udp(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++;
        coap_notify("/count"); // push the new value to every observer
    }
}
```
