gpt4 book ai didi

javascript - webUSB api 工作但接收到的数据未正确解码

转载 作者:行者123 更新时间:2023-12-04 08:33:11 30 4
gpt4 key购买 nike

更新代码
我正在使用此代码声明使用串行 2-USB 设备。它出现了,我可以查询有关它的信息,即。 “连接到USB2.0-Serial VID:6790 PID:29987”(秦恒的CH34x)。需要明确的是,我使用了 winUSB 驱动程序(使用 zadig-2.5.exe 下载,Windows 10)并卸载了原始驱动程序。
我将接收到的数据作为数据 View 获取,但是当我进行解码时,它会出现乱码。而且我看到数组长度几乎对应于我知道我的 ESP8266 通过串行端口发布的内容。
解码数据示例:�(#���D"D�T�b�!A#7mP�R�N����#�m93aw9 ½�d-K��b��BF+3ѡ��kag1�R�#��#!!r����g�!d��a��谛oa��399�}��1D�#��'99�����9�����'99���'99����@@譛我以 Uint8Arrays 的形式获取数据,但即使我尝试使自己的解析器产生相同的乱码,最终结果也是如此。我是否需要像开始位、确认位等那样关心 USB 的东西?那里的所有示例代码只是做一个文本解码器,仅此而已。
serial.js + index.html

  
//https://zadig.akeo.ie/ --- for windows, get supported driver "winusb"
//https://www.cypress.com/file/134171/download --- read up on the USB standard
//https://cscott.net/usb_dev/data/devclass/usbcdc11.pdf --- more here
//http://www.linux-usb.org/usb.ids --- list of ids
//https://developer.mozilla.org/en-US/docs/Web/API/USBDevice --- more info

// host --- data pipe ---> vendor/device TX (host) transmitting data
// host <-- control pipe --> vendor/device Endpoint 0 (this is the one we us to control data up (from device to host) and data down (from host to device)
// host <-- data pipe ---- vendor/device RX (host) receiving data

//It's possible to send interrupt, bulk ant isochronus data packages.... this flasher only use bulk (and control transfers on the control pipe)

//Typically the usb cable work/look as follows:
//red 5V, black 0V/GND, green TX(host), white RX(host)

//USB standard transaction:
// [TOKEN][DATA][HANDSHAKE]
//... the data package ....
//1: Packet ID (PID): 8 bits (4 type bits + 4 error check bits) this is where we till if the transfer is "in / out / setup / SOF (start of frame)
//2: Optional device address: 7 bits (max 127 devices in the bus)
//3: Optional endpoint address: 4 bits (max 16 endpoints in a device)
//4: Optional payload data (0 ... 1023 bytes)
//5: Optional CRC (cyclic redundancy checksum)

//this flasher uses 8-N-1 for the serial parameters (8 data bits, No parity and 1 stop bit)

//when we control the "device" we control the USB chip directly, when we control the "interface" we control the serial functionality (class: 0xFF, vendor specific driver)

window.addEventListener("load", initiate, false);

//The different hardware we support + their specific data/configs
const table = {
0x0403: {"FTDI": {
0x6001: "FT232R",
0x6010: "FT2232H",
0x6011: "FT4232H",
0x6014: "FT232H",
0x6015: "FT231X", // same ID for FT230X, FT231X, FT234XD
}},
0x1a86: {"Quinheng": {
0x7523: "CH340",
0x5523: "CH341A",
}},
0x10c4: {"Silicon Labs": {
0xea60: "CP210x", // same ID for CP2101, CP2103, CP2104, CP2109
0xea70: "CP2105",
0xea71: "CP2108",
}},
0x067b: {"Prolific": {
0x2303: "PL2303"
}}
}

const config = {
"DEBUG" : true,
"DEFAULT_BAUD_RATE" : 115200,
//CH34x --> https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c <-- we have used the linux driver and made into a webUSB driver
"CH340": {
"BAUD_RATES" : [600,1200,2400,4800,9600,14400,19200,38400,57600,76800,115200,230400], // highest is 300 0000 limited by the BAUD_RATE_MAX_BPS
"REQUEST_READ_VERSION" : 0x5F,
"REQUEST_READ_REGISTRY" : 0x95,
"REQUEST_WRITE_REGISTRY" : 0x9A,
"REQUEST_SERIAL_INITIATION" : 0xA1,
"REG_DIVISOR" : 0x13,
"REG_PRESCALER" : 0x12,
"REG_LCR_1" : 0x18,
"REG_LCR_2" : 0x25,
"REG_MODEM_CTRL" : 0xA4,
"REG_MODEM_VALUE_OFF" : 0xFF,
"REG_MODEM_VALUE_ON" : 0xDF,
"REG_MODEM_VALUE_CALL" : 0x9F, // what does it do??
//"BAUD_RATE_CHIP_CLOCK_FREQ" : 48000000, // which one is it? 12MHz is the official value for the 340...
"BAUD_RATE_CHIP_CLOCK_FREQ" : 12000000,
"BAUD_RATE_MAX_DIVISOR" : function (ps, fact) {
return (1 << (12 - 3 * (ps) - (fact)))
},
"BAUD_RATE_MIN" : function (ps) {
return (config.CH340.BAUD_RATE_CHIP_CLOCK_FREQ / (config.CH340.BAUD_RATE_MAX_DIVISOR(ps, 1) * 512))
},
"BAUD_RATE_MIN_BPS" : function () { // 47 bps
return Math.ceil((config.CH340.BAUD_RATE_CHIP_CLOCK_FREQ + (config.CH340.BAUD_RATE_MAX_DIVISOR(0, 0) * 256) - 1) / (config.CH340.BAUD_RATE_MAX_DIVISOR(0, 0) * 256)); //Linux DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
},
"BAUD_RATE_MAX_BPS" : function () { // 3000000 bps
return Math.floor(config.CH340.BAUD_RATE_CHIP_CLOCK_FREQ / (config.CH340.BAUD_RATE_MAX_DIVISOR(3, 0) * 2));
},
"QUIRK_LIMITED_PRESCALER" : 0b0, //binary 0
"QUIRK_SIMULATE_BREAK" : 0b1, //binary 1
"QUIRKS" : 0,
"LCR_ENABLE_RX" : 0x80,
"LCR_ENABLE_TX" : 0x40,
"LCR_DATA_BITS_8N1" : 0x03,
"LCR" : function () {
return config.CH340.LCR_ENABLE_RX | config.CH340.LCR_ENABLE_TX | config.CH340.LCR_DATA_BITS_8N1;
},
"FLAG_DTR" : false,
"FLAG_RTS" : false,
"REG_MCR_DTR" : 0x20,
"REG_MCR_RTS" : 0x40,
"MCR" : function () {
return (config.CH340.FLAG_RTS ? config.CH340.REG_MCR_RTS : 0) | (config.CH340.FLAG_DTR ? config.CH340.REG_MCR_DTR : 0); // if false, set to 0, else set to value of the REG_MCR_<...>
},
}
}

const serial = {};
let device = {};
let port;

(function() {
'use strict';
serial.getPorts = function() {
return navigator.usb.getDevices().then(devices => {
return devices.map(device => new serial.Port(device));
});
};

serial.requestPort = function() {
let supportedHardware = [];
//This one create the filter of hardware based on the hardware table
Object.keys(table).map(vendorId => {
Object.keys(table[vendorId]).map(vendorName => {
Object.keys(table[vendorId][vendorName]).map(productId => {
supportedHardware.push({
"vendorId": vendorId,
"productId": productId
})
})
})});
//device contains the "device descriptor" (see USB standard), add as a new device to be able to control
return navigator.usb.requestDevice({ 'filters': supportedHardware }).then(
device => new serial.Port(device)
);
}

//set it to the active device..
serial.Port = function(device) {
this.device_ = device;
};

//here's the config + read loop is taking place....
serial.Port.prototype.connect = function() {
//this is the read loop on whatever port is currently used... it will repeat itself
let readLoop = () => {
this.device_.transferIn(this.endpointIn_, 64).then(result => {
this.onReceive(result.data);
readLoop();
}, error => {
this.onReceiveError(error);
});
};

return this.device_.open()
.then(() => {
//first we get some GUI stuff populated, we use "device" for that... serial and port are used for the configuration elsewhere
device.hostName = port.device_.productName;
device.vendorName = Object.keys(table[port.device_.vendorId])[0];
device.chip = table[port.device_.vendorId][device.vendorName][port.device_.productId];
device.serialNumber = port.device_.serialNumber;
device.manufacturerName = port.device_.manufacturerName;
//1: we set an configuration (configuration descriptor in the USB standard)
if (this.device_.configuration === null) {
return this.device_.selectConfiguration(1);
}
})
.then(() => {
//2: we set what endpoints for data we will use, we use only "bulk" transfer and thus we parse their addresses
let configInterfaces = this.device_.configuration.interfaces;
configInterfaces.forEach(element => {
element.alternates.forEach(elementalt => {
if (elementalt.interfaceClass === 0xff) {
this.interfaceNumber_ = element.interfaceNumber;
elementalt.endpoints.forEach(elementendpoint => {
//This part here get the bulk in and out endpoints programmatically
if (elementendpoint.direction === "out" && elementendpoint.type === "bulk") {
this.endpointOut_ = elementendpoint.endpointNumber;
this.endpointOutPacketSize_ = elementendpoint.packetSize;
}
if (elementendpoint.direction === "in" && elementendpoint.type === "bulk") {
this.endpointIn_ = elementendpoint.endpointNumber;
this.endpointInPacketSize_ = elementendpoint.packetSize;
}
})
}
})
})
})
//3: we claim this interface and select the alternative interface
.then(() => this.device_.claimInterface(this.interfaceNumber_))
.then(() => this.device_.selectAlternateInterface(this.interfaceNumber_, 0))
//4: we configure in and out transmissions, based on detected hardware
.then(() => serial[device.chip](this))
//5: we start the loop
.then(() => {
//console.log(this);
readLoop();
})
};

//upon disconnect, what to do
serial.Port.prototype.disconnect = async function() {
await serial[device.chip](this).DISCONNECT;
};

//send data, what to do
serial.Port.prototype.send = function(data) {
return this.device_.transferOut(this.endpointOut_, data);
};

serial.controlledTransfer = async function (object, direction, type, recipient, request, value = 0, data = new DataView(new ArrayBuffer(0)), index = object.interfaceNumber_) {
direction = direction.charAt(0).toUpperCase() + direction.slice(1);
type = type.toLowerCase();
recipient = recipient.toLowerCase();
if (data.byteLength === 0 && direction === "In") {
// we set how many bits we want back for an "in"
// so set data = 0....N in the call otherwise it will default to 0
data = 0;
}
return await object.device_["controlTransfer" + direction]({
'requestType': type,
'recipient': recipient,
'request': request,
'value': value,
'index': index
}, data)
.then(res => {
if (config.DEBUG) {
//debugger; // remove comment for extra debugging tools
console.log(res);
}
if (res.status !== "ok") {
let errorRequest = `
controlTransfer` + direction + `
'requestType': ` + type + `,
'recipient': ` + recipient + `,
'request': 0x` + request.toString(16) + `,
'value': 0x` + value.toString(16) + `,
'index': 0x` + index.toString(16) + `
}`;
console.warn("error!", errorRequest, data) // add more here
}
if (res.data !== undefined && res.data.buffer !== undefined) {
return res.data.buffer;
}
return null;
});
};

// you can really use any numerical value since JS treat them the same:
// dec = 15 // dec will be set to 15
// bin = 0b1111; // bin will be set to 15
// oct = 0o17; // oct will be set to 15
// oxx = 017; // oxx will be set to 15
// hex = 0xF; // hex will be set to 15
// note: bB oO xX are all valid
serial.hexToDataView = function (number) {
if (number === 0) {
let array = new Uint8Array([0]);
return new DataView(array.buffer)
}
let hexString = number.toString(16);
// split the string into pairs of octets
let pairs = hexString.match(/[\dA-F]{2}/gi);
// convert the octets to integers
let integers = pairs.map(function(s) {
return parseInt(s, 16);
});
let array = new Uint8Array(integers);
return new DataView(array.buffer);
}

// you can give this method a string like "00 AA F2 01 23" and it will turn it into a DataView for the webUSB API transfer data
serial.hexStringArrayToDataView = function (hexString) {
// remove the leading 0x (if any)
hexString = hexString.replace(/^0x/, '');
// split the string into pairs of octets
let pairs = hexString.split(/ /);
// convert the octets to integers
let integers = pairs.map(function(s) {
return parseInt(s, 16);
});
let array = new Uint8Array(integers);
return new DataView(array.buffer);
}

// these are the hardware specific initialization procedures...
serial["CH340"] = async function (obj, baudRate = config.DEFAULT_BAUD_RATE) {
/*
direction request value
in 0xA1 0xC39C -
in 0x9A 0xF2C -
in 0xA4 0xDF - modem on? 0xFF modem off?
in 0xA4 0x9F - modem?
in 0x95 0x706 - got 2 bytes
in 0x9A 0x2727 - LCR wrong??
in 0x9A 0x1312 - baud rate seems ok
in 0x95 0x706 - got 2 bytes
in 0x9A 0x2727 -
*/
/* await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REQUEST_READ_REGISTRY, 0x706); //test
return;
await serial.controlledTransfer(obj,"out", "vendor", "device", config.CH340.REQUEST_SERIAL_INITIATION, 0xC39C); // test
await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REQUEST_WRITE_REGISTRY, 0xF2C); // test
await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REG_MODEM_CTRL, config.CH340.REG_MODEM_VALUE_ON); // test
await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REG_MODEM_CTRL, config.CH340.REG_MODEM_VALUE_CALL); // test
await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REQUEST_READ_REGISTRY, 0x706); //test
await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REQUEST_WRITE_REGISTRY, 0x2727); //test
await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REQUEST_WRITE_REGISTRY, 0x1312); //test
await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REQUEST_READ_REGISTRY, 0x706); //test
await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REQUEST_WRITE_REGISTRY, 0x2727); //test
*/

await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REQUEST_READ_VERSION); // we expect to get a OK with the response of [48, 00] 0x30 0x00 or 0x27 0x00.... you can look at the response by adding a "let r = await serial.controlledTrans....." and then console log the "r"
//return; // if I uncomment this and thus not gonna continue the script I get gibbrish.... but as far as I can tell I have followed the linux initialization ?
await serial.controlledTransfer(obj,"out", "vendor", "device", config.CH340.REQUEST_SERIAL_INITIATION);
await serial["CH340"].setBaudRate(obj, baudRate);
await serial.controlledTransfer(obj,"out", "vendor", "device", config.CH340.REG_MODEM_CTRL, config.CH340.MCR()); // handshake

// now what? all the control transfers came back "ok"?
}

serial["CH340"].setBaudRate = async function (obj, baudRate) {
let data = serial["CH340"].getDivisor(baudRate);
// CH34x buffers data until a full endpoint size packet (32 bytes) has been reached unless bit 7 is set
data |= (1 << 7); // data |= (1 << 6); //seems correct for the 7th bit??
data = serial.hexToDataView(data);
await serial.controlledTransfer(obj,"out", "vendor", "device", config.CH340.REQUEST_WRITE_REGISTRY, (config.CH340.REG_DIVISOR << 8 | config.CH340.REG_PRESCALER), data);
data = serial.hexToDataView(config.CH340.LCR());
await serial.controlledTransfer(obj,"out", "vendor", "device", config.CH340.REQUEST_WRITE_REGISTRY, (config.CH340.REG_LCR_2 << 8 | config.CH340.REG_LCR_1), data);
}

serial["CH340"].getDivisor = function (baudRate) {
let forceFactor0 = false;
// make sure our baud rate is with the min max
let baudRateChecked = Math.min(Math.max(parseInt(baudRate), config.CH340.BAUD_RATE_MIN_BPS()), config.CH340.BAUD_RATE_MAX_BPS());
// start with highest possible base clock (factor = 1) that will give a divisor strictly less than 512.
let factor = 1;
let ps = 3;
for (ps; ps >= 0; ps--) {
if (baudRateChecked > config.CH340.BAUD_RATE_MIN(ps)) {
break;
}
}
// determine corresponding divisor, rounding down
let clockDivisor = Math.floor(config.CH340.BAUD_RATE_MAX_DIVISOR(ps, factor));
let divisor = config.CH340.BAUD_RATE_CHIP_CLOCK_FREQ / (clockDivisor * baudRateChecked);
// some devices require a lower base clock if ps < 3
if (ps < 3 && (config.CH340.QUIRKS & config.CH340.QUIRK_LIMITED_PRESCALER)) {
forceFactor0 = true;
}
// if we force a factor = 0 or have divisors outside range, split the base clock divisor by 2 and make factor=0
if (divisor < 9 || divisor > 255 || forceFactor0) {
divisor /= 2;
clockDivisor *= 2;
factor = 0;
}
divisor = Math.ceil(divisor);
// pick next divisor if resulting rate is closer to the requested one, scale up (16x) to avoid rounding errors on low rates.
let compare1 = 16 * config.CH340.BAUD_RATE_CHIP_CLOCK_FREQ / (clockDivisor * divisor) - 16 * baudRateChecked;
let compare2 = 16 * baudRateChecked - 16 * (config.CH340.BAUD_RATE_CHIP_CLOCK_FREQ / (clockDivisor * (divisor + 1)));
if (compare1 >= compare2) {
divisor++;
}
// prefer lower base clock (factor = 0) if even divisor "divisor % 2"... this makes the receiver more tolerant to errors
if (factor === 1 && (divisor % 2 === 0) ) {
divisor /= 2;
factor = 0;
}
return (0x100 - divisor) << 8 | factor << 2 | ps;
}

serial["CH340"].DISCONNECT = async function (obj) {
await serial.controlledTransfer(obj,"in", "vendor", "device", config.CH340.REG_MODEM_CTRL, config.CH340.REG_MODEM_VALUE_OFF);
}

serial["CP210x"] = async function (obj, baudRate = config.DEFAULT_BAUD_RATE) {

}

serial["CP2105"] = async function (obj, baudRate = config.DEFAULT_BAUD_RATE) {

}

serial["CP2108"] = async function (obj, baudRate = config.DEFAULT_BAUD_RATE) {

}

serial["PL2303"] = async function (obj, baudRate = config.DEFAULT_BAUD_RATE) {

}

serial["FT2232H"] = async function (obj, baudRate = config.DEFAULT_BAUD_RATE) {

}

serial["FT4232H"] = async function (obj, baudRate = config.DEFAULT_BAUD_RATE) {

}

serial["FT232H"] = async function (obj, baudRate = config.DEFAULT_BAUD_RATE) {

}

serial["FT231X"] = async function (obj, baudRate = config.DEFAULT_BAUD_RATE) {

}
})();

