├── .restful.py.swp
├── LICENSE
├── README.md
├── dachuang.ino
├── image
├── bannerImage.png
├── image1.gif
└── image2.gif
├── mp.py
├── pjhubs.txt
├── raspberrypi.py
├── restful.py
├── test.ino
└── test.py
/.restful.py.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/windstormeye/watchDog/c1c854c3a360ea41605afb0eb2fa9e3a0d91a834/.restful.py.swp
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 PJHubs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 使用微信+树莓派+Arduino+服务器构建智能家庭小助手
2 |
3 |
4 |
5 | ### 前言
6 | 这是我去年的大创项目《一种基于微信的主动式家庭智能监测系统设计与实现》,因为时间关系,一直都没有好好的梳理一遍应该如何去复现它,最近时间较为充裕,我会较为仔细的描述清楚该项目的核心难点(其实并没有难点🙄)。当初报这个项目是为了学习一些硬件的简单相关知识,再结合一下前年(17年的项目要在16年年末申报)的社会热点问题,当时大家都比较热衷于“智能家庭”的概念。当时的小米“家庭智能”套件火的是一塌糊涂,甚至还出了贺岁版礼包(如果我没记错的话),再结合当时对自己的技术路线的一个定位,需要弥补一些关于硬件的知识,遂有了这个项目。
7 |
8 | 因为时间间隔的比较久远,不保证复现过程中100%正确,如果你有跟着走,出现了问题请务必告知,我们一起完善!大部分都是`Python`和`Arduino`代码,建表SQL因为没法保证大家的物料跟我是一致的,而且大家也不一定会做的跟我完全一样,这块就保留了吧。当然,如果你喜欢论文严谨的格式,也可以到知网down下与[本项目相关的渣作](http://kns.cnki.net/KCMS/detail/detail.aspx?dbcode=CJFQ&dbname=CJFDLAST2018&filename=BJGY201802014&uid=WEEvREcwSlJHSldRa1FhdkJkVWI3b1ZpYW5qRm5Fekw3WWhmM1JGaW4xaz0=$AiWoHpiIFem7UrzGXWMU2pCrz7uWVXrw-rnFauXDWKZCB8nomqJ3KA!!&v=MTY3NDBKeWZNZDdHNEg5bk1yWTlFWUlSOGVYMUx1eFlTN0RoMVQzcVRyV00xRnJDVVJMS2ZZZVJ1RnlIbVY3dkI=)
9 |
10 | ### 物料准备
11 |
12 | 我将使用微信公众号、树莓派、Arduino和一台乞丐版配置的云服务器构建一个智能家庭小助手,用于协助我们对室内环境有一个较好的把控。如果你什么都没有可以参考以下清单先行购买物料(所有的必须物料下来,勉强三百多一些?):
13 |
14 | 1. **一块树莓派**。版本随意,如果你资金比较充裕,可以购买最新型号的树莓派,毕竟最新的3B型号wifi模块信号更好,整体的处理速度更快。¥150~300
15 | 2. **一套Arduino开发套件**。注意,是开发套件而不是Arduino这一块板子,我们需要开发套件中的其它元器件。¥150~300
16 | 3. **一台云服务器**。如果你要用自己的电脑也可以,在校园网、小区、公司内记得先做内网穿透,不过一台乞丐版的服务器也没多少钱,能省很多事。¥0~10
17 | 4. **微信公众号** 如果你之前没申请过的话,貌似开通审核得等两三天?¥0
18 |
19 | ### 信息配置
20 |
21 | 如果一切顺利,现在你的手上应该有一块树莓派、一套Arduino开发套件、一台云服务器、一个微信公众号。微信提供了一套公众号开发SDK,可以使用它,虽然官方提供开发文档已经非常成熟了,但还是觉得不够简洁。在此推荐大家使用[itchatmp](https://github.com/littlecodersh/itchatmp)。
22 |
23 | **微信公众号**:
24 | 进入[微信公众平台](https://mp.weixin.qq.com/)在左下角找到“开发”-“基本配置”,
25 |
26 |
27 |
28 | 在该页面中填写相关信息,
29 |
30 |
31 |
32 | 1. **服务器地址(URL)**:填写IP地址。但必须是公网IP或者已经做了内网穿透的IP地址,也可解析好域名后填入对应域名。
33 | 2. **令牌(Token)**:用于微信公众号和服务器进行双向交互时的验证。
34 | 3. **消息加解密密钥**:随意。
35 | 所有内容都填写完毕后,别着急提交。进行下一步,
36 |
37 | ### 服务器
38 | 登录服务器后,先检查是否安装了Python环境(可直接上Python3)。安装完成后,使用pip下载itchatmp,
39 |
40 | ```shell
41 | $ pip install itchatmp
42 | ```
43 | 下载完成后,新建一个.py文件(此处以mp.py为例),在文件中写下,
44 | ```python
45 | import itchatmp
46 |
47 | itchatmp.update_config(itchatmp.WechatConfig(
48 | # 填写上一步在微信公众号的配置内容
49 | token='yourToken',
50 | appId = 'yourAppId',
51 | appSecret = 'yourAppSecret'))
52 |
53 | @itchatmp.msg_register(itchatmp.content.TEXT)
54 | def text_reply(msg):
55 | return msg['Content']
56 |
57 | itchatmp.run()
58 | ```
59 | 此时执行,(需要root权限)
60 | ```shell
61 | $ python mp.py
62 | ```
63 | 看到下边这句话后就可以去微信公众号点击确认啦~
64 | ```shell
65 | itchatmp started! press Ctrl+C to exit.
66 | ```
67 |
68 | **效果:**
69 | 进入到对应的微信公众号中,你输入任何内容,它都会给你返回相同的内容。如果微信公众平台告诉你Token验证失效估计就是你的IP地址不对。
70 |
71 | ### 数据库
72 | 使用数据库是为了存储数据(完全可以使用txt文件来维护),在此为了简化手拼SQL易出错以及本项目并不需要进行多少性能优化的情况下,直接采用[ORM(对象关系映射技术)](https://www.cnblogs.com/wgbs25673578/p/5140482.html)。
73 | P.S.我将采用`sqlalchemy`这个框架进行,在[廖雪峰的博客](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014320114981139589ac5f02944601ae22834e9c521415000)上有较为细致的讲解,大家可以先自行研究一番到底是个什么东西。
74 |
75 | 这是定义好的硬件类,其实也就是硬件表,
76 | ```python
77 | # 硬件表
78 | class Hardware(Base):
79 | __tablename__ = 'hardware'
80 |
81 | id = Column(Integer, primary_key=True)
82 | name = Column(String(64), nullable=False)
83 | status = Column(Integer, nullable=False)
84 | num = Column(Integer, nullable=False)
85 | ```
86 | 新建一个py文件(以test.py为例),在其中写下,
87 | ```python
88 | from sqlalchemy import create_engine
89 | from sqlalchemy.ext.declarative import declarative_base
90 | from sqlalchemy import Column, String, Integer
91 | from sqlalchemy.orm import sessionmaker
92 |
93 | # '数据库类型+数据库驱动名称://用户名:口令@机器地址:端口号/数据库名'
94 | engine = create_engine('mysql+mysqldb://root:mimamima@localhost:3306/restful?charset=utf8')
95 | Base = declarative_base()
96 | Base.metadata.create_all(engine)
97 | Session = sessionmaker(bind = engine)
98 | session = Session()
99 | ```
100 | 到这一步为止,就完成了使用ORM进行MySQL数据库操作的铺垫。接下来,我们将进行数据库的增删改查方法的编写。
101 | 1. 增加一个元器件:
102 | ```python
103 | # 添加电子原件方法
104 | # 原件name及针脚num需要配置
105 | # 原件状态默认关闭
106 | def addNewUnit(hardwareName, status, num):
107 | Base.metadata.create_all(engine)
108 | Session = sessionmaker(bind=engine)
109 | session = Session()
110 | unit = Hardware(
111 | name = hardwareName,
112 | status = status,
113 | num = num)
114 | session.add(unit)
115 | session.commit()
116 | ```
117 | 2. 修改一个元器件的状态:
118 | ```python
119 | # 执行write操作
120 | def writeHardware(hardwarename, status, num):
121 | unit = readHardware(hardwarename)
122 | unit = session.query(Hardware).get(unit.id)
123 | if unit:
124 | unit.status = status
125 | if 'Unit' in hardwarename:
126 | unit.num = num;
127 | session.add(unit)
128 | session.commit()
129 | return '操作成功'
130 | return '操作失败,请联系管理员'
131 | ```
132 | 3. 读取一个元器件的状态:
133 | ```python
134 | # 执行read操作
135 | def readHardware(hardwarename):
136 | Base.metadata.create_all(engine)
137 | Session = sessionmaker(bind = engine)
138 | session = Session()
139 | unit = session.query(Hardware).filter_by(name=hardwarename).first()
140 | return unit
141 | ```
142 | 4. 稍微做了点封装的update方法:
143 | ```python
144 | # 电子原件执行read或write筛选方法
145 | def updateStatusWithHardware(tableName, operatorStatus, hardwarename, status):
146 | if tableName == 'hardware':
147 | if operatorStatus == 1:
148 | return writeHardware(hardwarename, status, 0)
149 | else:
150 | return readHardware(hardwarename)
151 | ```
152 | 现在我们完成了test.py的编写,主要完成了使用ORM技术编写了操作数据库的各种方法。接下来,我们要使用微信公众号对数据库进行修改。
153 |
154 | ### 上位机配置
155 | 在这个环节中,我们要做到用户发送“开灯”、“关灯”、“开风扇”、“温度”等消息给公众号后,能够在数据库中看到状态被修改并且反馈。
156 |
157 | 简单的来概括一下要做的工作:首先要让服务器接收到公众号发送而来的消息;其次要对发送者进行筛选,不能谁都可以操作这套系统;接着匹配消息,执行不同的方法;最后给公众号反馈回消息。
158 |
159 | 服务器接收公众号发送的消息我们已经在第一步中完成了,现在要对接收到的消息体进行解析,根据userID来筛选谁能对这套系统进行操作。我的做法非常简单,用一个"pjhubs.txt"文件保存了能够操作这套系统的用户ID。每次接收到消息时,都先从消息体中取出fromUserName字段数据与txt文件中的数据进行比对,如果在txt文件中才允许接着进行操作。
160 | ```python
161 | import itchatmp
162 | import test
163 |
164 | # 配置微信公众号信息
165 | itchatmp.update_config(itchatmp.WechatConfig(
166 | token='你的token',
167 | appId = '你的appId',
168 | appSecret = '你的appSecret'))
169 |
170 | # 接收用户消息
171 | @itchatmp.msg_register(itchatmp.content.TEXT)
172 | def text_reply(msg):
173 | toUserName = msg['FromUserName']
174 | content = msg['Content']
175 | isContain = 0
176 | # pjhubs.txt为有权限的用户列表
177 | f = open("pjhubs.txt","r")
178 | lines = f.readlines()
179 | for line in lines:
180 | if line[:-1] == toUserName:
181 | isContain = 1;
182 | if isContain == 0:
183 | return '该系统并未对您开放,请联系PJ进行配置'
184 | else:
185 | if content == '添加':
186 | # test.addNewUnit('tempUnit', 1, 2)
187 | return '操作成功!'
188 | elif content == '开灯':
189 | return test.updateStatusWithHardware('hardware', 1, 'redLED', 1)
190 | elif content == '关灯':
191 | return test.updateStatusWithHardware('hardware', 1, 'redLED', 0)
192 | elif content == '温度':
193 | unit = test.updateStatusWithHardware('hardware', 0, 'tempUnit', 1)
194 | returnString = '当前温度为:' + str(unit.num) + '°'
195 | return returnString
196 | elif content == '开风扇':
197 | return test.updateStatusWithHardware('hardware', 1, 'tempUnit', 1)
198 | elif content == '关风扇':
199 | return test.updateStatusWithHardware('hardware', 1, 'tempUnit', 0)
200 |
201 | # 新用户关注公众号时
202 | @itchatmp.msg_register(itchatmp.content.EVENT)
203 | def user_management(event):
204 | if(event['Event']=='subscribe'):
205 | return u'欢迎来到PJHubs,如果你想试用室内环境智能监测系统,请联系PJ'
206 | itchatmp.run()
207 | ```
208 | 执行,
209 | ```shell
210 | $ python mp.py
211 | ```
212 | 在微信公众号中发送“开灯”、“关灯”、“开风扇”、“温度”等指令都会对数据库进行操作。此时可以select对应表查看数据是否一致再进行下一步。
213 |
214 | ### API编写
215 | 这是知乎上一些[关于API的内容讲解](https://www.zhihu.com/question/38594466)。我们在此使用[Flask](http://docs.jinkan.org/docs/flask/)轻量级的web框架进行API编写。主要是给树莓派操作数据库使用的。
216 | 通过pip安装好flask后,我们可以先尝试写一个最简单的restful格式的API:
217 | ```python
218 | from flask import Flask
219 | from flask_restful import Resource, Api
220 | from flask import jsonify, request
221 | from flask import abort
222 | from flask import make_response
223 | import test
224 |
225 | app = Flask(__name__)
226 | api = Api(app)
227 |
228 | @app.route('/')
229 | def index():
230 | return 'Get out!🙂'
231 |
232 | if __name__ == '__main__':
233 | app.run(host='0.0.0.0',debug=True)
234 | ```
235 | 此时我们去浏览器中输入ip地址或域名,即可看到“Get out!🙂”这句话。现在我们要接着编写几个资源访问路径以便树莓派访问。
236 | ```python
237 | # 获取所有硬件信息(求快可以这么写)
238 | @app.route('/dachuang/api/v1/allHardware')
239 | def get_allHardware():
240 | LED = test.readHardware('redLED')
241 | UNIT= test.readHardware('tempUnit')
242 | LEDres = { 'id' : LED.id,
243 | 'name' : LED.name,
244 | 'status' : LED.status,
245 | 'num' : LED.num }
246 | UNITres = { 'id' : UNIT.id,
247 | 'name' : UNIT.name,
248 | 'status' : UNIT.status,
249 | 'num' : UNIT.num }
250 | return jsonify([LEDres, UNITres])
251 |
252 | # 更新固定元器件(求快用了GET,最好是POST)
253 | @app.route('/dachuang/api/v1/updateHardware', methods=['GET'])
254 | def get_updateHardware():
255 | hardwarename = request.args.get('hardwarename')
256 | status = request.args.get('status')
257 | num = request.args.get('num')
258 | if status == '3':
259 | unit = test.readHardware(hardwarename)
260 | test.writeHardware(hardwarename, unit.status, num)
261 | else:
262 | test.writeHardware(hardwarename, unit.status, num)
263 | return jsonify({'code' : '1'})
264 | ```
265 | 我们只需要起两个API服务即可满足要求。此时我们可以根据写好的API访问规则到浏览器中验证一番。
266 |
267 | ### 下位机配置——树莓派
268 | 树莓派是整套系统的灵魂所在,对上承载着数据库的更新,对下负担着Arduino的操作。当然,如果不考虑性能你可以直接用Arduino的wifi模组,直接对API发起请求。
269 |
270 | 树莓派首先要去在固定时间间隔内轮询特定API,根据API反馈回来的数据对固定串口发送特定字符,接收Arduino传递上来的数据,拼接API更新数据库。
271 |
272 | serial是对树莓派上的串口进行操作库,urllib2是网络请求库,json是解析和发送JSON格式库。
273 | ```python
274 | import serial
275 | import urllib2
276 | import json
277 |
278 | hostname = 'http://你的地址/dachuang/api/v1/allHardware'
279 | # /dev/ttyACM0 是树莓派上编号为0的USB口(可以在/dev目录下通过观察拔插对应的USB口找到对应的编号)
280 | ser = serial.Serial('/dev/ttyACM0', 9600, timeout = 4)
281 |
282 | while 1:
283 | r = urllib2.Request(hostname)
284 | r = urllib2.urlopen(r)
285 | res = r.read()
286 | result = json.loads(res)
287 | print result
288 | send = ''
289 | # 通过json库解析完后的数据就是字典
290 | if result[0]['status'] == 1:
291 | send += 'a'
292 | else:
293 | send += 'A'
294 | if result[1]['status'] == 1:
295 | send += 'b'
296 | else:
297 | send += 'B'
298 | # 从下位机Arduino上读取到的数据拼接URL发送回服务器,更新数据库
299 | ser.write(send)
300 | response = ser.readall()
301 | if '' != response:
302 | response = response[0:2]
303 | ret = urllib2.Request("http://你的地址/dachuang/api/v1/updateHardware?hardwarename=tempUnit&status=3" + '&num=' + response)
304 | ret = urllib2.urlopen(ret)
305 | ```
306 | 我在此重新定义了一套操作流程,
307 | a -> “开灯”
308 | A -> “关灯”
309 | b -> “开风扇”
310 | B -> “关风扇”
311 | 因为受到Arduino本身性能的影响,如果你还给它发一长串的字符串比如“open light”等,那估计单单就解析并匹配,分时操作已经过了。😂。因此我才想重新定义一套ASCII码关系映射,并且限制树莓派每次轮询的时间为4秒一次,可根据用户所搭建的下位机硬件系统复杂适度增减轮询时间。
312 |
313 | ### 下位机配置——Arduino
314 | Arduino要做的事情只有接收串口数据,解析串口数据,根据数据分别操作不同的硬件。Arduino用C写的,定义了一套规则,用起来非常顺手亲切。
315 | ```c
316 | #define yellowLED 13
317 | #define REDled 12
318 | #define Buzzer 8
319 | #define fanPin 2
320 |
321 | void setup() {
322 | Serial.begin(9600); // 9600 bps
323 | pinMode(yellowLED, OUTPUT);
324 | pinMode(Buzzer,OUTPUT);
325 | pinMode(REDled,OUTPUT);
326 | pinMode(fanPin,OUTPUT);
327 | }
328 | void loop() {
329 | //读取A0口的电压值,温度传感器所在串口
330 | int n = analogRead(A0);
331 | //使用浮点数存储温度数据,温度数据由电>压值换算得到
332 | float vol = n * (5.0 / 1023.0*100);
333 | if ( Serial.available() ) {
334 | // 向串口写入温度
335 | Serial.println(vol);
336 | // 读取树莓派写入串口的数据
337 | int res = Serial.read();
338 | // 根据ASCII码执行不同硬件操作函数
339 | if (res == 97) {
340 | digitalWrite(yellowLED, HIGH);
341 | }
342 | if (res == 65){
343 | digitalWrite(yellowLED, LOW);
344 | }
345 | if (res == 98) {
346 | digitalWrite(fanPin, HIGH);
347 | }
348 | if (res == 66) {
349 | digitalWrite(fanPin, LOW);
350 | }
351 | }
352 | // 超过30°后开启高温预警,蜂鸣器奏响和风扇打开
353 | if (vol > 30) {
354 | buzzerBegin();
355 | }
356 | }
357 |
358 | // 蜂鸣器响铃
359 | void buzzerBegin() {
360 | digitalWrite(fanPin, HIGH);
361 | digitalWrite(REDled, HIGH);
362 | //频率从200HZ 增加到800HZ,模拟警报声
363 | for(int i=200;i<=800;i++) {
364 | tone(Buzzer,i);
365 | delay(5);
366 | }
367 | delay(100);
368 | for(int i=800;i>=200;i--) {
369 | tone(Buzzer,i);
370 | delay(5);
371 | }
372 |
373 | digitalWrite(REDled, LOW);
374 | digitalWrite(fanPin, LOW);
375 | }
376 | ```
377 |
378 | ### 上下位机联调
379 | 至此我们完成了全部的基础工作,在联调的过程中,当初我也发生了非常多的问题,这无法避免,稍不注意电路连错了以后就全盘皆输了,在此我只能祝大家好运,Arduino连接各个元器件的方式并没有展开,因为我相信大家的电路设计一定比我强!👍
380 |
381 | 在联调的过程中,你需要做的是,
382 | 1. 运行restful.py,把整套API服务跑起来;
383 | 2. 运行mp.py,让公众号和服务器打通;
384 | 3. Arduino通过USB与树莓派相连后,树莓派再通电;
385 | 4. 在公众号上发送指令,观察Arduino上元器件状态变化。
386 |
387 | **结果:**
388 |
389 |
390 |
391 |
392 |
393 |
394 |
--------------------------------------------------------------------------------
/dachuang.ino:
--------------------------------------------------------------------------------
1 | #define yellowLED 13
2 | #define REDled 12
3 | #define Buzzer 8
4 | #define fanPin 2
5 |
6 | void setup() {
7 | Serial.begin(9600); // 9600 bps
8 | pinMode(yellowLED, OUTPUT);
9 | pinMode(Buzzer,OUTPUT);
10 | pinMode(REDled,OUTPUT);
11 | pinMode(fanPin,OUTPUT);
12 | }
13 | void loop() {
14 | int n = analogRead(A0); //读取A0口的电压值
15 | float vol = n * (5.0 / 1023.0*100); //使用浮点数存储温度数据,温度数据由电压值换算得到
16 | if ( Serial.available() ) {
17 | Serial.println(vol);
18 | int res = Serial.read();
19 | if (res == 97) {
20 | digitalWrite(yellowLED, HIGH);
21 | }
22 | if (res == 65){
23 | digitalWrite(yellowLED, LOW);
24 | }
25 | if (res == 98) {
26 | digitalWrite(fanPin, HIGH);
27 | }
28 | if (res == 66){
29 | digitalWrite(fanPin, LOW);
30 | }
31 | }
32 |
33 | if (vol > 30) { // 温度
34 | buzzerBegin();
35 | }
36 | }
37 |
38 | void buzzerBegin() {
39 | digitalWrite(fanPin, HIGH);
40 | digitalWrite(REDled, HIGH);
41 | for(int i=200;i<=800;i++) //用循环的方式将频率从200HZ 增加到800HZ
42 | {
43 | tone(Buzzer,i);
44 | delay(5);
45 | }
46 | delay(100);
47 | for(int i=800;i>=200;i--)
48 | {
49 | tone(Buzzer,i);
50 | delay(5);
51 | }
52 |
53 | noTone(Buzzer);
54 | digitalWrite(REDled, LOW);
55 | digitalWrite(fanPin, LOW);
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/image/bannerImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/windstormeye/watchDog/c1c854c3a360ea41605afb0eb2fa9e3a0d91a834/image/bannerImage.png
--------------------------------------------------------------------------------
/image/image1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/windstormeye/watchDog/c1c854c3a360ea41605afb0eb2fa9e3a0d91a834/image/image1.gif
--------------------------------------------------------------------------------
/image/image2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/windstormeye/watchDog/c1c854c3a360ea41605afb0eb2fa9e3a0d91a834/image/image2.gif
--------------------------------------------------------------------------------
/mp.py:
--------------------------------------------------------------------------------
1 | import itchatmp
2 |
3 | import test
4 |
5 | # 配置微信公众号信息
6 | itchatmp.update_config(itchatmp.WechatConfig(
7 | token='pjhubs',
8 | appId = 'wx02fb9d00f976e7f0',
9 | appSecret = 'wx02fb9d00f976e7f0'))
10 |
11 | # 接收用户消息
12 | @itchatmp.msg_register(itchatmp.content.TEXT)
13 | def text_reply(msg):
14 | toUserName = msg['FromUserName']
15 | content = msg['Content']
16 | isContain = 0;
17 | # pjhubs.txt为有权限的用户列表
18 | f = open("pjhubs.txt","r")
19 | lines = f.readlines()
20 | for line in lines:
21 | if line[:-1] == toUserName:
22 | isContain = 1;
23 | if isContain == 0:
24 | return '该系统并未对您开放,请联系PJ进行配置'
25 | else:
26 | if content == '添加':
27 | # test.addNewUnit('tempUnit', 1, 2)
28 | return '操作成功!'
29 | elif content == '开灯':
30 | return test.updateStatusWithHardware('hardware', 1, 'redLED', 1)
31 | elif content == '关灯':
32 | return test.updateStatusWithHardware('hardware', 1, 'redLED', 0)
33 | elif content == '温度':
34 | unit = test.updateStatusWithHardware('hardware', 0, 'tempUnit', 1)
35 | returnString = '当前温度为:' + str(unit.num) + '°'
36 | return returnString
37 | elif content == '开风扇':
38 | return test.updateStatusWithHardware('hardware', 1, 'tempUnit', 1)
39 | elif content == '关风扇':
40 | return test.updateStatusWithHardware('hardware', 1, 'tempUnit', 0)
41 |
42 | # 新用户关注公众号时
43 | @itchatmp.msg_register(itchatmp.content.EVENT)
44 | def user_management(event):
45 | if(event['Event']=='subscribe'):
46 | return u'欢迎来到PJHubs,如果你想试用室内环境智能监测系统,请联系PJ'
47 |
48 | itchatmp.run()
49 |
--------------------------------------------------------------------------------
/pjhubs.txt:
--------------------------------------------------------------------------------
1 | o37u9wc2A4VYf2lAoYK81gDbkoRE
2 | o37u9wQmMxjk6Lk0GbSI3_O5LWLM
3 |
--------------------------------------------------------------------------------
/raspberrypi.py:
--------------------------------------------------------------------------------
1 | import serial
2 | import RPi.GPIO
3 | import urllib2
4 | import json
5 |
6 | hostname = 'http://139.199.168.197:5000/dachuang/api/v1/allHardware'
7 |
8 | ser = serial.Serial('/dev/ttyACM0', 9600, timeout = 4)
9 |
10 | while 1:
11 | r = urllib2.Request(hostname)
12 | r = urllib2.urlopen(r)
13 | res = r.read()
14 | result = json.loads(res)
15 | print result
16 | send = ''
17 | if result[0]['status'] == 1:
18 | send += 'a'
19 | else:
20 | send += 'A'
21 | if result[1]['status'] == 1:
22 | send += 'b'
23 | else:
24 | send += 'B'
25 | ser.write(send)
26 | response = ser.readall()
27 | if '' != response:
28 | response = response[0:2]
29 | ret = urllib2.Request("http://139.199.168.197:5000/dachuang/api/v1/updateHardware?hardwarename=tempUnit&status=3" + '&num=' + response)
30 | ret = urllib2.urlopen(ret)
31 |
--------------------------------------------------------------------------------
/restful.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | from flask_restful import Resource, Api
3 | from flask import jsonify, request
4 | from flask import abort
5 | from flask import make_response
6 |
7 | import test
8 |
9 | app = Flask(__name__)
10 | api = Api(app)
11 |
12 | @app.route('/')
13 | def index():
14 | return 'Get out!🙂'
15 |
16 | @app.route('/dachuang/api/v1/tasks', methods=['GET'])
17 | def get_tasks():
18 | return jsonify({'tasks': tasks})
19 |
20 | @app.route('/dachuang/api/v1/hardware')
21 | def get_hardware():
22 | hardwarename = request.args.get('hardwarename')
23 | unit = test.readHardware(hardwarename)
24 | response = {'id' : unit.id,
25 | 'name' : unit.name,
26 | 'status' : unit.status,
27 | 'num' : unit.num}
28 | return jsonify(response)
29 |
30 | @app.route('/dachuang/api/v1/allHardware')
31 | def get_allHardware():
32 | LED = test.readHardware('redLED')
33 | UNIT= test.readHardware('tempUnit')
34 | LEDres = { 'id' : LED.id,
35 | 'name' : LED.name,
36 | 'status' : LED.status,
37 | 'num' : LED.num }
38 | UNITres = { 'id' : UNIT.id,
39 | 'name' : UNIT.name,
40 | 'status' : UNIT.status,
41 | 'num' : UNIT.num }
42 | return jsonify([LEDres, UNITres])
43 |
44 | @app.route('/dachuang/api/v1/updateHardware', methods=['GET'])
45 | def get_updateHardware():
46 | hardwarename = request.args.get('hardwarename')
47 | status = request.args.get('status')
48 | num = request.args.get('num')
49 | if status == '3':
50 | unit = test.readHardware(hardwarename)
51 | test.writeHardware(hardwarename, unit.status, num)
52 | else:
53 | test.writeHardware(hardwarename, unit.status, num)
54 | return jsonify({'code' : '1'})
55 |
56 | @app.route('/dachuang/api/v1/tasks/', methods=['GET'])
57 | def get_task(task_id):
58 | isID = 0
59 | for taskUnit in tasks:
60 | if taskUnit['id'] == task_id:
61 | isID = 1
62 | return jsonify({'task':taskUnit})
63 | if isID == 0:
64 | abort(404);
65 | else:
66 | return unit;
67 |
68 | @app.errorhandler(404)
69 | def no_found(error):
70 | return make_response(jsonify({'error':'Not found'}), 404)
71 |
72 | if __name__ == '__main__':
73 | app.run(host='0.0.0.0',debug=True)
74 |
--------------------------------------------------------------------------------
/test.ino:
--------------------------------------------------------------------------------
1 | #define yellowLED 13
2 |
3 |
4 | void setup() {
5 | Serial.begin(9600); // 9600 bps
6 | pinMode(yellowLED, OUTPUT);
7 | }
8 | void loop() {
9 | if ( Serial.available()) {
10 | if('y' == Serial.read()) {
11 | operatorLED(13, 1);
12 | } else {
13 | operatorLED(13, 0);
14 | }
15 | }
16 | }
17 |
18 |
19 | void operatorLED(int hardwarenum, int hardwarestatus) {
20 | if(hardwarenum == 13) {
21 | if(hardwarestatus == 1) {
22 | digitalWrite(yellowLED, HIGH);
23 | Serial.println("yellowLED HIGH");
24 | } else {
25 | digitalWrite(yellowLED, LOW);
26 | Serial.println("yellowLED LOW");
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | from sqlalchemy import create_engine
2 | from sqlalchemy.ext.declarative import declarative_base
3 | from sqlalchemy import Column, String, Integer
4 | from sqlalchemy.orm import sessionmaker
5 |
6 | from faker import Factory
7 |
8 | engine = create_engine('mysql+mysqldb://root:woaiwoziji123@localhost:3306/restful?charset=utf8')
9 | Base = declarative_base()
10 |
11 | Base.metadata.create_all(engine)
12 | Session = sessionmaker(bind = engine)
13 | session = Session()
14 |
15 | # 硬件表
16 | class Hardware(Base):
17 | __tablename__ = 'hardware'
18 |
19 | id = Column(Integer, primary_key=True)
20 | name = Column(String(64), nullable=False)
21 | status = Column(Integer, nullable=False)
22 | num = Column(Integer, nullable=False)
23 |
24 |
25 | # 添加电子原件方法
26 | # 原件name及针脚num需要配置
27 | # 原件状态默认关闭
28 | def addNewUnit(hardwareName, status, num):
29 | Base.metadata.create_all(engine)
30 | Session = sessionmaker(bind=engine)
31 | session = Session()
32 | unit = Hardware(
33 | name = hardwareName,
34 | status = status,
35 | num = num)
36 | session.add(unit)
37 | session.commit()
38 |
39 | # 电子原件执行read或write筛选方法
40 | def updateStatusWithHardware(tableName, operatorStatus, hardwarename, status):
41 | if tableName == 'hardware':
42 | if operatorStatus == 1:
43 | return writeHardware(hardwarename, status, 0)
44 | else:
45 | return readHardware(hardwarename)
46 |
47 | # 执行write操作
48 | def writeHardware(hardwarename, status, num):
49 | unit = readHardware(hardwarename)
50 | unit = session.query(Hardware).get(unit.id)
51 | if unit:
52 | unit.status = status
53 | if 'Unit' in hardwarename:
54 | unit.num = num;
55 | session.add(unit)
56 | session.commit()
57 | return '操作成功'
58 | return '操作失败,请联系管理员'
59 |
60 | # 执行read操作
61 | def readHardware(hardwarename):
62 | Base.metadata.create_all(engine)
63 | Session = sessionmaker(bind = engine)
64 | session = Session()
65 | unit = session.query(Hardware).filter_by(name=hardwarename).first()
66 | return unit
67 |
68 |
--------------------------------------------------------------------------------