Maker.io main logo

CircuitPython BLE Advertising Beacons

2024-02-21 | By Adafruit Industries

License: See Original Project Bluetooth / BLE

Courtesy of Adafruit

Guide by John Park

Overview

Mmmmm, bacon.....‎

What?‎

Oh. Beacons? Yes, beacons!‎

bacon_1

Beacons are low power, low-cost Bluetooth LE devices that can be ‎used for a very simple job: to advertise a URL to users running an ‎app on their mobile device within range of the device!‎

Technically, beacons can also be used for a few different scenarios ‎where a localized, wireless data broadcast is desirable, such as ‎indoor location tracking, point of sales/marketing, and experience ‎personalization.‎

We'll create two version -- first, a simple beacon that can run on any ‎CircuitPython capable nRF52840 board. Second, a custom version for ‎the CLUE board that also shows QR codes and allows you to scroll ‎among multiple beacon URL choices to advertise!‎

Parts

All you need to advertise your own BLE beacons is any Adafruit ‎Bluefruit nRF52840 board:‎

You'll also want a USB cable for programming it and power, or you ‎can use a LiPoly battery for remote powering.‎

Additionally, you'll need a mobile device running an Eddystone ‎beacon discovery app, such as Physical Web:‎

Understanding BLE Advertising ‎Beacons

beacon_2

Bluetooth LC beacons are low power devices that are used to ‎broadcast a tiny amount of data to any device that's listening. This ‎often takes the form of a mobile device running an app, such as ‎Physical Web, which can read the beacon's advertising broadcast ‎and display a URL the user can then visit.‎

There are two primary BLE beacon standards: Apple's iBeacon, and ‎Google's Eddystone. In this guide, we'll use the Eddystone standard ‎to create a beacon that advertises a website URL (which you can ‎customize to any URL you like.)‎

Advertising

When an Eddystone beacon is running, it transmits a packet of ‎information that includes a Unique Identifier (UID), and another ‎packet of information that includes a website URL.‎

Note, this data is transmitted with no need for connecting, pairing, or ‎bonding with a mobile device. The beacon is pretty much just always ‎sitting there yelling, "Hey! I'm a beacon! This is my name, and this is ‎a URL I think you should visit!"‎

Other Uses

This guide focuses solely on using beacons to advertise a URL, ‎however there are a few other common uses for beacons. These ‎include indoor navigation, tracking (such as lost key finders like Tile), ‎and interaction notifications, such as interactive museum tours.‎

Simple Advertising Beacons

simple_3

CircuitPython Setup

For our simple beacon example, we'll use the ItsyBitsy nRF52840. ‎We'll show you how to set it up with the proper libraries and code to ‎use on the Itsy, but you can apply these instructions to the Feather ‎nRF52840, Circuit Playground Bluefruit, or CLUE boards as well by ‎downloading the appropriate version of CircuitPython for those ‎boards.‎

CircuitPython for ItsyBitsy nRF52840 ‎Express

CircuitPython is a derivative of MicroPython designed to simplify ‎experimentation and education on low-cost microcontrollers. It ‎makes it easier than ever to get prototyping by requiring no upfront ‎desktop software downloads. Simply copy and edit files on ‎the CIRCUITPY drive to iterate.‎

Set up CircuitPython Quick Start!

Follow this quick step-by-step for super-fast Python power :)‎

Download the latest version of CircuitPython for this board via ‎CircuitPython.org

Further Information

For more detailed info on installing CircuitPython, check ‎out Installing CircuitPython.‎

Click the link above and download the latest UF2 file.‎

Download and save it to your desktop (or wherever is handy).‎

download_4

Plug your Itsy nRF52840 into your computer using a known-good ‎USB cable.‎

A lot of people end up using charge-only USB cables and it is very ‎frustrating! So, make sure you have a USB cable you know is good ‎for data sync.‎

In the image, the Reset button is indicated by the magenta arrow, ‎and the BTLE status LED is indicated by the green arrow.‎

Double-click the Reset button on your board (magenta arrow), and ‎you will see the BTLE LED (green arrow) will pulse quickly then ‎slowly blue. If the DotStar LED turns red, check the USB cable, try ‎another USB port, etc.‎

If double-clicking doesn't work the first time, try again. Sometimes it ‎can take a few tries to get the rhythm right!‎

plug_5

You will see a new disk drive appear called ITSY840BOOT.‎

Drag the adafruit_circuitpython_etc.uf2 file to ITSY840BOOT.‎

