├── .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 | --------------------------------------------------------------------------------