【雑誌付録】F503iをLabVIEWで制御してみた | マーブルルール

【雑誌付録】F503iをLabVIEWで制御してみた

その他

スポンサーリンク

この記事では、F503iをLabVIEWで制御する方法を紹介しています。

F503iとは昔のNTTドコモ携帯端末ですが、小学館が出している雑誌、小学8年生の4月号付録にて、「ケータイ型プログラミング体験付録」としてF503iを模した機器が付属していました。

これは、「専用アプリ」(LabVIEWでない)を使用することで、子供でも比較的取り扱いやすいプログラミング環境にてプログラムを書くことができ動かせる代物です。

・・・だったらLabVIEWでも制御できるんじゃない?ということで試してみました。

なお、はじめに断っておきますが、私は小学館の回し者ではありません。

この雑誌自体は、この記事が出ている時点で相当手に入りにくく、フリマアプリなどでしか手に入れられないと思います(私もフリマアプリにて入手しました・・・)。

なので、この記事で紹介していることをそのまま再現しようとしてもそもそも環境を用意できない、という方が大多数だと思います。

ただ、LabVIEWでこんなこともできるんだ、というアプリケーションの一例として見て頂ければと思います。

ちなみに、どうしても入手したいという方は、定価の〇倍を覚悟してフリマアプリで入手するか、2025年4月18日までなら抽選でこの付録を入手できるチャンスがあるようです。

詳しくは以下の記事を見てください。(記事内容の応募については完全自己判断でお願いいたします)

comotto| comotto×小学8年生 特設ページ - NTTドコモ
小学8年生とのコラボ企画!comottoメルマガを登録して、着メロやひみつのコードを手に入れよう!

なお、同じページに、スマホ用に作れる着メロクリエイターもあります。

これだけでも楽しめそうですね。

スポンサーリンク

付録はBluetooth接続で通信。ところが

雑誌付録でついてくる、F503iの詳細な説明は本記事の趣旨ではないので省きますが、機器本体には「ガラケー」としての数字ボタンおよび「*」と「#」のボタンがあり、また、光センサーがついています。

さらに、3色のLEDが搭載され、ブザーを鳴らすこともできます。

私もガラケーを使っていた世代なので、とても懐かしい気持ちになる一品です。

さて、LabVIEWで制御するにあたって、そもそも必要なことは、PCで対象のF503iを認識させることです。

専用アプリは、スマホやタブレットを使用してそこからF503iとつなげて使用する、という使い方ができ、雑誌にも接続方法はBluetoothでできると書いてあったので、Bluetooth接続ができるのであればLabVIEWからBluetooth通信用の関数を使用して制御できるものと思われます。

ところが・・・。

結果的には、何をどうやってもPCからBluetooth接続させることができませんでした。

Windows PCで、近くのBluetooth接続機器を探し、検出すること自体はできるのですが、接続がどうやってもうまくいきませんでした。

ということで、LabVIEWで通信することはできませんでした。

ここまで読んでいただきありがとうございました。

・・・では記事が終わってしまうので何とか粘った結果、ググっていくと、WebembotというドライバAPIを使用すればブラウザ経由で制御できる方法がありました。

これにはGitHubで公開されているコードがあります。

GitHub - code4fukui/Webembot
Contribute to code4fukui/Webembot development by creating an account on GitHub.

このアプローチで、なんとか頑張って最終的にLabVIEWから制御できるようになるとは思うのですが、ブラウザも必要になるなど少し面倒なことになるので、ここから少し環境を変えて、PythonでAPIを実行するようにします。

必要な準備

では、以降で紹介するプログラムを動かすのに必要なソフトウェアを挙げると

  • LabVIEW
  • Python

この二つです。

ただし、LabVIEWからPythonを呼び出すような作りにするため、使用するLabVIEWのバージョンと、このバージョンで呼び出せるPythonのバージョンに気を付ける必要があります。

