该物联网花园系统利用树莓派和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文章
29933浏览量
214377 -
物联网
+关注
关注
2933文章
46461浏览量
395478 -
树莓派
+关注
关注
122文章
2054浏览量
107831
发布评论请先 登录
树莓派下一个关注的领域是什么?
树莓派在物联网开发的应用
树莓派能做什么
树莓派3wifi配置_树莓派3开启wifi热点_树莓派3的wifi使用教程
树莓派 3 或开启物联网革命的大门
基于树莓派的智能花园滴灌
类树莓派网关:物联网应用的新标杆

评论