Arduino上的自动水族箱控制系统

我想分享我创建Arduino水族馆这样的东西的初次经验。以前,我根本不使用电子产品,而且,我也不知道微控制器的编程方式。但是,尽管如此,我还是决定尝试一下,并希望分享结果。



创建一个水族馆的想法


碰巧的是,我主要从事.NET编程,并学会了绕过C ++。这也许就是为什么我从未见过微电路和微控制器的原因,尽管几乎每年都想了解它们。特别是最近几年,我发现了Arduino。但是有必要为他提出一个实际的应用程序。这个问题很快得到解决。

我们的房间里有一个水族馆,每天我们都必须爬到桌子下面,关掉鱼的灯,然后在早晨打开。另外,鱼在寒冷时必须打开加热器,在温暖时必须关闭加热器。有时我的健忘导致水族馆里的鱼死亡,不得不购买新的鱼。甚至鱼也必须定期更换2/3的水。对于我们的水族馆,此过程非常漫长且令人不快。

首先,我研究了现成的水族馆解决方案。有很多。这些主要是youtube上的视频。关于极客时间也有很多有趣的文章。但是出于我的目的-学习并了解微电路的世界-太复杂了,并且没有找到Internet上“从头开始”的详细指南。开发水族箱控制器的想法必须推迟到研究微电子本身的基础知识为止。

熟悉微电子


我从准备好用于学习Arduino的套件开始我的旅程。当他熟悉这个平台时,大概每个人都会收集到类似的东西:一个



普通的灯泡(LED),一个220欧姆的电阻器。Arduino使用C ++算法控制灯泡。立即预订已购买的任何现成的Arduino或其类似产品,这是不可能的,组装或多或少有用的东西是不可能的。好吧,除了高音喇叭或家用温度计。可以通过课程学习平台本身,但仅此而已。对于有用的东西我不得不学习焊接,印刷电路板,印刷电路板的设计和其他电子工具。

建立您的第一个水族馆原型


因此,我从原型水族箱开始的第一件事就是制定该设备的纸张要求。

水族馆应:

  1. , , ;
  2. , , ( ) ;
  3. ( ) ;
  4. , , ;
  5. « »
  6. ;
  7. . , 9:00 AM;
  8. , ;
  9. .
  10. 按下遥控器上的按钮时,带有日期的屏幕应突出显示。如果5秒钟内未按任何操作,则熄灭。

我决定从探索LCD和Arduino的操作开始。

创建主菜单。LCD操作


对于LCD,我决定使用LiquidCrystal库。巧合的是,除了Arduino外,我的套件中还装有LCD屏幕。他可以输出文本,数字。这就足够了,我开始研究此屏幕与Arduino的连接。从这里获取了基本的连接信息还有“ Hello World”输出的代码示例。

在处理完屏幕后,我决定创建控制器的主菜单。菜单包含以下各项:

  1. 基本信息;
  2. 时间设定;
  3. 日期设定;
  4. 温度;
  5. 气候;
  6. 背光灯
  7. 设备

每个项目都是输出到LCD文本屏幕的特定信息模式。我想允许创建一个多级菜单,每个子级在屏幕上都有自己的输出实现。

实际上,基类是用C ++编写的,所有其他子菜单都将从中继承。

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

例如,对于“设备”菜单,qQuariumMode基类的实现将如下所示:

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

这是菜单第一级的执行结果:



硬件部分。连接组件的细微差别


我想谈谈水族馆控制器的硬件。为了使控制器正常运行,我必须购买:

  1. 1个Arduino Uno / Mega。后来他决定与Mego合作;
  2. 1个实时时钟,例如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;

每个组件都有其自己的连接类型及其操作驱动程序。我不会描述连接所有组件的细微差别,因为它们可以在制造商的网站或论坛上找到。如果您打算使用与我相同的组件,那么您将不必更改源代码。

零件损坏


小心。尝试先阅读有关插件组件的信息。它必须在为其创建的电压范围内精确运行。通常在制造商的网站上指出。在开发水族箱控制器时,我破坏了2个密封的温度传感器和一个实时时钟。由于我将它们连接到12V,因此必须将其连接到5V,因此传感器出现故障。由于我的故障,实时时钟因电路中的“随机”短路而死亡。

RGB LED灯条


LED灯带特别困难。我尝试实现以下方案:



连接Arduino时,我使用了支持PWM(脉冲宽度调制)的引脚。同时打开所有3个引脚的最大电压时,我的磁带非常热。结果,如果您将其放置一两个小时,则某些LED会停止发光。我相信这是由于某些电阻器的故障造成的。该方案的另一个缺点是每种颜色的LED灯带的亮度不同。例如,如果我在胶带的红色部分上施加最大电压,那么我得到的红色胶带的条件亮度为255个单位。如果我在最大电压下同时打开红色和蓝色组件,则亮度将等于255 + 255 = 510单位,而颜色将变为紫色。通常,此解决方案不适合我。

决定实施以下算法:

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

在该实施例中,红色和紫色具有相同的亮度。那些。第一种情况下,红色LED的亮度为255个单位,而紫色为紫色,红色的亮度为127个单位,蓝色的亮度为127个单位,最终大约等于255个单位:



LED灯条白


使用LED灯条,这可能是最简单的。唯一困难的时刻是确保更改一天中的时间时亮度的平滑变化。

为了实现这个想法,我应用了一种线性算法来更改白色LED灯条的亮度。

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

波纹“火山”


实现的想法是偶然出现的。我只想通过向晶体管施加低电压和高电压来打开和关闭装饰性火山。在鱼品商店,我照顾了一个好的火山,火山中装有压缩机的引出管和与水隔绝的LED。



它带有一个适配器,其输出为12V DC,输入为220V AC。我不需要适配器,因为我通过Arduino实现了火山的力量和亮度。

火山脉动的实现方式如下:

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

火山在晚上完美照亮了水族馆,涟漪本身看起来非常美丽:



水泵。更换水族箱的水


水泵有助于快速更换水族箱中的水。我购买了可在直流12V电压下运行的泵。泵通过场效应晶体管控制。设备本身的驱动程序可以做两件事:打开泵,关闭泵。在实现驱动程序时,我只是从基类BaseOnOfDeviceHelper继承而未在驱动程序中定义其他任何内容。设备的整个算法可以很好地实现基类。


我在架子上测试了泵:



尽管泵工作正常,但我遇到了一件不明显的事情。如果您将水泵入另一个水箱,则连通容器的法则开始适用。结果,我成为了房间中洪水的元凶,因为如果您关闭泵,如果水位低于水族馆中的水位,水仍会流到另一个水箱中。就我而言,仅此而已。

红外端口及其替换愿望


我按照初步培训的示例通过红外线进行了水族箱的管理。该示例的实质如下:当我打开网络上的控制器时,我会询问左,右,上,下,确定的动作。用户选择他与每个操作关联的远程控制按钮。此实现的优点是可以绑定任何不必要的遥控器。
水族馆是通过Learn方法进行培训的,其本质如下所示:

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

后来我得出结论,遥控器不方便。只是因为您需要寻找它,这是房屋中的额外设备。最好通过手机或平板电脑实施控制。我的想法是使用Raspberry PI微型计算机,通过Mono和NancyFX在其上引发ASP.NET MVC 5 Web应用程序。接下来,将jquery移动框架用于跨平台的Web应用程序。通过Raspberry,通过WiFi或LAN与Arduino通信。在这种情况下,您甚至可以放弃LCD屏幕,因为所有必需的信息都可以在智能手机或平板电脑上查看。但是这个项目只是在我脑海中。

印刷电路板及其制造


一种或另一种方式,我得出的结论是,我们需要制造印刷电路板。这是在我的架子上出现了许多电线之后发生的,以至于在组装完成的设备时,其中一些电线开始与其他电线的意外压力断开连接。这在眼睛上是看不见的,并且可能导致难以理解的结果。而且,这种设备的外观非常不理想。

在电路板上组装(由Arduino Uno使用):



我在Fritzing程序中开发了单层PCB。事实证明(使用Arduino Mega):



制作电路板最讨厌的事情是钻孔。特别是当我尝试创建Shield型电路板时 她穿着一头arduino。用细钻头钻50多个孔是一项繁琐的任务。最困难的事情是从妻子那里拿起她的新熨斗,并说服他购买激光打印机。

顺便说一句,如果有人担心激光熨烫技术,我会马上说这很简单。我是第一次这样做:



组装本身也很简单-足以将主要部件焊接到板上:



尽管如此,我还是第一次还是最后一次在家中制作了印刷电路板。将来我只会在工厂订购。而且最有可能您必须掌握比Fritzing更难的东西。

结论


水族馆固件项目可在GitHub上获得适用于Arduino Mega。使用Uno时,您必须摆脱一些功能。根本没有足够的内存,性能和空闲引脚来连接所有模块。

Source: https://habr.com/ru/post/zh-CN387017/


All Articles