使用しているLabVIEWでどのPythonバージョンが使用できるかは、Pythonノードのヘルプの記載やあるいはLabVIEWのリリースノートの情報を見てください。

また、ビット数もそろえる必要があります。

例えばPython 3.9 64 bitを使用する場合には、LabVIEWも(Python 3.9が扱えるバージョンの)64 bit版を使用する必要があります。

さらに、Python側ではbleakライブラリを使用するため、これをインストールする必要があります。

bleakライブラリは、BLE (Bluetooth Low Energy)通信のために必要、と思ってください。

これらを用意出来たらソフトウェア側としては他に必要ありません(Pythonのスクリプトを記述する、開発環境は適宜用意してください)。

どのようなことができるか

では、実際にどのような制御ができるかについて結果例を以下に紹介します。

そもそもこのF503iについては、冒頭で紹介したように

  • 押されたキーを検出する
  • 光センサーの値を取得する
  • LEDを光らせる
  • ブザーを鳴らす

の主に4つの機能があります。

そしてこれらすべての機能をLabVIEWで行っていきます。

作成したLabVIEWプログラムの一例が以下の図のものです。

フロントパネル上にはそれぞれの機能を一通り試せる画面があります。

(LEDやブザーについては実機F503iが実際に指定したとおりに反応します)

実行して、F503iから値を受け取ったり、LEDやブザーに対する制御値を送ることができます。

また、応用すればLEDを好きな順番で自由に光らせたり、ブザーを使用して音楽を流すこともできます。

プログラムの構造

では、プログラムの構造を紹介します。

まずは、Pythonスクリプトを用意します。

機能使用についてはPythonを介するので、LabVIEWからPythonの各関数を呼び出せるように、各機能を関数として定義しています。

Pythonを普段から扱っている方から見ると、「なんでこんなことしているの?」というツッコミどころは多いかもしれませんがご容赦ください。

import asyncio
from bleak import BleakClient, BleakScanner

SERVICE_UUID = "f7fce510-7a0b-4b89-a675-a79137223e2c"
CHAR_UUID_KEY = "f7fce531-7a0b-4b89-a675-a79137223e2c"
CHAR_UUID_LIGHT = "f7fce532-7a0b-4b89-a675-a79137223e2c"
CHAR_UUID_LED = "f7fce533-7a0b-4b89-a675-a79137223e2c"
CHAR_UUID_BUZZER = "f7fce521-7a0b-4b89-a675-a79137223e2c"

KEYS = "0123456789*#"

CHAR_UUID_LED = {
    0: "f7fce517-7a0b-4b89-a675-a79137223e2c",  # 緑
    1: "f7fce518-7a0b-4b89-a675-a79137223e2c",  # 黄
    2: "f7fce51b-7a0b-4b89-a675-a79137223e2c",  # 赤
}

client = None
latest_key = None

def parse_keys(data_bytes):
    if len(data_bytes) != 2:
        return "Invalid"
    value = int.from_bytes(data_bytes, byteorder='little')
    for i in range(12):
        if not (value & (1 << i)):
            return KEYS[i]  # Return first detected key
    return None

def notification_handler(sender, data):
    global latest_key
    key = parse_keys(data)
    if key:
        latest_key = key

def get_event_loop():
    try:
        return asyncio.get_event_loop()
    except RuntimeError:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        return loop

async def connect_f503i():
    global client
    devices = await BleakScanner.discover(timeout=5)
    for device in devices:
        if device.name and device.name.startswith("F503i"):
            client = BleakClient(device.address)
            await client.connect()
            await client.start_notify(CHAR_UUID_KEY, notification_handler)
            return "successfully connected"
    return "connection fail"

def connect():
    loop = get_event_loop()
    try:
        return loop.run_until_complete(connect_f503i())
    except Exception as e:
        return f"ERROR: {str(e)}"

