Home

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

Overall Setup

  1. Upgrade Micro:bit to latest firmware
  2. Program the Micro:bit with one of the example programs that generates serial data
  3. Setup a Web Server & Open the project's page to run the sample application (index.html) (Live Example of it) (Can just open the index.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

  1. Plug the Micro:bit into the computer
  2. Open https://makecode.microbit.org/_Pya288iq3eo2
  3. Select the Gear Menu in the upper right
  4. Select the Pair Device option
  5. Select Pair Device
  6. Select the Micro:bit device
  7. Download the code (Blue Download button at the bottom of the window)
  8. See what data looks like with Makecode's existing graphs via the Console Simulator or Console Device buttons in the simulator.
  9. 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.

  1. Connect USB cable
  2. Open the MakeCode Editor
  3. Select JavaScript from the Blocks/JavaScript slider.
  4. 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)
})
  1. Select the Gear Menu in the upper right
  2. Select the Pair Device option
  3. Select Pair Device
  4. Select the Micro:bit device
  5. Download the code (Blue Download button at the bottom of the window)
  6. See what data looks like with Makecode's existing graphs via the Console Simulator or Console Device buttons in the simulator.
  7. Unplug/re-plug Micro:bit (to Un-Pair device)

Open index.html locally

  1. In browser go to file>open and browse to index.html or right-click on index.html and use file navigation to open in a browser.

Setup a Web Server

  1. Install a local web server
    1. Python 3 / Python: Use pip or pip3 to install the http package. (ex: Open a terminal window / command prompt and run pip install http)
    2. Run the http.server module in the directory containing the project: python -m http.server or python3 -m http.server as appropriate.

Open the project's page

  1. Open browser to http://localhost:8000/ (Default page for Python Server)
  2. Make sure the micro:bit is connected via the USB cable
  3. Click on Connect
  4. Select the micro:bit from the pop-up menu that appears

API

Summary

There are three main functions:

  1. uBitConnectDevice(callback): Prompt user to connect to device and provide callback function for device events
  2. uBitSend(device): Send data (a string) to a micro:bit
  3. uBitDisconnect(device): Disconnect from the designated micro:bit

JSDocs: Documentation on the functions

JSDocs Here

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

Format: NAME:STRING

Values for a graph with multiple series

Format: NAME.SERIES:NUMBER

Format: NAME.SERIES:STRING

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):

  1. Select device configuration 1
  2. Claim interface 4 (CMSIS-DAP)
  3. Control Transfer Out DAP Vendor Specific Command (ID_DAP_Vendor2 or 0x82) to set the UART baud to 115,200bps
    • Bytes: [[0x82, 0x00, 0xc2, 0x01, 0x00]

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.

  1. Select device configuration 1
  2. Claim interface 4 (CMSIS-DAP)
  3. Control Transfer Out DAP_Connect to default (connect the default device)
    • Bytes: [2, 0]
  4. Control Transfer Out DAP_SWJ_Clock to 10MHz
    • Bytes: [0x11, 0x80, 0x96, 0x98, 0]
  5. Control Transfer Out DAP_SWD_Configure to configure the software debug (1 clock turn around, no Wait/Fault phases)
    • Bytes: [0x13, 0]]
  6. Control Transfer Out DAP Vendor Specific Command (ID_DAP_Vendor2 or 0x82) to set the UART baud to 115,200bps
    • Bytes: [[0x82, 0x00, 0xc2, 0x01, 0x00]

Micro:bit USB UART Data Read

  1. Control Transfer Out DAP Vendor Specific Command (ID_DAP_VEndor3 or 0x83) request UART data up to 64 bytes.
  2. 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

Micro:bit USB UART Data Write

  1. Control Transfer Out DAP Vendor Specific Command (ID_DAP_VEndor4 or 0x84) request UART data up to 64 bytes.
    • Bytes: [0x84, string length, string[0], string[1], ..., string[length-1]]

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

  1. Open the page in question
  2. Open the JavaScript console (Inspect the page via developer tools; Right-click on page and select Insepct)
  3. 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)
  1. Use the page to trigger USB operations
  2. 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/

  1. Enable virtual port for monitoring: sudo ifconfig XHC20 up
  2. Open Wireshark for capture.
  3. Select XHC20 device for capture
  4. 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
  5. When done disable XHC20: sudo ifconfig XHC20 down

