該物聯網花園系統利用樹莓派和Telegram機器人,可實現植物澆水、燈光開關、拍攝NDVI圖片及視頻等多種功能……

本項目所需物品
硬件組件
樹莓派 3 Model B x 1
滴灌套裝x 1
水族箱水泵x 1
SparkFun 433MHz 發射芯片x 1
軟件應用及在線服務
Telegram(電報)
項目背景
物聯網花園項目背后的理念
你是否玩過《開心農場》?這是一款社交網絡游戲,在游戲中,玩家基本上是管理一個模擬農場,既可以單人模式游玩,也能進行多人聯機游戲。現在,假如這個農場是真實存在的——你可能會說,那不就是現實中的務農嘛——但請聽我繼續說,假如你依然可以保持在線狀態,以單人模式或多人聯機模式來管理這個現實中的農場呢?那可就不只是“單純的務農”了,對吧。
好吧,我并沒有農場,我住在城市里的一間公寓,有一個小花園。但上述想法啟發了我,讓我創建了一個物聯網(IoT)社區花園。
這個花園可以由一群志同道合的人通過Telegram應用群聊進行遠程管理。對于那些不熟悉Telegram的人來說,它是一款類似Whatsapp的即時通訊應用,但它允許任何用戶使用應用程序接口創建一個機器人。機器人其實就是一些代碼,能夠以自動化的方式對指令做出響應。于是,我通過編寫代碼實現了我的想法,創建了一個在樹莓派上運行的Telegram機器人,它可以接收用戶的指令——給植物澆水、拍照、開關燈。我計劃未來進一步擴展這個項目,添加溫度和濕度傳感器,不過目前我只是通過Dark Sky的天氣API獲取天氣數據。2019年4月4日,我在Facebook群組中發布了關于這個機器人的信息,很快就有來自世界各地的陌生人加入了我的Telegram群組,來擺弄我的機器人。目前,在我撰寫這篇文章時,大約有30到35人每天都會與這個機器人互動。
讓我們稍微停頓一下——如果此時你想親自測試我們的社區物聯網花園,那么在你的智能手機上下載Telegram應用程序,然后簡單地尋找我們的聊天組“物聯網花園”,或者從你的移動設備上點擊鏈接//t.me/iotgarden。另一個選擇是直接尋找https://t.me/Zenofallbot。
這兩者之間有什么區別呢?
? Iotgarden 是一個 Telegram 聊天群組——該群組內有多個用戶,名為 Zenofallbot 的機器人被添加到群組中,就像一個真實的用戶一樣。更重要的是,每個人都能看到彼此與機器人的互動情況。這里的理念是建立一個社區,我們不僅可以作為一個社區與機器人進行互動,還可以討論改進項目的想法、其他動手實踐項目,或者進行一般的技術交流。此外,你還可以在這里直接與我互動并提出問題,我是這個群組的管理員。
? Zenofallbot 是實際的 Telegram 機器人——如果你想在不加入社區的情況下與機器人進行私密互動,那么可以直接搜索它的名字——這樣,其他人都無法看到你的互動內容。
一旦你與聊天群組或直接與機器人發起聊天,只需輸入指令“/start”并點擊發送,屏幕上應該會彈出一個帶有操作按鈕的菜單。
就我們的物聯網花園而言,管理工作僅包括定期給植物澆水、通過使用紅外成像(NDVI)監測光合作用活動來分析植物健康狀況,以及最后控制LED串燈的開關——最后這一項操作其實并不影響植物健康,純粹是為了花園裝飾。在花園里隨意開關燈光,然后拍照或錄像,檢查一切是否按設計正常運行,這對我來說簡直太有趣了——因為無論你身處世界的哪個角落,都有可能為我花園里的燈光進行開關操作。
如我之前所說,我住在公寓里,有一個小花園,但如果你有一個大后院,或者周圍有野生動物或嚙齒動物出沒,會吃掉你花園里的農作物,那么你可以考慮在這個系統中引入一個運動檢測系統來擴展其應用——當檢測到運動時,打開聚光燈或其他形式的威懾裝置。這樣一個集成的物聯網系統能做什么,其可能性僅受人們想象力的限制。只要樹莓派能夠連接到互聯網,這一概念還可以進一步擴展到管理一個真正的農場——人們可以在農場中使用低功耗的Xbee傳感器來監測環境活動。
系統搭建
我是使用一些簡單且價格低廉的組件來構建這個系統的,例如:
樹莓派(Raspberry Pi):作為運行機器人的“大腦”。
帶藍色濾鏡的樹莓派無紅外(NoIR)攝像頭。
433兆赫無線發射芯片。
433兆赫圣誕燈開關(接收器)。
小型水族箱水泵。
LED串燈。
這個項目需要電源供應和無線網絡連接。對我來說這不是問題,因為我住在公寓里,花園距離我的路由器只有10英尺。
滴灌
水族箱泵
管道
滴灌系統主要由一個小型水族箱水泵和若干管道組成。水泵連接至etekcity出品的433兆赫無線電源插座,而該插座又與電源相連。水泵被放置在一個裝滿水的水桶中。之所以選擇433兆赫的無線電源插座,是因為我們可以利用樹莓派上的發射芯片來遠程控制該插座的開關。我之前的一篇博客文章中已經解釋過如何實現這一操作。
在選擇水泵和管道時,主要有兩點需要考慮:
水泵的功率:就我的應用場景而言,我需要大約6到12英寸的揚程。隨著揚程的增加,水輸送速率會降低。我選擇的水泵在零揚程時能提供211加侖/小時的流量,在12英寸揚程時大約能提供192加侖/小時的流量。由于我的花園面積較小,這個流量對我來說已經足夠了。我計劃在給植物澆水時只運行水泵1分鐘,這應該能在1分鐘內輸送大約192加侖/小時 ÷ 60分鐘 = 3.2加侖的水,但根據實際經驗,我發現水泵每運行1分鐘,實際輸送的水量接近0.5加侖。不過,這對我來說仍然足夠用了。
管道應與水泵適配:這是我在搭建灌溉系統時遇到的主要問題。水泵自帶的最小連接器內徑(ID)為1/4英寸,但管道的外徑(OD)也是1/4英寸。在購買之前,我就根據產品規格知道這兩者是不兼容的,但我不得不購買,因為我找不到其他尺寸的滴灌管道,也找不到帶有更小噴嘴的水泵。我通過從Home Depot購買一個彎頭連接器解決了這個問題,這個連接器兩端的直徑尺寸都合適。
燈光
我還在花園周圍布置了一些裝飾性的LED串燈,這些燈同樣連接到了一個433兆赫的無線電源插座上,和灌溉水泵用的是一樣的插座。如前文所述,通過樹莓派搭配一個433兆赫的發射芯片,就能遠程控制這個433兆赫插座的開關。獲取插座開關代碼的校準步驟,在我之前的一篇博客文章中已經提及過。
攝像頭
我使用的是樹莓派無紅外攝像頭,并在其上貼了一層薄薄的藍色濾鏡,這有助于利用歸一化植被指數(簡稱 NDVI)來記錄光合作用活動。我將在后面的章節中解釋 NDVI 背后的原理。普通攝像頭與無紅外攝像頭的區別在于,無紅外攝像頭沒有紅外光阻擋濾鏡,也就是說,它能捕捉到紅外光。
關于如何將攝像頭連接到樹莓派的詳細步驟,在我之前的一篇文章中已有說明。
植物健康監測與控制——基于數字圖像的NDVI、氣象監測及灌溉管理
樹莓派視圖
NDVI 視圖
如前文所述,我使用的是樹莓派無紅外攝像頭,并在其上貼了一層薄薄的藍色濾鏡。實際上,這片藍色濾鏡就包含在樹莓派無紅外攝像頭的包裝盒里。
攝像頭捕獲的數字圖像由像素構成,每個像素都包含值,即紅、綠和藍三種顏色,也稱為RGB通道。因此,數字形式的圖像本質上就是一個多維數組,可表示為[高度索引, 寬度索引, [r, g, b]]。例如,假設我們有一張尺寸為1280 x 960的圖像A.jpg,那么第一個像素位于A[0, 0],其[r, g, b]值可能為[12, 40, 22](R、G、B每個值的范圍都是0-255),而最后一個像素則位于A[959, 1279],具有其他[r, g, b]值,整個多維數組共同構成了一張圖像,即[像素行, 像素列, R, G, B]。
歸一化植被指數(NDVI)的基本原理在于,植物在光合作用過程中會吸收可見光譜的光,但會反射紅外波長。因此,通過測量反射的紅外光強度,可以間接反映光合作用的情況。通常,紅色通道的互補金屬氧化物半導體傳感器對紅外波長也較為敏感,而在現代攝像頭中,通常會配備一個特殊濾鏡來濾除這些紅外波長。不過,樹莓派無紅外攝像頭并未配備此濾鏡,因此其紅色通道會間接記錄紅外波長。
如果在攝像頭上貼上一片物理藍色薄膜,理論上它將能夠捕獲更多的藍光和紅外光,同時濾除可見光譜中的紅光(R)和綠光(G)波長,從而便于從矢量化圖像數據中輕松計算出NDVI值。
因此,NDVI的計算公式為:(NIR-Blue)/(NIR+Blue),隨后可對圖像進行重新組合。
在實際搭建過程中,需要將攝像頭連接到樹莓派上,并在其頂部貼上那片薄薄的藍色濾鏡(即攝像頭包裝盒中附帶的那片)。
監測系統的另一部分則涉及對天氣的持續關注。我們利用Dark Sky API和Python庫來查詢天氣信息。Dark Sky是一項天氣監測服務,可根據GPS坐標提供天氣數據。當然,也可以使用直接連接到樹莓派的溫度濕度傳感器來獲取這些數據,但我發現傳感器讀數容易出錯。因此,為了避免在傳感器上投入過多資金,我們決定使用這項在線服務。
光合作用監測數據與天氣數據的結合,足以全面反映監測情況。
最后,我們轉向控制部分——即根據天氣情況和上次灌溉時間來決定是否給植物澆水。我為此設置了一些限制條件,將在下一節中進行詳細解釋。
軟件功能——Telegram 機器人
該軟件主要以 Python 腳本的形式實現——我力求保持其簡潔性,并未刻意追求完美的面向對象編程(OOP)設計模式,因為這并非我的目標。它功能實用且運行穩定。我更傾向于在樹莓派上使用一個名為“tmux”的應用程序,該程序可以創建一個獨立的會話,讓我在啟動 Python 腳本后,能夠關閉遠程 SSH 客戶端,而無需擔心會話中斷或 Python 代碼被終止。請執行以下操作安裝 tmux:
sudo apt-getinstall tmux
當你遠程登錄到樹莓派時,首先啟動 tmux,然后運行腳本:
tmuxpython3gardenBot_publish_v1.py &
要退出會話,請按下 Cntrl+B,然后按 D。現在,你可以關閉遠程 SSH 客戶端(如 Putty)。這個 Python 腳本實現了一個 Telegram 機器人,并作為控制客戶端與 Telegram 服務器進行通信。用戶發送的命令會先傳到服務器,然后運行在樹莓派上的 Python 代碼會輪詢這些命令。在之前的一篇文章中,我已經詳細解釋了如何創建自己的 Telegram 機器人。要讓這個腳本正常運行,你需要滿足一些先決條件:通過輸入以下命令來安裝 Darksky Python 庫:
pip3install darksky
名為 MP4box 的工具可用于視頻轉換。通過輸入以下命令進行安裝:
sudo apt-getinstall gpac
*gpac 是一組工具的集合,MP4box 是其中之一。
Telegram 機器人 API 密鑰
Telegram 機器人 Python 庫:通過輸入以下命令進行安裝:
pip3install python-telegram-bot
由于我計劃允許其他用戶監控和控制我的花園,因此我想確保自己作為管理員始終擁有完全的控制權——即能夠移除用戶、阻止添加更多用戶等。實際上,通過使用 Telegram 用戶 ID,這很容易實現。每個使用 Telegram 應用進行消息傳遞的用戶都會被分配一個唯一的用戶 ID,你可以通過簡單地給你的機器人發送消息,并使用網頁 API 讀取該消息來找到你的唯一用戶 ID。這個用戶 ID 是由 Telegram 分配給你的手機的,因此無法被軟件偽造。
如何查找你的用戶 ID?
在你的 iOS 或安卓設備上打開 Telegram 應用,搜索你新創建的機器人,并向它發送一條消息——比如“你好”。然后在你的網絡瀏覽器中輸入以下命令:
https://api.telegram.org/bot/getUpdates(這里的< token >是您在創建bot時分配的令牌,也不包括< >,只是復制粘貼密鑰)
服務器上的bot將此記錄為簡單的post請求,并在瀏覽器上顯示:
用戶功能
一旦你獲取了這個用戶 ID,你就可以利用它來實現你的機器人,使機器人僅對這個特定用戶做出響應。就我的情況而言,我希望其他人也能使用我的機器人來監控和控制我的花園,但同時我也要保持完全的控制權,因此我編寫了相應的函數來賦予我管理員權限。
我實現的主要功能是用戶與機器人開始交互時使用的:
/start
這個功能會返回基本的使用說明,將新用戶添加到物聯網控制系統中,并彈出一個命令菜單,用戶可以使用這個菜單來執行各種操作。
普通用戶只能使用通過這個菜單提供的函數,而管理員則擁有更多的可用函數。
管理員功能
由于樹莓派資源有限,因此需要控制澆水間隔和燈光切換間隔,這通過在代碼中使用一些控制變量來實現,以限制使用頻率。
示例:
只能在設定的間隔時間內澆水——例如每4小時或6小時
燈光可以每10分鐘切換一次,但不得超過這個頻率
可以每2分鐘拍攝一張照片,每5分鐘錄制一段視頻
基本上,管理員可以設置這些控制參數,這就引出了我們的管理員控制功能。
有一些特殊功能只有管理員才能使用:
/stop:停止機器人運行
/fetch:獲取當前物聯網用戶列表
/add:添加用戶
/rm:移除用戶
/disable {args}:禁用菜單按鈕功能和自動添加
/setexp {arg}:設置相機的曝光度
/setawb {arg}:設置相機的白平衡
/setLimit {arg}:為拍照、澆水、燈光、天氣和視頻設置時間限制
禁用自動添加功能后,管理員可以完全控制哪些用戶可以手動添加,以管理花園。禁用自動添加后,管理員可以使用/fetch、/add和/rm命令進行用戶管理。
此外,一旦執行了/stop命令,機器人代碼將終止運行,要重新啟動,需要登錄到樹莓派并手動啟動它。
Python代碼中提供了進一步的說明。
代碼部分
終于到了大家翹首以盼的環節,以下是我實際開發的社區物聯網花園機器人代碼。這部分代碼由三個文件組成:
1.gardenBot_publish_v1.py:這是Telegram機器人的封裝代碼以及用于運行的主文件。
2.iotcontrol.py:這是實際實現用戶功能的地方。
3.NDVI.py:這是用于處理NDVI圖片的文件。
你可以在注冊后,從我的網站上以zip格式下載這三個文件。
免責聲明:以下Python源代碼由Zen Of All LLC在注冊后免費提供,代碼使用者(下載者)可自由根據需要修改代碼。Zen Of All LLC對代碼本身、其維護、使用或使用者(下載者)所做的任何修改不承擔任何法律責任或義務。
英文說明:我為了測試我的概念驗證想法而非常迅速地編寫了這段代碼——請隨意仔細閱讀并根據你自己的需求進行編輯,如果出現問題,我不承擔責任。
你需要為下載該代碼而在本網站上創建一個登錄賬號。
電路原理圖-系統電路圖
代碼-物聯網花園代碼
Python 代碼 - 運行文件,請將這三個文件都放在同一個文件夾中,然后運行:
python3 gardenBot_publish_v1.py &
https://www.hackster.io/code_files/253191/download
# -*- coding: utf-8 -*-"""Created on Sat Apr 7 1328 2018@author: Zen Of All LLC (zenofall.com)"""importthreadingimportosfromiotcontrolimportiotcontrolfromtelegramimportInlineKeyboardButton, InlineKeyboardMarkupfromtelegram.extimportUpdaterfromtelegram.extimportCommandHandler , CallbackQueryHandlerfromtelegram.extimportMessageHandler, Filtersimportlogging##############################################################################################darkskyKey =''updater = Updater(token='')#Insert bot token#required by bot to execute functions see example: https://python-telegram-bot.org/control = iotcontrol(1234567,darkskyKey,33.8463634,-84.373057)# 1234567 is example admin id : find yours by going to web-api of telegram: https://api.telegram.org/bot/getUpdates# is telegram token during bot creation#read: https://zenofall.com/raspberry-pi-telegram-home-automation/#replace: 33.8463634,-84.373057 by latitude longitude of your location from google maps#we use adminId for special privileges on the botdispatcher = updater.dispatcher#bot examples: https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples#logging is always good#define logging format and file location####################Bot User Functions ##############################defstart(bot,update):#display menu """ BotFunction: First function using which users are supposed to interact with this bot"""
control.autoAdd(bot,update)
keyboard = [[InlineKeyboardButton("Instructions", callback_data='5')], [InlineKeyboardButton("Take Pic", callback_data='1')], [InlineKeyboardButton("NDVI Pic", callback_data='8')], [InlineKeyboardButton("Video", callback_data='9')], [InlineKeyboardButton("Local Weather", callback_data='7')], [InlineKeyboardButton("Water Plants", callback_data='2')], [InlineKeyboardButton("Ligts on/off", callback_data='3')], [InlineKeyboardButton("Status", callback_data='4')], [InlineKeyboardButton("Tutorial & Code", callback_data='6')] ]
message="Hello {} (telegram_id:{}).\nPlease read: Instructions first.\nType& send:/start again to pull up menu at any time.\n*Disclaimer: Bot logs all user commands. \nPlease Choose: \n".format(update.message.from_user.first_name,update.message.from_user.id) reply_markup = InlineKeyboardMarkup(keyboard) bot.send_message(chat_id=update.message.chat_id,text=message, reply_markup=reply_markup)
defbutton(bot,update): """ BotFunction: Function that implements the button callbacks""" query = update.callback_query bot.answer_callback_query(callback_query_id=query.id, text="Choice registered,processing request..")
if(query.data=='1'):
#control.sendPic(bot,update,False) t=threading.Thread(target=control.sendPic,args=(bot,update,False)) t.setDaemon(True) t.start() if(query.data=='8'): #global enablePic
t=threading.Thread(target=control.sendPic,args=(bot,update,True)) t.setDaemon(True) t.start() if(query.data=='9'): #global enablePic
t=threading.Thread(target=control.recordVideo,args=(bot,update)) t.setDaemon(True) t.start() if(query.data=='2'):
control.water(bot,update)
if(query.data=='3'):
control.light(bot,update)
if(query.data=='7'):
control.weather(bot,update) if(query.data=='4'):
control.status(bot,update) if(query.data=='5'): control.instructions(bot,update)
if(query.data=='6'): control.tutorial(bot,update)############# Admin only program functions####################
defunknown(bot, update): """ Unkwown command handler""" logging.info('/unknown,{},{}'.format(update.message.from_user.first_name,update.message.from_user.id)) bot.send_message(chat_id=update.message.chat_id, text="Sorry Command Not Recognized! Type: /start for all user actions.") defshutdown():
updater.stop() updater.is_idle=Falsedefstop(bot, update):#hardcoded for security only i can issue this command """ Admin Command: stop the bot Usage: /stop
""" #global adminId logging.info('/stop,{},{}'.format(update.message.from_user.first_name,update.message.from_user.id)) if(update.message.from_user.id==control.getAdminId()): bot.send_message(chat_id=update.message.chat_id, text="Stopping Server!") threading.Thread(target=shutdown).start() else: bot.send_message(chat_id=update.message.chat_id, text="ERROR: Unauthorized User!")defaddIoTUser(bot,update,args): """ Admin Command: add users manually Usage: /add 12345 324567 8726251
The numbers are ids of users
""" control.addIoTUser(bot,update,args)
defremoveIoTUser(bot,update,args): """ Admin Command: remove users manually Usage: /rm all /rm 123445 """ control.removeIoTUser(bot,update,args)
deffetchIoTUserList(bot,update):
""" Admin Command: add users manually Usage: /fetch
""" control.fetchIoTUserList(bot,update)
defdisable(bot,update,args):
""" Admin Command: disable functions in button menu Usage: /disable 1 (for pic) /disable 2 (for water) /disable 3 (for light) /disable 4 (for video) /disable all (disable all)
""" control.disable(bot,update,args)
defsetAwb(bot,update, args):
""" Admin function: set white balance Usage: /set 1.0 1.3 (red,blue)
""" control.setAwb(bot,update,args)
defsetExp(bot,update, args):
""" Admin function: set white balance Usage: /setExp auto [auto, sports, night] https://picamera.readthedocs.io/en/release-1.10/api_camera.html?highlight=exposure_mode#picamera.camera.PiCamera.exposure_mode """ control.setExp(bot,update,args)defsetLimit(bot,update,args):
""" Admin function: set time limits Usage: /setLimit pic 2.0 /setLimit water 5.0
""" control.setLimit(bot,update,args)
#def clear(bot,update,chat_data):# chat_data.clear()
####____global variables____#########
#Kill ServoBlaster - interferes with GPIO #something specific to me###comment this out if you dont have servo blastertry: os.system("sudo killall servod") exceptExceptionase: print(e) pass############ #comment ends
#####____HANDLERS_____########bot examples: https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examplesstart_handler = CommandHandler('start', start)stop_handler = CommandHandler('stop', stop)#menu_handler = CommandHandler('menu', menu)addHandler = CommandHandler('add',addIoTUser, pass_args=True)removeHandler = CommandHandler('rm',removeIoTUser,pass_args=True)fetchHandler = CommandHandler('fetch',fetchIoTUserList)disable_Handler = CommandHandler('disable',disable,pass_args=True)awbHandler = CommandHandler('setawb',setAwb,pass_args=True)expModeHandler = CommandHandler('setexp',setExp,pass_args=True)setLimitHandler = CommandHandler('setLimit',setLimit,pass_args=True)#clearHandler = CommandHandler('clear',clear,pass_chat_data=True)unknown_handler= MessageHandler(Filters.command, unknown)#####____DISPATCHERS_____#######dispatcher.add_handler(start_handler)dispatcher.add_handler(stop_handler)#dispatcher.add_handler(menu_handler)dispatcher.add_handler(addHandler)dispatcher.add_handler(removeHandler)dispatcher.add_handler(fetchHandler)dispatcher.add_handler(disable_Handler)dispatcher.add_handler(awbHandler)dispatcher.add_handler(expModeHandler)dispatcher.add_handler(setLimitHandler)dispatcher.add_handler(CallbackQueryHandler(button))#dispatcher.add_handler(clearHandler)dispatcher.add_handler(unknown_handler)#Always keep unkown handler last else commands not recognized#start the bot client - poll for server messagesupdater.start_polling()updater.idle()
-
機器人
+關注
關注
213文章
29504瀏覽量
211613 -
物聯網
+關注
關注
2927文章
45900瀏覽量
388204 -
樹莓派
+關注
關注
121文章
1947瀏覽量
106998
發布評論請先 登錄
樹莓派下一個關注的領域是什么?
基于樹莓派采集網關
樹莓派在物聯網開發的應用
樹莓派能做什么
樹莓派3wifi配置_樹莓派3開啟wifi熱點_樹莓派3的wifi使用教程
樹莓派 3 或開啟物聯網革命的大門
基于樹莓派的智能花園滴灌
類樹莓派網關:物聯網應用的新標桿

評論