利用Arduino制作自动浇花工具

自动浇花工具已经有很多电子爱好者分享了,闲来无事,波波也利用Arduino自己DIY了一个自动浇花神器。但是本篇笔记波波除了分享自己做的源码之外,还要记录下在制作过程中自己的一些看法。

首先看下效果图。

利用Arduino制作自动浇花工具

整个项目基本上用到了Arduino开发板,V5扩展板,DH11温湿度传感器、土壤传感器、1602LCD屏幕以及小水泵、软管、继电器、无源蜂鸣器等相关材料。

我的整体思路是首先检测花卉养殖的室温、空气湿度以及土壤湿度,根据这三个数据以及日期来分别设置春夏秋冬不同季节的浇水参数。自动为植物进行浇水,浇水过程中伴随着蜂鸣器发出的简单音乐频谱。这是整个项目的设计思路,但是实际开发中发现想法过于单纯了,最简单的比如说浇水过程中蜂鸣器播放音乐,这个想法其实需要Arduino在达到浇水条件时启用一个线程来实现,但是看了网上别人写的线程类库发现效果还是差劲很多,不是我理想中的样子。他们实现的原理都是通过时间分片,并非真正意义上的线程。

当然这也难为那些贡献类库的大佬们了,毕竟一个10几块钱的单片机......所以程序也不再往下写了,分享下目前已经实现的功能吧,不过唯一的效果是先播放音乐,播放音乐完毕开始浇水。代码中有注释,至于接口的连线,也不多写了都在上面define里边定义。代码有注释,通读代码应该不难。

