microbit-pxt-blehid

blehid

blehid=github:bsiever/microbit-pxt-blehid

This extension allows the micro:bit V2 to act like a Human Interface Devices (HID) over Bluetooth. It currently supports services for Keyboard, Mouse, Media Keys (play, pause, volume up, etc.), a Gamepad, and an Absolute Mouse (puts the mouse at a specific position on the screen).

See Cool Ideas and Challenges for some examples of how this extension can be used.

Not all services are supported on all devices or operating systems. Operating systems with some support are:

You are welcome to use the Extensions’s discussions forum to share any new/different platforms that work or don’t:

Not all features are supported on each operating system:

Service Windows macOS Android iOS
Keyboard X X X X
Media X X X  
Mouse X X X Note 1
Gamepad X X X  
Absolute Mouse X X    

Note 1: iOS can support mouse control if AssistiveTouch is enabled.

Pairing and Programming Quirks

Security is important for wireless Human Interface Devices, like keyboards. The Micro:bit’s Bluetooth communication goes through a process called “pairing” to ensure data exchanged is secure. The pairing process works like this:

1. Program the micro:bit

Here’s an example program:

input.onButtonPressed(Button.A, function () {
    keyboard.sendString("Hello Bluetooth HID!")
})
keyboard.startKeyboardService()

Notice that it starts a service in the start.

2. Have your device connect to the micro:bit

  1. Open bluetooth preferences on the device you want to interact with (a computer, phone, or tablet),
  2. select the micro:bit that you want to connect to (it will be named something like “uBit [XXXXX]”), and
  3. your phone/computer/tablet may ask if you want to “Pair” (Some devices, like Macs and PCs assume you want to “Pair” because you selected the micro:bit).

Every time pairing happens a new “key” is created to encrypt all future communication. The micro:bit and the other device will both store the key so they can immediately communicate in the future without going through this pairing process. For example, if your micro:bit loses power and re-starts, the other device will automatically connect to it without going through the pairing process again. However, sometimes changes to your program will cause the micro:bit’s key to be destroyed! (See Re-programming and keys)

Here are examples of all three steps on each operating system:

iOS Pairing

https://user-images.githubusercontent.com/1421446/149055788-594206e2-3ec7-477f-8c7f-28380c855a23.mp4

In the video the micro:bit’s bluetooth name is “BBC micro:bit”. Yours will be named something like “uBit [XXXXX]”, where “XXXXX” will be the unique name of your micro:bit.

macOS Pairing

https://user-images.githubusercontent.com/1421446/149054357-aed2a475-ddfb-4786-8f2d-72e843a1811f.mp4

In the video the micro:bit’s bluetooth name is “BBC micro:bit”. Yours will be named something like “uBit [XXXXX]”, where “XXXXX” will be the unique name of your micro:bit.

Android Pairing

https://user-images.githubusercontent.com/1421446/149252209-67c847b2-aa58-4785-869f-91105304ef8f.mp4

In the video the micro:bit’s bluetooth name is “BBC micro:bit”. Yours will be named something like “uBit [XXXXX]”, where “XXXXX” will be the unique name of your micro:bit.

Windows Pairing

https://user-images.githubusercontent.com/1421446/149252173-0dd6ebf0-fa2b-4070-8c2c-92288513195c.mp4

In the video the micro:bit’s bluetooth name is “BBC micro:bit”. Yours will be named something like “uBit [XXXXX]”, where “XXXXX” will be the unique name of your micro:bit.

Re-programming and keys

Reprogramming the micro:bit can cause the micro:bit’s version of the key to be destroyed, but the other device (computer, phone, tablet, etc.) will still have its copy of the key. The other device will still try to connect to the micro:bit, but then it will ignore any commands from the micro:bit since the micro:bit doesn’t have a valid key. To be able to communicate the devices will have to exchange keys again. You’ll need to make the other device (computer, phone, tablet, etc.) “forget” the key and then go through the pairing process again. You also use bluetooth settings to cause devices to forget keys (un-pair).

Here are examples of unpairing on each operating system:

iOS Un Pair

https://user-images.githubusercontent.com/1421446/149056052-7e46139f-718e-443a-a550-fae258d8e9c2.mp4

In the video the micro:bit’s bluetooth name is “BBC micro:bit”. Yours will be named something like “uBit [XXXXX]”, where “XXXXX” will be the unique name of your micro:bit.

