Awesome
Overview
This provides an API for interacting with the micro:bit over USB. It also includes a demo application to show how it works.
Files / Manifest
ubitwebusb.js
: The actual API (the only file needed for other applications)- Demo application (a web-based console that shows the different messages)
index.html
: HTML with in-line JavaScript for the application- View Live Version Only works in Chrome
style.css
- Requires
ubitwebusb.js
- Documentation files
Overall Setup
- Upgrade Micro:bit to latest firmware
- Program the Micro:bit with one of the example programs that generates serial data
- Setup a Web Server & Open the project's page to run the sample application (
index.html
) (Live Example of it) (Can just open theindex.html
locally with current version)
Upgrade Micro:bit to latest firmware
This should only be needed one time for each micro:bit.
Upgrade the micro:bit firmware as describe at: Updating your micro:bit firmware
Program the Micro:bit from Shared Project
- Plug the Micro:bit into the computer
- Open https://makecode.microbit.org/_Pya288iq3eo2
- Select the Gear Menu in the upper right
- Select the
Pair Device
option - Select
Pair Device
- Select the Micro:bit device
- Download the code (Blue Download button at the bottom of the window)
- See what data looks like with Makecode's existing graphs via the
Console Simulator
orConsole Device
buttons in the simulator. - Unplug/re-plug Micro:bit (to Un-Pair device)
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)
Program the Micro:bit from JavaScript Source
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.
- Connect USB cable
- Open the MakeCode Editor
- Select JavaScript from the Blocks/JavaScript slider.
- Paste in the code above
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)
})
- Select the Gear Menu in the upper right
- Select the
Pair Device
option - Select
Pair Device
- Select the Micro:bit device
- Download the code (Blue Download button at the bottom of the window)
- See what data looks like with Makecode's existing graphs via the
Console Simulator
orConsole Device
buttons in the simulator. - Unplug/re-plug Micro:bit (to Un-Pair device)
Open index.html
locally
- In browser go to file>open and browse to
index.html
or right-click onindex.html
and use file navigation to open in a browser.
Setup a Web Server
- Install a local web server
- Python 3 / Python: Use
pip
orpip3
to install thehttp
package. (ex: Open a terminal window / command prompt and runpip install http
) - Run the
http.server
module in the directory containing the project:python -m http.server
orpython3 -m http.server
as appropriate.
- Python 3 / Python: Use
Open the project's page
- Open browser to http://localhost:8000/ (Default page for Python Server)
- Make sure the micro:bit is connected via the USB cable
- Click on
Connect
- Select the micro:bit from the pop-up menu that appears
API
Summary
There are three main functions:
uBitConnectDevice(callback)
: Prompt user to connect to device and provide callback function for device events- See
uBitEventCallback
for callback format and argument descriptions.
- See
uBitSend(device)
: Send data (astring
) to a micro:bituBitDisconnect(device)
: Disconnect from the designatedmicro:bit
JSDocs: Documentation on the functions
Example
See index.html
for a complete example application.
Technical Details and Notes
Now uses DAP.js for DAPLink (serial) interactions and events.
Communication and App Message Formats
Graph Message Types and Formats
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.
Values for a graph with a single series
Format: NAME:NUMBER
NAME
is a string name of the graph (and the name of the series)NUMBER
is a numeric value to graph
Format: NAME:STRING
NAME
is a string name of the graphString
is a string for an event
Values for a graph with multiple series
Format: 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 graph
Format: 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 event
Console messages
Any 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
.
Micro:bit USB & Console Data
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.
DAPJS
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
To Re-generate API Docs
jsdoc ubitwebusb.js -r jsdoc.md -d docs
Below is old / historical data from prior versions
Below is old info from sniffing out USB UART
Micro:bit USB Configuration Sequence
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):
- Select device configuration 1
- Claim interface 4 (CMSIS-DAP)
- Control Transfer Out DAP Vendor Specific Command (
ID_DAP_Vendor2
or0x82
) to set the UART baud to 115,200bps- Bytes:
[[0x82, 0x00, 0xc2, 0x01, 0x00]
- Bytes:
Old version (not necessary)
This approach was based on the sequence sent from Makecode, but it doesn't appear that the majority of it is necessary.
- Select device configuration 1
- Claim interface 4 (CMSIS-DAP)
- Control Transfer Out
DAP_Connect
to default (connect the default device)- Bytes:
[2, 0]
- Bytes:
- Control Transfer Out
DAP_SWJ_Clock
to 10MHz- Bytes:
[0x11, 0x80, 0x96, 0x98, 0]
- Bytes:
- Control Transfer Out
DAP_SWD_Configure
to configure the software debug (1 clock turn around, no Wait/Fault phases)- Bytes:
[0x13, 0]]
- Bytes:
- Control Transfer Out DAP Vendor Specific Command (
ID_DAP_Vendor2
or0x82
) to set the UART baud to 115,200bps- Bytes:
[[0x82, 0x00, 0xc2, 0x01, 0x00]
- Bytes:
Micro:bit USB UART Data Read
- Control Transfer Out DAP Vendor Specific Command (
ID_DAP_VEndor3
or0x83
) request UART data up to 64 bytes. - Control Transfer In of up to 64 bytes.
- 1st byte is repeat of command (
0x83
) - 2nd byte is length of string
- Remaining bytes (2..2+length) are utf-8 string
- 1st byte is repeat of command (
Micro:bit USB UART Data Write
- Control Transfer Out DAP Vendor Specific Command (
ID_DAP_VEndor4
or0x84
) request UART data up to 64 bytes.- Bytes:
[0x84, string length, string[0], string[1], ..., string[length-1]]
- Bytes:
Capturing All USB functions in JavaScript
This can be done to identify all the messages being sent to a USB device from a WebUSB page, like MakeCode
- Open the page in question
- Open the JavaScript console (Inspect the page via developer tools; Right-click on page and select Insepct)
- Paste in the following code, which adds a debugging print message to all calls to most USB functions
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)
- Use the page to trigger USB operations
- Enter
trackerLogOn = false
in the Console to stop collecting data, then scroll back and examine all messages/traffic.
Capturing USB Packets on Mac
Based on https://www.umpah.net/how-to-sniff-usb-traffic-reverse-engineer-usb-device-interactions/
- Enable virtual port for monitoring:
sudo ifconfig XHC20 up
- Open Wireshark for capture.
- Select
XHC20
device for capture - Filter based on device's location ID (get it from Apple Menu,
About this Mac...
,System Report
,USB
, select the device and look at the Location ID):usb.darwin.location_id == 0x14200000
- When done disable
XHC20
:sudo ifconfig XHC20 down
Misc: App Notes / Docs on USB, WebUSB, etc.
- USB Made Simple: Sections on Introduction, Data Flow and Protocol are helpful.
- Makecode/pxt's webusb.ts source
Misc: USB Enumeration for Micro:Bit and communication details
- 0: USBInterface USB Mass Storage
- interfaceClass: 8
- interfaceProtocol: 80
- interfaceSubclass: 6
- interfaceNumber: 0
- 1: USBInterface CDC Control
- interfaceClass: 2
- interfaceProtocol: 1
- interfaceSubclass: 2
- interfaceNumber: 1
- 2: USBInterface CDC - Data
- interfaceClass: 10
- interfaceProtocol: 0
- interfaceSubclass: 0
- interfaceNumber: 2
- 3: USBInterface
- interfaceNumber: 3
- 4: USBInterface CMSIS-DAP
- interfaceNumber: 4
Captured / Decoded USB Sequence from Makecode
- DAP_SWJ_CLOCK [17,128,150,152,0]
- DAP_Connect [2,0]
- DAP_TransferConfigure [4, 0, 80, 0, 0, 0]
- SWD Configure [19, 0]
- [18, 56, 255, 255, 255, 255, 255, 255, 255] // DAP_SWJ_Sequence
- [18, 16, 158, 231]
- [18, 8, 0]
- DAP_Transfer [5, ...]
- [130, 0, 194, 1, 0]// Set UART Config https://github.com/ARMmbed/DAPLink/blob/0711f11391de54b13dc8a628c80617ca5d25f070/source/daplink/cmsis-dap/DAP_vendor.c
- [0, 254] // DAP_Info / Get Packet Size
- [17, 128, 150, 152, 0] // DAP_SWJ_Clock
- [2, 0] // DAP_Connect Defaults
- [17, 128, 150, 152, 0] // DAP_SWJ_Clock
- [4, 0, 80, 0, 0, 0] // DAP_TransferConfigure
- [19, 0] // DAP_SWD_Configure default
- [18, 56, 255, 255, 255, 255, 255, 255, 255] // DAP_SWJ_Sequence
Captured / Decoded USB Sequence from Makecode for v2.21
-
.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]
- ID_DAP_Info / 0x04 = Get the CMSIS-DAP Protocol Version (string).
-
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]
- ID_DAP_SWJ_Clock ... ???
-
Transfer in 5, 64
-
Transfer out 5,[4, 0, 80, 0, 0, 0]
- ID_DAP_TransferConfigure
- 0 idle cyclles
- 80, 0, wait retry
- 00 00 match retry
-
Transfer in 5, 64
-
Transfer out 5,[19, 0]
- DAP_SWD_Configure
-
Transfer in 5, 64
-
Transfer out 5,[18, 56, 255, 255, 255, 255, 255, 255, 255]
- ID_DAP_SWJ_Sequence
-
Transfer in 5, 64
-
Transfer out 5,[18, 16, 158, 231]
- ID_DAP_SWJ_Sequence
-
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
- Transfer out 5,[17, 128, 150, 152, 0]
- Transfer out 5,[2, 0]
- Transfer out 5,[19, 0]
- Transfer out 5,[130, 0, 194, 1, 0]
ok from here on
- [130, 0, 194, 1, 0] Connecting...
- [0,254]
- [17, 128, 150, 152, 0]
- [2,0]
- [17, 128, 150, 152, 0]
- [4, 0, 80, 0, 0, 0]
- [19, 0]
- [18, 56, 255, 255, 255, 255, 255, 255, 255]
- [18, 16, 158, 23]