内容简介:这是我去年的大创项目《一种基于微信的主动式家庭智能监测系统设计与实现》,因为时间关系,一直都没有好好的梳理一遍应该如何去复现它,最近时间较为充裕,我会较为仔细的描述清楚该项目的核心难点(其实并没有难点因为时间间隔的比较久远,不保证复现过程中100%正确,如果你有跟着走,出现了问题请务必告知,我们一起完善!大部分都是
使用微信+树莓派+Arduino+服务器构建智能家庭小助手
前言
这是我去年的大创项目《一种基于微信的主动式家庭智能监测系统设计与实现》,因为时间关系,一直都没有好好的梳理一遍应该如何去复现它,最近时间较为充裕,我会较为仔细的描述清楚该项目的核心难点(其实并没有难点
因为时间间隔的比较久远,不保证复现过程中100%正确,如果你有跟着走,出现了问题请务必告知,我们一起完善!大部分都是 Python
和 Arduino
代码,建表 SQL 因为没法保证大家的物料跟我是一致的,而且大家也不一定会做的跟我完全一样,这块就保留了吧。当然,如果你喜欢论文严谨的格式,也可以到知网down下与 本项目相关的渣作
物料准备
我将使用微信公众号、树莓派、Arduino和一台乞丐版配置的云服务器构建一个智能家庭小助手,用于协助我们对室内环境有一个较好的把控。如果你什么都没有可以参考以下清单先行购买物料(所有的必须物料下来,勉强三百多一些?):
- 一块树莓派 。版本随意,如果你资金比较充裕,可以购买最新型号的树莓派,毕竟最新的3B型号wifi模块信号更好,整体的处理速度更快。¥150~300
- 一套Arduino开发套件 。注意,是开发套件而不是Arduino这一块板子,我们需要开发套件中的其它元器件。¥150~300
- 一台云服务器 。如果你要用自己的电脑也可以,在校园网、小区、公司内记得先做内网穿透,不过一台乞丐版的服务器也没多少钱,能省很多事。¥0~10
- 微信公众号 如果你之前没申请过的话,貌似开通审核得等两三天?¥0
信息配置
如果一切顺利,现在你的手上应该有一块树莓派、一套Arduino开发套件、一台云服务器、一个微信公众号。微信提供了一套公众号开发SDK,可以使用它,虽然官方提供开发文档已经非常成熟了,但还是觉得不够简洁。在此推荐大家使用 itchatmp 。
微信公众号: 进入 微信公众平台 在左下角找到“开发”-“基本配置”,
在该页面中填写相关信息,
- 服务器地址(URL) :填写IP地址。但必须是公网IP或者已经做了内网穿透的IP地址,也可解析好域名后填入对应域名。
- 令牌(Token) :用于微信公众号和服务器进行双向交互时的验证。
- 消息加解密密钥 :随意。 所有内容都填写完毕后,别着急提交。进行下一步,
服务器
登录服务器后,先检查是否安装了 Python 环境(可直接上Python3)。安装完成后,使用pip下载itchatmp,
$ pip install itchatmp
下载完成后,新建一个.py文件(此处以mp.py为例),在文件中写下,
import itchatmp itchatmp.update_config(itchatmp.WechatConfig( # 填写上一步在微信公众号的配置内容 token='yourToken', appId = 'yourAppId', appSecret = 'yourAppSecret')) @itchatmp.msg_register(itchatmp.content.TEXT) def text_reply(msg): return msg['Content'] itchatmp.run()
此时执行,(需要root权限)
$ python mp.py
看到下边这句话后就可以去微信公众号点击确认啦~
itchatmp started! press Ctrl+C to exit.
效果:进入到对应的微信公众号中,你输入任何内容,它都会给你返回相同的内容。如果微信公众平台告诉你Token验证失效估计就是你的IP地址不对。
数据库
使用数据库是为了存储数据(完全可以使用txt文件来维护),在此为了简化手拼SQL易出错以及本项目并不需要进行多少性能优化的情况下,直接采用 ORM(对象关系映射技术) 。 P.S.我将采用 sqlalchemy
这个框架进行,在 廖雪峰的博客 上有较为细致的讲解,大家可以先自行研究一番到底是个什么东西。
这是定义好的硬件类,其实也就是硬件表,
# 硬件表 class Hardware(Base): __tablename__ = 'hardware' id = Column(Integer, primary_key=True) name = Column(String(64), nullable=False) status = Column(Integer, nullable=False) num = Column(Integer, nullable=False)
新建一个py文件(以test.py为例),在其中写下,
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, String, Integer from sqlalchemy.orm import sessionmaker # '数据库类型+数据库驱动名称://用户名:口令@机器地址:端口号/数据库名' engine = create_engine('mysql+mysqldb://root:mimamima@localhost:3306/restful?charset=utf8') Base = declarative_base() Base.metadata.create_all(engine) Session = sessionmaker(bind = engine) session = Session()
到这一步为止,就完成了使用ORM进行 MySQL 数据库操作的铺垫。接下来,我们将进行数据库的增删改查方法的编写。
- 增加一个元器件:
# 添加电子原件方法 # 原件name及针脚num需要配置 # 原件状态默认关闭 def addNewUnit(hardwareName, status, num): Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() unit = Hardware( name = hardwareName, status = status, num = num) session.add(unit) session.commit()
- 修改一个元器件的状态:
# 执行write操作 def writeHardware(hardwarename, status, num): unit = readHardware(hardwarename) unit = session.query(Hardware).get(unit.id) if unit: unit.status = status if 'Unit' in hardwarename: unit.num = num; session.add(unit) session.commit() return '操作成功' return '操作失败,请联系管理员'
- 读取一个元器件的状态:
# 执行read操作 def readHardware(hardwarename): Base.metadata.create_all(engine) Session = sessionmaker(bind = engine) session = Session() unit = session.query(Hardware).filter_by(name=hardwarename).first() return unit
- 稍微做了点封装的update方法:
# 电子原件执行read或write筛选方法 def updateStatusWithHardware(tableName, operatorStatus, hardwarename, status): if tableName == 'hardware': if operatorStatus == 1: return writeHardware(hardwarename, status, 0) else: return readHardware(hardwarename)
现在我们完成了test.py的编写,主要完成了使用ORM技术编写了操作数据库的各种方法。接下来,我们要使用微信公众号对数据库进行修改。
上位机配置
在这个环节中,我们要做到用户发送“开灯”、“关灯”、“开风扇”、“温度”等消息给公众号后,能够在数据库中看到状态被修改并且反馈。
简单的来概括一下要做的工作:首先要让服务器接收到公众号发送而来的消息;其次要对发送者进行筛选,不能谁都可以操作这套系统;接着匹配消息,执行不同的方法;最后给公众号反馈回消息。
服务器接收公众号发送的消息我们已经在第一步中完成了,现在要对接收到的消息体进行解析,根据userID来筛选谁能对这套系统进行操作。我的做法非常简单,用一个"pjhubs.txt"文件保存了能够操作这套系统的用户ID。每次接收到消息时,都先从消息体中取出fromUserName字段数据与txt文件中的数据进行比对,如果在txt文件中才允许接着进行操作。
import itchatmp import test # 配置微信公众号信息 itchatmp.update_config(itchatmp.WechatConfig( token='你的token', appId = '你的appId', appSecret = '你的appSecret')) # 接收用户消息 @itchatmp.msg_register(itchatmp.content.TEXT) def text_reply(msg): toUserName = msg['FromUserName'] content = msg['Content'] isContain = 0 # pjhubs.txt为有权限的用户列表 f = open("pjhubs.txt","r") lines = f.readlines() for line in lines: if line[:-1] == toUserName: isContain = 1; if isContain == 0: return '该系统并未对您开放,请联系PJ进行配置' else: if content == '添加': # test.addNewUnit('tempUnit', 1, 2) return '操作成功!' elif content == '开灯': return test.updateStatusWithHardware('hardware', 1, 'redLED', 1) elif content == '关灯': return test.updateStatusWithHardware('hardware', 1, 'redLED', 0) elif content == '温度': unit = test.updateStatusWithHardware('hardware', 0, 'tempUnit', 1) returnString = '当前温度为:' + str(unit.num) + '°' return returnString elif content == '开风扇': return test.updateStatusWithHardware('hardware', 1, 'tempUnit', 1) elif content == '关风扇': return test.updateStatusWithHardware('hardware', 1, 'tempUnit', 0) # 新用户关注公众号时 @itchatmp.msg_register(itchatmp.content.EVENT) def user_management(event): if(event['Event']=='subscribe'): return u'欢迎来到PJHubs,如果你想试用室内环境智能监测系统,请联系PJ' itchatmp.run()
执行,
$ python mp.py
在微信公众号中发送“开灯”、“关灯”、“开风扇”、“温度”等指令都会对数据库进行操作。此时可以select对应表查看数据是否一致再进行下一步。
API编写
这是知乎上一些 关于API的内容讲解 。我们在此使用 Flask 轻量级的web框架进行API编写。主要是给树莓派操作数据库使用的。 通过pip安装好flask后,我们可以先尝试写一个最简单的restful格式的API:
from flask import Flask from flask_restful import Resource, Api from flask import jsonify, request from flask import abort from flask import make_response import test app = Flask(__name__) api = Api(app) @app.route('/') def index(): return 'Get out! ' if __name__ == '__main__': app.run(host='0.0.0.0',debug=True)
此时我们去浏览器中输入ip地址或域名,即可看到“Get out!
# 获取所有硬件信息(求快可以这么写) @app.route('/dachuang/api/v1/allHardware') def get_allHardware(): LED = test.readHardware('redLED') UNIT= test.readHardware('tempUnit') LEDres = { 'id' : LED.id, 'name' : LED.name, 'status' : LED.status, 'num' : LED.num } UNITres = { 'id' : UNIT.id, 'name' : UNIT.name, 'status' : UNIT.status, 'num' : UNIT.num } return jsonify([LEDres, UNITres]) # 更新固定元器件(求快用了GET,最好是POST) @app.route('/dachuang/api/v1/updateHardware', methods=['GET']) def get_updateHardware(): hardwarename = request.args.get('hardwarename') status = request.args.get('status') num = request.args.get('num') if status == '3': unit = test.readHardware(hardwarename) test.writeHardware(hardwarename, unit.status, num) else: test.writeHardware(hardwarename, unit.status, num) return jsonify({'code' : '1'})
我们只需要起两个API服务即可满足要求。此时我们可以根据写好的API访问规则到浏览器中验证一番。
下位机配置——树莓派
树莓派是整套系统的灵魂所在,对上承载着数据库的更新,对下负担着Arduino的操作。当然,如果不考虑性能你可以直接用Arduino的wifi模组,直接对API发起请求。
树莓派首先要去在固定时间间隔内轮询特定API,根据API反馈回来的数据对固定串口发送特定字符,接收Arduino传递上来的数据,拼接API更新数据库。
serial是对树莓派上的串口进行操作库,urllib2是网络请求库,json是解析和发送JSON格式库。
import serial import urllib2 import json hostname = 'http://你的地址/dachuang/api/v1/allHardware' # /dev/ttyACM0 是树莓派上编号为0的USB口(可以在/dev目录下通过观察拔插对应的USB口找到对应的编号) ser = serial.Serial('/dev/ttyACM0', 9600, timeout = 4) while 1: r = urllib2.Request(hostname) r = urllib2.urlopen(r) res = r.read() result = json.loads(res) print result send = '' # 通过json库解析完后的数据就是字典 if result[0]['status'] == 1: send += 'a' else: send += 'A' if result[1]['status'] == 1: send += 'b' else: send += 'B' # 从下位机Arduino上读取到的数据拼接URL发送回服务器,更新数据库 ser.write(send) response = ser.readall() if '' != response: response = response[0:2] ret = urllib2.Request("http://你的地址/dachuang/api/v1/updateHardware?hardwarename=tempUnit&status=3" + '#=' + response) ret = urllib2.urlopen(ret)
我在此重新定义了一套操作流程, a -> “开灯” A -> “关灯” b -> “开风扇” B -> “关风扇” 因为受到Arduino本身性能的影响,如果你还给它发一长串的字符串比如“open light”等,那估计单单就解析并匹配,分时操作已经过了。
下位机配置——Arduino
Arduino要做的事情只有接收串口数据,解析串口数据,根据数据分别操作不同的硬件。Arduino用C写的,定义了一套规则,用起来非常顺手亲切。
#define yellowLED 13 #define REDled 12 #define Buzzer 8 #define fanPin 2 void setup() { Serial.begin(9600); // 9600 bps pinMode(yellowLED, OUTPUT); pinMode(Buzzer,OUTPUT); pinMode(REDled,OUTPUT); pinMode(fanPin,OUTPUT); } void loop() { //读取A0口的电压值,温度传感器所在串口 int n = analogRead(A0); //使用浮点数存储温度数据,温度数据由电>压值换算得到 float vol = n * (5.0 / 1023.0*100); if ( Serial.available() ) { // 向串口写入温度 Serial.println(vol); // 读取树莓派写入串口的数据 int res = Serial.read(); // 根据ASCII码执行不同硬件操作函数 if (res == 97) { digitalWrite(yellowLED, HIGH); } if (res == 65){ digitalWrite(yellowLED, LOW); } if (res == 98) { digitalWrite(fanPin, HIGH); } if (res == 66) { digitalWrite(fanPin, LOW); } } // 超过30°后开启高温预警,蜂鸣器奏响和风扇打开 if (vol > 30) { buzzerBegin(); } } // 蜂鸣器响铃 void buzzerBegin() { digitalWrite(fanPin, HIGH); digitalWrite(REDled, HIGH); //频率从200HZ 增加到800HZ,模拟警报声 for(int i=200;i<=800;i++) { tone(Buzzer,i); delay(5); } delay(100); for(int i=800;i>=200;i--) { tone(Buzzer,i); delay(5); } digitalWrite(REDled, LOW); digitalWrite(fanPin, LOW); }
上下位机联调
至此我们完成了全部的基础工作,在联调的过程中,当初我也发生了非常多的问题,这无法避免,稍不注意电路连错了以后就全盘皆输了,在此我只能祝大家好运,Arduino连接各个元器件的方式并没有展开,因为我相信大家的电路设计一定比我强!
在联调的过程中,你需要做的是,
- 运行restful.py,把整套API服务跑起来;
- 运行mp.py,让公众号和服务器打通;
- Arduino通过USB与树莓派相连后,树莓派再通电;
- 在公众号上发送指令,观察Arduino上元器件状态变化。
结果:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 如何计算STM32定时器、独立看门狗和窗口看门狗
- 纯技术上来说,《看门狗》里的各种骇客技术有可能实现吗?
- 面试时遇到 “看门狗” 脖子上挂着 “时间轮”,我就问你怕不怕?
- [树莓派]树莓派的入门教程
- 「玩转树莓派」树莓派 3B+ 安装 OpenCv
- 「玩转树莓派」树莓派 3B+ 查询本机IP
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。