使用微信+树莓派+Arduino+服务器构建你的看门狗
使用微信+樹莓派+Arduino+服務(wù)器構(gòu)建智能家庭小助手
前言
這是我去年的大創(chuàng)項(xiàng)目《一種基于微信的主動(dòng)式家庭智能監(jiān)測(cè)系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)》,因?yàn)闀r(shí)間關(guān)系,一直都沒有好好的梳理一遍應(yīng)該如何去復(fù)現(xiàn)它,最近時(shí)間較為充裕,我會(huì)較為仔細(xì)的描述清楚該項(xiàng)目的核心難點(diǎn)(其實(shí)并沒有難點(diǎn)?)。當(dāng)初報(bào)這個(gè)項(xiàng)目是為了學(xué)習(xí)一些硬件的簡(jiǎn)單相關(guān)知識(shí),再結(jié)合一下前年(17年的項(xiàng)目要在16年年末申報(bào))的社會(huì)熱點(diǎn)問題,當(dāng)時(shí)大家都比較熱衷于“智能家庭”的概念。當(dāng)時(shí)的小米“家庭智能”套件火的是一塌糊涂,甚至還出了賀歲版禮包(如果我沒記錯(cuò)的話),再結(jié)合當(dāng)時(shí)對(duì)自己的技術(shù)路線的一個(gè)定位,需要彌補(bǔ)一些關(guān)于硬件的知識(shí),遂有了這個(gè)項(xiàng)目。
因?yàn)闀r(shí)間間隔的比較久遠(yuǎn),不保證復(fù)現(xiàn)過程中100%正確,如果你有跟著走,出現(xiàn)了問題請(qǐng)務(wù)必告知,我們一起完善!大部分都是Python和Arduino代碼,建表SQL因?yàn)闆]法保證大家的物料跟我是一致的,而且大家也不一定會(huì)做的跟我完全一樣,這塊就保留了吧。當(dāng)然,如果你喜歡論文嚴(yán)謹(jǐn)?shù)母袷?#xff0c;也可以到知網(wǎng)down下與本項(xiàng)目相關(guān)的渣作
物料準(zhǔn)備
我將使用微信公眾號(hào)、樹莓派、Arduino和一臺(tái)乞丐版配置的云服務(wù)器構(gòu)建一個(gè)智能家庭小助手,用于協(xié)助我們對(duì)室內(nèi)環(huán)境有一個(gè)較好的把控。如果你什么都沒有可以參考以下清單先行購買物料(所有的必須物料下來,勉強(qiáng)三百多一些?):
信息配置
如果一切順利,現(xiàn)在你的手上應(yīng)該有一塊樹莓派、一套Arduino開發(fā)套件、一臺(tái)云服務(wù)器、一個(gè)微信公眾號(hào)。微信提供了一套公眾號(hào)開發(fā)SDK,可以使用它,雖然官方提供開發(fā)文檔已經(jīng)非常成熟了,但還是覺得不夠簡(jiǎn)潔。在此推薦大家使用itchatmp。
微信公眾號(hào): 進(jìn)入微信公眾平臺(tái)在左下角找到“開發(fā)”-“基本配置”,
在該頁面中填寫相關(guān)信息,
服務(wù)器
登錄服務(wù)器后,先檢查是否安裝了Python環(huán)境(可直接上Python3)。安裝完成后,使用pip下載itchatmp,
pip install itchatmp 復(fù)制代碼下載完成后,新建一個(gè).py文件(此處以mp.py為例),在文件中寫下,
import itchatmpitchatmp.update_config(itchatmp.WechatConfig(# 填寫上一步在微信公眾號(hào)的配置內(nèi)容token='yourToken',appId = 'yourAppId',appSecret = 'yourAppSecret'))def text_reply(msg):return msg['Content']itchatmp.run() 復(fù)制代碼此時(shí)執(zhí)行,(需要root權(quán)限)
python mp.py 復(fù)制代碼看到下邊這句話后就可以去微信公眾號(hào)點(diǎn)擊確認(rèn)啦~
itchatmp started! press Ctrl+C to exit. 復(fù)制代碼效果: 進(jìn)入到對(duì)應(yīng)的微信公眾號(hào)中,你輸入任何內(nèi)容,它都會(huì)給你返回相同的內(nèi)容。如果微信公眾平臺(tái)告訴你Token驗(yàn)證失效估計(jì)就是你的IP地址不對(duì)。
數(shù)據(jù)庫
使用數(shù)據(jù)庫是為了存儲(chǔ)數(shù)據(jù)(完全可以使用txt文件來維護(hù)),在此為了簡(jiǎn)化手拼SQL易出錯(cuò)以及本項(xiàng)目并不需要進(jìn)行多少性能優(yōu)化的情況下,直接采用ORM(對(duì)象關(guān)系映射技術(shù))。 P.S.我將采用sqlalchemy這個(gè)框架進(jìn)行,在廖雪峰的博客上有較為細(xì)致的講解,大家可以先自行研究一番到底是個(gè)什么東西。
這是定義好的硬件類,其實(shí)也就是硬件表,
# 硬件表 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) 復(fù)制代碼新建一個(gè)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# '數(shù)據(jù)庫類型+數(shù)據(jù)庫驅(qū)動(dòng)名稱://用戶名:口令@機(jī)器地址:端口號(hào)/數(shù)據(jù)庫名' 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() 復(fù)制代碼到這一步為止,就完成了使用ORM進(jìn)行MySQL數(shù)據(jù)庫操作的鋪墊。接下來,我們將進(jìn)行數(shù)據(jù)庫的增刪改查方法的編寫。
現(xiàn)在我們完成了test.py的編寫,主要完成了使用ORM技術(shù)編寫了操作數(shù)據(jù)庫的各種方法。接下來,我們要使用微信公眾號(hào)對(duì)數(shù)據(jù)庫進(jìn)行修改。
上位機(jī)配置
在這個(gè)環(huán)節(jié)中,我們要做到用戶發(fā)送“開燈”、“關(guān)燈”、“開風(fēng)扇”、“溫度”等消息給公眾號(hào)后,能夠在數(shù)據(jù)庫中看到狀態(tài)被修改并且反饋。
簡(jiǎn)單的來概括一下要做的工作:首先要讓服務(wù)器接收到公眾號(hào)發(fā)送而來的消息;其次要對(duì)發(fā)送者進(jìn)行篩選,不能誰都可以操作這套系統(tǒng);接著匹配消息,執(zhí)行不同的方法;最后給公眾號(hào)反饋回消息。
服務(wù)器接收公眾號(hào)發(fā)送的消息我們已經(jīng)在第一步中完成了,現(xiàn)在要對(duì)接收到的消息體進(jìn)行解析,根據(jù)userID來篩選誰能對(duì)這套系統(tǒng)進(jìn)行操作。我的做法非常簡(jiǎn)單,用一個(gè)"pjhubs.txt"文件保存了能夠操作這套系統(tǒng)的用戶ID。每次接收到消息時(shí),都先從消息體中取出fromUserName字段數(shù)據(jù)與txt文件中的數(shù)據(jù)進(jìn)行比對(duì),如果在txt文件中才允許接著進(jìn)行操作。
import itchatmp import test# 配置微信公眾號(hào)信息 itchatmp.update_config(itchatmp.WechatConfig(token='你的token',appId = '你的appId',appSecret = '你的appSecret'))# 接收用戶消息 def text_reply(msg):toUserName = msg['FromUserName']content = msg['Content']isContain = 0# pjhubs.txt為有權(quán)限的用戶列表f = open("pjhubs.txt","r")lines = f.readlines()for line in lines:if line[:-1] == toUserName:isContain = 1; if isContain == 0:return '該系統(tǒng)并未對(duì)您開放,請(qǐng)聯(lián)系PJ進(jìn)行配置'else:if content == '添加':# test.addNewUnit('tempUnit', 1, 2)return '操作成功!'elif content == '開燈':return test.updateStatusWithHardware('hardware', 1, 'redLED', 1)elif content == '關(guān)燈':return test.updateStatusWithHardware('hardware', 1, 'redLED', 0)elif content == '溫度':unit = test.updateStatusWithHardware('hardware', 0, 'tempUnit', 1)returnString = '當(dāng)前溫度為:' + str(unit.num) + '°'return returnStringelif content == '開風(fēng)扇':return test.updateStatusWithHardware('hardware', 1, 'tempUnit', 1)elif content == '關(guān)風(fēng)扇':return test.updateStatusWithHardware('hardware', 1, 'tempUnit', 0)# 新用戶關(guān)注公眾號(hào)時(shí) def user_management(event):if(event['Event']=='subscribe'):return u'歡迎來到PJHubs,如果你想試用室內(nèi)環(huán)境智能監(jiān)測(cè)系統(tǒng),請(qǐng)聯(lián)系PJ' itchatmp.run() 復(fù)制代碼執(zhí)行,
python mp.py 復(fù)制代碼在微信公眾號(hào)中發(fā)送“開燈”、“關(guān)燈”、“開風(fēng)扇”、“溫度”等指令都會(huì)對(duì)數(shù)據(jù)庫進(jìn)行操作。此時(shí)可以select對(duì)應(yīng)表查看數(shù)據(jù)是否一致再進(jìn)行下一步。
API編寫
這是知乎上一些關(guān)于API的內(nèi)容講解。我們?cè)诖耸褂肍lask輕量級(jí)的web框架進(jìn)行API編寫。主要是給樹莓派操作數(shù)據(jù)庫使用的。 通過pip安裝好flask后,我們可以先嘗試寫一個(gè)最簡(jiǎn)單的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 testapp = Flask(__name__) api = Api(app) def index():return 'Get out!?'if __name__ == '__main__':app.run(host='0.0.0.0',debug=True) 復(fù)制代碼此時(shí)我們?nèi)g覽器中輸入ip地址或域名,即可看到“Get out!?”這句話。現(xiàn)在我們要接著編寫幾個(gè)資源訪問路徑以便樹莓派訪問。
# 獲取所有硬件信息(求快可以這么寫) 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) 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'}) 復(fù)制代碼我們只需要起兩個(gè)API服務(wù)即可滿足要求。此時(shí)我們可以根據(jù)寫好的API訪問規(guī)則到瀏覽器中驗(yàn)證一番。
下位機(jī)配置——樹莓派
樹莓派是整套系統(tǒng)的靈魂所在,對(duì)上承載著數(shù)據(jù)庫的更新,對(duì)下負(fù)擔(dān)著Arduino的操作。當(dāng)然,如果不考慮性能你可以直接用Arduino的wifi模組,直接對(duì)API發(fā)起請(qǐng)求。
樹莓派首先要去在固定時(shí)間間隔內(nèi)輪詢特定API,根據(jù)API反饋回來的數(shù)據(jù)對(duì)固定串口發(fā)送特定字符,接收Arduino傳遞上來的數(shù)據(jù),拼接API更新數(shù)據(jù)庫。
serial是對(duì)樹莓派上的串口進(jìn)行操作庫,urllib2是網(wǎng)絡(luò)請(qǐng)求庫,json是解析和發(fā)送JSON格式庫。
import serial import urllib2 import jsonhostname = 'http://你的地址/dachuang/api/v1/allHardware' # /dev/ttyACM0 是樹莓派上編號(hào)為0的USB口(可以在/dev目錄下通過觀察拔插對(duì)應(yīng)的USB口找到對(duì)應(yīng)的編號(hào)) 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 resultsend = ''# 通過json庫解析完后的數(shù)據(jù)就是字典if result[0]['status'] == 1:send += 'a'else:send += 'A'if result[1]['status'] == 1:send += 'b'else:send += 'B'# 從下位機(jī)Arduino上讀取到的數(shù)據(jù)拼接URL發(fā)送回服務(wù)器,更新數(shù)據(jù)庫ser.write(send)response = ser.readall()if '' != response:response = response[0:2]ret = urllib2.Request("http://你的地址/dachuang/api/v1/updateHardware?hardwarename=tempUnit&status=3" + '&num=' + response)ret = urllib2.urlopen(ret) 復(fù)制代碼我在此重新定義了一套操作流程, a -> “開燈” A -> “關(guān)燈” b -> “開風(fēng)扇” B -> “關(guān)風(fēng)扇” 因?yàn)槭艿紸rduino本身性能的影響,如果你還給它發(fā)一長(zhǎng)串的字符串比如“open light”等,那估計(jì)單單就解析并匹配,分時(shí)操作已經(jīng)過了。?。因此我才想重新定義一套ASCII碼關(guān)系映射,并且限制樹莓派每次輪詢的時(shí)間為4秒一次,可根據(jù)用戶所搭建的下位機(jī)硬件系統(tǒng)復(fù)雜適度增減輪詢時(shí)間。
下位機(jī)配置——Arduino
Arduino要做的事情只有接收串口數(shù)據(jù),解析串口數(shù)據(jù),根據(jù)數(shù)據(jù)分別操作不同的硬件。Arduino用C寫的,定義了一套規(guī)則,用起來非常順手親切。
void setup() {Serial.begin(9600); // 9600 bpspinMode(yellowLED, OUTPUT);pinMode(Buzzer,OUTPUT);pinMode(REDled,OUTPUT);pinMode(fanPin,OUTPUT); } void loop() {//讀取A0口的電壓值,溫度傳感器所在串口int n = analogRead(A0); //使用浮點(diǎn)數(shù)存儲(chǔ)溫度數(shù)據(jù),溫度數(shù)據(jù)由電>壓值換算得到float vol = n * (5.0 / 1023.0*100); if ( Serial.available() ) {// 向串口寫入溫度Serial.println(vol);// 讀取樹莓派寫入串口的數(shù)據(jù)int res = Serial.read();// 根據(jù)ASCII碼執(zhí)行不同硬件操作函數(shù)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°后開啟高溫預(yù)警,蜂鳴器奏響和風(fēng)扇打開if (vol > 30) { buzzerBegin();} }// 蜂鳴器響鈴 void buzzerBegin() {digitalWrite(fanPin, HIGH);digitalWrite(REDled, HIGH);//頻率從200HZ 增加到800HZ,模擬警報(bào)聲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); } 復(fù)制代碼上下位機(jī)聯(lián)調(diào)
至此我們完成了全部的基礎(chǔ)工作,在聯(lián)調(diào)的過程中,當(dāng)初我也發(fā)生了非常多的問題,這無法避免,稍不注意電路連錯(cuò)了以后就全盤皆輸了,在此我只能祝大家好運(yùn),Arduino連接各個(gè)元器件的方式并沒有展開,因?yàn)槲蚁嘈糯蠹业碾娐吩O(shè)計(jì)一定比我強(qiáng)!?
在聯(lián)調(diào)的過程中,你需要做的是,
結(jié)果:
github地址:github.com/windstormey…
總結(jié)
以上是生活随笔為你收集整理的使用微信+树莓派+Arduino+服务器构建你的看门狗的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【IoT】 产品设计:BRD、MRD、P
- 下一篇: vs2013 应用程序无法正常启动