我们用 JavaScript Hack 了家里的咖啡机

除了区块链,物联网或智能家电大约是当下最时髦的话题。若能将已有设备改装成物联网版本,就无需购买智能家电了。我们决定就这么做,用 JavaScript 构建一个网络驱动的咖啡机,并在超文本咖啡壶控制协议Hyper Text Coffee Pot Control Protocol)创建 19 年后的今天,重新使用此协议。
geek-and-poke-iot

讲故事时间

我和室友有几个通过 Amazon Echo Dots 控制的网络驱动设备。虽然很便捷,但还不能做到在床上就能触发咖啡机。

幸运的是,室友有一台闲置的德龙 Latissima 咖啡机,他也舍得此咖啡机;而且此咖啡机有一些电子按钮,整个系统貌似可编程。

为何采用 JavaScript?室友并非开发人员,而我已有三年没写过一行 C++ 代码了。此外,个人觉得 JavaScript 用起来很舒服。硬件和 JavaScript 均是事件驱动,其中心问题均是像“此控件是否被按下?”。但 JavaScript 硬件编程文档很少,这对我们构成一个不小的挑战。

硬件选择

确定采用 JavaScript 进行硬件编程之后,接下来需要考虑的是选取何种硬件:

Espruino

espruino-header

Espruino是一个开源的固件,有了它便可以在 ESP8266,或其它低功耗的微控制器上运行 JavaScript。

Espruino 自带一个基于网页的 IDE,方便程序员编写 JavaScript,并将其部署在硬件上。同时,它还拥有一套类似 Node.js 的模块系统,当然 并不是要让 Node.js 运行在微控制器上。

家里仅有的 Espruino 板是 Espruino Pico,而且不带WiFi,为此我必须焊接一个 ESP8266,不过谁有时间做这些?我们需要的只是咖啡!

Tessel

 

tessel-header

另一个选项是名为 Tessel 的开源硬件项目,此项目中的一部分便是 Tessel 2。硬件不仅能被 JavaScript 所控制,而且完全支持 Node.js 运行,还能存储整个程序,无需与电脑进行连接。
tessel-photo

Johnny-Five

johnny-five-header
第三个选项是 Johnny-Five,它实际上是一个 npm 模块,可以与多个微控制器进行通信。初始化阶段,借助 Johnny-Five 的 I/O plug-ins, 采用 Firmata 协议,便可与 Arduinos,或者其它平台进行通信。例如,Particle PhotonRaspberry PI 甚至 Tessel 2

我们的选择

我们决定用 Tessel 搭配 Johnny-Five。通过这种方式,写的代码与平台独立,但可以直接运行;无需链接计算机,或者在云端运行部分代码。

相信我,我是一名工程师

现在,我们已经知道了要用的工具,接下来开始我们的咖啡机操控之旅。考虑到硬件并没有一个版本控制系统,我们先看一眼这个漂亮的、正在工作的咖啡机,并记住它。
original-latissima

那我们开始吧!我们俩都不是电子工程师,而且虽然在大学期间学过两个学期,但什么都不记得了。
Dog floating in space with caption I have no clue what I'm doing here

注意:我们工作的硬件,通电后会很烫。从这个角度看,我们做的一些事,既不智能,也不安全。倘若你要做同样的事情,风险自己承担。
homer-electro-shock

在没有任何提示线索的情况下,如何操控硬件呢?

我们采用一种最浅显的方式,拆解咖啡机,一探究竟。
open-latissima

然后看到的就是这些,哪些是我们关心的呢?我的意思是,我们并不打算更改煮咖啡的方式,仅是触发煮咖啡的工序,以及打开/关闭咖啡机。

幸运的是,看起来貌似行得通。细看你会发现,带有按钮的控制面板,以及控制煮咖啡的流程的微控制器,是由一个普通的可插拔电缆进行连接。
Annotated Picture of opened Latissima highlighting Buttons and Microcontroller

倘若我们能够模拟控制板和微控制器之间的信号,想必便能控制咖啡机。此刻,仅需要找出此类信号。

我们该做些什么呢?

为了鉴别发送了何种信号,首先先看控制板:

可以看到如下有意思的控件:

  • 带有八个管脚的插头
  • 七个LEDs
  • 六个按钮开关

我们认为,八个管脚中至少需要一个用来提供电量。我们发现,八根电线中标记为红色的那一条它可能负责提供电源。那么剩下的七个管脚呢?按照程序员的思维方式,我们采用二进制的标签,表示这七个 LED 和六个开关的状态,因此需要为 LED 准备三个管脚,为开关准备三个管脚。最后一个管脚我们用来做什么呢?

