Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

port/rp2: Added WebUSB to RP2040. #11301

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

JackAtKitronik
Copy link

Added basic WebUSB functionality into the RP2040 port using tinyusb.

The URL (creepier-dragonfly-8429.dataplicity.io) used in the WebUSB URL Descriptor is a temporary website we are using to test WebUSB on the Pico. It is a modified version of Edublocks to include an RP2040 mode that supports WebUSB.

Adding WebUSB to the Pico and other RP2040 based boards could really help with the adoption of the RP2040, particularly in schools where installing additional software is a pain.

@JackAtKitronik JackAtKitronik changed the title port/rp2: Added WebUSB to RP2040 port/rp2: Added WebUSB to RP2040. Apr 20, 2023
Signed-off-by: Jack Faulkner <jack.faulkner@kitronik.co.uk>
@codecov-commenter
Copy link

Codecov Report

Merging #11301 (75ce05d) into master (b525f1c) will not change coverage.
The diff coverage is n/a.

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

@@           Coverage Diff           @@
##           master   #11301   +/-   ##
=======================================
  Coverage   98.50%   98.50%           
=======================================
  Files         155      155           
  Lines       20549    20549           
=======================================
  Hits        20241    20241           
  Misses        308      308           

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@JackAtKitronik
Copy link
Author

Hi @dpgeorge, I don't understand why these checks have failed. I have tried to fix it but the error messages don't give much indication of why they aren't successful. Thanks in advance for any help!

@Gadgetoid
Copy link
Contributor

From the commit message formatting error:

error: commit cfa9e12: Subject line: port/rp2: Added WebUSB to RP2040
error: commit cfa9e12: * should end with "."
error: commit cfa9e12: * should match: '^[^!]+: [A-Z]+.+ .+\\.$'
error: commit cfa9e12: * Example: "py/runtime: Add support for foo to bar."
warning: commit cfa9e12: Message should be signed-off. Use "git commit -s".
See https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md

Eg, port/rp2: Added WebUSB to RP2040 should be port/rp2: Add WebUSB to RP2040. and you should use git commit -s to sign it off.

The mentioned file - https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md - has more human-readable details.

The code formatting produces a diff to correct your code: https://github.com/micropython/micropython/actions/runs/4753003928/jobs/8444066321?pr=11301

You can rebase to amend your commit and run these tools locally:

source tools/ci.sh && ci_code_formatting_setup && ci_code_formatting_run

@Gadgetoid
Copy link
Contributor

CI issues aside- this will conflict #9497 and I believe the preferred approach would be for that to be finished & merged and for WebUSB drivers to be written in Python as per the examples here micropython/micropython-lib#558

@JackAtKitronik
Copy link
Author

CI issues aside- this will conflict #9497 and I believe the preferred approach would be for that to be finished & merged and for WebUSB drivers to be written in Python as per the examples here micropython/micropython-lib#558

Why is this the preferred approach rather than adding WebUSB into the ports? I'm not sure I understand why writting the USB interfaces in Python is better.

If this is the approach which is preferred for MicroPython, is there any timeline on when this will be added?

WebUSB would be massively helpful for getting Pico and MicroPython adopted in schools. So the sooner WebUSB support is added the better.

@dpgeorge This is ready now and can be used on RP2040 based board right away..

@Gadgetoid
Copy link
Contributor

Why is this the preferred approach rather than adding WebUSB into the ports? I'm not sure I understand why writting the USB interfaces in Python is better.

I am only guessing based on the existing PRs and discussion but the simple answer is: it's flexible. Right now we're missing USB HID support and so forth on Pico, and those are hopefully going to come in some form or another. Piling everything into C doesn't work very well on a micro with limited RAM/flash and adding "just one little thing" every now and then eventually becomes a problem.

If this is the approach which is preferred for MicroPython, is there any timeline on when this will be added?

It looks like it is, as per: #9356 (comment)

I don't dare even finger-in-the-air guess at timescales.

Worst case, you can build and deploy your own fork of MicroPython for edu purposes in the meantime. It's relatively easy to get GitHub to generate firmware builds and I've got more experience with it than I'd care to admit if you need a hand.

ports/rp2/main.c Outdated
#include "main.h"
#endif

#ifndef MICROPY_GC_HEAP_SIZE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is inadvertently reverting c80e7c1

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was leftover from a previous approach I was trying. It has been removed now.