def read_key(timeout=2):
    global latest_key

    async def wait_for_key():
        global latest_key
        for _ in range(int(timeout * 10)):
            if latest_key:
                key = latest_key
                latest_key = None
                return key
            await asyncio.sleep(0.1)
        return "none"

    loop = get_event_loop()
    return loop.run_until_complete(wait_for_key())

def is_connected():
    loop = get_event_loop()

    async def check():
        if client:
            return bool(await client.is_connected())
        return False

    return loop.run_until_complete(check())

def disconnect():
    loop = get_event_loop()

    async def do_disconnect():
        global client
        if client and await client.is_connected():
            await client.disconnect()
            return "disconnected"
        return "not connected"

    return loop.run_until_complete(do_disconnect())

def read_light():
    async def do_read():
        if client and await client.is_connected():
            try:
                data = await client.read_gatt_char(CHAR_UUID_LIGHT)
                raw = int.from_bytes(data, byteorder="little")
            except Exception as e:
                print(f"Error in read_light: {e}")
                return "-3"
            return(str(raw))                      
    loop = get_event_loop()
    return loop.run_until_complete(do_read())

def set_led(led_type, brightness):
    async def do_write():
        if led_type not in range(len(CHAR_UUID_LED)):
            return "invalid led_type"
        if not (0 <= brightness <= 255):
            return "invalid brightness"

        if client and await client.is_connected():
            uuid = CHAR_UUID_LED[led_type]
            data = bytes([brightness & 0xFF])
            await client.write_gatt_char(uuid, data)
            return "led set"
        ###return "not connected"

    loop = get_event_loop()
    return loop.run_until_complete(do_write())

def set_buzzer(level):
    async def do_write():
        if client and await client.is_connected():
            data = int(level)
            await client.write_gatt_char(CHAR_UUID_BUZZER, bytes([data]))
            return "buzzer set"
        return "not connected"

    loop = get_event_loop()
    return loop.run_until_complete(do_write())

このスクリプトを用意したうえで、LabVIEWプログラムを作っていきます。

今回は、各処理を行うにあたり、それぞれの処理をサブVIとして実装しました。

こうすることで、特定の機能を使用するときに対応するサブVIを置くだけでその機能を使用できるようになるためです。

まずはOpen Python Session.viです。

LabVIEWからPythonスクリプトを呼び出すのに必要な関数を使用してsessionリファレンスを取得しています。

以下の図ではPythonバージョンとして3.9を指定していますが、これは実際に使用するバージョンに適宜変更します。

次にGet Script Path.viです。

上で紹介した、呼び出すPythonスクリプトファイルのパスを取得する関数で、以降で紹介するサブVIの中で使用していきます。

パスはどこでも構いませんし、Pythonスクリプトの名前も以下の図の通りにする必要はありません。

ただ、基本的にスクリプトファイル名およびパスは日本語が入っていない方が余計なトラブルが起こりにくいです。

次にCheck Connection.viです。

対象機器であるF503iをBLEモードにしてからLabVIEWを実行するわけですが、まずこの関数によって、PC側から見てF503iが接続可能かどうかを判定します。

接続可能であるかどうかは文字列として値を受け取り、その文字列がsuccessfully connectedなら接続成功、それ以外は接続失敗、と判断できます。

この関数の出力文字列を、実際のメインプログラムの条件分岐部分で使用していきます。

次にRead Key Info.viです。