Misc: App Notes / Docs on USB, WebUSB, etc.

Misc: USB Enumeration for Micro:Bit and communication details

Captured / Decoded USB Sequence from Makecode

  1. DAP_SWJ_CLOCK [17,128,150,152,0]
  2. DAP_Connect [2,0]
  3. DAP_TransferConfigure [4, 0, 80, 0, 0, 0]
  4. SWD Configure [19, 0]
  5. [18, 56, 255, 255, 255, 255, 255, 255, 255] // DAP_SWJ_Sequence
  6. [18, 16, 158, 231]
  7. [18, 8, 0]
  8. DAP_Transfer [5, ...]
  9. [130, 0, 194, 1, 0]// Set UART Config https://github.com/ARMmbed/DAPLink/blob/0711f11391de54b13dc8a628c80617ca5d25f070/source/daplink/cmsis-dap/DAP_vendor.c
  10. [0, 254] // DAP_Info / Get Packet Size
  11. [17, 128, 150, 152, 0] // DAP_SWJ_Clock
  12. [2, 0] // DAP_Connect Defaults
  13. [17, 128, 150, 152, 0] // DAP_SWJ_Clock
  14. [4, 0, 80, 0, 0, 0] // DAP_TransferConfigure
  15. [19, 0] // DAP_SWD_Configure default
  16. [18, 56, 255, 255, 255, 255, 255, 255, 255] // DAP_SWJ_Sequence

Captured / Decoded USB Sequence from Makecode for v2.21

  1. .then(() => device.selectConfiguration(1))

  2. .then(() => device.claimInterface(5))

  3. 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

  1. Transfer in 5, 64 ID_DAP_Vendor0 ???

  2. Transfer out 5, [0,4]

    1. ID_DAP_Info / 0x04 = Get the CMSIS-DAP Protocol Version (string).
  3. Transfer in 5, 64

  4. Transfer out 5,[128] 0x80? 1.

  5. Transfer in 5, 64

  6. Transfer out 5,[130, 0, 194, 1, 0] 0x82

  7. Transfer in 5, 64

  8. Transfer out 5,[0, 254] // Get the maximum Packet Count (BYTE).

  9. Transfer in 5, 64

  10. Transfer out 5,[17, 128, 150, 152, 0] //MMM

    1. DAP_SWJ_Clock https://arm-software.github.io/CMSIS_5/DAP/html/group__DAP__SWJ__Clock.html
  11. Transfer in 5, 64

  12. Transfer out 5,[2, 0]

  13. Transfer in 5, 64

  14. Transfer out 5,[17, 128, 150, 152, 0]

    1. ID_DAP_SWJ_Clock ... ???
  15. Transfer in 5, 64

  16. Transfer out 5,[4, 0, 80, 0, 0, 0]

    1. ID_DAP_TransferConfigure
    2. 0 idle cyclles
    3. 80, 0, wait retry
    4. 00 00 match retry
  17. Transfer in 5, 64

  18. Transfer out 5,[19, 0]

    1. DAP_SWD_Configure
  19. Transfer in 5, 64

  20. Transfer out 5,[18, 56, 255, 255, 255, 255, 255, 255, 255]

    1. ID_DAP_SWJ_Sequence
  21. Transfer in 5, 64

  22. Transfer out 5,[18, 16, 158, 231]

    1. ID_DAP_SWJ_Sequence
  23. Transfer in 5, 64

  24. Transfer out 5,[18, 56, 255, 255, 255, 255, 255, 255, 255]

  25. Transfer in 5, 64

  26. Transfer out 5,[18, 8, 0]

  27. 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

  1. Transfer out 5,[17, 128, 150, 152, 0]
  2. Transfer out 5,[2, 0]
  3. Transfer out 5,[19, 0]
  4. Transfer out 5,[130, 0, 194, 1, 0]

ok from here on

  1. [130, 0, 194, 1, 0] Connecting...
  2. [0,254]
  3. [17, 128, 150, 152, 0]
  4. [2,0]
  5. [17, 128, 150, 152, 0]
  6. [4, 0, 80, 0, 0, 0]
  7. [19, 0]
  8. [18, 56, 255, 255, 255, 255, 255, 255, 255]
  9. [18, 16, 158, 23]