我如何使用 Linux 喂我的猫
猫咪喜欢玩具。我们的猫,Cotton 和 Tulip,慢慢地用它们的小塑料玩意儿——乒乓球、毛茸茸的老鼠、铃铛、弹簧和抓挠的东西占领了我们的房子。猫咪很少感到无聊。在周末,我和我的妻子会满足小猫们的要求,在房子周围扔它们的玩具,甩动绳子,摇铃铛。我们挠它们的背,喂它们零食。它们都非常喜欢这些臭烘烘的小鱼零食;我们只需要摇一摇罐子,它们就会停止正在做的事情,冲到厨房。它们的英语词汇现在包括它们的名字以及单词“good”(好)和“treats”(零食)。
然而,周一到周五,朝九晚五,猫咪们要自己负责娱乐。当我们外出时,我们确信猫咪们会玩得很开心。我们的地毯几乎总是被移动,乒乓球最终会出现在水碗里,毛发覆盖着我们的椅子。工作日和周末之间唯一真正的区别是我们的存在和缺少零食。
我们必须工作,但这并不意味着我们的猫咪应该没有臭烘烘的小鱼,对吧?为什么我们的经济需求应该对它们的零食时间产生负面影响?难道不应该由我们来为它们制造一台联网的、基于 Linux 的猫粮喂食器吗?
我们从哪里开始呢?Linux 联网猫粮喂食器有三个关键要素:系统上的逻辑、与设备通信的方式以及与之通信的设备。我选择了 Python 作为逻辑部分,通过串行端口与我自己设计的微控制器猫粮喂食器进行通信。让我们从最底层,设备开始,逐步向上,直到逻辑。
我第一次听说 BASIC Stamp 微控制器是在 Slashdot 上的一篇文章中,文章中说三个人使用 BASIC Stamp 来控制螺栓枪。他们拍了一些螺栓摧毁水果的精彩照片。我很快了解到,微控制器无处不在。它们是我们微波炉和遥控器中的逻辑部件。它们很小,而且通常难以使用。
Parallax, Inc. 专门为非工程师,特别是为学生和业余爱好者制造微控制器。Parallax 产品文档齐全,易于使用且相对便宜。我从 Radio Shack 购买了 Homework Board,这是最便宜的入门套件,价格约为 75 美元。它附带了一本书、一袋用于书中实验的电子元件以及电路板和芯片。
Stamp 本身实际上是一个带有少量内存的 PIC 微控制器。通常,您需要使用汇编语言等低级语言来对微控制器进行编程。BASIC Stamp 与典型微控制器的区别在于您用来使其执行操作的编程语言。Parallax 开发了 BASIC 的超集,称为 PBASIC,它使您可以轻松快速地构建富有表现力且有用的程序。此外,Homework Board 还有一个集成的免焊面包板,可以快速重新布线项目。
BASIC Stamp 有 16 个 I/O 引脚。每个引脚根据您创建的程序设置为高电平 (+5V) 或低电平 (0V)。假设您想让 LED 闪烁。您将一端连接到 I/O 引脚,另一端连接到接地引脚。您编写一个程序,该程序指示每秒将 I/O 引脚设置为高电平(开),等待一秒钟,然后将其设置为低电平(关)。现在用伺服电机替换 LED,我们就有了猫粮喂食器的雏形。
I/O 引脚也监听 +5V 或 0V。PBASIC 甚至有一个内置函数,允许 I/O 引脚读取串行数据,串行数据的基础是构成二进制字的高/低电荷。暂时不要太担心串行连接;我们将在下一节中更多地介绍它们。现在,请理解 BASIC Stamp 可以轻松地通过串行电缆从 Linux 系统接收命令,并打开驱动猫粮喂食器的伺服电机。
Parallax 在创建一个有趣的业余爱好者社区方面做得非常出色。有两个邮件列表专门讨论其产品,并且有数十个站点提供项目创意。尽管 BASIC Stamp 的最佳集成开发环境仅适用于 Microsoft Windows,但在 Parallax 的帮助下,创建了一个名为 bstamp 的工具,可以使用 Linux 对 BASIC Stamp 进行编程。以下是标记化程序并运行它的示例
# bstamp_tokenize catcode.bs2 catcode.tok PBASIC Tokenizer Library version 1.16 # bstamp_run catcode.tok Model: Basic Stamp 2 Firmware version BCD = 16 Ack = 0 Ack = 0 Ack = 0 Ack = 0 Ack = 0 Ack = 0 Ack = 0 Ack = 0 Ack = 0 Ack = 0 Ack = 0 Ack = 0 DEBUG OUTPUT: (Press [Control]-[C] to complete sequence) _________________________________________________________ Waiting for Command Received Command: B Feed the kitty! Waiting for Command Received Command: B Feed the kitty! Waiting for Command __________________________________________________________ Received [Control]-[C]! Shutting down communication!
我所知道的关于串行的所有知识都来自 David S. Lawyer 和 Greg Hankins 的优秀“串行 Howto”。这是一份厚厚的文件,其中包含许多关于 RS-232 标准的有趣低级信息。
尽管 BASIC Stamp 通过串行连接与 bstamp 通信,但 Homework Board 提供的串行端口并不是真正串行通信的良好候选者。Parallax 以非传统方式连接了该端口。首先,发送到端口的所有命令都会回显到主机,这使得双向通信变得困难。
RS-232 标准规定沿电缆传输的电信号为 +/- 12V。因此,如果我们直接将串行连接连接到 Stamp I/O 引脚,我们很可能会将其烧坏,因为它期望 +5V。解决方案是使用中介来降低来自 PC 的 12V 信号至 5V,并将来自 Stamp 的 5V 信号升至 12V。确实存在这样一种芯片,它被称为 MAX232。幸运的是,您可以从一位名叫 Al Williams 的德克萨斯人那里获得专门为免焊面包板构建的基于 MAX232 的 RS-232 兼容适配器。该设备称为 RS-1,本文的在线资源中包含指向其网站的链接。
从 2.4 内核开始,Linux 将串行端口命名为 /dev/ttyS0、1、2、3 等等。这些设备文件的作用类似于任何其他文件。您打开它们、读取或写入它们并关闭它们。操作系统使用 16k 缓冲区缓冲读取和写入,这对于大多数串行通信来说已经足够了。这很好;您不必担心因为您没有在设备通过电线发送位时立即读取而丢失位。这也意味着当您准备发送时,您需要在操作系统端显式刷新缓冲区。
由于端口被视为文件,因此您需要相应地设置权限。在我的情况下,因为我最终希望 CGI 程序驱动喂食器,所以我将 apache 设置为所有者。如果您处于安全环境中,您可以始终chmod 777 /dev/ttyS0,但这显然是不安全的。最好预先决定您要对端口执行的操作,并尽可能以安全的方式设置权限。
由于 Linux 将我们的串行端口视为文件,因此很容易使用 Python 与 Stamp 通信。Python 的文件对象使读取和写入文件变得简单
>>> f = open("/tmp/cotton.txt",'w') >>> f.write("Cotton loves treats!") >>> f.close() >>> f = open("/tmp/cotton.txt",'r') >>> f.read() 'Cotton loves treats!' >>> f.close()
但是,您可以看到,虽然打开和关闭文件很容易,但如果该文件实际上是串行端口,则这样做可能会变得棘手。对我们来说幸运的是,我们的 Python 脚本只需要一次写入一个字母,以告诉喂食器分配零食。也就是说,我想尽可能使用强大的通信方法,所有这些打开和关闭都让我感到担忧,因为我认为这个项目将永远是一个进行中的工作。也许猫咪们会想按下一个按钮,向我们在工作场所发送消息,谁知道呢?关键是,我想要一些比直接文件句柄更了解串行的东西。幸运的是,其他人也想要完全相同的东西。Chris Liechti 非常好心地创建了 PySerial,正是为了这种情况。以下是 PySerial 在操作中的示例
>>> import serial >>> sp = serial.Serial(0) >>> sp.portstr '/dev/ttyS0' >>> sp.write("F") >>> sp.readline()
我们实际上并没有打开 /dev/ttyS0,我们打开的是 0。PySerial 非常聪明,知道我们指的是第一个串行端口并相应地打开它。这也意味着 PySerial 是跨平台的,因此您不必知道您的端口在一台机器上是 /dev/ttyS0,而在另一台机器上是 /dev/ttya。这一切都由 PySerial 处理。
现在 Python 正在通过串行端口进行通信,我们需要将其联机。我承认,我不太喜欢 CGI 环境中的 Python。不过,不要让这阻止您;有一个工作组的使命是改进 CGI 库,并且有几个 Python Web 框架使 CGI 变得不必要。此外,mod_python 在其最新版本中包含了 Python Server Pages (PSP),这是一种类似于 PHP 的语法,用于将 Python 直接混合到 HTML 页面中。简而言之,当涉及到在线使用 Python 时,您有很多选择。然而,对于我们的目的而言,Python CGI 库足以让我们的猫咪吃饱。
以下是一个简短的 CGI 示例,用于一个简陋的猫粮喂食器
#!/usr/bin/env python import serial import cgi class Feeder: def __init__(self): self.port = serial.Serial(0) def feed(self): self.port.write("B") print 'Content-Type: text/html' print # Blank line marking end of HTTP headers cgiParameters = cgi.FieldStorage() control = Feeder() control.feed() print "<p>Thanks for feeding the kittens!"
首先,我导入 PySerial 和 CGI 模块,然后我声明一个类 feeder。该类的构造函数打开串行端口。该类有一个方法 feed,它在线路上向喂食器发送一个任意字符,在本例中为 B。在另一端,PBASIC 正在监听字符 B,并在看到它时分配零食。
我用转盘式设计构建了猫粮喂食器,零食将被放入单元格中,由伺服电机驱动的旋转桨分隔。当桨叶旋转时,一堆零食会通过其中一个单元格的切口掉落到食物碗中。我使用了一个用于存放冷冻华夫饼的容器作为转盘,带有一个定制切割的旋转桨和一个 Parallax 伺服电机来驱动它。整个组件,包括 BASIC Stamp 电路,都安装在我家庭办公室的塑料储物箱中。该盒子连接到我的 Web 服务器上的 /dev/ttyS0 用于喂食器,/dev/ttyS1 用于调试端口。图 1 是我书架上的猫粮喂食器的照片。我正在使用 Fedora Core 1 和 Apache 2.0.48。

