Communicate an NFC reader with a web application
Gautier Darchen
- 23 🎂
-
Fullstack dev at Takima since 2018
Working at Cartier in a lab- React
- Node.js
- Java / Spring
- Devops
- Easily know the remaining tasks to be performed on a piece of jewelry for a given people
- Bar code / QR code
-
Innovation & a lot of data to hold
-
Innovation & a lot of data to hold
-
RFID- Short-range
- Short-range
- NFC ✅
NFC today
- Omnipresent in our daily life
- Contactless payment, access badge, etc.
- Cheap (NTAG 213 : about $ 0.08 per chip)
- Different types: frequency, encryption, etc.
Target application 🎯
Loading page
Result page
Local server NFC Reader and web-sockets
1st iteration
Summary
-
Buy a NFC Reader 💸
- ACR 122U — USB NFC Reader
Advanced Card System Ltd.
- ACR 122U — USB NFC Reader
- Start a Node.js server on the client side to have access to plugged devices 🔌
- Use a PC/SC library 📕
- Communicate the read payload to the web app via web-sockets 🧦
Client - server
- Need asynchronous communication
- polling
- server-sent events
- websockets
- Choice of a library: Socket.io
Connection workflow
NTAG 213
Memory organization
Local server
// src/index.js
// ...
io.on('connection', (socket) => {
socket.on('require-scan', () => {
const nfc = new NFC();
nfc.on('reader', (reader) => {
const pageNumber = 4;
const bufferLength = 48;
reader.on('card', async (card) => {
try {
const data = await reader.read(pageNumber, bufferLength);
const extractedPayload = data.toString().split('/')[1];
socket.emit('nfc-tag-scanned', extractedPayload);
} catch (err) {
// boilerplate code to handle the error
}
});
});
});
});
Client app
// src/pages/home/components/HomeContainer.jsx
// ...
const HomeContainer = () => {
const [socket, setSocket] = useState();
const [readTag, setReadTag] = useState();
useEffect(() => {
setSocket(SocketIO.connect(APILocalSocketUrl));
}, [APILocalSocketUrl]);
useEffect(() => {
if (!socket) {
return () => {};
}
socket.emit("require-scan");
socket.on("nfc-tag-scanned", payload => {
setReadTag(payload);
});
return () => socket.close();
}, [socket]);
return <Home readTag={readTag} />;
};
export default HomeContainer;
Demo 1
Cons
- Requires Node.js on the client 🛠
- Requires the user to start the web-socket server 👷
- Need to update on each client if the code changes for the web-socket server 🔄
- Works well on servers, but we are working here for desktop apps
- Change our distribution mode
Pros
- Users always have access to the last version of the web app ⚡️
Key learnings
- Easy installation 🛠️
- Easy distribution 🚚
- Desktop app 🖥️
How to do it with web technologies? 🤔
Electron application using a NFC Reader
2nd iteration
Electron
Build cross platform desktop apps with JavaScript, HTML, and CSS
- Have a desktop app developed using web frameworks/libraries (like an "embedded browser")
- Easy installation on the client side
- Node.js server on the client-side
- Access to the file-system, hardware, etc.
- Access to the file-system, hardware, etc.
- Atom, VS Code, Slack, etc.
Summary
-
Use the USB NFC Reader bought for solution #1
- ACR 122U — USB NFC Reader
Advanced Card System Ltd.
- ACR 122U — USB NFC Reader
- Use Electron to have access to plugged devices 🔌
- Use a PC/SC library that we already master 📕
Use the library
const HomeContainer = () => {
const nfc = new NFC();
const [readTag, setReadTag] = useState(undefined);
nfc.on("reader", reader => {
console.log(`${reader.reader.name} device attached`);
const pageNumber = 4;
const bufferLength = 48;
reader.on("card", card => {
console.log(`${reader.reader.name} card detected`, card);
reader.read(pageNumber, bufferLength).then(data => {
const extractedPayload = data.toString().split("/")[1];
setReadTag(extractedPayload);
});
});
});
return <Home readTag={readTag} />;
};
Demo 2
Limits
- Requires a build for each target platform (on each release)
- Requires a reinstall on each device after updates
- Manage the packaging and installation
- Slow and heavy release cycles
- Micro application with the release process of heavy industrial softwares
- Micro application with the release process of heavy industrial softwares
- Lots of configuration to use native modules
- Choose CI/CD providers that support all OS
Dream solution
- Web app 🌐
- Nothing installed client-side 🤤
Using WebUSB and an Arduino board
3rd iteration
WebUSB API
- Use an API implemented in the browser 🌐
- "Plug and play" devices for web applications 🔌
- Security: devices declare some "web origins" they are allowed to communicate with (white list) 🛡
-
Equivalent to CORS for HTTP requests
-
Equivalent to CORS for HTTP requests
- Native code & SDKs ➡️ cross-platform and "web-ready" libraries 📚
W3C spec draft: wicg.github.io/webusb
WebUSB API
Workflow
NFC Reader
- Arduino Leonardo
See compatible devices here:
https://github.com/webusb/arduino#compatible-hardware
- Adafruit PN532 NFC/RFID Controller Shield
- A bit of solder 👨🏭
1/2
NFC Reader
- About $50 per device 💵
- Handicraft and not yet industrialized 👨🔬
- Will be subcontracted
2/2
Arduino programming
Import libraries and configure the WebUSB Serial
// NFC Reading libraries
#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
#include <NfcAdapter.h>
// WebUSB library
#include <WebUSB.h>
// Web origin
#define PROTOCOL_HTTP 0 /* http:// */
WebUSB WebUSBSerial(PROTOCOL_HTTP, "localhost:3000");
// NFC Shield configuration
PN532_I2C pn532_i2c(Wire);
NfcAdapter nfc = NfcAdapter(pn532_i2c);
#define BAUD_RATE 9600
Arduino programming
Reading loop
void loop() {
if (nfc.tagPresent()){
NfcTag tag = nfc.read();
if (tag.hasNdefMessage()) { // If your tag has a message
NdefMessage message = tag.getNdefMessage();
// If you have more than 1 record then it will cycle through them
int recordCount = message.getRecordCount();
for (int i = 0; i < recordCount; i++) {
NdefRecord record = message.getRecord(i);
int payloadLength = record.getPayloadLength();
byte payload[payloadLength];
record.getPayload(payload);
String payloadAsString = ""; // Processes the message as a string vs as a HEX value
for (int c = 0; c < payloadLength; c++) {
payloadAsString += (char)payload[c];
}
WebUSBSerial.write(payloadAsString.c_str());
}
}
};
WebUSBSerial.flush();
delay(250);
}
Client live coding
👨💻
Working zone 🚧
- Draft Community status (since Aug. 2019)
- Enabled in Chrome 61 (Sept. 2017)
- Disabled for several months (security)
- Only available in secured context (HTTPs)
Production 🏭
- Only client-side code
- https://gdarchen.github.io/webusb-arduino-nfc/
Thank you! 🙏
Communicate a NFC Reader with a web application
By Gautier Darchen
Communicate a NFC Reader with a web application
Lorsqu'on développe un outil qui interagit avec un périphérique, nous sommes encore forcés de développer des clients lourds ou d'utiliser des conteneurs comme Electron. Pourquoi ne pas directement utiliser le navigateur? Eh oui, vos browsers, et notamment Chrome, ont de plus en plus d'APIs vous permettant d'accéder à des parties natives de vos équipements. Viens découvrir la WebUSB API à travers un exemple simple : la réalisation d'un lecteur de carte NFC en 100% web.
- 4,980