0
0
Fork 0

reformat(static_buffer) to memory_pool and feat:global resource management

This commit is contained in:
kerms 2024-05-24 16:25:30 +08:00
parent 3a5968f0fa
commit 8fba1208f3
20 changed files with 225 additions and 111 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@ package-lock.json
**/priv_note.md **/priv_note.md
_priv_tools/ _priv_tools/
project_components/wifi_manager/wifi_configuration.h project_components/wifi_manager/wifi_configuration.h
managed_components/

View File

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2024 kerms <kerms@niazo.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "global_module.h"
#include <stddef.h>
#include <esp_log.h>
#define TAG __FILE_NAME__
#define GLOBAL_MODULE_MAX 10
static global_module_t module_arr[GLOBAL_MODULE_MAX] = {0};
static uint8_t module_count = 0;
int global_module_reg(const global_module_t *g_mod)
{
if (g_mod->module_id > GLOBAL_MODULE_MAX) {
ESP_LOGE(TAG, "g_id > max");
return 1;
}
if (module_arr[g_mod->module_id].init != NULL) {
ESP_LOGE(TAG, "g_id.init not NULL");
return 1;
}
ESP_LOGI(TAG, "g_id: %d", g_mod->module_id);
module_arr[g_mod->module_id].module_id = g_mod->module_id;
module_arr[g_mod->module_id].init = g_mod->init;
module_count++;
return 0;
}
int global_module_init()
{
for (int i = 0; i < GLOBAL_MODULE_MAX; ++i) {
if (module_arr[i].init) {
module_arr[i].init();
}
}
return 0;
}

View File

@ -0,0 +1,28 @@
/*
* SPDX-FileCopyrightText: 2024 kerms <kerms@niazo.org>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef GLOBAL_MODULE_H_GUARD
#define GLOBAL_MODULE_H_GUARD
#include <stdint.h>
typedef int (*global_module_init_func)(void);
typedef struct global_module_t {
global_module_init_func init;
uint8_t module_id;
} global_module_t;
int global_module_reg(const global_module_t *g_mod);
int global_module_init();
#define GLOBAL_MODULE_REGISTER(NAME, GLOBAL_MODULE_CFG) \
__attribute__((used, constructor)) void cons_G_MOD_ ## NAME(); \
void cons_G_MOD_ ## NAME() { global_module_reg(GLOBAL_MODULE_CFG); }
#endif //GLOBAL_MODULE_H_GUARD

View File

@ -4,17 +4,19 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include "static_buffer.h" #include "memory_pool.h"
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/queue.h> #include <freertos/queue.h>
#define BUFFER_NR 4 #define BUFFER_NR 8
#define BUFFER_SZ 2048 #define BUFFER_SZ 2048
static uint8_t buf[BUFFER_NR][BUFFER_SZ]; static uint8_t buf[BUFFER_NR][BUFFER_SZ];
/* TODO: use CAS */
static QueueHandle_t buf_queue = NULL; static QueueHandle_t buf_queue = NULL;
int static_buffer_init() int memory_pool_init()
{ {
if (buf_queue != NULL) if (buf_queue != NULL)
return 0; return 0;
@ -33,14 +35,14 @@ int static_buffer_init()
return 0; return 0;
} }
void *static_buffer_get(uint32_t tick_wait) void *memory_pool_get(uint32_t tick_wait)
{ {
void *ptr = NULL; void *ptr = NULL;
xQueueReceive(buf_queue, &ptr, tick_wait); xQueueReceive(buf_queue, &ptr, tick_wait);
return ptr; return ptr;
} }
void static_buffer_put(void *ptr) void memory_pool_put(void *ptr)
{ {
//printf("put buf %d\n", uxQueueMessagesWaiting(buf_queue)); //printf("put buf %d\n", uxQueueMessagesWaiting(buf_queue));
if (unlikely(xQueueSend(buf_queue, &ptr, 0) != pdTRUE)) { if (unlikely(xQueueSend(buf_queue, &ptr, 0) != pdTRUE)) {
@ -48,7 +50,7 @@ void static_buffer_put(void *ptr)
} }
} }
uint32_t static_buffer_get_buf_size() inline uint32_t memory_pool_get_buf_size()
{ {
return BUFFER_SZ; return BUFFER_SZ;
} }

View File

@ -9,13 +9,13 @@
#include <stdint.h> #include <stdint.h>
int static_buffer_init(); int memory_pool_init();
void *static_buffer_get(uint32_t tick_wait); void *memory_pool_get(uint32_t tick_wait);
void static_buffer_put(void *ptr); void memory_pool_put(void *ptr);
uint32_t static_buffer_get_buf_size(); uint32_t memory_pool_get_buf_size();
#endif //STATIC_BUFFER_H_GUARD #endif //STATIC_BUFFER_H_GUARD

