feat: HTTP-Server auf Port 3333 mit App-API-Kontext
This commit is contained in:
@@ -14,9 +14,11 @@
|
||||
#include "light/nl_boot_state.h"
|
||||
#include "light/nl_control.h"
|
||||
#include "light/nl_engine.h"
|
||||
#include "net/nl_http_server.h"
|
||||
#include "net/nl_mdns.h"
|
||||
#include "net/nl_wifi.h"
|
||||
#include "sys/nl_app.h"
|
||||
#include "sys/nl_app_api.h"
|
||||
#include "sys/nl_console.h"
|
||||
|
||||
#define NL_LIGHT_PRIO 30
|
||||
@@ -186,6 +188,8 @@ static void app_task(void *arg)
|
||||
nl_wifi_start(NINLIGHT_WIFI_SSID, NINLIGHT_WIFI_PASSWORD, NINLIGHT_AP_PASSWORD);
|
||||
}
|
||||
|
||||
nl_http_server_start(nl_app_api_ctx());
|
||||
|
||||
boot_ms = nl_app_now_ms();
|
||||
beat_seen = s_light_beat;
|
||||
for (;;) {
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "wm_include.h"
|
||||
#include "wm_sockets.h"
|
||||
|
||||
#include "net/nl_http.h"
|
||||
#include "net/nl_http_server.h"
|
||||
#include "sys/nl_app.h"
|
||||
|
||||
#define NL_HTTP_PRIO 42
|
||||
|
||||
static u32 s_stack[1536]; /* 6 KB: cJSON + API handler */
|
||||
static char s_buf[NL_HTTP_MAX_REQUEST];
|
||||
static char s_out[NL_API_BODY_MAX + 160];
|
||||
static nl_api_response_t s_resp;
|
||||
static const nl_api_ctx_t *s_ctx;
|
||||
|
||||
static void send_all(int fd, const char *p, size_t n)
|
||||
{
|
||||
while (n) {
|
||||
int w = send(fd, p, n, 0);
|
||||
if (w <= 0)
|
||||
return;
|
||||
p += w;
|
||||
n -= (size_t)w;
|
||||
}
|
||||
}
|
||||
|
||||
static void simple_error(int status, const char *msg)
|
||||
{
|
||||
s_resp.status = status;
|
||||
s_resp.after = NL_API_AFTER_NONE;
|
||||
strcpy(s_resp.body, msg);
|
||||
}
|
||||
|
||||
static void serve(int fd)
|
||||
{
|
||||
struct timeval tv;
|
||||
nl_http_request_t req;
|
||||
nl_http_parse_t pr = NL_HTTP_INCOMPLETE;
|
||||
size_t len = 0, out;
|
||||
|
||||
tv.tv_sec = 5;
|
||||
tv.tv_usec = 0;
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
|
||||
while (pr == NL_HTTP_INCOMPLETE && len < sizeof s_buf) {
|
||||
int n = recv(fd, s_buf + len, sizeof s_buf - len, 0);
|
||||
if (n <= 0)
|
||||
return; /* timeout or closed: drop silently */
|
||||
len += (size_t)n;
|
||||
pr = nl_http_parse(s_buf, len, &req);
|
||||
}
|
||||
if (pr == NL_HTTP_COMPLETE)
|
||||
nl_api_handle(s_ctx, &req, &s_resp);
|
||||
else if (pr == NL_HTTP_BAD_REQUEST)
|
||||
simple_error(400, "{\"error\":\"bad request\"}");
|
||||
else
|
||||
simple_error(413, "{\"error\":\"request too large\"}");
|
||||
out = nl_http_response(s_out, sizeof s_out, s_resp.status, s_resp.body);
|
||||
if (out)
|
||||
send_all(fd, s_out, out);
|
||||
}
|
||||
|
||||
static void server_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
for (;;) {
|
||||
struct sockaddr_in addr;
|
||||
int ls = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(NL_HTTP_PORT);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (ls < 0 || bind(ls, (struct sockaddr *)&addr, sizeof addr) != 0 || listen(ls, 4) != 0) {
|
||||
if (ls >= 0)
|
||||
closesocket(ls);
|
||||
tls_os_time_delay(HZ);
|
||||
continue;
|
||||
}
|
||||
for (;;) {
|
||||
struct sockaddr_in peer;
|
||||
socklen_t plen = sizeof peer;
|
||||
int fd = accept(ls, (struct sockaddr *)&peer, &plen);
|
||||
if (fd < 0) {
|
||||
tls_os_time_delay(HZ / 10);
|
||||
continue;
|
||||
}
|
||||
s_resp.after = NL_API_AFTER_NONE;
|
||||
serve(fd);
|
||||
closesocket(fd);
|
||||
if (s_resp.after == NL_API_AFTER_REBOOT) {
|
||||
tls_os_time_delay(HZ / 2); /* let the TCP stack flush the response */
|
||||
nl_app_reboot();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nl_http_server_start(const nl_api_ctx_t *ctx)
|
||||
{
|
||||
s_ctx = ctx;
|
||||
tls_os_task_create(NULL, "nl_http", server_task, NULL, (u8 *)s_stack, sizeof(s_stack), NL_HTTP_PRIO, 0);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef NL_HTTP_SERVER_H
|
||||
#define NL_HTTP_SERVER_H
|
||||
|
||||
#include "net/nl_api.h"
|
||||
|
||||
#define NL_HTTP_PORT 3333
|
||||
|
||||
/* Serves one connection at a time (listen backlog 4, 5 s receive timeout). */
|
||||
void nl_http_server_start(const nl_api_ctx_t *ctx);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,131 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "wm_include.h"
|
||||
#include "wm_mem.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "light/nl_control.h"
|
||||
#include "light/nl_engine.h"
|
||||
#include "net/nl_mdns.h"
|
||||
#include "net/nl_names.h"
|
||||
#include "net/nl_wifi.h"
|
||||
#include "sys/nl_app.h"
|
||||
#include "sys/nl_app_api.h"
|
||||
|
||||
static void *json_alloc(size_t n) { return tls_mem_alloc(n); }
|
||||
static void json_free(void *p) { tls_mem_free(p); }
|
||||
|
||||
static void get_state(nl_light_state_t *out)
|
||||
{
|
||||
nl_app_lock();
|
||||
nl_ctrl_get(out);
|
||||
nl_app_unlock();
|
||||
}
|
||||
|
||||
static nl_ctrl_err_t apply(const nl_light_change_t *c)
|
||||
{
|
||||
nl_ctrl_err_t e;
|
||||
nl_app_lock();
|
||||
e = nl_ctrl_apply(c, nl_app_now_ms());
|
||||
nl_app_unlock();
|
||||
return e;
|
||||
}
|
||||
|
||||
static void get_settings(nl_settings_t *out)
|
||||
{
|
||||
nl_app_lock();
|
||||
*out = *nl_app_settings();
|
||||
nl_app_unlock();
|
||||
}
|
||||
|
||||
/* The API never changes Wi-Fi credentials through here (that goes through the onboarding test),
|
||||
* so the current ones are kept even if the network task stored new ones meanwhile. */
|
||||
static int save_settings(const nl_settings_t *s)
|
||||
{
|
||||
nl_settings_t *cur;
|
||||
char name[NL_NAME_LEN];
|
||||
bool name_changed;
|
||||
|
||||
nl_app_lock();
|
||||
cur = nl_app_settings();
|
||||
name_changed = strcmp(cur->name, s->name) != 0;
|
||||
{
|
||||
nl_settings_t merged = *s;
|
||||
memcpy(merged.wifi_ssid, cur->wifi_ssid, sizeof merged.wifi_ssid);
|
||||
memcpy(merged.wifi_pass, cur->wifi_pass, sizeof merged.wifi_pass);
|
||||
*cur = merged;
|
||||
}
|
||||
nl_ctrl_set_fades(cur->fade_in_ms, cur->fade_out_ms);
|
||||
memcpy(name, cur->name, sizeof name);
|
||||
nl_app_unlock();
|
||||
if (name_changed)
|
||||
nl_mdns_set_name(name);
|
||||
return nl_app_save_settings();
|
||||
}
|
||||
|
||||
static int wifi_configure(const char *ssid, const char *pass) { return nl_wifi_request_onboard(ssid, pass); }
|
||||
static bool open_access(void) { return nl_wifi_open_access(); }
|
||||
|
||||
static void get_info(nl_sys_info_t *out)
|
||||
{
|
||||
nl_wifi_status_t w;
|
||||
uint8_t mac[6];
|
||||
|
||||
memset(out, 0, sizeof *out);
|
||||
nl_wifi_mac(mac);
|
||||
nl_names_mac_str(mac, out->mac);
|
||||
nl_app_lock();
|
||||
memcpy(out->name, nl_app_settings()->name, sizeof out->name);
|
||||
nl_app_unlock();
|
||||
nl_wifi_status(&w);
|
||||
out->wifi_mode = w.mode;
|
||||
memcpy(out->ssid, w.ssid, sizeof out->ssid);
|
||||
out->rssi = w.rssi;
|
||||
memcpy(out->ip, w.ip, sizeof out->ip);
|
||||
memcpy(out->sta_ip, w.sta_ip, sizeof out->sta_ip);
|
||||
out->wifi_error = w.error;
|
||||
out->uptime_s = nl_app_now_ms() / 1000u;
|
||||
out->free_heap = tls_mem_get_avail_heapsize();
|
||||
out->last_ota_error = NULL;
|
||||
}
|
||||
|
||||
static void factory_reset(void) { nl_app_request_factory_reset(); }
|
||||
|
||||
static void ping(void)
|
||||
{
|
||||
nl_app_lock();
|
||||
nl_engine_blink(3);
|
||||
nl_app_unlock();
|
||||
}
|
||||
|
||||
static int fw_update(const char *url)
|
||||
{
|
||||
(void)url;
|
||||
return -1; /* OTA arrives in plan 3 */
|
||||
}
|
||||
|
||||
const nl_api_ctx_t *nl_app_api_ctx(void)
|
||||
{
|
||||
static nl_api_ctx_t ctx;
|
||||
static bool ready;
|
||||
|
||||
if (!ready) {
|
||||
cJSON_Hooks hooks = {json_alloc, json_free};
|
||||
cJSON_InitHooks(&hooks);
|
||||
ctx.board = nl_board_get();
|
||||
ctx.fw_version = NINLIGHT_FW_VERSION;
|
||||
ctx.build_date = NINLIGHT_BUILD_DATE;
|
||||
ctx.get_state = get_state;
|
||||
ctx.apply = apply;
|
||||
ctx.get_settings = get_settings;
|
||||
ctx.save_settings = save_settings;
|
||||
ctx.wifi_configure = wifi_configure;
|
||||
ctx.open_access = open_access;
|
||||
ctx.get_info = get_info;
|
||||
ctx.factory_reset = factory_reset;
|
||||
ctx.ping = ping;
|
||||
ctx.fw_update = fw_update;
|
||||
ready = true;
|
||||
}
|
||||
return &ctx;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef NL_APP_API_H
|
||||
#define NL_APP_API_H
|
||||
|
||||
#include "net/nl_api.h"
|
||||
|
||||
/* API context backed by the running application. First call installs the cJSON allocator. */
|
||||
const nl_api_ctx_t *nl_app_api_ctx(void);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user