//GUI function "connect"
function connect() {
port.connect().then(() => {
document.getElementById('editor').value = "connected to: " + device.hostName + "\nvendor name: " + device.vendorName + "\nchip type: " + device.chip;
port.onReceive = data => {
console.log(data);
document.getElementById('output').value += new TextDecoder().decode(data);
}
port.onReceiveError = error => {
//console.error(error);
port.disconnect();
};
});
}

//GUI function "disconnect"
function disconnect() {
port.disconnect();
}

//GUI function "send"
function send(string) {
console.log("sending to serial:" + string.length);
if (string.length === 0)
return;
console.log("sending to serial: [" + string +"]\n");

let data = new TextEncoder('utf-8').encode(string);
console.log(data);
if (port) {
port.send(data);
}
}

//the init function which we have an event listener connected to
function initiate(){
serial.getPorts()
.then(ports => {
//these are devices already paired, let's try the first one...
if (ports.length > 0) {
port = ports[0];
connect();
}
});

document.querySelector("#connect").onclick = async function () {
await serial.requestPort().then(selectedPort => {
if (port === undefined || port.device_ !== selectedPort.device_) {
port = selectedPort;
connect();
} else {
// port already selected...
}
});
}

document.querySelector("#disconnect").onclick = function() {
disconnect()
}

document.querySelector("#submit").onclick = () => {
let source = document.querySelector("#editor").value;
send(source);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="serial.js"></script>
<title>Grovkillen test webusb</title>
</head>


<body>
<button id="connect">Connect</button>
<button id="disconnect">disConnect</button>
<label for="editor">TX</label><textarea id="editor">Flash Easy</textarea>
<button id="submit">Send</button>
<label for="output">RX</label><textarea id="output"></textarea>
</body>

在 Android 10 上运行 Chrome 的 POC
Running Chrome on Android 10
在 Windows 10 上运行 Chrome 的 POC
Running Chrome on Windows 10
作为测试,我使用了名为 Serial USB Terminal 的很棒的软件。对于安卓。而且那个应用程序可以连接得很好,因为我可以在其中设置波特率等......但是看看当我分屏并关闭终端应用程序上的连接并在我的网络应用程序上打开它时发生了什么......显然,故障在于 Chrome 启动连接的方式。
fix

最佳答案

这不是 WebUSB 的缺陷,而是您正在运行的脚本的缺陷。 Arduino 示例存储库中包含的 serial.js 脚本旨在与 Arduino 设备配合使用,这些设备不需要设置波特率,因为端口是虚拟的。为了在 USB 到串行适配器上设置波特率,您需要发送 SET_LINE_CODING 控制传输。这应该在现有代码中的 SET_CONTROL_LINE_STATE 命令之前。这里有控制传输结构的文档:
https://github.com/MarkDing/lufa-efm32#311-set-line-coding

关于javascript - webUSB api 工作但接收到的数据未正确解码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64929987/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com