Browse Source

first commit

houjie 4 years ago
commit
87954456b7

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+/.hbuilderx
+/node_modules
+/unpackage
+
+

+ 36 - 0
App.vue

@@ -0,0 +1,36 @@
+<!-- <template>
+	<view class="aaa">
+		<image class="logo" src="/static/logo.png"></image>
+	</view>
+</template> -->
+<script>
+	//import mqtt from 'mqtt/dist/mqtt.js'
+	export default {
+		onLoad() {
+			//连接mqtt服务器
+			//this.$root.connect()
+			console.log('App Load')
+		},
+		onLaunch: function() {
+			//连接mqtt服务器
+			//this.$root.connect()
+			console.log(`App Launch`)
+			//this.$store.commit('connect')
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		},
+
+	}
+</script>
+
+<style>
+	/*每个页面公共css */
+	/* .aaa{
+		background: #2C3E50;
+	} */
+	
+</style>

File diff suppressed because it is too large
+ 14626 - 0
common/mqtt.js


+ 75 - 0
common/util.js

@@ -0,0 +1,75 @@
+function formatTime(time) {
+	if (typeof time !== 'number' || time < 0) {
+		return time
+	}
+
+	var hour = parseInt(time / 3600)
+	time = time % 3600
+	var minute = parseInt(time / 60)
+	time = time % 60
+	var second = time
+
+	return ([hour, minute, second]).map(function (n) {
+		n = n.toString()
+		return n[1] ? n : '0' + n
+	}).join(':')
+}
+
+function formatLocation(longitude, latitude) {
+	if (typeof longitude === 'string' && typeof latitude === 'string') {
+		longitude = parseFloat(longitude)
+		latitude = parseFloat(latitude)
+	}
+
+	longitude = longitude.toFixed(2)
+	latitude = latitude.toFixed(2)
+
+	return {
+		longitude: longitude.toString().split('.'),
+		latitude: latitude.toString().split('.')
+	}
+}
+var dateUtils = {
+	UNITS: {
+		'年': 31557600000,
+		'月': 2629800000,
+		'天': 86400000,
+		'小时': 3600000,
+		'分钟': 60000,
+		'秒': 1000
+	},
+	humanize: function (milliseconds) {
+		var humanize = '';
+		for (var key in this.UNITS) {
+			if (milliseconds >= this.UNITS[key]) {
+				humanize = Math.floor(milliseconds / this.UNITS[key]) + key + '前';
+				break;
+			}
+		}
+		return humanize || '刚刚';
+	},
+	format: function (dateStr) {
+		var date = this.parse(dateStr)
+		var diff = Date.now() - date.getTime();
+		if (diff < this.UNITS['天']) {
+			return this.humanize(diff);
+		}
+		var _format = function (number) {
+			return (number < 10 ? ('0' + number) : number);
+		};
+		return date.getFullYear() + '/' + _format(date.getMonth() + 1) + '/' + _format(date.getDate()) + '-' +
+			_format(date.getHours()) + ':' + _format(date.getMinutes());
+	},
+	parse: function (str) { //将"yyyy-mm-dd HH:MM:ss"格式的字符串,转化为一个Date对象
+		var a = str.split(/[^0-9]/);
+		return new Date(a[0], a[1] - 1, a[2], a[3], a[4], a[5]);
+	}
+};
+
+
+
+module.exports = {
+	formatTime: formatTime,
+	formatLocation: formatLocation,
+	dateUtils: dateUtils
+}

+ 38 - 0
components/mycomponent.vue

@@ -0,0 +1,38 @@
+<template>
+	<view>
+		<label>{{title}}</label>
+	</view>
+	<view>
+		<label>{{content}}</label>
+	</view>
+</template>
+
+<script>
+	export default {
+		name:"mycomponent",
+		props:{
+			title:{
+				type:String,
+				default:""
+			},
+			content:{
+				type:String,
+				default:""
+			}
+		}
+		data() {
+			return {
+				
+			};
+		},
+		methods:{
+			tab(event){
+				console.log(event)
+			}
+		}
+	}
+</script>
+
+<style>
+
+</style>

+ 42 - 0
main.js

@@ -0,0 +1,42 @@
+import Vue from 'vue'
+import App from './App'
+//import mqtt from 'mqtt/dist/mqtt.js'
+import store from './store'
+
+Vue.prototype.$store = store
+
+Vue.config.productionTip = false
+
+App.mpType = 'app'
+
+// const options = {
+// 				connectTimeout: 4000, // 超时时间
+// 				clientId: "mqtt_" + parseInt(Math.random() * 100 + 800, 10),
+// 				port: 8081,
+// 			};
+
+// const store = {
+// 	 debug: true,
+// 	 mqttClient:undefined,
+// 	  connect(){
+// 		  this.mqttClient = mqtt.connect('wxs://test.mosquitto.org', options)
+// 		  let that = this;
+// 		  //连接成功回调
+// 		  that.mqttClient.on("connect", function() {
+// 		  	that.isConnect = true;
+// 		  	console.log("connect success!");
+// 		  });
+		  
+// 		  //异常回调
+// 		  that.mqttClient.on("error", function(err) {
+// 		  	that.isConnect = false;
+// 		  	console.log(err);
+// 		  });
+// 	  }
+// 	}			
+
+const app = new Vue({
+	 store:store,
+	...App
+})
+app.$mount()

+ 76 - 0
manifest.json

@@ -0,0 +1,76 @@
+{
+    "name" : "mqttTest",
+    "appid" : "",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx08f94a3e90881910",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true,
+		"permission" : {
+		    "scope.userLocation" : {
+		        "desc" : "扫描附近设备"
+		    }
+		}
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    }
+}

File diff suppressed because it is too large
+ 1622 - 0
package-lock.json


+ 7 - 0
package.json

@@ -0,0 +1,7 @@
+{
+  "dependencies": {
+    "axios": "^0.21.1",
+    "mqtt": "^4.1.0",
+    "protobufjs": "^6.11.2"
+  }
+}

+ 106 - 0
pages.json

@@ -0,0 +1,106 @@
+{
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		//mqtt页面
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "IOT"
+			}
+		},
+		//设备页面
+		{
+            "path" : "pages/index/device",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "device"
+            }
+            
+        },
+		//我的页面
+		{
+		    "path" : "pages/index/mine",
+		    "style" :                                                                                    
+		    {
+		        "navigationBarTitleText": "mine"
+		    }
+		    
+		},
+		
+		//扫描蓝牙设备页面
+	    {
+            "path" : "pages/ble/ScanBleDevice",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "扫描设备"
+				
+            }
+        },
+		//连接蓝牙设备页面
+        {
+            "path" : "pages/ble/ConnectBleDevice",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "连接设备",
+                "enablePullDownRefresh": false
+            }
+            
+        }
+        
+    ],
+	"dependencies": {
+	  "mqtt": "^3.0.0"
+	},
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#6d6d6d",
+		"enablePullDownRefresh":true,
+		"backgroundTextStyle":"light"
+	},
+	"condition" : { //模式配置,仅开发期间生效
+		"current": 0, //当前激活的模式(list 的索引项)
+		"list": [
+			{
+				"name": "", //模式名称
+				"path": "", //启动页面,必选
+				"query": "" //启动参数,在页面的onLoad函数里面得到
+			}
+		]
+	},
+	 "tabBar": {
+	        "color": "#7A7E83",
+	        "selectedColor": "#3cc51f",
+	        "borderStyle": "black",
+	        "backgroundColor": "#ffffff",
+	        "height": "50px",
+	        "fontSize": "10px",
+	        "iconWidth": "24px",
+	        "spacing": "3px",
+	        "list": [{
+	            "pagePath": "pages/index/index",
+	            "iconPath": "static/image/iot.png",
+	            "selectedIconPath": "static/image/iot_org.png",
+	            "text": "IOT"
+	        }, {
+	            "pagePath": "pages/index/device",
+	            "iconPath": "static/image/device.png",
+	            "selectedIconPath": "static/image/device_red.png",
+	            "text": "device"
+	        }
+			, {
+			    "pagePath": "pages/index/mine",
+			    "iconPath": "static/image/mine.png",
+			    "selectedIconPath": "static/image/mine_pressed.png",
+			    "text": "mine"
+			}],
+	        "midButton": {
+	            "width": "80px",
+	            "height": "50px",
+	            "text": "文字",
+	            "iconPath": "static/image/mine.png",
+	            "iconWidth": "24px",
+	            "backgroundImage": "static/image/midButton_backgroundImage.png"
+	        }
+	    }
+}