drag_6

drag_7

The LED will flash. Then, the ITSY840BOOT drive will disappear, and ‎a new disk drive called CIRCUITPY will appear.‎

That's it, you're done! :)‎

done_8

Simple Beacon Code in CircuitPython

Text Editor

Adafruit recommends using the Mu editor for using your ‎CircuitPython code with Adafruit boards. You can get more info ‎in this guide.‎

Alternatively, you can use any text editor that saves files.‎

CircuitPython Libraries & Code.py

To use with CircuitPython, you need to first install a few libraries, into ‎the lib folder on your CIRCUITPY drive. Then you need to ‎update code.py with the example script.‎

Thankfully, we can do this in one go. In the example below, click ‎the Download Project Bundle button below to download the ‎necessary libraries and the code.py file in a zip file. Extract the ‎contents of the zip file, open the directory examples/ and then click ‎on the directory that matches the version of CircuitPython you're ‎using and copy the contents of that directory to ‎your CIRCUITPY drive.‎

Your CIRCUITPY drive should now look similar to the following ‎image:‎

drive_9

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

"""This example broadcasts our Mac Address as our Eddystone ID and a link to the Adafruit Discord
server."""

import time

import adafruit_ble
from adafruit_ble_eddystone import uid, url

radio = adafruit_ble.BLERadio()

# Reuse the BLE address as our Eddystone instance id.
eddystone_uid = uid.EddystoneUID(radio.address_bytes)
eddystone_url = url.EddystoneURL("https://adafru.it/discord")

while True:
# Alternate between advertising our ID and our URL.
radio.start_advertising(eddystone_uid)
time.sleep(0.5)
radio.stop_advertising()

radio.start_advertising(eddystone_url)
time.sleep(0.5)
radio.stop_advertising()

time.sleep(4)

View on GitHub

Code Explainer

Libraries

Here's how this code works. First, we import ‎the time and adafruit_ble libraries.‎

Then, we import uid and url from the adafruit_ble_eddystone library. These ‎two give us the capability to advertise a unique identifier (UID) and a ‎website URL.‎

Download File

Copy Code
import time

import adafruit_ble
from adafruit_ble_eddystone import uid, url

Radio & Eddystone Setup

Next, we'll create an instance of the BLERadio() and then create a ‎variable for the eddystone_uid. This is the unique ID that is used to ‎differentiate beacons when multiple of them are present. We can re-‎use the BLE radio.address_bytes as our UID.‎

We'll also create a variable for the website URL we want to be ‎advertised from our beacon, eddystone_url.‎

In this case, we are using https://adafru.it/discord as the beacon ‎URL, but you can swap in a URL of your choosing. Just be careful it ‎isn't too long -- anything under 18 characters will work, otherwise ‎you can use a URL shortener service, such as bit.ly.‎

Download File

Copy Code
radio = adafruit_ble.BLERadio()

# Reuse the BLE address as our Eddystone instance id.
eddystone_uid = uid.EddystoneUID(radio.address_bytes)
eddystone_url = url.EddystoneURL("https://adafru.it/discord")

You may get away with a longer URL than you think you should -- ‎this is because for certain common URL prefixes ('https://www.' for ‎example) and domains ('.com', '.org', etc.) we substitute them for a ‎single non-printing byte.‎

Advertising

With setup complete, the main, ever-repeating loop of the code now ‎takes over. Here, we alternate between advertising the beacon's UID ‎and the beacon's URL, pause for four seconds, and repeat.‎

Download File

Copy Code
while True:
# Alternate between advertising our ID and our URL.
radio.start_advertising(eddystone_uid)
time.sleep(0.5)
radio.stop_advertising()

radio.start_advertising(eddystone_url)
time.sleep(0.5)
radio.stop_advertising()

time.sleep(4)

Usage

To use it, simply power up the beacon and then launch your Physical ‎Web app (or another beacon-aware app) on your mobile device. ‎You'll see the app scan for beacons.‎

When the beacon is found, you will see some info about it, including ‎a clickable link to take you to the advertised URL.‎

Any icons and descriptive text are scraped from the website itself, ‎and not part of the data delivered by the beacon.‎

usage_10

That's all there is to it! Next, we'll create a more sophisticated version ‎on the CLUE.‎

CLUE Beacon

clue_11

First, set up CircuitPython on the CLUE following the instructions on ‎this page.‎

Libraries

