neu-rah / ArduinoMenu

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

targetSel for LCD I2C + Rotary Encoder+Arduino Uno #296

Closed kapete23 closed 4 years ago

kapete23 commented 4 years ago

Hello Sir

There is an error in navigation if you use a rotary encoder, can not set textfield. please try the following code

/********************
Arduino generic menu system

Rui Azevedo - ruihfazevedo(@rrob@)gmail.com

output: Serial
input: Serial

user defined array menu (userMenu plugin)
*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>//F. Malpartida LCD's driver
#include <menu.h>//menu macros and objects
#include <menuIO/lcdOut.h>//malpartidas lcd menu output
#include <menuIO/serialIn.h>//Serial input
#include <menuIO/encoderIn.h>//quadrature encoder driver and fake stream
#include <menuIO/keyIn.h>//keyboard driver and fake stream (for the encoder button)
#include <menuIO/chainStream.h>
#include <plugin/userMenu.h>

using namespace Menu;

//===========================================================================

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); 

//some values for user data record
enum SelTest {Zero=0,One,Two};
enum ChooseTest {First=1,Second=2,Third=3,Last=-1};
constexpr int dataSz=5;
constexpr int nameSz=10;

//some user data record type to be edited by the menu
struct Data {
  char name[nameSz+1]="<name>     ";
  bool valid=0;
  SelTest selTest=Zero;
  ChooseTest chooseTest=First;
  //how to copy this data, being a simple data c++ will generate this for you
  Data& operator=(Data& o){
  strcpy(name,o.name);
  valid=o.valid;
  selTest=o.selTest;
  chooseTest=o.chooseTest;
  return o;
  }
};

//THE DATA <-----------------------------------------
Data myTargets[dataSz];//our data

//a temporary to be edited
//the data type must have assignment operator
//because we will later on move data back and forth between the actual data and this temporary
//menu edit is wired to this temp. and menu events will deal with the copy-call (from/to)
Data target;

//characters allowed on name field
char* constMEM alphaNum MEMMODE=" 0123456789.ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz,\\|!\"#$%&/()=?~*^+-{}[]€";
char* constMEM alphaNumMask[] MEMMODE={alphaNum};

//some enumeration of values allowed on some fields
TOGGLE(target.valid,editValid,"Valid: ",doNothing,noEvent,noStyle//,doExit,enterEvent,noStyle
  ,VALUE("On",HIGH,doNothing,noEvent)
  ,VALUE("Off",LOW,doNothing,noEvent)
);

SELECT(target.selTest,selMenu,"Select",doNothing,noEvent,noStyle
  ,VALUE("Zero",Zero,doNothing,noEvent)
  ,VALUE("One",One,doNothing,noEvent)
  ,VALUE("Two",Two,doNothing,noEvent)
);

CHOOSE(target.chooseTest,chooseMenu,"Choose",doNothing,noEvent,noStyle
  ,VALUE("First",First,doNothing,noEvent)
  ,VALUE("Second",Second,doNothing,noEvent)
  ,VALUE("Third",Third,doNothing,noEvent)
  ,VALUE("Last",Last,doNothing,noEvent)
);

//a function to save the edited data record
result saveTarget(eventMask e,navNode& nav) {
  _trace(MENU_DEBUG_OUT<<"saveTarget"<<endl);
  idx_t n=nav.root->path[nav.root->level-1].sel;//get selection of previous level
  myTargets[n]=target;
  return quit;
}

//if you want to print the data record name as the title,
//then you MUST create a customized print menu to replace this default one
MENU(targetEdit,"Target edit",doNothing,noEvent,wrapStyle
  ,EDIT("Name",target.name,alphaNumMask,doNothing,noEvent,noStyle)
  ,SUBMENU(editValid)
  ,SUBMENU(selMenu)
  ,SUBMENU(chooseMenu)
  ,OP("Save",saveTarget,enterEvent)
  ,EXIT("<Back")
);

//handling the user menu selection
//this will copy the selected recotd into the temp var
result targetEvent(eventMask e,navNode& nav) {
  _trace(MENU_DEBUG_OUT<<"copy data to temp.\n");
  target=myTargets[nav.sel];
  //you can store nav.sel for future reference
  return proceed;
}

//the customized print of records
//menu system wil use this to print the list of all records
struct TargetMenu:UserMenu{
  using UserMenu::UserMenu;

  // override sz() function to have variable/custom size
  // inline idx_t sz() const override {return 0;}

  //customizing the print of user menu item (len is the availabe space)
  Used printItem(menuOut& out, int idx,int len) override {
    //just printing the string name to the menu output device
    //as an item representative
    return len?out.printText(myTargets[idx].name,len):0;
  }
};

//build the user menu object, optionally giving a sub menu
#ifdef MENU_USERAM
  //for non-AVR devices or when MENU_USERAM is defined at compiler level
  TargetMenu targetsMenu("Targets",dataSz,targetEdit,targetEvent,enterEvent);
#else
  //menu allocation compatible with AVR flash ---------------------------------
  constMEM char targetsMenuTitle[] MEMMODE="Targets";
  constMEM menuNodeShadowRaw targetsMenuInfoRaw MEMMODE={
    (callback)targetEvent,
    (systemStyles)(_menuData|_canNav),
    targetsMenuTitle,
    enterEvent,
    wrapStyle,
    dataSz,
    NULL
  };
  constMEM menuNodeShadow& targetsMenuInfo=*(menuNodeShadow*)&targetsMenuInfoRaw;
  TargetMenu targetsMenu(targetsMenuInfo,targetEdit);
#endif

MENU(mainMenu,"Main menu",doNothing,noEvent,wrapStyle
  ,OBJ(targetsMenu)//attach the array edit menu on a macri build nenu
  ,EXIT("<Back")
);

//////////////////////////////////////////////////////////////////////////////
// menu IO and root navigation control

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

  encoderIn<encA,encB> encoder;//simple quad encoder driver
  #define ENC_SENSIVITY 4
  encoderInStream<encA,encB> encStream(encoder,ENC_SENSIVITY);// simple quad encoder fake Stream

  //a keyboard with only one key as the encoder button
  keyMap encBtn_map[]={{-encBtn,defaultNavCodes[enterCmd].ch}};//negative pin numbers use internal pull-up, this is on when low
  keyIn<1> encButton(encBtn_map);//1 is the number of keys

  serialIn serial(Serial);

  //input from the encoder + encoder button + serial
  menuIn* inputsList[]={&encStream,&encButton,&serial};
  chainStream<3> in(inputsList);

  #define MAX_DEPTH 2

MENU_OUTPUTS(out,MAX_DEPTH
  ,LCD_OUT(lcd,{0,0,16,2})
  ,NONE//must have 2 items at least
);

NAVROOT(nav,mainMenu,MAX_DEPTH,in,out);

//function for menu idle/timemout
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.");
      nav.reset();
      break;
  }
  return proceed;
}

void setup() {
  pinMode(encBtn,INPUT_PULLUP);
  Serial.begin(115200);
  while(!Serial);
  Serial.println("menu 4.x test =================");Serial.flush();
  nav.timeOut=3000;
  nav.idleTask=idle;//point a function to be used when menu is suspended
  encoder.begin();
  lcd.begin(16,2);
  nav.showTitle=false;
  lcd.setCursor(0, 0);
  lcd.print("Menu 4.x LCD");
  lcd.setCursor(0, 1);
  lcd.print("r-site.net");
}

void loop() {
  nav.poll();
  delay(100);//simulate a delay when other tasks are done
}

and this is the result of the implementation picture in the serial.

Capture

in LCD I2C + Rotary encoder 20200515_161526

somethink is wrong on navigation, if you use a rotary encoder, can not set textfield.

Thanks

kapete23 commented 4 years ago

@neu-rah Hello Sir

if i use a rotary encoder, can't set textField. Do you know how I can fix it?

20200515_161526

neu-rah commented 4 years ago

solved?

kapete23 commented 4 years ago

yes, because i change max_depth = 4