图 1. Cotton 从喂食器获取零食
最初的问题是我如何确定桨叶已旋转到足以掉落零食并阻止它们旋转?最简单的解决方案是在转盘侧面放置一个小传感器,该传感器可以检测到桨叶在其前方经过。我选择了一个来自 Parallax 的传感器,主要用于在地面上找到黑线。我在每个桨叶的边缘放置了一块扁平的黑色海报板,并将传感器放置在孔边缘正后方的转盘底部。当喂食器喂食时,传感器会检测到第一个桨叶何时移动过去;当第二个桨叶经过传感器时,伺服电机停止。
我花了几天时间进行实验,相对快速地连接了 Stamp。随附的面包板是 Homework Board 的一项出色功能。我能够快速地重新布线和测试电路,而无需焊接和拆焊。图 2 显示了完整的原理图。图 3 是连接好的 Homework Board 的照片。
编写 BASIC Stamp 的代码主要是从 Parallax 网站复制和粘贴代码示例。列表 1 是我在 Stamp 上使用的最终代码。PBASIC 在很大程度上依赖于 GOTO 语句进行流程控制,这让我花了一些时间来适应。
列表 1. 喂食器 PBASIC 代码
'{$STAMP BS2} cmd VAR Byte temp VAR Word LineSnsrPwr CON 10 LineSnsrIn CON 9 Sense VAR Word SStart VAR Word main: DEBUG "Waiting for Command", CR SERIN 7, 84, [cmd] DEBUG "Received Command: ", cmd, CR IF cmd = "B" THEN feed GOTO main feed: DEBUG "Feed the kitty!", CR HIGH LineSnsrPwr ' activate sensor HIGH LineSnsrIn ' discharge QTI cap PAUSE 1 RCTIME LineSnsrIn, 1, SStart DEBUG "First Reading: ", DEC SStart, CR GOTO sensor feed2: IF Sense < (SStart - 200) THEN pastfirst IF Sense > (SStart + 200) THEN stopfeed FOR temp = 1 TO 100 PULSOUT 0,600 GOTO sensor sensor: HIGH LineSnsrPwr ' activate sensor HIGH LineSnsrIn ' discharge QTI cap PAUSE 1 RCTIME LineSnsrIn, 1, Sense GOTO feed2 pastfirst: DEBUG "Past First!", CR SStart = Sense GOTO sensor stopfeed: DEBUG DEC Sense, CR GOTO main
列表 2 显示了我们用来驱动喂食器的完整 Python 代码。CGI 脚本使用了 Python 的内置 shelf 模块;shelf 允许您将活动对象存储在 DBM 数据库中。除了 shelf 之外,我还使用了 Cheetah 模板引擎。行t = Template(open('feeder.tmpl.py').read())打开一个名为 feeder.tmpl.py 的 HTML 模板,读取内容并将其用作 Cheetah 模板。模板格式类似于<p>猫咪已被喂食 $fed 次</p>。当我们将模板变量 t.fed 设置为某个数字(例如 5)时,该行变为<p>猫咪已被喂食 5 次</p>。我的妻子,一位职业平面设计师,为该页面制作了一些图形。
列表 2. 最终的 Python CGI 代码
#!/usr/bin/python import serial import cgi from Cheetah.Template import Template import shelve t = Template(open('feeder.tmpl.py').read()) port = serial.Serial(0) class Feeder: def __init__(self): self.total_fed = 0 def feed(self): self.total_fed = self.total_fed + 1 port.write("B") def getTotalFed(self): return self.total_fed print 'Content-Type: text/html' print # Blank line marking end of HTTP headers form = cgi.FieldStorage() d = shelve.open("feeder.dbm") if d.has_key("control"): """ if shelf file exists, open it, otherwise create it and a new instance of the Feeder class """ control = d['control'] else: control = Feeder() d['control'] = control if form.has_key("command") and \ form['command'].value == 'feed': """ if we received the feed command, feed, otherwise, show the index page""" control.feed() contents = """ <p class="header"> Thanks for the Treat!</p> <p class="body">Meow!</p> <p valign="bottom"> <a href="index.py">Back</a></p>""" else: """The index welcome page""" contents = """ <p class="header">Cotton & Tulip Love Treats!</p> <p class="body"> Click the Fish Below to Give Cotton and Tulip a Treat</p> <p><a href="index.py?command=feed"> <img border="0" src="images/dance_fish.gif"> </a></p> <p><br><p> <p class="footer" valign="bottom"> The kitten feeder is an honest to goodness device attached to a Linux Server in Chris and Camri's apartment. """ """Set the variables that Cheetah will use""" t.contents = contents t.fed = control.getTotalFed() """Print the complete Page""" print t """Save the control to our shelf""" d['control'] = control d.close()
猫粮喂食器已开始营业。偶尔,会有零食卡住,但 95% 的时间,猫咪都能吃到臭烘烘的小鱼。我们已经有了猫粮喂食器 v.2.0 的计划。我们很想添加一个网络摄像头,以便白天可以看到小猫,并将设备移动到厨房的无线笔记本电脑上。与我们的大多数项目一样,喂食器是一个正在进行中的工作。欢迎访问网站,给 Cotton 和 Tulip 喂食零食。
本文资源: /article/7741。
Chris McAvoy 是伊利诺伊州芝加哥市的一名 UNIX 管理员。他和他的妻子 Camri 以及他们的两只猫 Cotton 和 Tulip 住在一起。Chris 和 Camri 有很多爱好。他们的网站是 www.lonelylion.com。