4999

Use Bluetooth feature of raspberry pi pico w

Introduction
In this tutorial I am going to tell you an important thing about raspberry pi pico w and this is that now Raspberry pi pico w supports Bluetooth functionality. A good thing is that to use the bluetooth on your raspberry pi pico w you do't need to purchase a new one. To use the bluetooth on your older raspberry pi pico w you need to update the firmware of your raspberry pi pico w. You need to download the latest version of micropython firmware for raspberry pi pico w and install it on your pico w board. After updating the the firmware of raspberry pi pico w, You will be able to use bluetooth on your pico w.

Here in this tutorial I am going to write a simple micropython program to use the bluetooth of my raspberry pi pico w board. I have written a program to stream the temperature reading of raspberry pi pico w on an android phone via bluetooth low energy.

Theory
Updating the Firmware of Raspberry pi pico w

You can download the latest version of the micropython firmware for your pico w board form the link given below.

https://micropython.org/download/rp2-pico-w/

After updating the firmware reconnect your pico w board to computer and now type the following line in the shell of Thonny IDE.

>>> help('modules')
Now press enter key. This line prints the list of all modules installed on your pico w board.

>>> help('modules')
__main__          aioble/server     json              ssl
_boot             array             lwip              struct
_boot_fat         binascii          machine           sys
_onewire          bluetooth         math              time
_rp2              builtins          micropython       uasyncio/__init__
_thread           cmath             mip/__init__      uasyncio/core
_uasyncio         collections       neopixel          uasyncio/event
_webrepl          cryptolib         network           uasyncio/funcs
aioble/__init__   dht               ntptime           uasyncio/lock
aioble/central    ds18x20           onewire           uasyncio/stream
aioble/client     errno             os                uctypes
aioble/core       framebuf          random            urequests
aioble/device     gc                re                webrepl
aioble/l2cap      hashlib           rp2               webrepl_setup
aioble/peripheral heapq             select            websocket
aioble/security   io                socket            zlib
Plus any modules on the filesystem
>>>
In the above list you can see that there is a bluetooth module on your w. This indicates that now your pico w board has bluetooth feature.

Installing the nrf connect android app on your smart phone

You need to install an android application(nrf connect) on your smart phone to stream the temperature readings. You can download it from google play store the link has been given below.

https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=en&gl=US&pli=1


About the App

nRF Connect for Mobile is a powerful generic tool that allows you to scan, advertise and explore your Bluetooth Low Energy (BLE) devices and communicate with them. nRF Connect supports number of Bluetooth SIG adopted profiles together with Device Firmware Update profile (DFU) from Nordic Semiconductors and Mcu Manager on Zephyr and Mynewt.Features:- Scans for Bluetooth Low Energy (BLE) devices- Parses advertisement data- Shows RSSI graph, allows export to CSV and Excel formats- Connects to a connectible the Bluetooth LE device- Discoveries and parses services and characteristics- Allows to read and write characteristics- Allows to enable and disable notifications and indications- Supports Reliable Write- Parses number of characteristics adopted by Bluetooth SIG- Bluetooth LE advertising (Android 5+ required)- Read and update PHY (Android 8+ required)- GATT Server configuration- Supports Device Firmware Update (DFU) profile which let the user to upload a new application, SoftDevice or a bootloader over-the-air (OTA)- Supports McuMgr, profile that lets the user control and update Zephyr-based devices- Supports the Nordic UART Service- Allow to record and replay common operations using Macros- Allows to perform automated tests defined in XML file on Bluetooth LE devices.Visit the GitHub page: https://github.com/NordicSemiconductor/Android-nRF-Connect for more information about automated tests.Note:- Supported on Android version 4.3 or later.- nRF5x Development kits can be ordered from http://www.nordicsemi.com/eng/Buy-Online.Works well with nRF Logger application, that will store your logs in case something bad happens with nRF Connect.Download nRF Logger from: https://play.google.com/store/apps/details?id=no.nordicsemi.android.log

Writing the micropython Program

Copy the given program and save it on your raspberry pi pico w with name as ble_advertising.py.

# Helpers for generating BLE advertising payloads
from micropython import const
import struct
import bluetooth

# Advertising payloads are repeated packets of the following form:
# 1 byte data length (N + 1)
# 1 byte type (see constants below)
# N bytes type-specific data

_ADV_TYPE_FLAGS = const(0x01)
_ADV_TYPE_NAME = const(0x09)
_ADV_TYPE_UUID16_COMPLETE = const(0x3)
_ADV_TYPE_UUID32_COMPLETE = const(0x5)
_ADV_TYPE_UUID128_COMPLETE = const(0x7)
_ADV_TYPE_UUID16_MORE = const(0x2)
_ADV_TYPE_UUID32_MORE = const(0x4)
_ADV_TYPE_UUID128_MORE = const(0x6)
_ADV_TYPE_APPEARANCE = const(0x19)


# Generate a payload to be passed to gap_advertise(adv_data=...).
def advertising_payload(limited_disc=False, br_edr=False, name=None, services=None,appearance=0):
    payload = bytearray()
    def _append(adv_type, value):
        nonlocal payload
        payload += struct.pack("BB", len(value) + 1, adv_type) + value

    _append(_ADV_TYPE_FLAGS,struct.pack("B", (0x01 if limited_disc else 0x02) + (0x18 if br_edr else 0x04)),)
    if name:
        _append(_ADV_TYPE_NAME, name)
    if services:
        for uuid in services:
            b = bytes(uuid)
            if len(b) == 2:
                _append(_ADV_TYPE_UUID16_COMPLETE, b)
            elif len(b) == 4:
                _append(_ADV_TYPE_UUID32_COMPLETE, b)
            elif len(b) == 16:
                _append(_ADV_TYPE_UUID128_COMPLETE, b)

