Importing bluetooth module in micropython

I have been trying to communicate between a linux pc and microbit to exchange data. I can do this with uart over serial usb but I wanted to see if it is possible to use bluetooth. One thing I can’t get past is, I cannot import the bluetooth module. The following program does not work:

# Minimal test
from microbit import display, Image
try:
    import bluetooth
    print("Bluetooth module imported successfully!")
    display.show(Image.YES)
except ImportError:
    print("Failed to import bluetooth module.")
    display.show(Image.NO)

# Keep running so you can see the result
while True:
    pass

I have the microbit v 2.21 and I am using the latest firmware. I also saw this page:

and I tried flashing the CODAL pre-compiled hex file, but it did not help either. Is this something that is not possible or am I missing an obvious step?

Please note that I am not trying to pair microbit via bluetooth. I want to write my own python code to do the advertising, service and characteristic specifications and connect from the linux pc using my own code that acts as a bleak client. I hope that makes sense and thank you in advance.

Cheers

Hi @microzirtik ,

MakeCode doesn’t provide access to customize Bluetooth advertising or to create custom services, but you can use the provided services. And rather than a custom service, you can use the UART service to exchange messages and do custom things. Many mobile apps that interact with micro:bits use this approach and can often be easily used to interact with the micro:bit in applications the app wasn’t originally meant for. There are some examples of that elsewhere in this forum, like the discussion at: Android app/Discord - #8 by bsiever

You can control advertising and characteristics via C/C++ and through MakeCode via C++ based extensions.

I wrote MakeCode BLE HID blocks that control the advertising and create services (here). It provides some examples of how to customize advertising.

I’ve also written a bluetooth service to retrieve the datalogger data (here). There’s a corresponding JavaScript library/webpage to retrieve the data from a computer here (source) or a more user-friendly prototype website at https://2024-ui-sp.github.io/Micro-bit-ble/ (source) . This work may be a better example of writing a custom service than the BLE HID blocks.

Hope that helps a bit,
Bill

P.S. According to the docs, the other version of Python, micropython, doesn’t support BLE for anything other than flashing. See https://microbit-micropython.readthedocs.io/en/v2-docs/ble.html.

4 Likes

Thank you Bill. This is extremely useful. When I was looking at your code on Android app/Discord above, I saw that you were using the bluetooth modules. I clicked on the edit button and saw that the python code was indeed using them fine. It occurred to me then that I did not have the Bluetooth extension enabled on MakeCode. Going to extensions and enabling them automatically removed the radio extension, and replaced that with Bluetooth. This allowed me to import the bluetooth module, so I can continue experimenting with the uart service.

Cheers

1 Like

I did try the uart service but looks like I am not able to make it discoverable through my iphone, mac or linux pc. I wrote a very minimal version of it just to see it among the list of devices. Here is my code:

# -------- MINIMAL BLUETOOTH TEST (MakeCode Python) --------
import bluetooth # Make sure bluetooth module is recognized
import basic   # Make sure basic module is recognized

basic.show_icon(IconNames.HEART) # Show something to confirm program start
basic.pause(1000)

bluetooth.set_transmit_power(7.)
# Attempt to start bluetooth services. This should trigger advertising.
bluetooth.start_uart_service() # Or even just bluetooth.start_accelerometer_service() to test *any* BT service
basic.show_icon(IconNames.HAPPY) # Show Bluetooth icon if start_uart_service was called

# Keep the program alive so Bluetooth stack can run
def on_forever_minimal():
    basic.pause(500)
basic.forever(on_forever_minimal)

I also tried different options from “settings” → “project settings” and chose “no pairing required” before flashing the firmware. I have two microbits that have the same issue so I’m thinking this is not about the hardware. I also tried flashing Bill’s code in the above reply (Android one) and the microbit was still not discoverable. Any tips on making this work?

Here is my DETAILS.TXT file in case it helps:

DAPLink Firmware - see https://daplink.io

Build ID: v0257-gc782a5ba (gcc)
Unique ID: 99063602000528201dac4ab6511d7107000000006e052820
HIC ID: 6e052820
Auto Reset: 1
Automation allowed: 0
Overflow detection: 0
Incompatible image detection: 1
Page erasing: 0
Daplink Mode: Interface
Interface Version: 0257
Bootloader Version: 0257
Git SHA: c782a5ba907377658bc28aa8d132a0fa44543687
Local Mods: 0
USB Interfaces: MSD, CDC, HID, WebUSB
Bootloader CRC: 0x725bea7d
Interface CRC: 0xe561f1de
Remount count: 0

and I am using a Microbit v2.2

Thanks

I tried your link / code and it showed up in the LightBlue app on my iPhone and I was able to connect (micro:bit V2) — the version with “No Pairing Required”. I didn’t make any changes.

Two things to consider/try:

  1. If you have previously tried to pair devices, you probably need to go to settings on your phone/computer/etc. and “forget” the micro:bit device. Once paired, the keys are retained until the device is forgotten. The retained settings can hinder future connections, especially if the pairing type is changed or the micro:bit’s code is moderately changed (which can reset the stored pairing credentials). It may also help to turn bluetooth off and on to flush settings.
  2. iOS/macOS’s Bluetooth Settings will only show devices that provide O.S.-level services, like keyboards and mice. They will not show other bluetooth devices, like the micro:bit. Instead you have to use an app that supports generic bluetooth discovery, like LightBlue or nRF Connect. (I’m not sure, but I think iOS/macOS also filter the O.S.-level services, so generic bluetooth discovery apps can not see the O.S.-level stuff, like a keyboard).

Hope that helps

Hello there, thanks for replying. When I read your reply, I tried the IOS app LightBlue based on your suggestion and it worked. Then I got busy with work and didn’t get a chance to work on this in Linux until last week. The problem is the same as on iOS/macOS — the standard Bluetooth settings panel (e.g., GNOME Bluetooth) only shows devices that provide OS-level services like keyboards and mice. It does not show generic BLE peripherals like the micro:bit. You need a BLE-specific scanner, the Linux equivalent of LightBlue on iOS.

Here’s everything I did step by step in case someone else runs into the same problem and finds this page:

Check if your Bluetooth adapter supports BLE

Run this command to see your adapter’s capabilities:

btmgmt info

Look at the “supported settings” line. You need “le” to be listed there. My adapter is HCI Version 4.2 (Qualcomm), which supports BLE.

Enable BLE on the adapter

In my case, “le” was in the supported settings but NOT in the “current settings”, meaning BLE was disabled. I enabled it with:

sudo btmgmt le on

The output confirmed it was enabled:

hci0 Set Low Energy complete, settings: powered connectable discoverable bondable ssp br/edr le secure-conn

You can verify by checking that “le” now appears in “current settings”:

btmgmt info | grep "current settings"

Set up a Python BLE scanner

I created a virtual environment and installed the “bleak” library

python3 -m venv .venv
.venv/bin/pip install bleak

Then I wrote this simple scanner script (ble_scanner.py):

import asyncio
from bleak import BleakScanner

async def scan():
    print("Scanning for BLE devices (10 seconds)...")
    devices = await BleakScanner.discover(timeout=10, return_adv=True)

    for address, (device, adv_data) in devices.items():
        name = device.name or "Unknown"
        print(f"\n  Name:    {name}")
        print(f"  Address: {address}")
        print(f"  RSSI:    {adv_data.rssi} dBm")
        if adv_data.service_uuids:
            print(f"  Services: {adv_data.service_uuids}")

    print(f"\nTotal: {len(devices)} device(s) found.")

asyncio.run(scan())

If you get the error “org.bluez.Error.InProgress - Operation already in progress”, it means something else is already running a Bluetooth scan (e.g., the GNOME Bluetooth panel). Close any Bluetooth settings windows, or restart the Bluetooth service:

sudo systemctl restart bluetooth
sudo btmgmt le on

(You need to re-enable LE after restarting the service.)

Then, run the scanner

.venv/bin/python ble_scanner.py

My micro:bit showed up immediately:

Name:    BBC micro:bit [vovez]
Address: xx:xx:xx:xx:xx:xx
RSSI:    -34 dBm

On the micro:bit side, I used “No Pairing Required” in Project Settings as before, and this same MakeCode Python code:

import bluetooth
import basic

basic.show_icon(IconNames.HEART)
basic.pause(1000)

bluetooth.set_transmit_power(7)
bluetooth.start_uart_service()
basic.show_icon(IconNames.HAPPY)

def on_forever_minimal():
    basic.pause(500)
basic.forever(on_forever_minimal)

Cheers!