This provides an API for interacting with the micro:bit over USB. It also includes a demo application to show how it works.
ubitwebusb.js: The actual API (the only file needed for other applications)index.html: HTML with in-line JavaScript for the application
style.cssubitwebusb.jsindex.html) (Live Example of it) (Can just open the index.html locally with current version)This should only be needed one time for each micro:bit.
Upgrade the micro:bit firmware as describe at: Updating your micro:bit firmware
Pair Device optionPair DeviceConsole Simulator or Console Device buttons in the simulator.The micro:bit retains it's program until it is explicitly re-programmed or the firmware is upgraded. This programming process won't need to be repeated unless a different program is desired (which may be needed to demonstrate different the different ways to annotate graphs)
This is an alternative to the above. Either can be done, but there's no reason to do both.
The program below will send serial data and can be used for initial testing/debugging.
serial.onDataReceived(serial.delimiters(Delimiters.NewLine), function () {
serial.writeLine("echo " + serial.readUntil(serial.delimiters(Delimiters.NewLine)))
})
basic.forever(function () {
serial.writeValue("x", Math.map(Math.randomRange(0, 100), 0, 100, -2.4, 18.2))
serial.writeValue("y data", Math.randomRange(0, 10))
serial.writeValue("graph2.a", Math.randomRange(-5, 10))
serial.writeValue("graph2.b data", Math.randomRange(-5, 10))
if (Math.randomRange(0, 10) == 5) {
serial.writeLine("x:\"Hi\"")
}
if (Math.randomRange(0, 50) == 5) {
serial.writeLine("console log")
}
basic.pause(500)
})
Pair Device optionPair DeviceConsole Simulator or Console Device buttons in the simulator.index.html locallyindex.html or right-click on index.html and use file navigation to open in a browser.pip or pip3 to install the http package. (ex: Open a terminal window / command prompt and run pip install http)http.server module in the directory containing the project: python -m http.server or python3 -m http.server as appropriate.ConnectThere are three main functions:
uBitConnectDevice(callback): Prompt user to connect to device and provide callback function for device events
uBitEventCallback for callback format and argument descriptions.uBitSend(device): Send data (a string) to a micro:bituBitDisconnect(device): Disconnect from the designated micro:bitSee index.html for a complete example application.
Now uses DAP.js for DAPLink (serial) interactions and events.
Data may be shown in a combination of zero or more graphs that show a single series and zero or more graphs that show multiple series. There may also be "events" (descriptive items with a string)
The number of graphs and series will now be known in advance. It must be determined from the streaming data.
Format: NAME:NUMBER
NAME is a string name of the graph (and the name of the series)NUMBER is a numeric value to graphFormat: NAME:STRING
NAME is a string name of the graphString is a string for an eventFormat: NAME.SERIES:NUMBER
NAME is a string name of the graph (and the name of the series)SERIES is a string name of the series for the dataNUMBER is a numeric value to graphFormat: NAME.SERIES:STRING
NAME is a string name of the graphSERIES is a string name of the series for the dataString is a string for an eventAny message that doesn't include a colon is a console message. Console messages should end with a newline (\n), which will be sent implicitly with serial write line (not serial write). Ex: This is a message sent via serial write line.
The micro:bit console messages (serial write blocks and serial.* TypeScript commands) are directed over the USB interface. They actually go through ARM's CMSIS-DAP. DAP is described as:
CMSIS-DAP is a specification and a implementation of a Firmware that supports access to the CoreSight Debug Access Port (DAP).
DAP has several well defined commands as well as the ability to support custom vendor commands. Messages begin with a byte indicating the command type. The firmware in the micro:bit supports two custom messages that are used to configure the baud rate.
Current work uses DAPJS.
Get the DAPJS file:
mkdir temp
cd temp
npm install dapjs
cp node_modules/dapjs/dist/dap.umd.js ..
cp node_modules/dapjs/dist/dap.umd.js.map ..
cd ..
rm -Rf temp
jsdoc ubitwebusb.js -r jsdoc.md -d docs
Immediately after connection to micro:bit (this assumes the connected device is a micro:bit and all control transfers in/out go to endpoint 4, the CMSIS-DAP endpoint):
ID_DAP_Vendor2 or 0x82) to set the UART baud to 115,200bps
[[0x82, 0x00, 0xc2, 0x01, 0x00]This approach was based on the sequence sent from Makecode, but it doesn't appear that the majority of it is necessary.
DAP_Connect to default (connect the default device)
[2, 0]DAP_SWJ_Clock to 10MHz
[0x11, 0x80, 0x96, 0x98, 0]DAP_SWD_Configure to configure the software debug (1 clock turn around, no Wait/Fault phases)
[0x13, 0]]ID_DAP_Vendor2 or 0x82) to set the UART baud to 115,200bps
[[0x82, 0x00, 0xc2, 0x01, 0x00]ID_DAP_VEndor3 or 0x83) request UART data up to 64 bytes.0x83)ID_DAP_VEndor4 or 0x84) request UART data up to 64 bytes.
[0x84, string length, string[0], string[1], ..., string[length-1]]This can be done to identify all the messages being sent to a USB device from a WebUSB page, like MakeCode
var trackerLogOn = true;
function addTracker(methodName, object) {
var temp = object[methodName];
object[methodName] = function() {
if(trackerLogOn) {
console.log(methodName + ":");
console.dir(arguments);
console.log("----------------")
}
// this is a USBDevice object
return temp.apply(this, arguments)
}
}
addTracker("claimInterface",USBDevice.prototype)
addTracker("selectAlternateInterface", USBDevice.prototype)
addTracker("controlTransferIn", USBDevice.prototype)
addTracker("controlTransferOut", USBDevice.prototype)
addTracker("transferIn", USBDevice.prototype)
addTracker("transferOut", USBDevice.prototype)
addTracker("selectConfiguration", USBDevice.prototype)
addTracker("isochronousTransferIn", USBDevice.prototype)
addTracker("isochronousTransferOut", USBDevice.prototype)
trackerLogOn = false in the Console to stop collecting data, then scroll back and examine all messages/traffic.Based on https://www.umpah.net/how-to-sniff-usb-traffic-reverse-engineer-usb-device-interactions/
sudo ifconfig XHC20 upXHC20 device for captureAbout this Mac..., System Report, USB, select the device and look at the Location ID): usb.darwin.location_id == 0x14200000XHC20: sudo ifconfig XHC20 down.then(() => device.selectConfiguration(1))
.then(() => device.claimInterface(5))
Transfer out 5, [5, 0, 4, 8, 0, 0, 0, 0, 1, 82, 0, 0, 35, 5, 240, 237, 0, 224, 13, 3, 0, 95, 160] ID_DAP_Transfer https://arm-software.github.io/CMSIS_5/DAP/html/group__DAP__Transfer.html index 0, transfer 4 8, 0, 0, 0, 0, 1, 82, 0, 0, 35, 5, 240, 237, 0, 224, 13, 3, 0, 95, 160 https://arm-software.github.io/CMSIS_5/DAP/html/group__DAP__Transfer.html
BYTE | BYTE *****| BYTE **********| BYTE *************| WORD *********|
0x05 | DAP Index | Transfer Count | Transfer Request | Transfer Data | 5, 0, 4,
8: 0, 0, 0, 0 //write A3 Register Address bit 3. ??? 1: 82, 0, 0, 35, write to access port 5: 240, 237, 0, 224 a2 register write to AP ??? 13:, 3, 0, 95, 160 a3 and a2
Transfer in 5, 64 ID_DAP_Vendor0 ???
Transfer out 5, [0,4]
Transfer in 5, 64
Transfer out 5,[128] 0x80? 1.
Transfer in 5, 64
Transfer out 5,[130, 0, 194, 1, 0] 0x82
Transfer in 5, 64
Transfer out 5,[0, 254] // Get the maximum Packet Count (BYTE).
Transfer in 5, 64
Transfer out 5,[17, 128, 150, 152, 0] //MMM
Transfer in 5, 64
Transfer out 5,[2, 0]
Transfer in 5, 64
Transfer out 5,[17, 128, 150, 152, 0]
Transfer in 5, 64
Transfer out 5,[4, 0, 80, 0, 0, 0]
Transfer in 5, 64
Transfer out 5,[19, 0]
Transfer in 5, 64
Transfer out 5,[18, 56, 255, 255, 255, 255, 255, 255, 255]
Transfer in 5, 64
Transfer out 5,[18, 16, 158, 231]
Transfer in 5, 64
Transfer out 5,[18, 56, 255, 255, 255, 255, 255, 255, 255]
Transfer in 5, 64
Transfer out 5,[18, 8, 0]
Transfer in 5, 64 Connected Transfer out 5, [5, 0, 1, 2] Transfer out 5, [5, 0, 4, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 4, 0, 0, 0, 80, 6] Transfer out 5, [5, 0, 4, 4, 0, 15, 0, 80, 8, 0, 0, 0, 0, 8, 240, 0, 0, 0, 15] Transfer out 5, [5, 0, 4, 8, 0, 0, 0, 0, 1, 82, 0, 0, 35, 5, 0, 32, 0, 224, 15] Breakpoints messqge Transfer out 5, [5, 0, 4, 8, 0, 0, 0, 0, 1, 82, 0, 0, 35, 5, 0, 32, 0, 224, 13, 2, 0, 0, 0] Transfer out 5, [5, 0, 4, 8, 0, 0, 0, 0, 1, 82, 0, 0, 35, 5, 8, 32, 0, 224, 13, 0, 0, 0, 0] Transfer out 5, [] Transfer out 5, [] Transfer out 5, [] Transfer out 5, [] Transfer out 5, [] Transfer out 5, [] Transfer out 5, []
// product id 516
ok from here on
53 commits
JavaScript
59.1%
HTML
33.9%
CSS
7.0%
This provides an API for interacting with the micro:bit over USB. It also includes a demo application to show how it works.
ubitwebusb.js: The actual API (the only file needed for other applications)index.html: HTML with in-line JavaScript for the application
style.cssubitwebusb.jsindex.html) (Live Example of it) (Can just open the index.html locally with current version)This should only be needed one time for each micro:bit.
Upgrade the micro:bit firmware as describe at: Updating your micro:bit firmware
Pair Device optionPair DeviceConsole Simulator or Console Device buttons in the simulator.The micro:bit retains it's program until it is explicitly re-programmed or the firmware is upgraded. This programming process won't need to be repeated unless a different program is desired (which may be needed to demonstrate different the different ways to annotate graphs)
This is an alternative to the above. Either can be done, but there's no reason to do both.
The program below will send serial data and can be used for initial testing/debugging.
serial.onDataReceived(serial.delimiters(Delimiters.NewLine), function () {
serial.writeLine("echo " + serial.readUntil(serial.delimiters(Delimiters.NewLine)))
})
basic.forever(function () {
serial.writeValue("x", Math.map(Math.randomRange(0, 100), 0, 100, -2.4, 18.2))
serial.writeValue("y data", Math.randomRange(0, 10))
serial.writeValue("graph2.a", Math.randomRange(-5, 10))
serial.writeValue("graph2.b data", Math.randomRange(-5, 10))
if (Math.randomRange(0, 10) == 5) {
serial.writeLine("x:\"Hi\"")
}
if (Math.randomRange(0, 50) == 5) {
serial.writeLine("console log")
}
basic.pause(500)
})
Pair Device optionPair DeviceConsole Simulator or Console Device buttons in the simulator.index.html locallyindex.html or right-click on index.html and use file navigation to open in a browser.pip or pip3 to install the http package. (ex: Open a terminal window / command prompt and run pip install http)http.server module in the directory containing the project: python -m http.server or python3 -m http.server as appropriate.ConnectThere are three main functions:
uBitConnectDevice(callback): Prompt user to connect to device and provide callback function for device events
uBitEventCallback for callback format and argument descriptions.uBitSend(device): Send data (a string) to a micro:bituBitDisconnect(device): Disconnect from the designated micro:bitSee index.html for a complete example application.
Now uses DAP.js for DAPLink (serial) interactions and events.
Data may be shown in a combination of zero or more graphs that show a single series and zero or more graphs that show multiple series. There may also be "events" (descriptive items with a string)
The number of graphs and series will now be known in advance. It must be determined from the streaming data.
Format: NAME:NUMBER
NAME is a string name of the graph (and the name of the series)NUMBER is a numeric value to graphFormat: NAME:STRING
NAME is a string name of the graphString is a string for an eventFormat: NAME.SERIES:NUMBER
NAME is a string name of the graph (and the name of the series)SERIES is a string name of the series for the dataNUMBER is a numeric value to graphFormat: NAME.SERIES:STRING
NAME is a string name of the graphSERIES is a string name of the series for the dataString is a string for an eventAny message that doesn't include a colon is a console message. Console messages should end with a newline (\n), which will be sent implicitly with serial write line (not serial write). Ex: This is a message sent via serial write line.
The micro:bit console messages (serial write blocks and serial.* TypeScript commands) are directed over the USB interface. They actually go through ARM's CMSIS-DAP. DAP is described as:
CMSIS-DAP is a specification and a implementation of a Firmware that supports access to the CoreSight Debug Access Port (DAP).
DAP has several well defined commands as well as the ability to support custom vendor commands. Messages begin with a byte indicating the command type. The firmware in the micro:bit supports two custom messages that are used to configure the baud rate.
Current work uses DAPJS.
Get the DAPJS file:
mkdir temp
cd temp
npm install dapjs
cp node_modules/dapjs/dist/dap.umd.js ..
cp node_modules/dapjs/dist/dap.umd.js.map ..
cd ..
rm -Rf temp
jsdoc ubitwebusb.js -r jsdoc.md -d docs
Immediately after connection to micro:bit (this assumes the connected device is a micro:bit and all control transfers in/out go to endpoint 4, the CMSIS-DAP endpoint):
ID_DAP_Vendor2 or 0x82) to set the UART baud to 115,200bps
[[0x82, 0x00, 0xc2, 0x01, 0x00]This approach was based on the sequence sent from Makecode, but it doesn't appear that the majority of it is necessary.
DAP_Connect to default (connect the default device)
[2, 0]DAP_SWJ_Clock to 10MHz
[0x11, 0x80, 0x96, 0x98, 0]DAP_SWD_Configure to configure the software debug (1 clock turn around, no Wait/Fault phases)
[0x13, 0]]ID_DAP_Vendor2 or 0x82) to set the UART baud to 115,200bps
[[0x82, 0x00, 0xc2, 0x01, 0x00]ID_DAP_VEndor3 or 0x83) request UART data up to 64 bytes.0x83)ID_DAP_VEndor4 or 0x84) request UART data up to 64 bytes.
[0x84, string length, string[0], string[1], ..., string[length-1]]This can be done to identify all the messages being sent to a USB device from a WebUSB page, like MakeCode
var trackerLogOn = true;
function addTracker(methodName, object) {
var temp = object[methodName];
object[methodName] = function() {
if(trackerLogOn) {
console.log(methodName + ":");
console.dir(arguments);
console.log("----------------")
}
// this is a USBDevice object
return temp.apply(this, arguments)
}
}
addTracker("claimInterface",USBDevice.prototype)
addTracker("selectAlternateInterface", USBDevice.prototype)
addTracker("controlTransferIn", USBDevice.prototype)
addTracker("controlTransferOut", USBDevice.prototype)
addTracker("transferIn", USBDevice.prototype)
addTracker("transferOut", USBDevice.prototype)
addTracker("selectConfiguration", USBDevice.prototype)
addTracker("isochronousTransferIn", USBDevice.prototype)
addTracker("isochronousTransferOut", USBDevice.prototype)
trackerLogOn = false in the Console to stop collecting data, then scroll back and examine all messages/traffic.Based on https://www.umpah.net/how-to-sniff-usb-traffic-reverse-engineer-usb-device-interactions/
sudo ifconfig XHC20 upXHC20 device for captureAbout this Mac..., System Report, USB, select the device and look at the Location ID): usb.darwin.location_id == 0x14200000XHC20: sudo ifconfig XHC20 down.then(() => device.selectConfiguration(1))
.then(() => device.claimInterface(5))
Transfer out 5, [5, 0, 4, 8, 0, 0, 0, 0, 1, 82, 0, 0, 35, 5, 240, 237, 0, 224, 13, 3, 0, 95, 160] ID_DAP_Transfer https://arm-software.github.io/CMSIS_5/DAP/html/group__DAP__Transfer.html index 0, transfer 4 8, 0, 0, 0, 0, 1, 82, 0, 0, 35, 5, 240, 237, 0, 224, 13, 3, 0, 95, 160 https://arm-software.github.io/CMSIS_5/DAP/html/group__DAP__Transfer.html
BYTE | BYTE *****| BYTE **********| BYTE *************| WORD *********|
0x05 | DAP Index | Transfer Count | Transfer Request | Transfer Data | 5, 0, 4,
8: 0, 0, 0, 0 //write A3 Register Address bit 3. ??? 1: 82, 0, 0, 35, write to access port 5: 240, 237, 0, 224 a2 register write to AP ??? 13:, 3, 0, 95, 160 a3 and a2
Transfer in 5, 64 ID_DAP_Vendor0 ???
Transfer out 5, [0,4]
Transfer in 5, 64
Transfer out 5,[128] 0x80? 1.
Transfer in 5, 64
Transfer out 5,[130, 0, 194, 1, 0] 0x82
Transfer in 5, 64
Transfer out 5,[0, 254] // Get the maximum Packet Count (BYTE).
Transfer in 5, 64
Transfer out 5,[17, 128, 150, 152, 0] //MMM
Transfer in 5, 64
Transfer out 5,[2, 0]
Transfer in 5, 64
Transfer out 5,[17, 128, 150, 152, 0]
Transfer in 5, 64
Transfer out 5,[4, 0, 80, 0, 0, 0]
Transfer in 5, 64
Transfer out 5,[19, 0]
Transfer in 5, 64
Transfer out 5,[18, 56, 255, 255, 255, 255, 255, 255, 255]
Transfer in 5, 64
Transfer out 5,[18, 16, 158, 231]
Transfer in 5, 64
Transfer out 5,[18, 56, 255, 255, 255, 255, 255, 255, 255]
Transfer in 5, 64
Transfer out 5,[18, 8, 0]
Transfer in 5, 64 Connected Transfer out 5, [5, 0, 1, 2] Transfer out 5, [5, 0, 4, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 4, 0, 0, 0, 80, 6] Transfer out 5, [5, 0, 4, 4, 0, 15, 0, 80, 8, 0, 0, 0, 0, 8, 240, 0, 0, 0, 15] Transfer out 5, [5, 0, 4, 8, 0, 0, 0, 0, 1, 82, 0, 0, 35, 5, 0, 32, 0, 224, 15] Breakpoints messqge Transfer out 5, [5, 0, 4, 8, 0, 0, 0, 0, 1, 82, 0, 0, 35, 5, 0, 32, 0, 224, 13, 2, 0, 0, 0] Transfer out 5, [5, 0, 4, 8, 0, 0, 0, 0, 1, 82, 0, 0, 35, 5, 8, 32, 0, 224, 13, 0, 0, 0, 0] Transfer out 5, [] Transfer out 5, [] Transfer out 5, [] Transfer out 5, [] Transfer out 5, [] Transfer out 5, [] Transfer out 5, []
// product id 516
ok from here on
53 commits
JavaScript
59.1%
HTML
33.9%
CSS
7.0%