# See org.bluetooth.characteristic.gap.appearance.xml
    if appearance:
        _append(_ADV_TYPE_APPEARANCE, struct.pack("<h", appearance))
    return payload

def decode_field(payload, adv_type):
    i = 0
    result = []
    while i + 1 < len(payload):
        if payload[i + 1] == adv_type:
            result.append(payload[i + 2 : i + payload[i] + 1])
        i += 1 + payload[i]
    return result


def decode_name(payload):
    n = decode_field(payload, _ADV_TYPE_NAME)
    return str(n[0], "utf-8") if n else ""

def decode_services(payload):
    services = []
    for u in decode_field(payload, _ADV_TYPE_UUID16_COMPLETE):
        services.append(bluetooth.UUID(struct.unpack("<h", u)[0]))
    for u in decode_field(payload, _ADV_TYPE_UUID32_COMPLETE):
        services.append(bluetooth.UUID(struct.unpack("<d", u)[0]))
    for u in decode_field(payload, _ADV_TYPE_UUID128_COMPLETE):
        services.append(bluetooth.UUID(u))
    return services
def demo():
    payload = advertising_payload(name="micropython",
services=[bluetooth.UUID(0x181A), bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")],)
    print(payload)
    print(decode_name(payload))
    print(decode_services(payload))


if __name__ == "__main__":
    demo()

Now write the main.py program file. The code is given below.

import bluetooth
import random
import struct
import time
import machine
import ubinascii
from ble_advertising import advertising_payload
from micropython import const
from machine import Pin

_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_INDICATE_DONE = const(20)

_FLAG_READ = const(0x0002)
_FLAG_NOTIFY = const(0x0010)
_FLAG_INDICATE = const(0x0020)

# org.bluetooth.service.environmental_sensing
_ENV_SENSE_UUID = bluetooth.UUID(0x181A)

# org.bluetooth.characteristic.temperature
_TEMP_CHAR = (bluetooth.UUID(0x2A6E), _FLAG_READ | _FLAG_NOTIFY | _FLAG_INDICATE,)
_ENV_SENSE_SERVICE = (_ENV_SENSE_UUID,(_TEMP_CHAR,),)

# org.bluetooth.characteristic.gap.appearance.xml
_ADV_APPEARANCE_GENERIC_THERMOMETER = const(768)

class BLETemperature:
    def __init__(self, ble, name=""):
        self._sensor_temp = machine.ADC(4)
        self._ble = ble
        self._ble.active(True)
        self._ble.irq(self._irq)
        ((self._handle,),) = self._ble.gatts_register_services((_ENV_SENSE_SERVICE,))
        self._connections = set()
        if len(name) == 0:
            name = 'Pico %s' % ubinascii.hexlify(self._ble.config('mac')[1],':').decode().upper()
        print('Sensor name %s' % name)
        self._payload = advertising_payload(name=name, services=[_ENV_SENSE_UUID])
        self._advertise()

    def _irq(self, event, data):
        # Track connections so we can send notifications.
        if event == _IRQ_CENTRAL_CONNECT:
            conn_handle, _, _ = data
            self._connections.add(conn_handle)
        elif event == _IRQ_CENTRAL_DISCONNECT:
            conn_handle, _, _ = data
            self._connections.remove(conn_handle)
            # Start advertising again to allow a new connection.
            self._advertise()
        elif event == _IRQ_GATTS_INDICATE_DONE:
            conn_handle, value_handle, status = data
   
    def update_temperature(self, notify=False, indicate=False):
        # Write the local value, ready for a central to read.
        temp_deg_c = self._get_temp()
        print("write temp %.2f degc" % temp_deg_c);
        self._ble.gatts_write(self._handle, struct.pack("<h", int(temp_deg_c * 100)))
        if notify or indicate:
            for conn_handle in self._connections:
                if notify:
                    # Notify connected centrals.
                    self._ble.gatts_notify(conn_handle, self._handle)
                if indicate:
                    # Indicate connected centrals.
                    self._ble.gatts_indicate(conn_handle, self._handle)

    def _advertise(self, interval_us=500000):
        self._ble.gap_advertise(interval_us, adv_data=self._payload)

    def _get_temp(self):
        conversion_factor = 3.3 / (65535)
        reading = self._sensor_temp.read_u16() * conversion_factor
        return 27 - (reading - 0.706) / 0.001721 

def demo():
    ble = bluetooth.BLE()
    temp = BLETemperature(ble)
    counter = 0
    led = Pin('LED', Pin.OUT)
    while True:
        if counter % 10 == 0:
            temp.update_temperature(notify=True, indicate=False)
        led.toggle()
        time.sleep_ms(1000)
        counter += 1
if __name__ == "__main__":
    demo()
save the mian.py file on your pico w board and click on run option.


Open the nrf connect application on your android phone and tun on the bluetooth of your phone and. Not connect your phone to pico W via bluetooth and now you will be able to see the temperature reading on your smart phone screen.