Skip to content

Commit 210cda7

Browse files
committed
samples: usb: add new WebUSB sample
Add a WebUSB sample that uses the new USB device support. Signed-off-by: Johann Fischer <[email protected]>
1 parent c9521e6 commit 210cda7

File tree

11 files changed

+831
-0
lines changed

11 files changed

+831
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(webusb)
6+
7+
include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake)
8+
FILE(GLOB app_sources src/*.c)
9+
target_sources(app PRIVATE ${app_sources})
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2023 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
# Source common USB sample options used to initialize new experimental USB
5+
# device stack. The scope of these options is limited to USB samples in project
6+
# tree, you cannot use them in your own application.
7+
source "samples/subsys/usb/common/Kconfig.sample_usbd"
8+
9+
source "Kconfig.zephyr"
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
.. zephyr:code-sample:: webusb-next
2+
:name: WebUSB-next
3+
:relevant-api: usbd_api
4+
5+
Receive and echo data from a web page using WebUSB API.
6+
7+
Overview
8+
********
9+
10+
This sample demonstrates how to use the Binary Device Object Store (BOS),
11+
Microsoft OS 2.0 descriptors, and WebUSB descriptors to implement a WebUSB
12+
sample application. The sample USB function receives the data and echoes back
13+
to the WebUSB API based application running in the browser on your local host.
14+
This sample can be found at :zephyr_file:`samples/subsys/usb/webusb-next` in the
15+
Zephyr project tree.
16+
17+
Requirements
18+
************
19+
20+
This project requires a USB device controller driver using the UDC API.
21+
On your host computer, this project requires a web browser that supports the
22+
WebUSB API, such as Chromium or a Chromium-based browser.
23+
24+
Building and Running
25+
********************
26+
27+
Build and flash webusb sample with:
28+
29+
.. zephyr-app-commands::
30+
:zephyr-app: samples/subsys/usb/webusb-next
31+
:board: <board to use>
32+
:goals: flash
33+
:compact:
34+
35+
Demonstration
36+
*************
37+
38+
The sample includes a simple WebUSB API application and can be found in the
39+
sample directory: :zephyr_file:`samples/subsys/usb/webusb-next/index.html`.
40+
41+
There are two ways to access this sample page:
42+
43+
* Using browser go to :doc:`demo`
44+
45+
* Start a web server in the sample directory:
46+
47+
.. code-block:: console
48+
49+
$ python -m http.server
50+
51+
Then follow these steps:
52+
53+
#. Connect the board to your host.
54+
55+
#. Once the device has booted, you may see a notification from the browser: "Go
56+
to localhost to connect". Click on the notification to open the demo page. If
57+
there is no notification from the browser, open the URL http://localhost:8001/
58+
in your browser.
59+
60+
#. Click on the :guilabel:`Connect` button to connect to the device.
61+
62+
#. Send some text to the device by clicking on the :guilabel:`Send` button.
63+
The demo application will receive the same text from the device and display
64+
it in the text area.
65+
66+
References
67+
***********
68+
69+
WebUSB API Specification:
70+
https://wicg.github.io/webusb/
71+
72+
Chrome for Developers, "Access USB Devices on the Web":
73+
https://developer.chrome.com/docs/capabilities/usb
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
:orphan:
2+
3+
WebUSB HTML Demo App
4+
====================
5+
6+
.. raw:: html
7+
:file: index.html
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>WebUSB Serial Sample Application</title>
5+
</head>
6+
7+
<body>
8+
<script>
9+
var serial = {};
10+
11+
(function() {
12+
'use strict';
13+
14+
serial.getPorts = function() {
15+
return navigator.usb.getDevices().then(devices => {
16+
return devices.map(device => new serial.Port(device));
17+
});
18+
};
19+
20+
serial.requestPort = function() {
21+
const filters = [
22+
{ 'vendorId': 0x2fe3, 'productId': 0x0100 },
23+
{ 'vendorId': 0x2fe3, 'productId': 0x00a },
24+
{ 'vendorId': 0x8086, 'productId': 0xF8A1 },
25+
];
26+
return navigator.usb.requestDevice({ 'filters': filters }).then(
27+
device => new serial.Port(device)
28+
);
29+
}
30+
31+
serial.Port = function(device) {
32+
this.device_ = device;
33+
};
34+
35+
serial.Port.prototype.connect = function() {
36+
let readLoop = () => {
37+
const {
38+
endpointNumber
39+
} = this.device_.configuration.interfaces[0].alternate.endpoints[0]
40+
this.device_.transferIn(endpointNumber, 64).then(result => {
41+
this.onReceive(result.data);
42+
readLoop();
43+
}, error => {
44+
this.onReceiveError(error);
45+
});
46+
};
47+
48+
return this.device_.open()
49+
.then(() => {
50+
if (this.device_.configuration === null) {
51+
return this.device_.selectConfiguration(1);
52+
}
53+
})
54+
.then(() => this.device_.claimInterface(0))
55+
.then(() => {
56+
readLoop();
57+
});
58+
};
59+
60+
serial.Port.prototype.disconnect = function() {
61+
return this.device_.close();
62+
};
63+
64+
serial.Port.prototype.send = function(data) {
65+
const {
66+
endpointNumber
67+
} = this.device_.configuration.interfaces[0].alternate.endpoints[1]
68+
return this.device_.transferOut(endpointNumber, data);
69+
};
70+
})();
71+
72+
let port;
73+
74+
function connect() {
75+
port.connect().then(() => {
76+
port.onReceive = data => {
77+
let textDecoder = new TextDecoder();
78+
console.log("Received:", textDecoder.decode(data));
79+
document.getElementById('output').value += textDecoder.decode(data);
80+
}
81+
port.onReceiveError = error => {
82+
console.error(error);
83+
document.querySelector("#connect").style = "visibility: initial";
84+
port.disconnect();
85+
};
86+
});
87+
}
88+
89+
function send(string) {
90+
console.log("sending to serial:" + string.length);
91+
if (string.length === 0)
92+
return;
93+
console.log("sending to serial: [" + string +"]\n");
94+
95+
let view = new TextEncoder('utf-8').encode(string);
96+
console.log(view);
97+
if (port) {
98+
port.send(view);
99+
}
100+
};
101+
102+
window.onload = _ => {
103+
document.querySelector("#connect").onclick = function() {
104+
serial.requestPort().then(selectedPort => {
105+
port = selectedPort;
106+
this.style = "visibility: hidden";
107+
connect();
108+
});
109+
}
110+
111+
document.querySelector("#submit").onclick = () => {
112+
let source = document.querySelector("#input").value;
113+
send(source);
114+
}
115+
}
116+
117+
</script>
118+
<button id="connect" style="visibility: initial">Connect To WebUSB Device</button>
119+
<br><br><label for="input">Sender: </label> <br>
120+
<textarea id="input" rows="25" cols="80">WebUSB!</textarea>
121+
<br><button id="submit">Send</button>
122+
<br><br>
123+
<label for="output">Receiver: </label> <br>
124+
<textarea id="output" rows="25" cols="80"></textarea>
125+
</body>
126+
</html>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CONFIG_USB_DEVICE_STACK_NEXT=y
2+
3+
CONFIG_LOG=y
4+
CONFIG_USBD_LOG_LEVEL_WRN=y
5+
CONFIG_UDC_DRIVER_LOG_LEVEL_WRN=y
6+
CONFIG_SAMPLE_USBD_PID=0x000A
7+
CONFIG_SAMPLE_USBD_20_EXTENSION_DESC=y
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
sample:
2+
name: WebUSB
3+
tests:
4+
sample.usb.webusb-next:
5+
depends_on: usbd
6+
tags: usb
7+
integration_platforms:
8+
- nrf52840dk/nrf52840
9+
- nrf54h20dk/nrf54h20/cpuapp
10+
- frdm_k64f
11+
- stm32f723e_disco
12+
- nucleo_f413zh
13+
- mimxrt685_evk/mimxrt685s/cm33
14+
- mimxrt1060_evk
15+
harness: TBD
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) 2023-2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <sample_usbd.h>
8+
9+
#include <zephyr/sys/byteorder.h>
10+
#include <zephyr/usb/usbd.h>
11+
#include <zephyr/usb/class/usbd_hid.h>
12+
#include <zephyr/usb/msos_desc.h>
13+
14+
#include <zephyr/logging/log.h>
15+
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
16+
17+
/*
18+
* There are three BOS descriptors used in the sample, a USB 2.0 EXTENSION from
19+
* the USB samples common code, a Microsoft OS 2.0 platform capability
20+
* descriptor, and a WebUSB platform capability descriptor.
21+
*/
22+
#include "webusb.h"
23+
#include "msosv2.h"
24+
25+
static void msg_cb(struct usbd_context *const usbd_ctx,
26+
const struct usbd_msg *const msg)
27+
{
28+
LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type));
29+
30+
if (usbd_can_detect_vbus(usbd_ctx)) {
31+
if (msg->type == USBD_MSG_VBUS_READY) {
32+
if (usbd_enable(usbd_ctx)) {
33+
LOG_ERR("Failed to enable device support");
34+
}
35+
}
36+
37+
if (msg->type == USBD_MSG_VBUS_REMOVED) {
38+
if (usbd_disable(usbd_ctx)) {
39+
LOG_ERR("Failed to disable device support");
40+
}
41+
}
42+
}
43+
}
44+
45+
int main(void)
46+
{
47+
struct usbd_context *sample_usbd;
48+
int ret;
49+
50+
sample_usbd = sample_usbd_setup_device(msg_cb);
51+
if (sample_usbd == NULL) {
52+
LOG_ERR("Failed to setup USB device");
53+
return -ENODEV;
54+
}
55+
56+
ret = usbd_add_descriptor(sample_usbd, &bos_vreq_msosv2);
57+
if (ret) {
58+
LOG_ERR("Failed to add MSOSv2 capability descriptor");
59+
return ret;
60+
}
61+
62+
ret = usbd_add_descriptor(sample_usbd, &bos_vreq_webusb);
63+
if (ret) {
64+
LOG_ERR("Failed to add WebUSB capability descriptor");
65+
return ret;
66+
}
67+
68+
ret = usbd_init(sample_usbd);
69+
if (ret) {
70+
LOG_ERR("Failed to initialize device support");
71+
return ret;
72+
}
73+
74+
if (!usbd_can_detect_vbus(sample_usbd)) {
75+
ret = usbd_enable(sample_usbd);
76+
if (ret) {
77+
LOG_ERR("Failed to enable device support");
78+
return ret;
79+
}
80+
}
81+
82+
return 0;
83+
}

0 commit comments

Comments
 (0)