neu-rah / ArduinoMenu

Arduino generic menu/interactivity system
GNU Lesser General Public License v2.1
948 stars 191 forks source link

How to refresh only one field? #381

Closed dhysuiej closed 2 years ago

dhysuiej commented 2 years ago

I'm making some test combining the examples ClickEncoder and U8X8.

Basically the only changes I made were increasing the variable test and calling refresh before calling poll in the loop function.

The result is that the test field is updated on the screen but the screen flickers. If I'm not mistaken there is a way to redraw only a portion of the creen with U8x8 but I'm not sure how to accomplish that with ArduinoMenu.

Could someone please point me in the right direction to get only one specific field updated on the screen without causing the screen to flicker.

This is the ino file:

#include <Arduino.h>

/********************
Arduino generic menu system
Arduino menu using clickEncoder and I2C LCD

Sep.2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com
Feb.2018 Ken-Fitz - https://github.com/Ken-Fitz

LCD library:
https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home
http://playground.arduino.cc/Code/LCD3wires
*/

#include <Wire.h>
#include <U8x8lib.h>
#include <menu.h>//menu macros and objects
#include <menuIO/U8x8Out.h>
#include <TimerOne.h>
#include <ClickEncoder.h>
#include <menuIO/clickEncoderIn.h>
#include <menuIO/keyIn.h>
#include <menuIO/chainStream.h>
#include <menuIO/serialOut.h>
#include <menuIO/serialIn.h>
using namespace Menu;

U8X8_SSD1306_128X64_NONAME_HW_I2C display(/* reset = */ U8X8_PIN_NONE);

// Encoder /////////////////////////////////////
#define encA 2
#define encB 3
//this encoder has a button here
#define encBtn 4

ClickEncoder clickEncoder(encB,encA,encBtn,2);
ClickEncoderStream encStream(clickEncoder,1);
MENU_INPUTS(in,&encStream);
void timerIsr() {clickEncoder.service();}

result doAlert(eventMask e, prompt &item);

result showEvent(eventMask e,navNode& nav,prompt& item) {
    Serial.print("event: ");
    Serial.println(e);
    return proceed;
}

int test=55;

result action1(eventMask e,navNode& nav, prompt &item) {
    Serial.print("action1 event: ");
    Serial.print(e);
    Serial.println(", proceed menu");
    Serial.flush();
    return proceed;
}

result action2(eventMask e,navNode& nav, prompt &item) {
    Serial.print("action2 event: ");
    Serial.print(e);
    Serial.println(", quiting menu.");
    Serial.flush();
    return quit;
}

int ledCtrl=LOW;

result myLedOn() {
    ledCtrl=HIGH;
    return proceed;
}
result myLedOff() {
    ledCtrl=LOW;
    return proceed;
}

TOGGLE(ledCtrl,setLed,"Led: ",doNothing,noEvent,noStyle//,doExit,enterEvent,noStyle
    ,VALUE("On",HIGH,doNothing,noEvent)
    ,VALUE("Off",LOW,doNothing,noEvent)
);

int selTest=0;
SELECT(selTest,selMenu,"Select",doNothing,noEvent,noStyle
    ,VALUE("Zero",0,doNothing,noEvent)
    ,VALUE("One",1,doNothing,noEvent)
    ,VALUE("Two",2,doNothing,noEvent)
);

int chooseTest=-1;
CHOOSE(chooseTest,chooseMenu,"Choose",doNothing,noEvent,noStyle
    ,VALUE("First",1,doNothing,noEvent)
    ,VALUE("Second",2,doNothing,noEvent)
    ,VALUE("Third",3,doNothing,noEvent)
    ,VALUE("Last",-1,doNothing,noEvent)
);

//customizing a prompt look!
//by extending the prompt class
class altPrompt:public prompt {
public:
    altPrompt(constMEM promptShadow& p):prompt(p) {}
    Used printTo(navRoot &root,bool sel,menuOut& out, idx_t idx,idx_t len,idx_t) override {
        return out.printRaw(F("special prompt!"),len);;
    }
};