macOS Un Pair

https://user-images.githubusercontent.com/1421446/149055265-510663ac-4243-4d7d-a922-9b40f27a1d3a.mp4

In the video the micro:bit’s bluetooth name is “BBC micro:bit”. Yours will be named something like “uBit [XXXXX]”, where “XXXXX” will be the unique name of your micro:bit.

Android Un Pair

https://user-images.githubusercontent.com/1421446/149252273-4bd5c3ae-7389-4ef5-ac34-43e34076cbaf.mp4

In the video the micro:bit’s bluetooth name is “BBC micro:bit”. Yours will be named something like “uBit [XXXXX]”, where “XXXXX” will be the unique name of your micro:bit.

Windows Un Pair

https://user-images.githubusercontent.com/1421446/149252110-cc18c5ba-0cdd-4823-af8b-047f2a683a6d.mp4

In the video the micro:bit’s bluetooth name is “BBC micro:bit”. Yours will be named something like “uBit [XXXXX]”, where “XXXXX” will be the unique name of your micro:bit.

~hint

Reprogramming without pairing

If you are just making small changes to a paired micro:bit, you can usually reprogram if without losing the stored key.

~

Keyboard Service #keyboard

Starting the Keyboard Service #keyboard-startKeyboardService

keyboard.startKeyboardService()

Starts the keyboard service. This must execute in the start block. All other keyboard blocks require the service be started.

~hint

Showing the iOS Keyboard with Keyboard Service

When the regular Keyboard service is used with iOS it will disable use of the on-screen keyboard. You can re-enable it by sending an eject with the media service

~

Sending keystrokes #keyboard-sendString

keyboard.sendString()

Send a sequence of characters one-at-a-time. For example, [keyboard.sendString('Hello Keyboard!')] is equivalent to typing the individual letters one-at-a-time.

Special Keys #keyboard-keys

keyboard.keys()

Keys that can’t be typed in strings. To simulate pressing enter send enter, like: [keyboard.sendString(keyboard.keys(keyboard._Key.enter))]

These can be combined with typed characters by joining them together: [keyboard.sendString('Hello Keyboard!'+keyboard.keys(keyboard._Key.enter))]

Key Modifiers #keyboard-modifiers

keyboard.modifiers()

Modifiers are keys like Control, or Alt, the Windows key on a PC, or the Command key on a Mac. Modifiers modify another key and are often used for special commands. For example, pressing Control and “S” is often used to save files (Command and “S” on Macs). You can send a “Control+S” by joining the Control modifier to "s": [keyboard.sendString("" + keyboard.modifiers(keyboard._Modifier.control) + "s")]. The Modifier modifies the first character joined after the “+”. For example, [keyboard.sendString("" + keyboard.modifiers(keyboard._Modifier.control) + "stop")] would send “Contrl+s” and then send “t”, “o”, and finally “p”.

Some commands require multiple modifiers, which can also be joined. All the modifiers in a series add to the first non-modifier. For example, simultaneously pressing Control and Alt and the delete key can be done via: [keyboard.sendString("" + keyboard.modifiers(keyboard._Modifier.control) + keyboard.modifiers(keyboard._Modifier.alt) + keyboard.keys(keyboard._Key.delete))]

It’s also possible to send a sequence of modified keys. For example, on Macs Command with “c” copies items and Command with “v” pastes a copied items. A copy/paste/paste could be done by:

[keyboard.sendString("" + keyboard.modifiers(keyboard._Modifier.apple) + "c" + keyboard.modifiers(keyboard._Modifier.apple) + "v" + keyboard.modifiers(keyboard._Modifier.apple) + "v") ]

Sending several simultaneously pressed keys #keyboard-sendSimultaneousKeys

keyboard.sendSimultaneousKeys()

This command is in the “… more” palette of advanced commands. It allows up to 6 simultaneous keys to be sent at the same time. This is like smashing down keys at the exact same time. The “+” on the block can be expanded to give additional control over when keys are released. For example, while playing a video game you may want to hold down on the right arrow to move right: [keyboard.sendSimultaneousKeys(keyboard.keys(keyboard._Key.right), true)].

At some point you may also want to simulate pushing the space bar to jump while still moving to the right: [keyboard.sendSimultaneousKeys("" + keyboard.keys(keyboard._Key.right) + " ", true)]

Finally, you may want to release all the keys: [keyboard.releaseKeys()]

