Microbit ラズパイ Bluetooth〜マイクロビットとラズパイ間をSDPで通信する〜

今回は、Microbitとラズパイの間をBluetooth経由で通信するためのサーバーを作成し始めようと思います。
参考にするページは本家Oracleのページです

元々のやりたいこと

一応この実装の目的は、Microbitからのbluetooth経由メッセージをラズパイで受信して、Midiメッセージを音源モジュールに送信、音を鳴らすというのが目的です。下はイメージになります。

SDPで通信

SDP(Sockets Direct Protocol)はMicrobitとラズパイの通信で使用できるという情報を得て、現在に至りますが、早い話が、Bluetoothでの通信時にこのSDPが使用できるということです。このテクノロジーを使用するためにサポートされて(JDKに入って)いるクラスは下に記載しました。(参考サイト参照)

  • java.net package
    • Socket
    • ServerSocket
  • java.nio.channels package:
    • SocketChannel
    • ServerSocketChannel
    • AsynchronousSocketChannel
    • AsynchronousServerSocketChannel

必要な作業

  1. SDP構成ファイルの作成
  2. SDPプロトコルの有効化
  3. SDPのデバッグ

こんな感じの作業が必要になると思いますが、如何せんやってみないことには始まらないので。。。

SDPサーバーを作る

SDP構成ファイルの作成このページを読んでみると、構成ファイル(設定ファイル)を作る必要がある、と記載しているのでその通りにやってみます。。。
しかし、記載ルールが書いてあるだけなのでよくわからない状態です。
わからないので調べます。そして、ちょうど良いのがありました。設定ファイルのテンプレートのようなものらしいです。早速失敬して。。。
ご丁寧に、全部コメントアウトされています。必要な部分を修正、コメントを外して使用します。

設定ファイル(Conf File)

#
# Configuration file to enable InfiniBand Sockets Direct Protocol.
#
# Each line that does not start with a comment (#) is a rule to indicate when
# the SDP transport protocol should be used. The format of a rule is as follows:
#   ("bind"|"connect") 1*LWSP-char (hostname|ipaddress["/"prefix]) 1*LWSP-char ("*"|port)["-"("*"|port)]
#
# A "bind" rule indicates that the SDP protocol transport should be used when
# a TCP socket binds to an address/port that matches the rule. A "connect" rule
# indicates that the SDP protocol transport should be used when an unbound
# TCP socket attempts to connect to an address/port that matches the rule.
# Addresses may be specified as hostnames or literal Internet Protocol (IP)
# addresses. When a literal IP address is used then a prefix length may be used
# to indicate the number of bits for matching (useful when a block of addresses
# or subnet is allocated to the InfiniBand fabric). 

# Use SDP for all sockets that bind to specific local addresses
#bind    192.168.4.1    *
#bind    fe80::21b:24ff:fe3d:7896    *

# Use SDP for all sockets that bind to the wildcard address in a port range
#bind    0.0.0.0    5000-5999
#bind    ::0        5000-5999

# Use SDP when connecting to all application services on 192.168.1.*
#connect 192.168.1.0/24       1024-*

# Use SDP when connecting to the http server or MySQL database on hpccluster.
#connect hpccluster.foo.com   80
#connect hpccluster.foo.com   3306

Socketの実装

以前作成したものですが、これに設定ファイルを適用すればいけるみたいです。下のものはSocketサーバーを起動するコードです。
クライアントから送信されたメッセージ(テキストなど)をそのまま返す処理になっています。

