Uniapp 开发物联网项目MQTT通信使用和TTS语音播报以及无输入框扫描枪读取

最近做用uniapp做大屏的安卓操作系统,遇到的问题和方法总结

首先需要就是和硬件的交互,就是RFID感应将读取的卡的数据通过MQTT传给前端,以及前端通过后台返回的仓储位置进行一键寻物,点亮对应储位的指示灯,语音播报用的是TTS,做的这个项目有点类似现在菜鸟驿站那种扫码寻物的感觉,反正都是仓储管理,不同的是,我们这个有拿走有归还!

1. 首先要使用3.0.0版本的MQTT,"mqtt": "^3.0.0",很多时候H5正常App会出现一些莫名其妙的错误

  1. 错误1 (4.x的版本App有这个问题)

  2. 错误2 (3.0.0的版本有这个问题)

js 复制代码
TypeError: socketTask.onOpen is not a function

解决方式方式在 node_modlues 中 mqtt/dist/mqtt.js里将

js 复制代码
socketTask = wx.connectSocket({
	url: url,
	protocols: websocketSubProtocol,
	success: () => {} //增加这行(没错就是这一行)
})
  1. mqtt 封装支持H5和App,可以拿过来直接使用只需修改自己对应的参数
js 复制代码
//@ts-ignore
let url = null;
// #ifdef H5
import mqtt from "mqtt/dist/mqtt.js";
url = 'ws://x.x.x.x:9527/mqtt';
// #endif
// #ifdef MP-WEIXIN||APP-PLUS
import * as mqtt from 'mqtt/dist/mqtt.js'
url = 'wx://x.x.x.x:9527/mqtt';
// #endif


const mqttTool = {
	client: null,
	subscriptions: new Map(), // 用于存储订阅的主题和对应的处理函数
};

/** 连接Mqtt */
mqttTool.connect = function() {
	const options = {
		username: "",
		password: "",
		cleanSession: true,
		keepAlive: 30,
		clientId: "mqtt_" + Math.random().toString(16).substr(2, 8),
		connectTimeout: 60000,
		reconnection: true, //断开是否重连
		reconnectPeriod: 5000, // 断开后每5秒重连一次
	};

	// 配置Mqtt地址
	// let url = mqtt_url;
	console.log("mqtt地址:", url);
	mqttTool.client = mqtt.connect(url, options);
	mqttTool.client.on("connect", (e) => {
		console.log("mqtt连接成功");
		// 重新订阅之前的主题
		mqttTool.subscriptions.forEach((handler, topic) => {
			mqttTool.client.subscribe(topic, { qos: 0 });
		});
	});
	// 重新连接
	mqttTool.client.on("reconnect", (error) => {
		console.log("正在重连:", error);
	});
	// 发生错误
	mqttTool.client.on("error", (error) => {
		console.log("Mqtt客户端连接失败:", error);
		// 销毁当前连接
		mqttTool.client.end(true);
		mqttTool.client = null;
		// 重新创建连接
		setTimeout(() => {
			mqttTool.connect();
		}, 5000);
	});
	// 断开连接
	mqttTool.client.on("close", function(res) {
		console.log("已断开Mqtt连接");
		// 销毁当前连接
		mqttTool.client.end(true);
		mqttTool.client = null;
		// 重新创建连接
		setTimeout(() => {
			mqttTool.connect();
		}, 5000);
	});

	// 设置全局消息处理器
	mqttTool.client.on("message", (topic, message) => {
		const messageString = message.toString("utf8");
		console.log("收到消息,主题:", topic, "内容:", messageString);

		// 调用对应主题的处理函数
		const handler = mqttTool.subscriptions.get(topic);
		if (handler) {
			handler(topic, messageString);
		}
	});
};

/** 断开连接 */
mqttTool.end = function() {
	return new Promise((resolve, reject) => {
		if (mqttTool.client == null) {
			resolve("未连接");
			console.log("未连接");
			return;
		}
		mqttTool.client.end(true);
		mqttTool.client = null;
		mqttTool.subscriptions.clear(); // 清空订阅列表
		console.log("Mqtt服务器已断开连接!");
		resolve("连接终止");
	});
};

/** 重新连接 */
mqttTool.reconnect = function() {
	return new Promise((resolve, reject) => {
		if (mqttTool.client == null) {
			// 如果客户端不存在,重新连接
			mqttTool.connect();
			resolve("重新连接成功");
			return;
		}
		// 销毁当前连接
		mqttTool.client.end(true);
		mqttTool.client = null;
		// 重新创建连接
		mqttTool.connect();
		resolve("重新连接成功");
	});
};