Releasing any “held” keys #keyboard-releaseKeys

keyboard.releaseKeys()

Release any keys that were held down by use of [keyboard.sendSimultaneousKeys("...", true)]

Raw scancodes for additional keys #keyboard-rawScanCode

keyboard.rawScancode()

HID keyboards send “scancodes” that represent keys. You may want to send keys that aren’t covered here, like the Function Keys (F1, etc.). You can do this by sending the scancode for the key. Supported scancodes can be found starting on page 83 of the “HID Usage Tables for Universal Serial Bus (USB) v 1.21”. If you look up Keyboard F1 in the table, you’ll find it has a scancode of 112 (in the AT-101 column of the table). So, to send an F1: [keyboard.sendString(keyboard.rawScancode(112))]

Controlling Rates #keyboard-setEventsPerSecond

keyboard.setEventsPerSecond()

Set the number of keys that can be sent per second. The maximum is 33 and the minimum is 5. The default is 28.

Detecting if the keyboard service use has changed #keyboard-setStatusChangeHandler

keyboard.setStatusChangeHandler()

The device using the service needs to “subscribe” to the service. This event handler will indicate that the status of the subscription has changed (either the other device has subscribed or unsubscribed). This is an indicator that the device is “listening” to your code. Some operating systems, like Android, subscribe to every service whether they use it or not.

Detecting if the keyboard service may be in use #keyboard-isEnabled

keyboard.isEnabled()

true indicates that the device is currently subscribed to the service. false indicates the device is not currently subscribed to the service. This may mean that the other device is off or out of range.

Media Service #media #media-keys

Starting the Media Service #media-startMediaService

media.startMediaService()

Starts the media service. This must execute in the start block. All other media blocks require the service be started.

Specific Media Keys

media.keys(media._MediaKey.next)

Select a specific media key to send. Only the keys listed may be sent.

Sending a media key #media-sendCode

media.sendCode()

Send a media key. For example, to send “play/pause” [media.sendCode(media.keys(media._MediaKey.playPause))]

~hint

Showing the iOS Keyboard with Keyboard Service

When the regular Keyboard service is used with iOS it will disable use of the on-screen keyboard. You can re-enable it by sending an eject: [media.sendCode(media.keys(media._MediaKey.eject))]

~

Detecting if the media service use has changed #media-setStatusChangeHandler

media.setStatusChangeHandler()

The device using the service needs to “subscribe” to the service. This event handler will indicate that the status of the subscription has changed (either the other device has subscribed or unsubscribed). This is an indicator that the device is “listening” to your code. However, it isn’t perfect. Some operating systems, like Android, subscribe to every service whether they use it or not.

Detecting if the media service may be in use #media-isEnabled

media.isEnabled()

true indicates that the device is currently subscribed to the service. false indicates the device is not currently subscribed to the service. This may mean that the other device is off or out of range.

Mouse Service #mouse

Starting the Mouse Service #mouse-startMouseService

mouse.startMouseService()

Starts the mouse service. This must execute in the start block. All other mouse blocks require the service be started.

mouse.movexy()

Move the mouse by the given amounts in the x (horizontal) and y (vertical) directions. x can be from -127 to 127. y can be from -127 to 127.

mouse.click()

Click the left mouse button.

mouse.middleClick()

Click the middle mouse button.

mouse.rightClick()

Click the right mouse button.

mouse.scroll()

Scroll the scroll wheel by the given number of “clicks”. The clicks can be from -127 to 127.

mouse.send()

Send all mouse behavior simultaneously. x is the amount to move horizontaly (-127 to 127), y is the amount to move vertically (-127 to 127), left indicates if the left mouse button is pressed, middle indicates if the middle mouse button is pressed, right indicates if the right mouse button is pressed, scroll is the amount to scroll (-127 to 127), and hold indicates if the buttons should be “held” until the next mouse command (when false the buttons are “clicked”).

Detecting if the mouse service use has changed #mouse-setStatusChangeHandler

mouse.setStatusChangeHandler()

The device using the service needs to “subscribe” to the service. This event handler will indicate that the status of the subscription has changed (either the other device has subscribed or unsubscribed). This is an indicator that the device is “listening” to your code. However, it isn’t perfect. Some operating systems, like Android, subscribe to every service whether they use it or not.

Detecting if the mouse service may be in use #mouse-isEnabled

mouse.isEnabled()