+ 479 - 0
pages/ble/ConnectBleDevice.vue

@@ -0,0 +1,479 @@
+<template>
+	<view class="box">
+		<view class="content">
+			<view>
+				<label class="radio" @click="isWifi=true">
+					<radio value="wifi" :checked="isWifi" />wifi
+				</label>
+				<label class="radio" @click="isWifi=false">
+					<radio value="4G" :checked="!isWifi" />4G
+				</label>
+			</view>
+			<view class="input-row">
+				<text>wifi名称:</text>
+				<input class="edittext" v-model="wifiName" />
+			</view>
+			<view class="input-row">
+				<text>wifi密码:</text>
+				<input class="edittext" v-model="wifiPwd" />
+			</view>
+			<view style="margin-top: 10rpx;">
+				<button @click="connectBle()">下一步</button>
+			</view>
+			<text style="margin-top: 10rpx;">{{errorText}}</text>
+		</view>
+
+		<view class="loadView" v-if="showLoading">
+			<image class="rotateAnim" src="../../static/loading.svg"></image>
+			<text style="margin-left: 10rpx;">{{loadingText}}</text>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	const server_uuid = "0000FFE5-0000-1000-8000-00805F9B34FB";
+	//const baidu_server_uuid = "00001111-0000-1000-8000-00805f9b34fb";
+	export default {
+
+		data() {
+			return {
+				deviceName: "",
+				deviceId: "",
+				isWifi: true,
+				wifiName: "JackieHou_Mac",
+				wifiPwd: "1234567890",
+				connected: false,
+				connecting: false,
+				iccid: '',
+				withoutWifiInterval: undefined,
+				configTimeout: undefined,
+				showLoading: false, //是否显示loading
+				loadingText: '正在连接设备', //load框的文字提示
+				errorText: "",
+
+			}
+		},
+		onLoad(option) {
+			this.deviceName = option.name;
+			this.deviceId = option.mac
+			console.log(`name = ${this.deviceName},mac = ${this.deviceId}`);
+			//this.getWifiBuffer()
+		},
+		methods: {
+			test2() {
+				function doSomething(str) {
+					return new Promise((resolve, reject) => {
+						setTimeout(() => {
+							resolve(str.toUpperCase());
+						}, 2000)
+
+					})
+				}
+
+				let strs = ['aaaa', 'bbbbb', 'ccccc', 'ddddd', 'eeeeeee'];
+				let promiseList = strs.map(doSomething);
+				let time = Date.now();
+				(async () => {
+					let res = await Promise.allSettled(promiseList)
+					console.log(Date.now() - time);
+					console.log(res);
+				})();
+			},
+			test() {
+				let buffer = Buffer.from(
+					"1abcdefghijklmnopqrstuvwxyz2abcdefghijklmnopqrstuvwxyz3abcdefghijklmnopqrstuvwxyz")
+				console.log(buffer.length);
+				let count = Math.ceil(buffer.length / 20)
+				let buffers = []
+				for (var i = 0; i < count; i++) {
+					let size = Math.min(20, buffer.length - i * 20)
+					console.log(size);
+					let buf = buffer.slice(i * 20, size + i * 20)
+					buffers.push(buf)
+				}
+				for (let b of buffers) {
+					console.log(b);
+				}
+			},
+			connectBle() {
+				let that = this
+				if (!that.connecting && !that.connected) {
+					console.log('创建连接')
+					clearInterval(that.withoutWifiInterval)
+					clearTimeout(that.configTimeout)
+					that.connecting = true
+					that.showLoading = true
+					that.loadingText = '正在连接设备'
+					that.errorText = ""
+					uni.createBLEConnection({
+						// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
+						deviceId: that.deviceId,
+						success(res) {
+							that.connected = true
+							console.log('连接成功')
+							console.log(res)
+							that.connecting = false
+							that.loadingText = '正在获取服务'
+							//获取服务
+							that.getBleDeviceServices()
+						},
+						fail(err) {
+							console.log('连接失败')
+							console.log(err)
+							that.connecting = false
+							that.showLoading = false
+							that.errorText = '连接失败!请将手机靠近设备'
+							uni.showToast({
+								title: '连接失败!请将手机靠近设备',
+								icon: 'none',
+								duration: 2000
+							})
+						}
+					})
+				}
+			},
+			listenBleConnectState() {
+				uni.onBLEConnectionStateChange(function(res) {
+					// 该方法回调中可以用于处理连接意外断开等异常情况
+					console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`)
+					that.connected = res.connected
+					if (res.connected === false) {
+						clearInterval(that.withoutWifiInterval)
+						clearTimeout(that.configTimeout)
+						that.showLoading = false
+						that.errorText = '连接已断开,请重试!'
+						uni.showToast({
+							title: '连接已断开,请重试!',
+							icon: 'none',
+							duration: 2000
+						})
+					}
+				})
+			},
+			getBleDeviceServices() {
+				let that = this
+				setTimeout(() => {
+					uni.getBLEDeviceServices({
+						deviceId: that.deviceId,
+						success(res) {
+							console.log('获取服务成功')
+							console.log(res)
+							for (let service of res.services) {
+								console.log(service)
+								if (server_uuid.toUpperCase() == service.uuid.toUpperCase()) {
+									that.loadingText = '正在获取特征'
+									//获取特征
+									that.getBLEDeviceCharacteristics(server_uuid)
+									return
+								}
+							}
+						},
+						fail(err) {
+							console.log('获取服务失败')
+							console.log(err);
+							that.closeBLEConnection()
+							that.errorText = '获取服务失败'
+						}
+					})
+				}, 1000)
+
+			},
+			getBLEDeviceCharacteristics(uuid) {
+				console.log("获取特征");
+				let that = this
+				//setTimeout(() => {
+				uni.getBLEDeviceCharacteristics({
+					deviceId: that.deviceId,
+					serviceId: uuid,
+					success(res) {
+						console.log('获取特征成功');
+						console.log(res);
+						//todo直接写入数据
+						//that.sendCongifComm(res.characteristics[0].uuid)
+						that.notifyBLECharacteristicValueChange(res.characteristics[0].uuid)
+					},
+					fail(err) {
+						console.log('获取特征失败');
+						console.log(err);
+						that.closeBLEConnection()
+						that.errorText = '获取特征失败'
+					}
+				})
+				//}, 1000)
+			},
+			notifyBLECharacteristicValueChange(characteristicId) {
+				let that = this
+				setTimeout(() => {
+					that.loadingText = '正在配网'
+
+					//设置一个超时配网的时间
+					that.configTimeout = setTimeout(() => {
+						that.closeBLEConnection()
+						uni.showToast({
+							title: "配网超时,请重试",
+							icon: 'none',
+							duration: 1000
+						})
+						that.errorText = '配网超时,请重试'
+					}, 20 * 1000)
+
+					uni.notifyBLECharacteristicValueChange({
+						state: true,
+						deviceId: that.deviceId,
+						serviceId: server_uuid,
+						characteristicId: characteristicId,
+					}).then(res => {
+						console.log("notify成功");
+						console.log(res);
+					}).catch(err => {
+						console.log("notify失败");
+						console.log(err);
+					})
+
+					//监听设备发送过来的数据
+					uni.onBLECharacteristicValueChange(function(res) {
+						//console.warn('收到设备发送过来的数据');
+						//console.log(res);
+						let buf = Buffer.from(res.value);
+						let receiveStr = buf.toString()
+						console.log(`收到数据:${receiveStr}`);
+						if (receiveStr === 'connect_success') {
+							let text = `${that.isWifi ? 'WIFI' : '4g'}配网成功`
+							uni.showToast({
+								title: text,
+								icon: 'none',
+								duration: 1000
+							})
+							that.closeBLEConnection()
+							that.errorText = text
+						} else if (receiveStr.indexOf('failure') >= 0) {
+							uni.showToast({
+								title: '配网失败',
+								icon: 'none',
+								duration: 1000
+							})
+							that.closeBLEConnection()
+							that.errorText = '配网失败'
+						} else if (receiveStr.indexOf('ic0:') >= 0) { //用来获取iccid的
+							that.iccid = receiveStr.substring(4, receiveStr.length);
+						} else if (receiveStr.indexOf('ic1:') >= 0) { //用来获取iccid的 把ic0 和ic1拼接起来
+							let ic1 = receiveStr.substring(4, receiveStr.length);
+							that.iccid = that.iccid + ic1;
+							//发送4G配网指令
+							//let buf = Buffer.from('without_WiFi')
+							//setTimeout(() =>{that.sendCongifCmd(characteristicId,buf.buffer)},800)
+							setTimeout(() => {
+								that.sendWithoutWifiCommand(characteristicId);
+							}, 800)
+						} else if (receiveStr.indexOf('start_network') >= 0 || receiveStr.indexOf(
+								'recv_finish') >= 0) {
+							//发送	AIrSMArT_CLOSE指令	
+							setTimeout(() => {
+								let buf = Buffer.from('AIrSMArT_CLOSE')
+								that.sendCongifCmd(characteristicId,buf.buffer)
+							}, 2000)
+						} else if (receiveStr.indexOf('without_WiFi_OK') >= 0) {
+							let text = `4G配网成功`
+							uni.showToast({
+								title: text,
+								icon: 'none',
+								duration: 1000
+							})
+							that.errorText = text
+							that.closeBLEConnection()
+						}
+					})
+
+					//发送数据给设备
+					setTimeout(() => {
+						let buffer
+						if (that.isWifi) {
+							buffer = that.getWifiBuffer()
+							//that.sendCongifCmd(characteristicId,buffer)
+						} else {
+							//4G设备先获取iccid
+							let buf = Buffer.from('get_iccid')
+							buffer = buf.buffer
+						}
+						that.sendCongifCmd(characteristicId, buffer)
+					}, 800)
+				}, 1000)
+			},
+			sendCongifCmd(characteristicId, buffer) { //发送配网指令
+				let that = this
+
+				let count = Math.ceil(buffer.byteLength / 20)
+				//拆分成的ArrayBuffer数组,每个ArrayBuffer不超过20个字节
+				let buffers = []
+				for (var i = 0; i < count; i++) {
+					let size = Math.min(20, buffer.byteLength - i * 20)
+					let buf = buffer.slice(i * 20, size + i * 20)
+					buffers.push(buf)
+				}
+
+				//返回的是一个promise对象
+				function sendData(buffer) {
+					return uni.writeBLECharacteristicValue({
+						deviceId: that.deviceId,
+						serviceId: server_uuid,
+						characteristicId: characteristicId,
+						value: buffer,
+					})
+				};
+
+				(async () => {
+					for (let b of buffers) {
+						let res = await sendData(b)
+						//console.log(res);
+						console.log(`发送的数据为:${Buffer.from(b)} errorCode: ${res[res.length-1].errCode}`);
+					}
+					// let promises = buffers.map((b) => sendData(b))
+					// let res  = await Promise.all(promises)
+				})();
+			},
+			sendCongifCmd1(characteristicId, buffer) { //发送配网指令
+				let that = this
+				uni.writeBLECharacteristicValue({
+					deviceId: that.deviceId,
+					serviceId: server_uuid,
+					characteristicId: characteristicId,
+					value: buffer,
+					success(res) {
+						console.log(`发送数据成功 ${Buffer.from(buffer)}`);
+						console.log(res)
+					},
+					fail(err) {
+						console.log(`发送数据失败 ${Buffer.from(buffer)}`);
+						console.log(err)
+					}
+				})
+			},
+			sendWithoutWifiCommand(characteristicId) { //间隔2S发送WithoutWifi指令,直到设备有返回指令
+				let that = this
+
+				function sendWithoutWifi(uuid) {
+					let buf = Buffer.from('without_WiFi')
+					that.sendCongifCmd(characteristicId, buf.buffer)
+				}
+				if (that.withoutWifiInterval === undefined) {
+					that.withoutWifiInterval = setInterval(sendWithoutWifi, 2000, characteristicId)
+				}
+				sendWithoutWifi()
+			},
+			getWifiBuffer() {
+				let ssidBuf = Buffer.from(this.wifiName)
+				console.log(ssidBuf.length);
+
+				let pwdBuf = Buffer.from(this.wifiPwd)
+				console.log(pwdBuf.length);
+
+				let buffer = new ArrayBuffer(ssidBuf.length + pwdBuf.length + 6);
+				let dataView = new DataView(buffer);
+
+				dataView.setUint8(0, 0x22)
+				dataView.setUint8(1, ssidBuf.length + pwdBuf.length + 6)
+				dataView.setUint8(2, 0x33)
+				dataView.setUint8(3, ssidBuf.length)
+				let j = 0
+				for (var i = 4; i < ssidBuf.length + 4; ++i) {
+					dataView.setUint8(i, ssidBuf[j++])
+				}
+
+				dataView.setUint8(ssidBuf.length + 4, 0x44)
+				dataView.setUint8(ssidBuf.length + 5, pwdBuf.length)
+				j = 0
+				for (var i = ssidBuf.length + 6; i < ssidBuf.length + pwdBuf.length + 6; ++i) {
+					dataView.setUint8(i, pwdBuf[j++])
+				}
+				//打印arrayBuffer
+				// let buf = Buffer.from(buffer);
+				// console.log(buf);
+
+				//console.log(buffer.byteLength)
+				return buffer;
+			},
+			closeBLEConnection() {
+				let that = this
+				uni.closeBLEConnection({
+					deviceId: that.deviceId,
+					success(res) {
+						console.log('关闭连接成功');
+						console.log(res)
+					},
+					fail(err) {
+						console.log(err)
+						console.log('关闭连接失败');
+					}
+				})
+				clearInterval(that.withoutWifiInterval)
+				clearTimeout(that.configTimeout)
+				that.connected = false
+				that.showLoading = false
+			}
+
+		}
+	}
+</script>
+
+<style>
+	.box {
+		display: flex;
+		flex-direction: column;
+		width: 100vw;
+		height: 100vh;
+	}
+
+
+
+	.content {
+		display: flex;
+		flex-direction: column;
+
+		/* align-items: center;
+	justify-content: center; */
+	}
+
+	.input-row {
+		display: flex;
+		margin-top: 10rpx;
+		flex-direction: row;
+		align-items: center;
+	}
+
+	.loadView {
+		display: flex;
+		flex-direction: column;
+		align-self: center;
+		align-items: center;
+		/* margin-top: 35vh; */
+	}
+
+	.edittext {
+		display: flex;
+		width: 60vw;
+		height: 50rpx;
+
+		border: 1rpx solid #333333;
+	}
+
+	.rotateAnim {
+		width: 60rpx;
+		height: 60rpx;
+		animation: turn 1200ms linear infinite;
+	}
+
+	@keyframes turn {
+		0% {
+			transform: rotate(0deg);
+		}
+
+		50% {
+			transform: rotate(180deg);
+		}
+
+		100% {
+			transform: rotate(360deg);
+		}
+	}
+</style>

+ 262 - 0
pages/ble/ScanBleDevice.vue

@@ -0,0 +1,262 @@
+<template>
+	<view class="content">
+		<view class="loadView" v-if="showLoading">
+			<image class="rotateAnim" src="../../static/loading.svg"></image>
+			<text style="margin-left: 10rpx;">{{loadingText}}</text>
+		</view>
+		<view v-for="device in scanDeviceList" v-bind:key='device.mac' @click="gotoConnect(device)">
+			<text>设备名称:{{device.name}} \n Mac:{{device.mac}}</text>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				isStartScan: false, //是否开始ble扫描
+				stopScanTimer: undefined, //超时停止扫描的对象
+				scanDeviceList: [], //扫描到的设备列表
+				loadingText: '正在扫描设备', //load框的文字提示
+				showLoading: false, //是否显示loading
+				isOpenSetting:false,//是否跳转小程序设置界面
+			}
+		},
+		onLoad() {
+			//this.authorize()
+			this.init()
+		},
+		onShow() {
+			if(this.isOpenSetting){//如果为true就是从微信小程序设置返回到该页面,重新获取权限。
+				this.isOpenSetting = false
+				this.authorize()
+			}
+		},
+		onBackPress() {
+			console.log('onBackPress');
+			this.stopScan()
+		},
+		methods: {
+			init() {
+				let that = this
+				//初始化蓝牙模块
+				uni.openBluetoothAdapter({
+					success(res) {
+						console.log(`初始化蓝牙模块成功 ${res}`)
+						//开始扫描设备
+						that.startBtScan();
+					},
+					fail(error) {
+						if (error.errCode === 10001) { //在用户蓝牙开关未开启或者手机不支持蓝牙功能的情况下
+							uni.showModal({
+								title: '提示',
+								content: '请打开蓝牙!',
+								success: function(res) {
+									if (res.confirm) {
+										console.log('用户点击确定');
+										uni.showToast({
+											title: '请打开手机蓝牙以便扫描设备',
+											icon: 'none',
+										})
+										that.showLoading = true;
+										that.loadingText = '等待开启蓝牙。。。'
+										that.lisenBtState()
+									} else if (res.cancel) {
+										console.log('用户点击取消');
+										uni.navigateBack({});
+									}
+								}
+							});
+						} else { //其他错误
+							uni.showToast({
+								title: `查看手机蓝牙是否打开`,
+								duration: 1000,
+								icon: "none"
+							})
+							console.log(error)
+						}
+					},
+				})
+			},
+			lisenBtState() { //监听蓝牙是否打开
+				let that = this;
+				uni.onBluetoothAdapterStateChange(function(res) {
+					console.log('onBluetoothAdapterStateChange')
+					console.log(res)
+					if (res.available) { //蓝牙已打开
+						//开始扫描设备
+						that.startBtScan();
+					}
+				})
+			},
+			startBtScan() { //开始扫描蓝牙设备
+				let that = this
+				that.showLoading = true;
+				that.loadingText = '正在扫描设备。。。'
+				let count = that.scanDeviceList.length
+				that.scanDeviceList.splice(0, count);
+				//清除停止扫描的定时器
+				clearTimeout(that.stopScanTimer)
+
+
+				//开始扫描
+				uni.startBluetoothDevicesDiscovery({
+					success(res) {
+						that.isStartScan = true
+						console.log('开始扫描蓝牙设备');
+						console.log(res);
+						//30秒之后停止扫描
+						that.stopScanTimer = setTimeout(() => {
+							//停止扫描
+							uni.stopBluetoothDevicesDiscovery({
+								complete() {
+									that.isStartScan = false
+									that.showLoading = false;
+									if(that.scanDeviceList.length == 0){
+										uni.showModal({
+											title: '提示',
+											content: '沒有扫描到设备,是否重试!',
+											success: function(res) {
+												if (res.confirm) {
+													console.log('重试!');
+													that.startBtScan()
+												} else if (res.cancel) {
+													uni.navigateBack({});
+												}
+											}
+										});
+									}
+								}
+							})
+						}, 30 * 1000);
+						uni.onBluetoothDeviceFound(function(res) {
+							//console.log('new device list has founded')
+							console.log(res)
+							console.log(res.devices)
+
+							let devices = res.devices
+							for (var i = 0; i < devices.length; i++) {
+								console.log(devices[i])
+								console.log(`名称:${devices[i].name}, mac : ${devices[i].deviceId}`)
+								if (devices[i].name !== undefined && devices[i].name !== null && 
+								devices[i].name !== '' && devices[i].name.startsWith('AIrSMArT')) {
+									that.scanDeviceList.push({
+										name: devices[i].name,
+										mac: devices[i].deviceId
+									})
+								}
+							}
+						})
+					},
+					fail(err) {
+						console.log(err)
+					}
+				})
+			},
+			gotoConnect(device) {
+				this.stopScan()
+				uni.navigateTo({
+					url: `./ConnectBleDevice?name=${device.name}&mac=${device.mac}`
+				})
+			},
+			stopScan() {
+				//if(this.isStartScan){
+					uni.stopBluetoothDevicesDiscovery({})
+					clearTimeout(this.stopScanTimer)
+					this.isStartScan = false
+				//}
+				
+			},
+			authorize(){
+				let that = this
+				// #ifdef MP-WEIXIN
+				//uni.wx.getSetting()
+				//uni.getUserPr
+				//微信的定位授权
+				uni.getSetting({
+					success(res) {
+						console.log("获取setting");
+						console.log(res);
+						if(!res.authSetting['scope.userLocation']){
+							uni.authorize({
+								  scope: 'scope.userLocation',
+								  success() {
+								  	console.log("获取到定位权限成功");
+									//初始化蓝牙模块
+									that.init()
+								  },
+								  fail(err){
+									  console.log("获取到定位权限失败");
+									  console.log(err)
+									  uni.showModal({
+									  	title:'未授权',
+										content:'需要打开小程序设置,授权定位权限,来扫描蓝牙设备',
+										success() {
+											uni.openSetting({
+												success() {
+													that.isOpenSetting = true
+													console.log('openSetting 成功')
+												}
+											})
+										},
+										fail() {
+											uni.navigateBack({})
+										}
+									  })
+								  }
+							})
+						}else{
+							//初始化蓝牙模块
+							that.init()
+						}
+					}
+				})
+				
+				// #endif
+				
+			}
+		}
+
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		width: 100vw;
+		height: 100vh;
+		flex-direction: column;
+		align-items: center;
+		/* justify-content: center; */
+
+	}
+
+	.loadView {
+		display: flex;
+		flex-direction: row;
+		align-items: center;
+		/* margin-top: 35vh; */
+		/* justify-content: center; */
+	}
+
+	.rotateAnim {
+		width: 60rpx;
+		height: 60rpx;
+		animation: turn 1200ms linear infinite;
+	}
+
+	@keyframes turn {
+		0% {
+			transform: rotate(0deg);
+		}
+
+		50% {
+			transform: rotate(180deg);
+		}
+
+		100% {
+			transform: rotate(360deg);
+		}
+	}
+</style>

+ 77 - 0
pages/index/device.vue

@@ -0,0 +1,77 @@
+<template>
+	<view class="content">
+		<button @click="godoBle">添加设备</button>
+		
+		<button @click="test1">test1</button>
+	</view>
+</template>
+
+<script>
+	import {common} from '../../proto/bundle.js';
+	
+	
+	export default {
+		data() {
+			return {
+
+			}
+		},
+		onLoad() {
+
+		},
+		methods: {
+			test1(){
+				let errinfo = common.ErrorInfo.create({
+					errorCode: 0,
+					errorMessage:Buffer.from('成功')
+				})
+				
+				//把ErrorInfo对象 编码成Uint8Array (browser) or Buffer (node)
+				let errBuffer = common.ErrorInfo.encode(errinfo).finish()
+				
+				//把Uint8Array (browser) 或者 Buffer (node) 解码成ErrorInfo对象
+				let message = common.ErrorInfo.decode(errBuffer)
+				
+				//转化为一个对象
+				let obj = common.ErrorInfo.toObject(message, {
+					enums: String, // enums as string names
+					longs: String, // longs as strings (requires long.js)
+					//bytes: String,
+				})
+				console.log(obj);
+				let buf = Buffer.from(obj.errorMessage)
+				console.log(`errorMessage = ${buf.toString()}`);
+				// let buf = Buffer.from(obj.errorMessage)
+				// console.log(buf);
+				// console.log(buf.toString());
+			},
+			godoBle() { //添加设备
+				// #ifdef MP-WEIXIN||APP-PLUS
+				uni.navigateTo({
+					url: '../ble/ScanBleDevice'
+				})
+				// #endif
+				// #ifdef H5
+				uni.navigateTo({
+					url: '../ble/ConnectBleDevice'
+				})
+				// uni.showToast({
+				// 	title:'H5页面不支持扫描设备',
+				// 	icon:'none'
+				// })
+				// #endif
+
+			}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		margin: 20rpx;
+	}
+</style>

+ 205 - 0
pages/index/index.vue

@@ -0,0 +1,205 @@
+<template>
+	<view class="content">
+		<view>
+			<button type="primary" @click="connectOrDisconnect">{{connectText}}</button>
+		</view>
+		<!-- <image class="logo" src="/static/logo.png"></image> -->
+		<text class="title">发布消息</text>
+		<view class="br"></view>
+		<view class="text-area">
+			<text>Topic</text>
+			<input class="edittext" v-model="publishTopic" />
+		</view>
+		<view class="br"></view>
+		<view class="text-area">
+			<text>Payload</text>
+			<textarea class="edittext" v-model="publishMsg" />
+		</view>
+		<view class="br"></view>
+		<button @click="publish">publish</button>
+
+		<text class="title">订阅主题</text>
+		<view class="text-area">
+			<!-- 订阅 -->
+			<input class="edittext" v-model="subscribeTopic" />
+		</view>
+		<view class="br"></view>
+		<button @click="subscribe">subscribe</button>
+		<view class="br"></view>
+		<text class="title">收到的Message</text>
+		<view class="br"></view>
+		<view v-for="(item, index) in subList" :key="index">
+			<text>Topic:{{ item.topic }} ,Payload: {{ item.msg }}</text>
+		</view>
+		<view>
+
+		</view>
+	</view>
+</template>
+
+<script>
+	//import store from '@/store/index.js';//需要引入store
+	export default {
+		data() {
+			return {
+				publishMsg: "",
+				publishTopic: "",
+				subscribeTopic: "",
+			}
+		},
+		onLoad() {
+			console.log('index.vue onload')
+		},
+		created() {
+			console.log('index.vue created')
+		},
+		computed: {
+			connectText() {
+				return (this.$store.getters['moduleMqtt/connected']) ? '断开' : '连接'
+			},
+			subList() {
+				return this.$store.state.moduleMqtt.msgList
+			},
+		},
+		methods: {
+			publish() { //发布
+				let that = this;
+				//发布消息
+				that.$store.dispatch({
+					type: 'moduleMqtt/publish',
+					topic: that.publishTopic,
+					payload: that.publishMsg,
+				}).then(() => {/* success */}, (err) =>{
+					console.log(err);
+					uni.showToast({
+						title: "发布消息失败",
+						duration: 2000,
+						icon: "none"
+					});
+				});
+			},
+			subscribe() { //订阅
+				let that = this;
+				//订阅消息
+				that.$store.dispatch('moduleMqtt/subscribe', that.subscribeTopic)
+					.then(() => {
+							uni.showToast({
+								title: `订阅${that.subscribeTopic}主题成功`,
+								duration: 2000,
+								icon: "none"
+							})
+						},
+						(err) => {
+							uni.showToast({
+								title: `订阅${that.subscribeTopic}主题失败`,
+								duration: 2000,
+								icon: "none"
+							})
+						});
+			},
+			connectOrDisconnect() { //连接或者断开mqtt服务
+				if (this.$store.getters['moduleMqtt/connected'] === false) {
+					//连接mqtt服务器
+					this.$store.commit('moduleMqtt/connect')
+				} else {
+					//断开连接
+					this.$store.commit('moduleMqtt/disconnect')
+				}
+			},
+			connectWithWxApi(){
+				uni.connectSocket({
+					url:'wss://mqtt.test.radio1964.com',
+					success(res){
+						console.log('连接成功');
+					},
+					fail(err){
+						console.log('连接失败');
+					}
+				});
+				uni.onSocketOpen(function(res){
+					console.log('onSocketOpen');
+					console.log(res);
+				})
+				uni.onSocketClose(function(res){
+					console.log('onSocketClose');
+					console.log(res);
+				})
+				uni.onSocketError(function(err){
+					console.log('err');
+					console.log(err);
+					
+				})
+			},
+			closeSocket(){
+				uni.closeSocket({
+					success() {
+						console.log("closeSocket success");
+					},
+					fail() {
+						console.log("closeSocket fail");
+					}
+				})
+			},
+			godoBle() { //添加设备
+				// #ifdef MP-WEIXIN||APP-PLUS
+				uni.navigateTo({
+					url: '../ble/ScanBleDevice'
+				})
+				// #endif
+				// #ifdef H5
+				uni.navigateTo({
+					url: '../ble/ConnectBleDevice'
+				})
+				// uni.showToast({
+				// 	title:'H5页面不支持扫描设备',
+				// 	icon:'none'
+				// })
+				// #endif
+
+			}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		/* align-items: center; */
+		justify-content: center;
+		margin: 20rpx;
+	}
+
+	.logo {
+		height: 200rpx;
+		width: 200rpx;
+		margin-top: 200rpx;
+		margin-left: auto;
+		margin-right: auto;
+		margin-bottom: 50rpx;
+	}
+
+	.br {
+		height: 10rpx;
+	}
+
+	.text-area {
+		display: flex;
+		/* justify-content: center; */
+	}
+
+	.title {
+		font-size: 30rpx;
+		color: #2c3e50;
+		margin: 5rpx;
+	}
+
+	textarea {
+		height: 50rpx;
+	}
+
+	.edittext {
+		display: flex;
+		border: 1rpx solid #333333;
+	}
+</style>

+ 197 - 0
pages/index/index_backoff1.vue

@@ -0,0 +1,197 @@
+<template>
+	<view class="content">
+		<view>
+			<button type="primary" @click="connectOrDisconnect">{{connectText}}</button>
+			<button @click="godoBle">添加设备</button>
+		</view>
+		<!-- <image class="logo" src="/static/logo.png"></image> -->
+		<text class="title">发布消息</text>
+		<view class="br"></view>
+		<view class="text-area">
+			<text>Topic</text>
+			<input class="edittext" v-model="publishTopic" />
+		</view>
+		<view class="br"></view>
+		<view class="text-area">
+			<text>Payload</text>
+			<textarea class="edittext" v-model="publishMsg" />
+		</view>
+		<view class="br"></view>
+		<button @click="publish">publish</button>
+
+		<text class="title">订阅主题</text>
+		<view class="text-area">
+			<!-- 订阅 -->
+			<input class="edittext" v-model="subscribeTopic" />
+		</view>
+		<view class="br"></view>
+		<button @click="subscribe">subscribe</button>
+		<view class="br"></view>
+		<text class="title">收到的Message</text>
+		<view class="br"></view>
+		<view v-for="(item, index) in subList" :key="index">
+			<text>Topic:{{ item.topic }} ,Payload: {{ item.msg }}</text>
+		</view>
+		<view>
+
+		</view>
+	</view>
+</template>
+
+<script>
+	//import store from '@/store/index.js';//需要引入store
+	
+	
+
+	export default {
+		data() {
+			return {
+
+				publishMsg: "",
+				publishTopic: "",
+				subscribeTopic: "",
+				subList: [],
+			}
+		},
+		onLoad() {
+			console.log('index.vue onload')
+			this.init();
+
+		},
+		created() {
+			//this.init();
+		},
+		computed: {
+			connectText() {
+				return (this.$store.getters.isConnect) ? '断开' : '连接'
+			}
+		},
+		methods: {
+			publish() {
+				//发布消息
+				if (this.$store.getters.isConnect) {
+					this.$store.state.mqttClient.publish(this.publishTopic, this.publishMsg, (err) => {
+						if (err) {
+							//alert(`发布消息失败`);
+							uni.showToast({
+								title: "发布消息失败",
+								duration: 2000,
+								icon: "none"
+							})
+						}
+					});
+				}
+			},
+			subscribe() {
+				//console.log(this.$root)
+				console.log(`subscribe isconnect = ${this.$store.getters.isConnect}`)
+				if (this.$store.getters.isConnect) {
+					//订阅消息
+					this.$store.state.mqttClient.subscribe(this.subscribeTopic, (err) => {
+						console.log(err);
+						if (!err) {
+							uni.showToast({
+								title: `订阅${this.subscribeTopic}主题成功`,
+								duration: 2000,
+								icon: "none"
+							})
+						} else {
+							uni.showToast({
+								title: `订阅${this.subscribeTopic}主题失败`,
+								duration: 2000,
+								icon: "none"
+							})
+						}
+					});
+				}
+			},
+			init() {
+				//连接mqtt服务器
+				//this.$store.commit('connect')
+				//this.mqttListen()
+			},
+			mqttListen() {
+				const that = this;
+				if (this.$store.state.mqttClient !== undefined) {
+					//收到消息的回调
+					this.$store.state.mqttClient.on("message", function(topic, message) {
+						console.log(`message = ${message.toString()}`);
+						that.subList.push({
+							topic: topic.toString(),
+							msg: message.toString(),
+						});
+					});
+				}
+			},
+			connectOrDisconnect() { //连接或者断开mqtt服务
+				if (this.$store.getters.isConnect === false) {
+					//连接mqtt服务器
+					this.$store.commit('connect')
+					this.mqttListen()
+				} else {
+					//断开连接
+					this.$store.commit('disconnect')
+				}
+			},
+			godoBle() { //添加设备
+				// #ifdef MP-WEIXIN||APP-PLUS
+				uni.navigateTo({
+					url: '../ble/ScanBleDevice'
+				})
+				// #endif
+				// #ifdef H5
+				uni.navigateTo({
+					url: '../ble/ConnectBleDevice'
+				})
+				// uni.showToast({
+				// 	title:'H5页面不支持扫描设备',
+				// 	icon:'none'
+				// })
+				// #endif
+
+			}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		/* align-items: center; */
+		justify-content: center;
+		margin: 20rpx;
+	}
+
+	.logo {
+		height: 200rpx;
+		width: 200rpx;
+		margin-top: 200rpx;
+		margin-left: auto;
+		margin-right: auto;
+		margin-bottom: 50rpx;
+	}
+
+	.br {
+		height: 10rpx;
+	}
+
+	.text-area {
+		display: flex;
+		/* justify-content: center; */
+	}
+
+	.title {
+		font-size: 20rpx;
+		color: #2c3e50;
+	}
+
+	textarea {
+		height: 50rpx;
+	}
+
+	.edittext {
+		display: flex;
+		border: 1rpx solid #333333;
+	}
+</style>

+ 185 - 0
pages/index/indexbackoff.vue

@@ -0,0 +1,185 @@
+<template>
+	<view class="content">
+		<!-- <image class="logo" src="/static/logo.png"></image> -->
+		<text class="title">发布消息</text>
+		<view class="br"></view>
+		<view class="text-area">
+			<text>Topic</text>
+			<input class="edittext" v-model="publishTopic" />
+		</view>
+		<view class="br"></view>
+		<view class="text-area">
+			<text>Payload</text>
+			<textarea class="edittext" v-model="publishMsg" />
+		</view>
+		<view class="br"></view>
+		<button @click="publish">publish</button>
+
+		<text class="title">订阅主题</text>
+		<view class="text-area">
+			<!-- 订阅 -->
+			<input class="edittext" v-model="subscribeTopic" />
+		</view>
+		<view class="br"></view>
+		<button @click="subscribe">subscribe</button>
+		<view class="br"></view>
+		<text class="title">收到的Message</text>
+		<view class="br"></view>
+		<view v-for="(item, index) in subList" :key="index">
+			<text>Topic:{{ item.topic }} ,Payload: {{ item.msg }}</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	import mqtt from 'mqtt/dist/mqtt.js'
+
+	//let mqtt = require('mqtt/dist/mqtt.js')
+
+	const options = {
+		connectTimeout: 4000, // 超时时间
+		clientId: "mqtt_" + parseInt(Math.random() * 100 + 800, 10),
+		port: 8081,
+	};
+	
+	// #ifdef H5
+	let url = 'wss://test.mosquitto.org'
+	// #endif
+	// #ifdef MP-WEIXIN||APP-PLUS
+	let url = 'wxs://test.mosquitto.org'
+	// #endif
+
+	let client = mqtt.connect(url, options)
+
+
+
+	export default {
+		data() {
+			return {
+				publishMsg: "",
+				publishTopic: "",
+				subscribeTopic: "",
+				subList: [],
+				isConnect: false,
+			}
+		},
+		onLoad() {
+			//this.init();
+		},
+		created() {
+			this.init();
+		},
+		methods: {
+			publish() {
+				//发布消息
+				if (this.isConnect) {
+					client.publish(this.publishTopic, this.publishMsg, (err) => {
+						if (err) {
+							//alert(`发布消息失败`);
+							uni.showToast({
+								title: "发布消息失败",
+								duration: 2000,
+								icon: "none"
+							})
+						}
+					});
+				}
+			},
+			subscribe() {
+				if (this.isConnect) {
+					client.subscribe(this.subscribeTopic, (err) => {
+						if (!err) {
+							uni.showToast({
+								title: `订阅${this.subscribeTopic}主题成功`,
+								duration: 2000,
+								icon: "none"
+							})
+						} else {
+							uni.showToast({
+								title: `订阅${this.subscribeTopic}主题失败`,
+								duration: 2000,
+								icon: "none"
+							})
+						}
+					});
+				}
+			},
+			init() {
+				const that = this;
+
+				//连接成功回调
+				client.on("connect", function() {
+					that.isConnect = true
+					console.log("connect success!");
+				});
+
+				//异常回调
+				client.on("error", function(err) {
+					that.isConnect = false
+					console.log(err);
+				});
+				
+				client.on("offline", function() {
+					that.isConnect = false
+					console.log("offline");
+				});
+				
+				client.on("close", function() {
+					that.isConnect = false
+					console.log("close");
+				});
+
+				//收到消息的回调
+				client.on("message", function(topic, message) {
+					console.log(`message = ${message.toString()}`);
+					that.subList.push({
+						topic: topic.toString(),
+						msg: message.toString(),
+					});
+				});
+			},
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		/* align-items: center; */
+		justify-content: center;
+		margin: 20rpx;
+	}
+
+	.logo {
+		height: 200rpx;
+		width: 200rpx;
+		margin-top: 200rpx;
+		margin-left: auto;
+		margin-right: auto;
+		margin-bottom: 50rpx;
+	}
+
+	.br {
+		height: 10rpx;
+	}
+
+	.text-area {
+		display: flex;
+		/* justify-content: center; */
+	}
+
+	.title {
+		font-size: 20rpx;
+		color: #2c3e50;
+	}
+
+	textarea {
+		height: 50rpx;
+	}
+
+	.edittext {
+		display: flex;
+		border: 1rpx solid #333333;
+	}
+</style>

+ 163 - 0
pages/index/mine.vue

@@ -0,0 +1,163 @@
+<template>
+	<view class="content">
+		<button type="default" open-type="getPhoneNumber" @getphonenumber="decryptPhoneNumber"
+			@click="login">微信授权一键登录</button>
+
+		<button @click="httpTest" style="margin-top: 10rps;">h5 axios http请求</button>
+
+		<button @click="wxHttpTest" style="margin-top: 10rps;">miniprogram http请求</button>
+	</view>
+</template>
+
+<script>
+	import {common,	user} from '../../proto/proto.js';
+	//const protobuf = require("protobufjs/minimal");
+	const axios = require('axios');
+
+	let loginMessage = user.login_req.create({
+		phone: Buffer.from('1234567890'),
+		type: 1,
+		verifyInfo: Buffer.from('123456a'),
+	})
+	//proto对象转buffer
+	let buffer = user.login_req.encode(loginMessage).finish()
+	console.log(buffer);
+	let requestMessage = common.MsgWebsocket.create({
+		version: 1,
+		app: 1,
+		server: 2,
+		servant: 1005,
+		data: buffer,
+	})
+	let requestBuffer = common.MsgWebsocket.encode(requestMessage).finish()
+	console.log(requestBuffer);
+	
+	let typeStr = Object.prototype.toString.call(requestBuffer.buffer)
+	console.log(typeStr);
+	export default {
+		data() {
+			return {
+
+			}
+		},
+		methods: {
+			login() {
+
+			},
+			decryptPhoneNumber(e) {
+				console.log(e.detail.errMsg)
+				console.log(e.detail.iv)
+				console.log(e.detail.encryptedData)
+			},
+			wxHttpTest() {
+				let typeStr = Object.prototype.toString.call(requestBuffer.buffer)
+				console.log(typeStr);
+				let requestArrayBuf = new Uint8Array([...requestBuffer]).buffer
+				uni.request({
+					//url:'https://test.api1.radio1964.com:80/Ohplay/Web/HttpToTcp'
+					url: 'http://60.205.190.38:80/Ohplay/Web/HttpToTcp',
+					header: {
+						"X-Requested-With": "XMLHttpRequest",
+						"Content-Type": "application/x-protobuf",
+						'Token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VyaWQiOjY1MDYzMywibmFtZSI6IjE4MTI2NDQ3MDE1IiwiZXhwIjoxNjIyODg0NTA1fQ.f7jIm0856-VnynA99MBoA2Dl1pePxI0HT_ECsUp5QHA'
+					},
+					method: 'POST',
+					timeout: 15000,
+					dataType: 'protobuf',
+					responseType: 'arraybuffer',
+					data: requestArrayBuf
+				}).then((res) => {
+					console.log(res);
+					for (let response of res) {
+						if (response !== null && response !== undefined && response.statusCode === 200) {
+							try {
+								let enc = new TextDecoder('utf-8')
+								let res = JSON.parse(enc.decode(new Uint8Array(response.data))) //转化成json对象
+								console.log(res);
+							} catch (e) {
+								//let resBuf = protobuf.util.newBuffer(response.data)
+								let resBuf = Buffer.from(response.data)
+								//console.log(resBuf.toString());
+								let resMessage = common.MsgWebsocket.decode(resBuf)
+								console.log(resMessage);
+								let loginRspBuf = resMessage.data
+								console.log(loginRspBuf);
+								let loginRspMessage = user.login_rsp.decode(loginRspBuf)
+								console.log(loginRspMessage);
+								let obj = user.login_rsp.toObject(loginRspMessage, {
+									longs: String,
+									enums: String,
+								})
+								console.log(obj);
+								console.log(`errorMessage = ${Buffer.from(loginRspMessage.errInfo.errorMessage).toString()}`);
+								//console.log(Buffer.from(loginRspMessage.errInfo.errorMessage).toString());
+							}
+						}
+					}
+				}, (err) => {
+					console.log(err);
+				});
+			},
+			httpTest() { //测试http请求
+				// let blob = new Blob([requestBuffer], {
+				// 	type: 'buffer'
+				// });
+				// 将请求数据encode成二进制,encode是proto.js提供的方法
+				function transformRequest(data) {
+				  return common.MsgWebsocket.encode(requestMessage).finish()
+				}
+				axios.create({
+						timeout: 15000,
+						method: 'post',
+						headers: {
+							"X-Requested-With": "XMLHttpRequest",
+							"Content-Type": "application/octet-stream",
+							//'Token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VyaWQiOjY1MDYzMywibmFtZSI6IjE4MTI2NDQ3MDE1IiwiZXhwIjoxNjIyODg0NTA1fQ.f7jIm0856-VnynA99MBoA2Dl1pePxI0HT_ECsUp5QHA'
+						},
+						responseType: 'arraybuffer',
+
+					})
+					.post('http://60.205.190.38:80/Ohplay/Web/HttpToTcp', requestMessage,{
+						 transformRequest:transformRequest
+					})
+					.then((response) => {
+						console.log(response);
+						if (response.status === 200 /* && response.data.byteLength>0*/ ) {
+							try {
+								let enc = new TextDecoder('utf-8')
+								let res = JSON.parse(enc.decode(new Uint8Array(response.data))) //转化成json对象
+								console.log(res);
+							} catch (e) {
+
+								//let resBuf = protobuf.util.newBuffer(response.data)
+								let resBuf = Buffer.from(response.data)
+								console.log(resBuf.toString());
+								let resMessage = common.MsgWebsocket.decode(resBuf)
+								let loginRspBuf = resMessage.data
+								let loginRspMessage = user.login_rsp.decode(loginRspBuf)
+								let obj = user.login_rsp.toObject(loginRspMessage, {
+									longs: String,
+									enums: String,
+								})
+								console.log(obj);
+								console.log(`errorMessage = ${Buffer.from(loginRspMessage.errInfo.errorMessage).toString()}`);
+							}
+						}
+					}, (err) => {
+						console.log(err);
+					});
+
+			}
+		}
+	}
+</script>
+
+<style>
+	.content {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		margin: 20rpx;
+	}
+</style>

+ 30 - 0
proto/Common.proto

@@ -0,0 +1,30 @@
+syntax = "proto3";
+
+package common;
+
+message ErrorInfo {
+    fixed32 errorCode = 1; //错误码
+    bytes errorMessage = 2; //错误描述信息
+}
+
+message AwesomeMessage{
+    fixed32 version     = 1; 
+    fixed32 app         = 2; 
+    fixed32 server      = 3;
+    fixed32 servant     = 4; 
+    bytes data          = 9;
+}
+
+
+message MsgWebsocket {
+    fixed32 version     = 1;               ///< 协议版本号
+    fixed32 app         = 2;               ///<应用名称
+    fixed32 server      = 3;               ///<应用内具体业务模块
+    fixed32 servant     = 4;               ///<业务模块内部具体接
+    fixed32 seq         = 5;               ///< 序列号
+    fixed32 route_id    = 6;              ///<负载均衡路由字段
+    fixed32 encrypt     = 7;              ///<加密方式,0:oaep(rsa v2),1:pkcs1(rsa v1.5)
+    fixed32 cache_is    = 8;              ///<1用缓存,2不用缓存
+    bytes data          = 9;
+}
+

+ 25 - 0
proto/User.proto

@@ -0,0 +1,25 @@
+syntax = "proto3";
+
+package user;
+
+import "Common.proto";
+
+//登录请求
+message login_req
+{
+    bytes  phone            = 1;
+    uint32 type             = 2;//登录类型,1:密码登录,2:验证码登录
+    bytes  verify_info      = 3;//1密码,2验证码
+    int64  time				= 4;//纳秒,游客登陆用的
+    uint32 system_type      = 5;//系统类型,0全部,1:ios,2:android
+
+}
+
+//登录响应
+message login_rsp {
+    ErrorInfo errInfo  = 1; // 错误码信息
+    uint32   id       = 2;
+    bytes    token    = 3; // 访问令牌
+    bytes sskey       = 4;
+}
+

File diff suppressed because it is too large
+ 1498 - 0
proto/bundle.js


File diff suppressed because it is too large
+ 1213 - 0
proto/proto.js


+ 124 - 0
proto/proto.json

@@ -0,0 +1,124 @@
+{
+  "nested": {
+    "common": {
+      "nested": {
+        "ErrorInfo": {
+          "fields": {
+            "errorCode": {
+              "type": "fixed32",
+              "id": 1
+            },
+            "errorMessage": {
+              "type": "bytes",
+              "id": 2
+            }
+          }
+        },
+        "MsgHead": {
+          "fields": {
+            "bodyLen": {
+              "type": "fixed32",
+              "id": 1
+            },
+            "version": {
+              "type": "fixed32",
+              "id": 2
+            },
+            "app": {
+              "type": "fixed32",
+              "id": 3
+            },
+            "server": {
+              "type": "fixed32",
+              "id": 4
+            },
+            "servant": {
+              "type": "fixed32",
+              "id": 5
+            },
+            "seq": {
+              "type": "fixed32",
+              "id": 6
+            },
+            "routeId": {
+              "type": "fixed32",
+              "id": 7
+            },
+            "encrypt": {
+              "type": "fixed32",
+              "id": 8
+            }
+          }
+        },
+        "MsgBody": {
+          "fields": {
+            "body": {
+              "type": "bytes",
+              "id": 1
+            },
+            "extend": {
+              "type": "bytes",
+              "id": 2
+            }
+          }
+        },
+        "audio_type": {
+          "values": {
+            "AUDIO_TYPE_MIN": 0,
+            "MUSIC_BROADCAST": 1,
+            "RADIO_BROADCAST": 2,
+            "MUSIC_UNICAST": 3,
+            "RADIO_UNICAST": 4,
+            "MUSIC_PROGRAM": 5,
+            "RADIO_PROGRAM": 6,
+            "RADIO_SELECTION": 7
+          }
+        }
+      }
+    },
+    "user": {
+      "nested": {
+        "user_info_get_req": {
+          "fields": {
+            "id": {
+              "type": "uint32",
+              "id": 1
+            }
+          }
+        },
+        "user_info_get_rsp": {
+          "fields": {
+            "errInfo": {
+              "type": "ErrorInfo",
+              "id": 1
+            },
+            "nickname": {
+              "type": "bytes",
+              "id": 2
+            },
+            "address": {
+              "type": "bytes",
+              "id": 3
+            },
+            "headUrl": {
+              "type": "bytes",
+              "id": 4
+            },
+            "constellation": {
+              "type": "uint32",
+              "id": 5
+            },
+            "audioDuration": {
+              "type": "uint32",
+              "id": 6
+            },
+            "credits": {
+              "type": "uint32",
+              "id": 7
+            }
+          }
+        }
+      }
+    }
+  }
+}

BIN
static/image/device.png


BIN
static/image/device_red.png


BIN
static/image/iot.png


BIN
static/image/iot_org.png


BIN
static/image/mine.png


BIN
static/image/mine_pressed.png


File diff suppressed because it is too large
+ 1 - 0
static/loading.svg


BIN
static/logo.png


+ 102 - 0
store/index-backoff.js

@@ -0,0 +1,102 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import mqtt from 'mqtt/dist/mqtt.js'
+
+Vue.use(Vuex); //vue的插件机制
+
+const options = {
+	clientId: "mqtt_" + parseInt(Math.random() * 100 + 800, 10),
+	port: 8081,
+	keepalive: 20,
+	clean: true,
+	reconnectPeriod: 0,
+	connectTimeout: 30 * 1000,
+	protocolId: 'MQTT',
+	protocolVersion: 4
+};
+
+// #ifdef H5
+let url = 'wss://test.mosquitto.org'
+// #endif
+// #ifdef MP-WEIXIN||APP-PLUS
+let url = 'wxs://test.mosquitto.org'
+// #endif
+
+//Vuex.Store 构造器选项
+const store = new Vuex.Store({
+	state: { //存放状态
+		mqttClient: undefined,
+	},
+	getters:{
+		isConnect:state =>{
+			return state.mqttClient !== undefined && state.mqttClient.connected
+		}
+	},
+	mutations: {
+		connect(state) {
+			console.log('connect')
+			if (state.mqttClient === undefined) {
+				state.mqttClient = mqtt.connect(url, options)
+				//连接成功回调
+				state.mqttClient.on("connect", function() {
+					console.log("connect success!");
+				});
+
+				//异常回调
+				state.mqttClient.on("error", function(err) {
+					console.log(err);
+				});
+				
+				state.mqttClient.on('offline', function() {
+					console.log(`offline callback connected = ${state.mqttClient.connected}`);
+				})
+
+				state.mqttClient.on('end', function() {
+					console.log(`end callback connected = ${state.mqttClient.connected}`);
+				})
+
+				state.mqttClient.on('close', function() {
+					console.log(`close callback connected = ${state.mqttClient.connected}`);
+					//console.log(state.mqttClient.connected);
+				})
+
+			} else {
+				if (state.isConnect === false) {
+					state.mqttClient.reconnect()
+					//重连成功的回调
+					state.mqttClient.on("reconnect", function() {
+						state.isConnect = true;
+						console.log("reconnect success!");
+					});
+				}
+			}
+
+
+		},
+		disconnect(state) {
+			console.log('disconnect')
+			if (state.mqttClient !== undefined) {
+
+				state.mqttClient.end(true)
+				//state.mqttClient = undefined
+				
+				console.log(state.mqttClient.connected);
+
+			}
+		}
+		
+		// stream.removeListener('complete', onfinish);
+		// stream.removeListener('abort', onclose);
+		// stream.removeListener('request', onrequest);
+		// if (stream.req) stream.req.removeListener('finish', onfinish);
+		// stream.removeListener('end', onlegacyfinish);
+		// stream.removeListener('close', onlegacyfinish);
+		// stream.removeListener('finish', onfinish);
+		// stream.removeListener('exit', onexit);
+		// stream.removeListener('end', onend);
+		// stream.removeListener('error', onerror);
+		// stream.removeListener('close', onclose);
+
+	}
+})
+export default store

+ 14 - 0
store/index.js

@@ -0,0 +1,14 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import moduleMqtt from '@/store/modules/moduleMqtt'
+
+Vue.use(Vuex); //vue的插件机制
+
+
+//Vuex.Store 构造器选项
+const store = new Vuex.Store({
+	modules:{
+		moduleMqtt,//封装了mqtt模块
+	}
+})
+export default store

+ 148 - 0
store/modules/moduleMqtt.js

@@ -0,0 +1,148 @@
+import mqtt from '../../common/mqtt.js'
+//import mqtt from 'mqtt/dist/mqtt.js'
+
+const options = {
+	clientId: "mqtt_" + parseInt(Math.random() * 100 + 800, 10),
+	keepalive: 20,
+	connectTimeout: 30 * 1000,
+};
+
+// #ifdef H5
+//let url = 'wss://test.mosquitto.org'
+//let url = 'ws://60.205.190.38'
+let url = 'wss://mqtt.test.radio1964.com'
+// #endif
+// #ifdef MP-WEIXIN||APP-PLUS
+//let url = 'wxs://test.mosquitto.org'
+//let url = 'wx://60.205.190.38'
+let url = 'wxs://mqtt.test.radio1964.com'
+// #endif
+
+export default {
+	namespaced: true,
+	state:{
+		mqttClient: undefined,
+		msgList:[],
+		
+	},
+	getters:{
+		//mqtt连接状态
+		connected:state =>{
+			return state.mqttClient !== undefined && state.mqttClient.connected
+		},
+		//最新的一个消息
+		newMessage:state =>{
+			return msgList[msgList.length-1]
+		}
+	},
+	mutations:{
+		connect(state,getters) {
+			console.log('connect')
+			if (state.mqttClient === undefined) {
+				state.mqttClient = mqtt.connect(url, options)
+				//连接成功回调
+				state.mqttClient.on("connect", function() {
+					console.log("connect success!");
+				});
+		
+				//异常回调
+				state.mqttClient.on("error", function(err) {
+					console.log(err);
+				});
+				
+				state.mqttClient.on('offline', function() {
+					//console.log(`offline callback connected = ${state.mqttClient.connected}`);
+					console.log(`offline callback`);
+				})
+		
+				state.mqttClient.on('end', function() {
+					//console.log(`end callback connected = ${state.mqttClient.connected}`);
+					console.log(`end callback`);
+				})
+		
+				state.mqttClient.on('close', function() {
+					//console.log(`close callback connected = ${state.mqttClient.connected}`);
+					console.log(`close callback`);
+				})
+				state.mqttClient.on('message',function(topic,message){
+					console.log(`message = ${message.toString()}`);
+					state.msgList.push({
+						topic: topic.toString(),
+						msg: message.toString(),
+					});
+				})
+		
+			} else {
+				//console.log(`reconnect connected = ${state.mqttClient.connected}`)
+				//console.log(`reconnect111 connected = ${getters.connected}`)
+				if (state.mqttClient !== undefined && !state.mqttClient.connected) {
+					console.log('reconnect')
+					state.mqttClient.reconnect()
+					//重连成功的回调
+					state.mqttClient.on("reconnect", function() {
+						state.isConnect = true;
+						console.log("reconnect success!");
+					});
+				}
+			}
+		
+		
+		},
+		disconnect(state) {
+			console.log('disconnect')
+			if (state.mqttClient !== undefined) {
+				//mqtt.js在小程序运行有问题,调用end 不会回调close,而且state.mqttClient.connected一直是true
+				state.mqttClient.end(false,() =>{
+					console.log(`state.mqttClient.connected = ${state.mqttClient.connected}`);
+				})
+				
+				
+				//state.mqttClient = undefined
+		
+			}
+		}
+	},
+	actions:{
+		//发布消息
+		publish({state,commit,getters},message){
+			console.log('publish');
+			console.log(message);
+			return new Promise((resolve,reject) =>{
+				if(getters.connected){
+					//发布消息
+					state.mqttClient.publish(message.topic,message.payload,(err) =>{
+						if(err){
+							console.log(err);
+							reject(err)
+						}else{
+							resolve()
+						}
+					})
+				}else{
+					reject("mqttClient is not connected")
+				}
+			})
+		},
+		//订阅消息
+		subscribe({state,commit,getters},topic){
+			console.log(`subscribe topic = ${topic}`);
+			return new Promise((resolve,reject) =>{
+				if(getters.connected){
+					//发布消息
+					state.mqttClient.subscribe(topic,(err) =>{
+						if(err){
+							console.log(err);
+							reject(err)
+						}else{
+							resolve()
+						}
+					})
+				}else{
+					reject("mqttClient is not connected")
+				}
+			})
+		}
+		
+	}
+	
+}

+ 76 - 0
uni.scss

@@ -0,0 +1,76 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:24rpx;
+$uni-font-size-base:28rpx;
+$uni-font-size-lg:32rpx;
+
+/* 图片尺寸 */
+$uni-img-size-sm:40rpx;
+$uni-img-size-base:52rpx;
+$uni-img-size-lg:80rpx;
+
+/* Border Radius */
+$uni-border-radius-sm: 4rpx;
+$uni-border-radius-base: 6rpx;
+$uni-border-radius-lg: 12rpx;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 10px;
+$uni-spacing-row-base: 20rpx;
+$uni-spacing-row-lg: 30rpx;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 8rpx;
+$uni-spacing-col-base: 16rpx;
+$uni-spacing-col-lg: 24rpx;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:40rpx;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:36rpx;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:30rpx;