spirik / gem Goto Github PK
View Code? Open in Web Editor NEWGood Enough Menu for Arduino
License: GNU Lesser General Public License v3.0
Good Enough Menu for Arduino
License: GNU Lesser General Public License v3.0
Can i control your lib with keypad 4x4 ? I'm newbie.
Just making sure I'm not missing something - the sprites for things like leftArrow
, rightArrow
, and selectArrows
are fixed at 6x8 pixels?
My C++ is a little rusty - but I'm thinking that since the sprites are defined as constants, outside of the GEM class (I think they have to be that way, because they have to be PROGMEM
?), the only way I can replace them is by editing GEM.cpp (and then not upgrading GEM - or reapplying the changes when I upgrade)? The PROGMEM
requirement means they can't be replaced on the fly, either.
I'm using a font that's 14x24 pixels (Adafruit GFX FreeMono12pt7b) - the existing sprites are really small on the screen, so I'd like to replace them with a bigger version of the same sprite.
/libraries/GEM/src/GEMItem.cpp: In constructor 'GEMItem::GEMItem(const char*, byte&, GEMSelect&, void (*)(GEMCallbackData))':
/libraries/GEM/src/GEMItem.cpp:96:38: error: either all initializer clauses should be designated or none of them should be
96 | , callbackData{ .pMenuItem = this, { 0 } }
My demo Project https://wokwi.com/projects/402474219113695233
Your menu looks great.
Please, add support for these two major GFX libraries. Or, at least, for u8g2. Thanks in advance.
When choosing a variable item I want it to be limited so the user won't be able to increase it more than a maximum or decrease it less than the minimum, how should I implement it?
I used the constrain function but it doesn't work.
Hello Alexander,
In my project, I use a check box "Use motor" with a lot of parameter for my motor for exemple "Option 1",....
When "Use motor" is don't check, i use the read only function to lock the parameter but i will be more clean to don't see these parameter with a visible function.
example :
MyMenuItem.visible(false);
....
For now, only GEM_VAL_INTEGER and GEM_VAL_BYTE are supported. What about adding GEM_VAL_FLOAT?
It would also be very handy to have a GEM_VAL_HEX type, showing int/byte values in hexa, but it may be an option for those 2 types; don't know what is better...
I think the types management could be refactored, to support new types, with there specific edition way. For example, I'm thinking to a GEM_VAL_IP, to edit an IP value. But I guess it is a lot of work.
When there aren't enough components around to build a prototype, maybe this is a good way to test.
I think there should be a possibility to disable splash screen and/or to change display time of it. Something like this (pseudocode):
void GEM::init(bool show_splash, uint32_t delay_ms)
{
init_stuff_here();
if(show_splash)
{
splash_XBMP_display();
if(_enableVersion)
{
delay(delay_ms/2);
version_display();
delay(delay_ms/2);
}
else
{
delay(delay_ms);
}
}
clear_stuff_here();
}
BTW: using hard-coded delay()
or any do_nothing_for_some_time
inside library functions are strongly discouraged in embedded software unless they are explicitly named (e.g. init_splash_wait
), it could break the whole app, especially those not RTOS-based!
Hi, im' working on a rather big menu (as for today, about 90 GEMPages, 450 GEMItems), & having issues with ESP32 memory for other tasks. some of that items are references to the same object in struct, but execute different methods ( hence they go on different pages) . if I bind a GEMItem to certain page, i cannot bind it to a different one afterwards.
any hints about this? is it a known limitation? thanks!
Howdy. First of all, this lib is very nice, seems easy to use and I couldn't find many others that can use SPI 128x64 display.
But back on the topic. I wanted to use it in my project with uses quite a lot of pins rn on its own. Soo reusing 7 just for menu when i have already 16 keys on 4x4 keyboard, thats plugged into i2c multiplexer, soo actually it uses 2 pins of micro controler.
I tought using 2 rotary encoders will do the trick, with they didn't becouse of how they work.
Soo i tought if there is a way of doing it, or easy way to implement those funcionalities directlly. Mebe by using something like functions insead of pins. like instead of pin 6 being home/cancel, calling GEM_cancel()
would do same.
Thanks in advance.
Hello,
I have a display with only 3 buttons (up, down, enter). I would like to use the library to create menus, but to do so, I have to switch the key behaviour when the menu is in editing mode or in navigation mode.
Is it possible to have the feedback of the menu state to switch behaviour?
Thanks
Hi. i'm trying to call a function passing a parameter to it, depending on the button i'm clicking, so as not to have to create as many functions as buttons i have. All without success. Is it posible?
Hello,
I'm using GEM in combination with a rotaryEncoder on a ESP32 board.
I've created a main menu with some options and cyclically call the code below:
`
if(menu.readyForKey()) {
byte keyCode = encoder_status;
menu.registerKeyPress(keyCode);
}
The encoder_status is initiated = GEM_KEY_NONE.
Whenever a change in the encoder value is detected, I calculate the delta and set the value of encoder_status accordingly:
Unfortunately, every time menu.registerKeyPress(keyCode); is called with an input value other than GEM_KEY_NONE, it causes my board to restart with the following trace below. Any tip what could be happening?
Thank you very much for your time.
`
Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.
Core 1 register dump:
PC : 0x400014dc PS : 0x00060e30 A0 : 0x800d48e4 A1 : 0x3ffb1e80
A2 : 0x800d3fd7 A3 : 0x800d3fd3 A4 : 0x000000ff A5 : 0x0000ff00
A6 : 0x00ff0000 A7 : 0xff000000 A8 : 0x000000aa A9 : 0x00000040
A10 : 0x00000001 A11 : 0x00000000 A12 : 0x00000000 A13 : 0x0000ffff
A14 : 0x0000ffff A15 : 0x3ffb0060 SAR : 0x00000013 EXCCAUSE: 0x0000001c
EXCVADDR: 0x800d3fd7 LBEG : 0x4000c46c LEND : 0x4000c477 LCOUNT : 0x00000000
ELF file SHA256: 0000000000000000
Backtrace: 0x400014dc:0x3ffb1e80 0x400d48e1:0x3ffb1e90 0x400d4901:0x3ffb1eb0 0x400d339c:0x3ffb1ed0 0x400d39ad:0x3ffb1ef0 0x400d39ee:0x3ffb1f10 0x400d3f7a:0x3ffb1f30 0x400d3fd4:0x3ffb1f50 0x400d0ac1:0x3ffb1f70 0x400d0773:0x3ffb1f90 0x400d4de1:0x3ffb1fb0 0x40089a2e:0x3ffb1fd0
Rebooting...
ets Jul 29 2019 12:21:46
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10944
load:0x40080400,len:6388
entry 0x400806b4
`
I copied example and just changed
U8G2_KS0108_128X64_1 u8g2(U8G2_R0, 8, 9, 10, 11, 12, 13, 18, 19, /*enable=*/ A0, /*dc=*/ A1, /*cs0=*/ A3, /*cs1=*/ A2, /*cs2=*/ U8X8_PIN_NONE, /* reset=*/ U8X8_PIN_NONE); // Set R/W to low!
to
U8G2_SSD1327_MIDAS_128X128_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
Build with -D GEM_DISABLE_GLCD -D GEM_DISABLE_ADAFRUIT_GFX
, but it cause an esp32 flash error when uploaded and rebooted, tried two pieces but end with the same result.
rst:0x10 (RTCWDT_RTC_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
Then can not use esptool.py to write or erase flash anymore.
WARNING: Failed to communicate with the flash chip, read/write operations will fail. Try checking the chip connections or removing any other hardware connected to IOs.
A fatal error occurred: Packet content transfer stopped (received 8 bytes)
I'm going to put some variables on the same pages and I want them to be limited, e.g. when setting variable X and pushing the next button it shouldn't be able to get lower than 2 and if so I want to show an error and go back to the page.
Hi there!
Would be really nice to have functional callbacks in menu items. I.e. like here. This is required to use capturing lambda functions or class instance members for menu callbacks.
Thanks!
Hi!
In my application, I plan to use the screen to display some informations (using u8g2). And, sometimes, I want to switch to GEM menu system to do some configuration and specific tasks, then go back to my own display.
So, I need to enable/disable the menu system on request.
How would you do that?
Thanks,
Now that we have this working on my Adafruit M0 and Featherwing OLED, I'm having trouble editing the number
value... If I set the highest digit ("-" by default) to 0 or blank, the number becomes 512. There doesn't seem to be a way to add the negative value back in except for changing the 5 to a "-", the 1 to a 5, the 2 to a 1, and adding a digit for the 2... or am I missing something? (I have tried adding buttons to the sketch, for instance, left
but it doesn't seem like you can move the cursor left to create an additional most significant digit. Thank you so much!
In file included from /Users/devnull/Documents/Arduino/libraries/GEM/src/GEM.h:40,
from /Users/devnull/Documents/Arduino/libraries/GEM/src/GEM.cpp:34:
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:197:25: error: section attribute not allowed for 'crlf'
197 | char const PROGMEM *crlf;
| ^~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:283:40: error: section attribute not allowed for 's'
283 | void putstr_P (const char PROGMEM *s);
| ~~~~~~~~~~~~~~~~~~~~^
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:340:42: error: section attribute not allowed for 'data'
340 | void write_P (const uint8_t PROGMEM *data, int length);
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:568:93: error: section attribute not allowed for 's'
568 | void setString_P(uint8_t posX, uint8_t posY, uint8_t justification, const char PROGMEM *s)
| ~~~~~~~~~~~~~~~~~~~~^
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:842:59: error: section attribute not allowed for 'sprite'
842 | void loadSprite_P (uint8_t id, const uint8_t PROGMEM *sprite)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:857:47: error: section attribute not allowed for 'sprite_pixels'
857 | const uint8_t PROGMEM *sprite_pixels)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:871:71: error: section attribute not allowed for 'sprite'
871 | void loadSprite_P (uint8_t id, int length, const uint8_t PROGMEM *sprite)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:931:43: error: section attribute not allowed for 'sprite'
931 | const uint8_t PROGMEM *sprite)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:948:59: error: section attribute not allowed for 'sprite_pixels'
948 | uint8_t height, const uint8_t PROGMEM *sprite_pixels)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:965:55: error: section attribute not allowed for 'sprite'
965 | int length, const uint8_t PROGMEM *sprite)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:990:62: error: section attribute not allowed for 'xylist'
990 | void drawPolygon_P (uint8_t mode, const uint8_t PROGMEM *xylist)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:1013:48: error: section attribute not allowed for 'xylist'
1013 | void drawPolygon_P (const uint8_t PROGMEM *xylist)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:1039:60: error: section attribute not allowed for 'xylist'
1039 | void drawLines_P (uint8_t mode, const uint8_t PROGMEM *xylist)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/Users/devnull/Documents/Arduino/libraries/AltSerialGraphicLCD/AltSerialGraphicLCD.h:1062:46: error: section attribute not allowed for 'xylist'
1062 | void drawLines_P (const uint8_t PROGMEM *xylist)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
In file included from /Users/devnull/Documents/Arduino/libraries/GEM/src/GEM.cpp:34:
/Users/devnull/Documents/Arduino/libraries/GEM/src/GEM.h:97:43: error: section attribute not allowed for 'sprite'
97 | void setSplash(const uint8_t PROGMEM *sprite); // Set custom sprite displayed as the splash screen when GEM is being initialized. Should be called before GEM::init().
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
/Users/devnull/Documents/Arduino/libraries/GEM/src/GEM.h:138:28: error: section attribute not allowed for '_splash'
138 | const uint8_t PROGMEM *_splash;
| ^~~~~~~
/Users/devnull/Documents/Arduino/libraries/GEM/src/GEM.cpp:123:44: error: section attribute not allowed for 'sprite'
123 | void GEM::setSplash(const uint8_t PROGMEM *sprite) {
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
exit status 1
Error compiling for board NodeMCU 1.0 (ESP-12E Module).
AltSerialGraphicLCD v1.38
Hello,
I put your new release (1.2.2) tonight and itoa is not define with my configuration (stm32 core).
i need to add this
`
#include <Arduino.h>
#include "GEM_u8g2.h"
#include <itoa.h>
#ifdef GEM_ENABLE_U8G2_VERSION
`
This release is perfect, with the possibility to change the resolution for float.
I use the setTitle for change the lang of the menu but it's impossble tu change the name of a pageitem !
a small video :
https://www.youtube.com/watch?v=-S_ZRFjv2ss
Great job and thankyou
I am able to run the example adafrute gfx library. I tried to update the GEM library but its only showing dots.
Hello Alexander,
It's possible to add the possibility from the main menu when i press cancel to launch a item ( i want to launch my main context without the need to scroll up and click ok).
Thanks.
What about:
Hi
Thanks for this absolute blast of a library! I really appreciate the extensive documentation.
I'm trying to make a menu with GEM_adafruit_gfx.h on this screen and want to display a value that automatically updates (e.g. the runtime millis()) as a MenuItem. My problem is now that the value does not update unless I press one of the buttons. I tried to call menu.drawMenu every certain time interval, but this leads to extensive blinking of the screen (i.e. going to black and then correctly displaying the Menu).
Obviously there must be a way to update the screen without blinking, as this happens when a key is pressed. I tried to understand what exactly happens when pressing a button, but could find nothing else but .drawMenu(). What am I missing?
How can I update the screen (or displayed values), without the blinking?
Cheers,
gerberli
Looks like nice library, well-designed and clean code!
However, you've selected the CC license for it. That license is not intended to apply to code. I believe it will work, but there are other more specific licenses, such as the GPL (which should be similar in terms with CC-SA, not sure about its attribution requirements). Did you intentionally choose CC, despite of this? Or should you perhaps reconsider the license?
When I want to setting only one or a a little attributes, it need to fill complete instance here. Do you want to support a set function like setMenuItemHeight
etc.?
before:
GEM_u8g2 menu(u8g2, /*menuPointerType_=*/GEM_POINTER_ROW,
/*menuItemsPerScreen_=*/GEM_ITEMS_COUNT_AUTO,
/*menuItemHeight_=*/12,
/*menuPageScreenTopOffset_=*/10, /*menuValuesLeftOffset_=*/86);
after:
GEM_u8g2 menu(u8g2);
menu.setMenuItemHeight(12);
So, I'm working on an incredibly silly mp3 player, and things are going fairly smooth.
I've set up a menuContextLoop which displays a "now playing screen". It's a static screen for now, so I didn't notice, but the loop is only triggering when a button is pressed, or the rotary encoder turns. I'm working off the "Party Hard" sketch, and it looks like I've set it up the same way, but it's just not running continuously. I have a progress bar, for example, that should update every second, but it never fires unless I turn the dial. The rest of the program works, but without the loop triggering as expected, I can't check if playback has stopped.
#include <Arduino.h>
#include <string.h>
#include <FastLED.h>
#include <U8g2lib.h>
#include <DYPlayerArduino.h>
#include <SD.h>
#include "bitmaps.h"
#include <GEM_u8g2.h>
#include "gemproxy.h"
#include "main.h"
#include <Encoder.h>
#include <Automaton.h>
#include "healzone.h"
#include "sg02.h"
#define HAS_HARDWARE_SERIAL
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
#define NUM_LEDS 16
#define LED_DATA_PIN 4
#define BTN_A 20
#define BTN_B 19
#define BEEPER 23
#define ENC_A 5
#define ENC_B 6
#define ENC_BTN 22
#define DY_TX 0 // on the RX1 pin
#define DY_RX 1
#define DY_BUSY 2
#define SD_CS 10
#define SD_MOSI 11
#define SD_SCK 13
#define SD_MISO 12
#define LCD_CS 9
#define LCD_CLOCK 14
#define LCD_DATA 7
#define GEM_DISABLE_GLCD
#define GEM_DISABLE_ADAFRUIT_GFX
#define ENC_SLOWDOWN 2
using namespace DY;
U8G2_ST7920_128X64_F_SW_SPI lcd(U8G2_R0, LCD_CLOCK, LCD_DATA, LCD_CS);
Player mp3(&Serial1);
Encoder dial(5,6);
Atm_button dial_btn;
Atm_button a_btn;
Atm_button b_btn;
CRGBArray<NUM_LEDS> leds;
CRGBSet bottombar(leds(0,7));
CRGBSet topbar(leds(8,15));
void lcd_prepare(void) {
lcd.setFont(u8g2_font_6x10_tf);
lcd.setFontRefHeightExtendedText();
lcd.setDrawColor(1);
lcd.setFontPosTop();
lcd.setFontDirection(0);
}
GEMProxy menu = GEMProxy(lcd, GEM_POINTER_DASH);
bool playback_state = false;
uint8_t next_song;
uint8_t current_song;
uint8_t ghue = 0;
long next_frame;
long oldposition = -999;
void songContextEnter();
void songContextExit();
void songContextLoop();
void playSong(GEMCallbackData);
void bpm()
{
if(playback_state) {
// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
uint8_t BeatsPerMinute = 64;
CRGBPalette16 palette = RainbowColors_p;
uint8_t beat = beatsin8( BeatsPerMinute, 16, 255);
for( int i = 0; i <8; i++) { //9948
topbar[i] = ColorFromPalette(palette, ghue+(i*20), beat-ghue+(i*30));
bottombar[i] = ColorFromPalette(palette, ghue+(i*25), beat-ghue+(i*15));
}
} else {
leds.fadeToBlackBy(64);
}
}
song songlist[] = {
{"Turbo Killer", "Carpenter Brut", 209000, 144},
{"Anarchy Road", "Carpenter Brut", 213000, 108},
{"This FFFire", "Edgerunners OST", 224000, 144},
{"I Really Wan..", "Edgerunners OST", 246000, 123},
{"Clutch", "STRLGHT", 157000, 63},
{"U Got That", "Halogen", 172000, 123},
{"Full Bodied", "GHOST DATA",248000, 117},
{"Projekt Melody", "GHOST DATA", 234000, 161},
{"KNEEL", "Moxi Floxi", 192000, 129}
};
void check_playback() {
int busy = digitalRead(DY_BUSY);
playback_state = !busy;
}
void progress_bar(uint8_t max, uint8_t current, uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t rounded = 0) {
// draws a progress bar
// bar is 2px inset of the W/H to look good - so 4 px smaller
uint8_t max_length = w-4;
uint8_t max_height = h-4;
uint8_t mapped = map(current, 0, max, 0, max_length);
if(rounded) {
lcd.drawRFrame(x,y,w,h,3);
lcd.drawRBox(x+2,y+2,mapped, max_height, 1);
} else {
lcd.drawFrame(x,y,w,h);
lcd.drawBox(x+2,y+2,mapped, max_height);
}
}
GEMPageProxy menuPageMain("SONG PICKER");
void menuSetup() {
for(uint8_t i=0; i <ARRAY_SIZE(songlist); i++) {
menuPageMain.addMenuItem(new GEMItem(songlist[i].SongName, playSong, i+1));
}
}
void clickHandler (int v, int btn) {
// handle the clicks, depending on context 1 = dial, 2 = A, 3 = B
switch(btn) {
case 1:
// dial
if(!playback_state) {
if(v == 1) {
// longpress to stop
// songContextExit();
menu.context.exit();
}
}
else {
if (v == 1) {
menu.registerKeyPress(GEM_KEY_OK);
}
if (v == 2) {
menu.registerKeyPress(GEM_KEY_CANCEL);
}
}
}
}
void songContextEnter() {
lcd.clear();
lcd.setFont(healzone);
// just a demo for now!
lcd.drawStr(-5,0,"! NOW *&*");
lcd.drawStr(2,20,"PLAYING");
lcd.setFont(sg02);
lcd.drawStr(10,42, songlist[current_song].SongName);
lcd.drawStr(10,51, songlist[current_song].Artist);
}
void songContextLoop() {
//Serial.println("song loop");
if(playback_state) {
Serial.println("playing");
lcd.drawDisc(120,8,8,U8G2_DRAW_ALL);
}
if(!playback_state) {
Serial.println("not playing");
lcd.drawBox(116,4,124,12);
//menu.context.exit();
}
lcd.sendBuffer();
}
void songContextExit() {
mp3.stop();
menu.reInit();
menu.drawMenu();
menu.clearContext();
}
void playSong(GEMCallbackData data) {
char fname[16];
sprintf(fname, "/%05d.MP3", data.valInt);
current_song = (data.valInt -1);
mp3.playSpecifiedDevicePath(Device::Sd, fname);
// start playback animation here, with
menu.context.loop = songContextLoop;
menu.context.enter = songContextEnter;
menu.context.exit = songContextExit;
menu.context.allowExit = false;
menu.context.enter();
}
void setup() {
Serial.begin(9600);
Serial.println("coming online....");
FastLED.addLeds<NEOPIXEL, LED_DATA_PIN>(leds, NUM_LEDS);
FastLED.setBrightness(16);
lcd.begin();
lcd_prepare();
lcd.clearBuffer();
mp3.begin();
delay(100);
mp3.setVolume(20); // leave it at 20
menuSetup();
oldposition = dial.read();
dial_btn.begin(ENC_BTN)
.longPress(2,500)
.onPress( [] (int idx, int v, int up) {
switch(v) {
case 1:
clickHandler(1, 1);
return;
case 2:
songContextExit();
return;
}
});
a_btn.begin(BTN_A)
.longPress(2,500)
.onPress( [] (int idx, int v, int up) {
clickHandler(v, 2);
});
a_btn.begin(BTN_B)
.longPress(2,500)
.onPress( [] (int idx, int v, int up) {
clickHandler(v,3);
});
menu.setSplash(onosendai_width,onosendai_height,onosendai_bits)
.hideVersion()
.setMenuPageCurrent(menuPageMain)
.init();
menu.drawMenu();
}
void loop() {
// do input checks! Separate function?
long newposition = dial.read();
automaton.run();
if(newposition != oldposition) {
if(abs(newposition - oldposition) > ENC_SLOWDOWN) {
if(newposition > oldposition) {
menu.registerKeyPress(GEM_KEY_DOWN);
} else if(newposition < oldposition) {
menu.registerKeyPress(GEM_KEY_UP);
}
}
oldposition = newposition;
playback_state = !digitalRead(2);
}
lcd.sendBuffer();
}
Inspired by m5ez and Qt, maybe any void functions can return instance for chain operations.
Before:
GEM_u8g2 menu(u8g2);
GEMPage menuPageMain("Main Menu");
int number = -512;
boolean enablePrint = false;
GEMItem menuItemInt("Number:", number);
GEMItem menuItemBool("Enable print:", enablePrint);
GEMPage menuPageSystemSettings("SYSTEM SETTINGS");
GEMItem menuItemSystemSettings("System Settings", menuPageSystemSettings);
void setup() {
// ...
menu.setSplash(logo_width, logo_height, logo_bits);
menu.hideVersion();
menu.enableCyrillic();
menuPageSystemSettings.setParentMenuPage(menuPageMain);
menuPageMain.addMenuItem(menuItemInt);
menuPageMain.addMenuItem(menuItemBool);
menuPageMain.addMenuItem(menuItemSystemSettings);
menu.setMenuPageCurrent(menuPageMain);
menu.init();
// ...
}
After:
int number = -512;
boolean enablePrint = false;
GEMPage menuPageSystemSettings("SYSTEM SETTINGS")
.setXXX(...);
GEMPage menuPageMain("Main Menu")
.addMenuItem("Number:", number)
.addMenuItem("Enable print:", enablePrint);
.addMenuItem("System Settings", menuPageSystemSettings);
GEM_u8g2 menu(u8g2)
.setSplash(logo_width, logo_height, logo_bits)
.hideVersion()
.enableCyrillic()
.setMenuPageCurrent(menuPageMain);
void setup() {
// ...
menu.init();
// ...
}
Hello Alexander,
The last for today !
If the text of a item is too long, Automatic scroll the text to see all the information.
Exemple :
Item title : "Parameters of the motor"
item view : "Parameters of the mot"
I want view (50ms step for example):
"Parameters of the mot" --> "arameters of the moto" --> "rameters of the motor" --> ...
Hello,
I have a button with a text : “push to save”
It’s possible when the button is pressed to change the text with “save ok” and update the screen ( without use a context )
Thanks a lot for your work ! , my project use your library ( I add the float support ).
Howto Add a parameter to limit caractere value ?
Normal all code
DEC 0 to 9
HEXA 0 to F
bonus
TEXT a to Z
Thanks for the job !!!
JF
Дисплей с контроллером HX1230 (или же STE2007, типа Nokia 1220)
ESP32-WROOM-32 (с гравировкой QR кода) на собственной плате
GEM 1.4.6
U8g2 2.34.22
Arduino IDE 2.2.1
Не могу понять, почему не отображаются значение в пунктах меню?
В названии пункта числа отображаются, а в значении - нет
Загружаю примеры с кириллицей - ничего, не с кириллицей - тоже ничего, в чём может быть причина?
Делаю список опций - не работает, вывожу просто значение с помощью GEMItem digit("Цифра", cyfra); и всё так же нет значения cyfra - int, пробовал int, byte, через GEMSelect - тоже ничего.
In a page in menus I need to set floating point numbers, my main problem can be defined this way: "after setting the number to a N digit number I can't set the number to a N+1 digit number". How can I resolve this?
Hello,
I use your library for a project, thanks a lot. I need to modifiy 32bits float number. I you can add this format in the library. Thanks a lot.
Ok, I continue my suggestions ;o)
When the user enters edit mode, the cursor should be set to the lower digit. Up/down buttons should increment/decrement the digit, as it is done now, but overflow/underflow should be taken into account, and increment/decrement the left digit (and so on). Left/right buttons should select digit, as it is done now.
This way, when you are on the lower digit, you have the best resolution, but you can increment the entire value as high as you want without changing digit. And when selecting other digits, you can increment the entire value even faster (10/100/1000... times faster, depending on the digit).
This is how Agilent/HP lab instruments work, and this is very handy.
This would need to recompute the value at each key press, not only when leaving edit mode. This would be very handy, especially with rotary encoders.
Another nice feature would be to associate min/max/inc params with the item, to limit the amplitude and change it faster. Rollover or not should be an option.
I'm working on an ESP32 project where everything GEM-related is dynamically allocated.
However LoadProhibited fatal error occurs when menu.drawMenu() is called. Turns out that pointer menuItemNext defined in GEMItem.h is wild and the issue disappears if menuItemNext is initialized with nullptr.
GEMPage* parentPage = nullptr;
GEMPage* linkedPage;
GEMItem* menuItemNext = nullptr; // <- Here
void (*buttonAction)();
I wonder if this is because that all objects are dynamically allocated or just that I'm retarded (I did not dig too deep into the code).
Code can be provided if required. :)
Hello,
I see that the title displayed for a button with a sub menu is 17 char max before trimming and can be 18 normally.
Just a remark .
I use the normal size for the menu and GEM 1.2.3
Best regards
I'm trying to build the Example-01_Basic.ino for the LilyGo TTGO T-Display. When compiling I get a lot of errors such as the following:
~/Documents/Arduino/libraries/GEM/src/GEMItem.cpp: In constructor 'GEMItem::GEMItem(const char*, byte&, GEMSelect&, void (*)(GEMCallbackData))':
~/Documents/Arduino/libraries/GEM/src/GEMItem.cpp:96:38: error: either all initializer clauses should be designated or none of them should be
96 | , callbackData{ .pMenuItem = this, { 0 } }
| ^
The failing compile command is (likely) as follows:
~/Library/Arduino15/packages/esp32/tools/esp-x32/2302/bin/xtensa-esp32-elf-g++ -MMD -c @~/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.1-bd2b9390ef/esp32/flags/cpp_flags -w -Os -DF_CPU=240000000L -DARDUINO=10607 -DARDUINO_LILYGO_T_DISPLAY -DARDUINO_ARCH_ESP32 "-DARDUINO_BOARD=\"LILYGO_T_DISPLAY\"" "-DARDUINO_VARIANT=\"lilygo_t_display\"" -DARDUINO_PARTITION_default "-DARDUINO_HOST_OS=\"macosx\"" "-DARDUINO_FQBN=\"esp32:esp32:lilygo_t_display:UploadSpeed=921600,CPUFreq=240,FlashFreq=80,FlashMode=qio,FlashSize=16M,PartitionScheme=default,DebugLevel=none,PSRAM=disabled,LoopCore=1,EventsCore=1,EraseFlash=none\"" -DESP32 -DCORE_DEBUG_LEVEL=0 -DARDUINO_RUNNING_CORE=1 -DARDUINO_EVENT_RUNNING_CORE=1 -DARDUINO_USB_CDC_ON_BOOT=0 @~/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.1-bd2b9390ef/esp32/flags/defines -I~/Documents/Projects/Code/nutrient_mixer -iprefix ~/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.1-bd2b9390ef/esp32/include/ @~/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.1-bd2b9390ef/esp32/flags/includes -I~/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.1-bd2b9390ef/esp32/qio_qspi/include -I~/Library/Arduino15/packages/esp32/hardware/esp32/3.0.2/cores/esp32 -I~/Library/Arduino15/packages/esp32/hardware/esp32/3.0.2/variants/lilygo_t_display -I~/Documents/Arduino/libraries/KeyDetector/src -I~/Documents/Arduino/libraries/Ai_Esp32_Rotary_Encoder/src -I~/Documents/Arduino/libraries/Adafruit_ST7735_and_ST7789_Library -I~/Documents/Arduino/libraries/Adafruit_GFX_Library -I~/Documents/Arduino/libraries/Adafruit_BusIO -I~/Library/Arduino15/packages/esp32/hardware/esp32/3.0.2/libraries/Wire/src -I~/Library/Arduino15/packages/esp32/hardware/esp32/3.0.2/libraries/SPI/src -I~/Documents/Arduino/libraries/GEM/src -I~/Documents/Arduino/libraries/U8g2/src @/private/var/folders/mt/pp6hn_fx1j5b67vzkvxk217h0000gn/T/arduino/sketches/23D5DF54B931F69BDFF8D10FC97C386F/build_opt.h @/private/var/folders/mt/pp6hn_fx1j5b67vzkvxk217h0000gn/T/arduino/sketches/23D5DF54B931F69BDFF8D10FC97C386F/file_opts ~/Documents/Arduino/libraries/GEM/src/GEMItem.cpp -o /private/var/folders/mt/pp6hn_fx1j5b67vzkvxk217h0000gn/T/arduino/sketches/23D5DF54B931F69BDFF8D10FC97C386F/libraries/GEM/GEMItem.cpp.o
The error seems like it could be caused by the use of a different compiler or a different version of the compiler. Has anyone else had similar issues or know what could cause such errors?
Key, @Spirik ! I'm very much enjoying GEM - thank you for putting this library together and maintaining it. It's turning out to be very easy to use vs. the other options out there.
I had a thought for a new Item type - basically, an Item where in edit mode, it increments or decrements the full value, rather than one character position at a time. This would really just be making a convenience option vs. doing it with a GEMSelect, since I think I can get the same effect with a GEMSelect - it just takes a big SelectOptionsInt[] to hold the values between min/max for the field (say, from 0 to 200).
I'm attempting to implement a menu using three buttons (basically, the Adafruit ESP32-S3 Feather Reverse TFT, with the buttons that come onboard), and an Item type like that would make the user experience a little easier for editing integers, and make it easier on the programmer to code vs. a GEMSelect option.
The basic way it could work is, in edit mode, passing GEM_KEY_UP essentially does value++ and displays it, and GEM_KEY_DOWN does value--. We'd need a way to define min/max values. Internally, it could work a lot like a GEMSelect, potentially?
The advantage here vs. a normal MenuItem is that I don't need to worry about figuring out how to go to the next character position. Right now, I'm doing a long press of the 2nd and 3rd buttons to emulate GEM_KEY_LEFT and GEM_KEY_RIGHT. With something like this, I can just do UP/DOWN and I can use key auto-repeat easily to move quickly through the values.
Hi there,
Trying to find examples of using GEM using a single push-button rotary encoder. Do you have any examples of this?
Cheers
Hi, I have an RP2040-Plus board (which is pin compatible with a Pi Pico).
I'm using the arduino-pico core, ssd1306 OLED, internal 2MB flash
I noticed that when entering a context all works fine, but when I try to exit the program freezes up.
I took the party hard example and changed the u8g2 declaration to
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 5, /* data=*/ 4); // ESP32 Thing, HW I2C with pin remapping
and the u8g2.begin()
part to
u8g2.begin(/*Select/OK=*/ 26, /*Right/Next=*/ 21, /*Left/Prev=*/ 17, /*Up=*/ 19, /*Down=*/ 22, /*Home/Cancel=*/ 20);
the rest is the same
Tried to change compiler optimization flags but no success.
also without removing float edit support it won't allow me to edit all other types, just simple checkboxes
Hi, thank you very much for your library. I am trying to implement it in a small project.
-I have problems setting a float in the menu.
I would like to set the boiler temperature between 85 and 96 degrees C in 0.1 degree steps
So I call it as follows:
float BoilerTemp =92.0;
void validateBoilerTemp();
GEMItem menuItemBoilerTemp("Temp degC:", BoilerTemp, validateBoilerTemp);
menuPageSettings.addMenuItem(menuItemBoilerTemp);
void validateBoilerTemp() {
// Check if mtime variable is within allowable range (i.e. 10..60)
if (BoilerTemp < 85.0) {
BoilerTemp = 85.0;
}
if (BoilerTemp > 96.0) {
BoilerTemp = 96.0;
}
When I go into the settings menu, the line reads "Temp degC 882.7" or after I enter the second time "Temp degC -242.0", far outside of the validated range from 85.0 to 96.0.
If I change it, sometimes it sets to 85, but as soon as I leave and reenter the menu, it's back at 882.7.
-Btw, How can I call the method setPrecision for floats? At the moment I manually set it to 1 digit in config.h, but I don't need all the floatsI want to represent only with 1 digits
Thank you very much for your help, if you need any more info, code, let me know.
Hi!
I'm using an I2cNavKey to navigate the menu. This system has an interrupt pin when a key changes, so I only call registerKeyPress() in this case.
But in order to GEM to refresh its loop(s), it seems that readForKey() as to be called very often. Am I right? So, can I call it even if I don't have any key binded to GEM nor u8g2? Or is there another method to call to refresh GEM internals?
Thanks,
Hi Alex!
It's me again! The design and structure of GEM is truly excellent! I love it!
When I try to enable UTF8 print like the u8g2 example, but seems can't works.
My codes:
GEMItem menuItemInt("数字:", number);
void setup() {
Serial.begin(115200);
u8g2.begin();
u8g2.enableUTF8Print();
// https://github.com/larryli/u8g2_wqy
u8g2.setFont(u8g2_font_wqy12_t_gb2312);
menu.init();
setupMenu();
menu.drawMenu();
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.