Notepad of a tinker, maker, hacker or whatever you call it :)

ESP32 / MQTT - BLE beacon tracker powered by microPython

I am currently working with BLE beacons -only iBeacons for now- with Espressif's esp-idf libraries. The aims it to use the ESP32 as a gateway to published detected beacons to a MQTT broker.
It was surprisingly easy and I will write something about that when the code will be more polished.

By the way, by curiosity and to speed up development, I wanted to try microPython port to ESP32, and I did. Unfortunately BLE support is not yet reliable enough and lot of advertisements was lost with 4 to 6 beacons. This post is to keep track of my work and hopefully use in the future when microPython port will be reliable :-)

microPython compilation

While compiling microPython was really straightforward, I had one issue afterwards: I was unable to use MQTT library, neither by calling it mqtt or umqtt, nor by putting it manually in flash. I did not investigate why but I found that adding it to the "frozen" scripts worked.

The code used is from Pycom with Wipy 2.0 as target. I used this commit against this version of esp-idf.

  • Clone both repositories, update esp-idf's submodules
  • Set PATH to xtensa compilation tools. Be careful to use only version 1.22.0-61 for the mentioned Pycom & esp-idf commits.
  • Set IDF_PATH to esp-idf directory
  • Change directory to pycom-micropython-sigfox
  • run
    • cd mpy-cross && make clean && make
    • cd ../ESP32/frozen
    • wget
    • cd ..
    • make BOARD=WIPY TARGET=boot
    • make BOARD=WIPY TARGET=app
    • make BOARD=WIPY flash

You can now connect to microPython REPL on your ESP32!

Python BLE tracker

Following is a quick and dirty code I used to test it. It does:

  • Connect to WiFi
  • Connect to MQTT server
  • Scan for BLE advertisements
  • Send one MQTT message by discovered device

from network import Bluetooth, WLAN
from mqtt import MQTTClient
from binascii import hexlify
import machine
import time
import ujson

def hexlifyNone( object ):
    return None if object is None else hexlify( object )

def scan( ):
    devices = {}
    print( '***   Starting   ***' )
    bt = Bluetooth( )
    print( 'Scan...', end = '' )
    bt.start_scan( 10 )
    time.sleep( 10 )
    print( 'OK' )
    advs = bt.get_advertisements()
    print( advs )

    for adv in advs:
        devices[ hexlify( adv.mac ) ] = {
                            'Mac': hexlify( adv.mac ),
                            'Rssi': adv.rssi,
                            'Data': hexlifyNone( ),
                            'MfrData': hexlifyNone( bt.resolve_adv_data(, Bluetooth.ADV_MANUFACTURER_DATA ) ),
                            'NameShort': hexlifyNone( bt.resolve_adv_data(, Bluetooth.ADV_NAME_SHORT ) ),
                            'NameComplete': hexlifyNone( bt.resolve_adv_data(, Bluetooth.ADV_NAME_CMPL ) ),
                            'Flag': bt.resolve_adv_data(, Bluetooth.ADV_FLAG ),
                            'Appearance': hexlifyNone( bt.resolve_adv_data(, Bluetooth.ADV_APPEARANCE ) )
        print( 'MAC: {}\n\tRSSI: {}\n\tData: {}\n\tName: {}\n\tMfrData: {}'.format(
                hexlify( adv.mac ),
                hexlify( ),
                bt.resolve_adv_data(, Bluetooth.ADV_NAME_SHORT ),
                hexlify( bt.resolve_adv_data(, Bluetooth.ADV_MANUFACTURER_DATA ) )
    print( "\n***********\nDevices: ", devices )
    for key, data in devices.items():
        print( "\n***********\n", data )
        msgJson = ujson.dumps( data )
        client.publish( topic="/test", msg = msgJson )

    print( '***   End   ***' )

def sub_cb(topic, msg):

wlan = WLAN( mode=WLAN.STA )
wlan.connect( "SSID name", auth=( WLAN.WPA2, "password" ), timeout=5000 )
while not wlan.isconnected( ): 
    machine.idle( )
print( "Connected to Wifi\n" )

client = MQTTClient( "device_id", "MQTT broker address",user="user if any", password="password if any", port=1883, ssl=True )
client.set_callback( sub_cb )
client.connect( )
client.subscribe( topic="/test" )
client.publish( topic="/test", msg="Started" )
scan( )