@@ -71,6 +88,25 @@ bi_decl(bi_program_feature_group_with_flags(BINARY_INFO_TAG_MICROPYTHON,
BINARY_INFO_ID_MP_FROZEN, "frozen modules",
BI_NAMED_GROUP_SEPARATE_COMMAS | BI_NAMED_GROUP_SORT_ALPHA));

#if MICROPY_HW_USB_VENDOR
// WebUSB URL Descriptor
#define URL "creepier-dragonfly-8429.dataplicity.io"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the end-user override this? Is there just one blessed front-end for development via WebUSB?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user cannot override this. It is a temorary URL we're using to test the implementation. It would idealy be replaced with a link to something like a MicroPython page about WebUSB support with a list of supported WebUSB enabled editors.

ports/rp2/main.c Outdated
break;

case TUSB_REQ_TYPE_CLASS:
if (request->bRequest == 0x22) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does 0x22 have a human-readable representation?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been updated to a more readable format.

@JackAtKitronik
Copy link
Author

I am only guessing based on the existing PRs and discussion but the simple answer is: it's flexible. Right now we're missing USB HID support and so forth on Pico, and those are hopefully going to come in some form or another. Piling everything into C doesn't work very well on a micro with limited RAM/flash and adding "just one little thing" every now and then eventually becomes a problem.

Okay. I can see how that would give the user more freedom in how they can setup their device, but how will exposing the bare API and writing custom USB devices in Python use less resources than having them implemented in C? Won't the user end up using more resources by having to implement the USB device in Python?

Worst case, you can build and deploy your own fork of MicroPython for edu purposes in the meantime. It's relatively easy to get GitHub to generate firmware builds and I've got more experience with it than I'd care to admit if you need a hand.

We were tring to avoid having our own fork as we don't want to fragment MicroPython more than it already is.

Also, having a different version for WebUSB is likely to confuse teachers and students. Meaning they'll end up using the wrong version and be confused when it doesn't work.

…t number.

Signed-off-by: Jack Faulkner <jack.faulkner@kitronik.co.uk>
@Gadgetoid
Copy link
Contributor

Okay. I can see how that would give the user more freedom in how they can setup their device, but how will exposing the bare API and writing custom USB devices in Python use less resources than having them implemented in C? Won't the user end up using more resources by having to implement the USB device in Python?

It's not so much that it would use fewer overall resources, but the resources it does use are the ones that aren't critically limited. Anyway- I can't make a determination about whether WebUSB would fall into either bucket, but you should be very wary of proposing a change that conflicts something like the USB overhaul.

