Horno de curado para materiales compuestos
Este es el proyecto final del Grupo 2 de la asignatura de Diseño de Sistemas Empotrados del grado de Ingeniería de Computadores formado por:
- Álvaro Palomo Mazo
- Raúl Rebollo Pascual
- Marcia García de la Mata Pinto
- Rocío Arranz Esteban
Objetivo
El objetivo de esta práctica era la adaptación de un horno doméstico en un horno especializado en el curado de materiales compuestos usando Arduino como requisito esencial. Dicho horno permitirá a los usuarios curar materiales como fibra de carbono principalmente.
Material compuesto: Material formado por dos o más componentes, de forma que las propiedades del material final sean superiores que las de los componentes por separado.
Inicialmente elegimos un proyecto completamente diferente a este, un pequeño vehículo que detectase obstáculos mediante inteligencia artificial, haciendo uso del kit de machine learning. Sin embargo, rápidamente echamos esa idea para atrás debido a dificultades y problemas recurrentes con el módulo de la cámara y el tamaño máximo que debía tener el código de manera que lo pudiese ejecutar una Arduino nano.
Tras recapacitar bien acerca de la nueva dirección que tomaría nuestro trabajo, decidimos enfocar nuestros esfuerzos en este proyecto de manera que tenga una finalidad práctica tocando un aspecto de la ingeniería que aparentemente no tiene relación ninguna con la informática como lo es trabajar con materiales compuestos pero que, sin embargo, lo tenga todo que ver con ella debido a la importancia del diseño de sistemas empotrados.
Diseño
El diseño de nuestro proyecto se dividió en varias partes:
- El diseño de la propia estructura física
- El diseño de la electrónica a implementar
Mostraremos los diagramas y bocetos que planteamos para cada uno.
En el dibujo superior podemos apreciar la idea de la implementación.
Puesto que el horno va a curar materiales compuestos (principalmente fibra de carbono y esta para curarse necesita una condición de vacío), incluimos un tubo de vacío el cual incluiremos mediante la perforación de un agujero en el lateral derecho del horno que permitirá la extracción de aire. Colocaremos también un sensor de temperatura, adaptando la estructura del horno para ello.
Modificaremos el panel de control original del horno, quitando o desactivando las ruedas y botones innecesarios. Incluiremos una pantalla táctil en el panel, para ello hicimos un hueco para la pantalla cortando el panel original usando una radial. También añadimos un nuevo interruptor que corta la corriente del sistema. Mantendremos el botón de la iluminación del horno, el piloto que indica si el horno se encuentra en funcionamiento (listo para calentar), y la rueda de selección de los elementos calefactores del horno (superior, inferior o ambos a la vez).
También colocábamos nuestro sensor de temperatura en la posición original donde se encontraba el sensor de temperatura original del fabricante, que tuvo que ser eliminado junto al termostato original para hacer esta adaptación. El sensor mlx90614 fue elegido debido a que ya lo teníamos disponible y es el sensor mas económico que puede soportar estas temperaturas, al principio al ser un sensor infrarrojo planteamos la posibilidad de que leyera la temperatura de la superficie del material, sin embargo después descubrimos que el rango de este era bastante limitado (unos 3-5 cm) y montarlo en la parte de arriba del horno apuntado a la pieza habría reducido su vida útil además de afectar a las medidas debido a que se encontraría pegado a la resistencia superior. Por lo que finalmente se opto por montarlo en la parte de atrás apuntando a la puerta del horno de manera que lea la temperatura del aire.
Planteamos la localización de la electrónica justo encima del horno y tras el panel delantero, ya que nos permite acceder a todo el cableado necesario y permitiéndonos ocultarlo más fácilmente.
Para el diseño hemos utilizado una pantalla , la cual es un dispositivo HMI de Nextion, lo que nos proporciona una interfaz gráfica de usuario. El pin TX (emisor) de la pantalla va conectada al pin RX (receptor) del Arduino, a su vez, el pin TX del Arduino irá conectado al RX de la pantalla.
El pin 4 de Arduino va conectado a un Relé que es el que se encarga de abrir el paso a la corriente o cerrarlo, una vez permite el paso de la corriente llegamos al selector de elemento de calefactor, este selector nos permite elegir si queremos que el horno caliente en la parte de arriba, en la de abajo o ambas. El sensor de temperatura se encontrará soldado a la placa.
Para proporcionar alimentación y tierra a todos los componentes usamos un cargador normal de teléfono, todos los cargadores tienen en su interior una bobina llamada transformador que se encarga de transformar los 220 voltios de alterna en 5 voltios de continua. La transformación de voltajes se llevará o no al resto del circuito dependiendo del interruptor del panel frontal que mencionamos en el diagrama anterior.
MAteriales
La lista de materiales que hemos utilizado es la siguiente:
- Horno de cocina 30€
- Pantalla Nextion NX4827P043 40€
- Sensor de temperatura MLX90614 5€
- Arduino nano 15€
- Cargador Samsung 10€
- Tubo de vacío 20€
- Relé 5v 3.50€
TOTAL …………………………………………………………….. 123,50€
Código
El código por su parte necesitará la implementación de las librerías:
#include <EasyNextionLibrary.h>
#include <trigger.h>
#include <Adafruit_MLX90614.h>
#include <timer.h>
Y su programación es el siguiente:
void setup()
{
myNex.begin(9600);
pinMode(pinRele, OUTPUT); // Rele
digitalWrite(pinRele, HIGH); // Abrimos el rele por seguridad
// TIMERS
timerMain.setInterval(60000);
timerMain.setCallback(updateTime);
Serial.begin(9600);
if (!mlx.begin()) exception("No se ha podido inicializar el sensor de temperatura");
modoSeleccionado = 0; // modo selecionado 0 - normal 1 - curvas
// Declaramos los programas en el array progrmas
/****FIBRA****/
programas[0].nombre = "fibra";
programas[0].tempObj = 90;
programas[0].progresion = 1;
programas[0].tiempo = 90;
/****BIAXIAL****/
programas[1].nombre = "biaxial";
programas[1].tempObj = 135;
programas[1].progresion = 1;
programas[1].tiempo = 60;
/****FIBRA DE VIDIRIO****/
programas[1].nombre = "F Vidrio";
programas[1].tempObj = 125;
programas[1].progresion = 1;
programas[1].tiempo = 60;
}
void loop()
{
safetyWatchdog();
temperaturaSensor = (int) mlx.readObjectTempC();
if(temperaturaSensor == NAN) exception("Fallo en el sensor de temperatura");
//temperaturaSensor = TEMP;
modoSeleccionado = myNex.readNumber("cb0.val");
switch (modoSeleccionado)
{
case 0:
if (myNex.readNumber("sw0.val") == 1) {
if (timerMain.isStopped() == true)
{
timerMain.start();
tiempoRestante = myNex.readNumber("n2.val"); // Leemos pantalla y lo pasamos a minutos
}
timerMain.update();
myNex.writeNum("n2.val", tiempoRestante);
temperaturaDeseada = myNex.readNumber("n0.val");
if (tiempoRestante > 0) updateHeaterState(temperaturaDeseada);
else
{
//updateHeaterState(0); //Desactivamos el calefactor por seguridad
myNex.writeNum("sw0.val", 0);
digitalWrite(pinRele, HIGH); // Como el tiempo ha terminado reseteamos el switch a 0.
}
}
else {
timerMain.stop(); //Reseteamos el temporizador
digitalWrite(pinRele, HIGH); //Desactivamos el elemento calefactor por seguridad
}
break;
case 1: // CURVAS
/*Indicar programa*/
programaSeleccionado = myNex.readNumber("cb1.val");
myNex.writeNum("n2.val", programas[programaSeleccionado].tempObj);
myNex.writeNum("n3.val", programas[programaSeleccionado].progresion);
myNex.writeNum("n4.val", programas[programaSeleccionado].tiempo);
if (myNex.readNumber("sw0.val") == 1) {
if (timerMain.isStopped() == true)
{
timerMain.start();
tiempoRestante = programas[programaSeleccionado].tiempo;
temperaturaDeseada = temperaturaSensor;
}
myNex.writeNum("n0.val", tiempoRestante);
myNex.writeNum("n5.val", temperaturaDeseada);
if (tiempoRestante > 0) updateHeaterState(temperaturaDeseada);
else {
myNex.writeNum("sw0.val", 0);
digitalWrite(pinRele, HIGH);
}
}
else {
timerMain.stop();
digitalWrite(pinRele, HIGH); //Desactivamos el elemento calefactor por seguridad
}
break;
}
myNex.NextionListen();
myNex.writeNum("n1.val", (uint32_t) temperaturaSensor);
delay(100);
}
En el void setup inicializamos las distintas curvas de curado usando un struct y también inicializaremos un temporizador que llamara a la función «updateTime» cada minuto para actualizar el tiempo restante y la temperatura en el caso de las curvas.
Después en el void loop tenemos un switch que se encarga de seleccionar en que modo de funcionamiento estamos, modo normal o modo curvas, y posteriormente si el usuario pone el horno en funcionamiento usando el switch de la pantalla, se actualiza los valores de la pantalla y el estado del elemento calefactor para llegar a la temperatura objetivo actual.
Puesto que la seguridad es nuestra prioridad al tratarse de una máquina que trabaja enchufada a la red eléctrica y con temperaturas altas hemos, además de que se le va a dar un uso durante bastante tiempo, hemos implementado algunas medidas de seguridad.
void safetyWatchdog() {
if(temperaturaSensor > 150) {
myNex.writeStr("page page 3");
digitalWrite(pinRele, HIGH);
delay(100);
}
}
void exception(String mensajeExcepcion) {
while(true) {
myNex.writeStr("page page 4");
myNex.writeStr("t1.txt", mensajeExcepcion);
digitalWrite(pinRele, HIGH);
delay(100);
}
La primera «safetyWatchdog()» se ejecuta en cada iteración del loop y comprueba que la temperatura no exceda 150 ºC algo que podría llegar a ocurrir si el rele se estropea y se queda atascado en su posición cerrada, cuando esto ocurre se pone un mensaje de alerta en la pantalla Nextion pidiéndole al operario que desconecte el dispositivo de manera inmediata ya que de lo contrario este podría llegar a incendiarse.
La segunda función «exception()» se utiliza para mostrar mensajes de menor gravedad, como que el sensor de temperatura ha dejado de funcionar, y parar el funcionamiento del horno.
Código completo
#include <EasyNextionLibrary.h>
#include <trigger.h>
#include <Adafruit_MLX90614.h>
#include <timer.h>
/////////////////////////// VARIABLES /////////////////////////////
#define pinRele 4
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
EasyNex myNex(Serial);
struct Programa
{
String nombre; // Tipo de programa
int tempObj; // En grados
int tiempo; // En minutos
int progresion; // En grados/minuto
};
Programa* programas = (Programa*) malloc(10 * sizeof(Programa)); // Array que almacena las curvas
// Variables globales
Timer timerMain;
int temperaturaSensor;
int temperaturaDeseada;
long tiempoRestante;
int modoSeleccionado;
int programaSeleccionado;
///////////////////////////////////////////////////////////////////
/////////////////////////// FUNCIONES /////////////////////////////
// Funcion que actualiza el estado del elemento calefactor
void updateHeaterState(int temperaturaSel)
{
temperaturaDeseada = temperaturaSel;
temperaturaSensor = (int) mlx.readObjectTempC();
if (temperaturaSensor < temperaturaSel)
digitalWrite(pinRele,LOW); // Encendemos la resistencia
else if (temperaturaSensor >= temperaturaSel + 2)
digitalWrite(pinRele, HIGH); // Apgamos la resistencia
}
void safetyWatchdog() {
if(temperaturaSensor > 150) {
myNex.writeStr("page page 3");
digitalWrite(pinRele, HIGH);
delay(100);
}
}
void exception(String mensajeExcepcion) {
while(true) {
myNex.writeStr("page page 4");
myNex.writeStr("t1.txt", mensajeExcepcion);
digitalWrite(pinRele, HIGH);
delay(100);
}
}
void updateTime() {
tiempoRestante--;
if(programaSeleccionado == 1 && temperaturaDeseada < programas[programaSeleccionado].tempObj) temperaturaDeseada += programas[programaSeleccionado].progresion; //Actualizamos la curva
}
///////////////////////////////////////////////////////////////////
void setup()
{
myNex.begin(9600);
pinMode(pinRele, OUTPUT); // Rele
digitalWrite(pinRele, HIGH); // Abrimos el rele por seguridad
// TIMERS
timerMain.setInterval(60000);
timerMain.setCallback(updateTime);
Serial.begin(9600);
if (!mlx.begin()) exception("No se ha podido inicializar el sensor de temperatura");
modoSeleccionado = 0; // modo selecionado 0 - normal 1 - curvas
// Declaramos los programas en el array progrmas
/****FIBRA****/
programas[0].nombre = "fibra";
programas[0].tempObj = 90;
programas[0].progresion = 1;
programas[0].tiempo = 90;
/****BIAXIAL****/
programas[1].nombre = "biaxial";
programas[1].tempObj = 135;
programas[1].progresion = 1;
programas[1].tiempo = 60;
/****FIBRA DE VIDIRIO****/
programas[1].nombre = "F Vidrio";
programas[1].tempObj = 125;
programas[1].progresion = 1;
programas[1].tiempo = 60;
}
void loop()
{
safetyWatchdog();
temperaturaSensor = (int) mlx.readObjectTempC();
if(temperaturaSensor == NAN) exception("Fallo en el sensor de temperatura");
modoSeleccionado = myNex.readNumber("cb0.val");
switch (modoSeleccionado)
{
case 0:
if (myNex.readNumber("sw0.val") == 1) {
if (timerMain.isStopped() == true)
{
timerMain.start();
tiempoRestante = myNex.readNumber("n2.val"); // Leemos pantalla y lo pasamos a minutos
}
timerMain.update();
myNex.writeNum("n2.val", tiempoRestante);
temperaturaDeseada = myNex.readNumber("n0.val");
if (tiempoRestante > 0) updateHeaterState(temperaturaDeseada);
else
{
myNex.writeNum("sw0.val", 0);
digitalWrite(pinRele, HIGH); // Como el tiempo ha terminado reseteamos el switch a 0.
}
}
else {
timerMain.stop(); //Reseteamos el temporizador
digitalWrite(pinRele, HIGH); //Desactivamos el elemento calefactor por seguridad
}
break;
case 1: // CURVAS
/*Indicar programa*/
programaSeleccionado = myNex.readNumber("cb1.val");
myNex.writeNum("n2.val", programas[programaSeleccionado].tempObj);
myNex.writeNum("n3.val", programas[programaSeleccionado].progresion);
myNex.writeNum("n4.val", programas[programaSeleccionado].tiempo);
if (myNex.readNumber("sw0.val") == 1) {
if (timerMain.isStopped() == true)
{
timerMain.start();
tiempoRestante = programas[programaSeleccionado].tiempo;
temperaturaDeseada = temperaturaSensor;
}
myNex.writeNum("n0.val", tiempoRestante);
myNex.writeNum("n5.val", temperaturaDeseada);
if (tiempoRestante > 0) updateHeaterState(temperaturaDeseada);
else {
myNex.writeNum("sw0.val", 0);
digitalWrite(pinRele, HIGH);
}
}
else {
timerMain.stop();
digitalWrite(pinRele, HIGH); //Desactivamos el elemento calefactor por seguridad
}
break;
}
myNex.NextionListen();
myNex.writeNum("n1.val", (uint32_t) temperaturaSensor);
delay(100);
}
Interfaces
Puesto que tratamos con una pantalla táctil necesitábamos no solo mostrar la información relevante, sino que también necesitábamos implementar los botones y las opciones no físicas. Para ello diseñamos las siguientes pantallas mediante el propio programa ofrecido por el fabricante de nuestro modelo, Nextion.
En este modo, el horno tiene un funcionamiento muy similar a un horno doméstico convencional.
En el cuadro de arriba a la izquierda se muestra la temperatura (ºC) seleccionada mediante los botones + y – de la izquierda de la pantalla, marcados con una Tº en el medio.
En el cuadro de arriba a la derecha se muestra la temperatura (ºC) captada por el sensor dentro del horno.
Abajo a la izquierda y a la derecha se muestran los minutos del temporizador seleccionado (para modificar este valor tenemos los botones + y – marcados con un símbolo de un reloj entre ellos).
Arriba del todo a la izquierda nos encontraremos con el selector de modos, este varía entre el normal y las curvas de curado.
Y por último, el botón de abajo del todo a la derecha nos encontramos con un interruptor que inicia o para el programa que hemos configurado con las opciones anteriores.
En este otro modo, lo que más puede destacar es la gráfica que nos indica el progreso de la temperatura con las lecturas del sensor, de esta manera podemos observar el estado del proceso de curado y si la curva es la deseada o se esta produciendo alguna anomalía en el funcionamiento.
En la derecha nos encontramos con toda la información referente al tipo de curado que se va a realizar, pudiendo elegir este con el selector de arriba del todo en este mismo lado. Entre la información que se nos presenta nos encontraremos las variables constantes de la curva seleccionada, empezando por la temperatura objetivo final, la progresión del curado (de 0 – 100%) y la duración total del proceso.
Debajo de esta información, nos encontraremos con la temperatura actual y la objetivo (se va actualizando en intervalos) y el tiempo restante.
Al igual que en el modo anterior, tendremos un interruptor abajo a la derecha para iniciar el programa.
Por último, diseñamos dos pantallas de mensajes de error para avisar a los usuarios en caso de que su seguridad esté comprometida si se continua usando en la condición de error y se requiera que este, desconecte inmediatamente el aparato de la corriente por un posible sobrecalentamiento o para indicar que se ha producido un error grave como que se haya perdido la comunicación de la Arduino con el sensor de temperatura.
Implementación y resultados
Una vez explicado todo lo anterior, para facilitar la comprensión de nuestro proyecto, mostraremos los resultados tras la implementación.
No tuvimos demasiadas dificultades más allá de aprender a usar las herramientas y pelearnos con el código para lograr los resultados que buscábamos, sin embargo estos son algunos de los problemas con los que nos encontramos:
- Al principio la pantalla presentaba errores al leer el valor de la temperatura, posteriormente descubrimos que esto se producía debido a que al imprimir mensajes en el bus serie estábamos saturando el bus de manera que la pantalla no recibía sus mensajes.
- Tuvimos que cambiar de cargador debido a que al principio usábamos uno que solo podía entregar un 1 A por lo que cuando se accionaba el relé el sistema se reiniciaba debido a la falta de potencia, al principio probamos con reducir el brillo de la pantalla para que esta consumiera menos energía, pero finalmente decidimos usar un cargador más potente.
- Tuvimos que añadir un margen en la función updateHeaterState() debido a que el relé se estaba encendiendo y apagando muy frecuentemente lo que podría reducir su vida útil significativamente sobre todo con la corriente que estamos manejando.
Y por último, el vídeo con la demostración de su funcionamiento:
Esperamos que os haya gustado nuestro proyecto.