Add menu (display + encoder) functional - wip
Added functional menu using display and encoder: - a menu item is defined in one struct - scroll in a list of defined items - select option - modify value - save value, return to list Currently only menu is run (button and status display disabled) - Add menu.cpp/hpp - Add encoder.cpp/hpp mostly from previous test in board-control - display.cpp: only run new handleMenu() function - main.cpp: initialize encoder at startup, disable button task for testing
This commit is contained in:
parent
f16b96c100
commit
fc5aad0c14
@ -7,6 +7,8 @@ idf_component_register(
|
||||
"fan.cpp"
|
||||
"auto.cpp"
|
||||
"display.cpp"
|
||||
"menu.cpp"
|
||||
"encoder.cpp"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
)
|
||||
|
@ -4,6 +4,8 @@ extern "C"{
|
||||
#include "esp_ota_ops.h"
|
||||
}
|
||||
|
||||
#include "menu.hpp"
|
||||
|
||||
|
||||
|
||||
//==== display config ====
|
||||
@ -219,26 +221,32 @@ void display_task( void * pvParameters ){
|
||||
// repeatedly update display with content
|
||||
while (1)
|
||||
{
|
||||
//--- fast loop ---
|
||||
showScreen1();
|
||||
|
||||
if (countFastloop >= SLOW_LOOP_INTERVAL / FAST_LOOP_INTERVAL)
|
||||
{
|
||||
//--- slow loop ---
|
||||
//currently only showing menu:
|
||||
handleMenu(&dev);
|
||||
|
||||
if (countSlowLoop >= VERY_SLOW_LOOP_INTERVAL / SLOW_LOOP_INTERVAL)
|
||||
{
|
||||
//--- very slow loop ---
|
||||
// clear display - workaround for bugged line order after a few minutes
|
||||
countSlowLoop = 0;
|
||||
ssd1306_clear_screen(&dev, false);
|
||||
}
|
||||
countFastloop = 0;
|
||||
countSlowLoop++;
|
||||
}
|
||||
countFastloop++;
|
||||
vTaskDelay(FAST_LOOP_INTERVAL / portTICK_PERIOD_MS);
|
||||
// TODO add pages and menus
|
||||
|
||||
//status screen currently disabled:
|
||||
// //--- fast loop ---
|
||||
// showScreen1();
|
||||
|
||||
// if (countFastloop >= SLOW_LOOP_INTERVAL / FAST_LOOP_INTERVAL)
|
||||
// {
|
||||
// //--- slow loop ---
|
||||
|
||||
// if (countSlowLoop >= VERY_SLOW_LOOP_INTERVAL / SLOW_LOOP_INTERVAL)
|
||||
// {
|
||||
// //--- very slow loop ---
|
||||
// // clear display - workaround for bugged line order after a few minutes
|
||||
// countSlowLoop = 0;
|
||||
// ssd1306_clear_screen(&dev, false);
|
||||
// }
|
||||
// countFastloop = 0;
|
||||
// countSlowLoop++;
|
||||
// }
|
||||
// countFastloop++;
|
||||
// vTaskDelay(FAST_LOOP_INTERVAL / portTICK_PERIOD_MS);
|
||||
// // TODO add pages and menus
|
||||
}
|
||||
}
|
||||
|
||||
|
83
board_single/main/encoder.cpp
Normal file
83
board_single/main/encoder.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
extern "C"
|
||||
{
|
||||
#include <stdio.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_event.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "encoder.h"
|
||||
}
|
||||
|
||||
#include "encoder.hpp"
|
||||
|
||||
//-------------------------
|
||||
//------- variables -------
|
||||
//-------------------------
|
||||
static const char * TAG = "encoder";
|
||||
uint16_t encoderCount;
|
||||
rotary_encoder_btn_state_t encoderButtonState = {};
|
||||
//global event queue:
|
||||
QueueHandle_t encoderQueue = NULL;
|
||||
|
||||
//encoder config
|
||||
rotary_encoder_t encoderConfig = {
|
||||
.pin_a = PIN_A,
|
||||
.pin_b = PIN_B,
|
||||
.pin_btn = PIN_BUTTON,
|
||||
.code = 1,
|
||||
.store = encoderCount,
|
||||
.index = 0,
|
||||
.btn_pressed_time_us = 20000,
|
||||
.btn_state = encoderButtonState
|
||||
};
|
||||
|
||||
|
||||
|
||||
//==================================
|
||||
//========== encoder_init ==========
|
||||
//==================================
|
||||
//initialize encoder
|
||||
void encoder_init(){
|
||||
encoderQueue = xQueueCreate(QUEUE_SIZE, sizeof(rotary_encoder_event_t));
|
||||
rotary_encoder_init(encoderQueue);
|
||||
rotary_encoder_add(&encoderConfig);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==================================
|
||||
//====== task_encoderExample =======
|
||||
//==================================
|
||||
//receive and handle all available encoder events
|
||||
void task_encoderExample(void *arg) {
|
||||
static rotary_encoder_event_t ev; //store event data
|
||||
while (1) {
|
||||
if (xQueueReceive(encoderQueue, &ev, portMAX_DELAY)) {
|
||||
//log enocder events
|
||||
switch (ev.type){
|
||||
case RE_ET_CHANGED:
|
||||
ESP_LOGI(TAG, "Event type: RE_ET_CHANGED, diff: %d", ev.diff);
|
||||
break;
|
||||
case RE_ET_BTN_PRESSED:
|
||||
ESP_LOGI(TAG, "Button pressed");
|
||||
break;
|
||||
case RE_ET_BTN_RELEASED:
|
||||
ESP_LOGI(TAG, "Button released");
|
||||
break;
|
||||
case RE_ET_BTN_CLICKED:
|
||||
ESP_LOGI(TAG, "Button clicked");
|
||||
break;
|
||||
case RE_ET_BTN_LONG_PRESSED:
|
||||
ESP_LOGI(TAG, "Button long-pressed");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unknown event type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
board_single/main/encoder.hpp
Normal file
20
board_single/main/encoder.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
extern "C" {
|
||||
#include "freertos/FreeRTOS.h" // FreeRTOS related headers
|
||||
#include "freertos/task.h"
|
||||
#include "encoder.h"
|
||||
}
|
||||
|
||||
//config
|
||||
#define QUEUE_SIZE 10
|
||||
#define PIN_A GPIO_NUM_25
|
||||
#define PIN_B GPIO_NUM_26
|
||||
#define PIN_BUTTON GPIO_NUM_27
|
||||
|
||||
//global encoder queue
|
||||
extern QueueHandle_t encoderQueue;
|
||||
|
||||
//init encoder with config in encoder.cpp
|
||||
void encoder_init();
|
||||
|
||||
//task that handles encoder events
|
||||
void task_encoderExample(void *arg);
|
@ -29,6 +29,7 @@ extern "C"
|
||||
#include "uart_common.hpp"
|
||||
|
||||
#include "display.hpp"
|
||||
#include "encoder.hpp"
|
||||
|
||||
//tag for logging
|
||||
static const char * TAG = "main";
|
||||
@ -155,6 +156,7 @@ void setLoglevels(void){
|
||||
//esp_log_level_set("current-sensors", ESP_LOG_INFO);
|
||||
//esp_log_level_set("speedSensor", ESP_LOG_INFO);
|
||||
esp_log_level_set("chair-adjustment", ESP_LOG_INFO);
|
||||
esp_log_level_set("menu", ESP_LOG_INFO);
|
||||
}
|
||||
|
||||
|
||||
@ -173,6 +175,11 @@ extern "C" void app_main(void) {
|
||||
//---- define log levels ----
|
||||
setLoglevels();
|
||||
|
||||
// init encoder
|
||||
//--- initialize encoder ---
|
||||
encoder_init();
|
||||
// now global encoderQueue providing all encoder events is available
|
||||
|
||||
//----------------------------------------------
|
||||
//--- create task for controlling the motors ---
|
||||
//----------------------------------------------
|
||||
@ -194,7 +201,11 @@ extern "C" void app_main(void) {
|
||||
//--- create task for button ---
|
||||
//------------------------------
|
||||
//task that evaluates and processes the button input and runs the configured commands
|
||||
#define MENU_TEST
|
||||
//currently disabled due to using button/encoder for testing the menu
|
||||
#ifndef MENU_TEST
|
||||
xTaskCreate(&task_button, "task_button", 4096, NULL, 4, NULL);
|
||||
#endif
|
||||
|
||||
//-----------------------------------
|
||||
//--- create task for fan control ---
|
||||
|
307
board_single/main/menu.cpp
Normal file
307
board_single/main/menu.cpp
Normal file
@ -0,0 +1,307 @@
|
||||
extern "C"{
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "ssd1306.h"
|
||||
}
|
||||
|
||||
#include "menu.hpp"
|
||||
#include "encoder.hpp"
|
||||
|
||||
|
||||
//--- variables ---
|
||||
static const char *TAG = "menu";
|
||||
static menuState_t menuState = MAIN_MENU;
|
||||
static int value = 0;
|
||||
|
||||
|
||||
|
||||
//================================
|
||||
//===== CONFIGURE MENU ITEMS =====
|
||||
//================================
|
||||
// note: when line4 * and line5 * are empty the value is printed large
|
||||
|
||||
void maxDuty_action(int value)
|
||||
{
|
||||
//TODO actually store the value
|
||||
ESP_LOGW(TAG, "set max duty to %d", value);
|
||||
}
|
||||
|
||||
int maxDuty_currentValue()
|
||||
{
|
||||
//TODO get real current value
|
||||
return 84;
|
||||
}
|
||||
|
||||
menuItem_t item_first = {
|
||||
maxDuty_action, // function action
|
||||
maxDuty_currentValue,
|
||||
-255, // valueMin
|
||||
255, // valueMAx
|
||||
2, // valueIncrement
|
||||
"first item", // title
|
||||
"line 1 - above", // line1 (above value)
|
||||
"line 2 - above", // line2 (above value)
|
||||
"line 4 - below", // line4 * (below value)
|
||||
"line 5 - below", // line5 *
|
||||
"line 6 - below", // line6
|
||||
"line 7 - last", // line7
|
||||
};
|
||||
|
||||
menuItem_t item_maxDuty = {
|
||||
maxDuty_action, // function action
|
||||
maxDuty_currentValue,
|
||||
1, // valueMin
|
||||
99, // valueMAx
|
||||
1, // valueIncrement
|
||||
"set max duty", // title
|
||||
"", // line1 (above value)
|
||||
" set max-duty: ", // line2 (above value)
|
||||
"", // line4 * (below value)
|
||||
"", // line5 *
|
||||
" 1-99 ", // line6
|
||||
" percent ", // line7
|
||||
};
|
||||
|
||||
menuItem_t item_next = {
|
||||
maxDuty_action, // function action
|
||||
maxDuty_currentValue,
|
||||
-500, // valueMin
|
||||
4500, // valueMAx
|
||||
50, // valueIncrement
|
||||
"set large number", // title
|
||||
"line 1 - above", // line1 (above value)
|
||||
"line 2 - above", // line2 (above value)
|
||||
"", // line4 * (below value)
|
||||
"", // line5 *
|
||||
"line 6 - below", // line6
|
||||
"line 7 - last", // line7
|
||||
|
||||
};
|
||||
|
||||
menuItem_t item_last = {
|
||||
maxDuty_action, // function action
|
||||
maxDuty_currentValue,
|
||||
0, // valueMin
|
||||
100, // valueMAx
|
||||
5, // valueIncrement
|
||||
"last item", // title
|
||||
"", // line1 (above value)
|
||||
"", // line2 (above value)
|
||||
"", // line4 * (below value)
|
||||
"", // line5 *
|
||||
"", // line6
|
||||
"", // line7
|
||||
};
|
||||
|
||||
//store all configured menu items in one array
|
||||
menuItem_t menuItems[] = {item_first, item_maxDuty, item_next, item_last};
|
||||
int itemCount = 4;
|
||||
|
||||
|
||||
|
||||
|
||||
//--------------------------
|
||||
//------ showItemList ------
|
||||
//--------------------------
|
||||
#define SELECTED_ITEM_LINE 4
|
||||
#define FIRST_ITEM_LINE 1
|
||||
#define LAST_ITEM_LINE 7
|
||||
void showItemList(SSD1306_t *display, int selectedItem)
|
||||
{
|
||||
//--- variables ---
|
||||
char buf[20];
|
||||
int len;
|
||||
|
||||
//-- show title line --
|
||||
len = snprintf(buf, 19, " --- menu --- ");
|
||||
ssd1306_display_text(display, 0, buf, len, true);
|
||||
|
||||
//-- show item list --
|
||||
for (int line = FIRST_ITEM_LINE; line <= LAST_ITEM_LINE; line++)
|
||||
{ // loop through all lines
|
||||
int printItemIndex = selectedItem - SELECTED_ITEM_LINE + line;
|
||||
// TODO: when reaching end of items, instead of showing "empty" change position of selected item to still use the entire screen
|
||||
if (printItemIndex < 0 || printItemIndex >= itemCount) // out of range of available items
|
||||
{
|
||||
len = snprintf(buf, 20, " -- empty -- ");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (printItemIndex == selectedItem)
|
||||
{
|
||||
// add '> <' when selected item
|
||||
len = snprintf(buf, 20, "> %-13s<", menuItems[printItemIndex].title);
|
||||
}
|
||||
else
|
||||
{
|
||||
len = snprintf(buf, 20, "%-16s", menuItems[printItemIndex].title);
|
||||
}
|
||||
}
|
||||
// update display line
|
||||
ssd1306_display_text(display, line, buf, len, line == SELECTED_ITEM_LINE);
|
||||
// logging
|
||||
ESP_LOGD(TAG, "showItemList: loop - line=%d, item=%d, (selected=%d '%s')", line, printItemIndex, selectedItem, menuItems[selectedItem].title);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//---------------------------
|
||||
//----- showValueSelect -----
|
||||
//---------------------------
|
||||
//TODO show previous value in one line?
|
||||
void showValueSelect(SSD1306_t *display, int selectedItem)
|
||||
{
|
||||
//--- variables ---
|
||||
char buf[20];
|
||||
int len;
|
||||
|
||||
//-- show title line --
|
||||
len = snprintf(buf, 19, " -- set value -- ");
|
||||
ssd1306_display_text(display, 0, buf, len, true);
|
||||
|
||||
//-- show text above value --
|
||||
len = snprintf(buf, 20, "%-16s", menuItems[selectedItem].line1);
|
||||
ssd1306_display_text(display, 1, buf, len, false);
|
||||
len = snprintf(buf, 20, "%-16s", menuItems[selectedItem].line2);
|
||||
ssd1306_display_text(display, 2, buf, len, false);
|
||||
|
||||
//-- show value and other configured lines --
|
||||
// print value large, if 2 description lines are empty
|
||||
if (strlen(menuItems[selectedItem].line4) == 0 && strlen(menuItems[selectedItem].line5) == 0 )
|
||||
{
|
||||
//print large value + line5 and line6
|
||||
len = snprintf(buf, 20, " %d ", value);
|
||||
ssd1306_display_text_x3(display, 3, buf, len, false);
|
||||
len = snprintf(buf, 20, "%-16s", menuItems[selectedItem].line6);
|
||||
ssd1306_display_text(display, 6, buf, len, false);
|
||||
len = snprintf(buf, 20, "%-16s", menuItems[selectedItem].line7);
|
||||
ssd1306_display_text(display, 7, buf, len, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// print value centered
|
||||
int numDigits = snprintf(NULL, 0, "%d", value);
|
||||
int numSpaces = (16 - numDigits) / 2;
|
||||
snprintf(buf, sizeof(buf), "%*s%d%*s", numSpaces, "", value, 16 - numSpaces - numDigits, "");
|
||||
ESP_LOGD(TAG, "showValueSelect: center number - value=%d, needed-spaces=%d, resulted-string='%s'", value, numSpaces, buf);
|
||||
ssd1306_display_text(display, 3, buf, len, false);
|
||||
// print description lines 4 to 7
|
||||
len = snprintf(buf, 20, "%-16s", menuItems[selectedItem].line4);
|
||||
ssd1306_display_text(display, 4, buf, len, false);
|
||||
len = snprintf(buf, 20, "%-16s", menuItems[selectedItem].line5);
|
||||
ssd1306_display_text(display, 5, buf, len, false);
|
||||
len = snprintf(buf, 20, "%-16s", menuItems[selectedItem].line6);
|
||||
ssd1306_display_text(display, 6, buf, len, false);
|
||||
len = snprintf(buf, 20, "%-16s", menuItems[selectedItem].line7);
|
||||
ssd1306_display_text(display, 7, buf, len, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//========================
|
||||
//====== handleMenu ======
|
||||
//========================
|
||||
//controls menu with encoder input and displays the text on oled display
|
||||
//function is repeatedly called when in menu state
|
||||
void handleMenu(SSD1306_t *display)
|
||||
{
|
||||
static int selectedItem = 0;
|
||||
rotary_encoder_event_t event; // store event data
|
||||
|
||||
switch (menuState)
|
||||
{
|
||||
//-------------------------
|
||||
//---- State MAIN MENU ----
|
||||
//-------------------------
|
||||
case MAIN_MENU:
|
||||
// update display
|
||||
showItemList(display, selectedItem); // shows list of items with currently selected one on display
|
||||
// wait for encoder event
|
||||
if (xQueueReceive(encoderQueue, &event, portMAX_DELAY))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case RE_ET_CHANGED:
|
||||
//--- scroll in list ---
|
||||
if (event.diff < 0)
|
||||
{
|
||||
if (selectedItem != itemCount - 1)
|
||||
selectedItem++;
|
||||
ESP_LOGD(TAG, "showing next item: %d '%s'", selectedItem, menuItems[selectedItem].title);
|
||||
//note: display will update at start of next run
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selectedItem != 0)
|
||||
selectedItem--;
|
||||
ESP_LOGD(TAG, "showing previous item: %d '%s'", selectedItem, menuItems[selectedItem].title);
|
||||
//note: display will update at start of next run
|
||||
}
|
||||
break;
|
||||
|
||||
case RE_ET_BTN_PRESSED:
|
||||
//--- switch to edit value page ---
|
||||
ESP_LOGI(TAG, "Button pressed - switching to state SET_VALUE");
|
||||
// change state (menu to set value)
|
||||
menuState = SET_VALUE;
|
||||
// get currently configured value
|
||||
value = menuItems[selectedItem].currentValue();
|
||||
// clear display
|
||||
ssd1306_clear_screen(display, false);
|
||||
break;
|
||||
|
||||
case RE_ET_BTN_RELEASED:
|
||||
case RE_ET_BTN_CLICKED:
|
||||
case RE_ET_BTN_LONG_PRESSED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
//-------------------------
|
||||
//---- State SET VALUE ----
|
||||
//-------------------------
|
||||
case SET_VALUE:
|
||||
// wait for encoder event
|
||||
if (xQueueReceive(encoderQueue, &event, portMAX_DELAY))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case RE_ET_CHANGED:
|
||||
//-- change value --
|
||||
// increment value
|
||||
if (event.diff < 0)
|
||||
value += menuItems[selectedItem].valueIncrement;
|
||||
else
|
||||
value -= menuItems[selectedItem].valueIncrement;
|
||||
// limit to min/max range
|
||||
if (value > menuItems[selectedItem].valueMax)
|
||||
value = menuItems[selectedItem].valueMax;
|
||||
if (value < menuItems[selectedItem].valueMin)
|
||||
value = menuItems[selectedItem].valueMin;
|
||||
break;
|
||||
case RE_ET_BTN_PRESSED:
|
||||
//-- apply value --
|
||||
ESP_LOGI(TAG, "Button pressed - running action function with value=%d for item '%s'", value, menuItems[selectedItem].title);
|
||||
menuItems[selectedItem].action(value);
|
||||
menuState = MAIN_MENU;
|
||||
break;
|
||||
case RE_ET_BTN_RELEASED:
|
||||
case RE_ET_BTN_CLICKED:
|
||||
case RE_ET_BTN_LONG_PRESSED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
showValueSelect(display, selectedItem);
|
||||
break;
|
||||
}
|
||||
}
|
32
board_single/main/menu.hpp
Normal file
32
board_single/main/menu.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "display.hpp"
|
||||
|
||||
|
||||
//--- menuState_t ---
|
||||
// modes the menu can be in
|
||||
typedef enum {
|
||||
MAIN_MENU = 0,
|
||||
SET_VALUE
|
||||
} menuState_t;
|
||||
|
||||
|
||||
//--- menuItem_t ---
|
||||
// struct describes one menu element (all defined in menu.cpp)
|
||||
typedef struct {
|
||||
void (*action)(int); // pointer to function run when confirmed
|
||||
int (*currentValue)(); // pointer to function to get currently configured value
|
||||
int valueMin; // min allowed value
|
||||
int valueMax; // max allowed value
|
||||
int valueIncrement; // amount changed at one encoder tick (+/-)
|
||||
const char title[17]; // shown in list
|
||||
const char line1[17]; // above value
|
||||
const char line2[17]; // above value
|
||||
const char line4[17]; // below value *
|
||||
const char line5[17]; // below value *
|
||||
const char line6[17]; // below value
|
||||
const char line7[17]; // below value
|
||||
} menuItem_t;
|
||||
|
||||
|
||||
void handleMenu(SSD1306_t * display);
|
Loading…
x
Reference in New Issue
Block a user