ネットワーク

デバイスの間で互いにメッセージの送受信を行えます。これをネットワークといいます。相互接続されたネットワークのネットワークはインターネットと呼ばれます。ザ・インターネットはすべてのインターネットのインターネットです。

ネットワーク処理は大変であり、これは以降のプログラムにも表れています。しかし、このプロジェクトの美しいところは、あなたが知る必要があるネットワークプログラミングの共通点がすべて含まれていることです。それはまた非常にシンプルで楽しいものです。

しかし、まず、シーンを設定しましょう …

結線

ネットワークを一連の階層と想像してみてください。一番下の部分は通信の最も基本的な部分です。あるデバイスから別のデバイスに信号を到達するための何らかの方法が必要です。これは無線接続で行われることもありますが、この例では単に2本のワイヤを使用します。

../_images/network.png

この基盤の上に、 ネットワークスタック 内の他のすべての階層を構築できます。

図に示すように、青と赤のマイクロビットはワニ口クリップのリード線で接続します。両方とも出力には端子1を使い、入力の場合は端子2を使います。一方のデバイスからの出力は、他方の入力に接続します。これは、電話の受話器をどう持つのかを知っているようなものです。一方の端にはマイク(入力)があり、他方にはスピーカー(出力)があります。マイクによるあなたの声は、もう一方の人のスピーカーから聞こえます。あなたが受話器を間違って持てば、奇妙な結果を得るでしょう!

この場合もまったく同じです。ワイヤを正しく接続する必要があります。

信号

ネットワークスタック の次の層が信号です。多くの場合、これは接続の特性に依存します。この例では、単に I/O 端子を介してワイヤーで送られるデジタルのオン/オフ信号です。

覚えているでしょうが、I/O 端子は次のように使えます:

pin1.write_digital(1)  # 信号をオンにする
pin1.write_digital(0)  # 信号をオフにする
input = pin2.read_digital()  # 信号の値(1か0)を読む

次のステップでは、信号の使用方法と処理方法を説明します。そのために必要なのは …

プロトコル

あなたがエリザベス女王に会ったことがあるなら、あなたがどう行動すべきかについて期待されることがあります。たとえば、女王が到着したときには身を低くしてお辞儀し、女王が手を丁寧に振ってくれたら、「陛下」と言い、その後「女王様」と言います。この一連の規則はロイヤルプロトコルと呼ばれます。プロトコルは、特定の状況(女王との出会いなど)が与えられたときの行動の仕方を説明します。ある状況が起こる前に何が起こっているのかを誰もが確実に理解できるように、プロトコルがあらかじめ定義されています。

../_images/queen.jpg

この理由から、コンピュータネットワークを介してメッセージを伝達するためのプロトコルを定義して使用します。コンピュータでは、メッセージを送受信する方法を渡す前に同意する必要があります。おそらく、最もよく知られているプロトコルは、ワールドワイドウェブによって使用されるハイパーテキスト転送プロトコル(HTTP)でしょう。

メッセージを送信するためのもう1つの有名なプロトコルは(コンピュータが現れる前のからありますが)モールス符号です。これは、長時間または短時間のオン/オフ信号を介して文字ベースのメッセージを送信する方法を定義します。このような信号は、しばしばビープ音として再生されます。長い信号は長点(-)と呼ばれ、短い信号は短点(.)です。長点と短点を組み合わせることで、モールスは文字を送信する方法を定義しています。たとえば、標準のモールスアルファベットがどのように定義されているかを次に示します。

.-    A     .---  J     ...   S     .----  1      ----.  9
-...  B     -.-   K     -     T     ..---  2      -----  0
-.-.  C     .-..  L     ..-   U     ...--  3
-..   D     --    M     ...-  V     ....-  4
.     E     -.    N     .--   W     .....  5
..-.  F     ---   O     -..-  X     -....  6
--.   G     .--.  P     -.--  Y     --...  7
....  H     --.-  Q     --..  Z     ---..  8
..    I     .-.   R

上図を見ると、文字 「H」 を送信するには、4つの短点(....)で示してあるとおり、短い信号を4回スイッチオンします。文字 「」L」 についても、信号を4回スイッチオンしますが、第2信号はより長い持続時間になります(.-..)

明らかに、信号のタイミングは重要です。短点と長点の方法を取り決めておく必要があります。これはプロトコルのもう一つのポイントです。そうしたことに同意することで、すべてのプロトコルの実装が他のすべての実装と連携できるようになります。この例では、次のように言います。

  • 持続時間が 250 ミリ秒未満の信号は短点とします。
  • 250 ミリ秒から 500 ミリ秒未満の持続時間を有する信号は長点とします。
  • 他の持続時間の信号は無視するものとします。
  • 信号のない時間が 500 ミリ秒を超えると、文字の終わりを示します。

このように、文字 「H」 の送信は、それぞれが250ミリ秒を超えない4つの「オン」信号として定義され、続いて信号を出さない時間を 500 ミリ秒(文字の終りを示す)を超えるまで続けます。

メッセージ

私たちは最終的にメッセージを作り上げる段階に入りました。メッセージは実際に私たち人間にとって何かを意味します。これはネットワークスタックの一番上の層となります

上で定義したプロトコルを使用して、以下の信号シーケンスを物理的なワイヤーを介して他の micro:bit に送ることができます:

...././.-../.-../---/.--/---/.-./.-../-..

あなたはそれが何を言っているのか解明できますか?

アプリケーション

ネットワークスタックを作ることは非常にうまくいっていますが、メッセージを送受信するためのアプリケーションの形式も必要です。HTTP は興味深いですが、 ほとんど の人はそれを知らず、Web ブラウザで処理します。ワールドワイドウェブの基盤となる ネットワークスタック は隠されています(そうあるべきなのです)。

