以前做过一个小项目,利用树莓派搭建了一个采集终端,并用花生壳将其映射为服务器,向微信公众号发送信息;客户可通过关注该公众号,获取树莓派处的实时图像,湿度及温度信息
系统架构如下:
树莓派端接线如下:
软件结构如下:
软件部分可分为以下几个部分进行实现:
下面是主程序index.py,连接微信服务器,并等待公众号客户查询信息,在实现的过程中查询了网上信息,并查看了微信文档,发现网上很多东西并不是那么靠谱。主程序将采集的图像保存在一个文件夹中,并没有申请微信服务器,这是实现起来比较简单,而且省钱,当然这只是一个实验性的项目,正式产品不能这么设计。
其中温湿度采集用dth11,图像采集用普通的USB摄像头
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import web
import time
import hashlib
from lxml import etree
import sys
import urllib2
import poster.encode
from poster.streaminghttp import register_openers
import json
import urllib
import humidity
#解决UnicodeEncodeError: 'ascii' codec can't encode characters in position问题
reload(sys)
sys.setdefaultencoding("utf-8")
#
urls = (
'/','WeixinInterface'
)
#下面三个参数非常重要
#_appId,_appSecret从微信公众平台申请
_appId=XXXXXXXXXX
_appSecret=XXXXXXXXXX
#_token在微信公众平台设置,二者要保存一致,本应用设置为myflower
_token="myflower"
def _check_hash(data):
#sha1加密算法
signature=data.signature
timestamp=data.timestamp
nonce=data.nonce
#自己的token
token=_token #这里改写你在微信公众平台里输入的token
#字典序排序
list=[token,timestamp,nonce]
list.sort()
sha1=hashlib.sha1()
map(sha1.update,list)
hashcode=sha1.hexdigest()
#如果是来自微信的请求,则回复True
if hashcode == signature:
return True
return False
def _get_access_token(appId, appSecret):
postUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s" % (appId, appSecret)
urlResp = urllib.urlopen(postUrl)
urlResp = json.loads(urlResp.read())
#print urlResp
return urlResp['access_token']
def _upload_img(accessToken, filePath, mediaType):
openFile = open(filePath, "rb")
param = {'media': openFile}
postData, postHeaders = poster.encode.multipart_encode(param)
postUrl = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s" % (accessToken, mediaType)
request = urllib2.Request(postUrl, postData, postHeaders)
urlResp = urllib2.urlopen(request)
urlResp = json.loads(urlResp.read())
#print urlResp
return urlResp['media_id']
class WeixinInterface:
#try:
def __init__(self):
self.app_root = os.path.dirname(__file__)
self.templates_root = os.path.join(self.app_root,'templates/')
self.render = web.template.render(self.templates_root)
#self.render = web.template.render('templates/')
print self.templates_root
register_openers()
def GET(self):
#获取输入参数
data = web.input()
if _check_hash(data):
print "ok"
return data.echostr
def POST(self):
str_xml = web.data() #获得post来的数据
print str_xml
xml = etree.fromstring(str_xml)#进行XML解析
fromUser=xml.find("FromUserName").text
toUser=xml.find("ToUserName").text
content=u"欢迎来到花情监测与出行助手"
msgType=xml.find("MsgType").text
if msgType=='text':
return self.render.reply_text(fromUser,toUser,int(time.time()),content)
elif msgType=='event':
eventKey=xml.find("EventKey").text
if eventKey=="photo":
access_token=_get_access_token(_appId,_appSecret)
#等待1秒钟,抓拍一张大小为1024*768的照片,并保存为source/wechat.jpg
#os.system("sudo raspistill -o source/wechat.jpg -w 1024 -h 768 -t 10000")#树莓派摄像头模块
os.system("fswebcam -r 640x480 source/wechat.jpg")#USB摄像头拍摄命令
MediaId=_upload_img(access_token,"source/wechat.jpg","image")
return self.render.reply_img(fromUser,toUser,int(time.time()),MediaId)
elif eventKey=="temperature":
hum, temp=humidity.get_humidity()
print hum
content=u"当前的温度:%s *C" % (temp)
return self.render.reply_text(fromUser,toUser,int(time.time()),content)
elif eventKey=="humiture":
hum, temp=humidity.get_humidity()
print hum
content=u"当前的湿度:%s %%" % (hum)
return self.render.reply_text(fromUser,toUser,int(time.time()),content)
else :
print "不处理"
return "successful"
else :
print "不处理"
return "successful"
application = web.application(urls, globals())
if __name__ == "__main__":
application.run()
下面是humidity.py其采用模块dth11采集温湿度信息
import RPi.GPIO as GPIO
import time
DHTPIN = 17
GPIO.setmode(GPIO.BCM)
MAX_UNCHANGE_COUNT = 100
STATE_INIT_PULL_DOWN = 1
STATE_INIT_PULL_UP = 2
STATE_DATA_FIRST_PULL_DOWN = 3
STATE_DATA_PULL_UP = 4
STATE_DATA_PULL_DOWN = 5
def read_dht11_dat():
GPIO.setup(DHTPIN, GPIO.OUT)
GPIO.output(DHTPIN, GPIO.HIGH)
time.sleep(0.05)
GPIO.output(DHTPIN, GPIO.LOW)
time.sleep(0.02)
GPIO.setup(DHTPIN, GPIO.IN, GPIO.PUD_UP)
unchanged_count = 0
last = -1
data = []
while True:
current = GPIO.input(DHTPIN)
data.append(current)
if last != current:
unchanged_count = 0
last = current
else:
unchanged_count += 1
if unchanged_count > MAX_UNCHANGE_COUNT:
break
state = STATE_INIT_PULL_DOWN
lengths = []
current_length = 0
for current in data:
current_length += 1
if state == STATE_INIT_PULL_DOWN:
if current == GPIO.LOW:
state = STATE_INIT_PULL_UP
else:
continue
if state == STATE_INIT_PULL_UP:
if current == GPIO.HIGH:
state = STATE_DATA_FIRST_PULL_DOWN
else:
continue
if state == STATE_DATA_FIRST_PULL_DOWN:
if current == GPIO.LOW:
state = STATE_DATA_PULL_UP
else:
continue
if state == STATE_DATA_PULL_UP:
if current == GPIO.HIGH:
current_length = 0
state = STATE_DATA_PULL_DOWN
else:
continue
if state == STATE_DATA_PULL_DOWN:
if current == GPIO.LOW:
lengths.append(current_length)
state = STATE_DATA_PULL_UP
else:
continue
if len(lengths) != 40:
#print "Data not good, skip"
return False
shortest_pull_up = min(lengths)
longest_pull_up = max(lengths)
halfway = (longest_pull_up + shortest_pull_up) / 2
bits = []
the_bytes = []
byte = 0
for length in lengths:
bit = 0
if length > halfway:
bit = 1
bits.append(bit)
#print "bits: %s, length: %d" % (bits, len(bits))
for i in range(0, len(bits)):
byte = byte << 1
if (bits[i]):
byte = byte | 1
else:
byte = byte | 0
if ((i + 1) % 8 == 0):
the_bytes.append(byte)
byte = 0
#print the_bytes
checksum = (the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3]) & 0xFF
if the_bytes[4] != checksum:
#print "Data not good, skip"
return False
return the_bytes[0], the_bytes[2]
def get_humidity():
#print "Raspberry Pi wiringPi DHT11 Temperature test program\n"
while True:
result = read_dht11_dat()
if result:
return result
time.sleep(1)
模板有两个,分别是回应文字和图像的,如下所示:
reply_img.xml
$def with (toUser,fromUser,createTime,MediaId)
$createTime
reply_txt.xml
$def with (toUser,fromUser,createTime,content)
$createTime