Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

Elixir+PhoenixとSpread.Viewsでリアルタイムな出勤管理アプリを作ろう

IoT時代の救世主! SpreadJSで作るデータ可視化アプリ 第2回

  • LINEで送る
  • このエントリーをはてなブックマークに追加

目次

Raspberry PiとPhoenixの連携処理

 今回、Phoenixとの通信のやり取りはPhoenix ChannelというWebSocketを抽象化した機能を利用します。Phoenixではこの独自の仕組みを持っていてリアルタイム処理を扱いやすくしてくれています。Node.jsでいうところのSocket.ioに近い仕組みです。

サーバー側でPhoenix Channelのコードを記述

 Phoenix内でPhoenix Channelを扱うための準備です。

 今回はshifttable:lobbyというトピックを作成し、Raspberry Piと通信をします。

 lib/myapp_web/channels/user_socket.exに追記します。

 channel "shifttable:lobby", MyappWeb.ShifttableChannelの行を追加しましょう。

lib/myapp_web/channels/user_socket.ex
defmodule MyappWeb.UserSocket do
  use Phoenix.Socket

  ## Channels
  # channel "room:*", MyappWeb.RoomChannel
  channel "shifttable:lobby", MyappWeb.ShifttableChannel #ここを追記

 これでshifttable:lobbyトピックをMyappWeb.ShifttableChannelモジュールで処理をするという記述が追加されました。

 次はMyappWeb.ShifttableChannelモジュールの中を記述します。lib/myapp_web/channels/shifttable_channel.exを作成しましょう。

 クライアントが接続した時の処理として、join関数を作成し、クライアントから接続があった場合にtouch_nfcイベントを受け付ける関数としてhandle_in関数を作成します。

lib/myapp_web/channels/shifttable_channel.ex
defmodule MyappWeb.ShifttableChannel do
    use MyappWeb, :channel # チャネル関連の機能を使うための宣言

    def join("shifttable:lobby",payload, socket) do
        Process.flag(:trap_exit, true) # 異常時にプロセスが死なない為の設定
        IO.inspect payload
        {:ok, socket}
    end

    def handle_in("touch_nfc", payload, socket) do
        broadcast! socket, "touch_nfc", payload
        {:reply, {:ok, payload}, socket}
    end

end

クライアント側でPhoenix Channelのコードを記述

 assets/js/app.jsを編集します。app.jsの最後に以下を追加しましょう。

assets/js/app.js
// 省略

const chan = socket.channel("shifttable:lobby", {})
chan.join()
    .receive("ignore", () => console.log("auth error"))
    .receive("ok", (messages) => {
        console.log("join ok")
    })
    .receive("timeout", () => console.log("Connection interruption"))

// touch_nfcイベントを受信した時の処理
chan.on("touch_nfc", card => {
    console.log(card);
});

 試しにhttp://localhost:4000/shifttableに再アクセスしてみましょう。

 コンソール側でエラーが出ず、以下のような表示が出れば問題なくWebSocketが接続されています。

[info] JOIN "shifttable:lobby" to MyappWeb.ShifttableChannel
  Transport:  Phoenix.Transports.WebSocket (2.0.0)
  Serializer:  Phoenix.Transports.V2.WebSocketSerializer
  Parameters: %{}
%{}
[info] Replied shifttable:lobby :ok

Raspberry PiからPhoenix Chennelに接続する

 Socket.ioでも他の言語のクライアントライブラリが存在しますが、Phoenix Chennelでも同様に、以下のようなさまざまな言語のクライアントライブラリが存在します。

 今回はNode.jsでPhoenix Channelに接続するクライアントライブラリのphoenix-channelsを利用します。

 「Node.jsでElixir/PhoenixのChannelに接続する」にもまとめています。

 ここからはRaspberry Pi上のNode.jsで作業します。

 先ほどRaspberry Pi側で作成したNFC読み取りのapp.js(shifttable-clientフォルダ)と同じプロジェクトにライブラリをインストールします。

npm i --save phoenix-channels

 同じ階層にclient.jsを作成して接続してみましょう。xx.xx.xx.xxの箇所はPhoenixが動いているマシンのIPアドレスを指定します。

 また、MacBookなどのローカルマシン上でPhoenixのサーバーを立てている場合は、Raspberry PiとMac Bookなどのローカルマシンが同じネットワークに接続していることを確認しましょう。

shifttable-client/client.js
'use strict';

const { Socket } = require('phoenix-channels')
const socket = new Socket("ws://xx.xx.xx.xx:4000/socket");
socket.connect();
const channel = socket.channel("shifttable:lobby", {});

channel.join()
    .receive('ok', resp => console.log(`> joining channel  ${channel.topic}`))
    .receive("error", reason => console.log(`Error joining channel:`, reason));

channel.on("new_msg", msg => console.log(msg));

channel.onError(e => console.log("something went wrong", e))

 実行します。joining channel shifttable:lobbyと表示されれば、無事にRaspberry PiからPhoenix Channelに接続が出来ています。

$ node phoenix.js
> joining channel  shifttable:lobby

ICカード読み取りの処理と繋げる

 ICカードを読み取ったらPhoenix Channelへ情報を送信します。

 Raspberry Pi側のshifttable-client/client.jsを編集して、ICカード読み取りの機能も組み込みます。

shifttable-client/client.js
'use strict';

//NFCリーダーモジュール
const NfcpyId = require('node-nfcpy-id').default;
const nfc = new NfcpyId().start();

const { Socket } = require('phoenix-channels');
const socket = new Socket("ws://xx.xx.xx.xx:4000/
socket");
socket.connect();

let flag = ''; //重複フラグ

const channel = socket.channel("shifttable:lobby", {});
channel.join()
    .receive('ok', resp => console.log(`> joining channel  ${channel.topic}`))
    .receive("error", reason => console.log(`Error joining channel:`, reason));
channel.on("new_msg", msg => console.log(msg));

nfc.on('touchstart', (card) => {
    if(flag === card.id){
        console.log('重複です');
    }else{
        channel.push("touch_nfc", card);
        setTimeout(()=>{
            flag = '';
            console.log('リセット');
        },5*1000);
    }

    flag = card.id;
    console.log(card);
});

nfc.on('touchend', () => {
    console.log('Card was away.');
});

nfc.on('error', (err) => {
    // standard error output (color is red)
    console.error('\u001b[31m', err, '\u001b[0m');
});

channel.onError(e => console.log("something went wrong", e))

 nfc.on('touchstart')のリスナーで、card変数に情報を取得できます。

 channel.push("touch_nfc", card);の部分でPhoenix Channelに情報を送信します。

 また、同じ人が何回もICカードを反応させた場合の重複検知をさせるためにflag変数を使っているのと、setTimeout()で定期的にflagをリセットする処理を書いています。

 これでICカードをリーダーにかざした瞬間にPhoenixのサーバーに情報が飛ぶようになりました。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 菅原のびすけ(dotstudio株式会社)(スガワラ ノビスケ)

     日本最大規模のIoTコミュニティ「IoTLT」主催。岩手県立大学大学院ソフトウェア情報学研究科を卒業後、株式会社LIGでWebエンジニアとして入社し、Web開発に携わる。2016年にdotstudio株式会社を立ち上げ、今はIoT領域を中心に活動している。JavaScript Roboticsコミ...

バックナンバー

連載:IoT時代の救世主! SpreadJSで作るデータ可視化アプリ
All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5