Skip to content

Commit

Permalink
Improve USBVendor Example (#9349)
Browse files Browse the repository at this point in the history
* Add WebUSB console

* Improve Console Page

* Improve example

* Add comments

* Add flush method

---------

Co-authored-by: Jan Procházka <[email protected]>
  • Loading branch information
lucasssvaz and P-R-O-C-H-Y authored Mar 11, 2024
1 parent b92ad55 commit dce754b
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 6 deletions.
2 changes: 1 addition & 1 deletion cores/esp32/USB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
#define USB_WEBUSB_ENABLED false
#endif
#ifndef USB_WEBUSB_URL
#define USB_WEBUSB_URL "https://espressif.github.io/arduino-esp32/webusb.html"
#define USB_WEBUSB_URL "https://docs.espressif.com/projects/arduino-esp32/en/latest/_static/webusb.html"
#endif

#if CFG_TUD_DFU
Expand Down
128 changes: 128 additions & 0 deletions docs/_static/webusb.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<!-- Based on https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/subsys/usb/webusb -->

<!DOCTYPE html>
<html lang="en">
<head>
<title>Espressif WebUSB Console Example</title>
</head>

<body>
<script>
var serial = {};

(function() {
'use strict';

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

serial.requestPort = function() {
const filters = [
{ 'vendorId': 0x10c4, 'productId': 0xea60 },
{ 'vendorId': 0x303a, 'productId': 0x1001 },
{ 'vendorId': 0x303a, 'productId': 0x0002 },
];
return navigator.usb.requestDevice({ 'filters': filters }).then(
device => new serial.Port(device)
);
}

serial.Port = function(device) {
this.device_ = device;
};

serial.Port.prototype.connect = function() {
let readLoop = () => {
const {
endpointNumber
} = this.device_.configuration.interfaces[0].alternate.endpoints[0]
this.device_.transferIn(endpointNumber, 64).then(result => {
this.onReceive(result.data);
readLoop();
}, error => {
this.onReceiveError(error);
});
};

return this.device_.open()
.then(() => {
if (this.device_.configuration === null) {
return this.device_.selectConfiguration(1);
}
})
.then(() => this.device_.claimInterface(0))
.then(() => {
readLoop();
});
};

serial.Port.prototype.disconnect = function() {
return this.device_.close();
};

serial.Port.prototype.send = function(data) {
const {
endpointNumber
} = this.device_.configuration.interfaces[0].alternate.endpoints[1]
return this.device_.transferOut(endpointNumber, data);
};
})();

let port;

function connect() {
port.connect().then(() => {
port.onReceive = data => {
let textDecoder = new TextDecoder();
console.log("Received:", textDecoder.decode(data));
document.getElementById('output').value += textDecoder.decode(data);
}
port.onReceiveError = error => {
console.error(error);
document.querySelector("#connect").style = "visibility: initial";
port.disconnect();
};
});
}

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

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

window.onload = _ => {
document.querySelector("#connect").onclick = function() {
serial.requestPort().then(selectedPort => {
port = selectedPort;
this.style = "visibility: hidden";
connect();
});
}

document.querySelector("#submit").onclick = () => {
let source = document.querySelector("#input").value;
send(source);
}
}
</script>

<button id="connect" style="visibility: initial">Connect To ESP Device</button>
<br><br><label for="input">Sender: </label> <br>
<textarea id="input" rows="25" cols="80">Send to ESP Device</textarea>
<br><button id="submit">Send</button>
<br><br>
<label for="output">Receiver: </label> <br>
<textarea id="output" rows="25" cols="80"></textarea>
</body>
</html>
2 changes: 1 addition & 1 deletion docs/en/api/usb.rst
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ This function is used to get the ``webUSBURL``.
const char * webUSBURL(void);
The default ``webUSBURL`` is: https://espressif.github.io/arduino-esp32/webusb.html
The default ``webUSBURL`` is: https://docs.espressif.com/projects/arduino-esp32/en/latest/_static/webusb.html

enableDFU
^^^^^^^^^
Expand Down
8 changes: 6 additions & 2 deletions libraries/USB/examples/USBVendor/USBVendor.ino
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ void setup() {

USB.onEvent(usbEventCallback);
USB.webUSB(true);
USB.webUSBURL("http://localhost/webusb");
// Set the URL for your WebUSB landing page
USB.webUSBURL("https://docs.espressif.com/projects/arduino-esp32/en/latest/_static/webusb.html");
USB.begin();
}

Expand All @@ -176,9 +177,11 @@ void loop() {
if (buttonState == LOW) {
Serial.println("Button Pressed");
Vendor.println("Button Pressed");
Vendor.flush(); //Without flushing the data will only be sent when the buffer is full (64 bytes)
} else {
Vendor.println("Button Released");
Serial.println("Button Released");
Vendor.println("Button Released");
Vendor.flush(); //Without flushing the data will only be sent when the buffer is full (64 bytes)
}
delay(100);
}
Expand All @@ -188,6 +191,7 @@ void loop() {
uint8_t b[l];
l = Serial.read(b, l);
Vendor.write(b, l);
Vendor.flush(); //Without flushing the data will only be sent when the buffer is full (64 bytes)
}
}
#endif /* ARDUINO_USB_MODE */
133 changes: 133 additions & 0 deletions libraries/USB/examples/USBVendor/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<!-- Based on https://github.com/zephyrproject-rtos/zephyr/tree/main/samples/subsys/usb/webusb -->