源代码:

  1. #include <SoftwareSerial.h>
  2. #include <Wire.h>
  3. #include <LiquidCrystal_I2C.h>
  4. #include <dht11.h>
  5. #include <ThreeWire.h>  
  6. #include <RtcDS1302.h>
  7. LiquidCrystal_I2C lcd(0x27,16,2);//初始化LCD屏幕地址信息
  8. int temprature;  //温度
  9. int humi;   //湿度
  10. int ohumi;  //土壤湿度
  11. uint8_t wendu[8] = {0x10,0x06,0x09,0x08,0x08,0x09,0x06,0x00};//温度符号
  12. dht11 DHT11;
  13. #define DHT11PIN 12
  14. #define Tu_A0 0
  15. #define Tu_D0 7
  16. #define speaker 4
  17. #define ssrPin 8  
  18. boolean isPlay = false;
  19. boolean isWatering = false;
  20. const int kCePin   = 1;
  21. const int kIoPin   = 2;
  22. const int kSclkPin = 3;
  23. ThreeWire myWire(kIoPin,kSclkPin,kCePin); // IO, SCLK, CE
  24. RtcDS1302<ThreeWire> Rtc(myWire);
  25. #define countof(a) (sizeof(a) / sizeof(a[0]))
  26.  //音乐节拍
  27. #define D0 -1
  28. #define D1 262
  29. #define D2 293
  30. #define D3 329
  31. #define D4 349
  32. #define D5 392
  33. #define D6 440
  34. #define D7 494
  35. #define M1 523
  36. #define M2 586
  37. #define M3 658
  38. #define M4 697
  39. #define M5 783
  40. #define M6 879
  41. #define M7 987
  42. #define H1 1045
  43. #define H2 1171
  44. #define H3 1316
  45. #define H4 1393
  46. #define H5 1563
  47. #define H6 1755
  48. #define H7 1971
  49. //列出全部D调的频率
  50. #define WHOLE 1
  51. #define HALF 0.5
  52. #define QUARTER 0.25
  53. #define EIGHTH 0.25
  54. #define SIXTEENTH 0.625
  55. int tune[]=
  56. {
  57.   M3,M3,M4,M5,
  58.   M5,M4,M3,M2,
  59.   M1,M1,M2,M3,
  60.   M3,M2,M2,
  61.   M3,M3,M4,M5,
  62.   M5,M4,M3,M2,
  63.   M1,M1,M2,M3,
  64.   M2,M1,M1,
  65.   M2,M2,M3,M1,
  66.   M2,M3,M4,M3,M1,
  67.   M2,M3,M4,M3,M2,
  68.   M1,M2,D5,D0,
  69.   M3,M3,M4,M5,
  70.   M5,M4,M3,M4,M2,
  71.   M1,M1,M2,M3,
  72.   M2,M1,M1
  73. };
  74. float durt[]={
  75.   1,1,1,1,
  76.   1,1,1,1,
  77.   1,1,1,1,
  78.   1+0.5,0.5,1+1,
  79.   1,1,1,1,
  80.   1,1,1,1,
  81.   1,1,1,1,
  82.   1+0.5,0.5,1+1,
  83.   1,1,1,1,
  84.   1,0.5,0.5,1,1,
  85.   1,0.5,0.5,1,1,
  86.   1,1,1,1,
  87.   1,1,1,1,
  88.   1,1,1,0.5,0.5,
  89.   1,1,1,1,
  90.   1+0.5,0.5,1+1,
  91. };
  92. int mlength;
  93. void setup(){
  94.   Serial.begin(115200);
  95.   Rtc.Begin();
  96.   //初始化时间日期
  97.   Rtc.SetIsWriteProtected(false);
  98.   Rtc.SetDateTime(RtcDateTime(2019, 10, 9, 16, 12, 0));
  99.   lcd.init();
  100.   lcd.backlight();
  101.   lcd.clear();
  102.   lcd.createChar(0, wendu);
  103.   //土壤模块接口
  104.   pinMode(Tu_A0, INPUT);
  105.   pinMode(Tu_D0, INPUT);
  106.   //蜂鸣器
  107.   pinMode(speaker,INPUT);
  108.   mlength = sizeof(tune)/sizeof(tune[0]);
  109.   //继电器
  110.   pinMode(ssrPin,OUTPUT);
  111.  }
  112. void loop(){
  113.   RtcDateTime now = Rtc.GetDateTime();
  114.   //获取DHT11数据
  115.   int chk = DHT11.read(DHT11PIN);
  116.   temprature = (int)DHT11.temperature;
  117.   humi = (int)DHT11.humidity;
  118.   //获取时钟数据
  119.   const String nowTime = printTime(now);
  120.   lcd.setCursor(0,0);
  121.   lcd.print(nowTime);
  122.   lcd.setCursor(0,1);
  123.   lcd.print(String("")+"T:"+temprature);
  124.   lcd.write(byte(0));
  125.   lcd.setCursor(6,1);
  126.   lcd.print(String("")+"H:"+humi+"%");
  127.   //获取土壤数据
  128.   ohumi = (int)analogRead(Tu_A0);
  129.   lcd.setCursor(12,1);
  130.   lcd.print(ohumi,DEC);
  131.   //蜂鸣器报警
  132.   if(!isPlay && ohumi > 500 && (int)digitalRead(Tu_D0) == 0){
  133.     pinMode(speaker,OUTPUT);
  134.     playMusic();
  135.   }
  136.   //浇水设定
  137.   if(!isWatering && ohumi > 500 && (int)digitalRead(Tu_D0) == 0){
  138.     //goWater();
  139.     isWatering = true;
  140.     digitalWrite(ssrPin,HIGH);
  141.     Serial.println("Watering Start!");
  142.   }else if(isWatering && ohumi <= 500 && (int)digitalRead(Tu_D0) == 0){
  143.     isWatering = false;
  144.     digitalWrite(ssrPin,LOW);
  145.   }
  146.   Serial.println(String("")+"OH:"+ohumi+"  D0:"+(int)digitalRead(Tu_D0));
  147.   //delay(1000);
  148.  }
  149.  void playMusic(){
  150.     isPlay = true;
  151.     Serial.println("Music Start!");
  152.     for(int x=0;x<mlength;x++){
  153.     tone(speaker,tune[x]);
  154.     delay(500*durt[x]);
  155.     noTone(speaker);
  156.   }
  157.   pinMode(speaker,INPUT);
  158.   Serial.println("Music Stop!");
  159. }
  160. void goWater(){
  161.   isWatering = true;
  162.   while(ohumi > 500 && (int)digitalRead(Tu_D0) == 0){
  163.     digitalWrite(ssrPin,HIGH);
  164.     ohumi = (int)analogRead(Tu_A0);
  165.     lcd.setCursor(12,1);
  166.     lcd.print(ohumi,DEC);
  167.     Serial.println("Watering!");
  168.     }
  169. }
  170. String printTime(const RtcDateTime& dt) {
  171.   char buf[17];
  172.   snprintf_P(buf,
  173.             countof(buf),
  174.             PSTR("%04u/%02u/%02u %02u:%02u"),
  175.             dt.Year(),
  176.             dt.Month(),
  177.             dt.Day(),
  178.             dt.Hour(),
  179.             dt.Minute()
  180.            );
  181.   return buf;
  182. }

代码第一次烧录的时候需要预设下DS1302的初始时间,在setup()中。时间初始化之后,把这部分代码注释或删掉之后,再烧录一次就可以了,如果不想麻烦也可以通过串口来设置DS1302的时间。至于goWater()函数大家删掉即可。因为我本意是想用线程来并行处理浇水的事情,结果效果不是理想的效果,所以也没再用。

在这个代码之上其实我们还可以扩展节气、是否有露水形成、春天什么时候浇水、夏天什么时候浇水等一系列内容,当然波波就不再赘述,大家可以自由发挥哈。

 

你想把广告放到这里吗?

发表评论

您必须 登录 才能发表留言!