Skip to content

Commit

Permalink
v0.1.7: Add new example m1-uid-scanner.html
Browse files Browse the repository at this point in the history
  • Loading branch information
taichunmin committed Dec 20, 2022
1 parent 0e4fc59 commit a0e536b
Show file tree
Hide file tree
Showing 6 changed files with 436 additions and 262 deletions.
9 changes: 7 additions & 2 deletions documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ toc:
description: |
URL: <http://taichunmin.idv.tw/pn532.js/m1-uid4b-writer.html>
This tools can control PN532 via Web Bluetooth or Web Serial to write 4 bytes UID to Chinese Magic Card (support UID and CUID).
This tools can write 4 bytes UID to Chinese Magic Card (support UID and CUID) via Web Bluetooth or Web Serial.
- name: M1 EML Toolkit
description: |
URL: <http://taichunmin.idv.tw/pn532.js/m1-eml-toolkit.html>
This tools can control PN532 via Web Bluetooth or Web Serial to read or write data from Mifare Card (support UID and CUID).
This tools can read or write data from Mifare Classic 1k (support UID and CUID) via Web Bluetooth or Web Serial.
- name: M1 UID Scanner
description: |
URL: <http://taichunmin.idv.tw/pn532.js/m1-uid-scanner.html>
This tools can scan UID of NFC Type A Tags via Web Bluetooth or Web Serial.
- name: other
children:
- Pn532Frame
Expand Down
1 change: 1 addition & 0 deletions layout/default.pug
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ html(lang="zh-Hant")
function gtag () { dataLayer.push(arguments) } // eslint-disable-line
gtag('js', new Date())
gtag('config', 'UA-39556213-3')
link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/animate.css@4/animate.min.css")
link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/css/bootstrap.min.css")
link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/font-awesome@4/css/font-awesome.min.css")
link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-tomorrow.min.css")
Expand Down
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pn532.js",
"version": "0.1.6",
"version": "0.1.7",
"author": "taichunmin <[email protected]>",
"browser": "dist/pn532.min.js",
"description": "pn532.js is a JavaScript library for PN532 base on Web Bluetooth and Web Serial.",
Expand All @@ -25,20 +25,20 @@
"web-serial-polyfill": "^1.0.14"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^23.0.2",
"@rollup/plugin-json": "^5.0.1",
"@rollup/plugin-commonjs": "^23.0.5",
"@rollup/plugin-json": "^5.0.2",
"@rollup/plugin-node-resolve": "^15.0.1",
"cross-env": "^7.0.3",
"dayjs": "^1.11.6",
"documentation": "^14.0.0",
"dayjs": "^1.11.7",
"documentation": "^14.0.1",
"dotenv": "^16.0.3",
"eslint": "^8.27.0",
"eslint": "^8.30.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.5.1",
"eslint-plugin-n": "^15.6.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-pug": "^1.2.4",
"eslint-plugin-pug": "^1.2.5",
"fast-glob": "^3.2.12",
"finalhandler": "^1.2.0",
"html-minifier": "^4.0.0",
Expand All @@ -47,7 +47,7 @@
"livereload": "^0.9.3",
"node-watch": "^0.7.3",
"pug": "^3.0.2",
"rollup": "^3.2.5",
"rollup": "^3.7.4",
"rollup-plugin-terser": "^7.0.2",
"serve-static": "^1.15.0",
"web-streams-polyfill": "^3.2.1"
Expand Down
10 changes: 6 additions & 4 deletions src/plugin/Hf14a.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default class Pn532Hf14a {
* @param {object} args
* @param {number} args.maxTg The maximum number of mifare targets to be initialized by the PN532. The PN532 is capable of handling 2 targets maximum at once, so this field should not exceed `0x02`.
* @param {Packet} args.uid Set to UID of card if wants to initialize a target with a known UID.
* @param {number} args.timeout The maxinum timeout for waiting response.
* @param {number=3e4} args.timeout The maxinum timeout for waiting response.
* @returns {Promise<Pn532Hf14a~MifareTarget[]>} Resolve with an array of detected mifare targets.
*/
async function inListPassiveTarget ({ maxTg = 1, uid = new Packet(), timeout } = {}) {
Expand Down Expand Up @@ -104,15 +104,17 @@ export default class Pn532Hf14a {
}

/**
* This function is used to detect one mifare target in passive mode.
* This function is used to detect one mifare target in passive mode. It will release the target if reader connection is opened.
* @memberof Pn532Hf14a
* @instance
* @async
* @param {object} args
* @param {number=3e4} args.timeout The maxinum timeout for waiting response.
* @returns {Promise<Pn532Hf14a~MifareTarget>} Resolve with detected mifare target.
*/
async function mfSelectCard () {
async function mfSelectCard ({ timeout } = {}) {
try {
return (await inListPassiveTarget())?.[0]
return (await inListPassiveTarget({ timeout }))?.[0]
} finally {
await inReleaseIfOpened()
}
Expand Down
166 changes: 166 additions & 0 deletions web/m1-uid-scanner.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
extends /layout/default

block beforehtml
- const title = 'Mifare UID Scanner'

block style
meta(property="og:description", content=title)
meta(property="og:locale", content="zh_TW")
meta(property="og:title", content=title)
meta(property="og:type", content="website")
meta(property="og:url", content=`${baseurl}m1-uid-scanner.html`)
style
:sass
[v-cloak]
display: none
body, .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6
font-family: 'Noto Sans TC', sans-serif
.letter-spacing-n1px
letter-spacing: -1px
block content
#app.my-3.container.text-monospace(v-cloak)
h3.mb-3.text-center= title
.form-group
label 選擇 PN532 的連線方式
select.form-control.form-control-sm(v-model="h.adapter")
option(value="ble") 透過藍芽 BLE 連線 (支援 PC 及 Android)
option(value="usb") 透過 USB 連線 (支援 PC)
.row.mx-n1
.col.px-1: button.btn.btn-block.btn-danger(type="button", @click="btnClear") 清空資料
.col.px-1
button.btn.btn-block.btn-success(v-if="!scanning", type="button", @click="btnScanStart") 開始掃描
button.btn.btn-block.btn-warning(v-else, type="button", @click="btnScanStop") 停止掃描
.table-responsive.mt-3.letter-spacing-n1px(style="font-size: .85rem")
table.table.table-striped.table-bordered.table-sm.text-center
caption.text-right: small 點選表格可以複製資料
thead: tr
th 掃描時間
th UID
th ATQA
th SAK
tbody
tr(
v-for="card in h.cards",
:key="`${card.uid}-${card.atqa}-${card.sak}`",
:class="card.ts + 1000 > tsnow ? 'animate__animated animate__flash' : ''",
)
td {{ dayjs(card.ts).format('YYYY-MM-DD HH:mm:ss') }}
td(@click="btnCopy(card.uid)") {{ card.uid }}
td(@click="btnCopy(card.atqa)") {{ card.atqa }}
td(@click="btnCopy(card.sak)") {{ card.sak }}

block script
script.
const {
Pn532: { Pn532, Packet, utils: Pn532utils }, // eslint-disable-line no-unused-vars
Pn532Hf14a,
Pn532LoggerRxTx, // eslint-disable-line no-unused-vars
Pn532WebbleAdapter,
Pn532WebserialAdapter,
} = window

// usb adapter
const pn532usb = new Pn532()
pn532usb.use(new Pn532WebserialAdapter())
pn532usb.use(new Pn532Hf14a())

// ble adapter
const pn532ble = new Pn532()
pn532ble.use(new Pn532WebbleAdapter())
pn532ble.use(new Pn532Hf14a())

if (new URL(location).searchParams.has('debug')) {
// debug mode
pn532usb.use(new Pn532LoggerRxTx())
pn532ble.use(new Pn532LoggerRxTx())
}

window.vm = new Vue({
el: '#app',
data: {
scanning: false,
tsnow: Date.now(),
h: {
adapter: 'ble',
cards: [],
},
},
async mounted () {
// 自動儲存功能
try {
const saved = JSON5.parse(localStorage.getItem(location.pathname))
if (saved) this.$set(this, 'h', { ...this.h, ...saved })
} catch (err) {}
this.$watch('h', () => {
localStorage.setItem(location.pathname, JSON5.stringify(this.h))
}, { deep: true })
},
computed: {
pn532 () {
return this.h.adapter === 'usb' ? pn532usb : pn532ble
},
},
methods: {
async btnScanStart () {
try {
this.showLoading('請稍候', '連線到讀卡機')
const version = await this.pn532.getFirmwareVersion()
console.log(`version = ${JSON.stringify(version)}`)
this.scanning = true
Swal.close() // scanning started
while (this.scanning) {
try {
const card = await this.pn532.$hf14a.mfSelectCard({ timeout: 1e3 })
if (card) {
const newCards = _.uniqBy([{
uid: card.uid.hex,
atqa: card.atqa.hex,
sak: card.sak.hex,
ts: Date.now(),
}, ...this.h.cards], c => `${c.uid}-${c.atqa}-${c.sak}`)
this.$set(this.h, 'cards', newCards)
}
} catch (err) {
// throw error if disconnected
if (!this.pn532.$adapter?.isOpen?.()) throw err
}
this.tsnow = Date.now()
await Pn532utils.sleep(100)
}
Swal.close() // scanning stopped
} catch (err) {
console.error(err)
this.scanning = false
await Swal.fire({ icon: 'error', title: '掃描失敗', text: err.message })
}
},
async btnScanStop () {
this.showLoading('請稍候', '正在停止掃描')
this.scanning = false
},
async btnClear () {
this.$set(this.h, 'cards', [])
},
async btnCopy (text, container = null) {
if (!container) container = document.body
const dom = document.createElement('textarea')
dom.value = text
container.appendChild(dom)
dom.select()
dom.setSelectionRange(0, 1e6) // For mobile devices
document.execCommand('copy')
container.removeChild(dom)
await Swal.fire({ icon: 'success', title: '複製成功' })
},
showLoading (title, text) {
Swal.fire({
title,
text,
allowOutsideClick: false,
showConfirmButton: false,
willOpen: () => { Swal.showLoading() },
})
},
},
})
Loading

0 comments on commit a0e536b

Please sign in to comment.