我与IOT的故事

今年年初,有个卖别墅的朋友找我分享他的点子:他想弄个手机软件来控制房子里面的空调,这样可以在回家之前先把空调打开。他觉得这个点子很赞,而且经济效益应该也不错,找到我是因为只差一个程序员了。我给他找了个app智能空调的链接,告诉他这样的产品已经有了,如果想装在别墅里,可以买个海尔的。他听完之后若有所思的走了,没有下文。
出于对技术的好奇,草草的应付完这个朋友之后,我开始设想这种智能空调应该怎么做。
首先我感觉应该需要一个板子,一个可以联网,可以编程的板子。刚好郑烨他们现在就在做这种板子,而且销量堪忧,我就买了个他们的产品: Ruff板子。这个板子使用的是JS,准确点说是Jquery风格的JS,对于大部分的开发者尤其是前端来说十分友好。而且Ruff板子买的时候除了板子还送10个小组件,包括一些常用的探测器,LCD显示屏等,做些小的Demo非常合适。就空调遥控器而言,小组件里面刚好有个红外发射器和红外接收器。如果用红外接收器来录制好遥控器的红外信号,然后使用发射器模拟出来,就可以替代遥控器了。
这个问题的核心不是在硬件上,我得想想怎么通过app来远程控制板子上的组件。总结起来走过三条路:1 Http Server轮询,2 板子搭Server 和 3 Mqtt。
1 Http Server轮询方案
这个方案的大体思路是这样,我弄一个Http Server来存储一些状态值,在app端可以通过RESTful的api来修改这些状态。这些状态值对应到板子上一些设备的状态,板子通过轮询的方式查询服务器上相关的状态值,根据查询到的状态值或者状态值的变更来修改板子上硬件组件的状态和行为。也就是说总的架构图大概是这样的:


我与IOT的故事
文章图片
1 Http Server轮询方案 【我与IOT的故事】为了快速做出来成果,server是个简单的放在Heroku上的Rails脚手架的应用。期间咨询过思聪,他给我推荐了一个叫 jsonblob.com 的网站可以自行创建一些数据,围绕该数据它会提供全套的增删查改的RESTful 的api。不幸的是,Ruff板子彼时的版本只能使用http协议而非https协议,所以还是拿着我的heroku服务来做的。实施完这个方案大概花了半天时间,主要的时间都花在熟悉Ruff板子的联网方式上了。简单来说,ruff板子自己会发射一个特殊的wifi信号,每当需要更新Ruff板子上的硬件代码时,都需要连上这个特殊的wifi,然后执行一些命令。Ruff板子也能自己实现互联网接入,但是同样需要通过其自身的wifi进入其控制页面,进而设置联网wifi参数,而且由于板子能力有限,只能接入2.4GHz的普通wifi(还有一种是5GHz的)。
这个做完之后,颇有成就感。但是有几个显而易见的问题没有解决:
1 板子需要轮询状态,也就是大约每5秒发一次请求来获取最新状态。
2 状态发起切换到生效有大约一个轮询周期,也就是5s的延时。
3 某些组件不是接受器,而是传感器,比如温湿度传感器,光照传感器等。这部分传感器采集到的数据如果需要回传给app显示的话,基于这种架构,app上也得轮询这一部分状态才可以实现。
4 跟第二条一样,有轮询就会有延时。
2 板子搭Server
在思考了第一种方案的利弊之后,我决定做些改进,也趁机去Ruff的客服群咨询了一波。他们说是ruff板子可以直接启动一个http server,然后让app端通过访问这个server上的api来控制板子上的硬件的行为。这个方案解决了一些问题,但是并没有解决所有的。比如传感器的数据回传到app上的问题还是没有很好的解决。而且还引入了一个新的问题是:为了实现真正意义上的远程控制,得确保板子上的这个server在任何一个联网设备上都可以访问的到,期间咨询过大师,有一些做内网穿透的工具比如花生壳确实可以做到这一点。但是鉴于方案本身还是无法解决我的所有问题,都没有来得及实际实施过。如果画个图的话,大概是这样的:


我与IOT的故事
文章图片
2 板子搭Server 3 Mqtt 方案
再后来,听说了这个叫Mqtt的即时通讯协议,在物联网相关的介绍中被广泛提到。Mqtt是专为物联网设备所设计的,网络开销很小。Mqtt使用的是发布/订阅消息模式,事件发布方将事件和数据包装到一个Topic中,然后发给broker(某些地方翻译为消息代理)。然后broker会找到所有订阅了该Topic的终端,将数据发送给这些终端。mqtt的broker已经有了像mosquito这种成熟产品了,mosquito目前就已经提供了一个无密码的测试的broker供开发者使用,地址是这个:mqtt://test.mosquitto.org。在docker hub上搜索mqtt也能找到很多广为流传的mqtt broker镜像,比如如果你想在本地启动一个broker的话,不妨试试下面这个命令:

docker run -d -P --name mqtt ncarlier/mqtt
最后我找了一个在线的提供mqtt消息代理功能的产品,注册自己的broker也很简单,地址是这个 www.cloudmqtt.com。这个产品里面还自带了一个调试页面,可以很方便的做些订阅发布的调试。而且不像test.mosquitto.org这种四门大开的测试代理,cloudmqtt上创建的broker默认是需要认证用户名和密码的。
选好了关键性的broker之后,我们可以来详细的看看整个方案是如何实施的。
首先Ruff板子上,目前已经提供了支持mqtt协议的依赖包,使用时只需要添加该依赖,也就是执行:
rap install mqtt --save
然后在使用处引入mqtt模块即可:
require('mqtt')
然后只需要按照ruff的文档链接broker,以及订阅对应的topic即可:
var mqtt= require('mqtt');
var client= mqtt.connect(
{ host: 'm10.cloudmqtt.com', port: 10441, username: 'username', password: 'password' });
client.on('connect', function () {client.subscribe('honglai/buzzer'); });
client.on('message', function (topic, message) {
// message is Buffer
console.log('topic:', topic);
console.log('message:', message.toString());
if (topic === 'honglai/buzzer') {
if (message.toString() === 'on') {$("#buzzer").turnOn(); }
else {$("#buzzer").turnOff(); }}
});
这里贴的代码是在接收到订阅消息之后启动蜂鸣器而不是发射红外信号。发射红外信号比开启蜂鸣器略微复杂一些。
板子这边订阅好Mqtt的Topic后,剩下的就简单了,只需要在app或者别的地方发布对应的Topic即可。在安卓系统上,有一些已有的app可以用来测试Mqtt协议,其功能已经非常完善了,比如有个叫MQTT dashboard的app,只需要在app上设置好broker参数,它就能以控件的操控来发布自定义的Topic。


我与IOT的故事
文章图片
MQTT dashboard 此外在命令行中,只需要使用npm全局安装mqtt依赖之后就可以直接在命令行发布和订阅Mqtt Topic了。当然npm这个安装方式完全可以打包放到docker镜像中,可能只需要使用类似这样的Dockerfile 来build一个镜像就好了:
FROM mhart/alpine-node
MAINTAINER "Honglaisenburg"
RUN npm install -g mqtt
我当时就这么打了个镜像挂在dockerhub,名字叫做 zhanhonglai/mqttjs。有了这个镜像之后,其实可以做很多事情,比如把发布的时机放在CI的build中,也就是我们常常听说的CI灯,CI报警器等。比如你只需要在build脚本的最后加上这些代码就能在每次失败的build之后拉响我的蜂鸣器了:


我与IOT的故事
文章图片
CI 脚本 最后还有一点点小细节,是关于遥控器的红外信号的。红外信号本身是一串数字形式的编码,比如创维电视机的开机键对应的是73个不同的数字16781816, 4459, ..... 16777796,在发射器中只用原封不动的发射就好。然后跟所有的红外遥控器一样,如果没有对准接受机,发射出去的红外信号会丢失掉。因此最后成型的样子是,ruff板子上拧的那个红外发射灯,一直对着它时刻准备控制着的电视机(只找到电视机遥控器,我们家空调呢?),场面十分僵硬。我相信如果我制造电视机或者空调,一定可以绕过遥控器直接把订阅了某个Topic的控制器装在空调或者电视机的内部,用电线连接来稳定,有效的控制这些机器。
前不久,我老婆给推荐了一个app让我玩,叫开心夹娃娃。在app里面你可以选择一些线下的夹娃娃机,每一个的夹子上都安装有摄像头,你可以看到娃娃的摆放状态以及夹子的位置。在app里面通过上下左右来遥控这个不知道放在哪里的夹娃娃机上的夹子,夹中并且拧起来成功放到出口处的娃娃,会在支付一笔快递费后邮寄给玩家。我老婆给我推荐的app一向乏善可陈,但这一个确猛然让我想起了物联网,它会不会也跟我想的一样是用Mqtt来实现的呢?我又陷入了一阵沉思..

    推荐阅读