aptpod Tech Blog

株式会社アプトポッドのテクノロジーブログです

【Web NFC】JavaScriptでNFCタグのデータを読み書きしてみた

Webチームの蔵下です。Chrome 81でWeb NFCが試験的に導入されました! ちょっと変わり種なのでネット上ではあまり話題にならなかったのですが、個人的にはビッグニュースでした。

Web NFCを使うと、下記のTweetのような実在するカードとWebサイトを組み合わせたゲームなどが実装できます! すごい!

勢いのままにWeb NFCを触ってみたので、ソースコードを交えて使い方を紹介します。

Web NFCとは?

Web NFCとは、JavaScriptでWebサイトからNFCタグにデータを読み書きできるAPIです。記事公開時点(2020年6月)ではAndroid Chromeでの対応となっているため、NFC機能が搭載されているAndroidスマートフォンで動作が確認できます。

NFCタグ

NFC(Near Field Communication)とは近接型無線通信方式で、デバイスでNFCタグにタッチするだけでデータの読み書きができます。今回Web NFC確認用に Amazon で購入したNFCタグは、容量が144Byteと小さいですが価格も安く手軽に試せます。

購入したNFCタグ。シールになっていて貼り付けられる。

Web NFCを試す前の下準備

Web NFCはまだ試験的な導入のため、 chrome://flags/#enable-experimental-web-platform-features のフラグを Enabled にするか、 Origin Trials の設置が必要です。

chrome://flags/#enable-experimental-web-platform-featuresをEnabledに変更

Web NFCのソースコード

Web NFCを実際に試したソースコードを紹介します。

Scan

NFCタグからデータを読み込むには、NDEFReaderscan() でScan機能を起動します。起動した状態でデバイスでNFCタグにタッチすると reading Eventが発火し、NFCタグに付与されているシリアルナンバー( serialNumber )と書き込まれているデータ( message )が受け取れます。一度Scanすると、NFCタグにタッチする度にEventが発火します。

const scan = async () => {
  try {
    const reader = new NDEFReader()
    await reader.scan()

    // Scanは起動しているが、NFCタグからデータが読み込めなかった
    reader.addEventListener('error', (event) => {
      console.log(error)
    })

    // データを読み込んだ
    reader.addEventListener('reading', ({ serialNumber, message }) => {
      const record = message.records[0]
      const { data, recordType } = record
      // recordTypeごとにdecode処理を実行する
    })
  } catch (error) {
    // Scan起動失敗
    console.error(error)
  }
}

受け取った message の中に、NFCに書き込まれていたデータが records として配列で格納されています。複数のデータが書き込まれている場合、 records に複数個の record が入ります。 record に付与されている recordType でデータの種類が特定できるので、recordType ごとにdecode処理を実装します。

※ 初めて Scan を実行する際に下記のようなダイアログが表示されます

NFC機能確認ダイアログ

Text

recordTypetext の場合はTextデータとなります。 TextDecoder でdecodeすることで、 data を文字列として扱えます。

const { data, encoding, recordType } = record
if (recordType === 'text') {
  const textDecoder = new TextDecoder(encoding)
  const text = textDecoder.decode(data)
  console.log(`Text: ${text}`)
}
JSON

recordTypemimemediaTypeapplication/json の場合はJSONデータとなります。 JSON.parse() でparseすることで、Objectとして扱えます。

const { data, mediaType, recordType } = record
if (recordType === 'mime' && mediaType === 'application/json') {
  const textDecoder = new TextDecoder()
  const json = JSON.parse(textDecoder.decode(data))
  console.log(json)
}
Image

recordTypemimemediaTypeapplication/png の場合はPNGデータ(Image)となります。 dataBlob へ変換し、 image.srcURL.createObjectURL() で渡すことで画像が表示できます。

const { data, mediaType, recordType } = record
if (recordType === 'mime' && mediaType === 'image/png') {
  const blob = new Blob([data], { type: mediaType })
  const image = new Image()
  image.src = URL.createObjectURL(blob)
}

Write