<!--
Example of WebUSB web page to communicate with a USB device.
This can be used as a starting point for your self-hosted WebUSB landing page.
-->

<!DOCTYPE html>
<html lang="en">
<head>
<title>Espressif WebUSB Console Example</title>
</head>

<body>
<script>
var serial = {};

(function() {
'use strict';

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

serial.requestPort = function() {
const filters = [
{ 'vendorId': 0x10c4, 'productId': 0xea60 },
{ 'vendorId': 0x303a, 'productId': 0x1001 },
{ 'vendorId': 0x303a, 'productId': 0x0002 },
];
return navigator.usb.requestDevice({ 'filters': filters }).then(
device => new serial.Port(device)
);
}

serial.Port = function(device) {
this.device_ = device;
};

serial.Port.prototype.connect = function() {
let readLoop = () => {
const {
endpointNumber
} = this.device_.configuration.interfaces[0].alternate.endpoints[0]
this.device_.transferIn(endpointNumber, 64).then(result => {
this.onReceive(result.data);
readLoop();
}, error => {
this.onReceiveError(error);
});
};

return this.device_.open()
.then(() => {
if (this.device_.configuration === null) {
return this.device_.selectConfiguration(1);
}
})
.then(() => this.device_.claimInterface(0))
.then(() => {
readLoop();
});
};

serial.Port.prototype.disconnect = function() {
return this.device_.close();
};

serial.Port.prototype.send = function(data) {
const {
endpointNumber
} = this.device_.configuration.interfaces[0].alternate.endpoints[1]
return this.device_.transferOut(endpointNumber, data);
};
})();

let port;

function connect() {
port.connect().then(() => {
port.onReceive = data => {
let textDecoder = new TextDecoder();
console.log("Received:", textDecoder.decode(data));
document.getElementById('output').value += textDecoder.decode(data);
}
port.onReceiveError = error => {
console.error(error);
document.querySelector("#connect").style = "visibility: initial";
port.disconnect();
};
});
}

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

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

window.onload = _ => {
document.querySelector("#connect").onclick = function() {
serial.requestPort().then(selectedPort => {
port = selectedPort;
this.style = "visibility: hidden";
connect();
});
}

document.querySelector("#submit").onclick = () => {
let source = document.querySelector("#input").value;
send(source);
}
}
</script>

<button id="connect" style="visibility: initial">Connect To ESP Device</button>
<br><br><label for="input">Sender: </label> <br>
<textarea id="input" rows="25" cols="80">Send to ESP Device</textarea>
<br><button id="submit">Send</button>
<br><br>
<label for="output">Receiver: </label> <br>
<textarea id="output" rows="25" cols="80"></textarea>
</body>
</html>
4 changes: 3 additions & 1 deletion libraries/USB/src/USBVendor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,9 @@ size_t USBVendor::read(uint8_t *buffer, size_t size){
return count;
}

void USBVendor::flush(void){}
void USBVendor::flush(void){
tud_vendor_n_write_flush(itf);
}

#endif /* CONFIG_TINYUSB_VENDOR_ENABLED */
#endif /* SOC_USB_OTG_SUPPORTED */
2 changes: 1 addition & 1 deletion variants/esp32s2usb/pins_arduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#define USB_PRODUCT "ESP32-S2-USB"
#define USB_SERIAL "0"
#define USB_WEBUSB_ENABLED false
#define USB_WEBUSB_URL "https://espressif.github.io/arduino-esp32/webusb.html"
#define USB_WEBUSB_URL "https://docs.espressif.com/projects/arduino-esp32/en/latest/_static/webusb.html"

// Default USB FirmwareMSC Settings
#define USB_FW_MSC_VENDOR_ID "ESP32-S2" //max 8 chars
Expand Down

0 comments on commit dce754b

Please sign in to comment.