Message |
|
Related to my other post, I am currently looking into handling the button interrupts on my own and passing them to the corresponding menuMgr function.
I am having trouble with menuMgr.valueChanged(int value), however. This is my code, which (obviously) is not working:
void onButtonPressedClb(uint8_t pin, bool heldDown) {
switch (pin) {
case UP_BUTTON_GPIO:
menuMgr.valueChanged(-1);
break;
case DOWN_BUTTON_GPIO:
menuMgr.valueChanged(+1);
break;
case LEFT_BUTTON_GPIO:
if(!heldDown) menuMgr.performDirectionMove(true);
break;
case RIGHT_BUTTON_GPIO:
if(!heldDown) menuMgr.performDirectionMove(false);
break;
case A_BUTTON_GPIO:
menuMgr.onMenuSelect(heldDown);
break;
case B_BUTTON_GPIO:
if(!heldDown) menuMgr.performDirectionMove(true);
break;
}
}
After reading through the source code of tcmenu and IoAbstraction, I now understand why simply passing -1 and 1 as values does not work. My idea then was to get the current state of the menu and add or substract 1, but I cannot find a way to get/read the current state of the menu.
Do I have to setup a dummy encoder and manipulate it in my callback? Is there no other way?
|
|
|
Thanks Dave!
Using isSwitchPressed works. I need it to flip through a couple of pages - for this use case the method is enough.
Downside: As expected, sometimes it misses a button press (for me roughly 1-2 presses out of 10). This happens whenever the button is pressed & released between the calls of the function referenced by takeOverDisplay. From a usability point of view, the user might think there is a hardware issue since the button press does not lead to any reaction and it needs to be pressed again.
Here is my code as reference for others looking for a similar functionality:
(Screen.h)
struct MyStruct {
int screenCounter;
int currentPage;
int numOfPages;
};
class Screen {
public:
static void showCustomPages(unsigned int encoderValue, RenderPressMode clicked);
private:
static MyStruct myStruct;
};
(Screen.cpp)
#include "Screen.h"
#include "tcmenu_menu.h" // or whatever your tcmenu header is called
MyStruct Screen::myStruct = {0,0,5};
void Screen::showCustomPages(unsigned int encoderValue, RenderPressMode clicked) {
if (myStruct.screenCounter == 0) {
// initial page of the custom screen.
myStruct.infoScreenCounter++;
myStruct.currentPage = 0;
// this example draws a footer with page number on a 240x240 screen
gfx.fillRect(0, 215, 240, 25, 0x4A69);
gfx.setTextColor(ST77XX_WHITE);
gfx.setCursor(20,232);
gfx.setFont(&FreeSans9pt7b);
gfx.print("Page: ");
gfx.print(myStruct.currentPage+1); // count starts at 0, human readable value should start at 1
gfx.print("/");
gfx.print(myStruct.numOfPages);
gfx.print("");
gfx.setCursor(140,232);
gfx.print("<A - Exit>");
}
if (switches.isSwitchPressed(RIGHT_BUTTON_GPIO) ) {
if (myStruct.numOfPages > myStruct.currentPage) {
myStruct.currentPage++;
// draw next page
// this example draws a footer with page number on a 240x240 screen
gfx.fillRect(0, 215, 240, 25, 0x4A69);
gfx.setTextColor(ST77XX_WHITE);
gfx.setCursor(20,232);
gfx.setFont(&FreeSans9pt7b);
gfx.print("Page: ");
gfx.print(myStruct.currentPage+1); // count starts at 0, human readable value should start at 1
gfx.print("/");
gfx.print(myStruct.numOfPages);
gfx.print("");
gfx.setCursor(140,232);
gfx.print("<A - Exit>");
}
}
if (switches.isSwitchPressed(LEFT_BUTTON_GPIO) ) {
if (myStruct.currentPage > 0) {
myStruct.currentPage--;
gfx.fillRect(0, 215, 240, 25, 0x4A69);
gfx.setTextColor(ST77XX_WHITE);
gfx.setCursor(20,232);
gfx.setFont(&FreeSans9pt7b);
gfx.print("Page: ");
gfx.print(myStruct.currentPage+1); // count starts at 0, human readable value should start at 1
gfx.print("/");
gfx.print(myStruct.numOfPages);
gfx.print("");
gfx.setCursor(140,232);
gfx.print("<A - Exit>");
}
}
if (clicked) {
myStruct.infoScreenCounter = 0;
renderer.giveBackDisplay();
}
}
|
|
|
Hi,
I am back with a new question.
I am using the renderer.takeOverDisplay function to display my own screen which works great. According to the API, the method which is called by takeOverDisplay needs to have the following parameters:
unsigned int encoderValue, RenderPressMode clicked
My issue here is that I do not use a rotary encoder but a push button setup (up/down/left/right/a/b). In case I only need a single button press to return the control to tcMenu, this works. For one custom screen, however, I need the other buttons as well. Hence my question: Is there a way to read the other button states instead of the encoder value when the method is called? In case not, is there a solution/workaround which I am overseeing at the moment?
|
|
|
Hey,
I got a new prototype of my hardware in and had to adjust the GPIO numbers for my buttons on my old project. After I recompiled it, something odd happened. The UP and DOWN buttons were switched. First I checked my wiring (since it is a new PCB and could have a design error) but everything seemed ok. After going through the code and API I stumbeled onto an issue with the initForUpDownOk parameters. Even though it is called UP_DOWN_OK, it takes its parameters as DOWN_UP_OK. The GUI, however, generates code in the fashion of UP_DOWN_OK which resulted in my buttons being swapped.
Here is a screenshoot from the designer:
and the generated code:
menuMgr.initForUpDownOk(&renderer, &menuArsenal, UP_BUTTON_GPIO, DOWN_BUTTON_GPIO, A_BUTTON_GPIO);
(the GPIO definitions are in a separate config file)
The API for initForUpDownOk however states that DOWN comes before UP:
void MenuManager::initForUpDownOk ( MenuRenderer * renderer,
MenuItem * root,
pinid_t downPin,
pinid_t upPin,
pinid_t okPin
)
Is this a bug or am I doing something wrong?
|
|
|
Thanks!
I will put it on my ToDo list. Have to implement some other stuff beforehand. In case I succeed, I will let you know.
|
|
|
Thank you for your feedback!
I removed setResetIntervalTimeSeconds and setResetCallback from the if(clicked block) as you suggested. Running the code again, it seems that the callback is never called again.
Well, it only seems. After debugging a bit further I owe you an apology. Nothing wrong with your code. The callback is called again. My counter, however, issn't rest to 0 - which is why the splash screen isn't shown again. I have takeOverMenuCounter = 0 in my if(clicked) block but it seems that I am messing something up with all the static functions and variables.
So, sorry for bothering you and thanks for looking into it anyway!
---
Follow up question: Does anyone know a good site / tutorial which gives me some insight on how to use static methods & variables of a class properly? Any suggestions how to fix my code are, of course, welcome as well.
EDIT (a few minutes later):
Well, here we go. Its facepalm time.
Have a look at my code and closely watch the takeOverMenuCounter:
void Screen::screenSaverCallback(unsigned int encoderValue, RenderPressMode clicked) {
// in case this is the first call to the function, display the screensaver splash screen
if (takeOverMenuCounter == 0)
showScreenSaver();
// in case the A button was pressed, give control back to tcmenu and restart the screensaver intervall timer
if(clicked) {
renderer.giveBackDisplay();
takeOverMenuCounter = 0;
}
takeOverMenuCounter++;
}
Did you notice that I increase it imidiatly after it is set to zero? That means, whenever the callback is called a second time, the counter is already at 1. Hence the splash screen not appearing again. Here ist the correct code (in case someone is looking for a similar functionality) and it is working like a charm:
void Screen::screenSaverCallback(unsigned int encoderValue, RenderPressMode clicked) {
// in case this is the first call to the function, display the screensaver splash screen
if (takeOverMenuCounter == 0) {
showScreenSaver();
takeOverMenuCounter++;
}
// in case the A button was pressed, give control back to tcmenu
if(clicked) {
takeOverMenuCounter = 0;
renderer.giveBackDisplay();
}
}
|
|
|
Hi again,
here goes my second question. I am not sure though, whether it belongs to the tcMenu or IoAbstraction subform (do not hesitate to move the post accordingly).
I am using tcMenu with regular pushbuttons - which is working great. What I would like, however, is, in addition to navigating the menu with the buttons, some sort of history which buttons have been pressed. I want this in order to implement some sort of 'cheat code' (e.g., KONAMI code). What I would need for this is a callback whenever a button was pressed. This would enable me to compile my own history of all pressed buttons. Is there a way to do this?
I browsed the API references of TcMenu but couldn't find anything. IO Abstraction seems to have what I am looking for (the callback given to the addSwitch method). Since I am using tcMenu to setup my buttons, however, I am not sure whether I can still register a callback that way in addition to what tcMenu (presumedly) internally already registers. Is that possible or would it break things?
Thanks!
|
|
|
Hi,
I have a question regarding renderer.setResetCallback. I want to use it in order to implement some kind of inactivity splash screen (like a screensaver). So, whenever the user is not pushing a button for a pre specified time, I want the splash screen to appear. Once a button is pressed, the plash screen shall disappear and the inactivity timer / counter shall be restarted. My problem is that the first appearance of the splash screen works fine as well as letting it disappear by a press of a button. The thing is, it never reappears in case the user is inactive subsequently. I am probably misunderstanding the logic behind the render reset and giveBackDisplay. Can you give me hint what I am doing wrong?
Here is my code.
In setup() of main.cpp
// register screensaver
renderer.setResetIntervalTimeSeconds(SCREENSAVER_START_INTERVAL);
renderer.setResetCallback([] {
renderer.takeOverDisplay(Screen::screenSaverCallback);
});
and the code of the callback method (within the Screen class):
int Screen::takeOverMenuCounter = 0;
void Screen::screenSaverCallback(unsigned int encoderValue, RenderPressMode clicked) {
// in case this is the first call to the function, display the screensaver splash screen
if (Screen::takeOverMenuCounter == 0)
showScreenSaver();
// in case the A button was pressed, give control back to tcmenu and restart the screensaver intervall timer
if(clicked) {
renderer.giveBackDisplay();
Screen::takeOverMenuCounter = 0;
renderer.setResetIntervalTimeSeconds(SCREENSAVER_START_INTERVAL);
renderer.setResetCallback([] {
renderer.takeOverDisplay(Screen::screenSaverCallback);
});
}
Screen::takeOverMenuCounter++;
}
/*
* Displays a splash screen as screensaver. Image is loaded from SPIFFS as bmp
*/
void Screen::showScreenSaver() {
gfx.fillScreen(ST77XX_BLACK);
gfx.setTextColor(ST77XX_BLACK);
SPIFFS_ImageReader reader;
reader.drawBMP("/system/splashscreen.bmp",gfx,0,0);
gfx.setCursor(30, 225);
gfx.println("<press A to continue>");
}
I also tried the same code without re-setting the timer interval within the callback (line 12) - unfortunately with the same result.
As background info (but probably doesn't matter):
I am using tcmenu on an ESP32 with an ST7789 attached to it.
---
Offtopic:
Since this is my first post some additional words. TcMenu really rocks! It takes a bit to figure out all of its functionality but it is really a time saver in order to implement nice menus. Thanks a lot for having written TcMenu in the first place and maintaining it with high quality documentation!
|
|
|
|
|