public void startServer() {
    try {
        System.out.println("*** SocketServer start " + server.isBound() + "***");
        Socket recieve = server.accept();
        System.out.println("*** Server Get Request start***");
        // 受信データの読み込み
        StringBuilder responseTxt = new StringBuilder("");
        // 受信状態
        System.out.println("クライアント: " + recieve.getRemoteSocketAddress());
        System.out.println("接続: " + recieve.isConnected());
        System.out.println("入力ストリームが閉じている: " + recieve.isInputShutdown());

        // 受信したリクエスト
        InputStream in = recieve.getInputStream();
        // 返却するレスポンス
        OutputStream writer = recieve.getOutputStream();
        System.out.print("Server recieve is ...");
        int c = 0;
        // CRとCRLFの場合で入力が終了している時がある
        while((c = in.read()) != -1) {
            char ch = (char)c;
            responseTxt.append(ch);
            // 空の場合
            if (c == 10 || c == 13) {
                break;
            }
        }
        System.out.println(responseTxt.toString());
        System.out.println("*** Server Send Response start***");
        // レスポンス送信
        writer.write((responseTxt.toString() + System.getProperty("file.separator")).getBytes());
        writer.flush();
        in.close();
        writer.close();
        System.out.println("*** SocketServer end ***");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 開いたストリームを閉じる
        try {
            closeServer();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
一度書けばどこでも動くの精神

ということです(笑)
あとは動かしてみてうまくいくかどうか?を確認します。
そして、これをベースにして「〜を受信したときに〜する」というような処理を実装していきます。
ソースはGithubにコミットしてありますので、よかったら全体のソースも見てみてください、

でわでわ。。。



AngularJS チュートリアル7〜$http 外部画面(HTML)を取得する〜

今回は$httpオブジェクトを使用して自分のホームページ(サーバー)を起点として、外部にあるHTMLを取得、表示するためのチュートリアルをやろうと思います。

$httpオブジェクト

参考URLはこちらです。
単純にHTTPリクエストを送信して対象のURLからHTMLを取得するものです。

リクエストの種類

GET

今回使用するリクエストのメソッドです。よく「GETリクエスト」などと呼びます。このリクエストはhttp://XXXX,com/XXX.htmlなどのように対象のHTMLをダウンロードしようとします。ブラウザで表示しているのもこのリクエストになります。
こうして取得したHTMLをブラウザで表示、もしくは何かしらの処理を行う。。。などいろいろなことができます。

そして、パラメータをつけてJSONを取得するなどのWebAPIを使用する場合にも使用します。パラメータは下のようにつけてやります。
http://XXX.com/webapi/request/?param1=XXX¶m2=OOOOこんな感じで送信します。

POST

上のようにGETで用が足りてしまうのでPOSTメソッドはいらないだろう。。。そう思った方素晴らしい視点です。
しかし、上のようにパラメータを送信すると渡した値がURLで直接見えるため、セキュリティ的にアウトです。
例えば、URLに"XXX市XXX町2-1"などのように個人情報を含むような情報は送信できません。(情報漏洩です)
なのでPOSTメソッドの出番、となるわけです。

POSTメソッドは、単純にURLに値を渡さない方法です。
送信するURL(リクエスト)はhttp://XXX.comで良いのですが、ここでパラメータ(ユーザー名など)をサーバー側に送信したい場合はフォームに値を設定して送信します。
このようなリクエストをPOSTリクエストと呼びます。

本題

今回のように、HTMLをダウンロードするなどしてファイルを参照するときは、Webサーバーを起動してからAngularを実行する必要があります。
最近のブラウザはセキュリティがかかっているのでサーバーを解さないとエラーで対象のHTMLが取得できません。

実行する

参考サイトのコードをコピって(ちょいと修正して)起動してみると下のように表示できます。

コードは下のようになっています。HTMLとJSを1つのファイルに書いています。(チュートリアルなので、わかりやすいようにしています)、そして実行結果はこちらで確認できます。取得している「test.html」は、ブラウザで表示すれば、ただのテキストですが、取得したものは <div>タグがついています。。。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
    </head>
    <body>
        <div ng-app="myApp" ng-controller="myCtrl">

        <p>Today's welcome message is:</p>
        <h1>{{myWelcome}}</h1>

        </div>

        <script>
        var app = angular.module('myApp', []);
        app.controller('myCtrl', function($scope, $http) {
        $http.get("test.html")
          .then(function(response) {
            $scope.myWelcome = response.data;
          });
        });
        </script>
    </body>
</html>

コードについて

初めにJSが起動します。
var app = angular.module('myApp', []);でアングラーモジュールを作成。
app.controller('myCtrl', function($scope, $http) {でコントローラーを作成します。controller()の第一引数はコントローラー名、第二引数はコントローラーの処理(function)です。
ここで$scopeに設定している変数がHTML側で{{}}で囲っている部分にバインドされます。
なので、上のコード$scope.myWelcomeの値を表示するときは{{myWelcome}}を書けば良いです。

でわでわ。。。