let ArrNotifyListen = [ "0000fff1-0000-1000-8000-00805f9b34fb", "FFF1" ]; let ArrWriteListen = [ "0000fff2-0000-1000-8000-00805f9b34fb", "FFF2" ]; let ArrReadListen = [ "0000fff1-0000-1000-8000-00805f9b34fb", "FFF1" ]; let bledevice; let characteristic; //读取cocopi上的蓝牙设备信息 async function requestDevice() { return await new Promise((resolve, reject) => { navigator.bluetooth.requestDevice({ filters: [{ services: ['0000fff0-0000-1000-8000-00805f9b34fb'] }] }).then(device => { try { console.log(device); bledevice = device; resolve(device); //连接设备 // return device.gatt.connect(); } catch (e) { } }).then(server => { try { //获取设备所有的服务 return server.getPrimaryServices(); } catch (e) { } }).then(services => { try { //绑定特征 return overrideServices(services); } catch (e) { } }).catch(error => { console.log(error); reject(error) return error; }); }) } //把所有的服务的特性读取 async function bleconnect() { // try { //绑定特征 if (bledevice) { try { const server = await bledevice.gatt.connect(); console.log('Connected to Bluetooth Device'); // 读取来自蓝牙设备的数据 server.getPrimaryService('0000fff0-0000-1000-8000-00805f9b34fb').then(service => { return service.getCharacteristic('0000fff2-0000-1000-8000-00805f9b34fb'); }) .then(characteristic => { characteristic.startNotifications(); characteristic.addEventListener('characteristicvaluechanged', handleCharacteristicValueChanged); return characteristic.startNotifications(); }) .catch(error => { console.error('Error reading data:', error); }); } catch (error) { console.error('Error connecting to Bluetooth Device:', error); } service = await bledevice.gatt.getPrimaryService('0000fff0-0000-1000-8000-00805f9b34fb'); characteristic = await service.getCharacteristic('0000fff1-0000-1000-8000-00805f9b34fb'); return bledevice; } return null; // } // catch (e) { // } } function handleCharacteristicValueChanged(event) { const value = new Uint8Array(event.target.value.buffer); if (value[0] === value[1] && value[0] === 0xFF) { window.blechangedcallback && window.blechangedcallback(event); } } //把所有的服务的特性读取 async function overrideServices(services) { try { let queue = Promise.resolve(); services.forEach(service => { queue = queue.then(_ => service.getCharacteristics().then(characteristics => { startNotifications(characteristics); })); }); return queue; } catch (e) { } } //服务和读写的功能绑定 function startNotifications(newservices) { try { newservices.forEach(newservice => { //启动通知 if (ArrNotifyListen.indexOf(newservice.uuid) != -1) { addNotifyListen(newservice); } //发送api的绑定 if (ArrWriteListen.indexOf(newservice.uuid) != -1) { addWriteListen(newservice); } //读取api的绑定 if (ArrReadListen.indexOf(newservice.uuid) != -1) { addReadListen(newservice); } }); } catch (e) { } } //绑定通知服务 function addNotifyListen(service) { if (service.properties.notify) { service.startNotifications();//启动通知 //获取通知信息 service.addEventListener('characteristicvaluechanged', function (event) { // const rechex = new TextDecoder().decode(event.target.value.buffer); // console.log(rechex); window.blechangedcallback && window.blechangedcallback(event); }); } } //发送api function addWriteListen(service) { if (service.properties.write) { //由于目前蓝牙设备只有一个写入功能,所以直接把写入公布到window里 window.blewrite = async function (value, callback) { if (value) { //如果发送的数据是字符串,转化成unit8Array if (typeof value === 'string') { value = new TextEncoder().encode(value); } //发送数据 const data = await service.writeValue(value); // .then( // //发送成功处理 // function (resolve) { // callback && callback(resolve); // return resolve; // }, // //发送失败处理 // function (error) { // console.error(error); // return error; // } // ); return data; } } } } //读取返回的数据功能 function addReadListen(service) { if (service.properties.read) { window.bleread = async function (callback) { const data = await service.readValue().then( function (resolve) { try { const rechex = new TextDecoder().decode(resolve.buffer); callback(rechex); //监听后获取返回的数据,判断需要发送什么信号过去 return rechex; } catch (e) { console.log(e); }; return null; }, function (e) { console.error(e); return null; } ); return data; } } } function uint8ArrayToInt(array) { let num = 0n; for (let i = 0; i < array.length; i++) { num += BigInt(array[i]) << BigInt((array.length - 1 - i) * 8); } return Number(num); } //文件分块发送 async function blefilesend(event, datas, len) { const returnarr = new Uint8Array(event.target.value.buffer); //判断信号是否是发送文件的信号 if (returnarr[0] === returnarr[1] && returnarr[0] === 0xFF) { try { let split = uint8ArrayToInt(returnarr.slice(2, 8)); let crc = returnarr.slice(8, 16); //已经传输完毕无需再上传 if (split == len && Uint8ArraysEqual(crc, sendcrc)) { senddata(new Uint8Array([0xFF, 0xFF, 0xFF])); } //断点续传,满足是同一个文件的情况 else if (split != 0 && split < len) { const splitcrc = new TextEncoder().encode((CRC32.buf(new Uint8Array([...datas.subarray(0, split)])) >>> 0).toString(16).padStart(8, '0')); if (Uint8ArraysEqual(crc, splitcrc)) { for (let i = split; i < datas.length; i += 240) { if (i + 240 >= datas.length) { timeoutId = setTimeout(() => { senddata(new Uint8Array([0xFF, 0xFF, 0xFF])); }, 2000); window.blechangedcallback = async function (event) { window.blechangedcallback = null; clearTimeout(timeoutId); }; } if (i == split) { await senddata(new Uint8Array([ ...new Uint8Array([0xFF, 0xFF, 0x01]), ...datas.subarray(i, i + 240)])); } else { await senddata(new Uint8Array([...datas.subarray(i, i + 240)])); } } } else { for (let i = 0; i < datas.length; i += 240) { if (i + 240 >= datas.length) { timeoutId = setTimeout(() => { senddata(new Uint8Array([0xFF, 0xFF, 0xFF])); }, 2000); window.blechangedcallback = async function (event) { window.blechangedcallback = null; clearTimeout(timeoutId); }; } if (i == 0) { await senddata(new Uint8Array([ ...new Uint8Array([0xFF, 0xFF, 0xF0]), ...datas.subarray(i, i + 240)])); } else { await senddata(new Uint8Array([...datas.subarray(i, i + 240)])); } } } } //文件直接上传 else { for (let i = 0; i < datas.length; i += 240) { if (i + 240 >= datas.length) { timeoutId = setTimeout(() => { senddata(new Uint8Array([0xFF, 0xFF, 0xFF])); }, 2000); window.blechangedcallback = async function (event) { window.blechangedcallback = null; clearTimeout(timeoutId); }; } if (i == 0) { await senddata(new Uint8Array([ ...new Uint8Array([0xFF, 0xFF, 0xF0]), ...datas.subarray(i, i + 240)])); } else { await senddata(new Uint8Array([...datas.subarray(i, i + 240)])); } } } return true; } catch (error) { console.error('发送消息错误:', error); return false; } } } //停止运行 async function blestop(callback) { await senddata(new Uint8Array([0xFF, 0xFF, 0x11]), callback); } //运行py文件 async function blerun(filename, callback) { const start = new Uint8Array([0xFF, 0xFF, 0x10]); const sendmessage = new TextEncoder().encode(filename) const newarr = new Uint8Array([...start, ...sendmessage]); window.blechangedcallback = async function (event) { //判断是否结束运行了 const isrun = new Uint8Array(event.target.value.buffer).toString().indexOf(new Uint8Array([0xFF, 0xFF, 0x1F]).toString()) > -1; let rechex = ""; if (isrun) { rechex = new TextDecoder().decode(new Uint8Array(event.target.value.buffer).slice(0, -3)); } else { rechex = new TextDecoder().decode(event.target.value.buffer); } console.log("111111111111111111",rechex,isrun); callback(rechex, isrun); }; await senddata(newarr); } //上传py文件 async function bleuploadfile(filename, sendstring, callback) { const zip = new JSZip(); zip.file("user_latest_code.py", sendstring, { compression: 'DEFLATE', compressionOptions: { level: 9 } }); // Use DEFLATE compression with highest level (9) const temp = await zip.generateAsync({ type: 'uint8array' }) const textec = new TextEncoder(); sendcrc = textec.encode((CRC32.buf(temp) >>> 0).toString(16).padStart(8, '0')); const sendarr = new Uint8Array([ ...new Uint8Array([0xFF, 0xFF]), ...textec.encode(filename), ...intToUint8Array(temp.length), ...sendcrc, ]); window.blechangedcallback = async function (event) { window.blechangedcallback = null; blefilesend(event, temp, temp.length).then(value => { callback(value); }); }; // console.log(sendarr); await senddata(sendarr); } async function senddata(value) { if (value) { if (typeof value === 'string') { value = new TextEncoder().encode(value); } await characteristic.writeValue(value); } } function intToUint8Array(num) { num = BigInt(num) const array = new Uint8Array(6); for (let i = 5; i >= 0; i--) { array[i] = Number(num % 256n); num = num >> 8n; } return array; } //crc8数据加密 function crc8(datas) { let crc = 0x00; for (let i = 0; i < datas.length; i++) { crc = (crc ^ datas[i]) & 0xFF; crc = cal_crc(crc); } return (crc & 0xFF).toString(16).padStart(2, '0'); } //crc8数据加密 function cal_crc(data) { let crc = data; const poly = 0x07; for (let i = 8; i > 0; i--) { if (((crc & 0x80) >> 7) === 1) { crc = (crc << 1) ^ poly; } else { crc = (crc << 1); } } return crc & 0xFF; } function Uint8ArraysEqual(arr1, arr2) { if (arr1.length !== arr2.length) { return false; } for (let i = 0; i < arr1.length; i++) { if (arr1[i] !== arr2[i]) { return false; } } return true; } // function calculateCRC8(data) { // const poly = 0x07; // 多项式 x^8 + x^2 + x + 1 对应的值 // let crc = 0x00; // for (let i = 0; i < data.length; i++) { // crc ^= data[i]; // for (let j = 0; j < 8; j++) { // if (crc & 0x80) { // crc = (crc << 1) ^ poly; // } else { // crc <<= 1; // } // } // } // return (crc & 0xFF).toString(16); // } // //合并unit8Array数组 // function merge(arr) { // let len = 0; // arr.forEach(item => { // length += item.length; // }); // let newarr = new Uint8Array(len); // let offset = 0; // newarr.forEach(item => { // arr.set(item, offset); // offset += item.length; // }); // }