This builds a complete REST API for a small in-memory "sensor database": list, fetch-by-id, create, partial-update, and delete, with real HTTP semantics (bearer-token auth, Content-Type checking, and a spread of status codes). It is the step up from Basic: same three-part structure (global server, setup() registers routes, loop() pumps handle()), but now the handlers do meaningful work.
#include "protocore.h"
static const char *SSID = "YOUR_SSID";
static const char *PASSWORD = "YOUR_PASSWORD";
PC server;
static const char *EXPECTED_TOKEN = "Bearer secret_admin_token";
struct SensorDevice
{
int id;
char name[16];
float temperature;
bool active;
bool in_use;
};
#define MAX_SENSORS 4
static SensorDevice sensor_db[MAX_SENSORS] = {
{0, "Living Room", 22.4, true, true},
{1, "Kitchen", 24.1, true, true},
{2, "Garage", 15.8, false, true},
{3, "", 0.0, false, false}
};
bool is_authorized(
const HttpReq *req)
{
const char *auth_hdr = http_get_header(req, "Authorization");
return (auth_hdr && strcmp(auth_hdr, EXPECTED_TOKEN) == 0);
}
bool json_get_float(const char *json, const char *key, float &out_val)
{
char key_pattern[32];
snprintf(key_pattern, sizeof(key_pattern), "\"%s\":", key);
const char *ptr = strstr(json, key_pattern);
if (!ptr)
return false;
ptr += strlen(key_pattern);
while (*ptr == ' ' || *ptr == '\t')
ptr++;
out_val = (float)atof(ptr);
return true;
}
bool json_get_string(const char *json, const char *key, char *out_buf, size_t max_len)
{
char key_pattern[32];
snprintf(key_pattern, sizeof(key_pattern), "\"%s\":", key);
const char *ptr = strstr(json, key_pattern);
if (!ptr)
return false;
ptr += strlen(key_pattern);
while (*ptr == ' ' || *ptr == '\t' || *ptr == '"')
ptr++;
size_t i = 0;
while (*ptr && *ptr != '"' && *ptr != ',' && *ptr != '}' && i < (max_len - 1))
out_buf[i++] = *ptr++;
out_buf[i] = '\0';
return true;
}
bool json_get_bool(const char *json, const char *key, bool &out_val)
{
char key_pattern[32];
snprintf(key_pattern, sizeof(key_pattern), "\"%s\":", key);
const char *ptr = strstr(json, key_pattern);
if (!ptr)
return false;
ptr += strlen(key_pattern);
while (*ptr == ' ' || *ptr == '\t')
ptr++;
if (strncmp(ptr, "true", 4) == 0) { out_val = true; return true; }
if (strncmp(ptr, "false", 5) == 0) { out_val = false; return true; }
return false;
}
void handle_get_sensors(uint8_t slot_id,
HttpReq *req)
{
const char *active_filter = http_get_query(req, "active");
bool filter_by_active = (active_filter != nullptr);
bool active_target_val = (filter_by_active && strcmp(active_filter, "1") == 0);
char response_buf[512];
int len = snprintf(response_buf, sizeof(response_buf), "[");
bool first = true;
for (int i = 0; i < MAX_SENSORS; i++)
{
if (!sensor_db[i].in_use)
continue;
if (filter_by_active && (sensor_db[i].active != active_target_val))
continue;
if (!first)
len += snprintf(response_buf + len, sizeof(response_buf) - len, ",");
first = false;
len += snprintf(response_buf + len, sizeof(response_buf) - len,
"{\"id\":%d,\"name\":\"%s\",\"temp\":%.1f,\"active\":%s}", sensor_db[i].id, sensor_db[i].name,
sensor_db[i].temperature, sensor_db[i].active ? "true" : "false");
}
snprintf(response_buf + len, sizeof(response_buf) - len, "]");
server.send(slot_id, 200, "application/json", response_buf);
}
void handle_get_sensor_by_id(uint8_t slot_id,
HttpReq *req)
{
if (strlen(req->
path) <= 13)
{
server.send(slot_id, 400, "text/plain", "Missing sensor ID");
return;
}
int id = atoi(req->
path + 13);
if (id < 0 || id >= MAX_SENSORS || !sensor_db[id].in_use)
{
server.send(slot_id, 404, "application/json", "{\"error\":\"Sensor not found\"}");
return;
}
char response_buf[192];
snprintf(response_buf, sizeof(response_buf), "{\"id\":%d,\"name\":\"%s\",\"temp\":%.1f,\"active\":%s}",
sensor_db[id].id, sensor_db[id].name, sensor_db[id].temperature, sensor_db[id].active ? "true" : "false");
server.send(slot_id, 200, "application/json", response_buf);
}
void handle_create_sensor(uint8_t slot_id,
HttpReq *req)
{
if (!is_authorized(req))
{
server.send(slot_id, 401, "text/plain", "401 Unauthorized: Invalid token");
return;
}
const char *content_type = http_get_header(req, "Content-Type");
if (!content_type || strstr(content_type, "application/json") == nullptr)
{
server.send(slot_id, 400, "text/plain", "400 Bad Request: Content-Type must be application/json");
return;
}
int empty_slot = -1;
for (int i = 0; i < MAX_SENSORS; i++)
if (!sensor_db[i].in_use) { empty_slot = i; break; }
if (empty_slot == -1)
{
server.send(slot_id, 409, "application/json", "{\"error\":\"Database table full\"}");
return;
}
const char *body = (
const char *)req->
body;
char name[16] = "";
float temp = 0.0;
bool active = false;
if (!json_get_string(body, "name", name, sizeof(name)) || !json_get_float(body, "temp", temp) ||
!json_get_bool(body, "active", active))
{
server.send(slot_id, 400, "application/json", "{\"error\":\"Invalid JSON format or missing keys\"}");
return;
}
sensor_db[empty_slot].id = empty_slot;
strncpy(sensor_db[empty_slot].name, name, sizeof(sensor_db[empty_slot].name) - 1);
sensor_db[empty_slot].temperature = temp;
sensor_db[empty_slot].active = active;
sensor_db[empty_slot].in_use = true;
char response_buf[192];
snprintf(response_buf, sizeof(response_buf), "{\"id\":%d,\"name\":\"%s\",\"status\":\"created\"}", empty_slot,
sensor_db[empty_slot].name);
server.send(slot_id, 201, "application/json", response_buf);
}
void handle_patch_sensor(uint8_t slot_id,
HttpReq *req)
{
if (!is_authorized(req))
{
server.send(slot_id, 401, "text/plain", "401 Unauthorized: Invalid token");
return;
}
if (strlen(req->
path) <= 13)
{
server.send(slot_id, 400, "text/plain", "Missing sensor ID");
return;
}
int id = atoi(req->
path + 13);
if (id < 0 || id >= MAX_SENSORS || !sensor_db[id].in_use)
{
server.send(slot_id, 404, "application/json", "{\"error\":\"Sensor not found\"}");
return;
}
const char *body = (
const char *)req->
body;
float new_temp;
bool new_active;
if (json_get_float(body, "temp", new_temp))
sensor_db[id].temperature = new_temp;
if (json_get_bool(body, "active", new_active))
sensor_db[id].active = new_active;
char response_buf[192];
snprintf(response_buf, sizeof(response_buf), "{\"id\":%d,\"name\":\"%s\",\"temp\":%.1f,\"active\":%s}",
sensor_db[id].id, sensor_db[id].name, sensor_db[id].temperature, sensor_db[id].active ? "true" : "false");
server.send(slot_id, 200, "application/json", response_buf);
}
void handle_delete_sensor(uint8_t slot_id,
HttpReq *req)
{
if (!is_authorized(req))
{
server.send(slot_id, 401, "text/plain", "401 Unauthorized: Invalid token");
return;
}
if (strlen(req->
path) <= 13)
{
server.send(slot_id, 400, "text/plain", "Missing sensor ID");
return;
}
int id = atoi(req->
path + 13);
if (id < 0 || id >= MAX_SENSORS || !sensor_db[id].in_use)
{
server.send(slot_id, 404, "application/json", "{\"error\":\"Sensor not found\"}");
return;
}
sensor_db[id].in_use = false;
server.send_empty(slot_id, 204);
}
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println("\n--- PC Advanced REST CRUD Example ---");
Physical.wifi->init(SSID, PASSWORD);
while (!Physical.wifi->ready())
{
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi Associated!");
Serial.print("Local IP: ");
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));
server.set_cors("*");
int32_t result = server.begin(80);
if (result < 0)
{
Serial.printf("begin() failed (error %d)\n", result);
return;
}
Serial.println("REST API server running on port 80");
Serial.println("Admin token: 'Authorization: Bearer secret_admin_token'");
}
void loop()
{
server.handle();
}
Layer 1 (Physical) - link bring-up, the interface registry, and live egress reporting.
uint8_t body[BODY_BUF_SIZE+1]