View File

@ -10,7 +10,7 @@
#include "wt_storage.h" #include "wt_storage.h"
#include "wifi_manager.h" #include "wifi_manager.h"
#include "web_server.h" #include "web_server.h"
#include "static_buffer.h" #include "memory_pool.h"
#include "request_runner.h" #include "request_runner.h"
#include "uart_tcp_bridge.h" #include "uart_tcp_bridge.h"
@ -18,18 +18,17 @@
void app_main() void app_main()
{ {
assert(static_buffer_init() == 0); assert(memory_pool_init() == 0); // static buffer
assert(request_runner_init() == 0); assert(request_runner_init() == 0);
wt_storage_init(); wt_storage_init();
ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_event_loop_create_default());
wt_mdns_init();
wifi_manager_init(); wifi_manager_init();
DAP_Setup(); DAP_Setup();
start_webserver(); start_webserver();
wt_mdns_init();
xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 14, NULL); xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 14, NULL);
// DAP handle task // DAP handle task

View File

@ -1,4 +1,4 @@
file(GLOB SOURCES * file(GLOB SOURCES *.c
) )

View File

@ -25,12 +25,13 @@ typedef enum api_json_req_status_e {
API_JSON_OK = 0, API_JSON_OK = 0,
API_JSON_ASYNC = 1, API_JSON_ASYNC = 1,
API_JSON_BAD_REQUEST = 2, API_JSON_BAD_REQUEST = 2,
API_JSON_INTERNAL_ERR = 3,
} api_json_req_status_e; } api_json_req_status_e;
typedef int (*api_json_on_req)(uint16_t cmd, api_json_req_t *req, api_json_module_async_t *rsp); typedef int (*api_json_on_req)(uint16_t cmd, api_json_req_t *req, api_json_module_async_t *rsp);
typedef struct api_json_module_cfg_t { typedef struct api_json_module_cfg_t {
api_json_on_req on_req; api_json_on_req on_req; /* input request callback */
uint8_t module_id; uint8_t module_id;
} api_json_module_cfg_t; } api_json_module_cfg_t;
@ -40,9 +41,6 @@ void api_json_module_dump();
int api_json_module_add(api_json_init_func); int api_json_module_add(api_json_init_func);
/**
* @brief Register a module that will be init with PRI(priority) order.
*/
#define API_JSON_MODULE_REGISTER(PRI, INIT) \ #define API_JSON_MODULE_REGISTER(PRI, INIT) \
__attribute__((used, constructor(PRI))) void cons_ ## INIT(); \ __attribute__((used, constructor(PRI))) void cons_ ## INIT(); \
void cons_ ## INIT() { api_json_module_add(INIT); } void cons_ ## INIT() { api_json_module_add(INIT); }

View File

@ -26,7 +26,7 @@ _Noreturn void req_long_task(void *arg)
if (unlikely(xQueueReceive(long_run_queue, &req, portMAX_DELAY) != pdTRUE)) { if (unlikely(xQueueReceive(long_run_queue, &req, portMAX_DELAY) != pdTRUE)) {
continue; continue;
} }
req->status = req->module.helper_cb(req->module.arg); req->status = req->module.cb(req->module.arg);
/* if send out queue is busy, set status and let the cb to cancel send out /* if send out queue is busy, set status and let the cb to cancel send out
* */ * */

View File

@ -15,7 +15,7 @@ typedef struct req_send_out_cb_t {
} req_send_out_cb_t; } req_send_out_cb_t;
typedef struct req_module_cb_t { typedef struct req_module_cb_t {
int (*helper_cb)(void *arg); int (*cb)(void *arg);
void *arg; void *arg;
} req_module_cb_t; } req_module_cb_t;

View File

@ -10,7 +10,7 @@ idf_component_register(
SRCS ${SOURCES} SRCS ${SOURCES}
INCLUDE_DIRS "." INCLUDE_DIRS "."
REQUIRES esp_http_server REQUIRES esp_http_server
PRIV_REQUIRES request_runner api_router json static_buffer utils html PRIV_REQUIRES request_runner api_router json memory_pool utils html
) )
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON) idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON)

View File

