index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<script type="text/javascript">
if(isIE() || isFirefox()){
alert('当前浏览器(IE浏览器、火狐浏览器)不支持网页串口!请使用Edge或谷歌浏览器访问!')
}
else{
window.location.href = "./b.html"
}
//判断是否为IE浏览器
function isIE(){
if(!!window.ActiveXObject || "ActiveXObject" in window){//ie浏览器不支持
return true;
}else{
return false;
}
}
// 判断是否为火狐浏览器
function isFirefox(){
str = navigator.userAgent;
if(str.indexOf("Firefox") >= 0){
return true
}
else{
return false
}
}
</script>
</head>
<body>
</body>
</html>
b.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/bootstrap.min.css">
<script src="jQuery/jquery-1.10.2.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script type="text/javascript" charset="UTF-8">
console.log(window.location.href) //http://172.16.4.131/wwx/webSerial/6.html
let reader, textEncoder, writer
let KeepReading=true
let port
let isOpen = false//当前串口是否打开;false代表未打开串口
let res = ''//保存返回结果
//连接串口
async function myconnect(){
if ("serial" in navigator) {
var baudRate=$("#baudRate option:selected").text();
var dataBits=$("#dataBits option:selected").text();
var stopBits=$("#stopBits option:selected").text();
var parity=$("#parity option:selected").text();
// 提示用户选择一个串口。
port = await navigator.serial.requestPort();
var open_val = {baudRate:Number(baudRate),dataBits:Number(dataBits),stopBits:Number(stopBits),parity:parity,flowControl:'none', bufferSize:16}
// 等待串口打开 SerialOptions
await port.open(open_val)//使用前端传的串口参数
isOpen = true;
$('#openBtn').hide()
$('#closeBtn').show()
$('#sendBtn').show()
// 創建一個讀取器並將流鎖定到它。當流被鎖定時,在釋放此讀取器之前無法獲取其他讀取器
reader = port.readable.getReader();
//调用port.readable.getReader()创建一个读取器并将其锁定为readable。当可读被锁定时,串口不能被关闭。
}
else{
alert("当前浏览器(IE浏览器、火狐浏览器)不支持网页串口!无法使用网页进行串口通讯!请使用Edge或谷歌浏览器访问!")
}
}
// 发送指令并读取数据
async function mysend(){
var myendStr = $("input[name='end']:checked").val();//是否以\r结束
res = ''
var sendMsg = $("#send").val();
if(sendMsg==''){
alert('请输入指令!')
}
else{//8 位无符号整数值的类型化数组 Uint8Array 对象
//23 30 31 0D
// uart.write(b'\x7b\x46\x30\x30\x52\x44\x44\x5b\x0d') //高温高湿机
// cmd = b'\x01\x03\x00\x00\x00\x03' #讀振動傳感器值命令
// 发送文本指令
textEncoder = new TextEncoderStream();//轉換流,它抓取所有Uint8Array塊並將它們轉換為字符串。
writableStreamClosed = textEncoder.readable.pipeTo(port.writable);
writer = textEncoder.writable.getWriter();
console.log('sendMsg=='+sendMsg)
if(myendStr=='N'){
await writer.write(sendMsg)
}else{
await writer.write(sendMsg + "\r")
}
// await writer.write("#01\r")
await writer.close()
writer.releaseLock();// 允许稍后关闭串口
$('#receive').val('');//清空上次返回数据
//读取串口数据
while (port.readable && KeepReading) {
// 創建一個讀取器並將流鎖定到它。當流被鎖定時,在釋放此讀取器之前無法獲取其他讀取器
// reader = port.readable.getReader();
//调用port.readable.getReader()创建一个读取器并将其锁定为readable。当可读被锁定时,串口不能被关闭。
try {
while (true) {//监听来自串行设备的数据
// 当使用循环从串行设备连续读取数据时,端口Readable将一直被锁定,直到遇到错误。在这种情况下,调用reader.cancel()将强制reader.read()
// 立即解析为{value: undefined, done: true},从而允许循环调用reader.releaseLock()
const { value, done } = await reader.read();
if (done) {//如果done为真,则串行端口已经关闭,或者没有更多的数据输入。
break;
}
if (value) {
console.log(value)
// res += value
res += Uint8ArrayToString(value).toString()
$('#receive').val(res)
}
}
} catch (error) {//只要错误是非致命的,一个新的ReadableStream就会自动创建。如果发生致命错误,如串行设备被删除,则端口。可读的变成了零。
// TODO: 处理非致命的读错误。
console.log(error)
console.log("处理非致命的读错误");
alert("数据读取失败")
}finally{
// 允许稍后关闭串口。
reader.releaseLock();
await port.close()
console.log('串口已关闭')
myclose()
isOpen = false
}
}
}
}
// 关闭串口
async function myclose(){
if(isOpen){
window.location.href = "./b.html"
// KeepReading = false;
// reader.cancel();
console.log('关闭reader')
$('#openBtn').show()
$('#closeBtn').hide()
$('#sendBtn').hide()
}
else{
alert("串口未打开,无需关闭串口!")
}
}
// 数组转字符串
function Uint8ArrayToString(fileData){
var dataString = "";
for (var i = 0; i < fileData.length; i++) {
dataString += String.fromCharCode(fileData[i]);
}
return dataString;
}
// 字符串转数组
function toUint8Arr(str) {
const buffer = [];
for (let i of str) {
const _code = i.charCodeAt(0);
if (_code < 0x80) {
buffer.push(_code);
} else if (_code < 0x800) {
buffer.push(0xc0 + (_code >> 6));
buffer.push(0x80 + (_code & 0x3f));
} else if (_code < 0x10000) {
buffer.push(0xe0 + (_code >> 12));
buffer.push(0x80 + (_code >> 6 & 0x3f));
buffer.push(0x80 + (_code & 0x3f));
}
}
return Uint8Array.from(buffer);
}
// 字符串转换UTF8字节
function strToUtf8Bytes(str) {
const utf8 = [];
for (let ii = 0; ii < str.length; ii++) {
let charCode = str.charCodeAt(ii);
if (charCode < 0x80) utf8.push(charCode);
else if (charCode < 0x800) {
utf8.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f));
} else if (charCode < 0xd800 || charCode >= 0xe000) {
utf8.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f));
} else {
ii++;
charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (str.charCodeAt(ii) & 0x3ff));
utf8.push(
0xf0 | (charCode >> 18),
0x80 | ((charCode >> 12) & 0x3f),
0x80 | ((charCode >> 6) & 0x3f),
0x80 | (charCode & 0x3f),
);
}
}
//兼容汉字,ASCII码表最大的值为127,大于127的值为特殊字符
for(let jj=0;jj<utf8.length;jj++){
var code = utf8[jj];
if(code>127){
utf8[jj] = code - 256;
}
}
return utf8;
}
// 根据返回字节码进行16进制转换
function strToHexCharCode(str){
var hexCharCode = [];
var chars = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"];
for(var i = 0; i < str.length; i++) {
var bit = (str[i] & 0x0f0) >> 4;
hexCharCode.push(chars[bit]);
var bit = str[i] & 0x0f;
hexCharCode.push(chars[bit]);
}
return hexCharCode.join("");
}
</script>
</head>
<body>
<table border="0" width="50%" align="center">
<tr align="center"><td colspan="4"><h1>网页串口工具</h3></td></tr>
<tr>
<td style="padding: 5px;" colspan="1">
波特率
<select name="" id="baudRate" class="form-control ">
<option value ="110">110</option>
<option value ="300">300</option>
<option value ="600">600</option>
<option value ="1200">1200</option>
<option value ="2400">2400</option>
<option value ="4800">4800</option>
<option value ="9600">9600</option>
<option value ="14400">14400</option>
<option value ="19200">19200</option>
<option value ="38400">38400</option>
<option value ="56000">56000</option>
<option value ="57600">57600</option>
<option value ="115200">115200</option>
</select></td>
<td style="padding: 5px;" colspan="1">
奇偶校验模式
<select name="" id="parity" class="form-control ">
<option value="none">none</option>
<option value="even">even</option>
<option value="odd">odd</option>
</select>
</td><td style="padding: 5px;" colspan="1">数据位
<select name="" id="dataBits" class="form-control ">
<option value="7">7</option>
<option value="8">8</option>
</select></td>
<td style="padding: 5px;" colspan="1">停止位
<select name="" id="stopBits" class="form-control ">
<option value="1">1</option>
<option value="2">2</option>
</select></td>
</tr>
<tr align="">
<td colspan="1" style="padding: 5px;">发送指令(不包含\r)
<textarea rows="3" cols="5" id="send" class="form-control "></textarea></td>
<td colspan="3" style="padding: 5px;">接收内容
<textarea rows="3" cols="5" id="receive" class="form-control "></textarea></td></tr>
<tr align="">
<td>是否以\r结束
<input type="radio" checked="checked" name="end" value="Y"/>是
<input type="radio" name="end" value="N"/>否</td>
</tr>
<tr align="center">
<td align="center" style="padding: 5px;" colspan="4">
<button id="openBtn" type="button" onclick="myconnect()" class="btn-xs btn-primary" >打开串口</button>
<button id="closeBtn" type="button" onclick="myclose()" class="btn-xs btn-primary" style="display: none;">关闭串口</button>
<button id="sendBtn" type="button" onclick="mysend()" class="btn-xs btn-primary" style="display: none;">发送指令</button> </td></tr>
</table>
</body>
</html>