GitHub Copilot で iBeacon 発信ツールを作れる?

要は「GitHub Copilot x Python」本の宣伝記事です。自前のブログ記事だから自前の本の宣伝をしてもよいのですが、内容を紹介しているだけでは “私が” 詰らないので、もうちょっと先のところを書きます。

この書籍ではPythonを学ぶことになっていますが、実はGitHub Copilotを学ぶのが目的です。GitHub Copilotはコーディング時に適した生成AIで Visual Studio Code と Copilot を組み合わせて使います。昨今では、プログラムのコーディング時に生成AIを使う(ChatGPTとかGeminiとか)を使うことは普通になってきましたが、もう一歩進んでコーディングに適した使い方を学んでいこうというのが主旨です。なので、実はツールは問いません。この本ではGitHub Copilotを使っていますが Cursor とか他のツールでも構いません。

生成AIを伴ってコーディングをする

ChatGPTなどのツールをコーディングに使おうとしたとき、真っ先に考えるのは「○○のツールを作って」というプロンプトを使ったコード生成の仕方です。実際、簡単なゲーム(インベーダーとかブロック崩しとか)のコードは、これだけのプロンプトでも簡単に作ることができます。色を付けたり、音が付いたりするので、かなりの驚きを覚えるのですが、まあ、実際のところインベーダーゲームを作ったりブロック崩しを作ったりしたいわけではないので、その後が続きません。

最初に断っておくと、おそらくですが「ゲーム」のように閉じられた環境の場合には、このプロンプトの方式はかなり有効です。おそらく、同じパターンでロールプレイングゲームとかか未芝居型のギャルゲーとかもいけそうです。小学生向けのさんすうゲームとかもいけそうです。おそらく、小奇麗なショッピングさいととも可能かもしれません。

これはいわゆる巷にあるテンプレート的なものに対して生成AIのコーディング力が非常に強いためです。ある意味、プログラミングというものが既存のライブラリの組み合わせや、Webのユーザーインターフェース(HTMLとCSSの組み合わせとかアニメーションとか)のサンプルコードがインターネットにたくさん紹介されていて、それらのノウハウの蓄積が早いからでしょう。

ただし、この手の目的にぴったりのモノが出てくる場合はいいのですが、ぴったりのものが出て来ない場合はどうすればいいでしょうか?ひたすら生成AIに渡すプロンプトを工夫してコードを練り上げていく方がよいのでしょうか?

ひとつの方法として「何度もプロンプトを繰り返す」というのもあるのですが、もう少しプログラマらしい手法を取ってみましょう。というのが主旨です。なにも、全てを生成AIに任せるのではなく、生成AIに足りない部分は人が補い、逆に人に足りない部分(知識とか時間とか)を生成AIで補わせるという方法です。端的に言えば、生成AIとペアプロをするという方法です。

iBeacon を発信させる

GitHub Copilot の細かい使い方やPythonの文法などは先の本に任せるとして、ここでは先に進んでみましょう。

注)最初に書いておきますが、Windows 環境で Python を使って iBeacon を送信することはできません。そうそう、.NETでもできなかったんですよね。WSLを使うとできるようなできないような感じでしたが…ひとまず、それを忘れて「Windows で Python を使って iBeacon を送信していみよう!」と思って、作り始めた記録です。

仮想環境 .venv を作って vscode に transmit-ibeacon.py というファイルを開きます。

まずは、初手として Copilot にプロンプトを入れます。色々設計してもよいのですが、作りたいツールを直接入れてしまうのが初手としては手っ取り早いです。

「iBeacon を発信するコードを書いて。」

中味は GPT 4o なので結構コードが生成されます。

これを「同意する」を押して確定させます。

import time
import bluetooth

# iBeacon settings
uuid = "12345678-1234-1234-1234-123456789abc"
major = 1
minor = 1
tx_power = -59

# Create the iBeacon advertisement packet
def create_ibeacon_packet(uuid, major, minor, tx_power):
    prefix = b'\x02\x01\x1a\x1a\xff\x4c\x00\x02\x15'
    uuid_bytes = bytes.fromhex(uuid.replace('-', ''))
    major_bytes = major.to_bytes(2, byteorder='big')
    minor_bytes = minor.to_bytes(2, byteorder='big')
    tx_power_byte = tx_power.to_bytes(1, byteorder='big', signed=True)
    return prefix + uuid_bytes + major_bytes + minor_bytes + tx_power_byte

# Transmit iBeacon
def transmit_ibeacon():
    ibeacon_packet = create_ibeacon_packet(uuid, major, minor, tx_power)
    sock = bluetooth.BluetoothSocket(bluetooth.LE_ADVERTISING)
    sock.bind(("", 0))
    sock.send(ibeacon_packet)
    print("iBeacon is transmitting...")

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        sock.close()
        print("iBeacon transmission stopped.")

if __name__ == "__main__":
    transmit_ibeacon()

実は、このプログラムを実行すると bluetooth ライブラリが無くてこけます。

どうやら、先頭の「import bluetooth」がエラーになっているので、インポートするライブラリが足りないようです。

ここで「Copilot を使用して修正する」を選択します。書籍のほうでは、いろいろと試行錯誤をしていますが、ここではちゃっちゃと聞いてしまいましょう。だいたい、pip コマンドでライブラリを入れれば解決できる話なので。

予想通り、pybluez と言うライブラリを入れればよさそうですね。

