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:
- Android 11 and later (May work on some Android 9+ devices)
- Tested on a Galaxy Tab A (8", 2019)
- Here's an unofficial Android BLE HID support list, which includes details on HID support for about 120 different Android devices.
- Windows 10 Pro and later
- Tested on a Microsoft Surface Pro (5th Gen)
- iOS: Tested on 15.1
- According to Apple Support, the HID Profile is supported on "iPhone 5s and later, iPad Air and later, and iPod touch (6th generation) and later".
- Tested on an iPhone 11 Pro
- macOS: Tested on 11.4 (Big Sur)
- Tested on an M1 Air, but it should work on any Mac made after 2014 running Big Sur or later
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.
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:
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
.
- Open bluetooth preferences on the device you want to interact with (a computer, phone, or tablet),
- select the micro:bit that you want to connect to (it will be named something like "uBit [XXXXX]"), and
- 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:
iOSPairing.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.
macOSPairing.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.
AndroidPairing.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.
WindowsPairing.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.
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:
iOSUNpairing.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.
macOSUNpairing.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.
AndroidUNpairing.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.
WindowsUNpairing.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.
If you are just making small changes to a paired micro:bit, you can usually reprogram if without losing the stored key.
keyboard.startKeyboardService()
Starts the keyboard service. This must execute in the start
block. All other keyboard blocks require the service be started.
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
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.
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))]
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") ]
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()]
keyboard.releaseKeys()
Release any keys that were held down by use of [keyboard.sendSimultaneousKeys("...", true)]
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))]
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.
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.
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.startMediaService()
Starts the media service. This must execute in the start
block. All other media blocks require the service be started.
media.keys(media._MediaKey.next)
Select a specific media key to send. Only the keys listed may be sent.
media.sendCode()
Send a media key. For example, to send "play/pause" [media.sendCode(media.keys(media._MediaKey.playPause))]
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))]
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.
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.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").
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.
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.
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 currently only works on macOS and Windows. Interactions beteen Absolute Mouse and Mouse may not be well defined.
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.
You can:
- Identify the resolution of your screen (Ex: 2560x1600)
- Measure the distance over and down to something you want to interact with as well as the total screen width and height.
- Use the above to compute the approximate coordinates of an item on the sceen
- Take advantage of the
map
block ([y=Math.map(0,0,0,0,0)]
) to convert from the desired screen coordinate to the absolute mouse coordinate.
For example, if:
- Screen is 28cm wide and 2560 pixels horizontal resolution
- The item you want to click on the screen is 6cm from the left
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").
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.
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.startGamepadService()
Starts the gamepad service. This must execute in the start
block. All other gamepad blocks require the service be started.
gamepad._dpad(GameDirection.noDirection)
A direction value for the direction pad
gamepad._buttons()
A value indicating if a designated button is pressed or not.
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.
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.
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.
Here are some examples: https://youtu.be/n4J5GN72N_4
- Create a mouse using the accelerometer
- Create a gamepad using the accelerometer
- Use the keyboard support to control a slideshow (Google Slides, PowerPoint, Keynote, etc.)
- Use the micro:bit to take pictures and start/stop recordings on your phone/tablet. Hint: most mobile devices' camera app will take a picture when a volume button is pressed
- Make a remote control to play/pause music and/or control volume
- Automate repetitive computer tasks
Use the Media Eject to allow the keyboard to be used while also using the Keyboard service.
- You may need an application to "remap" the GamePad controls provided by this extension to work with a specific game.
- You may need an application to "remap" the GamePad controls provided by this extension to work with a specific game. Applications like Joystick Monitor can help test if/how the GamePad is detected.
- Doesn't support a mouse or gamepad unless AssistiveTouch is enabled
- Doesn't honor media "Stop"
- Doesn't support
Absolute Mouse
- Doesn't support
Absolute Mouse
- I develop micro:bit extensions in my spare time to support activities I'm enthusiastic about, like summer camps and science curricula. You are welcome to become a sponsor of my micro:bit work (one time or recurring payments), which helps offset equipment costs: here. Any support at all is greatly appreciated!
for PXT/microbit
<script src="https://makecode.com/gh-pages-embed.js"></script> <script>makeCodeRender("{{ site.makecode.home_url }}", "{{ site.github.owner_name }}/{{ site.github.repository_name }}");</script>