├── README.md ├── berrypi └── mqtt.py ├── client ├── app.js ├── app.json ├── app.wxss ├── pages │ ├── index │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ └── logs │ │ ├── logs.js │ │ ├── logs.json │ │ ├── logs.wxml │ │ └── logs.wxss └── utils │ └── util.js └── server └── berrypiServer.js /README.md: -------------------------------------------------------------------------------- 1 | # 微信小程序-树莓派照片监控 2 | 3 | ### 说明: 4 | 5 | 实现树莓派照片监控功能,特色: 6 | - 小程序和服务器使用webSocket通信 7 | - 树莓派和服务器使用mqtt通信 8 | 9 | ### 数据接口: 10 | 11 | - wss://socket.getweapp.com 12 | 13 | ### 目录结构: 14 | 15 | client: 16 | - pages — 存放项目页面文件 17 | - utils — 存放格式化文件 18 | 19 | server: 20 | - berrypiServer.js — 服务器文件 21 | 22 | berrypi: 23 | - mqtt.js — 树莓派文件 24 | 25 | ### 开发环境: 26 | 27 | 微信web开发者工具 v0.11.122100 28 | 29 | ### 项目截图: 30 | 31 | https://www.getweapp.com/project?projectId=5899330e52e1e8733dc567fc 32 | -------------------------------------------------------------------------------- /berrypi/mqtt.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # 树莓派照片监控 4 | # 作者:刘焱旺 5 | # 官网:www.getweapp.com 6 | # QQ群:499859691 7 | # 调试过程中有任何问题欢迎和我们联系 8 | 9 | import paho.mqtt.client as mqtt 10 | import json 11 | 12 | from qiniu import Auth, put_file, etag, urlsafe_base64_encode 13 | import qiniu.config 14 | import os, time 15 | 16 | #需要填写你的服务器IP 17 | ip = 'xxx' 18 | 19 | #需要填写你的 Access Key 和 Secret Key 20 | access_key = 'xxx' 21 | secret_key = 'xxx' 22 | 23 | #构建鉴权对象 24 | q = Auth(access_key, secret_key) 25 | 26 | #需要填写你的空间 27 | bucket_name = 'xxx' 28 | 29 | #需要填写你的空间域名 30 | base = 'http://xxx' 31 | 32 | #照片缓存 33 | cache = '' 34 | lastUpdated = 0 35 | 36 | #生成上传 Token,可以指定过期时间等 37 | token = q.upload_token(bucket_name, None, 3600) 38 | 39 | def upload(localfile): 40 | ret, info = put_file(token, None, localfile) 41 | return ret 42 | 43 | # 连接成功回调函数 44 | def on_connect(client, userdata, flags, rc): 45 | print("Connected with result code " + str(rc)) 46 | client.subscribe("CAMERA/PHOTO") 47 | 48 | # 消息推送回调函数 49 | def on_message(client, userdata, msg): 50 | global base, cache, lastUpdated 51 | print(msg.topic+" "+str(msg.payload)) 52 | if int(time.time()) < lastUpdated + 5: 53 | client.publish('CAMERA/PHOTO/BACK', '{"id":"'+str(msg.payload)+'", "path":"'+cache+'"}') 54 | return 55 | r = os.system('fswebcam -r 640*480 imagex01.jpg') 56 | ret = upload('./imagex01.jpg') 57 | print ret['hash'] 58 | cache = base+ret['hash'] 59 | lastUpdated = int(time.time()) 60 | client.publish('CAMERA/PHOTO/BACK', '{"id":"'+str(msg.payload)+'", "path":"'+base+ret['hash']+'"}') 61 | 62 | 63 | if __name__ == '__main__': 64 | client = mqtt.Client() 65 | client.on_connect = on_connect 66 | client.on_message = on_message 67 | 68 | try: 69 | # 请根据实际情况改变MQTT代理服务器的IP地址 70 | global ip 71 | client.connect(ip, 1883, 60) 72 | client.loop_forever() 73 | except KeyboardInterrupt: 74 | client.disconnect() 75 | -------------------------------------------------------------------------------- /client/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 树莓派照片监控 3 | * 作者:刘焱旺 4 | * 官网:www.getweapp.com 5 | * QQ群:499859691 6 | * 调试过程中有任何问题欢迎和我们联系 7 | */ 8 | 9 | App({ 10 | onLaunch: function() { 11 | //调用API从本地缓存中获取数据 12 | var logs = wx.getStorageSync('logs') || [] 13 | logs.unshift(Date.now()) 14 | wx.setStorageSync('logs', logs) 15 | }, 16 | getUserInfo: function(cb) { 17 | var that = this 18 | if (this.globalData.userInfo) { 19 | typeof cb == "function" && cb(this.globalData.userInfo) 20 | } else { 21 | //调用登录接口 22 | wx.login({ 23 | success: function() { 24 | wx.getUserInfo({ 25 | success: function(res) { 26 | that.globalData.userInfo = res.userInfo 27 | typeof cb == "function" && cb(that.globalData.userInfo) 28 | } 29 | }) 30 | } 31 | }) 32 | } 33 | }, 34 | globalData: { 35 | userInfo: null 36 | } 37 | }) 38 | -------------------------------------------------------------------------------- /client/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages":[ 3 | "pages/index/index", 4 | "pages/logs/logs" 5 | ], 6 | "window":{ 7 | "backgroundTextStyle":"light", 8 | "navigationBarBackgroundColor": "#1AAD19", 9 | "navigationBarTitleText": "树莓派照片监控", 10 | "navigationBarTextStyle":"light" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client/app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | .container { 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: space-between; 8 | padding: 200rpx 0; 9 | box-sizing: border-box; 10 | } 11 | -------------------------------------------------------------------------------- /client/pages/index/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 树莓派照片监控 3 | * 作者:刘焱旺 4 | * 官网:www.getweapp.com 5 | * QQ群:499859691 6 | * 调试过程中有任何问题欢迎和我们联系 7 | */ 8 | 9 | Page({ 10 | data: { 11 | photo: '', 12 | open: false 13 | }, 14 | fetch: function() { 15 | if(!this.data.open){ 16 | console.log('请稍后再试') 17 | return 18 | } 19 | wx.sendSocketMessage({ 20 | data:JSON.stringify({ 21 | cmd:'BERRY_PI' 22 | }) 23 | }) 24 | }, 25 | onLoad: function() { 26 | wx.connectSocket({ 27 | url: 'wss://socket.getweapp.com' 28 | }) 29 | 30 | wx.onSocketOpen((res) => { 31 | console.log('连接服务器:成功') 32 | this.setData({ 33 | open: true 34 | }) 35 | }) 36 | 37 | wx.onSocketMessage((res) => { 38 | console.log('receve:'+res.data) 39 | console.log(this) 40 | this.setData({ 41 | photo: JSON.parse(res.data).path 42 | }) 43 | }) 44 | 45 | wx.onSocketClose((res) => { 46 | console.log('连接服务器:关闭') 47 | this.setData({ 48 | open: false 49 | }) 50 | wx.connectSocket({ 51 | url: 'wss://socket.getweapp.com/test' 52 | }) 53 | }) 54 | 55 | wx.onSocketError((res) => { 56 | console.log('连接服务器:失败') 57 | this.setData({ 58 | open: false 59 | }) 60 | }) 61 | } 62 | }); 63 | -------------------------------------------------------------------------------- /client/pages/index/index.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /client/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /client/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | page{ 3 | background:black; 4 | } 5 | .container{ 6 | display: block; 7 | } 8 | .container image{ 9 | width: 100%; 10 | position: fixed; 11 | top: 0; 12 | height: 500px; 13 | } 14 | 15 | .container button{ 16 | position: fixed; 17 | bottom: 0; 18 | border-radius: 0; 19 | width: 100%; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /client/pages/logs/logs.js: -------------------------------------------------------------------------------- 1 | //logs.js 2 | var util = require('../../utils/util.js') 3 | Page({ 4 | data: { 5 | logs: [] 6 | }, 7 | onLoad: function() { 8 | this.setData({ 9 | logs: (wx.getStorageSync('logs') || []).map(function(log) { 10 | return util.formatTime(new Date(log)) 11 | }) 12 | }) 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /client/pages/logs/logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "查看启动日志" 3 | } -------------------------------------------------------------------------------- /client/pages/logs/logs.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{index + 1}}. {{log}} 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/pages/logs/logs.wxss: -------------------------------------------------------------------------------- 1 | .log-list { 2 | display: flex; 3 | flex-direction: column; 4 | padding: 40rpx; 5 | } 6 | .log-item { 7 | margin: 10rpx; 8 | } 9 | -------------------------------------------------------------------------------- /client/utils/util.js: -------------------------------------------------------------------------------- 1 | function formatTime(date) { 2 | var year = date.getFullYear() 3 | var month = date.getMonth() + 1 4 | var day = date.getDate() 5 | 6 | var hour = date.getHours() 7 | var minute = date.getMinutes() 8 | var second = date.getSeconds() 9 | 10 | 11 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') 12 | } 13 | 14 | function formatNumber(n) { 15 | n = n.toString() 16 | return n[1] ? n : '0' + n 17 | } 18 | 19 | module.exports = { 20 | formatTime: formatTime 21 | } 22 | -------------------------------------------------------------------------------- /server/berrypiServer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 树莓派照片监控 3 | * 作者:刘焱旺 4 | * 官网:www.getweapp.com 5 | * QQ群:499859691 6 | * 调试过程中有任何问题欢迎和我们联系 7 | */ 8 | 9 | import { Server } from 'ws' 10 | import uuid from 'uuid' 11 | import moment from 'moment' 12 | 13 | import mqtt from 'mqtt' 14 | 15 | const client = mqtt.connect('mqtt://127.0.0.1') 16 | 17 | client.on('connect', () => { 18 | console.log('connected') 19 | client.subscribe('CAMERA/PHOTO/BACK') 20 | }) 21 | 22 | client.on('message', (topic, message) => { 23 | const data = JSON.parse(message.toString()) 24 | console.log(topic, data) 25 | if(!(data.id in clients)){ 26 | return 27 | } 28 | clients[data.id].send(JSON.stringify({ 29 | cmd:"CAMERA/PHOTO", 30 | path: data.path 31 | })) 32 | }) 33 | 34 | const wss = new Server({ port: 3000}); 35 | 36 | const messages = [] 37 | 38 | onst BERRY_PI = 'BERRY_PI' 39 | const clients = {} 40 | 41 | 42 | wss.on('connection', function connection(ws) { 43 | ws.clientId = uuid.v1() 44 | clients[ws.clientId] = ws 45 | console.log('client connected:'+ws.clientId) 46 | 47 | ws.on('message', (message) => { 48 | try{ 49 | const data = JSON.parse(message) 50 | switch(data.cmd){ 51 | case BERRY_PI: 52 | client.publish('CAMERA/PHOTO', ws.clientId) 53 | return 54 | default: 55 | return 56 | } 57 | }catch(e){ 58 | console.log(e) 59 | } 60 | }) 61 | 62 | ws.on('close', () => { 63 | if(ws.clientId) 64 | delete clients[ws.clientId] 65 | }) 66 | }) 67 | 68 | --------------------------------------------------------------------------------