Menu LCD+ Encoder + click encoder... como hacerlo?

Hola a todos, la razón por la que pongo este poste, es por que quiero hacer un menu con un encoder, y ademas agregar la función de que el clic de el encoder sirva como "enter" para grabar nuevos valores.

Basicamente es un menu a lcd, que puede modificar variables en el programa, de esta manera uno puede variar el comportamiento en el programa sin necesidad de re-programarlo cada vez... y es que cuando este terminado sera un control electronico para un enorme vitrina que contendra mucha carne en su interior, de ahi la necesidad de poder modificar variables como; temp. incio del ciclo de refrigeracion, temp. final del ciclo de refrigeracion, tem. inicio de la congelacion, temp, final de congelacion, modo nocturno, modo turbo, etc.

Actualmente ya tengo funcionando el encoder, y el click del encoder, asi como el LCd y el adaptador IC2

Le he probado apropiadamente con, este ejemplo;

#include "Arduino.h"  //Incluye Ide Arduino.
//#include "WProgram.h" 
#include "Switch.h"     //INCLUYE LA LIBRERIA SWITCH
#include <Wire.h>                //INCLUYE LA LIBRERIA PARA EL DAPTADOR IC2
#include <LiquidCrystal_I2C.h>    // INCLUYE LA LIBRERIA PARA EL LCD 
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);// CONSTRUCTOR DE EL OBJETO LCD 


const byte pinA = 2;      // encoder pin A to Arduino pin 2 which is also interrupt pin 0 which we will use
const byte pinB = 3;      // encoder pin B to Arduino pin 3 which is also interrupt pin 1 but we won't use it
byte state = 0;           // will store two bits for pins A & B on the encoder which we will get from the pins above
int level = 0;            // a value bumped up or down by the encoder

const byte  swEpin = 7;  //PIN EN EL CONECTAREMOS EL SWITCH 
const byte  pinD = 13;   //PIN PARA HACER BRILLAR EL LED DEL PIN13 DE ARDUINO
int contador =0;         //VARIABLE PARA ALMACENAR EL CONTADOR 

/* A truth table of possible moves 
    1 for clockwise
   -1 for counter clockwwise
    0 for error - keybounce */
int bump[] = {0,0,-1,1};
String bits[] = {"00","01","10","11"}; //Solo para imprimir en serial, sera eliminado mas tarde.

     Switch swE        =   Switch(  swEpin,   INPUT, HIGH); //CONSTRUCTOR, QUE CREA UN OBJETO SWITCH
//     ↑     ↑                 ↑      ↑          ↑      ↑
//  FUNCION NOMBRE DEL      FUNCION  PIN DEL   ESTO ES IGUAL QUE DIGITAL
//            SWITCH                 SWITCH    DIGITAL.READ(INPUT), ENABLE PULL DOWN RESISTOR

void setup() {
  pinMode(pinA,INPUT);    // reads Pin A of the encoder
  pinMode(pinB,INPUT);    // reads Pin B of the encoder
    /* Writing to an Input pin turns on an internal pull up resistor */
  digitalWrite(pinA,HIGH);
  digitalWrite(pinB,HIGH);
    /* Set up to call our knob function any time pinA rises */
  attachInterrupt(0,knobTurned,RISING);    // calls our 'knobTurned()' function when pinA goes from LOW to HIGH
  level = 50;              // a value to start with
  
  Serial.begin(9600);   
  /* Set up for using the on-screen monitor */
  Serial.println("Encoder Ready");
  Serial.print("level = ");
  Serial.println(level);   // to remind us where we're starting
  
  pinMode(pinD, OUTPUT); 
  lcd.begin(16,2);   // initialize the lcd for 16 chars 2 lines, turn on backlight
  lcd.backlight();
  lcd.clear();       //LIMPIA LA PANTALLA LCD. 

  lcd.setCursor(0,0); 
  lcd.print("POSIC. ");
  lcd.setCursor(0,1); 
  lcd.print("CONTADOR ");
}

void loop(){
swE.poll(); 
//↑ FUNCION NECESARIA PARA REFRESCAR EL ESTADO DEL BOTON. 


  if(swE.released()== HIGH){
//   ↑N.SW   ↑ ESTA FUNCION SOLO DEVUELVE HIGH CUANDO EL BOTON HA SIDO SOLTADO 
//    POR LO QUE ES PERFECTA PARA ESTE TRABAJO.
        contador= contador+1 ;
        Serial.print("Contador ="); 
        Serial.println(contador); 
        digitalWrite(pinD, HIGH); 
        lcd.setCursor(9,1); 
        lcd.print(contador);}
   
  else{
    digitalWrite(pinD, LOW); }
    lcd.setCursor(9,0); 
    lcd.print(level);
}



void knobTurned(){
  /* AH HA! the knob was turned */
  state = 0;    // reset this value each time
  state = state + digitalRead(pinA);   // add the state of Pin A
  state <<= 1;  // shift the bit over one spot
  state = state + digitalRead(pinB);   // add the state of Pin B
  
  /* now we have a two bit binary number that holds the state of both pins
     00 - something is wrong we must have got here with a key bounce
     01 - sames as above - first bit should never be 0
     10 - knob was turned backwards
     11 - knob was turned forwards
     */
     
  /* We can pull a value out of our truth table and add it to the current level */
  level = level + bump[state];
  
  /* Let's see what happened */
  Serial.print(bits[state] + "    ");  // show us the two bits
  Serial.print(bump[state],DEC);       // show us the direction of the turn
  Serial.print("    ");
  Serial.println(level);               // show us the new value
}

Una vez que quede mas limpio la idea es agregar un numero de "pasos" en el menu; algo como

level = 0
if (level == 6){ level =-5);
if(level < 5){ codigo}
if level ==-6) {level = 5);