では、BBC micro:bit のためにどのようなアプリケーションを書くべきでしょうか? ユーザーの視点からは、どのように機能すべきでしょうか?

明らかに、メッセージを送信するには、短点と長点を入力できる必要があります(これにはボタン A を使用できます)。送受信したメッセージを見たい場合は、ディスプレイ上にメッセージをスクロールするよう指示できる必要があります(そのためにボタン B を使用できます)。最後に、これはモールス符号であり、スピーカーが接続されている場合は、ユーザーがメッセージを入力している間にビープ音を聴覚フィードバックの一形態として再生する必要があります。

最終結果

以下がプログラムです。上記に検討したことはすべて盛り込んであります。何を行っているかが分かりやすいようにたくさんのコメントをつけています:

from microbit import *
import music


# モールス信号とそれが表す文字のルックアップテーブル。
MORSE_CODE_LOOKUP = {
    ".-": "A",
    "-...": "B",
    "-.-.": "C",
    "-..": "D",
    ".": "E",
    "..-.": "F",
    "--.": "G",
    "....": "H",
    "..": "I",
    ".---": "J",
    "-.-": "K",
    ".-..": "L",
    "--": "M",
    "-.": "N",
    "---": "O",
    ".--.": "P",
    "--.-": "Q",
    ".-.": "R",
    "...": "S",
    "-": "T",
    "..-": "U",
    "...-": "V",
    ".--": "W",
    "-..-": "X",
    "-.--": "Y",
    "--..": "Z",
    ".----": "1",
    "..---": "2",
    "...--": "3",
    "....-": "4",
    ".....": "5",
    "-....": "6",
    "--...": "7",
    "---..": "8",
    "----.": "9",
    "-----": "0"
}


def decode(buffer):
    # ルックアップテーブルからモールス信号を探します
    # 該当するものがなければ、終止符を返します。
    return MORSE_CODE_LOOKUP.get(buffer, '.')


# 単一の短点を表示する方法。
DOT = Image("00000:"
            "00000:"
            "00900:"
            "00000:"
            "00000:")


# 単一の長点を表示する方法。
DASH = Image("00000:"
             "00000:"
             "09990:"
             "00000:"
             "00000:")


# 短点を作るには 250 ミリ秒未満でボタンを押す必要あり。
DOT_THRESHOLD = 250
# 長点を作るには 250 ミリ秒未満でボタンを押す必要ある。
DASH_THRESHOLD = 500


# 入ってくるモールス信号を保持。
buffer = ''
# モールス信号から文字に変換したものを保持。
message = ''
# 次にボタンが押されるのをデバイスが待ち始めた時間。
started_to_wait = running_time()


# デバイスがボタンの押下を待って反応するためのループ。
while True:
    # デバイスがボタンの押下を待っている時間を計算。
    waiting = running_time() - started_to_wait
    # key_down_time のタイムスタンプをリセット。
    key_down_time = None
    # ボタンAが押されたら ...
    while button_a.is_pressed():
        # ビープ音を鳴らす - つまりモールス信号を鳴らします ;-)
        music.pitch(880, 10)
        # 端子1 (出力) を "オン" にします。
        pin1.write_digital(1)
        # ... そして key_down_time が設定されていない場合はすぐに設定 !
        if not key_down_time:
            key_down_time = running_time()
    # 端子2が信号を受け取っている場合は、ボタンAが押されたかのような
    # ふりをして ...
    while pin2.read_digital():
        if not key_down_time:
            key_down_time = running_time()
    # 現在の時刻を取得して key_up_time に入れます。
    key_up_time = running_time()
    # 端子1 (出力)を "オフ" にします。
    pin1.write_digital(0)
    # key_down_time に値が入っていた場合(ボタンAが押されたときに
    # 設定されます)
    if key_down_time:
        # ... ボタンが押されていた時間を調べます。
        duration = key_up_time - key_down_time
        # 押されていた時間が "短点" の最大長未満であれば ...
        if duration < DOT_THRESHOLD:
            # ... 着信したモールス信号を格納するバッファに短点を追加して、
            # ディスプレイに短点を表示。
            buffer += '.'
            display.show(DOT)
        # そうでなく、押されていた時間が "長点" の最大長未満であれば ...
        # (しかし、上で判定した短点よりは長い)
        elif duration < DASH_THRESHOLD:
            # ... バッファに長点を追加し、長点を表示
            buffer += '-'
            display.show(DASH)
        # さもなければ、その他の押下時間は無視します。(これは必要ではなく、
        # "わかりやすさ" のために追記しています)
        else:
            pass
        # ボタン押下を処理したので、ボタン押下をデバイスが
        # 待ち始める時間をリセット。
        started_to_wait = running_time()
    # さもなければ、このループのサイクルではボタンAの押下が無かったで、
    # モールス信号文字の終端を示す一時停止が無いかをチェックします。
    # 一時停止は長点信号の時間よりも長い必要があります。
    elif len(buffer) > 0 and waiting > DASH_THRESHOLD:
        # バッファになにかあり、信号の終端に達したので ...
        # バッファにあるものをデコードします。
        character = decode(buffer)
        # バッファを空にリセットします。
        buffer = ''
        # デコード結果の文字を表示します。
        display.show(character)
        # メッセージに文字を追加します。
        message += character
    # 最後に、上記すべてが行われている間にボタンBが押されていたら ...
    if button_b.was_pressed():
        # ... メッセージを表示して、
        display.scroll(message)
        # メッセージを空にリセットします(新しいメッセージの準備)。
        message = ''

どのように改善しますか? 短点と長点の定義を変更して、高速のモールスコードをユーザが使えるようにできますか? 両方のデバイスが同時に送信している場合はどうなりますか? この状況を処理するためには何をすればよいでしょうか?