Skip to content

Commit

Permalink
add dfu support and demo page for dfu (#125)
Browse files Browse the repository at this point in the history
  • Loading branch information
taichunmin authored Jun 21, 2024
2 parents a08ed86 + c1346f3 commit da173c8
Show file tree
Hide file tree
Showing 27 changed files with 1,396 additions and 217 deletions.
2 changes: 0 additions & 2 deletions eslint-local-rules.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ const _ = require('lodash')
const eslintPluginTsdoc = require('eslint-plugin-tsdoc')
const fs = require('fs')

fs.writeFileSync('./debug.txt', 'test\r\n', { flag: 'as' })

function eslintPluginTsdocPatch () {
const origRule = eslintPluginTsdoc.rules.syntax
return {
Expand Down
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"dependencies": {
"@taichunmin/buffer": "^0.13.6",
"debug": "^4.3.4",
"jszip": "^3.10.1",
"lodash": "^4.17.21",
"serialport": "^12.0.0",
"web-serial-polyfill": "^1.0.15",
Expand Down Expand Up @@ -99,6 +100,15 @@
"require": "./dist/Crypto1.js",
"script": "./dist/Crypto1.global.js"
},
"./plugin/DfuZip": {
"types": {
"import": "./dist/plugin/DfuZip.d.mts",
"require": "./dist/plugin/DfuZip.d.ts"
},
"import": "./dist/plugin/DfuZip.mjs",
"require": "./dist/plugin/DfuZip.js",
"script": "./dist/plugin/DfuZip.global.js"
},
"./plugin/SerialPortAdapter": {
"types": {
"import": "./dist/plugin/SerialPortAdapter.d.mts",
Expand Down
30 changes: 25 additions & 5 deletions pages/demos.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
- [Demos](#demos)
- [device-settings.html](#device-settingshtml)
- [Features](#features)
- [mfkey32.html](#mfkey32html)
- [dfu.html](#dfuhtml)
- [Features](#features-1)
- [Related links](#related-links)
- [mifare1k.html](#mifare1khtml)
- [mfkey32.html](#mfkey32html)
- [Features](#features-2)
- [hf14a-scanner.html](#hf14a-scannerhtml)
- [Related links](#related-links-1)
- [mifare1k.html](#mifare1khtml)
- [Features](#features-3)
- [mifare-xiaomi.html](#mifare-xiaomihtml)
- [hf14a-scanner.html](#hf14a-scannerhtml)
- [Features](#features-4)
- [mifare-value.html](#mifare-valuehtml)
- [mifare-xiaomi.html](#mifare-xiaomihtml)
- [Features](#features-5)
- [mifare-value.html](#mifare-valuehtml)
- [Features](#features-6)

## [device-settings.html](https://taichunmin.idv.tw/chameleon-ultra.js/device-settings.html)

Expand All @@ -33,6 +36,23 @@ A ChameleonUltra tool to management the device info and settings.

- - -

## [dfu.html](https://taichunmin.idv.tw/chameleon-ultra.js/dfu.html)

A tool to upload firmware to ChameleonUltra.

### Features

- Select tag to upload firmware

### Related links

- [Uploading the code in DFU mode](https://github.com/RfidResearchGroup/ChameleonUltra/blob/main/docs/development.md#uploading-the-code-in-dfu-mode)
- [nRF5 SDK: DFU protocol](https://docs.nordicsemi.com/bundle/sdk_nrf5_v17.1.0/page/lib_dfu_transport.html)
- [GitHub: GameTec-live/ChameleonUltraGUI](https://github.com/GameTec-live/ChameleonUltraGUI/blob/main/chameleonultragui/lib/bridge/dfu.dart)
- [GitHub: thegecko/web-bluetooth-dfu](https://github.com/thegecko/web-bluetooth-dfu)

- - -

## [mfkey32.html](https://taichunmin.idv.tw/chameleon-ultra.js/mfkey32.html)

A ChameleonUltra tool to detect the mifare key that reader is authenticating (a.k.a. MFKey32).
Expand Down
1 change: 1 addition & 0 deletions pug/include/bootstrapV4.pug
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ html(lang="zh-Hant")
script(crossorigin="anonymous", src=`${baseurl}index.global.js`)
script(crossorigin="anonymous", src=`${baseurl}Crypto1.global.js`)
script(crossorigin="anonymous", src=`${baseurl}plugin/Debug.global.js`)
script(crossorigin="anonymous", src=`${baseurl}plugin/DfuZip.global.js`)
script(crossorigin="anonymous", src=`${baseurl}plugin/WebbleAdapter.global.js`)
script(crossorigin="anonymous", src=`${baseurl}plugin/WebserialAdapter.global.js`)
block script
8 changes: 4 additions & 4 deletions pug/src/device-settings.pug
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ block content
block script
script(crossorigin="anonymous", src="https://cdn.jsdelivr.net/npm/joi@17/dist/joi-browser.min.js")
script.
const { AnimationMode, ButtonAction, ButtonType, ChameleonDebug, ChameleonUltra, DeviceMode, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const { AnimationMode, ButtonAction, ButtonType, ChameleonUltra, Debug, DeviceMode, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const ultraUsb = new ChameleonUltra()
ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new Debug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra()
ultraBle.use(new ChameleonDebug())
ultraBle.use(new Debug())
ultraBle.use(new WebbleAdapter())

const { joi: Joi } = window
Expand Down Expand Up @@ -231,7 +231,7 @@ block script
},
async btnEnterBootloader () {
if (!await this.confirm('Enter bootloader?', 'Yes', 'Cancel')) return
await this.ultra.cmdEnterBootloader().catch(err => { this.ultra.emitter.emit('error', err) })
await this.ultra.cmdDfuEnter().catch(err => { this.ultra.emitter.emit('error', err) })
},
async btnResetSettings () {
if (!await this.confirm('Reset to default settings?', 'Yes', 'Cancel')) return
Expand Down
193 changes: 193 additions & 0 deletions pug/src/dfu.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
extends /include/bootstrapV4

block beforehtml
- const title = 'Upload Firmware'

block style
meta(property="og:description", content="A tool to upload firmware to ChameleonUltra.")
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}dfu.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
.input-group-prepend > .input-group-text
width: 80px
.letter-spacing-n1px
&, .btn, textarea, select, input
letter-spacing: -1px
.text-sm
font-size: 0.875rem
block content
#app.my-3.container.text-monospace(v-cloak)
h4.mb-3.text-center.letter-spacing-n1px #[.bgicon.bgicon-chameleon-ultra.mr-1] #{title}
.form-group.letter-spacing-n1px
label Connect method:
.input-group.input-group-sm.mb-3
select.form-control(v-model="ls.adapter")
option(value="ble") BLE (PC & Android)
option(value="usb") USB Serial (PC only)
.input-group-append: button.btn.btn-outline-secondary(@click="btnAdapterTips") #[i.fa.fa-fw.fa-question]
.form-group.letter-spacing-n1px.mb-3
label Tag Name:
select.form-control.form-control-sm(v-model="ss.tagName")
option(v-for="[k, v] of tagNames", :value="k") {{ v }}
button.btn.btn-block.btn-outline-primary.letter-spacing-n1px.mb-2(@click="btnUploadFirmware") #[i.fa.mr-1.fa-download] Upload Firmware

block script
script.
const { Buffer, ChameleonUltra, Debug, DfuZip, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const ultraUsb = new ChameleonUltra()
ultraUsb.use(new Debug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra()
ultraBle.use(new Debug())
ultraBle.use(new WebbleAdapter())

window.vm = new Vue({
el: '#app',
data: {
ls: {
adapter: 'ble',
},
ss: {
tagName: '',
},
manifest: {},
},
async mounted () {
// 自動儲存功能
for (const [storage, key] of [[localStorage, 'ls'], [sessionStorage, 'ss']]) {
try {
const saved = JSON5.parse(storage.getItem(location.pathname))
if (saved) this.$set(this, key, _.merge(this[key], saved))
} catch (err) {}
this.$watch(key, () => {
storage.setItem(location.pathname, JSON5.stringify(this[key]))
}, { deep: true })
}
await this.fetchManifest()
},
computed: {
ultra () {
return this.ls.adapter === 'usb' ? ultraUsb : ultraBle
},
tagNames () {
return _.map(_.orderBy(this?.manifest?.releases, ['createdAt'], ['desc']), release => {
if (_.isNil(release.gitVersion)) return [release.tagName, release.tagName]
return [release.tagName, `${release.tagName} (${release.gitVersion})`]
})
},
releases () {
const isAsset = model => asset => asset.name.indexOf(model) >= 0 && asset.name.indexOf('app') >= 0
const isUltraAsset = isAsset('ultra')
const isLiteAsset = isAsset('lite')
return _.fromPairs(_.map(this?.manifest?.releases, release => [release.tagName, {
..._.pick(release, ['commit', 'gitVersion', 'prerelease', 'tagName']),
createdAt: new Date(release.createdAt),
lite: _.find(release.assets, isLiteAsset),
ultra: _.find(release.assets, isUltraAsset),
}]))
},
},
methods: {
async btnAdapterTips () {
await Swal.fire({
title: 'Browser & OS',
html: '<strong class="text-success">BLE</strong> is available in ChromeOS, Chrome for Android 6.0, Mac (Chrome 56) and Windows 10 (Chrome 70), <a class="btn-link" target="_blank" href="https://apps.apple.com/app/bluefy-web-ble-browser/id1492822055">Bluefy</a> for iPhone and iPad.<hr><strong class="text-success">USB</strong> is available on all desktop platforms (ChromeOS, Linux, macOS, and Windows) in Chrome 89.',
})
},
async fetchManifest () {
this.showLoading({ text: 'Loading firmwares...' })
const url = `https://taichunmin.idv.tw/ChameleonUltra-releases/manifest.json?t=${Math.trunc(Date.now() / 6e5)}`
this.$set(this, 'manifest', (await axios.get(url))?.data ?? {})
this.ss.tagName = _.first(this.tagNames)[0]
Swal.close()
},
async btnUploadFirmware () {
const { ultra } = this
const numFormater = new Intl.NumberFormat('en', { minimumFractionDigits: 1, maximumFractionDigits: 1 })
const showProgress = ({ func, offset, size, type }) => {
if (func !== 'dfuUploadObject' || type !== 2) return
const width = _.round(size > 0 ? offset / size * 100 : 0, 1)
const html = `<div class="d-flex flex-column"><div class="progress mb-2"><div class="progress-bar progress-bar-striped" role="progressbar" style="width: ${width}%"></div></div><div class="d-flex justify-content-between"><span>Uploading:</span><span>${numFormater.format(width)} %</span></div></div>`
this.showLoading({ html })
}
try {
this.showLoading({ text: 'Download firmware...' })
const release = this.releases[this.ss.tagName]
if (_.isNil(release)) throw new Error('Invalid tagName')
const images = await Promise.all(_.map(['ultra', 'lite'], async model => {
const dfuZipUrl = `${release[model].url}?t=${release.createdAt.getTime()}` // 避免快取,加上時間戳
ultra.emitter.emit('debug', 'web', `model = ${model}, url = ${dfuZipUrl}`)
const dfuZip = new DfuZip(new Buffer((await axios.get(dfuZipUrl, { responseType: 'arraybuffer' }))?.data))
return await dfuZip.getAppImage()
}))
this.showLoading({ text: 'Connect device...' })
await ultra.connect()
if (!ultra.isDfu()) {
const gitVersion = await ultra.cmdGetGitVersion()
if (!_.isNil(release.gitVersion) && release.gitVersion === gitVersion) {
const msg1 = `gitVersion(${gitVersion}) is the same, do you want to upload again?`
if (!await this.confirm(msg1, 'Yes', 'Cancel')) return
}
await ultra.cmdDfuEnter()
}
this.showLoading({ text: 'Uploading Firmware...' })
ultra.emitter.on('progress', showProgress)
let isUploadSuccess = false
for (const image of images) {
try {
await ultra.dfuUploadImage(image)
isUploadSuccess = true
break
} catch (err) {
ultra.emitter.emit('error', _.set(new Error(err.message), 'originalError', err))
}
}
if (!isUploadSuccess) throw new Error('Upload failed')
await Swal.fire({ icon: 'success', title: 'Upload Success' })
} catch (err) {
ultra.emitter.emit('error', err)
await Swal.fire({ icon: 'error', title: 'Upload Failed', text: err.message })
}
ultra.emitter.removeListener('progress', showProgress)
},
async sleep (t) {
await new Promise(resolve => setTimeout(resolve, t))
},
async confirm (text, confirmButtonText, cancelButtonText) {
return await new Promise((resolve, reject) => {
let isConfirmed = false
const args = {
cancelButtonColor: '#3085d6',
cancelButtonText,
confirmButtonColor: '#d33',
confirmButtonText,
didDestroy: () => { resolve(isConfirmed) },
focusCancel: true,
icon: 'warning',
reverseButtons: true,
showCancelButton: true,
text,
}
Swal.fire(args).then(res => { isConfirmed = res.isConfirmed })
})
},
showLoading (opts = {}) {
opts = {
allowOutsideClick: false,
showConfirmButton: false,
...opts,
}
if (Swal.isVisible()) return Swal.update(_.omit(opts, ['progressStepsDistance']))
Swal.fire({ ...opts, didRender: () => { Swal.showLoading() } })
},
},
})

6 changes: 3 additions & 3 deletions pug/src/hf14a-scanner.pug
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ block content

block script
script.
const { ChameleonDebug, ChameleonUltra, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const { ChameleonUltra, Debug, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const ultraUsb = new ChameleonUltra()
ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new Debug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra()
ultraBle.use(new ChameleonDebug())
ultraBle.use(new Debug())
ultraBle.use(new WebbleAdapter())
const toHex = buf => _.toUpper(buf.toString('hex'))

Expand Down
6 changes: 3 additions & 3 deletions pug/src/mfkey32.pug
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ block content

block script
script.
const { Buffer, ChameleonDebug, ChameleonUltra, DeviceMode, FreqType, Mf1KeyType, TagType, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const { Buffer, ChameleonUltra, Debug, DeviceMode, FreqType, Mf1KeyType, TagType, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const ultraUsb = new ChameleonUltra()
ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new Debug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra()
ultraBle.use(new ChameleonDebug())
ultraBle.use(new Debug())
ultraBle.use(new WebbleAdapter())

window.vm = new Vue({
Expand Down
6 changes: 3 additions & 3 deletions pug/src/mifare-value.pug
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,12 @@ block content
block script
script(crossorigin="anonymous", src="https://cdn.jsdelivr.net/npm/joi@17/dist/joi-browser.min.js")
script.
const { Buffer, ChameleonDebug, ChameleonUltra, Mf1KeyType, Mf1VblockOperator, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const { Buffer, ChameleonUltra, Debug, Mf1KeyType, Mf1VblockOperator, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const ultraUsb = new ChameleonUltra()
ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new Debug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra()
ultraBle.use(new ChameleonDebug())
ultraBle.use(new Debug())
ultraBle.use(new WebbleAdapter())

window.vm = new Vue({
Expand Down
6 changes: 3 additions & 3 deletions pug/src/mifare-xiaomi.pug
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,12 @@ block content

block script
script.
const { Buffer, ChameleonDebug, ChameleonUltra, DeviceMode, FreqType, TagType, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const { Buffer, ChameleonUltra, Debug, DeviceMode, FreqType, TagType, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const ultraUsb = new ChameleonUltra()
ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new Debug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra()
ultraBle.use(new ChameleonDebug())
ultraBle.use(new Debug())
ultraBle.use(new WebbleAdapter())

const toHex = buf => _.toUpper(buf.toString('hex'))
Expand Down
6 changes: 3 additions & 3 deletions pug/src/mifare1k.pug
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,12 @@ block content
block script
script(crossorigin="anonymous", src="https://cdn.jsdelivr.net/npm/joi@17/dist/joi-browser.min.js")
script.
const { Buffer, ChameleonDebug, ChameleonUltra, DeviceMode, FreqType, TagType, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const { Buffer, ChameleonUltra, Debug, DeviceMode, FreqType, TagType, WebbleAdapter, WebserialAdapter } = window.ChameleonUltraJS
const ultraUsb = new ChameleonUltra()
ultraUsb.use(new ChameleonDebug())
ultraUsb.use(new Debug())
ultraUsb.use(new WebserialAdapter())
const ultraBle = new ChameleonUltra()
ultraBle.use(new ChameleonDebug())
ultraBle.use(new Debug())
ultraBle.use(new WebbleAdapter())

const toHex = buf => _.toUpper(buf.toString('hex'))
Expand Down
Loading

0 comments on commit da173c8

Please sign in to comment.