NFCタグへデータを書き込むには、NDEFWriterwrite() を実行し、デバイスをNFCタグにタッチします。データは文字列で書き込まれるため、データの種類ごとにencode処理が必要になります。

Text

Textは文字列のため、そのまま write() で書き込みます。

const writeText = async(text) => {
  try {
    const writer = new NDEFWriter()
    await writer.write(text)
  } catch (error) {
    console.error(error)
  }
}
JSON

dataは JSON.stringify() で文字列へ変換し、 recordType , mediaType を付与した record を作成します。作成した recordrecords 配列に格納して write() で書き込みます。

const writeJson = async(data) => {
  const encoder = new TextEncoder()
  const jsonRecord = {
    recordType: 'mime',
    mediaType: 'application/json',
    data: encoder.encode(JSON.stringify(data)),
  }

  try {
    const writer = new NDEFWriter()
    await writer.write({ records: [jsonRecord] })
  } catch (error) {
    console.error(error)
  }
}
Image

書き込みたいImageを ArrayBuffer へ変換し、 recordType , mediaType を付与した record を作成します。作成した recordrecords 配列に格納して write() で書き込みます。

const writeImage = async (url) => {
  const imageRecord = {
    recordType: 'mime',
    mediaType: 'image/png',
    data: await (await fetch(url)).arrayBuffer(),
  }

  try {
    const writer = new NDEFWriter()
    await writer.write({ records: [imageRecord] })
  } catch (error) {
    console.error(error)
  }
}

Imageを書き込む方法は上記のように用意されていますが、NFCタグは一般的に数百バイトと容量が小さく、私達が普段扱っているような画像は書き込めません。今後のNFCタグの性能向上に期待したいですが、しばらくは画像をNFCタグへ直接書き込むことは難しいでしょう。

まとめ

デバイスをタッチしてアクションを起こすという動作は、スマートフォンや交通系ICカードなどの普及もあり、私達の生活で当たり前のものとなりました。今まではネイティブのアプリケーションでしか実装できなかったNFCのデータ通信も、JavaScriptを使ってWebサイトで実装できるようになると、新しいサービスや体験が生まれてくるかもしれません!

今後も変わり種JavaScriptの情報を発信していきますのでご期待ください!


採用情報

アプトポッドでは、一緒に働いてくださる仲間を募集中です。

弊社は、IoT(Internet of Things)という言葉がバズるはるか以前、当時まだM2M(Machine-to-Machine)と呼ばれていたような時代から、IoTミドルウェアやIoTデータの可視化ダッシュボード開発を手掛けてきた、IoTプロダクトのスペシャリスト集団です。(弊社の成り立ちについては、弊社VPoPが投稿しているこちらの記事 アプトポッドの過去と現在、そして未来について - aptpod Tech Blog をご覧ください)

弊社の主力製品は、 IoTプラットフォームの構築用ミドルウェア intdash と、そのミドルウェアを流れる大量のIoTデータを可視化する Webベースのダッシュボード Visual M2M Data Visualizer です。リンク先の弊社サイトでもご紹介している通り、自動車やロボットから収集した秒間数千点にもなるIoTデータを、リアルタイムに遅延なくブラウザ上に描画するパフォーマンスに強みを持っています。

www.aptpod.co.jp

サーバーから WebSocket や WebTransport をベースとした独自プロトコル iSCP(intdash Stream Control Protocol)によってプッシュ型で配信されてくる大量のデータには、数千Hzにもなるセンサーデータだけでなく、映像データや音声データなど、様々なものを含みます。これらをブラウザ上で統合的に可視化することによって、IoTデータの利活用を強力にサポートします。

弊社のWebアプリケーション開発は、大量に送りつけられるデータの捌き方、様々な種類のマルチモーダルデータの取り扱い、独自プロトコルのハンドリングなど、なかなか他では体験できない開発トピックが盛りだくさんです。

情報技術の総合格闘技とも言われるIoTの分野で、我々と一緒に、技術で未来を切り開いてみませんか?

応募は、当社リクルートページよりお願いします。 www.aptpod.co.jp