基于假设,首个管脚为电源,那我们为其连上电源,来探究接通余下的管脚会发生什么。为此,将原始电线的一端连在控制板上,将一束跳线连到另一端。通过此种方式,便可将七个未知管脚连入 Tessel 管脚中。
wireup

对于电源,有两种可选项 3.3V 和 5V。首先尝试 3.3V,很少有 LED 被点亮。接着我们尝试 5V,成功了。有一半成功了,七个 LED 中有三个被点亮,这也是一种进步。

Tessel 带有模拟和数字管脚,这意味着除了数字信号(高/低,也叫 0/1)之外,我们还可以测量这些管脚的电压。我们先尝试测量余下七个管脚的电压。

我们为 Tessel 写了一个很小的脚本,借助于 Johnny-Five 初始化连接主板,并在模拟模式下创建管脚实例(模式:2)。接着将这些管脚放入 Johnny-Five 的 REPL 中,这样便可测量任何我们想要的值。

不幸的是,模拟读数并不能得到我们想要的结论。测量值波动很大,无法有效提取信息;或许数值读数能帮到我们:

借助这样的配置,采用一个比特,便可探究是否控制了 LED 或者找出一些规律。确实如此,对数字管脚分别设置 HIGH 和 LOW,我们发现管脚 4-6 可以打开/关掉 LED4,LED5,LED7。但不能打开剩余的LEDs。但至少我们知晓有 4 个管脚与 LED 相连。剩下的呢?
homer-confusion

或许它们是按钮!我们将剩余的管脚初始化为按钮,尝试其是否工作:

这也凸显了 JavaScript 和硬件相结合的一个优势。相信每位 JavaScript 开发人员此刻都会感到非常自在。我们有一个 EventEmitter 类,其基于 Button 类拥有两个可监听事件 ‘release’ 和 ‘press’。

我们确实测到了一些东西!按下最上面的两个开关,检测到 btn7 和 btn8 触发了这些事件;按下右下角两个按钮,同样也能检测到 btn7 和 btn8 触发了这些事件。将 p3 设置为 HIGH,能检测到剩余的两个按钮的行为。
control-plate (1)

那么现在我们都知道了些什么呢?

  • 管脚 1 为电源,打开 LED4, LED5, LED7
  • 管脚 4-6 能够关闭 LED4,LED5,LED7
  • 管脚 7-8 对 S1-S6 开关能做出反应
  • 管脚 2 能操纵开关 3 和开关 4
  • 管脚 3 能操纵开关 1 和开关 2

但这远远不够,如何分辨按下的是哪个按钮?我们能够触动或使按钮失效,但更想知道的是这些按钮是何时被按下的。经过5个小时的低效尝试,须改变策略了。
Gif of monkey pushing down a laptop

重新绘制电路板

fritzing-header

于是我们采用一种更加系统的方式,试着理清主板上的数字电路。为此,我们为主板拍照,将其放大,并追踪其上的每一条线。接着采用万用表测量两点之间的电阻,验证两点之间是否存在连接。倘若存在某个连接,使用一个叫 Fritzing 的工具,便可在图表上标记这条连接。Fritzing 是一个很棒的开源工具,用以标记硬件设置。本图表忽略了很多东西,以至于看起来像一个电阻器。最后呈现出来的是一个很漂亮的图表。
original-schema

我知道你想说什么,这看起来比之前还糟糕。但事实上却不尽然。首先,我们很容易就找出了 LED 连接。但最重要的是须知晓 LED 如何允许电流仅按某一方向通过。而在图表上进行标记,有助于我们追踪电流,以便更好地理解整个电路。

除了管脚 1 之外,管脚 2 和 3 均为电源!这就解释了为何剩余 LED 无法点亮。那开关呢?

经历了 8 个小时缓慢的进展,盯着这个小板子看,我们意识到一直以来自己都忽略了一件事。
control-plate-diodes

这三个元件并不是电阻器,它们是二极管!!二极管允许 LED(发光二极管)中电流只沿着一方向流动。因此,我们更新了图表,移除了 LED,并对图表进行了清理。
schema-switches

终于解决了困惑我们已久的协议:

 

有人想来杯咖啡吗?

如何运用此协议煮咖啡呢?考虑到开关依赖于两个管脚之间传输的电流,模拟此协议势必太过复杂。但有一个更简单的方式,用继电器!

继电器与开关类似,但是通过微控制器,可以数字化地操控这些继电器。考虑到我们已知控制板的所有电路,可以自行创建一个相同的电路,用继电器对开关做简单替换。

