本文通過釘釘機器人調用函數計算實作的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網關,便于進行通路、流量控制等。
總結
阿裡雲提供的一系列産品和服務,就像擰開水龍頭一樣,打開即用。通過物聯網平台,使用者可以快速地将原有傳統硬體産品迅速地進行更新改造,實作裝置迅速上雲。