/** 消息订阅 */
mqttTool.subscribe = function(topic, messageHandler) {
	return new Promise((resolve, reject) => {
		if (mqttTool.client == null) {
			resolve("未连接");
			console.log("未连接");
			return;
		}

		mqttTool.client.subscribe(topic, { qos: 0 }, function(err) {
			if (!err) {
				console.log("订阅成功,主题:", topic);
				mqttTool.subscriptions.set(topic, messageHandler);
				resolve("订阅成功");
			} else {
				console.log("订阅失败,主题:", topic);
				resolve("订阅失败");
			}
		});
	});
};

/** 取消订阅 */
mqttTool.unsubscribe = function(topic) {
	return new Promise((resolve, reject) => {
		if (mqttTool.client == null) {
			resolve("未连接");
			console.log("未连接");
			return;
		}
		mqttTool.client.unsubscribe(topic, function(err) {
			if (!err) {
				mqttTool.subscriptions.delete(topic);
				resolve("取消订阅成功");
				console.log("取消订阅成功,主题:", topic);
			} else {
				resolve("取消订阅失败");
				console.log("取消订阅失败,主题:", topic);
			}
		});
	});
};

mqttTool.publish = function(topic, message, name) {
	return new Promise((resolve, reject) => {
		if (mqttTool.client == null) {
			resolve("Mqtt客户端未连接");
			console.log("Mqtt客户端未连接");
			return;
		}
		mqttTool.client.publish(topic, message, { qos: 1 }, function(err) {
			console.log("发送主题:", topic);
			console.log("发送内容:", message);
			if (!err) {
				if (topic.indexOf("offline") > 0) {
					console.log("[ " + name + " ] 影子指令发送成功");
					resolve("[ " + name + " ] 影子指令发送成功");
				} else {
					console.log("[ " + name + " ] 指令发送成功");
					resolve("[ " + name + " ] 指令发送成功");
				}
			} else {
				console.log("[ " + name + " ] 指令发送失败");
				reject("[ " + name + " ] 指令发送失败");
			}
		});
	});
};

export default mqttTool;

在项目中使用

javascript 复制代码
app.vue

可以将mqttTool.connect()连接放到App.vue中,然后在需要接收数据的地方订阅主题Topic

import mqttTool from './common/mqtt.js'; //mqtt连接
 
onMounted( () => {
    mqttTool.connect();//mqtt连接
    mqttTool.subscribe(`login`, (topic, msg) => { //订阅(可以放到需要接收消息的页面)
            console.log(msg)// 订阅login主题接收的 mqtt返回的消息
    });
})

2. TTS语音播放使用的是Uniapp插件,感谢牛逼的作者

ext.dcloud.net.cn/plugin?id=3...

  1. 插件下载下来一定要放到这个名字命名的文件夹里,其他的都不需要修改
  1. 封装成公共的全局方法使用
javascript 复制代码
common 中voice.js--------------------------

const SpeechTTS = uni.requireNativePlugin("MT-TTS-Speech"); //引入包
// SpeechTTS.setEngine('com.google.android.tts');//加载谷歌的文字转语音包

SpeechTTS.init((callback) => {
	console.log('>> tts: init success', '成功了!!!!!!!!!!!!!');
	// SpeechTTS.speak({
	// 	text: 'motherFucker!'
	// });
})
export default SpeechTTS;

plugin.js中--------------------------

import tools from './index.js'
// #ifdef APP-PLUS
import SpeechTTS from './voice.js'
//#endif 
// import countDown from './countDown.js'
export const ToolPlugin = {
    install(app) {
            app.config.globalProperties.$tools = tools;
            // #ifdef APP-PLUS
            app.config.globalProperties.$SpeechTTS = SpeechTTS;
            //#endif
    }
};

在main.js中--------------------------
import App from './App'
import store from '@/store';
import { ToolPlugin } from './common/plugin.js';

// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
	...App
})
app.$mount()
// #endif

// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
	const app = createSSRApp(App);
	app.use(ToolPlugin) //这里是使用ToolPlugin的所有方法
	app.use(store)
	return {
		app
	}
}
// #endif
  1. 在组件中使用
xml 复制代码
<script setup>
    const { proxy } = getCurrentInstance();
    proxy.$SpeechTTS.speak({
            text: '你好,叼毛'
    });