@ -1,7 +1,7 @@
#include "web_uri_module.h" #include "web_uri_module.h"
#include "api_json_router.h" #include "api_json_router.h"
#include "request_runner.h" #include "request_runner.h"
#include "static_buffer.h" #include "memory_pool.h"
#include <esp_http_server.h> #include <esp_http_server.h>
#include <esp_log.h> #include <esp_log.h>
@ -32,13 +32,13 @@ static esp_err_t api_post_handler(httpd_req_t *req)
char *buf; char *buf;
uint32_t remaining = req->content_len; uint32_t remaining = req->content_len;
buf_len = static_buffer_get_buf_size() - sizeof(post_request_t); buf_len = memory_pool_get_buf_size() - sizeof(post_request_t);
if (unlikely(buf_len < remaining)) { if (unlikely(buf_len < remaining)) {
ESP_LOGE(TAG, "req size %lu > buf_len %lu", remaining, buf_len); ESP_LOGE(TAG, "req size %lu > buf_len %lu", remaining, buf_len);
return ESP_FAIL; return ESP_FAIL;
} }
post_req = static_buffer_get(pdMS_TO_TICKS(20)); post_req = memory_pool_get(pdMS_TO_TICKS(20));
if (unlikely(post_req == NULL)) { if (unlikely(post_req == NULL)) {
ESP_LOGE(TAG, "static buf busy"); ESP_LOGE(TAG, "static buf busy");
return ESP_FAIL; return ESP_FAIL;
@ -97,7 +97,7 @@ end:
ESP_LOGE(TAG, "resp_send err: %s", esp_err_to_name(err)); ESP_LOGE(TAG, "resp_send err: %s", esp_err_to_name(err));
} }
put_buf: put_buf:
static_buffer_put(post_req); memory_pool_put(post_req);
return err; return err;
} }
@ -107,7 +107,7 @@ int uri_api_send_out(httpd_req_t *req, post_request_t *post_req, int err)
uint32_t buf_len; uint32_t buf_len;
buf = post_req->buf; buf = post_req->buf;
buf_len = static_buffer_get_buf_size() - sizeof(post_request_t); buf_len = memory_pool_get_buf_size() - sizeof(post_request_t);
cJSON_Delete(post_req->json.in); cJSON_Delete(post_req->json.in);
if (post_req->json.out) { if (post_req->json.out) {
ESP_LOGI(TAG, "json out ok"); ESP_LOGI(TAG, "json out ok");
@ -136,7 +136,7 @@ void async_send_out_cb(void *arg, int module_status)
/* clean resources */ /* clean resources */
httpd_req_async_handler_complete(req->req_out); httpd_req_async_handler_complete(req->req_out);
static_buffer_put(req); memory_pool_put(req);
}; };
/** /**

View File

@ -6,7 +6,7 @@
#include "web_uri_module.h" #include "web_uri_module.h"
#include "api_json_router.h" #include "api_json_router.h"
#include "static_buffer.h" #include "memory_pool.h"
#include <esp_http_server.h> #include <esp_http_server.h>
#include <esp_log.h> #include <esp_log.h>
@ -20,8 +20,11 @@
#define TAG __FILE_NAME__ #define TAG __FILE_NAME__
#define MSG_BUSY_ERROR "{\"error\":\"Resource busy\"}" #define MSG_BUSY_ERROR "{\"error\":\"Resource busy\"}"
#define MSG_JSON_ERROR "{\"error\":\"JSON parse error\"}" #define MSG_JSON_ERROR "{\"error\":\"JSON parse error\"}"
#define MSG_BAD_REQUEST_ERROR "{\"error\":\"Bad json request\"}"
#define MSG_SEND_JSON_ERROR "{\"error\":\"JSON generation error\"}" #define MSG_SEND_JSON_ERROR "{\"error\":\"JSON generation error\"}"
#define MSG_INTERNAL_ERROR "{\"error\":\"\"}" #define MSG_INTERNAL_ERROR "{\"error\":\"Internal error\"}"
#define WS_MODULE_ID 3
typedef struct ws_msg_t { typedef struct ws_msg_t {
api_json_req_t json; api_json_req_t json;
@ -33,13 +36,13 @@ typedef struct ws_msg_t {
uint8_t payload[0]; /* size = static_buf_size - offsetof(this, delim) */ uint8_t payload[0]; /* size = static_buf_size - offsetof(this, delim) */
} ws_msg_t; } ws_msg_t;
#define PAYLOAD_LEN static_buffer_get_buf_size() - sizeof(ws_msg_t) #define PAYLOAD_LEN memory_pool_get_buf_size() - sizeof(ws_msg_t)
struct ws_ctx_t { struct ws_ctx_t {
struct ws_client_info_t { struct ws_client_info_t {
httpd_handle_t hd; httpd_handle_t hd;
int fd; /* range 58 ~ 58+max_socket */ int fd; /* range 64 - max_socket ~ 64 */
} clients[CONFIG_LWIP_MAX_SOCKETS+1]; } clients[CONFIG_LWIP_MAX_SOCKETS + 1];
TaskHandle_t task_heartbeat; TaskHandle_t task_heartbeat;
int8_t client_count; int8_t client_count;
} ws_ctx; } ws_ctx;
@ -47,14 +50,14 @@ struct ws_ctx_t {
static int ws_on_text_data(httpd_req_t *req, ws_msg_t *ws_msg); static int ws_on_text_data(httpd_req_t *req, ws_msg_t *ws_msg);
static int ws_on_binary_data(httpd_req_t *req, ws_msg_t *ws_msg); static int ws_on_binary_data(httpd_req_t *req, ws_msg_t *ws_msg);
static int ws_on_socket_open(httpd_req_t *req); static int ws_on_socket_open(httpd_req_t *req);
static int ws_on_close(httpd_req_t *req, ws_msg_t *msg); static int ws_on_close(httpd_req_t *req, httpd_ws_frame_t *ws_pkt, void *msg);
static void ws_async_resp(void *arg); static void ws_async_resp(void *arg);
static void async_send_out_cb(void *arg, int module_status); static void async_send_out_cb(void *arg, int module_status);
static void json_to_text(ws_msg_t *msg); static void json_to_text(ws_msg_t *msg);
/* Heartbeat related */ /* Heartbeat related */
static inline void ws_add_fd(httpd_handle_t hd, int fd); static inline int8_t ws_add_fd(httpd_handle_t hd, int fd);
static inline void ws_rm_fd(int fd); static inline void ws_rm_fd(int fd);
_Noreturn static void heartbeat_task(void *arg); _Noreturn static void heartbeat_task(void *arg);
@ -75,7 +78,7 @@ static esp_err_t ws_req_handler(httpd_req_t *req)
httpd_ws_frame_t *ws_pkt; httpd_ws_frame_t *ws_pkt;
ws_msg_t *ws_msg; ws_msg_t *ws_msg;
ws_msg = static_buffer_get(pdMS_TO_TICKS(10)); ws_msg = memory_pool_get(pdMS_TO_TICKS(10));
if (unlikely(ws_msg == NULL)) { if (unlikely(ws_msg == NULL)) {
httpd_ws_frame_t resp_pkt; httpd_ws_frame_t resp_pkt;
resp_pkt.type = HTTPD_WS_TYPE_TEXT; resp_pkt.type = HTTPD_WS_TYPE_TEXT;
@ -92,12 +95,12 @@ static esp_err_t ws_req_handler(httpd_req_t *req)
err = httpd_ws_recv_frame(req, ws_pkt, 0); err = httpd_ws_recv_frame(req, ws_pkt, 0);
if (unlikely(err != ESP_OK)) { if (unlikely(err != ESP_OK)) {
ESP_LOGE(TAG, "ws recv len error"); ESP_LOGE(TAG, "ws recv len error");
return ws_on_close(req, ws_msg); return ws_on_close(req, ws_pkt, ws_msg);
} }
ESP_LOGI(TAG, "frame len: %d, type: %d", ws_pkt->len, ws_pkt->type); ESP_LOGI(TAG, "frame len: %d, type: %d", ws_pkt->len, ws_pkt->type);
if (unlikely(ws_pkt->len > PAYLOAD_LEN)) { if (unlikely(ws_pkt->len > PAYLOAD_LEN)) {
ESP_LOGE(TAG, "frame len is too big"); ESP_LOGE(TAG, "frame len is too big");
return ws_on_close(req, ws_msg); return ws_on_close(req, ws_pkt, ws_msg);
} }
switch (ws_pkt->type) { switch (ws_pkt->type) {
@ -109,20 +112,13 @@ static esp_err_t ws_req_handler(httpd_req_t *req)
err = httpd_ws_recv_frame(req, ws_pkt, ws_pkt->len); err = httpd_ws_recv_frame(req, ws_pkt, ws_pkt->len);
if (unlikely(err != ESP_OK)) { if (unlikely(err != ESP_OK)) {
ESP_LOGE(TAG, "ws recv data error"); ESP_LOGE(TAG, "ws recv data error");
return ws_on_close(req, ws_msg); return ws_on_close(req, ws_pkt, ws_msg);
} }
return ws_on_text_data(req, ws_msg); return ws_on_text_data(req, ws_msg);
case HTTPD_WS_TYPE_BINARY: case HTTPD_WS_TYPE_BINARY:
ws_pkt->payload = ws_msg->payload;
/* read incoming data */
err = httpd_ws_recv_frame(req, ws_pkt, ws_pkt->len);
if (unlikely(err != ESP_OK)) {
ESP_LOGE(TAG, "ws recv data error");
return ws_on_close(req, ws_msg);
}
return ws_on_binary_data(req, ws_msg); return ws_on_binary_data(req, ws_msg);
case HTTPD_WS_TYPE_CLOSE: case HTTPD_WS_TYPE_CLOSE:
return ws_on_close(req, ws_msg); return ws_on_close(req, ws_pkt, ws_msg);
case HTTPD_WS_TYPE_PING: case HTTPD_WS_TYPE_PING:
/* Now turn the frame to PONG */ /* Now turn the frame to PONG */
ws_pkt->type = HTTPD_WS_TYPE_PONG; ws_pkt->type = HTTPD_WS_TYPE_PONG;
@ -136,39 +132,11 @@ static esp_err_t ws_req_handler(httpd_req_t *req)
goto end; goto end;
} }
end: end:
static_buffer_put(ws_msg); memory_pool_put(ws_msg);
return err; return err;
} }
void ws_set_err_msg(httpd_ws_frame_t *p_frame, api_json_req_status_e ret);
/**
* REGISTER MODULE
* */
static const httpd_uri_t uri_api = {
.uri = "/ws",
.method = HTTP_GET,
.handler = ws_req_handler,
.user_ctx = NULL,
.is_websocket = true,
.supported_subprotocol = NULL,
.handle_ws_control_frames = true,
};
static int WS_REQ_INIT(const httpd_uri_t **uri_conf) {
*uri_conf = &uri_api;
xTaskCreate(heartbeat_task, "hb task", 2048, NULL, 3, &ws_ctx.task_heartbeat);
return 0;
}
static int WS_REQ_EXIT(const httpd_uri_t **uri_conf) {
*uri_conf = &uri_api;
vTaskDelete(ws_ctx.task_heartbeat);
ws_ctx.task_heartbeat = NULL;
return 0;
}
WEB_URI_MODULE_REGISTER(101, WS_REQ_INIT, WS_REQ_EXIT)
int ws_on_text_data(httpd_req_t *req, ws_msg_t *ws_msg) int ws_on_text_data(httpd_req_t *req, ws_msg_t *ws_msg)
{ {
@ -183,9 +151,9 @@ int ws_on_text_data(httpd_req_t *req, ws_msg_t *ws_msg)
ESP_LOGI(TAG, "heap min: %lu, cur: %lu", esp_get_minimum_free_heap_size(), esp_get_free_heap_size()); ESP_LOGI(TAG, "heap min: %lu, cur: %lu", esp_get_minimum_free_heap_size(), esp_get_free_heap_size());
/* Decode */ /* Decode */
ws_msg->json.in = cJSON_ParseWithLength((char *) ws_pkt->payload, ws_pkt->len); ws_msg->json.in = cJSON_ParseWithLength((char *)ws_pkt->payload, ws_pkt->len);
if (unlikely(ws_msg->json.in == NULL)) { if (unlikely(ws_msg->json.in == NULL)) {
ws_pkt->payload = (uint8_t *) MSG_JSON_ERROR; ws_pkt->payload = (uint8_t *)MSG_JSON_ERROR;
ws_pkt->len = strlen(MSG_JSON_ERROR); ws_pkt->len = strlen(MSG_JSON_ERROR);
goto put_buf; goto put_buf;
} }
@ -204,10 +172,7 @@ int ws_on_text_data(httpd_req_t *req, ws_msg_t *ws_msg)
/* ret, buf will be release latter in async send out */ /* ret, buf will be release latter in async send out */
return ESP_OK; return ESP_OK;
} else if (ret != API_JSON_OK) { } else if (ret != API_JSON_OK) {
ws_pkt->len = strlen(MSG_BUSY_ERROR); ws_set_err_msg(ws_pkt, ret);
ws_pkt->payload = (uint8_t *)MSG_BUSY_ERROR;
ws_pkt->final = 1;
err = httpd_ws_send_frame_async(req->handle, httpd_req_to_sockfd(req), ws_pkt);
goto end; goto end;
} else if (ws_msg->json.out == NULL) { } else if (ws_msg->json.out == NULL) {
goto end; goto end;
@ -220,39 +185,62 @@ end:
cJSON_Delete(ws_msg->json.in); cJSON_Delete(ws_msg->json.in);
put_buf: put_buf:
httpd_ws_send_frame_async(req->handle, httpd_req_to_sockfd(req), ws_pkt); httpd_ws_send_frame_async(req->handle, httpd_req_to_sockfd(req), ws_pkt);
static_buffer_put(ws_msg); memory_pool_put(ws_msg);
return err; return err;
} }
void ws_set_err_msg(httpd_ws_frame_t *p_frame, api_json_req_status_e ret)
{
switch (ret) {
case API_JSON_BAD_REQUEST:
p_frame->len = strlen(MSG_BAD_REQUEST_ERROR);
p_frame->payload = (uint8_t *)MSG_BAD_REQUEST_ERROR;
p_frame->final = 1;
break;
case API_JSON_INTERNAL_ERR:
p_frame->len = strlen(MSG_INTERNAL_ERROR);
p_frame->payload = (uint8_t *)MSG_INTERNAL_ERROR;
p_frame->final = 1;
break;
default:
return;
}
}
_Static_assert(sizeof(httpd_ws_frame_t) <= 16, "bin_data_internal padding not sufficient for httpd_ws_frame_t");
int ws_on_binary_data(httpd_req_t *req, ws_msg_t *ws_msg) int ws_on_binary_data(httpd_req_t *req, ws_msg_t *ws_msg)
{ {
(void) req; (void) req;
static_buffer_put(ws_msg); memory_pool_put(ws_msg);
return 0; return 0;
} }
int ws_on_socket_open(httpd_req_t *req) int ws_on_socket_open(httpd_req_t *req)
{ {
int sock_fd = httpd_req_to_sockfd(req); int sock_fd = httpd_req_to_sockfd(req);
ws_add_fd(req->handle, sock_fd);
ESP_LOGI(TAG, "ws open: %d", sock_fd); ESP_LOGI(TAG, "ws open: %d", sock_fd);
return ESP_OK; return ws_add_fd(req->handle, sock_fd);
} }
static int ws_on_close(httpd_req_t *req, ws_msg_t *msg) /**
* @param msg must only used as generic ptr, truncated by ws_on_bin()
* @return
*/
static int ws_on_close(httpd_req_t *req, httpd_ws_frame_t *ws_pkt, void *msg)
{ {
/* Read the rest of the CLOSE frame and response */ /* Read the rest of the CLOSE frame and response */
/* Please refer to RFC6455 Section 5.5.1 for more details */ /* Please refer to RFC6455 Section 5.5.1 for more details */
msg->ws_pkt.len = 0; ws_pkt->len = 0;
msg->ws_pkt.type = HTTPD_WS_TYPE_CLOSE; ws_pkt->type = HTTPD_WS_TYPE_CLOSE;
ESP_LOGI(TAG, "ws %d closed", httpd_req_to_sockfd(req)); ESP_LOGI(TAG, "ws %d closed", httpd_req_to_sockfd(req));
ws_rm_fd(httpd_req_to_sockfd(req)); ws_rm_fd(httpd_req_to_sockfd(req));
int err = httpd_ws_send_frame(req, &msg->ws_pkt); int err = httpd_ws_send_frame(req, ws_pkt);
if (err) { if (err) {
ESP_LOGE(TAG, "on close %s", esp_err_to_name(err)); ESP_LOGE(TAG, "on close %s", esp_err_to_name(err));
} }
httpd_sess_trigger_close(req->handle, httpd_req_to_sockfd(req)); httpd_sess_trigger_close(req->handle, httpd_req_to_sockfd(req));
static_buffer_put(msg); memory_pool_put(msg);
return err; return err;
} }
@ -265,10 +253,10 @@ static void ws_async_resp(void *arg)
ESP_LOGI(TAG, "ws async fd : %d", fd); ESP_LOGI(TAG, "ws async fd : %d", fd);
err = httpd_ws_send_frame_async(hd, fd, &req->ws_pkt); err = httpd_ws_send_frame_async(hd, fd, &req->ws_pkt);
if (err) { if (unlikely(err)) {
ESP_LOGE(TAG, "%s", esp_err_to_name(err)); ESP_LOGE(TAG, "%s", esp_err_to_name(err));
} }
static_buffer_put(req); memory_pool_put(req);
} }
void async_send_out_cb(void *arg, int module_status) void async_send_out_cb(void *arg, int module_status)
@ -282,6 +270,7 @@ void async_send_out_cb(void *arg, int module_status)
json_to_text(req); json_to_text(req);
err = httpd_queue_work(req->hd, ws_async_resp, req); err = httpd_queue_work(req->hd, ws_async_resp, req);
if (likely(err == ESP_OK)) { if (likely(err == ESP_OK)) {
/* msg queued, let callee release the buffer */
return; return;
} }
@ -289,7 +278,7 @@ void async_send_out_cb(void *arg, int module_status)
end: end:
/* clean resources */ /* clean resources */
static_buffer_put(req); memory_pool_put(req);
} }
void json_to_text(ws_msg_t *ws_msg) void json_to_text(ws_msg_t *ws_msg)
@ -297,27 +286,29 @@ void json_to_text(ws_msg_t *ws_msg)
int err; int err;
httpd_ws_frame_t *ws_pkt = &ws_msg->ws_pkt; httpd_ws_frame_t *ws_pkt = &ws_msg->ws_pkt;
/* api function returns something, send it to http client */ /* api function returns something, send it to http client */
err = !cJSON_PrintPreallocated(ws_msg->json.out, (char *) ws_msg->payload, PAYLOAD_LEN - 5, 0); err = !cJSON_PrintPreallocated(ws_msg->json.out, (char *)ws_msg->payload, PAYLOAD_LEN - 5, 0);
cJSON_Delete(ws_msg->json.out); cJSON_Delete(ws_msg->json.out);
if (unlikely(err)) { if (unlikely(err)) {
ws_pkt->len = strlen(MSG_SEND_JSON_ERROR); ws_pkt->len = strlen(MSG_SEND_JSON_ERROR);
ws_pkt->payload = (uint8_t *) MSG_SEND_JSON_ERROR; ws_pkt->payload = (uint8_t *)MSG_SEND_JSON_ERROR;
ws_pkt->final = 1; ws_pkt->final = 1;
} }
ws_pkt->len = strlen((char *) ws_pkt->payload); ws_pkt->len = strlen((char *)ws_pkt->payload);
} }
/* Clients array manipulation function /* Clients array manipulation function
* */ * */
static inline void ws_add_fd(httpd_handle_t hd, int fd) static inline int8_t ws_add_fd(httpd_handle_t hd, int fd)
{ {
if (ws_ctx.client_count > CONFIG_LWIP_MAX_SOCKETS) { if (ws_ctx.client_count > CONFIG_LWIP_MAX_SOCKETS) {
return; return 1;
} }
ws_ctx.clients[ws_ctx.client_count].fd = fd; ws_ctx.clients[ws_ctx.client_count].fd = fd;
ws_ctx.clients[ws_ctx.client_count].hd = hd; ws_ctx.clients[ws_ctx.client_count].hd = hd;
ws_ctx.client_count++; ws_ctx.client_count++;
return 0;
} }
/** /**
@ -328,8 +319,8 @@ static inline void ws_rm_fd(int fd)
{ {
for (int i = 0; i < ws_ctx.client_count; ++i) { for (int i = 0; i < ws_ctx.client_count; ++i) {
if (ws_ctx.clients[i].fd == fd) { if (ws_ctx.clients[i].fd == fd) {
ws_ctx.clients[i].fd = ws_ctx.clients[ws_ctx.client_count-1].fd; ws_ctx.clients[i].fd = ws_ctx.clients[ws_ctx.client_count - 1].fd;
ws_ctx.clients[i].hd = ws_ctx.clients[ws_ctx.client_count-1].hd; ws_ctx.clients[i].hd = ws_ctx.clients[ws_ctx.client_count - 1].hd;
ws_ctx.client_count--; ws_ctx.client_count--;
return; return;
} }
@ -368,10 +359,41 @@ static inline void ws_broadcast_heartbeat()
_Noreturn _Noreturn
void heartbeat_task(void *arg) void heartbeat_task(void *arg)
{ {
(void) arg; (void)arg;
while (1) { while (1) {
vTaskDelay(pdMS_TO_TICKS(2000)); vTaskDelay(pdMS_TO_TICKS(2000));
ws_broadcast_heartbeat(); ws_broadcast_heartbeat();
} }
}; };
/**
* REGISTER MODULE
* */
static const httpd_uri_t uri_api = {
.uri = "/ws",
.method = HTTP_GET,
.handler = ws_req_handler,
.user_ctx = NULL,
.is_websocket = true,
.supported_subprotocol = NULL,
.handle_ws_control_frames = true,
};
static int WS_REQ_INIT(const httpd_uri_t **uri_conf)
{
*uri_conf = &uri_api;
xTaskCreate(heartbeat_task, "hb task", 2048, NULL, 3, &ws_ctx.task_heartbeat);
return 0;
}
static int WS_REQ_EXIT(const httpd_uri_t **uri_conf)
{
*uri_conf = &uri_api;
vTaskDelete(ws_ctx.task_heartbeat);
ws_ctx.task_heartbeat = NULL;
return 0;
}
WEB_URI_MODULE_REGISTER(101, WS_REQ_INIT, WS_REQ_EXIT)

