Layer: L7 Application ยท Build flags: PC_ENABLE_COAP, PC_ENABLE_COAP_BLOCK, PC_COAP_MAX_PAYLOAD
What this example teaches
A single CoAP message has to fit in one datagram, which is small. Block-wise transfer (RFC 7959) pages a large representation across many messages, in both directions. Two resources show each direction, building on the plain CoAP server in CoAP:
- GET /big returns a representation larger than one datagram. A client pages it with the Block2 option, one block at a time; a whole-datagram client still gets it inline.
- PUT /upload accepts a payload uploaded with the Block1 option. Each non-final block is acknowledged
2.31 Continue; the final block delivers the whole reassembled payload to the handler in one call.
The handlers are ordinary - the library does the slicing/reassembly. h_big just fills the full representation; the library carves it into blocks per the client's Block2 size:
void h_big(const CoapRequest *req, CoapResponse *resp) {
const size_t n = 512;
for (size_t i = 0; i < n && i < resp->payload_cap; i++)
resp->payload[i] = (uint8_t)('0' + (int)(i % 10));
resp->payload_len = (n < resp->payload_cap) ? n : resp->payload_cap;
resp->content_format = CoapContentFormat::COAP_CF_TEXT;
resp->code = (uint8_t)CoapResponseCode::COAP_RSP_CONTENT;
}
h_upload is called once, with the complete reassembled payload:
void h_upload(const CoapRequest *req, CoapResponse *resp) {
uint32_t sum = 0;
for (size_t i = 0; i < req->payload_len; i++) sum += req->payload[i];
Serial.printf("upload: %u bytes, checksum=%lu\n", (unsigned)req->payload_len, (unsigned long)sum);
resp->payload_len = 0; resp->code = (uint8_t)CoapResponseCode::COAP_RSP_CHANGED;
}
PC_COAP_MAX_PAYLOAD sizes the static reassembly buffer; set it to the largest representation/upload you need (here 1024).
Build and run
pio ci --board=esp32dev --project-option="framework=arduino" \
--project-option="build_flags=-DPC_ENABLE_COAP=1 -DPC_ENABLE_COAP_BLOCK=1 -DPC_COAP_MAX_PAYLOAD=1024" \
--lib="." examples/L7-Application/CoapBlock/CoapBlock.ino
coap-client -m get -b 64 coap://<ip>/big # libcoap, -b = block size
coap-client -m put -b 64 -f firmware.bin coap://<ip>/upload
Annotated source
The complete sketch (CoapBlock.ino), reproduced verbatim with added explanatory comments:
#define PC_ENABLE_COAP 1
#define PC_ENABLE_COAP_BLOCK 1
#define PC_COAP_MAX_PAYLOAD 1024
static const char *SSID = "YOUR_SSID";
static const char *PASSWORD = "YOUR_PASSWORD";
void h_big(const CoapRequest *req, CoapResponse *resp)
{
(void)req;
const size_t n = 512;
for (size_t i = 0; i < n && i < resp->payload_cap; i++)
resp->payload[i] = (uint8_t)('0' + (int)(i % 10));
resp->payload_len = (n < resp->payload_cap) ? n : resp->payload_cap;
resp->content_format = CoapContentFormat::COAP_CF_TEXT;
resp->code = (uint8_t)CoapResponseCode::COAP_RSP_CONTENT;
}
void h_upload(const CoapRequest *req, CoapResponse *resp)
{
uint32_t sum = 0;
for (size_t i = 0; i < req->payload_len; i++)
sum += req->payload[i];
Serial.printf("upload: %u bytes, checksum=%lu\n", (unsigned)req->payload_len, (unsigned long)sum);
resp->payload_len = 0;
resp->code = (uint8_t)CoapResponseCode::COAP_RSP_CHANGED;
}
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("/big", CoapMethodMask::COAP_ALLOW_GET, h_big);
pc_coap_server_add_resource("/upload", CoapMethodMask::COAP_ALLOW_PUT, h_upload);
pc_coap_server_begin(5683);
Serial.println("CoAP server on :5683");
Serial.println(" GET coap://<ip>/big (block-wise responses)");
Serial.println(" PUT coap://<ip>/upload (block-wise uploads)");
}
void loop()
{
}
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.