</script>

注意: 因为使用了原生插件,我们做完这些还不够,要在配置中导入这个插件,然后测试的话记得要先自定义基座运行一次以后就可以在手机测试了,安卓设备有文字转语音设置,可以切换声音语速等

3.霍尼韦尔红外扫描枪普通的扫码枪,非PAD 在无输入框的情况读取(keyCode.js是我安卓设备无key值和code只有keyCode的值)

javascript 复制代码
common 中keyCode.js 安卓中需要使用,h5不必

export default {
	"7": "0",
	"8": "1",
	"9": "2",
	"10": "3",
	"11": "4",
	"12": "5",
	"13": "6",
	"14": "7",
	"15": "8",
	"16": "9",
	"29": "A",
	"30": "B",
	"31": "C",
	"32": "D",
	"33": "E",
	"34": "F",
	"35": "G",
	"36": "H",
	"37": "I",
	"38": "J",
	"39": "K",
	"40": "L",
	"41": "M",
	"42": "N",
	"43": "O",
	"44": "P",
	"45": "Q",
	"46": "R",
	"47": "S",
	"48": "T",
	"49": "U",
	"50": "V",
	"51": "W",
	"52": "X",
	"53": "Y",
	"54": "Z",
	"55": ",",
	"56": ".",
	"59": "",
	"69": "-",
	"70": "=",
	"71": "{",
	"72": "}",
	"74": ":",
	"75": "\"",
	"81": "+"
}

common 中 scan.js

import keymap from "./keyCode.js";
export const useScan = (options) => {
	let keypress = null;
	const keyupSymbol = Symbol('keyupHandler');
	const { onScanComplete } = options;
	const codeResult = ref('');
	const inputCache = ref('')
	const onConfirm = (code) => {
		console.log('拿到的code', code);
		onScanComplete(code)
	}
	keypress = (e) => {
		if (e.keyCode === 23 || e.key == 'Enter') {
			onConfirm(inputCache.value)
			inputCache.value = '';
		} else {
			// #ifdef APP-PLUS
			inputCache.value += keymap[e.keyCode] || '';
			// #endif 
			// #ifdef H5
			if (e.key != 'Shift') {
				inputCache.value += e.key;
			}

			// #endif

		}
	}
	onMounted(() => {
		// #ifdef APP-PLUS
		plus.key.addEventListener("keydown", keypress);
		// #endif 
		// #ifdef H5
		document.addEventListener("keydown", keypress);
		// #endif

	})
	return {
		keyupSymbol // 可选:暴露标识符供外部管理 
	};
}

项目中使用

javascript 复制代码
import { useScan } from "@/common/scan.js";
useScan({ //扫码枪
    onScanComplete: (code) => {
        console.log(code)//处理逻辑
    }
})

3.人脸识别(我们这边就是拍照上传给后端后端去比对的,比较low的那种,不需要什么摇头点头的)用的是mcc-face3 好像2和1都有,就是3可以定制样式 支持H5和 app

ext.dcloud.net.cn/plugin?id=1...

在这里修改 用到了render.js 我还特地研究了一番,学习了

这个是在H5中的效果(捕捉到的一帧)

分享结束,散会!!!

相关推荐
喝拿铁写前端几秒前
你以为你在封装组件,其实你在引入混乱
前端·架构
Json____12 分钟前
智慧酒店企业站官网-前端静态网站模板【前端练习项目】
前端·网站模板·静态网站·企业站·智慧酒店网站
不爱说话郭德纲12 分钟前
没有CICD,怎么自动化部署?
前端·javascript·vue.js
Aotman_13 分钟前
Vue表单组件el-form校验规则rules,条件判断rules表单验证显示必填或非必填
javascript·vue.js·elementui
哔哩哔哩技术15 分钟前
漫画产业加密技术探索与实践:抵御盗版的创新之路
前端
YYDS31415 分钟前
坦克大战HTML网页游戏 (永久免费)
javascript·游戏·html
开心小老虎17 分钟前
ThreeJs实现裸眼3D地球仪
前端·3d·threejs
大强的博客31 分钟前
《Vue Router实战教程》21.扩展 RouterLink
前端·javascript·vue.js
天天向上102432 分钟前
vue2 设置ant-table和el-table隔行变色
vue.js
@是你太难忘35 分钟前
6.4案例:使用渲染函数渲染列表
前端·javascript·vue.js