Progressive Web App NFC NDEFReader 入門實作

me
林彥成
2021-09-19 | 2 min.
文章目錄
  1. 1. 什麼是 Web NFC?
  2. 2. 怎麼使用 NDEFReader?

什麼是 Web NFC?

NFC (Near Field Communication) 近場通訊是高頻 (13.56 MHz) 短距離無線通訊技術,只要距離 5-10 公分內 Web NFC 就能夠讀取和寫入 NFC 標籤,傳輸速率高達 424 kbit/s。

目前 Web NFC 只支援 NDEF,尚不支援 ISO-DEP、NFC-A/B、NFC-F、HCE。

Demo 站台如下,只會示範讀取功能,因為不太確定真的執行寫入悠遊卡會不會壞掉 Orz
https://linyencheng.github.io/pwa-web-nfc/

Web App 會透過 Page Visibility API 來偵測目前網站是否在 “可見” 的狀態,在可見狀態下才能夠執行掃描讀取或寫入。

當用戶成功使用其設備掃描 NFC 標籤時,瀏覽器會使用震動來提示。

  • 如果螢幕關閉或設備被鎖定,則對 NFC 讀取將被中止
  • 對於不可見的網頁,接收和推送 NFC 內容被暫停,並在網頁再次可見時恢復

Web NFC 示意圖(圖片來源: https://web-dev)

使用 Web NFC 的情境包括:

  • 博物館中將裝置跟展覽的 Tag 接觸時,可以顯示有關訊息
  • 商店可以做庫存管理
  • 馬拉松比賽可以拿來讀取跑友的 RFID

怎麼使用 NDEFReader?

  1. 判斷是否支援,接著是取得權限 NDEFReader,只要叫用 scan() 和 write() 就會觸發權限的提示,也可以透過程式觸發。
1
2
3
4
5
6
7
8
9
10
11
if ("NDEFReader" in window) {
// 支援
const nfcPermissionStatus = await navigator.permissions.query({
name: "nfc",
});
if (nfcPermissionStatus.state === "granted") {
// 有開啟權限
} else {
// 未開啟權限
}
}
  1. new 一個 NDEFReader 然後就可以叫用 scan(),叫用後當 NFC 標籤接近時,NDEFReadingEvent 會觸發事件。
    • reading 成功後會收到兩個屬性
      • serialNumber: 表示設備的序列號(例如 00-11-22-33-44-55-66),如果沒有,則為空字符串。
      • message: 表示存儲在 NFC 標籤中的 NDEF 消息。
    • 寫入時的 option 也有兩種屬性
      • overwrite: 不可覆寫要記得設定為 false
      • records: 可以寫入多筆資訊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 讀
async function readTag() {
try {
const ndef = new NDEFReader();
await ndef.scan();

ndef.addEventListener("readingerror", () => {
log("讀取錯誤");
});

ndef.addEventListener("reading", ({ message, serialNumber }) => {
log(`> Serial Number: ${serialNumber}`);
log(`> Records:(${message.records.length})`);
});
} catch (error) {
log("錯誤" + error);
}
}

// 寫
async function writeTag() {
try {
const ndef = new NDEFReader();
await ndef.write("Hello world!", { overwrite: false });
await ndef.write({
records: [
{ recordType: "url", data: "https://w3c.github.io/web-nfc/" },
{ recordType: "url", data: "https://web.dev/nfc/" },
],
});
} catch (error) {
log("錯誤" + error);
}
}
  1. 收到的 message 可能含有多筆 records 這時候可以一筆一筆依照屬性取解析
1
2
3
4
5
6
7
8
9
10
11
12
for (const record of message.records) {
console.log("Record type: " + record.recordType);
console.log("MIME type: " + record.mediaType);
console.log("Record id: " + record.id);
switch (record.recordType) {
case "text":
break;
case "url":
break;
default:
}
}
  1. 停止操作,透過 AbortController 中的 signal 當成參數送進去 scan()、write() 中可以隨時終止目前的動作。
1
2
3
4
5
6
7
8
9
10
11
12
13
const abortController = new AbortController();
abortController.signal.onabort = (event) => {};

const ndef = new NDEFReader();
await ndef.scan({ signal: abortController.signal });

await ndef.write("Hello world", { signal: abortController.signal });

function abortAction(event) {
abortController.abort();
}

abortAction();

喜歡這篇文章,請幫忙拍拍手喔 🤣

share