Sistema automático de control de acuarios en Arduino

Me gustaría compartir mi primera experiencia en la creación de un acuario Arduino. Anteriormente, no trabajaba en absoluto con la electrónica y, además, no sabía cómo se programan los microcontroladores. Sin embargo, decidí probar suerte y me gustaría compartir los resultados.



La idea de crear un acuario.


Dio la casualidad de que estaba principalmente involucrado en la programación .NET y aprendí a evitar C ++. Esta es probablemente la razón por la que nunca me encontré con microcircuitos y microcontroladores, aunque el deseo de conocerlos creció casi todos los años. Especialmente, los últimos años cuando me enteré de Arduino. Pero era necesario encontrar una aplicación práctica para él. Y este problema se resolvió rápidamente.

Hay un acuario en nuestra habitación, y todos los días teníamos que subirnos debajo de la mesa y apagar la luz para los peces, y luego encenderla por la mañana. Además, los peces tenían que encender el calentador cuando estaban fríos y apagarlos cuando estaban calientes. A veces mi olvido provocó la muerte de peces en el acuario y tuve que comprar otros nuevos. Incluso los peces tuvieron que cambiar periódicamente 2/3 del agua. Y para nuestro acuario, este procedimiento fue muy largo y desagradable.

En primer lugar, busqué soluciones listas para usar en acuarios. Hay muchos de ellos. Estos son principalmente videos en youtube. También hay artículos bastante interesantes sobre geektimes. Pero para mi propósito, estudiar y conocer el mundo de los microcircuitos, era demasiado complicado y no se encontró una guía detallada "desde cero" en Internet. La idea de desarrollar un controlador de acuario tuvo que posponerse hasta que se estudiaran los conceptos básicos de la microelectrónica.

Conocimiento de microelectrónica.


Comencé mi viaje con un kit listo para aprender Arduino. Probablemente, todos recogieron algo similar cuando se familiarizó con esta plataforma: una



bombilla ordinaria (LED), una resistencia de 220 ohmios. Arduino controla la bombilla utilizando un algoritmo C ++. Inmediatamente haga una reserva de que después de haber comprado cualquier conjunto de Arduino o su análogo, es imposible ensamblar algo más o menos útil. Bueno, excepto por el tweeter o, digamos, un termómetro casero. Es posible aprender la plataforma en sí a través de lecciones, pero no más. Por cosas útiles tienen que tuve que aprender de soldadura, placas de circuito impreso, diseño de placas de circuito impreso y otros placeres electrónicos.

Construye tu primer prototipo de acuario


Entonces, lo primero que comencé con mi prototipo de acuario fue formular requisitos de papel para este dispositivo.

El acuario debe:

  1. , , ;
  2. , , ( ) ;
  3. ( ) ;
  4. , , ;
  5. « »
  6. ;
  7. . , 9:00 AM;
  8. , ;
  9. .
  10. La pantalla con la fecha cuando presiona el botón en el control remoto debe estar resaltada. Si no se presiona nada en 5 segundos, entonces salga.

Decidí comenzar explorando el funcionamiento de la pantalla LCD y Arduino.

Crea el menú principal. Operación LCD


Para LCD, decidí usar la biblioteca LiquidCrystal. Casualmente, además de Arduino, también tenía una pantalla LCD en mi kit. Podía generar texto, números. Eso fue suficiente, y comencé a estudiar la conexión de esta pantalla con el Arduino. Tomé la información de conexión básica de aquí . También hay ejemplos de código para la salida de "Hello World".

Teniendo un pequeño trato con la pantalla, decidí crear el menú principal del controlador. El menú constaba de los siguientes elementos:

  1. Información básica;
  2. Configuracion de hora;
  3. Ajuste de fecha;
  4. Temperatura;
  5. Clima;
  6. Iluminar desde el fondo
  7. Dispositivos

Cada elemento es un modo específico de salida de información a la pantalla de texto LCD. Quería permitir la posibilidad de crear un menú de varios niveles, donde cada subnivel tendrá su propia implementación de la salida a la pantalla.

En realidad, la clase base se escribió en C ++, de la que se heredarán todos los demás submenús.

 class qQuariumMode
{
protected:
	LiquidCrystal* LcdLink;
public:

	//    ,   bool  isLcdUpdated.
	bool isLcdUpdated = false;
    
	//     .
	void exit();
	
	//  loop      . ,      
	//   .        .
	virtual void loop();

	// ,    virtual,      
	// . 
	virtual void OkClick();
	virtual void CancelClick();
	virtual void LeftClick();
	virtual void RightClick();
};

Por ejemplo, para el menú "Dispositivos", la implementación de la clase base qQuariumMode se verá así:

#include "qQuariumMode.h"
class qQuariumDevicesMode :
	public qQuariumMode
{
private:

	int deviceCategoryLastIndex = 4;
	
	//    
	enum DeviceCategory
	{
		MainLight, //   
		Aeration, //  
		Compressor, //  
		Vulcanius, //  
		Pump //  
	};
	
	DeviceCategory CurrentDeviceCategory = MainLight;

	char* headerDeviceCategoryText = NULL;

	//   "",      
	BaseOnOfDeviceHelper* GetDeviceHelper();

public:
	void loop();
	void OkClick();
	void CancelClick();
	void LeftClick();
	void RightClick();
};

Aquí está el resultado de la implementación del primer nivel del menú:



Pieza de hardware. Los matices de los componentes de conexión.


Quiero decir algunas palabras sobre el hardware del controlador del acuario. Para el funcionamiento normal del controlador, tuve que comprar:

  1. 1 x Arduino Uno / Mega. Más tarde decidió trabajar con Mego;
  2. 1 x reloj de tiempo real, por ejemplo DS1307;
  3. 2 x RTD14005, , .. 220 ;
  4. 1 x ;
  5. 1 x ;
  6. 5 x IRF-530 MOSFET N . (3 RGB , 1 , 1 );
  7. 1 x RGB . , . ;
  8. 1 x White ;
  9. 1 x LCD ;
  10. 1 x . DS18B20;
  11. 1 x . DHT11;

Cada componente tiene su propio tipo de conexión y sus controladores para la operación. No describiré los matices de conectar todos los componentes, ya que se pueden encontrar en el sitio web del fabricante o en los foros. Si planea usar los mismos componentes que yo, entonces no tendrá que cambiar el código fuente.

Deterioro de componentes


Ten cuidado. Intente leer primero sobre el componente del complemento. Debe ser operado con precisión en el rango de voltaje para el cual fue creado. Esto generalmente se indica en el sitio web del fabricante. Mientras desarrollaba el controlador del acuario, destruí 2 sensores de temperatura sellados y un reloj en tiempo real. Los sensores fallaron debido al hecho de que los conecté a 12V, y era necesario a 5V. El reloj en tiempo real murió debido a un cortocircuito "aleatorio" en el circuito debido a mi culpa.

Tira de LED RGB


Surgieron dificultades particulares con las tiras de LED. Traté de implementar el siguiente esquema:



Al conectarme a Arduino, utilicé pines que admiten PWM (modulación de ancho de pulso). Mientras encendía simultáneamente el voltaje máximo de los 3 pines, mi cinta estaba muy caliente. Como resultado, si lo deja durante una o dos horas, algunos LED dejarán de brillar. Creo que esto se debió a la falla de algunas resistencias. Otra desventaja de este esquema es el brillo diferente de la tira de LED para cada uno de los colores. Por ejemplo, si pongo el voltaje máximo en el componente rojo de la cinta, entonces obtengo el brillo condicional de la cinta roja en 255 unidades. Si enciendo los componentes rojo y azul al voltaje máximo, entonces el brillo será 255 + 255 = 510 unidades, y el color será púrpura. En general, esta solución no me convenía.

Se decidió implementar el siguiente algoritmo:

void LedRgbHelper::Show(RGBColorHelper colorToShow)
{	
	// RGBColorHelper         . 
	//  ,     
	int sumColorParts = colorToShow.RedPart + colorToShow.GreenPart + colorToShow.BluePart;

	//      
	float rK = 0;
	float gK = 0;
	float bK = 0;

	if (sumColorParts != 0)
	{
		float redPartAsFloat = (float)colorToShow.RedPart;
		float greenPartAsFloat = (float)colorToShow.GreenPart;
		float bluePartAsFloat = (float)colorToShow.BluePart;

		float sumColorPartsAsFloat = (float)sumColorParts;

		int brightness = colorToShow.Brightness;
		

		//       .
		rK = redPartAsFloat / sumColorPartsAsFloat;
		gK = greenPartAsFloat / sumColorPartsAsFloat;
		bK = bluePartAsFloat / sumColorPartsAsFloat;
		
		//      
		rK = rK*brightness;
		gK = gK*brightness;
		bK = bK*brightness;
	}
		
	uint8_t totalCParts = (uint8_t)rK + (uint8_t)gK + (uint8_t)bK;
	
	if (totalCParts <= 255){
		//      .        255 .
		analogWrite(RedPinNum, (uint8_t)rK);
		analogWrite(GreenPinNum, (uint8_t)gK);
		analogWrite(BluePinNum, (uint8_t)bK);
	}	
}

En esta realización, el color rojo y el color violeta tenían el mismo brillo. Aquellos. Los LED rojos en el primer caso brillaban con un brillo de 255 unidades, y con el violeta, el rojo tenía un brillo de 127 unidades y el azul con un brillo de 127 unidades, que al final era aproximadamente igual a 255 unidades:



Tira LED blanca


Con la tira de LED, fue probablemente la más fácil. El único momento difícil es asegurar un cambio suave en el brillo al cambiar la hora del día.

Para implementar esta idea, apliqué un algoritmo lineal para cambiar el brillo de una tira de LED blanca.

void MainLightHelper::HandleState()
{
	if (!IsFadeWasComplete)
	{
		unsigned long currentMillis = millis();
		if (currentMillis - previousMillis > 50) {
			previousMillis = currentMillis;

			switch (CurrentLevel)
			{
			case MainLightHelper::Off:
			{
				//    ,          .
				if (currentBright != 0)
				{
					if (currentBright > 0)
					{
						currentBright--;
					}
					else
					{
						currentBright++;
					}
				}
				else
				{
					//    ,     .
					currentBright = 0;
					IsFadeWasComplete = true;
				}
				break;
			}
			case MainLightHelper::Low:
			case MainLightHelper::Medium:
			case MainLightHelper::High:
			{
				//      ,         
				if (currentBright != CurrentLevel)
				{
					if (currentBright > CurrentLevel)
					{
						currentBright--;
					}
					else
					{
						currentBright++;
					}
				}
				else
				{
					currentBright = CurrentLevel;
					IsFadeWasComplete = true;
				}
			}
			break;
			}
			
			//         .
			analogWrite(PinNum, currentBright);
		}
	}
}

Ondulación "volcán"


La idea de implementación me vino por casualidad. Solo quería encender y apagar el volcán decorativo aplicando bajo voltaje y alto voltaje al transistor. En la tienda de pescado, cuidé de un buen volcán con un tubo de salida para el compresor y un LED aislado del agua.



Viene con un adaptador, cuya salida es de 12V DC, y la entrada es de 220V AC. No necesitaba un adaptador, porque implementé la potencia y el brillo del volcán a través de Arduino.

La pulsación del volcán mismo se implementó de la siguiente manera:

long time = 0;
int periode = 10000;

void VulcanusHelper::HandleState()
{
	if (IsActive){
		// time -  cos     . 
		//   -       
		time = millis();
		int value = 160 + 95 * cos(2 * PI / periode*time);

		analogWrite(PinNum, value);
	}
	else
	{
		analogWrite(PinNum, 0);
	}
}

El volcán ilumina perfectamente el acuario por la noche, y la ondulación en sí se ve muy hermosa:



Bomba de agua. Reemplazar el agua del acuario


Una bomba de agua ayuda a cambiar rápidamente el agua en el acuario. Compré una bomba que funciona con DC 12V. La bomba se controla a través de un transistor de efecto de campo. El controlador del dispositivo en sí puede hacer dos cosas: encender la bomba, apagar la bomba. Al implementar el controlador, simplemente heredé de la clase base BaseOnOfDeviceHelper y no definí nada más en el controlador. El algoritmo completo del dispositivo bien puede implementar la clase base.


Probé la bomba en el stand:



Aunque la bomba funcionaba bien, me encontré con una cosa no obvia. Si bombea agua a otro tanque, entonces la ley de los vasos comunicantes comienza a aplicarse. Como resultado, me convertí en el culpable de la inundación en la habitación, porque si apaga la bomba, el agua seguirá yendo a otro tanque, si su nivel de agua está por debajo del nivel del agua en el acuario. En mi caso, fue solo eso.

Puerto de infrarrojos y el deseo de reemplazarlo


Realicé el control del acuario por infrarrojos siguiendo el ejemplo de entrenamiento preliminar. La esencia del ejemplo es la siguiente: cuando enciendo el controlador en la red, interrogo las acciones de izquierda, derecha, arriba, abajo, ok. El usuario elige qué botones de control remoto asocia con cada una de las acciones. La ventaja de esta implementación es la capacidad de vincular cualquier control remoto innecesario.
El acuario se entrena a través del método Learn, cuya esencia se muestra a continuación:

void ButtonHandler::Learn(IRrecv* irrecvLink, LiquidCrystal* lcdLink)
{
	//      
	irrecvLink->enableIRIn();
	
	//       
	decode_results irDecodeResults;
	...
	...
		while (true) {
		//       
		if (irrecvLink->decode(&irDecodeResults)) {
						
			//   
			irrecvLink->resume();

			//     .
			if (irDecodeResults.bits >= 16 && 
				irDecodeResults.value != 0xC53A9966// fix for Pioneer DVD
				) {
			
				lcdLink->setCursor(0, 1);
				//        HEX
				lcdLink->print(irDecodeResults.value, HEX);
				
				//     Arduino  
				irRemoteButtonId = irDecodeResults.value;
				
				...
				...

Más tarde llegué a la conclusión de que el control remoto es inconveniente. Solo porque necesita buscarlo y este es un dispositivo adicional en la casa. Es mejor implementar el control a través de un teléfono móvil o tableta. Tuve la idea de utilizar el microordenador Raspberry PI para generar la aplicación web ASP.NET MVC 5 a través de Mono y NancyFX. A continuación, use el marco móvil jquery para aplicaciones web multiplataforma. A través de Raspberry, comunícate con Arduino a través de WiFi o LAN. En este caso, incluso puede abandonar la pantalla LCD, porque toda la información necesaria se puede ver en un teléfono inteligente o tableta. Pero este proyecto solo está en mi cabeza.

Placa de circuito impreso y su fabricación


De una forma u otra, llegué a la conclusión de que necesitamos hacer una placa de circuito impreso. Esto sucedió después de que aparecieron tantos cables en mi soporte que al ensamblar el dispositivo terminado, algunos de ellos comenzaron a desconectarse de la presión accidental de otros cables. Esto sucede de manera invisible a los ojos y puede conducir a resultados incomprensibles. Y la apariencia de tal dispositivo dejaba mucho que desear.

Montaje en placas de circuito (utilizado por Arduino Uno):



desarrollé un PCB de una sola capa en el programa Fritzing. Resultó lo siguiente (usando Arduino Mega):



Lo más desagradable de hacer una placa de circuito era perforar. Especialmente cuando intenté crear una placa de circuito tipo Shield, es decir, ella se estaba vistiendo con un arduino. Perforar más de 50 agujeros con un taladro delgado es una tarea tediosa. Y lo más difícil es recoger su nuevo hierro de su esposa y convencerlo de que compre una impresora láser.

Por cierto, si alguien tiene miedo de la tecnología de planchado láser, diré de inmediato que es muy simple. Lo hice la primera vez:



el ensamblaje en sí también era simple: fue suficiente para soldar los componentes principales a la placa:



pero a pesar de esto, la primera y la última vez que creé una placa de circuito impreso en casa. En el futuro solo ordenaré en la fábrica. Y lo más probable es que tengas que dominar algo más difícil que Fritzing.

Conclusión


El proyecto de firmware del acuario está disponible en GitHub . Adaptado para Arduino Mega. Al usar Uno, debe deshacerse de algunas de las funcionalidades. Simplemente no hay suficiente memoria, rendimiento y pines libres para conectar todos los módulos.

Source: https://habr.com/ru/post/es387017/


All Articles