自动浇花工具已经有很多电子爱好者分享了,闲来无事,波波也利用Arduino自己DIY了一个自动浇花神器。但是本篇笔记波波除了分享自己做的源码之外,还要记录下在制作过程中自己的一些看法。
首先看下效果图。
整个项目基本上用到了Arduino开发板,V5扩展板,DH11温湿度传感器、土壤传感器、1602LCD屏幕以及小水泵、软管、继电器、无源蜂鸣器等相关材料。
我的整体思路是首先检测花卉养殖的室温、空气湿度以及土壤湿度,根据这三个数据以及日期来分别设置春夏秋冬不同季节的浇水参数。自动为植物进行浇水,浇水过程中伴随着蜂鸣器发出的简单音乐频谱。这是整个项目的设计思路,但是实际开发中发现想法过于单纯了,最简单的比如说浇水过程中蜂鸣器播放音乐,这个想法其实需要Arduino在达到浇水条件时启用一个线程来实现,但是看了网上别人写的线程类库发现效果还是差劲很多,不是我理想中的样子。他们实现的原理都是通过时间分片,并非真正意义上的线程。
当然这也难为那些贡献类库的大佬们了,毕竟一个10几块钱的单片机......所以程序也不再往下写了,分享下目前已经实现的功能吧,不过唯一的效果是先播放音乐,播放音乐完毕开始浇水。代码中有注释,至于接口的连线,也不多写了都在上面define里边定义。代码有注释,通读代码应该不难。
源代码:
- #include <SoftwareSerial.h>
- #include <Wire.h>
- #include <LiquidCrystal_I2C.h>
- #include <dht11.h>
- #include <ThreeWire.h>
- #include <RtcDS1302.h>
- LiquidCrystal_I2C lcd(0x27,16,2);//初始化LCD屏幕地址信息
- int temprature; //温度
- int humi; //湿度
- int ohumi; //土壤湿度
- uint8_t wendu[8] = {0x10,0x06,0x09,0x08,0x08,0x09,0x06,0x00};//温度符号
- dht11 DHT11;
- #define DHT11PIN 12
- #define Tu_A0 0
- #define Tu_D0 7
- #define speaker 4
- #define ssrPin 8
- boolean isPlay = false;
- boolean isWatering = false;
- const int kCePin = 1;
- const int kIoPin = 2;
- const int kSclkPin = 3;
- ThreeWire myWire(kIoPin,kSclkPin,kCePin); // IO, SCLK, CE
- RtcDS1302<ThreeWire> Rtc(myWire);
- #define countof(a) (sizeof(a) / sizeof(a[0]))
- //音乐节拍
- #define D0 -1
- #define D1 262
- #define D2 293
- #define D3 329
- #define D4 349
- #define D5 392
- #define D6 440
- #define D7 494
- #define M1 523
- #define M2 586
- #define M3 658
- #define M4 697
- #define M5 783
- #define M6 879
- #define M7 987
- #define H1 1045
- #define H2 1171
- #define H3 1316
- #define H4 1393
- #define H5 1563
- #define H6 1755
- #define H7 1971
- //列出全部D调的频率
- #define WHOLE 1
- #define HALF 0.5
- #define QUARTER 0.25
- #define EIGHTH 0.25
- #define SIXTEENTH 0.625
- int tune[]=
- {
- M3,M3,M4,M5,
- M5,M4,M3,M2,
- M1,M1,M2,M3,
- M3,M2,M2,
- M3,M3,M4,M5,
- M5,M4,M3,M2,
- M1,M1,M2,M3,
- M2,M1,M1,
- M2,M2,M3,M1,
- M2,M3,M4,M3,M1,
- M2,M3,M4,M3,M2,
- M1,M2,D5,D0,
- M3,M3,M4,M5,
- M5,M4,M3,M4,M2,
- M1,M1,M2,M3,
- M2,M1,M1
- };
- float durt[]={
- 1,1,1,1,
- 1,1,1,1,
- 1,1,1,1,
- 1+0.5,0.5,1+1,
- 1,1,1,1,
- 1,1,1,1,
- 1,1,1,1,
- 1+0.5,0.5,1+1,
- 1,1,1,1,
- 1,0.5,0.5,1,1,
- 1,0.5,0.5,1,1,
- 1,1,1,1,
- 1,1,1,1,
- 1,1,1,0.5,0.5,
- 1,1,1,1,
- 1+0.5,0.5,1+1,
- };
- int mlength;
- void setup(){
- Serial.begin(115200);
- Rtc.Begin();
- //初始化时间日期
- Rtc.SetIsWriteProtected(false);
- Rtc.SetDateTime(RtcDateTime(2019, 10, 9, 16, 12, 0));
- lcd.init();
- lcd.backlight();
- lcd.clear();
- lcd.createChar(0, wendu);
- //土壤模块接口
- pinMode(Tu_A0, INPUT);
- pinMode(Tu_D0, INPUT);
- //蜂鸣器
- pinMode(speaker,INPUT);
- mlength = sizeof(tune)/sizeof(tune[0]);
- //继电器
- pinMode(ssrPin,OUTPUT);
- }
- void loop(){
- RtcDateTime now = Rtc.GetDateTime();
- //获取DHT11数据
- int chk = DHT11.read(DHT11PIN);
- temprature = (int)DHT11.temperature;
- humi = (int)DHT11.humidity;
- //获取时钟数据
- const String nowTime = printTime(now);
- lcd.setCursor(0,0);
- lcd.print(nowTime);
- lcd.setCursor(0,1);
- lcd.print(String("")+"T:"+temprature);
- lcd.write(byte(0));
- lcd.setCursor(6,1);
- lcd.print(String("")+"H:"+humi+"%");
- //获取土壤数据
- ohumi = (int)analogRead(Tu_A0);
- lcd.setCursor(12,1);
- lcd.print(ohumi,DEC);
- //蜂鸣器报警
- if(!isPlay && ohumi > 500 && (int)digitalRead(Tu_D0) == 0){
- pinMode(speaker,OUTPUT);
- playMusic();
- }
- //浇水设定
- if(!isWatering && ohumi > 500 && (int)digitalRead(Tu_D0) == 0){
- //goWater();
- isWatering = true;
- digitalWrite(ssrPin,HIGH);
- Serial.println("Watering Start!");
- }else if(isWatering && ohumi <= 500 && (int)digitalRead(Tu_D0) == 0){
- isWatering = false;
- digitalWrite(ssrPin,LOW);
- }
- Serial.println(String("")+"OH:"+ohumi+" D0:"+(int)digitalRead(Tu_D0));
- //delay(1000);
- }
- void playMusic(){
- isPlay = true;
- Serial.println("Music Start!");
- for(int x=0;x<mlength;x++){
- tone(speaker,tune[x]);
- delay(500*durt[x]);
- noTone(speaker);
- }
- pinMode(speaker,INPUT);
- Serial.println("Music Stop!");
- }
- void goWater(){
- isWatering = true;
- while(ohumi > 500 && (int)digitalRead(Tu_D0) == 0){
- digitalWrite(ssrPin,HIGH);
- ohumi = (int)analogRead(Tu_A0);
- lcd.setCursor(12,1);
- lcd.print(ohumi,DEC);
- Serial.println("Watering!");
- }
- }
- String printTime(const RtcDateTime& dt) {
- char buf[17];
- snprintf_P(buf,
- countof(buf),
- PSTR("%04u/%02u/%02u %02u:%02u"),
- dt.Year(),
- dt.Month(),
- dt.Day(),
- dt.Hour(),
- dt.Minute()
- );
- return buf;
- }
代码第一次烧录的时候需要预设下DS1302的初始时间,在setup()中。时间初始化之后,把这部分代码注释或删掉之后,再烧录一次就可以了,如果不想麻烦也可以通过串口来设置DS1302的时间。至于goWater()函数大家删掉即可。因为我本意是想用线程来并行处理浇水的事情,结果效果不是理想的效果,所以也没再用。
在这个代码之上其实我们还可以扩展节气、是否有露水形成、春天什么时候浇水、夏天什么时候浇水等一系列内容,当然波波就不再赘述,大家可以自由发挥哈。