本文通过钉钉机器人调用函数计算实现的 serverless web 服务, 打通物联网平台, 和树莓派实时通讯. 实现了将原有传统的磁吸门禁, 改造成可以由钉钉来控制开门的简单应用.
场景
由于本部门拥有独立封闭的空间, 在大门口配置了磁吸玻璃门, 因此规定在工作期间出入需要随手关门, 以保证工作环境的私密性和安全性. 但前台并没有小妹, 这样对于来访客人就不是特别方便, 往往需要电话通知接待者到大门口来接, 要么接待者向管理员要来无线遥控器来开门(tuo ku zi fang pi). 这对于我这种重度懒惰病患者来说, 简直是种折磨. So...
思路
是否可以由树莓派模拟按动无线门禁遥控器的按钮来开门?
经过对无线遥控器的暴力拆解, 发现遥控器的开门按钮是由一个有 4 个引脚的贴片按钮构成. 经过试验, 只需把这 2 个接口短路, 就相当于按下按钮的动作. 因此可以在上面接驳一个继电器, 由树莓派控制继电器来实现遥控器的按钮接通. 图参考 3.1 节
目标: 将有经常需要接待的人 (重度懒惰病患者) 加入到公司某个内部钉钉群中, 有来访者需要开门时, 直接向群机器人发送 "开门", 门禁就会打开.
实现方式: 在钉钉群加入一个自定义的 outgoing 机器人, 向机器人发送指令后, 远程 HTTP POST 调用函数计算服务, 函数计算向物联网平台的 Topic 上送消息, 而订阅此 Topic 的树莓派会在收到消息后打开继电器开关, 让无线遥控器的按钮短路, 发送无线信号让门禁开启.
成本估算
物联网平台:
使用基础版产品免费
函数计算:
调用次数: 每月前 100 万次函数调用免费.
执行时间: 每月前 400000(GB * 秒) 费用免费.
唯一可能产生费用的就是极少量的网络使用费
准备
物料准备
树莓派
一路继电器
门禁无线遥控器
母对母杜邦线 3 根
公对公杜邦线 2 根
阿里云环境准备
物联网平台
函数计算
日志服务(可选)
操作步骤
1 云端开发
1.1 物联网平台
登录阿里云控制台, 进入物联网平台控制面板
1.1.1 新建产品
进入设备管理, 创建产品, 选择基础版或高级版都可以, 本实例使用基础版就可以满足基本要求.
系统会自动创建 3 个 Topic, 我们需要使用 /ProductName/${deviceName}/get, 作为设备订阅消息的 Topic.
1.1.2 设备管理
在产品中新增设备, 并获得设备的 3 元组, 在 2.3 节的设备代码的编写时需要使用此 3 元组. 设备三元组是设备的唯一标示
1.2 函数计算
函数计算可以通过 flask Web 工程来实现 serverless, 我们可以直接使用 url 去访问和调用函数. 好处在于, 直接在函数计算里编写代码即可, 而省去了购买 ECS 及搭建相应的运行环境, 使用起来非常方便.
通过使用 http 触发器, 函数计算可提供
http://${account-id}.${region}.fc.aliyuncs.com/2016-08-15/proxy/$(serviceName)/$(functionName)/
来访问调用函数.
1.2.1 新建服务
新建服务 iot-demo, 如果需要记录和回溯函数执行的日志, 则需要开通日志服务, 并配置好日志仓库.
1.2.2 新建函数
新建函数, 使用模版 flask-Web, 函数名和触发器填写 officegate, 运行环境选择 python2.7
1.2.3 函数代码
- # -*- coding: utf-8 -*-
- from flask import Flask
- from flask import request
- from flask import make_response
- import logging
- from aliyunsdkcore import client
- from aliyunsdkcore.request import RpcRequest
- import base64
- try:
- from urllib.parse import urlparse
- except:
- from urlparse import urlparse
- App = Flask(__name__)
- base_path = ''return_string='''{
- "msgtype": "text",
- "text": {
- "content": "%s"
- }
- }'''
- # 参数定义
- options = {
- 'productKey': '', # 设备标识三元组
- 'deviceName': '', # 设备标识三元组
- 'accessKeyId': '',
- 'accessKeySecret': '',
- 'token': '12345678', # Dingding Outgoing token
- }
- clt = client.AcsClient(options['accessKeyId'], options['accessKeySecret'], 'cn-shanghai')
- # 推送消息到 IoT Hub Topic
- def pushMsg(msg):
- request = RpcRequest('Iot', '2018-01-20', 'Pub')
- request.set_accept_format('json')
- request.add_query_param('ProductKey',options['productKey'])
- request.add_query_param('TopicFullName','/' + options['productKey'] + '/' + options['deviceName'] +'/get') #消息发送到的 Topic 全名
- request.add_query_param('MessageContent',base64.b64encode(msg)) #Base64
- request.add_query_param('Qos',0)
- result = clt.do_action_with_exception(request)
- logging.info('result :' + result)
- # 健康检查
- @App.route('/', methods=['GET', 'POST'])
- def home():
- resp = make_response('I am ok!', 200)
- return resp
- # 对应 DingDing outgoing URL
- @App.route('/dingbot',methods=['POST'])
- def bot_receive() :
- token=request.headers.get('token')
- data=request.get_json()
- logging.debug("token="+token+"\nmessage:"+str(data))
- msgtype=data.get("msgtype")
- if token != options['token'] and msgtype != "text" :
- return make_response("error",403)
- content=str(data["text"]["content"])
- senderId=data["senderId"]
- senderNick=data["senderNick"]
- logging.info('%s(%s) talk: %s' % (senderNick,senderId,content))
- pushMsg(content.strip())
- ret = return_string % "门开了"
- resp = make_response(ret,200)
- resp.headers['Content-Type']="application/json; charset=utf-8"
- return resp
- # 函数计算主入口
- def handler(environ, start_response):
- parsed_tuple = urlparse(environ['fc.request_uri'])
- li = parsed_tuple.path.split('/')
- global base_path
- if not base_path:
- base_path = "/".join(li[0:5])
- return App(environ, start_response)
代码创建完成后, 在代码编辑框下方的调试 Http 触发器, 可以获得调用的 url.
注意, 我们代码里路由入口是 / dingbot 因此, 实际的访问网址应该是:
https://$(account-id).cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/iot-demo/officegate/dingbot
2 钉钉机器人的配置
如图所示, 添加自定义机器人时, 需要勾选中 是否开启 Outgoing 机制, 在 POST 地址里, 把上一节中的 url 粘贴进来, 而 Token 必须和函数计算代码里参数配置的一致, 本例中是
12345678
.
添加成功后, 尝试着给钉钉机器人发指令 "开门", 如果机器人能回应 "门开了", 那说明函数计算已经可以正常提供服务.
3 设备端开发
3.1 硬件安装
首先把门禁遥控器的外壳拆开(大卸八块)
用电烙铁把开门按钮的 4 个引脚从电路板上取下来
把 2 根公杜邦线的引脚焊接在电路板的 2 个焊点上, 并把另一头分别接在继电器 A,B 端上
再拿 3 根母杜邦线, VCC,GND,IN 分别接到树莓派 GPIO 接口的 4,6,16 引脚上.
3.2 环境准备
我们在树莓派上使用 python2.7 作为开发语言. 安装阿里云物联网的 SDK:
pip install aliyun-python-sdk-iot-client
3.3 代码开发
gate-demo.py 内容如下:
- # -*- coding: utf-8 -*-
- import JSON
- from time import sleep
- import RPi.GPIO as GPIO
- import aliyunsdkiotclient.AliyunIotMqttClient as iot
- # 参数配置
- options = {
- 'productKey':'', # 设备三元组
- 'deviceName':'', # 设备三元组
- 'deviceSecret':'', # 设备三元组
- 'port':1883,
- 'host':'iot-as-mqtt.cn-shanghai.aliyuncs.com',
- 'gate_pin':23 #in 接 GPIO 23 针脚
- }
- # 订阅的 IoT Hub Topic
- SUBSCRIBE_TOPIC = '/'+options['productKey']+'/'+options['deviceName']+'/user/get'
- GPIO.setmode(GPIO.BCM)
- GPIO.setwarnings(False)
- # 设置 GPIO 接口是输出, 默认低电平
- GPIO.setup(options['gate_pin'],GPIO.OUT,initial=GPIO.LOW)
- # 开门
- def openGate() :
- GPIO.output(options['gate_pin'],GPIO.HIGH) # 打开高电平
- sleep(0.8)
- GPIO.output(options['gate_pin'],GPIO.LOW) # 恢复低电平
- # 收到发布的消息回调
- def on_message(client, userdata, msg):
- print(msg.payload)
- if msg.payload=='开门':
- openGate()
- def on_connect(client, userdata, flags_dict, rc):
- print("Connected with result code" + str(rc))
- # 订阅消息
- client.subscribe(topic=SUBSCRIBE_TOPIC)
- def on_disconnect(client, userdata, flags_dict, rc):
- print("Disconnected.")
- if __name__ == '__main__':
- # IoT 初始化
- client = iot.getAliyunIotMqttClient(options['productKey'], options['deviceName'], options['deviceSecret'], secure_mode=3)
- client.on_connect = on_connect
- client.on_message = on_message
- client.connect(host=options['productKey'] + '.' + options['host'], port=options['port'], keepalive=60)
- client.loop_forever()
4 测试运行
4.1 设备端运行
在 py-demo 文件夹下运行
python gate-demo.py
4.2 钉钉发送消息
在钉钉群上 @机器人, 发送 "开门" 指令, 如果函数计算运行正常, 则应能收到机器人 "门开了" 的应答.
4.3 云端查看上送消息
在函数计算里的日志查询界面中, 可以看到程序运行时记录的日志:
在物联网平台的设备的 Topic 列表中, 可以看到收到了函数计算发布的消息:
4.4 测试结果
树莓派的 python 程序打印出日志, 继电器的信号灯闪动, 同时遥控器发出无线指令, 大门磁吸门禁收到信号后开启.
后续
本例只是一个基于物联网的简单用例, 并未考虑过多的安全问题. 下一步可以对安全性进一步完善, 例如, 授权和控制谁能够进行开门等. 此外, 提高系统的安全性, 还引入 API 网关, 便于进行访问, 流量控制等.
总结
阿里云提供的一系列产品和服务, 就像拧开水龙头一样, 打开即用. 通过物联网平台, 使用者可以快速地将原有传统硬件产品迅速地进行升级改造, 实现设备迅速上云.
来源: https://yq.aliyun.com/articles/672021