我们仅有四个继电器,因此只模拟右边三个开关,它们分别控制大杯咖啡、浓咖啡、电源。这些正是我们需要的!同样,我们分别接了三个 LED 作为视觉反馈,表明一切正常运行。

初次布线看上去不是特别整洁,如下:
first-wire-up

没有足够的跳线,因此决定将继电器倒置放入面包板(breadboard)中。

接着检测布线。不过在打开咖啡机前,我们要写点代码。快速实现一个小脚本,借助 Johnny-Five 的内嵌类 Relay,手动控制三个继电器,将其放入 REPL 中。

写好代码,倒一些水到咖啡机中,通过设备背面的主开关,打开咖啡机,为代码通电。倘若一切正常,运行 power.open() 便可打开设备。需要加热 20 秒,直到 LED 停止闪动。接着开始煮咖啡,其实第一次测试我们用的是水。
Gif of first run using the REPL

成功了!10个小时过后,太阳渐渐升起,我们终于得到了咖啡。或者说是热水。因为我们忘记买 Nespresso 胶囊了,不管怎样第一步成功了。
Gif of cast of Friends jumping

互联网化的咖啡壶

太棒了,仅需要手提电脑连接 Tessel,Tessel 连接咖啡机,就可以这样煮咖啡了。然后写点代码至 REPL 中。此刻是时候将其改装为互联网驱动的咖啡壶了。

幸运的是,19年前的今天,一些聪明的人们,开发了一个足以改变生活的协议。超文本咖啡壶控制协议Hyper Text Coffee Pot Control Protocol),或简写为 HTCPCP,用以控制互联网驱动的咖啡壶。这难道不是我们应该在 2017 年重新启用这个已被遗忘的协议,用以控制我们的咖啡机的一个信号吗?

什么是超文本咖啡壶控制协议?

或许你注意到 HTCPCP 是 1998 年的一个愚人节玩笑。那是因为它建议将 HTTP 状态代码 418-I’am a teapot,作为 RFC 文件的一部分。此状态代码时至今日依旧不是一个实际的标准,仅作为许多软件“复活节小彩蛋”。以谷歌为例:google.com/teapot

当然此协议远不止这一个状态代码,我们来探究一番:

1 基于HTTP
2 它提议除了 POST 方法外,BREW 方法也可以用来发布煮咖啡的命令
3 定义了 coffee:// URI规则,借此访问不同的咖啡壶
4 甚至有国际化本 URI 规则,强烈建议你阅读此部分
5 推荐一个新的头字段 Accept-Additions,指定你想要的调味品,比如牛奶、威士忌、或者南瓜拿铁
6 清晰地定义了 Content-Type,以及可传递的信息
7 其它事项

换言之,它命中注定要被实现。

在正式开始之前,我们需要讨论一下本 RFC 文件的不确定性以及缺点。

文件两次提及,需使用不同的两个 Content-Type。其一为 application/coffee-pot-command,其二为 message/coffeepot。 application/coffee-pot-command 作为 Content-Type 发送给咖啡机,message/coffeepot 作为回应类型,因此我们不能同时使用这两个值。

另外,与1997年的标准咖啡壶,如今的咖啡机所支持的咖啡种类繁多。因为需要修改 URI 规则解决这些差异:coffee://host/pot-{identifier}/{coffee type}。这尤为重要,因为 application/coffee-pot-command 仅支持 start 和 stop 这两个官方值。

现在我们来实现它

418 – 我是个茶壶


考虑到 HTCPCP 基于 HTTP,以及便捷和集成,我们采用 Node.js 和 http module,实现一个标准HTTP服务器。为了保持 Tessel 代码的轻量级,决定不使用任何诸如 Express 的库,而是使用 URL 这种纯粹的方式。

同样,与硬件交互的逻辑代码放入独有的文件中,以后便可容易拿来为其它咖啡机所用,采用同样的服务器逻辑。

此处用到的某些 JavaScript 特性并没有包含在 Tessel Node 版本中,因此借助 TypeScript 将其转译为 ES5 兼容的代码。

作为服务器,我们决定实现以下功能:

1. GET, POST 以及 BREW方法实现

我们接纳了 BREW 方法,并对其做了实现。传统的 GET 用来获取咖啡机状态,而 POST 则用来发布命令

2. Content-Type 校验

仅接受 application/coffee-pot-command 以及 text/plain,作为 POST 以及 BREW 请求的合格的 Content type。随后还要用 Alexa 为集成添加 text/plain。

3. 增加 Accept-Additions 支持