Esto mas o menos para hacer que el "menu" tenga 10 posiciones, y si se gira de mas el encoder, se reincie, el menu en su parte mas baja o alta... digamos un tipo de rodex, que gira entre los pasos seleccionados

Para el contador no sera necesario ya que funcionara, solo como "enter"

Ahora bien, estuve revisando dos opciones que encontre, una es una maquina de estado finitos
MEF Arduino

Y la otra es precisamente un menu para display. Phi-Menu (Gracias a @Surbyte, por señalar esta opcion).

Lo que me gusta de la segunda opcion es que utilizar char, en lugar de String, y por lo que tengo entendido es menos memoria.

Sin embargo me parece que esta libreria, depende de 4 botones que estan muy bien definidos, la pregunta seria, como hacer menus, y sub menus que interactuen de acuerdo al contador del encoder?

La estructura que estaba pensando es algo asi;

MENU PRINCIPAL
AJUSTE DE TERMOSTATO
TEM. INCIO REFRIGERACION
TEMP //MODIFICA UNA VARIABLE INT CON ENCODER CLIK PARA ENTRAR
TEMP. FINAL REFRIGERACION
TEMP //MODIFICA UNA VARIABLE INT CON ENCODER CLIK PARA ENTRAR
VOLVER

MODO NOCTURNO
ACTIVADO
MODIFICA UNA VARIABLE BOOLEAN CLIK ENCODER ENTER
DESACTIVADO
MODIFICA UNA VARIABLE BOOLEAN CLIK ENCODER ENTER
HORA INICIO
ALMACENA HORA, MINUTO, SEGUNDO PARA SER USADO POR LIBRERIA
ALARMS.H
HORA FINAL
LO MISMO QUE LA ANTERIOR.
VOLVER

DESHIELO
ENCENDIDO
OK/ VOLVER //MODIFICA VAR. BOOLEAN
APAGADO
OK/ VOLVER //MODIFICA VAR BOOLEAN
TIEMPO DESHIELO
EN MINUTOS //ALAMACENA TIEMPO EN MINUTOS
CANTIDAD DESHIELOS
//MODIFICA UNA VARIABLE BYTE
VOLVER

ALARMA ALTA TEMPERATURA
ACTIVADA
OK/ VOLVER
DESCATIVADA
OK/VOLVER
EN VITRINA
TEM. ALARMA
OK/VOLVER
EN BODEGA
TEM. ALARMA
OK/VOLVER
VOLVER

Bueno, sin mas por el momento, espero que me puedan indicar la dirección adecuada.
Gracias de antemano.

-Alex.

IMG_20141221_184542.jpg

Y esto lo vas a hacer usando phi menu?

Pues la idea es que si, pero a lo que veo la libreria funciona con cuatro botones... Y estan muy bien definidos, en alguns casos las personas han tendio que modificar por completo la libreria para agregar o quitar una tecla... segun el blog.

De ahi la duda... Si sera los mas conveniente hacer esto, o si dado que no son muchas variables que modificar, se pueda hacer mediante otro metodo..

Un switch case por ejemplo?

Que vaya cambiando de acuerdo al encoder, y que de acuerdo al caso imprima el menu?

Cuatro botones? Funciona con los botones a A0, con botones, con keypad, con Serial, y con Rotary Enconder.. lee bien la página

uso con Rotary Enconder

SInceramente sin palabras.

Vaya efectivamente...
La libreria que yo me baje... La Phi-menu demo No tenia ninguna mencion a encoders...
Ahora que baje la phi-interfaces V.100

https://code.google.com/p/phi-prompt-user-interface-library/downloads/detail?name=phi_interfaces_V100.zip&can=2&q=#makechanges

Si trae la mencion a los encoder, y documentos explicativos.

Bueno, gracias por el aporte, comenzare a hacer el menu y regreso cuando tenga alguna otra duda.

Gracias.

Conclusión: AlexLPD. Siempre dedica un tiempo a buscar la información referente a un tema porque rara vez, no encuentrarás solución. Si me has leído antes..no pierdas el tiempo en español, ahi hay pocas chances de encontrar lo raro. Pero esto... lo ha hecho mucha gente

Antes de empezar mira M2tklib que es otra rutina de menúes maravillosa. Junto con Phi menu las mejores para mi gusto.
También tiene soporte para Enconder y es espectacular su presentación en texto y en paneles graficos.

Bueno, ya me descargue ambas librerias... y tengo una duda algo mas solida que la anterior.

Probe el ejemplo que incluye para leer la direccion del encoder y funciona, bien;

/** \file
 *  \brief     This is the first official release of the phi_interfaces library.
 *  \details   This library unites buttons, rotary encoders and several types of keypads libraries under one library, the phi_interfaces library, for easy of use. This is the first official release. All currently supported input devices are buttons, matrix keypads, rotary encoders, analog buttons, and liudr pads. User is encouraged to obtain compatible hardware from liudr or is solely responsible for converting it to work on other shields or configurations.
 *  \author    Dr. John Liu
 *  \version   1.0
 *  \date      01/24/2012
 *  \copyright Dr. John Liu. Free software for educational and personal uses. Commercial use without authorization is prohibited.
*/

#include <phi_interfaces.h>

#define Encoder1ChnA 2
#define Encoder1ChnB 3
#define EncoderDetent 12

char mapping1[]={'U','D'}; // This is a rotary encoder so it returns U for up and D for down on the dial.
phi_rotary_encoders my_encoder1(mapping1, Encoder1ChnA, Encoder1ChnB, EncoderDetent);
multiple_button_input* dial1=&my_encoder1;

void setup()
{
  Serial.begin(9600);
  Serial.println("Phi_interfaces library rotary encoder test code");
}

