Simple Menu Sketch I can tweak

Hi All,

I have to advise I am not very good at coding but I have been successful so far. I need a little help to find a Simple Menu sketch that uses clickencoder.h, liquidecrystal.h. It will run on Mega2560 for now but eventually run on a NANO.
I Have found many examples but find that they all are different or use different ways of getting menu’s or input devices. Am hoping I can find something that would be simple for me to change as my skills in programming are limited although this will certainly help me get more skills.

Basic Menu would be:
Charge
Discharge
Gas Gauge
Shutdown

As an example this code would run in Charge mode in a loop. All of them would be similar. Disregard Serial portion as it would not be need. Eventually I would need to break out the last 2 Bytes to extract some ADC measurements and display on LCD but that will be for another thread and time. Baby steps I guess
Thank you very much in advance :slight_smile:
Luca

#include <SPI.h>

//Set CS_Pin
const int CS_Pin = 49;

//Set Variables
int incomingByte1;
int incomingByte2;
int incomingByte3;
int incomingByte4;
int incomingByte5;
int incomingByte6;


void setup() {

  // set the slaveSelectPin as an output:
  pinMode(CS_Pin, OUTPUT);
  pinMode(53, OUTPUT); //Insure Mega is Master
  digitalWrite(CS_Pin, HIGH); // Activate the CS line (CS is active High)
  //Initalize SPI
  SPI.begin();
  SPI.beginTransaction(SPISettings(100000, MSBFIRST, SPI_MODE0));
  //Initialize Serial
  Serial.begin(9600);
  //Let things stabalize
  delay(100);
}

void loop() {



  //Loop Write-Read from LTC1325


  // Set CS Pin LOW
  digitalWrite(CS_Pin, LOW); // Activate the CS line (CS is active LOW)
  delay(100);


  //Send 6 Bytes
  SPI.transfer(0b00000010); // Send First byte
  delay(1);
  //incomingByte1 = SPI.transfer(0); // get RXbyte1 result and place in incomingByte 1
  //Serial.println("Byte1");
  //Serial.println(incomingByte1);
  SPI.transfer(0b00100100); // Send Second byte
  delay(1);
  //incomingByte2 = SPI.transfer(0); // get RXbyte2 result and place in incomingByte 2
  //Serial.println("Byte2");
  //Serial.println(incomingByte2);
  SPI.transfer(0b00000011); // Send Third byte
  delay(1);
  //incomingByte3 = SPI.transfer(0); // get RXbyte3 result and place in incomingByte 3
  //Serial.println("Byte3");
  //Serial.println(incomingByte3);
  SPI.transfer(0b11000000); // Send Forth byte
  delay(1);
  //incomingByte4 = SPI.transfer(0); // get RXbyte4 result and place in incomingByte 4
  //Serial.println("Byte4");
  //Serial.println(incomingByte4);
  SPI.transfer(0b00000000); // Send Fifth byte
  delay(1);
  //incomingByte5 = SPI.transfer(0); // get RXbyte5 result and place in incomingByte 5
  //Serial.println("Byte5");
  //Serial.println(incomingByte5);
  SPI.transfer(0b00000000); // Send Sixte byte
  delay(1);
  //incomingByte6 = SPI.transfer(0); // get RXbyte6 result and place in incomingByte 6
  //Serial.println("Byte6");
  //Serial.println(incomingByte6);
  delay(1);
  //Set CS_Pin High
  digitalWrite(CS_Pin, HIGH); // Enable internal pull-up

  //Set SerialPrint 6 Bytes
  //Serial.println("Byte1");
  //Serial.println(incomingByte1);
  //Serial.println("Byte2");
  //Serial.println(incomingByte2);
  //Serial.println("Byte3");
  //Serial.println(incomingByte3);
  //Serial.println("Byte4");
  //Serial.println(incomingByte4);
  //Serial.println("Byte5");
  //Serial.println(incomingByte5);
  //Serial.println("Byte6");
  //Serial.println(incomingByte6);

  delay(10000); // 100ms Wait
}

I'm going to play "bad cop" now. The approach of trying to find a sketch that does almost what you want, and then modifying it, is a very bad way to program. You will say, "ah but I'm a beginner". I say that makes it ten times worse. Yes, you should cast around for some code. But instead of copying and tinkering around with some lame duck garbage written by an idiot, try to understand what is going on, go and read completely the source documentation, anything you can find. If it's a library, good. Those usually aren't too bad, and have example sketches that are much "cleaner" than your average puffed up pretentious project on a vanity site.

Let this be a transition for you, to learn how to approach a project and run it through recognizable stages, like research, specification, implementation and testing. You don't have to be really formal about it, but do it. You will soon be programming 10x better.

aarg

I agree 100%, I would very much like to understand exactly what I am doing. I have installed pretty much all Menu Libraries. I see that not one is alike and everyone does it differently. I would have liked to find a Arduino FAQ like digitalwrite() that gives commands and examples, from there you can google and see what is possible.
So from my post above the Sketch I am looking at is below which I will try to modify. Hence my post from above was more me trying to find something with more documentation or simpler…

Thank you

#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 <LiquidCrystal_I2C.h>//F. Malpartida LCD's driver
#include <menu.h>//menu macros and objects
#include <menuIO/lcdOut.h>//malpartidas lcd menu output
#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;

//LiquidCrystal_I2C lcd(0x27);//, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3,POSITIVE);  // Set the LCD I2C address and pinout

// Encoder /////////////////////////////////////
#define encA 24
#define encB 22
//this encoder has a button here
#define encBtn 26


ClickEncoder clickEncoder(encA,encB,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
  ,LCD_OUT(lcd,{0,0,20,4})
  ,NONE
);
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();
  lcd.begin(20,4);
  nav.idleTask=idle;//point a function to be used when menu is suspended
  mainMenu[1].enabled=disabledStatus;
  nav.showTitle=false;
  lcd.setCursor(0, 0);
  lcd.print("Menu 4.x LCD");
  lcd.setCursor(0, 1);
  lcd.print("r-site.net");
  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr);
  delay(2000);
}

void loop() {
  nav.poll();
  digitalWrite(LED_BUILTIN, ledCtrl);
  delay(100);//simulate a delay as if other tasks are running
}

If you think about it, there are so many different kinds of menu and different requirements, in addition to different displays. This is really why there is such a diversity of code solutions. It's painful, but you have to just navigate through them all to figure out which one best suits your needs. Having said that, a "menu system" is highly conceptual by itself, and certainly can be coded from scratch - sometimes this can be the best idea if the required menus are super super simple. It's when you have multi level, multiple selection menus that a library can step in and make things easier.

I would have liked to find a Arduino FAQ like digitalwrite() that gives commands and examples

Most Arduino libraries come with some examples. But keep in mind that these are FREE, and the result of volunteer effort.

You can buy commercial software libraries for graphics and menus, etc. that come with excellent, complete documentation, but expect to pay surprisingly large amounts of money for the convenience. As in hundreds to thousands of $ for a single copy license.

If I was to do something like this would that also work as a simple menu...

lcd.clear();                                             
  lcd.setCursor(1,0);                                      
  lcd.print("Charge");                                   
  lcd.clear();

Would I then be able to select a specific row with an encoder and button to select it which would then run some code?
tks
Luca

Yes. But as you will discover, clearing the display immediately after you have printed the menu item is not useful.

Play with these ideas a bit, and you can come up with a useful system very quickly.

Thank you Gents
I will attempt and see what i come up with…I may be back :confused:

I have successfully printed on the LCD my 4 options. I have done much searching and have not come up with a way to move a cursor. Seems lcd.cursor cannot be put in a position of choice ie under the C of Charging and D of Discharging. I’m thinking that would be one way of doing it and then with the help of some encoder programming get it to move and be clicked which then sends the code into a loop like void charging(). I’m ot even sure I can have multiple loops within one sketch.

Any pointers on how to implement in my sketch?
Thank you

#include <SPI.h>
#include <LiquidCrystal.h>
#include <ClickEncoder.h>

//--------------------------------Setup SPI for LTC1325------------------------
//Set CS_Pin
const int CS_Pin = 49;

//--------------------------------Setup Encoder and Button------------------------
const byte encA = 8;                          //digital pin for the A pin of the Rotary Encoder
const byte encB = 9;                          //digital pin for the B pin of the Rotary Encoder
const byte Click = 10;                        //rotary encoder push button

//--------------------------------Setup LCD------------------------
// liquid crystal needs (rs, e, dat4, dat5, dat6, dat7)
LiquidCrystal lcd(32, 34, 36, 38, 40, 42);

void setup() {
  //LCD Setup
  //set up the LCD's number of columns and rows and Print options:
  lcd.begin(20, 4);
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print("Charge");
  lcd.setCursor(1, 1);
  lcd.print("Discharge");
  lcd.setCursor(1, 2);
  lcd.print("Gas Gauge");
  lcd.setCursor(1, 3);
  lcd.print("Shutdown");
  //delay(30000);
  //lcd.clear();

  //SPI for LTC1325
  pinMode(CS_Pin, OUTPUT); // set the slaveSelectPin as an output:
  pinMode(53, OUTPUT); //Insure Mega is Master
  digitalWrite(CS_Pin, HIGH); // Activate the CS line (CS is active High)
  SPI.begin();
  SPI.beginTransaction(SPISettings(125000, MSBFIRST, SPI_MODE0));
  delay(100); //Let things stabalize

  //Encoder Setup
  pinMode(encA, INPUT);
  pinMode(encB, INPUT);
  pinMode(Click, INPUT_PULLUP);
}

void loop() {
  // Turn off the cursor:

  lcd.noCursor();

  delay(500);

  // Turn on the cursor:

  lcd.cursor();

  delay(500);

}

void Charging() {

  //LCD Print
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print("Charging");
  lcd.setCursor(2, 0);
  lcd.print("Vbat=");
  
  // Set CS Pin LOW
  digitalWrite(CS_Pin, LOW); // Activate the CS line (CS is active LOW)
  delay(100);


  //Send 6 Bytes
  SPI.transfer(0b00000010); // Send First byte
  delay(1);
  //incomingByte1 = SPI.transfer(0); // get RXbyte1 result and place in incomingByte 1
  //Serial.println("Byte1");
  //Serial.println(incomingByte1);
  SPI.transfer(0b00100100); // Send Second byte
  delay(1);
  //incomingByte2 = SPI.transfer(0); // get RXbyte2 result and place in incomingByte 2
  //Serial.println("Byte2");
  //Serial.println(incomingByte2);
  SPI.transfer(0b00000011); // Send Third byte
  delay(1);
  //incomingByte3 = SPI.transfer(0); // get RXbyte3 result and place in incomingByte 3
  //Serial.println("Byte3");
  //Serial.println(incomingByte3);
  SPI.transfer(0b11000000); // Send Forth byte
  delay(1);
  //incomingByte4 = SPI.transfer(0); // get RXbyte4 result and place in incomingByte 4
  //Serial.println("Byte4");
  //Serial.println(incomingByte4);
  SPI.transfer(0b00000000); // Send Fifth byte
  delay(1);
  //incomingByte5 = SPI.transfer(0); // get RXbyte5 result and place in incomingByte 5
  //Serial.println("Byte5");
  //Serial.println(incomingByte5);
  SPI.transfer(0b00000000); // Send Sixte byte
  delay(1);
  //incomingByte6 = SPI.transfer(0); // get RXbyte6 result and place in incomingByte 6
  //Serial.println("Byte6");
  //Serial.println(incomingByte6);
  delay(1);
  //Set CS_Pin High
  digitalWrite(CS_Pin, HIGH); // Enable internal pull-up

  //Set SerialPrint 6 Bytes
  //Serial.println("Byte1");
  //Serial.println(incomingByte1);
  //Serial.println("Byte2");
  //Serial.println(incomingByte2);
  //Serial.println("Byte3");
  //Serial.println(incomingByte3);
  //Serial.println("Byte4");
  //Serial.println(incomingByte4);
  //Serial.println("Byte5");
  //Serial.println(incomingByte5);
  //Serial.println("Byte6");
  //Serial.println(incomingByte6);

  delay(10000); // 100ms Wait



}

void Discharging() {

  //LCD Print
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print("Charging");

  // Set CS Pin LOW
  digitalWrite(CS_Pin, LOW); // Activate the CS line (CS is active LOW)
  delay(100);


  //Send 6 Bytes
  SPI.transfer(0b00000010); // Send First byte
  delay(1);
  //incomingByte1 = SPI.transfer(0); // get RXbyte1 result and place in incomingByte 1
  //Serial.println("Byte1");
  //Serial.println(incomingByte1);
  SPI.transfer(0b00100100); // Send Second byte
  delay(1);
  //incomingByte2 = SPI.transfer(0); // get RXbyte2 result and place in incomingByte 2
  //Serial.println("Byte2");
  //Serial.println(incomingByte2);
  SPI.transfer(0b00000011); // Send Third byte
  delay(1);
  //incomingByte3 = SPI.transfer(0); // get RXbyte3 result and place in incomingByte 3
  //Serial.println("Byte3");
  //Serial.println(incomingByte3);
  SPI.transfer(0b11000000); // Send Forth byte
  delay(1);
  //incomingByte4 = SPI.transfer(0); // get RXbyte4 result and place in incomingByte 4
  //Serial.println("Byte4");
  //Serial.println(incomingByte4);
  SPI.transfer(0b00000000); // Send Fifth byte
  delay(1);
  //incomingByte5 = SPI.transfer(0); // get RXbyte5 result and place in incomingByte 5
  //Serial.println("Byte5");
  //Serial.println(incomingByte5);
  SPI.transfer(0b00000000); // Send Sixte byte
  delay(1);
  //incomingByte6 = SPI.transfer(0); // get RXbyte6 result and place in incomingByte 6
  //Serial.println("Byte6");
  //Serial.println(incomingByte6);
  delay(1);
  //Set CS_Pin High
  digitalWrite(CS_Pin, HIGH); // Enable internal pull-up

  //Set SerialPrint 6 Bytes
  //Serial.println("Byte1");
  //Serial.println(incomingByte1);
  //Serial.println("Byte2");
  //Serial.println(incomingByte2);
  //Serial.println("Byte3");
  //Serial.println(incomingByte3);
  //Serial.println("Byte4");
  //Serial.println(incomingByte4);
  //Serial.println("Byte5");
  //Serial.println(incomingByte5);
  //Serial.println("Byte6");
  //Serial.println(incomingByte6);

  delay(10000); // 100ms Wait


}

have not come up with a way to move a cursor

Your posted program moves the cursor. Study the library Blink example to learn how to turn it into a blinking block.

Have you figured out how to actually use the encoder yet? That would be an important first step. There are plenty of encoder tutorials on line.

Have you figured out how to actually use the encoder yet?

Programming wise somewhat little over my head…Electronic wise yes. Looking at this code for encoder…
I will study Blink example…

tks

#define WITH_LCD 1

#include <ClickEncoder.h>
#include <TimerOne.h>

#ifdef WITH_LCD
#include <LiquidCrystal.h>

#define LCD_CHARS   20
#define LCD_LINES    4

LiquidCrystal lcd(32, 34, 36, 38, 40, 42);
#endif

ClickEncoder *encoder;
int16_t last, value;

void timerIsr() {
  encoder->service();
}

#ifdef WITH_LCD
void displayAccelerationStatus() {
  lcd.setCursor(0, 1);  
  lcd.print("Acceleration ");
  lcd.print(encoder->getAccelerationEnabled() ? "on " : "off");
}
#endif

void setup() {
  Serial.begin(9600);
  encoder = new ClickEncoder(A1, A0, A2);

#ifdef WITH_LCD
  lcd.begin(LCD_CHARS, LCD_LINES);
  lcd.clear();
  displayAccelerationStatus();
#endif

  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr); 
  
  last = -1;
}

void loop() {  
  value += encoder->getValue();
  
  if (value != last) {
    last = value;
    Serial.print("Encoder Value: ");
    Serial.println(value);
#ifdef WITH_LCD
    lcd.setCursor(0, 0);
    lcd.print("         ");
    lcd.setCursor(0, 0);
    lcd.print(value);
#endif
  }
  
  ClickEncoder::Button b = encoder->getButton();
  if (b != ClickEncoder::Open) {
    Serial.print("Button: ");
    #define VERBOSECASE(label) case label: Serial.println(#label); break;
    switch (b) {
      VERBOSECASE(ClickEncoder::Pressed);
      VERBOSECASE(ClickEncoder::Held)
      VERBOSECASE(ClickEncoder::Released)
      VERBOSECASE(ClickEncoder::Clicked)
      case ClickEncoder::DoubleClicked:
          Serial.println("ClickEncoder::DoubleClicked");
          encoder->setAccelerationEnabled(!encoder->getAccelerationEnabled());
          Serial.print("  Acceleration is ");
          Serial.println((encoder->getAccelerationEnabled()) ? "enabled" : "disabled");
#ifdef WITH_LCD
          displayAccelerationStatus();
#endif
        break;
    }
  }    
}

What encoder are you using? Please post a link to the product page.

I imagine that there are simple examples that will get you started. What you posted above is not.

For all intents and purposes I have something like this. Rotary Encoder + Extras : ID 377 : $4.50 : Adafruit Industries, Unique & fun DIY electronics and kits

I will continue my searching. Thanks for letting me know the above sketch is not were I should be.

You don't need a library for an encoder like that.

If you want to learn how they work, and how to use them very simply, start with How Rotary Encoder Works and How To Use It with Arduino - HowToMechatronics

I think I will give this library a shot GitHub - Jomelo/LCDMenuLib2: Create a tree menu. Use it with different lcd types / console output / ssh console.. Seems it will do what I want.
Thanks to all that helped but my skills are limited and so far I have success.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.