Système de contrôle automatique d'aquarium sur Arduino

Je voudrais partager ma première expérience dans la création d'un aquarium Arduino. Auparavant, je ne travaillais pas du tout avec l'électronique et, de plus, je ne savais pas comment les microcontrôleurs étaient programmés. Mais néanmoins, j'ai décidé de m'essayer et j'aimerais partager les résultats.



L'idée de créer un aquarium


Il se trouve que j'ai été principalement impliqué dans la programmation .NET et j'ai appris à contourner C ++. C'est probablement pourquoi je n'ai jamais rencontré de microcircuiterie et de microcontrôleurs, bien que l'envie de les connaître grandisse presque chaque année. Surtout, les dernières années où j'ai découvert Arduino. Mais il fallait trouver une application pratique pour lui. Et ce problème a été rapidement résolu.

Il y a un aquarium dans notre chambre, et chaque jour nous devions grimper sous la table et éteindre la lumière pour les poissons, puis l'allumer le matin. De plus, les poissons devaient allumer le radiateur lorsqu'ils étaient froids et les éteindre lorsqu'ils étaient chauds. Parfois, mon oubli a entraîné la mort de poissons dans l'aquarium et a dû en acheter de nouveaux. Même le poisson devait changer périodiquement 2/3 de l'eau. Et pour notre aquarium, cette procédure a été très longue et désagréable.

Tout d'abord, j'ai examiné des solutions toutes faites pour les aquariums. Il y en a beaucoup. Ce sont principalement des vidéos sur YouTube. Il y a aussi des articles assez intéressants sur les geektimes. Mais pour mon but - étudier et apprendre à connaître le monde des microcircuits - c'était trop compliqué, et un guide détaillé «à partir de zéro» n'a pas été trouvé sur Internet. L'idée de développer un contrôleur d'aquarium a dû être reportée jusqu'à ce que les bases de la microélectronique elle-même aient été étudiées.

Connaissance de la microélectronique


J'ai commencé mon voyage avec un kit prêt à l'emploi pour apprendre l'Arduino. Probablement, tout le monde a collecté quelque chose de similaire quand il s'est familiarisé avec cette plate-forme: une



ampoule ordinaire (LED), une résistance de 220 Ohms. Arduino contrôle l'ampoule à l'aide d'un algorithme C ++. Faites immédiatement une réservation: après avoir acheté un ensemble prêt à l'emploi d'Arduino ou de son analogue, il est impossible d'assembler une chose plus ou moins utile. Eh bien, sauf pour le tweeter ou, disons, un thermomètre domestique. Il est possible d'apprendre la plate-forme elle-même à travers des leçons, mais pas plus. Pour les choses utiles ont je devais apprendre à souder, cartes de circuits imprimés, la conception de cartes de circuits imprimés et autres délices électroniques.

Construisez votre premier prototype d'aquarium


Donc, la première chose que j'ai commencée avec mon prototype d'aquarium a été de formuler les exigences de papier pour cet appareil.

L'aquarium doit:

  1. , , ;
  2. , , ( ) ;
  3. ( ) ;
  4. , , ;
  5. « »
  6. ;
  7. . , 9:00 AM;
  8. , ;
  9. .
  10. L'écran avec la date à laquelle vous appuyez sur le bouton de la télécommande doit être mis en surbrillance. Si vous n'appuyez sur rien dans les 5 secondes, sortez.

J'ai décidé de commencer par explorer le fonctionnement de l'écran LCD et de l'Arduino.

Créez le menu principal. Fonctionnement LCD


Pour l'écran LCD, j'ai décidé d'utiliser la bibliothèque LiquidCrystal. Par coïncidence, en plus d'Arduino, j'avais également un écran LCD dans mon kit. Il pouvait produire du texte, des nombres. C'était suffisant, et j'ai commencé à étudier la connexion de cet écran à l'Arduino. J'ai pris les informations de connexion de base d'ici . Il existe également des exemples de code pour la sortie de "Hello World".

Ayant un peu affaire avec l'écran, j'ai décidé de créer le menu principal du contrôleur. Le menu comprenait les éléments suivants:

  1. Informations de base;
  2. Réglage de l'heure;
  3. Réglage de la date;
  4. Température;
  5. Climat;
  6. Rétroéclairage
  7. Dispositifs

Chaque élément est un mode spécifique de sortie d'informations vers l'écran de texte LCD. Je voulais permettre la possibilité de créer un menu à plusieurs niveaux, où chaque sous-niveau aura sa propre implémentation de la sortie à l'écran.

En fait, la classe de base a été écrite en C ++, dont tous les autres sous-menus seront hérité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();
};

Par exemple, pour le menu "Périphériques", l'implémentation de la classe de base qQuariumMode ressemblera à ceci:

#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();
};

Voici le résultat de l'implémentation du premier niveau du menu:



Partie matériel. Les nuances des composants de connexion


Je veux dire quelques mots sur le matériel du contrôleur d'aquarium. Pour le fonctionnement normal du contrôleur, j'ai dû acheter:

  1. 1 x Arduino Uno / Mega. Plus tard, il a décidé de travailler avec Mego;
  2. 1 x horloge en temps réel, par exemple 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;

Chaque composant possède son propre type de connexion et ses pilotes de fonctionnement. Je ne décrirai pas les nuances de la connexion de tous les composants, car elles peuvent être trouvées sur le site Web du fabricant ou sur les forums. Si vous prévoyez d'utiliser les mêmes composants que moi, vous n'aurez pas à modifier le code source.

Altération des composants


Faites attention. Essayez de lire d'abord le composant plug-in. Il doit fonctionner avec précision dans la plage de tension pour laquelle il a été créé. Ceci est généralement indiqué sur le site Web du fabricant. Pendant que je développais le contrôleur d'aquarium, j'ai détruit 2 capteurs de température scellés et une horloge en temps réel. Les capteurs sont tombés en panne du fait que je les ai connectés à 12V, et il a fallu 5V. L'horloge en temps réel est morte en raison d'un court-circuit "aléatoire" dans le circuit en raison de ma faute.

Bande LED RGB


Des difficultés particulières sont apparues avec les bandes LED. J'ai essayé de mettre en œuvre le schéma suivant:



Lors de la connexion à Arduino, j'ai utilisé des broches qui prennent en charge PWM (Pulse Width Modulation). Tout en activant simultanément la tension maximale des 3 broches, ma bande était très chaude. En conséquence, si vous le laissez pendant une heure ou deux, certaines LED ont cessé de briller. Je pense que cela est dû à la défaillance de certaines résistances. Un autre inconvénient de ce schéma est la luminosité différente de la bande LED pour chacune des couleurs. Par exemple, si je mets la tension maximale sur la composante rouge de la bande, alors j'obtiens la luminosité conditionnelle de la bande rouge à 255 unités. Si j'allume les composants rouge et bleu à la tension maximale, alors la luminosité sera égale à 255 + 255 = 510 unités, et la couleur sera violette. En général, cette solution ne me convenait pas.

Il a été décidé de mettre en œuvre l'algorithme suivant:

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);
	}	
}

Dans ce mode de réalisation, la couleur rouge et la couleur violette avaient la même luminosité. Ceux. les LED rouges dans le premier cas brillaient avec une luminosité de 255 unités, et avec une couleur violette, le rouge était avec une luminosité de 127 unités et le bleu avec une luminosité de 127 unités, ce qui au final était approximativement égal à 255 unités:



Ruban LED blanc


Avec la bande LED, c'était probablement le plus simple. Le seul moment difficile est d'assurer un changement de luminosité en douceur lors du changement d'heure.

Pour mettre en œuvre cette idée, j'ai appliqué un algorithme linéaire pour changer la luminosité d'une bande LED blanche.

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);
		}
	}
}

Ondulation "volcan"


L'idée de mise en œuvre m'est venue par hasard. Je voulais juste allumer et éteindre le volcan décoratif en appliquant une basse tension et une haute tension au transistor. Dans la poissonnerie, j'ai soigné un bon volcan avec un tube de sortie pour le compresseur et une LED isolée de l'eau.



Il est venu avec un adaptateur, dont la sortie est de 12V DC et l'entrée est 220V AC. Je n'avais pas besoin d'un adaptateur, car j'ai implémenté la puissance et la luminosité du volcan via Arduino.

La pulsation du volcan lui-même a été mise en œuvre comme suit:

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);
	}
}

Le volcan illumine parfaitement l'aquarium le soir, et l'ondulation elle-même est très belle:



Pompe à eau. Remplacement de l'eau d'aquarium


