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()


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)

    # ref https://github.com/raspberrypi/pico-micropython-examples/blob/master/adc/temperature.py
    def _get_temp(self):
        conversion_factor = 3.3 / (65535)
        reading = self._sensor_temp.read_u16() * conversion_factor
        
        # The temperature sensor measures the Vbe voltage of a biased bipolar diode, connected to the fifth ADC channel
        # Typically, Vbe = 0.706V at 27 degrees C, with a slope of -1.721mV (0.001721) per degree. 
        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.