Definición de interfaz en Arduino?

Estoy escribiendo un nuevo proyecto de Arduino que utiliza una pantalla de cristal LCD de 16x2 con KeyShield. Me gustaría construir una estructura de menú en la que el usuario pueda navegar usando los botones.

Soy nuevo en Arduino pero tengo 5 años de experiencia en C#. Lo que me gustaría hacer es lo siguiente:

Defina una interfaz llamada Screeny herede cada Pantalla de ella, como LanguageScreen, TurnOffAfterScreen, MainScreen, etc. Cada Pantalla especializada implementa un método llamado drawScreenque se encargará del contenido de la pantalla.

Ahora me gustaría tener una clase nombrada ScreenControllerque maneje la navegación entre las pantallas. En esta clase, me gustaría almacenar las pantallas en una matriz Screen screens[3].

Cuando el usuario presiona el Selectbotón en KeyShield, el controlador llamará al siguiente drawScreenmétodo de pantalla de la siguiente manera:

void showNextScreen()
{
   currentScreen++;
   screens[currentScreen]->drawScreen();
}

¿Es esto posible de hacer en un proyecto Arduino? ¿Estoy esperando demasiado? :)

No hay ninguna razón particular por la que esto no pueda funcionar. Sin embargo, en C++, Screentendría que ser una clase base (abstracta), y su matriz tendría que ser Screen * screens[3].
¡Suena bien! ¿Puedes publicar algo de código también?
He agregado algo de código en una respuesta a continuación. Este es un código C ++ realmente sencillo con poca relevancia específica de Arduino.

Respuestas (2)

class Screen {
public:
   virtual void drawScreen() = 0;  // Needs to be implemented by each subclass

   virtual ~Screen() {}            // Destructor
};

class LanguageScreen : public Screen {
public:
   virtual void drawScreen();
};

class MainScreen : public Screen {
public:
   virtual void drawScreen();
};

void LanguageScreen::drawScreen() {
   // Draw language screen
}

void MainScreen::drawScreen() {
   // Draw main screen
}

MainScreen     mainScreen;
LanguageScreen languageScreen;
Screen *const  screens[2]   = {&mainScreen, &languageScreen};
int            currenScreen = 0;
Sería una buena idea evitar llamar a new en un ejemplo de microcontrolador. Declararlos como estáticos/globales y luego usar sus direcciones.
Buen punto, @walrii. He revisado el código para hacer eso.
¡Muchas gracias! Esto fue muy útil. Ahora tengo una buena estructura de menú usando un código OOP bien definido :)
Vengo de PHP y espero recibir un mensaje de error si no implemento un método en una clase que se basa en una interfaz. class Screen { public: virtual void drawScreen() = 0; // Needs to be implemented by each subclass virtual ~Screen() {} // Destructor }; class LanguageScreen : public Screen { // I didn't define the void drawScreen(); };Entonces, en realidad quiero recibir un mensaje de que falta el método drawScreen() en la clase LanguageScreen ya que implementa la pantalla de clase de "interfaz". ¿O es esto diferente a PHP?
@alve89 si intenta esto en C++, encontrará que = 0;en la declaración original también garantizará un mensaje de error si intenta usar una subclase que no implementa drawScreen. La diferencia es que puede heredar en varios niveles, y solo uno de ellos necesita implementar el método.
@microtherion Gracias! Probé y funcionó. El problema era que esperaba que se mostrara el mensaje de error al compilar sin instanciar el objeto, pero el error aparece cuando el objeto está definido únicamente. Pero ahora lo sé, ¡gracias de nuevo!

El uso de clases de C++ en un microcontrolador con memoria limitada generalmente no se considera una estrategia de desarrollo viable. Hay formas literalmente ilimitadas en las que puede construir un "sistema de menús" en una pantalla sin tener que recurrir al estilo de programación OOP intensivo en recursos.

Editar: estoy sorprendido por la reacción del lector al poner muchos votos negativos en mi respuesta. Puedo decirle que con más de 30 años de experiencia en el desarrollo de soluciones de código y firmware para microcontroladores de muchos tipos, la declaración de mi respuesta es cierta. Hay muchas aplicaciones que usan MCU de bajo rendimiento (en comparación con sus hermanos mayores en PC y similares) donde el programa a veces incluso necesita codificarse en lenguaje ensamblador para realizar el trabajo de manera eficiente y con baja latencia.

No será un gran proyecto, así que no tengo miedo de quedarme sin memoria. Prefiero OOP al código spagetti.
@papaiatis: un sistema de menú se puede codificar en un estilo de programación agradable y limpio en C o incluso en lenguaje ensamblador sin que se convierta en código spagetti. El último lío de código generalmente se produce cuando intenta programar algo sin tomarse el tiempo para hacer un diseño arquitectónico completo de la funcionalidad prevista que se va a desarrollar.
Puede que tengas razón, no tengo ninguna duda. Pero todavía estoy bastante interesado en la forma OOP.
El uso de clases de C ++ es viable en un microcontrolador siempre que limite la cantidad de memoria que puede usar una clase dada. La creación de menús dinámicos podría ser problemática si tiene un estado persistente, pero si solo proporciona funcionalidad de representación y selección, debería estar bien. También tendrá que ver cuánto espacio de código está utilizando al crear este conveniente modelo de objetos, ya que este tipo de construcciones pueden ocupar un espacio significativo. Eso es todo.
La biblioteca de E/S de Arduino es, de hecho, un diseño OOP muy convencional, construido en torno a un conjunto de clases con métodos virtuales. Hay poco en el estilo de programación OOP que es inherentemente "intensivo en recursos".
Estoy de acuerdo: dada la memoria limitada de estos cachorros, tiene razón con su respuesta. Tampoco me gustan los botones de estos protectores LCD, ya que usan una resistencia variable para cada botón a través de una entrada analógica, y el protector que tengo da valores muy diferentes cada vez.
+1 - Estoy de acuerdo con una advertencia. Sin embargo, algunas características del lenguaje son más costosas que otras y un subconjunto limitado puede ser bastante útil. La lista combinada puede ser tu amiga aquí. OTOH, si sabe que estará presionando con fuerza contra los límites del hardware y probablemente tendrá que optimizar, será mejor que comience con las técnicas de desarrollo más eficientes en hardware para las que pueda permitirse los recursos humanos y el tiempo de desarrollo. - es decir, posponga la optimización tanto como pueda, pero prepárese para hacerlo lo más fácil posible.
A juzgar por la forma en que la gente usa Arduino, estoy bastante seguro de que ejecutar un atenuador de luz con código OOP no será un problema de memoria. Sin embargo, cuando necesita exprimir mucho de un pequeño dispositivo, OOP está fuera de valor práctico en lugar de la programación orientada a objetos. Pruebe una biblioteca de clases en un Amtel Tiny, por ejemplo. Voté esto porque creo que esta respuesta tiene un punto muy válido.