BLE通信 〜nRF52480編〜

2023/12/19に公開

前回、micripythonのubluetoothを使って、ラズパイpicoWやESP32でBLE通信をする方法を書きました。

電子工作界隈ではこれらのマイコンがメジャーですが、消費電力が大きいという弱点があります。

一方でnRF52480は消費電力がかなり小さく、ボタン電池一つで1年くらい稼働するそうです。

電子工作で使うなら、Seeed StudioのDevelopment Boardsがおすすめ
https://www.mouser.jp/ProductDetail/Seeed-Studio/102010448?qs=Znm5pLBrcAJ5g%252BWAkitg4w%3D%3D
(昔は一千円ちょいだったのですが、円安で相当値上がりしてますね。ファック日銀)

ただ、一個致命的な弱点がありまして、2023年6月時点ではnRF52480(以下nRF)はファームウェアの仕様上microPythonのubluetoothやujsonなどいくつかのmicropythonライブラリは使えません。

そこでcircuitPythonを使って実装していくことになります。
(エディタはMu editorを使っています)

なお、circuitPythonのライブラリですが、pip等を使ってインストールできないので、手動で追加していきます。

ライブラリを追加

以下よりライブラリのzipファイルをダウンロードし、解凍します。
https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases

このうち8.x系を選びます。

解凍すると、たくさんのフォルダとファイルができますが、これら全てをnRFに入れることは容量の都合でできないので、必要なものだけを入れます。

どのファイルを入れるかについては後述します。

コードを書く

こちらにドキュメントやサンプルコードが載っています。
https://github.com/adafruit/Adafruit_CircuitPython_BLE/tree/main/examples

今回は①キャラクタリスティックにデータをJSON形式で格納したかったので、ble_jsonのコードを参考にしました。

スクリーンショット2023-07-0222.41.22.png

以下が今回作成したコードです。

ble_json_service.py

from adafruit_ble.uuid import VendorUUID
from adafruit_ble.services import Service
from adafruit_ble.characteristics import Characteristic
from adafruit_ble.characteristics.json import JSONCharacteristic


# A custom service with two JSON characteristics for this device.  The "sensors" characteristic
# provides updated sensor values for any connected device to read.  The "settings" characteristic
# can be changed by any connected device to update the peripheral's settings.  The UUID of your
# service can be any valid random uuid (some BLE UUID's are reserved).
# NOTE: JSON data is limited by characteristic max_length of 512 byes.
class SensorService(Service):
    # pylint: disable=too-few-public-methods

    uuid = VendorUUID("a72c3f53-ac6f-4367-8d65-c855ee89acee")

    tx = JSONCharacteristic(
        uuid=VendorUUID("93222d1f-2837-4f1d-88d0-e30b6d1935e1"),
        properties=Characteristic.READ,
        initial_value={"humid": 50, "temp": 20},
    )

    rx = JSONCharacteristic(
        uuid=VendorUUID("4836c2f5-001a-4d2b-a67f-a2701b1354e5"),
        properties=Characteristic.WRITE,
    )

    def __init__(self, service=None):
        super().__init__(service=service)
        self.connectable = True

code.py

# SPDX-FileCopyrightText: 2020 Mark Raleson
#
# SPDX-License-Identifier: MIT

# Provide readable sensor values and writable settings to connected devices via JSON characteristic.
import alarm
import board
import time
import random
from ble_json_service import SensorService
from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement


# Create BLE radio, custom service, and advertisement.
ble = BLERadio()
service = SensorService()
advertisement = ProvideServicesAdvertisement(service)


# Function to get some fake weather sensor readings for this example in the desired unit.
def measure(unit):
    temperature = random.uniform(0.0, 10.0)
    humidity = random.uniform(0.0, 100.0)
    if unit == "fahrenheit":
        temperature = (temperature * 9.0 / 5.0) + 32.0
    return {"temp": temperature, "humid": humidity}


# Advertise until another device connects, when a device connects, provide sensor data.
while True:
    print("Advertise services")
    ble.stop_advertising()  # you need to do this to stop any persistent old advertisement
    ble.start_advertising(advertisement)

    print("Waiting for connection...")
    while not ble.connected:
        pass

    print("Connected")
    while ble.connected:
        rx = service.rx
        if rx is None:
            continue
        else:
            instruction, val = rx  # ('sleep',1000)のようなタプル型
            if instruction == "sleep":
                print("good night!")
                time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + val)
                pin_alarm = alarm.pin.PinAlarm(board.D7,value=False)
                alarm.exit_and_deep_sleep_until_alarms(time_alarm, pin_alarm)
            elif instruction == "measure":
                service.tx = measure({"unit": "celsius"})
                print(service.tx)
            else:
                pass

        time.sleep(1)

    print("Disconnected")

ubluetoothで存在したイベント発生時の割り込み関数(BLE.irq)はAdaftuitのcircuitPythonライブラリにはありませんので、whileループを使ってセントラル側からの指示を受け取ったり、指示に対して測定などのアクションをしています。

ディープスリープがmachineライブラリではなくalarmライブラリにあるなど、かなり書き方が違うので慣れるまで大変かもしれません。

ライブラリの追加

ble_json_service.pyのimport部分より、必要なライブラリは「adafruit_ble」だとわかります。

先ほどダウンロードしたライブラリファイル一覧から、adafruit_bleをフォルダごとnRFのlibフォルダに放り込みましょう

スクリーンショット2023-07-0222.49.50.png

これでcircuitPythonを実行すれば、セントラル側とデータをやり取りできるはずです。

Discussion