void loop()
{
  char temp;
//Rotary encoder 1:  
//  temp=my_encoder1.getKey(); // Use phi_keypads object to access the keypad
  temp=dial1->getKey(); // Use the phi_interfaces to access the same keypad
  if (temp!=NO_KEY) Serial.println(temp);
}

Probe el ejemplo de los botones (Solo cambie el boton B a 4, ya que es el unico que me interesa)
Y funciona, doy clik, me imprime uan nueva B en el monitor serial.
Aunque nada mas voy a utilizar un boton, en la documentacion dice que es mejor dejarlo como grupo, de botones... textualmente dice que esto da acceso a poder leer rotary encoders y etc.

"Collection of single buttons into a group and handled as a keypad so each button push is translated into a named key value such as '1'. The pointer to pins has no column or row lines. Each pin is connected to one button. This is the way to go if you want to start small with few buttons and intend to expand your interface into more buttons and add rotary encoders and keypads. Using this class instead of phi_buttons class also gives you a virtual layer, where you can develop your project without any buttons or keypads and simulate such input with serial You should only use the phi_buttons class if you are happy with just a few buttons and don't intend to expand your interface into mixtures of keypads, rotary encoders etc."

/** \file
 *  \author    Dr. John Liu
 *  \version   1.0
*/

#include <phi_interfaces.h>

//The following button pins apply to phi-1 and phi-2 shields. Please make appropriate modification for your own setup.
// For phi-1 shield btn_r is 3. For phi-2 shield btn_r is 4
#define btn_u 4
#define btn_d 10
#define btn_l 11
#define btn_r 4
#define btn_b 14
#define btn_a 15

#define total_buttons 6

char mapping[]={'U','D','L','R','B','A'}; // This is a list of names for each button.
byte pins[]={btn_u,btn_d,btn_l,btn_r,btn_b,btn_a}; // The digital pins connected to the 6 buttons.
phi_button_groups my_btns(mapping, pins, total_buttons);
multiple_button_input* pad1=&my_btns;

void setup(){
  Serial.begin(9600);
  Serial.println("Phi_interfaces library button groups test code");
  my_btns.set_repeat(100); // You don't have to set this since it has a default value of 200. This sets how often a held key is repeated, 0.1s. So the held key is repeated 10 times a second.
  my_btns.set_hold(2000); // You don't have to set this since it has a default value of 2000. This sets how long you need to hold a key before it is repeated, 2s. So the held key starts repeating after being held for 2 seconds.
}

void loop(){
  char temp;
  temp=my_btns.getKey(); // Use phi_button_groups object to access the group of buttons
//  temp=pad1->getKey(); // Use the generic multiple_button_interface to access the same hardware
  if (temp!=NO_KEY) Serial.write(temp);
}

Ahora mi duda es;
¿Como se leen ambos?
Por lo que dice la documentacion la funcion get.key() regresa el valor que se le haya asignado a el boton, sin embargo observando los dos ejemplos... la usan tanto para el encoder, como para el boton.
Me supongo que debe estar vinculada a una funcion... algo como;
byte phi_rotary_encoders::getKey () [virtual]

Encontre a alguien que hizo algo similar, pero sin poder leer el clik del encoder...
Phi- Menu help?

A lo que menciona el menu se muestra perfectamente en el display, pero no dio con la manera de leer el clik.

Si tienes algún menú que funcione con encoder @Surbyte seria de gran ayuda para comenzar por ahi.

Armo algo con el simulador y te respondo, aquí mismo.
Bueno no me funciona en la pantalla. Estoy viendo que problema tengo.

Incorporé un Rotary Enconder al Proteus.
Esta la simulación en Proteus y el sketch phi_menu

phi_menu.ino (4.92 KB)

ROTARY ENCODER.zip (69.8 KB)

Hola @Surbyte, a penas me desocupe, este trabajo me quita mucho tiempo de cosas importantes, pero bueno descargando y probando, te mantengo al tanto.
Gracias!

Yo no tengo el simulador, pero intentare conectarlo directamente a el hardaware.

Bueno no fue mucho hasta que me tope con el primer detalle...

la libreria phi, define un LCd usando 7 pines digitales, en mi caso uso un adaptador IC2,
así que ya comence a ver como puedo pasarlo...

En lugar de;

#define LCD_RS 8
#define LCD_RW 7
#define LCD_EN 6
#define LCD_D4 12
#define LCD_D5 11
#define LCD_D6 10
#define LCD_D7 9

Voy a agregar;
#include <Wire.h> //INCLUYE LA LIBRERIA PARA EL DAPTADOR IC2
#include <LiquidCrystal_I2C.h> // INCLUYE LA LIBRERIA PARA EL LCD
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // CONSTRUCTOR DE EL OBJETO LCD

como en cualquier otro esquema.

Ahora tengo un detalle algo mas especifico de la librería y es que el IDE me tira error cuando llega a esta linea;

void setup()
{
  Serial.begin(9600);
  lcd.begin(lcd_columns, lcd_rows);
  pinMode(EncoderGreen, OUTPUT);
  pinMode(EncoderRed, OUTPUT);
  lcd.clear();
  lcd.print("Hola esto funciona?");
  init_phi_prompt(&lcd,keypads,function_keys, lcd_columns, lcd_rows, '~'); //Esta linea... me dice que   //int_phi_promt no ha sido declarado en este escope... enseguida busco algo en la literatura. 
}

Bueno, en un rato mas espero poder cargarlo con éxito al arduino y comenzar a hacer pruebas.
Efectivamente, no tenía instalada la libreria phi_promt.

Si ya cargo... ahora el problema que me temía;
cannot convert "liquid_crystal IC2" to "liquid crystal"

Vere por ahi en su la pag, dan soporte al IC2... Pero en todo caso, me pregunto si no podrían declararse los pines del LCD?

Esto ↓↓↓ Lo saque de aqui → [SOLVED] I2C LCD - Setup instructions for 16x2 - Displays - Arduino Forum
Entrada #12
Usa el mismo chip que yo.... PCF8574T

  • My LCD setup had PCF8574T chip as the 8-bit I/O expander for I2C bus. (Address 0x27).
  • 8 pins from PCF8574T connect to the LCD display as follows (in this model of the board) :
    LCD <------> pcf8574T CHIP
    RS(pin4) - P0 (pin4)
    RW(pin5) - P1 (pin5)
    EN(pin6) - P2 (pin6)
    BL(pin3) - P3 (pin7)
    D4(pin11) - P4 (pin9)
    D5(pin12) - P5 (pin10)
    D6(pin13) - P6 (pin11)
    D7(pin14) - P7 (pin12)

So, for example, in the code:
#define En_pin 2
means EN signal of LCD is being addressed by P2 of the 8 bit (P0-P7) address bus (parallel).

Sigo en la busqueda. ...

Y creo que es insalvable...

A menos que haya una manera que desconozca esta este mensaje del Dr. Liu;

...That is what my panel will do. Except it can't talk I2C.#5 el 18/Jul/2011 :fearful:

Hummm Pues se pone muy interesante... fui a dar un vistazo rapido a M2KLib, como me lo sugeriste , la instale y me di una vuelta por la wiki... y no parece que tenga soporte para IC2...

Le sigo buscando... :disappointed_relieved: :disappointed_relieved:

Bueno, aparentemente alguíen hizo un traductor que permite usar un display IC2, como medio de salida de la libreria M2TKLIB, mediante el uso de las librerias de display de Malpartida (que actualmente ya uso... )

#7

Aunque esta libreria es un poco pesada 14,200K solo para el ejemplo del encoder... me comienzo a preguntar... si no es hasta cierto punto practico escribir el código del menu? ... :disappointed_relieved: :disappointed_relieved: :disappointed_relieved:

La libreria M2ktlib es Excelente, no se si de las mejores, solo que me resulta algo compleja para confeccionar el menú.
Pero si usas un display gráfico esta muy buena por los efectos que permite.
Queria comentarte que algo ocurre con mi simulacion de enconder porque este ejemplo con botones y el phi_menu anda perfecto

/*
.______    __    __   __        .______   .______        ______   .___  ___. .______   .___________.
|   _  \  |  |  |  | |  |       |   _  \  |   _  \      /  __  \  |   \/   | |   _  \  |           |
|  |_)  | |  |__|  | |  |       |  |_)  | |  |_)  |    |  |  |  | |  \  /  | |  |_)  | `---|  |----`
|   ___/  |   __   | |  |       |   ___/  |      /     |  |  |  | |  |\/|  | |   ___/      |  |     
|  |      |  |  |  | |  |       |  |      |  |\  \----.|  `--'  | |  |  |  | |  |          |  |     
| _|      |__|  |__| |__|  _____| _|      | _| `._____| \______/  |__|  |__| | _|          |__|     

 _______  .______                 __       __   __    __  
|       \ |   _  \               |  |     |  | |  |  |  | 
|  .--.  ||  |_)  |              |  |     |  | |  |  |  | 
|  |  |  ||      /               |  |     |  | |  |  |  | 
|  '--'  ||  |\  \----.    __    |  `----.|  | |  `--'  | 
|_______/ | _| `._____|   (__)   |_______||__|  \______/  

http://liudr.wordpress.com
*/

#define lcd_rows 4
#define lcd_columns 20

//The following button pins apply to phi-1 and phi-2 shields. Please make appropriate modification for your own setup.
// For phi-1 shield btn_r is 3. For phi-2 shield btn_r is 4
#define btn_u A3
#define btn_d A4
#define btn_l A5
#define btn_r 7
#define btn_b 3
#define btn_a 2
#define total_buttons 6

//// LCD pin setting
//// For phi-1 shield LCD_D7 is 4. For phi-2 shield LCD_D7 is 3
//#define LCD_RS 8
//#define LCD_EN 9
//#define LCD_D4 7
//#define LCD_D5 6
//#define LCD_D6 2
//#define LCD_D7 3

// LCD pin setting
// For phi-1 shield LCD_D7 is 4. For phi-2 shield LCD_D7 is 3
#define LCD_RS 8
//#define LCD_RW 7
#define LCD_EN 6
#define LCD_D4 12
#define LCD_D5 11
#define LCD_D6 10
#define LCD_D7 9

#include <LiquidCrystal.h>
#include <phi_interfaces.h>
#include <phi_prompt.h>

LiquidCrystal lcd(LCD_RS,LCD_EN,LCD_D4,LCD_D5,LCD_D6,LCD_D7); // Create the lcd object

char mapping[]={1,2,3,4,5,6}; // This is a list of names for each button.
byte pins[]={btn_u,btn_d,btn_l,btn_r,btn_b,btn_a}; // The digital pins connected to the 6 buttons.
phi_button_groups my_btns(mapping, pins, total_buttons);
phi_serial_keypads debug_keypad(&Serial,115200);
multiple_button_input * keypads[]={&my_btns,&debug_keypad,0};
char up_keys[]={1,0}; ///< All keys that act as the up key are listed here.
char down_keys[]={2,0}; ///< All keys that act as the down key are listed here.
char left_keys[]={3,0}; ///< All keys that act as the left key are listed here.
char right_keys[]={4,0}; ///< All keys that act as the right key are listed here.
char enter_keys[]={5,0}; ///< All keys that act as the enter key are listed here.
char escape_keys[]={6,0}; ///< All keys that act as the escape key are listed here.
char * function_keys[]={up_keys,down_keys,left_keys,right_keys,enter_keys,escape_keys}; ///< All function key names are gathered here fhr phi_prompt.

PROGMEM prog_char msg_00[]="Developed by:\nDr.Liu 05/23/11\nhttp://liudr.wordpress.com\nThis is just a mock-up of an actual data acquisition system with a 2-level menu.\nIt serves as a template for your actual project. It also shows off various features of the phi_prompt library.\nGo in \"Set Menu Style\" to find out some menu features you could be using in your project.\nPress Confirm to continue";
// Setting up two menus top and sub_1

void setup()
{
  Serial.begin(115200);
  lcd.begin(lcd_columns, lcd_rows);
  init_phi_prompt(&lcd,keypads,function_keys, lcd_columns, lcd_rows, '~'); // Supply the liquid crystal object, input keypads, and function key names. Also supply the column and row of the lcd, and indicator as '>'. You can also use '\x7e', which is a right arrow.
  show_credit();
}

void loop()
{
// A two-level menu using two lists, one for top menu and one for a sub menu. Create any level of menu you like.
/*
.___  ___.  _______ .__   __.  __    __  
|   \/   | |   ____||  \ |  | |  |  |  | 
|  \  /  | |  |__   |   \|  | |  |  |  | 
|  |\/|  | |   __|  |  . `  | |  |  |  | 
|  |  |  | |  |____ |  |\   | |  `--'  | 
|__|  |__| |_______||__| \__|  \______/  
*/
  top_menu(); // See Example_menu.pde
}

void show_credit()
{
  // Display credits
  phi_prompt_struct myLongMsg;

  lcd.clear();
  lcd.noBlink();
  center_text("Credits"); // Prompt user for input. Use center_text to display center-aligned.
  myLongMsg.ptr.msg_P=msg_00; // Assign the address of the text string to the pointer.
  myLongMsg.low.i=0; // Default text starting position. 0 is highly recommended.
  myLongMsg.high.i=strlen_P(msg_00); // Position of the last character in the text string, which is size of the string - 1.
  myLongMsg.step.c_arr[0]=lcd_rows-1; // rows to auto fit entire screen
  myLongMsg.step.c_arr[1]=lcd_columns-1; // one col list
  myLongMsg.col=0; // Display the text area starting at column 0
  myLongMsg.row=1; // Display the text area starting at row 0
  myLongMsg.option=1; // Option 0, display classic message, option 1, display message with scroll bar on right.

  text_area_P(&myLongMsg);
}

el ejmplo del enconder de phi_intefaces debe funcionar. Como ya dije, a mi no me funciona porque no existe el encoder rotatorio en Proteus, lo simulé y no funciona bien.
Algo logré, el ejemplo que posteaste me anduvo, UP, DOWN presentaba por el Serial.
Pero luego no me dejaba ajustar los menúes.

El detalle que esa libreria no funciona con IC2... ... Y necesito muchos espacios en el Arduino.

Si vi que la MK2Lib hace que el display adopte toda clase de salidas, tiene incluso resaltado para textos y todo.

Creo que siendo pocos los menus, intentare hacer algo sencillo.

Tienes alguna idea, como hacer para clicar un enter?

Yo tengo la idea de;

1.- Tomando en cuenta el numero de menus... hacer un rodex como dije anteriormente
2.- Que el numero que devuelve la funcion del encoder = a un objeto... creo que tipo char, por lo que me gusto tanto del manejo de memoria de la libreria del Dr. Liu.

3.- Poner dos opciones en pantalla ... Ok/Regresar..

4- Hacer un enter???

Será la edad.. pero no me di cuenta hasta recién que usas LCD con interfaz I2C.
Veré que encuentro.
Lo que si te digo, es que nada es insalvable y menos programación. QUe tu y yo no sepamos adapatar la librería es una cosa pero que se puede, te aseguro que se puede.

Bueno ya estoy trabajando un poco en el codigo, me ha llevado un rato pero parece que ya va quedando...
Aunque hay un bug que me llama mucho la antención, y es que he insertado una subrutina para que re-imprima el menu de la LCD cada que hay un cambio en el potenciometro( ya que se quedan digitos de la vieja medida y a pesar de que cuente bien, se ve en el serial... brinca algo como 4,5,405 o una cosa asi,
continuación dejo el código a ver que le notan.

#include "Arduino.h"                                          //IDE ARDUINO NEC. PARA SWITCH.
#include "Switch.h"                                           //INCLUYE LA LIBRERIA SWITCH
#include <Wire.h>                                             //INCLUYE LA LIBRERIA PARA EL DAPTADOR IC2
#include <LiquidCrystal_I2C.h>                                // INCLUYE LA LIBRERIA PARA EL LCD 
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);// CONSTRUCTOR DE EL OBJETO LCD 


   const byte pinA = 2;   // Pin A del encoder a Arduino Pin 2, que usa attach interrupt 
   const byte pinB = 3;   // Pin B del encoder a Arduino Pin 3
        byte state = 0;   // Almacena los baloreso de dos bits a&b, de los pines de arriba
         int level = 0;   // Un valor que cambia con el giro del encoder
      int levelAnt = 0;   //Variable para ver si ha cambiado 

const byte  swEpin = 7;   //Pin Switch encoder... Res a gnd la otra punta a Vcc
 const byte  pinD = 13;   //Pin para hacer brillar el led del arduino. TEMPORAL
   int  limMenuSup = 5;    //Variable lim. sup  para hacer el rodex del menu.. diez pasos 
   int  limMenuInf =-5;   //Variable lim. sup  para hacer el rodex del menu.. diez pasos 
       byte contador =0;   //Variable para almacenar contador de cliks 
     byte contadorAnt=0;   //Variable para comprar si el contador ha cambiado  

