树莓派的简单应用--利用公众号获取实时图像和信息

以前做过一个小项目,利用树莓派搭建了一个采集终端,并用花生壳将其映射为服务器,向微信公众号发送信息;客户可通过关注该公众号,获取树莓派处的实时图像,湿度及温度信息

系统架构如下:

树莓派端接线如下:

软件结构如下:

软件部分可分为以下几个部分进行实现:

  1. DTH11驱动模块的设计。DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。单线制串行接口,使系统集成变得简易快捷。一次通讯时间4ms左右,数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。DTH11的驱动采用python开发
  2. 微信公众号平台的配置。在微信公共平台进行基本的配置,如url,token,IP白名单等。
  3. 微信公众号服务器的设计。主要有实现连接微信公共平台,接收文本信息,被动回复文本消息、新建临时素材、被动回复图片消息、创建菜单界面等。
  4. 花生壳系统的配置。配置内网穿透的域名、内网IP等

 下面是主程序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)
<xml>
<ToUserName><![CDATA[$toUser]]></ToUserName>
<FromUserName><![CDATA[$fromUser]]></FromUserName>
<CreateTime>$createTime</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[$MediaId]]></MediaId>
</Image>
</xml>

reply_txt.xml

$def with (toUser,fromUser,createTime,content)
<xml>
<ToUserName><![CDATA[$toUser]]></ToUserName>
<FromUserName><![CDATA[$fromUser]]></FromUserName>
<CreateTime>$createTime</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[$content]]></Content>
</xml>
发布了27 篇原创文章 · 获赞 3 · 访问量 5557

猜你喜欢

转载自blog.csdn.net/wxg_wuchujie88/article/details/105077804