- HTML and IoT
- The WebIot Package
– Web NFC
– Web Bluetooth
– Web Serial
– Web USB - Current Browser Limitations
- The Source Code
– Project Init
– The index.js file
– The WebIOT Class
– The USBManager
– The SerialManager
– The BluetoothManager
– The NFCManager - Examples
– USB Example
– Serial Example
– Bluetooth Example
– NFC Example
HTML and IoT
IoT has been increasing in relevance over the past decade. The idea of connecting physical devices to a website has always intrigued me. Bringing the physical world into the digital in my opinion brings us closer together globally.
In this tutorial, I will go over how to build a WebIOT package so you can add IoT capabilities to your Javascript applications. Please like, comment, and share this article, it really helps.
Introducing the WebIoT NPM Package
Following my FOSS commitment for 2023 (check out SpeechKit and Laravel OpenAI API) my March package is called WebIOT it brings together a collection of Web APIs to easily give developers functions for interacting with IoT devices. It has classes for NFC, Bluetooth, Serial and USB. I am actively looking for PRs and constructive criticism.
Web NFC
The Web NFC API is a cool API that allows for interaction with NFC chips. It consists of a message, a reader and a record.
The Web NFC API allows exchanging data over NFC via light-weight NFC Data Exchange Format (NDEF) messages.
https://developer.mozilla.org/en-US/docs/Web/API/Web_NFC_API
You can get NFC chips really cheap on Amazon (use this link and I get a commission !) You can use NFC for all sorts of cool interactive things.
- Extend offline activity: NFC is an offline technology, it doesn’t need to be connected to a network in order to exchange data, it gets its electricity from the close contact radio wave exchange hitting the wire (yay physics). A cool implementation is adding real-world nodes for your web game, when users tap it they get a special prize.
- IoT device configurations: You can have users on your website get configuration data for your IoT devices without them having to download any additional software. This is extremely useful when paired with the Web Bluetooth API for GATT server configs.
- Sending data to devices: NFC is a secure way to write data to your IoT devices and let those handle the processing
Web Bluetooth
The Web Bluetooth API provides the ability to connect and interact with Bluetooth Low Energy peripherals.
https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API
The Web Bluetooth API allows developers to connect to Bluetooth LE devices and read and write data. Some useful implementations of Web Bluetooth
- Get local device updates
- Run webpage functionality based on device state i.e. a heart monitor make an animation run on certain BPM
Web Serial
The Web Serial API provides a way for websites to read from and write to serial devices. These devices may be connected via a serial port, or be USB or Bluetooth devices that emulate a serial port.
https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API
Web Serial allows us to connect to generic serial ports and interact with our devices. This means we can do things like connect our webpages to embedded devices such as a Raspberry Pi.
Web USB
The WebUSB API provides a way to expose non-standard Universal Serial Bus (USB) compatible devices services to the web, to make USB safer and easier to use.
https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API
The Web USB API allows us to work directly with USB peripherals and by extension if those devices has programs, run them.
Current Browser Limitations
Most of these APIs cannot be used on iOS currently.
Listen To Some Hacker Music While You Code
The Source Code
Init The Project
Creating a new project and running the npm create script
mkdir web-iot && cd web-iot
npm init
The package.json looks like this:
{
"name": "@mastashake08/web-iot",
"version": "1.0.0",
"description": "Connect to your IoT devices via usb, serial, NFC or Bluetooth",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mastashake08/web-iot.git"
},
"keywords": [
"iot",
"webserial",
"bluetooth",
"webusb",
"nfc"
],
"author": "Mastashake",
"license": "MIT",
"bugs": {
"url": "https://github.com/mastashake08/web-iot/issues"
},
"homepage": "https://github.com/mastashake08/web-iot#readme"
}
The index.js file
This is the entry point for the package and it simply exports our classes
import { WebIOT } from './classes/WebIOT'
import { NFCManager } from './classes/NFCManager'
import { BluetoothManager } from './classes/BluetoothManager'
import { SerialManager } from './classes/SerialManager'
import { USBManager } from './classes/USBManager'
export {
WebIOT,
NFCManager,
BluetoothManager,
SerialManager,
USBManager
}
The WebIOT Class
This is the base class for all our managers. It contains some functions for sending data to a remote server, a functionality that’s usually needed when dealing with IoT devices.
export class WebIOT {
debug = false
constructor(debug = false) {
this.debug = debug
}
sendData (url, options = {}, type='fetch') {
try {
switch (type) {
case 'fetch':
return this.sendFetch(url, options)
break;
case 'beacon':
return this.sendBeacon(url, options)
break;
default:
return this.sendFetch(url, options)
}
} catch (e) {
this.handleError(e)
}
}
sendBeacon (url, data) {
try {
navigator.sendBeacon(url, data)
} catch (e) {
this.handleError(e)
}
}
async sendFetch(url, options) {
try {
const res = await fetch(url, options)
if (res.status != 200) {
throw new Error(`HTTP error! Status: ${res.status}`)
} else {
return res
}
} catch (e) {
this.handleError(e)
}
}
handleError (e) {
if(this.debug) {
alert(e.message)
console.log(e.message)
} else {
throw e
}
}
}
The USBManager
The USBManager is responsible for working with USB devices.
import { WebIOT } from './WebIOT'
export class USBManager extends WebIOT{
#devices = {}
#selectedDevice = {}
constructor (debug = false) {
super(debug)
}
async getDevices () {
this.devices = await navigator.usb.getDevices()
return this.devices
}
async requestDevice(options = {}) {
this.selectedDevice = this.selectedDevice = await navigator.usb.requestDevice(options)
return this.selectedDevice
}
async openDevice() {
await this.connectDevice()
}
async closeDevice(options) {
await this.selectedDevice.close()
}
async connectDevice() {
await this.selectedDevice.open();
if (this.selectedDevice.configuration === null)
await this.selectedDevice.selectConfiguration(1);
await this.selectedDevice.claimInterface(0);
return this.selectedDevice
}
async writeData(endpointNumber, data) {
return await this.selectedDevice.transferOut(endpointNumber, data)
}
async readData(endpointNumber, data) {
return await this.selectedDevice.transferIn(endpointNumber, data)
}
}
}
The SerialManager
The SerialManager class manages serial connections.
import { WebIOT } from './WebIOT'
export class SerialManager extends WebIOT {
#ports = {}
#selectedPort = {}
constructor (debug = false) {
super(debug)
}
async getPorts () {
this.ports = await navigator.serial.getPorts()
return this.ports
}
async requestPort(options = {}) {
this.selectedPort = this.selectedPort = await navigator.serial.requestPort(options)
return this.selectedPort
}
async openPort(options) {
await this.selectedPort.open(options)
}
async closePort(options) {
await this.selectedPort.close()
}
async getInfo() {
await this.selectedPort.getInfo()
}
async setSignals(options) {
await this.selectedPort.setSignals(options)
}
async getSignals() {
return await this.selectedPort.getSignals()
}
async readData () {
const reader = this.selectedPort.readable.getReader();
// Listen to data coming from the serial device.
while (true) {
const { value, done } = await reader.read();
if (done) {
// Allow the serial port to be closed later.
reader.releaseLock();
break;
}
// value is a Uint8Array.
return value
}
}
async writeData(data) {
const writer = port.writable.getWriter();
await writer.write(data);
// Allow the serial port to be closed later.
writer.releaseLock();
}
}
The BluetoothManager
The BluetoothManager is responsible for managing Bluetooth devices
import { WebIOT } from './WebIOT'
export class BluetoothManager extends WebIOT {
#bluetooth = {}
#device = {}
#server = {}
#selectedService = {}
#services = {}
#characteristic = {}
#currentValue = null
constructor (debug = false) {
super(debug)
navigator.bluetooth.getAvailability().then((available) => {
if (available) {
this.bluetooth = navigator.bluetooth
} else {
alert("Doh! Bluetooth is not supported");
}
});
}
async getDevices (options = {acceptAllDevices: true}) {
return await this.requestDevice(options)
}
async requestDevice (options) {
try {
this.device = await navigator.bluetooth.requestDevice(options)
return this.device
} catch(e) {
alert(e.message)
}
}
async connectToServer () {
this.server = await this.device.gatt.connect()
}
async getService (service) {
this.selectedService = await this.server.getPrimaryService(service)
return this.selectedService
}
async getServices () {
this.services = await this.server.getPrimaryServices()
return this.services
}
async getCharacteristic (char) {
this.characteristic = await this.selectedService.getCharacteristic(char)
return this.characteristic
}
async getCharacteristics () {
return await this.selectedService.getCharacteristics()
}
async getValue () {
this.currentValue = await this.characteristic.readValue()
return this.currentValue
}
async writeValue(data) {
await this.characteristic.writeValue(data)
}
}
The NFCManager
Work with NFC tags with the NFCManager
import { WebIOT } from './WebIOT'
export class NFCManager extends WebIOT {
#nfc = {}
constructor (debug = false) {
super(debug)
if ('NDEFReader' in window) { /* Scan and write NFC tags */
this.nfc = new NDEFReader()
} else {
alert('NFC is not supported in your browser')
}
}
startNFC () {
this.nfc = new NDEFReader()
}
async readNFCData (readCb, errorCb = (event) => console.log(event)) {
this.nfc.onreading = readCb()
await this.nfc.scan()
}
async writeNFCData (records, errorCb = (event) => console.log(event)) {
try {
await this.nfc.write(records)
} catch (e) {
errorCb(e)
}
}
async lockNFCTag(errorCb = (event) => console.log(event)) {
try {
await this.nfc.makeReadOnly()
} catch(e) {
errorCb(e)
}
}
static generateNFC () {
return new NDEFReader()
}
}
Using It In Action
USB Example
Select a device and read/write data to pin 4
import { USBManager } from '@mastashake08/web-iot'
....
const usb = new USBManager()
// get devices
const devices = usb.getDevices()
// request a single device
const device = usb.requestDevice()
// open device after connecting to it
device = usb.openDevice()
// read 64 bytes of data from pin 4 on device
const readData = usb.readData(4, 64)
// write 64 bytes of data to pin 4
usb.writeData(4, new Uint8Array(64))
Serial Example
Have a user select a serial device and write 64 bytes of data to it
import { SerialManager } from '@mastashake08/web-iot'
....
const serial = new SerialManager()
// get a port
port = serial.requestPort()
// read data
const data = serial.readData()
// write 64 bytes data
serial.writeData(new Uint8Array(64))
Bluetooth Example
Let a user select a Bluetooth device and get the battery level
import { BluetoothManager } from '@mastashake08/web-iot'
....
const bt = new BluetoothManager()
// get a device
const device = bt.requestDevice(options)
// get services
const services = bt.getServices()
// get battery service
const service = bt.getService('battery_service')
// get batter level
const char = bt.getCharacteristic('battery_level')
// get battery value
const battery = bt.getValue()
//write value
bt.writeValue(new Uint8Array(64))
NFC Example
Read, Write, and lock tags
import { NFCManager } from '@mastashake08/web-iot'
....
// start NFC
const nfc = new NFCManager()
nfc.startNFC()
//Read a tag
const data = nfc.readNFCData(successCb, errorCb)
const writeData = "Hello World"
// Write to a tag
nfc.writeNFCData(writeData)
// Lock tag
nfc.lockNFCTag(errorCb)
Did You Enjoy This Tutorial?
If so please leave a comment and like this article and please share it on social media! I post weekly so please come back for more content!
Follow Me On Social Media
Follow Me On Youtube!
Get Your Next Domain Cheap & Support The Channel
I use Namecheap for all of my domains! Whenever I need a cheap solution for a proof-of-concept project I grab a domain name for as little as $1! When you sign up and buy your first domain with Namecheap I get a commission, it’s a great way to get a quality service and support this platform!
Get Your Next Domain Cheap
CLICK HERE
Become A Sponsor
Open-source work is free to use but it is not free to develop. If you enjoy my content and would like to see more please consider becoming a sponsor on Github or Patreon! Not only do you support me but you are funding tech programs for at risk youth in Louisville, Kentucky.
Join The Newsletter
By joining the newsletter, you get first access to all of my blogs, events, and other brand-related content delivered directly to your inbox. It’s 100% free and you can opt out at any time!
Check The Shop
You can also consider visiting the official #CodeLife shop! I have my own clothing/accessory line for techies as well as courses designed by me covering a range of software engineering topics.
-
Product on saleGPT Genie: A Solo Developer’s Guide to Mastering ChatGPT$1.99
-
These Fingers Unisex t-shirt$23.55 – $31.55
-
#CodeLife AirPods case$15.00
-
Embroidered #CodeLife Champion Backpack$44.50
-
#CodeLife Laptop Sleeve$25.00 – $28.00
-
#CodeLife Unisex T-Shirt$20.00 – $22.00
-
#CodeLife Unisex Joggers$31.50 – $33.50
-
Cuffed #CodeLife Beanie$20.00
-
Unisex #CodeLife Hoodie$36.50 – $38.50
[…] submitted by /u/One-Ad1988 [link] […]
[…] View Reddit through One-Ad1988 – View Supply […]