View File

@ -11,10 +11,15 @@ typedef struct uri_module_t {
uint8_t priority; uint8_t priority;
} uri_module_t; } uri_module_t;
/**
* @param priority smaller number will be called first
* @return 0: SUCCESS, 1: max module count reached
*/
int uri_module_add(uint8_t priority, uri_module_func init, uri_module_func exit); int uri_module_add(uint8_t priority, uri_module_func init, uri_module_func exit);
/** /**
* @brief Register a uri module that will be init with PRI(priority) order. * @brief Register a uri module that will be init with PRI(priority) order.
* smaller priority will be called first.
*/ */
#define WEB_URI_MODULE_REGISTER(PRI, INIT, EXIT) \ #define WEB_URI_MODULE_REGISTER(PRI, INIT, EXIT) \
__attribute__((used, constructor(PRI))) void cons_ ## INIT(); \ __attribute__((used, constructor(PRI))) void cons_ ## INIT(); \

View File

@ -3,7 +3,7 @@
#include <lwip/ip4_addr.h> #include <lwip/ip4_addr.h>
#define WIFI_API_MODULE_ID 1 #define WIFI_MODULE_ID 1
typedef enum wifi_api_json_cmd_t { typedef enum wifi_api_json_cmd_t {
UNKNOWN = 0, UNKNOWN = 0,

View File

@ -19,7 +19,7 @@ static int wifi_api_json_disconnect(api_json_req_t *req);
static int wifi_api_json_ap_get_info(api_json_req_t *req); static int wifi_api_json_ap_get_info(api_json_req_t *req);
/* the upper caller call cb() with void *, this let us use custom function arg */
static int async_helper_cb(void *arg) static int async_helper_cb(void *arg)
{ {
api_json_module_req_t *req = arg; api_json_module_req_t *req = arg;
@ -30,7 +30,7 @@ static inline int set_async(api_json_req_t *req, api_json_module_async_t *async,
{ {
async->module.func = func; async->module.func = func;
async->module.arg = req; async->module.arg = req;
async->req_task.module.helper_cb = async_helper_cb; async->req_task.module.cb = async_helper_cb;
async->req_task.module.arg = &async->module; async->req_task.module.arg = &async->module;
return API_JSON_ASYNC; return API_JSON_ASYNC;
} }
@ -52,7 +52,6 @@ static int on_json_req(uint16_t cmd, api_json_req_t *req, api_json_module_async_
return wifi_api_json_disconnect(req); return wifi_api_json_disconnect(req);
case WIFI_API_JSON_AP_GET_INFO: case WIFI_API_JSON_AP_GET_INFO:
return wifi_api_json_ap_get_info(req); return wifi_api_json_ap_get_info(req);
break;
} }
ESP_LOGI(TAG, "cmd %d not executed\n", cmd); ESP_LOGI(TAG, "cmd %d not executed\n", cmd);
@ -127,7 +126,7 @@ int wifi_api_json_disconnect(api_json_req_t *req)
static int wifi_api_json_init(api_json_module_cfg_t *cfg) static int wifi_api_json_init(api_json_module_cfg_t *cfg)
{ {
cfg->on_req = on_json_req; cfg->on_req = on_json_req;
cfg->module_id = WIFI_API_MODULE_ID; cfg->module_id = WIFI_MODULE_ID;
return 0; return 0;
} }

View File

@ -6,7 +6,7 @@
static void wifi_api_json_set_header(cJSON *root, uint16_t cmd) static void wifi_api_json_set_header(cJSON *root, uint16_t cmd)
{ {
cJSON_AddNumberToObject(root, "cmd", cmd); cJSON_AddNumberToObject(root, "cmd", cmd);
cJSON_AddNumberToObject(root, "module", WIFI_API_MODULE_ID); cJSON_AddNumberToObject(root, "module", WIFI_MODULE_ID);
} }
cJSON *wifi_api_json_serialize_ap_info(wifi_api_ap_info_t *ap_info, wifi_api_json_cmd_t cmd) cJSON *wifi_api_json_serialize_ap_info(wifi_api_ap_info_t *ap_info, wifi_api_json_cmd_t cmd)

View File

@ -14,6 +14,22 @@ void wt_mdns_init()
/* TODO: read instance description from NVS */ /* TODO: read instance description from NVS */
ESP_ERROR_CHECK(mdns_instance_name_set(MDSN_INSTANCE_DESC)); ESP_ERROR_CHECK(mdns_instance_name_set(MDSN_INSTANCE_DESC));
//structure with TXT records
mdns_txt_item_t serviceTxtData[] = {
#if defined CONFIG_IDF_TARGET_ESP32S3
{"board", "esp32S3"},
#elif defined CONFIG_IDF_TARGET_ESP32C3
{"board", "esp32c3"},
#elif defined CONFIG_IDF_TARGET_ESP32
{"board", "esp32"},
#endif
};
//initialize service
ESP_ERROR_CHECK(mdns_service_add("WebServer", "_http", "_tcp", 80,
serviceTxtData, sizeof(serviceTxtData)/sizeof(serviceTxtData[0])));
ESP_ERROR_CHECK(mdns_service_subtype_add_for_host("WebServer", "_http", "_tcp", NULL, "_server") );
} }
int wt_mdns_set_hostname(const char *hostname) int wt_mdns_set_hostname(const char *hostname)

View File

@ -2,7 +2,7 @@
#define WT_MDNS_CONFIG_H_GUARD #define WT_MDNS_CONFIG_H_GUARD
#define MDSN_DEFAULT_HOSTNAME "dap" // + serial number (4 char) #define MDSN_DEFAULT_HOSTNAME "dap" // + serial number (4 char)
#define MDSN_INSTANCE_DESC "无线STM32调试器 by 允斯工作室" #define MDSN_INSTANCE_DESC "ESP32 无线透传"
void wt_mdns_init(); void wt_mdns_init();