処理内容
・kintone で 命令を選択し、制御命令レコードを追加すると、MQTTS にコマンドを送信(Publish)します
・MQTTS からコマンドを受信 (Subscribe) した Raspberry Pi が、制御コマンドを受信し、命令の処理(計測・撮影)を行います。
・Raspberry Pi から API 経由で kintone のレコードにデータを PUT して更新します。
・kintone の詳細画面で計測・撮影結果が表示されます。(結果が戻るか、指定回数に到達するまでは ReLoad を繰り返す。)
kintone 側の処理結果は https://qiita.com/yukataoka/items/1c608e2fa2d994068458 を参照
Raspberry Pi 事前準備
以下のパッケージ、ライブラリィをインストールしておきます。
$ sudo apt-get install libopencv-dev python-opencv python-pip
$ sudo pip install poster AWSIoTPythonSDK
Raspberry Pi 処理順
下のコードで紹介する kintoneCommand.py をバックグランドに常駐させるだけ
Raspberry Pi コード
MQTTSコマンドを受信 (Subscribe) 常駐プログラム
kintone から MQTTS 経由で届く制御指示を待つって、制御指示が届いたら該当する処理を実行します。
MQTTSやその処理の実装についての詳細は、以下を参照ください。
MQTTとは
https://kfep.jp/solution/iot-mqtt/mqtt
AWS IoTにPythonでMQTTイベントの送信、受信するまで
https://symfoware.blog.fc2.com/blog-entry-2224.html
AWSIoTPythonSDK
https://s3.amazonaws.com/aws-iot-device-sdk-python-docs/sphinx/html/index.html
# !/usr/bin/python
# -*- coding: utf-8 -*-
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
from sht21 import SHT21
from TakePicture import TakePicture
from PostFileForKintone import PostFileForKintone
from PostDataForKintone import PostDataForKintone
import sys
import logging
import time
import getopt
import json
import subprocess
# Custom MQTT message callback(制御指示を受信した時の処理)
def customCallback(client, userdata, message):
print("Received a new message: " + message.payload)
print("from topic: " + message.topic)
print("--------------")
jsonData = json.loads(message.payload)
# 温湿度計測
if(jsonData["command"] == u"温湿度取得" or jsonData["command"] == u"全て"):
sht21 = SHT21(1)
temp = str(round((sht21.read_temperature() * 1), 1))
hum = str(round((sht21.read_humidity() * 1), 1))
record = {"temp":{"value":temp},"hum":{"value":hum}}
posted = PostDataForKintone(jsonData["domain"], jsonData["appid"], jsonData["token"])
posted.putToKintone(jsonData["key"], record)
# USBカメラ撮影
if(jsonData["command"] == u"即時撮影" or jsonData["command"] == u"全て"):
picture = TakePicture('/home/pi/human/')
path = picture.TakeForDateTimeName()
postImage = PostFileForKintone(jsonData["domain"], jsonData["token"], path)
data = json.loads(postImage.upload());
fkey = str(data['fileKey']);
record = {"picture":{"value":[{"fileKey":fkey}]}}
posted = PostDataForKintone(jsonData["domain"], jsonData["appid"], jsonData["token"])
posted.putToKintone(jsonData["key"], record)
command = "rm "+path
subprocess.call(command, shell=True)
# Read in command-line parameters
useWebsocket = False
host = "HOST.iot.ap-northeast-1.amazonaws.com"
rootCAPath = "/home/pi/human/key/root-CA.crt"
certificatePath = "/home/pi/key/XXXXXXXXXXX-certificate.pem.crt"
privateKeyPath = "/home/pi/key/XXXXXXXXXXX-private.pem.key"
# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core") # Python 2
# logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)
# Init AWSIoTMQTTClient
myAWSIoTMQTTClient = None
myAWSIoTMQTTClient = AWSIoTMQTTClient("RaspiZeroHumanPicture")
myAWSIoTMQTTClient.configureEndpoint(host, 8883)
myAWSIoTMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)
# AWSIoTMQTTClient connection configuration
myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1) # Infinite offline Publish queueing
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10) # 10 sec
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5) # 5 sec
# Connect and subscribe to AWS IoT
myAWSIoTMQTTClient.connect()
myAWSIoTMQTTClient.subscribe("command/picture", 1, customCallback)
time.sleep(2)
# Publish to the same topic in a loop forever
while True:
time.sleep(1)
I2C温湿度センサSH21用
sh21.py
https://github.com/jaques/sht21_python/blob/master/sht21.py より入手。
USBカメラで撮影した画像をファイルに保管
Raspberry Pi に取り付けたUSBカメラで静止画を撮影し、ファイルに保管するプログラムです。
# !/usr/bin/python
# -*- coding: utf-8 -*-
import time
import cv2
class TakePicture:
def __init__(self, path = "./"):
self.path = path
def TakeForDateTimeName(self):
capture = cv2.VideoCapture(0)
ret, frame = capture.read()
path = self.path + time.strftime('%Y%m%d_%H%M%S.jpg')
cv2.imwrite(path, frame)
return path
def Take(self, fileName='image.jpg'):
capture = cv2.VideoCapture(0)
ret, frame = capture.read()
path = self.path + fileName
cv2.imwrite(path,frame)
return path
if __name__ == "__main__":
takePicture = TakePicture()
print takePicture.TakeForDateTimeName()
print takePicture.Take('picture.jpg')
kintone に画像ファイルをアップロードする
kintone に画像ファイルを API でアップロードするプログラムです。
# !/usr/bin/python
# -*- coding: utf-8 -*-
from poster.encode import multipart_encode
from poster.streaminghttp import register_openers
import urllib2
import json
class PostFileForKintone:
def __init__(self, subdomain, token, file):
self.subdomain = subdomain
self.token = token
self.file = file
def upload(self):
register_openers()
with open(self.file, "rb") as fp:
datagen, headers = multipart_encode({"file": fp})
request = urllib2.Request("https://"+self.subdomain+".cybozu.com/k/v1/file.json", datagen, headers)
request.add_header('X-Cybozu-API-Token', self.token)
response = urllib2.urlopen(request)
# print "---------- RESPONSE HEAD ----------"
# print response.info()
# print "---------- RESPONSE BODY ----------"
return response.read()
kintone のデータを更新する
kintone に制御結果を API 経由で更新するプログラムです。
# !/usr/bin/python
# -*- coding: utf-8 -*-
import httplib
import json
import time
class PostDataForKintone:
def __init__(self, subdomain, appId, token):
self.subdomain = subdomain
self.appId = appId
self.token = token
def registToKintone(self, record):
request = {"app":self.appId,"record":record}
requestJson = json.dumps(request)
headers = {"X-Cybozu-API-Token": self.token, "Content-Type" : "application/json"}
connect = httplib.HTTPSConnection(self.subdomain + ".cybozu.com:443")
connect.request("POST", "/k/v1/record.json", requestJson, headers)
response = connect.getresponse()
return response
def putToKintone(self, id, record):
request = {"app":self.appId,"id":id,"record":record}
requestJson = json.dumps(request)
headers = {"X-Cybozu-API-Token": self.token, "Content-Type" : "application/json"}
connect = httplib.HTTPSConnection(self.subdomain + ".cybozu.com:443")
connect.request("PUT", "/k/v1/record.json", requestJson, headers)
response = connect.getresponse()
return response