關(guān)于自動澆花估計很多Arduino的愛好者都玩過,原理也很簡單,就是通過土壤濕度檢測來自動給植物澆水。那么今天波波為朋友們分享的原理基本接近,但比之前的更好玩兒。
首先看下波波完工的視頻:
準(zhǔn)備材料:
1、Arduino開發(fā)板,本例采用的是 Arduino uno r3
2、傳感器:DH11、DS1302、土壤濕度傳感器、紅外接收模塊、遙控器(可以用家中電視機(jī)的都行)
4、其他材料:按鍵一個、5v繼電器一個、5v隔膜小水泵一個、1602顯示屏、導(dǎo)線若干。
5、本例中使用了9v1A的太陽能電池板,(這個電池板跟共享單車是一樣的)電池板后面需要接一個TPP穩(wěn)壓輸入輸出電路,保障電池的安全。沒有這個電路板電池很可能一天不到就損壞了。
實(shí)現(xiàn)功能:
1、按鍵:按鍵按下的時候可以切換1602屏幕亮暗狀態(tài)。平時不用的時候可以讓屏幕休眠,節(jié)約用電。
2、自動澆水:根據(jù)室內(nèi)當(dāng)前的溫度、濕度、土壤的水分來實(shí)現(xiàn)自動澆水。只要電源充足,無需人工干預(yù)。
3、裝逼模式:這是為了給來家里玩兒的小朋友們演示專門添加的。就是如果內(nèi)置模式達(dá)不到澆水條件的時候,我們可以通過遙控器為花短暫澆水,按下遙控器,1602屏幕亮起,開始澆水。再按一下屏幕熄滅,澆水停止。
在此之前波波曾經(jīng)是采用播放音樂的方式,但是開發(fā)過程中播放音樂的tone()函數(shù)與IRremote庫產(chǎn)生了沖突,所以去掉了音樂播放,大家在開發(fā)過程中可以通過MP3模塊來增加音樂播放,而不要采用蜂鳴器。
相關(guān)代碼:
這是我優(yōu)化之前的代碼,放出來。現(xiàn)在正在用的代碼暫時不方便公開。因?yàn)檫€有聯(lián)網(wǎng)控制,自己寫的服務(wù)器前后端。
```cpp
#include <SoftwareSerial.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <dht11.h>
#include <ThreeWire.h>?
#include <RtcDS1302.h>
#include <IRremote.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? //土壤數(shù)字信號
#define speaker 4? //無源蜂鳴器
#define ssrPin 8? //繼電器
#define buttonPin 9 //按鈕按鍵
boolean isPlay = false;
boolean isWatering = false;
int buttonStat = 0;
boolean isLightening = false; //Lcd背光狀態(tài)
const int kCePin? = 1;
const int kIoPin? = 2;
const int kSclkPin = 3;
ThreeWire myWire(kIoPin,kSclkPin,kCePin); // IO, SCLK, CE
RtcDS1302<ThreeWire> Rtc(myWire);
int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results results;
#define countof(a) (sizeof(a) / sizeof(a[0]))
//音樂節(jié)拍
#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調(diào)的頻率
#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);
? lcd.init();
? lcd.backlight();
? //lcd.noBacklight();
? lcd.clear();
? lcd.createChar(0, wendu);
? //初始化時間日期
? Rtc.Begin();
? Rtc.SetIsWriteProtected(true);
? //Rtc.SetDateTime(RtcDateTime(2019, 11, 19, 14, 20, 0));
? Rtc.SetIsRunning(true);
? //土壤模塊接口
? pinMode(Tu_A0, INPUT);
? pinMode(Tu_D0, INPUT);
? //蜂鳴器
? pinMode(speaker,INPUT);
? mlength = sizeof(tune)/sizeof(tune[0]);
? //繼電器
? pinMode(ssrPin,OUTPUT);
? //按鍵按鈕
? pinMode(buttonPin,INPUT_PULLUP);
? // Start the receiver
? irrecv.enableIRIn();
}
void loop(){
? RtcDateTime now = Rtc.GetDateTime();
? //獲取DHT11數(shù)據(jù)
? int chk = DHT11.read(DHT11PIN);
? temprature = (int)DHT11.temperature;
? humi = (int)DHT11.humidity;
? //獲取時鐘數(shù)據(jù)
? 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+"%");
? //獲取土壤數(shù)據(jù)
? ohumi = (int)analogRead(Tu_A0);
? lcd.setCursor(12,1);
? lcd.print(ohumi,DEC);
? //按鍵控制LCD背光
? buttonStat = digitalRead(buttonPin);
? if(buttonStat == LOW){
? ? delay(80);
? ? if(buttonStat == LOW && !isLightening){
? ? ? lcd.backlight();
? ? ? isLightening = true;
? ? ? }
? ? }else{
? ? ? lcd.noBacklight();
? ? ? isLightening = false;
? }
? //蜂鳴器報警
? /*
? if(!isPlay && ohumi > 300 && (int)digitalRead(Tu_D0) == 0){
? ? pinMode(speaker,OUTPUT);
? ? playMusic();
? }
? */
? //澆水設(shè)定
? if(!isWatering && ohumi > 300 && (int)digitalRead(Tu_D0) == 0){
? ? //goWater();
? ? lcd.backlight();
? ? isWatering = true;
? ? digitalWrite(ssrPin,HIGH);
? ? Serial.println("Watering Start!");
? }else if(isWatering && ohumi <= 240 && (int)digitalRead(Tu_D0) == 0){
? ? lcd.noBacklight();
? ? isWatering = false;
? ? digitalWrite(ssrPin,LOW);
? ? Serial.println("Watering Stop!");
? }else{
? ? //nothing to do
? }
? if (irrecv.decode(&results)) {
? ? Serial.println(results.value, HEX);
? ? irrecv.resume();
? }
? Serial.println(String("")+"OH:"+ohumi+"? D0:"+(int)digitalRead(Tu_D0));
? delay(200);
}
/*
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;
}
```
這是寫的第二版的代碼,編譯如果報錯,注意看文中加粗文字。如果大家想優(yōu)化的話,可以加入線程控制。這里我就不分享相關(guān)代碼了。以上內(nèi)容僅供初學(xué)者練手,高手們可以直接飄過。