feat(master): endpoint GET/POST /game/peers #4
@@ -496,6 +496,130 @@ static esp_err_t handle_scenario_relay_post(httpd_req_t *req) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ─── GET/POST /game/peers ───────────────────────────────────────────────────
|
||||
// Relay peer registry management over HTTP — replaces the desktop
|
||||
// NvsConfigurator round-trip for ESP-NOW provisioning.
|
||||
// POST { "alias": "plip", "mac": "AA:BB:CC:DD:EE:FF" }
|
||||
// → persists to NVS namespace "peers" (key = alias, blob = 6-byte MAC,
|
||||
// the exact format main.c seeds from at boot) AND registers the peer
|
||||
// live in the scenario_mesh table (no reboot needed).
|
||||
// GET → { "peers": [ { "alias": "...", "mac": "..." }, ... ] }
|
||||
|
||||
static esp_err_t handle_peers_post(httpd_req_t *req) {
|
||||
if (req->content_len <= 0 || req->content_len > 256) {
|
||||
return send_error(req, "413 Payload Too Large",
|
||||
"body must be 1..256 bytes");
|
||||
}
|
||||
char body[257];
|
||||
int total = 0;
|
||||
while (total < (int) req->content_len) {
|
||||
int got = httpd_req_recv(req, body + total, req->content_len - total);
|
||||
if (got <= 0) {
|
||||
if (got == HTTPD_SOCK_ERR_TIMEOUT) continue;
|
||||
return send_error(req, "400 Bad Request", "recv failed");
|
||||
}
|
||||
total += got;
|
||||
}
|
||||
body[total] = '\0';
|
||||
|
||||
cJSON *root = cJSON_Parse(body);
|
||||
if (!root) {
|
||||
return send_error(req, "400 Bad Request", "invalid JSON");
|
||||
}
|
||||
const cJSON *alias_j = cJSON_GetObjectItem(root, "alias");
|
||||
const cJSON *mac_j = cJSON_GetObjectItem(root, "mac");
|
||||
if (!cJSON_IsString(alias_j) || !cJSON_IsString(mac_j)) {
|
||||
cJSON_Delete(root);
|
||||
return send_error(req, "400 Bad Request",
|
||||
"alias and mac (string) required");
|
||||
}
|
||||
const char *alias = alias_j->valuestring;
|
||||
size_t alias_len = strlen(alias);
|
||||
if (alias_len == 0 || alias_len > 15) { // NVS key limit
|
||||
cJSON_Delete(root);
|
||||
return send_error(req, "400 Bad Request",
|
||||
"alias must be 1..15 chars (NVS key)");
|
||||
}
|
||||
uint8_t mac[6];
|
||||
if (sscanf(mac_j->valuestring, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
||||
&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) {
|
||||
cJSON_Delete(root);
|
||||
return send_error(req, "400 Bad Request",
|
||||
"mac must be AA:BB:CC:DD:EE:FF");
|
||||
}
|
||||
|
||||
nvs_handle_t h;
|
||||
esp_err_t err = nvs_open("peers", NVS_READWRITE, &h);
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_set_blob(h, alias, mac, sizeof(mac));
|
||||
if (err == ESP_OK) err = nvs_commit(h);
|
||||
nvs_close(h);
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "peers: NVS write \"%s\" failed: %s",
|
||||
alias, esp_err_to_name(err));
|
||||
cJSON_Delete(root);
|
||||
return send_error(req, "500 Internal Server Error", "NVS write failed");
|
||||
}
|
||||
|
||||
esp_err_t reg = scenario_mesh_register_peer(alias, mac);
|
||||
if (reg != ESP_OK) {
|
||||
ESP_LOGW(TAG, "peers: \"%s\" stored in NVS but live registration "
|
||||
"failed: %s (effective after reboot)",
|
||||
alias, esp_err_to_name(reg));
|
||||
}
|
||||
ESP_LOGI(TAG, "peers: \"%s\" -> %02X:%02X:%02X:%02X:%02X:%02X (%s)",
|
||||
alias, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
|
||||
reg == ESP_OK ? "live" : "reboot needed");
|
||||
cJSON_Delete(root);
|
||||
|
||||
char resp[128];
|
||||
snprintf(resp, sizeof(resp),
|
||||
"{\"ok\":true,\"alias\":\"%s\",\"live\":%s}",
|
||||
alias, reg == ESP_OK ? "true" : "false");
|
||||
return send_json(req, "200 OK", resp);
|
||||
}
|
||||
|
||||
static esp_err_t handle_peers_get(httpd_req_t *req) {
|
||||
cJSON *resp = cJSON_CreateObject();
|
||||
cJSON *arr = cJSON_AddArrayToObject(resp, "peers");
|
||||
|
||||
nvs_handle_t h;
|
||||
if (nvs_open("peers", NVS_READONLY, &h) == ESP_OK) {
|
||||
nvs_iterator_t it = NULL;
|
||||
esp_err_t err = nvs_entry_find("nvs", "peers", NVS_TYPE_BLOB, &it);
|
||||
while (err == ESP_OK && it != NULL) {
|
||||
nvs_entry_info_t info;
|
||||
nvs_entry_info(it, &info);
|
||||
uint8_t mac[6];
|
||||
size_t len = sizeof(mac);
|
||||
if (nvs_get_blob(h, info.key, mac, &len) == ESP_OK && len == 6) {
|
||||
cJSON *p = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(p, "alias", info.key);
|
||||
char mac_str[18];
|
||||
snprintf(mac_str, sizeof(mac_str),
|
||||
"%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
cJSON_AddStringToObject(p, "mac", mac_str);
|
||||
cJSON_AddItemToArray(arr, p);
|
||||
}
|
||||
err = nvs_entry_next(&it);
|
||||
}
|
||||
nvs_release_iterator(it);
|
||||
nvs_close(h);
|
||||
}
|
||||
|
||||
char *resp_str = cJSON_PrintUnformatted(resp);
|
||||
cJSON_Delete(resp);
|
||||
if (!resp_str) {
|
||||
return send_error(req, "500 Internal Server Error",
|
||||
"response serialize failed");
|
||||
}
|
||||
esp_err_t ret = send_json(req, "200 OK", resp_str);
|
||||
cJSON_free(resp_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ─── public init ────────────────────────────────────────────────────────────
|
||||
|
||||
esp_err_t game_endpoint_init(httpd_handle_t server) {
|
||||
@@ -529,6 +653,18 @@ esp_err_t game_endpoint_init(httpd_handle_t server) {
|
||||
.handler = handle_scenario_relay_post,
|
||||
.user_ctx = NULL,
|
||||
};
|
||||
static const httpd_uri_t uri_peers_get = {
|
||||
.uri = "/game/peers",
|
||||
.method = HTTP_GET,
|
||||
.handler = handle_peers_get,
|
||||
.user_ctx = NULL,
|
||||
};
|
||||
static const httpd_uri_t uri_peers_post = {
|
||||
.uri = "/game/peers",
|
||||
.method = HTTP_POST,
|
||||
.handler = handle_peers_post,
|
||||
.user_ctx = NULL,
|
||||
};
|
||||
|
||||
// Bring up the ESP-NOW mesh transport. The master is primarily a sender
|
||||
// (the relay handler) but we also register the apply adapter so a peer
|
||||
@@ -567,9 +703,19 @@ esp_err_t game_endpoint_init(httpd_handle_t server) {
|
||||
esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
// Peer registry management — non-fatal if registration fails.
|
||||
err = httpd_register_uri_handler(server, &uri_peers_get);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "register GET /game/peers: %s", esp_err_to_name(err));
|
||||
}
|
||||
err = httpd_register_uri_handler(server, &uri_peers_post);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "register POST /game/peers: %s", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "game endpoint registered "
|
||||
"(GET+POST /game/group_profile, POST /game/scenario%s)",
|
||||
"(GET+POST /game/group_profile, POST /game/scenario%s, "
|
||||
"GET+POST /game/peers)",
|
||||
mesh_err == ESP_OK ? ", POST /game/scenario/relay" : "");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user