Une pompe à eau permet de changer rapidement l'eau de l'aquarium. J'ai acheté une pompe qui fonctionne sur DC 12V. La pompe est contrôlée par un transistor à effet de champ. Le pilote de l'appareil lui-même peut faire deux choses: allumer la pompe, éteindre la pompe. Lors de l'implémentation du pilote, j'ai simplement hérité de la classe de base BaseOnOfDeviceHelper et n'ai rien défini d'autre dans le pilote. L'ensemble de l'algorithme du périphérique peut très bien implémenter la classe de base.


J'ai testé la pompe sur le stand:



Bien que la pompe ait bien fonctionné, je suis tombé sur une chose non évidente. Si vous pompez de l'eau dans un autre réservoir, la loi des vases communicants commence à s'appliquer. En conséquence, je suis devenu le coupable de l'inondation dans la pièce, car si vous éteignez la pompe, l'eau ira toujours dans un autre réservoir, si son niveau d'eau est inférieur au niveau de l'eau dans l'aquarium. Dans mon cas, c'était juste ça.

Port infrarouge et envie de le remplacer


J'ai effectué la gestion de l'aquarium par infrarouge en suivant l'exemple de la formation préalable. L'essence de l'exemple est la suivante: lorsque j'allume le contrôleur sur le réseau, j'interroge les actions de gauche, droite, haut, bas, ok. L'utilisateur choisit les boutons de télécommande qu'il associe à chacune des actions. L'avantage de cette implémentation est la possibilité de lier toute télécommande inutile.
L'aquarium est formé par la méthode Learn, dont l'essence est affichée ci-dessous:

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;
				
				...
				...

Plus tard, je suis arrivé à la conclusion que la télécommande ne convenait pas. Juste parce que vous devez le chercher et c'est un appareil supplémentaire dans la maison. Il est préférable de mettre en œuvre le contrôle via un téléphone mobile ou une tablette. J'ai eu l'idée d'utiliser le micro-ordinateur Raspberry PI, pour y monter l'application web ASP.NET MVC 5 via Mono et NancyFX. Ensuite, utilisez le framework mobile jquery pour les applications Web multiplates-formes. Via Raspberry, communiquez avec Arduino via WiFi ou LAN. Dans ce cas, vous pouvez même abandonner l'écran LCD, car toutes les informations nécessaires peuvent être consultées sur un smartphone ou une tablette. Mais ce projet n'est que dans ma tête.

Carte de circuit imprimé et sa fabrication


D'une manière ou d'une autre, je suis arrivé à la conclusion que nous devons fabriquer une carte de circuit imprimé. Cela s'est produit après que tant de fils soient apparus sur mon stand que lors de l'assemblage de l'appareil fini, certains d'entre eux ont commencé à se déconnecter de la pression accidentelle d'autres fils. Cela se produit de manière invisible aux yeux et peut conduire à des résultats incompréhensibles. Et l'apparition d'un tel appareil laissait beaucoup à désirer.

Assemblage sur circuits imprimés (utilisé par Arduino Uno):



J'ai développé un PCB monocouche dans le programme Fritzing. Il s'est avéré ce qui suit (en utilisant Arduino Mega):



La chose la plus méchante à propos de la fabrication d'un circuit imprimé était le forage. Surtout quand j'ai essayé de créer une carte de circuit imprimé de type Shield, c'est-à-dire elle s'habillait sur un arduino. Percer plus de 50 trous avec un foret mince est une tâche fastidieuse. Et le plus difficile est de récupérer son nouveau fer à repasser auprès de sa femme et de le persuader d'acheter une imprimante laser.

Soit dit en passant, si quelqu'un a peur de la technologie de repassage laser, je dirai tout de suite que c'est très simple. Je l'ai fait la première fois:



l'assemblage lui-même était aussi simple - il suffisait de souder les composants principaux à la carte:



mais malgré cela, la première et la dernière fois j'ai créé une carte de circuit imprimé à la maison. À l'avenir, je ne commanderai qu'à l'usine. Et vous devrez probablement maîtriser quelque chose de plus difficile que Fritzing.

Conclusion


Le projet de micrologiciel d'aquarium est disponible sur GitHub . Adapté pour Arduino Mega. Lorsque vous utilisez Uno, vous devez vous débarrasser de certaines fonctionnalités. Il n'y a tout simplement pas assez de mémoire, de performances et de broches libres pour connecter tous les modules.

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


All Articles