A year ago I posted asking if I could change the priority of a task at runtime.
However, the forum members said so many things that it discouraged me and I ended up not doing it.
The fact is that I work with device systems that have an embedded web server. This is used by the user to configure the product, which is very common.
However, in a complex system like mine,
the web server is “kind of slow…” which gives the user a very bad impression that the product is defective or not working well.
To solve this problem, I need to take the FreeRTOS task and increase its priority significantly.
However, there’s a problem: Increasing the task priority makes the system very busy.
And the web server isn’t the system’s main task. It only serves the purpose of configuration after the user configures it, it theoretically serves no other purpose! It consumes resources unnecessarily.
Well, this year I decided to take on the challenge and implement this system! In production, and I’d like to share the results with you. In my experience, it’s been very good!!
/*
* WEBSERVER PRIORITY - Dynamic HTTP Server priority control
*
* Objective:
* Automatically adjust the priority of the "httpd" task according to the
* web interface usage, preventing the webserver from stealing system
* performance when nobody is using the page.
*
* The logic has 3 modes:
*
* WEB_MODE_IDLE:
* Webserver running with low priority.
* Used when there is no recent activity on the page.
*
* WEB_MODE_ACTIVE:
* Webserver running with high priority.
* Used while the user is browsing or configuring the web interface.
*
* WEB_MODE_RUN:
* Webserver forced to low priority.
* Used when the user clicks the RUN button to prioritize the system.
*
*
* How to use:
*
* 1) Start the priority monitor once after starting the webserver:
*
* f_WebserverPriorityStart();
*
* This function creates the "t_WebPrioMon" task.
* If PSRAM is enabled, the task is preferably created in PSRAM.
*
*
* 2) Register the endpoints in the webserver:
*
* /web-active -> pWebActive
* /web-run -> pWebRun
* /web-config -> pWebConfig
*
*
* 3) In the web page, periodically call the activity endpoint:
*
* fetch("/web-active");
*
* This should be done while the configuration page is open.
* Recommended JavaScript example:
*
* setInterval(() => {
* fetch("/web-active").catch(() => {});
* }, 5000);
*
*
* Behavior:
*
* pWebActive():
* Called by the web page to notify that the user is still active.
* If the current mode is not RUN, it changes to WEB_MODE_ACTIVE and
* increases the priority of the "httpd" task.
*
* pWebConfig():
* Forces configuration mode.
* Immediately increases the webserver priority.
* Use it when the user clicks CONFIG or enters a settings page.
*
* pWebRun():
* Forces run mode.
* Immediately lowers the webserver priority.
* Use it when the user clicks RUN and wants to prioritize the main
* system tasks.
*
*
* Automatic timeout:
*
* The monitor checks every 1 second whether the webserver is in
* WEB_MODE_ACTIVE.
*
* If there is no activity for more than WEBSERVER_TIMEOUT_MS:
*
* WEB_MODE_ACTIVE -> WEB_MODE_IDLE
*
* And the "httpd" priority goes back to:
*
* WEBSERVER_PRIORITY_IDLE
*
*
* Priorities used:
*
* Low priority:
*
* WEBSERVER_PRIORITY_IDLE
*
* High priority:
*
* configMAX_PRIORITIES - 3
*
* Warning:
* The high priority should be enough to keep browsing responsive,
* but it should not be higher than critical system tasks.
*
*
* Important notes:
*
* - The HTTP Server task must be named "httpd".
* The function xTaskGetHandle("httpd") depends on this name.
*
* - If "HTTPD task not found" appears in the log, the monitor was probably
* started before the webserver or the task name is different.
*
* - pWebActive() does not change the priority if the system is in
* WEB_MODE_RUN.
* This prevents the page from increasing the priority again after the
* user clicks RUN.
*
* - WEB_MODE_RUN only exits this mode when pWebConfig() is called.
*
* - This module does not stop the webserver. It only changes the task
* priority.
*
*
* Recommended interface flow:
*
* When opening a configuration page:
*
* call /web-config once.
*
* While the page is open:
*
* call /web-active every 5 seconds.
*
* When clicking the RUN button:
*
* call /web-run.
*
* When clicking the CONFIG button:
*
* call /web-config.
*
*
* JavaScript usage example:
*
* function webConfigMode() {
* fetch("/web-config").catch(() => {});
* }
*
* function webRunMode() {
* fetch("/web-run").catch(() => {});
* }
*
* setInterval(() => {
* fetch("/web-active").catch(() => {});
* }, 5000);
*
*
* Practical summary:
*
* - Page open / configuration active -> high priority.
* - Page closed / inactive by timeout -> low priority.
* - RUN button clicked -> forced low priority.
* - CONFIG button clicked -> high priority again.
*/
#include "f_webserver_priority.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define WEBSERVER_PRIORITY_IDLE 2
#define WEBSERVER_TIMEOUT_MS 15000
#define WEBSERVER_PRIO_TASK_STACK 2048
#define WEBSERVER_PRIO_TASK_PRIORITY 1
static const char *TAG = "WEB_PRIORITY";
typedef enum {
WEB_MODE_IDLE = 0,
WEB_MODE_ACTIVE,
WEB_MODE_RUN
} web_mode_t;
static volatile int64_t web_last_activity_ms = 0;
static volatile web_mode_t web_mode = WEB_MODE_IDLE;
static bool web_priority_task_started = false;
static int64_t f_web_millis(void) { return esp_timer_get_time() / 1000;}
static void f_WebserverSetPriority(UBaseType_t nova_prioridade) {
TaskHandle_t h = xTaskGetHandle("httpd");
if (h != NULL) {
vTaskPrioritySet(h, nova_prioridade);
ESP_LOGI(TAG, "HTTPD priority changed to %u", nova_prioridade);
} else {
ESP_LOGW(TAG, "HTTPD task not found");
}
}
static void f_WebserverTouch(void) {
web_last_activity_ms = f_web_millis();
if (web_mode == WEB_MODE_RUN) {return;}
if (web_mode != WEB_MODE_ACTIVE) {
web_mode = WEB_MODE_ACTIVE;
f_WebserverSetPriority(configMAX_PRIORITIES - 3);
}
}
static void f_WebserverPriorityMonitor(void *pv) {
(void)pv;
while (1) {
if (web_mode == WEB_MODE_ACTIVE) {
int64_t now = f_web_millis();
if ((now - web_last_activity_ms) > WEBSERVER_TIMEOUT_MS) {
web_mode = WEB_MODE_IDLE;
f_WebserverSetPriority(WEBSERVER_PRIORITY_IDLE);
}
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void f_WebserverPriorityStart(void) {
if (web_priority_task_started) {return;}
web_priority_task_started = true;
web_mode = WEB_MODE_IDLE;
web_last_activity_ms = f_web_millis();
#if defined(CONFIG_SPIRAM) && CONFIG_SPIRAM
BaseType_t ret = xTaskCreateWithCaps(f_WebserverPriorityMonitor,"t_WebPrioMon", WEBSERVER_PRIO_TASK_STACK, NULL, WEBSERVER_PRIO_TASK_PRIORITY, NULL, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
#else
BaseType_t ret = xTaskCreate(f_WebserverPriorityMonitor, "t_WebPrioMon", WEBSERVER_PRIO_TASK_STACK, NULL, WEBSERVER_PRIO_TASK_PRIORITY, NULL);
#endif
if (ret == pdPASS) {
ESP_LOGI(TAG, "Webserver priority monitor started");
} else {
web_priority_task_started = false;
ESP_LOGE(TAG, "Failed to start webserver priority monitor");
}
}
esp_err_t pWebActive(httpd_req_t *req) {
f_WebserverTouch();
httpd_resp_set_status(req, "204 No Content");
httpd_resp_send(req, NULL, 0);
return ESP_OK;
}
esp_err_t pWebRun(httpd_req_t *req) {
web_mode = WEB_MODE_RUN;
web_last_activity_ms = f_web_millis();
f_WebserverSetPriority(WEBSERVER_PRIORITY_IDLE);
httpd_resp_set_status(req, "204 No Content");
httpd_resp_send(req, NULL, 0);
return ESP_OK;
}
esp_err_t pWebConfig(httpd_req_t *req) {
web_mode = WEB_MODE_ACTIVE;
web_last_activity_ms = f_web_millis();
f_WebserverSetPriority(configMAX_PRIORITIES - 3);
httpd_resp_set_status(req, "204 No Content");
httpd_resp_send(req, NULL, 0);
return ESP_OK;
}
I will also share a screenshot of my product where the user can choose the priority, based on two concepts:
RUN or Config
T: Starting automatic monitoring of SNMP interfaces
I (7429) OTA_Manager: Firmware is running from factory partition. No validation needed.
E (7429) SNMP_CLIENT: No valid device found
I (7459) f_alarme: Alarm System Started
I (24379) WEB_PRIORITY: HTTPD priority changed to 2
I (158539) WEB_PRIORITY: HTTPD priority changed to 22
I (158549) WEB_PRIORITY: HTTPD priority changed to 22
I (166579) WEB_PRIORITY: HTTPD priority changed to 22
I (212509) WEB_PRIORITY: HTTPD priority changed to 22
I (238379) WEB_PRIORITY: HTTPD priority changed to 2
I (605779) WEB_PRIORITY: HTTPD priority changed to 22
I (613159) WEB_PRIORITY: HTTPD priority changed to 22
I (722379) WEB_PRIORITY: HTTPD priority changed to 2
I (766769) WEB_PRIORITY: HTTPD priority changed to 22
I (782379) WEB_PRIORITY: HTTPD priority changed to 2
I (826769) WEB_PRIORITY: HTTPD priority changed to 22
I (842379) WEB_PRIORITY: HTTPD priority changed to 2
I (886679) WEB_PRIORITY: HTTPD priority changed to 22
I (902379) WEB_PRIORITY: HTTPD priority changed to 2
I (946689) WEB_PRIORITY: HTTPD priority changed to 22
I (962379) WEB_PRIORITY: HTTPD priority changed to 2
I (1006689) WEB_PRIORITY: HTTPD priority changed to 22
I (1022379) WEB_PRIORITY: HTTPD priority changed to 2
I (1066649) WEB_PRIORITY: HTTPD priority changed to 22
I (1082379) WEB_PRIORITY: HTTPD priority changed to 2
I (1126709) WEB_PRIORITY: HTTPD priority changed to 22
I (1142379) WEB_PRIORITY: HTTPD priority changed to 2
I (1186709) WEB_PRIORITY: HTTPD priority changed to 22
I (1202379) WEB_PRIORITY: HTTPD priority changed to 2
I (1246719) WEB_PRIORITY: HTTPD priority changed to 22
I (1262379) WEB_PRIORITY: HTTPD priority changed to 2
I (1306729) WEB_PRIORITY: HTTPD priority changed to 22
I (1322379) WEB_PRIORITY: HTTPD priority changed to 2
I (1360589) WEB_PRIORITY: HTTPD priority changed to 22
I (1449379) WEB_PRIORITY: HTTPD priority changed to 2
I (1486759) WEB_PRIORITY: HTTPD priority changed to 22
I (1502379) WEB_PRIORITY: HTTPD priority changed to 2
I (1546759) WEB_PRIORITY: HTTPD priority changed to 22
I (1562379) WEB_PRIORITY: HTTPD priority changed to 2
If you’re curious to know what the product is, check it out at this link:
