En el artículo técnico anterior, "Introducción a RS485 para Smart Display", presentamos los conceptos básicos de la comunicación RS485 y del protocolo Modbus. En este artículo avanzaremos con un ejemplo práctico de varias páginas para explicar algunas técnicas habituales en el desarrollo con Smart Display, entre ellas:
- Cómo cambiar de página mediante el Button widget.
- Cómo actualizar los valores de los widgets después de cambiar de página.
- Cómo generar señales acústicas de distinta duración con el buzzer.
- Cómo utilizar el Graph widget.
Material necesario
- Un Smart Display con interfaz RS485. En este ejemplo utilizaremos un Smart Display de 3,5".
- Arduino (Uno o Mega2560).
- Placa de expansión RS485 para Arduino (Enlace a DFRobot).
- Potenciómetro (VR).
Conexiones
- Potenciómetro: conecte Vcc a 5 V, GND a GND y la salida a la entrada A0 del Arduino.
- Conecte el terminal A de la placa de expansión RS485 a RS485+ del Smart Display.
- Conecte el terminal B de la placa de expansión RS485 a RS485- del Smart Display.
Contenido del ejemplo
Este proyecto consta de tres páginas, entre las que se puede cambiar mediante el Button widget. El contenido y las funciones de cada página son los siguientes:
Página 1:
En el lado derecho hay un botón para avanzar a la página siguiente y, debajo, un Horizontal Slider que permite ajustar el brillo de la pantalla. La vista previa se muestra a continuación:

Página 2:
En el lado izquierdo hay un botón para volver a la página anterior y, en el lado derecho, otro para avanzar a la página siguiente. Además, los tres botones situados en la parte inferior permiten activar el buzzer con señales acústicas de duración corta, media y larga.

Página 3:
En el lado izquierdo hay un botón para volver a la página anterior. Se utiliza un potenciómetro para simular una entrada analógica y los valores obtenidos se muestran mediante el Graph widget.

Como en este ejemplo se utiliza una placa de expansión RS485, antes de comenzar con la programación es necesario descargar la biblioteca correspondiente mediante el Library Manager:

Creación del contenido de las páginas
En primer lugar, utilice el GUI Builder para crear el contenido de las páginas. El texto puede introducirse directamente en el Text widget de cada página, por lo que no es necesario definirlo en el programa.

Los detalles de los widgets utilizados se muestran en la siguiente tabla:

Puede elegir el modelo de Smart Display en función de los recursos disponibles y utilizar distintos widgets para crear la interfaz deseada. Lo importante es que el widget ID y el type de cada página coincidan con las definiciones utilizadas en el programa. Para simplificar el desarrollo, se recomienda crear el contenido de las páginas tomando como referencia el widget ID y el type indicados en esta tabla.
A continuación, pasaremos a la parte de programación.
Explicación del programa
(1) Archivos de inclusión
Después de descargar la biblioteca ModbusMaster, asegúrese de incluirla mediante la directiva #include:

Además, para facilitar la gestión, hemos agrupado algunas constantes relacionadas con Smart Display en el archivo SmartDisplayModbus.h. Asegúrese de incluir también este archivo.
(2) Conversión del Widget ID a la dirección de registro
En el artículo anterior se explicó el método para convertir el widget ID en la dirección de registro correspondiente, tal como se muestra a continuación:

Sin embargo, esta tabla se creó cuando cada página admitía un máximo de 10 widgets. Actualmente, cada página puede contener hasta 64 widgets. La tabla sigue siendo válida para los primeros 10 widgets, pero a partir del undécimo es necesario utilizar un nuevo bloque de registros. Para ello, hemos definido las siguientes macros:

Para facilitar su uso, definimos una función que realiza las operaciones de lectura y escritura a partir del widget ID y efectúa internamente la conversión a la dirección de registro correspondiente:

(3) Widget ID y diseño de la GUI
Al crear el contenido de las páginas con el GUI Builder, hemos indicado que los widget IDs deben mantenerse coherentes con las definiciones utilizadas en el programa. Por este motivo, las constantes correspondientes a los widget IDs se definen de la siguiente manera:

Estas constantes facilitan la identificación del tipo de widget y permiten compararlo con el diseño real de las páginas. Si posteriormente se modifica el diseño de la GUI, recuerde comprobar también estos valores para evitar errores.
(4) Buzzer
El firmware controla el buzzer mediante contadores para generar la señal 0/1 y utiliza otros contadores para controlar los intervalos de activación y desactivación del sonido. Para generar la señal acústica, es necesario escribir en cuatro registros:

Para ajustar la duración del sonido, creamos una función que recibe el parámetro SMARTDISPLAY_BUZZER_HIGH:

El buzzer solo se activa cuando el valor escrito en SMARTDISPLAY_BUZZER_ACTIVE es diferente del valor utilizado en la escritura anterior. Por este motivo, este valor se mantiene como static dentro de la función y se invierte antes de cada llamada.
Por último, en la función de gestión de la página se pueden generar señales acústicas de distinta duración en función del estado de los botones:

(5) Cambio de página
Antes de cambiar de página, es necesario entrar en CONFIG_MODE. Una vez configurada la nueva página, se puede volver a DISPLAY_MODE. Durante el cambio de página, el Smart Display vuelve a cargar todos los widgets de la nueva página. Para evitar operaciones de lectura o escritura antes de que finalice este proceso, normalmente se añade un tiempo de espera suficiente. La duración necesaria depende de la complejidad de la página. Si se observa algún comportamiento anómalo después de cambiar de página, puede aumentarse este tiempo de espera para comprobar si el problema se resuelve.

Después de cambiar de página, los widgets de la nueva página se cargan con sus valores predeterminados. Por este motivo, implementamos un proceso que gestiona el cambio de página, guarda los valores de los widgets y los restaura cuando es necesario.
En primer lugar, definimos un puntero a función para gestionar las páginas. De este modo, podemos crear una tabla de páginas y asignar a cada una su correspondiente función de gestión:

A continuación, creamos la tabla de páginas:

La constante MAX_WIDGETS_PER_PAGE define el número de widgets cuyos valores se guardan en cada página. Si posteriormente se añaden más widgets, será necesario actualizar este valor.
Por último, la función loop() se encarga de gestionar el cambio de página y el procesamiento de los widgets:

Si la página actual _curPage es distinta de la siguiente página _nextPage, el indicador de cambio de página newPage se establece en true. De este modo, la función de gestión de la página sabe que debe realizar la inicialización correspondiente. Veamos Page0 como ejemplo:

Cuando el parámetro init es true, significa que acabamos de entrar en la página. En este caso, establecemos WIDGET_P0_BUTTON_NEXT en 0. Al cargar una nueva página, el valor predeterminado del botón es 0 y queremos ejecutar una acción específica —en este caso, cambiar a PAGE_1— únicamente cuando el botón se pulse y su valor cambie de 0 a 1. Por este motivo, es necesario restablecer el valor guardado a 0, ya que podría haberse cambiado anteriormente a 1.
En el caso del Slider widget, su valor inicial también es 0 cuando se carga la página. Sin embargo, si anteriormente se ha utilizado para ajustar el brillo, debemos restaurar el valor actual almacenado en _brightness.
En el resto de los casos, cuando init es false, podemos procesar cada widget en función de si su valor ha cambiado. Al finalizar, guardamos los valores de todos los widgets para utilizarlos como referencia en el siguiente ciclo de procesamiento.
Para establecer la siguiente página mediante _nextPage, puede utilizarse la siguiente función:

Si la función de gestión de la página comprueba el estado de los widgets en cada ciclo y detecta que el valor de un botón ha cambiado de 0 a 1, puede llamar a la función correspondiente para solicitar el cambio de página. El siguiente ejemplo muestra cómo hacerlo:

(6) Graph Widget
El Graph widget permite representar gráficamente los valores que se escriben en él. En este ejemplo, utilizamos una resistencia variable conectada a A0 para simular una entrada analógica y mostramos la variación de los datos mediante el Graph widget. Como la entrada analógica de Arduino tiene un rango de 0 a 1023 y el Graph widget admite valores de 0 a 100, utilizamos la función map() para adaptar el rango.
El Graph widget está diseñado para actualizar la visualización únicamente cuando cambia el valor. Si se desea obtener un efecto de actualización periódica, puede utilizarse un pequeño recurso: comprobar el valor periódicamente y, si coincide con el valor leído anteriormente, modificarlo ligeramente sumando o restando 1. De este modo, el gráfico puede mostrar una actualización visual continua aunque el valor de entrada permanezca estable.

Conclusión
Con esto finaliza la explicación de este programa de ejemplo. Además de facilitar la comprensión de las funciones descritas, este proyecto puede utilizarse como base para futuros desarrollos y ayudar a reducir el tiempo necesario para implementar aplicaciones similares. El programa completo está disponible en el siguiente enlace: Modbus-MultiPageOperation en GitHub.
Si todavía no está familiarizado con RS485/Modbus, puede consultar el artículo anterior para conocer los conceptos básicos y el funcionamiento de esta comunicación: Introducción a RS485 para Smart Display.