//Tabla de verdad para posibles movmientos 1 g.der, -1 giro izq, 0 error o debounce
int bump[] = {0,0,-1,1};

//Solo para imprimir en serial, sera eliminado mas tarde.
String bits[] = {"00","01","10","11"}; 

//FUNCION PARA VERIFICAR EL CLIK DEL ENCODER  
     Switch swE        =   Switch(  swEpin,   INPUT, HIGH); //CONSTRUCTOR, QUE CREA UN OBJETO SWITCH
//     ↑     ↑                 ↑      ↑          ↑      ↑
//  FUNCION NOMBRE DEL      FUNCION  PIN DEL   ESTO ES IGUAL QUE DIGITAL
//            SWITCH                 SWITCH    DIGITAL.READ(INPUT), ENABLE PULL DOWN RESISTOR

void setup() {
  pinMode(pinA,INPUT);      // Lee el pin A del encoder 
  pinMode(pinB,INPUT);      // Lee el pin B del encoder 
  digitalWrite(pinA,HIGH);  // Poner HIGH a una entrada digital activa el resistor pullUp
  digitalWrite(pinB,HIGH);
    /* Configuracion de la funcion knobTurned, cada que el pin 2, se ponga en estado alto */
  attachInterrupt(0,knobTurned,RISING);    // Llama a la funcion 'knobTurned()'cada que el pin A va de HIGH a LOW 
  level = 0;              // Un valor para comenzar 
  
  Serial.begin(19200);   
  /* Set up for using the on-screen monitor */
  Serial.println("Encoder Ready");
  Serial.print("level = ");
  Serial.println(level);   // to remind us where we're starting
  
  pinMode(pinD, OUTPUT); 
  lcd.begin(16,2);   // Inicializa el objeto LCD, 16x2 
  lcd.backlight();   //Enciende el backligth
  lcd.clear();       //Limpia la pantalla LCD 

  lcd.setCursor(0,0); 
  lcd.print("POSIC. ");
  lcd.setCursor(0,1); 
  lcd.print("CONTADOR ");
}

void loop(){
swE.poll(); //← FUNCION NECESARIA PARA REFRESCAR EL ESTADO DEL BOTON. 

if(contador == 5){  
   contador = 0;}
   
  if(swE.released()== HIGH){
//   ↑N.SW   ↑ ESTA FUNCION SOLO DEVUELVE HIGH CUANDO EL BOTON HA SIDO SOLTADO 
//    POR LO QUE ES PERFECTA PARA ESTE TRABAJO.     
            contador= contador+1 ;
            Serial.print("Contador ="); 
            Serial.println(contador); 
            digitalWrite(pinD, HIGH); 
            lcd.setCursor(9,1); 
            lcd.print(contador);}          
  
  else{
    digitalWrite(pinD, LOW); }
    lcd.setCursor(9,0); 
    lcd.print(level);
}



void knobTurned(){
  state = 0;                           // Inicia de 0 cada vez
  state = state + digitalRead(pinA);   // Agrega el estado del Pin A 
  state <<= 1;                         // Mueve el bit a la izq, un lugar.
  state = state + digitalRead(pinB);   // Agrega el estado del Pin B
  
  /* Ahora tenemos un numero binario que tiene el estado de ambos pines 
  que se comparara en la tabla bump... aagregando o restando segun sea 
  el caso a la variable level.  */

level = level + bump[state]; //Guarda cada vez en level, el level y le agrega 
                            //o resta de acuerdo a la tabla bump.

if(level >limMenuSup) level = limMenuInf +1; //lim superior del menu 
if(level <limMenuInf) level = limMenuSup -1; //lim inferior del menu

imprimeMenu();     //Actualmente no funciona... de hecho no se realiza la interrupcion
                  // digamos que se "bloquea" el programa.


  /* Let's see what happened */
  Serial.print(bits[state] + "    ");  // show us the two bits
  Serial.print(bump[state],DEC);       // show us the direction of the turn
  Serial.print("    ");
  Serial.println(level);               // show us the new value
}


void imprimeMenu(){
  lcd.clear();
    lcd.setCursor(0,0); 
    lcd.print("POSIC. ");
  lcd.setCursor(9,0); 
  lcd.print(level);
    lcd.setCursor(0,1); 
    lcd.print("CONTADOR ");
  lcd.setCursor(9,1); 
  lcd.print(contador);  }

Bueno... he hecho algunas modificaciones al codigo, para comenzar a trabajar en los menus, por si mismos
ya logre "activar" la impresion en el display lcd... mapeo el encoder de -5 a 5 y lo convierto del 1 al 10, que utilizare en un sistema switch case, para seleccionar e imprimir los menus... Funciona, pero el lcd blinkea debido a que cada ciclo de loop se limpia y reescribe... de momento no encontré como evitarlo, aunque se me ocurren algunas cosas;

Una variable booleana, que despues tendra una especie de timmer... asi si permanece inactivo el menu... digamos 3 segundos, regresara a imprimir la "pantalla principal" temperaturas actuales.
tenia otra... pero el sueño me la espanto...
Bueno, dejo el codigo... todavia se ve muy parchado, pero es trabajo en progreso, aunque igual si me sugieren maneras de eficientarlo les agradezco. :sunglasses:

#include "Arduino.h"                                          //IDE ARDUINO NEC. PARA SWITCH.
#include "Switch.h"                                           //INCLUYE LA LIBRERIA SWITCH
#include <Wire.h>                                             //INCLUYE LA LIBRERIA PARA EL DAPTADOR IC2
#include <LiquidCrystal_I2C.h>                                // INCLUYE LA LIBRERIA PARA EL LCD 
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);// CONSTRUCTOR DE EL OBJETO LCD 


   const byte pinA = 2;   // Pin A del encoder a Arduino Pin 2, que usa attach interrupt 
   const byte pinB = 3;   // Pin B del encoder a Arduino Pin 3
        byte state = 0;   // Almacena los baloreso de dos bits a&b, de los pines de arriba
         int level = 0;   // Un valor que cambia con el giro del encoder
      int levelAnt = 0;   //Variable para ver si ha cambiado 

const byte  swEpin = 7;   //Pin Switch encoder... Res a gnd la otra punta a Vcc
 const byte  pinD = 13;   //Pin para hacer brillar el led del arduino. TEMPORAL
   int  limMenuSup = 5;    //Variable lim. sup  para hacer el rodex del menu.. diez pasos 
   int  limMenuInf =-5;   //Variable lim. sup  para hacer el rodex del menu.. diez pasos 
       byte contador =0;   //Variable para almacenar contador de cliks 
     byte contadorAnt=0;   //Variable para comprar si el contador ha cambiado  
byte num_menu =0;          //Variable mapeada para regresar el menu conrrespondiente
byte num_menu_ant=0;  //Para revisar si ha cambiado. 

//Tabla de verdad para posibles movmientos 1 g.der, -1 giro izq, 0 error o debounce
int bump[] = {0,0,-1,1};

//Solo para imprimir en serial, sera eliminado mas tarde.
String bits[] = {"00","01","10","11"}; 

//FUNCION PARA VERIFICAR EL CLIK DEL ENCODER  
     Switch swE        =   Switch(  swEpin,   INPUT, HIGH); //CONSTRUCTOR, QUE CREA UN OBJETO SWITCH
//     ↑     ↑                 ↑      ↑          ↑      ↑
//  FUNCION NOMBRE DEL      FUNCION  PIN DEL   ESTO ES IGUAL QUE DIGITAL
//            SWITCH                 SWITCH    DIGITAL.READ(INPUT), ENABLE PULL DOWN RESISTOR

void setup() {
  pinMode(pinA,INPUT);      // Lee el pin A del encoder 
  pinMode(pinB,INPUT);      // Lee el pin B del encoder 
  digitalWrite(pinA,HIGH);  // Poner HIGH a una entrada digital activa el resistor pullUp
  digitalWrite(pinB,HIGH);
    /* Configuracion de la funcion knobTurned, cada que el pin 2, se ponga en estado alto */
  attachInterrupt(0,knobTurned,RISING);    // Llama a la funcion 'knobTurned()'cada que el pin A va de HIGH a LOW 
  level = 0;              // Un valor para comenzar 
  
  Serial.begin(19200);   
  /* Set up for using the on-screen monitor */
  Serial.println("Encoder Ready");
  Serial.print("level = ");
  Serial.println(level);   // to remind us where we're starting
  
  pinMode(pinD, OUTPUT); 
  lcd.begin(16,2);   // Inicializa el objeto LCD, 16x2 
  lcd.backlight();   //Enciende el backligth
  lcd.clear();       //Limpia la pantalla LCD 

  lcd.setCursor(0,0); 
  lcd.print("POSIC. ");
  lcd.setCursor(0,1); 
  lcd.print("CONTADOR ");
}

void loop(){
swE.poll(); //← FUNCION NECESARIA PARA REFRESCAR EL ESTADO DEL BOTON. 

if(contador == 5){  
   contador = 0;}
   
  if(swE.released()== HIGH){
//   ↑N.SW   ↑ ESTA FUNCION SOLO DEVUELVE HIGH CUANDO EL BOTON HA SIDO SOLTADO 
//    POR LO QUE ES PERFECTA PARA ESTE TRABAJO.     
            contador= contador+1 ;
            Serial.print("Contador ="); 
            Serial.println(contador); 
            digitalWrite(pinD, HIGH); 
            lcd.setCursor(9,1); 
            lcd.print(contador);}          
  
  else{
    digitalWrite(pinD, LOW); }
    lcd.setCursor(9,0); 
    lcd.print(level);
/////

if(num_menu =! num_menu_ant) imprime_menu();

}

void knobTurned(){
  state = 0;                           // Inicia de 0 cada vez
  state = state + digitalRead(pinA);   // Agrega el estado del Pin A 
  state <<= 1;                         // Mueve el bit a la izq, un lugar.
  state = state + digitalRead(pinB);   // Agrega el estado del Pin B
  
  /* Ahora tenemos un numero binario que tiene el estado de ambos pines 
  que se comparara en la tabla bump... aagregando o restando segun sea 
  el caso a la variable level.  */

level = level + bump[state]; //Guarda cada vez en level, el level y le agrega 
                            //o resta de acuerdo a la tabla bump.

if(level >limMenuSup) level = limMenuInf +1; //lim superior del menu 
if(level <limMenuInf) level = limMenuSup -1; //lim inferior del menu

//imprimeMenu();     //Actualmente no funciona... de hecho no se realiza la interrupcion
                  // digamos que se "bloquea" el programa.

num_menu =map(level, -5, 5, 0,10); 

  /* Let's see what happened */
  Serial.print(bits[state] + "    ");  // show us the two bits
  Serial.print(bump[state],DEC);       // show us the direction of the turn
  Serial.print("    ");
  Serial.println(level);               // show us the new value
  Serial.println(num_menu);               // show us the new value
}


void imprime_menu(){
  switch (num_menu){
  case 1: 
    lcd.clear();
    lcd.print("MENU 1 ");
    break;   
  case 2: 
    lcd.clear();
    lcd.print("MENU 2");
    break; 
  case 3: 
    lcd.clear();
    lcd.print("MENU 3");
    break;
  case 4:
    lcd.clear();
    lcd.print("MENU 4");
    break;
  default: 
    lcd.clear();
    lcd.print("Esperando Encoder");
}}

Bueno, ya imprime adecuadamente los menus...

ahora hay que ver lo de los sub-menus, el enter, atras y modifica una variable... jejje

#include "Arduino.h"                                          //IDE ARDUINO NEC. PARA SWITCH.
#include "Switch.h"                                           //INCLUYE LA LIBRERIA SWITCH
#include <Wire.h>                                             //INCLUYE LA LIBRERIA PARA EL DAPTADOR IC2
#include <LiquidCrystal_I2C.h>                                // INCLUYE LA LIBRERIA PARA EL LCD 
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);// CONSTRUCTOR DE EL OBJETO LCD 


   const byte pinA = 2;   // Pin A del encoder a Arduino Pin 2, que usa attach interrupt 
   const byte pinB = 3;   // Pin B del encoder a Arduino Pin 3
        byte state = 0;   // Almacena los baloreso de dos bits a&b, de los pines de arriba
         int level = 0;   // Un valor que cambia con el giro del encoder
      int levelAnt = 0;   //Variable para ver si ha cambiado 

const byte  swEpin = 7;   //Pin Switch encoder... Res a gnd la otra punta a Vcc
 const byte  pinD = 13;   //Pin para hacer brillar el led del arduino. TEMPORAL
   int  limMenuSup = 5;    //Variable lim. sup  para hacer el rodex del menu.. diez pasos 
   int  limMenuInf =-5;   //Variable lim. sup  para hacer el rodex del menu.. diez pasos 
       byte contador =0;   //Variable para almacenar contador de cliks 
     byte contadorAnt=0;   //Variable para comprar si el contador ha cambiado  
byte num_menu =0;          //Variable mapeada para regresar el menu conrrespondiente


boolean refresca_menu =false; //flag para imprimir una sola vez el el menu por brinco de encoder.


char* items_menu[]      ={"Menu1", "menu2", "menu3", "menu4"};
const byte total_menu   = 4; 

//Tabla de verdad para posibles movmientos 1 g.der, -1 giro izq, 0 error o debounce
int bump[] = {0,0,-1,1};

//Solo para imprimir en serial, sera eliminado mas tarde.
String bits[] = {"00","01","10","11"}; 

//FUNCION PARA VERIFICAR EL CLIK DEL ENCODER  
     Switch swE        =   Switch(  swEpin,   INPUT, HIGH); //CONSTRUCTOR, QUE CREA UN OBJETO SWITCH
//     ↑     ↑                 ↑      ↑          ↑      ↑
//  FUNCION NOMBRE DEL      FUNCION  PIN DEL   ESTO ES IGUAL QUE DIGITAL
//            SWITCH                 SWITCH    DIGITAL.READ(INPUT), ENABLE PULL DOWN RESISTOR

void setup() {
  pinMode(pinA,INPUT);      // Lee el pin A del encoder 
  pinMode(pinB,INPUT);      // Lee el pin B del encoder 
  digitalWrite(pinA,HIGH);  // Poner HIGH a una entrada digital activa el resistor pullUp
  digitalWrite(pinB,HIGH);
    /* Configuracion de la funcion knobTurned, cada que el pin 2, se ponga en estado alto */
  attachInterrupt(0,knobTurned,RISING);    // Llama a la funcion 'knobTurned()'cada que el pin A va de HIGH a LOW 
  level = 0;              // Un valor para comenzar 
  
  Serial.begin(19200);   
  /* Set up for using the on-screen monitor */
  Serial.println("Encoder Ready");
  Serial.print("level = ");
  Serial.println(level);   // to remind us where we're starting
  
  pinMode(pinD, OUTPUT); 
  lcd.begin(16,2);   // Inicializa el objeto LCD, 16x2 
  lcd.backlight();   //Enciende el backligth
  lcd.clear();       //Limpia la pantalla LCD 

  //lcd.setCursor(0,0); 
  //lcd.print("POSIC. ");
  //lcd.setCursor(0,1); 
  //lcd.print("CONTADOR ");
}

void loop(){
swE.poll(); //← FUNCION NECESARIA PARA REFRESCAR EL ESTADO DEL BOTON. 

if(contador == 5){  
   contador = 0;}
   
  if(swE.released()== HIGH){
            contador= contador+1 ;
            Serial.print("Contador ="); 
            Serial.println(contador); 
            digitalWrite(pinD, HIGH); 
            lcd.setCursor(9,1); 
            lcd.print(contador);}          
  
  else{
    digitalWrite(pinD, LOW); }
    lcd.setCursor(9,0); 
    lcd.print(level);

if(refresca_menu == true){
imprime_menu(); 
refresca_menu = false; 
}
}

void knobTurned(){
  state = 0;                           // Inicia de 0 cada vez
  state = state + digitalRead(pinA);   // Agrega el estado del Pin A 
  state <<= 1;                         // Mueve el bit a la izq, un lugar.
  state = state + digitalRead(pinB);   // Agrega el estado del Pin B
  level = level + bump[state];         //Guarda cada vez en level, el level y le agrega 
                                       //o resta de acuerdo a la tabla bump.
if(level >limMenuSup) level = limMenuInf +1; //lim superior del menu 
if(level <limMenuInf) level = limMenuSup -1; //lim inferior del menu

num_menu =map(level, -5, 5, 0,total_menu); 
refresca_menu = true; 

  /* Let's see what happened */
  Serial.print(bits[state] + "    ");  // show us the two bits
  Serial.print(bump[state],DEC);       // show us the direction of the turn
  Serial.print("    ");
  Serial.println(level);               // show us the new value
  Serial.println(num_menu);               // show us the new value
}


void imprime_menu(){
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(items_menu[num_menu]);
}