25 if (!body || !key || !out || cap == 0)
28 size_t klen = strnlen(key, cap);
29 const char *val =
nullptr;
30 for (
const char *p = body; *p; p++)
33 if ((p == body || p[-1] ==
'&') && strncmp(p, key, klen) == 0 && p[klen] ==
'=')
43 for (
const char *q = val; *q && *q !=
'&'; q++)
56 c = (char)((h << 4) | l);
73#if DETWS_ENABLE_PROVISIONING && defined(ARDUINO)
79#include <Preferences.h>
88 uint8_t ap_ip[4] = {192, 168, 4, 1};
97static void prov_dns_recv(
const uint8_t *req,
size_t qlen,
struct DetUdpPeer *peer,
void *ctx)
106 while (qend < qlen && req[qend] != 0)
107 qend += (size_t)req[qend] + 1;
113 if (qend + 16 >
sizeof(resp))
115 memcpy(resp, req, qend);
120 resp[8] = resp[9] = resp[10] = resp[11] = 0x00;
135 resp[n++] = s_prov.ap_ip[0];
136 resp[n++] = s_prov.ap_ip[1];
137 resp[n++] = s_prov.ap_ip[2];
138 resp[n++] = s_prov.ap_ip[3];
145 if (ssid && ssid_cap)
150 if (!prefs.begin(DETWS_PROV_NVS_NAMESPACE,
true))
152 String s = prefs.getString(DETWS_PROV_KEY_SSID,
"");
153 String k = prefs.getString(DETWS_PROV_KEY_PSK,
"");
157 strncpy(ssid, s.c_str(), ssid_cap - 1);
158 ssid[ssid_cap - 1] =
'\0';
159 strncpy(psk, k.c_str(), psk_cap - 1);
160 psk[psk_cap - 1] =
'\0';
167 if (prefs.begin(DETWS_PROV_NVS_NAMESPACE,
false))
174static void prov_form_handler(uint8_t slot_id,
HttpReq *req)
177 s_prov.server->send(slot_id, 200, DET_MIME_TEXT_HTML,
DETWS_PROV_FORM);
180static void prov_save_handler(uint8_t slot_id,
HttpReq *req)
189 s_prov.server->send(slot_id, 400, DET_MIME_TEXT_PLAIN,
"SSID required");
193 prefs.begin(DETWS_PROV_NVS_NAMESPACE,
false);
194 prefs.putString(DETWS_PROV_KEY_SSID, ssid);
195 prefs.putString(DETWS_PROV_KEY_PSK, psk);
204 s_prov.server = &server;
206 WiFi.softAP(ap_ssid);
207 IPAddress ip = WiFi.softAPIP();
208 s_prov.ap_ip[0] = ip[0];
209 s_prov.ap_ip[1] = ip[1];
210 s_prov.ap_ip[2] = ip[2];
211 s_prov.ap_ip[3] = ip[3];
224 if (ssid && ssid_cap)
Single-port HTTP server with deterministic, zero-allocation execution.
void on(const char *path, HttpMethod method, Handler callback)
Register a route handler.
Layer 7 (Application) - public HTTP routing API.
@ HTTP_POST
Non-idempotent create / action.
@ HTTP_GET
Safe, idempotent read.
Tiny no-stdlib hex encode / decode / nibble helpers (one shared copy).
int det_hex_val(char c)
Hex digit -> 0..15, or -1 if c is not a hex digit (either case).
Shared HTTP Content-Type ("MIME") string constants (one source of truth).
void detws_provisioning_begin(DetWebServer &server, const char *ap_ssid)
Start the captive portal: softAP ap_ssid + catch-all DNS + form routes.
bool detws_prov_form_field(const char *body, const char *key, char *out, size_t cap)
Extract and URL-decode a field from an x-www-form-urlencoded body.
bool detws_provisioning_load(char *ssid, size_t ssid_cap, char *psk, size_t psk_cap)
Load stored WiFi credentials from NVS.
void detws_provisioning_clear()
Erase stored credentials (forces re-provisioning on next boot).
First-boot WiFi provisioning via a captive portal (DETWS_ENABLE_PROVISIONING).
Fully-parsed HTTP/1.1 request.
uint8_t body[BODY_BUF_SIZE+1]
Stored body bytes, always null-terminated.
bool det_udp_send(struct DetUdpPeer *peer, const uint8_t *data, size_t len)
Send a datagram back to the peer captured during the handler call.
bool det_udp_listen(uint16_t port, DetUdpHandler handler, void *ctx)
Bind a UDP port and route incoming datagrams to handler.
Layer 4 (Transport) - connectionless UDP datagram service.
const char DETWS_PROV_SAVED_HTML[]
Success page shown after credentials are saved and the device reboots.
const char DETWS_PROV_FORM[]
Captive-portal form for WiFi credential entry (provisioning service).
Layer 7 (Application) - embedded web assets generated from src/web/input/.