MENU(subMenu,"Sub-Menu",showEvent,anyEvent,noStyle
    ,OP("Sub1",showEvent,anyEvent)
    ,OP("Sub2",showEvent,anyEvent)
    ,OP("Sub3",showEvent,anyEvent)
    ,altOP(altPrompt,"",showEvent,anyEvent)
    ,EXIT("<Back")
);

/*extern menu mainMenu;
TOGGLE((mainMenu[1].enabled),togOp,"Op 2:",doNothing,noEvent,noStyle
    ,VALUE("Enabled",enabledStatus,doNothing,noEvent)
    ,VALUE("disabled",disabledStatus,doNothing,noEvent)
);*/

char* constMEM hexDigit MEMMODE="0123456789ABCDEF";
char* constMEM hexNr[] MEMMODE={"0","x",hexDigit,hexDigit};
char buf1[]="0x11";

MENU(mainMenu,"Main menu",doNothing,noEvent,wrapStyle
    ,OP("OpA",action1,anyEvent)
    ,OP("OpB",action2,enterEvent)
    //,SUBMENU(togOp)
    ,FIELD(test,"Test","%",0,100,10,1,doNothing,noEvent,wrapStyle)
    ,SUBMENU(subMenu)
    ,SUBMENU(setLed)
    ,OP("LED On",myLedOn,enterEvent)
    ,OP("LED Off",myLedOff,enterEvent)
    ,SUBMENU(selMenu)
    ,SUBMENU(chooseMenu)
    ,OP("Alert test",doAlert,enterEvent)
    ,EDIT("Hex",buf1,hexNr,doNothing,noEvent,noStyle)
    ,EXIT("<Back")
);

#define MAX_DEPTH 2

/*const panel panels[] MEMMODE={{0,0,16,2}};
navNode* nodes[sizeof(panels)/sizeof(panel)];
panelsList pList(panels,nodes,1);
idx_t tops[MAX_DEPTH];
lcdOut outLCD(&lcd,tops,pList);//output device for LCD
menuOut* constMEM outputs[] MEMMODE={&outLCD};//list of output devices
outputsList out(outputs,1);//outputs list with 2 outputs
*/

MENU_OUTPUTS(out,MAX_DEPTH
    ,SERIAL_OUT(Serial)
    ,U8X8_OUT(display,{0,0,16,7})
);
NAVROOT(nav,mainMenu,MAX_DEPTH,in,out);//the navigation root object

result alert(menuOut& o,idleEvent e) {
    if (e==idling) {
        o.setCursor(0,0);
        o.print("alert test");
        o.setCursor(0,1);
        o.print("[select] to continue...");
    }
    return proceed;
}

result doAlert(eventMask e, prompt &item) {
    nav.idleOn(alert);
    return proceed;
}

result idle(menuOut& o,idleEvent e) {
    switch(e) {
        case idleStart:o.print("suspending menu!");break;
        case idling:o.print("suspended...");break;
        case idleEnd:o.print("resuming menu.");break;
    }
    return proceed;
}

void setup() {
    pinMode(encBtn,INPUT_PULLUP);
    pinMode(LED_BUILTIN,OUTPUT);
    Serial.begin(9600);
    while(!Serial);
    Serial.println("Arduino Menu Library");Serial.flush();
    display.begin();
    display.setFont(u8x8_font_victoriabold8_r);
    nav.idleTask=idle;//point a function to be used when menu is suspended
    mainMenu[1].enabled=disabledStatus;
    nav.showTitle=false;
    display.setCursor(0, 0);
    display.print("Menu 4.x LCD");
    display.setCursor(0, 1);
    display.print("r-site.net");
    Timer1.initialize(1000);
    Timer1.attachInterrupt(timerIsr);
    delay(2000);
}

void loop() {
    test++;
    test %= 101;
    nav.refresh(); //<---- redraw the full screen, but has flickering
    nav.poll();
    digitalWrite(LED_BUILTIN, ledCtrl);
    delay(100);//simulate a delay as if other tasks are running
}
dhysuiej commented 2 years ago

Imgur

dhysuiej commented 2 years ago

I'm not sure why but only after adding mainMenu[0].dirty = true; I was able to update by hand the field. Also, it seems better that way, since I'm able to check the precision, for instance to update the field only when it changes in one whole unit or include up to an arbitrary number of decimal places.