Comments (16)
If you're using vanilla JavaScript (rather than building your app with a bundler that can import the NPM module), do this:
<script type="module">
import { serial as webSerialPolyfill } from "https://cdn.jsdelivr.net/npm/[email protected]";
// Use webSerialPolyfill as you would navigator.serial.
</script>
from web-serial-polyfill.
Awesome, that works!
At least for 99.9% of the cases.
After a while(some seconds) I got an error:
NetworkError: Failed to execute 'transferIn' on 'USBDevice': A transfer error has occurred.
I have quite some data coming in over the CDC UART, but I have not seen this on a desktop with navigator.serial
.
Any hints?
from web-serial-polyfill.
Open chrome://device-log
on the device to see the low-level USB error message. It may also be helpful to collect a bug report from the device in order to look at the kernel log. When running on a phone it's possible there are novel issues like insufficient power to reliably run the device.
from web-serial-polyfill.
Thanks for your prompt answer!
Yeah, I understand it is a different beast...
See here the device-log:
from web-serial-polyfill.
Sorry, the screenshot shows a little big... also note that I got the 'transferIn' error only once on my webpage. It disconnects.
When the error arrives on my web-page, it is displayed in textarea, the datetime was "2023/11/21 11:17:06.862"
from web-serial-polyfill.
This made me realize I need to supply the bottom of that page....
from web-serial-polyfill.
Some other datapoints:
1] I'm trying to discern what happens on the MCU-side. After the error message appears, I clear the device-log, then I reload the page(app), and re-connect to the serial port, while keeping the MCU in the USB port of the phone. This works correctly, until that error appears again. Therefore I believe the MCU keeps running in case of that event. (Also I need to sent something to the MCU before it sends on a regular basis something back).
2] I have tried to send less often (every 2 seconds), but that does not help either.
3] I don't know how big the data-chunks are that the MCU is sending over, is there a limit of what web-serial-polyfill
can receive at once?
3bis] I changed the firmware to perform a 'flush' every 32 bytes, no change.
4] Same behavior without the 'Generic Billboard Device' (usb-hub device), and thus using a direct connection between MCU and the phone (USBC to USBA).
from web-serial-polyfill.
I think the issue is that the Linux kernel imposes a limit on the total size of outstanding USB transfers in order to limit the amount of kernel memory that is consumed. It's not about how much data the device is sending but how much the page is asking for at once. If it ends up creating too many transferIn()
calls or specifies too large a transfer size then you'll get this error. As written the polyfill should only be issuing one transferIn()
call at once (having multiple would be a nice performance optimization) and the transfer size is based on the desiredSize
reported by the ReadableStream
, which comes from the bufferSize
parameter passed in when opening the port. What do you have this set to?
from web-serial-polyfill.
I guess you mean this parameter:
https://developer.mozilla.org/en-US/docs/Web/API/SerialPort/open#buffersize
I'm trying to increase it, I was using the default, which is 255 bytes, but I can confirm my chunks are larger!
I keep you posted.
from web-serial-polyfill.
Okay here it is:
I have increased the bufferSize to 10000 ==> after first datachunk received, it failed
Then I changed it to 32 ==> similar results as before
Then I changed it to 5000 ==> it failed after the 2nd datachunk.
Note: A datachuck is 4635 bytes long.
Is there something I can do in the webpage, to always empty the buffer; and thus buffer it inside the browser in stead of inside the kernel?
Note2: I checked 'device-log', same error.
from web-serial-polyfill.
Is there something I can do in the webpage, to always empty the buffer; and thus buffer it inside the browser in stead of inside the kernel?
USB is a pull-based transport rather than a push-based transport. This means that the device never sends data unless asked. So data can't get buffered in the kernel. The site asks for N bytes of data, the kernel sets aside N bytes of memory and starts asking the device for data. When it gets some that completes the transaction and the data is sent to the site. If the device sent more than N bytes that's called a "babble" error and doesn't seem to be the problem you're seeing here.
The "out of memory" error is the kernel complaining that the sum of all the transfers Chrome has requested is greater than an internal limit it has set. Given how the polyfill is implemented this should't happen because there's only ever one IN transfer requested at a time and the size is based on the requested buffer size, which you've said is very small.
My best guess of what's going on here is that there is a bug in the polyfill causing it to not wait for each transfer to complete and is instead submitting lots of transfers at once. The larger the buffer size the more quickly that will cause problems. Either that or you are also trying to send a very large chunk of data to the device.
To debug further you'll need to put some debug logging into the polyfill itself to understand when and how often it is calling transferIn()
. You can rebuild the library by checking out this repository and running,
npm install # One-time setup
npm run build # Generates dist/serial.js
from web-serial-polyfill.
This is again very helpful!
I have build the dist/serial.js
And have since I have no console.log
, I added a div on the page and updated the serial.js file as follows:
try {
let elem = document.querySelector('#console');
my_counter = my_counter + 1;
let local_counter = my_counter;
elem.innerHTML = elem.innerHTML + "<hr>START["+local_counter.toString()+"]: chunkSize: "+chunkSize.toString()+" -- this.endpoint_.endpointNumber: "+this.endpoint_.endpointNumber.toString()+" <br>";
const result = await this.device_.transferIn(this.endpoint_.endpointNumber, chunkSize);
elem.innerHTML = elem.innerHTML + "RESULT["+local_counter.toString()+"]: "+result.status+"<br>";
if (result.status != 'ok') {
controller.error(`USB error: ${result.status}`);
this.onError_();
}
if ((_a = result.data) === null || _a === void 0 ? void 0 : _a.buffer) {
elem.innerHTML = elem.innerHTML + "DATA["+local_counter.toString()+"]: byteOffset: "+result.data.byteOffset.toString()+" | byteLength: "+result.data.byteLength.toString()+" <hr>";
const chunk = new Uint8Array(result.data.buffer, result.data.byteOffset, result.data.byteLength);
controller.enqueue(chunk);
}
Note that my_counter
is a global variable, while local_counter
has a scope only for within the function.
Now, I must say that my knowledge about await
and asynch
programming is limited. So I hope you can make sense of the log it has generated. (see below)
from web-serial-polyfill.
console:
START[1]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[1]: ok
DATA[1]: byteOffset: 0 | byteLength: 38
START[2]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[3]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[2]: ok
DATA[2]: byteOffset: 0 | byteLength: 2
START[4]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[5]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[3]: ok
DATA[3]: byteOffset: 0 | byteLength: 109
START[6]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[7]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[4]: ok
DATA[4]: byteOffset: 0 | byteLength: 1
START[8]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[9]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[5]: ok
DATA[5]: byteOffset: 0 | byteLength: 2
START[10]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[11]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[6]: ok
DATA[6]: byteOffset: 0 | byteLength: 1
START[12]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[13]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[7]: ok
DATA[7]: byteOffset: 0 | byteLength: 1
START[14]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[15]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[8]: ok
DATA[8]: byteOffset: 0 | byteLength: 1
START[16]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[17]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[9]: ok
DATA[9]: byteOffset: 0 | byteLength: 24
START[18]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[19]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[10]: ok
DATA[10]: byteOffset: 0 | byteLength: 1
START[20]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[21]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[11]: ok
DATA[11]: byteOffset: 0 | byteLength: 1
START[22]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[23]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[12]: ok
DATA[12]: byteOffset: 0 | byteLength: 2
START[24]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[25]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[13]: ok
DATA[13]: byteOffset: 0 | byteLength: 2
START[26]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[27]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[14]: ok
DATA[14]: byteOffset: 0 | byteLength: 1
START[28]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[29]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[15]: ok
DATA[15]: byteOffset: 0 | byteLength: 1
START[30]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[31]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[16]: ok
DATA[16]: byteOffset: 0 | byteLength: 1
START[32]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[33]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[17]: ok
DATA[17]: byteOffset: 0 | byteLength: 1
START[34]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[35]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[18]: ok
DATA[18]: byteOffset: 0 | byteLength: 1
START[36]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[37]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[19]: ok
DATA[19]: byteOffset: 0 | byteLength: 1
START[38]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[39]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[20]: ok
DATA[20]: byteOffset: 0 | byteLength: 2
START[40]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[41]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[21]: ok
DATA[21]: byteOffset: 0 | byteLength: 1
START[42]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[43]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[22]: ok
DATA[22]: byteOffset: 0 | byteLength: 2
START[44]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[45]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[23]: ok
DATA[23]: byteOffset: 0 | byteLength: 1
START[46]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[47]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[24]: ok
DATA[24]: byteOffset: 0 | byteLength: 1
START[48]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[49]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[25]: ok
DATA[25]: byteOffset: 0 | byteLength: 1
START[50]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[51]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[26]: ok
DATA[26]: byteOffset: 0 | byteLength: 1
START[52]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[53]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[27]: ok
DATA[27]: byteOffset: 0 | byteLength: 1
START[54]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[55]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[28]: ok
DATA[28]: byteOffset: 0 | byteLength: 1
START[56]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[57]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[29]: ok
DATA[29]: byteOffset: 0 | byteLength: 2
START[58]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[59]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[30]: ok
DATA[30]: byteOffset: 0 | byteLength: 1
START[60]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[61]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[31]: ok
DATA[31]: byteOffset: 0 | byteLength: 1
START[62]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[63]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[32]: ok
DATA[32]: byteOffset: 0 | byteLength: 1
START[64]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[65]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[33]: ok
DATA[33]: byteOffset: 0 | byteLength: 1
START[66]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[67]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[34]: ok
DATA[34]: byteOffset: 0 | byteLength: 1
START[68]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[69]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[35]: ok
DATA[35]: byteOffset: 0 | byteLength: 2
START[70]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[71]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[36]: ok
DATA[36]: byteOffset: 0 | byteLength: 1
START[72]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[73]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[37]: ok
DATA[37]: byteOffset: 0 | byteLength: 1
START[74]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[75]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[38]: ok
DATA[38]: byteOffset: 0 | byteLength: 1
START[76]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[77]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[39]: ok
DATA[39]: byteOffset: 0 | byteLength: 55
START[78]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[79]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[40]: ok
DATA[40]: byteOffset: 0 | byteLength: 161
START[80]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[81]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[41]: ok
DATA[41]: byteOffset: 0 | byteLength: 1
START[82]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[83]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[42]: ok
DATA[42]: byteOffset: 0 | byteLength: 1
START[84]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[85]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[43]: ok
DATA[43]: byteOffset: 0 | byteLength: 1
START[86]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[87]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[44]: ok
DATA[44]: byteOffset: 0 | byteLength: 2
START[88]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[89]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[45]: ok
DATA[45]: byteOffset: 0 | byteLength: 1
START[90]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[91]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[46]: ok
DATA[46]: byteOffset: 0 | byteLength: 1
START[92]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[93]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[47]: ok
DATA[47]: byteOffset: 0 | byteLength: 1
START[94]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[95]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[48]: ok
DATA[48]: byteOffset: 0 | byteLength: 1
START[96]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[97]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[49]: ok
DATA[49]: byteOffset: 0 | byteLength: 1
START[98]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[99]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[50]: ok
DATA[50]: byteOffset: 0 | byteLength: 1
START[100]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[101]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[51]: ok
DATA[51]: byteOffset: 0 | byteLength: 2
START[102]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[103]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[52]: ok
DATA[52]: byteOffset: 0 | byteLength: 1
START[104]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[105]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[53]: ok
DATA[53]: byteOffset: 0 | byteLength: 1
START[106]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[107]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[54]: ok
DATA[54]: byteOffset: 0 | byteLength: 3
START[108]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[109]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[55]: ok
DATA[55]: byteOffset: 0 | byteLength: 2
START[110]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[111]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[56]: ok
DATA[56]: byteOffset: 0 | byteLength: 2
START[112]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[113]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
RESULT[57]: ok
DATA[57]: byteOffset: 0 | byteLength: 2
START[114]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
START[115]: chunkSize: 5056 -- this.endpoint_.endpointNumber: 2
As you can see it looks like there are more than one entry into the transferIn
function...
Not sure what the next steps should be...
from web-serial-polyfill.
Hey reillyeon, Many thanks for your help!
I have tried to isolate the issue by making a small web-page demonstrator for web-serial (both -api and -polyfill).
Have a look here: https://github.com/karelv/web-serial-example and here https://karelv.github.io/web-serial-example/
It seems to keep working, meaning I need to review my more complex project, I guess.
Note: when use the serial.js with my_counter & local_counter, it still indicates there are several transferIn called at the same time.
from web-serial-polyfill.
Actually it still happens... (it takes just a whole lot longer!)
from web-serial-polyfill.
Let's close this one, I will make another ticket, as the example script is clear:
https://github.com/karelv/web-serial-example
from web-serial-polyfill.
Related Issues (20)
- Adding and removing event listeners HOT 1
- No compiled file published to npm in 1.0.7 HOT 8
- Unable to find interface with class 2
- Implement support for Prolific virtual COM port over WebUSB HOT 6
- add optional parameter signalOptions to open HOT 2
- Implement support for CDC composite gadget HOT 3
- Nothing happens after clicking RequestInfo HOT 2
- serial api(polyfill) show "access Denied" in windows and android chrome. HOT 5
- web-serial-polyfill and web-serial desktop and Android HOT 4
- Feature Request: Seamless Javascript integration between web-serial-polyfill and webSerial HOT 1
- Create a readable byte stream
- TypeError: serial.SerialPort is not a constructor
- Android: usb.getDevices() succeeds but serial.getDevices() fails HOT 3
- WebSerial update? HOT 5
- Static web serial Polyfill NPM example please. HOT 8
- Feature request: Design a webpage that informs if webSerial or polyfill works on the device. HOT 2
- Which phones support Android Polyfill? HOT 14
- Add support for bluetooth serial devices HOT 4
- NetworkError: Failed to execute 'transferIn' on 'USBDevice': A transfer error has occurred. HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from web-serial-polyfill.