The USB descriptors - I understand - are required for identifying a board as WebUSB compatible, but what are the other changes doing? MicroPython already has a REPL and raw REPL which work with both Thonny, mpremote (https://docs.micropython.org/en/latest/reference/mpremote.html) and other tools. What's particularly unique about WebUSB that requires this special treatment?

I built your fork locally, flashed it to Pico and connected it to a Windows 11 PC whereupon my browser gave me a WebUSB notification. I then contrived this program in the block editor:

image

When I hit "Flash" it briefly pops up the flashing dialog and the Pico does nothing. There are also no errors or feedback of any type to divine why it might not be working. Installing Thonny on the same PC and copying the same Python code over and running it works.

Is there a minimum viable test you use to determine if the feature is working as intended?

The test block editor is also a bit rough around the edges. Are there any tools out there that would use WebUSB if it were available today?

@JackAtKitronik
Copy link
Author

The USB descriptors - I understand - are required for identifying a board as WebUSB compatible, but what are the other changes doing?

The callbacks in main.c are used to manage the connection with the board and send board descriptors.

The updates in mphalport.c add the WebUSB into the STDIO functions. This allows us to use the REPL over WebUSB.

The changes in shared/tinyusb add the WebUSB descriptor to the list when it is enabled.

What's particularly unique about WebUSB that requires this special treatment?

It allows users to start coding without having to install any software. As it'll work right on the browser (just like the micro:bit editors) this removes the need to install an editor, which is a common barrier in education.

When I hit "Flash" it briefly pops up the flashing dialog and the Pico does nothing. There are also no errors or feedback of any type to divine why it might not be working. Installing Thonny on the same PC and copying the same Python code over and running it works.

The test block editor is also a bit rough around the edges. Are there any tools out there that would use WebUSB if it were available today?

Yes, the editor is a work in progress. Once the Pico has been flashed it currently requires you to disconnect the device and then the program will start running. The idea is to add a "Run" button to the editor once the Pico has been flashed, just like in Thonny.

The editor is a modified version of EduBlocks, which doesn't have any error checking/ feedback. For this to be useful in education we would need to add error checking, but currently it is just to test the viability of the WebUSB implementation.

There aren't currently any web editors that can be used to code the Pico over WebUSB. The test block editor does need additional work before it would be suitable for mainstream use.

Is there a minimum viable test you use to determine if the feature is working as intended?

The current test is to put the custom firmware on the Pico. Flash it using the test editor. Then you either have to power cycle the Pico (which is done when you click the disconnect button on the editor), or you can run the file in the "Serial" tab on the editor by typing "import main". This will be easier to test once there is a "Run" button.

@Gadgetoid
Copy link
Contributor

It allows users to start coding without having to install any software. As it'll work right on the browser (just like the micro:bit editors) this removes the need to install an editor, which is a common barrier in education.

WebSerial already works with MicroPython as-is so why is any change necessary at all?

The extra discoverability is...kinda cool... but in a classroom setting (and since the software to be discovered doesn't exist) does it actually matter?

The current test is to put the custom firmware on the Pico. Flash it using the test editor. Then you either have to power cycle the Pico (which is done when you click the disconnect button on the editor), or you can run the file in the "Serial" tab on the editor by typing "import main". This will be easier to test once there is a "Run" button.

This should probably be mentioned in the PR, along with updates to the MicroPython documentation detailing the WebUSB interface, since nobody is going to merge a change they can't test or use.

@JackAtKitronik
Copy link
Author

WebSerial already works with MicroPython as-is so why is any change necessary at all?

I didn't know of any platforms that supported WebSerial for Pico and provided a code editor. Can you give me some examples so I can take a look?

The extra discoverability is...kinda cool... but in a classroom setting (and since the software to be discovered doesn't exist) does it actually matter?

As far as I am aware, there are no options that allow for coding on the Pico without having to install additional software. The aim is to remove this barrier and allow coding straight away. Removing the need to install additional software makes a big difference in schools.

This should probably be mentioned in the PR, along with updates to the MicroPython documentation detailing the WebUSB interface, since nobody is going to merge a change they can't test or use.

Yes, I will add these updates.

@Gadgetoid
Copy link
Contributor

I didn't know of any platforms that supported WebSerial for Pico and provided a code editor. Can you give me some examples so I can take a look?

That's my point. I can't find anything substantive and the Pico has been around for more than two years. There must be a reason other than "no WebUSB support" why none exist, since WebSerial works just fine with a vanilla Pico on Windows/Edge (ugly serial port name notwithstanding).

On the upside, it looks like Raspberry Pi Org might be aiming to fill this gap. They've launched https://editor.raspberrypi.org/. It may be worth trying to figure out what their game plan is and if it includes code download to Pico/Pico W. There's very little in that editor to suggest it might be MicroPython related, but a possible giveaway is the choice of "main.py" for a default filename.

enum {
VENDOR_REQUEST_WEBUSB = 1,
VENDOR_REQUEST_MICROSOFT = 2,
VENDOR_REQUEST_WEBUSB_CONNECTION = 34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a link for where this vendor request number is from? I can find the other two but seemingly not this one.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got this number from the tinyusb repo in the webusb example.
This number is then used in the browser code to request to connect and disconnect with the device

@projectgus
Copy link
Contributor

projectgus commented Jun 8, 2023

Sorry I haven't had much time lately to look at MicroPython things, planning for this to change soon! But I saw this linked from the "USB in Python" PR so came to take a look.

I had not actually realised that there were device-side extensions for WebUSB, I thought it was purely a browser feature. I found this Chromium document, are there other references that you used to create this PR?

If I understand correctly, there's at least three pieces of functionality in this PR. They're mostly (but maybe not entirely) gated under the MICROPY_HW_USB_VENDOR compile flag.

  • Add the custom WebUSB "OBS" endpoint for the host to request a "URL descriptor" with a URL to help with WebUSB setup.
  • Add the Microsoft custom OS descriptors that cause Windows to replace the driver for the rp2 from the default COM port driver to WinUSB. This would mean the rp2 no longer appears as a COM port (that would have worked with WebSerial or other serial programs) but will bind to WinUSB allowing it to be used with WebUSB instead.
  • Adds handler for a vendor request for "WebUSB Connection", that I didn't find in my quick read-up on WebUSB so I'm not sure what exactly it represents. From reading the code, this controls the REPL behaviour between normal USB-CDC and the WebUSB-mediated serial connection.

... do I understand this correctly?

this will conflict #9497 and I believe the preferred approach would be for that to be finished & merged and for WebUSB drivers to be written in Python as per the examples here micropython/micropython-lib#558

I can see how that would give the user more freedom in how they can setup their device, but how will exposing the bare API and writing custom USB devices in Python use less resources than having them implemented in C? Won't the user end up using more resources by having to implement the USB device in Python?

This is exactly the kind of thing that we're hoping USB support in Python will unlock. There are dozens of possible USB device drivers, and implementing them all via C code and #ifdefs is quite complex compared to having packages that can be mip install-ed in order to bring up different functions.

Regarding resources, so far it's looking very promising on resource use compared to the same functionality in C. There will be some overhead, and probably the very fastest USB throughput or lowest latency would only happen in C, but it's looking pretty good otherwise.

The other advantage is exposing cross-port functionality, so each port doesn't have its own set of supported USB functions. Some of this work has already happened with the move of tinyusb support to shared/tinyusb. Most ports with native USB can or do support TinyUSB already, the exception is stm32 port but once a USB Python API is introduced then it doesn't matter so much what the underlying C layer driver is.

If this is the approach which is preferred for MicroPython, is there any timeline on when this will be added?

Unfortunately it's been stalled for some time, but I'm planning to work on it again and focus on it starting from July. I don't think anyone knows of a timeline for completion and merging, though.

WebSerial already works with MicroPython as-is so why is any change necessary at all?

This seems like a legitimate question to ask. If this change (however it is implemented) disables the existing COM port function under Windows then it's always going to be something that has to be installed or configured in addition to the base rp2 firmware, rather than working "out of the box" on the default rp2 firmware download.

@JackAtKitronik If your company is already modifying EduBlocks then is it worthwhile investigating the viability of adding WebSerial support to EduBlocks, instead? Browser support for Web Serial seems to be expanding and it's already on the two most popular Windows browsers (Chrome, Edge).

@Gadgetoid
Copy link
Contributor

@JackAtKitronik If your company is already modifying EduBlocks then is it worthwhile investigating the viability of adding WebSerial support to EduBlocks, instead? Browser support for Web Serial seems to be expanding and it's already on the two most popular Windows browsers (Chrome, Edge).

EduBlocks was acquired by Anaconda Inc. recently so any questions around official support for WebSerial or WebUSB would have to be directed at them. With a team behind the app- rather than the original sole developer- that might actually bode well for this change or something like it.

@JackAtKitronik
Copy link
Author

I had not actually realised that there were device-side extensions for WebUSB, I thought it was purely a browser feature. I found this Chromium document, are there other references that you used to create this PR?

@projectgus I mainly used the examples in the tinyusb repo for the firmware, then they also provide a web page for testing their webusb examples in this repo.

... do I understand this correctly?

Yes, so:

  • There's the URL descriptor for the host to request which displays that the device is in webusb mode to the user.
  • The Microsoft BOS descriptor to tell the host the device is webusb capable, but it still appears as a USB serial device on a COM port and is still able to be used with standard serial programs.
  • The vendor control transfer handler is mainly used to allow the browser to create a webusb connection with the rp2.
  • The REPL is then used from the browser through the webusb connection using the in/ out functionality in mphalport.

If this change (however it is implemented) disables the existing COM port function under Windows

Adding the webusb functionality hasn't disabled the existing COM port function. With the webusb firmware on the rp2 it is possible to use the standard serial with a program like Thonny, and use the webusb functionality with our test website without having to put different versions of the firmware onto the rp2.

If your company is already modifying EduBlocks then is it worthwhile investigating the viability of adding WebSerial support to EduBlocks, instead?

If the support for this is already in MicroPython then this may be the approach to go with.

EduBlocks was acquired by Anaconda Inc. recently so any questions around official support for WebSerial or WebUSB would have to be directed at them. With a team behind the app- rather than the original sole developer- that might actually bode well for this change or something like it.

@Gadgetoid Thanks, we'll look into it and see if it's something they are interested in.

@patfrench
Copy link

Hi

Will webusb work for other ports ?

I've built your rp2/webusb project and it works fine.

I've developed a JavaScript program that connects to PICO using the webusb API and I have access to REPL.

I can now make web applications. Webusb is compatible with Android and PC, whereas webserial only works on PC.

Thanks you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants