您现在的位置是:网站首页> HTML&JS

chrome里JS操作硬件

  • HTML&JS
  • 2023-10-16
  • 443人已阅读
摘要

chrome里js操作硬件


操作串口

操作蓝牙



操作串口

点击进入原始测试串口页面

页面部分

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1">

<title></title>

<link rel="stylesheet" href="/cloudSerial/css/bootstrap.min.css">

<link rel="manifest" href="/cloudSerial/manifest.json">

</head>

<style>

.banner {

  align-content: center;

  display: none;

  justify-content: center;

  width: 100%;

}


</style>

<body>


<nav class="navbar navbar-default navbar-fixed-top" style="background-color: #009966;">

<div class="container navbar-left">

<div class="navbar-header">

<!--<a class="navbar-brand " href="#">

<img src="img/rtlogo.png" width="25px" height="25px" onclick="menu()">

</a>-->

<text class="navbar-text" style="color: white;font-size: 20px;">串口调试助手</text>


<text id="serialID" class="navbar-text" style="color: white;font-size: 20px;"></text>


<text id="serialStatus" class="navbar-text" style="color: white;font-size: 20px;"></text>

</div>


</div>

</nav>




<div class="col-sm-12" id="contentDis " style="padding-top: 100px; ">

<div class="col-sm-3">

<div class="panel panel-default">

<div class="panel-heading">串口配置</div>

<div class="panel-body">

<text>串口号:</text>

<input type="text" name="" id="portName">


<button class="btn btn-info" id="portSelectBtn" onclick="portSelect()">选择串口</button>

<br /><br />

<text>波特率:</text>

<select id="baudRate" style="width:100px">

<option value="">1200</option>

<option value="">2400</option>

<option value="">4800</option>

<option value="" selected="selected">9600</option>

<option value="">115200</option>

</select><br /><br />

<text>校验位:</text>

<select id="parity" style="width:100px">

<option value="">none</option>

<option value="">odd</option>

<option value="">even</option>

</select><br /><br />

<text>数据位:</text>

<select id="dataBits" style="width:100px">

<option value="">8</option>

<option value="">7</option>

<option value="">6</option>

<option value="">5</option>

</select><br /><br />

<text>停止位:</text>

<select id="stopBits" style="width:100px">

<option value="">1</option>

<option value="">2</option>

</select><br /><br />

<button class="btn btn-info" onclick="serialOpen()" id="btnOpen">打开</button>

<button class="btn btn-info" style="margin-left: 20px;" onclick="serialClose()" id="btnClose">关闭</button>

</div>

</div>

<div class="panel panel-default">

<div class="panel-heading">控制</div>

<div class="panel-body">

<!-- <input type="checkbox" name="options" id="hexOption" > 16进制/字符串:发送

<br />

<br />

<input type="checkbox" name="options2" id="hexOption2"> 16进制/字符串:接收

<br />

<br /> -->

<button class="btn btn-info" id="receiverClear" onclick="receiverClear()">

清空接收区

</button>

</div>


</div>

</div>

<div class="col-sm-9">

<textarea rows="20" cols="80" id="receiverText">欢迎使用网页版串口助手。</textarea>

<div style="margin-top: 10px;">

<textarea rows="1" cols="60" style="float: left;" id="sendText"></textarea>

<button class="btn-info" style="margin-left: 10px;" onclick="serialSend()">

发送

</button>

</div>


</div>

<script type="text/javascript" src="/cloudSerial/js/jquery.min.js"></script>

<script type="text/javascript" src="/cloudSerial/js/bootstrap.min.js"></script>

<script type="text/javascript" src="/cloudSerial/js/index.js"></script>

<script>

if ('serviceWorker' in navigator) {

window.addEventListener('load', () => {

navigator.serviceWorker.register('/cloudSerial/sw.js',{scope:'/cloudSerial/'})

.then(registration => {

console.log('SW registered with scope:', registration.scope);

})

.catch(err => {

console.error('Registration failed:', err);

});

});

}

</script>

</body>


</html>

JS部分


let port = null;

let reader = null;


// serialReceive();

if ("serial" in navigator) {

// The Web Serial API is supported.

console.log("The Web Serial API is supported");


} else {

console.log("The Web Serial API is not supported");


}


async function portSelect() {

port = await navigator.serial.requestPort();

console.log(JSON.stringify(port.getInfo()));

// let ele = document.createElement('option');

// ele.setAttribute("value",JSON.stringify(port.getInfo()));

// ele.setAttribute("selected","selected");

// ele.innerHTML=JSON.stringify(port.getInfo())

// $("#portName")[0].appendChild(ele);

if (port) {

$("#portName").val(JSON.stringify(port.getInfo()));

}

}



//打开串口

async function serialOpen() {

if (port == null) {

alert('打开串口出错');

return;

}

let opt = {

baudRate: 9600,

parity: 0,

dataBits: 8,

stopBits: 1

};

opt.baudRate = parseInt($('#baudRate option:selected').text());

opt.parity = $('#parity option:selected').text();

// switch (tmp) {

// case "none":

// opt.parityMode = 0;

// break;

// case "odd":

// opt.parityMode = 1;

// break;

// case "even":

// opt.parityMode = 2;

// break;

// }

opt.dataBits = parseInt($('#dataBits option:selected').text());

opt.stopBits = parseInt($('#stopBits option:selected').text());


// Wait for the serial port to open.

await port.open(opt);



$("#btnOpen").attr('disabled', 'disabled');

// while (port.readable) {

reader = port.readable.getReader();



try {

while (true) {

const {

value,

done

} = await reader.read();

if (done) {

// Allow the serial port to be closed later.

reader.releaseLock();

break;

}

if (value) {

//value is a Uint8Array.

console.log(value);

const strvalue = new TextDecoder("utf-8").decode(value);

console.log(strvalue);

const temp = $("#receiverText").val();

$("#receiverText").val(temp + strvalue);

}

}

} catch (error) {

// TODO: Handle non-fatal read error.

}

// }

}


//关闭串口

async function serialClose() {

$('#btnOpen').removeAttr('disabled');

try {

await reader.cancel();

await port.close();

} catch (e) {

console.log(e);

//TODO handle the exception

}


}


//串口发送

async function serialSend() {


console.log(document.getElementById("sendText").value.length);

if (document.getElementById("sendText").value.length == 0) {

alert("发送内容不能为空");

return;

}


// console.log(document.getElementById("hexOption").checked);

// hexOption = document.getElementById("hexOption").checked;



const data = $("#sendText").val();


//是否选中16进制

// if (hexOption) {

// const writer = port.writable.getWriter();


// const data = new Uint8Array([104, 101, 108, 108, 111]); // hello

// await writer.write(data);

// // Allow the serial port to be closed later.

// writer.releaseLock();

// } else {

const textEncoder = new TextEncoderStream();

const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);


const writer = textEncoder.writable.getWriter();


await writer.write(data);

// Allow the serial port to be closed later.

writer.releaseLock();

// }


}



//清空接收区

function receiverClear() {

console.log("clear");

document.getElementById("receiverText").value = "";

}



操作蓝牙

点击进入原始蓝牙页面

页面部分

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<title>WebBle</title>

<link rel="stylesheet" href="/webBle/css/bootstrap.min.css">

<link rel="manifest" href="/webBle/manifest.json">

</head>

<body>

<nav class="navbar navbar-default navbar-fixed-top" style="background-color: #009966;">

<div class="container navbar-left">

<div class="navbar-header">

<text class="navbar-text" style="color: white;font-size: 20px;">ble调试助手</text>


<text id="serialID" class="navbar-text" style="color: white;font-size: 20px;"></text>


<text id="serialStatus" class="navbar-text" style="color: white;font-size: 20px;"></text>

</div>


</div>

</nav>


<div class="panel panel-default" style="padding-top: 100px; ">

<div class="panel-heading">

<button id="scanBtn" type="button">scan</button>

</div>

<div class="panel-body">

<table class="table">

<thead>

<tr>

<th scope="col">service</th>

<th scope="col">characteristic</th>

<th scope="col">properties</th>

<th scope="col">value</th>

<th scope="col">

action

</th>

</tr>

</thead>

<tbody>


</tbody>

</table>


</div>


</div>


<script type="text/javascript" src="/webBle/js/jquery.min.js"></script>

<script type="text/javascript" src="/webBle/js/index.js"></script>

<script>

if ('serviceWorker' in navigator) {

window.addEventListener('load', () => {

navigator.serviceWorker.register('/webBle/sw.js', {

scope: '/webBle/'

})

.then(registration => {

console.log('SW registered with scope:', registration.scope);

})

.catch(err => {

console.error('Registration failed:', err);

});

});

}

</script>

</body>

</html>


JS代码部分

index.js

let newservices = [];

$("#scanBtn")[0].addEventListener('click', function() {

console.log('Requesting any Bluetooth Device...');

navigator.bluetooth.requestDevice({

// filters: [...] <- Prefer filters to save energy & show relevant devices.


// filters: [{

//     services: ['6e400001-b5a3-f393-e0a9-e50e24dcca9e']

//   }]


// acceptAllAdvertisements: true,

// keepRepeatedDevices: true,

acceptAllDevices: true,

optionalServices: [

'6e400001-b5a3-f393-e0a9-e50e24dcca9e',

"alert_notification",

"automation_io",

"battery_service",

"blood_pressure",

"body_composition",

"bond_management",

"continuous_glucose_monitoring",

"current_time",

"cycling_power",

"cycling_power",

"cycling_speed_and_cadence",

"device_information",

"environmental_sensing",

"generic_access",

"generic_attribute",

"glucose",

"health_thermometer",

"heart_rate",

"human_interface_device",

"immediate_alert",

"indoor_positioning",

"internet_protocol_support",

"link_loss",

"location_and_navigation",

"next_dst_change",

"phone_alert_status",

"pulse_oximeter",

"reference_time_update",

"running_speed_and_cadence",

"scan_parameters",

"tx_power",

"user_data",

"weight_scale"

]

})

.then(device => {

console.log('Connecting to GATT Server...');

return device.gatt.connect();

})

.then(server => {

// Note that we could also get all services that match a specific UUID by

// passing it to getPrimaryServices().

console.log('Getting Services...');

return server.getPrimaryServices();

})

.then(services => {

return overrideServices(services);

})


.catch(error => {

console.log('Argh! ' + error);

});

});


function overrideServices(services) {

console.log('Getting Characteristics...');

let queue = Promise.resolve();

services.forEach(service => {


queue = queue.then(_ => service.getCharacteristics().then(characteristics => {

console.log('> Service: ' + service.uuid);

let properties;

characteristics.forEach(characteristic => {

console.log(service);

console.log(characteristic);


properties = getSupportedProperties(characteristic)


console.log('>> Characteristic: ' + characteristic.uuid + ' ' + properties);


newservices.push({

"serive": service,

"characteristic": characteristic,

"properties": properties

})

var eletr = document.createElement('tr');

eletr.setAttribute("id", characteristic.uuid);

eletr.innerHTML = `<tr>

<th scope="row">` + service.uuid + `</th>

<td>` + characteristic.uuid +

`</td>

<td>` + properties +

`</td>

<td><input type="text" name="" id="" value="" /></td>

<td>

<button type="button" >read</button>

<button type="button" >write</button></td>

</tr>`;


$('tbody')[0].appendChild(eletr);

if (properties.indexOf("NOTIFY") != -1) {

characteristic.startNotifications()

characteristic.addEventListener('characteristicvaluechanged', function(event) {

const value = event.target.value;

console.log('Received ' + value);


console.log(value.buffer, value.byteLength);


console.log(String.fromCharCode.apply(null, new Uint8Array(value.buffer)));

$('#' + characteristic.uuid + ' input').val(String.fromCharCode.apply(null, new Uint8Array(value.buffer)));

});

}


if (properties.indexOf("READ") != -1) {

$('#' + characteristic.uuid + ' button')[0].addEventListener("click", function() {

const value = characteristic.readValue();

$('#' + characteristic.uuid + ' input').val(String.fromCharCode.apply(null, new Uint8Array(value.buffer)));

});

}

if (properties.indexOf("WRITE") != -1) {

$('#' + characteristic.uuid + ' button')[1].addEventListener("click", function() {


// Writing 1 is the signal to reset energy expended.

const inputValue = $('#' + characteristic.uuid + ' input').val();

const value = stringToUint8Array(inputValue);

characteristic.writeValue(value);

});

}


});

}));

});


return queue;

}


function stringToUint8Array(str) {

var arr = [];

for (var i = 0, j = str.length; i < j; ++i) {

arr.push(str.charCodeAt(i));

}


var tmpUint8Array = new Uint8Array(arr);

return tmpUint8Array

}


/* Utils */

function getSupportedProperties(characteristic) {

let supportedProperties = [];

for (const p in characteristic.properties) {

if (characteristic.properties[p] === true) {

supportedProperties.push(p.toUpperCase());

}

}

return '[' + supportedProperties.join(', ') + ']';

}


sw.js


const cacheName = 'cache-v1';

const precacheResources = [

  '/webBle/',

  '/webBle/index.html',

  '/webBle/js/index.js',

  '/webBle/js/jquery.min.js',

  '/webBle/js/bootstrap.min.js',

  '/webBle/css/bootstrap.min.css',

  '/webBle/image/android-launchericon-48-48.png',

  '/webBle/image/android-launchericon-96-96.png',

  '/webBle/image/android-launchericon-72-72.png',

  '/webBle/image/android-launchericon-144-144.png',

  '/webBle/image/android-launchericon-192-192.png',

  '/webBle/image/android-launchericon-512-512.png'

];


self.addEventListener('install', event => {

  console.log('Service worker install event!');

  event.waitUntil(

    caches.open(cacheName)

      .then(cache => {

        return cache.addAll(precacheResources);

      })

  );

});


self.addEventListener('activate', event => {

  console.log('Service worker activate event!');

});


self.addEventListener('fetch', event => {

  console.log('Fetch intercepted for:', event.request.url);

  event.respondWith(caches.match(event.request)

    .then(cachedResponse => {

        if (cachedResponse) {

          return cachedResponse;

        }

        return fetch(event.request);

      })

    );

});







Top