Next, install the libraries needed. This guide page will show you ‎where to download them.‎

You'll need the following libraries for this project:

  • adafruit_bitmap_font
  • adafruit_ble
  • adafruit_ble_eddystone
  • adafruit_bus_device
  • adafruit_display_shapes
  • adafruit_display_text
  • adafruit_pybadger
  • adafruit_register
  • adafruit_lis3mdl.mpy
  • adafruit_lsm6ds.mpy
  • adafruit_miniqr.mpy
  • neopixel.mpy

Clue Libraries

Text Editor

Adafruit recommends using the Mu editor for using your ‎CircuitPython code with the Feather boards. You can get more info ‎in this guide.‎

Alternatively, you can use any text editor that saves files.‎

Image Files and Code.py

This project uses a couple of .bmp image files -- you can get them by ‎downloading the Project Zip link in the embed below. Unzip the ‎downloaded zip archive and then drag the two image files to your ‎CLUE board's CIRCIUTPY drive.‎

Copy the code shown below, paste it into Mu. Save the code from Mu ‎to the CLUE's CIRCUITPY drive as code.py.‎

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2020 John Park for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
Eddystone Beacon for CLUE
This example broadcasts our Mac Address as our Eddystone ID and a link to a URL of your choice.
Hold the A button to display QR code, use B button to pick URL from the list.
"""

import time
from adafruit_pybadger import pybadger
import adafruit_ble
from adafruit_ble_eddystone import uid, url

radio = adafruit_ble.BLERadio()

# Reuse the BLE address as our Eddystone instance id.
eddystone_uid = uid.EddystoneUID(radio.address_bytes)

# List of URLs to broadcast here:
ad_url = [("https://circuitpython.org", "CirPy"),
("https://adafru.it/discord","DISCORD"),
("https://forums.adafruit.com", "Forums"),
("https://learn.adafruit.com", "Learn")
]
pick = 0 # use to increment url choices

pybadger.play_tone(1600, 0.25)
pybadger.show_business_card(image_name="cluebeacon.bmp")

while True:
pybadger.auto_dim_display(delay=3, movement_threshold=4)
eddystone_url = url.EddystoneURL(ad_url[pick][0])

if pybadger.button.a and not pybadger.button.b: # Press button A to show QR code
pybadger.play_tone(1200, 0.1)
pybadger.brightness = 1
pybadger.show_qr_code(data=ad_url[pick][0]) # Tests QR code
time.sleep(0.1) # Debounce

elif pybadger.button.b and not pybadger.button.a: # iterate through urls to broadcast
pybadger.play_tone(1600, 0.2)
pick = (pick + 1) % len(ad_url)
pybadger.brightness = 1
pybadger.show_business_card(image_name="bg.bmp", name_string=ad_url[pick][1], name_scale=5,
email_string_one="", email_string_two=ad_url[pick][0])
time.sleep(0.1)

elif pybadger.button.a and pybadger.button.b:
pybadger.play_tone(1000, 0.2)
pybadger.brightness = 1
pybadger.show_business_card(image_name="cluebeacon.bmp")
time.sleep(0.1)

# Alternate between advertising our ID and our URL.
radio.start_advertising(eddystone_uid)
time.sleep(0.5)
radio.stop_advertising()

radio.start_advertising(eddystone_url)
time.sleep(0.5)
radio.stop_advertising()

time.sleep(1)

View on GitHub

projects_cluebacon

How It Works

Like the simple example before, we will import libraries for time, ble, ‎and eddystone support. We will also import the adafruit_pybadger library ‎which makes it convenient to use the buttons, speaker, and screen ‎on the CLUE.‎

Download File

Copy Code
import time
from adafruit_pybadger import pybadger
import adafruit_ble
from adafruit_ble_eddystone import uid, url

Setup

We'll set up the radio and eddystone_uid as before, but this time instead of ‎a single URL, we'll create a list of few to pick through.‎

This is a list of tuples, which each entry containing both a URL and a ‎‎"nice name".‎

The pick variable will be used to select a URL from the list when the B ‎button is pressed.‎

Download File

Copy Code
radio = adafruit_ble.BLERadio()

# Reuse the BLE address as our Eddystone instance id.
eddystone_uid = uid.EddystoneUID(radio.address_bytes)

# List of URLs to broadcast here:
ad_url = [("https://circuitpython.org", "CirPy"),
("https://adafru.it/discord","DISCORD"),
("https://forums.adafruit.com", "Forums"),
("https://learn.adafruit.com", "Learn")
]
pick = 0 # use to increment url choices

We'll use the pybadger.play_tone() command to beep the on-board ‎speaker, and the pybadger.show_business_card() command to display a .bmp ‎image.‎

Download File

Copy Code
pybadger.play_tone(1600, 0.25)
pybadger.show_business_card(image_name="cluebeacon.bmp")

Main Loop

In the main loop of the program, we will set the display to auto dim ‎after three seconds and set the movement threshold so you can ‎shake it a little to wake up the CLUE and undim it.‎

The eddystone_url is set to the first one in the list.‎

Download File

Copy Code
pybadger.auto_dim_display(delay=3, movement_threshold=4)
eddystone_url = url.EddystoneURL(ad_url[pick][0])

Buttons

When the A button is pressed by itself, ‎the pybadger.show_qr_code() command is run, displaying the automatically ‎generated QR code image for the currently selected URL.‎

Download File

Copy Code
if pybadger.button.a and not pybadger.button.b:  # Press button A to show QR code
pybadger.play_tone(1200, 0.1)
pybadger.brightness = 1
pybadger.show_qr_code(data=ad_url[pick][0]) # Tests QR code
time.sleep(0.1) # Debounce

When the B button is pressed, the next URL in the list is selected, ‎and this info is displayed on the screen using ‎the pybadger.show_business_card() command.‎

Download File

Copy Code
elif pybadger.button.b and not pybadger.button.a:  # iterate through urls to broadcast
pybadger.play_tone(1600, 0.2)
pick = (pick + 1) % len(ad_url)
pybadger.brightness = 1
pybadger.show_business_card(image_name="bg.bmp", name_string=ad_url[pick][1], name_scale=5,
email_string_one="", email_string_two=ad_url[pick][0])
time.sleep(0.1)

When both A and B buttons are pressed at the same time, the ‎display returns to the initial screen image.‎

Download File

Copy Code
elif pybadger.button.a and pybadger.button.b:
pybadger.play_tone(1000, 0.2)
pybadger.brightness = 1
pybadger.show_business_card(image_name="cluebeacon.bmp")
time.sleep(0.1)

Beacon Advertising

Finally, we have the beacon advertising code, just as with the simple ‎example. The beacon UID is advertised, then the URL is, and this ‎repeats every second.‎

Download File

Copy Code
# Alternate between advertising our ID and our URL.
radio.start_advertising(eddystone_uid)
time.sleep(0.5)
radio.stop_advertising()

radio.start_advertising(eddystone_url)
time.sleep(0.5)
radio.stop_advertising()

time.sleep(1)

CLUE Beacon Usage

Power up the CLUE and you'll see the welcome screen.‎

clue_12

Pick Beacon Advertisement

Press the B button to pick a URL from your list. There is a small beep ‎when the button press is recognized -- you may need to hold it for a ‎second first.‎

clue_13

The current beacon name and URL will be displayed.‎

clue_14

QR Code

Press the A button to go to QR code mode. This is great for any users ‎without a BLE beacon app who want to use their camera to scan the ‎URL instead.‎

code_15

Here is the auto generated QR code. All made in CircuitPython!‎

code_16

You can press the B button to switch to the other URLs which will ‎change the beacon advertisements almost instantly.‎

NOTE: Depending on the beacon app you're running, you may need ‎to refresh or delete the beacon cache in order to see the changed ‎advertisement URL show up.‎

switch_17

制造商零件编号 4481
ITSYBITSY NRF52840 EXPRESS BLE
Adafruit Industries LLC
¥162.39
Details
制造商零件编号 4500
CLUE NRF52840 EXPRESS
Adafruit Industries LLC
¥365.88
Details
制造商零件编号 4062
ADAFRUIT FEATHER NRF52840 EXPRES
Adafruit Industries LLC
¥203.09
Details
制造商零件编号 4333
CIRCUIT PLAYGROUND BLUEFRUIT BLE
Adafruit Industries LLC
¥203.09
Details
制造商零件编号 592
CABLE A PLUG TO MCR B PLUG 3'
Adafruit Industries LLC
¥24.01
Details
制造商零件编号 1304
MICRO USB LIION/LIPOLY CHARGER
Adafruit Industries LLC
¥48.43
Details
制造商零件编号 2124
PRO TRINKET LI-BATTERY BACKPACK
Adafruit Industries LLC
¥40.29
Details
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum