聚豐項目 > SmartGarden
SmartGarden是一款智能的植物養殖系統,可實現智能的灌溉,環境溫濕度的實時檢測,土壤濕度和水庫水位的實時檢測;意義在于針對在校學生或工作匆忙人群,經常出差等無法對植株進行即時養護,防止植物干枯或死亡,而實時的APP上位機可通過系統上搭建的Wifi模塊用來實現信息回傳,包括環境溫濕,水位土壤情況,以及系統工作狀態,而隨系統附帶的Oled也可實時展示信息給用戶,Oled不管在刺眼的陽光下,還是在暗室,既不會影響人的休息也不妨礙信息顯示,相信這套系統可以給用戶一個安心的體驗。
小影效應
分享小影效應
團隊成員
紀翔 學生
主控
采用大賽提供的Stm32F401-Nucleo開發板,支持Arduino板型擴展以及Mbed在線開發,極大的減輕了學習負擔,高性能的主控給代碼一個寬廣的跑道可以任他狂奔,86Mhz的主頻,體驗下來很簡單也很易用,在這感謝組委會
用了個自己的L476的圖片,和F401差不多
Wifi模塊
Wifi模塊采用了大賽提供的Ewm3080,板型跟隨Nucleo設計,模塊采用板上天線,EMW3080是單3.3V供電的、集成Wi-Fi和Cortex-M4F MCU的嵌入式Wi-Fi模塊,最高支持133M主頻和256K RAM,強大的浮點運算。在這寫入了AT穿透固件,SDK2.0下的開發模式,更加簡單,為了釋放開發者的創意,廠家沒少封裝那些晦澀難懂的東西,在這再次感謝大賽
Oled顯示
這里采用了SSD1306驅動的128X64的Oled,接口方式SPI,本想用的128X32的I2C的小屏幕,可惜最后可能由于第三方顯示驅動出了些問題放棄了,改用這個壓箱底的屏幕工作電壓2.2V-5.5V因為無須加背光或偏置電壓所以功耗很低,滿足適用場景
溫濕傳感器
溫濕傳感器采用了數字讀取的DHT11,支持數據校驗,最大的減小誤差,本來想用更加精確的DHT22,但手殘買錯了,手動笑Cry
工作電壓:3.3V-5V
工作電流:<2.5ma
濕度范圍:20%~90%(+-5%)
溫度范圍:0-50℃(+-2℃)
土壤濕度傳感
工作類似根據土壤不同濕度直接讀取土壤電阻,放大后ADC讀出模擬量,或采用可調電阻調整數字量上升或下降閥值
工作電壓:3.3V~5V
水位傳感器
通過水內電阻,讀出模擬量的值來測試水位值,但同上模擬器一樣都需要校準,后面軟件調試有涉及
工作電壓3.3V~5V
動作水泵
看的出來,水泵買大了,385的水泵,可以當洗衣機的抽水泵了,后期會改小,爭取實現無噪音工作
工作電壓:6V-12V
開發平臺采用了官方推薦的Mbed,極大簡化了開發步驟,本來還打算用MDK開發的,后來發現Mbed平臺和Arduino的函數操作及其類似,好用到停不下來,在線編譯,隨便找個電腦都可以直接下載編譯好的固件,不用擔心環境出錯而導致的編譯失敗的問題,簡直爽歪歪,,而對于我熟悉Arduino開發,給我省了不少事情,
因為下半年的安全比賽太多了,被拉去湊數,所以代碼是隨便寫寫的,后面會一點點精致,把該細化的地方搞定
先上代碼了,下面再細說
#include "mbed.h" #include "Adafruit_SSD1306.h" #include "DHT.h" Serial pc(SERIAL_TX, SERIAL_RX); //sensor DHT dht11(PC_10,DHT11); // Use the DHT11 sensor AnalogIn SoilValue(A0); AnalogIn WaterValue(A1); DigitalOut myled_R(LED_RED);//work status Led DigitalOut pump(PC_11); SPI spi(D11,NC,D13,NC); //I2C i2c(I2C_SDA,I2C_SCL); //Adafruit_SSD1306_I2c myOled(i2c,NC,0x78,32,128); Adafruit_SSD1306_Spi myOled(spi,D12,PA_11,NC,64,128); int main() { int sta; float soilV; float waterV; int err;//DH11 Error uint16_t x=0; myOled.begin(); // myOled.printf("%ux%u \nThis is Failed Test\r\n", myOled.width(), myOled.height()); //myOled.printf("This is Test Line \r\n"); //myOled.fillScreen(1); myOled.display(); wait(3); int CX; int CY; // myOled.fillScreen(0); myOled.clearDisplay(); //for(int i=0;i<3;i++){ // myOled.printf("\r\n"); // } // pc.printf("CX:%d CY%d \r\n",myOled.get_cursor_x(),myOled.get_cursor_y()); // pc.printf("CX:%d CY%d \r\n",myOled.get_cursor_x(),myOled.get_cursor_y()); // myOled.drawRect(myOled.get_cursor_x(), myOled.get_cursor_y()+1, 70, 5, 1); // pc.printf("CX:%d CY%d \r\n",myOled.get_cursor_x(),myOled.get_cursor_y()); float tmp; float hum; int timelast; bool dov=0; while(1) { myOled.clearDisplay(); float sa1=0,wa1=0,sa2=0,wa2=0; for(int i=0;i<3;i++) { sa1=SoilValue.read(); wa1=WaterValue.read(); sa2=sa2+sa1; wa2=wa2+wa1; wait(0.05); } soilV=sa2/3; waterV=wa2/3; myled_R = !myled_R; err = dht11.readData(); myOled.setTextCursor(0,0); myOled.printf("Runtime %d \r\n",x); myOled.setTextSize(0.5); // pc.printf("The ERROR is %d \r\n,err"); if (err == 0) { // myOled.setTextCursor(0,30); //tmp=int(dht11.ReadTemperature(CELCIUS)); //hum=int(dht11.ReadHumidity()); tmp=dht11.ReadTemperature(CELCIUS); hum=dht11.ReadHumidity(); } else{ //myOled.printf("ERROR Read"); } //Display soil water myOled.printf("Tem:%4.2f C \r\n",tmp); myOled.printf("Hum:%4.2f %%\r\n",hum); myOled.setTextCursor(0,24); myOled.printf("Soil:"); myOled.setTextCursor(30,24); myOled.printf("%d%%",int(soilV*100)); myOled.drawRect(myOled.get_cursor_x(), myOled.get_cursor_y()+1, myOled.width()-myOled.get_cursor_x(), 5, 1); myOled.fillRect(myOled.get_cursor_x(), myOled.get_cursor_y()+2, int((myOled.width()-myOled.get_cursor_x())*soilV), 3, 1); myOled.printf("\r\n"); myOled.printf("Water:%d%%",int(waterV*100)); myOled.drawRect(myOled.get_cursor_x(), myOled.get_cursor_y()+1, myOled.width()-myOled.get_cursor_x(), 5, 1); myOled.fillRect(myOled.get_cursor_x(), myOled.get_cursor_y()+2, int((myOled.width()-myOled.get_cursor_x())*waterV), 3, 1); pc.printf("%d %f \r\n",int((myOled.width()-myOled.get_cursor_x())*soilV),soilV); myOled.printf("\r\n"); // myOled.printf("SysStatus:"); // if(waterV0.4f&& waterV>0.4f&&dov){ pump=1; dov=0; timelast=x; } if(soilV<0.4f||waterV20){ dov=0; pump=0; timelast=0; } if(x%100==0){ dov=1; } x = x + 1; wait(0.5); if(x>20000) { x=0; } myOled.display(); } } /* int err; printf("\r\nDHT Test program"); printf("\r\n******************\r\n"); wait(1); // wait 1 second for device stable status while (1) { myled = 1; err = sensor.readData(); if (err == 0) { printf("Temperature is %4.2f C \r\n",sensor.ReadTemperature(CELCIUS)); //printf("Temperature is %4.2f F \r\n",sensor.ReadTemperature(FARENHEIT)); //printf("Temperature is %4.2f K \r\n",sensor.ReadTemperature(KELVIN)); printf("Humidity is %4.2f \r\n",sensor.ReadHumidity()); //printf("Dew point is %4.2f \r\n",sensor.CalcdewPoint(sensor.ReadTemperature(CELCIUS), sensor.ReadHumidity())); printf("Dew point (fast) is %4.2f \r\n",sensor.CalcdewPointFast(sensor.ReadTemperature(CELCIUS), sensor.ReadHumidity())); } else printf("\r\n read err val %i \n",err); myled = 0; wait(1); */
這套作品使用了兩個第三方的庫,Mbed的第三方庫不少,這點讓開發能簡化好多,但是和Arduino相比,庫資源還是少了很多的
第一個庫是: Adafruit_Oled, 這是一個第三方的顯示庫,從Arduino的U8glib移植過來的,開發人下了心思,但是和U8g比起來,這個庫還是有一定距離的,不過大幅度簡化了顯示難度,在這感謝作者
Adafruit_SSD1306_Spi myOled(spi,D12,PA_11,NC,64,128);
創建對象后調用庫函數即可,簡化很多操作
myOled.printf("Tem:%4.2f C \r\n",tmp); myOled.printf("Hum:%4.2f %%\r\n",hum); myOled.setTextCursor(0,24); myOled.printf("Soil:"); myOled.setTextCursor(30,24); myOled.printf("%d%%",int(soilV*100)); myOled.drawRect(myOled.get_cursor_x(), myOled.get_cursor_y()+1, myOled.width()-myOled.get_cursor_x(), 5, 1); myOled.fillRect(myOled.get_cursor_x(), myOled.get_cursor_y()+2, int((myOled.width()-myOled.get_cursor_x())*soilV), 3, 1); myOled.printf("\r\n"); myOled.printf("Water:%d%%",int(waterV*100)); myOled.drawRect(myOled.get_cursor_x(), myOled.get_cursor_y()+1, myOled.width()-myOled.get_cursor_x(), 5, 1); myOled.fillRect(myOled.get_cursor_x(), myOled.get_cursor_y()+2, int((myOled.width()-myOled.get_cursor_x())*waterV), 3, 1); pc.printf("%d %f \r\n",int((myOled.width()-myOled.get_cursor_x())*soilV),soilV); myOled.printf("\r\n");
第二個庫調用自DHT11,這是一個針對DHT系列的溫室庫,支持校驗,這節省了大量去學習DHT傳感器的時間
DHT dht11(PC_10,DHT11); if (err == 0) { //myOled.setTextCursor(0,30); //tmp=int(dht11.ReadTemperature(CELCIUS)); //hum=int(dht11.ReadHumidity()); tmp=dht11.ReadTemperature(CELCIUS); hum=dht11.ReadHumidity(); }
針對傳感器可能會有跳變的問題,為防止水泵重復啟動,或是澆水過多溺死植株,而自己又實在懶得去設計中斷和定時器,感覺效果差不多,就簡單粗暴寫了下,doV做時間閥標識,x指示runtime,soilV和waterV指示土壤和水位,防止水泵空轉
相關代碼如下
if(soilV>0.4f&& waterV>0.4f&&dov){ pump=1; dov=0; timelast=x; } if(soilV<0.4f||waterV20){ dov=0; pump=0; timelast=0; } if(x%100==0){ dov=1; } x = x + 1; wait(0.5); if(x>20000) { x=0; }
為了能只管展示作品目前的工作狀態,寫了一些簡單的檢測用來反饋實時的狀態
switch (sta){ case 0: myOled.printf("Work"); break; case 1: myOled.printf("Watering"); break; case 2: myOled.printf("No Water"); break; case 3: myOled.printf("Unknow"); break; default: myOled.printf("Run"); break; }
APP的代碼就不放了,朋友按需求編譯好的APP,結果我這里代碼已經沒有時間寫了,比較尷尬
本想著定制一套亞克力外殼的。圖紙和店家都商量好了,最后那頭出了點問題,加上雙十一,最后始終也沒能用得上這套外殼,最后心一橫直接快遞盒了,展示就好,希望各位見諒
演示部分就比較糟心了,隨便拍的,明天得出去北京比賽走好長時間,回來就錯過提交時間了,所以簡單寫了一點
▲整體外觀
▲安裝部分
▲顯示部分
▲溫濕度檢測,主控和動作繼電器部分
▲土壤濕度部分
▲動作水泵部分
▲APP上位機部分
APP通過wifi連接后可回傳相關信息,人工可設定是自動還是手動,或者人工干預水泵啟動,同時針對不同的傳感器和環境進行最低要求的調節和校準操作,最終實現自動化的目的,同時,系統反饋實時的工作狀態,并同時可一定周期內提醒施肥等操作
作品實在簡陋,無奈學生下半年實在是有些忙碌了,還望看官們不要嫌棄,同時后期會繼續跟進,項目進度會更新在論壇上,請隨時指正。
動心忍性1234: 您好我是無線電雜志的編輯,我們對您的項目十分感興趣,請問您有興趣投稿嗎?成為我們的作者除稿費外還有其他優厚條件。敬請參與。投稿請聯系QQ260534978.
回復