什麼是 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?
- 判斷是否支援,接著是取得權限 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 { } }
|
- 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); } }
|
- 收到的 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: } }
|
- 停止操作,透過
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();
|
喜歡這篇文章,請幫忙拍拍手喔 🤣