Code Monkey home page Code Monkey logo

Comments (16)

reillyeon avatar reillyeon commented on May 16, 2024

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.

karelv avatar karelv commented on May 16, 2024

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.

reillyeon avatar reillyeon commented on May 16, 2024

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.

karelv avatar karelv commented on May 16, 2024

Thanks for your prompt answer!

Yeah, I understand it is a different beast...
See here the device-log:
image

from web-serial-polyfill.

karelv avatar karelv commented on May 16, 2024

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.

karelv avatar karelv commented on May 16, 2024

This made me realize I need to supply the bottom of that page....
image

from web-serial-polyfill.

karelv avatar karelv commented on May 16, 2024

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.

reillyeon avatar reillyeon commented on May 16, 2024

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.

karelv avatar karelv commented on May 16, 2024

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.

karelv avatar karelv commented on May 16, 2024

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.

reillyeon avatar reillyeon commented on May 16, 2024

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.

karelv avatar karelv commented on May 16, 2024

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.

karelv avatar karelv commented on May 16, 2024
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.

karelv avatar karelv commented on May 16, 2024

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.

karelv avatar karelv commented on May 16, 2024

Actually it still happens... (it takes just a whole lot longer!)

from web-serial-polyfill.

karelv avatar karelv commented on May 16, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.