押されたキー(0から9あるいは「*」や「#」)がどれかを検出し文字列として受け取ることができます。

もしPythonスクリプト側で設定したタイムアウト(上に貼ったスクリプトだとtimeout=2秒)を過ぎるまでキーが何も押されないと、noneという文字列が返ってきます。

次にRead LightSensor Info.viです。

F503i本体搭載の光センサーの値を取得します。

暗いほど値が小さく、明るいほど値が大きくなり、明るさの程度をそれなりに識別できます。

次にSet LED Status.viです。

LEDは3色あり、それぞれで明るさの強弱を選べるので、それらを指定することができるサブVIとしています。

次にSet Buzzer.viです。

0から100までの数値を入力してブザーの音を変えることができるようなサブVIとしています。

最後はClose Python Session.viです。

開いたPythonセッションを閉じるのに使用します。

これらのサブVIを元に、実際に各機能の使い勝手を確かめるプログラムを作ってみました。

LabVIEWプログラムを実行する前にF503i本体のスイッチをBLEにセットし、LabVIEWを実行します。

無事接続が完了してからは、各機能に対するボタンを押して、F50eiで押されたキーがどれかを取得したり光センサーの値を取得したり、LEDの種類と強さを指定して光らせたりブザーの音を変えることができます。

このプログラムのブロックダイアグラムは以下のような作りとしました。

最初にPythonセッションを開いて、接続確認をCheck Connection.viで行い、その結果接続に成功したら、イベントストラクチャが入ったWhileループを実行させていきます。

接続する前に各ボタンが押されているのは順番上よくないので、接続が無事に成功するまでは各機能に対するボタンをグレーアウトして押せなくしています。

イベントストラクチャの中身は以下のように既に紹介したサブVIがそれぞれ入っているような簡単な作りです。

このように各機能をそれぞれのボタンを押してテストできたら、あとはサブVIを自由に組み合わせてもう少し複雑なことをしていきます。

応用例

では、上記の動作ができることが確かめられたうえで応用例を考えます。

まずは、特定のLEDの色を段々と変化させるプログラムです。

ループの反復端子を使用すれば簡単に実装できます。

次に、ハードウェア制御のプログラムも紹介します。

上の説明だけだと「これ結局Pythonでよくね?」と思われるかもしれないので、ハードウェア制御を得意とするLabVIEWらしさを踏まえた例です。

まずは、指定したLEDを徐々に明るさを変えていくプログラムです。

何のことはなく、Set LED Status.viを使用するだけなのですが、入力するLED Levelについては、0から上限である255を順番に指定していくためにループの反復端子を255で割って「商&余り」の関数の「余り」の値を使用しています。

次に、ハードウェア制御の例を一つ。

今回はNI社のDAQハードウェアの、アナログ出力を行うプログラムをF503iで制御するという例を考えてみました。

プログラム実行時に、F503i上の数字ボタンあるいは「*」を押して小数点をつけて出力値を指定し、「#」を押すことでその指令値を反映させて実際に出力させる、ということをしていきます。

ブロックダイアグラムは以下の通りで、F503iでどのキーが押されたかは常に監視する必要があるため上のWhileループでそれを行い、検出された「どのキーが押されたか」の情報によって、下のWhileループに値を渡したり、出力値を構成する、などといった処理を行います。

上のWhileループの中身は以下のようにしており、ずっと入力がない場合にnoneが検出されたらそれまで入力していた値をリセットさせています(そうでないと値を入力し間違えた時にその入力をやり直す処理をF503iだけで実装することができないため)。

「#」以外のキーが押されると文字列に追加していきますが、「*」が押されると小数点を追加するようにします。

これで、出力値としては整数値だけでなく小数値も可能になります。

「#」が押されたら、文字列を小数値に変換し、その後にハードウェアの仕様上の上限下限のリミットを越えていないかを確認してからキューを経由して下のWhileループにあるDAQmx書き込み関数に渡し信号を出力します。

もう少し複雑に、例えば「#1」が押されたらモードを変えて、指定した周波数の正弦波を出力する、みたいな凝ったプログラムにすることもできると思いますが、今回はとりあえずのお試しということで上のような簡単なプログラムの実装をしてみました。

本記事では、雑誌の付録のF503iをLabVIEWで制御する方法を紹介しました。

実機として付録のF503iを実際に使って確かめることができる機会は少ないかもしれないですが、LabVIEWでこんなこともできるんだ、ということの参考になればうれしいです。

ここまで読んでいただきありがとうございました。

コメント

タイトルとURLをコピーしました