pip install pybluez

これを仮想環境でインストールしましょう。

実は bybluez は Windows 環境にはインストールできません。Linux 用の Bluetooth ライブラリなので、Windows 環境では動かないのです。

おそらく Linux 環境で動かせば Copilot の提案通りに動く筈なのですが、いまは Windows 環境で動作させたいので、これは使えません。さてどうしたものか。

プロンプトで Copilot に頼んでみましょう。

Windows 環境で動くように書き換えて。

これはうまくいきませんでした。なにか変な import を追加しますが本筋ではありません。

仕方がなので、チャットを開いて pip で出て来たエラーを貼り付けて問い合わせをします。

(.venv) PS H:\BLE\py> pip install pybluez
Collecting pybluez
  Using cached PyBluez-0.23.tar.gz (97 kB)
  Preparing metadata (setup.py) ... error
  error: subprocess-exited-with-error

  × python setup.py egg_info did not run successfully.
  │ exit code: 1
  ╰─> [1 lines of output]
      error in PyBluez setup command: use_2to3 is invalid.
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.
(.venv) PS H:\BLE\py>

チャットの様子

実は、このエラーを Copilot のチャットや ChatGPT のプロンプトに貼り付けて問い合わせをするのはよくやります。コンパイル時の長いエラーや、ややこしいエラーメッセージなどを生成 AI が解析してくれるので便利です。

この回答では、setuptools や wheel の更新を求めていますが、実は駄目です。最後の「pip install bleak」というところで、bleakというライブラリを使うことになります。

最初から書き直して貰ってもいいのですが、bluetooth のところを直せば良さそうなので、この部分で「bleakで書き換えて」とプロンプトに入れてみます。

そうすると、Bleak ライブラリを使った形で書き換えてくれます。

出来上がったコードです。

import asyncio
from bleak import BleakScanner, BleakAdvertiser

# iBeacon settings
uuid = "12345678-1234-1234-1234-123456789abc"
major = 1
minor = 1
tx_power = -59

# Create the iBeacon advertisement packet
def create_ibeacon_packet(uuid, major, minor, tx_power):
    prefix = b'\x02\x01\x1a\x1a\xff\x4c\x00\x02\x15'
    uuid_bytes = bytes.fromhex(uuid.replace('-', ''))
    major_bytes = major.to_bytes(2, byteorder='big')
    minor_bytes = minor.to_bytes(2, byteorder='big')
    tx_power_byte = tx_power.to_bytes(1, byteorder='big', signed=True)
    return prefix + uuid_bytes + major_bytes + minor_bytes + tx_power_byte

# Transmit iBeacon using Bleak
async def transmit_ibeacon():
    ibeacon_packet = create_ibeacon_packet(uuid, major, minor, tx_power)
    advertiser = BleakAdvertiser()
    await advertiser.start(ibeacon_packet)
    print("iBeacon is transmitting...")

    try:
        while True:
            await asyncio.sleep(1)
    except KeyboardInterrupt:
        await advertiser.stop()
        print("iBeacon transmission stopped.")


if __name__ == "__main__":
    transmit_ibeacon()

さて、これを動かし見ると実はエラーで動きません。

これを Copilot に聞くと BleakAdvertiser 自体が無いことがわかります。つまりは Copilot のハルシネーションですね。勝手に作った(あるいは、どこかのサンプルコード?)になります。

あらためて、Copiot に尋ねてみましょう。

「Windows 環境で Python を使って iBeacon を発信するには?」

提案されたコード

import pygatt
import time

# iBeacon settings
uuid = "12345678-1234-1234-1234-123456789abc"
major = 1
minor = 1
tx_power = -59

# Create the iBeacon advertisement packet
def create_ibeacon_packet(uuid, major, minor, tx_power):
    prefix = b'\x02\x01\x1a\x1a\xff\x4c\x00\x02\x15'
    uuid_bytes = bytes.fromhex(uuid.replace('-', ''))
    major_bytes = major.to_bytes(2, byteorder='big')
    minor_bytes = minor.to_bytes(2, byteorder='big')
    tx_power_byte = tx_power.to_bytes(1, byteorder='big', signed=True)
    return prefix + uuid_bytes + major_bytes + minor_bytes + tx_power_byte

# Transmit iBeacon using pygatt
def transmit_ibeacon():
    adapter = pygatt.GATTToolBackend()
    adapter.start()
    try:
        ibeacon_packet = create_ibeacon_packet(uuid, major, minor, tx_power)
        adapter.sendline('sudo hcitool -i hci0 cmd 0x08 0x0008 ' + ibeacon_packet.hex())
        print("iBeacon is transmitting...")
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print("iBeacon transmission stopped.")
    finally:
        adapter.stop()

if __name__ == "__main__":
    transmit_ibeacon()

実行してみると、実はこれも動きません。

提案されている pygatt は Linux の BlueZ を使っているので、Windows では動かないんですよね。

結論的には Windows 環境から iBeacon を発信することはできないので、別の環境が必要です。Linux で hcitool を使うか、Android とかを使うか。

ちなみに M5Stack を使った例を示しておきます。

M5Stack で iBeacon を飛ばす(PlatformIO環境) | Moonmile Solutions Blog https://www.moonmile.net/blog/archives/11081

なので、これだとアレだから、Android で iBeacon を作る例を続きへ。

カテゴリー: 開発 パーマリンク