Compare commits

1 Commits
main ... dev

Author SHA1 Message Date
e42dd3b91b wip: 2026-01-25 20:53:24 +03:00
12 changed files with 455 additions and 3 deletions

View File

@@ -1 +1 @@
idf_component_register(SRCS "main.c" INCLUDE_DIRS "include")
idf_component_register(SRCS "menu_manager.c" INCLUDE_DIRS "include")

12
Kconfig Normal file
View File

@@ -0,0 +1,12 @@
menu "Menu Menager"
config MAX_DEPTH_PATH
int "Set max deth of submenus"
default 5
config SALVE_INDEX
bool "Savel last menu index selected"
default y
endmenu

View File

@@ -1,3 +1,67 @@
# esp_component_template
# Menu Manager
Easy and flexible menu manager for ESP-IDF
```mermaid
---
config:
title: Menu Manager
---
flowchart TD
start((menu init))
createQueue[Create Queue qCommand]
waitQueue[Wait Queue qCommand]
showDisplay[Show menu]
funtionInExecution{Is there a funtion in execution?}
isCommandBack{Is command back?}
exitFuntion[Exit funtion in execution]
swithCasesInput{Command}
navigateUP[Increments the index]
navigateDOWN[Decrements the index]
navigateBACK[return to parent menu]
navigateSELECTION{Is there a funtion?}
selectSubMenu[Select sub menu]
executeFuntion[Execute funtion]
start --> createQueue
createQueue --> waitQueue
waitQueue --> funtionInExecution
funtionInExecution --"Yes"--> isCommandBack
isCommandBack --"Yes"--> exitFuntion
funtionInExecution --"No"--> swithCasesInput
swithCasesInput --"UP"--> navigateUP
swithCasesInput --"DOWN"--> navigateDOWN
swithCasesInput --"BACK"--> navigateBACK
swithCasesInput --"SELECT"--> navigateSELECTION
exitFuntion --> showDisplay
navigateUP --> showDisplay
navigateDOWN --> showDisplay
navigateBACK --> showDisplay
navigateSELECTION --"No"--> selectSubMenu --> showDisplay
navigateSELECTION --"Yes"--> executeFuntion --> waitQueue
showDisplay --> waitQueue
%% Estilos e cores
classDef startEnd fill:#e1f5fe,stroke:#01579b,stroke-width:3px,color:#000
classDef queue fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:#000
classDef display fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px,color:#000
classDef decision fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000
classDef navigation fill:#fce4ec,stroke:#ad1457,stroke-width:2px,color:#000
classDef execution fill:#ffebee,stroke:#c62828,stroke-width:2px,color:#000
classDef selection fill:#e0f2f1,stroke:#00695c,stroke-width:2px,color:#000
class start startEnd
class createQueue,waitQueue queue
class showDisplay display
class funtionInExecution,isCommandBack,swithCasesInput,navigateSELECTION decision
class navigateUP,navigateDOWN,navigateBACK navigation
class exitFuntion,executeFuntion execution
class selectSubMenu selection```
esp_component_template

View File

View File

@@ -0,0 +1,8 @@
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(main)

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,6 @@
dependencies:
idf:
version: '>=4.1.0'
MarcioBulla/menu_manager:
git: https://github.com/MarcioBulla/menu_manager
version: main

View File

@@ -0,0 +1,88 @@
#include "menu_manager.h"
#include <esp_log.h>
#include <esp_system.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <sdkconfig.h>
#include <stdbool.h>
#include <stdio.h>
static const char *TAG = "main";
void dumb(void *args) {
ESP_LOGI(TAG, "I am dumb");
while (true) {
vTaskDelay(10000 / portTICK_PERIOD_MS);
}
}
menu_node_t submenu[3] = {
{.label = "funcA", .function = &dumb},
{.label = "funcB", .function = &dumb},
{.label = "funcC", .function = &dumb},
};
menu_node_t root = {
.label = "root",
.num_options = 3,
.submenus = (menu_node_t[3]){
{.label = "submenu1", .submenus = submenu, .num_options = 3},
{.label = "submenu2", .submenus = submenu, .num_options = 3},
{.label = "submenu3", .submenus = submenu, .num_options = 3},
}};
void simula_input(void *args) {
Navigate_t teste = NAVIGATE_UP;
vTaskDelay(6000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "NEXT");
xQueueSend(qCommands, &teste, portMAX_DELAY);
vTaskDelay(5000 / portTICK_PERIOD_MS);
teste = NAVIGATE_SELECT;
ESP_LOGI(TAG, "SELECT");
xQueueSend(qCommands, &teste, portMAX_DELAY);
vTaskDelay(5000 / portTICK_PERIOD_MS);
teste = NAVIGATE_DOWN;
ESP_LOGI(TAG, "DOWN");
xQueueSend(qCommands, &teste, portMAX_DELAY);
vTaskDelay(5000 / portTICK_PERIOD_MS);
teste = NAVIGATE_SELECT;
ESP_LOGI(TAG, "SELECT");
xQueueSend(qCommands, &teste, portMAX_DELAY);
vTaskDelay(5000 / portTICK_PERIOD_MS);
teste = NAVIGATE_BACK;
ESP_LOGI(TAG, "BACK");
xQueueSend(qCommands, &teste, portMAX_DELAY);
vTaskDelay(5000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "BACK");
xQueueSend(qCommands, &teste, portMAX_DELAY);
vTaskDelay(10000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG, "Finalizada");
vTaskDelete(NULL);
}
void display(menu_path_t *current_path) {
ESP_LOGI(TAG, "title: %s, index_select: %d",
current_path->current_menu->label, current_path->current_index);
ESP_LOGI(
TAG, "Option Selected %s",
current_path->current_menu->submenus[current_path->current_index].label);
}
void app_main(void) {
menu_config_t config = {
.root = root,
.loop = true,
.display = &display,
};
xTaskCreatePinnedToCore(&menu_init, "menu_init", 2048, &config, 3, NULL, 0);
vTaskDelay(5000 / portMAX_DELAY);
xTaskCreatePinnedToCore(&simula_input, "simula", 2048, NULL, 1, NULL, 0);
vTaskDelete(NULL);
}

View File

113
include/menu_manager.h Normal file
View File

@@ -0,0 +1,113 @@
/**
* @file menu_manager.h
* @brief This file defines structures and functions related to the Menu System.
*/
#ifndef __MENU_MANAGER_H__
#define __MENU_MANAGER_H__
#pragma once
#include "freertos/idf_additions.h"
#include "sdkconfig.h"
#include <esp_err.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define END_MENU_FUNCTION end_menuFunction()
#define SET_QUICK_FUNCTION setQuick_menuFunction()
extern TaskHandle_t tMenuFunction;
extern QueueHandle_t qCommands;
/**
* Type with possibles of action in menu system.
*
*/
typedef enum {
NAVIGATE_UP,
/**< Action up (index++). */
NAVIGATE_DOWN,
/**< Action DOWN (index--). */
NAVIGATE_SELECT,
/**< Action SELECT (depth++). */
NAVIGATE_BACK,
/**< Action BACH (depth--). */
NAVIGATE_NOTHING,
/**< Nothing. */
} Navigate_t;
/**
* Menu System is based in nodes all menus are nodes. Submenus are nodes with
* array of more submenus and function (leafs) are nodes with function.
*
*/
typedef struct menu_node {
char *label;
/**< Title of node. */
struct menu_node *submenus;
/**< Point of array submenus or one submenu. */
size_t num_options;
/**< number of option on submenu. */
void (*function)(void *args);
/**< function that defined this node as leaf. */
} menu_node_t;
/**
* Struct for save location and index.
*
*/
typedef struct {
menu_node_t *current_menu;
/**< Point if current menu. */
uint8_t current_index;
/**< current Index of selected option. */
} menu_path_t;
/**
* This struct is all essential args that menu_init will need.
*/
typedef struct {
menu_node_t root;
/**< Menu_node_t: origin of Menu System. */
void (*display)(menu_path_t *current_path);
/**< Function that receive menu_path_t and index for display current
* selection. */
bool loop;
/**< Loop menu. */
} menu_config_t;
/**
* Start menu system that receive generic input function and generic
* display function.
*
* @param params The struct that there are args.
*/
void menu_init(void *params);
/**
* @brief Use this function all option menus when finish
*/
void exitFunction(void);
/**
* @brief Exec especific funtioon
*
* @param Function addres of function
*/
void execFunction(void (*function)(void *args));
/**
* @brief Use this function when your function is a wuick function before
* end_menuFunction()
*/
void setQuick_menuFunction(void);
#ifdef __cplusplus
}
#endif
#endif //__MENU_MANAGER_H__

0
main.c
View File

159
menu_manager.c Normal file
View File

@@ -0,0 +1,159 @@
// TODO: Add comments
#include "menu_manager.h"
#include "esp_err.h"
#include "freertos/idf_additions.h"
#include "freertos/portmacro.h"
#include "freertos/projdefs.h"
#include "sdkconfig.h"
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
const static char *TAG = "menu_menager";
TaskHandle_t tMenuFunction = NULL;
QueueHandle_t qCommands = NULL;
menu_path_t steckPath[CONFIG_MAX_DEPTH_PATH];
menu_path_t path;
uint8_t depth = 0;
Navigate_t inputCommand = NAVIGATE_NOTHING;
void (**show)(menu_path_t *current_menu);
static void NavigationUp(bool loop) {
ESP_LOGI(TAG, "Command UP");
if (path.current_index < path.current_menu->num_options - 1 || loop) {
path.current_index++;
path.current_index %= path.current_menu->num_options;
}
}
static void NavigationDown(bool loop) {
ESP_LOGI(TAG, "Command DOWN");
if (path.current_index > 0 || loop) {
path.current_index =
(path.current_menu->num_options + path.current_index - 1) %
path.current_menu->num_options;
}
}
static void ExecFunction() {
ESP_LOGI(TAG, "Execute Function: %s",
path.current_menu->submenus[path.current_index].label);
xTaskCreatePinnedToCore(
path.current_menu->submenus[path.current_index].function,
path.current_menu->submenus[path.current_index].label, 10240, NULL, 10,
&tMenuFunction, 1);
}
static void SelectionOption() {
ESP_LOGI(TAG, "Open Submenu");
path.current_menu = &path.current_menu->submenus[path.current_index];
path.current_index = 0;
depth++;
steckPath[depth] = path;
}
static void NavigationBack() {
ESP_LOGI(TAG, "Command BACK");
depth--;
path = steckPath[depth];
#if !CONFIG_SALVE_INDEX
path.current_index = 0;
#endif
}
void menu_init(void *args) {
ESP_LOGI(TAG, "Start menu");
menu_config_t *params = (menu_config_t *)args;
qCommands = xQueueCreate(10, sizeof(Navigate_t));
path.current_index = 0;
path.current_menu = &params->root;
steckPath[depth] = path;
show = &params->display;
(*show)(&path);
ESP_LOGI(TAG, "Root Title: %s", path.current_menu->label);
while (true) {
xQueueReceive(qCommands, &inputCommand, portMAX_DELAY);
if (tMenuFunction == NULL) {
switch (inputCommand) {
case NAVIGATE_UP:
NavigationUp(params->loop);
break;
case NAVIGATE_DOWN:
NavigationDown(params->loop);
break;
case NAVIGATE_SELECT:
if (path.current_menu->submenus[path.current_index].function) {
ExecFunction();
} else {
SelectionOption();
}
break;
case NAVIGATE_BACK:
if (depth) {
NavigationBack();
}
break;
default:
ESP_LOGW(TAG, "Undenfined Input");
break;
}
(*show)(&path);
} else if (inputCommand == NAVIGATE_BACK) {
vTaskDelete(tMenuFunction);
tMenuFunction = NULL;
(*show)(&path);
}
}
}
void exitFunction(void) {
if (tMenuFunction != NULL) {
ESP_LOGI(TAG, "Exit Function");
(*show)(&path);
tMenuFunction = NULL;
vTaskDelete(tMenuFunction);
}
}
void execFunction(void (*function)(void *args)) {
ESP_LOGI(TAG, "Execute Function");
xTaskCreatePinnedToCore(function, "Function_by_menu", 10240, NULL, 10,
&tMenuFunction, 1);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
void setQuick_menuFunction(void) { (*show)(&path); }
#ifdef __cplusplus
}
#endif