true indicates that the device is currently subscribed to the service. false indicates the device is not currently subscribed to the service. This may mean that the other device is off or out of range.

~hint

iOS AssistiveTouch #assistivetouch

A mouse can be used in iOS when AssistiveTouch features are enabled, See (https://www.macworld.com/article/232969/how-to-use-a-mouse-with-your-ipad-or-iphone.html for more detail.

~

Absolute Mouse Service #absolute-mouse

Absolute mouse currently only works on macOS and Windows. Interactions beteen Absolute Mouse and Mouse may not be well defined.

Starting the Absolute Mouse Service #absmouse-startAbsoluteMouseService

absmouse.startAbsoluteMouseService()

Starts the absolute mouse service. This must execute in the start block. All other absolute mouse blocks require the service be started.

absmouse.movexy()

Move the mouse pointer to the specific location. x goes from -32767 to 32767. -32767 represents the left side of the screen and 32767 represents the right side of the screen. y goes from -32767 to 32767. -32767 represents the top of the screen and 32767 represents the bottom of the screen. (0,0) is the center of the screen.

~hint

Screen coordinates and mapping

You can:

For example, if:

The x coordinate is approximately: 6cm/28cm*2560pix = 548pix

The absolute y coordinate can be computed by: [y=Math.map(548,0,2560,-32767,32767)]

~

absmouse.click()

Click the left mouse button.

absmouse.middleClick()

Click the middle mouse button.

absmouse.rightClick()

Click the right mouse button.

absmouse.send()

Send all absolute mouse behavior simultaneously. x: number, y: number, left: boolean, middle: boolean, right: boolean, hold: boolean): void;

x is the horizontal location to place the mouse (-32767 to 32767), y is the vertical location to place the mouse (-32767 to 327679),left indicates if the left mouse button is pressed, middle indicates if the middle mouse button is pressed, right indicates if the right mouse button is pressed, and hold indicates if the buttons should be “held” until the next mouse command (when false the buttons are “clicked”).

Detecting if the absmouse service use has changed #absmouse-setStatusChangeHandler

absmouse.setStatusChangeHandler()

The device using the service needs to “subscribe” to the service. This event handler will indicate that the status of the subscription has changed (either the other device has subscribed or unsubscribed). This is an indicator that the device is “listening” to your code. However, it isn’t perfect. Some operating systems, like Android, subscribe to every service whether they use it or not.

Detecting if the absmouse service may be in use #absmouse-isEnabled

absmouse.isEnabled()

true indicates that the device is currently subscribed to the service. false indicates the device is not currently subscribed to the service. This may mean that the other device is off or out of range.

Gamepad Service #gamepad

Starting the Gamepad Service #gamepad-startGamepadService

gamepad.startGamepadService()

Starts the gamepad service. This must execute in the start block. All other gamepad blocks require the service be started.

Direction Pad keys (D-Pad) #gamepad-_dpad

gamepad._dpad(GameDirection.noDirection)

A direction value for the direction pad

Buttons #gamepad-_buttons

gamepad._buttons()

A value indicating if a designated button is pressed or not.

Send a gamepad command #gamepad-send

gamepad.send()

Send all gamepad behavior simultaneously. buttons is a set of buttons and their current state, x is the amount to move horizontally (-127 to 127), y is the amount to move vertically (-127 to 127), dpad is the value of the direction pad, z is the amount to move on the z-axis (-127 to 127), rx is the rotation about the x-axis.

Detecting if the gamepad service use has changed #gamepad-setStatusChangeHandler

gamepad.setStatusChangeHandler()

The device using the service needs to “subscribe” to the service. This event handler will indicate that the status of the subscription has changed (either the other device has subscribed or unsubscribed). This is an indicator that the device is “listening” to your code. However, it isn’t perfect. Some operating systems, like Android, subscribe to every service whether they use it or not.

Detecting if the gamepad service may be in use #gamepad-isEnabled

gamepad.isEnabled()

true indicates that the device is currently subscribed to the service. false indicates the device is not currently subscribed to the service. This may mean that the other device is off or out of range.

Cool Ideas and Challenges

Here are some examples: https://youtu.be/n4J5GN72N_4

Tips

iOS Screen Keyboard

Use the Media Eject to allow the keyboard to be used while also using the Keyboard service.

Known Limitations

Windows

macOS

iOS

Android

Supported targets

for PXT/microbit