仅有四个继电器,我们是无法操纵所有的咖啡机功能的。因此,咖啡机不支持任何附加功能。有人订阅了咖啡机并不支持的额外服务时,我们对头字段和行为更正添加了验证,以下是额外服务清单。

4. 418 – 我是个茶壶

显然,我们需要实现一些事无巨细地检查,检查连接服务器的咖啡壶是不是一个伪装的茶壶。在此情形下,我们需要返回一个恰当的状态码。

没有实现的部分:

目前无法通过编程检测咖啡机的状态。LED 虽能显示各种状态,但我们不得不解决在 Tessel 2 中检测状态的问题。而且我们不能恰当地实现 Safe 头字段或者 GET 状态。

尽管实现了一个 coffee:// 规则的基本版本,但我们并没有做全部的实现,而且做了必要的修改。否则,我们需要花费大量时间,还需要借助其它诸如 Amazon Alexa 的平台。

为了使 Tessel 便于访问,我们额外安装了 localtunnel module,创建了一个可外部访问 URL。

一切正常,你可以看到一个成功运行的 HTCPCP 协议:

Brew a coffee via HTTP using JavaScript and a hacked coffee machine

所有代码均存储在 GitHub 中

Implementation of the HTCPCP for DeLonghi Latissima using Tessel 2

IFTTC – If This Then Coffee

此刻已拥有一个正常运行的 HTTP 服务器,由此操纵咖啡机,接下来该连接 Alexa 了。最理想的方式是,创建 Smart Home skill,将其与家里的其它智能设备进行关联。然而,时间宝贵,因此,我们采用一种更快的方式:IFTTT (If-This-Then-That)。

幸运的是,Alexa 支持创建简单的 skills,借助于 IFTTT 便可进行 HTTP POST 请求。这样我们便可操控咖啡机!

对此我们提供了三种 skills。第一种是在待机状态下启动咖啡机;第二种,煮常规咖啡;第三种,煮浓咖啡。

最棒的是,做起来跟说出来一样容易,仅仅三分钟,即完成了对 Alexa 的集成,具体请看:

Brew a coffee using Amazon Alexa and JavaScript

下一步计划

我们依旧沉浸在取得初级版本的兴奋当中,但接下来还有很多工作要做,计划如下:

  • 制定出借助编程判定咖啡机状态的方案
  • 添加更多的继电器,以便实现咖啡机的所有功能
  • 利用传感器判断咖啡机中是否有杯子
  • 找出如何制作爱尔兰咖啡的方案,也就是如何在咖啡中添加威士忌
  • 为所有设备做一个套件,以便整齐地摆放在咖啡机顶部
  • 集成 Twillio,这样即使距离超远,没有 Alexa,也可以触发煮咖啡命令
  • 解决必须手动添置新的 Nespresso 咖啡包的问题

每过一天这个清单都会变长,一些酷炫的点子还会接着冒出来,我猜你或许也有不错的点子,如果有,请发推特至 @dkundel。期待听到你的消息!

结论

现今的设备大都是封闭的,以至于很难再打开一探究竟,能做这样一个小项目真的很棒。也不得不说借助 Tessel 2Johnny-Five 以及 JavaScript ,开展一些工作是一件很有意思的事情。本次是对硬件操纵的首次尝试,我在家里拥有一整套 Sparkfun 的 Johnny-Five Inventor’s Kit,此刻迫不及待,计划着将 kit 中的剩余部件应用于更多项目中。

个人认为,JavaScript 面向事件的方式可以很好的同硬件进行匹配。当然,你若计划创建一个能效、空间利用更高的项目,还须回到 C++。但与本例相似的硬件操纵项目,采用此种方式也是相当不错的。

总之,这个是一个很有趣的项目,最终我们将硬件操纵与 HTCPCP RFC 的读取以及实现集成起来。希望这能激发你在硬件操纵领域的兴趣,并为之积极探索。如果你在做了这方面工作,劳烦知晓一声。或许你们当中的有人愿意尝试操纵面包机。

toaster

打赏支持我翻译更多好文章,谢谢!

打赏译者

打赏支持我翻译更多好文章,谢谢!

任选一种支付方式

1 2 收藏 1 评论

关于作者:乔永琪

(新浪微博:@甜菜碱) 个人主页 · 我的文章 · 21

相关文章

可能感兴趣的话题



直接登录
最新评论
  • 看起来 好棒

    想想 要是能用 js控制 身边的所有智能家居

    那生活 一定是很舒服自在

    but

    我觉得 我应该去看点嵌入式

跳到底部
返回顶部